162 Commits

Author SHA1 Message Date
michiel301b
da27399dda i think timer fixed it but idk 2025-11-28 22:04:55 +01:00
lieght
c98edc37a6 Added documentation 2025-11-28 20:08:13 +01:00
lieght
c21f254234 Refactored switch statement 2025-11-28 20:05:08 +01:00
lieght
fd85a73608 Removed unused imports 2025-11-28 19:36:16 +01:00
lieght
ea7a5e11ba Refactored nextScreen runnable 2025-11-28 19:35:31 +01:00
lieght
164708dcf5 Added button to continue and start game. Refactors 2025-11-28 19:26:15 +01:00
lieght
eb5e69a59c Tutorial images now use ImageAsset.java 2025-11-28 16:50:34 +01:00
ramollia
b3c9f89f99 Merge remote-tracking branch 'origin/Tutorials' into Tutorials 2025-11-28 14:00:46 +01:00
ramollia
6b619604ec added a pop button 2025-11-28 14:00:26 +01:00
lieght
1ec1c0d13d Fixed garbage code 2025-11-28 13:59:11 +01:00
lieght
0ab071693f Removed views 2025-11-28 12:09:40 +01:00
Bas Antonius de Jong
1a11827ba3 Merge pull request #261 from 2OOP/Tutorials
merge tutorials to dev branch
2025-11-28 11:56:42 +01:00
ramollia
a2d651cd7d Merge remote-tracking branch 'refs/remotes/origin/Development' into Tutorials
# Conflicts:
#	app/src/main/java/org/toop/app/game/Connect4Game.java
2025-11-28 11:51:46 +01:00
0cb025edb9 Changed the way turns are being stored in TurnBasedGame. 2025-11-27 18:37:34 +01:00
6ea94fe658 Fixed compilation errors 2025-11-27 17:42:39 +01:00
tichohidding
ef5c1ce6e3 Merge pull request #241 from 2OOP/Widgets
Widgets into development
2025-11-27 17:19:30 +01:00
tichohidding
8c01c7fa7a Merge branch 'Development' into Widgets 2025-11-27 17:19:17 +01:00
Ticho Hidding
72dd78137b Merge remote-tracking branch 'origin/Development' into Development 2025-11-27 15:50:36 +01:00
71c918e9ee Squashed commit of the following:
commit a517f2f302baa89f8ef59946a31c7bb59c56770f
Author: Stef <stbuwalda@gmail.com>
Date:   Thu Nov 27 15:43:43 2025 +0100

    Make it so the game shows "Waiting on ... to make their move". Styling isn't done but it is easier to see who's turn it is. There is a lot of structuring to do in the previous code...
2025-11-27 15:45:31 +01:00
Ticho Hidding
81f4d307a2 fixed turn skip bug
fixed end score bug
now only shows legal and highlight moves when human
2025-11-27 15:41:09 +01:00
Ticho Hidding
710438ec1b resizable true 2025-11-27 14:07:39 +01:00
michiel
c14b66e892 rest van de tutorials toegevoegd 2025-11-27 11:42:38 +01:00
michiel301b
8c69453506 started a basis for the tutorials, tic tac toe is almost done with some general stuff still to do. 2025-11-26 22:03:06 +01:00
Bas de Jong
a6b835bddf Removed no more needed comments 2025-11-20 11:41:44 +01:00
Bas de Jong
ca11151381 Functional code, is now object orientated 2025-11-06 15:32:15 +01:00
ramollia
fa4e1ad5e3 widget system almost complete 2025-11-03 12:47:56 +01:00
Ticho Hidding
295c61c7eb added some comments and made some methods a bit more readable 2025-11-01 15:12:09 +01:00
ramollia
1c9af58264 half done with the widget system 2025-10-31 17:33:19 +01:00
6dc05e7123 Made connect4 public method private 2025-10-29 20:09:39 +01:00
d1a9f94ee0 Fixed warning "Warning:(27, 12) Copy constructor does not copy field 'mostRecentlyFlippedPieces'", removed unused field 2025-10-29 20:04:50 +01:00
edfb4ffe51 Changed checkForEarlyDraw so it doesn't need a game as input. 2025-10-29 20:03:07 +01:00
be31de4660 Reversi: made method private 2025-10-29 19:51:09 +01:00
ea30e20585 Privated methods that didn't have to be public 2025-10-29 18:05:01 +01:00
d7e370536e Applied encapsulation principle to TurnBasedBame.java 2025-10-29 17:37:21 +01:00
6811890531 Removed unused imports 2025-10-29 17:29:28 +01:00
5da0a02cc8 Refactored Game to follow encapsulation principle 2025-10-29 17:28:43 +01:00
50713c5021 Turned Abstract Method for AI into interface 2025-10-29 15:01:58 +01:00
d17edf7c4a Renamed Interface Playtable to IPlayable 2025-10-29 14:52:19 +01:00
84e257c17c Removed unused imports 2025-10-29 14:50:40 +01:00
925c848fda Moved the Move record into it's own file, seperated from Game 2025-10-29 14:49:43 +01:00
13bac113b7 Turned abstract methods into an interface 2025-10-29 14:37:31 +01:00
068337e01b Removed unused import 2025-10-29 14:27:51 +01:00
44d8ffbef2 Made the GameState enum it's own file and fixed imports 2025-10-29 14:26:41 +01:00
Ticho Hidding
ff611724e7 Merge remote-tracking branch 'origin/Development' into Development 2025-10-29 14:06:23 +01:00
Ticho Hidding
7f36d7f244 cool onhover effect for reversi 2025-10-29 14:06:05 +01:00
eda85ea888 Removed unused import and unused parameter 2025-10-29 13:51:56 +01:00
f026a4fec6 Manually fallback to the fallback locale when a ResourceBundle is missing a resource key. Fallsback to "MISSING RESOUREC" if it's not present in the fallback. 2025-10-29 13:49:50 +01:00
Bas de Jong
b84255e00e Added replace to reduce boiler plate code 2025-10-28 15:23:36 +01:00
Bas de Jong
84c17d185b Fixes for garbage code by Omar 2025-10-28 14:37:15 +01:00
michiel
3776167299 iets met timing verkeerd temporary fix 2025-10-28 14:23:53 +01:00
ramollia
48ed6df6b4 started working on the widget system 2025-10-28 12:59:15 +01:00
michiel
2428048bd5 updated music ma,es 2025-10-28 12:53:10 +01:00
michiel
1440190ac3 kleine ui fix 2025-10-28 12:30:48 +01:00
ramollia
3a51434f91 Merge remote-tracking branch 'origin/Development' into Development 2025-10-28 11:14:22 +01:00
ramollia
5b20909f80 moved score out of game 2025-10-28 11:13:56 +01:00
michiel
00b5f9dced mainview false for sendchallengeview 2025-10-28 11:10:17 +01:00
michiel
188a5e8a45 can now go to last using previous and being at the first song 2025-10-28 10:53:02 +01:00
ramollia
0638a38fc1 moved score out of game 2025-10-28 09:53:33 +01:00
ramollia
75fb865dad Merge branch 'Demo3' into Development 2025-10-28 09:16:22 +01:00
ramollia
2cda94e4db canvas changes 2025-10-28 09:13:09 +01:00
ramollia
d6f3bb2185 Merge remote-tracking branch 'origin/Demo3' into Demo3 2025-10-27 17:22:35 +01:00
ramollia
95d7ea5e1d Merge branch 'OtherGames' into Demo3 2025-10-27 17:22:07 +01:00
Ticho Hidding
6fb248bec4 spam minder v2 2025-10-27 17:21:50 +01:00
ramollia
7b5c188280 Merge remote-tracking branch 'origin/OtherGames' into OtherGames 2025-10-27 17:21:32 +01:00
ramollia
1c2736ac75 fixed setgamelabels 2025-10-27 17:21:04 +01:00
Ticho Hidding
caa812217f spam minder 2025-10-27 17:20:22 +01:00
Ticho Hidding
c115fb91af tourney ready 2025-10-27 17:14:36 +01:00
Ticho Hidding
b506afdade can start game from playerlist screen 2025-10-27 14:57:15 +01:00
michiel
59cc64ada7 fixed tests 2025-10-27 14:20:20 +01:00
Ticho Hidding
bd096df2c2 Merge remote-tracking branch 'origin/AudioDisplay' into OtherGames 2025-10-27 13:56:07 +01:00
Ticho Hidding
dfd3934b96 Merge branch 'Development' into OtherGames 2025-10-27 13:53:06 +01:00
michiel
2f7f2955c1 Merge remote-tracking branch 'origin/AudioDisplay' into Development 2025-10-27 10:00:49 +01:00
ramollia
fb8acbe228 add simple flip animations and fixed(?) server somewhat 2025-10-25 17:37:50 +02:00
michiel301b
be40ee0187 Made it so that it indicates with the play/pause button if its paused or played 2025-10-24 13:28:59 +02:00
michiel301b
0c536b0f9b Toegevoegd:
-Play Button + CSS + Events
-Previous Button + CSS + Events
-Changed interface for AudioResource to include a pause button which works really well with mediaplayer, however now SoundEffectAsset has an unnessescary pause
2025-10-24 13:22:42 +02:00
lieght
c85ab38142 Merge remote-tracking branch 'origin/185-networkingeventlistener' into Development 2025-10-21 20:43:21 +02:00
Ticho Hidding
c3f764f33d connect4 with minimax AI 2025-10-19 22:01:33 +02:00
Ticho Hidding
df493a5eba new reversi test (both players no legal moves) 2025-10-19 13:06:46 +02:00
Ticho Hidding
0447f7b0fe added method for sorting the flipped pieces by distance to Move.position 2025-10-19 02:18:11 +02:00
lieght
b1589032be test fix 2025-10-17 20:22:27 +02:00
lieght
55989ab8ce Merge remote-tracking branch 'origin/UI' into AudioDisplay 2025-10-17 20:13:05 +02:00
lieght
443f2da97d Merge remote-tracking branch 'origin/UI' into 185-networkingeventlistener 2025-10-17 20:12:41 +02:00
lieght
8f047cd7b5 Faster event schedule for PlayingMusic event 2025-10-17 19:51:49 +02:00
lieght
bb2630f3d5 Small event fix 2025-10-17 19:46:03 +02:00
michiel
f44bf6bd02 Tiny fix when natural skip 2025-10-16 14:00:40 +02:00
Bas de Jong
3a120803e3 Fixes for garbage code by Omar 2025-10-16 13:59:24 +02:00
michiel
6e35067c18 Skip Button 2025-10-16 13:37:01 +02:00
michiel
30f797022c Skip Button 2025-10-16 13:35:41 +02:00
lieght
abc173a1ee Merge remote-tracking branch 'origin/AudioDisplay' into AudioDisplay 2025-10-16 00:57:01 +02:00
lieght
3784cd790e Clips now also return positional information 2025-10-16 00:56:46 +02:00
michiel301b
f44c3932dc Basis Audio Display toegevoegd + standaard CSS toegevoegd
Kan nu zien hoe lang de song duurt, hoe lang ie al bezig is met draaien en de titel (-.mp3)
2025-10-16 00:45:29 +02:00
lieght
975207a6de Nuke everything on close. 2025-10-16 00:37:20 +02:00
lieght
545e830b3c Updated timings 2025-10-15 23:54:17 +02:00
lieght
977e9bb102 Updated test. 2025-10-15 23:24:21 +02:00
lieght
69bc55dc26 Polling music event, fires every 1 second 2025-10-15 23:21:00 +02:00
michiel301b
62e2b71ece begin van audio display 2025-10-15 22:58:25 +02:00
lieght
cf9dbef882 Correct client creation and user polling 2025-10-15 21:36:46 +02:00
Bas Antonius de Jong
09f3dc4e3e Merge pull request #186 from 2OOP/UI
Update UI for networking
2025-10-15 21:06:31 +02:00
lieght
6ae5f1c2d2 Merge remote-tracking branch 'origin/UI' into UI
# Conflicts:
#	app/src/main/java/org/toop/app/App.java
#	app/src/main/java/org/toop/app/layer/layers/MainLayer.java
#	app/src/main/java/org/toop/app/layer/layers/OptionsPopup.java
#	app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
#	app/src/main/java/org/toop/local/AppSettings.java
2025-10-15 21:05:33 +02:00
lieght
b30420616a Merge remote-tracking branch 'origin/185-networkingeventlistener' into UI
# Conflicts:
#	app/src/main/java/org/toop/app/App.java
#	app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
#	app/src/main/java/org/toop/app/layer/layers/MainLayer.java
#	app/src/main/java/org/toop/app/layer/layers/OptionsPopup.java
#	app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
#	app/src/main/java/org/toop/local/AppSettings.java
2025-10-15 21:05:01 +02:00
lieght
1134649197 Documentation 2025-10-15 20:11:43 +02:00
lieght
5beebf9663 Quick fix for closing connection. 2025-10-15 19:08:18 +02:00
lieght
798005de2c Refactor to make Events easier to work with. 2025-10-15 19:05:09 +02:00
ramollia
e71369f7ff visual update 2025-10-15 16:46:19 +02:00
ramollia
56ebe1347c add: server chat box 2025-10-15 16:13:42 +02:00
ramollia
7f8ed029b9 add: reversi game 2025-10-15 15:22:19 +02:00
lieght
bc6182e29a Fixed event bug 2025-10-15 03:26:19 +02:00
lieght
dc81d9c3ca Added exceptions. Added reconnect attempts and changeable address 2025-10-15 02:58:14 +02:00
ramollia
2c8db95ba7 Merge remote-tracking branch 'origin/Reversi' into UI
# Conflicts:
#	app/src/main/java/org/toop/app/App.java
#	app/src/main/java/org/toop/app/canvas/GameCanvas.java
#	app/src/main/java/org/toop/app/layer/layers/MainLayer.java
#	app/src/main/java/org/toop/app/layer/layers/OptionsPopup.java
#	app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
#	app/src/main/java/org/toop/local/AppSettings.java
#	app/src/main/resources/assets/localization/localization_ar.properties
#	app/src/main/resources/assets/localization/localization_de.properties
#	app/src/main/resources/assets/localization/localization_en.properties
#	app/src/main/resources/assets/localization/localization_es.properties
#	app/src/main/resources/assets/localization/localization_fr.properties
#	app/src/main/resources/assets/localization/localization_hi.properties
#	app/src/main/resources/assets/localization/localization_it.properties
#	app/src/main/resources/assets/localization/localization_ja.properties
#	app/src/main/resources/assets/localization/localization_ko.properties
#	app/src/main/resources/assets/localization/localization_nl.properties
#	app/src/main/resources/assets/localization/localization_ru.properties
#	app/src/main/resources/assets/localization/localization_zh.properties
2025-10-15 00:39:14 +02:00
ramollia
1f64a2dfd0 fixed merge conflicts 2025-10-15 00:26:27 +02:00
ramollia
0a6c2487bd Merge remote-tracking branch 'origin/UI' into UI
# Conflicts:
#	app/src/main/java/org/toop/app/App.java
#	app/src/main/java/org/toop/app/GameInformation.java
#	app/src/main/java/org/toop/app/canvas/GameCanvas.java
#	app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java
#	app/src/main/java/org/toop/app/layer/Container.java
#	app/src/main/java/org/toop/app/layer/Layer.java
#	app/src/main/java/org/toop/app/layer/NodeBuilder.java
#	app/src/main/java/org/toop/app/layer/Popup.java
#	app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java
#	app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java
#	app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
#	app/src/main/java/org/toop/app/layer/layers/CreditsPopup.java
#	app/src/main/java/org/toop/app/layer/layers/MainLayer.java
#	app/src/main/java/org/toop/app/layer/layers/MultiplayerLayer.java
#	app/src/main/java/org/toop/app/layer/layers/OptionsPopup.java
#	app/src/main/java/org/toop/app/layer/layers/QuitPopup.java
#	app/src/main/java/org/toop/app/layer/layers/game/GameFinishedPopup.java
#	app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
#	app/src/main/java/org/toop/local/AppSettings.java
2025-10-15 00:17:17 +02:00
ramollia
e382cf95f2 gui refactor 2025-10-15 00:14:39 +02:00
lieght
14e6785c6f Some better docs. 2025-10-15 00:01:41 +02:00
lieght
444a81abc3 Improved API for dependency injection 2025-10-14 23:44:45 +02:00
lieght
d0676d9363 Forgot to remove 2025-10-14 23:27:52 +02:00
lieght
8f7e78efb5 Reworked NetworkingClientManager into SRP model. 2025-10-14 23:27:12 +02:00
lieght
cb7e3298fd Added shuffling on user request 2025-10-14 19:57:07 +02:00
michiel
c09f0eb1f9 punk toegevoegd 2025-10-14 14:18:52 +02:00
michiel
a3203dd78e Merge remote-tracking branch 'origin/Reversi' into Reversi 2025-10-14 13:40:00 +02:00
michiel
efbab12b10 Tests toegevoegd 2025-10-14 13:39:22 +02:00
Ticho Hidding
8e823b4108 getLegalMoves logic seems fixed //todo write better tests 2025-10-14 13:34:47 +02:00
3165ff33b3 Tests for SoundEffectManager 2025-10-14 13:21:17 +02:00
Ticho Hidding
81abeef843 commit ofzo 2025-10-14 13:12:45 +02:00
Ticho Hidding
be26db953e Merge remote-tracking branch 'origin/Development' into Reversi
# Conflicts:
#	app/src/main/java/org/toop/app/canvas/GameCanvas.java
#	app/src/main/java/org/toop/app/layer/layers/MainLayer.java
#	game/src/main/java/org/toop/game/Game.java
#	game/src/main/java/org/toop/game/othello/Othello.java
#	game/src/main/java/org/toop/game/othello/OthelloAI.java
2025-10-14 11:25:46 +02:00
Ticho Hidding
76c7e44447 Merge remote-tracking branch 'origin/Development' into Reversi 2025-10-14 11:07:10 +02:00
Ticho Hidding
d9f6c7ee74 commit 2025-10-14 11:06:41 +02:00
Bas de Jong
0e3165ed98 Added linelistener to SoundEffectAsset 2025-10-13 13:45:16 +02:00
lieght
4bcf70d4dc Removed ResourceManager from AudioManagers 2025-10-13 02:47:34 +02:00
lieght
fcc0b9d0dd SoundEffectManager now generic 2025-10-13 02:27:04 +02:00
lieght
cdc34b432e Changed pom to be correct.
Fixed SnowflakeGenerator not making unique ids.
Changed naming for event implementation.
Automated id getter for events.
Added Error-Prone to all modules.
Added parents to all modules.
Added processors module.
2025-10-13 02:06:12 +02:00
Bas Antonius de Jong
00daf37244 Merge pull request #175 from 2OOP/166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen
166 audiomanager opslitsen om single responsibility principle te volgen Done
2025-10-12 20:44:57 +02:00
lieght
bab11b64a5 Merge remote-tracking branch 'origin/166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen' into 166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen 2025-10-12 20:42:09 +02:00
lieght
4b60fa88ee Moved restrictedAPI to future release 2025-10-12 20:41:56 +02:00
lieght
f3316145e7 Finished todo's 2025-10-12 20:41:54 +02:00
lieght
53a72cc340 Moved restrictedAPI to future release 2025-10-12 20:41:10 +02:00
lieght
081fbda7b1 Merge remote-tracking branch 'origin/166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen' into 166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen
# Conflicts:
#	framework/src/main/java/org/toop/framework/audio/MusicManager.java
2025-10-12 20:37:23 +02:00
lieght
d051e3e603 Finished todo's 2025-10-12 20:36:28 +02:00
Bas de Jong
5317c42c81 Removed no more needed code. 2025-10-12 07:20:28 +02:00
Bas de Jong
6085f6ebe2 Small fixes. 2025-10-12 07:19:27 +02:00
Bas de Jong
ea6264e519 Added ErrorProne for potential bugs. Fixed potential bugs. 2025-10-12 07:14:26 +02:00
lieght
e3bce3889e Renamed VolumeTypes to VolumeControl. Made it thread safe. Added docs to VolumeControl and co.
removed .updateAllVolumes() in favor of auto updating inside enum instead
2025-10-12 02:20:30 +02:00
lieght
b050e06ceb Minor changes in API design 2025-10-12 01:56:06 +02:00
lieght
7631a10838 Renamed VOLUME to MASTERVOLUME for better naming 2025-10-12 01:02:58 +02:00
lieght
ca25338971 Fixed grammer and spelling mistakes 2025-10-12 00:55:02 +02:00
lieght
42e03e0878 Removed file no longer in use 2025-10-12 00:52:02 +02:00
lieght
9b81ee1e65 Added ability to remove a manager from VolumeTypes 2025-10-12 00:39:16 +02:00
lieght
a4b0f890da Merge remote-tracking branch 'origin/166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen' into 166-audiomanager-opslitsen-om-single-responsibility-principle-te-volgen 2025-10-12 00:37:21 +02:00
lieght
a766b85a75 Fixed AudioVolumemanager, all volumes calculations are now made in VolumeTypes enum 2025-10-12 00:37:02 +02:00
Stef
34ccddaea5 Hotfix for loading clip volume issue (#174) 2025-10-12 00:04:59 +02:00
lieght
73a2fe3da2 Unit tests for MusicManager.java 2025-10-11 23:08:28 +02:00
Stef
d958b9730a Split SoundEffectManager from AudioManager. (#171)
Clips no longer create a new clip instance each time they are played.  A singular clip is made for each resource and is opened/closed when loaded/unloaded. When a clip is played that is already playing it'll stop playback and start again. Clip volume handling isn't done very well.
2025-10-11 23:00:06 +02:00
Bas de Jong
9749d3eee8 Added more flexible dependency injection to MusicManager for unittesting. Moved to event driven design for less complex code and lower runtime complexity. 2025-10-11 20:45:57 +02:00
Bas de Jong
1ecdb9a555 Made all of the updated classes more generic for better flexibility in unittesting 2025-10-11 19:31:55 +02:00
lieght
b101734fd7 Reworked to now use better defined generics and easier to use API. Added AudioResource to be used in changing volume 2025-10-11 06:09:13 +02:00
lieght
123ecc7d3a Working state. Split AudioManager into 3 different branches for easier testing and srp 2025-10-11 04:50:49 +02:00
Ticho Hidding
c1f7a093ef Moves flip dots. all tests pass. can play reversi local. 2025-10-11 00:51:46 +02:00
Ticho Hidding
5dda85248e legal moves now get highlighted in red 2025-10-09 15:27:58 +02:00
Ticho Hidding
5a3490af2e start to reversi logic 2025-10-08 17:27:50 +02:00
Bas de Jong
7f3d858320 AppSettings now also get loaded into the assetmanager 2025-10-08 00:14:40 +02:00
Bas de Jong
e9dfbbd150 Renamed asset folder to resource, made resourceLoader more robust. Completed some TODO's, formatting 2025-10-07 23:54:33 +02:00
Bas de Jong
72e322675e Fixed bugs and oversights 2025-10-07 22:39:47 +02:00
ramollia
e12e48b4fb fast server connection 2025-10-07 12:40:12 +02:00
201 changed files with 9431 additions and 5298 deletions

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@@ -5,14 +5,17 @@
<w>clid</w>
<w>dcompile</w>
<w>errorprone</w>
<w>español</w>
<w>flushnl</w>
<w>gaaf</w>
<w>gamelist</w>
<w>pism</w>
<w>playerlist</w>
<w>tictactoe</w>
<w>toop</w>
<w>vmoptions</w>
<w>xplugin</w>
<w>yourturn</w>
</words>
</dictionary>
</component>

4
.idea/encodings.xml generated
View File

@@ -1,12 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$APPLICATION_HOME_DIR$/bin/src/main/java" charset="UTF-8" />
<file url="file://$APPLICATION_HOME_DIR$/bin/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/app/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/app/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/processors/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/processors/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>

View File

@@ -2,7 +2,8 @@
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,org.toop.framework.audio.AudioPlayer,play" />
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,org.toop.framework.audio.AudioPlayer,play,java.util.Map,remove,java.util.concurrent.Executors,newSingleThreadScheduledExecutor" />
</inspection_tool>
<inspection_tool class="WriteOnlyObject" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ResourceBundleManager">
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ar.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_de.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_en.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_es.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_fr.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_hi.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_it.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ja.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ko.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_nl.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ru.properties" />
<file url="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_zh.properties" />
<custom-resource-bundle>
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/Localization_de.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/Localization_es.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/Localization_fr.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/Localization_it.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/Localization_nl.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/Localization_zh.properties" />
<base-name>localization</base-name>
</custom-resource-bundle>
<custom-resource-bundle>
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ar.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_de.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_en.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_es.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_fr.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_hi.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_it.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ja.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ko.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_nl.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_ru.properties" />
<file value="file://$PROJECT_DIR$/app/src/main/resources/assets/localization/localization_zh.properties" />
<base-name>localization</base-name>
</custom-resource-bundle>
</component>
</project>

View File

@@ -1,8 +1,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>org.toop</groupId>
<artifactId>pism_app</artifactId>
<parent>
<groupId>org.toop</groupId>
<artifactId>pism</artifactId>
<version>0.1</version>
</parent>
<artifactId>app</artifactId>
<version>0.1</version>
<properties>
@@ -24,24 +29,36 @@
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.toop</groupId>
<artifactId>pism_framework</artifactId>
<version>0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.toop</groupId>
<artifactId>pism_game</artifactId>
<version>0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>25</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.42.0</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>2.42.0</version>
</dependency>
<dependency>
<groupId>org.toop</groupId>
<artifactId>framework</artifactId>
<version>0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.toop</groupId>
<artifactId>game</artifactId>
<version>0.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@@ -112,14 +129,56 @@
</java>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>25</source>
<target>25</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<configuration>
<showWarnings>true</showWarnings>
<fork>true</fork>
<executable>${java.home}/bin/javac</executable>
<source>25</source>
<target>25</target>
<release>25</release>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
<arg>
-Xplugin:ErrorProne \
</arg>
<!-- TODO-->
<!-- -Xep:RestrictedApi:ERROR \-->
<!-- -XepOpt:RestrictedApi:annotation=org.toop.annotations.TestsOnly \-->
<!-- -XepOpt:RestrictedApi:allowlistRegex=(?s).*/src/test/java/.*|.*test\.java \-->
<!-- -XepOpt:RestrictedApi:message=This API is marked @TestsOnly and shouldn't be normally used.-->
<arg>-XDcompilePolicy=simple</arg>
<arg>--should-stop=ifError=FLOW</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.42.0</version>
</path>
</annotationProcessorPaths>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.42.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,21 +1,40 @@
package org.toop;
import org.toop.app.App;
import org.toop.framework.asset.ResourceLoader;
import org.toop.framework.asset.ResourceManager;
import org.toop.framework.audio.SoundManager;
import org.toop.framework.audio.*;
import org.toop.framework.networking.NetworkingClientEventListener;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
import org.toop.framework.resource.ResourceLoader;
import org.toop.framework.resource.ResourceManager;
import org.toop.framework.resource.resources.MusicAsset;
import org.toop.framework.resource.resources.SoundEffectAsset;
public final class Main {
public static void main(String[] args) {
static void main(String[] args) {
initSystems();
App.run(args);
}
private static void initSystems() throws NetworkingInitializationException {
private static void initSystems() {
ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets"));
new Thread(NetworkingClientManager::new).start();
new Thread(SoundManager::new).start();
new Thread(() -> new NetworkingClientEventListener(new NetworkingClientManager())).start();
new Thread(() -> {
MusicManager<MusicAsset> musicManager = new MusicManager<>(ResourceManager.getAllOfTypeAndRemoveWrapper(MusicAsset.class), true);
SoundEffectManager<SoundEffectAsset> soundEffectManager = new SoundEffectManager<>(ResourceManager.getAllOfType(SoundEffectAsset.class));
AudioVolumeManager audioVolumeManager = new AudioVolumeManager()
.registerManager(VolumeControl.MASTERVOLUME, musicManager)
.registerManager(VolumeControl.MASTERVOLUME, soundEffectManager)
.registerManager(VolumeControl.FX, soundEffectManager)
.registerManager(VolumeControl.MUSIC, musicManager);
new AudioEventListener<>(
musicManager,
soundEffectManager,
audioVolumeManager
).initListeners("medium-button-click.wav");
}).start();
}
}

View File

@@ -1,167 +1,113 @@
package org.toop.app;
import java.util.Stack;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.toop.app.layer.Layer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.app.layer.layers.QuitPopup;
import org.toop.framework.asset.ResourceManager;
import org.toop.framework.asset.resources.CssAsset;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.display.SongDisplay;
import org.toop.app.widget.popup.QuitPopup;
import org.toop.app.widget.view.MainView;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.resource.ResourceManager;
import org.toop.framework.resource.resources.CssAsset;
import org.toop.local.AppContext;
import org.toop.local.AppSettings;
public final class App extends Application {
private static Stage stage;
private static Scene scene;
private static StackPane root;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public final class App extends Application {
private static Stage stage;
private static Scene scene;
private static Stack<Layer> stack;
private static int height;
private static int width;
private static boolean isQuitting;
private static boolean isQuitting;
public static void run(String[] args) {
launch(args);
}
public static void run(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
final StackPane root = new StackPane();
final Scene scene = new Scene(root);
@Override
public void start(Stage stage) throws Exception {
final StackPane root = WidgetContainer.setup();
final Scene scene = new Scene(root);
stage.setTitle(AppContext.getString("appTitle"));
stage.setWidth(1080);
stage.setHeight(720);
stage.setTitle(AppContext.getString("app-title"));
stage.titleProperty().bind(AppContext.bindToKey("app-title"));
stage.setOnCloseRequest(
event -> {
event.consume();
stage.setWidth(1080);
stage.setHeight(720);
if (!isQuitting) {
quitPopup();
}
});
scene.getRoot();
stage.setScene(scene);
stage.setResizable(false);
stage.setMinWidth(1080);
stage.setMinHeight(720);
stage.setOnCloseRequest(event -> {
event.consume();
startQuit();
});
stage.show();
stage.setScene(scene);
stage.setResizable(true);
App.stage = stage;
App.scene = scene;
App.root = root;
stage.show();
App.stack = new Stack<>();
App.stage = stage;
App.scene = scene;
App.width = (int) stage.getWidth();
App.height = (int) stage.getHeight();
App.width = (int)stage.getWidth();
App.height = (int)stage.getHeight();
App.isQuitting = false;
App.isQuitting = false;
final AppSettings settings = new AppSettings();
settings.applySettings();
AppSettings.applySettings();
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
activate(new MainLayer());
}
WidgetContainer.add(Pos.CENTER, new MainView());
WidgetContainer.add(Pos.BOTTOM_RIGHT, new SongDisplay());
}
public static void activate(Layer layer) {
Platform.runLater(
() -> {
popAll();
push(layer);
});
}
public static void startQuit() {
if (isQuitting) {
return;
}
public static void push(Layer layer) {
Platform.runLater(
() -> {
root.getChildren().addLast(layer.getLayer());
stack.push(layer);
});
}
WidgetContainer.add(Pos.CENTER, new QuitPopup());
isQuitting = true;
}
public static void pop() {
Platform.runLater(
() -> {
root.getChildren().removeLast();
stack.pop();
public static void stopQuit() {
isQuitting = false;
}
isQuitting = false;
});
}
public static void quit() {
stage.close();
System.exit(0); // TODO: This is like dropping a nuke
}
public static void popAll() {
Platform.runLater(
() -> {
final int childrenCount = root.getChildren().size();
public static void setFullscreen(boolean fullscreen) {
stage.setFullScreen(fullscreen);
for (int i = 0; i < childrenCount; i++) {
try {
root.getChildren().removeLast();
} catch (Exception e) {
IO.println(e);
}
}
width = (int)stage.getWidth();
height = (int)stage.getHeight();
}
stack.removeAllElements();
});
}
public static void setStyle(String theme, String layoutSize) {
scene.getStylesheets().clear();
public static void quitPopup() {
Platform.runLater(
() -> {
push(new QuitPopup());
isQuitting = true;
});
}
scene.getStylesheets().add(ResourceManager.<CssAsset>get("general.css").getUrl());
scene.getStylesheets().add(ResourceManager.<CssAsset>get(theme + ".css").getUrl());
scene.getStylesheets().add(ResourceManager.<CssAsset>get(layoutSize + ".css").getUrl());
}
public static void quit() {
stage.close();
}
public static int getWidth() {
return width;
}
public static void reloadAll() {
stage.setTitle(AppContext.getString("appTitle"));
for (final Layer layer : stack) {
layer.reload();
}
}
public static void setFullscreen(boolean fullscreen) {
stage.setFullScreen(fullscreen);
width = (int) stage.getWidth();
height = (int) stage.getHeight();
reloadAll();
}
public static void setStyle(String theme, String layoutSize) {
final int stylesCount = scene.getStylesheets().size();
for (int i = 0; i < stylesCount; i++) {
scene.getStylesheets().removeLast();
}
scene.getStylesheets().add(ResourceManager.<CssAsset>get(theme + ".css").getUrl());
scene.getStylesheets().add(ResourceManager.<CssAsset>get(layoutSize + ".css").getUrl());
reloadAll();
}
public static int getWidth() {
return width;
}
public static int getHeight() {
return height;
}
public static int getHeight() {
return height;
}
}

View File

@@ -1,10 +1,56 @@
package org.toop.app;
public record GameInformation(
String[] playerName,
boolean[] isPlayerHuman,
int[] computerDifficulty,
int[] computerThinkTime,
boolean isConnectionLocal,
String serverIP,
String serverPort) {}
public class GameInformation {
public enum Type {
TICTACTOE(2, 5),
REVERSI(2, 10),
CONNECT4(2, 7),
BATTLESHIP(2, 5);
private final int playerCount;
private final int maxDepth;
Type(int playerCount, int maxDepth) {
this.playerCount = playerCount;
this.maxDepth = maxDepth;
}
public int getPlayerCount() {
return playerCount;
}
public int getMaxDepth() {
return maxDepth;
}
public String getTypeToString() {
String name = this.name();
return switch (name) {
case "TICTACTOE" -> "TicTacToe";
case "REVERSI" -> "Reversi";
case "CONNECT4" -> "Connect4";
case "BATTLESHIP" -> "Battleship";
default -> name;
};
}
}
public static class Player {
public String name = "";
public boolean isHuman = true;
public int computerDifficulty = 1;
public int computerThinkTime = 1;
}
public final Type type;
public final Player[] players;
public GameInformation(Type type) {
this.type = type;
players = new Player[type.getPlayerCount()];
for (int i = 0; i < players.length; i++) {
players[i] = new Player();
}
}
}

View File

@@ -0,0 +1,235 @@
package org.toop.app;
import org.toop.app.game.Connect4Game;
import org.toop.app.game.ReversiGame;
import org.toop.app.game.TicTacToeGame;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.popup.ChallengePopup;
import org.toop.app.widget.popup.ErrorPopup;
import org.toop.app.widget.popup.SendChallengePopup;
import org.toop.app.widget.view.ServerView;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.clients.TournamentNetworkingClient;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.framework.networking.types.NetworkingConnector;
import org.toop.local.AppContext;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public final class Server {
private String user = "";
private long clientId = -1;
private final List<String> onlinePlayers = new CopyOnWriteArrayList<>();
private final List<String> gameList = new CopyOnWriteArrayList<>();
private ServerView primary;
private boolean isPolling = true;
private final AtomicBoolean isSingleGame = new AtomicBoolean(false);
private ScheduledExecutorService scheduler;
public static GameInformation.Type gameToType(String game) {
if (game.equalsIgnoreCase("tic-tac-toe")) {
return GameInformation.Type.TICTACTOE;
} else if (game.equalsIgnoreCase("reversi")) {
return GameInformation.Type.REVERSI;
} else if (game.equalsIgnoreCase("connect4")) {
return GameInformation.Type.CONNECT4;
// } else if (game.equalsIgnoreCase("battleship")) {
// return GameInformation.Type.BATTLESHIP;
}
return null;
}
public Server(String ip, String port, String user) {
if (ip.split("\\.").length < 4) {
new ErrorPopup("\"" + ip + "\" " + AppContext.getString("is-not-a-valid-ip-address"));
return;
}
int parsedPort;
try {
parsedPort = Integer.parseInt(port);
} catch (NumberFormatException _) {
new ErrorPopup("\"" + port + "\" " + AppContext.getString("is-not-a-valid-port"));
return;
}
if (user.isEmpty() || user.matches("^[0-9].*")) {
new ErrorPopup(AppContext.getString("invalid-username"));
return;
}
new EventFlow()
.addPostEvent(NetworkEvents.StartClient.class,
new TournamentNetworkingClient(),
new NetworkingConnector(ip, parsedPort, 10, 1, TimeUnit.SECONDS)
)
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
this.user = user;
clientId = e.clientId();
new EventFlow().addPostEvent(new NetworkEvents.SendLogin(clientId, user)).postEvent();
primary = new ServerView(user, this::sendChallenge, this::disconnect);
WidgetContainer.getCurrentView().transitionNext(primary);
startPopulateScheduler();
populateGameList();
}).postEvent();
new EventFlow().listen(this::handleReceivedChallenge)
.listen(this::handleMatchResponse);
}
private void sendChallenge(String opponent) {
if (!isPolling) return;
new SendChallengePopup(this, opponent, (playerInformation, gameType) -> {
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)).postEvent();
isSingleGame.set(true);
});
}
private void handleMatchResponse(NetworkEvents.GameMatchResponse response) {
if (!isPolling) return;
String gameType = extractQuotedValue(response.gameType());
if (response.clientId() == clientId) {
isPolling = false;
onlinePlayers.clear();
final GameInformation.Type type = gameToType(gameType);
if (type == null) {
new ErrorPopup("Unsupported game type: " + gameType);
return;
}
final int myTurn = response.playerToMove().equalsIgnoreCase(response.opponent()) ? 1 : 0;
final GameInformation information = new GameInformation(type);
//information.players[0] = playerInformation;
information.players[0].name = user;
information.players[0].isHuman = false;
information.players[0].computerDifficulty = 5;
information.players[0].computerThinkTime = 1;
information.players[1].name = response.opponent();
Runnable onGameOverRunnable = isSingleGame.get()? null: this::gameOver;
switch (type) {
case TICTACTOE ->
new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage, onGameOverRunnable);
case REVERSI ->
new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage, onGameOverRunnable);
case CONNECT4 ->
new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage, onGameOverRunnable);
default -> new ErrorPopup("Unsupported game type.");
}
}
}
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
if (!isPolling) return;
String challengerName = extractQuotedValue(response.challengerName());
String gameType = extractQuotedValue(response.gameType());
final String finalGameType = gameType;
new ChallengePopup(challengerName, gameType, (playerInformation) -> {
final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", ""));
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent();
isSingleGame.set(true);
});
}
private void sendMessage(String message) {
new EventFlow().addPostEvent(new NetworkEvents.SendMessage(clientId, message)).postEvent();
}
private void disconnect() {
new EventFlow().addPostEvent(new NetworkEvents.CloseClient(clientId)).postEvent();
isPolling = false;
stopScheduler();
primary.transitionPrevious();
}
private void forfeitGame() {
new EventFlow().addPostEvent(new NetworkEvents.SendForfeit(clientId)).postEvent();
}
private void exitGame() {
forfeitGame();
startPopulateScheduler();
}
private void gameOver(){
startPopulateScheduler();
}
private void startPopulateScheduler() {
isPolling = true;
isSingleGame.set(false);
stopScheduler();
new EventFlow()
.listen(NetworkEvents.PlayerlistResponse.class, e -> {
if (e.clientId() == clientId) {
onlinePlayers.clear();
onlinePlayers.addAll(List.of(e.playerlist()));
onlinePlayers.removeIf(name -> name.equalsIgnoreCase(user));
primary.update(onlinePlayers);
}
}, false);
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
if (isPolling) {
new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(clientId)).postEvent();
} else {
stopScheduler();
}
}, 0, 5, TimeUnit.SECONDS);
}
private void stopScheduler() {
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdownNow();
}
}
private void gamesListFromServerHandler(NetworkEvents.GamelistResponse event) {
gameList.clear();
gameList.addAll(List.of(event.gamelist()));
}
public void populateGameList() {
new EventFlow().addPostEvent(new NetworkEvents.SendGetGamelist(clientId))
.listen(NetworkEvents.GamelistResponse.class, this::gamesListFromServerHandler, true)
.postEvent();
}
public List<String> getGameList() {
return gameList;
}
private String extractQuotedValue(String s) {
int first = s.indexOf('"');
int last = s.lastIndexOf('"');
if (first >= 0 && last > first) {
return s.substring(first + 1, last);
}
return s;
}
}

