175 Commits

Author SHA1 Message Date
Felix Koppe
c4e5b7f7c8 Merge main 2024-11-28 15:02:41 +01:00
Hanno Fleischer
cbbef16374 added two more todos 2024-11-27 09:20:22 +01:00
Hanno Fleischer
c9362a7a95 fixed bugs so the programm would start and added some Todo where code is missing or was fraudulent 2024-11-27 09:19:57 +01:00
Felix
35cf092d5c Merge commit 2024-11-27 08:44:29 +01:00
Felix
06e2d831ef Fix errors 2024-11-27 08:42:45 +01:00
Felix
75b53f8309 Fix model compile errors 2024-11-27 08:39:50 +01:00
Daniel Grigencha
20c9000d56 Merge remote-tracking branch 'origin/development' into development 2024-11-26 23:29:02 +01:00
Daniel Grigencha
5a911326ba minor changes to the server state automaton 2024-11-26 23:28:49 +01:00
Hanno Fleischer
621bb9efae fixed bugs inside of the client state machine and the message UpdateTSK 2024-11-26 20:31:16 +01:00
Felix
58f94e73db Merge commit 2024-11-26 20:23:23 +01:00
Fleischer Hanno
84c289cfd1 implemented all methods required for the state pattern in the client and adjusted the messages to work with player ids instead of names 2024-11-26 20:04:58 +01:00
Fleischer Hanno
f827757ad1 added next method for Color 2024-11-26 18:16:19 +01:00
Daniel Grigencha
e4a9b16fd5 Merge remote-tracking branch 'origin/development' into development 2024-11-26 18:12:32 +01:00
Daniel Grigencha
d63d0cc2a0 changed default constructor of 'Player' class 2024-11-26 18:12:08 +01:00
Hanno Fleischer
a127ee524a Merge remote-tracking branch 'origin/development' into development
# Conflicts:
#	Projekte/.run/MdgaApp.run.xml
2024-11-26 18:05:37 +01:00
Hanno Fleischer
5c2df2430d reworked the client state machine and removed the seperate classes for the statemachines
these machines are now directly included in the parent states
2024-11-26 18:04:56 +01:00
Daniel Grigencha
2e76c41d3a added new 'Disconnect' client message and updated 'Player' and 'Game' classes 2024-11-26 18:02:19 +01:00
Fleischer Hanno
46a6552bca refactored the whole client package structure 2024-11-26 11:12:58 +01:00
Fleischer Hanno
1d95146272 refactored the model to incoporate a correct folder structure 2024-11-26 11:12:56 +01:00
Benjamin Feyer
9859d52e02 edted some tests with null tests 2024-11-26 11:12:55 +01:00
Benjamin Feyer
e18ea15efa added javadocs 2024-11-26 11:12:54 +01:00
Benjamin Feyer
461a497353 added some more testmethods 2024-11-26 11:12:54 +01:00
Benjamin Feyer
4c3099ddf2 corrected testmethods in clientStateTest 2024-11-26 11:12:54 +01:00
Benjamin Feyer
b0ab870451 added tests in the serverStateTest
added the testmethods for rolldice and movepiece
2024-11-26 11:12:54 +01:00
Benjamin Feyer
993c94c306 edited some tests in ServerStateTest 2024-11-26 11:12:54 +01:00
Benjamin Feyer
e52af59cac minor changes 2024-11-26 11:12:54 +01:00
Benjamin Feyer
40f1bdb51f added some testmethods and corrected other in the clientStatemachineTests 2024-11-26 11:12:54 +01:00
Benjamin Feyer
436dae4ebc added some testmethods for the client testing the statechanges in the dialogs 2024-11-26 11:12:54 +01:00
Benjamin Feyer
1b2d4df96f added some mor testcases for the clientstatemachine 2024-11-26 11:12:54 +01:00
Benjamin Feyer
f3ca9f01c0 added the empty testmethods in serverstateTest and edited the testmethods for substates of choocePiece in Client into MovePiece 2024-11-26 11:12:54 +01:00
Benjamin Feyer
3c97cdae38 edited a test in the clientStateTest 2024-11-26 11:12:54 +01:00
Benjamin Feyer
227d4286e5 editet tests for the server and client statemachines 2024-11-26 11:12:54 +01:00
Benjamin Feyer
9939ec2861 added empty serverstatetests 2024-11-26 11:12:54 +01:00
Benjamin Feyer
1a6a460f9f initial test commit,
added all testclasses except Playertest, Viewtest, Cameratest, SettingsTest, SoundTest, ReactionTest and ClientStateTest. And filled all created testclasses with empty testmethods, except ServerStateTest.
2024-11-26 11:12:54 +01:00
Daniel Grigencha
ec58e9c85f added import statement with the refactored server messages 2024-11-26 11:12:54 +01:00
Daniel Grigencha
cf6777023f added mdga server controller 2024-11-26 11:12:54 +01:00
Daniel Grigencha
739279d3df added new server state chart 2024-11-26 11:12:52 +01:00
Daniel Grigencha
b6bf25671f reverted server messages 2024-11-26 11:12:50 +01:00
Daniel Grigencha
5a9fd2a939 deleted server state automaton 2024-11-26 11:12:49 +01:00
Hanno Fleischer
e1b21de718 added two Pieces in RequestPlayCard in order to differentiate between own and enemy pieces 2024-11-26 11:12:39 +01:00
Hanno Fleischer
f321608132 added two lists in PossiblePiece in order to differentiate between own and enemy pieces 2024-11-26 11:12:39 +01:00
Hanno Fleischer
dd7a27629b added static methods to construct a PlayCard message for each card type 2024-11-26 11:12:39 +01:00
Hanno Fleischer
eba681c350 added getter for ArrayList Player in Game and created a flag for ready status in Player 2024-11-26 11:12:39 +01:00
Daniel Grigencha
f97eea3e5e added default constructor for serialization purposes 2024-11-26 11:12:39 +01:00
Hanno Fleischer
1582038dfe added an ArrayList of Player in game and added the received methods in clientgamelogic 2024-11-26 11:12:39 +01:00
Daniel Grigencha
798e996a8d fixed sonarlint errors and deleted map playerConnectionID 2024-11-26 11:12:39 +01:00
Fleischer Hanno
472d87b0c9 refactored ceremony message 2024-11-26 11:12:39 +01:00
Fleischer Hanno
7cfb863e5c corrected refactoring mistake, ich which RankingResponce was renamed to RankingResponse 2024-11-26 11:12:35 +01:00
Fleischer Hanno
7e1d2e833e added getter for dialogstatemachine in dialogs 2024-11-26 11:12:30 +01:00
Fleischer Hanno
67a87ffa81 made all con structors of clients states public 2024-11-26 11:12:30 +01:00
Daniel Grigencha
12fbf4e77e added javadocs to all server messages 2024-11-26 11:12:30 +01:00
Daniel Grigencha
f6d16a81bf added javadocs to all client messages 2024-11-26 11:12:30 +01:00
Fleischer Hanno
6938ce16b7 added the constructors for all client states and their statemachines 2024-11-26 11:12:30 +01:00
Daniel Grigencha
85ea4d340c added more logic for the server state diagram 2024-11-26 11:12:30 +01:00
Fleischer Hanno
e3d5d8e2e9 added message contents to the messages
addedn the conentents for all messages regarding the BPMN diagramm and own interpretation.
also created an identifier for pieces to be used for network communication between server and client so that they talk about the same piece.
2024-11-26 11:12:30 +01:00
Daniel Grigencha
6dfb2980fa added more logic for the server state diagram 2024-11-26 11:12:26 +01:00
Fleischer Hanno
f3894a5058 added a method to check if a player has pieces in his waiting area 2024-11-26 11:11:51 +01:00
Hanno Fleischer
787d8b558c added the method tryMove and the methods used by it into serverstate 2024-11-26 11:11:51 +01:00
Daniel Grigencha
3949a00932 added more logic for the server state diagram 2024-11-26 11:11:51 +01:00
Fleischer Hanno
7eafa3da39 refactored the whole client package structure 2024-11-25 17:43:29 +01:00
Fleischer Hanno
7178935553 refactored the model to incoporate a correct folder structure 2024-11-25 16:41:44 +01:00
Daniel Grigencha
0b538efbfb Merge remote-tracking branch 'origin/development' into development 2024-11-25 16:36:53 +01:00
Daniel Grigencha
f2d5221328 added import statement with the refactored server messages 2024-11-25 16:36:40 +01:00
Daniel Grigencha
06195854d8 added mdga server controller 2024-11-25 16:35:49 +01:00
Daniel Grigencha
321909387d added new server state chart 2024-11-25 16:35:27 +01:00
Hanno Fleischer
933c7ecad7 Merge branch 'dev/test' into 'development'
merge for refactoring of project structure

See merge request progproj/gruppen-ht24/Gruppe-01!15
2024-11-25 15:34:42 +00:00
Felix
806a149bfc Merge dev/client_koppe 2024-11-25 16:33:04 +01:00
Benjamin Feyer
1803fb5549 edted some tests with null tests 2024-11-25 16:32:51 +01:00
Felix
96489f2454 Minor work 2024-11-25 16:32:16 +01:00
Felix
439231aecf Add rotateable cam 2024-11-25 16:15:59 +01:00
Daniel Grigencha
9759b6871b reverted server messages 2024-11-25 16:14:15 +01:00
Daniel Grigencha
52d9fff493 deleted server state automaton 2024-11-25 15:43:29 +01:00
Hanno Fleischer
31b449662f added two Pieces in RequestPlayCard in order to differentiate between own and enemy pieces 2024-11-25 15:09:24 +01:00
Hanno Fleischer
3be037d590 added two lists in PossiblePiece in order to differentiate between own and enemy pieces 2024-11-25 15:07:43 +01:00
Felix
8982662f5b Remove debug statement 2024-11-25 14:01:06 +01:00
Felix
d70efb32ea Fix audio settings 2024-11-25 13:59:55 +01:00
Hanno Fleischer
d15242f816 added static methods to construct a PlayCard message for each card type 2024-11-25 13:42:00 +01:00
Hanno Fleischer
4705512648 added getter for ArrayList Player in Game and created a flag for ready status in Player 2024-11-25 12:52:22 +01:00
Hanno Fleischer
7efb89c634 Merge remote-tracking branch 'origin/development' into development 2024-11-25 11:20:09 +01:00
Hanno Fleischer
15d1c36dad added an ArrayList of Player in game and added the received methods in clientgamelogic 2024-11-25 11:19:17 +01:00
Benjamin Feyer
ce528457a5 added javadocs 2024-11-25 02:30:43 +01:00
Benjamin Feyer
2cfa328c4c added some more testmethods 2024-11-25 01:50:02 +01:00
Benjamin Feyer
928304fb4b corrected testmethods in clientStateTest 2024-11-25 01:24:11 +01:00
Benjamin Feyer
aa88dff566 added tests in the serverStateTest
added the testmethods for rolldice and movepiece
2024-11-25 00:36:59 +01:00
Benjamin Feyer
f7bd9a0f38 edited some tests in ServerStateTest 2024-11-25 00:20:47 +01:00
Daniel Grigencha
cc800a8dd7 added default constructor for serialization purposes 2024-11-24 23:55:48 +01:00
Daniel Grigencha
7c5720cb9d fixed sonarlint errors and deleted map playerConnectionID 2024-11-24 23:06:30 +01:00
Benjamin Feyer
1821a222e9 minor changes 2024-11-24 22:24:39 +01:00
Benjamin Feyer
2e302ddc75 Merge branch 'development' into 'dev/test'
merge changes into the testbranch

See merge request progproj/gruppen-ht24/Gruppe-01!14
2024-11-24 21:23:23 +00:00
Fleischer Hanno
ed47883281 refactored ceremony message 2024-11-24 22:20:04 +01:00
Fleischer Hanno
599b3e8f47 corrected refactoring mistake, ich which RankingResponce was renamed to RankingResponse 2024-11-24 22:18:48 +01:00
Fleischer Hanno
040b8830ab added getter for dialogstatemachine in dialogs 2024-11-24 22:03:19 +01:00
Benjamin Feyer
6fa3e8c00d Merge branch 'development' into 'dev/test'
Development

See merge request progproj/gruppen-ht24/Gruppe-01!13
2024-11-24 20:58:39 +00:00
Fleischer Hanno
83c79af31a made all con structors of clients states public 2024-11-24 21:56:32 +01:00
Daniel Grigencha
653c3fb962 added javadocs to all server messages 2024-11-24 20:51:03 +01:00
Daniel Grigencha
8932c8a8cd added javadocs to all client messages 2024-11-24 19:56:51 +01:00
Benjamin Feyer
4c078ab0e2 added some testmethods and corrected other in the clientStatemachineTests 2024-11-24 19:37:03 +01:00
Benjamin Feyer
2a97ede985 added some testmethods for the client testing the statechanges in the dialogs 2024-11-24 19:03:19 +01:00
Benjamin Feyer
56492cdda6 added some mor testcases for the clientstatemachine 2024-11-24 18:40:10 +01:00
Benjamin Feyer
a71619612a added the empty testmethods in serverstateTest and edited the testmethods for substates of choocePiece in Client into MovePiece 2024-11-24 18:21:26 +01:00
Benjamin Feyer
f18a62b089 Merge branch 'development' into 'dev/test'
Testmerge

See merge request progproj/gruppen-ht24/Gruppe-01!12
2024-11-24 15:09:07 +00:00
Benjamin Feyer
c204984a74 edited a test in the clientStateTest 2024-11-24 16:03:12 +01:00
Fleischer Hanno
1d4048cf16 added the constructors for all client states and their statemachines 2024-11-24 16:02:04 +01:00
Benjamin Feyer
2095ea5866 editet tests for the server and client statemachines 2024-11-24 15:49:01 +01:00
Daniel Grigencha
f425eff26b added more logic for the server state diagram 2024-11-24 15:27:10 +01:00
Hanno Fleischer
cd22ece485 Merge branch 'dev/client' into 'development'
Merge results from dev/client into development

See merge request progproj/gruppen-ht24/Gruppe-01!11
2024-11-24 09:45:30 +00:00
Cedric Beck
16dc28013e merge development into dev/client 2024-11-24 10:39:28 +01:00
Cedric Beck
9ab71bfaf6 merge dev/client_koppe into dev/client 2024-11-24 10:34:48 +01:00
Cedric Beck
ccad47f95a Merge branch 'dev/client' into dev/client_koppe 2024-11-24 10:32:00 +01:00
Cedric Beck
f830aec8ce Merge branch 'dev/client_beck' into 'dev/client'
Added basic display of bonusCards for presentation on Monday

See merge request progproj/gruppen-ht24/Gruppe-01!10
2024-11-24 09:31:33 +00:00
Cedric Beck
c8c0188452 merge dev/client into dev/client_koppe 2024-11-24 10:30:37 +01:00
Cedric Beck
a5b7488e92 Merge branch 'dev/client' into dev/client_beck 2024-11-24 09:23:57 +01:00
Cedric Beck
11794b6ac7 added GuiHandler and showing bonuscards 2024-11-24 09:21:44 +01:00
Cedric Beck
a8d80fd3f4 Merge branch 'dev/client_beck' into 'dev/client'
Added all basic functionalitites to BoardHandler

See merge request progproj/gruppen-ht24/Gruppe-01!9
2024-11-23 21:12:14 +00:00
Fleischer Hanno
806f0d7d9d added message contents to the messages
addedn the conentents for all messages regarding the BPMN diagramm and own interpretation.
also created an identifier for pieces to be used for network communication between server and client so that they talk about the same piece.
2024-11-23 12:26:20 +01:00
Cedric Beck
7368014b10 merge dev/client into dev/client_beck 2024-11-22 20:38:00 +01:00
Cedric Beck
ebdedc6494 added working piece outline; added final tweaks for boardHandler 2024-11-22 19:53:32 +01:00
Benjamin Feyer
df2a7151f0 Merge branch 'development' into 'dev/test'
Merge Test to Development

See merge request progproj/gruppen-ht24/Gruppe-01!8
2024-11-22 12:56:51 +00:00
Cedric Beck
00014eeb09 tried fixing outline problem 2024-11-22 13:56:43 +01:00
Daniel Grigencha
89232901a7 added more logic for the server state diagram 2024-11-22 09:37:49 +01:00
Felix
a79a315f83 Minor work 2024-11-21 16:44:28 +01:00
Felix
c470c205e4 Add host&join menu and work on modelSyncronizer 2024-11-21 16:16:19 +01:00
Felix
982ca00b55 Make lobby buttons colored 2024-11-21 16:16:19 +01:00
Felix
c1f4ea480c Add host&join menu and work on modelSyncronizer 2024-11-21 16:14:06 +01:00
Cedric Beck
a8a725611f added highlighting -> bug all assets are transparent 2024-11-21 09:56:40 +01:00
Felix
02ce0df614 Make lobby buttons colored 2024-11-20 15:21:07 +01:00
Felix Koppe
6e57e309cc Merge branch 'dev/client_koppe' into 'dev/client'
Merge work

See merge request progproj/gruppen-ht24/Gruppe-01!7
2024-11-20 12:50:00 +00:00
Felix
d87acd46cc Fix camera init/shutdown 2024-11-20 13:48:59 +01:00
Felix
58395fe3cb Merge dev/client_beck 2024-11-20 13:42:10 +01:00
Felix
5f54eff038 Add inputHandler 2024-11-20 13:32:50 +01:00
Felix
184b565526 Add volume controll 2024-11-20 12:40:51 +01:00
Felix
176efc2aca Add settings menus 2024-11-20 09:24:01 +01:00
Felix
bb491a2682 Make button size dynamic 2024-11-19 19:27:04 +01:00
Felix
51e83c5ae4 Implement notificationSyncronizer 2024-11-19 19:01:52 +01:00
Felix
47f0e44d0a Merge development 2024-11-19 18:40:08 +01:00
Felix
80fab1ffdc Begin work on actionHandler 2024-11-19 18:36:49 +01:00
Felix
03eef66332 Finish buttons 2024-11-19 18:28:37 +01:00
Felix
34415bc9f2 Add left/right buttons 2024-11-19 14:16:01 +01:00
Felix
030842d251 Some work 2024-11-19 12:52:43 +01:00
Hanno Fleischer
a3a9f0d88d Merge branch 'development' into 'dev/test'
TestMerge 3

See merge request progproj/gruppen-ht24/Gruppe-01!6
2024-11-19 11:48:06 +00:00
Benjamin Feyer
6790be782e added empty serverstatetests 2024-11-19 12:42:11 +01:00
Cedric Beck
56256fb9c0 fixed rotation and added functionality für BoardHandler; added shiel visual 2024-11-18 21:30:30 +01:00
Felix
c6438d75cc Fix acousticHandler error 2024-11-18 16:40:01 +01:00
Felix
728530a8f2 Finish loop 2024-11-18 16:16:56 +01:00
Felix
7c09107d28 Work on Dialogs 2024-11-18 15:58:30 +01:00
Cedric Beck
2399bf2678 added world and trees for tree positioning 2024-11-18 12:40:40 +01:00
Cedric Beck
06d795b3e6 working on asset simplification 2024-11-18 12:03:42 +01:00
Cedric Beck
8d9a923970 added rotation for move piece, fixed problems because rotation on the z axis is reversed 2024-11-18 01:58:24 +01:00
Cedric Beck
c95beaeb14 working on pieceMovement 2024-11-17 23:57:36 +01:00
Daniel Grigencha
04501de11c Merge remote-tracking branch 'origin/development' into development 2024-11-17 21:18:38 +01:00
Fleischer Hanno
9cd9cc871c fixed a bug with use of addLast method for ArrayList which doesnot exist in Java 20 2024-11-17 21:18:19 +01:00
Fleischer Hanno
f69a2a9fda added a method to check if a player has pieces in his waiting area 2024-11-17 21:04:13 +01:00
Daniel Grigencha
e83ed1c835 Merge remote-tracking branch 'origin/development' into development 2024-11-17 20:17:29 +01:00
Daniel Grigencha
f379a6b638 added more logic for the server state diagram 2024-11-17 20:17:19 +01:00
Hanno Fleischer
90fb6e4133 added the method tryMove and the methods used by it into serverstate 2024-11-17 17:52:25 +01:00
Hanno Fleischer
9662e1f684 fixed a Bug where the import statement thought a package was a class because they where written the same 2024-11-17 15:34:39 +01:00
Daniel Grigencha
aae7ed9a87 added classes for client and server state machine
- a client state machine consits out of a 'ClientState' (every state of the machine) and a 'ClientStateMachine' (every state, which consists out of states), the machine starts with the ClientAutomaton
- analog for server
- started to implement logic for the server, transition from 'Lobby' to 'GameState'
2024-11-17 15:27:09 +01:00
Hanno Fleischer
07bd7dfa3d Merge branch 'development' into 'dev/test'
Testmerge

See merge request progproj/gruppen-ht24/Gruppe-01!5
2024-11-17 14:20:53 +00:00
Felix
b15dd96a86 Improve music fade and mock 2024-11-17 14:28:48 +01:00
Felix
ada90e787d Improve mock 2024-11-17 13:03:54 +01:00
Felix
cbeb296a44 Mock differen Views 2024-11-17 12:42:20 +01:00
Hanno Fleischer
a9fd13caab Merge branch 'dev/client' into 'development'
Client work of the Week

See merge request progproj/gruppen-ht24/Gruppe-01!4
2024-11-17 11:24:45 +00:00
Hanno Fleischer
a926554709 adjusted for check style 2024-11-17 12:01:00 +01:00
Cedric Beck
bbd84dd961 added code + checkstyle 2024-11-17 11:26:45 +01:00
Cedric Beck
e22f675f8b fixed merge conflict 2024-11-17 11:07:53 +01:00
Cedric Beck
956e9dd2fd merge from dev/client 2024-11-17 10:57:05 +01:00
Hanno Fleischer
020aa92cab added 4 more Notifications for Model -> View interaction 2024-11-17 10:39:36 +01:00
Felix Koppe
bb55ba7273 Merge branch 'dev/client_koppe' into 'dev/client'
Work of the week

