373 Commits

Author SHA1 Message Date
Fleischer Hanno
ec295c94f1 fixed the equals method in piece 2024-12-08 11:23:53 +01:00
Hanno Fleischer
c9c99709ba added more communication fixes, states now use correct messages 2024-12-08 09:52:23 +01:00
Hanno Fleischer
8b27ccce22 adjusted stattransition methods to work correctly 2024-12-08 03:08:47 +01:00
Hanno Fleischer
8c22d935a9 implemented rest of the server logic in choosepiece substates, and began to fix bugs after testing 2024-12-08 01:59:29 +01:00
Hanno Fleischer
0c49d7ed1c Merge branch 'dev/server_h' of https://athene1.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/server_h 2024-12-07 15:37:02 +01:00
Hanno Fleischer
2ba6a22422 Merge branch 'development' into 'dev/server_h'
Development

See merge request progproj/gruppen-ht24/Gruppe-01!37
2024-12-07 14:36:25 +00:00
Hanno Fleischer
c37bac4614 replaced DicaAgain message with DicveNow 2024-12-07 15:34:34 +01:00
Felix Koppe
06b37584cb Merge dev/client_beck into development 2024-12-07 15:05:03 +01:00
Felix Koppe
0c42a2df88 Merge branch 'dev/client_beck' into development
# Conflicts:
#	Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MdgaSound.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/SoundAsset.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/animation/JetAnimation.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java
#	Projekte/mdga/model/src/main/java/pp/mdga/client/gamestate/GameStates.java
2024-12-07 15:00:27 +01:00
Felix Koppe
d75d704878 Add smoke effekt to missileAnimation 2024-12-07 14:55:08 +01:00
Cedric Beck
6d3c733f91 added effect for shell flying 2024-12-07 14:45:39 +01:00
Felix Koppe
f96da2c46c Add notification delay 2024-12-07 14:37:19 +01:00
Cedric Beck
1a079dad44 added shell asset for ShellAnimation 2024-12-07 14:22:36 +01:00
Cedric Beck
32f49a6181 added shellAnimation without shell asset 2024-12-07 13:30:30 +01:00
Felix Koppe
525809899e Minor improvements 2024-12-07 11:03:30 +01:00
Felix Koppe
fd9708752c Merge dev/model into development 2024-12-06 18:49:15 +01:00
Hanno Fleischer
5a9f7a8118 added AnimationEndMessages to 'RollDiceState', 'MovePieceState' and 'PlayPowerCardState' 2024-12-06 18:45:14 +01:00
Felix Koppe
236d3db930 Add ambience 2024-12-06 18:10:09 +01:00
Cedric Beck
29c6b13300 added MatrixAnimation 2024-12-06 17:09:06 +01:00
Felix Koppe
6059e93276 Fix interruptDialog 2024-12-06 16:52:08 +01:00
Felix Koppe
f2eeb6dab4 Fix logic error regarding cardSelection 2024-12-06 16:27:01 +01:00
Hanno Fleischer
2e1fe3c050 fixed a missing method call ind TurnState and removed debug sout statements ind 'RollDiceMessage' 2024-12-06 15:19:04 +01:00
Cedric Beck
2ac2de645b working on matrix-animation 2024-12-06 14:56:04 +01:00
Felix Koppe
d39f85fbe9 Add some javaDoc to client 2024-12-06 14:26:13 +01:00
Felix Koppe
960a57caba Fix broken lose sound 2024-12-06 13:28:38 +01:00
Felix Koppe
36631df2e9 Fix broken jet-sound 2024-12-06 13:26:28 +01:00
Felix Koppe
df27c23cd5 Fix shadercode 2024-12-06 13:19:17 +01:00
Felix Koppe
acd64d1507 Fix shadercode 2024-12-06 13:14:54 +01:00
Felix Koppe
76f86c8a66 Improve audioSettings 2024-12-06 11:35:05 +01:00
Hanno Fleischer
308b592b65 Merge branch 'development' into 'dev/model'
Development merge

See merge request progproj/gruppen-ht24/Gruppe-01!36
2024-12-06 09:55:19 +00:00
Felix Koppe
c4e7fb1d41 Fix logic in modelSyncronizer 2024-12-06 10:54:02 +01:00
Felix Koppe
aacc0440b3 Update .gitignore 2024-12-06 10:33:13 +01:00
Hanno Fleischer
43c0e3bcc7 Merge branch 'development' into 'dev/model'
Development

See merge request progproj/gruppen-ht24/Gruppe-01!35
2024-12-06 09:09:29 +00:00
Felix Koppe
95635f5fb7 Fix ownColor in gameView 2024-12-06 09:59:28 +01:00
Felix Koppe
e337b1f888 Fix card select issue 2024-12-06 09:57:28 +01:00
Felix Koppe
0237bcc4be Fix card select issue 2024-12-06 09:51:17 +01:00
Felix Koppe
26836d16cc Fix card select issue 2024-12-06 09:48:24 +01:00
Felix Koppe
a6c8cc33f4 Make cardSelect trigger needCconfirm 2024-12-06 09:31:02 +01:00
Felix Koppe
7f5f4b8c68 Fix broken sound 2024-12-06 09:24:49 +01:00
Felix Koppe
7f3483aa6b Add broken sound 2024-12-06 09:22:10 +01:00
Felix Koppe
8422b7be1e Merge branch 'development' into dev/client_koppe2 2024-12-06 09:11:09 +01:00
Felix Koppe
f0b23ab9c2 Readd broken files 2024-12-06 09:10:47 +01:00
Felix Koppe
78f1dbb3d3 Remove broken files 2024-12-06 09:08:52 +01:00
Daniel Grigencha
4904b32ea3 Updated 'ChoosePieceState' class.
Updated the 'ChoosePieceState' class by adding the 'RequestMoveMessage' handling to it.
2024-12-06 08:58:51 +01:00
Daniel Grigencha
b00219c4fb Updated 'PlayPowerCardState' class.
Updated the 'PlayPowerCardState' class by adding the 'AnimationEndMessage' handling to it.
2024-12-06 08:58:01 +01:00
Daniel Grigencha
12cf5f3e71 Updated 'PowerCardState' class.
Updated the 'PowerCardState' class by adding the 'SelectedPiecesMessage' handling to it.
2024-12-06 08:57:08 +01:00
Daniel Grigencha
77b0207214 Updated 'TurnState' class.
Updated the 'TurnState' class by adding the 'SelectedPiecesMessage', 'NoPowerCardMessage', 'RequestDieMessage' and 'ReuqestMoveMessage' handling to it.
2024-12-06 08:56:21 +01:00
Daniel Grigencha
a18165bc02 Updated 'Game' class.
Updated the 'Game' class by commenting out the creation of turbo and shield cards. This is only for testing purposes.
2024-12-06 08:55:01 +01:00
Felix Koppe
9e758e4417 Merge branch 'dev/model' into dev/client_koppe2 2024-12-06 08:11:37 +01:00
Daniel Grigencha
62ceff822f Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by adding logic for the roll again event to the 'RequestDieMessage' handling.
2024-12-06 04:54:14 +01:00
Hanno Fleischer
33afc4ab3b added selectDice method in RollDiceState 2024-12-06 04:48:05 +01:00
Daniel Grigencha
322d539cfd Merge branch 'dev/model' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/model 2024-12-06 04:28:40 +01:00
Daniel Grigencha
9c4f2387ee Updated 'FirstRollState', 'SecondRollState' and 'ThirdROllState' classes.
Updated the 'FirstRollState', 'SecondRollState' and 'ThirdROllState' class by adding the 'received(RequestDieMessage msg, int from)' method to them.
2024-12-06 04:28:33 +01:00
Daniel Grigencha
9d1430e488 Updated 'AnimationState' class.
Updated the 'AnimationState' class by setting the data type of 'messageReceived' from 'Map' to 'Set'.
2024-12-06 04:27:10 +01:00
Daniel Grigencha
dd2146d417 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by setting the data type of 'messageReceived' from 'Map' to 'Set'.
2024-12-06 04:26:38 +01:00
Daniel Grigencha
d9ad0f0a4b Updated 'LobbyState' class.
Updated the 'LobbyState' class by setting the first waiting piece on the start node.
2024-12-06 04:25:45 +01:00
Hanno Fleischer
0368ec8541 implemented the correct transition, when the player can play no powercard 2024-12-06 04:18:58 +01:00
Daniel Grigencha
72f0bc5a2f Updated 'RollDiceState' class.
Updated the 'RollDiceState' class by adding the 'received(RequestDieMessage msg, int from)' method to it.
2024-12-06 04:17:29 +01:00
Daniel Grigencha
23ae4a3080 Updated 'DetermineStartPlayaerState' class.
Updated the 'DetermineStartPlayerState' class by calling the right method of the 'entry' inside the 'RequestDieMessage' handling.
2024-12-06 04:14:42 +01:00
Daniel Grigencha
765b1884fe Updated 'AnimationState' class.
Updated the 'AnimationState' class by removing the unused 'DiceNowMessage' call from it.
2024-12-06 03:57:42 +01:00
Daniel Grigencha
e3febd6ba1 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by removing the 'rolls' attribute and its usage from it.
2024-12-06 03:49:39 +01:00
Daniel Grigencha
1a562a8d38 Updated 'DeterminStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by adding the multi roll support to it. In Addition, the server model will be updated correctly.
2024-12-06 03:29:11 +01:00
Daniel Grigencha
39ed4238b5 Updated 'AnimationState' class.
Updated the 'AnimationState' class by adding a missing semicolon.
2024-12-06 03:27:55 +01:00
Daniel Grigencha
620063e894 Updated 'AnimationState' class.
Updated the 'AnimationState' class by updating the 'messageReceived' attribute in it. In Addition, the 'AnimationEndMessage' handling was updated.
2024-12-06 03:22:25 +01:00
Daniel Grigencha
2d0788eb72 Updated 'TurnState' class.
Updated the 'TurnState' class by updating the imports in it.
2024-12-06 03:20:28 +01:00
Daniel Grigencha
8470a96908 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by updating the 'AnimationEndMessage' handling in it. In Addition, the 'RequestDieMessage' handling was updated as well.
2024-12-06 02:55:14 +01:00
Hanno Fleischer
a6205c982a now sending AnimationEndMessage when in Waitranking State 2024-12-06 02:40:16 +01:00
Daniel Grigencha
58b9298c91 Merge branch 'dev/model' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/model 2024-12-06 02:38:53 +01:00
Daniel Grigencha
1e6856744b Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by adding the 'received(AnimationEndMessage msg, int from)' method to it.
2024-12-06 02:38:46 +01:00
Hanno Fleischer
f713e00c36 made all 'PowerCards' serializable 2024-12-06 02:30:13 +01:00
Daniel Grigencha
81ae896ae8 Updated 'TurnState' class.
Updated the 'TurnState' class by removing the start state of this state machine of the constructor.
2024-12-06 02:22:46 +01:00
Daniel Grigencha
5b55d39c9a Updated 'AnimationState' class.
Updated the 'AnimationState' class by solving the transition error due to the separat state change.
2024-12-06 02:22:07 +01:00
Hanno Fleischer
f36e2ff7bb adjusted the getter for the card in the intro to get BonusCards instead of PowerCards 2024-12-06 02:16:58 +01:00
Hanno Fleischer
16afe95aa6 Merge branch 'dev/model' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/model 2024-12-06 02:13:08 +01:00
Hanno Fleischer
55d398b428 adjusted all useages of the SelectedPieces consturctor to transfer the right parameters 2024-12-06 02:12:59 +01:00
Daniel Grigencha
affa2ecd7e Added 'ChoosePowerCardState', 'ShieldCardState', 'SwapCardState' and 'TurboCardState' classes.
Updated the 'ChoosePowerCardState', 'ShieldCardState', 'SwapCardState' and 'TurboCardState' classes to this project. They will be used inside the power card state. In Addition, the abstract 'PowerCardAutomatonState' class was added.
2024-12-06 02:11:32 +01:00
Hanno Fleischer
0a1bd1f503 adjusted all State containing 'received(PlayCardMessage msg)' to work with the new message 2024-12-06 02:04:55 +01:00
Daniel Grigencha
a2867fc88a Added 'ServerCardVisitor' class.
Added the 'ServerCardVisitor' class to this project. It will be used as a visitor on the server to differentiate between all types of power cards.
2024-12-06 01:41:45 +01:00
Daniel Grigencha
c6761d91d1 Updated 'PowerCardState' class.
Updated the 'PowerCardState' class by updating the content of the 'enter' in it.
2024-12-06 01:37:24 +01:00
Daniel Grigencha
5708ee6ffe Updated 'PlayCardMessage' class.
Updated the 'PlayCardMessage' class by removing the 'ownPieceID' and 'enemyPieceID' attributes and their getter methods from it. In Addition, the 'pieces' attribute and its getter method was added.
2024-12-06 01:28:44 +01:00
Hanno Fleischer
d61b68aa41 overrode hachCode and equals method of Piece 2024-12-06 01:15:06 +01:00
Daniel Grigencha
e98418b274 Merge branch 'dev/model' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/model 2024-12-06 01:06:20 +01:00
Daniel Grigencha
66dc9c02ea Updated 'PowerCardState' class.
Updated the 'PowerCardState' class by adding the 'currentState', 'choosePowerCardState', 'shieldCardState', 'swapCardState' and 'turboCardState' attributes and its getter methods to it. In Addition, logic was written into the 'enter' method.
2024-12-06 00:53:11 +01:00
Daniel Grigencha
dd95356abd Updated 'TurnState' class.
Updated the 'TurnState' class by adding the 'player' attribute and its getter method to it.
2024-12-06 00:50:48 +01:00
Daniel Grigencha
84776c71b2 Updated 'SelectedPiecesMessage' class.
Updated the 'SelectedPiecesMessage' class by removing 'pieceIdentifier' attribute and its getter method from it. In Addition, the 'pieces' attribute and its getter method was added.
2024-12-06 00:47:33 +01:00
Daniel Grigencha
d07eee6251 Updated 'Piece' class.
Updated the 'Piece' class by overwriting the 'toString' method in it.
2024-12-06 00:45:56 +01:00
Hanno Fleischer
b601ff2cf7 renamed 'PossibleCardMEssage' to 'PossibleCardsMessage' and completed JavaDocs for method 'getPowerCardByType' in 'Player' 2024-12-06 00:36:06 +01:00
Hanno Fleischer
04119d2f3e modified 'ChoosePowerCardState' to work with PowerCards as well as adjusted 'SelectcardMessage' to use 'PowerCards' 2024-12-06 00:31:12 +01:00
Hanno Fleischer
a92c06a70e added a method in 'Player' to return the first 'PowerCard' of a specific Type 2024-12-06 00:30:08 +01:00
Daniel Grigencha
50f9c0ef0c Updated 'PossibleCardMessage' class.
Updated the 'PossibleCardMessage' class by replacing all 'BonusCard' with 'PowerCard' in it.
2024-12-06 00:19:11 +01:00
Daniel Grigencha
f7f246daaa Merge branch 'dev/model' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/model 2024-12-06 00:16:42 +01:00
Daniel Grigencha
8a738a3633 Updated 'Game' class.
Updated the 'Game' class by replacing all 'BonusCard' with 'PowerCard' in it.
2024-12-06 00:16:38 +01:00
Daniel Grigencha
3a32a7ebf7 Updated 'Player' class.
Updated the 'Player' class by replacing all 'BonusCard' with 'PowerCard' in it.
2024-12-06 00:11:50 +01:00
Daniel Grigencha
6894802c00 Added 'HiddenCard', 'ShieldCard', 'SwapCard' and 'TurboCard' classes.
Updated the 'HiddenCard', 'ShieldCard', 'SwapCard' and 'TurboCard' classes to this project. They will be used to display different types of power cards.
2024-12-06 00:04:21 +01:00
Daniel Grigencha
a09211da5f Updated abstract 'PowerCard' class.
Updated the abstract 'PowerCard' class by setting the 'card' attribute to 'protected' in it.
2024-12-06 00:03:16 +01:00
Daniel Grigencha
c9c9c5dcf6 Added abstract 'PowerCard' class.
Added the abstract 'PowerCard' class to this project. It will be used to display different types of hand cards.
2024-12-05 23:58:43 +01:00
Daniel Grigencha
1e46b1dc59 Added 'Visitor' interface.
Added the 'Visitor' interface to this project. It will be used to handle all types of power cards.
2024-12-05 23:54:55 +01:00
Hanno Fleischer
6c74acc334 added a getter for the BonusCrad in PowerCard 2024-12-05 23:07:22 +01:00
Hanno Fleischer
421231aa12 made BonusCard Serializable and added the method calls to display handcards during the intro 2024-12-05 22:46:48 +01:00
Daniel Grigencha
d8bb458e9c Updated 'LobbyState' class.
Updated the 'LobbyState' class by updating the 'initializeGame' method in it.
2024-12-05 23:45:24 +01:00
Hanno Fleischer
f0080118d0 Changed and :
removed the selectAnimationEnd Method from Waitranking and moved logic into the receivedActivePlayerMessage. Added in selectStart to always send a StartGameMessage in order to trigger incorrectRequestMEssage
2024-12-05 22:34:53 +01:00
Daniel Grigencha
8a438ab069 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by updating the whole start player determination process in it.
2024-12-05 23:29:21 +01:00
Daniel Grigencha
0ce8184069 Updated 'Game' class.
Updated the 'Game' class by adding the 'draw' method to it. In Addition, the 'initializeDrawPile' method was updated by shuffling the 'drawPile' attribute after filling it.
2024-12-05 23:27:06 +01:00
Felix Koppe
2c524477d7 Merge branch 'dev/model' into dev/client_koppe2 2024-12-05 22:18:00 +01:00
Felix Koppe
587af466e8 Merge branch 'development' into dev/client_koppe2
# Conflicts:
#	Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java
2024-12-05 22:17:53 +01:00
Felix Koppe
8d398450f1 Minor changes 2024-12-05 22:15:59 +01:00
Felix Koppe
0a96dd6f9f Repair sounds 2024-12-05 22:07:36 +01:00
Felix Koppe
e8a556de27 Add missle 2024-12-05 22:02:02 +01:00
Hanno Fleischer
1214d3c87c added an getter for the error id in the IncorrectrequestMessage and implemeted how to handle it in the client game logic. 2024-12-05 21:11:40 +01:00
Daniel Grigencha
f2c34aee2d Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by updating the 'received(RequestDieMessage msg, int from)' method in it.
2024-12-05 22:03:48 +01:00
Daniel Grigencha
2da1fec7dd Updated 'RankingResponseMessage' class.
Updated the 'RankingResponseMessage' class by removing the 'startingPlayerId' attribute and its getter method from it. In Addition the 'rankingResults' attribute and its getter method were added.
2024-12-05 22:01:05 +01:00
Daniel Grigencha
5d76a89b95 Updated 'LobbyState' class.
Updated the 'LobbyState' class by updating the 'JoinedLobbyMessage' and 'LobbyReadyMessage' handlings in it.
2024-12-05 21:58:20 +01:00
Daniel Grigencha
60ebef3518 Updated 'LobbyState' class.
Updated the 'LobbyState' class by adding an 'IncorrectRequestMessage' to the start game handling.
2024-12-05 19:58:23 +01:00
Felix Koppe
a399b14291 Improve interrupt 2024-12-05 18:11:57 +01:00
Felix Koppe
9d21e2ce87 Merge commit 2024-12-05 18:00:27 +01:00
Felix Koppe
2255bfd648 Remove test binding 2024-12-05 18:00:04 +01:00
Hanno Fleischer
74194d8514 Merge branch 'development' into 'dev/model'
Development