View File

@@ -0,0 +1,11 @@
package org.toop.app.canvas;
import javafx.scene.paint.Color;
import java.util.function.Consumer;
public class Connect4Canvas extends GameCanvas {
public Connect4Canvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
super(color, Color.TRANSPARENT, width, height, 7, 6, 10, true, onCellClicked,null);
}
}

View File

@@ -1,130 +1,215 @@
package org.toop.app.canvas;
import java.util.function.Consumer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseButton;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
public abstract class GameCanvas {
protected record Cell(float x, float y, float width, float height) {}
protected record Cell(float x, float y, float width, float height) {
public boolean isInside(double x, double y) {
return x >= this.x && x <= this.x + width &&
y >= this.y && y <= this.y + height;
}
}
protected final Canvas canvas;
protected final GraphicsContext graphics;
protected final Canvas canvas;
protected final GraphicsContext graphics;
protected final Color color;
protected final Color color;
protected final Color backgroundColor;
protected int width;
protected int height;
protected final int width;
protected final int height;
protected final int rows;
protected final int columns;
protected final int rowSize;
protected final int columnSize;
protected final int gapSize;
protected final boolean edges;
protected final int gapSize;
protected final boolean edges;
protected final Cell[] cells;
protected final Cell[] cells;
protected GameCanvas(
Color color,
int width,
int height,
int rows,
int columns,
int gapSize,
boolean edges,
Consumer<Integer> onCellClicked) {
canvas = new Canvas(width, height);
graphics = canvas.getGraphicsContext2D();
protected GameCanvas(Color color, Color backgroundColor, int width, int height, int rowSize, int columnSize, int gapSize, boolean edges, Consumer<Integer> onCellClicked, Consumer<Integer> newCellEntered) {
canvas = new Canvas(width, height);
graphics = canvas.getGraphicsContext2D();
this.color = color;
this.color = color;
this.backgroundColor = backgroundColor;
this.width = width;
this.height = height;
this.width = width;
this.height = height;
this.rows = rows;
this.columns = columns;
this.rowSize = rowSize;
this.columnSize = columnSize;
this.gapSize = gapSize;
this.edges = edges;
this.gapSize = gapSize;
this.edges = edges;
cells = new Cell[rows * columns];
cells = new Cell[rowSize * columnSize];
final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows;
final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns;
final float cellWidth = ((float) width - gapSize * rowSize - gapSize) / rowSize;
final float cellHeight = ((float) height - gapSize * columnSize - gapSize) / columnSize;
for (int y = 0; y < columns; y++) {
final float startY = y * cellHeight + y * gapSize;
for (int y = 0; y < columnSize; y++) {
final float startY = y * cellHeight + y * gapSize + gapSize;
for (int x = 0; x < rows; x++) {
final float startX = x * cellWidth + x * gapSize;
cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
}
}
for (int x = 0; x < rowSize; x++) {
final float startX = x * cellWidth + x * gapSize + gapSize;
cells[x + y * rowSize] = new Cell(startX, startY, cellWidth, cellHeight);
}
}
canvas.setOnMouseClicked(
event -> {
if (event.getButton() != MouseButton.PRIMARY) {
return;
}
canvas.setOnMouseClicked(event -> {
if (event.getButton() != MouseButton.PRIMARY) {
return;
}
final int column = (int) ((event.getX() / width) * rows);
final int row = (int) ((event.getY() / height) * columns);
final int column = (int) ((event.getX() / this.width) * rowSize);
final int row = (int) ((event.getY() / this.height) * columnSize);
event.consume();
onCellClicked.accept(row * rows + column);
});
final Cell cell = cells[column + row * rowSize];
render();
}
if (cell.isInside(event.getX(), event.getY())) {
event.consume();
onCellClicked.accept(column + row * rowSize);
}
});
public void clear() {
graphics.clearRect(0, 0, width, height);
}
public void render() {
graphics.setFill(color);
for (int x = 1; x < rows; x++) {
graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height);
}
for (int y = 1; y < columns; y++) {
graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize);
}
render();
}
if (edges) {
graphics.fillRect(-gapSize, 0, gapSize, height);
graphics.fillRect(0, -gapSize, width, gapSize);
private void render() {
graphics.setFill(backgroundColor);
graphics.fillRect(0, 0, width, height);
graphics.fillRect(width - gapSize, 0, gapSize, height);
graphics.fillRect(0, height - gapSize, width, gapSize);
}
}
graphics.setFill(color);
public void draw(Color color, int cell) {
for (int x = 0; x < rowSize - 1; x++) {
final float start = cells[x].x + cells[x].width;
graphics.fillRect(start, gapSize, gapSize, height - gapSize * 2);
}
for (int y = 0; y < columnSize - 1; y++) {
final float start = cells[y * rowSize].y + cells[y * rowSize].height;
graphics.fillRect(gapSize, start, width - gapSize * 2, gapSize);
}
if (edges) {
graphics.fillRect(0, 0, width, gapSize);
graphics.fillRect(0, 0, gapSize, height);
graphics.fillRect(width - gapSize, 0, gapSize, height);
graphics.fillRect(0, height - gapSize, width, gapSize);
}
}
public void fill(Color color, int cell) {
final float x = cells[cell].x();
final float y = cells[cell].y();
final float width = cells[cell].width();
final float height = cells[cell].height();
graphics.setFill(color);
graphics.fillRect(x, y, width, height);
}
public void clear(int cell) {
final float x = cells[cell].x();
final float y = cells[cell].y();
final float width = cells[cell].width();
final float height = cells[cell].height();
graphics.clearRect(x, y, width, height);
graphics.setFill(backgroundColor);
graphics.fillRect(x, y, width, height);
}
public void clearAll() {
for (int i = 0; i < cells.length; i++) {
clear(i);
}
}
public void drawDot(Color color, int cell) {
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
graphics.setFill(color);
graphics.fillOval(x, y, width, height);
}
public void drawInnerDot(Color color, int cell, boolean slightlyBigger) {
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
float multiplier = slightlyBigger?1.4f:1.5f;
final float width = (cells[cell].width() - gapSize * 2)/multiplier;
final float height = (cells[cell].height() - gapSize * 2)/multiplier;
float offset = slightlyBigger?5f:4f;
graphics.setFill(color);
graphics.fillRect(x, y, width, height);
graphics.fillOval(x + width/offset, y + height/offset, width, height);
}
public void resize(int width, int height) {
canvas.setWidth(width);
canvas.setHeight(height);
private void drawDotScaled(Color color, int cell, double scale) {
final float cx = cells[cell].x() + gapSize;
final float cy = cells[cell].y() + gapSize;
this.width = width;
this.height = height;
final float fullWidth = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
clear();
render();
}
final float scaledWidth = (float)(fullWidth * scale);
final float offsetX = (fullWidth - scaledWidth) / 2;
public Canvas getCanvas() {
return canvas;
}
graphics.setFill(color);
graphics.fillOval(cx + offsetX, cy, scaledWidth, height);
}
public Timeline flipDot(Color fromColor, Color toColor, int cell) {
final int steps = 60;
final long duration = 250;
final double interval = duration / (double) steps;
final Timeline timeline = new Timeline();
for (int i = 0; i <= steps; i++) {
final double t = i / (double) steps;
final KeyFrame keyFrame = new KeyFrame(Duration.millis(i * interval),
_ -> {
clear(cell);
final double scale = t <= 0.5 ? 1 - 2 * t : 2 * t - 1;
final Color currentColor = t < 0.5 ? fromColor : toColor;
drawDotScaled(currentColor, cell, scale);
}
);
timeline.getKeyFrames().add(keyFrame);
}
return timeline;
}
public Canvas getCanvas() {
return canvas;
}
}

View File

@@ -0,0 +1,84 @@
package org.toop.app.canvas;
import javafx.scene.paint.Color;
import org.toop.game.records.Move;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
public final class ReversiCanvas extends GameCanvas {
private Move[] currentlyHighlightedMoves = null;
public ReversiCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked, Consumer<Integer> newCellEntered) {
super(color, new Color(0f,0.4f,0.2f,1f), width, height, 8, 8, 5, true, onCellClicked, newCellEntered);
drawStartingDots();
final AtomicReference<Cell> lastHoveredCell = new AtomicReference<>(null);
canvas.setOnMouseMoved(event -> {
double mouseX = event.getX();
double mouseY = event.getY();
int cellId = -1;
Cell hovered = null;
for (Cell cell : cells) {
if (cell.isInside(mouseX, mouseY)) {
hovered = cell;
cellId = turnCoordsIntoCellId(mouseX, mouseY);
break;
}
}
Cell previous = lastHoveredCell.get();
if (hovered != previous) {
lastHoveredCell.set(hovered);
newCellEntered.accept(cellId);
}
});
}
public void setCurrentlyHighlightedMovesNull() {
currentlyHighlightedMoves = null;
}
public void drawHighlightDots(Move[] moves){
if (currentlyHighlightedMoves != null){
for (final Move move : currentlyHighlightedMoves){
Color color = move.value() == 'W'? Color.BLACK: Color.WHITE;
drawInnerDot(color, move.position(), true);
}
}
currentlyHighlightedMoves = moves;
if (moves != null) {
for (Move move : moves) {
Color color = move.value() == 'B' ? Color.BLACK : Color.WHITE;
drawInnerDot(color, move.position(), false);
}
}
}
private int turnCoordsIntoCellId(double x, double y) {
final int column = (int) ((x / this.width) * rowSize);
final int row = (int) ((y / this.height) * columnSize);
return column + row * rowSize;
}
public void drawStartingDots() {
drawDot(Color.BLACK, 28);
drawDot(Color.WHITE, 36);
drawDot(Color.BLACK, 35);
drawDot(Color.WHITE, 27);
}
public void drawLegalPosition(int cell, char player) {
Color innerColor;
if (player == 'B') {
innerColor = new Color(0.0f, 0.0f, 0.0f, 0.6f);
}
else {
innerColor = new Color(1.0f, 1.0f, 1.0f, 0.75f);
}
drawInnerDot(innerColor, cell,false);
}
}

View File

@@ -1,37 +1,38 @@
package org.toop.app.canvas;
import java.util.function.Consumer;
import javafx.scene.paint.Color;
public class TicTacToeCanvas extends GameCanvas {
public TicTacToeCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
super(color, width, height, 3, 3, 10, false, onCellClicked);
}
import java.util.function.Consumer;
public void drawX(Color color, int cell) {
graphics.setStroke(color);
graphics.setLineWidth(gapSize);
public final class TicTacToeCanvas extends GameCanvas {
public TicTacToeCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
super(color, Color.TRANSPARENT, width, height, 3, 3, 30, false, onCellClicked,null);
}
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
public void drawX(Color color, int cell) {
graphics.setStroke(color);
graphics.setLineWidth(gapSize);
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
graphics.strokeLine(x, y, x + width, y + height);
graphics.strokeLine(x + width, y, x, y + height);
}
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
public void drawO(Color color, int cell) {
graphics.setStroke(color);
graphics.setLineWidth(gapSize);
graphics.strokeLine(x, y, x + width, y + height);
graphics.strokeLine(x + width, y, x, y + height);
}
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
public void drawO(Color color, int cell) {
graphics.setStroke(color);
graphics.setLineWidth(gapSize);
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
graphics.strokeOval(x, y, width, height);
}
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
graphics.strokeOval(x, y, width, height);
}
}

View File

@@ -0,0 +1,122 @@
package org.toop.app.game;
import org.toop.app.GameInformation;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.view.GameView;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.Game;
import org.toop.game.records.Move;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public abstract class BaseGameThread<TGame extends Game, TAI, TCanvas> {
protected final GameInformation information;
protected final int myTurn;
protected final Runnable onGameOver;
protected final BlockingQueue<Move> moveQueue;
protected final TGame game;
protected final TAI ai;
protected final GameView primary;
protected final TCanvas canvas;
protected final AtomicBoolean isRunning = new AtomicBoolean(true);
protected BaseGameThread(
GameInformation information,
int myTurn,
Runnable onForfeit,
Runnable onExit,
Consumer<String> onMessage,
Runnable onGameOver,
Supplier<TGame> gameSupplier,
Supplier<TAI> aiSupplier,
Function<Consumer<Integer>, TCanvas> canvasFactory) {
this.information = information;
this.myTurn = myTurn;
this.onGameOver = onGameOver;
this.moveQueue = new LinkedBlockingQueue<>();
this.game = gameSupplier.get();
this.ai = aiSupplier.get();
String type = information.type.getTypeToString();
if (onForfeit == null || onExit == null) {
primary = new GameView(null, () -> {
isRunning.set(false);
WidgetContainer.getCurrentView().transitionPrevious();
}, null, type);
} else {
primary = new GameView(onForfeit, () -> {
isRunning.set(false);
onExit.run();
}, onMessage, type);
}
this.canvas = canvasFactory.apply(this::onCellClicked);
addCanvasToPrimary();
WidgetContainer.getCurrentView().transitionNext(primary);
if (onForfeit == null || onExit == null)
new Thread(this::localGameThread).start();
else
new EventFlow()
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
setGameLabels(myTurn == 0);
}
private void onCellClicked(int cell) {
if (!isRunning.get()) return;
final int currentTurn = getCurrentTurn();
if (!information.players[currentTurn].isHuman) return;
final char value = getSymbolForTurn(currentTurn);
try {
moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {}
}
protected void gameOver() {
if (onGameOver != null) {
isRunning.set(false);
onGameOver.run();
}
}
protected void setGameLabels(boolean isMe) {
final int currentTurn = getCurrentTurn();
final String turnName = getNameForTurn(currentTurn);
primary.nextPlayer(
isMe,
information.players[isMe ? 0 : 1].name,
turnName,
information.players[isMe ? 1 : 0].name
);
}
protected abstract void addCanvasToPrimary();
protected abstract int getCurrentTurn();
protected abstract char getSymbolForTurn(int turn);
protected abstract String getNameForTurn(int turn);
protected abstract void onMoveResponse(NetworkEvents.GameMoveResponse response);
protected abstract void onYourTurnResponse(NetworkEvents.YourTurnResponse response);
protected abstract void localGameThread();
}

View File

@@ -0,0 +1,265 @@
package org.toop.app.game;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.canvas.Connect4Canvas;
import org.toop.app.widget.view.GameView;
import org.toop.app.widget.WidgetContainer;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.Connect4.Connect4;
import org.toop.game.Connect4.Connect4AI;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
public class Connect4Game {
private final GameInformation information;
private final int myTurn;
private Runnable onGameOver;
private final BlockingQueue<Move> moveQueue;
private final Connect4 game;
private final Connect4AI ai;
private final int columnSize = 7;
private final int rowSize = 6;
private final GameView primary;
private final Connect4Canvas canvas;
private final AtomicBoolean isRunning;
public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
this.information = information;
this.myTurn = myTurn;
this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Move>();
game = new Connect4();
ai = new Connect4AI();
isRunning = new AtomicBoolean(true);
if (onForfeit == null || onExit == null) {
primary = new GameView(null, () -> {
isRunning.set(false);
WidgetContainer.getCurrentView().transitionPrevious();
}, null, "Connect4");
} else {
primary = new GameView(onForfeit, () -> {
isRunning.set(false);
onExit.run();
}, onMessage, "Connect4");
}
canvas = new Connect4Canvas(Color.GRAY,
(App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,
(cell) -> {
if (onForfeit == null || onExit == null) {
if (information.players[game.getCurrentTurn()].isHuman) {
final char value = game.getCurrentTurn() == 0? 'X' : 'O';
try {
moveQueue.put(new Move(cell%columnSize, value));
} catch (InterruptedException _) {}
}
} else {
if (information.players[0].isHuman) {
final char value = myTurn == 0? 'X' : 'O';
try {
moveQueue.put(new Move(cell%columnSize, value));
} catch (InterruptedException _) {}
}
}
});
primary.add(Pos.CENTER, canvas.getCanvas());
WidgetContainer.getCurrentView().transitionNext(primary);
if (onForfeit == null || onExit == null) {
new Thread(this::localGameThread).start();
setGameLabels(information.players[0].isHuman);
} else {
new EventFlow()
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
setGameLabels(myTurn == 0);
}
updateCanvas();
}
public Connect4Game(GameInformation information) {
this(information, 0, null, null, null, null);
}
private void localGameThread() {
while (isRunning.get()) {
final int currentTurn = game.getCurrentTurn();
final String currentValue = currentTurn == 0? "RED" : "BLUE";
final int nextTurn = (currentTurn + 1) % information.type.getPlayerCount();
primary.nextPlayer(information.players[currentTurn].isHuman,
information.players[currentTurn].name,
currentValue,
information.players[nextTurn].name);
Move move = null;
if (information.players[currentTurn].isHuman) {
try {
final Move wants = moveQueue.take();
final Move[] legalMoves = game.getLegalMoves();
for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) {
move = wants;
break;
}
}
} catch (InterruptedException _) {}
} else {
final long start = System.currentTimeMillis();
move = ai.findBestMove(game, information.players[currentTurn].computerDifficulty);
if (information.players[currentTurn].computerThinkTime > 0) {
final long elapsedTime = System.currentTimeMillis() - start;
final long sleepTime = Math.abs(information.players[currentTurn].computerThinkTime * 1000L - elapsedTime);
try {
Thread.sleep((long)(sleepTime * Math.random()));
} catch (InterruptedException _) {}
}
}
if (move == null) {
continue;
}
final GameState state = game.play(move);
updateCanvas();
/*
if (move.value() == 'X') {
canvas.drawX(Color.INDIANRED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.ROYALBLUE, move.position());
}
*/
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
primary.gameOver(true, information.players[currentTurn].name);
} else if (state == GameState.DRAW) {
primary.gameOver(false, "");
}
isRunning.set(false);
}
}
}
private void onMoveResponse(NetworkEvents.GameMoveResponse response) {
if (!isRunning.get()) {
return;
}
char playerChar;
if (response.player().equalsIgnoreCase(information.players[0].name)) {
playerChar = myTurn == 0? 'X' : 'O';
} else {
playerChar = myTurn == 0? 'O' : 'X';
}
final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move);
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) {
primary.gameOver(true, information.players[0].name);
gameOver();
} else {
primary.gameOver(false, information.players[1].name);
gameOver();
}
} else if (state == GameState.DRAW) {
primary.gameOver(false, "");
gameOver();
}
}
if (move.value() == 'X') {
canvas.drawDot(Color.INDIANRED, move.position());
} else if (move.value() == 'O') {
canvas.drawDot(Color.ROYALBLUE, move.position());
}
updateCanvas();
setGameLabels(game.getCurrentTurn() == myTurn);
}
private void gameOver() {
if (onGameOver == null){
return;
}
isRunning.set(false);
onGameOver.run();
}
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
if (!isRunning.get()) {
return;
}
moveQueue.clear();
int position = -1;
if (information.players[0].isHuman) {
try {
position = moveQueue.take().position();
} catch (InterruptedException _) {}
} else {
final Move move = ai.findBestMove(game, information.players[0].computerDifficulty);
assert move != null;
position = move.position();
}
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short)position))
.postEvent();
}
private void updateCanvas() {
canvas.clearAll();
for (int i = 0; i < game.getBoard().length; i++) {
if (game.getBoard()[i] == 'X') {
canvas.drawDot(Color.RED, i);
} else if (game.getBoard()[i] == 'O') {
canvas.drawDot(Color.BLUE, i);
}
}
}
private void setGameLabels(boolean isMe) {
final int currentTurn = game.getCurrentTurn();
final String currentValue = currentTurn == 0? "RED" : "BLUE";
primary.nextPlayer(isMe,
information.players[isMe? 0 : 1].name,
currentValue,
information.players[isMe? 1 : 0].name);
}
}

View File

@@ -0,0 +1,333 @@
package org.toop.app.game;
import javafx.animation.SequentialTransition;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.canvas.ReversiCanvas;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.view.GameView;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import org.toop.game.reversi.Reversi;
import org.toop.game.reversi.ReversiAI;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import java.awt.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
public final class ReversiGame {
private final GameInformation information;
private final int myTurn;
private final Runnable onGameOver;
private final BlockingQueue<Move> moveQueue;
private final Reversi game;
private final ReversiAI ai;
private final GameView primary;
private final ReversiCanvas canvas;
private final AtomicBoolean isRunning;
private final AtomicBoolean isPaused;
public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
this.information = information;
this.myTurn = myTurn;
this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<>();
game = new Reversi();
ai = new ReversiAI();
isRunning = new AtomicBoolean(true);
isPaused = new AtomicBoolean(false);
if (onForfeit == null || onExit == null) {
primary = new GameView(null, () -> {
isRunning.set(false);
WidgetContainer.getCurrentView().transitionPrevious();
}, null, "Reversi");
} else {
primary = new GameView(onForfeit, () -> {
isRunning.set(false);
onExit.run();
}, onMessage, "Reversi");
}
canvas = new ReversiCanvas(Color.BLACK,
(App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,
(cell) -> {
if (onForfeit == null || onExit == null) {
if (information.players[game.getCurrentTurn()].isHuman) {
final char value = game.getCurrentTurn() == 0? 'B' : 'W';
try {
moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {}
}
} else {
if (information.players[0].isHuman) {
final char value = myTurn == 0? 'B' : 'W';
try {
moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {}
}
}
},this::highlightCells);
primary.add(Pos.CENTER, canvas.getCanvas());
WidgetContainer.getCurrentView().transitionNext(primary);
if (onForfeit == null || onExit == null) {
new Thread(this::localGameThread).start();
setGameLabels(information.players[0].isHuman);
} else {
new EventFlow()
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
setGameLabels(myTurn == 0);
}
updateCanvas(false);
}
public ReversiGame(GameInformation information) {
this(information, 0, null, null, null,null);
}
private void localGameThread() {
while (isRunning.get()) {
if (isPaused.get()) {
try {
Thread.sleep(200);
} catch (InterruptedException _) {}
continue;
}
final int currentTurn = game.getCurrentTurn();
final String currentValue = currentTurn == 0? "BLACK" : "WHITE";
final int nextTurn = (currentTurn + 1) % information.type.getPlayerCount();
primary.nextPlayer(information.players[currentTurn].isHuman,
information.players[currentTurn].name,
currentValue,
information.players[nextTurn].name);
Move move = null;
if (information.players[currentTurn].isHuman) {
try {
final Move wants = moveQueue.take();
final Move[] legalMoves = game.getLegalMoves();
for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) {
move = wants;
break;
}
}
} catch (InterruptedException _) {}
} else {
final long start = System.currentTimeMillis();
move = ai.findBestMove(game, information.players[currentTurn].computerDifficulty);
if (information.players[currentTurn].computerThinkTime > 0) {
final long elapsedTime = System.currentTimeMillis() - start;
final long sleepTime = information.players[currentTurn].computerThinkTime * 1000L - elapsedTime;
try {
Thread.sleep((long) (sleepTime * Math.random()));
} catch (InterruptedException _) {}
}
}
if (move == null) {
continue;
}
canvas.setCurrentlyHighlightedMovesNull();
final GameState state = game.play(move);
updateCanvas(true);
if (state != GameState.NORMAL) {
if (state == GameState.TURN_SKIPPED){
continue;
}
int winningPLayerNumber = getPlayerNumberWithHighestScore();
if (state == GameState.WIN && winningPLayerNumber > -1) {
primary.gameOver(true, information.players[winningPLayerNumber].name);
} else if (state == GameState.DRAW || winningPLayerNumber == -1) {
primary.gameOver(false, "");
}
isRunning.set(false);
}
}
}
private int getPlayerNumberWithHighestScore(){
Reversi.Score score = game.getScore();
if (score.player1Score() > score.player2Score()) return 0;
if (score.player1Score() < score.player2Score()) return 1;
return -1;
}
private void onMoveResponse(NetworkEvents.GameMoveResponse response) {
if (!isRunning.get()) {
return;
}
char playerChar;
if (response.player().equalsIgnoreCase(information.players[0].name)) {
playerChar = myTurn == 0? 'B' : 'W';
} else {
playerChar = myTurn == 0? 'W' : 'B';
}
final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move);
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) {
primary.gameOver(true, information.players[0].name);
gameOver();
} else {
primary.gameOver(false, information.players[1].name);
gameOver();
}
} else if (state == GameState.DRAW) {
primary.gameOver(false, "");
game.play(move);
}
}
updateCanvas(false);
setGameLabels(game.getCurrentTurn() == myTurn);
}
private void gameOver() {
if (onGameOver == null){
return;
}
isRunning.set(false);
onGameOver.run();
}
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
if (!isRunning.get()) {
return;
}
moveQueue.clear();
int position = -1;
if (information.players[0].isHuman) {
try {
position = moveQueue.take().position();
} catch (InterruptedException _) {}
} else {
final Move move = ai.findBestMove(game, information.players[0].computerDifficulty);
assert move != null;
position = move.position();
}
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short) position))
.postEvent();
}
private void updateCanvas(boolean animate) {
// Todo: this is very inefficient. still very fast but if the grid is bigger it might cause issues. improve.
canvas.clearAll();
for (int i = 0; i < game.getBoard().length; i++) {
if (game.getBoard()[i] == 'B') {
canvas.drawDot(Color.BLACK, i);
} else if (game.getBoard()[i] == 'W') {
canvas.drawDot(Color.WHITE, i);
}
}
final Move[] flipped = game.getMostRecentlyFlippedPieces();
final SequentialTransition animation = new SequentialTransition();
isPaused.set(true);
final Color fromColor = game.getCurrentPlayer() == 'W'? Color.WHITE : Color.BLACK;
final Color toColor = game.getCurrentPlayer() == 'W'? Color.BLACK : Color.WHITE;
if (animate && flipped != null) {
for (final Move flip : flipped) {
canvas.clear(flip.position());
canvas.drawDot(fromColor, flip.position());
animation.getChildren().addFirst(canvas.flipDot(fromColor, toColor, flip.position()));
}
}
animation.setOnFinished(_ -> {
isPaused.set(false);
if (information.players[game.getCurrentTurn()].isHuman) {
final Move[] legalMoves = game.getLegalMoves();
for (final Move legalMove : legalMoves) {
canvas.drawLegalPosition(legalMove.position(), game.getCurrentPlayer());
}
}
});
animation.play();
}
private void setGameLabels(boolean isMe) {
final int currentTurn = game.getCurrentTurn();
final String currentValue = currentTurn == 0? "BLACK" : "WHITE";
primary.nextPlayer(isMe,
information.players[isMe? 0 : 1].name,
currentValue,
information.players[isMe? 1 : 0].name);
}
private void highlightCells(int cellEntered) {
if (information.players[game.getCurrentTurn()].isHuman) {
Move[] legalMoves = game.getLegalMoves();
boolean isLegalMove = false;
for (Move move : legalMoves) {
if (move.position() == cellEntered){
isLegalMove = true;
break;
}
}
if (cellEntered >= 0){
Move[] moves = null;
if (isLegalMove) {
moves = game.getFlipsForPotentialMove(
new Point(cellEntered%game.getColumnSize(),cellEntered/game.getRowSize()),
game.getCurrentPlayer());
}
canvas.drawHighlightDots(moves);
}
}
}
}

View File

@@ -0,0 +1,250 @@
package org.toop.app.game;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.canvas.TicTacToeCanvas;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.view.GameView;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import org.toop.game.tictactoe.TicTacToe;
import org.toop.game.tictactoe.TicTacToeAI;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
public final class TicTacToeGame {
private final GameInformation information;
private final int myTurn;
private final Runnable onGameOver;
private final BlockingQueue<Move> moveQueue;
private final TicTacToe game;
private final TicTacToeAI ai;
private final GameView primary;
private final TicTacToeCanvas canvas;
private final AtomicBoolean isRunning;
public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
this.information = information;
this.myTurn = myTurn;
this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Move>();
game = new TicTacToe();
ai = new TicTacToeAI();
isRunning = new AtomicBoolean(true);
if (onForfeit == null || onExit == null) {
primary = new GameView(null, () -> {
isRunning.set(false);
WidgetContainer.getCurrentView().transitionPrevious();
}, null, "TicTacToe");
} else {
primary = new GameView(onForfeit, () -> {
isRunning.set(false);
onExit.run();
}, onMessage, "TicTacToe");
}
canvas = new TicTacToeCanvas(Color.GRAY,
(App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,
(cell) -> {
if (onForfeit == null || onExit == null) {
if (information.players[game.getCurrentTurn()].isHuman) {
final char value = game.getCurrentTurn() == 0? 'X' : 'O';
try {
moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {}
}
} else {
if (information.players[0].isHuman) {
final char value = myTurn == 0? 'X' : 'O';
try {
moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {}
}
}
});
primary.add(Pos.CENTER, canvas.getCanvas());
WidgetContainer.getCurrentView().transitionNext(primary);
if (onForfeit == null || onExit == null) {
new Thread(this::localGameThread).start();
} else {
new EventFlow()
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
setGameLabels(myTurn == 0);
}
}
public TicTacToeGame(GameInformation information) {
this(information, 0, null, null, null, null);
}
private void localGameThread() {
while (isRunning.get()) {
final int currentTurn = game.getCurrentTurn();
final String currentValue = currentTurn == 0? "X" : "O";
final int nextTurn = (currentTurn + 1) % information.type.getPlayerCount();
primary.nextPlayer(information.players[currentTurn].isHuman,
information.players[currentTurn].name,
currentValue,
information.players[nextTurn].name);
Move move = null;
if (information.players[currentTurn].isHuman) {
try {
final Move wants = moveQueue.take();
final Move[] legalMoves = game.getLegalMoves();
for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) {
move = wants;
break;
}
}
} catch (InterruptedException _) {}
} else {
final long start = System.currentTimeMillis();
move = ai.findBestMove(game, information.players[currentTurn].computerDifficulty);
if (information.players[currentTurn].computerThinkTime > 0) {
final long elapsedTime = System.currentTimeMillis() - start;
final long sleepTime = information.players[currentTurn].computerThinkTime * 1000L - elapsedTime;
try {
Thread.sleep((long)(sleepTime * Math.random()));
} catch (InterruptedException _) {}
}
}
if (move == null) {
continue;
}
final GameState state = game.play(move);
if (move.value() == 'X') {
canvas.drawX(Color.INDIANRED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.ROYALBLUE, move.position());
}
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
primary.gameOver(true, information.players[currentTurn].name);
} else if (state == GameState.DRAW) {
primary.gameOver(false, "");
}
isRunning.set(false);
}
}
}
private void onMoveResponse(NetworkEvents.GameMoveResponse response) {
if (!isRunning.get()) {
return;
}
char playerChar;
if (response.player().equalsIgnoreCase(information.players[0].name)) {
playerChar = myTurn == 0? 'X' : 'O';
} else {
playerChar = myTurn == 0? 'O' : 'X';
}
final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move);
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) {
primary.gameOver(true, information.players[0].name);
gameOver();
} else {
primary.gameOver(false, information.players[1].name);
gameOver();
}
} else if (state == GameState.DRAW) {
if(game.getLegalMoves().length == 0) { //only return draw in online multiplayer if the game is actually over.
primary.gameOver(false, "");
gameOver();
}
}
}
if (move.value() == 'X') {
canvas.drawX(Color.RED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.BLUE, move.position());
}
setGameLabels(game.getCurrentTurn() == myTurn);
}
private void gameOver() {
if (onGameOver == null){
return;
}
isRunning.set(false);
onGameOver.run();
}
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
if (!isRunning.get()) {
return;
}
moveQueue.clear();
int position = -1;
if (information.players[0].isHuman) {
try {
position = moveQueue.take().position();
} catch (InterruptedException _) {}
} else {
final Move move;
move = ai.findBestMove(game, information.players[0].computerDifficulty);
assert move != null;
position = move.position();
}
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short)position))
.postEvent();
}
private void setGameLabels(boolean isMe) {
final int currentTurn = game.getCurrentTurn();
final String currentValue = currentTurn == 0? "X" : "O";
primary.nextPlayer(isMe,
information.players[isMe? 0 : 1].name,
currentValue,
information.players[isMe? 1 : 0].name);
}
}