See merge request progproj/gruppen-ht24/Gruppe-01!3
2024-11-17 09:09:13 +00:00
Cedric Beck
a2c7b9e299 added all assets; added node array; added node/piece_control saving 2024-11-16 23:05:30 +01:00
Hanno Fleischer
0845aa80f9 adjusted the model infield creation to represent the current view postion regarding the placement of the teams on the board 2024-11-16 22:31:11 +01:00
Hanno Fleischer
44b623f9fd added all Notifications for the model view communication
added all required notifications with their content and getter methods.
2024-11-16 18:58:33 +01:00
Hanno Fleischer
6b2d775534 added 2 methods for Test usage in Board and PlayerData 2024-11-16 13:31:56 +01:00
Cedric Beck
d0be65323e added rotation compability to map.mdga 2024-11-15 22:47:23 +01:00
Cedric Beck
81facb869f added playerMap for location playerAssets 2024-11-15 21:26:23 +01:00
Benjamin Feyer
33dbbdbe5c initial test commit,
added all testclasses except Playertest, Viewtest, Cameratest, SettingsTest, SoundTest, ReactionTest and ClientStateTest. And filled all created testclasses with empty testmethods, except ServerStateTest.
2024-11-14 18:07:36 +01:00
381 changed files with 56390 additions and 2071 deletions

View File

@@ -1,17 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MdgaApp" type="Application" factoryName="Application" singleton="false" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="pp.mdga.client.MdgaApp" />
<module name="Projekte.mdga.client.main" />
<option name="VM_PARAMETERS" value="-Djava.util.logging.config.file=logging.properties -ea" />
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="pp.mdga.client.Board.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -9,6 +9,12 @@ implementation project(":jme-common")
implementation project(":mdga:model") implementation project(":mdga:model")
implementation libs.jme3.desktop implementation libs.jme3.desktop
implementation libs.jme3.core
implementation libs.jme3.lwjgl3
implementation libs.jme3.lwjgl
implementation libs.jme3.desktop
implementation libs.jme3.effects
runtimeOnly libs.jme3.awt.dialogs runtimeOnly libs.jme3.awt.dialogs
runtimeOnly libs.jme3.plugins runtimeOnly libs.jme3.plugins

View File

@@ -1,290 +0,0 @@
package pp.mdga.client.Acoustic;
import com.jme3.system.NanoTimer;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.MdgaState;
import java.util.*;
public class AcousticHandler {
private MdgaApp app;
private MdgaState state = MdgaState.NONE;
private boolean playGame = false;
private ArrayList<MusicAsset> gameTracks = new ArrayList<>();
private NanoTimer trackTimer = new NanoTimer();
private boolean fading = false;
private NanoTimer fadeTimer = new NanoTimer();
private final float FADE_DURATION = 3.0f;
private final float CROSSFADE_DURATION = 1.5f;
private float mainVolume = 1.0f;
private float musicVolume = 1.0f;
private float soundVolume = 1.0f;
private GameMusic scheduled = null;
private GameMusic playing = null;
private ArrayList<GameSound> sounds = new ArrayList<>();
public AcousticHandler(MdgaApp app) {
this.app = app;
}
/**
* This method updates the acousticHandler and should be called every frame
*/
public void update() {
updateVolumeAndTrack();
if(playGame) {
updateGameTracks();
}
Iterator<GameSound> iterator = sounds.iterator();
while (iterator.hasNext()) {
GameSound s = iterator.next();
s.update(getSoundVolumeTotal());
if (!s.isPlaying()) {
iterator.remove();
}
}
}
/**
* This method instantly plays a sound
*
* @param sound the sound to be played
*/
public void playSound(MdgaSound sound) {
ArrayList<SoundAssetDelayVolume> assets = new ArrayList<SoundAssetDelayVolume>();
switch (sound) {
case LOST:
assets.add(new SoundAssetDelayVolume(SoundAsset.LOST, 1.0f, 0.0f));
break;
case VICTORY:
assets.add(new SoundAssetDelayVolume(SoundAsset.VICTORY, 1.0f, 2.0f));
break;
default:
break;
}
for (SoundAssetDelayVolume sawd : assets) {
GameSound gameSound = new GameSound(app, sawd.asset(), getSoundVolumeTotal(), sawd.subVolume(), sawd.delay());
sounds.add(gameSound);
}
}
/**
* This method fades the played music to fit the state.
*
* @param state the state of which the corresponding music should be played to be played
*/
public void playState(MdgaState state) {
if(this.state == state) {
return;
}
MusicAsset asset = null;
switch (state) {
case MAIN:
playGame = false;
asset = MusicAsset.MAIN_MENU;
break;
case LOBBY:
playGame = false;
asset = MusicAsset.LOBBY;
break;
case GAME:
addGameTracks();
playGame = true;
assert(gameTracks.size() > 0) : "no more game music available";
asset = gameTracks.remove(0);
break;
case CEREMONY:
playGame = false;
asset = MusicAsset.CEREMONY;
break;
}
assert(null != asset) : "music sceduling went wrong";
scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop());
}
/**
* Performs linear interpolation between two float values.
*
* @param start The starting value.
* @param end The ending value.
* @param t The interpolation factor, typically between 0 and 1.
* @return The interpolated value between start and end.
*/
private float lerp(float start, float end, float t) {
return start + t * (end - start);
}
/**
* Updates the current volume and handles track crossfading logic.
* This method is responsible for fading out the currently playing track,
* fading in the scheduled track, and handling crossfade between the two tracks.
*/
private void updateVolumeAndTrack() {
if (playing == null && scheduled != null && !fading) {
playing = scheduled;
scheduled = null;
playing.play();
return;
}
if (scheduled != null && !fading) {
fading = true;
fadeTimer.reset();
}
if (fading) {
float time = fadeTimer.getTimeInSeconds();
if (time <= FADE_DURATION) {
float t = Math.min(time / FADE_DURATION, 1.0f);
float oldVolume = lerp(1.0f, 0.0f, t);
if (playing != null) {
playing.update(getMusicVolumeTotal()* oldVolume);
}
}
if (time > FADE_DURATION && time <= FADE_DURATION + CROSSFADE_DURATION) {
float t = Math.min((time - FADE_DURATION) / CROSSFADE_DURATION, 1.0f);
float newVolume = lerp(0.0f, 1.0f, t);
if (!scheduled.isPlaying()) {
scheduled.play();
}
scheduled.update(getMusicVolumeTotal() * newVolume);
}
if (time > FADE_DURATION + CROSSFADE_DURATION) {
if (playing != null) {
playing.pause();
}
playing = scheduled;
scheduled = null;
fading = false;
}
} else if (playing != null) {
playing.update(getMusicVolumeTotal());
}
}
/**
* Adds a list of game tracks to the gameTracks collection and shuffles them.
* This method adds predefined game tracks to the track list and shuffles the order.
*/
private void addGameTracks() {
Random random = new Random();
for (int i = 1; i <= 6; i++) {
gameTracks.add(MusicAsset.valueOf("GAME_" + i));
}
Collections.shuffle(gameTracks, random);
}
/**
* Updates the current game tracks. If the currently playing track is nearing its end,
* a new track will be scheduled to play. If the list of game tracks is empty, it will be refreshed.
*/
private void updateGameTracks() {
if(playing.nearEnd(10)) {
if (gameTracks.isEmpty()) {
addGameTracks();
}
}
if (playing != null && playing.nearEnd(3) && trackTimer.getTimeInSeconds() > 20) {
trackTimer.reset();
MusicAsset nextTrack = gameTracks.remove(0);
scheduled = new GameMusic(app, nextTrack, getMusicVolumeTotal(), nextTrack.getSubVolume(), nextTrack.getLoop());
}
}
/**
* Retrieves the main volume level.
*
* @return The current main volume level.
*/
public float getMainVolume() {
return mainVolume;
}
/**
* Retrieves the music volume level.
*
* @return The current music volume level.
*/
public float getMusicVolume() {
return musicVolume;
}
/**
* Retrieves the sound volume level.
*
* @return The current sound volume level.
*/
public float getSoundVolume() {
return soundVolume;
}
/**
* Sets the main volume level.
*
* @param mainVolume The desired main volume level.
*/
public void setMainVolume(float mainVolume) {
this.mainVolume = mainVolume;
}
/**
* Sets the music volume level.
*
* @param musicVolume The desired music volume level.
*/
public void setMusicVolume(float musicVolume) {
this.musicVolume = musicVolume;
}
/**
* Sets the sound volume level.
*
* @param soundVolume The desired sound volume level.
*/
public void setSoundVolume(float soundVolume) {
this.soundVolume = soundVolume;
}
/**
* Calculates the total music volume by multiplying the music volume by the main volume.
*
* @return The total music volume.
*/
float getMusicVolumeTotal() {
return getMusicVolume() * getMainVolume();
}
/**
* Calculates the total sound volume by multiplying the sound volume by the main volume.
*
* @return The total sound volume.
*/
float getSoundVolumeTotal() {
return getSoundVolume() * getMainVolume();
}
}

View File

@@ -1,4 +0,0 @@
package pp.mdga.client.Animation;
public enum MdgaAnimation {
}

View File

@@ -1,6 +1,6 @@
package pp.mdga.client.Board; package pp.mdga.client;
enum BoardAsset { public enum Asset {
bigTent, bigTent,
cardStack, cardStack,
cir, cir,
@@ -9,45 +9,60 @@ enum BoardAsset {
lw, lw,
marine, marine,
node_home_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"), node_home_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"),
node_wait_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"),
node_home_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"), node_home_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"),
node_wait_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"),
node_home_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"), node_home_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"),
node_home_yellow("./node_home/node_home.j3o", "./node_home/node_home_yellow_diff.png"), node_wait_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"),
node_home_yellow("./node_home/node_home.j3o", "./node_home/node_home_orange_diff.png"),
node_wait_yellow("./node_home/node_home.j3o", "./node_home/node_home_orange_diff.png"),
node_normal, node_normal,
node_start("./node_normal/node_normal.j3o", "./node_normal/node_start_diff.png"), node_start("./node_normal/node_normal.j3o", "./node_normal/node_start_diff.png"),
node_bonus("./node_normal/node_normal.j3o", "./node_normal/node_bonus_diff.png"), node_bonus("./node_normal/node_normal.j3o", "./node_normal/node_bonus_diff.png"),
radar, radar,
shieldCard, ship(0.8f),
ship,
smallTent, smallTent,
swapCard,
tank, tank,
// world(1.2f),
world("./world_new/world_export_new.obj", "./world_new/world_new_diff.png", 1.2f),
shield_ring("./shield_ring/shield_ring.obj", null),
tree_small("./tree_small/tree_small.obj", "./tree_small/tree_small_diff.png"),
tree_big("./tree_big/tree_big.obj", "./tree_big/tree_big_diff.png"),
turboCard, turboCard,
world(1.2f); swapCard,
shieldCard
;
private final String modelPath; private final String modelPath;
private final String diffPath; private final String diffPath;
private final float size; private final float size;
BoardAsset(){ Asset() {
String folderFileName = "./" + name() + "/" + name(); String folderFileName = "./" + name() + "/" + name();
this.modelPath = folderFileName + ".j3o"; this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png"; this.diffPath = folderFileName + "_diff.png";
this.size = 1f; this.size = 1f;
} }
BoardAsset(String modelPath, String diffPath){ Asset(String modelPath, String diffPath) {
this.modelPath = modelPath; this.modelPath = modelPath;
this.diffPath = diffPath; this.diffPath = diffPath;
this.size = 1f; this.size = 1f;
} }
BoardAsset(float size){ Asset(float size) {
String folderFileName = "./" + name() + "/" + name(); String folderFileName = "./" + name() + "/" + name();
this.modelPath = folderFileName + ".j3o"; this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png"; this.diffPath = folderFileName + "_diff.png";
this.size = size; this.size = size;
} }
Asset(String modelPath, String diffPath, float size){
this.modelPath = modelPath;
this.diffPath = diffPath;
this.size = size;
}
public String getModelPath() { public String getModelPath() {
return modelPath; return modelPath;
} }
@@ -56,8 +71,7 @@ public String getDiffPath() {
return diffPath; return diffPath;
} }
public float getSize(){ public float getSize() {
return size; return size;
} }
} }

View File

@@ -1,3 +0,0 @@
package pp.mdga.client.Board;
record AssetOnMap(BoardAsset boardAsset, int x, int y){}

View File

@@ -1,95 +0,0 @@
package pp.mdga.client.Board;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import pp.mdga.client.MdgaApp;
import java.util.ArrayList;
import java.util.List;
public class BoardView {
private static final float GRID_SIZE = 1.72f;
private static final float GRID_ELEVATION = 0.0f;
private static final String MAP_NAME = "circle_map.mdga";
private final MdgaApp app;
private PileControl drawPile = null;
private PileControl discardPile = null;
private ArrayList<NodeControl> infield = new ArrayList<NodeControl>(40);
private ArrayList<PieceControl> pieces;
public BoardView(MdgaApp app) {
assert(app != null) : "app is null";
this.app = app;
pieces = new ArrayList<PieceControl>(4 * 4);
initMap();
initCamera();
}
private void initCamera() {
app.getFlyByCamera().setEnabled(true);
int zoom = 20;
app.getCamera().setLocation(new Vector3f(zoom,0,zoom));
app.getCamera().lookAt(new Vector3f(0,0,0), new Vector3f(0,0,1));
DirectionalLight sun = new DirectionalLight();
sun.setColor(ColorRGBA.White);
sun.setDirection(new Vector3f(-1,0,-1));
app.getRootNode().addLight(sun);
AmbientLight ambient = new AmbientLight();
ambient.setColor(new ColorRGBA(0.3f,0.3f,0.3f,1));
app.getRootNode().addLight(ambient);
final int SHADOWMAP_SIZE= 1024 * 8;
DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(app.getAssetManager(), SHADOWMAP_SIZE, 4);
dlsr.setLight(sun);
app.getViewPort().addProcessor(dlsr);
}
private void initMap() {
List<AssetOnMap> assetsOnMap = MapLoader.loadMap(MAP_NAME);
for (AssetOnMap aom : assetsOnMap){
int x = aom.x();
int y = aom.y();
Vector3f pos = gridToWorld(x,y);
if(aom.boardAsset().name().contains("node")) {
infield.add(new NodeControl(app, pos, aom.boardAsset()));
} else {
Spatial model = createModel(aom.boardAsset());
model.setLocalTranslation(pos);
}
}
}
private Spatial createModel(BoardAsset boardAsset){
String modelName = boardAsset.getModelPath();
String texName = boardAsset.getDiffPath();
Spatial model = app.getAssetManager().loadModel(modelName);
model.scale(boardAsset.getSize());
model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(90));
model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(texName));
model.setMaterial(mat);
app.getRootNode().attachChild(model);
return model;
}
private static Vector3f gridToWorld(int x, int y) {
return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
}
}

View File

@@ -1,66 +0,0 @@
package pp.mdga.client.Board;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
class MapLoader {
static public List<AssetOnMap> loadMap(String mapName) {
List<AssetOnMap> assetsOnMap = new ArrayList<>();
try (InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(mapName);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
while (true) {
String entry = reader.readLine();
if(entry == null) break;
entry = entry.trim();
if(entry.isEmpty()) continue;
if(entry.charAt(0) == '#') continue;
String[] parts = entry.trim().split(" ");
assert(parts.length == 2) : "parts.lenghth != 2";
String assetName = parts[0];
String[] coordinates = parts[1].split(",");
assert(coordinates.length == 2) : "coordinates.lenghth != 2";
int x = Integer.parseInt(coordinates[0]);
int y = Integer.parseInt(coordinates[1]);
BoardAsset boardAsset = getLoadedAsset(assetName);
assetsOnMap.add(new AssetOnMap(boardAsset, x, y));
}
} catch (Exception e) {
e.printStackTrace();
}
return assetsOnMap;
}
static private BoardAsset getLoadedAsset(String assetName) {
return switch(assetName){
case "node" -> BoardAsset.node_normal;
case "node_start" -> BoardAsset.node_start;
case "node_bonus" -> BoardAsset.node_bonus;
case "node_home_blue" -> BoardAsset.node_home_blue;
case "node_home_yellow" -> BoardAsset.node_home_yellow;
case "node_home_black" -> BoardAsset.node_home_black;
case "node_home_green" -> BoardAsset.node_home_green;
case "world" -> BoardAsset.world;
case "tent_big" -> BoardAsset.bigTent;
case "tent_small" -> BoardAsset.smallTent;
case "stack" -> BoardAsset.cardStack;
case "jet" -> BoardAsset.jet;
case "radar" -> BoardAsset.radar;
case "ship" -> BoardAsset.ship;
case "tank" -> BoardAsset.tank;
default -> throw new IllegalStateException("Unexpected asset in .mdga file: " + assetName);
};
}
}

View File