See merge request progproj/gruppen-ht24/Gruppe-01!34
2024-12-05 16:58:50 +00:00
Fleischer Hanno
cabd98a24a adjusted a broadcast in lobby state to a send to reduce traffic (original commit f1124f32) and removed souts as well as added comments back into the code 2024-12-05 17:51:18 +01:00
Cedric Beck
f3816cb2a5 added particle_cir.png 2024-12-05 17:49:56 +01:00
Cedric Beck
997c4c589e removed setOwnColor 2024-12-05 17:23:15 +01:00
Cedric Beck
d14a0aef86 fixed ownColor sync problem 2024-12-05 17:22:37 +01:00
Felix Koppe
ac5d7ed74b Add sounds 2024-12-05 17:11:07 +01:00
Felix Koppe
f1124f3245 Fix missing ready update from server on join of new player in LobbyState 2024-12-05 16:38:09 +01:00
Felix Koppe
fc4a357e9e Merge branch 'dev/model' into dev/client_koppe2 2024-12-05 16:25:23 +01:00
Felix Koppe
a8b02faa96 Merge branch 'development' into dev/client_koppe2
# Conflicts:
#	Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java
2024-12-05 16:25:11 +01:00
Felix Koppe
4a7c23708c Add jetAnimation 2024-12-05 16:21:13 +01:00
Hanno Fleischer
4478291852 added the movement of pieces in the intro state to be also done in the model 2024-12-05 15:22:38 +01:00
Hanno Fleischer
0622c35303 fixed state transitions and implemented the Intro state to move the pieces to the correct start setup
added some logic so that the client only transitions to the intro state when the animation has finished at it received the new active Player, and after animating the setup it switches to the corresponding state baserd on  the active player and displays the now new active player.
2024-12-05 14:02:02 +01:00
Daniel Grigencha
3b0cd9ebdb Updated the JavaDocs in multiple classes, to improve readability. 2024-12-05 05:21:33 +01:00
Daniel Grigencha
e81aa67d36 Updated the JavaDocs in multiple classes, to improve readability. 2024-12-05 05:09:15 +01:00
Daniel Grigencha
4fb848420b Updated the 'DisconnectedMessage' class.
Updated the 'DisconnectedMessage' class by adding JavaDocs.
2024-12-05 05:03:39 +01:00
Daniel Grigencha
07a833afe7 Updated the 'ClientInterpreter' interface.
Updated the 'ClientInterpreter' interface by adjusting the JavaDocs.
2024-12-05 05:00:29 +01:00
Daniel Grigencha
6576250113 Updated the 'Player' class.
Updated the 'Player' class by adjusting the JavaDocs and writing the logic for the 'isFinished()' method.
2024-12-05 04:54:12 +01:00
Daniel Grigencha
154efccf31 Updated 'Game' class.
Updated the 'Game' class by adjusting the JavaDocs and rewriting the constructor for maintainability and readability.
2024-12-05 04:52:59 +01:00
Daniel Grigencha
f90aed7bbb Updated 'Color' enum.
Updated the 'Color' enum by adjusting the JavaDocs and adding a new static method 'getColor(int)'
2024-12-05 04:51:55 +01:00
Daniel Grigencha
3a86837307 Updated the JavaDocs in multiple classes, to improve readability. 2024-12-05 04:50:34 +01:00
Daniel Grigencha
da0756452c Updated the JavaDocs in multiple classes, to improve readability. 2024-12-05 04:49:49 +01:00
Daniel Grigencha
bfe8a20f92 Updated 'Board' class.
Updated the 'Board' class by rewriting the constructor, to make it maintainable and scalable.
2024-12-05 04:46:01 +01:00
Felix Koppe
0e6a2499b7 Fix fullscreen issue 2024-12-04 18:38:35 +01:00
Hanno Fleischer
354cdc0a9c added Intro state and its logic 2024-12-04 17:03:57 +01:00
Felix Koppe
00d86c5c10 Improve ceremonyView 2024-12-04 15:33:34 +01:00
Felix Koppe
990e476753 Add Q/E rotation 2024-12-04 15:27:33 +01:00
Felix Koppe
71fc08a05c Add interrupt 2024-12-04 15:11:31 +01:00
Cedric Beck
9e1ca584c7 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-04 13:56:08 +01:00
Cedric Beck
9199fbffd8 removed tests, fixed null exception in CardLayer 2024-12-04 13:56:03 +01:00
Felix Koppe
5c71531277 Merge branch 'development' into dev/client_koppe2 2024-12-04 13:43:33 +01:00
Felix Koppe
6f7c5346d2 Merge commit 2024-12-04 13:34:14 +01:00
Felix Koppe
ef1ce63db6 Try to make server stop on leave 2024-12-04 13:33:39 +01:00
Hanno Fleischer
e8d1442e5b made the model checkstyle compliant 2024-12-04 12:10:48 +01:00
Hanno Fleischer
bdacc4aad3 created the Intro state and renamed all packages to be checkstyle compliant 2024-12-04 12:08:38 +01:00
Cedric Beck
44ef21e6af added handCard Num to playerName display + added remove card in guiHandler 2024-12-04 11:38:35 +01:00
Hanno Fleischer
8e8104b672 forgot to add StartGameMessage in previous commit 2024-12-04 11:10:26 +01:00
Hanno Fleischer
de899cef35 removed from every Message the getInfoTest method and only overrode the toString method in messages with content, in every other message it is handled through the parent class 2024-12-04 11:07:55 +01:00
Felix Koppe
11d6dd4500 Improve video dialog 2024-12-04 10:00:41 +01:00
Felix Koppe
d71f824ca6 Add fullscreen option 2024-12-04 09:41:08 +01:00
Felix Koppe
8e6cb27662 Fix lobbyView ready behavior on tskChange 2024-12-04 07:53:18 +01:00
Daniel Grigencha
29711d6210 Updated 'ClientStartGameMessage' class.
Updated the 'ClientStartGameMesssage' class by preparing it for the correct BPMN diagram.
2024-12-04 02:46:07 +01:00
Daniel Grigencha
29d8e791f6 Updated 'Player' class.
Updated the 'Player' class by adding the 'setPieceInHome' method to it.
2024-12-04 02:44:28 +01:00
Daniel Grigencha
4440341f79 Updated 'LobbyState' class.
Updated the 'LobbyState' class by updating the creation of the 'ServerStartGameMessage' object. In Addition, the start process if all players are ready was removed.
2024-12-04 02:44:03 +01:00
Daniel Grigencha
de5c8bf44c Updated 'Game' class.
Updated the 'Game' class by adding the 'getPlayersAsList' method to it.
2024-12-04 02:43:29 +01:00
Daniel Grigencha
ab5cece1b3 Updated 'StartGameMessage' class.
Updated the 'StartGameMessage' class by removing the unused 'forceStartGame' attribute from it.
2024-12-04 02:42:57 +01:00
Daniel Grigencha
b8ed5060d6 Updated 'Board' class.
Updated the 'Board' class by removing the 'playerData' attribute and its getter method from it.
2024-12-04 02:42:16 +01:00
Daniel Grigencha
c0b72ae4da Updated 'ServerStartGameMessage' class.
Updated the 'ServerStartGameMessage' class by adding the 'players' attribute and its getter method to it.
2024-12-04 02:41:44 +01:00
Daniel Grigencha
c1b4caa82b Updated 'LobbyState' class.
Updated the 'LobbyState' class by updating the 'received(ServerStartGameMessage msg)' method in it after updating the 'ServerStartGameMessage' class.
2024-12-04 02:22:58 +01:00
Daniel Grigencha
a757158477 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by removing the serializer registration in it.
2024-12-04 02:09:20 +01:00
Daniel Grigencha
964ff87b11 Updated client states.
Updated the client states by removing all references to the 'PlayerData' class.
2024-12-04 01:47:31 +01:00
Hanno Fleischer
7053b163e5 adjusted LobbyState in the client to use the correct Data 2024-12-03 18:19:55 +01:00
Hanno Fleischer
81cb2f33ff adjusted all constuctors of nodes so that if someone creates a node the piece will be null and the option for a constuctor without arguments is still given for serialization purposes 2024-12-03 17:56:39 +01:00
Hanno Fleischer
69865bb504 added the playeringamenotification to be created from the right dataset 2024-12-03 16:48:08 +01:00
Felix Koppe
db50986f3f Fix serialisation issue 2024-12-03 15:38:13 +01:00
Felix Koppe
a0a088a0c4 Fix minor error in notification processing 2024-12-03 15:00:00 +01:00
Cedric Beck
c4d11ff961 added window title 2024-12-03 09:15:14 +01:00
Daniel Grigencha
bb51976127 Updated 'Node' class.
Updated the 'Node' class by overload the 'isOccupied' method in it.
2024-12-03 04:57:30 +01:00
Daniel Grigencha
0db1f08f3c Updated abstract 'GameAutomatonState' class.
Updated the abstract 'GameAutomatonState' class by adding the 'getGameAutomaton' method to it.
2024-12-03 04:49:16 +01:00
Daniel Grigencha
336f1ec316 Updated 'Resources' class.
Updated the 'Resources' class by adding the 'MAX_EYES' constant to it.
2024-12-03 04:29:16 +01:00
Daniel Grigencha
a1e687912a Updated abstract 'TurnAutomatonState' class.
Updated the abstract 'TurnAutomatonState' class by adding the 'getTurnAutomaton' method to it.
2024-12-03 04:09:35 +01:00
Daniel Grigencha
2248d044c1 Updated 'AnimationState' class.
Updated the 'AnimationState' class by updating the content inside the 'received(AnimationEndMessage msg, int from) method in it.
2024-12-03 03:41:58 +01:00
Daniel Grigencha
79bf1c16e8 Updated 'Game' class.
Updated the 'Game' class by adding the 'getActivePlayerId' method to it.
2024-12-03 03:40:24 +01:00
Daniel Grigencha
3353a890d3 Updated 'Game' class.
Updated the 'Game' class by adding the 'getPlayerIdByColor' method to it.
2024-12-03 03:36:22 +01:00
Daniel Grigencha
a012402a85 Updated abstract 'TurnAutomatonState' class.
Updated the abstract 'TurnAutomatonState' class by updating the JavaDoc text of the constructor.
2024-12-03 02:15:10 +01:00
Daniel Grigencha
5aaf8d4850 Updated 'TurnState' class.
Updated the 'TurnState' class by setting the start state in it.
2024-12-03 01:31:22 +01:00
Daniel Grigencha
35ab777f04 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by fixing the logic inside the received(RequestDieMessage msg, int from)' method in it.
2024-12-03 01:30:39 +01:00
Daniel Grigencha
c707abc465 Updated 'Die' class.
Updated the 'Die' class by adding another constructor for test cases to it.
2024-12-03 01:04:02 +01:00
Daniel Grigencha
2a84e7cf65 Updated 'Player' class.
Updated the 'Player' class by moving all content of 'PlayerData' class in 'Player' class.
2024-12-03 00:51:45 +01:00
Daniel Grigencha
3a02edb944 Updated 'PlayerData' class.
Updated the 'PlayerData' class by updating the 'Piece' creation inside the constructor.
2024-12-03 00:50:53 +01:00
Daniel Grigencha
1870d4fe0e Updated 'ShieldState' enumeration.
Updated the 'ShieldState' enumeration by removing unused methods from it.
2024-12-03 00:49:09 +01:00
Daniel Grigencha
5cf9746931 Updated 'PieceState' enumeration.
Updated the 'PieceState' enumeration by removing unused methods from it.
2024-12-03 00:48:39 +01:00
Daniel Grigencha
5b9bc7aa36 Updated 'Piece' class.
Updated the 'Piece' class by removing the unused 'id' parameter from the constructor.
2024-12-03 00:38:24 +01:00
Daniel Grigencha
abe66aff5d Updated 'LobbyState' class.
Updated 'LobbyState' class by removed unused imports in it.
2024-12-03 00:37:24 +01:00
Fleischer Hanno
eea566cc8b added the logic for server shutdown 2024-12-02 23:25:54 +01:00
Daniel Grigencha
bd07a44607 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 23:24:02 +01:00
Daniel Grigencha
1f64676d31 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by adding the 'shutdown' message to it.
2024-12-02 23:23:56 +01:00
Daniel Grigencha
838f59b9aa Updated 'ServerState' class.
Updated the 'ServerState' class by filling the 'received(LeaveGameMessage msg, int from)' in it.
2024-12-02 23:22:45 +01:00
Daniel Grigencha
002a42be38 Updated 'LobbyState' class.
Updated the 'LobbyState' class by removing the 'received(LeaveGameMessage msg, int from)' from it.
2024-12-02 23:21:42 +01:00
Daniel Grigencha
a1d10521ac Updated 'ServerSener' interface.
Updated the 'ServerSender' interface by adding the 'shutdown' method to it.
2024-12-02 23:14:29 +01:00
Fleischer Hanno
4e6a272e7a added that when the client is in the game state and recieves the ceremony message it always changes to ceremony state 2024-12-02 23:08:56 +01:00
Daniel Grigencha
516848a67e Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 23:06:49 +01:00
Daniel Grigencha
659d69d3eb Updated 'GameState' class.
Updated the 'GameState' class by sending a broadcast message after a player left the game and only one player is remaining.
2024-12-02 23:06:44 +01:00
Daniel Grigencha
fb6cbeaaf5 Updated 'ServerStartGameMessage' class.
Updated the 'ServerStartGameMessage' class by adding the 'board' attribute and its getter method to it.
2024-12-02 23:00:39 +01:00
Cedric Beck
25f750c8b6 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 22:58:58 +01:00
Cedric Beck
3f49b432c4 added SSAO and FXAA 2024-12-02 22:58:52 +01:00
Daniel Grigencha
252c37ae9a Updated 'LobbyState' class.
Updated the 'LobbyState' class by sending the 'ServerStartGameMessage' with a 'Board' object.
2024-12-02 22:58:46 +01:00
Fleischer Hanno
4566d4c9a8 Merge remote-tracking branch 'origin/development' into development 2024-12-02 22:46:12 +01:00
Fleischer Hanno
e9ba888651 changed the logic so that the isHost is not decided by the client and instead by the server 2024-12-02 22:45:44 +01:00
Cedric Beck
cbbb98037b Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 22:43:16 +01:00
Cedric Beck
aa651ec62f added trees 2024-12-02 22:43:03 +01:00
Daniel Grigencha
e163b87cc4 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by updating the logic inside the 'connectionAdded' method in it.
2024-12-02 22:41:17 +01:00
Daniel Grigencha
1eb24b7a66 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by updating the logic inside the 'connectionAdded' method in it.
2024-12-02 22:37:46 +01:00
Daniel Grigencha
492f7422f5 Updated 'LobbyAcceptMessage' class.
Updated the 'LobbyAcceptMessage' class by adding the 'host' attribute and its getter method to it.
2024-12-02 22:36:48 +01:00
Daniel Grigencha
27f8af70f5 Updated 'Game' class.
Updated the 'Game' class by setting the default value of 'host' attribute. In Addition, the 'isHost' method was added.
2024-12-02 22:34:45 +01:00
Fleischer Hanno
5910fcc701 added the client logic to receive the LobbyAccept and LobbyDeny message 2024-12-02 21:52:07 +01:00
Felix Koppe
e94ed1e019 Fix syntax error 2024-12-02 21:51:09 +01:00
Felix Koppe
7d54a906dd Add some more names 2024-12-02 21:48:23 +01:00
Felix Koppe
5ae65921bf Add more random names 2024-12-02 21:42:12 +01:00
Daniel Grigencha
468e4005dc Updated 'LobbyState' class.
Updated the 'LobbyState' class by sending a broadcast update the new ready state of the client.
2024-12-02 21:23:53 +01:00
Daniel Grigencha
72321eab9a Updated 'LobbyState' class.
Updated the 'LobbyState' class by updating all received methods in it.
2024-12-02 21:22:49 +01:00
Daniel Grigencha
951c92d890 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 21:12:03 +01:00
Felix Koppe
e87eb569c2 Add showInfo to MdgaView 2024-12-02 21:16:50 +01:00
Felix Koppe
baa967ecfc Merge commit 2024-12-02 21:12:56 +01:00
Felix Koppe
8d39d61c71 Add infoNotification 2024-12-02 21:12:43 +01:00
Daniel Grigencha
7fcee3cac0 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by register the 'IncorrectRequestMessage' class to the serializer.
2024-12-02 21:11:59 +01:00
Daniel Grigencha
06d4b322e7 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 21:04:41 +01:00
Daniel Grigencha
1cf14f65bb Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by updating the 'connectionAdded' method in it. In Addition, the JavaDoc text for this method was addded.
2024-12-02 21:04:36 +01:00
Fleischer Hanno
ebb9f839c7 added JavaDocs in Resources.java 2024-12-02 20:58:00 +01:00
Daniel Grigencha
3eef4b2a02 Updated 'PlayerData' class.
Updated the 'PlayerData' class by replacing the magic constants with the 'Resources' class. In Addition, some JavaDoc texts were updated.
2024-12-02 20:56:59 +01:00
Daniel Grigencha
c1fa679261 Updated 'Resources' class.
Updated the 'Resources' class by adding the 'MAX_PIECES' constant to it.
2024-12-02 20:45:07 +01:00
Daniel Grigencha
c48f924ead Updated 'Resources' class.
Updated the 'Resources' class by adding the 'MAX_PLAYERS' constant to it.
2024-12-02 20:43:47 +01:00
Fleischer Hanno
73859d8c81 added methods for getting Boolean, String, Double and int 2024-12-02 20:34:52 +01:00
Felix Koppe
1918aa80ff Merge commit 2024-12-02 20:33:36 +01:00
Felix Koppe
d062b9dabc Add forceStartGameButton to host in lobby 2024-12-02 20:32:46 +01:00
Fleischer Hanno
7ddcdc3f48 added the first error.messages and adjusted 2024-12-02 20:24:17 +01:00
Fleischer Hanno
2cefc2c293 Merge remote-tracking branch 'origin/development' into development 2024-12-02 20:19:44 +01:00
Daniel Grigencha
c4304ae99a Updated 'Game' class.
Updated the 'Game' class by removing the 'allReady' attribute in it. In Addtion, the 'areAllReady' method was added.
2024-12-02 20:19:13 +01:00
Fleischer Hanno
005df94114 added Resources calss to access teh properties 2024-12-02 19:55:18 +01:00
Daniel Grigencha
44f893ccef Updated 'Game' class.
Updated the 'Game' class by setting the 'die' attribute correctly inside the constructor.
2024-12-02 19:45:11 +01:00
Daniel Grigencha
0d9a922f55 Removed 'PlayerDataMessage' and 'StartBriefingMessage'. 2024-12-02 19:16:01 +01:00
Daniel Grigencha
289158cf35 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by removing the 'PlayerDataMessage' and 'StartBriefingMessage' from the serializer.
2024-12-02 19:14:11 +01:00
Fleischer Hanno
0a0762b6c9 removed all instances of PlayerDataMEssage and StartBriefingMessage 2024-12-02 19:07:58 +01:00
Fleischer Hanno
90a21087df added logic for incorrectRequest message and removed messages playerdata and startbriefing and created javadocs 2024-12-02 19:02:00 +01:00
Daniel Grigencha
294ecdc56f Updated 'IncorrectRequestMessage' class.
Updated the 'IncorrectRequestMessage' class by updating the content inside the 'accept' method in it.
2024-12-02 19:00:21 +01:00
Daniel Grigencha
347ed152b8 Added 'IncorrectRequestMessage' class.
Added the 'IncorrectRequestMessage' class to this project. It will be used to send the client an incorrect request message to show they did something wrong.
2024-12-02 18:58:15 +01:00
Daniel Grigencha
3daafde9f1 Updated 'ServerInterpreter' class.
Updated the 'ServerInterpreter' class by adding the 'received(IncorrectRequestMessage msg)' method to it.
2024-12-02 18:50:42 +01:00
Daniel Grigencha
09fda6b167 Updated 'Game' class.
Updated the 'Game' class by adding the 'isColorTaken' method to it.
2024-12-02 18:18:04 +01:00
Cedric Beck
f21fd9b0a6 added setPauseOnLostFocus(false) 2024-12-02 17:28:37 +01:00
Cedric Beck
41e204b1f2 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 17:12:00 +01:00
Cedric Beck
208261c6bf added clickDice Action + modelSync 2024-12-02 17:11:55 +01:00
Hanno Fleischer
0411f2ead4 fixed state transitions in gamestateclient automaton 2024-12-02 17:08:46 +01:00
Cedric Beck
eb819d4d5e merge development 2024-12-02 16:49:37 +01:00
Cedric Beck
a2856bb157 added rollRankingResults 2024-12-02 16:47:28 +01:00
Daniel Grigencha
bcf17a0651 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 16:35:49 +01:00
Daniel Grigencha
eee6fccde5 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by setting the start state correctly and enter it.
2024-12-02 16:35:40 +01:00
Felix Koppe
2118c72891 Adjust RollDiceNotification 2024-12-02 16:34:55 +01:00
Daniel Grigencha
cb362c4d0c Used auto-reformate code. 2024-12-02 16:31:57 +01:00
Daniel Grigencha
1bcb73cff7 Updated 'PlayerData' class.
Updated the 'PlayerData' class by adding the 'isFinished' method to it. In Addition, the empty constructor was optimized to initalize all class attributes for serializable cases.
2024-12-02 16:29:32 +01:00
Daniel Grigencha
167d898a3c Updated 'StartNode' class.
Updated the 'StartNode' class by reverting all enumerations to its origin types in it.
2024-12-02 16:25:33 +01:00
Daniel Grigencha
6c136b78b8 Updated 'Piece' class.
Updated the 'Piece' class by reverting all enumeration to its orgin types.
2024-12-02 16:22:50 +01:00
Felix Koppe
82234a7ff9 Merge commit 2024-12-02 15:44:41 +01:00
Felix Koppe
92d2e74748 Fix error regarding color in lobbyState 2024-12-02 13:06:59 +01:00
Felix Koppe
4561a962d4 Fix lobby isSelf logic and no longer assign color on join 2024-12-02 12:52:07 +01:00
Hanno Fleischer
5db7b64cef fixed bug with seriliazation of Board, now sending playerdata seperate from teh board 2024-12-02 12:16:53 +01:00
Felix Koppe
bb1b721e77 Fix lobby in serverAutomaton and adjust TskUpdateMessage 2024-12-02 11:48:54 +01:00
Cedric Beck
206cad2f79 removed test card 2024-12-02 11:01:02 +01:00
Cedric Beck
3717e7b794 added javadocs to board 2024-12-02 03:48:11 +01:00
Cedric Beck
a15d7932d4 fixed bug 2024-12-02 03:15:18 +01:00
Cedric Beck
d649e41e75 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 03:13:48 +01:00
Cedric Beck
6d0552f5a7 added javadocs to animation; moved outline package 2024-12-02 03:13:42 +01:00
Fleischer Hanno
47f9f46277 Merge remote-tracking branch 'origin/development' into development 2024-12-02 03:03:47 +01:00
Felix Koppe
b87f5de5fb Fix error 2024-12-02 03:03:54 +01:00
Fleischer Hanno
c434bcb684 minor changes 2024-12-02 03:03:36 +01:00
Daniel Grigencha
b9617c0a14 Merge remote-tracking branch 'origin/development' into development 2024-12-02 02:53:05 +01:00
Daniel Grigencha
0aa73ca6ee added the received method for a Shutdown Message 2024-12-02 02:52:48 +01:00
Cedric Beck
fbc7246037 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 02:52:41 +01:00
Cedric Beck
2703084df1 added move/swap/throw animation; reworked waitingNodes logic in BoardHandler 2024-12-02 02:51:52 +01:00
Daniel Grigencha
31b1d535ac added a new 'ShutdownMessage' for the server 2024-12-02 02:32:50 +01:00
Daniel Grigencha
b3fb2f8fa4 added a new 'ShutdownMessage' for the server 2024-12-02 02:31:55 +01:00
Daniel Grigencha
8fcac9b809 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 02:15:29 +01:00
Daniel Grigencha
6985d988f4 Updated 'DetermineStartPlayerState' class.
Updated the 'DetermineStartPlayerState' class by updating the logic inside the 'received(RequestDieMessage msg, int from)' method in it.
2024-12-02 02:15:22 +01:00
Fleischer Hanno
6c3103b2ed fixing serialization error 2024-12-02 02:14:45 +01:00
Fleischer Hanno
3658c88d72 made all enums look like the color enum 2024-12-02 02:11:40 +01:00
Fleischer Hanno
bfc812b003 minas please help no serialization 2024-12-02 02:07:02 +01:00
Felix Koppe
413f35d7bf Merge commit 2024-12-02 01:59:49 +01:00
Felix Koppe
c5cb3d4dd0 Add restart on resolution change 2024-12-02 01:59:37 +01:00
Daniel Grigencha
efb4439431 Updated 'Game' class.
Updated the 'Game' class by adding the 'setDie' method to it.
2024-12-02 01:53:10 +01:00
Felix Koppe
601366f08d Merge commit 2024-12-02 01:40:58 +01:00
Felix Koppe
918c1f2a8a Add videoSettings 2024-12-02 01:40:45 +01:00
Daniel Grigencha
bf75b8afc9 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 01:39:07 +01:00
Daniel Grigencha
0b45727bd7 Updated 'Die' class.
Updated the 'Die' class by adding a third constructor for test cases.
2024-12-02 01:39:01 +01:00
Daniel Grigencha
a19ac2fc51 Updated 'GameState' class.
Updated the 'GameState' class by changing the data types of 'determineStartPlayerState, 'animationState' and 'turnState' attributes in it.
2024-12-02 01:27:21 +01:00
Daniel Grigencha
79f0e55c52 default value of playerData in 'Board' 2024-12-02 01:19:41 +01:00
Fleischer Hanno
44378486d4 Merge remote-tracking branch 'origin/development' into development 2024-12-02 01:17:14 +01:00
Fleischer Hanno
149931d2cb fixed PlayerData mistake 2024-12-02 01:16:33 +01:00
Daniel Grigencha
1250500558 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 01:01:49 +01:00
Daniel Grigencha
177bfe3001 Updated 'DetermineStartPlayerState' class.
Updated the 'DeterminStartPlayerState' class by removing the 'getDiceResults' method from it. In Addition, the logic inside the 'received(RequestDieMessage msg, int from)' was added.
2024-12-02 01:01:42 +01:00
Felix Koppe
138444439d Merge remote-tracking branch 'origin/development' into development 2024-12-02 00:53:58 +01:00
Felix Koppe
702f96b8db Add videoSettings 2024-12-02 00:53:11 +01:00
Fleischer Hanno
1b6407b75b fixed bug 2024-12-02 00:52:28 +01:00
Fleischer Hanno
c649b8f3ae creating MapCreation Notification 2024-12-02 00:44:11 +01:00
Daniel Grigencha
41a669d44d Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 00:42:17 +01:00
Daniel Grigencha
4b6b12c0ac Updated 'Board' class.
Updated the 'Board' class by adding the 'addPlayerData' method to it.
2024-12-02 00:42:07 +01:00
Daniel Grigencha
c1280ba089 Updated 'LobbyState' class.
Updated the 'LobbyState' class by adding 'initializeGame' method to it.
2024-12-02 00:40:21 +01:00
Felix Koppe
02f285a258 Remove cheat button 2024-12-02 00:39:58 +01:00
Daniel Grigencha
bcb0ebc0f8 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 00:29:21 +01:00
Daniel Grigencha
a19902f819 Updated 'LobbyState' class.
Updated the 'LobbyState' class by changing the method call 'isActive' to 'isReady'.
2024-12-02 00:28:59 +01:00
Fleischer Hanno
585687055c Merge remote-tracking branch 'origin/development' into development 2024-12-02 00:28:41 +01:00
Fleischer Hanno
a86319082c included the LobbyPlayerJoined MEssage parameter of isHost 2024-12-02 00:28:04 +01:00
Daniel Grigencha
7acc55fe25 Updated 'MdgaServer' class.
Updated the 'MdgaServer' class by saving the host into the 'Game' class.
2024-12-02 00:28:01 +01:00
Daniel Grigencha
3e56de2a17 Updated 'LobbyState' class.
Updated the 'LobbyState' class by changing the method call 'isActive' to 'isReady'.
2024-12-02 00:23:03 +01:00
Daniel Grigencha
468cd97374 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-02 00:20:41 +01:00
Daniel Grigencha
dee6bf9f9c Updated 'Game' class.
Updated the 'Game' class by adding the 'host' attribute and its getter method to it.
2024-12-02 00:19:46 +01:00
Daniel Grigencha
ea6431faa4 Updated 'LobbyPlayerJoinedMessage' class.
Updated the 'LobbyPlayerJoinedMessage' class by adding the 'host' attribute and its getter method to.
2024-12-02 00:12:46 +01:00
Cedric Beck
f272fd6f08 fixed bug again 2024-12-02 00:10:58 +01:00
Cedric Beck
bfc74ee126 fixed bug 2024-12-02 00:04:48 +01:00
Cedric Beck
d890d11978 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:58:57 +01:00
Daniel Grigencha
9af20346f1 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:58:44 +01:00
Cedric Beck
f853b0b54f Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:57:59 +01:00
Cedric Beck
9dd2d3f58b added finish text; edited Notifications 2024-12-01 23:57:53 +01:00
Daniel Grigencha
06e43903e6 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:57:39 +01:00
Fleischer Hanno
00a04c2403 fixed a bug 2024-12-01 23:57:39 +01:00
Daniel Grigencha
92cbd9202a Updated 'LobbyState' class.
Updated the 'LobbyState' class by fixing the error by sending the 'ServerStartGameMessage' after all players are ready.
2024-12-01 23:57:26 +01:00
Felix Koppe
9a06afe998 Merge commit 2024-12-01 23:56:33 +01:00
Felix Koppe
289390c528 Add comment 2024-12-01 23:56:24 +01:00
Fleischer Hanno
7712a23d00 added transition from Lobby to Game 2024-12-01 23:50:37 +01:00
Fleischer Hanno
8137a727f5 Merge remote-tracking branch 'origin/development' into development 2024-12-01 23:49:30 +01:00
Felix Koppe
d904a28ee0 Remove mock 2024-12-01 23:48:18 +01:00
Fleischer Hanno
2f2d9c7479 Merge remote-tracking branch 'origin/development' into development 2024-12-01 23:42:56 +01:00
Fleischer Hanno
c2b6e6e9e9 made all classes for ServerStartGameMEssage serializiable 2024-12-01 23:42:32 +01:00
Daniel Grigencha
697b974ce1 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:42:09 +01:00
Felix Koppe
f0cab64910 Minor change 2024-12-01 23:46:46 +01:00
Daniel Grigencha
adcf65d7aa Updated 'LobbyState' class.
Updated the 'LobbyState' class by sending a broadcast message after all players are ready. In Addition, the 'ServerStartGameMessage' getting the 'Board' object.
2024-12-01 23:41:10 +01:00
Daniel Grigencha
8f53b76a3e Updated 'ServerStartGameMessage' class.
Updated the 'ServerStartGameMessage' class by adding the 'board' attribute and its getter method to it.
2024-12-01 23:39:36 +01:00
Cedric Beck
eaef4b20ba Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:34:02 +01:00
Cedric Beck
d3fe2fec14 added show/hide functionality for select button 2024-12-01 23:33:28 +01:00
Daniel Grigencha
02d7ef1dd8 Updated 'ChoosePieceState' class.
Updated the 'ChoosePieceState' to work correctly as a state automaton.
2024-12-01 23:28:50 +01:00
Daniel Grigencha
23aa2db714 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:27:50 +01:00
Daniel Grigencha
e68369074f Updated 'RollDiceState' class.
Updated the 'RollDiceState' class by removing a spelling mistake.
2024-12-01 23:27:45 +01:00
Daniel Grigencha
aafc804c3f Updated 'ChoosePieceState' class.
Updated the 'ChoosePieceState' to work correctly as a state automaton.
2024-12-01 23:26:53 +01:00
Felix Koppe
bec199036c Minor change 2024-12-01 23:10:25 +01:00
Fleischer Hanno
a7e048d4b4 Merge remote-tracking branch 'origin/development' into development 2024-12-01 23:08:50 +01:00
Felix Koppe
bcd510804f Merge commit 2024-12-01 23:12:46 +01:00
Felix Koppe
7319f7a62c Process startMenuNotification in lobby 2024-12-01 23:12:36 +01:00
Fleischer Hanno
a1e51fb2f4 added logic to the leave game method 2024-12-01 23:08:25 +01:00
Daniel Grigencha
f15d0eb5d8 Merge branch 'development' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into development 2024-12-01 23:06:31 +01:00
Daniel Grigencha
f484a4abc8 Updated server states.
Updated server stats by adding the logger to all states. In Addition, new joined clients should be updated correctly.
2024-12-01 23:06:02 +01:00
Felix Koppe
5905a232a5 Adjust state 2024-12-01 23:03:26 +01:00
Cedric Beck
12cb87ef70 added outline package back 2024-12-01 22:56:09 +01:00
Cedric Beck
9cb7a156bb Merge branch 'development' into dev/client_beck 2024-12-01 22:47:48 +01:00
Fleischer Hanno
76e2606847 reverted the client state machine 2024-12-01 22:47:13 +01:00
Cedric Beck
7762b97303 added init & taktical view by ownColor 2024-12-01 22:46:17 +01:00
Daniel Grigencha
eaf46f3e14 updated the project to pass the checkstyle 2024-12-01 22:37:49 +01:00
Fleischer Hanno
92be52b62a fixed server join logic 2024-12-01 22:33:30 +01:00
Fleischer Hanno
7a0a3589a5 trying to fix server ip to use outward ip 2024-12-01 22:14:09 +01:00
Cedric Beck
661c28f096 merge to dev/client_beck from development 2024-12-01 22:06:30 +01:00
Cedric Beck
4a94e85aac Merge branch 'dev/client' into dev/client_beck 2024-12-01 21:55:37 +01:00
Fleischer Hanno
5a68d3b146 fixed a method call for getMoveIndices 2024-12-01 21:52:28 +01:00
Cedric Beck
63fa692910 removed comments 2024-12-01 21:52:28 +01:00
Fleischer Hanno
133f900ec7 added the server and network functionality for mdga and fixed the communication in the Lobby 2024-12-01 21:50:28 +01:00
Fleischer Hanno
33ddea4221 Merge remote-tracking branch 'origin/development' into development 2024-12-01 21:49:30 +01:00
Daniel Grigencha
977a7294ad updated server state diagram and added missing classes 2024-12-01 21:49:11 +01:00
Cedric Beck
7db75a2ca1 added converted assets 2024-12-01 21:48:40 +01:00
Daniel Grigencha
71d5701cc7 added javadoc for the classes in the notification package 2024-12-01 21:44:35 +01:00
Fleischer Hanno
42feca466d Merge remote-tracking branch 'origin/development' into development 2024-12-01 21:05:41 +01:00
Daniel Grigencha
95a1f8d858 removed all inheritance of the automaton for testing purposes 2024-12-01 21:05:22 +01:00
Fleischer Hanno
76757c19a9 Merge remote-tracking branch 'origin/development' into development 2024-12-01 21:02:11 +01:00
Daniel Grigencha
c3fdcf4dc7 added the states of the server automaton for testing purposes 2024-12-01 21:01:37 +01:00
Fleischer Hanno
8ffab12f49 Merge remote-tracking branch 'origin/development' into development 2024-12-01 20:57:57 +01:00
Daniel Grigencha
8369797120 added JavaDocs for the 'InterruptState' classe 2024-12-01 20:41:30 +01:00
Daniel Grigencha
ef450a23f5 added logger to server state chart 2024-12-01 20:35:40 +01:00
Fleischer Hanno
1d8eff8ea9 Merge remote-tracking branch 'origin/development' into development 2024-12-01 19:45:06 +01:00
Fleischer Hanno
e9d45f241f Merge remote-tracking branch 'origin/development' into development 2024-12-01 18:22:29 +01:00
Fleischer Hanno
5d638a1da7 Merge remote-tracking branch 'origin/development' into development 2024-12-01 18:20:16 +01:00
Fleischer Hanno
b83279e835 Merge remote-tracking branch 'origin/development' into development 2024-12-01 18:16:05 +01:00
Felix Koppe
5b8032bed9 Prepare for model 2024-12-01 17:44:37 +01:00
Fleischer Hanno
5206b03966 added java docs 2024-12-01 17:03:56 +01:00
352 changed files with 882874 additions and 115123 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.run/
.gradle
build/
#!gradle/wrapper/gradle-wrapper.jar

View File

@@ -1,12 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MdgaApp" type="Application" factoryName="Application" singleton="false" nameIsGenerated="true">
<option name="ALTERNATIVE_JRE_PATH" value="temurin-20" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="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.Outline.*" />
<option name="PATTERN" value="pp.mdga.client.board.outline.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
@@ -14,4 +16,4 @@
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
</component>

File diff suppressed because one or more lines are too long

View File