View File

@@ -0,0 +1,176 @@
package org.toop.app.game;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.canvas.TicTacToeCanvas;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import org.toop.game.tictactoe.TicTacToe;
import org.toop.game.tictactoe.TicTacToeAI;
import java.util.function.Consumer;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
public final class TicTacToeGameThread extends BaseGameThread<TicTacToe, TicTacToeAI, TicTacToeCanvas> {
public TicTacToeGameThread(GameInformation info, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
super(info, myTurn, onForfeit, onExit, onMessage, onGameOver,
TicTacToe::new,
TicTacToeAI::new,
clickHandler -> new TicTacToeCanvas(Color.GRAY, (App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3, clickHandler)
);
}
public TicTacToeGameThread(GameInformation info) {
this(info, 0, null, null, null, null);
}
@Override
protected void addCanvasToPrimary() {
primary.add(Pos.CENTER, canvas.getCanvas());
}
@Override
protected int getCurrentTurn() {
return game.getCurrentTurn();
}
@Override
protected char getSymbolForTurn(int turn) {
return turn == 0 ? 'X' : 'O';
}
@Override
protected String getNameForTurn(int turn) {
return turn == 0 ? "X" : "O";
}
private void drawMove(Move move) {
if (move.value() == 'X') canvas.drawX(Color.RED, move.position());
else canvas.drawO(Color.BLUE, move.position());
}
@Override
protected void onMoveResponse(NetworkEvents.GameMoveResponse response) {
if (!isRunning.get()) {
return;
}
char playerChar;
if (response.player().equalsIgnoreCase(information.players[0].name)) {
playerChar = myTurn == 0? 'X' : 'O';
} else {
playerChar = myTurn == 0? 'O' : 'X';
}
final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move);
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) {
primary.gameOver(true, information.players[0].name);
gameOver();
} else {
primary.gameOver(false, information.players[1].name);
gameOver();
}
} else if (state == GameState.DRAW) {
if (game.getLegalMoves().length == 0) {
primary.gameOver(false, "");
gameOver();
}
}
}
drawMove(move);
setGameLabels(game.getCurrentTurn() == myTurn);
}
@Override
protected void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
if (!isRunning.get()) {
return;
}
moveQueue.clear();
int position = -1;
if (information.players[0].isHuman) {
try {
position = moveQueue.take().position();
} catch (InterruptedException _) {}
} else {
final Move move;
if (information.players[1].name.equalsIgnoreCase("pism")) {
move = ai.findWorstMove(game,9);
}else{
move = ai.findBestMove(game, information.players[0].computerDifficulty);
}
assert move != null;
position = move.position();
}
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short)position))
.postEvent();
}
@Override
protected void localGameThread() {
while (isRunning.get()) {
final int currentTurn = game.getCurrentTurn();
setGameLabels(currentTurn == myTurn);
Move move = null;
if (information.players[currentTurn].isHuman) {
try {
final Move wants = moveQueue.take();
final Move[] legalMoves = game.getLegalMoves();
for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) {
move = wants;
break;
}
}
} catch (InterruptedException _) {}
} else {
final long start = System.currentTimeMillis();
move = ai.findBestMove(game, information.players[currentTurn].computerDifficulty);
if (information.players[currentTurn].computerThinkTime > 0) {
final long elapsedTime = System.currentTimeMillis() - start;
final long sleepTime = information.players[currentTurn].computerThinkTime * 1000L - elapsedTime;
try {
Thread.sleep((long)(sleepTime * Math.random()));
} catch (InterruptedException _) {}
}
}
if (move == null) {
continue;
}
final GameState state = game.play(move);
drawMove(move);
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
primary.gameOver(information.players[currentTurn].isHuman, information.players[currentTurn].name);
} else if (state == GameState.DRAW) {
primary.gameOver(false, "");
}
isRunning.set(false);
}
}
}
}

View File

@@ -1,12 +0,0 @@
package org.toop.app.layer;
import javafx.scene.Node;
import javafx.scene.layout.Region;
public abstract class Container {
public abstract Region getContainer();
public abstract void addNodes(Node... nodes);
public abstract void addContainer(Container container, boolean fill);
}

View File

@@ -1,86 +0,0 @@
package org.toop.app.layer;
import javafx.geometry.Pos;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import org.toop.app.App;
import org.toop.app.canvas.GameCanvas;
public abstract class Layer {
protected StackPane layer;
protected Region background;
protected Layer(String... backgroundStyles) {
layer = new StackPane();
background = new Region();
background.getStyleClass().addAll(backgroundStyles);
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
layer.getChildren().addLast(background);
}
protected void addContainer(
Container container,
Pos position,
int xOffset,
int yOffset,
int widthPercent,
int heightPercent) {
StackPane.setAlignment(container.getContainer(), position);
final double widthUnit = App.getWidth() / 100.0;
final double heightUnit = App.getHeight() / 100.0;
if (widthPercent > 0) {
container.getContainer().setMaxWidth(widthPercent * widthUnit);
} else {
container.getContainer().setMaxWidth(Region.USE_PREF_SIZE);
}
if (heightPercent > 0) {
container.getContainer().setMaxHeight(heightPercent * heightUnit);
} else {
container.getContainer().setMaxHeight(Region.USE_PREF_SIZE);
}
container.getContainer().setTranslateX(xOffset * widthUnit);
container.getContainer().setTranslateY(yOffset * heightUnit);
layer.getChildren().addLast(container.getContainer());
}
protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) {
StackPane.setAlignment(canvas.getCanvas(), position);
final double widthUnit = App.getWidth() / 100.0;
final double heightUnit = App.getHeight() / 100.0;
canvas.getCanvas().setTranslateX(xOffset * widthUnit);
canvas.getCanvas().setTranslateY(yOffset * heightUnit);
layer.getChildren().addLast(canvas.getCanvas());
}
protected void pop() {
if (layer.getChildren().size() <= 1) {
return;
}
layer.getChildren().removeLast();
}
protected void popAll() {
final int containers = layer.getChildren().size();
for (int i = 1; i < containers; i++) {
layer.getChildren().removeLast();
}
}
public StackPane getLayer() {
return layer;
}
public abstract void reload();
}

View File

@@ -1,140 +0,0 @@
package org.toop.app.layer;
import java.util.function.Consumer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
public final class NodeBuilder {
public static void addCss(Node node, String... cssClasses) {
node.getStyleClass().addAll(cssClasses);
}
public static void setCss(Node node, String... cssClasses) {
node.getStyleClass().removeAll();
node.getStyleClass().addAll(cssClasses);
}
public static Text header(String x) {
final Text element = new Text(x);
setCss(element, "text-primary", "text-header");
return element;
}
public static Text text(String x) {
final Text element = new Text(x);
setCss(element, "text-secondary", "text-normal");
return element;
}
public static Label button(String x, Runnable runnable) {
final Label element = new Label(x);
setCss(element, "button", "text-normal");
element.setOnMouseClicked(
_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
runnable.run();
});
return element;
}
public static Label toggle(String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
final Label element = new Label(toggled ? x2 : x1);
setCss(element, "toggle", "text-normal");
final BooleanProperty checked = new SimpleBooleanProperty(toggled);
element.setOnMouseClicked(
_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
checked.set(!checked.get());
if (checked.get()) {
element.setText(x1);
} else {
element.setText(x2);
}
consumer.accept(checked.get());
});
return element;
}
public static Slider slider(int max, int initial, Consumer<Integer> consumer) {
final Slider element = new Slider(0, max, initial);
setCss(element, "bg-slider-track");
element.setMinorTickCount(0);
element.setMajorTickUnit(1);
element.setBlockIncrement(1);
element.setSnapToTicks(true);
element.setShowTickLabels(true);
element.setOnMouseClicked(
_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.valueProperty()
.addListener(
(_, _, newValue) -> {
consumer.accept(newValue.intValue());
});
return element;
}
public static TextField input(String x, Consumer<String> consumer) {
final TextField element = new TextField(x);
setCss(element, "input", "text-normal");
element.setOnMouseClicked(
_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.textProperty()
.addListener(
(_, _, newValue) -> {
consumer.accept(newValue);
});
return element;
}
public static <T> ChoiceBox<T> choiceBox(Consumer<T> consumer) {
final ChoiceBox<T> element = new ChoiceBox<>();
setCss(element, "choice-box", "text-normal");
element.setOnMouseClicked(
_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.valueProperty()
.addListener(
(_, _, newValue) -> {
consumer.accept(newValue);
});
return element;
}
public static Separator separator() {
final Separator element = new Separator(Orientation.HORIZONTAL);
setCss(element, "separator");
return element;
}
}

View File

@@ -1,20 +0,0 @@
package org.toop.app.layer;
import org.toop.app.App;
public abstract class Popup extends Layer {
protected Popup(boolean popOnBackground, String... backgroundStyles) {
super(backgroundStyles);
if (popOnBackground) {
background.setOnMouseClicked(
_ -> {
App.pop();
});
}
}
protected Popup(boolean popOnBackground) {
this(popOnBackground, "bg-popup");
}
}

View File

@@ -1,59 +0,0 @@
package org.toop.app.layer.containers;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import org.toop.app.layer.Container;
public final class HorizontalContainer extends Container {
private final HBox container;
public HorizontalContainer(int spacing, String... cssClasses) {
container = new HBox(spacing);
container.getStyleClass().addAll(cssClasses);
}
public HorizontalContainer(int spacing) {
this(spacing, "container");
}
@Override
public Region getContainer() {
return container;
}
@Override
public void addNodes(Node... nodes) {
container.getChildren().addAll(nodes);
}
@Override
public void addContainer(Container container, boolean fill) {
if (fill) {
container.getContainer().setMinSize(0, 0);
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
HBox.setHgrow(container.getContainer(), Priority.ALWAYS);
} else {
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
}
this.container.getChildren().add(container.getContainer());
if (fill) {
balanceChildWidths();
}
}
private void balanceChildWidths() {
final ObservableList<Node> children = container.getChildren();
final double widthPerChild = container.getWidth() / children.size();
for (final Node child : children) {
if (child instanceof Region) {
((Region) child).setPrefWidth(widthPerChild);
}
}
}
}

View File

@@ -1,59 +0,0 @@
package org.toop.app.layer.containers;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import org.toop.app.layer.Container;
public final class VerticalContainer extends Container {
private final VBox container;
public VerticalContainer(int spacing, String... cssClasses) {
container = new VBox(spacing);
container.getStyleClass().addAll(cssClasses);
}
public VerticalContainer(int spacing) {
this(spacing, "container");
}
@Override
public Region getContainer() {
return container;
}
@Override
public void addNodes(Node... nodes) {
container.getChildren().addAll(nodes);
}
@Override
public void addContainer(Container container, boolean fill) {
if (fill) {
container.getContainer().setMinSize(0, 0);
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(container.getContainer(), Priority.ALWAYS);
} else {
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
}
this.container.getChildren().add(container.getContainer());
if (fill) {
balanceChildHeights();
}
}
private void balanceChildHeights() {
final ObservableList<Node> children = container.getChildren();
final double heightPerChild = container.getHeight() / children.size();
for (final Node child : children) {
if (child instanceof Region) {
((Region) child).setPrefHeight(heightPerChild);
}
}
}
}

View File

@@ -1,235 +0,0 @@
package org.toop.app.layer.layers;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.game.TicTacToeLayer;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.local.AppContext;
public final class ConnectedLayer extends Layer {
private static Timer pollTimer = new Timer();
private static class ChallengePopup extends Popup {
private final GameInformation information;
private final String challenger;
private final String game;
private final long clientID;
private final int challengeID;
public ChallengePopup(
GameInformation information,
String challenger,
String game,
long clientID,
String challengeID) {
super(false, "bg-popup");
this.information = information;
this.challenger = challenger;
this.game = game;
this.clientID = clientID;
this.challengeID =
Integer.parseInt(challengeID.substring(18, challengeID.length() - 2));
reload();
}
@Override
public void reload() {
popAll();
final var challengeText = NodeBuilder.header(AppContext.getString("challengeText"));
final var challengerNameText = NodeBuilder.header(challenger);
final var gameText = NodeBuilder.text(AppContext.getString("gameIsText"));
final var gameNameText = NodeBuilder.text(game);
final var acceptButton =
NodeBuilder.button(
AppContext.getString("accept"),
() -> {
pollTimer.cancel();
new EventFlow()
.addPostEvent(
new NetworkEvents.SendAcceptChallenge(
clientID, challengeID))
.postEvent();
App.activate(new TicTacToeLayer(information, clientID));
});
final var denyButton =
NodeBuilder.button(
AppContext.getString("deny"),
() -> {
App.pop();
});
final Container controlContainer = new HorizontalContainer(30);
controlContainer.addNodes(acceptButton, denyButton);
final Container mainContainer = new VerticalContainer(30);
mainContainer.addNodes(challengeText, challengerNameText);
mainContainer.addNodes(gameText, gameNameText);
mainContainer.addContainer(controlContainer, false);
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
}
}
GameInformation information;
long clientId;
String user;
List<String> onlinePlayers = new CopyOnWriteArrayList<>();
public ConnectedLayer(GameInformation information) {
super("bg-primary");
this.information = information;
new EventFlow()
.addPostEvent(
NetworkEvents.StartClient.class,
information.serverIP(),
Integer.parseInt(information.serverPort()))
.onResponse(
NetworkEvents.StartClientResponse.class,
e -> {
clientId = e.clientId();
user = information.playerName()[0].replaceAll("\\s+", "");
new EventFlow()
.addPostEvent(
new NetworkEvents.SendLogin(this.clientId, this.user))
.postEvent();
Thread popThread = new Thread(this::populatePlayerList);
popThread.setDaemon(false);
popThread.start();
})
.postEvent();
new EventFlow().listen(this::handleReceivedChallenge);
reload();
}
private void populatePlayerList() {
EventFlow sendGetPlayerList =
new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId));
new EventFlow()
.listen(
NetworkEvents.PlayerlistResponse.class,
e -> {
if (e.clientId() == this.clientId) {
List<String> playerList =
new java.util.ArrayList<>(
List.of(e.playerlist())); // TODO: Garbage,
// but works
playerList.removeIf(name -> name.equalsIgnoreCase(user));
if (this.onlinePlayers != playerList) {
this.onlinePlayers.clear();
this.onlinePlayers.addAll(playerList);
}
}
});
TimerTask task =
new TimerTask() {
public void run() {
sendGetPlayerList.postEvent();
Platform.runLater(() -> reload());
}
};
pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later
}
private void sendChallenge(String oppUsername, String gameType) {
final AtomicInteger challengeId = new AtomicInteger(-1);
if (onlinePlayers.contains(oppUsername)) {
new EventFlow()
.addPostEvent(
new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType))
.listen(
NetworkEvents.ChallengeResponse.class,
e -> {
challengeId.set(
Integer.parseInt(
e.challengeId()
.substring(
18, e.challengeId().length() - 2)));
})
.listen(
NetworkEvents.GameMatchResponse.class,
e -> {
if (e.clientId() == this.clientId) {
pollTimer.cancel();
App.activate(new TicTacToeLayer(information, this.clientId));
}
},
false)
.postEvent();
// ^
// |
// |
// |
}
}
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
App.push(
new ChallengePopup(
information,
response.challengerName(),
response.gameType(),
clientId,
response.challengeId()));
}
@Override
public void reload() {
popAll();
ListView<Label> players = new ListView<>();
for (int i = 0; i < onlinePlayers.size(); i++) {
int finalI = i;
players.getItems()
.add(
NodeBuilder.button(
onlinePlayers.get(i),
() -> {
String clickedPlayer = onlinePlayers.get(finalI);
sendChallenge(clickedPlayer, "tic-tac-toe");
}));
}
final Container playersContainer = new VerticalContainer(10);
playersContainer.addNodes(players);
addContainer(playersContainer, Pos.CENTER, 0, 0, 0, 0);
}
}

View File

@@ -1,73 +0,0 @@
package org.toop.app.layer.layers;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.geometry.Pos;
import javafx.scene.text.Text;
import javafx.util.Duration;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.local.AppContext;
public final class CreditsPopup extends Popup {
private final int lineHeight = 100;
public CreditsPopup() {
super(true, "bg-primary");
reload();
}
@Override
public void reload() {
popAll();
final String[] credits = {
AppContext.getString("scrumMaster") + ": Stef",
AppContext.getString("productOwner") + ": Omar",
AppContext.getString("mergeCommander") + ": Bas",
AppContext.getString("localization") + ": Ticho",
AppContext.getString("ai") + ": Michiel",
AppContext.getString("developers") + ": Michiel, Bas, Stef, Omar, Ticho",
AppContext.getString("moralSupport") + ": Wesley",
AppContext.getString("opengl") + ": Omar"
};
final Text[] creditsHeaders = new Text[credits.length];
for (int i = 0; i < credits.length; i++) {
creditsHeaders[i] = NodeBuilder.header(credits[i]);
}
final Container creditsContainer = new HorizontalContainer(0);
final Container animatedContainer = new VerticalContainer(lineHeight);
creditsContainer.addContainer(animatedContainer, true);
animatedContainer.addNodes(creditsHeaders);
addContainer(creditsContainer, Pos.CENTER, 0, 0, 50, 100);
playCredits(animatedContainer, App.getHeight());
}
private void playCredits(Container container, double sceneLength) {
container.getContainer().setTranslateY(-sceneLength);
final TranslateTransition scrollCredits =
new TranslateTransition(Duration.seconds(20), container.getContainer());
scrollCredits.setFromY(-sceneLength - lineHeight);
scrollCredits.setToY(sceneLength + lineHeight);
scrollCredits.setOnFinished(
_ -> {
final PauseTransition pauseCredits = new PauseTransition(Duration.seconds(3));
pauseCredits.setOnFinished(_ -> playCredits(container, sceneLength));
pauseCredits.play();
});
scrollCredits.play();
}
}

View File

@@ -1,65 +0,0 @@
package org.toop.app.layer.layers;
import javafx.geometry.Pos;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.local.AppContext;
public final class MainLayer extends Layer {
public MainLayer() {
super("bg-primary");
reload();
}
@Override
public void reload() {
popAll();
final var tictactoeButton =
NodeBuilder.button(
AppContext.getString("tictactoe"),
() -> {
App.activate(new MultiplayerLayer());
});
final var othelloButton =
NodeBuilder.button(
AppContext.getString("othello"),
() -> {
App.activate(new MultiplayerLayer());
});
final var creditsButton =
NodeBuilder.button(
AppContext.getString("credits"),
() -> {
App.push(new CreditsPopup());
});
final var optionsButton =
NodeBuilder.button(
AppContext.getString("options"),
() -> {
App.push(new OptionsPopup());
});
final var quitButton =
NodeBuilder.button(
AppContext.getString("quit"),
() -> {
App.quitPopup();
});
final Container gamesContainer = new VerticalContainer(5);
gamesContainer.addNodes(tictactoeButton, othelloButton);
final Container controlContainer = new VerticalContainer(5);
controlContainer.addNodes(creditsButton, optionsButton, quitButton);
addContainer(gamesContainer, Pos.TOP_LEFT, 2, 2, 20, 0);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 20, 0);
}
}

View File

@@ -1,241 +0,0 @@
package org.toop.app.layer.layers;
import java.time.LocalDateTime;
import javafx.geometry.Pos;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.game.TicTacToeLayer;
import org.toop.local.AppContext;
public final class MultiplayerLayer extends Layer {
private boolean isConnectionLocal = true;
private boolean isPlayer1Human = true;
private String player1Name = "";
private int computer1Difficulty = 0;
private int computer1ThinkTime = 0;
private boolean isPlayer2Human = true;
private String player2Name = "";
private int computer2Difficulty = 0;
private int computer2ThinkTime = 0;
private String serverIP = "";
private String serverPort = "";
public MultiplayerLayer() {
super("bg-primary");
reload();
}
@Override
public void reload() {
popAll();
final Container player1Container = new VerticalContainer(20);
final Container player2Container = new VerticalContainer(20);
final var isPlayer1HumanToggle =
NodeBuilder.toggle(
AppContext.getString("human"),
AppContext.getString("computer"),
!isPlayer1Human,
(computer) -> {
isPlayer1Human = !computer;
reload();
});
player1Container.addNodes(isPlayer1HumanToggle);
if (isPlayer1Human) {
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
final var playerNameInput =
NodeBuilder.input(
player1Name,
(name) -> {
player1Name = name;
});
player1Container.addNodes(playerNameText, playerNameInput);
} else {
player1Name = "Pism Bot V" + LocalDateTime.now().getSecond();
final var computerNameText = NodeBuilder.text(player1Name);
final var computerNameSeparator = NodeBuilder.separator();
final var computerDifficultyText =
NodeBuilder.text(AppContext.getString("computerDifficulty"));
final var computerDifficultySeparator = NodeBuilder.separator();
final var computerDifficultySlider =
NodeBuilder.slider(
10,
computer1Difficulty,
(difficulty) -> computer1Difficulty = difficulty);
final var computerThinkTimeText =
NodeBuilder.text(AppContext.getString("computerThinkTime"));
final var computerThinkTimeSlider =
NodeBuilder.slider(
5, computer1ThinkTime, (thinkTime) -> computer1ThinkTime = thinkTime);
player1Container.addNodes(
computerNameText,
computerNameSeparator,
computerDifficultyText,
computerDifficultySlider,
computerDifficultySeparator,
computerThinkTimeText,
computerThinkTimeSlider);
}
if (isConnectionLocal) {
final var isPlayer2HumanToggle =
NodeBuilder.toggle(
AppContext.getString("human"),
AppContext.getString("computer"),
!isPlayer2Human,
(computer) -> {
isPlayer2Human = !computer;
reload();
});
player2Container.addNodes(isPlayer2HumanToggle);
if (isPlayer2Human) {
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
final var playerNameInput =
NodeBuilder.input(
player2Name,
(name) -> {
player2Name = name;
});
player2Container.addNodes(playerNameText, playerNameInput);
} else {
player2Name = "Pism Bot V" + LocalDateTime.now().getSecond();
final var computerNameText = NodeBuilder.text(player2Name);
final var computerNameSeparator = NodeBuilder.separator();
final var computerDifficultyText =
NodeBuilder.text(AppContext.getString("computerDifficulty"));
final var computerDifficultySeparator = NodeBuilder.separator();
final var computerDifficultySlider =
NodeBuilder.slider(
10,
computer2Difficulty,
(difficulty) -> computer2Difficulty = difficulty);
final var computerThinkTimeText =
NodeBuilder.text(AppContext.getString("computerThinkTime"));
final var computerThinkTimeSlider =
NodeBuilder.slider(
5,
computer2ThinkTime,
(thinkTime) -> computer2ThinkTime = thinkTime);
player2Container.addNodes(
computerNameText,
computerNameSeparator,
computerDifficultyText,
computerDifficultySlider,
computerDifficultySeparator,
computerThinkTimeText,
computerThinkTimeSlider);
}
} else {
final var serverIPText = NodeBuilder.text(AppContext.getString("serverIP"));
final var serverIPSeparator = NodeBuilder.separator();
final var serverIPInput =
NodeBuilder.input(
serverIP,
(ip) -> {
serverIP = ip;
});
final var serverPortText = NodeBuilder.text(AppContext.getString("serverPort"));
final var serverPortInput =
NodeBuilder.input(
serverPort,
(port) -> {
serverPort = port;
});
player2Container.addNodes(
serverIPText,
serverIPInput,
serverIPSeparator,
serverPortText,
serverPortInput);
}
final var versusText = NodeBuilder.header("VS");
final var connectionTypeText =
NodeBuilder.text(AppContext.getString("connectionType") + ":");
final var connectionTypeToggle =
NodeBuilder.toggle(
AppContext.getString("local"),
AppContext.getString("server"),
!isConnectionLocal,
(server) -> {
isConnectionLocal = !server;
reload();
});
final var playButton =
NodeBuilder.button(
isConnectionLocal
? AppContext.getString("start")
: AppContext.getString("connect"),
() -> {
final var information =
new GameInformation(
new String[] {player1Name, player2Name},
new boolean[] {isPlayer1Human, isPlayer2Human},
new int[] {computer1Difficulty, computer2Difficulty},
new int[] {computer1ThinkTime, computer2ThinkTime},
isConnectionLocal,
serverIP,
serverPort);
if (isConnectionLocal) {
App.activate(new TicTacToeLayer(information));
} else {
App.activate(new ConnectedLayer(information));
}
});
final Container mainContainer = new VerticalContainer(10);
final Container playersContainer = new HorizontalContainer(5);
final Container connectionTypeContainer = new HorizontalContainer(10);
mainContainer.addContainer(playersContainer, true);
mainContainer.addContainer(connectionTypeContainer, false);
mainContainer.addNodes(playButton);
connectionTypeContainer.addNodes(connectionTypeText, connectionTypeToggle);
playersContainer.addContainer(player1Container, true);
playersContainer.addNodes(versusText);
playersContainer.addContainer(player2Container, true);
final var backButton =
NodeBuilder.button(
AppContext.getString("back"),
() -> {
App.activate(new MainLayer());
});
final Container controlContainer = new VerticalContainer(0);
controlContainer.addNodes(backButton);
addContainer(mainContainer, Pos.CENTER, 0, 0, 75, 75);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
}
}

View File

@@ -1,222 +0,0 @@
package org.toop.app.layer.layers;
import java.util.Locale;
import javafx.geometry.Pos;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.framework.asset.resources.SettingsAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
import org.toop.local.AppSettings;
public final class OptionsPopup extends Popup {
AppSettings appSettings = new AppSettings();
SettingsAsset settings = appSettings.getPath();
private boolean isWindowed = !(settings.getFullscreen());
public OptionsPopup() {
super(true, "bg-primary");
reload();
}
@Override
public void reload() {
popAll();
final var languageHeader = NodeBuilder.header(AppContext.getString("language"));
final var languageSeparator = NodeBuilder.separator();
final var volumeHeader = NodeBuilder.header(AppContext.getString("volume"));
final var volumeSeparator = NodeBuilder.separator();
final var fxVolumeHeader = NodeBuilder.header(AppContext.getString("effectsVolume"));
final var fxVolumeSeparator = NodeBuilder.separator();
final var musicVolumeHeader = NodeBuilder.header(AppContext.getString("musicVolume"));
final var musicVolumeSeparator = NodeBuilder.separator();
final var themeHeader = NodeBuilder.header(AppContext.getString("theme"));
final var themeSeparator = NodeBuilder.separator();
final var layoutSizeHeader = NodeBuilder.header(AppContext.getString("layoutSize"));
final var layoutSizeSeparator = NodeBuilder.separator();
final var optionsContainer = new VerticalContainer(5);
optionsContainer.addNodes(languageHeader, languageChoiceBox(), languageSeparator);
optionsContainer.addNodes(volumeHeader, volumeSlider(), volumeSeparator);
optionsContainer.addNodes(fxVolumeHeader, fxVolumeSlider(), fxVolumeSeparator);
optionsContainer.addNodes(musicVolumeHeader, musicVolumeSlider(), musicVolumeSeparator);
optionsContainer.addNodes(themeHeader, themeChoiceBox(), themeSeparator);
optionsContainer.addNodes(layoutSizeHeader, layoutSizeChoiceBox(), layoutSizeSeparator);
optionsContainer.addNodes(fullscreenToggle());
final Container mainContainer = new VerticalContainer(50, "");
mainContainer.addContainer(optionsContainer, true);
final var backButton =
NodeBuilder.button(
AppContext.getString("back"),
() -> {
App.pop();
});
final Container controlContainer = new VerticalContainer(5);
controlContainer.addNodes(backButton);
addContainer(mainContainer, Pos.CENTER, 0, 0, 0, 0);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
}
private ChoiceBox<Locale> languageChoiceBox() {
assert AppContext.getLocalization() != null;
final ChoiceBox<Locale> languageChoiceBox =
NodeBuilder.choiceBox(
(locale) -> {
if (locale == AppContext.getLocale()) {
return;
}
settings.setLocale(locale.toString());
AppContext.setLocale(locale);
App.reloadAll();
});
languageChoiceBox.setConverter(
new javafx.util.StringConverter<>() {
@Override
public String toString(Locale locale) {
return AppContext.getString(locale.getDisplayName().toLowerCase());
}
@Override
public Locale fromString(String string) {
return null;
}
});
languageChoiceBox.getItems().addAll(AppContext.getLocalization().getAvailableLocales());
languageChoiceBox.setValue(AppContext.getLocale());
return languageChoiceBox;
}
private Slider volumeSlider() {
return NodeBuilder.slider(
100,
settings.getVolume(),
(volume) -> {
settings.setVolume(volume);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(volume.doubleValue()))
.asyncPostEvent();
});
}
private Slider fxVolumeSlider() {
return NodeBuilder.slider(
100,
settings.getFxVolume(),
(volume) -> {
settings.setFxVolume(volume);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeFxVolume(volume.doubleValue()))
.asyncPostEvent();
});
}
private Slider musicVolumeSlider() {
return NodeBuilder.slider(
100,
settings.getMusicVolume(),
(volume) -> {
settings.setMusicVolume(volume);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeMusicVolume(volume.doubleValue()))
.asyncPostEvent();
});
}
private Label fullscreenToggle() {
return NodeBuilder.toggle(
AppContext.getString("windowed"),
AppContext.getString("fullscreen"),
!isWindowed,
(fullscreen) -> {
isWindowed = !fullscreen;
settings.setFullscreen(fullscreen);
App.setFullscreen(fullscreen);
});
}
private ChoiceBox<String> themeChoiceBox() {
final ChoiceBox<String> themeChoiceBox =
NodeBuilder.choiceBox(
(theme) -> {
if (theme.equalsIgnoreCase(settings.getTheme())) {
return;
}
settings.setTheme(theme);
App.setStyle(theme, settings.getLayoutSize());
});
themeChoiceBox.setConverter(
new javafx.util.StringConverter<>() {
@Override
public String toString(String theme) {
return AppContext.getString(theme);
}
@Override
public String fromString(String string) {
return null;
}
});
themeChoiceBox.getItems().addAll("dark", "light", "dark-hc", "light-hc");
themeChoiceBox.setValue(settings.getTheme());
return themeChoiceBox;
}
private ChoiceBox<String> layoutSizeChoiceBox() {
final ChoiceBox<String> layoutSizeChoiceBox =
NodeBuilder.choiceBox(
(layoutSize) -> {
if (layoutSize.equalsIgnoreCase(settings.getLayoutSize())) {
return;
}
settings.setLayoutSize(layoutSize);
App.setStyle(settings.getTheme(), layoutSize);
});
layoutSizeChoiceBox.setConverter(
new javafx.util.StringConverter<>() {
@Override
public String toString(String layoutSize) {
return AppContext.getString(layoutSize);
}
@Override
public String fromString(String string) {
return null;
}
});
layoutSizeChoiceBox.getItems().addAll("small", "medium", "large");
layoutSizeChoiceBox.setValue(settings.getLayoutSize());
return layoutSizeChoiceBox;
}
}

View File

@@ -1,47 +0,0 @@
package org.toop.app.layer.layers;
import javafx.geometry.Pos;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.local.AppContext;
public final class QuitPopup extends Popup {
public QuitPopup() {
super(true);
reload();
}
@Override
public void reload() {
popAll();
final var sureText = NodeBuilder.header(AppContext.getString("quitSure"));
final var yesButton =
NodeBuilder.button(
AppContext.getString("yes"),
() -> {
App.quit();
});
final var noButton =
NodeBuilder.button(
AppContext.getString("no"),
() -> {
App.pop();
});
final Container controlContainer = new HorizontalContainer(30);
controlContainer.addNodes(yesButton, noButton);
final Container mainContainer = new VerticalContainer(30);
mainContainer.addNodes(sureText);
mainContainer.addContainer(controlContainer, false);
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
}
}

View File

@@ -1,55 +0,0 @@
package org.toop.app.layer.layers.game;
import javafx.geometry.Pos;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.local.AppContext;
public class GameFinishedPopup extends Popup {
private final boolean isDraw;
private final String winner;
public GameFinishedPopup(boolean isDraw, String winner) {
super(true, "bg-popup");
this.isDraw = isDraw;
this.winner = winner;
reload();
}
@Override
public void reload() {
popAll();
final Container mainContainer = new VerticalContainer(30);
if (isDraw) {
final var drawHeader = NodeBuilder.header(AppContext.getString("drawText"));
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
mainContainer.addNodes(drawHeader, goodGameText);
} else {
final var winHeader =
NodeBuilder.header(AppContext.getString("congratulations") + ": " + winner);
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
mainContainer.addNodes(winHeader, goodGameText);
}
final var backToMainMenuButton =
NodeBuilder.button(
AppContext.getString("backToMainMenu"),
() -> {
App.activate(new MainLayer());
});
mainContainer.addNodes(backToMainMenuButton);
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
}
}

View File