@@ -1,30 +0,0 @@
package pp.mdga.client.Board;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import pp.mdga.client.MdgaApp;
class NodeControl {
private final MdgaApp app;
private Spatial model;
NodeControl(MdgaApp app, Vector3f pos, BoardAsset boardAsset) {
this.app = app;
String modelName = boardAsset.getModelPath();
String texName = boardAsset.getDiffPath();
Spatial model = app.getAssetManager().loadModel(modelName);
model.scale(boardAsset.getSize());
model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(90));
model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(texName));
model.setMaterial(mat);
app.getRootNode().attachChild(model);
model.setLocalTranslation(pos);
}
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Board;
class PieceControl {
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Board;
class PileControl {
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Dialog;
public class Dialog {
}

View File

@@ -1,26 +0,0 @@
package pp.mdga.client.Dialog;
import pp.dialog.DialogManager;
import pp.mdga.client.MdgaApp;
public class DialogView {
private MdgaApp app;
private DialogManager dialogManager = new DialogManager(app);
private StartDialog dialog;
public DialogView(MdgaApp app) {
this.app = app;
}
DialogManager getDialogManager() {
return dialogManager;
}
public void mainMenu() {
//dialogManager = new DialogManager(app);
//di
//MainMenuDialog mainMenuDialog = new MainMenuDialog(app);
}
}

View File

@@ -1,4 +0,0 @@
package pp.mdga.client.Dialog;
public class InterruptDialog {
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Dialog;
public class MenuDialog {
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Dialog;
public class NetworkDialog {
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Dialog;
public class SoundDialog {
}

View File

@@ -1,35 +0,0 @@
package pp.mdga.client.Dialog;
import com.simsilica.lemur.Checkbox;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.dialog.DialogBuilder;
import pp.dialog.SimpleDialog;
import pp.mdga.client.MdgaApp;
public class StartDialog extends SimpleDialog {
StartDialog(MdgaApp app) {
super(app.getDialogView().getDialogManager());
Checkbox serverHost = new Checkbox("sdgfsdg");
serverHost.setChecked(false);
//serverHost.addClickCommands(s -> toggleServerHost());
final Container input = new Container(new SpringGridLayout());
input.addChild(new Label("sdgsgsdg"));
//input.addChild(host, 1);
input.addChild(new Label("sdfdsgsdgsdg"));
//input.addChild(port, 1);
input.addChild(serverHost);
DialogBuilder.simple(app.getDialogView().getDialogManager())
.setTitle("server.dialog")
.setOkButton("button.connect")
.setNoButton("button.cancel")
.setOkClose(false)
.setNoClose(false)
.build(this);
}
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Dialog;
public class VideoDialog {
}

View File

@@ -1,5 +0,0 @@
package pp.mdga.client.Gui;
public class GuiView {
}

View File

@@ -0,0 +1,72 @@
package pp.mdga.client;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.*;
public class InputSyncronizer {
private MdgaApp app;
private InputManager inputManager;
protected boolean rightMousePressed = false;
private float rotationAngle = 180f;
private int scrollValue = 0;
InputSyncronizer(MdgaApp app) {
this.app = app;
this.inputManager = app.getInputManager();
setupInput();
}
private void setupInput() {
inputManager.addMapping("Settings", new KeyTrigger(KeyInput.KEY_ESCAPE));
inputManager.addMapping("RotateRightMouse", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addMapping("MouseLeft", new MouseAxisTrigger(MouseInput.AXIS_X, false)); // Left movement
inputManager.addMapping("MouseRight", new MouseAxisTrigger(MouseInput.AXIS_X, true)); // Right movement
inputManager.addMapping("MouseScrollUp", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)); // Scroll up
inputManager.addMapping("MouseScrollDown", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)); // Scroll down
inputManager.addListener(actionListener, "Settings", "RotateRightMouse");
inputManager.addListener(analogListener, "MouseLeft", "MouseRight", "MouseScrollUp", "MouseScrollDown");
}
private final ActionListener actionListener = new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals("Settings") && isPressed) {
app.getView().pressEscape();
}
if (name.equals("RotateRightMouse")) {
rightMousePressed = isPressed;
}
}
};
private final AnalogListener analogListener = new AnalogListener() {
@Override
public void onAnalog(String name, float value, float tpf) {
if (name.equals("MouseLeft") && rightMousePressed) {
rotationAngle -= value * 360f;
} else if (name.equals("MouseRight") && rightMousePressed) {
rotationAngle += value * 360f;
} else if (name.equals("MouseScrollUp")) {
scrollValue = Math.max(1, scrollValue - 5);
} else if (name.equals("MouseScrollDown")) {
scrollValue = Math.min(100, scrollValue + 5);
}
}
};
public float getRotation() {
return (rotationAngle / 2) % 360;
}
public int getScroll() {
return scrollValue;
}
}

View File

@@ -1,29 +1,36 @@
package pp.mdga.client; package pp.mdga.client;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.system.NanoTimer; import com.simsilica.lemur.GuiGlobals;
import pp.mdga.client.Acoustic.AcousticHandler; import pp.mdga.client.acoustic.AcousticHandler;
import pp.mdga.client.Acoustic.MdgaSound; import pp.mdga.client.animation.AnimationHandler;
import pp.mdga.client.Animation.AnimationHandler;
import com.jme3.system.AppSettings; import com.jme3.system.AppSettings;
import pp.mdga.client.Board.BoardView; import pp.mdga.client.view.*;
import pp.mdga.client.Dialog.DialogView;
public class MdgaApp extends SimpleApplication { public class MdgaApp extends SimpleApplication {
private AnimationHandler animationHandler; private AnimationHandler animationHandler;
private AcousticHandler acousticHandler; private AcousticHandler acousticHandler;
private BoardView boardView; private NotificationSynchronizer notificationSynchronizer;
private DialogView dialogView; private InputSyncronizer inputSyncronizer;
private ModelSyncronizer modelSyncronizer;
NanoTimer test = new NanoTimer(); MdgaView view = null;
private MdgaState testState = MdgaState.MAIN; private MdgaState state = MdgaState.MAIN;
private static float resolutionFactor = 1.8f;
public static void main(String[] args) { public static void main(String[] args) {
AppSettings settings = new AppSettings(true); AppSettings settings = new AppSettings(true);
settings.setSamples(128); settings.setSamples(128);
settings.setCenterWindow(true); settings.setCenterWindow(true);
settings.setWidth(1280);
settings.setHeight(720); int width = (int)(1280 * resolutionFactor);
int height = (int)(720 * resolutionFactor);
settings.setWidth(width);
settings.setHeight(height);
settings.setVSync(false);
MdgaApp app = new MdgaApp(); MdgaApp app = new MdgaApp();
app.setSettings(settings); app.setSettings(settings);
@@ -35,36 +42,58 @@ public static void main(String[] args) {
public void simpleInitApp() { public void simpleInitApp() {
animationHandler = new AnimationHandler(this); animationHandler = new AnimationHandler(this);
acousticHandler = new AcousticHandler(this); acousticHandler = new AcousticHandler(this);
boardView = new BoardView(this); notificationSynchronizer = new NotificationSynchronizer(this);
dialogView = new DialogView(this); inputSyncronizer = new InputSyncronizer(this);
modelSyncronizer = new ModelSyncronizer(this);
//dialogView.mainMenu(); inputManager.deleteMapping("SIMPLEAPP_Exit");
//acousticHandler.playState(MdgaState.GAME); inputManager.deleteMapping("FLYCAM_ZoomIn");
inputManager.deleteMapping("FLYCAM_ZoomOut");
inputManager.deleteMapping("FLYCAM_RotateDrag");
flyCam.setEnabled(false);
GuiGlobals.initialize(this);
acousticHandler.playSound(MdgaSound.LOST); enter(state);
acousticHandler.playSound(MdgaSound.VICTORY);
} }
@Override @Override
public void simpleUpdate(float tpf) { public void simpleUpdate(float tpf) {
view.update();
acousticHandler.update(); acousticHandler.update();
notificationSynchronizer.update();
//test.reset();
if(test.getTimeInSeconds() > 10){
if(testState == MdgaState.MAIN) {
testState = MdgaState.LOBBY;
acousticHandler.playState(MdgaState.MAIN);
} else if (testState == MdgaState.LOBBY) {
testState = MdgaState.CEREMONY;
acousticHandler.playState(MdgaState.LOBBY);
}
else {
testState = MdgaState.MAIN;
acousticHandler.playState(MdgaState.CEREMONY);
} }
test.reset(); public void enter(MdgaState state) {
if(null != view) {
view.leave();
} }
this.state = state;
switch (state) {
case MAIN:
view = new MainView(this);
break;
case LOBBY:
view = new LobbyView(this);
break;
case GAME:
view = new GameView(this);
break;
case CEREMONY:
view = new CeremonyView(this);
break;
case NONE:
throw new RuntimeException("cant enter state NONE");
}
acousticHandler.playState(state);
view.enter();
}
public void afteGameCleanup() {
//TODO
} }
public AnimationHandler getAnimationHandler() { public AnimationHandler getAnimationHandler() {
@@ -75,11 +104,19 @@ public AcousticHandler getAcousticHandler() {
return acousticHandler; return acousticHandler;
} }
public BoardView getBoardView() { public MdgaState getState() {return state; }
return boardView;
public float getResolutionFactor() {
return resolutionFactor;
} }
public DialogView getDialogView() { public MdgaView getView() {
return dialogView; return view;
} }
public ModelSyncronizer getModelSyncronizer() {
return modelSyncronizer;
}
public InputSyncronizer getInputSyncronizer() { return inputSyncronizer; }
} }

View File

@@ -1,48 +1,9 @@
package pp.mdga.client; package pp.mdga.client;
import pp.mdga.notification.Notification;
import pp.mdga.notification.PieceInGameNotification;
import pp.mdga.notification.PlayerInGameNotification;
public enum MdgaState { public enum MdgaState {
NONE { NONE,
@Override MAIN,
void handleNotification(MdgaApp app, Notification notification) { LOBBY,
throw new RuntimeException("unexpected notification"); GAME,
} CEREMONY;
},
MAIN {
@Override
void handleNotification(MdgaApp app, Notification notification) {
throw new RuntimeException("unexpected notification");
}
},
LOBBY {
@Override
void handleNotification(MdgaApp app, Notification notification) {
throw new RuntimeException("unexpected notification");
}
},
GAME {
@Override
void handleNotification(MdgaApp app, Notification notification) {
if(notification instanceof PlayerInGameNotification) {
//TODO
}
else if(notification instanceof PieceInGameNotification){
//TODO
}
else {
throw new RuntimeException("unexpected notification");
}
}
},
CEREMONY {
@Override
void handleNotification(MdgaApp app, Notification notification) {
throw new RuntimeException("unexpected notification");
}
};
abstract void handleNotification(MdgaApp app, Notification notification);
} }

View File

@@ -0,0 +1,72 @@
package pp.mdga.client;
import pp.mdga.client.view.LobbyView;
import pp.mdga.game.Color;
public class ModelSyncronizer {
private MdgaApp app;
ModelSyncronizer(MdgaApp app) {
this.app = app;
}
public void selectPiece() {
//TODO call from somewhere
System.out.println("selectPiece");
}
public void selectCard() {
//TODO call from somewhere
System.out.println("selectCard");
}
public void selectTsk(Color color) {
//TODO call from somewhere
System.out.println("selectTsk: " + color);
LobbyView view = (LobbyView) app.getView();
view.setTaken(color, true, true, "OwnPlayerName");
}
public void unselectTsk() {
//TODO call from somewhere
System.out.println("unselectTsk");
}
public void rolledDice() {
//TODO call from somewhere
System.out.println("rolledDice");
}
public void setName(String name) {
//TODO call from somewhere
System.out.println("setName:" + name);
}
public void setReady() {
//TODO call from somewhere
System.out.println("setReady");
app.enter(MdgaState.GAME);
}
public void setHost(int port) {
//TODO call from somewhere
System.out.println("setHost: " + port);
app.enter(MdgaState.LOBBY);
}
public void setJoin(String ip, int port) {
//TODO call from somewhere
System.out.println("setJoin");
app.enter(MdgaState.LOBBY);
}
public void leave() {
System.out.println("leave");
app.enter(MdgaState.MAIN);
}
public void enter(MdgaState state) {
System.out.println("enter:" + state);
app.enter(state);
}
}

View File

@@ -1,23 +1,136 @@
package pp.mdga.client; package pp.mdga.client;
import pp.mdga.notification.Notification; import pp.mdga.client.view.GameView;
import pp.mdga.client.view.LobbyView;
import pp.mdga.notification.*;
import java.util.ArrayList; import java.util.ArrayList;
public class NotificationSynchronizer { public class NotificationSynchronizer {
private final MdgaApp app; private final MdgaApp app;
private MdgaState state = MdgaState.MAIN;
private ArrayList<Notification> notifications = new ArrayList<>();
NotificationSynchronizer(MdgaApp app) { NotificationSynchronizer(MdgaApp app) {
this.app = app; this.app = app;
} }
void update() { public void addTestNotification(Notification n) {
ArrayList<Notification> notifications = new ArrayList<>(); notifications.add(n);
}
public void update() {
//TODO fetch model notifications //TODO fetch model notifications
for (Notification n : notifications) { for (Notification n : notifications) {
state.handleNotification(app, n); switch (app.getState()) {
case MAIN:
handleMain(n);
break;
case LOBBY:
handleLobby(n);
break;
case GAME:
handleGame(n);
break;
case CEREMONY:
handleCeremony(n);
break;
case NONE:
throw new RuntimeException("no notification expected: " + n.toString());
}
}
}
private void handleMain(Notification notification) {
if (notification instanceof LobbyDialogNotification) {
app.enter(MdgaState.LOBBY);
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
}
}
private void handleLobby(Notification notification) {
LobbyView lobbyView = (LobbyView) app.getView();
if (notification instanceof TskSelectNotification) {
TskSelectNotification n = (TskSelectNotification)notification;
lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName());
} else if (notification instanceof TskUnselectNotification) {
TskUnselectNotification n = (TskUnselectNotification)notification;
lobbyView.setTaken(n.getColor(), false, false, null);
} else if (notification instanceof GameNotification) {
app.enter(MdgaState.GAME);
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
}
}
private void handleGame(Notification notification) {
GameView gameView = (GameView) app.getView();
if (notification instanceof AcquireCardNotification) {
// Handle AcquireCardNotification
} else if (notification instanceof ActivePlayerNotification) {
// Handle ActivePlayerNotification
} else if (notification instanceof CeremonyNotification) {
app.enter(MdgaState.CEREMONY);
} else if (notification instanceof DiceNowNotification) {
// Handle DiceNowNotification
} else if (notification instanceof DicingNotification) {
// Handle DicingNotification
} else if (notification instanceof DrawCardNotification) {
// Handle DrawCardNotification
} else if (notification instanceof HomeMoveNotification) {
HomeMoveNotification n = (HomeMoveNotification)notification;
gameView.getBoardHandler().moveHomePiece(n.getPieceId(), n.getHomeIndex());
} else if (notification instanceof InterruptNotification) {
// Handle InterruptNotification
} else if (notification instanceof MovePieceNotification) {
MovePieceNotification n = (MovePieceNotification)notification;
//gameView.getBoardHandler().movePiece(n.get); //TODO
} else if (notification instanceof MoveThrowPieceNotification) {
MoveThrowPieceNotification n = (MoveThrowPieceNotification)notification;
//gameView.getBoardHandler().throwPiece(n.); //TODO
} else if (notification instanceof NoShieldNotification) {
NoShieldNotification n = (NoShieldNotification)notification;
gameView.getBoardHandler().unshieldPiece(n.getPieceId());
} else if (notification instanceof PieceInGameNotification) {
// Handle PieceInGameNotification
} else if (notification instanceof PlayCardNotification) {
// Handle PlayCardNotification
} else if (notification instanceof PlayerInGameNotification) {
// Handle PlayerInGameNotification
} else if (notification instanceof ResumeNotification) {
// Handle ResumeNotification
} else if (notification instanceof RollDiceNotification) {
// Handle RollDiceNotification
} else if (notification instanceof SelectableCardsNotification) {
// Handle SelectableCardsNotification
} else if (notification instanceof SelectablePiecesNotification) {
// Handle SelectablePiecesNotification
} else if (notification instanceof ShieldActiveNotification) {
ShieldActiveNotification n = (ShieldActiveNotification)notification;
gameView.getBoardHandler().shieldPiece(n.getPieceId());
} else if (notification instanceof ShieldSuppressedNotification) {
ShieldSuppressedNotification n = (ShieldSuppressedNotification)notification;
gameView.getBoardHandler().suppressShield(n.getPieceId());
} else if (notification instanceof StartDialogNotification) {
app.enter(MdgaState.MAIN);
} else if (notification instanceof SwapPieceNotification) {
// Handle SwapPieceNotification
} else if (notification instanceof WaitMoveNotification) {
// Handle WaitMoveNotification
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
}
}
private void handleCeremony(Notification notification) {
if (notification instanceof StartDialogNotification) {
app.enter(MdgaState.MAIN);
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
} }
} }
} }

View File

@@ -0,0 +1,403 @@
package pp.mdga.client.acoustic;
import com.jme3.system.NanoTimer;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.MdgaState;
import java.util.*;
public class AcousticHandler {
private MdgaApp app;
private MdgaState state = MdgaState.NONE;
private boolean playGame = false;
private ArrayList<MusicAsset> gameTracks = new ArrayList<>();
private NanoTimer trackTimer = new NanoTimer();
private boolean fading = false; // Indicates if a fade is in progress
private NanoTimer fadeTimer = new NanoTimer(); // Timer to track fade progress
private static final float FADE_DURATION = 3.0f; // Duration for outfade
private static final float CROSSFADE_DURATION = 1.5f; // Duration for infade
private GameMusic playing = null; // Currently playing track
private GameMusic scheduled = null; // Scheduled track to play next
private GameMusic old = null; // Old track being faded out
private float mainVolume = 1.0f;
private float musicVolume = 1.0f;
private float soundVolume = 1.0f;
private ArrayList<GameSound> sounds = new ArrayList<>();
public AcousticHandler(MdgaApp app) {
this.app = app;
}
/**
* This method updates the acousticHandler and should be called every frame
*/
public void update() {
updateVolumeAndTrack();
if (playGame) {
updateGameTracks();
}
Iterator<GameSound> iterator = sounds.iterator();
while (iterator.hasNext()) {
GameSound s = iterator.next();
s.update(getSoundVolumeTotal());
if (!s.isPlaying()) {
iterator.remove();
}
}
}
/**
* This method instantly plays a sound
*
* @param sound the sound to be played
*/
public void playSound(MdgaSound sound) {
ArrayList<SoundAssetDelayVolume> assets = new ArrayList<SoundAssetDelayVolume>();
switch (sound) {
case LOST:
assets.add(new SoundAssetDelayVolume(SoundAsset.LOST, 1.0f, 0.0f));
break;
case VICTORY:
assets.add(new SoundAssetDelayVolume(SoundAsset.VICTORY, 1.0f, 2.0f));
break;
default:
break;
}
for (SoundAssetDelayVolume sawd : assets) {
GameSound gameSound = new GameSound(app, sawd.asset(), getSoundVolumeTotal(), sawd.subVolume(), sawd.delay());
sounds.add(gameSound);
}
}
/**
* This method fades the played music to fit the state.
*
* @param state the state of which the corresponding music should be played to be played
*/
public void playState(MdgaState state) {
if (this.state == state) {
return;
}
MusicAsset asset = null;
switch (state) {
case MAIN:
playGame = false;
asset = MusicAsset.MAIN_MENU;
break;
case LOBBY:
playGame = false;
asset = MusicAsset.LOBBY;
break;
case GAME:
addGameTracks();
playGame = true;
assert (!gameTracks.isEmpty()) : "no more game music available";
asset = gameTracks.remove(0);
break;
case CEREMONY:
playGame = false;
asset = MusicAsset.CEREMONY;
break;
case NONE:
throw new RuntimeException("no music for state NONE");
}
assert (null != asset) : "music sceduling went wrong";
scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f);
}
/**
* Performs linear interpolation between two float values.
*
* @param start The starting value.
* @param end The ending value.
* @param t The interpolation factor, typically between 0 and 1.
* @return The interpolated value between start and end.
*/
private float lerp(float start, float end, float t) {
return start + t * (end - start);
}
/**
* Updates the state of audio playback, handling track transitions and volume adjustments.
*
* This method ensures smooth transitions between tracks using fade-in and fade-out effects.
* It also handles cases where no track is playing, starting a scheduled track immediately at full volume.
* The method prioritizes the latest scheduled track if multiple scheduling occurs quickly.
*
* Behavior:
* 1. If nothing is scheduled and no track is playing, it exits early.
* 2. If a scheduled track exists and no track is playing, the scheduled track starts immediately at full volume.
* 3. If a scheduled track exists while a track is playing, it initiates a fade-out for the currently playing track
* and prepares for the new track to fade in.
* 4. If a track transition is in progress (fading), it processes the fade-out and fade-in states.
* If a new track is scheduled during this process, it interrupts the current transition and prioritizes the new track.
* 5. If no fading is needed and a track is playing, it ensures the track's volume is updated.
*
* Special cases:
* - If no track is playing and a new track is scheduled, it starts the track immediately without fading.
* - If a new track is scheduled during fading, it resets the transition to prioritize the new track.
*/
private void updateVolumeAndTrack() {
if (scheduled == null && !fading && playing == null) {
// Nothing to do, early exit
return;
}
if (scheduled != null && playing == null && !fading) {
// No current track, start scheduled track immediately at full volume
playing = scheduled;
scheduled = null;
playing.play();
playing.update(getMusicVolumeTotal()); // Set volume to full
return;
}
if (scheduled != null && !fading) {
// Initiate a fade process if a new track is scheduled
fading = true;
fadeTimer.reset();
old = playing; // The currently playing track becomes the old track
playing = null; // Clear the playing track during the fade process
}
if (fading) {
handleFadeProcess();
// Handle any interruptions due to newly scheduled tracks
if (scheduled != null && playing != null && playing != scheduled) {
// Interrupt the current infade and switch to the new scheduled track
old = playing; // Treat the currently infading track as the old track
playing = null; // Reset playing to allow switching
fadeTimer.reset(); // Restart fade timer for the new track
}
} else if (playing != null) {
// Update volume for the currently playing track
playing.update(getMusicVolumeTotal());
} else if (scheduled != null) {
// If no track is playing and one is scheduled, start it immediately at full volume
playing = scheduled;
scheduled = null;
playing.play();
playing.update(getMusicVolumeTotal()); // Set volume to full
}
}
/**
* Manages the fading process during audio track transitions.
*
* This method handles the fade-out of the currently playing (old) track, manages any pause between the fade-out
* and fade-in, and initiates the fade-in for the new track if applicable. It ensures smooth transitions between
* tracks while maintaining the correct volume adjustments.
*
* Behavior:
* 1. **Outfade:** Gradually decreases the volume of the `old` track over the duration of `FADE_DURATION`.
* Once the outfade completes, the `old` track is paused and cleared.
* 2. **Pause Handling:** Waits for a defined pause (if applicable) before initiating the infade for the next track.
* 3. **Infade:** If a `scheduled` track exists and the outfade and pause are complete, it begins playing
* the new track (`playing`) and initiates the infade process.
*
* Key Details:
* - The outfade volume adjustment is interpolated linearly from full volume to zero using the `lerp` function.
* - The pause duration is retrieved from the scheduled track if it is specified.
* - If a new track is scheduled during the fade process, it is handled by external logic to prioritize transitions.
*
* Preconditions:
* - `fading` is expected to be `true` when this method is called.
* - The method is invoked as part of the `updateVolumeAndTrack` process.
*/
private void handleFadeProcess() {
float time = fadeTimer.getTimeInSeconds();
// Handle outfade for the old track
if (old != null && time <= FADE_DURATION) {
float t = Math.min(time / FADE_DURATION, 1.0f);
float oldVolume = lerp(1.0f, 0.0f, t);
old.update(getMusicVolumeTotal() * oldVolume);
}
if (old != null && time > FADE_DURATION) {
// Complete outfade
old.pause();
old = null;
}
// Handle pause duration before infade
float pause = (scheduled != null) ? scheduled.getPause() : 0.0f;
if (time > FADE_DURATION + pause) {
if (playing == null && scheduled != null) {
// Begin infade for the new track
playing = scheduled;
scheduled = null;
playing.play(); // Start playing the new track
}
handleInfade(time - FADE_DURATION - pause);
}
}
/**
* Manages the fade-in process for the currently playing track.
*
* This method gradually increases the volume of the `playing` track from zero to full volume
* over the duration of `CROSSFADE_DURATION`. It ensures a smooth transition into the new track.
*
* Behavior:
* 1. If no track is set as `playing`, the method exits early, as there is nothing to fade in.
* 2. Linearly interpolates the volume of the `playing` track from 0.0 to 1.0 based on the elapsed
* `infadeTime` and the specified `CROSSFADE_DURATION`.
* 3. Once the fade-in is complete (when `infadeTime` exceeds `CROSSFADE_DURATION`), the method:
* - Marks the fade process (`fading`) as complete.
* - Ensures the `playing` track is updated to its full volume.
*
* Key Details:
* - Uses the `lerp` function to calculate the volume level for the `playing` track during the fade-in.
* - Ensures the volume is always a value between 0.0 and 1.0.
* - The `infadeTime` parameter should be relative to the start of the fade-in process.
*
* Preconditions:
* - The `playing` track must be initialized and actively fading in for this method to have an effect.
* - The method is invoked as part of the `updateVolumeAndTrack` process.
*
* @param infadeTime The elapsed time (in seconds) since the fade-in process started.
*/
private void handleInfade(float infadeTime) {
if (playing == null) {
// Nothing to infade
return;
}
// Proceed with the infade for the current playing track
float t = Math.min(infadeTime / CROSSFADE_DURATION, 1.0f);
float newVolume = lerp(0.0f, 1.0f, t);
playing.update(getMusicVolumeTotal() * newVolume);
if (infadeTime > CROSSFADE_DURATION) {
// Infade is complete, finalize state
fading = false;
playing.update(getMusicVolumeTotal()); // Ensure full volume
}
}
/**
* Adds a list of game tracks to the gameTracks collection and shuffles them.
* This method adds predefined game tracks to the track list and shuffles the order.
*/
private void addGameTracks() {
Random random = new Random();
for (int i = 1; i <= 6; i++) {
gameTracks.add(MusicAsset.valueOf("GAME_" + i));
}
Collections.shuffle(gameTracks, random);
}
/**
* Updates the current game tracks. If the currently playing track is nearing its end,
* a new track will be scheduled to play. If the list of game tracks is empty, it will be refreshed.
*/
private void updateGameTracks() {
if(null == playing) {
return;
}
if (playing.nearEnd(10)) {
if (gameTracks.isEmpty()) {
addGameTracks();
}
}
if (playing != null && playing.nearEnd(3) && trackTimer.getTimeInSeconds() > 20) {
trackTimer.reset();
MusicAsset nextTrack = gameTracks.remove(0);
scheduled = new GameMusic(app, nextTrack, getMusicVolumeTotal(), nextTrack.getSubVolume(), nextTrack.getLoop(), 0.0f);
}
}
/**
* Retrieves the main volume level.
*
* @return The current main volume level.
*/
public float getMainVolume() {
return mainVolume;
}
/**
* Retrieves the music volume level.
*
* @return The current music volume level.
*/
public float getMusicVolume() {
return musicVolume;
}
/**
* Retrieves the sound volume level.
*
* @return The current sound volume level.
*/
public float getSoundVolume() {
return soundVolume;
}
/**
* Sets the main volume level.
*
* @param mainVolume The desired main volume level.
*/
public void setMainVolume(float mainVolume) {
this.mainVolume = mainVolume;
}
/**
* Sets the music volume level.
*
* @param musicVolume The desired music volume level.
*/
public void setMusicVolume(float musicVolume) {
this.musicVolume = musicVolume;
}
/**
* Sets the sound volume level.
*
* @param soundVolume The desired sound volume level.
*/
public void setSoundVolume(float soundVolume) {
this.soundVolume = soundVolume;
}
/**
* Calculates the total music volume by multiplying the music volume by the main volume.
*
* @return The total music volume.
*/
float getMusicVolumeTotal() {
return getMusicVolume() * getMainVolume();
}
/**
* Calculates the total sound volume by multiplying the sound volume by the main volume.
*
* @return The total sound volume.
*/
float getSoundVolumeTotal() {
return getSoundVolume() * getMainVolume();
}
}

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.Acoustic; package pp.mdga.client.acoustic;
import com.jme3.audio.AudioData; import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode; import com.jme3.audio.AudioNode;
@@ -14,6 +14,7 @@ class GameMusic {
private float volume; private float volume;
private final float subVolume; private final float subVolume;
private final AudioNode music; private final AudioNode music;
private float pause;
/** /**
* Constructs a new GameMusic object. * Constructs a new GameMusic object.
@@ -24,9 +25,10 @@ class GameMusic {
* @param subVolume A relative volume that modifies the base music volume, typically a percentage. * @param subVolume A relative volume that modifies the base music volume, typically a percentage.
* @param loop A flag indicating whether the music should loop once it finishes. * @param loop A flag indicating whether the music should loop once it finishes.
*/ */
GameMusic(MdgaApp app, MusicAsset asset, float volume, float subVolume, boolean loop) { GameMusic(MdgaApp app, MusicAsset asset, float volume, float subVolume, boolean loop, float pause) {
this.volume = volume; this.volume = volume;
this.subVolume = subVolume; this.subVolume = subVolume;
this.pause = pause;
music = new AudioNode(app.getAssetManager(), asset.getPath(), AudioData.DataType.Stream); music = new AudioNode(app.getAssetManager(), asset.getPath(), AudioData.DataType.Stream);
music.setPositional(false); music.setPositional(false);
@@ -42,7 +44,7 @@ class GameMusic {
* If the music is not available, no action is performed. * If the music is not available, no action is performed.
*/ */
void play() { void play() {
if(null == music) { if (null == music) {
return; return;
} }
@@ -54,7 +56,7 @@ void play() {
* If the music is not available or is not playing, no action is performed. * If the music is not available or is not playing, no action is performed.
*/ */
void pause() { void pause() {
if(null == music) { if (null == music) {
return; return;
} }
@@ -102,9 +104,13 @@ boolean nearEnd(float thresholdSeconds) {
* @param newVolume The new total volume for the music. * @param newVolume The new total volume for the music.
*/ */
void update(float newVolume) { void update(float newVolume) {
if(volume != newVolume) { if (volume != newVolume) {
volume = newVolume; volume = newVolume;
music.setVolume(volume * subVolume); music.setVolume(volume * subVolume);
} }
} }
float getPause() {
return pause;
}
} }

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.Acoustic; package pp.mdga.client.acoustic;
import com.jme3.audio.AudioData; import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode; import com.jme3.audio.AudioNode;
@@ -62,21 +62,21 @@ boolean isPlaying() {
* @param newVolume The new total volume for the sound. * @param newVolume The new total volume for the sound.
*/ */
void update(float newVolume) { void update(float newVolume) {
if(!playing && timer.getTimeInSeconds() > delay) { if (!playing && timer.getTimeInSeconds() > delay) {
sound.play(); sound.play();
playing = true; playing = true;
} }
if(!playing) { if (!playing) {
return; return;
} }
if(volume != newVolume) { if (volume != newVolume) {
volume = newVolume; volume = newVolume;
sound.setVolume(volume * subVolume); sound.setVolume(volume * subVolume);
} }
if(sound != null && sound.getStatus() == AudioSource.Status.Playing) { if (sound != null && sound.getStatus() == AudioSource.Status.Playing) {
finished = true; finished = true;
} }
} }

View File

@@ -1,10 +1,10 @@
package pp.mdga.client.Acoustic; package pp.mdga.client.acoustic;
/** /**
* Enum representing the various sound effects used in the game. * Enum representing the various sound effects used in the game.
* Each sound corresponds to an event or action in the game and may consist of one or more * Each sound corresponds to an event or action in the game and may consist of one or more
* audio files, potentially with time delays between them. * audio files, potentially with time delays between them.
* * <p>
* These sounds are used to play specific audio cues, such as when a dice is rolled, * These sounds are used to play specific audio cues, such as when a dice is rolled,
* a turn starts or ends, a piece is moved or lost, and various other events in the game. * a turn starts or ends, a piece is moved or lost, and various other events in the game.
*/ */
@@ -19,5 +19,5 @@ public enum MdgaSound {
DESELECT, DESELECT,
HURRY, HURRY,
VICTORY, VICTORY,
LOST; LOST
} }

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.Acoustic; package pp.mdga.client.acoustic;
/** /**
* Enum representing various music assets used in the game. * Enum representing various music assets used in the game.
@@ -7,15 +7,15 @@
* These music assets are used to control the music that plays in different parts of the game, such as menus and in-game music. * These music assets are used to control the music that plays in different parts of the game, such as menus and in-game music.
*/ */
enum MusicAsset { enum MusicAsset {
MAIN_MENU("Spaceship.wav", 1.0f), MAIN_MENU("Spaceship.wav", true, 1.0f),
LOBBY("DeadPlanet.wav", 1.0f), LOBBY("DeadPlanet.wav", true, 1.0f),
CEREMONY("80s,Disco,Life.wav", 1.0f), CEREMONY("80s,Disco,Life.wav", true, 1.0f),
GAME_1("NeonRoadTrip.wav", false, 1.0f), GAME_1("NeonRoadTrip.wav", 1.0f),
GAME_2("NoPressureTrance.wav", false, 1.0f), GAME_2("NoPressureTrance.wav", 1.0f),
GAME_3("TheSynthRave.wav", false, 1.0f), GAME_3("TheSynthRave.wav", 1.0f),
GAME_4("LaserParty.wav", false, 1.0f), GAME_4("LaserParty.wav", 1.0f),
GAME_5("RetroNoir.wav", false, 1.0f), GAME_5("RetroNoir.wav", 1.0f),
GAME_6("SpaceInvaders.wav", false, 1.0f); GAME_6("SpaceInvaders.wav", 1.0f);
private final String path; private final String path;
private final boolean loop; private final boolean loop;

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.Acoustic; package pp.mdga.client.acoustic;
/** /**
* Enum representing various sound assets used in the game. * Enum representing various sound assets used in the game.

View File

@@ -1,7 +1,7 @@
package pp.mdga.client.Acoustic; package pp.mdga.client.acoustic;
/** /**
* A record that encapsulates a sound asset along with its playback settings: * A record that encapsulates a sound asset along with its playback settings:
* the relative volume (subVolume) and a delay before it starts playing. * the relative volume (subVolume) and a delay before it starts playing.
*/ */
record SoundAssetDelayVolume(SoundAsset asset, float subVolume, float delay) { } record SoundAssetDelayVolume(SoundAsset asset, float subVolume, float delay) {}

View File

@@ -1,7 +1,10 @@
package pp.mdga.client.Animation; package pp.mdga.client.animation;
abstract class Animation { abstract class Animation {
abstract void play(); abstract void play();
abstract void stop(); abstract void stop();
abstract boolean isOver(); abstract boolean isOver();
} }

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.Animation; package pp.mdga.client.animation;
import pp.mdga.client.MdgaApp; import pp.mdga.client.MdgaApp;
@@ -16,11 +16,11 @@ public void playAnimation(MdgaAnimation type) {
} }
public void update() { public void update() {
if(null == animation) { if (null == animation) {
return; return;
} }
if(animation.isOver()) { if (animation.isOver()) {
animation = null; animation = null;
//trigger next state in model //trigger next state in model

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.Animation; package pp.mdga.client.animation;
class EmptyAnimation extends Animation { class EmptyAnimation extends Animation {
@Override @Override

View File

@@ -0,0 +1,4 @@
package pp.mdga.client.animation;
public enum MdgaAnimation {
}

View File

@@ -0,0 +1,5 @@
package pp.mdga.client.board;
import pp.mdga.client.Asset;
record AssetOnMap(Asset asset, int x, int y, float rot) {}

View File

@@ -0,0 +1,323 @@
package pp.mdga.client.board;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
import java.util.*;
public class BoardHandler {
private static final float GRID_SIZE = 1.72f;
private static final float GRID_ELEVATION = 0.0f;
private static final String MAP_NAME = "map.mdga";
private final MdgaApp app;
private PileControl drawPile = null;
private PileControl discardPile = null;
private ArrayList<NodeControl> infield;
private Map<UUID, PieceControl> pieces;
private Map<Color, List<AssetOnMap>> colorAssetsMap;
private Map<Color, List<NodeControl>> homeNodesMap;
private Map<Color, List<NodeControl>> waitingNodesMap;
private Map<Color, List<PieceControl>> waitingPiecesMap;
private Map<UUID, Color> pieceColor;
private Node node;
private FilterPostProcessor fpp;
private boolean init;
public BoardHandler(MdgaApp app, FilterPostProcessor fpp) {
if(app == null) throw new RuntimeException("app is null");
this.init = false;
this.app = app;
this.fpp = fpp;
}
private void addFigureToPlayerMap(Color col, AssetOnMap assetOnMap) {
List<AssetOnMap> inMap = addItemToMapList(colorAssetsMap, col, assetOnMap);
if (inMap.size() > 4) throw new RuntimeException("to many assets for " + col);
}
private void initMap() {
if (init) return;
this.init = true;
this.node = new Node("Asset Node");
app.getRootNode().attachChild(node);
this.pieces = new HashMap<>();
this.colorAssetsMap = new HashMap<>();
this.infield = new ArrayList<>(40);
this.homeNodesMap = new HashMap<>();
this.waitingNodesMap = new HashMap<>();
this.waitingPiecesMap = new HashMap<>();
this.pieceColor = new HashMap<>();
List<AssetOnMap> assetOnMaps = MapLoader.loadMap(MAP_NAME);
for (AssetOnMap assetOnMap : assetOnMaps) {
switch (assetOnMap.asset()) {
case lw -> addFigureToPlayerMap(assetToColor(Asset.lw), assetOnMap);
case heer -> addFigureToPlayerMap(assetToColor(Asset.heer), assetOnMap);
case cir -> addFigureToPlayerMap(assetToColor(Asset.cir), assetOnMap);
case marine -> addFigureToPlayerMap(assetToColor(Asset.marine), assetOnMap);
case node_normal, node_bonus, node_start ->
infield.add(displayAndControl(assetOnMap, new NodeControl()));
case node_home_black -> addHomeNode(homeNodesMap, Color.AIRFORCE, assetOnMap);
case node_home_blue -> addHomeNode(homeNodesMap, Color.NAVY, assetOnMap);
case node_home_green -> addHomeNode(homeNodesMap, Color.ARMY, assetOnMap);
case node_home_yellow -> addHomeNode(homeNodesMap, Color.CYBER, assetOnMap);
case node_wait_black -> addHomeNode(waitingNodesMap, Color.AIRFORCE, assetOnMap);
case node_wait_blue -> addHomeNode(waitingNodesMap, Color.NAVY, assetOnMap);
case node_wait_green -> addHomeNode(waitingNodesMap, Color.ARMY, assetOnMap);
case node_wait_yellow -> addHomeNode(waitingNodesMap, Color.CYBER, assetOnMap);
default -> displayAsset(assetOnMap);
}
}
}
private Color assetToColor(Asset asset) {
return switch (asset) {
case lw -> Color.AIRFORCE;
case heer -> Color.ARMY;
case marine -> Color.NAVY;
case cir -> Color.CYBER;
default -> throw new RuntimeException("invalid asset");
};
}
private Spatial createModel(Asset asset, Vector3f pos, float rot) {
String modelName = asset.getModelPath();
String texName = asset.getDiffPath();
Spatial model = app.getAssetManager().loadModel(modelName);
model.scale(asset.getSize());
model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(rot));
model.setLocalTranslation(pos);
model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(texName));
model.setMaterial(mat);
node.attachChild(model);
// app.getRootNode().attachChild(model);
return model;
}
private static Vector3f gridToWorld(int x, int y) {
return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
}
private Spatial displayAsset(AssetOnMap assetOnMap) {
int x = assetOnMap.x();
int y = assetOnMap.y();
return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
}
private <T extends AbstractControl> T displayAndControl(AssetOnMap assetOnMap, T control) {
Spatial spatial = displayAsset(assetOnMap);
spatial.addControl(control);
return control;
}
private void movePieceToNode(PieceControl pieceControl, NodeControl nodeControl){
pieceControl.setLocation(nodeControl.getLocation());
}
private void addHomeNode(Map<Color, List<NodeControl>> map, Color color, AssetOnMap assetOnMap){
List<NodeControl> homeNodes = addItemToMapList(map, color, displayAndControl(assetOnMap, new NodeControl()));
if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
}
private float getRotationMove(Vector3f prev, Vector3f next) {
Vector3f direction = next.subtract(prev).normalizeLocal();
//I had to reverse dir.y, because then it worked.
return (float) Math.toDegrees(Math.atan2(direction.x, -direction.y));
}
private void movePiece_rek(UUID uuid, int curIndex, int moveIndex){
if (curIndex == moveIndex) return;
curIndex = (curIndex + 1) % 40;
PieceControl pieceControl = pieces.get(uuid);
NodeControl nodeControl = infield.get(curIndex);
pieceControl.setRotation(getRotationMove(pieceControl.getLocation(),nodeControl.getLocation()));
movePieceToNode(pieceControl, nodeControl);
movePiece_rek(uuid, curIndex, moveIndex);
}
private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
List<T> list = map.getOrDefault(key, new ArrayList<>());
list.add(item);
map.put(key, list);
return list;
}
private <T, E> List<T> removeItemFromMapList(Map<E,List<T>> map, E key, T item){
List<T> list = map.getOrDefault(key, new ArrayList<>());
list.remove(item);
map.put(key, list);
return list;
}
//public methods****************************************************************************************************
public void addPlayer(Color color, List<UUID> uuid) {
if (!init) throw new RuntimeException("BoardHandler is not initialized");
List<AssetOnMap> playerAssets = colorAssetsMap.get(color);
if (playerAssets == null) throw new RuntimeException("Assets for Player color are not defined");
if (uuid.size() != playerAssets.size()) throw new RuntimeException("UUID array and playerAssets are not the same size");
List<NodeControl> waitNodes = waitingNodesMap.get(color);
if (waitNodes.size() != playerAssets.size()) throw new RuntimeException("waitNodes size does not match playerAssets size");
for (int i = 0; i < playerAssets.size(); i++){
AssetOnMap assetOnMap = playerAssets.get(i);
PieceControl pieceControl = displayAndControl(assetOnMap, new PieceControl(assetOnMap.rot(), app.getAssetManager(), app, fpp));
movePieceToNode(pieceControl, waitNodes.get(i));
pieces.put(uuid.get(i), pieceControl);
pieceColor.put(uuid.get(i), color);
addItemToMapList(waitingPiecesMap, color, pieceControl);
}
}
public void moveHomePiece(UUID uuid, int index){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
List<NodeControl> homeNodes = homeNodesMap.get(color);
if(homeNodesMap.size() != 4) throw new RuntimeException("HomeNodes for" + color + " are not properly defined");
PieceControl pieceControl = pieces.get(uuid);
NodeControl nodeControl = homeNodes.get(index);
movePieceToNode(pieceControl, nodeControl);
//rotate piece in direction of homeNodes
NodeControl firstHomeNode = homeNodes.get(0);
NodeControl lastHomeNode = homeNodes.get(homeNodes.size()-1);
pieceControl.setRotation(getRotationMove(firstHomeNode.getLocation(), lastHomeNode.getLocation()));
}
public void movePieceStart(UUID uuid, int nodeIndex){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
PieceControl pieceControl = pieces.get(uuid);
movePieceToNode(pieceControl, infield.get(nodeIndex));
removeItemFromMapList(waitingPiecesMap, color, pieceControl);
}
public void movePiece(UUID uuid, int curIndex, int moveIndex){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
movePiece_rek(uuid, curIndex, moveIndex);
}
public void throwPiece(UUID uuid){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
PieceControl pieceControl = pieces.get(uuid);
List<NodeControl> waitNodes = waitingNodesMap.get(color);
List<PieceControl> waitPieces = waitingPiecesMap.get(color);
movePieceToNode(pieceControl, waitNodes.get(waitPieces.size()));
pieceControl.rotateInit();
}
public void shieldPiece(UUID uuid){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).activateShield();
}
public void unshieldPiece(UUID uuid){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).deactivateShield();
}
public void suppressShield(UUID uuid){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).suppressShield();
}
public void swapPieces(UUID piece1, UUID piece2){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
PieceControl piece1_control = pieces.get(piece1);
PieceControl piece2_control = pieces.get(piece2);
if(piece1_control == null) throw new RuntimeException("piece1 UUID is not valid");
if(piece2_control == null) throw new RuntimeException("piece2 UUID is not valid");
float rot1 = piece1_control.getRotation();
float rot2 = piece2_control.getRotation();
piece1_control.setRotation(rot2);
piece2_control.setRotation(rot1);
Vector3f pos1 = piece1_control.getLocation().clone();
Vector3f pos2 = piece2_control.getLocation().clone();
piece1_control.setLocation(pos2);
piece2_control.setLocation(pos1);
}
public void init(){
initMap();
}
public void shutdown(){
if (!init) return;
init = false;
app.getRootNode().detachChild(node);
}
//List<Pieces>
//List<NodesIndexe>
public void highlight(UUID uuid, boolean bool){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).outline(bool);
}
public void unHighlight(UUID uuid){
if (!init) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).deOutline();
}
}

View File

@@ -0,0 +1,77 @@
package pp.mdga.client.board;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.shadow.DirectionalLightShadowFilter;
import pp.mdga.client.MdgaApp;
public class CameraHandler {
MdgaApp app;
private DirectionalLight sun;
private AmbientLight ambient;
private static final int SHADOWMAP_SIZE = 1024 * 8;
public CameraHandler(MdgaApp app, FilterPostProcessor fpp){
this.app = app;
sun = new DirectionalLight();
sun.setColor(ColorRGBA.White);
sun.setDirection(new Vector3f(0.3f, 0, -1));
ambient = new AmbientLight();
ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1));
DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 4);
dlsf.setLight(sun);
fpp.addFilter(dlsf);
}
public void init() {
app.getRootNode().addLight(sun);
app.getRootNode().addLight(ambient);
}
public void shutdown() {
app.getRootNode().removeLight(sun);
app.getRootNode().removeLight(ambient);
}
public void update(float scroll, float rotation) {
float scrollValue = Math.max(0, Math.min(scroll, 100));
float rotationValue = rotation % 360;
if (rotationValue < 0) {
rotationValue += 360;
}
float radius;
float verticalAngle;
if (scroll < 100f) {
verticalAngle = 20f + (scrollValue / 100f) * 45f;
radius = 30f;
} else {
verticalAngle = 90f;
rotationValue = 270f;
radius = 50f;
}
float verticalAngleRadians = FastMath.DEG_TO_RAD * verticalAngle;
float z = radius * FastMath.sin(verticalAngleRadians);
float x = radius * FastMath.cos(verticalAngleRadians) * FastMath.sin(FastMath.DEG_TO_RAD * rotationValue);
float y = radius * FastMath.cos(verticalAngleRadians) * FastMath.cos(FastMath.DEG_TO_RAD * rotationValue);
Vector3f cameraPosition = new Vector3f(x, y, z);
app.getCamera().setLocation(cameraPosition);
app.getCamera().lookAt(Vector3f.ZERO, Vector3f.UNIT_Z);
}
}

View File

@@ -0,0 +1,92 @@
package pp.mdga.client.board;
import pp.mdga.client.Asset;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
class MapLoader {
private MapLoader() {
}
public static List<AssetOnMap> loadMap(String mapName) {
List<AssetOnMap> assetsOnMap = new ArrayList<>();
try (
InputStream inputStream = MapLoader.class.getClassLoader().getResourceAsStream(mapName);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))
) {
while (true) {
String entry = reader.readLine();
if (entry == null) break;
entry = entry.trim();
if (entry.isEmpty()) continue;
if (entry.charAt(0) == '#') continue;
String[] parts = entry.trim().split(" ");
assert (parts.length == 3) : "MapLoader: line has not 3 parts";
String assetName = parts[0];
String[] coordinates = parts[1].split(",");
assert (coordinates.length == 2) : "MapLoade: coordinates are wrong";
int x = Integer.parseInt(coordinates[0]);
int y = Integer.parseInt(coordinates[1]);
float rot = Float.parseFloat(parts[2]);
Asset asset = getLoadedAsset(assetName);
assetsOnMap.add(new AssetOnMap(asset, x, y, rot));
}
}
catch (IOException e) {
e.printStackTrace();
}
catch (IllegalArgumentException e) {
e.printStackTrace();
}
return assetsOnMap;
}
private static Asset getLoadedAsset(String assetName) {
return switch (assetName) {
case "lw" -> Asset.lw;
case "cir" -> Asset.cir;
case "marine" -> Asset.marine;
case "heer" -> Asset.heer;
case "node" -> Asset.node_normal;
case "node_start" -> Asset.node_start;
case "node_bonus" -> Asset.node_bonus;
case "node_home_blue" -> Asset.node_home_blue;
case "node_home_yellow" -> Asset.node_home_yellow;
case "node_home_black" -> Asset.node_home_black;
case "node_home_green" -> Asset.node_home_green;
case "node_wait_blue" -> Asset.node_wait_blue;
case "node_wait_yellow" -> Asset.node_wait_yellow;
case "node_wait_black" -> Asset.node_wait_black;
case "node_wait_green" -> Asset.node_wait_green;
case "world" -> Asset.world;
case "jet" -> Asset.jet;
case "big_tent" -> Asset.bigTent;
case "small_tent" -> Asset.smallTent;
case "radar" -> Asset.radar;
case "ship" -> Asset.ship;
case "tank" -> Asset.tank;
case "tree_small" -> Asset.tree_small;
case "tree_big" -> Asset.tree_big;
default -> throw new IllegalStateException("Unexpected value: " + assetName);
};
}
}

View File

@@ -0,0 +1,22 @@
package pp.mdga.client.board;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
public class NodeControl extends AbstractControl {
@Override
protected void controlUpdate(float v) {
}
@Override
protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
}
public Vector3f getLocation(){
return this.getSpatial().getLocalTranslation();
}
}

View File

@@ -0,0 +1,79 @@
package pp.mdga.client.board.Outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.MaterialDef;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.post.Filter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.texture.FrameBuffer;
public class OutlineFilter extends Filter {
private OutlinePreFilter outlinePreFilter;
private ColorRGBA outlineColor = new ColorRGBA(0, 1, 0, 1);
private float outlineWidth = 1;
public OutlineFilter(OutlinePreFilter outlinePreFilter) {
super("OutlineFilter");
this.outlinePreFilter = outlinePreFilter;
}
@Override
protected void initFilter(AssetManager assetManager, RenderManager renderManager, ViewPort vp, int w, int h) {
MaterialDef matDef = (MaterialDef) assetManager.loadAsset("MatDefs/SelectObjectOutliner/Outline.j3md");
material = new Material(matDef);
material.setVector2("Resolution", new Vector2f(w, h));
material.setColor("OutlineColor", outlineColor);
material.setFloat("OutlineWidth", outlineWidth);
}
@Override
protected void preFrame(float tpf) {
super.preFrame(tpf);
material.setTexture("OutlineDepthTexture", outlinePreFilter.getOutlineTexture());
// System.out.println("OutlineFilter.preFrame()");
}
@Override
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
super.postFrame(renderManager, viewPort, prevFilterBuffer, sceneBuffer);
// material.setTexture("OutlineDepthTexture", outlinePreFilter.getDefaultPassDepthTexture());
// System.out.println("OutlineFilter.postFrame()");
}
@Override
protected Material getMaterial() {
return material;
}
public ColorRGBA getOutlineColor() {
return outlineColor;
}
public void setOutlineColor(ColorRGBA outlineColor) {
this.outlineColor = outlineColor;
if (material != null) {
material.setColor("OutlineColor", outlineColor);
}
}
public float getOutlineWidth() {
return outlineWidth;
}
public void setOutlineWidth(float outlineWidth) {
this.outlineWidth = outlineWidth;
if (material != null) {
material.setFloat("OutlineWidth", outlineWidth);
}
}
public OutlinePreFilter getOutlinePreFilter() {
return outlinePreFilter;
}
}

View File

@@ -0,0 +1,67 @@
package pp.mdga.client.board.Outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.post.Filter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
public class OutlinePreFilter extends Filter {
private Pass normalPass;
private RenderManager renderManager;
/**
* Creates a OutlinePreFilter
*/
public OutlinePreFilter() {
super("OutlinePreFilter");
}
@Override
protected boolean isRequiresDepthTexture() {
return true;
}
@Override
protected void postQueue(RenderQueue queue) {
Renderer r = renderManager.getRenderer();
r.setFrameBuffer(normalPass.getRenderFrameBuffer());
renderManager.getRenderer().clearBuffers(true, true, false);
}
@Override
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
super.postFrame(renderManager, viewPort, prevFilterBuffer, sceneBuffer);
}
@Override
protected Material getMaterial() {
return material;
}
public Texture getOutlineTexture() {
return normalPass.getRenderedTexture();
}
@Override
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
this.renderManager = renderManager;
normalPass = new Pass();
normalPass.init(renderManager.getRenderer(), w, h, Format.RGBA8, Format.Depth);
material = new Material(manager, "MatDefs/SelectObjectOutliner/OutlinePre.j3md");
}
@Override
protected void cleanUpFilter(Renderer r) {
normalPass.cleanup(r);
}
}

View File

@@ -0,0 +1,71 @@
package pp.mdga.client.board.Outline;
import com.jme3.asset.AssetManager;
import com.jme3.math.ColorRGBA;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
public class SelectObjectOutliner {
private final FilterPostProcessor fpp;
private final RenderManager renderManager;
private final AssetManager assetManager;
private final Camera cam;
private final int width;
private boolean selected;
private ViewPort outlineViewport = null;
private OutlineFilter outlineFilter = null;
public SelectObjectOutliner(int width, FilterPostProcessor fpp, RenderManager renderManager, AssetManager assetManager, Camera cam) {
this.selected = false;
this.fpp = fpp;
this.renderManager = renderManager;
this.assetManager = assetManager;
this.cam = cam;
this.width = width;
}
public void deselect(Spatial model) {
if(selected){
selected = false;
hideOutlineFilterEffect(model);
}
}
public void select(Spatial model, ColorRGBA color) {
if(!selected){
selected = true;
showOutlineFilterEffect(model, width, color);
}
}
private void hideOutlineFilterEffect(Spatial model) {
outlineFilter.setEnabled(false);
outlineFilter.getOutlinePreFilter().setEnabled(false);
fpp.removeFilter(outlineFilter);
outlineViewport.detachScene(model);
renderManager.removePreView(outlineViewport);
outlineViewport = null;
}
private void showOutlineFilterEffect(Spatial model, int width, ColorRGBA color) {
outlineViewport = renderManager.createPreView("outlineViewport", cam);
FilterPostProcessor outlineFpp = new FilterPostProcessor(assetManager);
OutlinePreFilter outlinePreFilter = new OutlinePreFilter();
outlineFpp.addFilter(outlinePreFilter);
outlineViewport.attachScene(model);
outlineViewport.addProcessor(outlineFpp);
outlineFilter = new OutlineFilter(outlinePreFilter);
outlineFilter.setOutlineColor(color);
outlineFilter.setOutlineWidth(width);
fpp.addFilter(outlineFilter);
}
}

View File

@@ -0,0 +1,150 @@
package pp.mdga.client.board;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.board.Outline.SelectObjectOutliner;
public class PieceControl extends AbstractControl {
private final float initRotation;
private final AssetManager assetManager;
private Spatial shieldRing;
private final Material shieldMat;
private static final float SHIELD_SPEED = 1f;
private static final float SHIELD_TRANSPARENCY = 0.6f;
private static final ColorRGBA SHIELD_COLOR = new ColorRGBA(0, 0.9f, 1, SHIELD_TRANSPARENCY);
private static final ColorRGBA SHIELD_SUPPRESSED_COLOR = new ColorRGBA(1f, 0.5f, 0, SHIELD_TRANSPARENCY);
private static final float SHIELD_Z = 0f;
SelectObjectOutliner outlineOwn;
private static final ColorRGBA OUTLINE_OWN_COLOR = ColorRGBA.White;
private static final ColorRGBA OUTLINE_ENEMY_COLOR = ColorRGBA.Red;
private static final int OUTLINE_THICKNESS = 4;
private final Node parentNode;
public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, FilterPostProcessor fpp){
super();
this.parentNode = new Node();
this.initRotation = initRotation;
this.assetManager = assetManager;
this.shieldRing = null;
this.shieldMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
this.shieldMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
outlineOwn = new SelectObjectOutliner(OUTLINE_THICKNESS, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera());
}
public float getRotation() {
return (float) Math.toDegrees(this.spatial.getLocalRotation().toAngleAxis(new Vector3f(0,0,1)));
}
public void setRotation(float rot){
Quaternion quaternion = new Quaternion();
quaternion.fromAngleAxis((float) Math.toRadians(rot), new Vector3f(0,0,1));
this.spatial.setLocalRotation(quaternion);
}
public Vector3f getLocation(){
return this.getSpatial().getLocalTranslation();
}
@Override
protected void controlUpdate(float delta) {
if(shieldRing != null){
shieldRing.rotate(0, 0, delta * SHIELD_SPEED);
}
}
@Override
protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
}
public void setLocation(Vector3f loc){
this.getSpatial().setLocalTranslation(loc);
}
@Override
public void setSpatial(Spatial spatial){
if(this.getSpatial() == null && spatial != null){
super.setSpatial(spatial);
initSpatial();
}
else{
super.setSpatial(spatial);
}
}
public void initSpatial(){
setRotation(this.initRotation);
Node oldParent = this.spatial.getParent();
this.parentNode.setName(this.spatial.getName() + " Parent");
oldParent.detachChild(this.getSpatial());
this.parentNode.attachChild(this.getSpatial());
oldParent.attachChild(this.parentNode);
}
public void rotateInit() {
// rotate(rotation - initRotation);
}
public void activateShield(){
shieldRing = assetManager.loadModel(Asset.shield_ring.getModelPath());
shieldRing.scale(1f);
shieldRing.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(0));
shieldRing.setLocalTranslation(spatial.getLocalTranslation().add(new Vector3f(0,0,SHIELD_Z)));
shieldRing.setQueueBucket(RenderQueue.Bucket.Transparent); // Render in the transparent bucket
shieldMat.setColor("Color", SHIELD_COLOR);
shieldRing.setMaterial(shieldMat);
parentNode.attachChild(shieldRing);
}
public void deactivateShield(){
parentNode.detachChild(shieldRing);
shieldRing = null;
}
public void suppressShield(){
assert(shieldRing != null) : "PieceControl: shieldRing is not set";
shieldMat.setColor("Color", SHIELD_SUPPRESSED_COLOR);
}
public void setMaterial(Material mat){
this.spatial.setMaterial(mat);
}
public Material getMaterial(){
return ((Geometry) this.spatial).getMaterial();
}
public void outline(boolean enemy) {
ColorRGBA color = enemy ? OUTLINE_ENEMY_COLOR : OUTLINE_OWN_COLOR;
outlineOwn.select(this.getSpatial(), color);
}
public void deOutline() {
outlineOwn.deselect(this.getSpatial());
}
}