@@ -8,11 +8,11 @@
public enum Asset {
bigTent,
cardStack,
cir("Models/cir/cir_newOrigin.obj"),
heer("Models/heer/heer_newOrigin.obj"),
cir,
heer,
jet,
lw("Models/lw/lw_newOrigin.obj"),
marine("Models/marine/marine_newOrigin.obj"),
lw,
marine,
node_home_blue("Models/node_home/node_home.j3o", "Models/node_home/node_home_blue_diff.png"),
node_wait_blue("Models/node_home/node_home.j3o", "Models/node_home/node_home_blue_diff.png"),
node_home_black("Models/node_home/node_home.j3o", "Models/node_home/node_home_black_diff.png"),
@@ -28,29 +28,35 @@ public enum Asset {
ship(0.8f),
smallTent,
tank,
world("Models/world_new/world_export_new.obj", "Models/world_new/world_new_diff.png", 1.2f),
shield_ring("Models/shield_ring/shield_ring.obj", null),
tree_small("Models/tree_small/tree_small.obj", "Models/tree_small/tree_small_diff.png"),
tree_big("Models/tree_big/tree_big.obj", "Models/tree_big/tree_big_diff.png"),
turboCard("Models/turboCard/turboCard.obj", "Models/turboCard/turboCard_diff.png"),
turboSymbol("Models/turboCard/turboSymbol.obj", "Models/turboCard/turboCard_diff.png"),
swapCard("Models/swapCard/swapCard.obj", "Models/swapCard/swapCard_diff.png"),
swapSymbol("Models/swapCard/swapSymbol.obj", "Models/swapCard/swapCard_diff.png"),
shieldCard("Models/shieldCard/shieldCard.obj", "Models/shieldCard/shieldCard_diff.png"),
shieldSymbol("Models/shieldCard/shieldSymbol.obj", "Models/shieldCard/shieldCard_diff.png"),
dice("Models/dice/dice.obj", "Models/dice/dice_diff.jpeg")
world("Models/world/world_export_newTex.obj", "Models/world/world_diff.png",1.2f),
shieldRing("Models/shieldRing/shieldRing.j3o", null),
treeSmall(1.2f),
treeBig(1.2f),
turboCard,
turboSymbol("Models/turboCard/turboSymbol.j3o", "Models/turboCard/turboCard_diff.png"),
swapCard,
swapSymbol("Models/swapCard/swapSymbol.j3o", "Models/swapCard/swapCard_diff.png"),
shieldCard,
shieldSymbol("Models/shieldCard/shieldSymbol.j3o", "Models/shieldCard/shieldCard_diff.png"),
dice,
missile("Models/missile/AVMT300.obj", "Models/missile/texture.jpg", 0.1f),
tankShoot("Models/tank/tank_shoot_bot.obj", "Models/tank/tank_diff.png"),
tankShootTop("Models/tank/tank_shoot_top.obj", "Models/tank/tank_diff.png"),
treesSmallBackground("Models/treeSmall/small_trees_background.obj", "Models/treeSmall/treeSmall_diff.png", 1.2f),
treesBigBackground("Models/treeBig/big_trees_background.obj", "Models/treeBig/treeBig_diff.png", 1.2f),
shell
;
private final String modelPath;
private final String diffPath;
private final float size;
private static final String root = "Models/";
private static final String ROOT = "Models/";
/**
* Default constructor. Initializes modelPath and diffPath based on the enum name and sets default size to 1.0.
*/
Asset() {
String folderFileName = "./" + root + name() + "/" + name();
String folderFileName = "./" + ROOT + name() + "/" + name();
this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png";
this.size = 1f;
@@ -74,7 +80,7 @@ public enum Asset {
* @param modelPath Path to the 3D model file.
*/
Asset(String modelPath) {
String folderFileName = "./" + root + name() + "/" + name();
String folderFileName = "./" + ROOT + name() + "/" + name();
this.modelPath = modelPath;
this.diffPath = folderFileName + "_diff.png";;
this.size = 1f;
@@ -86,7 +92,7 @@ public enum Asset {
* @param size Scaling factor for the asset.
*/
Asset(float size) {
String folderFileName = "./" + root + name() + "/" + name();
String folderFileName = "./" + ROOT + name() + "/" + name();
this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png";
this.size = size;

View File

@@ -0,0 +1,48 @@
package pp.mdga.client;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
/**
* An abstract control class that serves as a base for initializing spatial objects
* in jMonkeyEngine. This class overrides the controlUpdate and controlRender methods
* from the AbstractControl class, providing default empty implementations,
* and adds the ability to initialize spatial objects when they are set.
*/
public abstract class InitControl extends AbstractControl {
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
/**
* Sets the spatial object to be controlled. This method also initializes the spatial
* if it is being set for the first time.
*
* @param spatial The spatial object to control.
*/
@Override
public void setSpatial(Spatial spatial) {
if (this.spatial == null && spatial != null) {
super.setSpatial(spatial);
initSpatial();
}
}
/**
* Initializes the spatial object. This method can be overridden by subclasses
* to define custom initialization logic for the spatial.
* This method is called automatically when the spatial is set for the first time.
*/
protected void initSpatial() {
// Default empty implementation. Override to add initialization logic.
}
}

View File

@@ -17,13 +17,17 @@
import pp.mdga.client.board.OutlineControl;
import pp.mdga.client.board.PieceControl;
import pp.mdga.client.gui.CardControl;
import pp.mdga.client.gui.DiceControl;
import pp.mdga.client.view.GameView;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import pp.mdga.game.Piece;
import pp.mdga.notification.FinishNotification;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.SelectableCardsNotification;
import java.util.List;
import java.util.UUID;
public class InputSynchronizer {
@@ -36,6 +40,11 @@ public class InputSynchronizer {
private CardControl hoverCard;
private PieceControl hoverPiece;
private boolean clickAllowed = true;
private boolean isRotateLeft = false;
private boolean isRotateRight = false;
/**
* Constructor initializes the InputSynchronizer with the application context.
* Sets up input mappings and listeners for user interactions.
@@ -51,6 +60,18 @@ public class InputSynchronizer {
setupInput();
}
public void update(float tpf) {
if(isRotateLeft && isRotateRight) {
return;
}
if(isRotateLeft) {
rotationAngle += 180 * tpf;
}
if(isRotateRight) {
rotationAngle -= 180 * tpf;
}
}
/**
* Configures input mappings for various actions and binds them to listeners.
*/
@@ -58,22 +79,22 @@ private void setupInput() {
inputManager.addMapping("Settings", new KeyTrigger(KeyInput.KEY_ESCAPE));
inputManager.addMapping("Forward", new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_Q));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_E));
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("MouseVertical", new MouseAxisTrigger(MouseInput.AXIS_Y, false), new MouseAxisTrigger(MouseInput.AXIS_Y, true)); //Mouse Up Down movement
inputManager.addMapping("MouseScrollUp", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)); // Scroll up
inputManager.addMapping("MouseScrollDown", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)); // Scroll down
inputManager.addMapping("Test", new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping("Click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(actionListener, "Settings", "Forward", "RotateRightMouse", "Click", "Test");
inputManager.addListener(actionListener, "Settings", "Forward", "RotateRightMouse", "Click", "Left", "Right", "Test");
inputManager.addListener(analogListener, "MouseLeft", "MouseRight", "MouseScrollUp", "MouseScrollDown");
}
private boolean test = false;
UUID p = null;
/**
* Handles action-based input events such as key presses and mouse clicks.
*/
@@ -90,11 +111,19 @@ public void onAction(String name, boolean isPressed, float tpf) {
rightMousePressed = isPressed;
}
if(name.equals("Click") && isPressed) {
if(!clickAllowed) {
return;
}
if (app.getView() instanceof GameView gameView) {
DiceControl diceSelect = checkHover(gameView.getGuiHandler().getCardLayerCamera(), gameView.getGuiHandler().getCardLayerRootNode(), DiceControl.class);
CardControl cardLayerSelect = checkHover(gameView.getGuiHandler().getCardLayerCamera(), gameView.getGuiHandler().getCardLayerRootNode(), CardControl.class);
OutlineControl boardSelect = checkHover(app.getCamera(), app.getRootNode(), OutlineControl.class);
if(cardLayerSelect != null) {
if(diceSelect != null) {
app.getModelSynchronize().rolledDice();
}
else if(cardLayerSelect != null) {
//cardSelect
if(cardLayerSelect.isSelectable()) gameView.getGuiHandler().selectCard(cardLayerSelect);
}
@@ -114,9 +143,40 @@ else if(boardSelect != null) {
}
if(name.equals("Test") &&isPressed){
if (name.equals("Left")) {
isRotateLeft = !isRotateLeft;
}
if (name.equals("Right")) {
isRotateRight = !isRotateRight;
}
if(name.equals("Test2") &&isPressed){
if(app.getView() instanceof GameView gameView){
app.getNotificationSynchronizer().addTestNotification(new SelectableCardsNotification(List.of(BonusCard.SHIELD)));
if(p == null) {
p = UUID.randomUUID();
gameView.getBoardHandler().addPlayer(Color.AIRFORCE,List.of(p,UUID.randomUUID(),UUID.randomUUID(),UUID.randomUUID()));
gameView.getBoardHandler().movePieceStartAnim(p,0);
//gameView.getBoardHandler().movePieceAnim(p,0, 8);
} else {
gameView.getBoardHandler().throwPiece(p, Color.NAVY);
//gameView.getBoardHandler().movePieceStartAnim(p,0);
}
// gameView.getGuiHandler().rollRankingResult(Color.AIRFORCE, 1);
// gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2);
// gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3);
// gameView.getGuiHandler().rollRankingResult(Color.CYBER, 4);
// gameView.getGuiHandler().showDice();
// UUID p1 = UUID.randomUUID();
// gameView.getBoardHandler().addPlayer(Color.AIRFORCE,List.of(p1,UUID.randomUUID(),UUID.randomUUID(),UUID.randomUUID()));
// gameView.getBoardHandler().movePieceStartAnim(p1,0);
//gameView.getGuiHandler().drawCard(Color.ARMY);
//gameView.getGuiHandler().addCardOwn(BonusCard.SHIELD);
//gameView.getGuiHandler().playCardOwn(BonusCard.SHIELD);
}
}
}
@@ -151,6 +211,7 @@ else if (name.equals("MouseLeft") || name.equals("MouseRight") || name.equals("M
* Detects the hovered piece and updates its hover state.
*/
private <T extends AbstractControl> T checkHover(Camera cam, Node root, Class<T> controlType) {
if(cam == null || root == null || controlType == null) return null;
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), getMousePos(cam).subtract(cam.getLocation()).normalize());
root.collideWith(ray, results);
@@ -164,6 +225,7 @@ private <T extends AbstractControl> T checkHover(Camera cam, Node root, Class<T>
* Detects the hovered card and updates its hover state.
*/
private <T extends AbstractControl> T checkHoverOrtho(Camera cam, Node root, Class<T> controlType) {
if(cam == null || root == null || controlType == null) return null;
CollisionResults results = new CollisionResults();
Vector3f mousePos = getMousePos(cam);
mousePos.setZ(cam.getLocation().getZ());
@@ -277,6 +339,10 @@ public float getRotation() {
return (rotationAngle / 2) % 360;
}
public void setRotation(float rotationAngle){
this.rotationAngle = rotationAngle;
}
/**
* Gets the current scroll value.
*
@@ -285,4 +351,12 @@ public float getRotation() {
public int getScroll() {
return scrollValue;
}
public void setClickAllowed(boolean allowed) {
clickAllowed = allowed;
}
public boolean isClickAllowed() {
return clickAllowed;
}
}

View File

@@ -2,19 +2,29 @@
import com.jme3.app.SimpleApplication;
import com.simsilica.lemur.GuiGlobals;
import com.sun.tools.javac.Main;
import pp.mdga.client.acoustic.AcousticHandler;
import pp.mdga.client.animation.AnimationHandler;
import com.jme3.system.AppSettings;
import pp.mdga.client.dialog.JoinDialog;
import pp.mdga.client.view.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.prefs.Preferences;
/**
* Main application class for the MdgaApp game.
* This class extends {@link SimpleApplication} and manages the game's lifecycle, states, and main components.
*/
public class MdgaApp extends SimpleApplication {
/** Handles animations in the application. */
private AnimationHandler animationHandler;
private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
/** Handles acoustic effects and state-based sounds. */
private AcousticHandler acousticHandler;
@@ -35,19 +45,31 @@ public class MdgaApp extends SimpleApplication {
private MdgaState state = null;
/** Scale for rendering images. */
private static final float IMAGE_SCALE = 1.5f;
private final float imageScale = prefs.getInt("scale", 1);
/** The main menu view. */
private MdgaView mainView;
private MainView mainView;
/** The lobby view. */
private MdgaView lobbyView;
private LobbyView lobbyView;
/** The game view. */
private MdgaView gameView;
private GameView gameView;
/** The ceremony view. */
private MdgaView ceremonyView;
private CeremonyView ceremonyView;
/** The client game logic. */
private final ClientGameLogic clientGameLogic;
private ExecutorService executor;
private ServerConnection networkConnection;
public MdgaApp() {
networkConnection = new NetworkSupport(this);
this.clientGameLogic = new ClientGameLogic(networkConnection);
}
/**
* Main entry point for the application.
@@ -58,14 +80,30 @@ public class MdgaApp extends SimpleApplication {
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setSamples(128);
settings.setCenterWindow(true);
settings.setWidth(1920);
settings.setHeight(1080);
settings.setVSync(false);
if(prefs.getBoolean("fullscreen", false)) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = (int) screenSize.getWidth();
int screenHeight = (int) screenSize.getHeight();
settings.setResolution(screenWidth, screenHeight);
settings.setFullscreen(true);
} else {
settings.setWidth(prefs.getInt("width", 1280));
settings.setHeight(prefs.getInt("height", 720));
}
settings.setCenterWindow(true);
settings.setVSync(false);
settings.setTitle("MDGA");
settings.setVSync(true);
MdgaApp app = new MdgaApp();
app.setSettings(settings);
app.setShowSettings(false);
app.setPauseOnLostFocus(false);
app.setDisplayStatView(false);
app.start();
}
@@ -80,7 +118,6 @@ public void simpleInitApp() {
flyCam.setEnabled(false);
animationHandler = new AnimationHandler(this);
acousticHandler = new AcousticHandler(this);
notificationSynchronizer = new NotificationSynchronizer(this);
inputSynchronizer = new InputSynchronizer(this);
@@ -104,6 +141,7 @@ public void simpleUpdate(float tpf) {
view.update(tpf);
acousticHandler.update();
notificationSynchronizer.update();
inputSynchronizer.update(tpf);
}
/**
@@ -141,14 +179,6 @@ public void enter(MdgaState state) {
view.enter();
}
/**
* Gets the animation handler.
*
* @return the {@link AnimationHandler} instance
*/
public AnimationHandler getAnimationHandler() {
return animationHandler;
}
/**
* Gets the acoustic handler.
@@ -174,7 +204,7 @@ public MdgaState getState() {
* @return the image scale as a float
*/
public float getImageScale() {
return IMAGE_SCALE;
return imageScale;
}
/**
@@ -219,5 +249,78 @@ public NotificationSynchronizer getNotificationSynchronizer() {
public void setup() {
}
public ClientGameLogic getGameLogic() {
return clientGameLogic;
}
public ExecutorService getExecutor() {
if (this.executor == null) {
this.executor = Executors.newCachedThreadPool();
}
return this.executor;
}
public ServerConnection getNetworkSupport(){
return networkConnection;
}
public void updateResolution(int width, int height, float imageFactor, boolean isFullscreen) {
if(isFullscreen) {
int baseWidth = 1280;
int baseHeight = 720;
float baseAspectRatio = (float) baseWidth / baseHeight;
float newAspectRatio = (float) width / height;
float scaleFactor = Math.max((float) width / baseWidth, (float) height / baseHeight);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = (int) screenSize.getWidth();
int screenHeight = (int) screenSize.getHeight();
settings.setResolution(screenWidth, screenHeight);
settings.setFullscreen(true);
prefs.putFloat("scale", scaleFactor);
prefs.putBoolean("fullscreen", true);
} else {
prefs.putInt("width", width);
prefs.putInt("height", height);
prefs.putFloat("scale", imageFactor);
prefs.putBoolean("fullscreen", false);
}
}
public static void restartApp() {
try {
String javaBin = System.getProperty("java.home") + "/bin/java";
String classPath = System.getProperty("java.class.path");
String className = System.getProperty("sun.java.command");
ProcessBuilder builder = new ProcessBuilder(
javaBin, "-cp", classPath, className
);
builder.start();
System.exit(0);
} catch (Exception e) {
throw new RuntimeException("restart failed");
}
}
public void afterGameCleanup() {
MainView main = (MainView) mainView;
main.getJoinDialog().disconnect();
main.getHostDialog().shutdownServer();
ceremonyView.afterGameCleanup();
}
public GameView getGameView(){
return gameView;
}
}

View File

@@ -1,15 +1,8 @@
package pp.mdga.client;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.view.CeremonyView;
import pp.mdga.client.view.GameView;
import pp.mdga.client.view.LobbyView;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import pp.mdga.message.client.LobbyReadyMessage;
import pp.mdga.notification.AcquireCardNotification;
import pp.mdga.notification.DrawCardNotification;
import pp.mdga.notification.TskSelectNotification;
import java.util.UUID;
import java.util.logging.Level;
@@ -22,16 +15,20 @@ public class ModelSynchronizer {
private UUID a;
private UUID b;
private BonusCard card;
private boolean swap;
ModelSynchronizer(MdgaApp app) {
this.app = app;
swap = false;
}
private Color testColor;
private int test = 0;
public void animationEnd() {
app.getGameLogic().selectAnimationEnd();
}
public void select(UUID a, UUID b){
if(swap) selectSwap(a,b);
else selectPiece(a);
}
public void selectSwap(UUID a, UUID b) {
@@ -69,10 +66,11 @@ public void selectCard(BonusCard card) {
this.card = card;
GameView gameView = (GameView) app.getView();
if(card != null) {
gameView.needConfirm();
} else {
gameView.noConfirm();
gameView.showNoPower();
}
}
@@ -82,106 +80,65 @@ public void confirm() {
GameView gameView = (GameView) app.getView();
if(a != null && b != null) {
//swap
app.getGameLogic().selectPiece(a);
app.getGameLogic().selectPiece(b);
gameView.getBoardHandler().clearSelectable();
} else if (a != null) {
//piece
app.getGameLogic().selectPiece(a);
gameView.getBoardHandler().clearSelectable();
} else if (card != null){
//card
gameView.getGuiHandler().clearSelectableCards();
} else {
throw new RuntimeException("nothing to confirm");
app.getGameLogic().selectCard(card);
gameView.getGuiHandler().clearSelectableCards();
}
gameView.noConfirm();
gameView.hideNoPower();
}
public void selectTsk(Color color) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "selectTsk: {0}", color);
LobbyView view = (LobbyView) app.getView();
view.setTaken(color, true, true, "OwnPlayerName");
testColor = color;
app.getGameLogic().selectTsk(color);
}
public void unselectTsk() {
// TODO call from somewhere
LOGGER.log(Level.INFO, "unselectTsk");
public void unselectTsk(Color color) {
app.getGameLogic().deselectTSK(color);
}
public void rolledDice() {
// TODO call from somewhere
LOGGER.log(Level.INFO, "rolledDice");
app.getGameLogic().selectDice();
}
public void setName(String name) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "setName: {0}", name);
app.getGameLogic().selectName(name);
}
public void setReady(boolean ready) {
LOGGER.log(Level.INFO, "setReady");
LobbyView view = (LobbyView) app.getView();
view.setReady(testColor, ready);
test++;
if(test > 2) {
testColor = null;
test = 0;
enter(MdgaState.GAME);
}
app.getGameLogic().selectReady(ready);
}
public void setHost(int port) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "setHost: {0}", port);
enter(MdgaState.LOBBY);
app.getGameLogic().selectJoin("");
}
public void setJoin(String ip, int port) {
// TODO call from somewhere
LOGGER.log(Level.INFO, "setJoin with IP: {0}, Port: {1}", new Object[]{ip, port});
enter(MdgaState.LOBBY);
app.getGameLogic().selectJoin(ip);
}
public void leave() {
LOGGER.log(Level.INFO, "leave");
app.getAcousticHandler().playSound(MdgaSound.LEAVE);
enter(MdgaState.MAIN);
app.getGameLogic().selectLeave();
}
public void enter(MdgaState state) {
LOGGER.log(Level.INFO, "enter: {0}", state);
app.enter(state);
//app.enter(state);
}
if (state == MdgaState.CEREMONY) {
CeremonyView ceremonyView = (CeremonyView) app.getView();
ceremonyView.addCeremonyParticipant(Color.AIRFORCE, 1, "ugidffdg");
ceremonyView.addCeremonyParticipant(Color.ARMY, 2, "ugidffdg");
ceremonyView.addCeremonyParticipant(Color.NAVY, 3, "ugidffdg");
ceremonyView.addCeremonyParticipant(Color.CYBER, 4, "ugidffdg");
public void setSwap(boolean swap){
this.swap = swap;
}
ceremonyView.addStatisticsRow("player sdgsd", 1, 2, 3, 4, 5, 6);
ceremonyView.addStatisticsRow("player sdgsd", 1, 2, 3, 4, 5, 6);
ceremonyView.addStatisticsRow("player sdgsd", 1, 2, 3, 4, 5, 6);
ceremonyView.addStatisticsRow("player sdgsd", 1, 2, 3, 4, 5, 6);
ceremonyView.addStatisticsRow("Gesamt", 1, 2, 3, 4, 5, 6);
}
public void force() {
if (state == MdgaState.GAME) {
GameView gameView = (GameView) app.getView();
//app.getNotificationSynchronizer().addTestNotification(new DrawCardNotification(Color.AIRFORCE, BonusCard.SHIELD));
selectPiece(UUID.randomUUID());
}
if (state == MdgaState.LOBBY) {
LobbyView lobbyView = (LobbyView) app.getView();
app.getNotificationSynchronizer().addTestNotification(new TskSelectNotification(Color.CYBER, "blablabupp", false));
app.getNotificationSynchronizer().addTestNotification(new TskSelectNotification(Color.ARMY, "Spieler 2", false));
lobbyView.setReady(Color.ARMY, true);
}
}
}

View File

@@ -0,0 +1,91 @@
package pp.mdga.client;
import com.jme3.network.*;
import pp.mdga.message.client.ClientMessage;
import pp.mdga.message.server.ServerMessage;
import java.io.IOException;
public class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
private static final System.Logger LOGGER = System.getLogger(NetworkSupport.class.getName());
private final MdgaApp app;
private Client client;
public NetworkSupport(MdgaApp app) {
this.app = app;
}
public MdgaApp getApp() {
return this.app;
}
public boolean isConnected() {
return this.client != null && this.client.isConnected();
}
public void connect() {
if (this.client != null) {
throw new IllegalStateException("trying to join a game again");
} else {
try {
this.initNetwork("localhost", 2345);
} catch (IOException e) {
LOGGER.log(System.Logger.Level.ERROR, "could not connect to server", e);
}
}
}
public void disconnect() {
if (this.client != null) {
this.client.close();
this.client = null;
LOGGER.log(System.Logger.Level.INFO, "client closed");
}
}
public void initNetwork(String host, int port) throws IOException {
if (this.client != null) {
throw new IllegalStateException("trying to join a game again");
} else {
this.client = Network.connectToServer(host, port);
this.client.start();
this.client.addMessageListener(this);
this.client.addClientStateListener(this);
}
}
public void messageReceived(Client client, Message message) {
LOGGER.log(System.Logger.Level.INFO, "message received from server: {0}", new Object[]{message});
if (message instanceof ServerMessage serverMessage) {
this.app.enqueue(() -> serverMessage.accept(this.app.getGameLogic()));
}
}
public void clientConnected(Client client) {
LOGGER.log(System.Logger.Level.INFO, "Client connected: {0}", new Object[]{client});
}
public void clientDisconnected(Client client, ClientStateListener.DisconnectInfo disconnectInfo) {
LOGGER.log(System.Logger.Level.INFO, "Client {0} disconnected: {1}", new Object[]{client, disconnectInfo});
if (this.client != client) {
throw new IllegalArgumentException("parameter value must be client");
} else {
LOGGER.log(System.Logger.Level.INFO, "client still connected: {0}", new Object[]{client.isConnected()});
this.client = null;
this.disconnect();
}
}
@Override
public void send(ClientMessage message) {
LOGGER.log(System.Logger.Level.INFO, "sending {0}", new Object[]{message});
if (this.client == null) {
LOGGER.log(System.Logger.Level.WARNING, "client not connected");
} else {
this.client.send(message);
}
}
}

View File

@@ -1,5 +1,7 @@
package pp.mdga.client;
import com.jme3.system.NanoTimer;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.board.BoardHandler;
import pp.mdga.client.gui.GuiHandler;
import pp.mdga.client.view.CeremonyView;
@@ -15,42 +17,59 @@ public class NotificationSynchronizer {
private ArrayList<Notification> notifications = new ArrayList<>();
private NanoTimer timer = new NanoTimer();
private float delay = 0;
private static final float STANDARD_DELAY = 2.5f;
NotificationSynchronizer(MdgaApp app) {
this.app = app;
}
public void addTestNotification(Notification n) {
notifications.add(n);
}
public void update() {
//TODO fetch model notifications
for (Notification n : notifications) {
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());
while (timer.getTimeInSeconds() >= delay) {
Notification n = app.getGameLogic().getNotification();
if(n == null) {
return;
}
timer.reset();
delay = 0;
if(n instanceof InfoNotification infoNotification) {
app.getView().showInfo(infoNotification.getMessage(), infoNotification.isError());
return;
}
if(n != null) {
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.getClass().getName());
}
}
}
notifications.clear();
}
private void handleMain(Notification notification) {
if (notification instanceof LobbyDialogNotification) {
app.enter(MdgaState.LOBBY);
} else if (notification instanceof StartDialogNotification) {
//nothing
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
throw new RuntimeException("notification not expected in main: "+ notification.getClass().getName());
}
}
@@ -59,15 +78,18 @@ private void handleLobby(Notification notification) {
if (notification instanceof TskSelectNotification n) {
lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName());
lobbyView.setTaken(n.getColor(), true, false, n.getName());
} else if (notification instanceof StartDialogNotification) {
app.afterGameCleanup();
app.enter(MdgaState.MAIN);
} else if (notification instanceof TskUnselectNotification n) {
lobbyView.setTaken(n.getColor(), false, false, null);
//} else if(notification instanceof LobbyReadyNotification lobbyReadyNotification) {
//lobbyView.setReady(lobbyReadyNotification.getColor(), lobbyReadyNotification.isReady()):
} else if(notification instanceof LobbyReadyNotification lobbyReadyNotification) {
lobbyView.setReady(lobbyReadyNotification.getColor(), lobbyReadyNotification.isReady());
} else if (notification instanceof GameNotification n) {
app.getGameView().setOwnColor(n.getOwnColor());
app.enter(MdgaState.GAME);
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
throw new RuntimeException("notification not expected in lobby: " + notification.getClass().getName());
}
}
@@ -75,11 +97,17 @@ private void handleGame(Notification notification) {
GameView gameView = (GameView) app.getView();
GuiHandler guiHandler = gameView.getGuiHandler();
BoardHandler boardHandler = gameView.getBoardHandler();
ModelSynchronizer modelSynchronizer = app.getModelSynchronize();
if (notification instanceof AcquireCardNotification n) {
guiHandler.addCard(n.getBonusCard());
guiHandler.addCardOwn(n.getBonusCard());
app.getAcousticHandler().playSound(MdgaSound.BONUS);
delay = STANDARD_DELAY;
} else if (notification instanceof ActivePlayerNotification n) {
gameView.getGuiHandler().setActivePlayer(n.getColor());
boardHandler.showDice(n.getColor());
app.getAcousticHandler().playSound(MdgaSound.UI90);
delay = STANDARD_DELAY;
} else if (notification instanceof CeremonyNotification ceremonyNotification) {
app.enter(MdgaState.CEREMONY);
CeremonyView ceremonyView = (CeremonyView) app.getView();
@@ -109,79 +137,85 @@ private void handleGame(Notification notification) {
}
} else if (notification instanceof DiceNowNotification) {
guiHandler.showDice();
} else if (notification instanceof DicingNotification n) {
//TODO ???
} else if (notification instanceof DrawCardNotification n) {
guiHandler.drawCard(n.getColor());
} else if (notification instanceof HomeMoveNotification home) {
boardHandler.moveHomePiece(home.getPieceId(), home.getHomeIndex());
boardHandler.movePieceHomeAnim(home.getPieceId(), home.getHomeIndex());
guiHandler.hideText();
} else if (notification instanceof InterruptNotification) {
app.enter(MdgaState.LOBBY);
} else if (notification instanceof InterruptNotification notification1) {
gameView.enterInterrupt(notification1.getColor());
} else if (notification instanceof MovePieceNotification n) {
if(n.isMoveStart()) {
//StartMove
boardHandler.movePieceStart(n.getPiece(), n.getMoveIndex());
boardHandler.movePieceStartAnim(n.getPiece(), n.getMoveIndex());
}
else {
//InfieldMove
boardHandler.movePiece(n.getPiece(), n.getStartIndex(), n.getMoveIndex());
boardHandler.movePieceAnim(n.getPiece(), n.getStartIndex(), n.getMoveIndex());
}
guiHandler.hideText();
} else if (notification instanceof ThrowPieceNotification n) {
boardHandler.throwPiece(n.getPieceId());
boardHandler.throwPiece(n.getPieceId(), n.getThrowColor());
} else if (notification instanceof NoShieldNotification n) {
boardHandler.unshieldPiece(n.getPieceId());
} else if (notification instanceof PlayCardNotification n) {
switch(n.getCard()){
case SWAP -> guiHandler.swap();
case TURBO -> guiHandler.turbo();
case SHIELD -> guiHandler.shield();
default -> throw new RuntimeException("invalid card");
}
if(n.getColor() == gameView.getOwnColor()) guiHandler.playCardOwn(n.getCard());
else guiHandler.playCardEnemy(n.getColor(), n.getCard());
} else if (notification instanceof PlayerInGameNotification n) {
boardHandler.addPlayer(n.getColor(),n.getPiecesList());
guiHandler.addPlayer(n.getColor(),n.getName());
} else if (notification instanceof ResumeNotification) {
//TODO
gameView.leaveInterrupt();
} else if (notification instanceof RollDiceNotification n) {
gameView.getGuiHandler().hideText();
if(n.getColor() == gameView.getOwnColor()){
guiHandler.rollDice(n.getEyes(), n.isTurbo() ? n.getMultiplier() : -1);
}
else {
boardHandler.hideDice();
if (n.isTurbo()) guiHandler.showRolledDiceMult(n.getEyes(), n.getMultiplier(), n.getColor());
else guiHandler.showRolledDice(n.getEyes(), n.getColor());
}
delay = 7;
} else if (notification instanceof SelectableCardsNotification n) {
guiHandler.setSelectableCards(n.getCards());
gameView.showNoPower();
} else if (notification instanceof ShieldActiveNotification n) {
boardHandler.shieldPiece(n.getPieceId());
} else if (notification instanceof ShieldSuppressedNotification n) {
boardHandler.suppressShield(n.getPieceId());
} else if (notification instanceof StartDialogNotification) {
app.afterGameCleanup();
app.enter(MdgaState.MAIN);
} else if (notification instanceof SwapPieceNotification n) {
boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece());
// boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece());
guiHandler.swap();
} else if (notification instanceof WaitMoveNotification) {
//TODO ???
} else if (notification instanceof SelectableMoveNotification n) {
boardHandler.outlineMove(n.getPieces(), n.getMoveIndexe(), n.getHomeMoves());
boardHandler.outlineMove(n.getPieces(), n.getMoveIndices(), n.getHomeMoves());
modelSynchronizer.setSwap(false);
} else if (notification instanceof SelectableSwapNotification n) {
boardHandler.outlineSwap(n.getOwnPieces(), n.getEnemyPieces());
modelSynchronizer.setSwap(true);
} else if (notification instanceof SelectableShieldNotification n) {
boardHandler.outlineShield(n.getPieces());
modelSynchronizer.setSwap(false);
} else if (notification instanceof TurboActiveNotification){
guiHandler.turbo();
} else if (notification instanceof FinishNotification n){
guiHandler.finish(n.getColorFinished());
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
throw new RuntimeException("notification not expected in game: " + notification.getClass().getName());
}
}
private void handleCeremony(Notification notification) {
if (notification instanceof StartDialogNotification) {
app.afterGameCleanup();
app.enter(MdgaState.MAIN);
} else {
throw new RuntimeException("notification not expected: " + notification.toString());
throw new RuntimeException("notification not expected in ceremony: " + notification.getClass().getName());
}
}
}

View File

@@ -0,0 +1,47 @@
package pp.mdga.client;
import com.jme3.math.Vector3f;
public class Util {
private Util(){}
/**
* Performs linear interpolation between two values.
*
* @param start The starting value.
* @param end The ending value.
* @param t A parameter between 0 and 1 representing the interpolation progress.
* @return The interpolated value.
*/
public static float linInt(float start, float end, float t) {
return start + t * (end - start);
}
/**
* Performs quadratic interpolation between three points.
*
* @param p1 The initial point.
* @param p2 The middle point.
* @param p3 The final point.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated point.
*/
public static Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) {
// Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3
float oneMinusT = 1 - t;
return p1.mult(oneMinusT * oneMinusT)
.add(p2.mult(2 * oneMinusT * t))
.add(p3.mult(t * t));
}
/**
* A smooth ease-in-out function for interpolation.
* It accelerates and decelerates the interpolation for a smoother effect.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The adjusted interpolation value.
*/
public static float easeInOut(float x){
return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2);
}
}

View File

@@ -18,12 +18,14 @@ public class AcousticHandler {
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 FADE_DURATION = 2.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 GameMusic birds;
private float mainVolume = 0.0f;
private float musicVolume = 1.0f;
private float soundVolume = 1.0f;
@@ -38,6 +40,8 @@ public AcousticHandler(MdgaApp app) {
mainVolume = prefs.getFloat("mainVolume", 1.0f);
musicVolume = prefs.getFloat("musicVolume", 1.0f);
soundVolume = prefs.getFloat("soundVolume", 1.0f);
birds = new GameMusic(app, MusicAsset.BIRDS, getSoundVolumeTotal(), MusicAsset.BIRDS.getSubVolume(), MusicAsset.BIRDS.getLoop(), 0.0f);
}
/**
@@ -60,6 +64,8 @@ public void update() {
iterator.remove();
}
}
birds.update(Math.min(getSoundVolumeTotal(), getMusicVolumeTotal() > 0 ? 0 : 1));
}
/**
@@ -109,6 +115,38 @@ public void playSound(MdgaSound sound) {
case LEAVE:
assets.add(new SoundAssetDelayVolume(SoundAsset.UI_SOUND2, 0.6f, 0.0f));
break;
case JET:
assets.add(new SoundAssetDelayVolume(SoundAsset.JET, 1.0f, 0.0f));
break;
case EXPLOSION:
assets.add(new SoundAssetDelayVolume(SoundAsset.EXPLOSION_1, 1.0f, 0f));
assets.add(new SoundAssetDelayVolume(SoundAsset.EXPLOSION_2, 1.0f, 0f));
assets.add(new SoundAssetDelayVolume(SoundAsset.THUNDER, 1.0f, 0f));
break;
case LOSE:
assets.add(new SoundAssetDelayVolume(SoundAsset.LOSE, 1.0f, 0.0f));
break;
case BONUS:
assets.add(new SoundAssetDelayVolume(SoundAsset.BONUS, 1.0f, 0.0f));
break;
case UI90:
assets.add(new SoundAssetDelayVolume(SoundAsset.UI90, 1.0f, 0.0f));
break;
case MISSILE:
assets.add(new SoundAssetDelayVolume(SoundAsset.MISSILE, 1.0f, 0.0f));
break;
case MATRIX:
assets.add(new SoundAssetDelayVolume(SoundAsset.MATRIX, 1.0f, 0.0f));
break;
case TURRET_ROTATE:
assets.add(new SoundAssetDelayVolume(SoundAsset.TURRET_ROTATE, 0.7f, 0f));
break;
case TANK_SHOOT:
assets.add(new SoundAssetDelayVolume(SoundAsset.TANK_SHOOT, 0.7f, 0f));
break;
case TANK_EXPLOSION:
assets.add(new SoundAssetDelayVolume(SoundAsset.EXPLOSION_1, 1.0f, 0f));
break;
default:
break;
}
@@ -130,6 +168,10 @@ public void playState(MdgaState state) {
}
MusicAsset asset = null;
birds.pause();
float pause = 0.0f;
switch (state) {
case MAIN:
playGame = false;
@@ -140,10 +182,12 @@ public void playState(MdgaState state) {
asset = MusicAsset.LOBBY;
break;
case GAME:
birds.play();
addGameTracks();
playGame = true;
assert (!gameTracks.isEmpty()) : "no more game music available";
asset = gameTracks.remove(0);
pause = 2.0f;
break;
case CEREMONY:
playGame = false;
@@ -155,7 +199,7 @@ public void playState(MdgaState state) {
assert (null != asset) : "music sceduling went wrong";
scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f);
scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), pause);
}
/**
@@ -431,7 +475,7 @@ public void setSoundVolume(float soundVolume) {
*/
float getMusicVolumeTotal() {
return getMusicVolume() * getMainVolume();
return getMusicVolume() * getMainVolume() / 2;
}
/**

View File

@@ -31,4 +31,14 @@ public enum MdgaSound {
OTHER_CONNECTED,
NOT_READY,
LEAVE,
JET,
EXPLOSION,
LOSE,
BONUS,
UI90,
MISSILE,
MATRIX,
TURRET_ROTATE,
TANK_SHOOT,
TANK_EXPLOSION
}

View File

@@ -10,17 +10,18 @@ enum MusicAsset {
MAIN_MENU("Spaceship.wav", true, 1.0f),
LOBBY("DeadPlanet.wav", true, 1.0f),
CEREMONY("80s,Disco,Life.wav", true, 1.0f),
GAME_1("NeonRoadTrip.wav", 1.0f),
GAME_2("NoPressureTrance.wav", 1.0f),
GAME_3("TheSynthRave.wav", 1.0f),
GAME_4("LaserParty.wav", 1.0f),
GAME_5("RetroNoir.wav", 1.0f),
GAME_6("SpaceInvaders.wav", 1.0f);
GAME_1("NeonRoadTrip.wav", 0.5f),
GAME_2("NoPressureTrance.wav", 0.5f),
GAME_3("TheSynthRave.wav", 0.5f),
GAME_4("LaserParty.wav", 0.5f),
GAME_5("RetroNoir.wav", 0.5f),
GAME_6("SpaceInvaders.wav", 0.5f),
BIRDS("nature-ambience.ogg", true, 1.0f);
private final String path;
private final boolean loop;
private final float subVolume;
private static final String root = "Music/";
private static final String ROOT = "Music/";
/**
* Constructs a new MusicAsset object with the specified name and sub-volume.
@@ -30,7 +31,7 @@ enum MusicAsset {
* @param subVolume A relative volume that modifies the base volume of the track (typically a percentage).
*/
MusicAsset(String name, float subVolume) {
this.path = root + name;
this.path = ROOT + name;
this.loop = false;
this.subVolume = subVolume;
}
@@ -43,7 +44,7 @@ enum MusicAsset {
* @param subVolume A relative volume that modifies the base volume of the track (typically a percentage).
*/
MusicAsset(String name, boolean loop, float subVolume) {
this.path = root + name;
this.path = ROOT + name;
this.loop = loop;
this.subVolume = subVolume;
}

View File

@@ -28,7 +28,20 @@ enum SoundAsset {
POWERUP("powerup.wav"),
ROBOT_READY("robotReady.wav"),
UNIT_READY("unitReady.wav"),
CONNECTED("connected.wav");
JET("jet-overhead.wav"),
EXPLOSION_1("exp.ogg"),
EXPLOSION_2("exp2.ogg"),
THUNDER("thunder.ogg"),
UI90("ui90.ogg"),
BONUS("bonus.ogg"),
LOSE("lose.ogg"),
MISSILE("missile.ogg"),
MATRIX("matrix.wav"),
CONNECTED("connected.wav"),
TURRET_ROTATE("turret_rotate.ogg"),
TANK_SHOOT("tank_shoot.ogg")
;
private final String path;

View File

@@ -0,0 +1,18 @@
package pp.mdga.client.animation;
import pp.mdga.client.InitControl;
public class ActionControl extends InitControl {
private final Runnable runnable;
public ActionControl(Runnable runnable){
this.runnable = runnable;
}
protected void action(){
if(runnable == null) throw new RuntimeException("runnable is null");
else runnable.run();
}
}

View File

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

View File

@@ -1,29 +0,0 @@
package pp.mdga.client.animation;
import pp.mdga.client.MdgaApp;
public class AnimationHandler {
private MdgaApp app;
private Animation animation = null;
public AnimationHandler(MdgaApp app) {
this.app = app;
}
public void playAnimation(MdgaAnimation type) {
}
public void update() {
if (null == animation) {
return;
}
if (animation.isOver()) {
animation = null;
//trigger next state in model
}
}
}

View File

@@ -1,18 +0,0 @@
package pp.mdga.client.animation;
class EmptyAnimation extends Animation {
@Override
void play() {
//nothing
}
@Override
void stop() {
//nothing
}
@Override
boolean isOver() {
return true;
}
}

View File

@@ -0,0 +1,131 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
/**
* The {@code Explosion} class represents an explosion effect in a 3D environment.
* It manages the creation, configuration, and triggering of particle emitters for fire and smoke effects.
*/
public class Explosion {
private final Node rootNode;
private final MdgaApp app;
private final Vector3f location;
private ParticleEmitter fire;
private ParticleEmitter smoke;
private boolean triggered = false;
private final Material mat;
/**
* Constructor for the {@code Explosion} class.
*
* @param app The main application managing the explosion.
* @param rootNode The root node to which the explosion effects will be attached.
* @param location The location of the explosion in world coordinates.
*/
public Explosion(MdgaApp app, Node rootNode, Vector3f location) {
this.app = app;
this.rootNode = rootNode;
this.location = location;
this.mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
}
/**
* Initializes the particle emitters for the explosion effect.
* Configures the fire and smoke emitters with appearance, behavior, and lifespan.
*/
private void initializeEmitter() {
fire = new ParticleEmitter("Effect", Type.Triangle,50);
fire.setMaterial(mat);
fire.setStartColor(ColorRGBA.Yellow);
fire.setEndColor(ColorRGBA.Red);
fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0.2f,0.2f,4f));
fire.getParticleInfluencer().setVelocityVariation(0.4f);
fire.setStartSize(0.1f);
fire.setEndSize(0.8f);
fire.setGravity(0, 0, -0.1f);
fire.setLowLife(0.5f);
fire.setHighLife(2.2f);
fire.setParticlesPerSec(0);
fire.setLocalTranslation(location);
smoke = new ParticleEmitter("Effect2", Type.Triangle,40);
smoke.setMaterial(mat);
smoke.setImagesX(2);
smoke.setImagesY(2);
smoke.setStartColor(ColorRGBA.DarkGray);
smoke.setEndColor(new ColorRGBA(0.05f, 0.05f, 0.05f, 1));
smoke.getParticleInfluencer().setInitialVelocity(new Vector3f(0.0f,0.0f,0.7f));
smoke.getParticleInfluencer().setVelocityVariation(0.5f);
smoke.setStartSize(0.2f);
smoke.setEndSize(0.5f);
smoke.setGravity(0, 0, -0.3f);
smoke.setLowLife(1.2f);
smoke.setHighLife(5.5f);
smoke.setParticlesPerSec(0);
smoke.setLocalTranslation(location);
app.getAcousticHandler().playSound(MdgaSound.EXPLOSION);
}
/**
* Triggers the explosion effect by attaching and activating the particle emitters for fire and smoke.
* Both emitters are automatically detached after a predefined duration.
*/
public void trigger() {
if (!triggered) {
triggered = true;
initializeEmitter();
}
rootNode.attachChild(fire);
fire.emitAllParticles();
fire.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
if (elapsedTime > 10f) {
rootNode.detachChild(fire);
fire.removeControl(this);
}
}
@Override
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
});
rootNode.attachChild(smoke);
smoke.emitAllParticles();
smoke.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
if (elapsedTime > 10f) {
rootNode.detachChild(smoke);
smoke.removeControl(this);
}
}
@Override
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
});
}
}

View File

@@ -0,0 +1,70 @@
package pp.mdga.client.animation;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import pp.mdga.client.InitControl;
import static pp.mdga.client.Util.linInt;
public class FadeControl extends ActionControl {
private float duration; // Duration of the fade effect
private float timeElapsed = 0;
private boolean init = false;
private float startAlpha;
private float endAlpha;
public FadeControl(float duration, float startAlpha, float endAlpha, Runnable actionAfter) {
super(actionAfter);
this.duration = duration;
this.startAlpha = startAlpha;
this.endAlpha = endAlpha;
}
public FadeControl(float duration, float startAlpha, float endAlpha) {
this(duration, startAlpha, endAlpha, null);
}
@Override
protected void initSpatial() {
init = true;
}
@Override
protected void controlUpdate(float tpf) {
if (!init) return;
timeElapsed += tpf;
float t = timeElapsed / duration; // Calculate progress (0 to 1)
if (t >= 1) {
// Fade complete
t = 1;
init = false;
spatial.removeControl(this);
action();
}
float alpha = linInt(startAlpha, endAlpha, t); // Interpolate alpha
// Update the material's alpha
if (spatial instanceof Geometry geometry) {
Material mat = geometry.getMaterial();
if (mat != null) {
ColorRGBA diffuse = (ColorRGBA) mat.getParam("Diffuse").getValue();
mat.setColor("Diffuse", new ColorRGBA(diffuse.r, diffuse.g, diffuse.b, alpha));
ColorRGBA ambient = (ColorRGBA) mat.getParam("Ambient").getValue();
mat.setColor("Ambient", new ColorRGBA(ambient.r, ambient.g, ambient.b, alpha));
// Disable shadows when the object is nearly invisible
if (alpha <= 0.1f) {
geometry.setShadowMode(RenderQueue.ShadowMode.Off);
} else {
geometry.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
}
} else throw new RuntimeException("Material is null");
} else throw new RuntimeException("Spatial is not instance of Geometry");
}
}

View File

@@ -0,0 +1,201 @@
package pp.mdga.client.animation;
import com.jme3.material.Material;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
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.client.acoustic.MdgaSound;
import pp.mdga.client.board.BoardHandler;
import pp.mdga.client.view.GameView;
import java.util.UUID;
/**
* The {@code JetAnimation} class handles the animation of a jet model in a 3D environment.
* It creates a jet model, animates its movement along a curved path, triggers an explosion at a target point,
* and performs additional actions upon animation completion.
*/
public class JetAnimation {
private final MdgaApp app;
private final Node rootNode;
private Spatial jetModel;
private final Vector3f spawnPoint;
private final Vector3f nodePoint;
private final Vector3f despawnPoint;
private final float curveHeight;
private final float animationDuration;
private Explosion explosion;
private final UUID id;
private Runnable actionAfter;
/**
* Constructor for the {@code JetAnimation} class.
*
* @param app The main application managing the jet animation.
* @param rootNode The root node to which the jet model will be attached.
* @param uuid A unique identifier for the animation.
* @param targetPoint The target point where the explosion will occur.
* @param curveHeight The height of the curve for the jet's flight path.
* @param animationDuration The total duration of the jet animation.
*/
public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint, float curveHeight, float animationDuration, Runnable actionAfter) {
Vector3f spawnPoint = targetPoint.add(170, 50, 50);
Vector3f controlPoint = targetPoint.add(new Vector3f(0, 0, -45));
Vector3f despawnPoint = targetPoint.add(-100, -100, 40);
this.app = app;
this.rootNode = rootNode;
this.spawnPoint = spawnPoint;
this.nodePoint = controlPoint;
this.despawnPoint = despawnPoint;
this.curveHeight = curveHeight;
this.animationDuration = animationDuration;
id = uuid;
explosion = new Explosion(app, rootNode, nodePoint);
this.actionAfter = actionAfter;
}
/**
* Starts the jet animation by spawning the jet model and initiating its movement along the predefined path.
*/
public void start() {
app.getAcousticHandler().playSound(MdgaSound.JET);
spawnJet();
animateJet();
}
/**
* Spawns the jet model at the designated spawn point, applying material, scaling, and rotation.
*/
private void spawnJet() {
jetModel = app.getAssetManager().loadModel(Asset.jet.getModelPath());
jetModel.setLocalTranslation(spawnPoint);
jetModel.scale(Asset.jet.getSize());
jetModel.rotate(FastMath.HALF_PI, 0, 0);
jetModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(Asset.jet.getDiffPath()));
jetModel.setMaterial(mat);
rootNode.attachChild(jetModel);
}
/**
* Animates the jet along a Bezier curve path, triggers the explosion effect at the appropriate time,
* and performs cleanup operations after the animation completes.
*/
private void animateJet() {
Vector3f controlPoint1 = spawnPoint.add(0, curveHeight, 0);
Vector3f controlPoint2 = nodePoint.add(0, curveHeight, 0);
BezierCurve3f curve = new BezierCurve3f(spawnPoint, controlPoint1, controlPoint2, despawnPoint);
app.getRootNode().addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
float progress = elapsedTime / animationDuration;
if(elapsedTime > 4.2f) {
explosion.trigger();
}
if (progress > 1) {
rootNode.detachChild(jetModel);
this.spatial.removeControl(this);
} else {
Vector3f currentPos = curve.interpolate(progress);
Vector3f direction = curve.interpolateDerivative(progress).normalizeLocal();
jetModel.setLocalTranslation(currentPos);
jetModel.lookAt(currentPos.add(direction), Vector3f.UNIT_Z);
jetModel.rotate(-FastMath.HALF_PI, 0, (float) Math.toRadians(-25));
}
if (elapsedTime > 6.0f) {
endAnim();
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {}
});
}
private void endAnim(){
actionAfter.run();
}
/**
* The {@code BezierCurve3f} class represents a 3D cubic Bezier curve.
* It provides methods to interpolate positions and derivatives along the curve.
*/
private static class BezierCurve3f {
private final Vector3f p0, p1, p2, p3;
/**
* Constructor for the {@code BezierCurve3f} class.
*
* @param p0 The starting point of the curve.
* @param p1 The first control point influencing the curve's shape.
* @param p2 The second control point influencing the curve's shape.
* @param p3 The endpoint of the curve.
*/
public BezierCurve3f(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
/**
* Interpolates a position along the curve at a given progress value {@code t}.
*
* @param t The progress value (0.0 to 1.0) along the curve.
* @return The interpolated position on the curve.
*/
public Vector3f interpolate(float t) {
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;
Vector3f point = p0.mult(uuu);
point = point.add(p1.mult(3 * uu * t));
point = point.add(p2.mult(3 * u * tt));
point = point.add(p3.mult(ttt));
return point;
}
/**
* Computes the derivative at a given progress value {@code t}, representing the direction along the curve.
*
* @param t The progress value (0.0 to 1.0) along the curve.
* @return The derivative (direction vector) at the specified progress.
*/
public Vector3f interpolateDerivative(float t) {
float u = 1 - t;
float tt = t * t;
Vector3f derivative = p0.mult(-3 * u * u);
derivative = derivative.add(p1.mult(3 * u * u - 6 * u * t));
derivative = derivative.add(p2.mult(6 * u * t - 3 * tt));
derivative = derivative.add(p3.mult(3 * tt));
return derivative;
}
}
}

View File

@@ -0,0 +1,200 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
import java.util.*;
public class MatrixAnimation extends ActionControl {
private MdgaApp app;
private static final Random RANDOM = new Random();
private Vector3f radarPos;
private Runnable runnable;
private boolean init = false;
private List<ParticleEmitter> activeEmitter = new ArrayList<>();
private ParticleEmitter radarEmitter = null;
private float timeElapsed = 0f;
private enum MatrixState{
RADAR_ON,
RADAR_OFF,
MATRIX_ON,
MATRIX_OFF
}
private MatrixState state;
public MatrixAnimation(MdgaApp app, Vector3f radarPos, Runnable runnable){
super(runnable);
this.app = app;
this.radarPos = radarPos;
}
@Override
protected void initSpatial() {
state = MatrixState.RADAR_ON;
timeElapsed = 0;
init = true;
radar();
}
@Override
protected void controlUpdate(float tpf) {
if(!init) return;
timeElapsed += tpf;
switch(state){
case RADAR_ON -> {
if(timeElapsed >= 2f){
state = MatrixState.RADAR_OFF;
timeElapsed = 0;
radarEmitter.setParticlesPerSec(0);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
app.getRootNode().detachChild(radarEmitter);
}
}, 3000);
}
}
case RADAR_OFF -> {
if(timeElapsed >= 0.1f){
state = MatrixState.MATRIX_ON;
timeElapsed = 0;
matrix();
}
}
case MATRIX_ON -> {
if(timeElapsed >= 3f){
state = MatrixState.MATRIX_OFF;
timeElapsed = 0;
turnOff();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
for (ParticleEmitter particleEmitter : activeEmitter){
app.getRootNode().detachChild(particleEmitter);
}
}
}, 3000);
}
}
case MATRIX_OFF -> {
if(timeElapsed >= 0.5f){
init = false;
spatial.removeControl(this);
action();
}
}
}
}
private void turnOff(){
for (ParticleEmitter particleEmitter : activeEmitter){
particleEmitter.setParticlesPerSec(0f);
}
}
private void radar(){
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture", app.getAssetManager().loadTexture("Images/particle/radar_beam.png"));
ParticleEmitter emitter = new ParticleEmitter("Effect", Type.Triangle, 50);
emitter.setMaterial(mat);
emitter.setImagesX(1); // columns
emitter.setImagesY(1); // rows
emitter.setSelectRandomImage(true);
emitter.setStartColor(ColorRGBA.White);
emitter.setEndColor(ColorRGBA.Black);
emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, 2));
emitter.getParticleInfluencer().setVelocityVariation(0f);
emitter.setStartSize(0.1f);
emitter.setEndSize(10);
emitter.setGravity(0, 0, 0);
float life = 2.6f;
emitter.setLowLife(life);
emitter.setHighLife(life);
emitter.setLocalTranslation(radarPos.add(new Vector3f(0,0,5)));
emitter.setParticlesPerSec(1.8f);
app.getRootNode().attachChild(emitter);
radarEmitter = emitter;
}
private void matrix(){
for(int i = 0; i < 5; i++){
particleStream(
generateMatrixColor(),
generateMatrixColor(),
getRandomFloat(0,1f),
getRandomPosition(),
getRandomFloat(1,2)
);
}
}
private void particleStream(ColorRGBA start, ColorRGBA end, float speedVar, Vector3f pos, float spawnVar){
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
mat.setTexture("Texture", app.getAssetManager().loadTexture("Images/particle/particle_cir.png"));
ParticleEmitter matrix = new ParticleEmitter("Effect", Type.Triangle, 50);
matrix.setMaterial(mat);
matrix.setImagesX(2); // columns
matrix.setImagesY(1); // rows
matrix.setSelectRandomImage(true);
matrix.setStartColor(start);
matrix.setEndColor(end);
matrix.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, -6f - speedVar));
matrix.getParticleInfluencer().setVelocityVariation(0f);
matrix.setStartSize(0.4f);
matrix.setEndSize(0.6f);
matrix.setGravity(0, 0, 2f);
matrix.setLowLife(3f);
matrix.setHighLife(3f);
matrix.setLocalTranslation(spatial.getLocalTranslation().add(pos).add(new Vector3f(0,0,15)));
matrix.setParticlesPerSec(spawnVar);
app.getRootNode().attachChild(matrix);
activeEmitter.add(matrix);
}
public static Vector3f getRandomPosition() {
// Generate a random angle in radians (0 to 2π)
float angle = (float) (2 * Math.PI * RANDOM.nextDouble());
// Generate a random radius with uniform distribution
float radius = (float) Math.sqrt(RANDOM.nextDouble());
radius *= 1f;
// Convert polar coordinates to Cartesian
float x = radius * (float) Math.cos(angle);
float y = radius * (float) Math.sin(angle);
return new Vector3f(x,y,0);
}
public static float getRandomFloat(float start, float end) {
if (start > end) {
throw new IllegalArgumentException("Start must be less than or equal to end.");
}
return start + RANDOM.nextFloat() * (end - start);
}
public static ColorRGBA generateMatrixColor() {
// Red is dominant
float red = 0.8f + RANDOM.nextFloat() * 0.2f; // Red channel: 0.8 to 1.0
// Green is moderately high
float green = 0.4f + RANDOM.nextFloat() * 0.3f; // Green channel: 0.4 to 0.7
// Blue is minimal
float blue = RANDOM.nextFloat() * 0.2f; // Blue channel: 0.0 to 0.2
float alpha = 1.0f; // Fully opaque
return new ColorRGBA(red, green, blue, alpha);
}
}