@@ -1,329 +0,0 @@
package org.toop.app.layer.layers.game;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.canvas.TicTacToeCanvas;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.Game;
import org.toop.game.tictactoe.TicTacToe;
import org.toop.game.tictactoe.TicTacToeAI;
import org.toop.local.AppContext;
public final class TicTacToeLayer extends Layer {
private TicTacToeCanvas canvas;
private AtomicReference<TicTacToe> ticTacToe;
private TicTacToeAI ticTacToeAI;
private GameInformation information;
private final Text currentPlayerNameText;
private final Text currentPlayerMoveText;
private final BlockingQueue<Game.Move> playerMoveQueue = new LinkedBlockingQueue<>();
// Todo: set these from the server
private char currentPlayerMove = Game.EMPTY;
private String player2Name = "";
final AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true);
public TicTacToeLayer(GameInformation information) {
super("bg-primary");
canvas =
new TicTacToeCanvas(
Color.LIME,
(App.getHeight() / 100) * 75,
(App.getHeight() / 100) * 75,
(cell) -> {
try {
if (information.isConnectionLocal()) {
if (ticTacToe.get().getCurrentTurn() == 0) {
playerMoveQueue.put(new Game.Move(cell, 'X'));
} else {
playerMoveQueue.put(new Game.Move(cell, 'O'));
}
} else {
if (information.isPlayerHuman()[0]
&& currentPlayerMove != Game.EMPTY) {
playerMoveQueue.put(
new Game.Move(
cell, firstPlayerIsMe.get() ? 'X' : 'O'));
}
}
} catch (InterruptedException _) {
}
});
ticTacToe = new AtomicReference<>(new TicTacToe());
ticTacToeAI = new TicTacToeAI();
this.information = information;
if (information.isConnectionLocal()) {
new Thread(this::localGameThread).start();
}
currentPlayerNameText = NodeBuilder.header("");
currentPlayerMoveText = NodeBuilder.header("");
reload();
}
public TicTacToeLayer(GameInformation information, long clientID) {
this(information);
Thread a = new Thread(this::serverGameThread);
a.setDaemon(false);
a.start();
reload();
}
@Override
public void reload() {
popAll();
canvas.resize((App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75);
for (int i = 0; i < ticTacToe.get().board.length; i++) {
final char value = ticTacToe.get().board[i];
if (value == 'X') {
canvas.drawX(Color.RED, i);
} else if (value == 'O') {
canvas.drawO(Color.BLUE, i);
}
}
final var backButton =
NodeBuilder.button(
AppContext.getString("back"),
() -> {
App.activate(new MainLayer());
});
final Container controlContainer = new VerticalContainer(5);
controlContainer.addNodes(backButton);
final Container informationContainer = new HorizontalContainer(15);
informationContainer.addNodes(currentPlayerNameText, currentPlayerMoveText);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
addContainer(informationContainer, Pos.TOP_LEFT, 2, 2, 0, 0);
addGameCanvas(canvas, Pos.CENTER, 0, 0);
}
private int compurterDifficultyToDepth(int maxDifficulty, int difficulty) {
return (int) (((float) maxDifficulty / difficulty) * 9);
}
private void localGameThread() {
boolean running = true;
while (running) {
final int currentPlayer = ticTacToe.get().getCurrentTurn();
currentPlayerNameText.setText(information.playerName()[currentPlayer]);
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0 ? "X" : "O");
Game.Move move = null;
if (information.isPlayerHuman()[currentPlayer]) {
try {
final Game.Move wants = playerMoveQueue.take();
final Game.Move[] legalMoves = ticTacToe.get().getLegalMoves();
for (final Game.Move legalMove : legalMoves) {
if (legalMove.position() == wants.position()
&& legalMove.value() == wants.value()) {
move = wants;
}
}
} catch (InterruptedException _) {
}
} else {
final long start = System.currentTimeMillis();
move =
ticTacToeAI.findBestMove(
ticTacToe.get(),
compurterDifficultyToDepth(
10, information.computerDifficulty()[currentPlayer]));
if (information.computerThinkTime()[currentPlayer] > 0) {
final long elapsedTime = System.currentTimeMillis() - start;
final long sleepTime =
information.computerThinkTime()[currentPlayer] * 1000L - elapsedTime;
try {
Thread.sleep(sleepTime);
} catch (InterruptedException _) {
}
}
}
if (move == null) {
continue;
}
final Game.State state = ticTacToe.get().play(move);
if (move.value() == 'X') {
canvas.drawX(Color.RED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.BLUE, move.position());
}
if (state != Game.State.NORMAL) {
if (state == Game.State.WIN) {
App.push(
new GameFinishedPopup(
false,
information.playerName()[ticTacToe.get().getCurrentTurn()]));
} else if (state == Game.State.DRAW) {
App.push(new GameFinishedPopup(true, ""));
}
running = false;
}
}
}
private void serverGameThread() {
new EventFlow()
.listen(this::handleServerGameStart) // <-----------
.listen(this::yourTurnResponse)
.listen(this::onMoveResponse)
.listen(this::handleReceivedMessage);
}
private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) {
// Meneer Bas de Jong. Dit functie wordt niet aangeroepen als je de challenger bent.
// Ik heb veel dingen geprobeert. FUCKING veel dingen. Hij doet het niet.
// Ik heb zelfs in jou code gekeken en unsubscribeAfterSuccess op false gezet. (zie
// ConnectedLayer).
// Alle andere functies worden wel gecalt. Behalve dit.
// Ben jij gehandicapt of ik? Want het moet 1 van de 2 zijn. Ik ben dit al 2 uur aan het
// debuggen.
// Ik ga nu slapen (04:46).
// ⠀⠀⠀⠀⠀⠀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⢀⣴⣿⣿⠿⣟⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⢸⣏⡏⠀⠀⠀⢣⢻⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⢸⣟⠧⠤⠤⠔⠋⠀⢿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠸⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⠘⣿⡀⢀⣶⠤⠒⠀⢻⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⠀⢹⣧⠀⠀⠀⠀⠀⠈⢿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠈⢿⣆⣠⣤⣤⣤⣤⣴⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⢀⣾⢿⢿⠀⠀⠀⢀⣀⣀⠘⣿⠋⠁⠀⠙⢇⠀⠀⠙⢿⣦⡀⠀⠀⠀⠀⠀
// ⠀⠀⠀⢀⣾⢇⡞⠘⣧⠀⢖⡭⠞⢛⡄⠘⣆⠀⠀⠀⠈⢧⠀⠀⠀⠙⢿⣄⠀⠀⠀⠀
// ⠀⠀⣠⣿⣛⣥⠤⠤⢿⡄⠀⠀⠈⠉⠀⠀⠹⡄⠀⠀⠀⠈⢧⠀⠀⠀⠈⠻⣦⠀⠀⠀
// ⠀⣼⡟⡱⠛⠙⠀⠀⠘⢷⡀⠀⠀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠈⣧⠀⠀⠀⠀⠹⣧⡀⠀
// ⢸⡏⢠⠃⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠘⣧⠀⠀⠀⠀⠸⣷⡀
// ⠸⣧⠘⡇⠀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⢹⡇⠀⠀⠀⠀⣿⠇
// ⠀⣿⡄⢳⠀⠀⠀⠀⠀⠀⠀⠈⣷⠀⠀⠀⠀⠀⠀⠈⠆⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠀
// ⠀⢹⡇⠘⣇⠀⠀⠀⠀⠀⠀⠰⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⣼⡟⠀⠀
// ⠀⢸⡇⠀⢹⡆⠀⠀⠀⠀⠀⠀⠙⠁⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⢳⣼⠟⠀⠀⠀
// ⠀⠸⣧⣀⠀⢳⡀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⢃⠀⢀⣴⡿⠁⠀⠀⠀⠀
// ⠀⠀⠈⠙⢷⣄⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⣠⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⠈⠻⢿⣷⣦⣄⣀⣀⣠⣤⠾⠷⣦⣤⣤⡶⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
// ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
player2Name = resp.opponent();
System.out.println(player2Name);
currentPlayerMoveText.setText("X");
if (!resp.playerToMove().equalsIgnoreCase(resp.opponent())) {
currentPlayerNameText.setText(information.playerName()[0]);
firstPlayerIsMe.set(true);
System.out.printf("I am starting: My client id is %d\n", resp.clientId());
} else {
currentPlayerNameText.setText(player2Name);
firstPlayerIsMe.set(false);
System.out.printf("I am NOT starting: My client id is %d\n", resp.clientId());
}
}
private void onMoveResponse(NetworkEvents.GameMoveResponse resp) {
char playerChar;
if (!resp.player().equalsIgnoreCase(player2Name)) {
playerChar = firstPlayerIsMe.get() ? 'X' : 'O';
} else {
playerChar = firstPlayerIsMe.get() ? 'O' : 'X';
}
final Game.Move move = new Game.Move(Integer.parseInt(resp.move()), playerChar);
final Game.State state = ticTacToe.get().play(move);
if (state
!= Game.State.NORMAL) { // todo differentiate between future draw guaranteed and is
// currently a draw
if (state == Game.State.WIN) {
App.push(
new GameFinishedPopup(
false, information.playerName()[ticTacToe.get().getCurrentTurn()]));
} else if (state == Game.State.DRAW) {
App.push(new GameFinishedPopup(true, ""));
}
}
if (move.value() == 'X') {
canvas.drawX(Color.RED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.BLUE, move.position());
}
currentPlayerNameText.setText(
ticTacToe.get().getCurrentTurn() == (firstPlayerIsMe.get() ? 0 : 1)
? information.playerName()[0]
: player2Name);
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0 ? "X" : "O");
}
private void yourTurnResponse(NetworkEvents.YourTurnResponse response) {
int position = -1;
if (information.isPlayerHuman()[0]) {
try {
position = playerMoveQueue.take().position();
} catch (InterruptedException _) {
}
} else {
final Game.Move move =
ticTacToeAI.findBestMove(
ticTacToe.get(),
compurterDifficultyToDepth(10, information.computerDifficulty()[0]));
position = move.position();
}
new EventFlow()
.addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short) position))
.postEvent();
}
private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) {
System.out.println("Received Message: " + msg.message()); // todo add chat window
}
}

View File

@@ -0,0 +1,179 @@
package org.toop.app.widget;
import javafx.scene.image.ImageView;
import org.toop.framework.resource.resources.ImageAsset;
import org.toop.local.AppContext;
import java.awt.*;
import java.io.File;
import java.util.function.Consumer;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.util.StringConverter;
public final class Primitive {
public static Text header(String key) {
var header = new Text();
header.getStyleClass().add("header");
if (!key.isEmpty()) {
header.setText(AppContext.getString(key));
header.textProperty().bind(AppContext.bindToKey(key));
}
return header;
}
public static Text text(String key) {
var text = new Text();
text.getStyleClass().add("text");
if (!key.isEmpty()) {
text.setText(AppContext.getString(key));
text.textProperty().bind(AppContext.bindToKey(key));
}
return text;
}
public static ImageView image(ImageAsset imageAsset) {
ImageView imageView = new ImageView(imageAsset.getImage());
imageView.getStyleClass().add("image");
imageView.setPreserveRatio(true);
imageView.setFitWidth(400);
imageView.setFitHeight(400);
return imageView;
}
public static Button button(String key, Runnable onAction) {
var button = new Button();
button.getStyleClass().add("button");
if (!key.isEmpty()) {
button.setText(AppContext.getString(key));
button.textProperty().bind(AppContext.bindToKey(key));
}
if (onAction != null) {
button.setOnAction(_ ->
onAction.run());
}
return button;
}
public static TextField input(String promptKey, String text, Consumer<String> onValueChanged) {
var input = new TextField();
input.getStyleClass().add("input");
if (!promptKey.isEmpty()) {
input.setPromptText(AppContext.getString(promptKey));
input.promptTextProperty().bind(AppContext.bindToKey(promptKey));
}
input.setText(text);
if (onValueChanged != null) {
input.textProperty().addListener((_, _, newValue) ->
onValueChanged.accept(newValue));
}
return input;
}
public static Slider slider(int min, int max, int value, Consumer<Integer> onValueChanged) {
var slider = new Slider();
slider.getStyleClass().add("slider");
slider.setMin(min);
slider.setMax(max);
slider.setValue(value);
if (onValueChanged != null) {
slider.valueProperty().addListener((_, _, newValue) ->
onValueChanged.accept(newValue.intValue()));
}
return slider;
}
@SafeVarargs
public static <T> ComboBox<T> choice(StringConverter<T> converter, T value, Consumer<T> onValueChanged, T... items) {
var choice = new ComboBox<T>();
choice.getStyleClass().add("choice");
if (converter != null) {
choice.setConverter(converter);
}
if (value != null) {
choice.setValue(value);
}
if (onValueChanged != null) {
choice.valueProperty().addListener((_, _, newValue) ->
onValueChanged.accept(newValue));
}
choice.setItems(FXCollections.observableArrayList(items));
return choice;
}
public static ScrollPane scroll(Node content) {
var scroll = new ScrollPane();
scroll.getStyleClass().add("scroll");
scroll.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
scroll.setFitToWidth(true);
scroll.setContent(content);
return scroll;
}
public static Separator separator() {
var separator = new Separator();
separator.getStyleClass().add("separator");
return separator;
}
public static HBox hbox(Node... nodes) {
var hbox = new HBox();
hbox.getStyleClass().add("container");
hbox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
for (var node : nodes) {
if (node != null) {
hbox.getChildren().add(node);
}
}
return hbox;
}
public static VBox vbox(Node... nodes) {
var vbox = new VBox();
vbox.getStyleClass().add("container");
vbox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
for (var node : nodes) {
if (node != null) {
vbox.getChildren().add(node);
}
}
return vbox;
}
}

View File

@@ -0,0 +1,5 @@
package org.toop.app.widget;
public interface Updatable {
void update();
}

View File

@@ -0,0 +1,21 @@
package org.toop.app.widget;
import javafx.geometry.Pos;
import javafx.scene.Node;
public interface Widget {
Node getNode();
default void show(Pos position) {
WidgetContainer.add(position, this);
}
default void hide() {
WidgetContainer.remove(this);
}
default void replace(Pos position, Widget widget) {
widget.show(position);
hide();
}
}

View File

@@ -0,0 +1,77 @@
package org.toop.app.widget;
import org.toop.app.widget.complex.PopupWidget;
import org.toop.app.widget.complex.ViewWidget;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.layout.StackPane;
public final class WidgetContainer {
private static StackPane root;
private static ViewWidget currentView;
public static synchronized StackPane setup() {
if (root != null) {
return root;
}
root = new StackPane();
root.getStyleClass().add("bg-view");
return root;
}
public static void add(Pos position, Widget widget) {
if (root == null || widget == null) {
return;
}
Platform.runLater(() -> {
if (root.getChildren().contains(widget.getNode())) {
return;
}
StackPane.setAlignment(widget.getNode(), position);
if (widget instanceof ViewWidget view) {
root.getChildren().addFirst(view.getNode());
currentView = view;
} else if (widget instanceof PopupWidget popup) {
currentView.add(Pos.CENTER, popup);
} else {
root.getChildren().add(widget.getNode());
}
});
}
public static void remove(Widget widget) {
if (root == null || widget == null) {
return;
}
Platform.runLater(() -> {
if (widget instanceof PopupWidget popup) {
currentView.remove(popup);
} else {
root.getChildren().remove(widget.getNode());
}
});
}
public static ViewWidget getCurrentView() {
return currentView;
}
public static void setCurrentView(ViewWidget view) {
if (root == null || view == null) {
return;
}
Platform.runLater(() -> {
root.getChildren().clear();
root.getChildren().add(view.getNode());
currentView = view;
});
}
}

View File

@@ -0,0 +1,38 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.Widget;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
public class ConfirmWidget implements Widget {
private final HBox buttonsContainer;
private final Text messageText;
private final VBox container;
public ConfirmWidget(String confirm) {
buttonsContainer = Primitive.hbox();
messageText = Primitive.text("");
container = Primitive.vbox(Primitive.header(confirm), messageText, Primitive.separator(), buttonsContainer);
}
public void setMessage(String message) {
messageText.setText(message);
}
public void addButton(String key, Runnable onClick) {
Platform.runLater(() -> {
var button = Primitive.button(key, onClick);
buttonsContainer.getChildren().add(button);
});
}
@Override
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,42 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.Widget;
import java.util.function.Consumer;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.util.StringConverter;
public class LabeledChoiceWidget<T> implements Widget {
private final ComboBox<T> comboBox;
private final VBox container;
@SafeVarargs
public LabeledChoiceWidget(
String key,
StringConverter<T> converter,
T initialValue,
Consumer<T> onValueChanged,
T... items
) {
var label = Primitive.text(key);
comboBox = Primitive.choice(converter, initialValue, onValueChanged, items);
container = Primitive.vbox(label, comboBox);
}
public T getValue() {
return comboBox.getValue();
}
public void setValue(T value) {
comboBox.setValue(value);
}
@Override
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,34 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.Widget;
import java.util.function.Consumer;
import javafx.scene.Node;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
public class LabeledInputWidget implements Widget {
private final TextField input;
private final VBox container;
public LabeledInputWidget(String key, String promptKey, String initialText, Consumer<String> onValueChanged) {
var label = Primitive.text(key);
input = Primitive.input(promptKey, initialText, onValueChanged);
container = Primitive.vbox(label, input);
}
public String getValue() {
return input.getText();
}
public void setValue(String text) {
input.setText(text);
}
@Override
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,49 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.Widget;
import java.util.function.Consumer;
import javafx.scene.Node;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
public class LabeledSliderWidget implements Widget {
private final Slider slider;
private final Text labelValue;
private final VBox container;
public LabeledSliderWidget(String key, int min, int max, int value, Consumer<Integer> onValueChanged) {
var label = Primitive.text(key);
labelValue = new Text(String.valueOf(value));
labelValue.getStyleClass().add("text");
slider = Primitive.slider(min, max, value, newValue -> {
labelValue.setText(String.valueOf(newValue));
if (onValueChanged != null) {
onValueChanged.accept(newValue);
}
});
var sliderRow = Primitive.hbox(slider, labelValue);
container = Primitive.vbox(label, sliderRow);
}
public int getValue() {
return (int)slider.getValue();
}
public void setValue(int newValue) {
slider.setValue(newValue);
labelValue.setText(String.valueOf(newValue));
}
@Override
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,83 @@
package org.toop.app.widget.complex;
import org.toop.app.GameInformation;
import org.toop.app.widget.Primitive;
import javafx.scene.Node;
import javafx.scene.layout.VBox;
public class PlayerInfoWidget {
private final GameInformation.Player information;
private final VBox container;
public PlayerInfoWidget(GameInformation.Player information) {
this.information = information;
container = Primitive.vbox(
buildToggle().getNode(),
buildContent()
);
}
private ToggleWidget buildToggle() {
return new ToggleWidget(
"computer", "player",
information.isHuman,
isHuman -> {
information.isHuman = isHuman;
container.getChildren().setAll(
buildToggle().getNode(),
buildContent()
);
}
);
}
private Node buildContent() {
if (information.isHuman) {
var nameInput = new LabeledInputWidget(
"name",
"enter-your-name",
information.name,
newName -> information.name = newName
);
return nameInput.getNode();
} else {
if (information.name == null || information.name.isEmpty()) {
information.name = "Pism Bot";
}
var playerName = Primitive.text("");
playerName.setText(information.name);
var nameDisplay = Primitive.vbox(
Primitive.text("name"),
playerName
);
var difficultySlider = new LabeledSliderWidget(
"computer-difficulty",
0, 5,
information.computerDifficulty,
newVal -> information.computerDifficulty = newVal
);
var thinkTimeSlider = new LabeledSliderWidget(
"computer-think-time",
0, 5,
information.computerThinkTime,
newVal -> information.computerThinkTime = newVal
);
return Primitive.vbox(
nameDisplay,
difficultySlider.getNode(),
thinkTimeSlider.getNode()
);
}
}
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,21 @@
package org.toop.app.widget.complex;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
public abstract class PopupWidget extends StackWidget {
private final Button popButton;
public PopupWidget() {
super("bg-popup");
popButton = new Button("X");
popButton.setOnAction(_ -> hide());
add(Pos.TOP_RIGHT, popButton);
}
protected void setOnPop(Runnable onPop) {
popButton.setOnAction(_ -> onPop.run());
}
}

View File

@@ -0,0 +1,47 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Widget;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
public abstract class StackWidget implements Widget {
private final StackPane container;
public StackWidget(String cssClass) {
container = new StackPane();
container.getStyleClass().add(cssClass);
}
public void add(Pos position, Node node) {
Platform.runLater(() -> {
if (container.getChildren().contains(node)) {
return;
}
StackPane.setAlignment(node, position);
container.getChildren().add(node);
});
}
public void add(Pos position, Widget widget) {
add(position, widget.getNode());
}
public void remove(Node node) {
Platform.runLater(() -> {
container.getChildren().remove(node);
});
}
public void remove(Widget widget) {
remove(widget.getNode());
}
@Override
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,62 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.Widget;
import org.toop.local.AppContext;
import java.util.function.Consumer;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
public class ToggleWidget implements Widget {
private final Button button;
private final VBox container;
private final String onKey;
private final String offKey;
private boolean state;
public ToggleWidget(String onKey, String offKey, boolean initialState, Consumer<Boolean> onToggle) {
this.onKey = onKey;
this.offKey = offKey;
this.state = initialState;
button = new Button(AppContext.getString(getCurrentKey()));
button.setOnAction(_ -> {
state = !state;
updateText();
if (onToggle != null) {
onToggle.accept(state);
}
});
container = Primitive.vbox(button);
}
private String getCurrentKey() {
return state? offKey : onKey;
}
private void updateText() {
button.setText(AppContext.getString(getCurrentKey()));
}
public boolean getState() {
return state;
}
public void setState(boolean newState) {
if (state != newState) {
state = newState;
updateText();
}
}
@Override
public Node getNode() {
return container;
}
}

View File

@@ -0,0 +1,49 @@
package org.toop.app.widget.complex;
import org.toop.app.widget.Primitive;
import javafx.geometry.Pos;
public abstract class ViewWidget extends StackWidget {
private ViewWidget previous = null;
public ViewWidget() {
super("bg-primary");
}
public void transition(ViewWidget view) {
view.previous = this;
replace(Pos.CENTER, view);
}
public void transitionNext(ViewWidget view) {
view.previous = this;
replace(Pos.CENTER, view);
var backButton = Primitive.button("back", () -> {
view.transitionPrevious();
});
view.add(Pos.BOTTOM_LEFT, Primitive.vbox(backButton));
}
public void transitionPrevious() {
if (previous == null) {
return;
}
replace(Pos.CENTER, previous);
previous = null;
}
public void reload(ViewWidget view) {
view.previous = previous;
replace(Pos.CENTER, view);
var backButton = Primitive.button("back", () -> {
view.transitionPrevious();
});
view.add(Pos.BOTTOM_LEFT, Primitive.vbox(backButton));
}
}

View File

@@ -0,0 +1,139 @@
package org.toop.app.widget.display;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.util.Duration;
import org.toop.app.widget.Widget;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.eventbus.GlobalEventBus;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import java.util.Timer;
public class SongDisplay extends VBox implements Widget {
private final Text songTitle;
private final ProgressBar progressBar;
private final Text progressText;
private boolean canClick = true;
public SongDisplay() {
new EventFlow()
.listen(this::updateTheSong);
setAlignment(Pos.CENTER);
setMaxHeight(Region.USE_PREF_SIZE);
getStyleClass().add("song-display");
// TODO ADD GOOD SONG TITLES WITH ARTISTS DISPLAYED
songTitle = new Text("song playing");
songTitle.getStyleClass().add("song-title");
progressBar = new ProgressBar(0);
progressBar.getStyleClass().add("progress-bar");
progressText = new Text("0:00/0:00");
progressText.getStyleClass().add("progress-text");
// TODO ADD BETTER CSS FOR THE SKIPBUTTON WHERE ITS AT A NICER POSITION
Button skipButton = new Button(">>");
Button pauseButton = new Button("");
Button previousButton = new Button("<<");
skipButton.getStyleClass().setAll("skip-button");
pauseButton.getStyleClass().setAll("pause-button");
previousButton.getStyleClass().setAll("previous-button");
skipButton.setOnAction( event -> {
if (!canClick) { return; }
GlobalEventBus.post(new AudioEvents.SkipMusic());
doCooldown();
});
pauseButton.setOnAction(event -> {
if (!canClick) { return; }
GlobalEventBus.post(new AudioEvents.PauseMusic());
if (pauseButton.getText().equals("")) {
pauseButton.setText("");
}
else if (pauseButton.getText().equals("")) {
pauseButton.setText("");
}
doCooldown();
});
previousButton.setOnAction( event -> {
if (!canClick) { return; }
GlobalEventBus.post(new AudioEvents.PreviousMusic());
doCooldown();
});
HBox control = new HBox(10, previousButton, pauseButton, skipButton);
control.setAlignment(Pos.CENTER);
control.getStyleClass().add("controls");
getChildren().addAll(songTitle, progressBar, progressText, control);
}
private void updateTheSong(AudioEvents.PlayingMusic event) {
Platform.runLater(() -> {
String text = event.name();
text = text.substring(0, text.length() - 4);
songTitle.setText(text);
double currentPos = event.currentPosition();
double duration = event.duration();
if (currentPos / duration > 0.05) {
double progress = currentPos / duration;
progressBar.setProgress(progress);
}
else if (currentPos / duration < 0.05) {
progressBar.setProgress(0.05);
}
progressText.setText(getTimeString(event.currentPosition(), event.duration()));
});
}
private String getTimeString(long position, long duration) {
long positionMinutes = position / 60;
long durationMinutes = duration / 60;
long positionSeconds = position % 60;
long durationSeconds = duration % 60;
String positionSecondsStr = String.valueOf(positionSeconds);
String durationSecondsStr = String.valueOf(durationSeconds);
if (positionSeconds < 10) {
positionSecondsStr = "0" + positionSeconds;
}
if (durationSeconds < 10) {
durationSecondsStr = "0" + durationSeconds;
}
String time = positionMinutes + ":" + positionSecondsStr + " / " + durationMinutes + ":" + durationSecondsStr;
return time;
}
private void doCooldown() {
canClick = false;
Timeline cooldown = new Timeline(
new KeyFrame(Duration.millis(300), event -> canClick = true)
);
cooldown.setCycleCount(1);
cooldown.play();
}
@Override
public Node getNode() {
return this;
}
}

View File

@@ -0,0 +1,60 @@
package org.toop.app.widget.popup;
import org.toop.app.GameInformation;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.PlayerInfoWidget;
import org.toop.app.widget.complex.PopupWidget;
import java.util.function.Consumer;
import javafx.geometry.Pos;
public final class ChallengePopup extends PopupWidget {
private final GameInformation.Player playerInformation;
private final String challenger;
private final String game;
private final Consumer<GameInformation.Player> onAccept;
public ChallengePopup(String challenger, String game, Consumer<GameInformation.Player> onAccept) {
this.challenger = challenger;
this.game = game;
this.onAccept = onAccept;
this.playerInformation = new GameInformation.Player();
setupLayout();
}
private void setupLayout() {
var challengeText = Primitive.text("you-were-challenged-by");
var challengerHeader = Primitive.header("");
challengerHeader.setText(challenger);
var gameText = Primitive.text("to-a-game-of");
gameText.setText(gameText.getText() + " " + game);
var acceptButton = Primitive.button("accept", () -> onAccept.accept(playerInformation));
var denyButton = Primitive.button("deny", () -> hide());
var leftSection = Primitive.vbox(
challengeText,
challengerHeader,
gameText,
Primitive.separator(),
Primitive.hbox(
acceptButton,
denyButton
)
);
var playerInfoWidget = new PlayerInfoWidget(playerInformation);
add(Pos.CENTER,
Primitive.hbox(
leftSection,
playerInfoWidget.getNode()
)
);
}
}

View File

@@ -0,0 +1,16 @@
package org.toop.app.widget.popup;
import org.toop.app.widget.complex.ConfirmWidget;
import org.toop.app.widget.complex.PopupWidget;
import javafx.geometry.Pos;
public class ErrorPopup extends PopupWidget {
public ErrorPopup(String error) {
var confirmWidget = new ConfirmWidget("error");
confirmWidget.setMessage(error);
confirmWidget.addButton("ok", this::hide);
add(Pos.CENTER, confirmWidget);
}
}

View File

@@ -0,0 +1,25 @@
package org.toop.app.widget.popup;
import org.toop.app.widget.complex.ConfirmWidget;
import org.toop.app.widget.complex.PopupWidget;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
public final class GameOverPopup extends PopupWidget {
public GameOverPopup(boolean iWon, String winner) {
var confirmWidget = new ConfirmWidget("game-over");
if (winner.isEmpty()) {
confirmWidget.setMessage(AppContext.getString("the-game-ended-in-a-draw"));
} else if (iWon) {
confirmWidget.setMessage(AppContext.getString("you-win"));
} else {
confirmWidget.setMessage(AppContext.getString("you-lost-against") + ": " + winner);
}
confirmWidget.addButton("ok", () -> hide());
add(Pos.CENTER, confirmWidget);
}
}

View File

@@ -0,0 +1,29 @@
package org.toop.app.widget.popup;
import org.toop.app.App;
import org.toop.app.widget.complex.ConfirmWidget;
import org.toop.app.widget.complex.PopupWidget;
import javafx.geometry.Pos;
public class QuitPopup extends PopupWidget {
public QuitPopup() {
var confirmWidget = new ConfirmWidget("are-you-sure");
confirmWidget.addButton("yes", () -> {
App.quit();
});
confirmWidget.addButton("no", () -> {
App.stopQuit();
hide();
});
add(Pos.CENTER, confirmWidget);
setOnPop(() -> {
App.stopQuit();
hide();
});
}
}

View File

@@ -0,0 +1,90 @@
package org.toop.app.widget.popup;
import org.toop.app.GameInformation;
import org.toop.app.Server;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.LabeledChoiceWidget;
import org.toop.app.widget.complex.PlayerInfoWidget;
import org.toop.app.widget.complex.PopupWidget;
import org.toop.local.AppContext;
import java.util.function.BiConsumer;
import javafx.geometry.Pos;
import javafx.util.StringConverter;
public final class SendChallengePopup extends PopupWidget {
private final Server server;
private final String opponent;
private final BiConsumer<GameInformation.Player, String> onSend;
private final GameInformation.Player playerInformation;
public SendChallengePopup(Server server, String opponent, BiConsumer<GameInformation.Player, String> onSend) {
this.server = server;
this.opponent = opponent;
this.onSend = onSend;
this.playerInformation = new GameInformation.Player();
setupLayout();
}
private void setupLayout() {
// --- Left side: challenge text and buttons ---
var challengeText = Primitive.text("challenge");
var opponentHeader = Primitive.header(opponent);
var gameText = Primitive.text("to-a-game-of");
var games = server.getGameList();
var gameChoice = new LabeledChoiceWidget<>(
"game",
new StringConverter<>() {
@Override
public String toString(String game) {
return AppContext.getString(game);
}
@Override
public String fromString(String s) { return null; }
},
games.getFirst(),
newGame -> {
playerInformation.computerDifficulty = Math.min(
playerInformation.computerDifficulty,
Server.gameToType(newGame).getMaxDepth()
);
},
games.toArray(new String[0])
);
var sendButton = Primitive.button(
"send",
() -> onSend.accept(playerInformation, gameChoice.getValue())
);
var cancelButton = Primitive.button("cancel", () -> hide());
var leftSection = Primitive.vbox(
challengeText,
opponentHeader,
gameText,
gameChoice.getNode(),
Primitive.separator(),
Primitive.hbox(
sendButton,
cancelButton
)
);
var playerInfoWidget = new PlayerInfoWidget(playerInformation);
add(Pos.CENTER,
Primitive.hbox(
leftSection,
playerInfoWidget.getNode()
)
);
}
}

View File

@@ -0,0 +1,116 @@
package org.toop.app.widget.tutorial;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.text.Text;
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.Updatable;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.complex.PopupWidget;
import org.toop.framework.resource.resources.ImageAsset;
import org.toop.local.AppContext;
import java.util.List;
/**
* A widget base for all the tutorial widgets.
*
* <p>Usage example:
*
* <pre>{@code
* public class Connect4TutorialWidget extends BaseTutorialWidget {
* public Connect4TutorialWidget(Runnable nextScreen) {
* super(List.of(
* new ImmutablePair<>("connect4.1", ResourceManager.get("connect41.png")),
* new ImmutablePair<>("connect4.2", ResourceManager.get("connect42.png"))
* ), nextScreen);
* }
* }</pre>
*/
public class BaseTutorialWidget extends PopupWidget implements Updatable {
private final Text tutorialText;
private final ImageView imagery;
private final Button previousButton;
private final Button nextButton;
private final List<ImmutablePair<String, ImageAsset>> pages;
private final Runnable nextScreen;
private int pageIndex = 0;
public BaseTutorialWidget(List<ImmutablePair<String, ImageAsset>> pages, Runnable nextScreen) {
this.tutorialText = Primitive.text(pages.getFirst().getKey());
this.imagery = Primitive.image(pages.getFirst().getValue());
this.pages = pages;
this.nextScreen = nextScreen;
previousButton = Primitive.button("goback", () -> { update(false); this.hide(); });
nextButton = Primitive.button(">", () -> update(true));
var w = Primitive.hbox(
previousButton,
nextButton
);
var x = Primitive.vbox(imagery, tutorialText);
add(Pos.CENTER, Primitive.vbox(x, w));
WidgetContainer.add(Pos.CENTER, this);
}
@Override
public void update() {
update(true);
}
// TODO Refactor if statements to make code easier to read.
public void update(boolean next) {
pageIndex = next ? pageIndex + 1 : pageIndex - 1;
if (pageIndex >= pages.size()) {
pageIndex--;
return;
} else if (pageIndex < 0) {
pageIndex++;
return;
}
if (pageIndex == pages.size()-1) {
nextButton.textProperty().unbind();
nextButton.setText(AppContext.getString("startgame"));
nextButton.setOnAction((_) -> {
this.hide();
nextScreen.run();
});
} else {
nextButton.textProperty().unbind();
nextButton.setText(AppContext.getString(">"));
nextButton.setOnAction((_) -> this.update(true));
}
if (pageIndex == 0) {
previousButton.textProperty().unbind();
previousButton.setText(AppContext.getString("goback"));
previousButton.setOnAction((_) -> this.hide());
} else {
previousButton.textProperty().unbind();
previousButton.setText(AppContext.getString("<"));
previousButton.setOnAction((_) -> this.update(false));
}
var currentPage = pages.get(pageIndex);
var text = currentPage.getKey();
var image = currentPage.getValue();
tutorialText.textProperty().unbind();
tutorialText.setText(AppContext.getString(text));
imagery.setImage(Primitive.image(image).getImage());
}
}

View File

@@ -0,0 +1,15 @@
package org.toop.app.widget.tutorial;
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
import org.toop.framework.resource.ResourceManager;
import java.util.List;
public class Connect4TutorialWidget extends BaseTutorialWidget {
public Connect4TutorialWidget(Runnable nextScreen) {
super(List.of(
new ImmutablePair<>("connect4.1", ResourceManager.get("connect41.png")),
new ImmutablePair<>("connect4.2", ResourceManager.get("connect42.png"))
), nextScreen);
}
}

View File

@@ -0,0 +1,17 @@
package org.toop.app.widget.tutorial;
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
import org.toop.framework.resource.ResourceManager;
import java.util.List;
public class ReversiTutorialWidget extends BaseTutorialWidget {
public ReversiTutorialWidget(Runnable nextScreen) {
super(List.of(
new ImmutablePair<>("reversi1", ResourceManager.get("reversi1.png")),
new ImmutablePair<>("reversi2", ResourceManager.get("reversi2.png")),
new ImmutablePair<>("reversi3", ResourceManager.get("cat.jpg")),
new ImmutablePair<>("reversi4", ResourceManager.get("cat.jpg"))
), nextScreen);
}
}

View File

@@ -0,0 +1,22 @@
package org.toop.app.widget.tutorial;
import javafx.geometry.Pos;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.complex.PopupWidget;
import org.toop.local.AppSettings;
public class ShowEnableTutorialWidget extends PopupWidget {
public ShowEnableTutorialWidget(Runnable tutorial, Runnable nextScreen, Runnable appSettingsSetter) {
var a = Primitive.hbox(
Primitive.button("ok", () -> { appSettingsSetter.run(); tutorial.run(); this.hide(); }),
Primitive.button("no", () -> { appSettingsSetter.run(); nextScreen.run(); this.hide(); }),
Primitive.button("never", () -> { AppSettings.getSettings().setTutorialFlag(false); nextScreen.run(); this.hide(); })
);
var txt = Primitive.text("tutorial");
add(Pos.CENTER, Primitive.vbox(txt, a));
WidgetContainer.add(Pos.CENTER, this);
}
}

View File

@@ -0,0 +1,16 @@
package org.toop.app.widget.tutorial;
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
import org.toop.framework.resource.ResourceManager;
import java.util.List;
public class TicTacToeTutorialWidget extends BaseTutorialWidget {
public TicTacToeTutorialWidget(Runnable nextScreen) {
super(List.of(
new ImmutablePair<>("tictactoe1", ResourceManager.get("tictactoe1.png")),
new ImmutablePair<>("tictactoe2", ResourceManager.get("tictactoe2.png"))
), nextScreen);
}
}

View File

@@ -0,0 +1,81 @@
package org.toop.app.widget.view;
import org.toop.app.App;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.ViewWidget;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.geometry.Pos;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.text.Text;
import javafx.util.Duration;
public class CreditsView extends ViewWidget {
public CreditsView() {
var scrumMasterCredit = newCredit("scrum-master", "Stef");
var productOwnerCredit = newCredit("product-owner", "Omar");
var mergeCommanderCredit = newCredit("merge-commander", "Bas");
var localizationCredit = newCredit("localization", "Ticho");
var aiCredit = newCredit("ai", "Michiel");
var developersCredit = newCredit("developers", "Michiel, Bas, Stef, Omar, Ticho");
var moralSupportCredit = newCredit("moral-support", "Wesley");
var openglCredit = newCredit("opengl", "Omar");
var topSpacer = new Region();
topSpacer.setPrefHeight(App.getHeight());
var bottomSpacer = new Region();
bottomSpacer.setPrefHeight(App.getHeight());
var creditsContainer = Primitive.vbox(
topSpacer,
scrumMasterCredit,
productOwnerCredit,
mergeCommanderCredit,
localizationCredit,
aiCredit,
developersCredit,
moralSupportCredit,
openglCredit,
bottomSpacer
);
var creditsScroll = Primitive.scroll(creditsContainer);
creditsScroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
creditsScroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
add(Pos.CENTER, creditsScroll);
animate(creditsScroll, 15);
}
private HBox newCredit(String key, String other) {
var credit = new Text(": " + other);
credit.getStyleClass().add("header");
var creditBox = Primitive.hbox(
Primitive.header(key),
credit
);
creditBox.setPrefHeight(App.getHeight() / 3.0);
return creditBox;
}
private void animate(ScrollPane scroll, int length) {
final Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0), new KeyValue(scroll.vvalueProperty(), 0.0)),
new KeyFrame(Duration.seconds(length), new KeyValue(scroll.vvalueProperty(), 1.0))
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}

View File

@@ -0,0 +1,114 @@
package org.toop.app.widget.view;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.ViewWidget;
import org.toop.app.widget.popup.GameOverPopup;
import java.util.function.Consumer;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.text.Text;
import org.toop.app.widget.tutorial.BaseTutorialWidget;
import org.toop.app.widget.tutorial.Connect4TutorialWidget;
import org.toop.app.widget.tutorial.ReversiTutorialWidget;
import org.toop.app.widget.tutorial.TicTacToeTutorialWidget;
public final class GameView extends ViewWidget {
private final Text currentPlayerHeader;
private final Text currentMoveHeader;
private final Text nextPlayerHeader;
private final Button forfeitButton;
private final Button exitButton;
private final Button tutorialButton;
private final TextField chatInput;
public GameView(Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, String gameType) {
currentPlayerHeader = Primitive.header("");
currentMoveHeader = Primitive.header("");
nextPlayerHeader = Primitive.header("");
if (onForfeit != null) {
forfeitButton = Primitive.button("forfeit", () -> onForfeit.run());
} else {
forfeitButton = null;
}
exitButton = Primitive.button("exit", () -> {
onExit.run();
transitionPrevious();
});
if (onMessage != null) {
chatInput = Primitive.input("enter-your-message", "", null);
chatInput.setOnAction(_ -> {
onMessage.accept(chatInput.getText());
chatInput.clear();
});
} else {
chatInput = null;
}
switch(gameType) {
case "TicTacToe":
this.tutorialButton = Primitive.button("tutorialstring", () -> new TicTacToeTutorialWidget(() -> {})); break;
case "Reversi":
this.tutorialButton = Primitive.button("tutorialstring", () -> new ReversiTutorialWidget(() -> {})); break;
case "Connect4":
this.tutorialButton = Primitive.button("tutorialstring", () -> new Connect4TutorialWidget(() -> {})); break;
default:
this.tutorialButton = null; break;
}
setupLayout();
}
private void setupLayout() {
var playerInfo = Primitive.vbox(
currentPlayerHeader,
Primitive.hbox(
Primitive.separator(),
currentMoveHeader,
Primitive.separator()
),
nextPlayerHeader
);
add(Pos.TOP_RIGHT, playerInfo);
var buttons = Primitive.vbox(
forfeitButton,
exitButton
);
add(Pos.BOTTOM_LEFT, buttons);
if (chatInput != null) {
add(Pos.BOTTOM_RIGHT, Primitive.vbox(chatInput));
}
if (tutorialButton != null) {
add(Pos.TOP_LEFT, tutorialButton);
}
}
public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) {
Platform.runLater(() -> {
currentPlayerHeader.setText(currentPlayer);
currentMoveHeader.setText(currentMove);
nextPlayerHeader.setText(nextPlayer);
if (isMe) {
currentPlayerHeader.getStyleClass().add("my-turn");
} else {
currentPlayerHeader.getStyleClass().remove("my-turn");
}
});
}
public void gameOver(boolean iWon, String winner) {
new GameOverPopup(iWon, winner).show(Pos.CENTER);
}
}

View File

@@ -0,0 +1,107 @@
package org.toop.app.widget.view;
import javafx.application.Platform;
import org.toop.app.GameInformation;
import org.toop.app.game.Connect4Game;
import org.toop.app.game.ReversiGame;
import org.toop.app.game.TicTacToeGameThread;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.complex.PlayerInfoWidget;
import org.toop.app.widget.complex.ViewWidget;
import org.toop.app.widget.popup.ErrorPopup;
import org.toop.app.widget.tutorial.*;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import org.toop.local.AppSettings;
public class LocalMultiplayerView extends ViewWidget {
private final GameInformation information;
public LocalMultiplayerView(GameInformation.Type type) {
this(new GameInformation(type));
}
public LocalMultiplayerView(GameInformation information) {
this.information = information;
var playButton = Primitive.button("play", () -> {
for (var player : information.players) {
if (player.isHuman && player.name.isEmpty()) {
new ErrorPopup(AppContext.getString("please-enter-your-name")).show(Pos.CENTER);
return;
}
}
switch (information.type) {
case TICTACTOE:
if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstTTT()) {
new ShowEnableTutorialWidget(
() -> new TicTacToeTutorialWidget(() -> new TicTacToeGameThread(information)),
() -> Platform.runLater(() -> new TicTacToeGameThread(information)),
() -> AppSettings.getSettings().setFirstTTT(false)
);
} else {
new TicTacToeGameThread(information);
}
break;
case REVERSI:
if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstReversi()) {
new ShowEnableTutorialWidget(
() -> new ReversiTutorialWidget(() -> new ReversiGame(information)),
() -> Platform.runLater(() -> new ReversiGame(information)),
() -> AppSettings.getSettings().setFirstReversi(false)
);
} else {
new ReversiGame(information);
}
break;
case CONNECT4:
if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstConnect4()) {
new ShowEnableTutorialWidget(
() -> new Connect4TutorialWidget(() -> new Connect4Game(information)),
() -> Platform.runLater(() -> new Connect4Game(information)),
() -> AppSettings.getSettings().setFirstConnect4(false)
);
} else {
new Connect4Game(information);
}
break;
}
// case BATTLESHIP -> new BattleshipGame(information);
});
var playerSection = setupPlayerSections();
add(Pos.CENTER, Primitive.vbox(
playerSection,
Primitive.separator(),
playButton
));
}
private ScrollPane setupPlayerSections() {
int playerCount = information.type.getPlayerCount();
VBox[] playerBoxes = new VBox[playerCount];
for (int i = 0; i < playerCount; i++) {
var player = information.players[i];
var playerHeader = Primitive.header("");
playerHeader.setText("player" + " #" + (i + 1));
var playerWidget = new PlayerInfoWidget(player);
playerBoxes[i] = Primitive.vbox(
playerHeader,
Primitive.separator(),
playerWidget.getNode()
);
}
return Primitive.scroll(Primitive.hbox(
playerBoxes
));
}
}

View File

@@ -0,0 +1,29 @@
package org.toop.app.widget.view;
import org.toop.app.GameInformation;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.ViewWidget;
import javafx.geometry.Pos;
public class LocalView extends ViewWidget {
public LocalView() {
var ticTacToeButton = Primitive.button("tic-tac-toe", () -> {
transitionNext(new LocalMultiplayerView(GameInformation.Type.TICTACTOE));
});
var reversiButton = Primitive.button("reversi", () -> {
transitionNext(new LocalMultiplayerView(GameInformation.Type.REVERSI));
});
var connect4Button = Primitive.button("connect4", () -> {
transitionNext(new LocalMultiplayerView(GameInformation.Type.CONNECT4));
});
add(Pos.CENTER, Primitive.vbox(
ticTacToeButton,
reversiButton,
connect4Button
));
}
}

View File

@@ -0,0 +1,38 @@
package org.toop.app.widget.view;
import org.toop.app.App;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.ViewWidget;
import javafx.geometry.Pos;
public class MainView extends ViewWidget {
public MainView() {
var localButton = Primitive.button("local", () -> {
transitionNext(new LocalView());
});
var onlineButton = Primitive.button("online", () -> {
transitionNext(new OnlineView());
});
var creditsButton = Primitive.button("credits", () -> {
transitionNext(new CreditsView());
});
var optionsButton = Primitive.button("options", () -> {
transitionNext(new OptionsView());
});
var quitButton = Primitive.button("quit", () -> {
App.startQuit();
});
add(Pos.CENTER, Primitive.vbox(
localButton,
onlineButton,
creditsButton,
optionsButton,
quitButton
));
}
}

View File

@@ -0,0 +1,38 @@
package org.toop.app.widget.view;
import org.toop.app.Server;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.LabeledInputWidget;
import org.toop.app.widget.complex.ViewWidget;
import javafx.geometry.Pos;
public class OnlineView extends ViewWidget {
public OnlineView() {
var serverInformationHeader = Primitive.header("server-information");
var serverIPInput = new LabeledInputWidget("ip-address", "enter-the-server-ip", "", _ -> {});
var serverPortInput = new LabeledInputWidget("port", "enter-the-server-port", "", _ -> {});
var playerNameInput = new LabeledInputWidget("player-name", "enter-your-name", "", _ -> {});
var connectButton = Primitive.button("connect", () -> {
new Server(
serverIPInput.getValue(),
serverPortInput.getValue(),
playerNameInput.getValue()
);
});
add(Pos.CENTER, Primitive.vbox(
serverInformationHeader,
Primitive.separator(),
serverIPInput.getNode(),
serverPortInput.getNode(),
playerNameInput.getNode(),
Primitive.separator(),
connectButton
));
}
}

View File

@@ -0,0 +1,161 @@
package org.toop.app.widget.view;
import org.toop.app.App;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.LabeledChoiceWidget;
import org.toop.app.widget.complex.LabeledSliderWidget;
import org.toop.app.widget.complex.ViewWidget;
import org.toop.app.widget.complex.ToggleWidget;
import org.toop.framework.audio.VolumeControl;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
import org.toop.local.AppSettings;
import java.util.Locale;
import javafx.geometry.Pos;
import javafx.scene.layout.VBox;
import javafx.util.StringConverter;
public class OptionsView extends ViewWidget {
public OptionsView() {
add(Pos.CENTER, Primitive.hbox(
generalSection(),
volumeSection(),
styleSection()
));
}
private VBox generalSection() {
var languageWidget = new LabeledChoiceWidget<>(
"language",
new StringConverter<>() {
@Override
public String toString(Locale locale) {
return AppContext.getString(locale.getDisplayName().toLowerCase());
}
@Override
public Locale fromString(String s) { return null; }
},
AppContext.getLocale(),
newLocale -> {
AppSettings.getSettings().setLocale(newLocale.toString());
AppContext.setLocale(newLocale);
reload(new OptionsView());
},
AppContext.getLocalization().getAvailableLocales().toArray(new Locale[0])
);
var fullscreenToggle = new ToggleWidget(
"fullscreen", "windowed",
AppSettings.getSettings().getFullscreen(),
fullscreen -> {
AppSettings.getSettings().setFullscreen(fullscreen);
App.setFullscreen(fullscreen);
}
);
return Primitive.vbox(
Primitive.header("general"),
Primitive.separator(),
languageWidget.getNode(),
fullscreenToggle.getNode()
);
}
private VBox volumeSection() {
var masterVolumeWidget = new LabeledSliderWidget(
"master-volume",
0, 100,
AppSettings.getSettings().getVolume(),
val -> {
AppSettings.getSettings().setVolume(val);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(val, VolumeControl.MASTERVOLUME))
.asyncPostEvent();
}
);
var effectsVolumeWidget = new LabeledSliderWidget(
"effects-volume",
0, 100,
AppSettings.getSettings().getFxVolume(),
val -> {
AppSettings.getSettings().setFxVolume(val);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(val, VolumeControl.FX))
.asyncPostEvent();
}
);
var musicVolumeWidget = new LabeledSliderWidget(
"music-volume",
0, 100,
AppSettings.getSettings().getMusicVolume(),
val -> {
AppSettings.getSettings().setMusicVolume(val);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(val, VolumeControl.MUSIC))
.asyncPostEvent();
}
);
return Primitive.vbox(
Primitive.header("volume"),
Primitive.separator(),
masterVolumeWidget.getNode(),
effectsVolumeWidget.getNode(),
musicVolumeWidget.getNode()
);
}
private VBox styleSection() {
var themeWidget = new LabeledChoiceWidget<>(
"theme",
new StringConverter<>() {
@Override
public String toString(String theme) {
return AppContext.getString(theme);
}
@Override
public String fromString(String s) { return null; }
},
AppSettings.getSettings().getTheme(),
newTheme -> {
AppSettings.getSettings().setTheme(newTheme);
App.setStyle(newTheme, AppSettings.getSettings().getLayoutSize());
},
"dark", "light", "high-contrast"
);
var layoutWidget = new LabeledChoiceWidget<>(
"layout-size",
new StringConverter<>() {
@Override
public String toString(String layout) {
return AppContext.getString(layout);
}
@Override
public String fromString(String s) { return null; }
},
AppSettings.getSettings().getLayoutSize(),
newLayout -> {
AppSettings.getSettings().setLayoutSize(newLayout);
App.setStyle(AppSettings.getSettings().getTheme(), newLayout);
},
"small", "medium", "large"
);
return Primitive.vbox(
Primitive.header("style"),
Primitive.separator(),
themeWidget.getNode(),
layoutWidget.getNode()
);
}
}