View File

@@ -0,0 +1,5 @@
package pp.mdga.client.board;
class PileControl {
}

View File

@@ -0,0 +1,12 @@
package pp.mdga.client.board;
public enum Rotation {
UP,
RIGHT,
DOWN,
LEFT,
UP_LEFT,
UP_RIGHT,
DOWN_RIGHT,
DOWN_LEFT
}

View File

@@ -0,0 +1,82 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.VAlignment;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import pp.mdga.client.MdgaApp;
import static com.jme3.math.FastMath.floor;
public abstract class Dialog {
protected final ColorRGBA COLOR_DEFAULT = ColorRGBA.Gray;
protected final ColorRGBA COLOR_HOVER = ColorRGBA.DarkGray;
protected Container container;
protected final MdgaApp app;
private final Node node;
protected final float vertical_step;
protected final float horitontal_step;
protected float fontSize = 35;
public Dialog(MdgaApp app, Node node) {
this.app = app;
this.node = node;
this.container = new Container();
this.horitontal_step = app.getCamera().getWidth() / 16;
this.vertical_step = app.getCamera().getHeight() / 9;
int val = (int) (32 * Math.min(app.getResolutionFactor() * 0.9f, 1));
fontSize = val;
}
public void show() {
node.attachChild(container);
}
public void hide () {
node.detachChild(container);
}
protected void createButton(String label, Runnable action, Vector3f size) {
Button button = new Button(label);
button.addClickCommands(source -> action.run());
button.setFontSize(fontSize);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setPreferredSize(size);
button.setTextHAlignment(HAlignment.Center);
button.setTextVAlignment(VAlignment.Center);
QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
button.setBackground(background);
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(hoverBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(normalBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
container.addChild(button);
}
}

View File

@@ -0,0 +1,15 @@
package pp.mdga.client.dialog;
import java.util.function.Supplier;
public class GetPercentRunnable {
private final Supplier<Float> action;
public GetPercentRunnable(Supplier<Float> action) {
this.action = action;
}
public float get() {
return action.get();
}
}

View File

@@ -0,0 +1,143 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.mdga.client.MdgaApp;
public class HostDialog extends Dialog {
private TextField portInput;
public HostDialog(MdgaApp app, Node node, Runnable backAction) {
super(app, node);
QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
container.setBackground(quad1);
Texture texture = app.getAssetManager().loadTexture("mdga_logo.png");
QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
Panel imagePanel = new Panel();
imagePanel.setBackground(b);
container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4 * app.getResolutionFactor(), texture.getImage().getHeight() / 4 * app.getResolutionFactor(), 0));
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
createTextField();
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
Container sub = new Container(new SpringGridLayout(Axis.X, Axis.Y));
createButton(sub, "Abbrechen", backAction, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
sub.addChild(new Panel(40 * app.getResolutionFactor(), 0 * app.getResolutionFactor(), ColorRGBA.Gray));
createButton(sub, "Starten", () -> tryStart(), new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
container.addChild(sub);
}
void tryStart() {
if (null == portInput.getText()) {
portInput.setText("");
return;
}
int port = 0;
try {
port = Integer.parseInt(portInput.getText());
} catch (NumberFormatException e) {
portInput.setText("");
return;
}
if(port >= 1 && port <= 65535) {
app.getModelSyncronizer().setHost(port);
}
portInput.setText("");
}
@Override
public void show() {
super.show();
container.setLocalTranslation(
app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
0
);
}
@Override
public void hide() {
super.hide();
}
private void createTextField() {
Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
Label nameLabel = new Label("Port:\t");
nameLabel.setFontSize(fontSize);
nameLabel.setColor(ColorRGBA.Black);
portInput = new TextField("");
portInput.setColor(ColorRGBA.Black);
portInput.setTextHAlignment(HAlignment.Left);
portInput.setFontSize(fontSize);
portInput.setSingleLine(true);
QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
portInput.setBackground(grayBackground);
subContainer.addChild(nameLabel);
subContainer.addChild(portInput);
container.addChild(subContainer);
}
protected void createButton(Container c, String label, Runnable action, Vector3f size) {
Button button = new Button(label);
button.addClickCommands(source -> action.run());
button.setFontSize(fontSize);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setPreferredSize(size);
button.setTextHAlignment(HAlignment.Center);
button.setTextVAlignment(VAlignment.Center);
QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
button.setBackground(background);
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(hoverBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(normalBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
c.addChild(button);
}
}

View File

@@ -0,0 +1,192 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.mdga.client.MdgaApp;
import java.util.regex.Pattern;
public class JoinDialog extends Dialog {
private TextField portInput;
private TextField ipInput;
public JoinDialog(MdgaApp app, Node node, Runnable backAction) {
super(app, node);
QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
container.setBackground(quad1);
Texture texture = app.getAssetManager().loadTexture("mdga_logo.png");
QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
Panel imagePanel = new Panel();
imagePanel.setBackground(b);
container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4 * app.getResolutionFactor(), texture.getImage().getHeight() / 4 * app.getResolutionFactor(), 0));
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
createIpField();
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
createPortField();
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
Container sub = new Container(new SpringGridLayout(Axis.X, Axis.Y));
createButton(sub, "Abbrechen", backAction, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
sub.addChild(new Panel(40 * app.getResolutionFactor(), 0 * app.getResolutionFactor(), ColorRGBA.Gray));
createButton(sub, "Beitreten", () -> tryJoin(), new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
container.addChild(sub);
}
void tryJoin() {
if (null == portInput.getText()) {
portInput.setText("");
ipInput.setText("");
return;
}
int port = 0;
try {
port = Integer.parseInt(portInput.getText());
} catch (NumberFormatException e) {
portInput.setText("");
ipInput.setText("");
return;
}
if(!(port >= 1 && port <= 65535)) {
portInput.setText("");
ipInput.setText("");
return;
}
if (null == ipInput.getText()) {
portInput.setText("");
ipInput.setText("");
return;
}
String ipv4Pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
if(!Pattern.matches(ipv4Pattern, ipInput.getText())) {
portInput.setText("");
ipInput.setText("");
return;
}
app.getModelSyncronizer().setJoin(ipInput.getText(), port);
}
@Override
public void show() {
super.show();
container.setLocalTranslation(
app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
0
);
}
@Override
public void hide() {
super.hide();
}
private void createPortField() {
Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
Label nameLabel = new Label("Port:\t");
nameLabel.setFontSize(fontSize);
nameLabel.setColor(ColorRGBA.Black);
portInput = new TextField("");
portInput.setColor(ColorRGBA.Black);
portInput.setTextHAlignment(HAlignment.Left);
portInput.setFontSize(fontSize);
portInput.setSingleLine(true);
QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
portInput.setBackground(grayBackground);
subContainer.addChild(nameLabel);
subContainer.addChild(portInput);
container.addChild(subContainer);
}
private void createIpField() {
Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
Label nameLabel = new Label("Ip:\t");
nameLabel.setFontSize(fontSize);
nameLabel.setColor(ColorRGBA.Black);
ipInput = new TextField("");
ipInput.setColor(ColorRGBA.Black);
ipInput.setTextHAlignment(HAlignment.Left);
ipInput.setFontSize(fontSize);
ipInput.setSingleLine(true);
QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
ipInput.setBackground(grayBackground);
subContainer.addChild(nameLabel);
subContainer.addChild(ipInput);
container.addChild(subContainer);
}
protected void createButton(Container c, String label, Runnable action, Vector3f size) {
Button button = new Button(label);
button.addClickCommands(source -> action.run());
button.setFontSize(fontSize);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setPreferredSize(size);
button.setTextHAlignment(HAlignment.Center);
button.setTextVAlignment(VAlignment.Center);
QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
button.setBackground(background);
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(hoverBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(normalBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
c.addChild(button);
}
}

View File

@@ -0,0 +1,165 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.VAlignment;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
public class LobbyButtonDialog extends Dialog {
private final int pos;
private Button button;
private Command<Button> clickCommand;
private boolean taken = false;
private final String label;
private int val;
public LobbyButtonDialog(MdgaApp app, Node node, String label, int pos) {
super(app, node);
clickCommand = (Button source) -> {
toggleButton();
};
this.pos = pos;
this.label = label;
this.button = new Button(label);
this.button.setFontSize(fontSize);
val = pos;
createNotTakenButton(new Vector3f(170 * app.getResolutionFactor(), 250 * app.getResolutionFactor(), 0));
}
@Override
public void show() {
super.show();
float x = ((2 + (4 * pos)) * horitontal_step) - ((0.33333333f * pos) * container.getPreferredSize().x);
float y = 6 * vertical_step;
container.setLocalTranslation(x, y, 0);
}
@Override
public void hide() {
super.hide();
}
public void setTaken(boolean isTaken, boolean self, String name) {
taken = isTaken;
if(isTaken) {
createTakenButton(new Vector3f(170 * app.getResolutionFactor(), 250 * app.getResolutionFactor(), 0), self);
button.setText(label + "\n\n" + name);
} else {
createNotTakenButton(new Vector3f(170 * app.getResolutionFactor(), 250 * app.getResolutionFactor(), 0));
button.setText(label);
}
}
protected void createNotTakenButton(Vector3f size) {
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setPreferredSize(size);
button.setTextHAlignment(HAlignment.Center);
button.setTextVAlignment(VAlignment.Center);
if(button.getClickCommands() != null) {
button.removeClickCommands(clickCommand);
}
button.addClickCommands(clickCommand);
QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
button.setBackground(background);
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(hoverBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(normalBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
container.addChild(button);
}
private void toggleButton() {
if(taken) {
app.getModelSyncronizer().unselectTsk();
}
else {
app.getModelSyncronizer().selectTsk(Color.values()[val]);
}
}
protected void createTakenButton(Vector3f size, boolean self) {
ColorRGBA color_a;
ColorRGBA color_b;
if(button.getClickCommands() != null) {
button.removeClickCommands(clickCommand);
}
if(!self) {
color_a = ColorRGBA.Red;
color_b = ColorRGBA.Red;
} else {
color_a = ColorRGBA.Green;
color_b = ColorRGBA.Yellow;
button.addClickCommands(clickCommand);
}
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setPreferredSize(size);
button.setTextHAlignment(HAlignment.Center);
button.setTextVAlignment(VAlignment.Center);
QuadBackgroundComponent background = new QuadBackgroundComponent(color_a);
background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
button.setBackground(background);
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(color_b);
hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(hoverBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(color_a);
normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(normalBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
});
container.addChild(button);
}
}

View File

@@ -0,0 +1,15 @@
package pp.mdga.client.dialog;
import java.util.function.Consumer;
public class PercentRunnable {
private final Consumer<Float> action;
public PercentRunnable(Consumer<Float> action) {
this.action = action;
}
public void run(float percent) {
action.accept(percent);
}
}

View File

@@ -0,0 +1,78 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.VAlignment;
import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import pp.mdga.client.MdgaApp;
public class SettingsButtonDialog extends Dialog {
private IconComponent icon;
public SettingsButtonDialog(MdgaApp app, Node node, String label, Runnable action) {
super(app, node);
icon = new IconComponent("zahnrad.png");
icon.setIconScale(0.1f * app.getResolutionFactor());
createButton(label, action, new Vector3f(60 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
}
@Override
public void show() {
super.show();
float x = (15.5f * horitontal_step) - container.getPreferredSize().x;
float y = 8.5f * vertical_step;
container.setLocalTranslation(x, y, 0);
}
@Override
public void hide() {
super.hide();
}
@Override
protected void createButton(String label, Runnable action, Vector3f size) {
Button button = new Button(label);
button.addClickCommands(source -> action.run());
button.setFontSize(fontSize);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setPreferredSize(size);
button.setTextHAlignment(HAlignment.Center);
button.setTextVAlignment(VAlignment.Center);
QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT);
background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
button.setBackground(background);
button.setIcon(icon);
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> {
QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER);
hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(hoverBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setIcon(icon);
});
button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> {
QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT);
normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor());
source.setBackground(normalBackground);
button.setHighlightColor(ColorRGBA.White);
button.setColor(ColorRGBA.Black);
button.setIcon(icon);
});
container.addChild(button);
}
}

View File

@@ -0,0 +1,112 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.event.CursorListener;
import com.simsilica.lemur.event.CursorMotionEvent;
import com.simsilica.lemur.event.MouseEventControl;
import pp.mdga.client.MdgaApp;
import com.simsilica.lemur.*;
import com.simsilica.lemur.style.*;
import java.util.HashMap;
public class SettingsDialog extends Dialog {
private TextField nameInput;
private HashMap<Slider, PercentRunnable> map = new HashMap<Slider, PercentRunnable>();
private HashMap<Slider, GetPercentRunnable> map2 = new HashMap<Slider, GetPercentRunnable>();
public SettingsDialog(MdgaApp app, Node node, String path) {
super(app, node);
QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
container.setBackground(quad1);
Texture texture = app.getAssetManager().loadTexture(path);
QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
Panel imagePanel = new Panel();
imagePanel.setBackground(b);
container.addChild(imagePanel).setPreferredSize(new Vector3f((texture.getImage().getWidth() / 2) * app.getResolutionFactor(), (texture.getImage().getHeight() / 2) * app.getResolutionFactor(), 0));
//abstandshalter
container.addChild(new Panel(80 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
}
@Override
public void show() {
super.show();
container.setLocalTranslation(
app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
0
);
}
@Override
public void hide() {
super.hide();
}
public void addButton(String label, Runnable action, Vector3f size) {
createButton(label, action, size);
}
public void addSlider(String label, PercentRunnable action, GetPercentRunnable action2, Vector3f size, int start) {
Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
Slider slider = new Slider("slider");
QuadBackgroundComponent background = new QuadBackgroundComponent(ColorRGBA.DarkGray);
slider.setBackground(background);
slider.setPreferredSize(size);
slider.setModel(new DefaultRangedValueModel(0, 100, start));
slider.setPreferredSize(new Vector3f(150 * app.getResolutionFactor(), 30 * app.getResolutionFactor(), 0));
slider.getDecrementButton().setText(" - ");
slider.getIncrementButton().setText(" + ");
slider.getDecrementButton().setFontSize(25 * app.getResolutionFactor());
slider.getIncrementButton().setFontSize(25 * app.getResolutionFactor());
Label nameLabel = new Label(label);
nameLabel.setFontSize(fontSize);
nameLabel.setColor(ColorRGBA.Black);
nameLabel.setPreferredSize(new Vector3f(150 * app.getResolutionFactor(), 10 * app.getResolutionFactor(), 0));
subContainer.addChild(nameLabel);
subContainer.addChild(slider);
container.addChild(subContainer);
map.put(slider, action);
map2.put(slider, action2);
//abstandshalter
container.addChild(new Panel(20 * app.getResolutionFactor(), 10 * app.getResolutionFactor(), ColorRGBA.Gray));
}
public void initVolume() {
map2.forEach((slider, runnable) -> {
double val = (double) runnable.get();
slider.getModel().setPercent(val);
});
}
public void update() {
map.forEach((slider, runnable) -> {
float val = (float) slider.getModel().getPercent();
runnable.run(val);
});
}
}

View File

@@ -0,0 +1,28 @@
package pp.mdga.client.dialog;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
public class SingleButtonLeftDialog extends Dialog {
public SingleButtonLeftDialog(MdgaApp app, Node node, String label, Runnable action) {
super(app, node);
createButton(label, action, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
}
@Override
public void show() {
super.show();
float x = 2 * horitontal_step;
float y = 1.8f * vertical_step;
container.setLocalTranslation(x, y, 0);
}
@Override
public void hide() {
super.hide();
}
}

View File

@@ -0,0 +1,28 @@
package pp.mdga.client.dialog;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
public class SingleButtonRightDialog extends Dialog {
public SingleButtonRightDialog(MdgaApp app, Node node, String label, Runnable action) {
super(app, node);
createButton(label, action, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0));
}
@Override
public void show() {
super.show();
float x = (14 * horitontal_step) - container.getPreferredSize().x;
float y = 1.8f * vertical_step;
container.setLocalTranslation(x, y, 0);
}
@Override
public void hide() {
super.hide();
}
}

View File

@@ -0,0 +1,86 @@
package pp.mdga.client.dialog;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.mdga.client.MdgaApp;
public class StartDialog extends Dialog {
private TextField nameInput;
public StartDialog(MdgaApp app, Node node) {
super(app, node);
QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray);
quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor());
container.setBackground(quad1);
Texture texture = app.getAssetManager().loadTexture("mdga_logo.png");
QuadBackgroundComponent b = new QuadBackgroundComponent(texture);
Panel imagePanel = new Panel();
imagePanel.setBackground(b);
container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4, texture.getImage().getHeight() / 4, 0));
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
createTextField();
//abstandshalter
container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray));
}
@Override
public void show() {
super.show();
container.setLocalTranslation(
app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2,
app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2,
0
);
}
@Override
public void hide() {
super.hide();
}
public void addButton(String label, Runnable action, Vector3f size) {
createButton(label, action, size);
}
private void createTextField() {
Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
Label nameLabel = new Label("Name:\t");
nameLabel.setFontSize(fontSize);
nameLabel.setColor(ColorRGBA.Black);
nameInput = new TextField("");
nameInput.setColor(ColorRGBA.Black);
nameInput.setTextHAlignment(HAlignment.Left);
nameInput.setFontSize(fontSize);
nameInput.setSingleLine(true);
QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray);
nameInput.setBackground(grayBackground);
subContainer.addChild(nameLabel);
subContainer.addChild(nameInput);
container.addChild(subContainer);
}
public String getName() {
return nameInput.getText();
}
}