View File

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

View File

@@ -0,0 +1,141 @@
package pp.mdga.client.animation;
import com.jme3.material.Material;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
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.client.acoustic.MdgaSound;
import pp.mdga.client.board.BoardHandler;
import java.util.UUID;
/**
* The {@code MissileAnimation} class handles the animation of a missile moving along a parabolic path
* towards a target point in a 3D environment. It also triggers an explosion at the target upon impact.
*/
public class MissileAnimation {
private final Node rootNode;
private final MdgaApp app;
private final Vector3f start;
private final Vector3f target;
private final float flightTime;
private Explosion explosion;
private Spatial missileModel;
private UUID id;
/**
* Constructor for the {@code MissileAnimation} class.
*
* @param app The main application managing the missile animation.
* @param rootNode The root node to which the missile model will be attached.
* @param uuid A unique identifier for the missile animation.
* @param target The target point where the missile will explode.
* @param flightTime The total flight time of the missile.
*/
public MissileAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f target, float flightTime) {
this.app = app;
this.rootNode = rootNode;
this.flightTime = flightTime;
explosion = new Explosion(app, rootNode, target);
id = uuid;
this.target = target.add(new Vector3f(1.5f, -1, 0));
start = BoardHandler.gridToWorld(12, 0);
start.add(new Vector3f(0, 0, 0));
}
/**
* Starts the missile animation by loading the missile model and initiating its parabolic movement.
*/
public void start() {
Smoke s = new Smoke(app, rootNode, start);
s.trigger();
loadMissile();
app.getAcousticHandler().playSound(MdgaSound.MISSILE);
animateMissile();
}
/**
* Loads the missile model into the scene, applies scaling, material, and sets its initial position.
*/
private void loadMissile() {
missileModel = app.getAssetManager().loadModel(Asset.missile.getModelPath());
missileModel.scale(Asset.missile.getSize());
missileModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(Asset.missile.getDiffPath()));
missileModel.setMaterial(mat);
missileModel.setLocalTranslation(start);
rootNode.attachChild(missileModel);
}
/**
* Animates the missile along a parabolic path, triggers the explosion near the target,
* and removes the missile model after the animation completes.
*/
private void animateMissile() {
missileModel.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
float progress = elapsedTime / flightTime;
if (progress >= 0.95f) {
explosion.trigger();
}
if (progress >= 1) {
explosion.trigger();
rootNode.detachChild(missileModel);
this.spatial.removeControl(this);
return;
}
Vector3f currentPosition = computeParabolicPath(start, target, progress);
missileModel.setLocalTranslation(currentPosition);
Vector3f direction = computeParabolicPath(start, target, progress + 0.01f)
.subtract(currentPosition)
.normalizeLocal();
missileModel.lookAt(currentPosition.add(direction), Vector3f.UNIT_Y);
missileModel.rotate(0, FastMath.HALF_PI, 0);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
});
}
/**
* Computes a position along a parabolic path at a given progress value {@code t}.
*
* @param start The starting point of the missile's flight.
* @param target The target point of the missile's flight.
* @param t The progress value (0.0 to 1.0) along the flight path.
* @return The interpolated position along the parabolic path.
*/
private Vector3f computeParabolicPath(Vector3f start, Vector3f target, float t) {
Vector3f midPoint = start.add(target).multLocal(0.5f);
midPoint.addLocal(0, 0, 20);
Vector3f startToMid = FastMath.interpolateLinear(t, start, midPoint);
Vector3f midToTarget = FastMath.interpolateLinear(t, midPoint, target);
return FastMath.interpolateLinear(t, startToMid, midToTarget);
}
}

View File

@@ -0,0 +1,99 @@
package pp.mdga.client.animation;
import com.jme3.math.Vector3f;
import static pp.mdga.client.Util.*;
/**
* A control that smoothly moves a spatial from an initial position to an end position
* using a quadratic interpolation, with the option to perform an action after the movement is complete.
* The movement path includes an intermediate "middle" position at a specified height.
*
* <p>Movement speed can be adjusted by modifying the MOVE_SPEED constant. The movement easing follows
* an ease-in-out curve to create a smooth start and stop effect.
* </p>
*/
public class MoveControl extends ActionControl {
private boolean moving;
private final Vector3f initPos;
private final Vector3f endPos;
private final Vector3f middlePos;
private final float height;
private final float duration;
private float timer = 0;
private boolean easing;
/**
* Creates a new MoveControl with specified initial and end positions, and an action to run after the movement.
* The movement follows a path with a midpoint at a fixed height.
*
* @param initPos The starting position of the spatial.
* @param endPos The target position of the spatial.
* @param actionAfter A Runnable that will be executed after the movement finishes.
*/
public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){
this(initPos, endPos, actionAfter, 2, 1, true);
}
public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter, float height, float duration, boolean easing){
super(actionAfter);
moving = false;
this.initPos = initPos;
this.endPos = endPos;
this.height = height;
this.duration = duration;
this.easing = easing;
middlePos = new Vector3f(
(initPos.x + endPos.x) / 2,
(initPos.y + endPos.y) / 2,
height
);
}
/**
* Initializes the movement by resetting the progress and setting the moving flag to true.
* This is called automatically when the spatial is set.
*/
@Override
protected void initSpatial() {
moving = true;
timer = 0;
}
/**
* Updates the movement of the spatial by interpolating its position along the defined path.
* The movement is smoothed using an easing function.
* Once the movement reaches the target, the {@link #end()} method is called to finish the movement.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
if(!moving) return;
timer += tpf;
float t = timer / duration;
if (t >= 1) t = 1;
float interpolated = easing ? easeInOut(t) : t;
spatial.setLocalTranslation(quadInt(initPos,middlePos,endPos, interpolated));
if(t >= 1) end();
}
/**
* Ends the movement by stopping the interpolation, running the action after the movement,
* and removing this control from the spatial.
*/
private void end(){
moving = false;
spatial.removeControl(this);
action();
}
}

View File

@@ -0,0 +1,167 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import pp.mdga.client.Asset;
import pp.mdga.client.InitControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.board.TankTopControl;
import java.util.Timer;
import java.util.TimerTask;
import static com.jme3.material.Materials.LIGHTING;
import static com.jme3.material.Materials.UNSHADED;
public class ShellAnimation extends ActionControl {
private static final float FLYING_DURATION = 1.25f;
private static final float FLYING_HEIGHT = 12f;
private TankTopControl tankTopControl;
private MdgaApp app;
public ShellAnimation(TankTopControl tankTopControl, MdgaApp app, Runnable actionAfter){
super(actionAfter);
this.tankTopControl = tankTopControl;
this.app = app;
}
@Override
protected void initSpatial() {
tankTopControl.rotate(spatial.getLocalTranslation(), this::shoot);
app.getAcousticHandler().playSound(MdgaSound.TURRET_ROTATE);
app.getRootNode().attachChild(createShell());
}
private Vector3f getShootPos(){
Vector3f localOffset = new Vector3f(0, -5.4f, 2.9f);
Quaternion turretRotation = tankTopControl.getSpatial().getLocalRotation();
Vector3f transformedOffset = turretRotation.mult(localOffset);
return tankTopControl.getSpatial().getLocalTranslation().add(transformedOffset);
}
private void shoot(){
app.getAcousticHandler().playSound(MdgaSound.TANK_SHOOT);
Vector3f shootPos = getShootPos();
createEffect(
shootPos,
"Images/particle/flame.png",
2, 2,
1, 3,
1f,
0.3f, 0.7f,
new ColorRGBA(1f, 0.8f, 0.4f, 0.5f),
new ColorRGBA(1f, 0f, 0f, 0f)
);
createEffect(
shootPos,
"Images/particle/vapor_cloud.png",
3, 3,
0.3f, 0.8f,
10,
0.1f, 0.35f,
new ColorRGBA(0.5f,0.5f,0.5f,0.5f),
ColorRGBA.Black
);
Spatial shell = createShell();
app.getRootNode().attachChild(shell);
shell.addControl(new ShellControl(this::hitExplosion, shootPos, spatial.getLocalTranslation(), FLYING_HEIGHT, FLYING_DURATION, app.getAssetManager()));
}
private Spatial createShell(){
Spatial model = app.getAssetManager().loadModel(Asset.shell.getModelPath());
model.scale(.16f);
model.setLocalTranslation(tankTopControl.getSpatial().getLocalTranslation());
Vector3f shootPos = tankTopControl.getSpatial().getLocalTranslation();
Vector3f targetPos = spatial.getLocalTranslation();
Vector3f direction = targetPos.subtract(shootPos).normalize();
Quaternion rotation = new Quaternion();
rotation.lookAt(direction, new Vector3f(1,0,0)); // Assuming UNIT_Y is the up vector
model.setLocalRotation(rotation);
model.rotate(FastMath.HALF_PI,0,0);
Material mat = new Material(app.getAssetManager(), LIGHTING);
mat.setBoolean("UseMaterialColors", true);
ColorRGBA color = ColorRGBA.fromRGBA255(143,117,0,255);
mat.setColor("Diffuse", color);
mat.setColor("Ambient", color);
model.setMaterial(mat);
return model;
}
private void hitExplosion(){
app.getAcousticHandler().playSound(MdgaSound.TANK_EXPLOSION);
createEffect(
spatial.getLocalTranslation().setZ(1),
"Images/particle/flame.png",
2, 2,
1, 5,
2f,
0.3f, 0.7f,
new ColorRGBA(1f, 0.8f, 0.4f, 0.5f),
new ColorRGBA(1f, 0f, 0f, 0f)
);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
action();
}
}, 800);
}
private void createEffect(Vector3f shootPos,
String image,
int x, int y,
float startSize, float endSize,
float velocity,
float lowLife, float highLife,
ColorRGBA start, ColorRGBA end){
// Create a particle emitter for the explosion
ParticleEmitter explosionEmitter = new ParticleEmitter("Explosion", ParticleMesh.Type.Triangle, 100);
Material explosionMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
explosionMat.setTexture("Texture", app.getAssetManager().loadTexture(image));
explosionEmitter.setMaterial(explosionMat);
// Particle properties
explosionEmitter.setImagesX(x); // Columns in the texture
explosionEmitter.setImagesY(y); // Rows in the texture
explosionEmitter.setSelectRandomImage(true); // Randomize images for variety
explosionEmitter.setStartColor(start); // Bright yellowish orange
explosionEmitter.setEndColor(end); // Fade to transparent red
explosionEmitter.setStartSize(startSize); // Initial size
explosionEmitter.setEndSize(endSize); // Final size
explosionEmitter.setLowLife(lowLife); // Minimum lifetime of particles
explosionEmitter.setHighLife(highLife); // Maximum lifetime of particles
explosionEmitter.setGravity(0, 0, 1); // Gravity to pull particles down
explosionEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, velocity));
explosionEmitter.getParticleInfluencer().setVelocityVariation(1f); // Adds randomness to the initial velocity
explosionEmitter.setFacingVelocity(true); // Particles face their velocity direction
explosionEmitter.setLocalTranslation(shootPos);
explosionEmitter.setParticlesPerSec(0);
explosionEmitter.emitAllParticles();
app.getRootNode().attachChild(explosionEmitter);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
app.getRootNode().detachChild(explosionEmitter);
}
}, 1000);
}
}

View File

@@ -0,0 +1,89 @@
package pp.mdga.client.animation;
import com.jme3.asset.AssetManager;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
public class ShellControl extends ActionControl {
private final Vector3f shootPos;
private final Vector3f endPos;
private final float height;
private final float duration;
private Vector3f oldPos;
private ParticleEmitter emitter;
private AssetManager assetManager;
public ShellControl(Runnable runnable, Vector3f shootPos, Vector3f endPos, float height, float duration, AssetManager assetManager){
super(runnable);
this.shootPos = shootPos;
this.endPos = endPos;
this.height = height;
this.duration = duration;
this.assetManager = assetManager;
}
@Override
protected void initSpatial() {
spatial.addControl(new MoveControl(
shootPos,
endPos,
()->{
emitter.killAllParticles();
emitter.setParticlesPerSec(0);
emitter.removeFromParent();
spatial.removeControl(this);
spatial.removeFromParent();
action();
},
height,
duration,
false
));
oldPos = spatial.getLocalTranslation().clone();
createEmitter();
}
private void createEmitter() {
emitter = new ParticleEmitter("ShellTrail", ParticleMesh.Type.Triangle, 200);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture", assetManager.loadTexture("Images/particle/line.png")); // Nutze eine schmale, linienartige Textur
emitter.setMaterial(mat);
// Comic-Style Farben
emitter.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f)); // Reinweiß
emitter.setEndColor(new ColorRGBA(1f, 1f, 1f, 0f)); // Transparent
// Partikelgröße und Lebensdauer
emitter.setStartSize(0.15f); // Startgröße
emitter.setEndSize(0.1f); // Endgröße
emitter.setLowLife(0.14f); // Sehr kurze Lebensdauer
emitter.setHighLife(0.14f);
emitter.setGravity(0, 0, 0); // Keine Gravitation
emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
emitter.getParticleInfluencer().setVelocityVariation(0f); // Kein Variationsspielraum
// Hohe Dichte für eine glatte Spur
emitter.setParticlesPerSec(500);
// Zur Shell hinzufügen
spatial.getParent().attachChild(emitter);
}
@Override
protected void controlUpdate(float tpf) {
Vector3f direction = spatial.getLocalTranslation().subtract(oldPos).normalize();
if (direction.lengthSquared() > 0) {
spatial.getLocalRotation().lookAt(direction, Vector3f.UNIT_X);
spatial.rotate(FastMath.HALF_PI,0,0);
}
oldPos = spatial.getLocalTranslation().clone();
emitter.setLocalTranslation(spatial.getLocalTranslation().clone());
}
}

View File

@@ -0,0 +1,126 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
public class Smoke {
private final Node rootNode;
private final MdgaApp app;
private final Vector3f location;
private ParticleEmitter fire;
private ParticleEmitter smoke;
private boolean triggered = false;
private final Material mat;
/**
* Constructor for the {@code Explosion} class.
*
* @param app The main application managing the explosion.
* @param rootNode The root node to which the explosion effects will be attached.
* @param location The location of the explosion in world coordinates.
*/
public Smoke(MdgaApp app, Node rootNode, Vector3f location) {
this.app = app;
this.rootNode = rootNode;
this.location = location;
this.mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
}
/**
* Initializes the particle emitters for the explosion effect.
* Configures the fire and smoke emitters with appearance, behavior, and lifespan.
*/
private void initializeEmitter() {
fire = new ParticleEmitter("Effect", ParticleMesh.Type.Triangle,50);
fire.setMaterial(mat);
fire.setStartColor(ColorRGBA.DarkGray);
fire.setEndColor(ColorRGBA.DarkGray);
fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0.2f,0.2f,8f));
fire.getParticleInfluencer().setVelocityVariation(0.4f);
fire.setStartSize(0.1f);
fire.setEndSize(0.8f);
fire.setGravity(0, 0, -0.1f);
fire.setLowLife(0.5f);
fire.setHighLife(1.2f);
fire.setParticlesPerSec(0);
fire.setLocalTranslation(location);
smoke = new ParticleEmitter("Effect2", ParticleMesh.Type.Triangle,40);
smoke.setMaterial(mat);
smoke.setImagesX(2);
smoke.setImagesY(2);
smoke.setStartColor(ColorRGBA.DarkGray);
smoke.setEndColor(new ColorRGBA(0.05f, 0.05f, 0.05f, 1));
smoke.getParticleInfluencer().setInitialVelocity(new Vector3f(0.0f,0.0f,2f));
smoke.getParticleInfluencer().setVelocityVariation(0.5f);
smoke.setStartSize(0.2f);
smoke.setEndSize(0.5f);
smoke.setGravity(0, 0, -0.3f);
smoke.setLowLife(1.2f);
smoke.setHighLife(2.5f);
smoke.setParticlesPerSec(0);
smoke.setLocalTranslation(location);
app.getAcousticHandler().playSound(MdgaSound.EXPLOSION);
}
/**
* Triggers the explosion effect by attaching and activating the particle emitters for fire and smoke.
* Both emitters are automatically detached after a predefined duration.
*/
public void trigger() {
if (!triggered) {
triggered = true;
initializeEmitter();
}
rootNode.attachChild(fire);
fire.emitAllParticles();
fire.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
if (elapsedTime > 10f) {
rootNode.detachChild(fire);
fire.removeControl(this);
}
}
@Override
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
});
rootNode.attachChild(smoke);
smoke.emitAllParticles();
smoke.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
if (elapsedTime > 10f) {
rootNode.detachChild(smoke);
smoke.removeControl(this);
}
}
@Override
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
});
}
}