View File

@@ -0,0 +1,60 @@
package org.toop.app.widget.view;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.ViewWidget;
import java.util.List;
import java.util.function.Consumer;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
public final class ServerView extends ViewWidget {
private final String user;
private final Consumer<String> onPlayerClicked;
private final Runnable onDisconnect;
private final ListView<Button> listView;
public ServerView(String user, Consumer<String> onPlayerClicked, Runnable onDisconnect) {
this.user = user;
this.onPlayerClicked = onPlayerClicked;
this.onDisconnect = onDisconnect;
this.listView = new ListView<>();
setupLayout();
}
private void setupLayout() {
var playerHeader = Primitive.header(user);
var playerListSection = Primitive.vbox(
playerHeader,
Primitive.separator(),
listView
);
add(Pos.CENTER, playerListSection);
var disconnectButton = Primitive.button("disconnect", () -> {
onDisconnect.run();
transitionPrevious();
});
add(Pos.BOTTOM_LEFT, Primitive.vbox(disconnectButton));
}
public void update(List<String> players) {
Platform.runLater(() -> {
listView.getItems().clear();
for (String player : players) {
var playerButton = Primitive.button(player, () -> onPlayerClicked.accept(player));
listView.getItems().add(playerButton);
}
});
}
}

View File

@@ -1,19 +1,34 @@
package org.toop.local;
import java.util.Locale;
import org.toop.framework.asset.ResourceManager;
import org.toop.framework.asset.resources.LocalizationAsset;
import java.util.MissingResourceException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.resource.ResourceManager;
import org.toop.framework.resource.resources.LocalizationAsset;
import java.util.Locale;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class AppContext {
private static final LocalizationAsset localization = ResourceManager.get("localization");
private static Locale locale = Locale.forLanguageTag("en");
private static final ObjectProperty<Locale> localeProperty = new SimpleObjectProperty<>(locale);
private static final Logger logger = LogManager.getLogger(AppContext.class);
public static LocalizationAsset getLocalization() {
return localization;
}
public static void setLocale(Locale locale) {
AppContext.locale = locale;
localeProperty.set(locale);
}
public static Locale getLocale() {
@@ -22,6 +37,31 @@ public class AppContext {
public static String getString(String key) {
assert localization != null;
return localization.getString(key, locale);
// TODO: Gebruik ResourceBundle.getBundle() zodat de fallback automatisch gaat.
// Hiervoor zou de assetManager aangepast moeten worden.
try{ // Main return
return localization.getString(key, locale);
}
catch (MissingResourceException e) {
logger.error("Missing resource key: {}, in bundle: {}. ", key, locale, e);
}
try{ // Fallback return
return localization.getString(key, localization.getFallback());
}
catch (MissingResourceException e) {
logger.error("Missing resource key: {}, in default bundle!", key, e);
}
// Default return
return "MISSING RESOURCE";
}
public static StringBinding bindToKey(String key) {
return Bindings.createStringBinding(
() -> localization.getString(key, locale),
localeProperty
);
}
}

View File

@@ -1,58 +1,108 @@
package org.toop.local;
import org.toop.app.App;
import org.toop.framework.audio.VolumeControl;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.resource.ResourceManager;
import org.toop.framework.resource.ResourceMeta;
import org.toop.framework.resource.resources.SettingsAsset;
import org.toop.framework.settings.Settings;
import java.io.File;
import java.util.Locale;
import org.toop.app.App;
import org.toop.framework.asset.resources.SettingsAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.settings.Settings;
public class AppSettings {
private static SettingsAsset settingsAsset;
private SettingsAsset settingsAsset;
public static void applySettings() {
settingsAsset = getPath();
if (!settingsAsset.isLoaded()) {
settingsAsset.load();
}
public void applySettings() {
SettingsAsset settings = getPath();
if (!settings.isLoaded()) {
settings.load();
checkSettings();
Settings settingsData = settingsAsset.getContent();
AppContext.setLocale(Locale.of(settingsData.locale));
App.setFullscreen(settingsData.fullScreen);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(settingsData.volume, VolumeControl.MASTERVOLUME))
.asyncPostEvent();
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(settingsData.fxVolume, VolumeControl.FX))
.asyncPostEvent();
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(settingsData.musicVolume, VolumeControl.MUSIC))
.asyncPostEvent();
App.setStyle(settingsAsset.getTheme(), settingsAsset.getLayoutSize());
}
public static SettingsAsset getPath() {
if (settingsAsset == null) {
String os = System.getProperty("os.name").toLowerCase();
String basePath;
if (os.contains("win")) {
basePath = System.getenv("APPDATA");
if (basePath == null) {
basePath = System.getProperty("user.home");
}
} else if (os.contains("mac")) {
basePath = System.getProperty("user.home") + "/Library/Application Support";
} else {
basePath = System.getProperty("user.home") + "/.config";
}
File settingsFile =
new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
// this.settingsAsset = new SettingsAsset(settingsFile);
ResourceManager.addAsset(new ResourceMeta<>("settings.json", new SettingsAsset(settingsFile)));
}
return ResourceManager.get("settings.json");
}
public static SettingsAsset getSettings() {
return settingsAsset;
}
public static void checkSettings() {
Settings s = settingsAsset.getContent();
boolean changed = false;
if (s.showTutorials == null) {
settingsAsset.setTutorialFlag(true);
changed = true;
}
if (s.firstReversi == null) {
settingsAsset.setFirstReversi(true);
changed = true;
}
if (s.firstTTT == null) {
settingsAsset.setFirstTTT(true);
changed = true;
}
if (s.firstConnect4 == null) {
settingsAsset.setFirstConnect4(true);
changed = true;
}
if (changed) {
getSettings().save();
}
Settings settingsData = settings.getContent();
AppContext.setLocale(Locale.of(settingsData.locale));
App.setFullscreen(settingsData.fullScreen);
new EventFlow()
.addPostEvent(new AudioEvents.ChangeVolume(settingsData.volume))
.asyncPostEvent();
new EventFlow()
.addPostEvent(new AudioEvents.ChangeFxVolume(settingsData.fxVolume))
.asyncPostEvent();
new EventFlow()
.addPostEvent(new AudioEvents.ChangeMusicVolume(settingsData.musicVolume))
.asyncPostEvent();
App.setStyle(settingsAsset.getTheme(), settingsAsset.getLayoutSize());
}
public SettingsAsset getPath() {
if (this.settingsAsset == null) {
String os = System.getProperty("os.name").toLowerCase();
String basePath;
if (os.contains("win")) {
basePath = System.getenv("APPDATA");
if (basePath == null) {
basePath = System.getProperty("user.home");
}
} else if (os.contains("mac")) {
basePath = System.getProperty("user.home") + "/Library/Application Support";
} else {
basePath = System.getProperty("user.home") + "/.config";
}
File settingsFile =
new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
this.settingsAsset = new SettingsAsset(settingsFile);
}
return this.settingsAsset;
public static void doDefaultSettings() {
settingsAsset.setFirstConnect4(true);
settingsAsset.setFirstTTT(true);
settingsAsset.setFirstReversi(true);
settingsAsset.setLocale("en");
settingsAsset.setTheme("dark");
settingsAsset.setFullscreen(false);
settingsAsset.setVolume(100);
settingsAsset.setFxVolume(20);
settingsAsset.setMusicVolume(15);
settingsAsset.setTutorialFlag(true);
settingsAsset.setLayoutSize("medium");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,52 +1,88 @@
accept=\u0642\u0628\u0648\u0644
ai=\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0635\u0646\u0627\u0639\u064a
appTitle=\u0645\u062e\u062a\u0627\u0631 \u0623\u0644\u0639\u0627\u0628 ISY
back=\u0631\u062c\u0648\u0639
backToMainMenu=\u0627\u0644\u0639\u0648\u062F\u0629 \u0625\u0644\u0649 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629
computer=\u0627\u0644\u062d\u0627\u0633\u0648\u0628
computerDifficulty=\u0635\u0639\u0648\u0628\u0629 \u0627\u0644\u062d\u0627\u0633\u0648\u0628
computerThinkTime=\u0632\u0645\u0646 \u062a\u0641\u0643\u064a\u0631 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631
congratulations=\u0645\u0628\u0631\u0648\u0643
connect=\u0627\u062a\u0635\u0644
connectionType=\u0646\u0648\u0639 \u0627\u0644\u0627\u062A\u0635\u0627\u0644
credits=\u0627\u0644\u0634\u0643\u0631 \u0648\u0627\u0644\u062a\u0642\u062f\u064a\u0631
dark-hc=\u063A\u0627\u0645\u0642 (\u062A\u0646\u0627\u0642\u0636 \u0639\u0627\u0644\u064D)
dark=\u063A\u0627\u0645\u0642
app-title=\u0645\u062e\u062a\u0627\u0631 \u0627\u0644\u0643\u0627\u0645 \u0627\u0644\u0645\u062e\u062a\u0627\u0631
are-you-sure=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f?
back=\u0627\u0644\u0631\u062c\u0648\u0639
cancel=\u0625\u0644\u063a\u0627\u0621
challenge=\u062a\u062d\u062f\u064a
connect4=Connect 4
computer-difficulty=\u0635\u0639\u0648\u0628 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631
computer-think-time=\u0648\u0642\u062a \u062a\u0641\u0643\u064a\u0631 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631
computer=\u0643\u0645\u0628\u064a\u0648\u062a\u0631
connect=\u0627\u062a\u0635\u0627\u0644
credits=\u0627\u0644\u0645\u0633\u0627\u0647\u0645\u0629
dark=\u0638\u0644\u0627\u0645
deny=\u0631\u0641\u0636
developers=\u0627\u0644\u0645\u0637\u0648\u0631\u0648\u0646
drawText=\u0627\u0646\u062A\u0647\u062A \u0627\u0644\u0644\u0639\u0628\u0629 \u0628\u062A\u0639\u0627\u062F\u0644
effectsVolume=\u062d\u062c\u0645 \u0627\u0644\u062a\u0623\u062b\u064a\u0631\u0627\u062a
musicVolume=Music Volume (translate me)
disconnect=\u0641\u0635\u0644 \u0627\u0644\u0627\u062a\u0635\u0627\u0644
effects-volume=\u0635\u0648\u062a \u0627\u0644\u062a\u0623\u062b\u064a\u0631\u0627\u062a
enter-the-server-ip=\u0623\u062f\u062e\u0644 \u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0633\u064a\u0631\u0641\u0631
enter-the-server-port=\u0623\u062f\u062e\u0644 \u0645\u0646\u0641\u0630 \u0627\u0644\u0633\u064a\u0631\u0641\u0631
enter-your-name=\u0623\u062f\u062e\u0644 \u0627\u0633\u0645\u0643
error=\u062e\u0637\u0623
exit=\u062e\u0631\u0648\u062c
forfeit=\u0627\u0633\u062a\u0643\u0644\u0627\u0644
fullscreen=\u0643\u0627\u0645\u0644 \u0627\u0644\u0634\u0627\u0634\u0629
goodGameText=\u0644\u0639\u0628\u0629 \u0631\u0627\u0626\u0639\u0629. \u0623\u062D\u0633\u0646\u062A.
human=\u0627\u0644\u0625\u0646\u0633\u0627\u0646
language=\u0627\u0644\u0644\u063a\u0629
large=\u0643\u0628\u064A\u0631
layoutSize=\u062D\u062C\u0645 \u0627\u0644\u062A\u0635\u0645\u064A\u0645
light-hc=\u0641\u0627\u062A\u062D (\u062A\u0646\u0627\u0642\u0636 \u0639\u0627\u0644\u064D)
light=\u0641\u0627\u062A\u062D
game-over=\u0627\u0646\u062a\u0647\u062a \u0627\u0644\u0644\u0639\u0628\u0629
general=\u0639\u0627\u0645
high-contrast=\u062A\u0628\u0627\u0646\u064A\u0629 \u0639\u0627\u0644\u064A
invalid-username=\u0627\u0633\u0645 \u063a\u064a\u0631 \u0635\u062d\u064a\u062d
ip-address=\u0639\u0646\u0648\u0627\u0646 IP
is-not-a-valid-ip-address=\u0644\u064a\u0633 \u0639\u0646\u0648\u0627\u0646 IP \u0635\u062d\u064a\u062d
is-not-a-valid-port=\u0644\u064a\u0633 \u0645\u0646\u0641\u0630 \u0635\u062d\u064a\u062d
language=\u0644\u063a\u0629
large=\u0633\u064a\u0631
layout-size=\u062d\u062c\u0645 \u0627\u0644\u062a\u0635\u0645\u064a\u0645
light=\u0636\u0624\u0621
local=\u0645\u062d\u0644\u064a
localization=\u062a\u0648\u0645\u064a\u0645 \u0627\u0644\u0644\u063a\u0629
medium=\u0645\u062A\u0648\u0633\u0637
mergeCommander=\u0642\u0627\u0626\u062f \u0627\u0644\u062f\u0645\u062c
moralSupport=\u062f\u0639\u0645 \u0645\u0639\u0646\u0648\u064a
localization=\u062a\u0639\u0631\u064a\u0628 \u0627\u0644\u0644\u063a\u0629
master-volume=\u0635\u0648\u062a \u0627\u0644\u0645\u0633\u062a\u0648\u0649
medium=\u0645\u062a\u0648\u0633\u0637
merge-commander=Merge Commander
moral-support=\u062f\u0639\u0645 \u0623\u0639\u0635\u0627\u0628\u064a
music-volume=\u0635\u0648\u062a \u0627\u0644\u0645\u0648\u0633\u064a\u0642\u0649
name=\u0627\u0633\u0645
never=\u0644\u0627\u060c \u0644\u0627 \u0623\u0631\u064A\u062F \u0623\u0646 \u0623\u0631\u0649 \u0623\u064A \u062F\u0631\u0648\u0633 \u0639\u0644\u0649 \u0627\u0644\u0625\u0637\u0644\u0627\u0642.
no=\u0644\u0627
ok=\u0645\u0648\u0627\u0641\u0642
online=\u0645\u062a\u0635\u0644
opengl=OpenGL
options=\u0627\u0644\u062e\u064a\u0627\u0631\u0627\u062a
othello=\u0623\u0648\u062a\u064a\u0644\u0648
playerName=\u0627\u0633\u0645 \u0627\u0644\u0644\u0627\u0639\u0628
productOwner=\u0645\u0627\u0644\u0643 \u0627\u0644\u0645\u0646\u062a\u062c
options=\u062e\u064a\u0627\u0631\u0627\u062a
play=\u0644\u0639\u0628
player-name=\u0627\u0633\u0645 \u0627\u0644\u0644\u0627\u0639\u0628
player=\u0644\u0627\u0639\u0628
please-enter-your-name=\u064a\u0631\u062c\u0649 \u0623\u062f\u062e\u0644 \u0627\u0633\u0645\u0643
port=\u0645\u0646\u0641\u0630
product-owner=\u0635\u0627\u062d\u0628 \u0627\u0644\u0645\u0646\u062a\u062c
quit=\u062e\u0631\u0648\u062c
quitSure=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f\u061f
scrumMaster=\u0645\u062f\u064a\u0631 \u0627\u0644\u0633\u0643\u0631\u0645
server=\u062e\u0627\u062f\u0645
serverIP=IP \u0627\u0644\u062e\u0627\u062f\u0645
serverPort=\u0645\u0646\u0641\u0630 \u0627\u0644\u062e\u0627\u062f\u0645
small=\u0635\u063A\u064A\u0631
start=\u0627\u0628\u062f\u0623
theme=\u0627\u0644\u0645\u0648\u0636\u0648\u0639
tictactoe=\u062a\u064a\u0643 \u062a\u0627\u0643 \u062a\u0648
volume=\u0627\u0644\u062d\u062c\u0645 \u0627\u0644\u0631\u0626\u064a\u0633\u064a
windowed=\u0646\u0627\u0641\u0630\u064a
reversi=\u0631\u064a\u0641\u0631\u0633\u064a
scrum-master=Scrum Master
send=\u0625\u0631\u0633\u0627\u0644
server-information=\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0627\u0644\u0633\u064a\u0631\u0641\u0631
small=\u0635\u063a\u064a\u0631
style=\u0623\u0633\u0644\u0648\u0628
the-game-ended-in-a-draw=\u0627\u0646\u062a\u0647\u062a \u0627\u0644\u0644\u0639\u0628\u0629 \u0628\u062a\u0639\u0627\u062f\u0644
theme=\u0645\u0648\u0636\u0648\u0639
tic-tac-toe=\u062a\u064a\u0643 \u062a\u0627\u0643 \u062a\u0648
tictactoe1 =\u0645\u0631\u062d\u0628\u064b\u0627 \u0628\u0643 \u0641\u064a \u0644\u0639\u0628\u0629 \u0625\u0643\u0633-\u0623\u0648! \u064a\u0645\u0643\u0646\u0643 \u0627\u0644\u0642\u064a\u0627\u0645 \u0628\u062d\u0631\u0643\u062a\u0643 \u0628\u0627\u0644\u0646\u0642\u0631 \u0639\u0644\u0649 \u0623\u064a \u0645\u0646 \u0627\u0644\u0645\u0631\u0628\u0639\u0627\u062a \u0627\u0644\u062a\u0633\u0639\u0629 \u0639\u0644\u0649 \u0627\u0644\u0644\u0648\u062d\u0629. \u0627\u0644\u0645\u062b\u0627\u0644 \u0623\u0639\u0644\u0627\u0647:
tictactoe2 =\u064a\u0641\u0648\u0632 \u0627\u0644\u0644\u0627\u0639\u0628 \u0639\u0646\u062f\u0645\u0627 \u064a\u062d\u0635\u0644 \u0639\u0644\u0649 \u062b\u0644\u0627\u062b \u0642\u0637\u0639 \u0645\u062a\u062a\u0627\u0644\u064a\u0629 ? \u0623\u0641\u0642\u064a\u064b\u0627 \u0623\u0648 \u0639\u0645\u0648\u062f\u064a\u064b\u0627 \u0623\u0648 \u0642\u0637\u0631\u064a\u064b\u0627. \u0641\u064a \u0627\u0644\u0645\u062b\u0627\u0644 \u0623\u0639\u0644\u0627\u0647\u060c \u064a\u0641\u0648\u0632 \u0627\u0644\u0644\u0627\u0639\u0628 \u0628\u0635\u0641 \u0642\u0637\u0631\u064a \u0645\u0646 \u062b\u0644\u0627\u062b \u0642\u0637\u0639
to-a-game-of=\u0625\u0644\u0649 \u0644\u0639\u0628\u0629 \u0645\u0646
tutorial=\u0647\u0644 \u062a\u0631\u064a\u062f \u0645\u0634\u0627\u0647\u062f\u0629 \u062f\u0644\u064a\u0644 \u0644\u0647\u0630\u0627 \u0627\u0644\u0644\u0639\u0628\u0629\u061f
volume=\u0635\u0648\u062a
windowed=\u0641\u064a \u0646\u0641\u0630\u0629
yes=\u0646\u0639\u0645
you-lost-against=\u0623\u0646\u062a \u062e\u0633\u0631\u062a \u0636\u062f
you-were-challenged-by=\u062a\u062d\u062f\u064a\u062a \u0645\u0646
you-win=\u0623\u0646\u062a \u062a\u0641\u0648\u0632
>=>
<=<
connect4.1=\u0645\u0631\u062d\u0628\u0627 \u0628\u0643 \u0641\u064a \u0644\u0639\u0628\u0629 Connect 4! \u064a\u0645\u0643\u0646\u0643 \u062a\u0633\u0645\u064a\u0629 \u0627\u0644\u0644\u0639\u0628 \u0645\u0646 \u0637\u0631\u0641 \u0645\u0646 \u0627\u0644\u0639\u0645\u0648\u062f\u0627\u062a. \u0633\u064a\u062a\u0645 \u0648\u0636\u0639 \u0627\u0644\u0644\u062d\u0629 \u0627\u0644\u0623\u0642\u0644 \u0641\u064a \u062a\u0644\u0643 \u0627\u0644\u0639\u0645\u0648\u062f\u0629 \u0627\u0644\u0645\u0644\u0623\u0629 \u0627\u0644\u062a\u064a \u0644\u064a\u0633\u062a\u0645\u0644\u0623 \u0627\u0644\u0645\u0644\u0623\u0629.
connect4.2=\u064a\u0645\u0643\u0646\u0643 \u0627\u0644\u0641\u0648\u0632 \u0628\u0647\u0632\u0645 \u0623\u0631\u062c\u0639 \u0645\u0646 \u0627\u0644\u0642\u0637\u0639 \u0627\u0644\u0641\u064a \u0627\u0644\u0623\u0641\u0642 \u0623\u0648 \u0627\u0644\u0645\u064a\u0646 \u0623\u0648 \u0623\u0641\u0642 \u0639\u0644\u0649 \u0627\u0644\u0645\u0644\u0623\u0629!
reversi1=\u0645\u0631\u062d\u0628\u0627 \u0628\u0643 \u0641\u064a \u0644\u0639\u0628\u0629 Reversi! \u064a\u0645\u0643\u0646\u0643 \u0627\u0644\u0644\u0639\u0628 \u0628\u0627\u0644\u0646\u0642\u0631 \u0639\u0644\u0649 \u0623\u064a \u0646\u0642\u0637\u0629 \u0645\u0634\u0631\u0641\u0629.
reversi2=\u0639\u0646\u062f\u0645\u0627 \u062a\u0646\u0642\u0631 \u0639\u0644\u0649 \u0646\u0642\u0637\u0629 \u0633\u064a\u062a\u063a\u064a\u0631 \u062c\u0645\u064a\u0639 \u0627\u0644\u0644\u0648\u0639\u0627\u0628 \u0628\u064a\u0646 \u0645\u0643\u0627\u0646 \u062a\u0636\u0639 \u0627\u0644\u0646\u0642\u0637\u0629 \u0648\u0646\u0642\u0637\u0629 \u0627\u0644\u0644\u0648\u0639 \u0627\u0644\u062a\u0627\u0644\u064a \u0627\u0644\u0645\u0648\u062c\u0648\u062f.
reversi3=\u0645\u0631\u062a\u0643 \u0642\u062f \u064a\u062a\u063a\u0627\u0637 \u0625\u0630\u0627 \u0644\u0645 \u064a\u0643\u0646 \u0647\u0646\u0627\u0643 \u062d\u0631\u0643 \u0642\u0627\u0646\u0648\u0646\u064a.
reversi4=\u0627\u0644\u0644\u0627\u0639\u0628 \u0627\u0644\u0630\u064a \u064a\u0641\u0648\u0632 \u0641\u064a \u0646\u0647\u0627\u064a\u0629 \u0627\u0644\u0644\u0639\u0628 \u0647\u0648 \u0627\u0644\u0630\u064a \u064a\u0643\u0648\u0646 \u0644\u062f\u064a\u0647 \u0627\u0644\u0623\u0643\u062b\u0631 \u0645\u0646 \u0627\u0644\u0644\u0648\u0639\u0627\u0628 \u0639\u0644\u0649 \u0627\u0644\u0644\u0648\u062d\u0629.
tutorialstring=\u0627\u0644\u062f\u0631\u0633 \u0627\u0644\u062a\u0648\u0636\u064a\u062d\u064a
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629
chinese=\u4e2d\u6587 (\u0627\u0644\u0635\u064a\u0646\u064a\u0629)

View File

@@ -1,52 +1,90 @@
accept=Akzeptieren
ai=K\u00fcnstliche Intelligenz
appTitle=ISY Spieleauswahl
app-title=ISY Spiele-Auswahl
are-you-sure=Sind Sie sicher?
back=Zur\u00fcck
backToMainMenu=Zur\u00FCck zum Hauptmen\u00FC
cancel=Abbrechen
challenge=Herausforderung
computer-difficulty=Computer Schwierigkeit
computer-think-time=Computer Denkzeit
computer=Computer
computerDifficulty=Computerschwierigkeit
computerThinkTime=Computer Denkzeit
congratulations=Gl\u00FCckwunsch
connect=Verbinden
connectionType=Verbindungstyp
connect4=Connect 4
credits=Credits
dark-hc=Dunkel (Hoher Kontrast)
dark=Dunkel
deny=Ablehnen
developers=Entwickler
drawText=Das Spiel endete unentschieden
effectsVolume=Effektlautst\u00e4rke
musicVolume=Music Volume (translate me)
disconnect=Trennen
effects-volume=Effektlautst\u00e4rke
enter-the-server-ip=Server-IP eingeben
enter-the-server-port=Server-Port eingeben
enter-your-name=Geben Sie Ihren Namen ein
error=Fehler
exit=Beenden
forfeit=Aufgeben
fullscreen=Vollbild
goodGameText=Gutes Spiel. Gut gespielt.
human=Mensch
game-over=Spiel vorbei
general=Allgemein
high-contrast=Hoher Kontrast
invalid-username=Ung\u00fcltiger Benutzername
ip-address=IP-Adresse
is-not-a-valid-ip-address=ist keine g\u00fcltige IP-Adresse
is-not-a-valid-port=ist kein g\u00fcltiger Port
language=Sprache
large=Gro\u00DF
layoutSize=Layout-Gr\u00F6\u00DFe
light-hc=Hell (Hoher Kontrast)
large=Gro\u00df
layout-size=Layout-Gr\u00f6\u00dfe
light=Hell
local=Lokal
localization=Lokalisierung
master-volume=Gesamtlautst\u00e4rke
medium=Mittel
mergeCommander=Merge-Kommandant
moralSupport=Mentale Unterst\u00fctzung
merge-commander=Merge Commander
moral-support=Mentale Unterst\u00fctzung
music-volume=Musiklautst\u00e4rke
name=Name
never=Nein, ich m\u00f6chte niemals irgendwelche Tutorials sehen.
no=Nein
ok=Ok
online=Online
opengl=OpenGL
options=Optionen
othello=Othello
playerName=Spielername
productOwner=Produktverantwortlicher
play=Spielen
player-name=Spielername
player=Spieler
please-enter-your-name=Bitte geben Sie Ihren Namen ein
port=Port
product-owner=Produktverantwortlicher
quit=Beenden
quitSure=Bist du dir sicher?
scrumMaster=Scrum Master
server=Server
serverIP=Server-IP
serverPort=Server-Port
reversi=Reversi
scrum-master=Scrum Master
send=Senden
server-information=Serverinformationen
small=Klein
start=Start
style=Stil
the-game-ended-in-a-draw=Das Spiel endete unentschieden
theme=Thema
tictactoe=Tic Tac Toe
volume=Hauptlautst<EFBFBD>rke
tic-tac-toe=Tic Tac Toe
tictactoe1 =Willkommen beim Spiel Tic Tac Toe! Du kannst deinen Zug machen, indem du auf eines der 9 Felder auf dem Spielfeld klickst. Beispiel oben:
tictactoe2 =Ein Spieler gewinnt, wenn er drei Steine in einer Reihe hat ? horizontal, vertikal oder diagonal. Im obigen Beispiel gewinnt der Spieler mit einer diagonalen Reihe von drei Steinen.
to-a-game-of=zu einem Spiel von
tutorial=M\u00f6chtest du ein Tutorial f\u00fcr dieses Spiel sehen?
volume=Lautst\u00e4rke
windowed=Fenstermodus
yes=Ja
you-lost-against=Sie haben gegen ... verloren
you-were-challenged-by=Sie wurden herausgefordert von ...
you-win=Sie gewinnen
>=>
<=<
connect4.1=Willkommen beim Spiel Connect 4! Du kannst einen Zug machen, indem du auf eine der Spalten klickst. Der Zug wird in der niedrigsten noch freien Reihe dieser Spalte platziert.
connect4.2=Du kannst gewinnen, indem du 4 Spielsteine deiner Farbe horizontal, diagonal oder vertikal verbindest! Siehe das obige Beispiel.
reversi1=Willkommen beim Spiel Reversi! Du kannst einen Zug machen, indem du auf einen der leicht transparenten Punkte klickst.
reversi2=Wenn du auf einen Punkt klickst, werden alle Spielsteine dazwischen umgedreht, bis zum n<>chsten Punkt. Siehe das Beispiel oben, wo Gelb die zu drehenden Steine ist.
reversi3=Dein Zug kann <20>bersprungen werden, wenn es keinen legalen Zug gibt. Dein Gegner spielt dann weiter, bis du einen legalen Zug machen kannst.
reversi4=Der Spieler, der am Ende die meisten Steine auf dem Brett hat, gewinnt.
tutorialstring=Tutorial
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabisch)
chinese=\u4e2d\u6587 (Chinesisch)

View File

@@ -1,56 +1,92 @@
accept=Accept
ai=Artificial Intelligence
appTitle=ISY Games Selector
app-title=ISY Games Selector
are-you-sure=Are you sure?
back=Back
backToMainMenu=Back to main menu
challengeText=You were challenged by
cancel=Cancel
challenge=Challenge
computer-difficulty=Computer difficulty
computer-think-time=Computer think time
computer=Computer
computerDifficulty=Computer Difficulty
computerThinkTime=Computer Think Time
congratulations=Congratulations
connect=Connect
connectionType=Connection Type
credits=Credits
dark=Dark
dark-hc=Dark (High Contrast)
deny=Deny
developers=Developers
drawText=The game ended in a draw
effectsVolume=Effects Volume
musicVolume=Music Volume
disconnect=Disconnect
effects-volume=Effects Volume
enter-the-server-ip=Enter the server ip
enter-the-server-port=Enter the server port
enter-your-name=Enter your name
error=Error
exit=Exit
forfeit=Forfeit
fullscreen=Fullscreen
gameIsText=To a game of
goodGameText=Good game. Well played.
human=Human
game-over=Game Over
general=General
high-contrast=High contrast
invalid-username=invalid username
ip-address=IP address
is-not-a-valid-ip-address=is not a valid-ip address
is-not-a-valid-port=is not a valid port
language=Language
large=Large
layoutSize=Layout Size
layout-size=Layout Size
light=Light
light-hc=Light (High Contrast)
local=Local
localization=Localization
master-volume=Master Volume
medium=Medium
mergeCommander=Merge Commander
moralSupport=Moral Support
merge-commander=Merge Commander
moral-support=Moral Support
music-volume=Music Volume
name=Name
no=No
never=No, I never want to see any tutorials
ok=Ok
online=Online
opengl=OpenGL
options=Options
othello=Othello
playerName=Player Name
productOwner=Product Owner
play=Play
player-name=Player name
player=Player
please-enter-your-name=Please enter your name
port=Port
product-owner=Product Owner
quit=Quit
quitSure=Are you sure?
scrumMaster=Scrum Master
server=Server
serverIP=Server IP
serverPort=Server Port
reversi=Reversi
scrum-master=Scrum Master
send=Send
server-information=Server information
small=Small
start=Start
style=Style
the-game-ended-in-a-draw=The game ended in a draw
theme=Theme
tictactoe=Tic Tac Toe
volume=Master Volume
tic-tac-toe=Tic Tac Toe
tictactoe1 =Welcome to the game of Tic Tac Toe! You can make your move by clicking on any of the 9 squares on the board. Example above:
tictactoe2 =A player wins by getting 3 pieces in a row - horizontally, vertically or diagonally. In the example above, the player wins with a diagonal row of three pieces.
tutorial=Do you want a tutorial for this game?
connect4=Connect 4
to-a-game-of=to a game of
volume=Volume
windowed=Windowed
yes=Yes
you-lost-against=You lost against
you-were-challenged-by=You were challenged by
you-win=You win
>=>
<=<
// tutorial
connect4.1=Welcome to the game of Connect 4! You can make a move by clicking one of the columns. The move will be placed in the lowest row of that column that is not filled yet.
connect4.2=You can win by getting 4 pieces of your row horizontally, diagonally or vertically! For an example, see above.
reversi1=Welcome to the game of Reversi! You can make a move by clicking on one of the slightly transparent dots.
reversi2=Clicking on a dot will flip all the moves between where you place the dot and the next dot it finds. See the example above, where yellow is the moves to be flipped.
reversi3=Your turn may be skipped if there is no legal move. This will let your opponent play again until you get an opportunity at a legal move.
reversi4=The player who wins at the end of the game is the one who has the most pieces on the board.
tutorialstring=Tutorial
startgame=Start game!
goback=Go back
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabic)
chinese=\u4e2d\u6587 (Chinese)

View File

@@ -1,52 +1,90 @@
accept=Aceptar
ai=Inteligencia Artificial
appTitle=Selector de Juegos ISY
app-title=Selector de Juegos ISY
are-you-sure=\u00bfEst\u00e1s seguro?
back=Atr\u00e1s
backToMainMenu=Volver al men\u00FA principal
cancel=Cancelar
challenge=Desaf\u00edo
computer-difficulty=Dificultad del ordenador
computer-think-time=Tiempo de pensamiento del ordenador
computer=Ordenador
computerDifficulty=Dificultad del Ordenador
computerThinkTime=Tiempo de pensamiento de la computadora
congratulations=Felicitaciones
connect=Conectar
connectionType=Tipo de conexi\u00F3n
connect4=Connect 4
credits=Cr\u00e9ditos
dark-hc=Oscuro (Alto Contraste)
dark=Oscuro
deny=Denegar
developers=Desarrolladores
drawText=El juego termin\u00F3 en empate
effectsVolume=Volumen de efectos
musicVolume=Music Volume (translate me)
disconnect=Desconectar
effects-volume=Volumen de efectos
enter-the-server-ip=Introduce la IP del servidor
enter-the-server-port=Introduce el puerto del servidor
enter-your-name=Introduce tu nombre
error=Error
exit=Salir
forfeit=Rendirse
fullscreen=Pantalla completa
goodGameText=Buen juego. Bien jugado.
human=Humano
game-over=Fin del juego
general=General
high-contrast=Alto contraste
invalid-username=nombre de usuario inv\u00e1lido
ip-address=Direcci\u00f3n IP
is-not-a-valid-ip-address=no es una direcci\u00f3n IP v\u00e1lida
is-not-a-valid-port=no es un puerto v\u00e1lido
language=Idioma
large=Grande
layoutSize=Tama\u00F1o del dise\u00F1o
light-hc=Claro (Alto Contraste)
layout-size=Tama\u00f1o de dise\u00f1o
light=Claro
local=Local
localization=Localizaci\u00f3n
master-volume=Volumen maestro
medium=Mediano
mergeCommander=Comandante de Merge
moralSupport=Apoyo moral
merge-commander=Merge Commander
moral-support=Apoyo moral
music-volume=Volumen de m\u00fasica
name=Nombre
never=No, no quiero ver ning\u00fan tutorial nunca.
no=No
ok=OK
online=En l\u00ednea
opengl=OpenGL
options=Opciones
othello=Othello
playerName=Nombre del Jugador
productOwner=Propietario del Producto
play=Jugar
player-name=Nombre del jugador
player=Jugador
please-enter-your-name=Por favor, introduce tu nombre
port=Puerto
product-owner=Propietario del producto
quit=Salir
quitSure=\u00BFAst\u00e1s seguro?
scrumMaster=Scrum Master
server=Servidor
serverIP=Servidor-IP
serverPort=Servidor-puerto
small=Peque\u00F1o
start=Iniciar
reversi=Reversi
scrum-master=Scrum Master
send=Enviar
server-information=Informaci\u00f3n del servidor
small=Peque\u00f1o
style=Estilo
the-game-ended-in-a-draw=El juego termin\u00f3 en empate
theme=Tema
tictactoe=Tres en Raya
volume=Volumen principal
windowed=Ventana
tic-tac-toe=Tres en raya
tictactoe1 =\u00a1Bienvenido al juego del Tres en Raya! Puedes hacer tu jugada haciendo clic en cualquiera de los 9 cuadros del tablero. Ejemplo arriba:
tictactoe2 =Un jugador gana al conseguir 3 fichas en l\u00ednea, ya sea horizontal, vertical o diagonalmente. En el ejemplo de arriba, el jugador gana con una fila diagonal de tres fichas.
to-a-game-of=a un juego de
tutorial=\u00bfQuieres ver un tutorial para este juego?
volume=Volumen
windowed=En ventana
yes=S\u00ed
you-lost-against=Perdiste contra
you-were-challenged-by=Fuiste desafiado por
you-win=Ganaste
>=>
<=<
connect4.1=\u00a1Bienvenido al juego de Connect 4! Puedes hacer un movimiento haciendo clic en una de las columnas. El movimiento se colocar<61> en la fila m<>s baja de esa columna que no est<73> llena.
connect4.2=\u00a1Puedes ganar consiguiendo 4 fichas de tu color horizontal, diagonal o verticalmente! Mira el ejemplo de arriba.
reversi1=\u00a1Bienvenido al juego de Reversi! Puedes hacer un movimiento haciendo clic en uno de los puntos ligeramente transparentes.
reversi2=Al hacer clic en un punto, se voltear<61>n todas las fichas entre donde colocas el punto y el siguiente punto que encuentre. Mira el ejemplo de arriba, donde amarillo son las fichas a voltear.
reversi3=Tu turno puede ser saltado si no hay un movimiento legal. Esto permitir<69> que tu oponente juegue nuevamente hasta que tengas una oportunidad legal.
reversi4=El jugador que gane al final del juego es quien tenga m<>s fichas en el tablero.
tutorialstring=Tutorial
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Ar\u00e1bigo)
chinese=\u4e2d\u6587 (Chino)

View File

@@ -1,52 +1,89 @@
accept=Accepter
ai=Intelligence Artificielle
appTitle=S\u00e9lecteur de Jeux ISY
app-title=S\u00c9LECTEUR DE JEUX ISY
are-you-sure=\u00cates-vous s\u00fbr ?
back=Retour
backToMainMenu=Retour au menu principal
cancel=Annuler
challenge=D\u00e9fi
computer-difficulty=Difficult\u00e9 de l'ordinateur
computer-think-time=Temps de r\u00e9flexion de l'ordinateur
computer=Ordinateur
computerDifficulty=Difficult\u00e9 de l'Ordinateur
computerThinkTime=Temps de r\u00e9flexion de l'ordinateur
congratulations=F\u00E9licitations
connect=Connecter
connectionType=Type de connexion
connect=Connexion
connect4=Connect 4
credits=Cr\u00e9dits
dark-hc=Sombre (Contraste \u00E9lev\u00E9)
dark=Sombre
deny=Refuser
developers=D\u00e9veloppeurs
drawText=La partie s'est termin\u00E9e par un match nul
effectsVolume=Volume des effets
musicVolume=Music Volume (translate me)
disconnect=D\u00e9connexion
effects-volume=Volume des effets
enter-the-server-ip=Entrez l'adresse IP du serveur
enter-the-server-port=Entrez le port du serveur
enter-your-name=Entrez votre nom
error=Erreur
exit=Quitter
forfeit=Abandonner
fullscreen=Plein \u00e9cran
goodGameText=Bien jou\u00E9. Bonne partie.
human=Humain
game-over=Fin de la partie
general=G\u00e9n\u00e9ral
high-contrast=Haut contraste
invalid-username=nom d'utilisateur invalide
ip-address=Adresse IP
is-not-a-valid-ip-address=n'est pas une adresse IP valide
is-not-a-valid-port=n'est pas un port valide
language=Langue
large=Grand
layoutSize=Taille de la disposition
light-hc=Clair (Contraste \u00E9lev\u00E9)
layout-size=Format de l'affichage
light=Clair
local=Local
localization=Localisation
localization=Traduction
master-volume=Volume principal
medium=Moyen
mergeCommander=Commandant de Merge
moralSupport=Soutien moral
merge-commander=Merge Commander
moral-support=Soutien moral
music-volume=Volume de la musique
name=Nom
never=Non, je ne veux jamais voir de tutoriels.
no=Non
ok=OK
online=En ligne
opengl=OpenGL
options=Options
othello=Othello
playerName=Nom du joueur
productOwner=Responsable du produit
play=Jouer
player-name=Nom du joueur
player=Joueur
please-enter-your-name=Veuillez entrer votre nom
port=Port
product-owner=Responsable produit
quit=Quitter
quitSure=Es-tu s\u00fbr ?
scrumMaster=Scrum Master
server=Serveur
serverIP=Serveur-IP
serverPort=Serveur-Port
reversi=Reversi
scrum-master=Scrum Master
send=Envoyer
server-information=Informations du serveur
small=Petit
start=D\u00e9marrer
theme=Th\u00E8me
tictactoe=Morpion
volume=Volume principal
windowed=Fen\u00eatre
style=Style
the-game-ended-in-a-draw=La partie s'est termin\u00e9e par un match nul
theme=Th\u00e8me
tic-tac-toe=Morpion
tictactoe1 =Bienvenue dans le jeu du Tic Tac Toe ! Vous pouvez jouer en cliquant sur l\u2019une des 9 cases du plateau. Exemple ci-dessus :
tictactoe2 =Un joueur gagne en alignant 3 pi\u00e8ces \u2013 horizontalement, verticalement ou en diagonale. Dans l\u2019exemple ci-dessus, le joueur gagne avec une diagonale de trois pi\u00e8ces.
to-a-game-of=\u00e0 une partie de
tutorial=Veux-tu voir un tutoriel pour ce jeu\u00a0?
volume=Volume
windowed=Fen\u00eatrr\u00e9
yes=Oui
you-lost-against=Vous avez perdu contre
you-were-challenged-by=Vous avez \u00e9t\u00e9 d\u00e9fi\u00e9 par
you-win=Vous avez gagn\u00e9
>=>
<=<
connect4.1=Bienvenue dans le jeu Connect 4 ! Vous pouvez effectuer un mouvement en cliquant sur l'une des colonnes. Le mouvement sera plac<61> dans la ligne la plus basse de cette colonne qui n'est pas encore remplie.
connect4.2=Vous pouvez gagner en alignant 4 pions de votre couleur horizontalement, diagonalement ou verticalement ! Voir l'exemple ci-dessus.
reversi1=Bienvenue dans le jeu Reversi ! Vous pouvez jouer en cliquant sur l'un des points l<>g<EFBFBD>rement transparents.
reversi2=Cliquer sur un point retournera tous les pions entre le point plac<61> et le prochain point trouv<75>. Voir l'exemple ci-dessus, o<> le jaune indique les pions <20> retourner.
reversi3=Votre tour peut <20>tre saut<75> s'il n'y a pas de coup l<>gal. Cela permettra <20> votre adversaire de jouer jusqu'<27> ce que vous ayez un coup l<>gal.
reversi4=Le joueur qui a le plus de pions <20> la fin du jeu gagne.
tutorialstring=Tutoriel
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabe)
chinese=\u4e2d\u6587 (Chinois)