View File

@@ -0,0 +1,23 @@
package pp.mdga.client.gui;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
public class CardControl extends AbstractControl {
public CardControl(){
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
}

View File

@@ -0,0 +1,77 @@
package pp.mdga.client.gui;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import pp.mdga.client.Asset;
import java.util.*;
public class CardLayer extends AbstractAppState {
private Node root;
private Application app;
private boolean init;
private List<Spatial> cardBuffer;
public CardLayer() {
this.cardBuffer = new ArrayList<>();
init = false;
}
@Override
public void initialize(AppStateManager stateManager, Application app ) {
this.app = app;
root = new Node("Under gui viewport Root");
Camera originalCam = app.getCamera();
Camera cam = new Camera(originalCam.getWidth(), originalCam.getHeight());
cam.setParallelProjection(false);
cam.setFrustum(originalCam.getFrustumNear(), originalCam.getFrustumFar(), originalCam.getFrustumLeft(), originalCam.getFrustumRight(),originalCam.getFrustumTop(), originalCam.getFrustumBottom());
cam.setFov(originalCam.getFov());
cam.setLocation(new Vector3f(0, 0, 10));
cam.lookAt(new Vector3f(0,0,0), Vector3f.UNIT_Y);
ViewPort view = app.getRenderManager().createMainView("Under gui ViewPort", cam);
view.setEnabled(true);
view.setClearFlags(false, true, false);
view.attachScene(root);
if(!init) init = true;
}
@Override
public void render(RenderManager rm) {
root.updateGeometricState();
}
@Override
public void update( float tpf ) {
root.updateLogicalState(tpf);
if (init && !cardBuffer.isEmpty()) {
for(Spatial spatial : cardBuffer){
root.attachChild(spatial);
}
cardBuffer.clear();
}
}
public void addCard(Spatial card){
cardBuffer.add(card);
}
}

View File

@@ -0,0 +1,70 @@
package pp.mdga.client.gui;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.BonusCard;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class GuiHandler {
private MdgaApp app;
private CardLayer cardLayer;
private Map<UUID, CardControl> ownCardsMap;
private static final Vector3f START = new Vector3f(-3,-3,0);
private static final Vector3f MARGIN = new Vector3f(2.5f,0,0);
public GuiHandler(MdgaApp app) {
this.app = app;
}
public void init(){
cardLayer = new CardLayer();
app.getStateManager().attach(cardLayer);
ownCardsMap = new HashMap<>();
addCard(BonusCard.SHIELD, UUID.randomUUID());
addCard(BonusCard.TURBO, UUID.randomUUID());
addCard(BonusCard.SWAP, UUID.randomUUID());
}
private Asset bonusToAsset(BonusCard card){
return switch (card){
case TURBO -> Asset.turboCard;
case SHIELD -> Asset.shieldCard;
case SWAP -> Asset.swapCard;
case HIDDEN -> throw new RuntimeException("HIDDEN is not allowed in GUI");
};
}
public void addCard(BonusCard card, UUID uuid) {
CardControl control = createCard(bonusToAsset(card), nextPos());
ownCardsMap.put(uuid, control);
cardLayer.addCard(control.getSpatial());
}
private Vector3f nextPos() {
return START.add(MARGIN.mult(ownCardsMap.size()));
}
private CardControl createCard(Asset card, Vector3f pos){
Spatial spatial = app.getAssetManager().loadModel(card.getModelPath());
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", app.getAssetManager().loadTexture(card.getDiffPath()));
spatial.setMaterial(mat);
spatial.setLocalScale(1f);
spatial.setLocalTranslation(pos);
spatial.rotate((float)Math.toRadians(90), (float)Math.toRadians(180), (float)Math.toRadians(180));
CardControl control = new CardControl();
spatial.addControl(control);
return control;
}
}

View File

@@ -0,0 +1,235 @@
package pp.mdga.client.server;
import com.jme3.network.*;
import com.jme3.network.serializing.Serializer;
import pp.mdga.game.Game;
import pp.mdga.game.Player;
import pp.mdga.message.client.*;
import pp.mdga.message.server.*;
import pp.mdga.server.ServerGameLogic;
import pp.mdga.server.ServerSender;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.LogManager;
/**
* Server implementing the visitor pattern as MessageReceiver for ClientMessages
*/
public class MdgaServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
private static final Logger LOGGER = System.getLogger(MdgaServer.class.getName());
private Server myServer;
private final ServerGameLogic logic;
private final BlockingQueue<ReceivedMessage> pendingMessages = new LinkedBlockingQueue<>();
static {
// Configure logging
LogManager manager = LogManager.getLogManager();
try {
manager.readConfiguration(new FileInputStream("logging.properties"));
LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS
} catch (IOException e) {
LOGGER.log(Level.INFO, e.getMessage());
}
}
/**
* Starts the Battleships server.
*/
public static void main(String[] args) {
new MdgaServer().run();
}
/**
* Creates a new MdgaServer.
*/
public MdgaServer() {
LOGGER.log(Level.INFO, "Creating MdgaServer"); //NON-NLS
logic = new ServerGameLogic(this, new Game());
}
public void run() {
startServer();
this.connectionAdded(myServer, myServer.getConnection(0));
while (true)
processNextMessage();
}
private void startServer() {
try {
LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS
myServer = Network.createServer(1234);
initializeSerializables();
myServer.start();
registerListeners();
LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS
} catch (IOException e) {
LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS
exit(1);
}
}
private void processNextMessage() {
try {
pendingMessages.take().process(logic);
} catch (InterruptedException ex) {
LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS
Thread.currentThread().interrupt();
}
}
private void initializeSerializables() {
Serializer.registerClass(AnimationEnd.class);
Serializer.registerClass(ClientStartGame.class);
Serializer.registerClass(DeselectTSK.class);
Serializer.registerClass(ForceContinueGame.class);
Serializer.registerClass(StartGame.class);
Serializer.registerClass(JoinServer.class);
Serializer.registerClass(LeaveGame.class);
Serializer.registerClass(LobbyNotReady.class);
Serializer.registerClass(LobbyReady.class);
Serializer.registerClass(NoPowerCard.class);
Serializer.registerClass(RequestBriefing.class);
Serializer.registerClass(RequestDie.class);
Serializer.registerClass(RequestMove.class);
Serializer.registerClass(RequestPlayCard.class);
Serializer.registerClass(SelectCard.class);
Serializer.registerClass(SelectedPieces.class);
Serializer.registerClass(SelectTSK.class);
Serializer.registerClass(ActivePlayer.class);
Serializer.registerClass(AnyPiece.class);
Serializer.registerClass(Briefing.class);
Serializer.registerClass(CeremonyMessage.class);
Serializer.registerClass(Die.class);
Serializer.registerClass(DiceAgain.class);
Serializer.registerClass(DiceNow.class);
Serializer.registerClass(EndOfTurn.class);
Serializer.registerClass(LobbyAccept.class);
Serializer.registerClass(LobbyDeny.class);
Serializer.registerClass(LobbyPlayerJoin.class);
Serializer.registerClass(LobbyPlayerLeave.class);
Serializer.registerClass(MoveMessage.class);
Serializer.registerClass(NoTurn.class);
Serializer.registerClass(PauseGame.class);
Serializer.registerClass(PlayCard.class);
Serializer.registerClass(PossibleCard.class);
Serializer.registerClass(PossiblePiece.class);
Serializer.registerClass(RankingResponse.class);
Serializer.registerClass(RankingRollAgain.class);
Serializer.registerClass(ReconnectBriefing.class);
Serializer.registerClass(ResumeGame.class);
Serializer.registerClass(ServerStartGame.class);
Serializer.registerClass(StartPiece.class);
Serializer.registerClass(UpdateReady.class);
Serializer.registerClass(UpdateTSK.class);
Serializer.registerClass(WaitPiece.class);
}
private void registerListeners() {
myServer.addMessageListener(this, AnimationEnd.class);
myServer.addMessageListener(this, ClientStartGame.class);
myServer.addMessageListener(this, DeselectTSK.class);
myServer.addMessageListener(this, ForceContinueGame.class);
myServer.addMessageListener(this, StartGame.class);
myServer.addMessageListener(this, JoinServer.class);
myServer.addMessageListener(this, LeaveGame.class);
myServer.addMessageListener(this, LobbyNotReady.class);
myServer.addMessageListener(this, LobbyReady.class);
myServer.addMessageListener(this, NoPowerCard.class);
myServer.addMessageListener(this, RequestBriefing.class);
myServer.addMessageListener(this, RequestDie.class);
myServer.addMessageListener(this, RequestMove.class);
myServer.addMessageListener(this, RequestPlayCard.class);
myServer.addMessageListener(this, SelectCard.class);
myServer.addMessageListener(this, SelectedPieces.class);
myServer.addMessageListener(this, SelectTSK.class);
myServer.addConnectionListener(this);
}
public void messageReceived(HostedConnection source, ClientMessage message) {
LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
pendingMessages.add(new ReceivedMessage(message, source.getId()));
}
@Override
public void connectionAdded(Server server, HostedConnection hostedConnection) {
LOGGER.log(Level.INFO, "new connection {0}", hostedConnection); //NON-NLS
// ToDo: Synchronize data between server and client.
logic.getGame().addPlayer(hostedConnection.getId(), new Player());
}
@Override
public void connectionRemoved(Server server, HostedConnection hostedConnection) {
LOGGER.log(Level.INFO, "connection closed: {0}", hostedConnection); //NON-NLS
final Player player = logic.getGame().getPlayerById(hostedConnection.getId());
if (player == null)
LOGGER.log(Level.INFO, "closed connection does not belong to an active player"); //NON-NLS
else { //NON-NLS
LOGGER.log(Level.INFO, "closed connection belongs to {0}", player); //NON-NLS
// exit(0);
this.handleDisconnect(hostedConnection.getId());
}
}
/**
* This method will be used to handle unintentional disconnections from players.
*
* @param id as the id of the disconnected player.
*/
public void handleDisconnect(int id) {
this.logic.received(new Disconnected(), id);
}
public void exit(int exitValue) { //NON-NLS
LOGGER.log(Level.INFO, "close request"); //NON-NLS
if (myServer != null)
for (HostedConnection client : myServer.getConnections()) //NON-NLS
if (client != null) client.close("Game over"); //NON-NLS
System.exit(exitValue);
}
/**
* Send the specified message to the specified connection.
*
* @param id the connection id
* @param message the message
*/
public void send(int id, ServerMessage message) {
if (myServer == null || !myServer.isRunning()) {
LOGGER.log(Level.ERROR, "no server running when trying to send {0}", message); //NON-NLS
return;
}
final HostedConnection connection = myServer.getConnection(id);
if (connection != null)
connection.send(message);
else
LOGGER.log(Level.ERROR, "there is no connection with id={0}", id); //NON-NLS
}
/**
* This method will be used to send the given message parameter to all connected players which are saved inside the
* players attribute of Game class.
*
* @param message as the message which will be sent to all players as a ServerMessage.
*/
public void broadcast(ServerMessage message) {
for (Map.Entry<Integer, Player> entry: this.logic.getGame().getPlayers().entrySet()) {
this.send(entry.getKey(), message);
}
}
//TODO:
@Override
public void messageReceived(HostedConnection source, Message m) {
}
}