View File

@@ -0,0 +1,213 @@
package pp.mdga.client.animation;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import pp.mdga.game.BonusCard;
/**
* A control that manages the animation of symbols representing different bonus card states.
* The symbol can animate with zoom, rotation, and translation effects based on the state of the bonus card.
*
* <p>The control supports three main states: SHIELD, SWAP, and TURBO. Each state has its own specific animation logic:
* <ul>
* <li>SHIELD: Zooms in and out, with a scaling effect.</li>
* <li>SWAP: Rotates the symbol 360 degrees.</li>
* <li>TURBO: Moves the symbol along the Y-axis with a zoom effect.</li>
* </ul>
* </p>
*/
public class SymbolControl extends InitControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float zoomSpeed = 1f;
private float zoomFactor = 3f;
private float progress = 0;
private BonusCard state;
private float rotationSpeed = 0.8f;
private Quaternion initialRotation = null;
private float y = 5;
/**
* Updates the symbol animation based on the current bonus card state.
* The method calls the corresponding update method for each state (SHIELD, SWAP, TURBO).
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
if (state == null) return;
switch (state) {
case SHIELD -> shieldUpdate(tpf);
case SWAP -> swapUpdate(tpf);
case TURBO -> turboUpdate(tpf);
case HIDDEN -> throw new RuntimeException("forbidden state");
}
}
/**
* Updates the symbol when the state is SHIELD. The symbol zooms in and then zooms out.
* When the zooming out finishes, the symbol is removed from the parent spatial.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
private void shieldUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
progress = 0;
}
}
}
/**
* Updates the symbol when the state is SWAP. The symbol rotates 360 degrees.
* After the rotation finishes, the symbol is removed from the parent spatial.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
private void swapUpdate(float tpf) {
if (initialRotation == null) {
initialRotation = spatial.getLocalRotation().clone();
}
progress += tpf * rotationSpeed;
if (progress < 0) return;
float angle = lerp(0, 360, easeInOut(progress));
Quaternion newRotation = new Quaternion();
newRotation.fromAngleAxis((float) Math.toRadians(angle), new Vector3f(0, 1, 0));
spatial.setLocalRotation(initialRotation.mult(newRotation));
if (progress >= 1.2f) {
state = null;
initialRotation = null;
progress = 0;
spatial.removeFromParent();
}
}
/**
* Updates the symbol when the state is TURBO. The symbol moves along the Y-axis with a zoom effect.
* After the movement finishes, the symbol is removed from the parent spatial.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
private void turboUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
float y = lerp(-this.y, 0, easeOut(progress));
spatial.setLocalTranslation(0, y, 0);
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
float y = lerp(0, this.y, easeIn(progress));
spatial.setLocalTranslation(0, y, 0);
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
}
}
}
/**
* Starts the SHIELD animation by zooming the symbol in and out.
* The symbol will first zoom in and then zoom out, and will be removed from the parent spatial once done.
*/
public void shield() {
if (state != null) throw new RuntimeException("another state is avtive");
state = BonusCard.SHIELD;
zoomingIn = true;
zoomingOut = false;
progress = 0;
spatial.setLocalScale(1f);
}
/**
* Starts the SWAP animation by rotating the symbol 360 degrees.
* The symbol will rotate once and then be removed from the parent spatial.
*/
public void swap() {
if (state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(3);
state = BonusCard.SWAP;
progress = -0.2f;
}
/**
* Starts the TURBO animation by moving the symbol along the Y-axis.
* The symbol will move upwards and then return to its initial position.
*/
public void turbo() {
if (state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(2);
state = BonusCard.TURBO;
zoomingIn = true;
zoomingOut = false;
progress = 0;
}
/**
* Performs linear interpolation between two values.
*
* @param start The starting value.
* @param end The target value.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated value.
*/
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
/**
* Ease-out function for smoothing the interpolation.
*
* @param t The interpolation parameter (0 <= t <= 1).
* @return The eased value.
*/
private static float easeOut(float t) {
return (float) Math.sqrt(1 - Math.pow(t - 1, 2));
}
/**
* Ease-in-out function for smoothing the interpolation.
*
* @param t The interpolation parameter (0 <= t <= 1).
* @return The eased value.
*/
private float easeInOut(float t) {
if (t > 1) t = 1;
return (float) -(Math.cos(Math.PI * t) - 1) / 2;
}
/**
* Ease-in function for smoothing the interpolation.
*
* @param t The interpolation parameter (0 <= t <= 1).
* @return The eased value.
*/
private float easeIn(float t) {
return t * t * t * t;
}
}

View File

@@ -0,0 +1,110 @@
package pp.mdga.client.animation;
import pp.mdga.client.InitControl;
/**
* A control that applies a zoom effect to a spatial, smoothly scaling it in and out.
* The zoom effect can be customized with speed and scaling factor.
*
* <p>The control supports zooming in and out with ease-in and ease-out transitions.
* It starts by zooming in, and once complete, it zooms out, eventually removing the spatial from its parent when the animation ends.</p>
*/
public class ZoomControl extends InitControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float progress = 0;
private float zoomSpeed = 1f;
private float zoomFactor = 1f;
/**
* Constructs a new ZoomControl with the default zoom speed.
*/
public ZoomControl() {
}
/**
* Constructs a new ZoomControl with a specified zoom speed.
*
* @param speed The speed at which the zoom effect occurs.
*/
public ZoomControl(float speed) {
zoomSpeed = speed;
}
/**
* Initializes the spatial for the zoom effect. This method is called when the control is added to the spatial.
* It sets the zooming state to zooming in.
*/
@Override
protected void initSpatial() {
zoomingIn = true;
}
/**
* Updates the zoom effect over time, either zooming in or zooming out.
*
* @param tpf Time per frame, the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingOut = false;
end();
}
}
}
/**
* Ends the zoom animation by removing the spatial from its parent and the control from the spatial.
*/
private void end() {
spatial.removeFromParent();
spatial.removeControl(this);
}
/**
* Performs linear interpolation between two values.
*
* @param start The starting value.
* @param end The target value.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated value.
*/
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
/**
* Ease-out function for smoothing the zoom-in transition.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The eased value.
*/
private float easeOut(float x) {
return x == 1 ? 1 : (float) (1 - Math.pow(2, -10 * x));
}
/**
* Ease-in function for smoothing the zoom-out transition.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The eased value.
*/
private float easeIn(float x) {
return x == 0 ? 0 : (float) Math.pow(2, 10 * x - 10);
}
}

View File

@@ -2,4 +2,7 @@
import pp.mdga.client.Asset;
/**
* Record for holding Asset information
*/
record AssetOnMap(Asset asset, int x, int y, float rot) {}

View File

@@ -1,6 +1,9 @@
package pp.mdga.client.board;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
@@ -9,41 +12,64 @@
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.animation.*;
import pp.mdga.client.gui.DiceControl;
import pp.mdga.game.Color;
import java.util.*;
/**
* BoardHandler is responsible for managing the game board in the MDGA client, including handling
* the initialization, movement, and management of game pieces and assets.
* It works closely with the MdgaApp to create and manipulate 3D models of assets, track player pieces,
* and manage movement across the game board.
*/
public class BoardHandler {
// Constants defining the grid size and elevation of the board
private static final float GRID_SIZE = 1.72f;
private static final float GRID_ELEVATION = 0.0f;
private static final String MAP_NAME = "Maps/map.mdga";
// The main application instance for accessing game assets and logic
private final MdgaApp app;
// Collection of in-game assets and board elements
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<Color, Map<UUID, NodeControl>> waitingNodes;
private Map<UUID, Color> pieceColor;
private Node rootNodeBoard;
private final Node rootNodeBoard;
private final Node rootNode;
private final FilterPostProcessor fpp;
private boolean isInitialised;
// Flags and lists for handling piece selection and movement
private List<PieceControl> selectableOwnPieces;
private List<PieceControl> selectableEnemyPieces;
private List<NodeControl> outlineNodes;
private PieceControl selectedOwnPiece;
private PieceControl selectedEnemyPiece;
private DiceControl diceControl;
//Radar Position for Matrix animation
private Vector3f radarPos;
//TankTop for shellAnimation
private TankTopControl tankTop;
/**
* Creates a new BoardHandler.
*
* @param app The main application instance
* @param rootNode The root node where the board will be attached
* @param fpp The post-processor for effects like shadows or filters
* @throws RuntimeException if the app is null
*/
public BoardHandler(MdgaApp app, Node rootNode, FilterPostProcessor fpp) {
if(app == null) throw new RuntimeException("app is null");
@@ -54,6 +80,9 @@ public BoardHandler(MdgaApp app, Node rootNode, FilterPostProcessor fpp) {
isInitialised = false;
}
/**
* Initializes the game board by setting up the pieces and nodes.
*/
public void init() {
isInitialised = true;
selectableOwnPieces = new ArrayList<>();
@@ -65,17 +94,30 @@ public void init() {
rootNode.attachChild(rootNodeBoard);
}
/**
* Shuts down the board handler by detaching all board-related nodes and clearing selected pieces.
*/
public void shutdown(){
clearSelectable();
isInitialised = false;
rootNode.detachChild(rootNodeBoard);
}
/**
* Adds an asset to the map of player assets, ensuring that the player does not have too many assets.
*
* @param col The color of the player
* @param assetOnMap The asset to be added
* @throws RuntimeException if there are too many assets for the player
*/
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);
}
/**
* Initializes the map with the assets loaded from the map file and corresponding nodes.
*/
private void initMap() {
pieces = new HashMap<>();
colorAssetsMap = new HashMap<>();
@@ -86,6 +128,12 @@ private void initMap() {
pieceColor = new HashMap<>();
diceControl = new DiceControl(app.getAssetManager());
diceControl.create(new Vector3f(0,0,0), 0.7f, true);
waitingNodes = new HashMap<>();
waitingNodes.put(Color.AIRFORCE, new HashMap<>());
waitingNodes.put(Color.ARMY, new HashMap<>());
waitingNodes.put(Color.NAVY, new HashMap<>());
waitingNodes.put(Color.CYBER, new HashMap<>());
List<AssetOnMap> assetOnMaps = MapLoader.loadMap(MAP_NAME);
@@ -105,12 +153,31 @@ private void initMap() {
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);
case radar -> addRadar(assetOnMap);
case tankShoot -> addTankShoot(assetOnMap);
default -> displayAsset(assetOnMap);
}
}
}
private void addTankShoot(AssetOnMap assetOnMap) {
displayAsset(assetOnMap);
tankTop = displayAndControl(new AssetOnMap(Asset.tankShootTop, assetOnMap.x(), assetOnMap.y(), assetOnMap.rot()), new TankTopControl());
}
private void addRadar(AssetOnMap assetOnMap) {
radarPos = gridToWorld(assetOnMap.x(), assetOnMap.y());
displayAsset(assetOnMap);
}
/**
* Converts an asset to its corresponding color.
*
* @param asset The asset to be converted
* @return The color associated with the asset
* @throws RuntimeException if the asset is invalid
*/
private Color assetToColor(Asset asset) {
return switch (asset) {
case lw -> Color.AIRFORCE;
@@ -121,6 +188,14 @@ private Color assetToColor(Asset asset) {
};
}
/**
* Creates a 3D model of an asset and adds it to the board.
*
* @param asset The asset to be displayed
* @param pos The position of the asset on the board
* @param rot The rotation of the asset
* @return The Spatial representation of the asset
*/
private Spatial createModel(Asset asset, Vector3f pos, float rot) {
String modelName = asset.getModelPath();
String texName = asset.getDiffPath();
@@ -129,39 +204,86 @@ private Spatial createModel(Asset asset, Vector3f pos, float rot) {
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));
mat.setBoolean("UseMaterialColors", true); // Required for Material Colors
mat.setColor("Diffuse", new ColorRGBA(1, 1, 1, 1)); // White color with full alpha
mat.setColor("Ambient", new ColorRGBA(1, 1, 1, 1)); // Ambient color with full alpha
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
model.setMaterial(mat);
rootNodeBoard.attachChild(model);
rootNodeBoard.attachChild(model);
return model;
}
private static Vector3f gridToWorld(int x, int y) {
/**
* Converts grid coordinates to world space.
*
* @param x The x-coordinate on the grid
* @param y The y-coordinate on the grid
* @return The corresponding world position
*/
public static Vector3f gridToWorld(int x, int y) {
return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
}
/**
* Displays an asset on the map at the given position with the specified rotation.
*
* @param assetOnMap The asset to be displayed.
* @return A spatial representation of the asset at the specified location and rotation.
*/
private Spatial displayAsset(AssetOnMap assetOnMap) {
int x = assetOnMap.x();
int y = assetOnMap.y();
return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
}
/**
* Adds a visual representation of an asset to the scene, attaches a control to it, and returns the control.
*
* @param assetOnMap The asset to be displayed in the 3D environment.
* @param control The control to be added to the spatial representing the asset.
* @param <T> The type of control, extending {@code AbstractControl}.
* @return The control that was added to the spatial.
*/
private <T extends AbstractControl> T displayAndControl(AssetOnMap assetOnMap, T control) {
Spatial spatial = displayAsset(assetOnMap);
spatial.addControl(control);
return control;
}
/**
* Moves a piece in the 3D environment to the location of a specified node.
*
* @param pieceControl The control managing the piece to be moved.
* @param nodeControl The control managing the target node to which the piece will move.
*/
private void movePieceToNode(PieceControl pieceControl, NodeControl nodeControl){
pieceControl.setLocation(nodeControl.getLocation());
}
/**
* Adds a home node for a specific player color, attaching it to the map of home nodes.
*
* @param map The map storing lists of home nodes by player color.
* @param color The color associated with the home nodes to be added.
* @param assetOnMap The asset representing the home node in the 3D environment.
* @throws RuntimeException if more than 4 home nodes are added for a single color.
*/
private void addHomeNode(Map<Color, List<NodeControl>> map, Color color, AssetOnMap assetOnMap){
List<NodeControl> homeNodes = addItemToMapList(map, color, displayAndControl(assetOnMap, new NodeControl(app, fpp)));
if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
}
/**
* Calculates the rotation angle required to move a piece from one position to another.
*
* @param prev The previous position.
* @param next The target position.
* @return The rotation angle in degrees.
*/
private float getRotationMove(Vector3f prev, Vector3f next) {
Vector3f direction = next.subtract(prev).normalizeLocal();
//I had to reverse dir.y, because then it worked.
@@ -170,6 +292,14 @@ private float getRotationMove(Vector3f prev, Vector3f next) {
return newRot;
}
/**
* Recursively moves a piece from its current index to the destination index,
* to keep track of the piece rotation.
*
* @param uuid The UUID of the piece to move.
* @param curIndex The current index of the piece.
* @param moveIndex The target index to move the piece to.
*/
private void movePieceRek(UUID uuid, int curIndex, int moveIndex){
if (curIndex == moveIndex) return;
@@ -185,6 +315,16 @@ private void movePieceRek(UUID uuid, int curIndex, int moveIndex){
movePieceRek(uuid, curIndex, moveIndex);
}
/**
* Adds an item to a list in a map. If the key does not exist in the map, a new list is created.
*
* @param map The map containing lists of items.
* @param key The key associated with the list in the map.
* @param item The item to be added to the list.
* @param <T> The type of items in the list.
* @param <E> The type of the key in the map.
* @return The updated list associated with the specified key.
*/
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);
@@ -192,16 +332,37 @@ private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
return list;
}
/**
* Removes an item from a list in a map. If the key does not exist in the map, a new list is created.
*
* @param map The map containing lists of items.
* @param key The key associated with the list in the map.
* @param item The item to be removed from the list.
* @param <T> The type of items in the list.
* @param <E> The type of the key in the map.
*/
private <T, E> void 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);
}
/**
* Calculates the mean position of the waiting nodes for a specific color.
*
* @param color The color associated with the waiting nodes.
* @return The mean position of the waiting nodes as a {@code Vector3f}.
*/
private Vector3f getWaitingPos(Color color){
return getMeanPosition(waitingNodesMap.get(color).stream().map(NodeControl::getLocation).toList());
}
/**
* Gets the mean position of a list of vectors.
*
* @param vectors The list of vectors.
* @return The mean position as a Vector3f.
*/
public static Vector3f getMeanPosition(List<Vector3f> vectors) {
if (vectors.isEmpty()) return new Vector3f(0, 0, 0);
@@ -212,9 +373,14 @@ public static Vector3f getMeanPosition(List<Vector3f> vectors) {
return sum.divide(vectors.size());
}
//public methods****************************************************************************************************
/**
* Adds a player to the game by associating a color and a list of UUIDs to corresponding assets and waiting nodes.
*
* @param color the color of the player
* @param uuid the list of UUIDs representing the player's assets
* @throws RuntimeException if the number of assets or waiting nodes does not match the provided UUIDs
*/
public void addPlayer(Color color, List<UUID> uuid) {
if (!isInitialised) 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");
@@ -226,20 +392,34 @@ public void addPlayer(Color color, List<UUID> uuid) {
for (int i = 0; i < playerAssets.size(); i++){
AssetOnMap assetOnMap = playerAssets.get(i);
UUID pieceUuid = uuid.get(i);
// Initialize PieceControl
PieceControl pieceControl = displayAndControl(assetOnMap, new PieceControl(assetOnMap.rot(), app.getAssetManager(), app, fpp));
pieceControl.setRotation(assetOnMap.rot());
movePieceToNode(pieceControl, waitNodes.get(i));
pieces.put(uuid.get(i), pieceControl);
// Assign piece to waiting node
NodeControl waitNode = getNextWaitingNode(color);
waitingNodes.get(color).put(pieceUuid, waitNode);
pieceColor.put(uuid.get(i), color);
// Move piece to node
movePieceToNode(pieceControl, waitNode);
// Update mappings
pieces.put(pieceUuid, pieceControl);
pieceColor.put(pieceUuid, color);
addItemToMapList(waitingPiecesMap, color, pieceControl);
}
}
public void moveHomePiece(UUID uuid, int index){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Moves a piece to its corresponding home node based on the given index.
*
* @param uuid the UUID of the piece to move
* @param index the index of the home node to move the piece to
* @throws RuntimeException if the UUID is not mapped to a color or if the home nodes are not properly defined
*/
private void moveHomePiece(UUID uuid, int index){
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
@@ -257,90 +437,146 @@ public void moveHomePiece(UUID uuid, int index){
NodeControl lastHomeNode = homeNodes.get(homeNodes.size()-1);
pieceControl.setRotation(getRotationMove(firstHomeNode.getLocation(), lastHomeNode.getLocation()));
app.getModelSynchronize().animationEnd();
}
public void movePieceStart(UUID uuid, int nodeIndex){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Starts the movement of a piece to a target node based on the given index.
*
* @param uuid the UUID of the piece to move
* @param nodeIndex the index of the target node to move the piece to
* @throws RuntimeException if the UUID is not mapped to a color or the piece control is not found
* @throws IllegalArgumentException if the node index is invalid
*/
private void movePieceStart(UUID uuid, int nodeIndex){
// Farbe des Pieces abrufen
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
if (color == null) throw new RuntimeException("UUID is not mapped to a color");
// PieceControl abrufen
PieceControl pieceControl = pieces.get(uuid);
movePieceToNode(pieceControl, infield.get(nodeIndex));
if (pieceControl == null) throw new RuntimeException("PieceControl not found for UUID: " + uuid);
// Zielknoten abrufen und prüfen
if (nodeIndex < 0 || nodeIndex >= infield.size()) {
throw new IllegalArgumentException("Invalid nodeIndex: " + nodeIndex);
}
NodeControl targetNode = infield.get(nodeIndex);
movePieceToNode(pieceControl, targetNode);
removeItemFromMapList(waitingPiecesMap, color, pieceControl);
waitingNodes.get(color).remove(uuid);
app.getModelSynchronize().animationEnd();
}
public void movePiece(UUID uuid, int curIndex, int moveIndex){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Moves a piece from its current position to the target position based on the given indexes.
*
* @param uuid the UUID of the piece to move
* @param curIndex the current index of the piece
* @param moveIndex the target index of the move
*/
private void movePiece(UUID uuid, int curIndex, int moveIndex){
movePieceRek(uuid, curIndex, moveIndex);
app.getModelSynchronize().animationEnd();
}
public void throwPiece(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Throws a piece to the next available waiting node and updates the waiting node mapping.
*
* @param uuid the UUID of the piece to throw
* @throws RuntimeException if the UUID is not mapped to a color or if no available waiting nodes are found
*/
private void throwPiece(UUID uuid){
// Farbe des Pieces abrufen
Color color = pieceColor.get(uuid);
if(color == null) throw new RuntimeException("uuid is not mapped to a color");
if (color == null) throw new RuntimeException("UUID is not mapped to a color");
// PieceControl abrufen
PieceControl pieceControl = pieces.get(uuid);
List<NodeControl> waitNodes = waitingNodesMap.get(color);
List<PieceControl> waitPieces = waitingPiecesMap.get(color);
if (pieceControl == null) throw new RuntimeException("PieceControl not found for UUID: " + uuid);
movePieceToNode(pieceControl, waitNodes.get(waitPieces.size()));
// Nächste freie Waiting Node abrufen
NodeControl nextWaitNode = getNextWaitingNode(color);
if (nextWaitNode == null) {
throw new IllegalStateException("No available waiting nodes for color: " + color);
}
// Bewegung durchführen
movePieceToNode(pieceControl, nextWaitNode);
// Waiting Nodes aktualisieren
waitingNodes.get(color).put(uuid, nextWaitNode);
// Synchronisation oder Animation
pieceControl.rotateInit();
app.getAcousticHandler().playSound(MdgaSound.LOSE);
app.getModelSynchronize().animationEnd();
}
/**
* Activates the shield for the specified piece.
*
* @param uuid the UUID of the piece to shield
*/
public void shieldPiece(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).activateShield();
}
/**
* Deactivates the shield for the specified piece.
*
* @param uuid the UUID of the piece to unshield
*/
public void unshieldPiece(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).deactivateShield();
}
/**
* Suppresses the shield for the specified piece.
*
* @param uuid the UUID of the piece to suppress the shield
*/
public void suppressShield(UUID uuid){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).suppressShield();
}
public void swapPieces(UUID piece1, UUID piece2){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
/**
* Swaps the positions and rotations of two pieces.
*
* @param p1 the first piece to swap
* @param p2 the second piece to swap
* @param loc1 the original location of the first piece
* @param rot1 the original rotation of the first piece
* @param loc2 the original location of the second piece
* @param rot2 the original rotation of the second piece
*/
private void swapPieces(PieceControl p1, PieceControl p2, Vector3f loc1, float rot1, Vector3f loc2, float rot2){
p1.setLocation(loc2);
p2.setLocation(loc1);
PieceControl piece1Control = pieces.get(piece1);
PieceControl piece2Control = pieces.get(piece2);
p1.setRotation(rot2);
p2.setRotation(rot1);
if(piece1Control == null) throw new RuntimeException("piece1 UUID is not valid");
if(piece2Control == null) throw new RuntimeException("piece2 UUID is not valid");
float rot1 = piece1Control.getRotation();
float rot2 = piece2Control.getRotation();
piece1Control.setRotation(rot2);
piece2Control.setRotation(rot1);
Vector3f pos1 = piece1Control.getLocation().clone();
Vector3f pos2 = piece2Control.getLocation().clone();
piece1Control.setLocation(pos2);
piece2Control.setLocation(pos1);
app.getModelSynchronize().animationEnd();
}
public void highlight(UUID uuid, boolean bool){
if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
pieces.get(uuid).highlight(bool);
pieces.get(uuid).setSelectable(bool);
}
//called when (dice) moveNum is received from server to display the movable pieces and corresponding moveNodes
/**
* Outlines the possible move nodes for a list of pieces based on the move indices and whether it's a home move.
*
* @param pieces the list of UUIDs representing the pieces to outline
* @param moveIndexe the list of indices for the target move nodes
* @param homeMoves the list indicating whether the move is a home move
* @throws RuntimeException if the sizes of the input lists do not match
*/
public void outlineMove(List<UUID> pieces, List<Integer> moveIndexe, List<Boolean> homeMoves) {
if(pieces.size() != moveIndexe.size() || pieces.size() != homeMoves.size()) throw new RuntimeException("arrays are not the same size");
@@ -370,7 +606,12 @@ public void outlineMove(List<UUID> pieces, List<Integer> moveIndexe, List<Boolea
}
}
//called when swap notification is received to highlight and select own/enemy pieces
/**
* Outlines the pieces that can be swapped based on the provided own and enemy pieces.
*
* @param ownPieces the list of UUIDs representing the player's pieces
* @param enemyPieces the list of UUIDs representing the enemy's pieces
*/
public void outlineSwap(List<UUID> ownPieces, List<UUID> enemyPieces){
selectableEnemyPieces.clear();
@@ -394,6 +635,11 @@ public void outlineSwap(List<UUID> ownPieces, List<UUID> enemyPieces){
}
}
/**
* Outlines the pieces that can be shielded based on the provided list of pieces.
*
* @param pieces the list of UUIDs representing the pieces to be shielded
*/
public void outlineShield(List<UUID> pieces){
selectableOwnPieces.clear();
selectableEnemyPieces.clear();
@@ -409,7 +655,11 @@ public void outlineShield(List<UUID> pieces){
}
}
//called from inputSynchronizer when a piece is selectable
/**
* Selects a piece from either the own or enemy pieces based on the input and deselects others if needed.
*
* @param pieceSelected the PieceControl instance representing the piece selected by the user
*/
public void pieceSelect(PieceControl pieceSelected) {
boolean isSelected = pieceSelected.isSelected();
if(selectableOwnPieces.contains(pieceSelected)){
@@ -439,9 +689,13 @@ else if(selectableEnemyPieces.contains(pieceSelected)) {
}
}
else throw new RuntimeException("pieceSelected is not in own/enemySelectablePieces");
app.getModelSynchronize().select(getKeyByValue(pieces, selectedOwnPiece), getKeyByValue(pieces, selectedEnemyPiece));
}
//called when view is no longer needed to select pieces
/**
* Clears all highlighted, selectable, and selected pieces and nodes.
*/
public void clearSelectable(){
for(PieceControl p : selectableEnemyPieces) {
p.unSelect();
@@ -463,19 +717,179 @@ public void clearSelectable(){
selectedOwnPiece = null;
}
public void enableHover(UUID uuid){
pieces.get(uuid).setHoverable(true);
}
/**
* Displays the dice for the specified color at the appropriate position.
*
* @param color the color of the player whose dice should be displayed
*/
public void showDice(Color color){
rootNodeBoard.attachChild(diceControl.getSpatial());
diceControl.setPos(getWaitingPos(color).add(new Vector3f(0,0,4)));
diceControl.spin();
}
/**
* Hides the dice from the view.
*/
public void hideDice(){
diceControl.hide();
}
private <K, V> K getKeyByValue(Map<K, V> map, V value) {
for (Map.Entry<K, V> entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
/**
* Animates the movement of a piece from its current index to a target index.
*
* @param uuid the UUID of the piece to animate
* @param curIndex the current index of the piece
* @param moveIndex the target index to animate the piece to
*/
public void movePieceAnim(UUID uuid, int curIndex, int moveIndex){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
infield.get(curIndex).getLocation(),
infield.get(moveIndex).getLocation(),
()->movePiece(uuid,curIndex,moveIndex)));
}
/**
* Animates the movement of a piece to its home position based on the given home index.
*
* @param uuid the UUID of the piece to animate
* @param homeIndex the index of the home node to move the piece to
*/
public void movePieceHomeAnim(UUID uuid, int homeIndex){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(),
homeNodesMap.get(pieceColor.get(uuid)).get(homeIndex).getLocation(),
()->moveHomePiece(uuid,homeIndex)));
}
/**
* Animates the start of the movement of a piece to a target index.
*
* @param uuid the UUID of the piece to animate
* @param moveIndex the target index to animate the piece to
*/
public void movePieceStartAnim(UUID uuid, int moveIndex){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(),
infield.get(moveIndex).getLocation(),
()->movePieceStart(uuid, moveIndex)
));
}
/**
* Animates the throwing of a piece to the next available waiting node.
*
* @param uuid the UUID of the piece to animate
*/
public void throwPieceAnim(UUID uuid){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(), getNextWaitingNode(pieceColor.get(uuid)).getLocation(),
()->throwPiece(uuid))
);
}
public void throwPiece(UUID uuid, Color throwColor){
switch(throwColor){
case ARMY -> throwShell(uuid);
case NAVY -> throwMissle(uuid);
case CYBER -> throwMatrix(uuid);
case AIRFORCE -> throwBomb(uuid);
default -> throw new RuntimeException("invalid color");
}
}
/**
* Animates the throwing of a piece to the next available waiting node.
*
* @param uuid the UUID of the piece to animate
*/
private void throwBomb(UUID uuid) {
Vector3f targetPoint = pieces.get(uuid).getLocation();
JetAnimation anim = new JetAnimation(app, rootNode, uuid, targetPoint, 40, 6, ()->throwPieceAnim(uuid));
anim.start();
}
private void throwMatrix(UUID uuid) {
//app.getAcousticHandler().playSound(MdgaSound.MATRIX);
Spatial piece = pieces.get(uuid).getSpatial();
piece.addControl(new MatrixAnimation(app, radarPos,()-> {
piece.addControl(new FadeControl(1,1,0,
() -> {
throwPiece(uuid);
piece.addControl(new FadeControl(1,0,1));
}
));
}));
}
private void throwMissle(UUID uuid) {
Vector3f targetPoint = pieces.get(uuid).getLocation();
MissileAnimation anim = new MissileAnimation(app, rootNode, uuid, targetPoint, 2);
anim.start();
}
private void throwShell(UUID uuid) {
pieces.get(uuid).getSpatial().addControl(new ShellAnimation(tankTop, app, ()-> throwPieceAnim(uuid)));
}
/**
* Animates the swapping of two pieces by swapping their positions and rotations.
*
* @param piece1 the UUID of the first piece
* @param piece2 the UUID of the second piece
*/
public void swapPieceAnim(UUID piece1, UUID piece2){
PieceControl piece1Control = pieces.get(piece1);
PieceControl piece2Control = pieces.get(piece2);
Vector3f loc1 = piece1Control.getLocation().clone();
Vector3f loc2 = piece2Control.getLocation().clone();
float rot1 = piece1Control.getRotation();
float rot2 = piece2Control.getRotation();
piece1Control.getSpatial().addControl(new MoveControl(
piece1Control.getLocation().clone(),
piece2Control.getLocation().clone(),
()->{}
));
piece2Control.getSpatial().addControl(new MoveControl(
piece2Control.getLocation().clone(),
piece1Control.getLocation().clone(),
()->swapPieces(piece1Control,piece2Control,loc1,rot1,loc2,rot2)
));
}
/**
* Retrieves the next available waiting node for the specified color.
*
* @param color the color of the player to get the next waiting node for
* @return the next available NodeControl for the specified color
* @throws IllegalStateException if no available waiting nodes are found for the color
*/
private NodeControl getNextWaitingNode(Color color) {
List<NodeControl> nodes = waitingNodesMap.get(color);
if (nodes == null || nodes.isEmpty()) {
throw new IllegalStateException("Keine verfügbaren Warteschleifen-Knoten für die Farbe " + color);
}
for (NodeControl node : nodes) {
if (!waitingNodes.getOrDefault(color, new HashMap<>()).containsValue(node)) {
return node;
}
}
throw new IllegalStateException("Keine freien Nodes im Wartebereich für die Farbe " + color);
}
}

View File