View File

@@ -1,52 +1,89 @@
ai=\u092a\u094d\u0930\u0924\u093f\u092a\u094d\u0930\u0923\u093e\u0924\u094d\u092e\u093e\u0928 \u092a\u094d\u0930\u092c\u094d\u0939\u093e\u0935\u0924\u094d\u0924\u093e
appTitle=ISY \u0917\u0947\u092e \u0938\u0947\u0932\u0947\u0915\u094d\u091f\u0930
back=\u092a\u093f\u091a\u093e\u0932
backToMainMenu=\u092E\u0947\u0928 \u092E\u0947\u0928\u0942 \u092A\u0930 \u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902
computer=\u0915\u092e\u092a\u094d\u092f\u0942\u091f\u0930
computerDifficulty=\u0915\u092e\u092a\u094d\u092f\u0942\u091f\u0930 \u092a\u094d\u0930\u092f\u093e\u0938
computerThinkTime=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930 \u0915\u0940 \u092a\u0930 \u092b\u0930 \u0938\u092e\u092f
congratulations=\u092C\u0927\u093E\u0908
connect=\u091c\u0942\u0921\u094d\u0921 \u0915\u0930\u0947\u0902
connectionType=\u0915\u0928\u0947\u0915\u094D\u0936\u0928 \u0915\u093E \u092A\u094D\u0930\u0915\u093E\u0930
credits=\u0916\u094d\u092f\u093e\u0924\u0947
dark-hc=\u0915\u093E\u0932\u093E (\u090A\u091A\u094D\u091A \u0915\u0949\u0928\u094D\u091F\u094D\u0930\u093E\u0938\u094D\u091F)
dark=\u0915\u093E\u0932\u093E
developers=\u0935\u093f\u0915\u0938\u093f\u0915\u0930
drawText=\u0916\u0947\u0932 \u091F\u0940\u091A \u092A\u0930 \u0916\u0924\u094D\u092E \u0939\u094B \u0917\u092F\u0940
effectsVolume=\u092a\u0631\u094d\u092a\u093e\u0935 \u0935\u094b\u0932\u094d\u092e
musicVolume=Music Volume (translate me)
fullscreen=\u092a\u0942\u0930\u094d\u0923 \u0938\u0915\u0940\u0928\u093e
goodGameText=\u0905\u091A\u094D\u091B\u0940 \u0916\u0947\u0932 \u0925\u0940\u0964 \u091B\u0940 \u0916\u0942\u092C \u0916\u0947\u0932\u093E.
human=\u092e\u093e\u0928\u0935
accept=\u0938\u094d\u0935\u0940\u0915\u093e\u0930
ai=\u0915\u0943\u0924\u094d\u0930\u093f\u092e \u092c\u0941\u0926\u094d\u0927\u093f
app-title=ISY \u0917\u0947\u092e \u091a\u092f\u0928\u0915\u093e\u0930\u0940
are-you-sure=\u0915\u094d\u092f\u093e \u0906\u092a \u0928\u093f\u0936\u094d\u091a\u093f\u0924 \u0939\u0948\u0902?
back=\u092a\u0940\u091b\u0947
cancel=\u0930\u0926\u094d\u0926 \u0915\u0930\u0947\u0902
challenge=\u091a\u0941\u0928\u094c\u0924\u0940
computer-difficulty=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930 \u0915\u0940 \u0915\u0920\u0928\u093e\u0908
computer-think-time=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930 \u091a\u093f\u0902\u0924\u0928 \u0938\u092e\u092f
computer=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930
connect=\u091c\u094b\u095c\u0947\u0902
connect4=Connect 4
credits=\u0915\u094d\u0930\u0947\u0921\u093f\u091f\u094d\u0938
dark=\u0917\u0939\u0930\u093e
deny=\u0907\u0902\u0915\u093e\u0930
developers=\u0935\u093f\u0915\u093e\u0938\u093f\u0924\u093e
disconnect=\u091c\u094b\u095c \u091f\u0942\u091f\u0928\u093e
effects-volume=\u090f\u092b\u0947\u0915\u094d\u091f \u0935\u0949\u0932\u094d\u092f\u0942\u092e
enter-the-server-ip=\u0938\u0930\u094d\u0935\u0930 IP \u0926\u093e\u0916\u093f\u090f
enter-the-server-port=\u0938\u0930\u094d\u0935\u0930 \u092a\u094b\u0930\u094d\u091f \u0926\u093e\u0916\u093f\u090f
enter-your-name=\u0905\u092a\u0928\u093e \u0928\u093e\u092e \u0926\u093e\u0916\u093f\u090f
error=\u0924\u094d\u0930\u0941\u091f\u093f
exit=\u092c\u093e\u0939\u0930 \u0928\u093f\u0915\u093e\u0932\u0940\u090f\u0902
forfeit=\u0939\u093e\u0930 \u092e\u093e\u0928\u0928\u093e
fullscreen=\u092a\u0942\u0930\u0940 \u0938\u094d\u0915\u094d\u0930\u0940\u0928
game-over=\u0916\u0947\u0932 \u0916\u0924\u094d\u092e
general=\u0938\u093e\u092e\u093e\u0928\u094d\u092f
high-contrast=\u0909\u091A\u094D\u091A\u093F\u0924 \u0915\u0949\u0902\u091F\u094D\u0930\u093E\u0938\u094D\u091F
invalid-username=\u0905\u092e\u093e\u0928\u094d\u092f \u0909\u092a\u092f\u094b\u0917\u0915\u0930\u094d\u0924\u093e \u0928\u093e\u092e
ip-address=IP \u092a\u0924\u093e
is-not-a-valid-ip-address=\u092f\u0939 \u090f\u0915 \u0905\u092e\u093e\u0928\u094d\u092f IP \u092a\u0924\u093e \u0928\u0939\u0940\u0902 \u0939\u0948
is-not-a-valid-port=\u092f\u0939 \u090f\u0915 \u0905\u092e\u093e\u0928\u094d\u092f \u092a\u094b\u0930\u094d\u091f \u0928\u0939\u0940\u0902 \u0939\u0948
language=\u092d\u093e\u0937\u093e
large=\u092C\u0921\u093C\u093E
layoutSize=\u0930\u0942\u092A\u0930\u0947\u0916 \u0915\u093E \u0906\u0915\u093E\u0930
light-hc=\u091A\u094D\u092E\u092C\u0940\u0932\u093E (\u090A\u091A\u094D\u091A \u0915\u0949\u0928\u094D\u091F\u094D\u0930\u093E\u0938\u094D\u091F)
light=\u091A\u094D\u092E\u092C\u0940\u0932\u093E
local=\u0938\u094d\u0925\u093e\u0928\u093f\u092f
localization=\u0938\u094d\u0925\u093e\u0928\u093f\u092f\u0915\u0930\u0923
medium=\u092E\u0927\u094D\u092F\u092E
mergeCommander=\u092e\u0930\u094d\u091c \u0915\u092e\u0902\u0921\u0930
moralSupport=\u0928\u094d\u092e\u093e\u0928\u093f\u0915 \u0938\u092e\u0930\u094d\u0925\u0928
large=\u092c\u095c\u093e
layout-size=\u0932\u0947\u090f\u0913\u091f \u0915\u093e \u0906\u0915\u093e\u0930
light=\u0915\u0947\u091c\u093c\u093e
local=\u0938\u094d\u0925\u093e\u0928\u0940\u092f
localization=\u0938\u094d\u0925\u093e\u0928\u0940\u092f\u0915\u0930\u0923
master-volume=\u092e\u093e\u0938\u094d\u091f\u0930 \u0935\u0949\u0932\u094d\u092f\u0942\u092e
medium=\u092e\u0927\u094d\u092f\u092e
merge-commander=\u092e\u0930\u094d\u091c \u0915\u092e\u093e\u0902\u0921\u0930
moral-support=\u0928\u0948\u0924\u093f\u0915 \u0938\u0939\u093e\u0930\u093e
music-volume=\u0938\u0902\u0917\u0940\u0924 \u0935\u0949\u0932\u094d\u092f\u0942\u092e
name=\u0928\u093e\u092e
never=\u0928\u0939\u0940\u0902, \u092e\u0948\u0902 \u0915\u092c\u094d\u0939\u0940 \u092d\u0940 \u0915\u094b\u0908 \u091f\u094d\u092f\u0942\u091f\u094b\u0930\u093f\u092f\u0932 \u0928\u0939\u0940\u0902 \u0926\u0947\u0916\u0928\u093e \u091a\u093e\u0939\u0924\u093e/\u091a\u093e\u0939\u0924\u0940\u0964
no=\u0928\u0939\u0940\u0902
ok=\u0920\u0940\u0915 \u0939\u0948
online=\u0911\u0928\u0932\u093e\u0907\u0928
opengl=OpenGL
options=\u0935\u093f\u0915\u0932\u094d\u092a
othello=\u0913\u0925\u0940\u0932\u094b
playerName=\u0915\u0941\u0930\u093e\u0930\u0940 \u0928\u093e\u092e
productOwner=\u0906\u092f\u0947\u0915\u093e \u092e\u093e\u0932\u093f\u0915
quit=\u0938\u094e\u091c\u094d
quitSure=\u0915\u094d\u092f\u093e \u0915\u094d\u092f\u093e \u091f\u0940\u091f \u0939\u0948\u0902?
scrumMaster=\u0938\u094d\u0915\u094d\u0930\u0941\u092e \u092e\u093e\u0938\u094d\u091f\u0930
server=\u0938\u0930\u094d\u0935\u0930
serverIP=\u0938\u0930\u094d\u0935\u0930 IP
serverPort=\u0938\u0930\u094d\u0935\u0930 \u092a\u094b\u0930\u094d\u091f
small=\u091B\u094B\u091F\u093E
start=\u092b\u093f\u0930\u0942
theme=\u0925\u0940\u092E
tictactoe=\u091f\u093f\u0915 \u091f\u0948\u0915 \u091f\u094b
volume=\u092e\u0941\u0916\u094d\u092f \u0906\u0935\u093e\u091c
windowed=\u0915\u094d\u0930\u094d\u0939 \u092e\u0947\u0902
yes=\u0939\u093e\u0907
play=\u0916\u0947\u0932\u0947\u0902
player-name=\u0916\u093f\u0932\u093e\u095e\u0940 \u0915\u093e \u0928\u093e\u092e
player=\u0916\u093f\u0932\u093e\u095e\u0940
please-enter-your-name=\u0915\u0943\u092a\u092f\u093e \u0905\u092a\u0928\u093e \u0928\u093e\u092e \u0926\u093e\u0916\u093f\u090f
port=\u092a\u094b\u0930\u094d\u091f
product-owner=\u092a\u094d\u0930\u094b\u0921\u0915\u094d\u091f \u0915\u093e \u092e\u093e\u0932\u093f\u0915
quit=\u091b\u094b\u0921\u0947\u0902
reversi=\u0930\u093f\u0935\u0930\u094d\u0938\u0940
scrum-master=\u0938\u094d\u0915\u094d\u0930\u092e \u092e\u093e\u0938\u094d\u091f\u0930
send=\u092d\u0947\u091c\u0947\u0902
server-information=\u0938\u0930\u094d\u0935\u0930 \u091c\u093e\u0928\u0915\u093e\u0930\u0940
small=\u091b\u094b\u091f\u093e
style=\u0936\u0948\u0932\u0940
the-game-ended-in-a-draw=\u0916\u0947\u0932 \u091f\u0940\u0915 \u0939\u094b \u0917\u092f\u093e
theme=\u0925\u0940\u092e
tic-tac-toe=\u091f\u093f\u0915-\u091f\u0948\u0915-\u091f\u094b
tictactoe1 =\u091f\u093f\u0915-\u091f\u0948\u0915-\u091f\u094b \u0915\u0947 \u0916\u0947\u0932 \u092e\u0947\u0902 \u0906\u092a\u0915\u093e \u0938\u094d\u0935\u093e\u0917\u0924 \u0939\u0948! \u0906\u092a \u092c\u094b\u0930\u094d\u0921 \u092a\u0930 \u0915\u093f\u0938\u0940 \u092d\u0940 9 \u0916\u093e\u0928\u0947 \u092a\u0930 \u0915\u094d\u0932\u093f\u0915 \u0915\u0930\u0915\u0947 \u0905\u092a\u0928\u0940 \u091a\u093e\u0932 \u091a\u0932 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u090a\u092a\u0930 \u0926\u093f\u092f\u093e \u0917\u092f\u093e \u0909\u0926\u093e\u0939\u0930\u0923 \u0926\u0947\u0916\u0947\u0902:
tictactoe2 =\u0915\u094b\u0908 \u0916\u093f\u0932\u093e\u0921\u093c\u0940 \u0924\u092c \u091c\u0940\u0924\u0924\u093e \u0939\u0948 \u091c\u092c \u0935\u0939 \u092a\u0948 \u092a\u0902\u0915\u094d\u0924\u093f \u092e\u0947\u0902 \u0924\u0940\u0928 \u0928\u093f\u0936\u093e\u0928 \u090f\u0915 \u092a\u0902\u0915\u094d\u0924\u093f \u092e\u0947\u0902 \u0932\u0917\u093e \u0926\u0947\u0924\u093e \u0939\u0948 ? \u0915\u094d\u0937\u0948\u0924\u093f\u091c, \u090a\u0930\u094d\u0927\u094d\u0935\u093e\u0927\u0930 \u092f\u093e \u0924\u093f\u0930\u091b\u0947\u0964 \u090a\u092a\u0930 \u0909\u0926\u093e\u0939\u0930\u0923 \u092e\u0947\u0902, \u0916\u093f\u0932\u093e\u0921\u093c\u0940 \u0924\u093f\u0930\u091b\u0940 \u092a\u0902\u0915\u094d\u0924\u093f \u092e\u0947\u0902 \u0924\u093f\u0930\u091b\u0940 \u092a\u0902\u0915\u094d\u0924\u093f \u0938\u0947 \u091c\u0940\u0924\u0924\u093e \u0939\u0948\u0964
to-a-game-of=\u0916\u0947\u0932 \u0915\u0940 \u090f\u0915 \u0916\u0947\u0932 \u0915\u0940 \u0913\u0930
tutorial=\u0915\u094d\u092f\u093e \u0906\u092a \u0907\u0938 \u0917\u0947\u092e \u0915\u0947 \u0932\u093f\u090f \u0915\u094b\u0908 \u091f\u094d\u092f\u0942\u091f\u094b\u0930\u093f\u092f\u0932 \u0926\u0947\u0916\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?
volume=\u0935\u0949\u0932\u094d\u092f\u0942\u092e
windowed=\u0935\u093f\u0902\u0921\u094b \u092e\u094b\u0921
yes=\u0939\u093e\u0901
you-lost-against=\u0906\u092a \u0939\u093e\u0930 \u0917\u090f
you-were-challenged-by=\u0906\u092a\u0915\u094b \u091a\u0941\u0928\u094c\u0924\u0940 \u0926\u0940 \u0917\u0908
you-win=\u0906\u092a \u091c\u0940\u0924 \u0917\u090f
>=>
<=<
connect4.1=\u0915\u0928\u094d\u0928\u0947 \u0915\u0940 \u091c\u0948 \u0915\u0947 \u091c\u094c\u0915 \u0915\u0928\u0947\u0915\u094d\u091f 4 \u092e\u0947\u0902! \u0906\u092a \u0915\u0940 \u091a\u0948\u0928 \u092a\u0932\u0938 \u092a\u0928\u094d\u0928 \u0928\u093e\u092e\u094d\u092c\u0921\u093e\u0928\u0947 \u0915\u0940 \u092f\u094b\u0917\u0924\u0940 \u092e\u0947\u0902 \u0915\u0940 \u0928\u093f\u091a\u094d\u091a\u0942\u0928 \u0915\u0940 \u0924\u0939 \u091a\u093f\u0928 \u092a\u0924\u093f \u0928\u0939\u0940\u0902 \u0926\u0947 \u092c\u0924\u0940.
connect4.2=\u092a\u093e\u0902\u091a \u092e\u0946\u092c\u0921 \u0915\u0940 \u092a\u0930\u092a\u0930\u092f\u094d\u0928 \u092d\u093e\u0935\u0940 \u0938\u0947 4 \u092a\u093f\u0938 \u0924\u093e\u0924\u093e \u0915\u0940 \u0930\u094f\u0936\u0942 \u0938\u0940\u0926\u094d\u0927 \u092e\u0947\u0902 \u092a\u0940\u0928\u094d\u0928\u094b\u0902 \u0915\u0940 \u092e\u093e\u0928\u094d\u0926\u0930 \u0939\u0940.
reversi1=\u0915\u0928\u094d\u0928\u0947 \u0915\u0940 \u091c\u0948 \u0910 \u0930\u0947\u0935\u0930\u094d\u0938\u0940 \u092e\u0947\u0902 \u0926\u0948\u091a \u092c\u0922\u093e\u0928 \u0915\u0940 \u092a\u0924\u094d\u0928 \u092a\u0930 \u092a\u094d\u0932\u0947 \u0916\u0942\u0928\u0947 \u0915\u0940 \u092a\u094d\u0930\u092f\u0948\u0915\u0944 \u0915\u0930\u0948\u0902.
reversi2=\u0915\u093f\u0938 \u092a\u0930 \u092a\u093f\u0938 \u092a\u0948\u0918 \u0915\u0940 \u091a\u0928 \u092e\u0947\u0902 \u0938\u092e\u093e\u0930\u094e \u092a\u093f\u0938 \u092a\u0940\u0918 \u092e\u0947\u0902 \u092a\u0930\u093f\u0923\u0924 \u0915\u093e \u092a\u0930\u093f\u0928\u0942\u0924\u0940 \u0915\u0940 \u092a\u094d\u0930\u0924\u093f \u092a\u0941\u0928\u0940 \u092a\u0930\u093f\u0928\u094d\u0924 \u0915\u0940 \u092a\u0940\u0938 \u092a\u0930\u094d\u092f\u0928\u0947 \u091c\u093e\u0902\u0918\u0942.
reversi3=\u092f\u0939 \u092a\u0930\u094d\u092f \u0938\u0947 \u0938\u0947\u091a \u0915\u0940 \u091c\u093e\u0902\u091c \u0928\u0939\u0940\u0902 \u0939\u0948 \u0914\u0938\u0924\u0947 \u0915\u0948 \u092a\u0948\u0928 \u092a\u0930\u094d\u092f \u0939\u0948 \u0914\u092a\u0915\u0940 \u0915\u0940 \u092a\u0932\u0947 \u092d\u0942\u0924 \u0915\u0940 \u0906\u0927\u093e \u092a\u0948\u0928 \u091c\u093e\u0902\u091c \u0915\u0930 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902.
reversi4=\u0916\u0941\u092f \u0915\u093f \u0915\u0940 \u0928\u093f\u092e\u0940 \u092e\u0947\u0902 \u091a\u093e\u0932 \u0938\u092c\u0938\u0947 \u091a\u0942\u0928\u094d\u0928\u0947 \u0939\u0948, \u0935\u0949 \u0915\u0947 \u092e\u093e\u0924\u094d\u0930 \u091c\u0940\u0924\u0947 \u0939\u0948.
tutorialstring=\u0924\u0942\u091f\u0949\u0930\u093f\u092f\u0932
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0905\u0930\u092c\u0940)
chinese=\u4e2d\u6587 (\u091a\u0940\u0928\u0940)