View File

@@ -0,0 +1,10 @@
package pp.mdga.client.server;
import pp.mdga.message.client.ClientInterpreter;
import pp.mdga.message.client.ClientMessage;
public record ReceivedMessage(ClientMessage msg, int from) {
void process(ClientInterpreter interpreter) {
msg.accept(interpreter, from);
}
}

View File

@@ -0,0 +1,98 @@
package pp.mdga.client.view;
import com.jme3.scene.Geometry;
import pp.mdga.client.dialog.SingleButtonLeftDialog;
import pp.mdga.client.dialog.SingleButtonRightDialog;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.MdgaState;
public class CeremonyView extends MdgaView {
private enum SubState {
AWARD_CEREMONY,
STATISTICS,
}
private SubState state;
private SingleButtonRightDialog continueButton;
private SingleButtonLeftDialog backButton;
private Geometry background;
public CeremonyView(MdgaApp app) {
super(app);
continueButton = new SingleButtonRightDialog(app, node, "Weiter", () -> forward());
backButton = new SingleButtonLeftDialog(app, node, "Zurück", () -> back());
}
@Override
public void onEnter() {
enterSub(SubState.AWARD_CEREMONY);
}
@Override
public void onLeave() {
continueButton.hide();
backButton.hide();
}
private void awardCeremony() {
background = createBackground("b1.png");
node.attachChild(background);
continueButton.show();
backButton.hide();
}
private void statistics() {
background = createBackground("b2.png");
node.attachChild(background);
continueButton.show();
backButton.show();
}
private void enterSub(SubState state) {
this.state = state;
if(null != background) {
node.detachChild(background);
}
backButton.hide();
continueButton.hide();
switch (state) {
case AWARD_CEREMONY:
awardCeremony();
break;
case STATISTICS:
statistics();
break;
}
}
private void forward() {
switch (state) {
case AWARD_CEREMONY:
enterSub(SubState.STATISTICS);
break;
case STATISTICS:
app.getModelSyncronizer().enter(MdgaState.MAIN);
break;
}
}
private void back() {
switch (state) {
case AWARD_CEREMONY:
//nothing
break;
case STATISTICS:
enterSub(SubState.AWARD_CEREMONY);
break;
}
}
}

View File

@@ -0,0 +1,79 @@
package pp.mdga.client.view;
import com.jme3.post.FilterPostProcessor;
import pp.mdga.client.board.BoardHandler;
import pp.mdga.client.board.CameraHandler;
import pp.mdga.client.dialog.SingleButtonLeftDialog;
import pp.mdga.client.dialog.SingleButtonRightDialog;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.MdgaState;
import pp.mdga.client.gui.GuiHandler;
public class GameView extends MdgaView {
private BoardHandler boardHandler;
private CameraHandler camera;
private GuiHandler guiHandler;
private SingleButtonLeftDialog leaveButton;
private SingleButtonRightDialog continueButton;
public GameView(MdgaApp app) {
super(app);
//Filter für Outline: Reihenfolge CameraHandler(dlsf) -> BoardHandler -> viewPort.addProcessor einhalten!
FilterPostProcessor fpp = new FilterPostProcessor(app.getAssetManager());
this.camera = new CameraHandler(app, fpp);
this.boardHandler = new BoardHandler(app, fpp);
app.getViewPort().addProcessor(fpp);
this.guiHandler = new GuiHandler(app);
leaveButton = new SingleButtonLeftDialog(app, settingsNode, "Verlassen", () -> leaveGame());
continueButton = new SingleButtonRightDialog(app, node, "Weiter", () -> app.getModelSyncronizer().enter(MdgaState.CEREMONY));
}
@Override
public void onEnter() {
camera.init();
boardHandler.init();
guiHandler.init();
continueButton.show();
}
@Override
public void onLeave() {
continueButton.hide();
camera.shutdown();
boardHandler.shutdown();
}
@Override
public void onUpdate() {
camera.update(app.getInputSyncronizer().getScroll(), app.getInputSyncronizer().getRotation());
}
@Override
protected void enterExtendedSettings() {
leaveButton.show();
}
@Override
protected void leaveExtendedSettings() {
leaveButton.hide();
}
private void leaveGame() {
leaveSettings(false);
app.getModelSyncronizer().leave();
app.afteGameCleanup();
}
public BoardHandler getBoardHandler() {
return boardHandler;
}
}