@@ -7,13 +7,20 @@
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FXAAFilter;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.scene.Spatial;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.util.SkyFactory;
import com.jme3.util.SkyFactory.EnvMapType;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
/**
* Handles the camera position, rotation, and lighting effects for the game.
* Provides methods for camera initialization, updates based on user input, and shutdown operations.
*/
public class CameraHandler {
MdgaApp app;
@@ -29,11 +36,23 @@ public class CameraHandler {
DirectionalLightShadowFilter dlsf;
Spatial sky;
private Color ownColor;
private boolean init;
private boolean initRot;
private SSAOFilter ssaoFilter;
private FXAAFilter fxaaFilter;
/**
* Constructor for the CameraHandler. Initializes the camera settings and lighting.
*
* @param app The main application instance that provides the camera and root node.
* @param fpp The FilterPostProcessor used for post-processing effects.
*/
public CameraHandler(MdgaApp app, FilterPostProcessor fpp) {
init = false;
initRot = false;
this.app = app;
this.fpp = fpp;
// Save the default camera state
this.defaultCameraPosition = app.getCamera().getLocation().clone();
this.defaultCameraRotation = app.getCamera().getRotation().clone();
@@ -50,19 +69,38 @@ public CameraHandler(MdgaApp app, FilterPostProcessor fpp) {
dlsf.setEnabled(true);
dlsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);
dlsf.setShadowIntensity(0.7f);
ssaoFilter = new SSAOFilter(6, 10f, 0.33f, 0.61f);
// ssaoFilter = new SSAOFilter();
fxaaFilter = new FXAAFilter();
sky = SkyFactory.createSky(app.getAssetManager(), "Images/sky/sky.dds", EnvMapType.EquirectMap).rotate(FastMath.HALF_PI*1,0,FastMath.HALF_PI*0.2f);
}
public void init() {
/**
* Initializes the camera with a specific color orientation.
* Adds lights, sky, and shadow filters to the scene.
*
* @param ownColor The color that defines the initial camera view angle.
*/
public void init(Color ownColor) {
app.getRootNode().addLight(sun);
app.getRootNode().addLight(ambient);
app.getRootNode().attachChild(sky);
fpp.addFilter(dlsf);
fpp.addFilter(ssaoFilter);
fpp.addFilter(fxaaFilter);
init = true;
initRot = true;
this.ownColor = ownColor;
app.getInputSynchronize().setRotation(getInitAngleByColor(ownColor)*2);
}
/**
* Shuts down the camera handler by removing all lights, sky, and filters,
* and resets the camera position and rotation to its default state.
*/
public void shutdown() {
app.getRootNode().removeLight(sun);
app.getRootNode().removeLight(ambient);
@@ -75,7 +113,15 @@ public void shutdown() {
fpp.removeFilter(dlsf);
}
/**
* Updates the camera position and rotation based on user input (scroll and rotation).
* Adjusts the vertical angle and radius based on zoom and rotation values.
*
* @param scroll The scroll input, determining zoom level.
* @param rotation The rotation input, determining camera orientation.
*/
public void update(float scroll, float rotation) {
if(!init) return;
float scrollValue = Math.max(0, Math.min(scroll, 100));
float rotationValue = rotation % 360;
@@ -83,6 +129,7 @@ public void update(float scroll, float rotation) {
rotationValue += 360;
}
float radius;
float verticalAngle;
@@ -91,10 +138,9 @@ public void update(float scroll, float rotation) {
radius = 30f;
} else {
verticalAngle = 90f;
rotationValue = 270f;
rotationValue = getAngleByColor(ownColor);
radius = 50f;
}
float verticalAngleRadians = FastMath.DEG_TO_RAD * verticalAngle;
float z = radius * FastMath.sin(verticalAngleRadians);
@@ -107,4 +153,30 @@ public void update(float scroll, float rotation) {
app.getCamera().lookAt(Vector3f.ZERO, Vector3f.UNIT_Z);
}
/**
* Returns the camera angle based on the specified color.
*
* @param color The color used to determine the camera angle.
* @return The camera angle in degrees.
*/
private float getAngleByColor(Color color){
return switch (color){
case ARMY -> 0;
case AIRFORCE -> 90;
case NAVY -> 270;
case CYBER -> 180;
default -> throw new RuntimeException("None is not allowed");
};
}
/**
* Returns the initial camera angle based on the specified color.
*
* @param color The color used to determine the camera angle.
* @return The initial camera angle in degrees.
*/
private float getInitAngleByColor(Color color){
return (getAngleByColor(color) + 180) % 360;
}
}

View File

@@ -10,11 +10,27 @@
import java.util.ArrayList;
import java.util.List;
/**
* A utility class for loading and parsing map data from a file.
* The map contains asset names and coordinates for objects placed on the map.
*/
class MapLoader {
/**
* Private constructor to prevent instantiation.
*/
private MapLoader() {
}
/**
* Loads a map file and parses its contents into a list of assets and their positions.
* Each line in the map file defines an asset, its coordinates, and its rotation.
*
* @param mapName The name of the map file to load. The file is expected to be located in the resources directory.
* @return A list of {@link AssetOnMap} objects representing the assets placed on the map.
* @throws IOException If an error occurs while reading the map file.
* @throws IllegalArgumentException If the map file contains invalid data.
*/
public static List<AssetOnMap> loadMap(String mapName) {
List<AssetOnMap> assetsOnMap = new ArrayList<>();
@@ -60,6 +76,13 @@ public static List<AssetOnMap> loadMap(String mapName) {
return assetsOnMap;
}
/**
* Returns the corresponding {@link Asset} for a given asset name.
*
* @param assetName The name of the asset to load.
* @return The {@link Asset} associated with the given name.
* @throws IllegalStateException If the asset name is unrecognized.
*/
private static Asset getLoadedAsset(String assetName) {
return switch (assetName) {
case "lw" -> Asset.lw;
@@ -84,8 +107,11 @@ private static Asset getLoadedAsset(String assetName) {
case "radar" -> Asset.radar;
case "ship" -> Asset.ship;
case "tank" -> Asset.tank;
case "tree_small" -> Asset.tree_small;
case "tree_big" -> Asset.tree_big;
case "treeSmall" -> Asset.treeSmall;
case "treeBig" -> Asset.treeBig;
case "tank_shoot" -> Asset.tankShoot;
case "treesBigBackground" -> Asset.treesBigBackground;
case "treesSmallBackground" -> Asset.treesSmallBackground;
default -> throw new IllegalStateException("Unexpected value: " + assetName);
};
}

View File

@@ -8,19 +8,40 @@
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
/**
* A control that adds highlighting functionality to a node in the game.
* This class extends {@link OutlineControl} to add an outline effect when the node is highlighted.
*/
public class NodeControl extends OutlineControl {
private static final ColorRGBA OUTLINE_HIGHLIGHT_COLOR = ColorRGBA.White;
private static final int OUTLINE_HIGHLIGHT_WIDTH = 6;
/**
* Constructs a {@link NodeControl} with the specified application and post processor.
* This constructor sets up the necessary elements for highlighting functionality.
*
* @param app The {@link MdgaApp} instance to use for the application context.
* @param fpp The {@link FilterPostProcessor} to apply post-processing effects.
*/
public NodeControl(MdgaApp app, FilterPostProcessor fpp) {
super(app, fpp);
}
/**
* Returns the location of the node in 3D space.
* This is the node's local translation in the scene.
*
* @return The {@link Vector3f} representing the node's location.
*/
public Vector3f getLocation(){
return this.getSpatial().getLocalTranslation();
}
/**
* Highlights the node by applying an outline effect.
* The outline color and width are predefined as white and 6, respectively.
*/
public void highlight() {
super.outline(OUTLINE_HIGHLIGHT_COLOR, OUTLINE_HIGHLIGHT_WIDTH);
}

View File

@@ -3,72 +3,89 @@
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;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.board.Outline.SelectObjectOutliner;
import pp.mdga.client.InitControl;
import pp.mdga.client.outline.SelectObjectOutliner;
public class OutlineControl extends AbstractControl {
private static final int THICKNESS_DEFAULT = 6;
/**
* A control that provides outline functionality to a spatial object.
* This class is responsible for adding an outline effect to a spatial
* object, allowing it to be highlighted or deselected.
*/
public class OutlineControl extends InitControl {
/** The {@link SelectObjectOutliner} responsible for managing the outline effect. */
private final SelectObjectOutliner outlineOwn;
private static final int THICKNESS_DEFAULT = 6;
private MdgaApp app;
/**
* Constructs an {@code OutlineControl} with default thickness for the object outline.
*
* @param app The main application managing the outline control.
* @param fpp The {@code FilterPostProcessor} used for post-processing effects.
*/
public OutlineControl(MdgaApp app, FilterPostProcessor fpp){
this.app = app;
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera(), app);
}
/**
* Constructs an {@code OutlineControl} with default thickness, allowing a custom camera to be specified.
*
* @param app The main application managing the outline control.
* @param fpp The {@code FilterPostProcessor} used for post-processing effects.
* @param cam The camera used for rendering the outlined objects.
*/
public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam){
this.app = app;
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
}
/**
* Constructs an {@code OutlineControl} with a specified thickness and custom camera.
*
* @param app The main application managing the outline control.
* @param fpp The {@code FilterPostProcessor} used for post-processing effects.
* @param cam The camera used for rendering the outlined objects.
* @param thickness The thickness of the outline.
*/
public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam, int thickness){
this.app = app;
outlineOwn = new SelectObjectOutliner(thickness, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
}
/**
* Applies an outline to the spatial object with the given color.
*
* @param color The {@link ColorRGBA} representing the color of the outline.
*/
public void outline(ColorRGBA color){
outlineOwn.select(spatial, color);
}
/**
* Applies an outline to the spatial object with the given color and width.
*
* @param color The {@link ColorRGBA} representing the color of the outline.
* @param width The width of the outline.
*/
public void outline(ColorRGBA color, int width){
deOutline();
outlineOwn.select(spatial, color, width);
}
/**
* Removes the outline effect from the spatial object.
*/
public void deOutline(){
outlineOwn.deselect(spatial);
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
public void initSpatial(){
}
@Override
public void setSpatial(Spatial spatial){
if(this.spatial == null && spatial != null){
super.setSpatial(spatial);
initSpatial();
}
else{
super.setSpatial(spatial);
}
}
/**
* Retrieves the instance of the {@code MdgaApp} associated with this control.
*
* @return The {@code MdgaApp} instance.
*/
public MdgaApp getApp() {
return app;
}

View File

@@ -14,6 +14,12 @@
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
/**
* A control that manages the behavior and properties of a game piece, such as its rotation,
* position, shield activation, and highlighting. This class extends {@link OutlineControl}
* to provide outline functionality and includes additional features like shield effects,
* hover states, and selection states.
*/
public class PieceControl extends OutlineControl {
private final float initRotation;
private final AssetManager assetManager;
@@ -43,7 +49,15 @@ public class PieceControl extends OutlineControl {
private boolean selectable;
private boolean select;
/**
* Constructs a {@link PieceControl} with the specified initial rotation, asset manager,
* application, and post-processor.
*
* @param initRotation The initial rotation of the piece in degrees.
* @param assetManager The {@link AssetManager} used for loading models and materials.
* @param app The {@link MdgaApp} instance to use for the application context.
* @param fpp The {@link FilterPostProcessor} to apply post-processing effects.
*/
public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, FilterPostProcessor fpp){
super(app, fpp);
this.parentNode = new Node();
@@ -59,10 +73,20 @@ public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app,
select = false;
}
/**
* Gets the current rotation of the piece in degrees.
*
* @return The rotation of the piece in degrees.
*/
public float getRotation() {
return (float) Math.toDegrees(spatial.getLocalRotation().toAngleAxis(new Vector3f(0,0,1)));
}
/**
* Sets the rotation of the piece to the specified value in degrees.
*
* @param rot The rotation in degrees to set.
*/
public void setRotation(float rot){
if(rot < 0) rot =- 360;
@@ -71,10 +95,20 @@ public void setRotation(float rot){
spatial.setLocalRotation(quaternion);
}
/**
* Gets the current location (position) of the piece.
*
* @return The location of the piece as a {@link Vector3f}.
*/
public Vector3f getLocation(){
return spatial.getLocalTranslation();
}
/**
* Updates the piece control every frame. If the shield is active, it will rotate.
*
* @param delta The time difference between frames (time per frame).
*/
@Override
protected void controlUpdate(float delta) {
if(shieldRing != null){
@@ -82,10 +116,19 @@ protected void controlUpdate(float delta) {
}
}
/**
* Sets the location (position) of the piece.
*
* @param loc The location to set as a {@link Vector3f}.
*/
public void setLocation(Vector3f loc){
this.spatial.setLocalTranslation(loc);
}
/**
* Initializes the spatial object and sets its rotation.
* This also moves the spatial to a new parent node for organizational purposes.
*/
@Override
public void initSpatial(){
setRotation(this.initRotation);
@@ -98,11 +141,15 @@ public void initSpatial(){
}
public void rotateInit() {
// rotate(rotation - initRotation);
setRotation(initRotation);
}
/**
* Activates the shield around the piece.
* This adds a visual shield effect in the form of a rotating ring.
*/
public void activateShield(){
shieldRing = assetManager.loadModel(Asset.shield_ring.getModelPath());
shieldRing = assetManager.loadModel(Asset.shieldRing.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)));
@@ -115,11 +162,18 @@ public void activateShield(){
parentNode.attachChild(shieldRing);
}
/**
* Deactivates the shield by removing the shield ring from the scene.
*/
public void deactivateShield(){
parentNode.detachChild(shieldRing);
shieldRing = null;
}
/**
* Suppresses the shield, changing its color to a suppressed state.
*/
public void suppressShield(){
assert(shieldRing != null) : "PieceControl: shieldRing is not set";
shieldMat.setColor("Color", SHIELD_SUPPRESSED_COLOR);
@@ -133,22 +187,36 @@ public Material getMaterial(){
return ((Geometry) getSpatial()).getMaterial();
}
/**
* Highlights the piece with the appropriate outline color based on whether it is an enemy or not.
*
* @param enemy True if the piece is an enemy, false if it is owned by the player.
*/
public void highlight(boolean enemy) {
this.enemy = enemy;
highlight = true;
super.outline(enemy ? OUTLINE_ENEMY_COLOR : OUTLINE_OWN_COLOR, OUTLINE_HIGHLIGHT_WIDTH);
}
/**
* Removes the highlight effect from the piece.
*/
public void unHighlight(){
highlight = false;
deOutline();
}
/**
* Applies a hover effect on the piece if it is hoverable.
*/
public void hover(){
if(!hoverable) return;
super.outline(enemy ? OUTLINE_ENEMY_HOVER_COLOR : OUTLINE_OWN_HOVER_COLOR, OUTLINE_HOVER_WIDTH);
}
/**
* Removes the hover effect from the piece.
*/
public void hoverOff(){
if(!hoverable) return;
@@ -157,29 +225,58 @@ public void hoverOff(){
else deOutline();
}
/**
* Deselects the piece and removes the selection outline. If the piece was highlighted,
* it will be re-highlighted. Otherwise, the outline is removed.
*/
public void unSelect(){
select = false;
if(highlight) highlight(enemy);
else deOutline();
}
/**
* Selects the piece and applies the selection outline. If the piece is an enemy, it will
* be outlined with the enemy selection color; otherwise, the own selection color will be used.
*/
public void select(){
if(!selectable) return;
select = true;
super.outline(enemy ? OUTLINE_ENEMY_SELECT_COLOR : OUTLINE_OWN_SELECT_COLOR, OUTLINE_SELECT_WIDTH);
}
/**
* Sets whether the piece is selectable.
*
* @param selectable True if the piece can be selected, false otherwise.
*/
public void setSelectable(boolean selectable){
this.selectable = selectable;
}
/**
* Checks if the piece is selected.
*
* @return True if the piece is selected, false otherwise.
*/
public boolean isSelected() { return select; }
/**
* Checks if the piece is selectable.
*
* @return True if the piece is selectable, false otherwise.
*/
public boolean isSelectable() {
return selectable;
}
/**
* Sets whether the piece is hoverable.
*
* @param hoverable True if the piece can be hovered over, false otherwise.
*/
public void setHoverable(boolean hoverable) {
this.hoverable = hoverable;
}
}

View File

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

View File

@@ -1,12 +0,0 @@
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,105 @@
package pp.mdga.client.board;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import static pp.mdga.client.Util.linInt;
public class TankTopControl extends InitControl {
private float timer = 0; // Time elapsed
private final static float DURATION = 1.5f; // Total rotation duration in seconds
private boolean rotating = false; // Flag to track if rotation is active
private float startAngle = 0;
private float endAngle = 0;
private Runnable actionAfter = null;
@Override
protected void controlUpdate(float tpf) {
if (!rotating) return;
// Update the timer
timer += tpf;
// Calculate interpolation factor (0 to 1)
float t = timer / DURATION;
if (t >= 1) t = 1;
float curAngle = linInt(startAngle, endAngle, t);
// Interpolate the rotation
Quaternion interpolatedRotation = new Quaternion();
interpolatedRotation.fromAngleAxis((float) Math.toRadians(curAngle), Vector3f.UNIT_Z);
// Apply the interpolated rotation to the spatial
spatial.setLocalRotation(interpolatedRotation);
if(t >= 1){
rotating = false;
if(actionAfter != null) actionAfter.run();
}
}
public void rotate(Vector3f enemyPos, Runnable actionAfter) {
if (spatial == null) throw new RuntimeException("spatial is null");
startAngle = getOwnAngle();
endAngle = getEnemyAngle(enemyPos);
// Adjust endAngle to ensure the shortest path
float deltaAngle = endAngle - startAngle;
if (deltaAngle > 180) {
endAngle -= 360; // Rotate counterclockwise
} else if (deltaAngle < -180) {
endAngle += 360; // Rotate clockwise
}
timer = 0;
rotating = true;
this.actionAfter = actionAfter; // Store the action to execute after rotation
}
private float getEnemyAngle(Vector3f enemyPos){
// Direction to the enemy in the XY plane
Vector3f direction = enemyPos.subtract(spatial.getLocalTranslation());
direction.z = 0; // Project to XY plane
direction.normalizeLocal();
Vector3f reference = Vector3f.UNIT_Y.mult(-1);
// Calculate the angle between the direction vector and the reference vector
float angle = FastMath.acos(reference.dot(direction));
// Determine rotation direction using the cross product
Vector3f cross = reference.cross(direction);
if (cross.z < 0) {
angle = -angle;
}
return (float) Math.toDegrees(angle); // Return the absolute angle in degrees
}
private float getOwnAngle() {
// Tank's forward direction in the XY plane
Vector3f forward = spatial.getLocalRotation().mult(Vector3f.UNIT_Y);
forward.z = 0; // Project to XY plane
forward.normalizeLocal();
// Reference vector: Positive X-axis
Vector3f reference = Vector3f.UNIT_Y;
// Calculate the angle between the forward vector and the reference vector
float angle = FastMath.acos(reference.dot(forward));
// Determine rotation direction using the cross product
Vector3f cross = reference.cross(forward);
if (cross.z < 0) { // For Z-up, check the Z component of the cross product
angle = -angle;
}
return (float) Math.toDegrees(angle); // Return the absolute angle in radians
}
}

View File

@@ -158,4 +158,8 @@ protected void calculateRelative() {
heightStep = verticalStep / 2;
widthStep = horizontalStep / 2;
}
public Vector2f getPos() {
return pos;
}
}

View File

@@ -197,14 +197,14 @@ protected void setImageRelative(Picture picture) {
return;
}
final float LARGER = 10;
final float larger = 10;
picture.setWidth(instance.getPreferredSize().x + LARGER);
picture.setHeight(instance.getPreferredSize().y + LARGER);
picture.setWidth(instance.getPreferredSize().x + larger);
picture.setHeight(instance.getPreferredSize().y + larger);
picture.setLocalTranslation(
instance.getLocalTranslation().x - LARGER / 2,
(instance.getLocalTranslation().y - picture.getHeight()) + LARGER / 2,
instance.getLocalTranslation().x - larger / 2,
(instance.getLocalTranslation().y - picture.getHeight()) + larger / 2,
instance.getLocalTranslation().z + 0.01f
);
}

View File

@@ -317,7 +317,6 @@ public void setTaken(Taken taken, String name) {
} else {
label.setText(name);
}
onUnHover();
}

View File

@@ -30,20 +30,9 @@ public SettingsButton(MdgaApp app, Node node, Runnable action) {
// Enable adjustment for positioning
adjust = true;
}
/**
* Displays the settings button by attaching it to the scene graph.
*/
@Override
public void show() {
release();
calculateRelative();
setRelative();
setImageRelative(pictureNormal);
node.attachChild(instance);
pictureNormal.setImage(app.getAssetManager(), "Images/Settings_Button_normal.png", true);
pictureHover.setImage(app.getAssetManager(), "Images/Settings_Button_hover.png", true);
}
/**
@@ -51,12 +40,6 @@ public void show() {
*/
@Override
public void onHover() {
icon = new IconComponent("Images/Settings_Button_hover.png");
icon.setIconScale(0.02f * app.getImageScale());
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
instance.setIcon(icon);
}
/**
@@ -64,12 +47,6 @@ public void onHover() {
*/
@Override
public void onUnHover() {
icon = new IconComponent("Images/Settings_Button_normal.png");
icon.setIconScale(0.02f * app.getImageScale());
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
instance.setIcon(icon);
}
}

View File

@@ -77,12 +77,17 @@ public SliderButton(MdgaApp app, Node node, String label) {
QuadBackgroundComponent background = new QuadBackgroundComponent(BUTTON_NORMAL);
slider.setBackground(background);
// Set label background
QuadBackgroundComponent labelBackground = new QuadBackgroundComponent(BUTTON_NORMAL);
this.label.setBackground(labelBackground);
// Configure the label font
this.label.setFont(font);
this.label.setTextHAlignment(HAlignment.Center);
// Default position and size
pos = new Vector2f(0, 0);
size = new Vector2f(5.5f, 1);
size = new Vector2f(6f, 1);
// Add label and slider to container
container.addChild(this.label);

View File

@@ -7,6 +7,11 @@
import pp.mdga.client.button.SliderButton;
import pp.mdga.client.view.MdgaView;
/**
* The {@code AudioSettingsDialog} class represents a dialog for adjusting audio settings in the application.
* It provides controls for managing main volume, music volume, and sound effect volume, and includes
* a button to return to the previous menu.
*/
public class AudioSettingsDialog extends Dialog {
private final MdgaView view;
@@ -18,6 +23,13 @@ public class AudioSettingsDialog extends Dialog {
private boolean active = false;
/**
* Constructs an {@code AudioSettingsDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The current view, used for navigation and interaction with the dialog.
*/
public AudioSettingsDialog(MdgaApp app, Node node, MdgaView view) {
super(app, node);
@@ -42,6 +54,9 @@ public AudioSettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Initializes and displays the volume controls and back button.
*/
@Override
protected void onShow() {
active = true;
@@ -57,6 +72,9 @@ protected void onShow() {
soundVolume.show();
}
/**
* Called when the dialog is hidden. Hides all volume controls and the back button.
*/
@Override
protected void onHide() {
active = false;
@@ -68,6 +86,10 @@ protected void onHide() {
soundVolume.hide();
}
/**
* Updates the application audio settings based on the current values of the sliders.
* This method is called continuously while the dialog is active.
*/
public void update() {
if(!active) {
return;

View File

@@ -10,17 +10,30 @@
import java.util.ArrayList;
/**
* The {@code CeremonyDialog} class displays a dialog containing statistical data in a tabular format.
* It allows adding rows of statistics and manages their visibility when shown or hidden.
*/
public class CeremonyDialog extends Dialog {
private ArrayList<ArrayList<LabelButton>> labels;
float offsetX;
/**
* Constructs a {@code CeremonyDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
*/
public CeremonyDialog(MdgaApp app, Node node) {
super(app, node);
prepare();
}
/**
* Called when the dialog is shown. Makes all label buttons in the table visible.
*/
@Override
protected void onShow() {
for (ArrayList<LabelButton> row : labels) {
@@ -30,6 +43,9 @@ protected void onShow() {
}
}
/**
* Called when the dialog is hidden. Hides all label buttons in the table.
*/
@Override
protected void onHide() {
for (ArrayList<LabelButton> row : labels) {
@@ -39,6 +55,17 @@ protected void onHide() {
}
}
/**
* Adds a row of statistical data to the dialog.
*
* @param name The name of the player or category for the row.
* @param v1 The value for the first column.
* @param v2 The value for the second column.
* @param v3 The value for the third column.
* @param v4 The value for the fourth column.
* @param v5 The value for the fifth column.
* @param v6 The value for the sixth column.
*/
public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5, int v6) {
float offsetYSmall = 0.5f;
@@ -76,6 +103,9 @@ public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5
labels.add(row);
}
/**
* Prepares the initial layout of the dialog, including header labels.
*/
public void prepare() {
offsetX = 0.5f;

View File

@@ -4,29 +4,53 @@
import com.simsilica.lemur.Container;
import pp.mdga.client.MdgaApp;
/**
* The {@code Dialog} class serves as an abstract base class for dialogs in the application.
* It provides functionality for showing and hiding the dialog and defines abstract methods
* for custom behavior when the dialog is shown or hidden.
*/
public abstract class Dialog {
protected final MdgaApp app;
protected final Node node = new Node();
private final Node root;
/**
* Constructs a {@code Dialog}.
*
* @param app The main application managing the dialog.
* @param node The root node to which the dialog's node will be attached.
*/
Dialog(MdgaApp app, Node node) {
this.app = app;
this.root = node;
}
/**
* Shows the dialog by attaching its node to the root node and invoking the {@code onShow} method.
*/
public void show() {
root.attachChild(node);
onShow();
}
/**
* Hides the dialog by detaching its node from the root node and invoking the {@code onHide} method.
*/
public void hide() {
root.detachChild(node);
onHide();
}
/**
* Called when the dialog is shown. Subclasses must implement this method to define custom behavior.
*/
protected abstract void onShow();
protected abstract void onHide ();
/**
* Called when the dialog is hidden. Subclasses must implement this method to define custom behavior.
*/
protected abstract void onHide();
}

View File

@@ -3,6 +3,7 @@
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.NetworkSupport;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.button.InputButton;
@@ -11,7 +12,11 @@
import java.util.prefs.Preferences;
public class HostDialog extends Dialog {
/**
* The {@code HostDialog} class represents a dialog for hosting a network game session.
* It allows users to input a port number, start hosting a server, and navigate back to the previous view.
*/
public class HostDialog extends NetworkDialog {
private InputButton portInput;
private ButtonRight hostButton;
@@ -21,8 +26,15 @@ public class HostDialog extends Dialog {
private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
/**
* Constructs a {@code HostDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The main view used for navigation and interaction with the dialog.
*/
public HostDialog(MdgaApp app, Node node, MainView view) {
super(app, node);
super(app, node, (NetworkSupport) app.getNetworkSupport());
this.view = view;
@@ -38,6 +50,9 @@ public HostDialog(MdgaApp app, Node node, MainView view) {
offset += 1.5f;
}
/**
* Called when the dialog is shown. Displays all input fields and buttons.
*/
@Override
protected void onShow() {
portInput.show();
@@ -45,6 +60,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all input fields and buttons.
*/
@Override
protected void onHide() {
portInput.hide();
@@ -52,17 +70,44 @@ protected void onHide() {
backButton.hide();
}
/**
* Updates the state of the port input field.
* This method is called periodically to synchronize the dialog state.
*/
public void update() {
portInput.update();
}
/**
* Retrieves the currently entered port number, saves it to preferences, and sets it as the active port.
*
* @return The port number as a string.
*/
public String getPort() {
prefs.put("hostPort", portInput.getString());
setPortNumber(Integer.parseInt(portInput.getString()));
return portInput.getString();
}
/**
* Resets the port input field to its default value and updates preferences accordingly.
*/
public void resetPort() {
portInput.reset();
prefs.put("hostPort", "11111");
}
/**
* Starts the server to host a network game.
*/
public void hostServer() {
startServer();
}
/**
* Connects to the server as a client.
*/
public void connectServerAsClient() {
connectServer();
}
}

View File

@@ -1,4 +1,84 @@
package pp.mdga.client.dialog;
public class InterruptDialog {
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.button.AbstractButton;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.button.LabelButton;
import pp.mdga.client.button.MenuButton;
import pp.mdga.client.view.MdgaView;
import pp.mdga.game.Color;
/**
* The {@code InterruptDialog} class represents a dialog that interrupts the game flow,
* providing a message and the option to force an action if the user is a host.
*/
public class InterruptDialog extends Dialog {
private ButtonRight forceButton;
private LabelButton label;
private String text = "";
/**
* Constructs an {@code InterruptDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
*/
public InterruptDialog(MdgaApp app, Node node) {
super(app, node);
forceButton = new ButtonRight(app, node, () -> app.getModelSynchronize().force(), "Erzwingen", 1);
}
/**
* Called when the dialog is shown. Displays the label and optionally the force button if the user is the host.
*/
@Override
protected void onShow() {
if(app.getGameLogic().isHost()) {
forceButton.show();
}
label = new LabelButton(app, node, "Warte auf " + text + "...", new Vector2f(5.5f * 1.5f, 2), new Vector2f(0.5f, 0f), false);
float offset = 2.8f;
label.setPos(new Vector2f(0, MenuButton.VERTICAL - offset));
label.show();
}
/**
* Called when the dialog is hidden. Hides the label and the force button.
*/
@Override
protected void onHide() {
forceButton.hide();
label.hide();
}
/**
* Sets the displayed text based on the specified color.
*
* @param color The color used to determine the text (e.g., "Luftwaffe" for AIRFORCE).
*/
public void setColor(Color color) {
switch (color) {
case AIRFORCE:
text = "Luftwaffe";
break;
case ARMY:
text = "Heer";
break;
case NAVY:
text = "Marine";
break;
case CYBER:
text = "CIR";
break;
}
}
}

View File

@@ -3,6 +3,7 @@
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.NetworkSupport;
import pp.mdga.client.acoustic.AcousticHandler;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
@@ -12,7 +13,11 @@
import java.util.prefs.Preferences;
public class JoinDialog extends Dialog {
/**
* The {@code JoinDialog} class represents a dialog for joining a network game.
* It allows users to input an IP address and port number, connect to a server, or navigate back to the previous view.
*/
public class JoinDialog extends NetworkDialog {
private InputButton ipInput;
private InputButton portInput;
@@ -23,8 +28,15 @@ public class JoinDialog extends Dialog {
private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
/**
* Constructs a {@code JoinDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The main view used for navigation and interaction with the dialog.
*/
public JoinDialog(MdgaApp app, Node node, MainView view) {
super(app, node);
super(app, node, (NetworkSupport) app.getNetworkSupport());
this.view = view;
@@ -45,6 +57,9 @@ public JoinDialog(MdgaApp app, Node node, MainView view) {
offset += 1.5f;
}
/**
* Called when the dialog is shown. Displays all input fields and buttons.
*/
@Override
protected void onShow() {
ipInput.show();
@@ -53,6 +68,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all input fields and buttons.
*/
@Override
protected void onHide() {
ipInput.hide();
@@ -61,28 +79,70 @@ protected void onHide() {
backButton.hide();
}
/**
* Updates the state of the input fields. This method is called periodically to synchronize the dialog state.
*/
public void update() {
ipInput.update();
portInput.update();
}
/**
* Retrieves the currently entered IP address, saves it to preferences, and sets it as the hostname.
*
* @return The IP address as a string.
*/
public String getIpt() {
prefs.put("joinIp", ipInput.getString());
setHostname(ipInput.getString());
return ipInput.getString();
}
/**
* Resets the IP input field to its default value and updates preferences accordingly.
*/
public void resetIp() {
ipInput.reset();
prefs.put("joinIp", "");
}
/**
* Retrieves the currently entered port number, saves it to preferences, and sets it as the active port.
*
* @return The port number as a string.
*/
public String getPort() {
prefs.put("joinPort", portInput.getString());
setPortNumber(Integer.parseInt(portInput.getString()));
return portInput.getString();
}
/**
* Resets the port input field to its default value and updates preferences accordingly.
*/
public void resetPort() {
portInput.reset();
prefs.put("joinPort", "11111");
}
/**
* Connects to the server using the current IP address and port number.
*/
public void connectToServer() {
connectServer();
}
/**
* Disconnects from the server if a network connection exists.
*/
public void disconnect() {
NetworkSupport network = getNetwork();
if (network != null) {
try {
network.disconnect();
} catch (Exception e) {
System.err.println("Error while disconnecting: " + e.getMessage());
}
}
}
}

View File

@@ -0,0 +1,149 @@
package pp.mdga.client.dialog;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.NetworkSupport;
import pp.mdga.client.server.MdgaServer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* The {@code NetworkDialog} class serves as an abstract base class for dialogs
* that involve network-related functionalities, such as connecting to a server or hosting a game.
* It provides methods for initializing, connecting to, and managing a network server.
*/
public abstract class NetworkDialog extends Dialog {
private NetworkSupport network;
private String hostname;
private int portNumber;
private Future<Object> connectionFuture;
private MdgaServer serverInstance;
private Thread serverThread;
/**
* Constructs a {@code NetworkDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param network The network support instance for managing network interactions.
*/
public NetworkDialog(MdgaApp app, Node node, NetworkSupport network) {
super(app, node);
this.network = network;
}
/**
* Sets the hostname for the network connection.
*
* @param hostname The hostname or IP address of the server.
*/
public void setHostname(String hostname) {
this.hostname = hostname;
}
/**
* Sets the port number for the network connection.
*
* @param portNumber The port number to use for the connection.
*/
public void setPortNumber(int portNumber) {
this.portNumber = portNumber;
}
/**
* Initializes the network connection using the current hostname and port number.
*
* @return {@code null} if successful, otherwise throws an exception.
*/
protected Object initNetwork() {
try {
this.network.initNetwork(this.hostname, this.portNumber);
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Starts the process of connecting to a server asynchronously.
*/
protected void connectServer() {
try {
connectionFuture = this.network.getApp().getExecutor().submit(this::initNetwork);
} catch (NumberFormatException var2) {
throw new NumberFormatException("Port must be a number");
}
}
/**
* Starts hosting a server in a separate thread.
*/
protected void startServer() {
serverThread = new Thread(() -> {
try {
serverInstance = new MdgaServer(portNumber);
serverInstance.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
serverThread.start();
}
/**
* Shuts down the hosted server and cleans up resources.
*/
public void shutdownServer() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Thread was interrupted: " + e.getMessage());
}
if (serverInstance != null) {
serverInstance.shutdown();
serverInstance = null;
}
if (serverThread != null && serverThread.isAlive()) {
serverThread.interrupt();
try {
serverThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
serverThread = null;
}
}
/**
* Updates the state of the connection process.
*
* @param delta The time elapsed since the last update call.
*/
public void update(float delta) {
if (this.connectionFuture != null && this.connectionFuture.isDone()) {
try {
this.connectionFuture.get();
} catch (ExecutionException ignored) {
// TODO: implement
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* Retrieves the {@code NetworkSupport} instance associated with this dialog.
*
* @return The {@code NetworkSupport} instance.
*/
public NetworkSupport getNetwork() {
return network;
}
}

View File

@@ -8,6 +8,10 @@
import pp.mdga.client.view.MainView;
import pp.mdga.client.view.MdgaView;
/**
* The {@code SettingsDialog} class represents a dialog for navigating to various settings sections,
* such as video and audio settings, or returning to the previous view.
*/
public class SettingsDialog extends Dialog {
private MenuButton videoButton;
private MenuButton audioButton;
@@ -15,6 +19,13 @@ public class SettingsDialog extends Dialog {
private final MdgaView view;
/**
* Constructs a {@code SettingsDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The view managing navigation and interaction with the settings dialog.
*/
public SettingsDialog(MdgaApp app, Node node, MdgaView view) {
super(app, node);
@@ -34,6 +45,9 @@ public SettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Displays all buttons for video settings, audio settings, and back navigation.
*/
@Override
protected void onShow() {
videoButton.show();
@@ -41,6 +55,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all buttons for video settings, audio settings, and back navigation.
*/
@Override
protected void onHide() {
videoButton.hide();

View File

@@ -14,6 +14,10 @@
import java.util.Random;
import java.util.random.RandomGenerator;
/**
* The {@code StartDialog} class represents the initial dialog in the application,
* allowing the user to input their name, host or join a game, or exit the application.
*/
public class StartDialog extends Dialog {
private InputButton nameInput;
@@ -23,6 +27,13 @@ public class StartDialog extends Dialog {
private final MainView view;
/**
* Constructs a {@code StartDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The main view used for navigation and interaction with the dialog.
*/
public StartDialog(MdgaApp app, Node node, MainView view) {
super(app, node);
@@ -48,6 +59,9 @@ public StartDialog(MdgaApp app, Node node, MainView view) {
endButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Displays the name input field and all buttons.
*/
@Override
protected void onShow() {
nameInput.show();
@@ -57,6 +71,9 @@ protected void onShow() {
endButton.show();
}
/**
* Called when the dialog is hidden. Hides the name input field and all buttons.
*/
@Override
protected void onHide ()
{
@@ -67,10 +84,18 @@ protected void onHide ()
endButton.hide();
}
/**
* Updates the state of the name input field. This method is called periodically to synchronize the dialog state.
*/
public void update() {
nameInput.update();
}
/**
* Retrieves the name entered by the user. If no name is provided, a random name is generated.
*
* @return The user's name or a randomly generated name.
*/
public String getName() {
String name = nameInput.getString();
@@ -177,6 +202,109 @@ public String getName() {
"CarryPro",
"ProBaiter",
"GameWarden",
"KartoffelKönig",
"SaufenderWolf",
"WurstGriller",
"Flitzekacke",
"BratwurstBub",
"Hoppeldoppels",
"BananenMensch",
"KlopapierGuru",
"SchnitzelKing",
"NerdNomade",
"Dönertänzer",
"GlitzerGurke",
"SchinkenShrek",
"KäseKalle",
"SchokoSchnecke",
"KeksKämpfer",
"QuarkPiraten",
"Müslimonster",
"KnuddelNase",
"FantaFighter",
"SchnapsSaurier",
"Wackelpudding",
"ZitronenZock",
"FettWurst",
"PlüschPanda",
"Zuckerschnur",
"FluffiKopf",
"DonutDöner",
"VollpfostenX",
"Waschlappen",
"Witzepumper",
"ToastTraum",
"FroschFighter",
"KrümelTiger",
"RegenWolke",
"PuddingPower",
"KoffeinKrieger",
"SpeckSchlumpf",
"SuperSuppe",
"BierBärchen",
"FischBär",
"Flauschi",
"Schokomonster",
"ChaosKäse",
"FlitzLappen",
"WurstWombat",
"KrümelMensch",
"PuddingBär",
"ZickZack",
"Schwabel",
"Fluffi",
"RülpsFrosch",
"PommesPapa",
"QuarkBär",
"KnusperKönig",
"ToastBrot",
"Ploppster",
"Schleimschwein",
"Äpfelchen",
"Knallbonbon",
"KaffeeKopf",
"WackelWurst",
"RennKeks",
"BröselBub",
"ZockerBrot",
"BierWurm",
"StinkFlummi",
"SchlumpfKing",
"PurzelBär",
"FlinkFluff",
"PloppPudel",
"Schnorchel",
"FliegenKopf",
"PixelPommes",
"SchwipsWürst",
"WutzBär",
"KnuddelKeks",
"FantaFlumm",
"ZockerKäse",
"LachHäufchen",
"GurkenGuru",
"PonySchnitzel",
"NudelNinja",
"VulkanKeks",
"WasserToast",
"MenschSalat",
"KampfKohl",
"SockenZirkus",
"SchwimmBärchen",
"TanzenderPudel",
"PizzamarktMensch",
"ZahnarztZocker",
"RollerRudi",
"PupsPilot",
"WitzigeZwiebel",
"Pillenschlucker",
"ZwiebelReiter",
"HüpfenderKaktus",
"AsteroidenAlf",
"ChaosKarotte",
"WolkenFurz",
"Krümelmonster",
"WackelBiene",
};
Random random = new Random();

View File

@@ -3,16 +3,44 @@
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.button.AbstractButton;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.button.MenuButton;
import pp.mdga.client.view.MdgaView;
import java.util.prefs.Preferences;
/**
* The {@code VideoSettingsDialog} class represents a dialog for configuring video settings,
* such as resolution and fullscreen mode. It also provides an option to restart the application
* when certain settings are changed.
*/
public class VideoSettingsDialog extends Dialog {
private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
private ButtonRight fullscreenButton;
private MenuButton backButton;
private ButtonRight restartButton;
private ButtonLeft hdButton9;
private ButtonLeft fullHdButton9;
private ButtonLeft wqhdButton9;
private ButtonRight hdButton10;
private ButtonRight fullHdButton10;
private ButtonRight wqhdButton10;
private final MdgaView view;
private boolean active = false;
/**
* Constructs a {@code VideoSettingsDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The view managing navigation and interaction with the video settings dialog.
*/
public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
super(app, node);
@@ -20,28 +48,98 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton = new MenuButton(app, node, view::leaveVideoSettings, "Zurück");
restartButton = new ButtonRight(app, node, MdgaApp::restartApp, "Neustart", 1);
fullscreenButton = new ButtonRight(app, node, () -> updateResolution(0, 0, 0, true), "Vollbild", 1);
hdButton9 = new ButtonLeft(app, node, () -> updateResolution(1280, 720, 1.0f, false), "hd 16:9", 10);
fullHdButton9 = new ButtonLeft(app, node, () -> updateResolution(1920, 1080, 2.25f, false), "full hd 16:9", 10);
wqhdButton9 = new ButtonLeft(app, node, () -> updateResolution(2560, 1440, 4.0f, false), "wqhd 16:9", 10);
hdButton10 = new ButtonRight(app, node, () -> updateResolution(1280, 800, 1.0f, false), "hd 16:10", 10);
fullHdButton10 = new ButtonRight(app, node, () -> updateResolution(1920, 1200, 2.25f, false), "full hd 16:10", 10);
wqhdButton10 = new ButtonRight(app, node, () -> updateResolution(2560, 1600, 4.0f, false), "wqhd 16:10", 10);
float offset = 2.8f;
hdButton9.setPos(new Vector2f(hdButton9.getPos().x, MenuButton.VERTICAL - offset));
hdButton10.setPos(new Vector2f(hdButton10.getPos().x, MenuButton.VERTICAL - offset));
fullscreenButton.setPos(new Vector2f(fullscreenButton.getPos().x, MenuButton.VERTICAL - offset));
offset += 1.5f;
fullHdButton9.setPos(new Vector2f(fullHdButton9.getPos().x, MenuButton.VERTICAL - offset));
fullHdButton10.setPos(new Vector2f(fullHdButton10.getPos().x, MenuButton.VERTICAL - offset));
offset += 1.5f;
wqhdButton9.setPos(new Vector2f(wqhdButton9.getPos().x, MenuButton.VERTICAL - offset));
wqhdButton10.setPos(new Vector2f(wqhdButton10.getPos().x, MenuButton.VERTICAL - offset));
offset += 1.5f;
backButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Displays all buttons and marks the dialog as active.
*/
@Override
protected void onShow() {
active = true;
hdButton9.show();
fullHdButton9.show();
wqhdButton9.show();
hdButton10.show();
fullHdButton10.show();
wqhdButton10.show();
fullscreenButton.show();
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all buttons and marks the dialog as inactive.
*/
@Override
protected void onHide() {
active = false;
hdButton9.hide();
fullHdButton9.hide();
wqhdButton9.hide();
hdButton10.hide();
fullHdButton10.hide();
wqhdButton10.hide();
fullscreenButton.hide();
backButton.hide();
restartButton.hide();
}
/**
* Updates the dialog's state. This method can be used for periodic updates while the dialog is active.
*/
public void update() {
if(!active) {
return;
}
}
/**
* Updates the resolution settings and optionally triggers the restart button if changes are detected.
*
* @param width The width of the resolution.
* @param height The height of the resolution.
* @param imageFactor The scaling factor for the resolution.
* @param isFullscreen {@code true} if fullscreen mode is enabled, {@code false} otherwise.
*/
public void updateResolution(int width, int height, float imageFactor, boolean isFullscreen) {
if(width != prefs.getInt("width", 1280) || height != prefs.getInt("height", 720) || isFullscreen != prefs.getBoolean("fullscreen", false)) {
restartButton.show();
}
app.updateResolution(width, height, imageFactor, isFullscreen);
}
}

View File

@@ -7,24 +7,48 @@
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import pp.mdga.client.Asset;
import pp.mdga.client.animation.ZoomControl;
import pp.mdga.game.Color;
public class ActionTextHandler {
/**
* The {@code ActionTextHandler} class manages the display of animated and stylized text messages in the game's UI.
* It supports dynamic text creation with spacing, color, and effects, such as dice rolls, player actions, and rankings.
*/
class ActionTextHandler {
private Node root;
private BitmapFont font;
private AppSettings appSettings;
private int ranking;
public ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){
float paddingRanked = 100;
/**
* Constructs an {@code ActionTextHandler}.
*
* @param guiNode The GUI node where the text messages will be displayed.
* @param assetManager The asset manager used to load fonts and other assets.
* @param appSettings The application settings for positioning and sizing.
*/
ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){
root = new Node("actionTextRoot");
guiNode.attachChild(root);
root.setLocalTranslation(center(appSettings.getWidth(), appSettings.getHeight(), Vector3f.ZERO));
font = assetManager.loadFont("Fonts/Gunplay.fnt");
this.appSettings = appSettings;
ranking = 0;
}
/**
* Creates a {@code Node} containing text with specified spacing, size, and colors for each segment of the text.
*
* @param textArr An array of strings representing the text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param colorArr An array of {@code ColorRGBA} representing the color for each string in {@code textArr}.
* @return A {@code Node} containing the styled text with spacing and color applied.
* @throws RuntimeException if the lengths of {@code textArr} and {@code colorArr} do not match.
*/
private Node createTextWithSpacing(String[] textArr, float spacing, float size, ColorRGBA[] colorArr) {
if(textArr.length != colorArr.length) throw new RuntimeException("text and color are not the same length");
@@ -51,18 +75,55 @@ private Node createTextWithSpacing(String[] textArr, float spacing, float size,
return textNode;
}
/**
* Creates a {@code Node} containing text with specified spacing, size, and a single color.
*
* @param text The text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param color The color of the text.
* @return A {@code Node} containing the styled text.
*/
private Node createTextWithSpacing(String text, float spacing, float size, ColorRGBA color) {
return createTextWithSpacing(new String[]{text}, spacing, size, new ColorRGBA[]{color});
}
/**
* Calculates the center position of a rectangle given its width, height, and an origin position.
*
* @param width The width of the rectangle.
* @param height The height of the rectangle.
* @param pos The origin position of the rectangle.
* @return A {@code Vector3f} representing the center position.
*/
private Vector3f center(float width, float height, Vector3f pos){
return new Vector3f(pos.x+width/2, pos.y+height/2,0);
}
/**
* Creates and positions a single-line text at the top of the screen with a specified vertical offset.
*
* @param name The text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param color The color of the text.
* @param top The vertical offset from the top of the screen.
* @return A {@code Node} containing the styled text positioned at the top.
*/
private Node createTopText(String name, float spacing, float size, ColorRGBA color, float top){
return createTopText(new String[]{name}, spacing, size, new ColorRGBA[]{color}, top);
}
/**
* Creates and positions multi-line text at the top of the screen with specified vertical offset, spacing, and colors.
*
* @param name An array of strings representing the text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param color An array of {@code ColorRGBA} representing the color for each string in {@code name}.
* @param top The vertical offset from the top of the screen.
* @return A {@code Node} containing the styled text positioned at the top.
*/
private Node createTopText(String[] name, float spacing, float size, ColorRGBA color[], float top){
Node text = createTextWithSpacing(name, spacing, size, color);
text.setLocalTranslation(0, (appSettings.getHeight()/2f)*0.8f-top,0);
@@ -70,47 +131,129 @@ private Node createTopText(String[] name, float spacing, float size, ColorRGBA c
return text;
}
/**
* Calculates the center position of a rectangle with negative width offset.
*
* @param width The negative width of the rectangle.
* @param height The height of the rectangle.
* @param pos The origin position of the rectangle.
* @return A {@code Vector3f} representing the center position.
*/
private Vector3f centerText(float width, float height, Vector3f pos){
return center(-width, height, pos);
}
public void activePlayer(String name, Color color){
/**
* Displays a message indicating the active player.
*
* @param name The name of the active player.
* @param color The color representing the player's team.
*/
void activePlayer(String name, Color color){
createTopText(new String[]{name," ist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
public void ownActive(Color color){
/**
* Displays a message indicating that the current player is active.
*
* @param color The color representing the player's team.
*/
void ownActive(Color color){
createTopText(new String[]{"Du"," bist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
public void diceNum(int diceNum, String name, Color color){
/**
* Displays a dice roll result for a player.
*
* @param diceNum The number rolled on the dice.
* @param name The name of the player.
* @param color The color representing the player's team.
*/
void diceNum(int diceNum, String name, Color color){
createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0);
createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 100);
}
public void diceNumMult(int diceNum,int mult, String name, Color color){
/**
* Displays a dice roll result with a multiplier for a player.
*
* @param diceNum The number rolled on the dice.
* @param mult The multiplier applied to the dice result.
* @param name The name of the player.
* @param color The color representing the player's team.
*/
void diceNumMult(int diceNum,int mult, String name, Color color){
createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0);
createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 100);
}
public void ownDice(int diceNum){
/**
* Displays the dice roll result for the current player.
*
* @param diceNum The number rolled on the dice.
*/
void ownDice(int diceNum){
createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 0);
}
public void ownDiceMult(int diceNum, int mult){
/**
* Displays the dice roll result with a multiplier for the current player.
*
* @param diceNum The number rolled on the dice.
* @param mult The multiplier applied to the dice result.
*/
void ownDiceMult(int diceNum, int mult){
createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 0);
}
public void drawCard(String name, Color color){
/**
* Displays a message indicating that a specified player received a bonus card.
*
* @param name The name of the player who received the bonus card.
* @param color The color representing the player's team.
*/
void drawCard(String name, Color color){
createTopText(new String[]{name," erhält eine Bonuskarte"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
public void drawCardOwn(Color color){
/**
* Displays a message indicating that the current player received a bonus card.
*
* @param color The color representing the player's team.
*/
void drawCardOwn(Color color){
createTopText(new String[]{"Du"," erhälst eine Bonuskarte"}, 5,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a message indicating that a specified player has completed their turn or action.
*
* @param name The name of the player who finished.
* @param color The color representing the player's team.
*/
void finishText(String name, Color color){
createTopText(new String[]{name," ist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a message indicating that the current player has completed their turn or action.
*
* @param color The color representing the player's team.
*/
void finishTextOwn(Color color){
createTopText(new String[]{"Du", " bist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Converts a player's team color to a corresponding {@code ColorRGBA}.
*
* @param color The player's team color.
* @return The corresponding {@code ColorRGBA}.
* @throws RuntimeException if the color is invalid.
*/
private ColorRGBA playerColorToColorRGBA(Color color){
return switch (color){
case ARMY -> ColorRGBA.Green;
@@ -121,9 +264,41 @@ private ColorRGBA playerColorToColorRGBA(Color color){
};
}
public void hide(){
root.detachAllChildren();
/**
* Hides all text messages displayed by the handler and resets the ranking counter.
*/
void hide(){
ranking = 0;
root.detachAllChildren();
}
/**
* Displays a ranked dice roll result for a specified player.
*
* @param name The name of the player.
* @param color The color representing the player's team.
* @param eye The dice roll result.
*/
void rollRankingResult(String name, Color color, int eye){
createTopText(new String[]{name,": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking);
ranking++;
}
/**
* Displays a ranked dice roll result for the current player.
*
* @param color The color representing the player's team.
* @param eye The dice roll result.
*/
void rollRankingResultOwn(Color color, int eye){
createTopText(new String[]{"Du",": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking);
ranking++;
}
/**
* Displays a message prompting the player to roll the dice.
*/
void diceNow(){
createTopText("Klicke zum Würfeln", 5, 80, ColorRGBA.White, 0);
}
}

View File

@@ -60,7 +60,7 @@ private Node createNum(){
Material mat = new Material(getApp().getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Black);
circle.setMaterial(mat);
root.attachChild(circle);
// root.attachChild(circle);
BitmapFont guiFont = getApp().getAssetManager().loadFont("Fonts/Gunplay.fnt");
num = new BitmapText(guiFont);
num.setSize(0.3f);

View File

@@ -4,7 +4,6 @@
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
@@ -15,16 +14,16 @@
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.texture.Image;
import com.jme3.texture.Texture2D;
import pp.mdga.client.Asset;
import java.util.*;
public class CardLayer extends AbstractAppState {
public static final int SHADOWMAP_SIZE = 1024 * 8;
private Node root;
private Application app;
private boolean init;
@@ -43,7 +42,7 @@ public CardLayer(FilterPostProcessor fpp, Camera overlayCam, Texture2D backTextu
}
@Override
public void initialize(AppStateManager stateManager, Application app ) {
public void initialize(AppStateManager stateManager, Application app) {
this.app = app;
root = new Node("Under gui viewport Root");
@@ -57,11 +56,9 @@ public void initialize(AppStateManager stateManager, Application app ) {
DirectionalLight sun = new DirectionalLight();
sun.setColor(ColorRGBA.White);
sun.setDirection(new Vector3f(.5f,-.5f,-1));
sun.setDirection(new Vector3f(.5f, -.5f, -1));
root.addLight(sun);
final int SHADOWMAP_SIZE=1024*8;
DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 3);
dlsf.setLight(sun);
dlsf.setEnabled(true);
@@ -71,25 +68,24 @@ public void initialize(AppStateManager stateManager, Application app ) {
view.addProcessor(fpp);
if(!init) init = true;
if (!init) init = true;
}
public void shutdown(){
public void shutdown() {
cardBuffer.clear();
root.detachAllChildren();
}
@Override
public void render(RenderManager rm) {
root.updateGeometricState();
}
@Override
public void update( float tpf ) {
public void update(float tpf) {
if (init && !cardBuffer.isEmpty()) {
for(Spatial spatial : cardBuffer){
for (Spatial spatial : cardBuffer) {
root.attachChild(spatial);
}
cardBuffer.clear();
@@ -97,19 +93,20 @@ public void update( float tpf ) {
root.updateLogicalState(tpf);
}
public void addSpatial(Spatial card){
cardBuffer.add(card);
public void addSpatial(Spatial card) {
if(root == null) cardBuffer.add(card);
else root.attachChild(card);
}
public void deleteSpatial(Spatial spatial){
public void deleteSpatial(Spatial spatial) {
root.detachChild(spatial);
}
public Camera getOverlayCam(){
public Camera getOverlayCam() {
return overlayCam;
}
public Node getRootNode(){
public Node getRootNode() {
return root;
}
}

View File

@@ -10,6 +10,7 @@
import com.jme3.texture.Texture2D;
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.animation.SymbolControl;
import pp.mdga.game.BonusCard;
import java.util.*;
@@ -84,6 +85,19 @@ public void addCard(BonusCard card) {
bonusCardControlMap.get(card).setNumCard(newNum);
}
public void removeCard(BonusCard card){
if(bonusCardControlMap.containsKey(card)){
bonusCardIntegerMap.put(card, bonusCardIntegerMap.get(card) - 1);
if(bonusCardIntegerMap.get(card) <= 0){
cardLayer.deleteSpatial(bonusCardControlMap.get(card).getRoot());
bonusCardIntegerMap.remove(card);
bonusCardControlMap.remove(card);
}
}
}
public void clearSelectableCards() {
for (CardControl control : selectableCards) {
control.setSelectable(false);
@@ -117,6 +131,8 @@ public void selectCard(CardControl cardControl) {
cardControl.select();
cardSelect = getKeyByValue(bonusCardControlMap, cardControl);
}
app.getModelSynchronize().selectCard(cardSelect);
}
public Camera getCardLayerCamera() {

View File

@@ -7,6 +7,7 @@
import com.jme3.texture.Texture2D;
import pp.mdga.client.MdgaApp;
import pp.mdga.game.Color;
import pp.mdga.game.BonusCard;
import java.util.List;
@@ -50,7 +51,7 @@ public void rollDice(int rollNum, int mult) {
if(mult == -1) actionTextHandler.ownDice(rollNum);
else actionTextHandler.ownDiceMult(rollNum, mult);
hideDice();
//TODO send Model finished
app.getModelSynchronize().animationEnd();
});
}
@@ -66,21 +67,45 @@ public void showRolledDice(int rollNum, Color color) {
public void showDice() {
cardLayerHandler.showDice();
actionTextHandler.diceNow();
}
public void hideDice() {
cardLayerHandler.hideDice();
}
public void addCard(pp.mdga.game.BonusCard card) {
//add own handCard
public void addCardOwn(BonusCard card) {
cardLayerHandler.addCard(card);
playerNameHandler.addCard(ownColor);
actionTextHandler.drawCardOwn(ownColor);
}
public void playCardOwn(BonusCard card){
getEffectByCard(card);
cardLayerHandler.removeCard(card);
playerNameHandler.removeCard(ownColor);
}
public void playCardEnemy(Color color, BonusCard card) {
getEffectByCard(card);
playerNameHandler.removeCard(color);
}
private void getEffectByCard(BonusCard bonus){
switch(bonus){
case SWAP -> swap();
case TURBO -> turbo();
case SHIELD -> shield();
default -> throw new RuntimeException("invalid card");
}
}
public void clearSelectableCards() {
cardLayerHandler.clearSelectableCards();
}
public void setSelectableCards(List<pp.mdga.game.BonusCard> select) {
public void setSelectableCards(List<BonusCard> select) {
cardLayerHandler.setSelectableCards(select);
}
@@ -123,10 +148,24 @@ public void hideText(){
actionTextHandler.hide();
}
//addCard Enemy (DrawCardNotification)
public void drawCard(Color color) {
if (ownColor == color) actionTextHandler.drawCardOwn(color);
else actionTextHandler.drawCard(playerNameHandler.getName(color), color);
//Color != ownColor
actionTextHandler.drawCard(playerNameHandler.getName(color), color);
playerNameHandler.addCard(color);
}
public void finish(Color color){
if(ownColor == color) actionTextHandler.finishTextOwn(color);
else actionTextHandler.finishText(playerNameHandler.getName(color), color);
}
public void rollRankingResult(Color color, int eye){
if(ownColor == color) actionTextHandler.rollRankingResultOwn(color, eye);
else actionTextHandler.rollRankingResult(playerNameHandler.getName(color), color, eye);
}
}

View File

@@ -4,23 +4,27 @@
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.system.AppSettings;
import com.jme3.ui.Picture;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
public class PlayerNameHandler {
private final BitmapFont playerFont;
private final Node playerNameNode;
private final List<Color> playerOrder;
private final Map<Color, String> colorNameMap;
private final Map<Color, Integer> colorCardMap;
private final AppSettings appSettings;
private final AssetManager assetManager;
private Color ownColor;
@@ -43,6 +47,7 @@ public PlayerNameHandler(Node guiNode, AssetManager assetManager, AppSettings ap
playerNameNode = new Node("player name node");
playerOrder = new ArrayList<>();
colorNameMap = new HashMap<>();
colorCardMap = new HashMap<>();
this.appSettings = appSettings;
this.assetManager = assetManager;
}
@@ -64,13 +69,38 @@ private void drawPlayers(){
if(!colorNameMap.containsKey(color)) throw new RuntimeException(color + " isn't mapped to a name");
Node nameParent = new Node("nameParent");
nameParent.attachChild(createName(colorNameMap.get(color), i == 0, color == ownColor));
nameParent.attachChild(createColor(color));
BitmapText name = createName(colorNameMap.get(color), i == 0, color == ownColor);
nameParent.attachChild(name);
if(colorCardMap.getOrDefault(color, 0) > 0){
Picture pic = createHandCard(name.getLineWidth());
nameParent.attachChild(pic);
nameParent.attachChild(createCardNum(colorCardMap.get(color), pic.getWidth(), pic.getLocalTranslation().getX()));
}
nameParent.setLocalTranslation(50,appSettings.getWindowHeight()-PADDING_TOP- MARGIN_NAMES *i,0);
playerNameNode.attachChild(nameParent);
}
}
private Spatial createCardNum(int num, float lastWidth, float lastX ) {
BitmapText hudText = new BitmapText(playerFont);
//renderedSize = 45
hudText.setSize(TEXT_SIZE);
hudText.setColor(NORMAL_COLOR);
hudText.setText(String.valueOf(num));
hudText.setLocalTranslation(lastX + lastWidth + 20,hudText.getHeight()/2, 0);
return hudText;
}
private Picture createHandCard(float width) {
Picture pic = new Picture("HUD Picture");
pic.setImage(assetManager, "./Images/handcard.png", true);
pic.setWidth(IMAGE_SIZE);
pic.setHeight(IMAGE_SIZE);
pic.setPosition(-pic.getWidth()/2 + width + PADDING_LEFT * 2 ,-pic.getHeight()/2);
return pic;
}
private String imagePath(Color color){
String root = "./Images/name_pictures/";
return switch(color){
@@ -93,7 +123,7 @@ private Spatial createColor(Color color) {
private Spatial createName(String name, boolean first, boolean own){
private BitmapText createName(String name, boolean first, boolean own){
BitmapText hudText = new BitmapText(playerFont);
//renderedSize = 45
hudText.setSize(TEXT_SIZE);
@@ -120,8 +150,22 @@ public void setActivePlayer(Color color) {
}
public String getName(Color color){
if(!colorNameMap.containsKey(color)) throw new RuntimeException("color is not in colorNameMap");
return colorNameMap.get(color);
}
public void addCard(Color color){
colorCardMap.put(color, colorCardMap.getOrDefault(color, 0) + 1);
drawPlayers();
}
public void removeCard(Color color){
if(colorCardMap.containsKey(color)){
colorCardMap.put(color, colorCardMap.getOrDefault(color, 0) - 1);
if(colorCardMap.get(color) <= 0) colorCardMap.remove(color);
}
drawPlayers();
}
}

View File

@@ -1,147 +0,0 @@
package pp.mdga.client.gui;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.game.BonusCard;
public class SymbolControl extends AbstractControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float zoomSpeed = 1f;
private float zoomFactor = 3f;
private float progress = 0;
private BonusCard state;
private float rotationSpeed = 0.8f;
private Quaternion initialRotation = null;
private float Y = 5;
@Override
protected void controlUpdate(float tpf) {
if(state == null) return;
switch (state){
case SHIELD -> shieldUpdate(tpf);
case SWAP -> swapUpdate(tpf);
case TURBO -> turboUpdate(tpf);
case HIDDEN -> throw new RuntimeException("forbidden state");
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
private void shieldUpdate(float tpf){
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
progress = 0;
}
}
}
private void swapUpdate(float tpf){
if (initialRotation == null) {
initialRotation = spatial.getLocalRotation().clone();
}
progress += tpf*rotationSpeed;
if(progress < 0) return;
float angle = lerp(0, 360, easeInOut(progress));
Quaternion newRotation = new Quaternion();
newRotation.fromAngleAxis((float) Math.toRadians(angle), new Vector3f(0, 1, 0));
spatial.setLocalRotation(initialRotation.mult(newRotation));
if (progress >= 1.2f) {
state = null;
initialRotation = null;
progress = 0;
spatial.removeFromParent();
}
}
private void turboUpdate(float tpf){
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
float y = lerp(-Y,0, easeOut(progress));
spatial.setLocalTranslation(0,y,0);
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
float y = lerp(0,Y, easeIn(progress));
spatial.setLocalTranslation(0,y,0);
if (progress > 1) {
zoomingIn = false;
spatial.removeFromParent();
state = null;
}
}
}
public void shield(){
if(state != null) throw new RuntimeException("another state is avtive");
state = BonusCard.SHIELD;
zoomingIn = true;
zoomingOut = false;
progress = 0;
spatial.setLocalScale(1f);
}
public void swap(){
if(state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(3);
state = BonusCard.SWAP;
progress = -0.2f;
}
public void turbo(){
if(state != null) throw new RuntimeException("another state is avtive");
spatial.setLocalScale(2);
state = BonusCard.TURBO;
zoomingIn = true;
zoomingOut = false;
progress = 0;
}
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
private static float easeOut(float t) {
return (float) Math.sqrt(1 - Math.pow(t - 1, 2));
}
private float easeInOut(float t) {
if(t>1) t=1;
return (float) -(Math.cos(Math.PI * t) - 1) / 2;
}
private float easeIn(float t) {
return t * t * t * t;
}
}

View File

@@ -1,82 +0,0 @@
package pp.mdga.client.gui;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
public class ZoomControl extends AbstractControl {
private boolean zoomingIn = false;
private boolean zoomingOut = false;
private float progress = 0;
private float zoomSpeed = 1f;
private float zoomFactor = 1f;
public ZoomControl(){}
public ZoomControl(float speed) {
zoomSpeed = speed;
}
@Override
public void setSpatial(Spatial spatial){
if(this.spatial == null && spatial != null){
super.setSpatial(spatial);
initSpatial();
}
}
private void initSpatial() {
zoomingIn = true;
}
@Override
protected void controlUpdate(float tpf) {
if (zoomingIn) {
progress += tpf * zoomSpeed;
if (progress > 1) progress = 1;
spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress)));
if (progress >= 1) {
zoomingIn = false;
zoomingOut = true;
progress = 0;
}
} else if (zoomingOut) {
progress += tpf * zoomSpeed;
spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress)));
if (progress > 1) {
zoomingOut = false;
end();
}
}
}
private void end(){
spatial.removeFromParent();
spatial.removeControl(this);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
private static float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
// private static float easeOut(float t) {
// return (float) Math.sqrt(1 - Math.pow(t - 1, 2));
// }
private float easeOut(float x) {
return x == 1 ? 1 : (float) (1 - Math.pow(2, -10 * x));
}
// private float easeIn(float t) {
// return t * t * t * t;
// }
private float easeIn(float x) {
return x == 0 ? 0 : (float) Math.pow(2, 10 * x - 10);
}
}

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
@@ -10,7 +10,7 @@
import com.jme3.renderer.ViewPort;
import com.jme3.texture.FrameBuffer;
public class OutlineFilter extends Filter {
private OutlinePreFilter outlinePreFilter;

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
@@ -11,12 +11,12 @@
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
*/

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;

View File

@@ -1,4 +1,4 @@
package pp.mdga.client.board.Outline;
package pp.mdga.client.outline;
import com.jme3.asset.AssetManager;
import com.jme3.math.ColorRGBA;

View File

@@ -2,8 +2,10 @@
import com.jme3.network.*;
import com.jme3.network.serializing.Serializer;
import pp.mdga.game.Game;
import pp.mdga.game.Player;
import com.jme3.network.serializing.serializers.EnumSerializer;
import pp.mdga.Resources;
import pp.mdga.game.*;
import pp.mdga.game.card.*;
import pp.mdga.message.client.*;
import pp.mdga.message.server.*;
import pp.mdga.server.ServerGameLogic;
@@ -13,8 +15,8 @@
import java.io.IOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.sql.Connection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.LogManager;
@@ -67,13 +69,13 @@ public void run() {
private void startServer() {
try {
LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS
myServer = Network.createServer(port);
initializeSerializables();
myServer.start();
registerListeners();
LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS
}
catch (IOException e) {
} catch (IOException e) {
LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS
exit(1);
}
@@ -89,6 +91,7 @@ private void processNextMessage() {
}
private void initializeSerializables() {
Serializer.registerClass(UUID.class, new UUIDSerializer());
Serializer.registerClass(AnimationEndMessage.class);
Serializer.registerClass(ClientStartGameMessage.class);
Serializer.registerClass(DeselectTSKMessage.class);
@@ -106,7 +109,6 @@ private void initializeSerializables() {
Serializer.registerClass(SelectCardMessage.class);
Serializer.registerClass(SelectedPiecesMessage.class);
Serializer.registerClass(SelectTSKMessage.class);
Serializer.registerClass(ActivePlayerMessage.class);
Serializer.registerClass(AnyPieceMessage.class);
Serializer.registerClass(BriefingMessage.class);
@@ -123,23 +125,44 @@ private void initializeSerializables() {
Serializer.registerClass(NoTurnMessage.class);
Serializer.registerClass(PauseGameMessage.class);
Serializer.registerClass(PlayCardMessage.class);
Serializer.registerClass(PossibleCardMessage.class);
Serializer.registerClass(PossibleCardsMessage.class);
Serializer.registerClass(PossiblePieceMessage.class);
Serializer.registerClass(RankingResponseMessage.class);
Serializer.registerClass(RankingRollAgainMessage.class);
Serializer.registerClass(ReconnectBriefingMessage.class);
Serializer.registerClass(ResumeGameMessage.class);
Serializer.registerClass(ServerStartGameMessage.class);
Serializer.registerClass(ShutdownMessage.class);
Serializer.registerClass(StartPieceMessage.class);
Serializer.registerClass(UpdateReadyMessage.class);
Serializer.registerClass(UpdateTSKMessage.class);
Serializer.registerClass(WaitPieceMessage.class);
Serializer.registerClass(IncorrectRequestMessage.class);
Serializer.registerClass(Player.class);
Serializer.registerClass(Statistic.class);
Serializer.registerClass(Board.class);
Serializer.registerClass(Node.class);
Serializer.registerClass(Piece.class);
Serializer.registerClass(BonusNode.class);
Serializer.registerClass(StartNode.class);
Serializer.registerClass(HomeNode.class);
Serializer.registerClass(PowerCard.class);
Serializer.registerClass(TurboCard.class);
Serializer.registerClass(SwapCard.class);
Serializer.registerClass(ShieldCard.class);
Serializer.registerClass(HiddenCard.class);
Serializer.registerClass(Color.class, new EnumSerializer());
Serializer.registerClass(PieceState.class, new EnumSerializer());
Serializer.registerClass(ShieldState.class, new EnumSerializer());
Serializer.registerClass(BonusCard.class, new EnumSerializer());
}
private void registerListeners() {
myServer.addMessageListener(this, AnimationEndMessage.class);
myServer.addMessageListener(this, ClientStartGameMessage.class);
myServer.addMessageListener(this, DeselectTSKMessage.class);
myServer.addMessageListener(this, DisconnectedMessage.class);
myServer.addMessageListener(this, ForceContinueGameMessage.class);
myServer.addMessageListener(this, StartGameMessage.class);
myServer.addMessageListener(this, JoinedLobbyMessage.class);
@@ -185,9 +208,32 @@ private void messageReceived(HostedConnection source, ClientMessage message) {
pendingMessages.add(new ReceivedMessage(message, source.getId()));
}
/**
* This method will be used to handle all connections which are connected to the server.
* It will check if the maximum number of connected clients are already reached. If yes it will send a
* LobbyDenyMessage to the given hostedConnection parameter and close it, otherwise it will send a
* LobbyAcceptMessage to the given hostedConnection parameter. In Addition, if the number of connected clients is
* equal to 1 it will set the host of the game to the id of the given hostedConnection parameter.
*
* @param server as the server which is contains all connections as a Server object.
* @param hostedConnection as the connection which is added to the server as a HostedConnection object.
*/
@Override
public void connectionAdded(Server server, HostedConnection hostedConnection) {
LOGGER.log(Level.INFO, "new connection {0}", hostedConnection); //NON-NLS
System.out.println("new connection " + hostedConnection); //NON-NLS
LOGGER.log(Level.DEBUG, "new connection {0}", hostedConnection); //NON-NLS
if (this.myServer.getConnections().size() > Resources.MAX_PLAYERS) {
this.logic.getServerSender().send(hostedConnection.getId(), new LobbyDenyMessage());
hostedConnection.close("");
} else {
if (hostedConnection.getAddress().contains("127.0.0.1") && this.logic.getGame().getHost() == -1) {
this.logic.getGame().setHost(hostedConnection.getId());
this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage(hostedConnection.getId()));
} else {
this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage());
}
}
}
@Override
@@ -247,7 +293,7 @@ public void send(int id, ServerMessage message) {
*/
@Override
public void broadcast(ServerMessage message) {
for (Map.Entry<Integer, Player> entry: this.logic.getGame().getPlayers().entrySet()) {
for (Map.Entry<Integer, Player> entry : this.logic.getGame().getPlayers().entrySet()) {
this.send(entry.getKey(), message);
}
}
@@ -261,4 +307,21 @@ public void broadcast(ServerMessage message) {
public void disconnectClient(int id) {
this.myServer.getConnection(id).close("");
}
/**
* This method will be used to shut down the server.
* It will iterate threw all connections of myServer attribute and check if they are equal to null. If not they will
* be closed. After that the myServer attribute will be closed and this program will be exited with the exit code 0.
*/
@Override
public void shutdown() {
for (HostedConnection client : this.myServer.getConnections()) {
if (client != null) {
client.close("Host closed the server.");
}
}
this.myServer.close();
this.exit(0);
}
}

View File

@@ -0,0 +1,26 @@
package pp.mdga.client.server;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.UUID;
import com.jme3.network.serializing.Serializer;
public class UUIDSerializer extends Serializer
{
@Override
public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException
{
byte[] uuid = new byte[36];
data.get(uuid);
return (T) UUID.fromString(new String(uuid));
}
@Override
public void writeObject(ByteBuffer buffer, Object object) throws IOException
{
UUID uuid = (UUID) object;
buffer.put(uuid.toString().getBytes());
}
}

View File

@@ -206,11 +206,19 @@ public void addCeremonyParticipant(Color color, int pos, String name) {
ceremonyButtons.add(button);
if(state.equals(SubState.AWARD_CEREMONY)) {
button.hide();
button.show();
}
}
public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5, int v6) {
ceremonyDialog.addStatisticsRow(name, v1, v2, v3, v4, v5, v6);
ceremonyDialog.hide();
ceremonyDialog.show();
}
public void afterGameCleanup() {
ceremonyDialog.prepare();
}
}

View File

@@ -1,109 +1,64 @@
package pp.mdga.client.view;
import com.jme3.post.FilterPostProcessor;
import pp.mdga.client.MdgaState;
import com.jme3.scene.Node;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.board.BoardHandler;
import pp.mdga.client.board.CameraHandler;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.button.ButtonLeft;
import pp.mdga.client.button.ButtonRight;
import pp.mdga.client.dialog.InterruptDialog;
import pp.mdga.client.gui.GuiHandler;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import pp.mdga.notification.AcquireCardNotification;
import pp.mdga.notification.ActivePlayerNotification;
import pp.mdga.notification.DiceNowNotification;
import pp.mdga.notification.GameNotification;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.PlayerInGameNotification;
import pp.mdga.notification.RollDiceNotification;
import pp.mdga.notification.SelectableCardsNotification;
import pp.mdga.notification.SelectableMoveNotification;
import pp.mdga.notification.ShieldActiveNotification;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class GameView extends MdgaView {
private BoardHandler boardHandler;
private CameraHandler camera;
private GuiHandler guiHandler;
private ButtonRight cheatButton; //TODO
private ButtonLeft leaveButton;
private ButtonRight confirmButton;
private ButtonRight noPowerButton;
private Color ownColor = null;
private InterruptDialog interruptDialog;
private FilterPostProcessor fpp;
private Node guiHandlerNode = new Node();
public GameView(MdgaApp app) {
super(app);
cheatButton = new ButtonRight(app, overlayNode, () -> app.getModelSynchronize().enter(MdgaState.CEREMONY), "CHEAT", 1);
leaveButton = new ButtonLeft(app, overlayNode, () -> app.getModelSynchronize().leave(), "Spiel verlassen", 1);
leaveButton = new ButtonLeft(app, settingsNode, () -> app.getModelSynchronize().leave(), "Spiel verlassen", 1);
confirmButton = new ButtonRight(app, guiNode, () -> app.getModelSynchronize().confirm(), "Bestätigen", 1);
noPowerButton = new ButtonRight(app, guiNode, () -> app.getModelSynchronize().confirm(), "Verzichten", 1);
interruptDialog = new InterruptDialog(app, guiNode);
fpp = new FilterPostProcessor(app.getAssetManager());
this.camera = new CameraHandler(app, fpp);
this.boardHandler = new BoardHandler(app, rootNode, fpp);
guiHandler = new GuiHandler(app, guiNode);
guiHandler = new GuiHandler(app, guiHandlerNode);
guiNode.attachChild(guiHandlerNode);
}
@Override
public void onEnter() {
camera.init();
camera.init(ownColor);
boardHandler.init();
setOwnColor(Color.AIRFORCE);
guiHandler.init(ownColor);
app.getViewPort().addProcessor(fpp);
app.getAcousticHandler().playSound(MdgaSound.START);
//Test
List<UUID> uuid1 = new ArrayList<>();
UUID p1 = UUID.randomUUID();
UUID p2 = UUID.randomUUID();
uuid1.add(p1);
uuid1.add(p2);
uuid1.add(UUID.randomUUID());
uuid1.add(UUID.randomUUID());
List<UUID> uuid2 = new ArrayList<>();
UUID p1_2 = UUID.randomUUID();
UUID p2_2 = UUID.randomUUID();
uuid2.add(p1_2);
uuid2.add(p2_2);
uuid2.add(UUID.randomUUID());
uuid2.add(UUID.randomUUID());
app.getNotificationSynchronizer().addTestNotification(new PlayerInGameNotification(Color.AIRFORCE, uuid1, "Cedric"));
app.getNotificationSynchronizer().addTestNotification(new PlayerInGameNotification(Color.NAVY, uuid2, "Test"));
app.getNotificationSynchronizer().addTestNotification(new MovePieceNotification(p1, 0, true));
app.getNotificationSynchronizer().addTestNotification(new MovePieceNotification(p1_2, 30, true));
// app.getNotificationSynchronizer().addTestNotification(new SelectableMoveNotification(List.of(p1), List.of(4), List.of(false)));
app.getNotificationSynchronizer().addTestNotification(new AcquireCardNotification(BonusCard.SHIELD));
// app.getNotificationSynchronizer().addTestNotification(new SelectableCardsNotification(List.of(BonusCard.SHIELD)));
// app.getNotificationSynchronizer().addTestNotification(new ShieldActiveNotification(p1));
// app.getNotificationSynchronizer().addTestNotification(new ActivePlayerNotification(Color.NAVY));
// app.getNotificationSynchronizer().addTestNotification(new DiceNowNotification());
// app.getNotificationSynchronizer().addTestNotification(new RollDiceNotification(Color.AIRFORCE, 5, true, 2));
p1 = p1;
}
@Override
@@ -114,6 +69,7 @@ public void onLeave() {
confirmButton.hide();
noPowerButton.hide();
app.getViewPort().removeProcessor(fpp);
}
@@ -127,7 +83,6 @@ public void onUpdate(float tpf) {
protected void onEnterOverlay(Overlay overlay) {
if(overlay == Overlay.SETTINGS) {
leaveButton.show();
cheatButton.show();
}
}
@@ -135,7 +90,6 @@ protected void onEnterOverlay(Overlay overlay) {
protected void onLeaveOverlay(Overlay overlay) {
if(overlay == Overlay.SETTINGS) {
leaveButton.hide();
cheatButton.hide();
}
}
@@ -160,10 +114,45 @@ public Color getOwnColor() {
}
public void needConfirm() {
noPowerButton.hide();
confirmButton.show();
}
public void noConfirm() {
confirmButton.hide();
}
public void showNoPower() {
confirmButton.hide();
noPowerButton.show();
}
public void hideNoPower() {
noPowerButton.hide();
}
public void enterInterrupt(Color color) {
enterOverlay(Overlay.INTERRUPT);
guiNode.detachChild(guiHandlerNode);
app.getGuiNode().attachChild(guiNode);
app.getInputSynchronize().setClickAllowed(false);
interruptDialog.setColor(color);
interruptDialog.show();
}
public void leaveInterrupt() {
leaveOverlay(Overlay.INTERRUPT);
app.getGuiNode().detachChild(guiNode);
guiNode.attachChild(guiHandlerNode);
app.getInputSynchronize().setClickAllowed(true);
app.getAcousticHandler().playSound(MdgaSound.START);
interruptDialog.hide();
}
}

View File

@@ -18,6 +18,7 @@
import pp.mdga.client.button.LobbyButton;
import pp.mdga.client.button.SettingsButton;
import pp.mdga.game.Color;
import pp.mdga.message.client.StartGameMessage;
import pp.mdga.notification.GameNotification;
public class LobbyView extends MdgaView {
@@ -25,6 +26,7 @@ public class LobbyView extends MdgaView {
private ButtonLeft leaveButton;
private ButtonRight readyButton;
private ButtonRight startButton;
private LobbyButton cyberButton;
private LobbyButton airforceButton;
@@ -42,6 +44,7 @@ public LobbyView(MdgaApp app) {
leaveButton = new ButtonLeft(app, guiNode, this::leaveLobby, "Verlassen", 1);
readyButton = new ButtonRight(app, guiNode, this::ready, "Bereit", 1);
startButton = new ButtonRight(app, guiNode, () -> app.getGameLogic().selectStart(), "Starten", 7);
cyberButton = new LobbyButton(app, guiNode, rootNode, () -> toggleTsk(Color.CYBER), Color.CYBER);
airforceButton = new LobbyButton(app, guiNode, rootNode, () -> toggleTsk(Color.AIRFORCE), Color.AIRFORCE);
@@ -61,6 +64,10 @@ public void onEnter() {
leaveButton.show();
readyButton.show();
if(app.getGameLogic().isHost()) {
startButton.show();
}
cyberButton.show();
airforceButton.show();
armyButton.show();
@@ -95,6 +102,7 @@ public void onEnter() {
public void onLeave() {
leaveButton.hide();
readyButton.hide();
startButton.hide();
airforceButton.hide();
armyButton.hide();
@@ -223,12 +231,16 @@ private void toggleTsk(Color color) {
break;
}
if(isReady) {
setReady(own, false);
}
switch (taken) {
case NOT:
app.getModelSynchronize().selectTsk(color);
break;
case SELF:
app.getModelSynchronize().unselectTsk();
app.getModelSynchronize().unselectTsk(color);
break;
case OTHER:
//nothing

View File

@@ -60,12 +60,12 @@ public void onUpdate(float tpf) {
@Override
protected void onEnterOverlay(Overlay overlay) {
guiNode.detachChild(background);
overlayNode.attachChild(background);
settingsNode.attachChild(background);
}
@Override
protected void onLeaveOverlay(Overlay overlay) {
overlayNode.detachChild(background);
settingsNode.detachChild(background);
guiNode.attachChild(background);
}
@@ -93,18 +93,24 @@ private void mainMenu() {
private void tryHost() {
int port = 0;
String text = hostDialog.getPort();
app.getGameLogic().selectHost("");
try {
port = Integer.parseInt(text);
if(port >= 1 && port <= 65535) {
app.getModelSynchronize().setName(startDialog.getName());
hostDialog.hostServer();
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
hostDialog.connectServerAsClient();
app.getModelSynchronize().setHost(port);
//app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT);
return;
}
} catch (NumberFormatException e) {
//nothing
} catch (NumberFormatException ignored) {
}
hostDialog.resetPort();
@@ -114,7 +120,8 @@ private void tryHost() {
private void tryJoin() {
int port = 0;
String ip = joinDialog.getIpt();
String portText = hostDialog.getPort();
String portText = joinDialog.getPort();
app.getGameLogic().selectJoin("");
try {
// Validate the port
@@ -122,18 +129,19 @@ private void tryJoin() {
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Invalid port");
}
joinDialog.setPortNumber(port);
// Validate the IP address
if (isValidIpAddress(ip)) {
app.getModelSynchronize().setName(startDialog.getName());
app.getModelSynchronize().setJoin(ip, port);
joinDialog.setHostname(ip);
joinDialog.connectToServer();
return;
}
} catch (IllegalArgumentException e) {
// Invalid input, fall through to reset
}
hostDialog.resetPort();
joinDialog.resetPort();
joinDialog.resetIp();
app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT);
}
@@ -216,5 +224,13 @@ public void back() {
break;
}
}
public JoinDialog getJoinDialog() {
return joinDialog;
}
public HostDialog getHostDialog() {
return hostDialog;
}
}

View File

@@ -2,9 +2,12 @@
import com.jme3.asset.TextureKey;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import com.jme3.system.NanoTimer;
import com.jme3.texture.Texture;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
@@ -22,7 +25,7 @@ public enum Overlay {
protected MdgaApp app;
protected Node rootNode = new Node("View Root");
protected Node guiNode = new Node("View Root GUI");
protected Node overlayNode = new Node("View Root Overlay");
protected Node settingsNode = new Node("View Root Overlay");
private SettingsButton settingsButton;
@@ -30,15 +33,18 @@ public enum Overlay {
private VideoSettingsDialog videoSettingsDialog;
private AudioSettingsDialog audioSettingsDialog;
protected LabelButton infoLabel = null;
protected NanoTimer infoTimer = new NanoTimer();
private int settingsDepth = 0;
public MdgaView(MdgaApp app) {
this.app = app;
settingsButton = new SettingsButton(app, guiNode, this::enterSettings);
settingsDialog = new SettingsDialog(app, overlayNode, this);
videoSettingsDialog = new VideoSettingsDialog(app, overlayNode, this);
audioSettingsDialog = new AudioSettingsDialog(app, overlayNode, this);
settingsDialog = new SettingsDialog(app, settingsNode, this);
videoSettingsDialog = new VideoSettingsDialog(app, settingsNode, this);
audioSettingsDialog = new AudioSettingsDialog(app, settingsNode, this);
}
public void enter() {
@@ -80,14 +86,23 @@ public void update(float tpf) {
videoSettingsDialog.update();
audioSettingsDialog.update();
if (null != infoLabel && infoTimer.getTimeInSeconds() > 5) {
infoLabel.hide();
infoLabel = null;
}
onUpdate(tpf);
}
protected abstract void onEnter();
protected abstract void onLeave();
protected void onUpdate(float tpf) {}
protected void onUpdate(float tpf) {
}
protected abstract void onEnterOverlay(Overlay overlay);
protected abstract void onLeaveOverlay(Overlay overlay);
protected Geometry createBackground(String texturePath) {
@@ -109,7 +124,7 @@ protected Geometry createBackground(String texturePath) {
public void enterSettings() {
enterOverlay(Overlay.SETTINGS);
app.getGuiNode().attachChild(overlayNode);
app.getGuiNode().attachChild(settingsNode);
settingsDialog.show();
@@ -119,7 +134,7 @@ public void enterSettings() {
public void leaveSettings() {
leaveOverlay(Overlay.SETTINGS);
app.getGuiNode().detachChild(overlayNode);
app.getGuiNode().detachChild(settingsNode);
settingsDialog.hide();
@@ -162,9 +177,9 @@ private void leaveAdvanced() {
}
public void pressEscape() {
if(settingsDepth == 0) {
if (settingsDepth == 0) {
enterSettings();
} else if(settingsDepth == 1) {
} else if (settingsDepth == 1) {
leaveSettings();
} else {
leaveAdvanced();
@@ -172,21 +187,41 @@ public void pressEscape() {
}
public void pressForward() {
if(this instanceof MainView mainView) {
if (this instanceof MainView mainView) {
mainView.forward(false);
app.getAcousticHandler().playSound(MdgaSound.BUTTON_PRESSED);
}
if(this instanceof LobbyView lobbyView) {
if (this instanceof LobbyView lobbyView) {
lobbyView.ready();
}
if(this instanceof GameView gameView) {
if (this instanceof GameView gameView) {
app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT);
}
if(this instanceof CeremonyView ceremonyView) {
if (this instanceof CeremonyView ceremonyView) {
ceremonyView.forward();
}
}
public void showInfo(String error, boolean isError) {
infoTimer.reset();
if(null != infoLabel) {
infoLabel.hide();
}
infoLabel = new LabelButton(app, guiNode, error, new Vector2f(5.5f, 2), new Vector2f(0.5f, AbstractButton.VERTICAL - 0.5f), false);
ColorRGBA color;
if(isError) {
color = ColorRGBA.Red.clone();
} else {
color = ColorRGBA.Green.clone();
}
infoLabel.setColor(ColorRGBA.Black, color);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,7 +1,7 @@
world 0,0 90
treesBigBackground 0,0 90
treesSmallBackground 0,0 90
#tree_small 1,1 0
#tree_big 0,0 0
#Marine Pos
marine 4,-5 270
@@ -58,7 +58,8 @@ big_tent -10,-9 130
big_tent 9,-10 225
radar 0,10 -20
tank -1,-10 135
tank 0,-18 180
#tank 0,-18 180
tank_shoot 0,-18 180
tank 3,-18 180
tank -3,-18 180
@@ -131,3 +132,145 @@ node_home_blue 4,0 0
node_home_blue 3,0 0
node_home_blue 2,0 0
node_home_blue 1,0 0
# Randomly Distributed Trees within Radius 12 to 40
treeSmall 10,15 180
treeBig -15,12 45
treeSmall -8,-22 270
treeBig 22,8 90
treeSmall -18,-10 135
treeBig 9,24 300
treeSmall 17,-9 60
treeBig -20,5 330
treeSmall -14,18 200
treeBig 25,-7 120
treeBig -12,-18 150
treeSmall 19,-16 45
treeBig 7,10 90
treeBig -19,-9 270
treeSmall 21,4 110
treeBig -11,17 300
treeSmall 3,-21 360
treeSmall -23,14 100
treeBig 4,26 330
treeSmall 12,13 270
treeBig -18,8 45
treeBig 11,-10 135
treeSmall 16,5 180
treeBig -13,-17 330
treeSmall -2,14 270
#treeBig 7,9 300
treeSmall 23,-10 240
treeBig -6,18 180
treeSmall 5,27 270
treeBig 14,-11 60
treeSmall 9,-16 180
treeBig -12,22 240
treeBig 18,7 360
treeSmall -24,-4 200
treeBig -8,21 300
treeSmall 12,-19 120
treeBig 6,-12 180
treeSmall -11,10 75
treeBig 9,6 270
treeSmall 8,-14 150
treeBig 3,18 30
treeSmall 17,13 100
treeBig -9,20 90
treeBig 6,-22 330
treeSmall -20,7 45
treeBig 21,11 150
treeSmall 15,-18 270
treeBig -3,-12 200
treeBig 12,-28 330
treeSmall -17,-7 120
treeBig -10,9 300
treeSmall 2,-14 240
treeBig 24,2 360
treeSmall 4,-13 300
treeBig -19,20 90
#treeSmall -11,5 45
treeBig 15,9 180
treeSmall -6,10 240
treeBig 3,15 30
treeSmall 9,-19 150
treeBig -21,-4 330
treeSmall 19,11 270
treeSmall 12,24 110
treeBig -13,15 45
treeSmall 7,-15 240
treeBig 26,-8 300
treeSmall -16,14 120
treeBig 14,18 360
treeSmall 8,21 100
treeBig -8,-18 240
treeSmall 9,15 180
treeBig 10,-20 270
treeSmall 2,27 90
treeBig 18,12 300
treeSmall -10,-14 150
treeBig -15,16 330
treeSmall -9,19 45
treeBig 17,-14 120
treeSmall 5,-25 180
treeBig 7,23 30
treeSmall -14,-12 200
treeBig 6,-16 300
treeSmall -20,-8 100
treeBig 4,11 240
treeSmall 24,-15 90
treeSmall -19,-19 360
treeBig 20,8 45
treeSmall 3,22 270
treeBig 13,-9 180
treeSmall -11,18 150
treeBig -17,-4 300
treeSmall 5,-14 240
treeBig 9,17 330
treeSmall 15,13 90
treeBig -21,18 30
treeSmall 6,20 100
treeBig -16,22 180
treeSmall -5,18 360
treeBig 22,11 45
treeSmall 10,-23 240
treeBig -10,-16 300
treeSmall -17,14 120
treeBig 20,4 150
treeSmall 11,-22 180
treeBig -24,-11 200
treeSmall 14,17 150
treeBig -8,-12 300
treeSmall 7,-18 100
treeBig -5,16 330
treeSmall 16,-14 200
treeBig 18,-8 90
treeSmall -23,-9 45
treeBig 24,10 300
treeSmall -4,19 180
treeBig 12,-5 330
treeSmall -19,16 100
treeBig 14,20 150
treeSmall 9,12 180
treeBig -22,8 60
treeSmall 6,18 360
treeBig 25,-9 45
treeBig -10,12 240
treeSmall 19,-17 100
treeSmall -13,19 90
treeSmall 16,-12 120
treeBig 22,-6 45
treeSmall -18,15 200
treeBig 14,-10 300
treeBig 6,10 330
treeSmall 17,18 90
treeBig -20,4 180
treeBig 19,-16 300
treeSmall -15,9 270
treeBig 12,22 360

View File

@@ -1,7 +1,6 @@
// Samplers for textures
uniform sampler2D m_Texture;
uniform sampler2D m_OutlineDepthTexture;
uniform sampler2D m_DepthTexture;
// Input texture coordinates from the vertex shader
in vec2 texCoord;
@@ -15,26 +14,25 @@ uniform float m_OutlineWidth;
out vec4 fragColor;
void main() {
// Sample depth textures
// Sample depth textures at various offsets
vec4 depth = texture(m_OutlineDepthTexture, texCoord);
vec4 depth1 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth2 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth3 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth4 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth5 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(0.0, m_OutlineWidth)) / m_Resolution);
vec4 depth6 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
vec4 depth7 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth8 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth1 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth2 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth3 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth4 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth5 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, m_OutlineWidth)) / m_Resolution);
vec4 depth6 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
vec4 depth7 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth8 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
// Sample the main texture
vec4 color = texture(m_Texture, texCoord);
// Determine whether to apply the outline color
if (depth == vec4(0.0) &&
(depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)) {
fragColor = m_OutlineColor; // Apply outline color
} else {
fragColor = color; // Use the original texture color
}
// Check if an outline should be applied
bool isEdge = (depth == vec4(0.0)) &&
(depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth);
// Output the final color
fragColor = isEdge ? m_OutlineColor : color;
}

View File

@@ -1,11 +1,10 @@
// Use 'in' instead of 'varying' for inputs from the vertex shader
// Input texture coordinates from the vertex shader
in vec2 texCoord;
// Declare a custom output variable for the fragment color
// Output variable for the fragment color
out vec4 fragColor;
// Uniform samplers
// Uniform samplers for textures
uniform sampler2D m_Texture;
uniform sampler2D m_NormalsTexture;
uniform sampler2D m_DepthTexture;
@@ -13,6 +12,7 @@ uniform sampler2D m_DepthTexture;
void main() {
// Sample the texture at the given texture coordinates
vec4 color = texture(m_Texture, texCoord);
// Assign the color to the output variable
fragColor = color;
}

View File

@@ -1,109 +1,78 @@
// Uniform samplers
uniform sampler2D m_Texture;
uniform sampler2D m_OutlineDepthTexture;
uniform sampler2D m_DepthTexture;
varying vec2 texCoord;
// Input texture coordinates from the vertex shader
in vec2 texCoord;
// Uniforms for resolution, outline color, and width
uniform vec2 m_Resolution;
uniform vec4 m_OutlineColor;
uniform float m_OutlineWidth;
// Output variable for fragment color
out vec4 fragColor;
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);
//如果是背景
float ratio=0.;
if(depth==vec4(0.) && (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth||depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)){
float dist=m_OutlineWidth;
//距离边的像素
vec4 nearDepth;
if(depth1 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth2 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,-i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth3 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth4 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,-i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth5 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth6 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth7 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,0.))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth8 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,0.))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}
//0:场景颜色 1:outline颜色
ratio = clamp(1.- dist/m_OutlineWidth,0.,1.);
//float off = (1.-ratio*ratio)*(1.-ratio*ratio);
gl_FragColor = color*(1.-ratio) +m_OutlineColor*ratio;
//gl_FragColor = m_OutlineColor;
}else{
gl_FragColor = color;
}
//debug
//gl_FragColor = vec4(0.,(1.-ratio),0.,1.);
}
vec4 depth = texture(m_OutlineDepthTexture, texCoord);
vec4 depth1 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth2 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth3 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth4 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth5 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, m_OutlineWidth)) / m_Resolution);
vec4 depth6 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
vec4 depth7 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth8 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
vec4 color = texture(m_Texture, texCoord);
float ratio = 0.0;
if (depth == vec4(0.0) &&
(depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)) {
float dist = m_OutlineWidth;
vec4 nearDepth;
// Iterate to find the distance to the nearest edge
for (float i = 0.0; i < m_OutlineWidth; i++) {
if (depth1 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth2 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, -i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth3 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth4 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, -i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth5 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth6 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth7 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, 0.0)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth8 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, 0.0)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
}
}
// Calculate ratio for outline blending
ratio = clamp(1.0 - dist / m_OutlineWidth, 0.0, 1.0);
// Blend the outline color with the base color
fragColor = color * (1.0 - ratio) + m_OutlineColor * ratio;
} else {
// No outline, use the base texture color
fragColor = color;
}
// Optional: Debugging outline visualization
// fragColor = vec4(0.0, 1.0 - ratio, 0.0, 1.0);
}

View File

@@ -1,8 +1,8 @@
// Use 'in' for vertex attributes
// Vertex attributes
in vec4 inPosition;
in vec2 inTexCoord;
// Use 'out' for passing data to the fragment shader
// Output to fragment shader
out vec2 texCoord;
void main() {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

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