View File

@@ -1,52 +1,88 @@
accept=Accetta
ai=Intelligenza Artificiale
appTitle=Selettore di Giochi ISY
app-title=Selettore di Giochi ISY
are-you-sure=Sei sicuro?
back=Indietro
backToMainMenu=Ritorna al menu principale
cancel=Annulla
challenge=Sfida
computer-difficulty=Difficolt\u00e0 del computer
computer-think-time=Tempo di riflessione del computer
computer=Computer
computerDifficulty=Difficolt\u00e0 del computer
computerThinkTime=Tempo di pensiero del computer
congratulations=Congratulazioni
connect=Connetti
connectionType=Tipo di connessione
connect4=Connect 4
credits=Crediti
dark-hc=Scuro (Alto Contrasto)
dark=Scuro
deny=Nega
developers=Sviluppatori
drawText=La partita \u00E8 terminata in parit\u00E0
effectsVolume=Volume effetti
musicVolume=Music Volume (translate me)
disconnect=Disconnetti
effects-volume=Volume degli effetti
enter-the-server-ip=Inserisci l'IP del server
enter-the-server-port=Inserisci la porta del server
enter-your-name=Inserisci il tuo nome
error=Errore
exit=Esci
forfeit=Abbandona
fullscreen=Schermo intero
goodGameText=Bel gioco. Ben giocato.
human=Umano
game-over=Fine del gioco
general=Generale
high-contrast=Alto contrasto
invalid-username=nome utente non valido
ip-address=Indirizzo IP
is-not-a-valid-ip-address=non \u00e8 un indirizzo IP valido
is-not-a-valid-port=non \u00e8 una porta valida
language=Lingua
large=Grande
layoutSize=Dimensione Layout
light-hc=Chiaro (Alto Contrasto)
layout-size=Dimensione layout
light=Chiaro
local=Locale
localization=Localizzazione
master-volume=Volume principale
medium=Medio
mergeCommander=Comandante di Merge
moralSupport=Supporto morale
merge-commander=Merge Commander
moral-support=Supporto morale
music-volume=Volume della musica
name=Nome
never=No, non voglio mai vedere alcun tutorial.
no=No
ok=OK
online=Online
opengl=OpenGL
options=Opzioni
othello=Othello
playerName=Nome del giocatore
productOwner=Proprietario del prodotto
quit=Uscire
quitSure=Sei sicuro?
scrumMaster=Scrum Master
server=Server
serverIP=Server-IP
serverPort=Porta del server
play=Gioca
player-name=Nome del giocatore
player=Giocatore
please-enter-your-name=Per favore inserisci il tuo nome
port=Porta
product-owner=Proprietario del prodotto
quit=Esci
reversi=Reversi
scrum-master=Scrum Master
send=Invia
server-information=Informazioni sul server
small=Piccolo
start=Inizia
style=Stile
the-game-ended-in-a-draw=La partita \u00e8 terminata in parit\u00e0
theme=Tema
tictactoe=Tic Tac Toe
volume=Volume principale
tic-tac-toe=Tris
tictactoe1 =Benvenuto nel gioco del Tris! Puoi fare la tua mossa cliccando su uno dei 9 quadrati della griglia. Esempio sopra:
tictactoe2 =Un giocatore vince mettendo 3 simboli in fila ? orizzontalmente, verticalmente o diagonalmente. Nell?esempio sopra, il giocatore vince con una fila diagonale di tre simboli.
to-a-game-of=a una partita di
tutorial=Vuoi vedere un tutorial per questo gioco?
volume=Volume
windowed=Finestra
yes=S\u00ec
you-lost-against=Hai perso contro
you-were-challenged-by=Sei stato sfidato da
you-win=Hai vinto
>=>
<=<
connect4.1=Benvenuto nel gioco Connect 4! Puoi fare una mossa cliccando su una delle colonne. La mossa sar<61> posizionata nella riga pi<70> bassa di quella colonna che non <20> ancora piena.
connect4.2=Puoi vincere ottenendo 4 pedine del tuo colore in orizzontale, diagonale o verticale! Guarda l'esempio sopra.
reversi1=Benvenuto nel gioco Reversi! Puoi fare una mossa cliccando su uno dei punti leggermente trasparenti.
reversi2=Cliccando su un punto, tutti i pezzi tra dove metti il punto e il prossimo punto trovato verranno girati. Guarda l'esempio sopra, dove il giallo indica i pezzi da girare.
reversi3=Il tuo turno pu<70> essere saltato se non ci sono mosse legali. Questo permetter<65> al tuo avversario di giocare fino a quando non avrai un'opportunit<69> legale.
reversi4=Il giocatore che alla fine del gioco ha pi<70> pezzi sulla scacchiera vince.
tutorialstring=Tutorial
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabo)
chinese=\u4e2d\u6587 (Cinese)

View File

@@ -1,52 +1,88 @@
ai=\u4eba\u5de5\u77e5\u80fd
appTitle=ISY \u30b2\u30fc\u30e0\u30bb\u30ec\u30af\u30bf\u30fc
accept=\u627f\u8a8d
ai=\u4eba\u5de5\u77e9\u7565
app-title=ISY\u30b2\u30fc\u30e0\u30bb\u30ec\u30af\u30bf\u30fc
are-you-sure=\u672c\u5f53\u306b\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f
back=\u623b\u308b
backToMainMenu=\u30E1\u30A4\u30F3\u30E1\u30CB\u30E5\u30FC\u306B\u623B\u308B
computer=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc
computerDifficulty=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u96e3\u6613\u5ea6
computerThinkTime=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u306e\u8003\u3048\u6642\u9593
congratulations=\u304A\u3081\u3067\u3068\u3046\u3054\u3056\u3044\u307E\u3059
cancel=\u30ad\u30e3\u30f3\u30bb\u30eb
challenge=\u6311\u6226
computer-difficulty=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u306e\u96e3\u6613\u5ea6
computer-think-time=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u306e\u601d\u8003\u6642\u9593
computer=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf
connect=\u63a5\u7d9a
connectionType=\u63A5\u7D9A\u30BF\u30A4\u30D7
connect4=Connect 4
credits=\u30af\u30ec\u30b8\u30c3\u30c8
dark-hc=\u30C0\u30FC\u30AF (\u9AD8\u30B3\u30F3\u30C8\u30E9\u30B9\u30C8)
dark=\u30C0\u30FC\u30AF
dark=\u30c0\u30fc\u30af
deny=\u62d2\u5426
developers=\u958b\u767a\u8005
drawText=\u30B2\u30FC\u30E0\u306F\u5E73\u7B49\u306B\u7D42\u4E86\u3057\u307E\u3057\u305F
effectsVolume=\u30a8\u30d5\u30a7\u30af\u30c8\u30dc\u30ea\u30e5\u30fc\u30e0
musicVolume=Music Volume (translate me)
fullscreen=\u5168\u753b\u9762
goodGameText=\u3044\u3044\u30B2\u30FC\u30E0\u3067\u3057\u305F\u3002\u3088\u304F\u6226\u3044\u307E\u3057\u305F\u3002
human=\u4eba\u9593
disconnect=\u5207\u65ad
effects-volume=\u52b9\u679c\u97f3\u91cf
enter-the-server-ip=\u30b5\u30fc\u30d0\u30fcIP\u3092\u5165\u529b
enter-the-server-port=\u30b5\u30fc\u30d0\u30fc\u30dd\u30fc\u30c8\u3092\u5165\u529b
enter-your-name=\u540d\u524d\u3092\u5165\u529b
error=\u30a8\u30e9\u30fc
exit=\u7d42\u4e86
forfeit=\u8f9e\u9000
fullscreen=\u30d5\u30eb\u30b9\u30af\u30ea\u30fc\u30f3
game-over=\u30b2\u30fc\u30e0\u30aa\u30fc\u30d0\u30fc
general=\u4e00\u822c
high-contrast=\u9AD8\u30B3\u30F3\u30C8\u30E9\u30B9
invalid-username=\u7121\u52b9\u306a\u30e6\u30fc\u30b6\u30fc\u540d
ip-address=IP\u30a2\u30c9\u30ec\u30b9
is-not-a-valid-ip-address=\u6709\u52b9\u306aIP\u30a2\u30c9\u30ec\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093
is-not-a-valid-port=\u6709\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7\u3067\u306f\u3042\u308a\u307e\u305b\u3093
language=\u8a00\u8a9e
large=\u5927
layoutSize=\u30EC\u30A4\u30A2\u30A6\u30C8\u30B5\u30A4\u30BA
light-hc=\u30E9\u30A4\u30C8 (\u9AD8\u30B3\u30F3\u30C8\u30E9\u30B9\u30C8)
light=\u30E9\u30A4\u30C8
local=\u5730\u57df
layout-size=\u30ec\u30a4\u30a2\u30a6\u30c8\u30b5\u30a4\u30ba
light=\u30e9\u30a4\u30c8
local=\u30ed\u30fc\u30ab\u30eb
localization=\u30ed\u30fc\u30ab\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3
medium=\u4E2D
mergeCommander=\u30de\u30fc\u30b8\u30b3\u30de\u30f3\u30c0\u30fc
moralSupport=\u6c17\u529b\u652f\u63f4
master-volume=\u30de\u30b9\u30bf\u30fc\u30dc\u30ea\u30e5\u30fc\u30e0
medium=\u4e2d
merge-commander=\u30de\u30fc\u30b8\u30b3\u30de\u30f3\u30c0\u30fc
moral-support=\u30e1\u30f3\u30bf\u30eb\u30b5\u30dd\u30fc\u30c8
music-volume=\u97f3\u697d\u97f3\u91cf
name=\u540d\u524d
never=\u3044\u3044\u3048\u3001\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u306f\u4e8c\u5ea6\u3068\u898b\u305f\u304f\u3042\u308a\u307e\u305b\u3093\u3002
no=\u3044\u3044\u3048
ok=OK
online=\u30aa\u30f3\u30e9\u30a4\u30f3
opengl=OpenGL
options=\u30aa\u30d7\u30b7\u30e7\u30f3
othello=\u30aa\u30bb\u30ed
playerName=\u30d7\u30ec\u30a4\u30e4\u30fc\u540d
productOwner=\u30d7\u30ed\u30c0\u30af\u30c8\u30aa\u30fc\u30ca\u30fc
play=\u30d7\u30ec\u30a4
player-name=\u30d7\u30ec\u30a4\u30e4\u30fc\u540d
player=\u30d7\u30ec\u30a4\u30e4\u30fc
please-enter-your-name=\u540d\u524d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
port=\u30dd\u30fc\u30c8
product-owner=\u30d7\u30ed\u30c0\u30af\u30c8\u30aa\u30fc\u30ca\u30fc
quit=\u7d42\u4e86
quitSure=\u672c\u5f53\u306b\u7d42\u4e86\u3057\u307e\u3059\u304b\uff1f
scrumMaster=\u30b9\u30af\u30e9\u30e0\u30de\u30b9\u30bf\u30fc
server=\u30b5\u30fc\u30d0\u30fc
serverIP=\u30b5\u30fc\u30d0\u30fc IP
serverPort=\u30b5\u30fc\u30d0\u30fc \u30dd\u30fc\u30c8
small=\u5C0F
start=\u59cb\u307e\u308a
theme=\u30C6\u30FC\u30DE
tictactoe=\u30bf\u30a4\u30af\u30bf\u30c3\u30c8\u30c8\u30a6
volume=\u30de\u30b9\u30bf\u30fc\u30dc\u30ea\u30e5\u30fc\u30e0
windowed=\u30a6\u30a3\u30f3\u30c9\u30a6
reversi=\u30ea\u30d0\u30fc\u30b7
scrum-master=\u30b9\u30af\u30e9\u30e0\u30de\u30b9\u30bf\u30fc
send=\u9001\u4fe1
server-information=\u30b5\u30fc\u30d0\u60c5\u5831
small=\u5c0f
style=\u30b9\u30bf\u30a4\u30eb
the-game-ended-in-a-draw=\u30b2\u30fc\u30e0\u306f\u5f15\u304d\u5206\u3051\u306b\u7d42\u308f\u308a\u307e\u3057\u305f
theme=\u30c6\u30fc\u30de
tic-tac-toe=\u4e09\u76ee\u4e26\u3079
tictactoe1 =\u4e09\u76ee\u4e26\u3079\u306e\u30b2\u30fc\u30e0\u3078\u3088\u3046\u3053\u305d\uff01\u30dc\u30fc\u30c9\u4e0a\u306e9\u3064\u306e\u30de\u30b9\u306e\u3044\u305a\u308c\u304b\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u624b\u3092\u6253\u3061\u307e\u3057\u3087\u3046\u3002\u4e0a\u306e\u4f8b\u3092\u53c2\u7167\uff1a
tictactoe2 =\u30d7\u30ec\u30a4\u30e4\u30fc\u306f\u3001\u6a2a\u30fb\u7e26\u30fb\u65b9\u5411\u306e\u3044\u305a\u308c\u304b\u3067\u30de\u30fc\u30af\u30923\u3064\u4e26\u3079\u308b\u3068\u52dd\u3061\u3067\u3059\u3002\u4e0a\u306e\u4f8b\u3067\u306f\u3001\u30d7\u30ec\u30a4\u30e4\u30fc\u304c\u659c\u3081\u306b3\u3064\u4e26\u3079\u3066\u52dd\u3063\u3066\u3044\u307e\u3059\u3002
to-a-game-of=\u30b2\u30fc\u30e0\u306b
tutorial=\u3053\u306e\u30b2\u30fc\u30e0\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3092\u898b\u305f\u3044\u3067\u3059\u304b\uff1f
volume=\u97f3\u91cf
windowed=\u30a6\u30a3\u30f3\u30c9\u30a6\u8868\u793a
yes=\u306f\u3044
you-lost-against=\u3042\u306a\u305f\u306f ... \u306b\u6557\u308c\u307e\u3057\u305f
you-were-challenged-by=\u3042\u306a\u305f\u306f ... \u304b\u3089\u6311\u6226\u3055\u308c\u307e\u3057\u305f
you-win=\u52dd\u5229\u3067\u3059
>=>
<=<
connect4.1=\u30b3\u30cd\u30af\u30c84\u306e\u30b2\u30fc\u30e0\u3078\u3088\u3046\u3053\u305d! \u30ab\u30e9\u30e0\u306e\u4e0a\u306e\u30ab\u30e9\u30e0\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u52d5\u304b\u3057\u3092\u884c\u3048\u307e\u3059\u3002\u52d5\u304b\u3057\u306f\u3001\u3060\u307e\u308a\u306f\u307e\u3067\u5869\u3067\u306a\u3044\u884c\u306b\u8a2d\u7f6e\u3055\u308c\u307e\u3059\u3002
connect4.2=\u6a2a\u7dda\u3001\u65b9\u5411\u306e\u307f\u3082\u306a\u3057\u3067\u30014\u3064\u306e\u8ca0\u3051\u3092\u7d50\u5408\u3055\u305b\u308b\u3068\u52dd\u3061\u307e\u3059! \u4e0a\u306e\u4f8b\u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
reversi1=\u30ea\u30d0\u30fc\u30b7\u30fb\u30b2\u30fc\u30e0\u3078\u3088\u3046\u3053\u305d! \u30ab\u30e9\u30e0\u306e\u30b9\u30dd\u30c3\u30c8\u30c9\u30c3\u30c8\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u52d5\u304b\u3057\u306e\u30d7\u30ec\u30a4\u304c\u3067\u304d\u307e\u3059\u3002
reversi2=\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u3001\u3064\u306a\u304c\u308a\u3092\u542b\u3081\u305f\u8ca0\u3051\u304c\u307e\u3067\u306e\u8ca0\u3051\u304c\u5909\u308f\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002
reversi3=\u6b21\u306e\u52d5\u304b\u3057\u304c\u306a\u3044\u5834\u5408\u3001\u8a8d\u5b9a\u3055\u308c\u305f\u52d5\u304b\u3057\u306e\u6642\u9593\u306f\u62d2\u7d76\u3055\u308c\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002
reversi4=\u672c\u6b21\u306b\u30dc\u30fc\u30c9\u4e0a\u3067\u6700\u591a\u306e\u8ca0\u3051\u3092\u6301\u3064\u30d7\u30ec\u30a4\u30e4\u30fc\u304c\u52dd\u3061\u307e\u3059\u3002
tutorialstring=\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u30a2\u30e9\u30d3\u30a2\u8a9e)
chinese=\u4e2d\u6587 (\u4e2d\u6587)

View File

@@ -1,52 +1,88 @@
ai=\uc778\uacf5 \uc9c0\ub2a5
appTitle=ISY \uac8c\uc784 \uc120\ud0dd\uae30
back=\ub4a4\ub85c
backToMainMenu=\uBA54\uC778 \uBA54\uB274\uB85C \uB3CC\uC544\uAC00\uAE30
computer=\uce74\ud14c\uae4c
computerDifficulty=\uce74\ud14c\uae4c \ub2e8\uacc4
computerThinkTime=\uc870\ub9ac\ud558\ub294 \uc2dc\uac04
congratulations=\uCD95\uD558\uD569\uB2C8\uB2E4
connect=\uc5f0\uacb0
connectionType=\uC5F0\uACB0 \uC720\uD615
credits=\uac10\uc0ac
dark-hc=\uC5B4\uB460 (\uACE0 \uB300\uBE44)
accept=\uC218\uB77D
ai=\uC778\uACF5 \uC9C0\uB2A5
app-title=ISY \uAC8C\uC784 \uC120\uD0DD\uAE30
are-you-sure=\uC815\uB9D0\uB85C \uD655\uC2E4\uD569\uB2C8\uAE4C?
back=\uB4A4\uB85C
cancel=\uCDE8\uC18C
challenge=\uCC38\uC5EC
computer-difficulty=\uCEF4\uD4E8\uD130 \uC5B4\uB9AC\uAE30
computer-think-time=\uCEF4\uD4E8\uD130 \uC0DD\uAC01 \uC2DC\uAC04
computer=\uCEF4\uD4E8\uD130
connect=\uC5F0\uACB0
connect4=Connect 4
credits=\uD06C\uB808\uB527
dark=\uC5B4\uB460
developers=\uac1c\ubc1c\uc790
drawText=\uAC8C\uC784\uC740 \uBB34\uC2B9\uBD84\uC73C\uB85C \uB05D\uB0AC\uC2B5\uB2C8\uB2E4
effectsVolume=\ud654\uac70 \ubc84\uc804
musicVolume=Music Volume (translate me)
fullscreen=\uc804\uccb4 \ud654\uba74
goodGameText=\uC88B\uC740 \uAC8C\uC784\uC774\uC600\uC2B5\uB2C8\uB2E4. \uC798 \uD50C\uB808\uC774\uD588\uC2B5\uB2C8\uB2E4.
human=\uc778\uac04
language=\uc5b8\uc5b4
deny=\uAC70\uBD80
developers=\uAC1C\uBC1C\uC790
disconnect=\uC5F0\uACB0 \uC624\uD508
effects-volume=\uD6A8\uACFC\uC74C \uBCFC\uB968
enter-the-server-ip=\uC11C\uBC84 IP\uB97C \uC785\uB825\uD558\uC138\uC694
enter-the-server-port=\uC11C\uBC84 \uD3EC\uD2B8\uB97C \uC785\uB825\uD558\uC138\uC694
enter-your-name=\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694
error=\uC624\uB958
exit=\uB098\uAC00\uAE30
forfeit=\uAE30\uAD6C
fullscreen=\uC804\uCCB4 \uD654\uBA74
game-over=\uAC8C\uC784 \uC885\uB8CC
general=\uC77C\uBC18
high-contrast=\uACE0\uB3C4 \uB2E8\uC5B4
invalid-username=\uBB34\uD6A8\uD55C \uC0AC\uC6A9\uC790 \uC774\uB984
ip-address=IP \uC8FC\uC18C
is-not-a-valid-ip-address=\uC62C\uBC14\uB978 IP \uC8FC\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4
is-not-a-valid-port=\uC62C\uBC14\uB978 \uD3EC\uD2B8\uAC00 \uC544\uB2D9\uB2C8\uB2E4
language=\uC5B8\uC5B4
large=\uD070
layoutSize=\uB808\uC774\uC544\uC6C3 \uD06C\uAE30
light-hc=\uBC1D\uC74C (\uACE0 \uB300\uBE44)
light=\uBC1D\uC74C
local=\ub85c\uceec
localization=\uc5b8\uc5b4\ud654
layout-size=\uB77C\uC774\uC6C3 \uD06C\uAE30
light=\uBCF4\uD1B5
local=\uB85C\uCEEC
localization=\uB85C\uCEEC\uB9AC\uC870
master-volume=\uBAA8\uB4E0 \uBCFC\uB968
medium=\uBCF4\uD1B5
mergeCommander=\uba54\uc9c0 \ucea0\ub9ac\ub354
moralSupport=\uc815\uc2e0\uc801 \uc9c0\uc6d0
no=\uc544\ub2c8\uc624
merge-commander=\uBA54\uC9C0 \uCEE8\uBA38\uB2C8
moral-support=\uC815\uC2E0\uC801 \uC9C0\uC6D0
music-volume=\uC74C\uC545 \uBCFC\uB968
name=\uC774\uB984
no=\uC544\uB2C8\uC624
never=\uc544\ub2c8\uc694, \uc800\ub294 \ud29c\ud1a0\ub9ac\uc5bc\uc744 \ub2e4\uc2dc\ub294 \ubcf4\uace0 \uc2f6\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.
ok=\uD655\uC778
online=\uC628\uB77C\uC778
opengl=OpenGL
options=\uc635\uc158
othello=\uc624\ud14c\ub85c
playerName=\ud50c\ub808\uc774\uc5b4 \uc774\ub984
productOwner=\uc81c\ud488 \uad00\ub9ac\uc790
quit=\uc885\ub8cc
quitSure=\uc815\ub9d0 \uc885\ub8cc\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?
scrumMaster=\uc2a4\ud06c\ub7fc \ub9c8\uc2a4\ud130
server=\uc11c\ubc84
serverIP=\uc11c\ubc84 IP
serverPort=\uc11c\ubc84 \ud3ec\ud2b8
small=\uC791\uC74C
start=\uc2dc\uc791
options=\uC635\uC158
play=\uC7AC\uC0DD
player-name=\uD50C\uB808\uC774\uC5B4 \uC774\uB984
player=\uD50C\uB808\uC774\uC5B4
please-enter-your-name=\uC774\uB984\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694
port=\uD3EC\uD2B8
product-owner=\uD504\uB85C\uB354\uD2B8 \uC624\uB108
quit=\uC885\uB8CC
reversi=\uB9AC\uBC84\uC2DC
scrum-master=\uC2A4\uD06C\uB7FC \uB9C8\uC2A4\uD130
send=\uBCF4\uB0B4\uAE30
server-information=\uC11C\uBC84 \uC815\uBCF4
small=\uC791\uC740
style=\uC2A4\uD0C0\uC77C
the-game-ended-in-a-draw=\uAC8C\uC784\uC774 \uBB34\uC2B9\uBD80\uB85C \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4
theme=\uC8FC\uC81C
tictactoe=\ud2f0\ud06c\ud0d0\ud1a0
volume=\ub9c8\uc2a4\ud130 \ubcfc\ub80c
windowed=\ucc3d \ubaa8\ub4dc
yes=\ub124
tic-tac-toe=\uD2F0\uD06C\uD0D1\uD1A0
tictactoe1 =\ud2f1\ud0dd\ud1a0 \uac8c\uc784\uc5d0 \uc624\uc2e0 \uac83\uc744 \ud658\uc601\ud569\ub2c8\ub2e4! \ubcf4\ub4dc\uc758 9\uac1c \uce78 \uc911 \ud558\ub098\ub97c \ud074\ub9ad\ud558\uc5ec \uc6c0\uc9c1\uc77c \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc704\uc758 \uc608\uc2dc\ub97c \ucc38\uace0\ud558\uc138\uc694:
tictactoe2 =\ud50c\ub808\uc774\uc5b4\ub294 \uac00\ub85c, \uc138\ub85c \ub610\ub294 \ub300\uac01\uc120\uc73c\ub85c \ub9d0 3\uac1c\ub97c \uc77c\ub82c\ub85c \ub193\uc73c\uba74 \uc2b9\ub9ac\ud569\ub2c8\ub2e4. \uc704\uc758 \uc608\uc5d0\uc11c \ud50c\ub808\uc774\uc5b4\ub294 \ub300\uac01\uc120\uc73c\ub85c \uc138 \uac1c\ub97c \uc5f0\uacb0\ud558\uc5ec \uc774\uacbc\uc2b5\ub2c8\ub2e4.
to-a-game-of=... \uAC8C\uC784\uC5D0
tutorial=\uc774 \uac8c\uc784\uc758 \ud29c\ud1a0\ub9ac\uc5bc\uc744 \ubcf4\uace0 \uc2f6\ub098\uc694?
volume=\uBCFC\uB968
windowed=\uCC3D \uBAA9\uB85D
yes=\uB124
you-lost-against=... \uC5D0\uAC8C \uC9C0\uC600\uC2B5\uB2C8\uB2E4
you-were-challenged-by=... \uB85C\uBD80\uD130 \uCC38\uC5EC \uC694\uCCAD\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4
you-win=\uC774\uACBC\uC2B5\uB2C8\uB2E4
>=>
<=<
connect4.1=Connect 4 \uacbd\uc6b0\uc5d0 \uc81c\uc2dc\ud569\ub2c8\ub2e4! \ud648\ub825\uc744 \ub2e4\ub978 \uc0c1\uc704\ub85c \ud074\ub9ad\ud558\uc2dc\uba70 \ub2e4\uc74c \uc815\uc758 \ud648\ub825\uc744 \ub610\ub294 \uc704\ub85c \uc0ac\uc6a9\ud558\uc2dc\uba70 \ud648\ub825\uc744 \uc124\uc815\ud569\ub2c8\ub2e4.
connect4.2=\uc0ac\uc6a9\uc790\uc758 \ud648\uc744 \uc54c\ub824 \ud574\uc8fc\uba70 \ud574\ub2f9 \ud648\uc758 4\uae38\uc744 \ud574\uc8fc\uba70 \ud655\uc9c0, \ub354\ub7ec \ubc29\uacfc \ub610\ub294 \uc0ac\uc6a9\uc790 \ud648\uc758 \uc5f4\ub9b0 \ucd5c\ub300 \ubc29\ud574\uc5d0 \uc5c6\uc74c\uc774\ub2e4!
reversi1=Reversi \uacbd\uc6b0\uc5d0 \uc81c\uc2dc\ud569\ub2c8\ub2e4! \ub2e4\ub978 \ud615\uc2dd\uc758 \ud648\uc744 \ud074\ub9ad\ud558\uc2dc\uba70 \ub2e4\uc74c \uc815\uc758 \ud648\uc744 \uc124\uc815\ud569\ub2c8\ub2e4.
reversi2=\ud074\ub9ad \ud558\uba70, \ub2e4\ub978 \ud648 \uc704\ub85c \ucd5c\uc2e0 \ubc1b\ub294 \ud648\uc5d0 \ub300\ud574 \ub2e4\uc774\ubc84\ub77c\uc758 \ud648\uc744 \ubcc0\uacbd\ud569\ub2c8\ub2e4.
reversi3=\uc0ac\uc6a9\uc790\uc758 \ud648\uc744 \ud074 \uc218 \uc5c6\uc2b5\uc2b5\ub2c8\ub2e4. \uc0ac\uc6a9\uc790 \ub2f5\uc5d0 \ub300\ud574 \uc811\ub2c8\ub2e4.
reversi4=\uacbd\uc6b0 \uc5d0\uc11c \ucd5c\ub300 \ud648\uc744 \uac00\uc838\ub294 \uc0ac\uc6a9\uc790\uc774 \uc52c\uc544\uc624\uba70 \uc0ac\uc6a9\uc790\uc758 \ud648\uc744 \uc54c\ub824\ud569\ub2c8\ub2e4.
tutorialstring=\ud14c\ud2b8\ub9ad
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0639\u0631\u0628\u064a\u0629)
chinese=\u4e2d\u6587 (\u4e2d\u6587)

View File

@@ -1,52 +1,88 @@
accept=Accepteren
ai=Kunstmatige Intelligentie
appTitle=ISY Spel Kiezer
app-title=ISY Spel Kiezer
are-you-sure=Weet je het zeker?
back=Terug
backToMainMenu=Terug naar hoofdmenu
cancel=Annuleren
challenge=Uitdaging
computer-difficulty=Computermoeilijkheid
computer-think-time=Denktijd computer
computer=Computer
computerDifficulty=Computermoeilijkheid
computerThinkTime=Computer Denk Tijd
congratulations=Gefeliciteerd
connect=Verbinden
connectionType=Verbindingstype
credits=Credits
dark-hc=Donker (Hoog Contrast)
dark=Donker
deny=Weigeren
developers=Ontwikkelaars
drawText=Het spel eindigde in een gelijkspel
effectsVolume=Effecten Volume
musicVolume=Music Volume (translate me)
disconnect=Verbreken
effects-volume=Effectvolume
enter-the-server-ip=Voer het server-IP in
enter-the-server-port=Voer de serverpoort in
enter-your-name=Voer je naam in
error=Fout
exit=Afsluiten
forfeit=Opgeven
fullscreen=Volledig scherm
goodGameText=Goed gespeeld. Mooie wedstrijd.
human=Mens
game-over=Spel afgelopen
general=Algemeen
high-contrast=Hoog contrast
invalid-username=ongeldige gebruikersnaam
ip-address=IP-adres
is-not-a-valid-ip-address=is geen geldig IP-adres
is-not-a-valid-port=is geen geldige poort
language=Taal
large=Groot
layoutSize=Lay-outgrootte
light-hc=Licht (Hoog Contrast)
layout-size=Lay-outgrootte
light=Licht
local=Lokaal
localization=Lokalisatie
medium=Middel
mergeCommander=Merge-commandant
moralSupport=Moraalsteun
master-volume=Hoofdvolume
medium=Middelgroot
merge-commander=Merge Commander
moral-support=Morele steun
music-volume=Muziekvolume
name=Naam
never=Nee, ik wil nooit tutorials zien.
no=Nee
ok=Ok<EFBFBD>
online=Online
opengl=OpenGL
options=Opties
othello=Othello
playerName=Spelernaam
productOwner=Producteigenaar
quit=Afsluiten
quitSure=Weet je het zeker?
scrumMaster=Scrum Master
server=Server
serverIP=Server-IP
serverPort=Serverpoort
play=Spelen
player-name=Spelersnaam
player=Speler
please-enter-your-name=Voer alstublieft je naam in
port=Poort
product-owner=Producteigenaar
quit=Stoppen
reversi=Reversi
scrum-master=Scrum Master
send=Verzenden
server-information=Serverinformatie
small=Klein
start=Start
style=Stijl
the-game-ended-in-a-draw=Het spel eindigde in een gelijkspel
theme=Thema
tictactoe=Boter Kaas en Eieren
volume=Hoofdvolume
tic-tac-toe=Boter Kaas en Eieren
tictactoe1 =Welkom bij het spel Boter, Kaas en Eieren! Je kunt je zet doen door op een van de 9 vakjes op het bord te klikken. Voorbeeld hierboven:
tictactoe2 =Een speler wint door 3 stukken op een rij te krijgen ? horizontaal, verticaal of diagonaal. In het voorbeeld hierboven wint de speler met een diagonale rij van drie stukken.
connect4=Vier op een rij
to-a-game-of=voor een spelletje
tutorial=Wil je een tutorial voor dit spel zien?
volume=Volume
windowed=Venstermodus
yes=Ja
you-lost-against=Je hebt verloren van
you-were-challenged-by=Je bent uitgedaagd door
you-win=Je wint
>=>
<=<
connect4.1=Welkom bij het spel Connect 4! Je kunt een zet doen door op een van de kolommen te klikken. De zet wordt geplaatst in de laagste nog lege rij van die kolom.
connect4.2=Je kunt winnen door 4 van je stukken horizontaal, diagonaal of verticaal op een rij te krijgen! Zie het voorbeeld hierboven.
reversi1=Welkom bij het spel Reversi! Je kunt een zet doen door op een van de licht transparante stippen te klikken.
reversi2=Door op een stip te klikken draai je alle stukken om tussen de plaats waar je de stip zet en de volgende stip die wordt gevonden. Zie het voorbeeld hierboven, waar geel de stukken zijn die omgedraaid worden.
reversi3=Je beurt kan worden overgeslagen als er geen legale zet is. Hierdoor kan je tegenstander doorgaan tot jij een legale zet kunt doen.
reversi4=De speler die aan het einde van het spel de meeste stukken op het bord heeft, wint.
tutorialstring=Tutorial
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabisch)
chinese=\u4e2d\u6587 (Chinees)

View File