View File

@@ -0,0 +1,58 @@
package pp.mdga.client.view;
import com.jme3.scene.Geometry;
import pp.mdga.client.dialog.LobbyButtonDialog;
import pp.mdga.client.dialog.SingleButtonLeftDialog;
import pp.mdga.client.dialog.SingleButtonRightDialog;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
import java.util.ArrayList;
public class LobbyView extends MdgaView {
private Geometry background;
private SingleButtonRightDialog readyButton;
private SingleButtonLeftDialog leaveButton;
private ArrayList<LobbyButtonDialog> lobbyButtons = new ArrayList<>();
public LobbyView(MdgaApp app) {
super(app);
background = createBackground("lobby.png");
node.attachChild(background);
readyButton = new SingleButtonRightDialog(app, node, "Fertig", () -> app.getModelSyncronizer().setReady());
leaveButton = new SingleButtonLeftDialog(app, node, "Verlassen", () -> app.getModelSyncronizer().leave());
lobbyButtons.add(new LobbyButtonDialog(app, node, "HEER", 0));
lobbyButtons.add(new LobbyButtonDialog(app, node, "MARINE", 1));
lobbyButtons.add(new LobbyButtonDialog(app, node, "CIR", 2));
lobbyButtons.add(new LobbyButtonDialog(app, node, "LUFTWAFFE", 3));
}
@Override
public void onEnter() {
readyButton.show();
leaveButton.show();
for (LobbyButtonDialog b : lobbyButtons) {
b.show();
}
}
@Override
public void onLeave() {
for (LobbyButtonDialog b : lobbyButtons) {
b.hide();
}
readyButton.hide();
leaveButton.hide();
}
public void setTaken(Color color, boolean isTaken, boolean isSelf, String name) {
lobbyButtons.get(color.ordinal()).setTaken(isTaken, isSelf, name);
}
}

View File

@@ -0,0 +1,79 @@
package pp.mdga.client.view;
import com.jme3.scene.Geometry;
import pp.mdga.client.dialog.Dialog;
import pp.mdga.client.dialog.HostDialog;
import pp.mdga.client.dialog.JoinDialog;
import pp.mdga.client.dialog.StartDialog;
import pp.mdga.client.MdgaApp;
import com.jme3.math.Vector3f;
public class MainView extends MdgaView {
private Geometry background;
private StartDialog dialog;
private Dialog subDialog;
public MainView(MdgaApp app) {
super(app);
background = createBackground("powercards.png");
node.attachChild(background);
Vector3f size = new Vector3f(280, 60, 0);
dialog = new StartDialog(app, node);
dialog.addButton("Spiel beitreten", () -> enterJoin(), size);
dialog.addButton("Spiel hosten", () -> enterHost(), size);
dialog.addButton("Einstellungen", () -> enterSettings(false), size);
dialog.addButton("Spiel beenden", () -> app.stop(), size);
}
@Override
public void onEnter() {
dialog.show();
}
@Override
public void onLeave() {
dialog.hide();
if(subDialog != null) {
subDialog.hide();
subDialog = null;
}
}
private void enterJoin() {
app.getModelSyncronizer().setName(dialog.getName());
subDialog = new JoinDialog(app, node, () -> leaveJoin());
dialog.hide();
subDialog.show();
}
private void leaveJoin() {
subDialog.hide();
dialog.show();
subDialog = null;
}
private void enterHost() {
app.getModelSyncronizer().setName(dialog.getName());
subDialog = new HostDialog(app, node, () -> leaveHost());
dialog.hide();
subDialog.show();
}
private void leaveHost() {
subDialog.hide();
dialog.show();
subDialog = null;
}
}

View File

@@ -0,0 +1,206 @@
package pp.mdga.client.view;
import com.jme3.asset.TextureKey;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import pp.mdga.client.dialog.GetPercentRunnable;
import pp.mdga.client.dialog.PercentRunnable;
import pp.mdga.client.dialog.SettingsButtonDialog;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.dialog.SettingsDialog;
public abstract class MdgaView {
protected MdgaApp app;
protected Node node;
protected int depth = 0;
private boolean isVideo = false;
private boolean isAudio = false;
protected Node settingsNode;
protected Node audioSettingsNode;
protected Node videoSettingsNode;
private SettingsButtonDialog settingsButton;
private Geometry settingsBackground;
private Geometry audioBackground;
private Geometry videoBackground;
private SettingsDialog settings;
private SettingsDialog audio;
private SettingsDialog video;
public MdgaView(MdgaApp app) {
this.app = app;
this.node = new Node();
this.settingsNode = new Node();
this.audioSettingsNode = new Node();
this.videoSettingsNode = new Node();
this.settingsButton = new SettingsButtonDialog(app, node, "", () -> enterSettings(false));
this.settingsBackground = createBackground("background/zahnräder.png");
settingsNode.attachChild(settingsBackground);
this.audioBackground = createBackground("background/lautsprecher.png");
audioSettingsNode.attachChild(audioBackground);
this.videoBackground = createBackground("background/monitors.png");
videoSettingsNode.attachChild(videoBackground);
Vector3f size = new Vector3f(280, 60, 0);
this.settings = new SettingsDialog(app, settingsNode, "zahnrad.png");
this.settings.addButton("Video", () -> enterVideo(), size);
this.settings.addButton("Audio", () -> enterAudio(), size);
this.settings.addButton("Zurück", () -> leaveSettings(false), size);
this.audio = new SettingsDialog(app, audioSettingsNode, "audio_icon.png");
this.audio.addSlider("Lautstärke", new PercentRunnable(app.getAcousticHandler()::setMainVolume), new GetPercentRunnable(app.getAcousticHandler()::getMainVolume), size, (int) app.getAcousticHandler().getMainVolume() * 100);
this.audio.addSlider("Musik", new PercentRunnable(app.getAcousticHandler()::setMusicVolume), new GetPercentRunnable(app.getAcousticHandler()::getMusicVolume), size, (int) app.getAcousticHandler().getMusicVolume() * 100);
this.audio.addSlider("Sound", new PercentRunnable(app.getAcousticHandler()::setSoundVolume), new GetPercentRunnable(app.getAcousticHandler()::getSoundVolume), size, (int) app.getAcousticHandler().getSoundVolume() * 100);
this.audio.addButton("Zurück", () -> leaveAudio(), size);
this.video = new SettingsDialog(app, videoSettingsNode, "monitor.png");
this.video.addButton("A", () -> System.out.println("A"), size);
this.video.addButton("B", () -> System.out.println("B"), size);
this.video.addButton("Zurück", () -> leaveVideo(), size);
}
public void enter() {
app.getGuiNode().attachChild(node);
audio.initVolume();
settingsButton.show();
onEnter();
}
public void leave() {
onLeave();
settingsButton.hide();
app.getGuiNode().detachChild(node);
}
public void update() {
audio.update();
onUpdate();
}
protected abstract void onEnter();
protected abstract void onLeave();
protected void onUpdate() {}
protected Geometry createBackground(String texturePath) {
TextureKey key = new TextureKey(texturePath, true);
Texture backgroundTexture = app.getAssetManager().loadTexture(key);
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry background = new Geometry("Background", quad);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", backgroundTexture);
background.setMaterial(mat);
background.setLocalTranslation(0, 0, -1);
return background;
}
protected void enterExtendedSettings() {}
protected void leaveExtendedSettings() {}
protected void enterSettings(boolean soft) {
leave();
app.getGuiNode().attachChild(settingsNode);
if(!soft) {
depth++;
enterExtendedSettings();
}
settings.show();
}
protected void leaveSettings(boolean soft) {
settings.hide();
app.getGuiNode().detachChild(settingsNode);
if(!soft) {
leaveExtendedSettings();
enter();
depth--;
}
}
protected void enterAudio() {
leaveSettings(true);
depth++;
isAudio = true;
app.getGuiNode().attachChild(audioSettingsNode);
audio.show();
}
protected void leaveAudio() {
audio.hide();
app.getGuiNode().detachChild(audioSettingsNode);
isAudio = false;
depth--;
enterSettings(true);
}
protected void enterVideo() {
leaveSettings(true);
app.getGuiNode().attachChild(videoSettingsNode);
depth++;
isVideo = true;
video.show();
}
protected void leaveVideo() {
video.hide();
app.getGuiNode().detachChild(videoSettingsNode);
depth--;
isVideo = false;
enterSettings(true);
}
public void pressEscape() {
if(depth == 0) {
enterSettings(false);
} else if(depth == 1) {
leaveSettings(false);
}
else if (depth == 2){
if(isVideo) {
leaveVideo();
}
if(isAudio) {
leaveAudio();
}
} else {
throw new RuntimeException();
}
}
}

View File

@@ -0,0 +1,29 @@
uniform sampler2D m_Texture;
uniform sampler2D m_OutlineDepthTexture;
uniform sampler2D m_DepthTexture;
varying vec2 texCoord;
uniform vec2 m_Resolution;
uniform vec4 m_OutlineColor;
uniform float m_OutlineWidth;
void main() {
vec4 depth = texture2D(m_OutlineDepthTexture, texCoord);
vec4 depth1 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,m_OutlineWidth))/m_Resolution);
vec4 depth2 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
vec4 depth3 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,m_OutlineWidth))/m_Resolution);
vec4 depth4 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
vec4 depth5 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,m_OutlineWidth))/m_Resolution);
vec4 depth6 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-m_OutlineWidth))/m_Resolution);
vec4 depth7 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,0.))/m_Resolution);
vec4 depth8 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,0.))/m_Resolution);
vec4 color = texture2D(m_Texture, texCoord);
//如果是背景
if(depth==vec4(0.) && (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth||depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)){
gl_FragColor = m_OutlineColor;
}else{
gl_FragColor = color;
}
//debug
//gl_FragColor = vec4(0.,(1.-ratio),0.,1.);
}

View File

@@ -0,0 +1,21 @@
MaterialDef Cartoon Edge {
MaterialParameters {
Int NumSamples
Int NumSamplesDepth
Texture2D Texture
Texture2D OutlineDepthTexture
Texture2D DepthTexture
Vector2 Resolution
Color OutlineColor
Float OutlineWidth
}
Technique {
VertexShader GLSL150: MatDefs/SelectObjectOutliner/Post15.vert
FragmentShader GLSL150: MatDefs/SelectObjectOutliner/Outline.frag
WorldParameters {
}
}
}

View File

@@ -0,0 +1,10 @@
varying vec2 texCoord;
uniform sampler2D m_Texture;
uniform sampler2D m_NormalsTexture;
uniform sampler2D m_DepthTexture;
void main(){
vec4 color = texture2D(m_Texture, texCoord);
gl_FragColor=color;
}

View File

@@ -0,0 +1,19 @@
MaterialDef Cartoon Edge {
MaterialParameters {
Int NumSamples
Int NumSamplesDepth
Texture2D Texture
Texture2D NormalsTexture
Texture2D DepthTexture
}
Technique {
VertexShader GLSL150: MatDefs/SelectObjectOutliner/Post15.vert
FragmentShader GLSL150: MatDefs/SelectObjectOutliner/OutlinePre.frag
WorldParameters {
}
}
}

View File

@@ -0,0 +1,10 @@
in vec4 inPosition;
in vec2 inTexCoord;
out vec2 texCoord;
void main() {
vec2 pos = inPosition.xy * 2.0 - 1.0;
gl_Position = vec4(pos, 0.0, 1.0);
texCoord = inTexCoord;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@@ -1,64 +1,129 @@
world 0,0 world 0,0 90
#tree_small 1,1 0
#tree_big 0,0 0
#Marine Pos
marine 4,-5 -90
marine 4,-4 -90
marine 5,-4 -90
marine 5,-5 -90
#Blue (Marine) wait Node
node_wait_blue 4,-5 0
node_wait_blue 4,-4 0
node_wait_blue 5,-4 0
node_wait_blue 5,-5 0
#Lw Pos
lw -5,4 90
lw -4,4 90
lw -4,5 90
lw -5,5 90
#Black (Lw) wait Node
node_wait_black -5,4 0
node_wait_black -4,4 0
node_wait_black -4,5 0
node_wait_black -5,5 0
#Heer Pos
heer -4,-5 180
heer -4,-4 180
heer -5,-4 180
heer -5,-5 180
#Green (Heer) Wait Node
node_wait_green -4,-5 0
node_wait_green -4,-4 0
node_wait_green -5,-4 0
node_wait_green -5,-5 0
#CIR Pos
cir 4,5 0
cir 4,4 0
cir 5,4 0
cir 5,5 0
#Assets
jet -10,-1 45
ship 11,0 169
big_tent -9,-7 130
big_tent 7,-10 225
small_tent -9,7 45
small_tent -10,5 60
radar 0,10 -20
small_tent 6,8 190
small_tent 8,7 160
tank -1,-10 135
#Yellow (CIR) wait Node
node_wait_yellow 4,5 0
node_wait_yellow 4,4 0
node_wait_yellow 5,4 0
node_wait_yellow 5,5 0
#Nodes für Map #Nodes für Map
node_start -1,-5 node_start -5,1 0
node -1,-4 node -4,1 0
node -1,-3 node -3,1 0
node -1,-2 node -2,1 0
node_bonus -1,-1 node_bonus -1,1 0
node -2,-1 node -1,2 0
node -3,-1 node -1,3 0
node -4,-1 node -1,4 0
node -5,-1 node -1,5 0
node -5,0 node 0,5 0
node_start -5,1 node_start 1,5 0
node -4,1 node 1,4 0
node -3,1 node 1,3 0
node -2,1 node 1,2 0
node_bonus -1,1 node_bonus 1,1 0
node -1,2 node 2,1 0
node -1,3 node 3,1 0
node -1,4 node 4,1 0
node -1,5 node 5,1 0
node 0,5 node 5,0 0
node_start 1,5 node_start 5,-1 0
node 1,4 node 4,-1 0
node 1,3 node 3,-1 0
node 1,2 node 2,-1 0
node_bonus 1,1 node_bonus 1,-1 0
node 2,1 node 1,-2 0
node 3,1 node 1,-3 0
node 4,1 node 1,-4 0
node 5,1 node 1,-5 0
node 5,0 node 0,-5 0
node_start 5,-1 node_start -1,-5 0
node 4,-1 node -1,-4 0
node 3,-1 node -1,-3 0
node 2,-1 node -1,-2 0
node_bonus 1,-1 node_bonus -1,-1 0
node 1,-2 node -2,-1 0
node 1,-3 node -3,-1 0
node 1,-4 node -4,-1 0
node 1,-5 node -5,-1 0
node 0,-5 node -5,0 0
#Node Home #Node Home
node_home_blue 0,-1 node_home_black -4,0 0
node_home_blue 0,-2 node_home_black -3,0 0
node_home_blue 0,-3 node_home_black -2,0 0
node_home_blue 0,-4 node_home_black -1,0 0
node_home_black 0,1 node_home_green 0,-4 0
node_home_black 0,2 node_home_green 0,-3 0
node_home_black 0,3 node_home_green 0,-2 0
node_home_black 0,4 node_home_green 0,-1 0
node_home_yellow 1,0 node_home_yellow 0,4 0
node_home_yellow 2,0 node_home_yellow 0,3 0
node_home_yellow 3,0 node_home_yellow 0,2 0
node_home_yellow 4,0 node_home_yellow 0,1 0
node_home_green -1,0 node_home_blue 4,0 0
node_home_green -2,0 node_home_blue 3,0 0
node_home_green -3,0 node_home_blue 2,0 0
node_home_green -4,0 node_home_blue 1,0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,616 @@
# Blender 3.6.5
# www.blender.org
o shield
v 0.329363 -0.615745 1.151866
v -0.000000 -0.615745 0.415793
v -0.000000 -0.615745 1.151866
v 0.329363 -0.615745 1.092511
v -0.000000 -0.615745 0.415793
v 0.326010 -0.615745 0.996204
v 0.316022 -0.615745 0.901858
v 0.299600 -0.615745 0.811392
v 0.277080 -0.615745 0.726650
v 0.248920 -0.615745 0.649355
v 0.215692 -0.615745 0.581082
v 0.178074 -0.615745 0.523220
v 0.136832 -0.615745 0.476947
v 0.092804 -0.615745 0.443205
v 0.046887 -0.615745 0.422681
v -0.329363 -0.615745 1.151866
v -0.329363 -0.615745 1.092511
v -0.326010 -0.615745 0.996204
v -0.316022 -0.615745 0.901858
v -0.299600 -0.615745 0.811392
v -0.277080 -0.615745 0.726650
v -0.248920 -0.615745 0.649355
v -0.215692 -0.615745 0.581082
v -0.178075 -0.615745 0.523220
v -0.136832 -0.615745 0.476947
v -0.092804 -0.615745 0.443205
v -0.046887 -0.615745 0.422681
v 0.329363 -0.590929 1.151866
v -0.000000 -0.590929 1.151866
v -0.000000 -0.590929 0.415793
v -0.000000 -0.590929 0.415793
v 0.046887 -0.590929 0.422681
v 0.092804 -0.590929 0.443205
v 0.136832 -0.590929 0.476947
v 0.178074 -0.590929 0.523220
v 0.215692 -0.590929 0.581082
v 0.248920 -0.590929 0.649355
v 0.277080 -0.590929 0.726650
v 0.299600 -0.590929 0.811392
v 0.316022 -0.590929 0.901858
v 0.326010 -0.590929 0.996204
v 0.329363 -0.590929 1.092511
v -0.329363 -0.590929 1.151866
v -0.046887 -0.590929 0.422681
v -0.092804 -0.590929 0.443205
v -0.136832 -0.590929 0.476947
v -0.178075 -0.590929 0.523220
v -0.215692 -0.590929 0.581082
v -0.248920 -0.590929 0.649355
v -0.277080 -0.590929 0.726650
v -0.299600 -0.590929 0.811392
v -0.316022 -0.590929 0.901858
v -0.326010 -0.590929 0.996204
v -0.329363 -0.590929 1.092511
v -0.000000 0.740870 0.678108
v -0.000000 0.740870 1.005550
v 0.144537 0.726635 0.678108
v 0.144537 0.726635 1.005550
v 0.283519 0.684475 0.678108
v 0.283519 0.684475 1.005550
v 0.411605 0.616011 0.678108
v 0.411605 0.616011 1.005550
v 0.523874 0.523875 0.678108
v 0.523874 0.523874 1.005550
v 0.616011 0.411606 0.678108
v 0.616011 0.411606 1.005550
v 0.684475 0.283519 0.678108
v 0.684475 0.283519 1.005550
v 0.726635 0.144537 0.678108
v 0.726635 0.144537 1.005550
v 0.740870 0.000000 0.678108
v 0.740870 0.000000 1.005550
v 0.726635 -0.144537 0.678108
v 0.726635 -0.144537 1.005550
v 0.684475 -0.283519 0.678108
v 0.684475 -0.283519 1.005550
v 0.616011 -0.411605 0.678108
v 0.616011 -0.411605 1.005550
v 0.523874 -0.523874 0.678108
v 0.523874 -0.523874 1.005550
v 0.411605 -0.616011 0.678108
v 0.411605 -0.616011 1.005550
v -0.411606 -0.616011 0.678108
v -0.411606 -0.616011 1.005550
v -0.523875 -0.523874 0.678108
v -0.523875 -0.523875 1.005550
v -0.616011 -0.411606 0.678108
v -0.616011 -0.411606 1.005550
v -0.684475 -0.283519 0.678108
v -0.684475 -0.283519 1.005550
v -0.726635 -0.144537 0.678108
v -0.726635 -0.144537 1.005550
v -0.740870 -0.000000 0.678108
v -0.740870 -0.000000 1.005550
v -0.726635 0.144537 0.678108
v -0.726635 0.144537 1.005550
v -0.684475 0.283519 0.678108
v -0.684475 0.283519 1.005550
v -0.616011 0.411606 0.678108
v -0.616011 0.411606 1.005550
v -0.523875 0.523874 0.678108
v -0.523875 0.523874 1.005550
v -0.411606 0.616011 0.678108
v -0.411606 0.616011 1.005550
v -0.283519 0.684475 0.678108
v -0.283519 0.684475 1.005550
v -0.144537 0.726635 0.678108
v -0.144537 0.726635 1.005550
v -0.142275 0.717330 1.005550
v -0.142275 0.717330 0.678108
v -0.279082 0.675829 1.005550
v -0.279082 0.675829 0.678108
v -0.405165 0.608437 1.005550
v -0.405165 0.608437 0.678108
v -0.515677 0.517742 1.005550
v -0.515677 0.517742 0.678108
v -0.606372 0.407230 1.005550
v -0.606372 0.407230 0.678108
v -0.673764 0.281147 1.005550
v -0.673764 0.281147 0.678108
v -0.715265 0.144340 1.005550
v -0.715265 0.144340 0.678108
v -0.729277 0.002065 1.005550
v -0.729277 0.002065 0.678108
v -0.715265 -0.140210 1.005550
v -0.715265 -0.140210 0.678108
v -0.673764 -0.277017 1.005550
v -0.673764 -0.277017 0.678108
v -0.606372 -0.403100 1.005550
v -0.606372 -0.403100 0.678108
v -0.515677 -0.513612 1.005550
v -0.515677 -0.513612 0.678108
v -0.405165 -0.604307 0.678108
v -0.405165 -0.604307 1.005550
v 0.405165 -0.604307 1.005550
v 0.405165 -0.604307 0.678108
v 0.515677 -0.513612 1.005550
v 0.515677 -0.513612 0.678108
v 0.606372 -0.403100 1.005550
v 0.606372 -0.403100 0.678108
v 0.673764 -0.277017 1.005550
v 0.673764 -0.277017 0.678108
v 0.715264 -0.140210 1.005550
v 0.715264 -0.140210 0.678108
v 0.729277 0.002065 1.005550
v 0.729277 0.002065 0.678108
v 0.715264 0.144340 1.005550
v 0.715264 0.144340 0.678108
v 0.673764 0.281147 1.005550
v 0.673764 0.281147 0.678108
v 0.606372 0.407230 1.005550
v 0.606372 0.407230 0.678108
v 0.515677 0.517742 1.005550
v 0.515677 0.517742 0.678108
v 0.405165 0.608437 1.005550
v 0.405165 0.608437 0.678108
v 0.279082 0.675829 1.005550
v 0.279082 0.675829 0.678108
v -0.000000 0.731342 1.005550
v 0.142275 0.717330 1.005550
v 0.142275 0.717330 0.678108
v -0.000000 0.731342 0.678108
vn -0.0000 -0.0000 1.0000
vn -0.9839 -0.0000 0.1786
vn 0.9994 -0.0000 0.0348
vn -0.4081 -0.0000 0.9130
vn 0.7465 -0.0000 0.6654
vn -0.9944 -0.0000 0.1053
vn 1.0000 -0.0000 -0.0000
vn -0.6083 -0.0000 0.7937
vn 0.8384 -0.0000 0.5451
vn -0.9994 -0.0000 0.0348
vn -0.7465 -0.0000 0.6654
vn 0.8992 -0.0000 0.4376
vn -1.0000 -0.0000 -0.0000
vn -0.8384 -0.0000 0.5451
vn 0.9396 -0.0000 0.3423
vn -0.0000 -0.0000 -1.0000
vn -0.8992 -0.0000 0.4376
vn 0.9665 -0.0000 0.2568
vn 0.1453 -0.0000 0.9894
vn -0.9396 -0.0000 0.3423
vn 0.9839 -0.0000 0.1786
vn 0.4081 -0.0000 0.9130
vn -0.9665 -0.0000 0.2568
vn 0.9944 -0.0000 0.1053
vn -0.1453 -0.0000 0.9894
vn 0.6083 -0.0000 0.7937
vn 0.0980 0.9952 -0.0000
vn 0.2903 0.9569 -0.0000
vn 0.4714 0.8819 -0.0000
vn 0.6344 0.7730 -0.0000
vn 0.7730 0.6344 -0.0000
vn 0.8819 0.4714 -0.0000
vn 0.9569 0.2903 -0.0000
vn 0.9952 0.0980 -0.0000
vn 0.9952 -0.0980 -0.0000
vn 0.9569 -0.2903 -0.0000
vn 0.8819 -0.4714 -0.0000
vn 0.7730 -0.6344 -0.0000
vn 0.6344 -0.7730 -0.0000
vn -0.6344 -0.7730 -0.0000
vn -0.7730 -0.6344 -0.0000
vn -0.8819 -0.4714 -0.0000
vn -0.9569 -0.2903 -0.0000
vn -0.9952 -0.0980 -0.0000
vn -0.9952 0.0980 -0.0000
vn -0.9569 0.2903 -0.0000
vn -0.8819 0.4714 -0.0000
vn -0.7730 0.6344 -0.0000
vn -0.6344 0.7730 -0.0000
vn -0.4714 0.8819 -0.0000
vn -0.2903 0.9569 -0.0000
vn -0.0980 0.9952 -0.0000
vn -0.8761 -0.4821 -0.0000
vn 0.4714 -0.8819 -0.0000
vn -0.4714 -0.8819 -0.0000
vn 0.8761 -0.4821 -0.0000
vn -0.2903 -0.9569 -0.0000
vn -0.0980 -0.9952 -0.0000
vn 0.0980 -0.9952 -0.0000
vn 0.2903 -0.9569 -0.0000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn -0.4081 -0.0000 0.9129
vn 0.4081 -0.0000 0.9129
vt 0.500025 0.000000
vt 0.500000 0.000000
vt 0.500000 1.000000
vt 0.571179 0.009358
vt 0.979748 0.660349
vt 0.954818 0.537446
vt 0.994911 0.788524
vt 1.000000 0.919363
vt 0.640884 0.037241
vt 0.707722 0.083081
vt 0.770332 0.145946
vt 1.000000 1.000000
vt 0.827439 0.224555
vt 0.877881 0.317308
vt 0.920631 0.422318
vt 0.968750 0.500000
vt 1.000000 0.500000
vt 0.968750 1.000000
vt 0.937500 0.500000
vt 0.937500 1.000000
vt 0.906250 0.500000
vt 0.906250 1.000000
vt 0.875000 0.500000
vt 0.875000 1.000000
vt 0.843750 0.500000
vt 0.843750 1.000000
vt 0.812500 0.500000
vt 0.781250 1.000000
vt 0.781250 0.500000
vt 0.750000 1.000000
vt 0.750000 0.500000
vt 0.718750 0.500000
vt 0.687500 1.000000
vt 0.687500 0.500000
vt 0.656250 0.500000
vt 0.656250 1.000000
vt 0.625000 0.500000
vt 0.625000 1.000000
vt 0.593750 0.500000
vt 0.406250 0.500000
vt 0.375000 1.000000
vt 0.375000 0.500000
vt 0.343750 1.000000
vt 0.343750 0.500000
vt 0.312500 0.500000
vt 0.312500 1.000000
vt 0.281250 0.500000
vt 0.250000 1.000000
vt 0.250000 0.500000
vt 0.218750 0.500000
vt 0.218750 1.000000
vt 0.187500 0.500000
vt 0.187500 1.000000
vt 0.156250 0.500000
vt 0.125000 1.000000
vt 0.125000 0.500000
vt 0.093750 0.500000
vt 0.093750 1.000000
vt 0.062500 0.500000
vt 0.000000 1.000000
vt 0.031250 1.000000
vt 0.062500 1.000000
vt 0.031250 0.500000
vt 0.000000 0.500000
vt 0.281250 1.000000
vt 0.593750 1.000000
vt 0.156250 1.000000
vt 0.718750 1.000000
vt 0.812500 1.000000
vt 0.406250 1.000000
s 0
f 5/1/1 2/2/1 3/3/1
f 2/2/1 5/1/1 27/4/1
f 30/2/1 31/1/1 32/4/1
f 31/1/1 30/2/1 29/3/1
f 7/5/2 39/6/2 8/6/2
f 18/7/3 54/8/3 17/8/3
f 15/4/4 33/9/4 32/4/4
f 25/10/5 47/11/5 24/11/5
f 6/7/6 40/5/6 7/5/6
f 17/8/7 43/12/7 16/12/7
f 14/9/8 34/10/8 33/9/8
f 24/11/9 48/13/9 23/13/9
f 4/8/10 41/7/10 6/7/10
f 12/11/11 34/10/11 13/10/11
f 23/13/12 49/14/12 22/14/12
f 1/12/13 42/8/13 4/8/13
f 11/13/14 35/11/14 12/11/14
f 22/14/15 50/15/15 21/15/15
f 3/3/16 28/12/16 1/12/16
f 16/12/16 29/3/16 3/3/16
f 10/14/17 36/13/17 11/13/17
f 21/15/18 51/6/18 20/6/18
f 5/1/19 44/4/19 27/4/19
f 9/15/20 37/14/20 10/14/20
f 20/6/21 52/5/21 19/5/21
f 26/9/22 44/4/22 45/9/22
f 8/6/23 38/15/23 9/15/23
f 19/5/24 53/7/24 18/7/24
f 15/4/25 31/1/25 5/1/25
f 25/10/26 45/9/26 46/10/26
f 56/12/27 57/16/27 55/17/27
f 58/18/28 59/19/28 57/16/28
f 60/20/29 61/21/29 59/19/29
f 62/22/30 63/23/30 61/21/30
f 64/24/31 65/25/31 63/23/31
f 66/26/32 67/27/32 65/25/32
f 67/27/33 70/28/33 69/29/33
f 69/29/34 72/30/34 71/31/34
f 72/30/35 73/32/35 71/31/35
f 73/32/36 76/33/36 75/34/36
f 76/33/37 77/35/37 75/34/37
f 78/36/38 79/37/38 77/35/38
f 80/38/39 81/39/39 79/37/39
f 83/40/40 86/41/40 85/42/40
f 85/42/41 88/43/41 87/44/41
f 88/43/42 89/45/42 87/44/42
f 90/46/43 91/47/43 89/45/43
f 91/47/44 94/48/44 93/49/44
f 94/48/45 95/50/45 93/49/45
f 96/51/46 97/52/46 95/50/46
f 98/53/47 99/54/47 97/52/47
f 99/54/48 102/55/48 101/56/48
f 102/55/49 103/57/49 101/56/49
f 104/58/50 105/59/50 103/57/50
f 56/60/1 109/61/1 159/60/1
f 106/62/51 107/63/51 105/59/51
f 108/61/52 55/64/52 107/63/52
f 94/48/1 125/65/1 123/48/1
f 64/24/1 151/26/1 66/26/1
f 82/66/53 136/39/53 81/39/53
f 80/38/1 135/66/1 82/66/1
f 79/37/16 136/39/16 138/37/16
f 88/43/1 131/41/1 129/43/1
f 87/44/16 132/42/16 85/42/16
f 104/58/1 115/55/1 113/58/1
f 103/57/16 116/56/16 101/56/16
f 58/18/1 157/20/1 60/20/1
f 102/55/1 117/67/1 115/55/1
f 74/68/1 141/33/1 76/33/1
f 95/50/16 124/49/16 93/49/16
f 68/69/1 147/28/1 70/28/1
f 56/12/1 160/18/1 58/18/1
f 72/30/1 143/68/1 74/68/1
f 97/52/16 122/50/16 95/50/16
f 96/51/1 123/48/1 121/51/1
f 112/59/54 113/58/54 114/57/54
f 118/54/37 119/53/37 120/52/37
f 126/47/34 123/48/34 125/65/34
f 130/44/31 131/41/31 132/42/31
f 138/37/48 139/36/48 140/35/48
f 144/32/45 145/30/45 146/31/45
f 150/27/42 151/26/42 152/25/42
f 156/21/55 157/20/55 158/19/55
f 93/49/16 126/47/16 91/47/16
f 60/20/1 155/22/1 62/22/1
f 77/35/16 138/37/16 140/35/16
f 126/47/33 127/46/33 128/45/33
f 128/45/32 129/43/32 130/44/32
f 59/19/16 156/21/16 158/19/16
f 86/41/1 134/70/1 131/41/1
f 67/27/16 148/29/16 150/27/16
f 132/42/30 134/70/30 133/40/30
f 92/65/1 127/46/1 125/65/1
f 136/39/49 137/38/49 138/37/49
f 76/33/1 139/36/1 78/36/1
f 85/42/16 133/40/16 83/40/16
f 140/35/47 141/33/47 142/34/47
f 65/25/16 150/27/16 152/25/16
f 144/32/46 141/33/46 143/68/46
f 75/34/16 140/35/16 142/34/16
f 55/64/16 110/63/16 107/63/16
f 99/54/16 120/52/16 97/52/16
f 83/40/56 134/70/56 84/70/56
f 146/31/44 147/28/44 148/29/44
f 55/17/16 161/16/16 162/17/16
f 108/61/1 111/62/1 109/61/1
f 148/29/43 149/69/43 150/27/43
f 57/16/16 158/19/16 161/16/16
f 63/23/16 152/25/16 154/23/16
f 71/31/16 144/32/16 146/31/16
f 101/56/16 118/54/16 99/54/16
f 152/25/41 153/24/41 154/23/41
f 98/53/1 121/51/1 119/53/1
f 90/46/1 129/43/1 127/46/1
f 154/23/40 155/22/40 156/21/40
f 73/32/16 142/34/16 144/32/16
f 107/63/16 112/59/16 105/59/16
f 70/28/1 145/30/1 72/30/1
f 100/67/1 119/53/1 117/67/1
f 158/19/57 160/18/57 161/16/57
f 69/29/16 146/31/16 148/29/16
f 89/45/16 130/44/16 87/44/16
f 161/16/58 159/12/58 162/17/58
f 62/22/1 153/24/1 64/24/1
f 162/64/59 109/61/59 110/63/59
f 66/26/1 149/69/1 68/69/1
f 110/63/60 111/62/60 112/59/60
f 106/62/1 113/58/1 111/62/1
f 91/47/16 128/45/16 89/45/16
f 61/21/16 154/23/16 156/21/16
f 114/57/39 115/55/39 116/56/39
f 116/56/38 117/67/38 118/54/38
f 105/59/16 114/57/16 103/57/16
f 78/36/1 137/38/1 80/38/1
f 120/52/36 121/51/36 122/50/36
f 122/50/35 123/48/35 124/49/35
f 3/3/61 1/12/61 4/8/61
f 4/8/61 6/7/61 3/3/61
f 6/7/61 7/5/61 3/3/61
f 7/5/61 8/6/61 3/3/61
f 8/6/61 9/15/61 3/3/61
f 9/15/61 10/14/61 3/3/61
f 10/14/61 11/13/61 5/1/61
f 3/3/61 10/14/61 5/1/61
f 11/13/61 12/11/61 5/1/61
f 12/11/61 13/10/61 5/1/61
f 13/10/61 14/9/61 5/1/61
f 14/9/61 15/4/61 5/1/61
f 27/4/61 26/9/61 2/2/61
f 26/9/61 25/10/61 2/2/61
f 25/10/61 24/11/61 2/2/61
f 24/11/61 23/13/61 2/2/61
f 23/13/61 22/14/61 2/2/61
f 22/14/61 21/15/61 3/3/61
f 21/15/61 20/6/61 3/3/61
f 20/6/61 19/5/61 3/3/61
f 19/5/61 18/7/61 3/3/61
f 18/7/61 17/8/61 3/3/61
f 17/8/61 16/12/61 3/3/61
f 22/14/61 3/3/61 2/2/61
f 32/4/62 33/9/62 30/2/62
f 33/9/62 34/10/62 30/2/62
f 34/10/62 35/11/62 30/2/62
f 35/11/62 36/13/62 30/2/62
f 36/13/62 37/14/62 30/2/62
f 37/14/62 38/15/62 29/3/62
f 38/15/62 39/6/62 29/3/62
f 39/6/62 40/5/62 29/3/62
f 40/5/62 41/7/62 29/3/62
f 41/7/62 42/8/62 29/3/62
f 42/8/62 28/12/62 29/3/62
f 37/14/62 29/3/62 30/2/62
f 29/3/62 43/12/62 54/8/62
f 54/8/62 53/7/62 29/3/62
f 53/7/62 52/5/62 29/3/62
f 52/5/62 51/6/62 29/3/62
f 51/6/62 50/15/62 29/3/62
f 50/15/62 49/14/62 29/3/62
f 49/14/62 48/13/62 31/1/62
f 29/3/62 49/14/62 31/1/62
f 48/13/62 47/11/62 31/1/62
f 47/11/62 46/10/62 31/1/62
f 46/10/62 45/9/62 31/1/62
f 45/9/62 44/4/62 31/1/62
f 7/5/2 40/5/2 39/6/2
f 18/7/3 53/7/3 54/8/3
f 15/4/63 14/9/63 33/9/63
f 25/10/5 46/10/5 47/11/5
f 6/7/6 41/7/6 40/5/6
f 17/8/7 54/8/7 43/12/7
f 14/9/8 13/10/8 34/10/8
f 24/11/9 47/11/9 48/13/9
f 4/8/10 42/8/10 41/7/10
f 12/11/11 35/11/11 34/10/11
f 23/13/12 48/13/12 49/14/12
f 1/12/13 28/12/13 42/8/13
f 11/13/14 36/13/14 35/11/14
f 22/14/15 49/14/15 50/15/15
f 3/3/16 29/3/16 28/12/16
f 16/12/16 43/12/16 29/3/16
f 10/14/17 37/14/17 36/13/17
f 21/15/18 50/15/18 51/6/18
f 5/1/19 31/1/19 44/4/19
f 9/15/20 38/15/20 37/14/20
f 20/6/21 51/6/21 52/5/21
f 26/9/64 27/4/64 44/4/64
f 8/6/23 39/6/23 38/15/23
f 19/5/24 52/5/24 53/7/24
f 15/4/25 32/4/25 31/1/25
f 25/10/26 26/9/26 45/9/26
f 56/12/27 58/18/27 57/16/27
f 58/18/28 60/20/28 59/19/28
f 60/20/29 62/22/29 61/21/29
f 62/22/30 64/24/30 63/23/30
f 64/24/31 66/26/31 65/25/31
f 66/26/32 68/69/32 67/27/32
f 67/27/33 68/69/33 70/28/33
f 69/29/34 70/28/34 72/30/34
f 72/30/35 74/68/35 73/32/35
f 73/32/36 74/68/36 76/33/36
f 76/33/37 78/36/37 77/35/37
f 78/36/38 80/38/38 79/37/38
f 80/38/39 82/66/39 81/39/39
f 83/40/40 84/70/40 86/41/40
f 85/42/41 86/41/41 88/43/41
f 88/43/42 90/46/42 89/45/42
f 90/46/43 92/65/43 91/47/43
f 91/47/44 92/65/44 94/48/44
f 94/48/45 96/51/45 95/50/45
f 96/51/46 98/53/46 97/52/46
f 98/53/47 100/67/47 99/54/47
f 99/54/48 100/67/48 102/55/48
f 102/55/49 104/58/49 103/57/49
f 104/58/50 106/62/50 105/59/50
f 56/60/1 108/61/1 109/61/1
f 106/62/51 108/61/51 107/63/51
f 108/61/52 56/60/52 55/64/52
f 94/48/1 92/65/1 125/65/1
f 64/24/1 153/24/1 151/26/1
f 82/66/53 135/66/53 136/39/53
f 80/38/1 137/38/1 135/66/1
f 79/37/16 81/39/16 136/39/16
f 88/43/1 86/41/1 131/41/1
f 87/44/16 130/44/16 132/42/16
f 104/58/1 102/55/1 115/55/1
f 103/57/16 114/57/16 116/56/16
f 58/18/1 160/18/1 157/20/1
f 102/55/1 100/67/1 117/67/1
f 74/68/1 143/68/1 141/33/1
f 95/50/16 122/50/16 124/49/16
f 68/69/1 149/69/1 147/28/1
f 56/12/1 159/12/1 160/18/1
f 72/30/1 145/30/1 143/68/1
f 97/52/16 120/52/16 122/50/16
f 96/51/1 94/48/1 123/48/1
f 112/59/54 111/62/54 113/58/54
f 118/54/37 117/67/37 119/53/37
f 126/47/34 124/49/34 123/48/34
f 130/44/31 129/43/31 131/41/31
f 138/37/48 137/38/48 139/36/48
f 144/32/45 143/68/45 145/30/45
f 150/27/42 149/69/42 151/26/42
f 156/21/55 155/22/55 157/20/55
f 93/49/16 124/49/16 126/47/16
f 60/20/1 157/20/1 155/22/1
f 77/35/16 79/37/16 138/37/16
f 126/47/33 125/65/33 127/46/33
f 128/45/32 127/46/32 129/43/32
f 59/19/16 61/21/16 156/21/16
f 86/41/1 84/70/1 134/70/1
f 67/27/16 69/29/16 148/29/16
f 132/42/30 131/41/30 134/70/30
f 92/65/1 90/46/1 127/46/1
f 136/39/49 135/66/49 137/38/49
f 76/33/1 141/33/1 139/36/1
f 85/42/16 132/42/16 133/40/16
f 140/35/47 139/36/47 141/33/47
f 65/25/16 67/27/16 150/27/16
f 144/32/46 142/34/46 141/33/46
f 75/34/16 77/35/16 140/35/16
f 55/64/16 162/64/16 110/63/16
f 99/54/16 118/54/16 120/52/16
f 83/40/56 133/40/56 134/70/56
f 146/31/44 145/30/44 147/28/44
f 55/17/16 57/16/16 161/16/16
f 108/61/1 106/62/1 111/62/1
f 148/29/43 147/28/43 149/69/43
f 57/16/16 59/19/16 158/19/16
f 63/23/16 65/25/16 152/25/16
f 71/31/16 73/32/16 144/32/16
f 101/56/16 116/56/16 118/54/16
f 152/25/41 151/26/41 153/24/41
f 98/53/1 96/51/1 121/51/1
f 90/46/1 88/43/1 129/43/1
f 154/23/40 153/24/40 155/22/40
f 73/32/16 75/34/16 142/34/16
f 107/63/16 110/63/16 112/59/16
f 70/28/1 147/28/1 145/30/1
f 100/67/1 98/53/1 119/53/1
f 158/19/57 157/20/57 160/18/57
f 69/29/16 71/31/16 146/31/16
f 89/45/16 128/45/16 130/44/16
f 161/16/58 160/18/58 159/12/58
f 62/22/1 155/22/1 153/24/1
f 162/64/59 159/60/59 109/61/59
f 66/26/1 151/26/1 149/69/1
f 110/63/60 109/61/60 111/62/60
f 106/62/1 104/58/1 113/58/1
f 91/47/16 126/47/16 128/45/16
f 61/21/16 63/23/16 154/23/16
f 114/57/39 113/58/39 115/55/39
f 116/56/38 115/55/38 117/67/38
f 105/59/16 112/59/16 114/57/16
f 78/36/1 139/36/1 137/38/1
f 120/52/36 119/53/36 121/51/36
f 122/50/35 121/51/35 123/48/35

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -1,4 +0,0 @@
package pp.mdga.client;
public class AudioSettings extends Settings {
}

View File

@@ -1,4 +0,0 @@
package pp.mdga.client;
public class Ceremony extends ClientState {
}

Some files were not shown because too many files have changed in this diff Show More