@@ -1,52 +1,88 @@
accept=\u041f\u0440\u0438\u043d\u044f\u0442\u044c
ai=\u0418\u0441\u043a\u0443\u0441\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442
appTitle=ISY \u0412\u044b\u0431\u043e\u0440 \u0438\u0433\u0440
app-title=ISY \u0412\u044b\u0431\u043e\u0440 \u0438\u0433\u0440
are-you-sure=\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b?
back=\u041d\u0430\u0437\u0430\u0434
backToMainMenu=\u041D\u0430\u0437\u0430\u0434 \u0432 \u0433\u043B\u0430\u0432\u043D\u043E\u0435 \u043C\u0435\u043D\u044E
cancel=\u041e\u0442\u043c\u0435\u043d\u0430
challenge=\u0412\u044b\u0437\u043e\u0432
connect4=Connect 4
computer-difficulty=\u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430
computer-think-time=\u0412\u0440\u0435\u043c\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430
computer=\u041a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440
computerDifficulty=\u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430
computerThinkTime=\u0412\u0440\u0435\u043c\u044f \u043e\u0431\u0434\u0443\u043c\u044b \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430
congratulations=\u041F\u043E\u0437\u0434\u0440\u0430\u0432\u043B\u044F\u0435\u043C
connect=\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f
connectionType=\u0442\u0438\u043F \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F
credits=\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u043d\u043e\u0441\u0442\u0438
dark-hc=\u0442\u0451\u043C\u043D\u044B\u0439 (\u0432\u044B\u0441\u043E\u043A\u0438\u0439 \u043A\u043E\u043D\u0442\u0440\u0430\u0441\u0442)
dark=\u0442\u0451\u043C\u043D\u044B\u0439
credits=\u041a\u0440\u0435\u0434\u0438\u0442\u044b
dark=\u0422\u0451\u043c\u043d\u044b\u0439
deny=\u041e\u0442\u043a\u0430\u0437\u0430\u0442\u044c
developers=\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438
drawText=\u0418\u0433\u0440\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u043D\u0438\u0447\u044C\u0435\u0439
effectsVolume=\u042d\u0444\u0444\u0435\u043a\u0442\u044b \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c
musicVolume=Music Volume (translate me)
disconnect=\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f
effects-volume=\u0413\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432
enter-the-server-ip=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP \u0441\u0435\u0440\u0432\u0435\u0440\u0430
enter-the-server-port=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0440\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430
enter-your-name=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448\u0435 \u0438\u043c\u044f
error=\u041e\u0448\u0438\u0431\u043a\u0430
exit=\u0412\u044b\u0445\u043e\u0434
forfeit=\u0421\u0434\u0430\u0442\u044c \u0441\u043e\u0440\u043e\u0432\u043d\u043e\u0441\u0442\u0438
fullscreen=\u041f\u043e\u043b\u043d\u043e\u044d\u043a\u0440\u0430\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c
goodGameText=\u0425\u043E\u0440\u043E\u0448\u0430\u044F \u0438\u0433\u0440\u0430. \u0425\u043E\u0440\u043E\u0448\u043E \u0441\u044B\u0433\u0440\u0430\u043D\u043E.
human=\u0427\u0435\u043b\u043e\u0432\u0435\u043a
game-over=\u0418\u0433\u0440\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430
general=\u041e\u0431\u0449\u0438\u0435
high-contrast=\u0412\u044B\u0441\u043E\u043A\u0430\u044F \u043A\u043E\u043D\u0442\u0440\u0430\u0441\u0442\u043D\u043E\u0441\u0442\u044C
invalid-username=\u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0438\u043c\u044f
ip-address=IP \u0430\u0434\u0440\u0435\u0441
is-not-a-valid-ip-address=\u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c IP \u0430\u0434\u0440\u0435\u0441\u043e\u043c
is-not-a-valid-port=\u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u0440\u0442\u043e\u043c
language=\u042f\u0437\u044b\u043a
large=\u0431\u043E\u043B\u044C\u0448\u043E\u0439
layoutSize=\u0440\u0430\u0437\u043C\u0435\u0440 \u043C\u0430\u043A\u0435\u0442\u0430
light-hc=\u0441\u0432\u0435\u0442\u043B\u044B\u0439 (\u0432\u044B\u0441\u043E\u043A\u0438\u0439 \u043A\u043E\u043D\u0442\u0440\u0430\u0441\u0442)
light=\u0441\u0432\u0435\u0442\u043B\u044B\u0439
large=\u0411\u043e\u043b\u044c\u0448\u043e\u0439
layout-size=\u0420\u0430\u0437\u043c\u0435\u0440 \u043c\u043e\u043d\u0442\u0430\u0436\u0430
light=\u0421\u0432\u0435\u0442\u043b\u044b\u0439
local=\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439
localization=\u041b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f
medium=\u0441\u0440\u0435\u0434\u043D\u0438\u0439
mergeCommander=\u041a\u043e\u043c\u0430\u043d\u0434\u0435\u0440 \u0441\u043b\u0438\u044f\u043d\u0438\u044f
moralSupport=\u041c\u043e\u0440\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430
master-volume=\u041e\u0431\u0449\u0438\u0439 \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c
medium=\u0421\u0440\u0435\u0434\u043d\u0438\u0439
merge-commander=Merge Commander
moral-support=\u041c\u043e\u0440\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430
music-volume=\u0413\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u043c\u0443\u0437\u044b\u043a\u0438
name=\u0418\u043c\u044f
never=\u041d\u0435\u0442, \u044f \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0445\u043e\u0447\u0443 \u0432\u0438\u0434\u0435\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u0443\u0447\u0435\u0431\u043d\u0438\u043a\u0438.
no=\u041d\u0435\u0442
ok=OK
online=\u0412 \u0441\u0435\u0442\u0438
opengl=OpenGL
options=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438
othello=\u041e\u0442\u0435\u043b\u043b\u043e
playerName=\u0418\u043c\u044f \u0438\u0433\u0440\u043e\u043a\u0430
productOwner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430
play=\u0418\u0433\u0440\u0430\u0442\u044c
player-name=\u0418\u043c\u044f \u0438\u0433\u0440\u043e\u043a\u0430
player=\u0418\u0433\u0440\u043e\u043a
please-enter-your-name=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448\u0435 \u0438\u043c\u044f
port=\u041f\u043e\u0440\u0442
product-owner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430
quit=\u0412\u044b\u0445\u043e\u0434
quitSure=\u0423\u0432\u0435\u0440\u0435\u043d\u044b \u043b\u0438?
scrumMaster=\u041c\u0430\u0441\u0442\u0435\u0440 Scrum
server=\u0421\u0435\u0440\u0432\u0435\u0440
serverIP=\u0418\u043f\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430
serverPort=\u041f\u043e\u0440\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430
small=\u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u0439
start=\u0421\u0442\u0430\u0440\u0442
theme=\u0442\u0435\u043C\u0430
tictactoe=\u041a\u0440\u0435\u0441\u0442\u0438\u043a\u0438
volume=\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c
reversi=\u0420\u0435\u0432\u0435\u0440\u0441\u0438
scrum-master=Scrum Master
send=\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c
server-information=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0435
small=\u041c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439
style=\u0421\u0442\u0438\u043b\u044c
the-game-ended-in-a-draw=\u0418\u0433\u0440\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u043d\u0438\u0447\u044c\u0435\u0439
theme=\u0422\u0435\u043c\u0430
tic-tac-toe=\u041a\u0440\u0435\u0441\u0442\u0438\u043a\u043e
tictactoe1 =\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0438\u0433\u0440\u0443 \u041a\u0440\u0435\u0441\u0442\u0438\u043a\u0438-\u043d\u043e\u043b\u0438\u043a\u0438! \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0434, \u043d\u0430\u0436\u0430\u0432 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 9 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u043e\u0432 \u043d\u0430 \u043f\u043e\u043b\u0435. \u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0448\u0435:
tictactoe2 =\u0418\u0433\u0440\u043e\u043a \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0441\u0442\u0430\u0432\u0438\u0442 3 \u0441\u0438\u043c\u0432\u043e\u043b\u0430 \u043f\u043e\u0434\u0440\u044f\u0434 ? \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438, \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438 \u0438\u043b\u0438 \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u0438. \u0412 \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u043c \u0432\u044b\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0433\u0440\u043e\u043a \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442 \u043f\u043e \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u0438
to-a-game-of=\u043a \u0438\u0433\u0440\u0435 \u0432
tutorial=\u0425\u043e\u0447\u0435\u0448\u044c \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0443\u0447\u0435\u0431\u043d\u0438\u043a \u043f\u043e \u044d\u0442\u043e\u0439 \u0438\u0433\u0440\u0435?
volume=\u0413\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c
windowed=\u041e\u043a\u043d\u043e
yes=\u0414\u0430
you-lost-against=\u0412\u044b \u043f\u0440\u043e\u0438\u0433\u0440\u0430\u043b\u0438 \u043a\u043e\u043c\u0443
you-were-challenged-by=\u0412\u0430\u0441 \u0432\u044b\u0437\u0432\u0430\u043b \u043d\u0430 \u0441\u043e\u0440\u0435\u0432\u043d\u0438\u043a
you-win=\u0412\u044b \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442\u0435
>=>
<=<
connect4.1=\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0438\u0433\u0440\u0443 Connect 4! \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0434, \u043a\u043b\u0438\u043a\u043d\u0443\u044f \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432. \u0425\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d \u0432 \u043d\u0438\u0436\u0430\u0439 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0432 \u044d\u0442\u043e\u0439 \u043a\u043e\u043b\u043e\u043d\u043a\u0435.
connect4.2=\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0432 4 \u0444\u0438\u0448\u043a\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u0446\u0432\u0435\u0442\u0430 \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e, \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u044c\u043d\u043e \u0438\u043b\u0438 \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e!
reversi1=\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0438\u0433\u0440\u0443 Reversi! \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0434, \u043a\u043b\u0438\u043a\u043d\u0443\u044f \u043f043 \u043f043 \u043d043 \u043e043 \u043d043 \u043e043 \u043a043 \u0430043 \u043a043 \u0430043.
reversi2=\u041d043 \u043d043 \u0430043 \u043a043 \u0430043 \u043a043 \u043e043 \u043c043 \u0435043 \u0436043 \u0435043 \u0434043 \u0435043 \u043d043 \u0430043.
reversi3=\u0412043 \u0430043 \u0436043 \u0434043 \u0430043 \u043d043 \u0438043 \u043d043 \u0435043 \u0435043 \u0432043 \u0430043.
reversi4=\u0418043 \u0433043 \u0440043 \u043e043 \u043a043 \u043e043 \u0442043 \u043e043 \u0442043 \u043e043 \u0435043 \u0435043 \u0430043 \u0435043 \u043d043 \u0438043 \u0435043 \u0435043 \u043c043 \u0430043.
tutorialstring=\u0423\u0447\u0435\u0431\u043d\u0438\u043a
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0410\u0440\u0430\u0431\u0441\u043a\u0438\u0439)
chinese=\u4e2d\u6587 (\u041a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0439)

View File

@@ -1,52 +1,88 @@
accept=\u63a5\u53d7
ai=\u4eba\u5de5\u667a\u80fd
appTitle=ISY \u6e38\u620f\u9009\u62e9\u5668
app-title=ISY \u6e38\u620f\u9009\u62e9\u5668
are-you-sure=\u60a8\u786e\u5b9a\u5417\uff1f
back=\u8fd4\u56de
backToMainMenu=\u8FD4\u56DE\u4E3B\u83DC\u5355
cancel=\u53d6\u6d88
challenge=\u6311\u6218
computer-difficulty=\u8ba1\u7b97\u673a\u96be\u5ea6
computer-think-time=\u8ba1\u7b97\u673a\u601d\u8003\u65f6\u95f4
computer=\u8ba1\u7b97\u673a
computerDifficulty=\u8ba1\u7b97\u673a\u96be\u5ea6
computerThinkTime=\u7535\u8111\u8003\u616e\u65f6\u95f4
congratulations=\u606D\u559C
connect=\u8fde\u63a5
connectionType=\u8FDE\u63A5\u7C7B\u578B
credits=\u6b23\u8d4f
dark-hc=\u6697 (\u9AD8\u5BF9\u6BD4)
dark=\u6697
connect4=Connect 4
credits=\u81f4\u8c22
dark=\u6697\u8272
deny=\u62d2\u7edd
developers=\u5f00\u53d1\u8005
drawText=\u6E38\u620F\u4EE5\u5E73\u5C40\u7ED3\u675F
effectsVolume=\u6548\u679c\u91cf
musicVolume=Music Volume (translate me)
disconnect=\u65ad\u5f00\u8fde\u63a5
effects-volume=\u6548\u679c\u97f3\u91cf
enter-the-server-ip=\u8f93\u5165\u670d\u52a1\u5668 IP
enter-the-server-port=\u8f93\u5165\u670d\u52a1\u5668\u7aef\u53e3
enter-your-name=\u8f93\u5165\u60a8\u7684\u540d\u5b57
error=\u9519\u8bef
exit=\u9000\u51fa
forfeit=\u68c0\u8f93
fullscreen=\u5168\u5c4f
goodGameText=\u6E38\u620F\u5F88\u597D. \u8868\u73B0\u4F18\u79C0.
human=\u4eba
game-over=\u6e38\u620f\u7ed3\u675f
general=\u901a\u7528
high-contrast=\u9AD8\u5BF9\u6BD4\u5EA6
invalid-username=\u7528\u6237\u540d\u65e0\u6548
ip-address=IP \u5730\u5740
is-not-a-valid-ip-address=\u4e0d\u662f\u6709\u6548\u7684 IP \u5730\u5740
is-not-a-valid-port=\u4e0d\u662f\u6709\u6548\u7684\u7aef\u53e3
language=\u8bed\u8a00
large=\u5927
layoutSize=\u5E03\u5C40\u5927\u5C0F
light-hc=\u4EAE (\u9AD8\u5BF9\u6BD4)
light=\u4EAE
layout-size=\u5e03\u5c40\u5927\u5c0f
light=\u4eae\u8272
local=\u672c\u5730
localization=\u6d88\u606f\u5c55\u793a / \u672c\u5730\u5316
medium=\u4E2D
mergeCommander=Merge \u63a7\u4e3b
moralSupport=\u795e\u7cbe\u652f\u6301
no=\u5426
localization=\u672c\u5730\u5316
master-volume=\u603b\u97f3\u91cf
medium=\u4e2d
merge-commander=\u5408\u5e76\u6307\u6325
moral-support=\u7cbe\u795e\u652f\u6301
music-volume=\u97f3\u4e50\u97f3\u91cf
name=\u540d\u5b57
never=\u4e0d\uff0c\u6211\u5b8c\u5168\u4e0d\u60f3\u518d\u770b\u5230\u4efb\u4f55\u6559\u7a0b\u3002
no=\u4e0d
ok=\u786e\u5b9a
online=\u5728\u7ebf
opengl=OpenGL
options=\u9009\u9879
othello=Othello
playerName=\u73a9\u5bb6\u540d\u79f0
productOwner=\u54c1\u76d8\u4ea7\u54c1\u4eba
play=\u6e38\u73a9
player-name=\u73a9\u5bb6\u540d
player=\u73a9\u5bb6
please-enter-your-name=\u8bf7\u8f93\u5165\u60a8\u7684\u540d\u5b57
port=\u7aef\u53e3
product-owner=\u4ea7\u54c1\u62e5\u6709\u8005
quit=\u9000\u51fa
quitSure=\u4f60\u786e\u5b9a\u5417?
scrumMaster=Scrum \u4f1a\u5458
server=\u670d\u52a1\u5668
serverIP=\u670d\u52a1\u5668 IP
serverPort=\u670d\u52a1\u5668 \u7aef\u53e3
small=\u5C0F
start=\u5f00\u59cb
theme=\u4E3B\u9898
tictactoe=Tic Tac Toe
volume=\u4e3b\u97f3\u91cf
reversi=\u9ed1\u767d\u68cb
scrum-master=\u9879\u76ee\u7ecf\u7406
send=\u53d1\u9001
server-information=\u670d\u52a1\u5668\u4fe1\u606f
small=\u5c0f
style=\u98ce\u683c
the-game-ended-in-a-draw=\u6e38\u620f\u4ee5\u548c\u5c40\u7ed3\u675f
theme=\u4e3b\u9898
tic-tac-toe=\u4e09\u5b9a\u7ebf
tictactoe1 =\u6b22\u8fce\u6765\u5230\u4e95\u5b57\u68cb\u6e38\u620f\uff01\u4f60\u53ef\u4ee5\u901a\u8fc7\u70b9\u51fb\u68cb\u76d8\u4e0a\u4efb\u610f\u4e00\u4e2a\u4e5d\u4e2a\u65b9\u683c\u6765\u843d\u5b50\u3002\u4e0a\u65b9\u793a\u4f8b\uff1a
tictactoe2 =\u73a9\u5bb6\u5728\u6a2a\u3001\u7ad6\u6216\u659c\u7ebf\u4e0a\u8fde\u7eed\u653e\u7f6e\u4e09\u4e2a\u68cb\u5b50\u5373\u53ef\u83b7\u80dc\u3002\u5728\u4e0a\u65b9\u7684\u793a\u4f8b\u4e2d\uff0c\u73a9\u5bb6\u901a\u8fc7\u659c\u7ebf\u4e0a\u7684\u4e09\u4e2a\u68cb\u5b50\u83b7\u80dc\u4e86\u6bd4\u8d5b\u3002
to-a-game-of=\u6311\u6218\u4e00\u573a
tutorial=\u4f60\u60f3\u770b\u8fd9\u4e2a\u6e38\u620f\u7684\u6559\u7a0b\u5417\uff1f
volume=\u97f3\u91cf
windowed=\u7a97\u53e3\u6a21\u5f0f
yes=\u662f
you-lost-against=\u60a8\u8f93\u7ed9\u4e86
you-were-challenged-by=\u60a8\u88ab\u6311\u6218\u81ea
you-win=\u60a8\u83b7\u80dc\u4e86
>=>
<=<
connect4.1=\u6b22\u8fce\u6765\u5230 Connect 4 \u6e38\u620f! \u4f60\u53ef\u4ee5\u70b9\u51fb\u4e00\u5217\u6761\u76ee\u64cd\u4f5c. \u64cd\u4f5c\u5c06\u88c5\u7f6e\u5728\u672a\u88c5\u5165\u7684\u6700\u4f4e\u884c.
connect4.2=\u5982\u679c\u5f97\u52304\u4e2a\u5bf9\u5e94\u7684\u4ee3\u7406\u7ec4\u6210\u6c34\u5e73\u3001\u5347\u5e26\u6216\u5782\u76f4\u5373\u53ef\u80dc. \u770b\u4e0a\u65b9\u793a\u4f8b.
reversi1=\u6b22\u8fce\u6765\u5230 Reversi \u6e38\u620f! \u4f60\u53ef\u4ee5\u70b9\u51fb\u4e00\u4e2a\u9ed8\u8272\u5149\u900f\u7a7a\u70b9\u64cd\u4f5c.
reversi2=\u70b9\u51fb\u4e00\u4e2a\u70b9\u65f6\u5c06\u5c06\u6240\u6709\u4e2d\u95f4\u7684\u4ee3\u7406\u7ffb\u8f6c\u3002 \u770b\u4e0a\u65b9\u793a\u4f8b\uff0c\u9ec4\u8272\u662f\u5bf9\u4ee3\u7406\u9700\u64ad\u7684\u4ee3\u7406.
reversi3=\u5982\u679c\u6ca1\u6709\u5408\u6cd5\u64cd\u4f5c\u4f60\u7684\u8fdb\u6b65\u53ef\u80fd\u88ab\u5ffd\u7565. \u8fd9\u4f1a\u8ba9\u5bf9\u624b\u518d\u6b21\u64cd\u4f5c\u5230\u4f60\u6709\u5408\u6cd5\u64cd\u4f5c\u65f6.
reversi4=\u672c\u6e38\u620f\u7ed3\u675f\u65f6\u8d62\u5f97\u6ee1\u8fc7\u76d8\u9762\u7684\u4ee3\u7406\u6570\u6700\u591a\u7684\u4eba\u5c31\u80dc.
tutorialstring=\u6559\u7a0b
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u963f\u62c9\u4f2f\u8bed)
chinese=\u4e2d\u6587

View File

@@ -1,215 +0,0 @@
/* ----------------------------
.background
----------------------------- */
.bg-primary {
-fx-background-color: #0a0a0a;
}
.bg-secondary {
-fx-background-color: #1a1a1a;
}
.bg-popup {
-fx-background-color: #0a0a0acc;
}
/* ----------------------------
.button
----------------------------- */
.button {
/* Layout */
-fx-padding: 10 20;
-fx-background-radius: 6;
-fx-cursor: hand;
/* Color */
-fx-background-color: #1a1a1a;
-fx-text-fill: #ffffff;
-fx-border-color: #ffffff;
-fx-border-width: 1;
/* Effects */
-fx-effect: dropshadow(gaussian, #00000099, 4, 0, 0, 1);
}
.button:hover {
-fx-background-color: #2a2a2a;
-fx-border-color: #00ff00;
}
/* ----------------------------
.choice-box
----------------------------- */
.choice-box {
/* Layout */
-fx-padding: 6;
-fx-background-radius: 4;
/* Color */
-fx-background-color: #1a1a1a;
-fx-border-color: #ffffff;
-fx-border-width: 1;
-fx-mark-color: #ffffff;
}
.choice-box:hover {
-fx-border-color: #00ff00;
}
.choice-box:focused {
-fx-border-color: #00cc66;
}
.choice-box .label {
-fx-text-fill: #ffffff;
}
/* ----------------------------
.choice-box popup styling
----------------------------- */
.choice-box .context-menu {
/* Layout */
-fx-padding: 4;
-fx-background-radius: 4;
/* Color */
-fx-background-color: #1a1a1a;
-fx-border-color: #ffffff;
-fx-border-width: 1;
}
.choice-box .menu-item {
/* Layout */
-fx-padding: 6 12;
}
.choice-box .menu-item .label {
-fx-text-fill: #ffffff;
}
.choice-box .menu-item:hover {
-fx-background-color: #2a2a2a;
}
.choice-box .menu-item:focused {
-fx-background-color: #00ff00;
-fx-text-fill: #000000;
}
/* ----------------------------
.container
----------------------------- */
.container {
/* Layout */
-fx-padding: 10;
-fx-alignment: center;
/* Color */
-fx-background-color: #1a1a1a;
}
/* ----------------------------
.input
----------------------------- */
.input {
/* Layout */
-fx-padding: 8;
-fx-background-radius: 4;
/* Color */
-fx-background-color: #1a1a1a;
-fx-text-fill: #ffffff;
-fx-border-color: #ffffff;
-fx-border-width: 1;
}
.input:hover {
-fx-border-color: #00ff00;
}
.input:focused {
-fx-border-color: #00cc66;
}
/* ----------------------------
.separator
----------------------------- */
.separator {
/* Layout */
-fx-padding: 10 0;
}
.separator .line {
/* Color */
-fx-border-color: #ffffff;
-fx-border-width: 0 0 1 0;
}
/* ----------------------------
.slider
----------------------------- */
.slider {
/* Layout */
-fx-padding: 6 0;
/* Color */
-fx-background-color: transparent;
}
.slider .track {
/* Color */
-fx-background-color: linear-gradient(to left, #00ff00, #ff0000);
-fx-background-insets: 0;
-fx-background-radius: 2;
-fx-pref-height: 4;
}
.slider .thumb {
/* Color */
-fx-background-color: #ffffff;
-fx-background-radius: 50%;
/* Effects */
-fx-effect: dropshadow(gaussian, #000000aa, 4, 0, 0, 1);
}
.slider .thumb:hover {
-fx-scale-x: 1.2;
-fx-scale-y: 1.2;
}
/* ----------------------------
.text-header
----------------------------- */
.text-header {
-fx-fill: #ffffff;
-fx-text-fill: #ffffff;
}
/* ----------------------------
.text-normal
----------------------------- */
.text-normal {
-fx-fill: #f0f0f0;
-fx-text-fill: #f0f0f0;
}
/* ----------------------------
.toggle-button
----------------------------- */
.toggle {
/* Layout */
-fx-padding: 8 16;
-fx-background-radius: 6;
/* Color */
-fx-background-color: #1f1f1f;
-fx-text-fill: #ffffff;
-fx-border-color: #ffffff;
}
.toggle:hover {
-fx-background-color: #00ff00;
-fx-text-fill: #000000;
-fx-border-color: #00ff00;
}

View File

@@ -1,215 +1,199 @@
/* ----------------------------
.background
----------------------------- */
.bg-primary {
-fx-background-color: #181818;
-fx-background-color: linear-gradient(to bottom, #2a3a45, #1c2730);
}
.bg-secondary {
-fx-background-color: #2a2a2a;
-fx-background-color: linear-gradient(to bottom, #3b4a57, #2a3742);
}
.bg-popup {
-fx-background-color: #1818187f;
-fx-background-color: rgba(20, 40, 55, 0.9);
-fx-background-radius: 8;
-fx-effect: dropshadow(gaussian, #224455cc, 8, 0, 0, 2);
}
.song-display {
-fx-padding: 8 12 8 12;
-fx-spacing: 6px;
-fx-alignment: center;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.5), 6, 0.5, 0, 2);
-fx-min-width: 220px;
-fx-max-width: 260px;
}
.song-title {
-fx-font-size: 14px;
-fx-fill: white;
}
.progress-bar {
-fx-pref-width: 200px;
-fx-accent: red;
}
.progress-bar > .track {
-fx-background-radius: 30;
}
.progress-bar > .bar {
-fx-background-radius: 30px;
}
.progress-text {
-fx-font-size: 11px;
-fx-fill: white;
}
.skip-button {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: hand;
-fx-text-fill: white;
}
.skip-button .text {
-fx-fill: white;
}
.pause-button {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: hand;
-fx-text-fill: white;
}
.pause-button .text {
-fx-fill: white;
}
.previous-button {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: hand;
-fx-text-fill: white;
}
.previous-button .text {
-fx-fill: white;
}
/* ----------------------------
.button
----------------------------- */
.button {
/* Layout */
-fx-padding: 10 20;
-fx-background-radius: 6;
-fx-background-color: linear-gradient(to bottom, #1f5e20, #2e7d2e);
-fx-background-radius: 8;
-fx-border-color: #3caf3f;
-fx-cursor: hand;
/* Color */
-fx-background-color: #2a2a2a;
-fx-text-fill: #f0f0f0;
-fx-border-color: #3a3a3a;
-fx-border-width: 1;
/* Effects */
-fx-effect: dropshadow(gaussian, #0000004d, 4, 0, 0, 1);
-fx-effect: dropshadow(gaussian, #3caf3fdd, 6, 0, 0, 2);
-fx-text-fill: #e0f2e9;
-fx-font-weight: bold;
}
.button:hover {
-fx-background-color: #3a3a3a;
-fx-border-color: #4caf50;
-fx-background-color: linear-gradient(to bottom, #44a044, #57b257);
-fx-border-color: #2f7d2f;
}
/* ----------------------------
.choice-box
----------------------------- */
.choice-box {
/* Layout */
-fx-padding: 6;
-fx-background-radius: 4;
/* Color */
-fx-background-color: #2a2a2a;
-fx-border-color: #444444;
-fx-border-width: 1;
-fx-mark-color: #f0f0f0;
.combo-box {
-fx-background-color: linear-gradient(to bottom, #3c5155, #2a3a3e);
-fx-background-radius: 6;
-fx-border-color: #3caf3f;
-fx-cursor: hand;
}
.choice-box:hover {
-fx-border-color: #4caf50;
.combo-box:hover {
-fx-border-color: #55a755;
}
.choice-box:focused {
-fx-border-color: #81c784;
.combo-box:focused {
-fx-border-color: #88cc88;
-fx-effect: dropshadow(gaussian, #88cc8899, 5, 0, 0, 1);
}
.choice-box .label {
-fx-text-fill: #f0f0f0;
.combo-box .arrow,
.combo-box .arrow-button {
-fx-background-color: transparent;
-fx-border-color: transparent;
}
/* ----------------------------
.choice-box popup styling
----------------------------- */
.choice-box .context-menu {
/* Layout */
-fx-padding: 4;
-fx-background-radius: 4;
/* Color */
-fx-background-color: #2a2a2a;
-fx-border-color: #444444;
-fx-border-width: 1;
.combo-box .list-cell {
-fx-background-color: transparent;
-fx-text-fill: #a8d0a8;
}
.choice-box .menu-item {
/* Layout */
-fx-padding: 6 12;
.combo-box .list-view {
-fx-background-color: #264233;
-fx-background-radius: 6;
-fx-border-color: #3caf3f;
}
.choice-box .menu-item .label {
-fx-text-fill: #dddddd;
.combo-box-popup .list-cell {
-fx-text-fill: #a8d0a8;
}
.choice-box .menu-item:hover {
-fx-background-color: #3a3a3a;
.combo-box-popup .list-cell:hover {
-fx-background-color: #44a044;
}
.choice-box .menu-item:focused {
-fx-background-color: #4caf50;
-fx-text-fill: #ffffff;
.combo-box-popup .list-cell:selected {
-fx-background-color: #2e7d2e;
-fx-text-fill: #e0f2e9;
}
/* ----------------------------
.container
----------------------------- */
.container {
/* Layout */
-fx-padding: 10;
-fx-alignment: center;
/* Color */
-fx-background-color: #2a2a2a;
}
/* ----------------------------
.input
----------------------------- */
.input {
/* Layout */
-fx-padding: 8;
-fx-background-radius: 4;
/* Color */
-fx-background-color: #2a2a2a;
-fx-text-fill: #f0f0f0;
-fx-border-color: #444444;
-fx-border-width: 1;
-fx-background-color: linear-gradient(to bottom, #1f3a3b, #124040);
-fx-background-radius: 6;
-fx-border-color: #3caf3f;
-fx-text-fill: #a8d0a8;
-fx-font-weight: normal;
}
.input:hover {
-fx-border-color: #4caf50;
-fx-border-color: #55a755;
}
.input:focused {
-fx-border-color: #81c784;
-fx-border-color: #88cc88;
-fx-effect: dropshadow(gaussian, #88cc8899, 5, 0, 0, 1);
}
/* ----------------------------
.separator
----------------------------- */
.separator {
/* Layout */
-fx-padding: 10 0;
.my-turn {
-fx-fill: #e05656;
-fx-font-weight: bold;
}
.separator .line {
/* Color */
-fx-border-color: #3a3a3a;
-fx-border-width: 0 0 1 0;
-fx-border-color: #55a755;
-fx-opacity: 1;
}
/* ----------------------------
.slider
----------------------------- */
.slider {
/* Layout */
-fx-padding: 6 0;
/* Color */
-fx-background-color: transparent;
}
.slider .track {
/* Color */
-fx-background-color: linear-gradient(to left, #4caf50, #f44336);
-fx-background-insets: 0;
-fx-background-radius: 2;
-fx-pref-height: 4;
}
.slider .thumb {
/* Color */
-fx-background-color: #f0f0f0;
-fx-background-color: linear-gradient(to bottom, #2e7d2e, #44a044);
-fx-background-radius: 50%;
/* Effects */
-fx-effect: dropshadow(gaussian, #00000066, 4, 0, 0, 1);
-fx-effect: dropshadow(gaussian, #3caf3fdd, 5, 0, 0, 1);
}
.slider .thumb:hover {
-fx-scale-x: 1.2;
-fx-scale-y: 1.2;
-fx-scale-x: 1.15;
-fx-scale-y: 1.15;
}
/* ----------------------------
.text-header
----------------------------- */
.text-header {
-fx-fill: #f0f0f0;
-fx-text-fill: #f0f0f0;
}
/* ----------------------------
.text-normal
----------------------------- */
.text-normal {
-fx-fill: #dddddd;
-fx-text-fill: #dddddd;
}
/* ----------------------------
.toggle-button
----------------------------- */
.toggle {
/* Layout */
-fx-padding: 8 16;
.slider .track {
-fx-background-color: linear-gradient(to left, #57b257, #e05c5c);
-fx-background-insets: 0;
-fx-background-radius: 6;
/* Color */
-fx-background-color: #333333;
-fx-text-fill: #cccccc;
-fx-border-color: #4a4a4a;
}
.toggle:hover {
-fx-background-color: #4caf50;
-fx-text-fill: #ffffff;
-fx-border-color: #4caf50;
.text {
-fx-fill: #a8d0a8;
-fx-font-weight: normal;
-fx-text-fill: #a8d0a8;
}
.header {
-fx-fill: #88cc88;
-fx-font-weight: bold;
-fx-text-fill: #88cc88;
}

View File

@@ -0,0 +1,42 @@
.combo-box .list-cell {
-fx-alignment: CENTER;
-fx-padding: 0;
}
.container {
-fx-alignment: TOP_CENTER;
-fx-background-color: transparent;
}
.scroll,
.scroll .viewport {
-fx-background-color: transparent;
-fx-border-color: transparent;
}
.scroll .scroll-bar .decrement-arrow,
.scroll .scroll-bar .decrement-button,
.scroll .scroll-bar .increment-arrow,
.scroll .scroll-bar .increment-button {
-fx-padding: 0;
-fx-pref-height: 0;
-fx-pref-width: 0;
-fx-shape: "";
}
.scroll .scroll-bar .thumb {
-fx-background-color: #888;
-fx-background-insets: 0;
-fx-background-radius: 1px;
}
.scroll .scroll-bar:horizontal,
.scroll .scroll-bar:vertical {
-fx-pref-height: 4px;
-fx-pref-width: 4px;
}
.header,
.text {
-fx-font-family: "Arial";
}

View File

@@ -0,0 +1,199 @@
.bg-primary {
-fx-background-color: linear-gradient(to bottom, #121b22, #0a0f14);
}
.bg-secondary {
-fx-background-color: linear-gradient(to bottom, #1a2b35, #0d181f);
}
.bg-popup {
-fx-background-color: rgba(10, 20, 30, 0.95);
-fx-background-radius: 8;
-fx-effect: dropshadow(gaussian, #1f6a22ff, 10, 0, 0, 3);
}
.song-display {
-fx-padding: 8 12 8 12;
-fx-spacing: 6px;
-fx-alignment: center;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.5), 6, 0.5, 0, 2);
-fx-min-width: 220px;
-fx-max-width: 260px;
}
.song-title {
-fx-font-size: 14px;
-fx-fill: white;
}
.progress-bar {
-fx-pref-width: 200px;
-fx-accent: red;
}
.progress-bar > .track {
-fx-background-radius: 30;
}
.progress-bar > .bar {
-fx-background-radius: 30px;
}
.progress-text {
-fx-font-size: 11px;
-fx-fill: white;
}
.skip-button {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: hand;
-fx-text-fill: white;
}
.skip-button .text {
-fx-fill: white;
}
.pause-button {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: hand;
-fx-text-fill: white;
}
.pause-button .text {
-fx-fill: white;
}
.previous-button {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-cursor: hand;
-fx-text-fill: white;
}
.previous-button .text {
-fx-fill: white;
}
.button {
-fx-background-color: linear-gradient(to bottom, #1b7a1b, #2da32d);
-fx-background-radius: 8;
-fx-border-color: #28a428;
-fx-cursor: hand;
-fx-effect: dropshadow(gaussian, #28a428ff, 8, 0, 0, 3);
-fx-text-fill: #e0ffe0;
-fx-font-weight: bold;
}
.button:hover {
-fx-background-color: linear-gradient(to bottom, #45d045, #60e060);
-fx-border-color: #1c841c;
}
.combo-box {
-fx-background-color: linear-gradient(to bottom, #26473b, #1a2e25);
-fx-background-radius: 6;
-fx-border-color: #28a428;
-fx-cursor: hand;
}
.combo-box:hover {
-fx-border-color: #52c352;
}
.combo-box:focused {
-fx-border-color: #70e070;
-fx-effect: dropshadow(gaussian, #70e070cc, 6, 0, 0, 2);
}
.combo-box .arrow,
.combo-box .arrow-button {
-fx-background-color: transparent;
-fx-border-color: transparent;
}
.combo-box .list-cell {
-fx-background-color: transparent;
-fx-text-fill: #a8f0a8;
}
.combo-box .list-view {
-fx-background-color: #16321f;
-fx-background-radius: 6;
-fx-border-color: #28a428;
}
.combo-box-popup .list-cell {
-fx-text-fill: #a8f0a8;
}
.combo-box-popup .list-cell:hover {
-fx-background-color: #45d045;
}
.combo-box-popup .list-cell:selected {
-fx-background-color: #2da32d;
-fx-text-fill: #f0fff0;
}
.input {
-fx-background-color: linear-gradient(to bottom, #15331a, #0e2b15);
-fx-background-radius: 6;
-fx-border-color: #28a428;
-fx-text-fill: #a8f0a8;
-fx-font-weight: normal;
}
.input:hover {
-fx-border-color: #52c352;
}
.input:focused {
-fx-border-color: #70e070;
-fx-effect: dropshadow(gaussian, #70e070cc, 6, 0, 0, 2);
}
.my-turn {
-fx-fill: #ff4b4b;
-fx-font-weight: bold;
}
.separator .line {
-fx-border-color: #52c352;
-fx-opacity: 1;
}
.slider {
-fx-background-color: transparent;
}
.slider .thumb {
-fx-background-color: linear-gradient(to bottom, #2da32d, #45d045);
-fx-background-radius: 50%;
-fx-effect: dropshadow(gaussian, #28a428ff, 6, 0, 0, 2);
}
.slider .thumb:hover {
-fx-scale-x: 1.15;
-fx-scale-y: 1.15;
}
.slider .track {
-fx-background-color: linear-gradient(to left, #60e060, #ff5555);
-fx-background-insets: 0;
-fx-background-radius: 6;
}
.text {
-fx-fill: #a8f0a8;
-fx-font-weight: normal;
-fx-text-fill: #a8f0a8;
}
.header {
-fx-fill: #70e070;
-fx-font-weight: bold;
-fx-text-fill: #70e070;
}

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