commit e0583018f58130bded9ffcf212d42b5c860e347a parent eb3c2f4ea4a544109e30fbafa0224f5e5f7b3192 Author: A Koens <[email protected]> Date: Tue, 25 Oct 2022 19:56:10 +0200 Squashed commit of the following: commit 2418cea1a688c2457133921c94081480bcabe442 Author: A Koens <[email protected]> Date: Tue Oct 25 19:50:36 2022 +0200 Squashed commit of the following: commit 8528158fbb3efcb721e7fedd87f80e4968aeb580 Author: A Koens <[email protected]> Date: Tue Oct 25 19:50:20 2022 +0200 Finished Game Menu & TTT Singleplayer commit 5209f2c4ad2af4597fb308c1b3948bc50155322a Author: A Koens <[email protected]> Date: Mon Oct 24 22:41:43 2022 +0200 Dynamically adding games To Menu Got it working so that the games from settings are dynamically added as fxml cards. commit 559ef5c4477e3e213928a3ff340161ac3500ae0b Author: A Koens <[email protected]> Date: Mon Oct 24 15:25:17 2022 +0200 Tic Tac Toe ComboBox commit 36ec0e280df4ca39c27bdf5433db1d598d190e9f Author: A Koens <[email protected]> Date: Mon Oct 24 14:26:58 2022 +0200 Tic Tac Toe Playable Tic Tac Toe can now be played properly. The buttons disable when the game is over. Also added some new GUI Elements, not all working yet. commit 5e3986e678c7e2d2f4d0b11057a7bf3ecbaecd46 Author: A Koens <[email protected]> Date: Sun Oct 23 16:00:19 2022 +0200 Squashed commit of the following: commit ca58fe04c9b92c1ce755b5097e167fe35dc830e2 Author: A Koens <[email protected]> Date: Sun Oct 23 15:59:33 2022 +0200 Delete settings.json commit de55c3ac0079212341da705643bf9458f6904ea1 Author: A Koens <[email protected]> Date: Sun Oct 23 15:58:17 2022 +0200 Update .gitignore commit 56eec25f6d63ab2c648740371414c827cca5addc Author: A Koens <[email protected]> Date: Sun Oct 23 15:57:26 2022 +0200 Changes default Settings Changes the way default settings are handled. commit 8c1ccc951cd6f403d9346217bfaa3d7459dfb3d2 Author: A Koens <[email protected]> Date: Sun Oct 23 15:49:28 2022 +0200 New Settings System Added new Settings handling using Gson. commit 09424e8c51e8babc32d9112c6b9a36c2a1027428 Merge: 5555af3 bb7d7df Author: A Koens <[email protected]> Date: Sun Oct 23 13:04:04 2022 +0200 Merge branch 'develop' of https://github.com/Akoens/ISYGameClient into develop commit bb7d7df14ea4f6e32372c3864e05e3b731f714df Author: A Koens <[email protected]> Date: Sun Oct 23 13:00:42 2022 +0200 Delete target/classes/nl/isygameclient/network directory commit 5555af34cdee3e1d9e1e506718099c5e5ac0f84a Merge: f6425cc cac0988 Author: A Koens <[email protected]> Date: Sat Oct 22 22:22:30 2022 +0200 Merge branch 'develop' of https://github.com/Akoens/ISYGameClient into develop commit f6425cc5d112cbbd4231ad6baa4461a4fda1de0f Author: A Koens <[email protected]> Date: Sat Oct 22 22:22:28 2022 +0200 Temp Tic Tac Toe Chances commit cac09888e98d8db6bcb6dbec77b0bbd88045f718 Author: Friedel Schoen <[email protected]> Date: Sat Oct 22 22:16:57 2022 +0200 some refactoring, splitting classes commit f6d26072156c2dd2710c1ea5b4f5cc75c408dc43 Author: A Koens <[email protected]> Date: Sat Oct 22 22:00:31 2022 +0200 TicTacToe With GUI commit 12ea818c5198954d3089f6b27a963a3889f36570 Author: A Koens <[email protected]> Date: Thu Oct 13 22:58:28 2022 +0200 List view observable list toegevoed met aantal dummy items. commit 3cfa4fb1c3f49c30499ac688fcdc43db29d6fe4b Author: A Koens <[email protected]> Date: Thu Oct 13 12:34:24 2022 +0200 Menu bar Have a nice looking menu bar now. commit dbcd6816dabe2736911939d4ddb3b08b3db9d0f0 Author: A Koens <[email protected]> Date: Thu Oct 13 11:06:08 2022 +0200 Typography and new theme commit 921c055c7da45a870242cc0394567220b95688f4 Author: A Koens <[email protected]> Date: Wed Oct 12 16:59:20 2022 +0200 Basic Css Setup Have some a theme going in css. Also trying to add icons. commit 42e72c32964e246e09efcd89386bb7fb41b88e2b Author: A Koens <[email protected]> Date: Wed Oct 12 17:06:15 2022 +0200 Delete TODOs.txt commit 8f857cba94d61d3ae050eb609966c27b3f0de8c5 Author: A Koens <[email protected]> Date: Wed Oct 12 17:05:53 2022 +0200 Delete ISYGameClient.iml commit c98fa14c3538651790635d0f1adc496ffa37500a Author: A Koens <[email protected]> Date: Wed Oct 12 17:05:44 2022 +0200 Delete .classpath commit 24b9a38cf14ef90f65300130c3b335c85a48f72a Author: A Koens <[email protected]> Date: Wed Oct 12 17:05:17 2022 +0200 Delete .project commit 1dd166dfaf04744ceff0fc7aefc7f451bec7d29f Author: A Koens <[email protected]> Date: Wed Oct 12 17:05:09 2022 +0200 Delete .factorypath commit 23d3a4981f179bce8032c176fc0cce9eb24d2348 Author: A Koens <[email protected]> Date: Wed Oct 12 17:04:48 2022 +0200 Update .gitignore commit efe2c9be85749861d72ac8ff9fbdc5c3472ab154 Author: A Koens <[email protected]> Date: Wed Oct 12 17:02:42 2022 +0200 Delete .settings directory commit 2d131a342d0dec06b24ed8958d5700be898cba9c Author: A Koens <[email protected]> Date: Wed Oct 12 17:02:29 2022 +0200 Update .gitignore commit f890ac06fe835ca401e3e10291d79397185041c1 Author: A Koens <[email protected]> Date: Wed Oct 12 17:01:44 2022 +0200 Update .gitignore commit 753f64aa15adfcc79c6fd51171d945145a0167e8 Author: A Koens <[email protected]> Date: Wed Oct 12 17:01:08 2022 +0200 Delete target directory commit ad0155a94eb494e86b392f7fe6f019414f872672 Author: Friedel Schoen <[email protected]> Date: Fri Oct 7 20:03:46 2022 +0200 added cool Match class (containing the current running match) commit dff6dc0b38a64a557489d5df1a677f2cf43829ed Author: Friedel Schoen <[email protected]> Date: Thu Oct 6 02:16:48 2022 +0200 fixing some git=cringe issues commit b150d3e46e015cd9edb3baa914966a91551465d5 Author: Friedel Schoen <[email protected]> Date: Thu Oct 6 02:10:48 2022 +0200 added cool network client commit db2d2efd9c560594cc576fd4b772671fd1a92d77 Author: A Koens <[email protected]> Date: Wed Oct 5 15:19:13 2022 +0200 Delete ISYGameClient.iml commit 738e3e8a4d3ad7620835d6eaef04de6b582dfbc0 Author: Friedel Schön <[email protected]> Date: Wed Oct 5 14:48:12 2022 +0200 change com.example.isygameclient to nl.isygameclient numero dos commit 4e050b6a242c6c5e5e5bb1fd3ba8ccad0ddc5c3e Author: Friedel Schön <[email protected]> Date: Wed Oct 5 14:38:36 2022 +0200 change com.example.isygameclient to nl.isygameclient commit 2311f65da3662e6865b5933be9b3a6960d477000 Author: A Koens <[email protected]> Date: Tue Sep 27 18:27:04 2022 +0200 Update Settings Settings refactoring, optimized it to allow for easier adding of settings. commit 52bcd6764df8b7ed35ccafd9b7277b7ce4699975 Author: A Koens <[email protected]> Date: Fri Sep 16 09:15:50 2022 +0200 Some Settings commit 53abf1aa95e24d2e2fa98bdafcb815d64760ebcc Author: A Koens <[email protected]> Date: Fri Sep 9 11:43:05 2022 +0200 Update .gitignore commit dbc8344e4b3f5cbbdaef35b5261313f9d7330bab Author: A Koens <[email protected]> Date: Fri Sep 9 11:40:48 2022 +0200 Create ISYGameClient.iml Diffstat:
43 files changed, 2730 insertions(+), 161 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -1,10 +1,19 @@ # Idea -.idea/ -.idea_modules/ +.idea/* +.idea_modules/* + +# Eclipse +.Settings/* +.project +.factorypath +.classpath # IntelliJ -out/ -target/ +out/* +target/* + +# Maven Configuration Files +*.iml # Maven Configuration Files *.iml @@ -19,7 +28,7 @@ target/ *.ctxt # Mobile Tools for Java (J2ME) -.mtj.tmp/ +.mtj.tmp/* # Package Files # *.jar @@ -30,6 +39,9 @@ target/ *.tar.gz *.rar +# Ignore Settings +settings.json + # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* diff --git a/TODOs.txt b/TODOs.txt @@ -1,32 +0,0 @@ -MODELS:: -- Game logic for tic tac toe. -- Game logic for Reversi/Othello. - - -VIEWS:: -- Design main menu. -- Design Tic Tac Toe window. -- Design Reversi/Othello window. -- Add animations / responsive interface features. - - -CONTROLLERS:: -- Add event handlers for the menu buttons. -- Add event handlers for the games. - - -NETWORK:: -- Create module to connect to and send and recieve data from server. -- Implement all the protocols. -- Implement Error handling for the protocols. - - -AI:: -- Random placing AI. -- Minimax => Minimax Alpha-Beta Pruning => Minimax ABP + Weight map -- Pre-calculate possible moves and their best possible responses. (Difficult / Optional) -- Use Machine Learning. (Optional) - - -THREADING:: -- Handle ai decision making on seperate thread. -\ No newline at end of file diff --git a/pom.xml b/pom.xml @@ -13,26 +13,61 @@ <junit.version>5.9.0</junit.version> </properties> <dependencies> + + <!-- JavaFx Dependencies --> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> - <version>18.0.2</version> + <version>19</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> - <version>18.0.2</version> + <version>19</version> </dependency> <dependency> <groupId>org.controlsfx</groupId> <artifactId>controlsfx</artifactId> - <version>11.1.1</version> + <version>11.1.2</version> + </dependency> + + <!-- JavaFX Styling Dependencies --> + <dependency> + <groupId>com.jfoenix</groupId> + <artifactId>jfoenix</artifactId> + <version>9.0.10</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/de.jensd/fontawesomefx-commons --> + <dependency> + <groupId>de.jensd</groupId> + <artifactId>fontawesomefx-commons</artifactId> + <version>9.1.2</version> </dependency> + + <!-- https://mvnrepository.com/artifact/de.jensd/fontawesomefx-materialicons --> <dependency> - <groupId>org.kordamp.bootstrapfx</groupId> - <artifactId>bootstrapfx-core</artifactId> - <version>0.4.0</version> + <groupId>de.jensd</groupId> + <artifactId>fontawesomefx-materialicons</artifactId> + <version>2.2.0-9.1.2</version> </dependency> + + <!-- https://mvnrepository.com/artifact/de.jensd/fontawesomefx-fontawesome --> + <dependency> + <groupId>de.jensd</groupId> + <artifactId>fontawesomefx-fontawesome</artifactId> + <version>4.7.0-9.1.2</version> + </dependency> + + <!-- GSON Dependencies --> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.9.0</version> + </dependency> + + + <!-- Junit Dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> @@ -45,6 +80,10 @@ <version>${junit.version}</version> <scope>test</scope> </dependency> + + + <!-- Lombok --> + <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> @@ -73,7 +112,7 @@ <!-- Default configuration for running with: mvn clean javafx:run --> <id>default-cli</id> <configuration> - <mainClass>nl.isygameclient/nl.isygameclient.Application</mainClass> + <mainClass>nl.isygameclient.Application</mainClass> <launcher>app</launcher> <jlinkZipName>app</jlinkZipName> <jlinkImageName>app</jlinkImageName> diff --git a/protocol.txt b/protocol.txt @@ -0,0 +1,204 @@ +New Game server - Protocol Documentatie 2021 + +Auteurs: Hilko Janssen, Remi Reuvekamp, Gertjan Haan, Timo Strating +Onder begeleiding van: Bas Heijne +Gemaakt in opdracht voor: Lech Bialek +--------------------------------------------- + + +!!! Dit project wat je nu aan het bekijken bent is een 2de iteratie van een oude server. De oude server was heel erg + strict in het hanteren van het protocol. Daarnaast verstuurde het ook dingen in een volgorde die soms anders + gedocumenteerd was. De nieuwe server staat een meer vrije implementatie toe van het protocol. Zo zijn kan de nieuwe + server ook commando's begrijpen als er geen ""-quotes om een argument heen staan. + + De nieuwe server is dus volledig backwards compatible met de stricte versie van dit protocol. + + +--------------------------------------------- +Commando's in detail + +C = Client +S = Server + +Notes bij server antwoorden: +Items tussen vierkante haken ('[' en ']') geven een lijst weer. +Items tussen accolades ('{' en '}') geven een map weer. Zoals bij alle maps, is de volgorde niet bepaald. + +Notes bij client commando's: +Alle commando's zijn niet hoofdlettergevoelig. +Alle argumenten zijn niet hoofdlettergevoelig. + +De server kan naast een server-berichten sturen ook de connectie verbreken. + + +Niet ondersteunde commando: +C: <niet ondersteunde commando> | <commando> <verkeerd(e) argument(en)> +S: ERR <reden> +->Geen gevolg. + +Inloggen: +C: login <speler> +S: OK | ERR <reden> +->Nu ingelogd met spelersnaam lowercase(<speler>). Een naam moet lowercase uniek zijn. + Als deze spelersnaam al bekend was, maar bijvoorbeeld vroegtijdig weg is gegaan bij een toernooi, dan wordt je weer opnieuw in het toernooi gezet. + +Uitloggen/Verbinding verbreken: +C: logout | exit | quit | disconnect | bye +S: - +->Verbinding is verbroken. De speler blijft in het toernooi en kan weer meespelen als hij met de oude spelersnaam opnieuw inlogt. + +Lijst opvragen met ondersteunde spellen: +C: get gamelist +S: OK +S: SVR GAMELIST ["<speltype>", ...] +->Lijst met spellen ontvangen. + +Lijst opvragen met verbonden spelers: +C: get playerlist +S: OK +S: SVR PLAYERLIST ["<speler>", ...] +->Lijst met spelers ontvangen. + +Inschrijven voor de spelzoeker om (voor de lol) een potje met iemand te spelen. +C: subscribe <speltype> +S: OK +->Ingeschreven voor speltype <speltype>. Zodra nog een speler zich heeft ingeschreven start een match hiermee. + +Match aangeboden krijgen, bericht naar beide spelers: +S: SVR GAME MATCH {PLAYERTOMOVE: "<naam speler1>", GAMTYPE: "<speltype>", OPPONENT: "<naam tegenstander>"} +->Nu bezig met een match, de inschrijving voor een speltype is vervallen. + +De beurt toegewezen krijgen tijdens match: +S: SVR GAME YOURTURN {TURNMESSAGE: "<bericht voor deze beurt>"} +->Nu mogelijkheid een zet te doen. + +Een zet doen na het toegewezen krijgen van een beurt: +C: move <zet> +S: OK +->De zet is geaccepteerd door de server, gevolg voor spel zal volgen. +NB: <zet> is een integer van 0 t/m 8 (Tic Tac Toe) resp. 0 t/m 63 (Othello). +De nummering van de vakjes op het bord is rij-gewijs, van linksboven naar rechtsonder. + +Resultaat van een zet ontvangen, bericht naar beide spelers: +S: SVR GAME MOVE {PLAYER: "<speler>", DETAILS: "<reactie spel op zet>", MOVE: "<zet>"} +->Er is een zet gedaan, dit bericht geeft aan wie deze gezet heeft, wat de reactie van het spel erop is + +Resultaat van een match ontvangen, bericht naar beide spelers: +S: SVR GAME <speler resultaat> {PLAYERONESCORE: "<score speler1>", PLAYERTWOSCORE: "<score speler2>", COMMENT: "<commentaar op resultaat>"} +->De match is afgelopen, <speler resultaat> kan de waarde 'WIN', 'LOSS' of 'DRAW' bevatten. + <score speler1> kan arbitrair zijn als + +Een match opgeven: +C: forfeit +S: OK +->De speler heeft het spel opgegeven, de server zal het resultaat van de match doorgeven. + +Resultaat van een match die opgegeven is door een speler, bericht naar beide spelers: +S: SVR GAME <speler resultaat> {PLAYERONESCORE: "<score speler1>", PLAYERTWOSCORE: "<score speler2>", COMMENT: "Player forfeited match"} +->De match is afgelopen, <speler> heeft de match opgegeven. + +Resultaat van een match, speler heeft de verbinding verbroken: +S: SVR GAME <speler resultaat> {PLAYERONESCORE: "<score speler1>", PLAYERTWOSCORE: "<score speler2>", COMMENT: "Client disconnected"} +->De match is afgelopen, <speler> heeft de verbinding verbroken. + +Een speler uitdagen voor een spel: +C: challenge "<speler>" "<speltype>" | challenge <speler> <speltype> +S: OK +->De speler is nu uitgedaagd voor een spel. Eerder gemaakte uitdagingen zijn komen te vervallen. Gebruik quotes als er een spatie in een naam zit + +Een uitdaging ontvangen: +S: SVR GAME CHALLENGE {CHALLENGER: "Sjors", GAMETYPE: "Guess Game", CHALLENGENUMBER: "1"} +->Nu mogelijkheid de uitdaging te accepteren. + +Resultaat van een uitdaging die is komen te vervallen: +S: SVR GAME CHALLENGE CANCELLED {CHALLENGENUMBER: "<uitdaging nummer>"} +->De uitdaging is vervallen. Mogelijke oorzaken: speler heeft een andere uitdaging gestart, speler is een match begonnen, speler heeft de verbinding verbroken. + +Een uitdaging accepteren: +C: challenge accept <uitdaging nummer> +S: OK +->De uitdaging is geaccepteerd. De match wordt gestart, bericht volgt. + +Chatten, alleen toegestaan als chat ingeschakeld is door de admin: +C: message <bericht zonder spaties> | message "<bericht met spaties>" +S: OK | ERR <reden> + +Help opvragen: +C: help +S: OK +->De client heeft nu help informatie opgevraagd, de server zal antwoorden met help informatie. + +Help opvragen van een commando: +C: help <commando> +S: OK +->De client heeft nu help informatie opgevraagd voor een commando, de server zal antwoorden met help informatie. + +Help informatie ontvangen: +S: SVR HELP <help informatie> +->Help informatie is ontvangen, kan meerdere achtereenvolgende responses bevatten. + + +--------------------------------------------- +Overzicht van server-berichten: + +OK Commando geaccepteerd +ERR Commando afgewezen +SVR [ HELP | GAME [MATCH | YOURTURN | MOVE | CHALLENGE | [WIN | LOSS | DRAW]]] + Bericht van server + HELP Bericht met help informatie + GAME Bericht met betrekking op een spel/match + MATCH Toewijzing van een match + YOURTURN Toewijzing van de beurt tijdens de match + MOVE Een zet gedaan tijdens de match + CHALLENGE Bericht met betrekking op een uitdaging + WIN Ontvanger heeft spel gewonnen + LOSS Ontvanger heeft spel verloren + DRAW Match is geeindigd in gelijk spel + +--------------------------------------------- +Overzicht van client-commando's: + +login Aanmelden als speler +logout | exit | quit | disconnect | bye + Uitloggen en verbinding verbreken +get <gamelist | playerlist> + Opvragen van gegevens + gamelist Opvragen van de lijst met ondersteunde speltypes + playerlist Opvragen van de lijst met aangemelde spelers +subscribe Inschrijven voor een speltype +move Een zet doen tijdens een match +challenge [accept] Uitdagingen behandelen + accept Uitdaging accepteren +forfeit De huidige match opgeven +message Stuur een berichtje +help [commando] Help weergeven + + + + + + + + +Enkele tips/tricks van de schrijvers van deze server en voormalig winnaars van het toernooi in 2019 +--------------------------------------------- + +1: Neem eens een kijkje op de wiki pagina van othello en reversi. Er zijn 4 manieren waardoor de server zal concluderen dat je hebt gewonnen. + +2: Hardcode het copyright bericht niet in je client. + +3: Veel server instellingen kunnen worden veranderd. Verwacht deze aanpasingen dan ook. Sommige oude clients gaan de mist in als de server de client bijvoorbeeld maar 5 seconde zou geven om te reageren. + +4: Daag eens een klasgenoot uit voor een potje voordat het toernooit begint. + +5: Sommige spellen hebben bordposities waarbij je beurt gepasseerd moet worden, maar de server heeft geen pass commando. + +6: Wees een beetje lief tegen de server, niet spammen enzo. + +7: Oude clients hadden soms popups die nare gevolgen met zich mee brachten. + +8: Iedereen die met de server is verbonden wordt "waarschijnlijk" tijdens het starten van een toernooi in het toernooi gezet. Er is dus niet een commando om deel te nemen aan het toernooi. + +9: Test Test Test + +10: De persoon die dit heeft getyped heeft dit project ook moeten doen maar dan zonder deze tips en heeft gewonnen dus doe er je voordeel mee (alvast sorry voor de spelfouten in het geval je die hebt gevonden). diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java @@ -1,15 +1,33 @@ module nl.isygameclient { - requires javafx.controls; + // JavaFX - FXMl requires javafx.fxml; - + requires javafx.controls; requires org.controlsfx.controls; - requires org.kordamp.bootstrapfx.core; + // Material Design + requires de.jensd.fx.glyphs.fontawesome; + requires de.jensd.fx.glyphs.materialicons; + requires de.jensd.fx.glyphs.commons; + requires com.jfoenix; + + // Json Class Data Saving + requires com.google.gson; + + // Data Access Generation requires static lombok; - exports nl.isygameclient; - exports nl.isygameclient.controllers; + exports nl.isygameclient; + // Controllers + exports nl.isygameclient.controllers.TicTacToeGame; + exports nl.isygameclient.controllers.GameSeletorMenu; + + // Models + exports nl.isygameclient.models; + exports nl.isygameclient.util; opens nl.isygameclient to javafx.fxml; - opens nl.isygameclient.controllers to javafx.fxml; + opens nl.isygameclient.util to com.google.gson; + + opens nl.isygameclient.controllers.GameSeletorMenu to javafx.fxml; + opens nl.isygameclient.controllers.TicTacToeGame to javafx.fxml; } \ No newline at end of file diff --git a/src/main/java/nl/isygameclient/Application.java b/src/main/java/nl/isygameclient/Application.java @@ -1,27 +1,87 @@ package nl.isygameclient; -import java.io.IOException; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.stage.Stage; +import nl.isygameclient.util.Settings; import nl.isygameclient.util.SettingsHandler; +import java.io.IOException; + + public class Application extends javafx.application.Application { - @Override - public void start(Stage primaryStage) throws IOException { - FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("views/hello-view.fxml")); - Scene scene = new Scene(fxmlLoader.load(), SettingsHandler.getSettings().getScreenWidth(), SettingsHandler.getSettings().getScreenHeight()); - primaryStage.setTitle(SettingsHandler.getSettings().getTitle()); - primaryStage.setScene(scene); - primaryStage.setFullScreen(SettingsHandler.getSettings().isFullscreen()); - primaryStage.show(); - primaryStage.setOnCloseRequest((windowEvent) -> { - SettingsHandler.saveProperties(); - }); - } - - - public static void main(String[] args) { - launch(); - } + + public static Stage primaryStage; + public static Stage gameStage; + + public static void changeGameScene(String viewSrc) throws IOException { + FXMLLoader fxmlSceneLoader = new FXMLLoader(Application.class.getResource(viewSrc)); + Scene scene = new Scene(fxmlSceneLoader.load()); + gameStage.setScene(scene); + gameStage.show(); + primaryStage.setIconified(true); + } + + public static void closeGameScene() { + gameStage.hide(); + primaryStage.setIconified(false); + } + + public static void openMainMenu() throws IOException { + FXMLLoader fxmlSceneLoader = new FXMLLoader(Application.class.getResource("views/GameSelectorMenu/GameSelectorMenu.fxml")); + Scene scene = new Scene(fxmlSceneLoader.load()); + primaryStage.setScene(scene); + } + + public static void main(String[] args) { + launch(); + } + + @Override + public void start(Stage stage) throws IOException { + primaryStage = stage; + gameStage = new Stage(); + + openMainMenu(); + loadSettings(); + + // Save Values on Close + primaryStage.setOnCloseRequest((windowEvent) -> saveSettings()); + + gameStage.setOnCloseRequest((windowEvent -> closeGameScene())); + + primaryStage.show(); + } + + private void loadSettings() { + // Load Settings from file + Settings settings = SettingsHandler.load(); + + // Set Initial Values + primaryStage.setTitle(settings.title); + primaryStage.setFullScreen(settings.isFullScreen); + primaryStage.setMaximized(settings.isMaximized); + primaryStage.setWidth(settings.screenWidth); + primaryStage.setHeight(settings.screenHeight); + primaryStage.setX(settings.screenX); + primaryStage.setY(settings.screenY); + } + + private void saveSettings() { + // Load Settings from file + Settings settings = SettingsHandler.load(); + + settings.screenWidth = primaryStage.getWidth(); + settings.screenHeight = primaryStage.getHeight(); + settings.isFullScreen = primaryStage.isFullScreen(); + settings.isMaximized = primaryStage.isMaximized(); + settings.screenX = primaryStage.getX(); + settings.screenY = primaryStage.getY(); + + SettingsHandler.save(settings); + } + + public Stage getPrimaryStage() { + return primaryStage; + } } \ No newline at end of file diff --git a/src/main/java/nl/isygameclient/controllers/GameSeletorMenu/GameCardController.java b/src/main/java/nl/isygameclient/controllers/GameSeletorMenu/GameCardController.java @@ -0,0 +1,51 @@ +package nl.isygameclient.controllers.GameSeletorMenu; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import nl.isygameclient.Application; +import nl.isygameclient.util.Settings; + +import java.io.IOException; + +public class GameCardController { + + private Settings.GameCard gameCard; + + @FXML + private ImageView gameImage; + + @FXML + private Label gameTitle; + + public void initializeCard(Settings.GameCard gameCard) { + this.gameCard = gameCard; + // Set Title + gameTitle.setText(gameCard.name); + loadImage(); + } + + public void loadImage() { + // Load Image + if (gameCard.imgSrc != null) { + var imageSrc = Application.class.getResource(gameCard.imgSrc); + if (imageSrc != null) { + gameImage.setImage(new Image(imageSrc.toExternalForm())); + } else { + System.err.printf("Image for: %s at %s, doen not exist\n", gameCard.name, gameCard.imgSrc); + } + } else { + System.err.printf("ImgSrc for: %s, not provided\n", gameCard.name); + } + } + + @FXML + protected void onCardMouseClick(MouseEvent event) throws IOException { + if (event.getClickCount() == 2 && gameCard.viewSrc != null && !Application.gameStage.isShowing()) { + Application.changeGameScene(gameCard.viewSrc); + Application.gameStage.setTitle(gameCard.name); + } + } +} diff --git a/src/main/java/nl/isygameclient/controllers/GameSeletorMenu/GameSelectorMenuController.java b/src/main/java/nl/isygameclient/controllers/GameSeletorMenu/GameSelectorMenuController.java @@ -0,0 +1,94 @@ +package nl.isygameclient.controllers.GameSeletorMenu; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.TextField; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.VBox; +import nl.isygameclient.Application; +import nl.isygameclient.util.Settings; +import nl.isygameclient.util.SettingsHandler; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class GameSelectorMenuController { + + private final List<Settings.GameCard> gameCards = SettingsHandler.load().gameCards; + + @FXML + public TextField searchBox; + @FXML + public ListView<Settings.GameCard> gamesList; + @FXML + public VBox gameDetail; + + @FXML + public FlowPane gameContainer; + + @FXML + protected void initialize() throws IOException { + initializeGamesListView(); + initializeGamesListCells(); + initializeGameCards(); + } + + private void initializeGamesListView() { + gamesList.setItems(FXCollections.observableList(gameCards)); + searchBox.textProperty().addListener((observable, oldValue, newValue) -> { + var filteredList = FXCollections.observableList(gameCards.stream().filter(gameCard -> gameCard.name.contains(newValue)).collect(Collectors.toList())); + gamesList.setItems(filteredList); + }); + } + + private void initializeGamesListCells() { + gamesList.setCellFactory(lv -> { + ListCell<Settings.GameCard> listCell = new ListCell<>() { + @Override + protected void updateItem(Settings.GameCard item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null || item.name == null) { + setText(null); + } else { + setText(item.name); + } + } + }; + listCell.setOnMouseClicked((event) -> { + if (event.getClickCount() == 2 && !gamesList.getItems().isEmpty() && listCell.getItem().viewSrc != null && !Application.gameStage.isShowing()) { + try { + Settings.GameCard gameCard = listCell.getItem(); + Application.changeGameScene(gameCard.viewSrc); + Application.gameStage.setTitle(gameCard.name); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + return listCell; + }); + } + + private void initializeGameCards() throws IOException { + for (Settings.GameCard gameCard : gameCards) { + FXMLLoader fxmlLoader = new FXMLLoader(Application.class.getResource("views/GameSelectorMenu/GameCard.fxml")); + Node node = fxmlLoader.load(); + GameCardController controller = fxmlLoader.getController(); + controller.initializeCard(gameCard); + gameContainer.getChildren().add(node); + } + } + + @FXML + protected void onClearSearchButtonClick() { + searchBox.clear(); + } +} diff --git a/src/main/java/nl/isygameclient/controllers/TicTacToeGame/TicTacToeMainMenuController.java b/src/main/java/nl/isygameclient/controllers/TicTacToeGame/TicTacToeMainMenuController.java @@ -0,0 +1,25 @@ +package nl.isygameclient.controllers.TicTacToeGame; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import nl.isygameclient.Application; + +import java.io.IOException; + +public class TicTacToeMainMenuController { + + @FXML + public void onSinglePlayerButtonClick(ActionEvent event) throws IOException { + Application.changeGameScene("views/TicTacToe/TicTacToeSinglePlayer.fxml"); + } + + @FXML + public void onMultiplayerButtonClick(ActionEvent event) { + //TODO Open multiplayer Scene + } + + @FXML + public void onExitButtonClick(ActionEvent event) throws IOException { + Application.closeGameScene(); + } +} diff --git a/src/main/java/nl/isygameclient/controllers/TicTacToeGame/TicTacToeSinglePlayerController.java b/src/main/java/nl/isygameclient/controllers/TicTacToeGame/TicTacToeSinglePlayerController.java @@ -0,0 +1,165 @@ +package nl.isygameclient.controllers.TicTacToeGame; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXComboBox; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.GridPane; +import nl.isygameclient.Application; +import nl.isygameclient.models.TicTacToe; + +import java.io.IOException; +import java.util.ArrayList; + +public class TicTacToeSinglePlayerController { + + private final ArrayList<JFXButton> boardButtons = new ArrayList<>(); + + @FXML + public JFXComboBox<String> difficultyCombo; + @FXML + public JFXComboBox<String> playingAsCombo; + @FXML + public JFXComboBox<String> opponentCombo; + + @FXML + protected Label currentPlayer; + @FXML + public Label gameOverText; + + @FXML + protected GridPane grid; + + private TicTacToe ttt = new TicTacToe(); + + @FXML + protected void initialize() { + initializeBoard(); + initializeDifficultyCombo(); + initializePlayingAsCombo(); + initializeOpponentCombo(); + updateCurrentPlayerLabel(); + } + + private void initializeDifficultyCombo() { + difficultyCombo.getItems().setAll("Easy", "Medium", "Hard"); + difficultyCombo.getSelectionModel().selectFirst(); + } + + private void initializePlayingAsCombo() { + playingAsCombo.getItems().setAll(ttt.getPlayers()); + playingAsCombo.getSelectionModel().selectFirst(); + } + + private void initializeOpponentCombo() { + opponentCombo.getItems().setAll("Human", "AI"); + opponentCombo.getSelectionModel().selectFirst(); + } + + private void initializeBoard() { + for (int i = 0; i < TicTacToe.BOARD_SIZE; i++) { + for (int j = 0; j < TicTacToe.BOARD_SIZE; j++) { + JFXButton button = new JFXButton(); + button.setId(String.valueOf(i + j * TicTacToe.BOARD_SIZE)); + button.setMinSize(200.0, 200.0); + var styleClass = button.getStyleClass(); + styleClass.add("ttt-button"); + styleClass.add("display-large"); + button.setOnAction((ActionEvent event) -> onMoveButtonClick(button)); + boardButtons.add(button); + grid.add(button, i, j); + } + } + } + + private void updateCurrentPlayerLabel() { + currentPlayer.setText(ttt.getCurrentPlayer()); + } + + private void onMoveButtonClick(JFXButton button) { + // Move + int pos = Integer.parseInt(button.getId()); + if (ttt.isMoveValid(pos)) { + ttt.move(pos); + button.setText(ttt.getCurrentPlayer()); + ttt.nextPlayer(); + updateCurrentPlayerLabel(); + } + + // Game Over + if (ttt.isGameOver()) { + onGameOver(); + } + } + + // TODO Create Game-over Modal + private void onGameOver() { + disableBoardButtons(); + System.out.println("Game Over"); + if (ttt.isDraw()) { + System.out.println("Draw!"); + gameOverText.setText("Draw!"); + gameOverText.setVisible(true); + } else { + System.out.printf("%s, Is the Winner!\n\n", ttt.getWinner()); + gameOverText.setText(String.format("%s, is the Winner!\n\n", ttt.getWinner())); + gameOverText.setVisible(true); + } + } + + private void clearBoardButtons() { + for (JFXButton button : boardButtons) { + button.setText(""); + } + } + + private void disableBoardButtons() { + for (JFXButton button : boardButtons) { + button.setDisable(true); + } + } + + private void enableBoardButtons() { + for (JFXButton button : boardButtons) { + button.setDisable(false); + } + } + + @FXML + protected void onDifficultyComboSelect() { + System.out.printf("Difficulty Changed to: %s\n", difficultyCombo.getValue()); + + } + + @FXML + protected void onPlayingAsComboSelect() { + System.out.printf("Now playing As: %s\n", playingAsCombo.getValue()); + + } + + @FXML + protected void onOpponentComboSelect() { + System.out.printf("Opponent Changed to: %s\n", opponentCombo.getValue()); + } + + @FXML + protected void onNewGameButtonClick() { + // Make new Game + ttt = new TicTacToe(); + + clearBoardButtons(); + enableBoardButtons(); + gameOverText.setVisible(false); + } + + @FXML + protected void onMainMenuButtonClick() throws IOException { + Application.changeGameScene("views/TicTacToe/TicTacToeMainMenu.fxml"); + } + + @FXML + protected void onExitButtonClick() throws IOException { + Application.closeGameScene(); + } +} diff --git a/src/main/java/nl/isygameclient/models/Difficulty.java b/src/main/java/nl/isygameclient/models/Difficulty.java @@ -0,0 +1,8 @@ +package nl.isygameclient.models; + +public enum Difficulty { + EASY("Easy"), MEDIUM("Medium"), HARD("Hard"); + + Difficulty(String difficulty) { + } +} diff --git a/src/main/java/nl/isygameclient/models/Game.java b/src/main/java/nl/isygameclient/models/Game.java @@ -0,0 +1,45 @@ +package nl.isygameclient.models; + +import lombok.Data; + +import java.util.Arrays; + + +@Data +public abstract class Game { + int currentPlayerIndex; + String[] players; + + public Game(int currentPlayer, String[] players) { + this.currentPlayerIndex = currentPlayer; + this.players = players; + } + + + public abstract void move(int pos); + + public abstract boolean isMoveValid(int pos); + + public abstract boolean isGameOver(); + + public abstract String getWinner(); + + public void nextPlayer() { + currentPlayerIndex += 1; + if (currentPlayerIndex >= players.length) { + currentPlayerIndex = 0; + } + } + + public String getCurrentPlayer() { + return players[currentPlayerIndex]; + } + + public void setCurrentPlayerIndex(int playerIndex) { + this.currentPlayerIndex = playerIndex; + } + + public void setCurrentPlayer(String player) { + currentPlayerIndex = Arrays.asList(players).indexOf(player); + } +} diff --git a/src/main/java/nl/isygameclient/models/TicTacToe.java b/src/main/java/nl/isygameclient/models/TicTacToe.java @@ -0,0 +1,69 @@ +package nl.isygameclient.models; + + +import java.util.ArrayList; +import java.util.Arrays; + +public class TicTacToe extends Game { + + public static final int BOARD_SIZE = 3; + + String[] board = new String[BOARD_SIZE * BOARD_SIZE]; + + public TicTacToe() { + super(0, new String[]{"X", "O"}); + } + + public boolean isMoveValid(int pos) { + return (pos >= 0 && pos < (BOARD_SIZE * BOARD_SIZE)) && board[pos] == null; + } + + public void move(int pos) { + board[pos] = players[currentPlayerIndex]; + } + + public ArrayList<Integer> getPossibleMoves() { + ArrayList<Integer> possibleMoves = new ArrayList<>(); + for (int i=0; i < board.length; i++) { + if (isMoveValid(i)) possibleMoves.add(i); + } + return possibleMoves; + } + + public boolean isDraw(){ + return !Arrays.asList(board).contains(null); + } + + public boolean isGameOver() { + return getWinner() != null || isDraw(); + } + + public String getWinner() { + for (String player : players) { + boolean topRow = player.equals(board[0]) && player.equals(board[1]) && player.equals(board[2]); + boolean midRow = player.equals(board[3]) && player.equals(board[4]) && player.equals(board[5]); + boolean botRow = player.equals(board[6]) && player.equals(board[7]) && player.equals(board[8]); + + boolean leftCol = player.equals(board[0]) && player.equals(board[3]) && player.equals(board[6]); + boolean midCol = player.equals(board[1]) && player.equals(board[4]) && player.equals(board[7]); + boolean rightCol = player.equals(board[2]) && player.equals(board[5]) && player.equals(board[8]); + + boolean lrCross = player.equals(board[0]) && player.equals(board[4]) && player.equals(board[8]); + boolean rlCross = player.equals(board[2]) && player.equals(board[4]) && player.equals(board[6]); + + if (topRow || midRow || botRow || leftCol || midCol || rightCol || lrCross || rlCross) { + return player; + } + } + return null; + } + + public void printGameBoard() { + for (int i = 0; i < board.length; i++) { + System.out.print(board[i] == null ? " " : board[i]); + if (i % BOARD_SIZE < BOARD_SIZE -1) System.out.print("|"); + if (i % BOARD_SIZE == BOARD_SIZE-1 && i < board.length-1) System.out.println("\n-+-+-"); + } + System.out.println(); + } +} +\ No newline at end of file diff --git a/src/main/java/nl/isygameclient/network/Event.java b/src/main/java/nl/isygameclient/network/Event.java @@ -0,0 +1,15 @@ +package nl.isygameclient.network; + +public class Event { + public final EventType type; + public final Object data; + + public Event(EventType type, Object data) { + this.type = type; + this.data = data; + } + + public String toString() { + return String.format("Event.%s(%s)", type, data); + } +} +\ No newline at end of file diff --git a/src/main/java/nl/isygameclient/network/EventParser.java b/src/main/java/nl/isygameclient/network/EventParser.java @@ -0,0 +1,81 @@ +package nl.isygameclient.network; + +import java.util.HashMap; +import java.util.LinkedList; + +public class EventParser { + private final String str; + + private int index = 0; + + private void stripLeft() { + while (Character.isSpaceChar(str.charAt(index))) + index++; + } + + public EventParser(String str) { + this.str = str; + } + + public Object parseData() throws GameClientException { + try { + stripLeft(); + + if (index >= str.length()) + return null; + + switch (str.charAt(index)) { + case '"': + index++; + var string = str.substring(index, str.indexOf("\"", index)); + index = str.indexOf("\"", index) + 1; + return string; + case '[': + index++; + var array = new LinkedList<Object>(); + for (;;) { + array.add(parseData()); + stripLeft(); + if (str.charAt(index) == ']') { + index++; + return array; + } else if (str.charAt(index) == ',') { + index++; + } else { + throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str)); + } + } + case '{': + index++; + var map = new HashMap<String, Object>(); + for (;;) { + stripLeft(); + var key = str.substring(index, str.indexOf(":", index)).toLowerCase(); + + index = str.indexOf(":", index) + 1; + map.put(key, parseData()); + stripLeft(); + if (str.charAt(index) == '}') { + index++; + return map; + } else if (str.charAt(index) == ',') { + index++; + } else { + throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str)); + } + } + default: + if (index != 0) + throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str)); + index = str.length(); + return str; + } + } catch (Exception ext) { + throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str)); + } + } + + public void reset() { + index = 0; + } +} diff --git a/src/main/java/nl/isygameclient/network/EventType.java b/src/main/java/nl/isygameclient/network/EventType.java @@ -0,0 +1,20 @@ +package nl.isygameclient.network; + +public enum EventType { + PLAYERLIST("PLAYERLIST"), + GAMELIST("GAMELIST"), + MATCH("GAME MATCH"), + YOURTURN("GAME YOURTURN"), + MOVE("GAME MOVE"), + CHALLENGE_ACCEPTED("GAME CALLENGE ACCEPT"), + CHALLENGE("GAME CHALLENGE"), + WIN("GAME WIN"), + LOSS("GAME LOSS"), + DRAW("GAME DRAW"); + + public final String identifier; + + EventType(String identifier) { + this.identifier = identifier; + } +} +\ No newline at end of file diff --git a/src/main/java/nl/isygameclient/network/GameClient.java b/src/main/java/nl/isygameclient/network/GameClient.java @@ -0,0 +1,87 @@ +package nl.isygameclient.network; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class GameClient extends GameClientBase { + private final String name; + + public GameClient(String host, int port, String name) throws UnknownHostException, IOException, InterruptedException { + super(host, port); + + this.name = name; + } + + + public String getName() { + return name; + } + + @SuppressWarnings("unchecked") + public List<String> games() throws IOException, GameClientException, InterruptedException { + send("get gamelist"); + + return (List<String>) event(EventType.GAMELIST).data; + } + + @SuppressWarnings("unchecked") + public List<String> players() throws IOException, GameClientException, InterruptedException { + send("get playerlist"); + + return (List<String>) event(EventType.GAMELIST).data; + } + + public void login() throws GameClientException, IOException { + send("login", name); + } + + public Match match(GameType game, String other) throws IOException, GameClientException, InterruptedException { + send("challenge", other, game.name); + + return new Match(this); + } + + public Match match(GameType game) throws IOException, GameClientException, InterruptedException { + send("subscribe", game.name); + + return new Match(this); + } + + public Match match() throws IOException, GameClientException, InterruptedException { + @SuppressWarnings("unchecked") + var challengeEvent = (Map<String, String>) event(-1, EventType.CHALLENGE).data; + var challengeID = challengeEvent.get("challengenumber"); + send("challenge", "accept", challengeID); + + return new Match(this); + } + + public static void main(String[] args) throws Exception { + var name = "testclient" + new Random().nextInt(100); + var client = new GameClient("localhost", 7789, name); // public: 145.33.225.170 + + client.login(); + System.out.println("connected as " + name); + + var current = client.match(GameType.TICTACTOE4); + for (;;) { + while (current.update()) { + int move; + do { + move = new Random().nextInt(current.getGame().maxMoves); + } while (current.getMove(move) != 0); + current.move(move); + } + System.out.printf("[%4s] %02d:%02d\n", current.getOutcome().type, current.getPointsSelf(), current.getPointsOther()); + if (current.getPointsSelf() >= 10 || current.getPointsOther() >= 10) + break; + + current.rematch(); + } + + client.close(); + } +} diff --git a/src/main/java/nl/isygameclient/network/GameClientBase.java b/src/main/java/nl/isygameclient/network/GameClientBase.java @@ -0,0 +1,130 @@ +package nl.isygameclient.network; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.LinkedList; + +public abstract class GameClientBase { + private final Socket socket; + private final OutputStream outputStream; + private final BufferedReader inputBuffer; + + private LinkedList<Event> eventQueue = new LinkedList<>(); + private LinkedList<String> eventLineQueue = new LinkedList<>(); + + public GameClientBase(String host, int port) throws UnknownHostException, IOException, InterruptedException { + socket = new Socket(host, port); + outputStream = socket.getOutputStream(); + inputBuffer = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + Thread.sleep(100); + while (inputBuffer.ready()) { + inputBuffer.readLine(); + Thread.sleep(100); + } + } + + protected void sendQuoted(String command, String... arguments) throws IOException, GameClientException { + for (int i = 0; i < arguments.length; i++) + arguments[i] = '"' + arguments[i] + '"'; + + send(command, arguments); + } + + + protected void send(String command, String... arguments) throws IOException, GameClientException { + var requestBuilder = new StringBuilder(command); + for (var argument : arguments) { + requestBuilder.append(' '); + requestBuilder.append(argument); + } + + requestBuilder.append('\n'); + outputStream.write(requestBuilder.toString().getBytes()); + + String response; + for (;;) { + response = inputBuffer.readLine(); + if (response.startsWith("ERR ")) + throw new GameClientException(response.substring(4)); + else if (response.startsWith("SVR ")) { + eventLineQueue.add(response); + continue; + } else if (response.equals("OK")) + break; + throw new GameClientException("invalid server response: '" + response + "'"); + } + } + + protected Event event(EventType... target) throws GameClientException, IOException, InterruptedException { + return event(1, true, target); + } + + protected Event event(double timeout, EventType... target) throws GameClientException, IOException, InterruptedException { + return event(timeout, true, target); + } + + protected Event event(double timeout, boolean consume, EventType... target) throws GameClientException, IOException, InterruptedException { + var targetList = Arrays.asList(target); + if (!eventQueue.isEmpty()) { + if (target == null) + return eventQueue.poll(); + + for (int i = 0; i < eventQueue.size(); i++) { + if (targetList.contains(eventQueue.get(i).type)) + return consume ? eventQueue.remove(i) : eventQueue.get(i); + } + } + + long start = System.currentTimeMillis(); + Event result; + String line; + + do { + if (eventLineQueue.isEmpty()) { + Thread.sleep(100); + if (!inputBuffer.ready()) + continue; + line = inputBuffer.readLine(); + } else { + line = eventLineQueue.poll(); + } + + if (!line.startsWith("SVR ")) + throw new GameClientException("invalid server response: '" + line + "'"); + + + result = null; + for (var evt : EventType.values()) { + if (line.startsWith(evt.identifier, 4)) { + result = new Event(evt, new EventParser(line.substring(evt.identifier.length() + 4)).parseData()); + break; + } + } + + if (result == null) + throw new GameClientException("invalid server response: unknown event '" + line + "'"); + + + if (target.length == 0 || target.length > 0 && targetList.contains(result.type)) { + if (!consume) + eventQueue.push(result); + return result; + } + + eventQueue.push(result); + } while (timeout == -1 || System.currentTimeMillis() - start < timeout * 1000); + + return null; + } + + protected void close() throws IOException { + outputStream.write("logout\n".getBytes()); + socket.close(); + } +} diff --git a/src/main/java/nl/isygameclient/network/GameClientException.java b/src/main/java/nl/isygameclient/network/GameClientException.java @@ -0,0 +1,7 @@ +package nl.isygameclient.network; + +public class GameClientException extends Exception { + public GameClientException(String reason) { + super(reason); + } +} +\ No newline at end of file diff --git a/src/main/java/nl/isygameclient/network/GameType.java b/src/main/java/nl/isygameclient/network/GameType.java @@ -0,0 +1,24 @@ +package nl.isygameclient.network; + +public enum GameType { + TICTACTOE("tic-tac-toe", 9), + TICTACTOE4("tic-tac-toe-4", 16), + REVERSI("reversi", 64, 27, 28, 35, 36); + + public final String name; + public final int maxMoves; + public final int[] reserved; + + private GameType(String name, int maxMoves, int... reserved) { + this.name = name; + this.maxMoves = maxMoves; + this.reserved = reserved; + } + + public static GameType byName(String name) { + for (GameType game : GameType.values()) + if (game.name.equals(name)) + return game; + return null; + } +} +\ No newline at end of file diff --git a/src/main/java/nl/isygameclient/network/Match.java b/src/main/java/nl/isygameclient/network/Match.java @@ -0,0 +1,110 @@ +package nl.isygameclient.network; + +import java.io.IOException; +import java.util.Map; + +public class Match { + private final GameClient client; + private final GameType game; + private final String opponent; + + private Event outcome = null; + private final int[] moves; + private int pointsSelf = 0; + private int pointsOther = 0; + + public Match(GameClient client) throws GameClientException, IOException, InterruptedException { + this.client = client; + + @SuppressWarnings("unchecked") + var data = (Map<String, String>) client.event(-1, EventType.MATCH).data; + + game = GameType.byName(data.get("gametype").toLowerCase()); + opponent = data.get("opponent"); + moves = new int[game.maxMoves]; + for (int reserved : game.reserved) + moves[reserved] = 2; + } + + public GameType getGame() { + return game; + } + + public boolean update() throws GameClientException, IOException, InterruptedException { + if (outcome != null) + return false; + + Event event; + for (;;) { + event = client.event(-1, EventType.MOVE, EventType.YOURTURN, EventType.WIN, EventType.DRAW, EventType.LOSS); + + switch (event.type) { + case YOURTURN: + return true; + case MOVE: + @SuppressWarnings("unchecked") + var moveMap = (Map<String, String>) event.data; + + moves[Integer.parseInt(moveMap.get("move"))] = moveMap.get("player").equals(client.getName()) ? 1 : -1; + break; + case WIN: + pointsSelf++; + outcome = event; + return false; + case LOSS: + pointsOther++; + outcome = event; + return false; + default: // loss + outcome = event; + return false; + } + } + } + + public int getMove(int move) { + return moves[move]; + } + + public void abort() throws GameClientException, IOException { + if (outcome == null) + client.send("forfeit"); + } + + public void rematch() throws IOException, GameClientException, InterruptedException { + if (outcome == null) + return; + + Event evt; + if ((evt = client.event(0.5, EventType.CHALLENGE)) != null) { + @SuppressWarnings("unchecked") + var id = ((Map<String, String>) evt.data).get("challengenumber"); + client.send("challenge", "accept", id); + } else { + client.send("challenge", opponent, game.name); + client.event(-1, EventType.MATCH); + } + + for (int i = 0; i < moves.length; i++) + moves[i] = 0; + outcome = null; + } + + public Event getOutcome() { + return outcome; + } + + public int getPointsSelf() { + return pointsSelf; + } + + public int getPointsOther() { + return pointsOther; + } + + public void move(int move) throws IOException, GameClientException { + if (outcome != null) + return; + client.send("move " + move); + } +} +\ No newline at end of file diff --git a/src/main/java/nl/isygameclient/util/Settings.java b/src/main/java/nl/isygameclient/util/Settings.java @@ -1,13 +1,34 @@ package nl.isygameclient.util; -import lombok.Data; +import java.util.ArrayList; +import java.util.Arrays; -@Data public class Settings { + public String title = "ISY Game Client"; + public boolean isFullScreen = true; + public boolean isMaximized = false; + public double screenHeight = 680.0; + public double screenWidth = 770.0; + public double screenX = 0; + public double screenY = 0; - private String title = "ISY Game Client"; - private boolean fullscreen = true; - private int screenHeight = 1080; - private int screenWidth = 1920; + public boolean tournamentMode = false; + + public ArrayList<GameCard> gameCards = new ArrayList<>(Arrays.asList( + new GameCard("TicTacToe", "views/TicTacToe/TicTacToeMainMenu.fxml", "images/tictactoe_logo.png"), + new GameCard("Othello", null, "images/othello_logo.png"), + new GameCard("Starvation", null, null))); + + public static class GameCard { + public String name; + public String viewSrc; + public String imgSrc; + + public GameCard(String name, String viewSrc, String imgSrc) { + this.name = name; + this.viewSrc = viewSrc; + this.imgSrc = imgSrc; + } + } } diff --git a/src/main/java/nl/isygameclient/util/SettingsHandler.java b/src/main/java/nl/isygameclient/util/SettingsHandler.java @@ -1,75 +1,44 @@ package nl.isygameclient.util; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.util.Properties; +import java.nio.file.Files; +import java.nio.file.Paths; -public abstract class SettingsHandler { - - private static Settings settings; - - public static Settings getSettings() { - if (settings == null) { - settings = new Settings(); - SettingsHandler.loadPropertiesIntoSettings(); - } - return settings; - } - - private static Properties loadPropertiesFromFile() throws IOException { - Properties properties = new Properties(); - InputStream is = new FileInputStream("settings.properties"); - properties.load(is); - return properties; - } - public static void loadPropertiesIntoSettings() { - try { - Properties properties = loadPropertiesFromFile(); - Field[] fields = settings.getClass().getDeclaredFields(); - for (Field field : fields) { - field.setAccessible(true); - Object value; - if (field.getType().equals(String.class)) { - value = String.valueOf(properties.getProperty(field.getName())); - } else if (field.getType().equals(int.class)) { - value = Integer.parseInt(properties.getProperty(field.getName())); - } else if (field.getType().equals(float.class)) { - value = Float.parseFloat(properties.getProperty(field.getName())); - } else if (field.getType().equals(boolean.class)) { - value = Boolean.parseBoolean(properties.getProperty(field.getName())); - } else { - return; - } - field.set(settings, value); - field.setAccessible(false); - } - } catch (IllegalAccessException | NumberFormatException | IOException e) { - System.out.println("ERROR: Unable to load properties from file"); - e.printStackTrace(); - } - } - - public static void saveProperties() { - try { - FileOutputStream fos = new FileOutputStream("settings.properties"); - Properties properties = new Properties(); - Field[] fields = settings.getClass().getDeclaredFields(); - for (Field field : fields) { - field.setAccessible(true); - properties.put(field.getName(), String.valueOf(field.get(settings))); - } - properties.store(fos, "Application Settings"); - fos.flush(); - fos.close(); - - } catch (IOException | IllegalAccessException e) { - System.out.println("ERROR: Unable to save to properties file"); - e.printStackTrace(); - } - } +public abstract class SettingsHandler { + public static final String SETTINGS_FILENAME = "settings.json"; + + public static void save(Settings settings) { + try { + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + + FileWriter writer = new FileWriter(SETTINGS_FILENAME); + writer.write(gson.toJson(settings)); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static Settings load() { + try { + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + + String inFile = new String(Files.readAllBytes(Paths.get(SETTINGS_FILENAME))); + return gson.fromJson(inFile, Settings.class); + } catch (IOException e) { + System.err.println("Could Not Load Settings File, Using Defaults Instead"); + var defaults = new Settings(); + save(defaults); + return defaults; + } + } } diff --git a/src/main/resources/nl/isygameclient/css/hello.css b/src/main/resources/nl/isygameclient/css/hello.css @@ -1,3 +0,0 @@ -.button { - -fx-background-color: #2196F3; -} -\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/css/modules/colors.module.css b/src/main/resources/nl/isygameclient/css/modules/colors.module.css @@ -0,0 +1,318 @@ +/* -- Primary -- */ +/* Inverse Primary */ +.inverse-primary { + -fx-background-color: -md-sys-color-inverse-primary; +} +.inverse-primary-text { + -fx-text-fill: -md-sys-color-inverse-primary; +} +.inverse-primary-fill { + -fx-fill: -md-sys-color-inverse-primary; +} + +/* On Primary Container */ +.on-primary-container { + -fx-background-color: -md-sys-color-on-primary-container; +} +.on-primary-container-text { + -fx-text-fill: -md-sys-color-on-primary-container; +} +.on-primary-container-fill { + -fx-fill: -md-sys-color-on-primary-container; +} + +/* On Primary */ +.on-primary { + -fx-background-color: -md-sys-color-on-primary; +} +.on-primary-text { + -fx-text-fill: -md-sys-color-on-primary; +} +.on-primary-fill { + -fx-fill: -md-sys-color-on-primary; +} + +/* Primary Container */ +.primary-container { + -fx-background-color: -md-sys-color-primary-container; +} +.primary-container-text { + -fx-text-fill: -md-sys-color-primary-container; +} +.primary-container-fill { + -fx-fill: -md-sys-color-primary-container; +} + +/* Primary */ +.primary { + -fx-background-color: -md-sys-color-primary; +} +.primary-text { + -fx-text-fill: -md-sys-color-primary; +} +.primary-fill { + -fx-fill: -md-sys-color-primary; +} + +/* -- Secondary -- */ +/* On Secondary Container */ +.on-secondary-container { + -fx-background-color: -md-sys-color-on-secondary-container; +} +.on-secondary-container-text { + -fx-text-fill: -md-sys-color-on-secondary-container; +} +.on-secondary-container-fill { + -fx-fill: -md-sys-color-on-secondary-container; +} + +/* On Secondary */ +.on-secondary { + -fx-background-color: -md-sys-color-on-secondary; +} +.on-secondary-text { + -fx-text-fill: -md-sys-color-on-secondary; +} +.on-secondary-fill { + -fx-fill: -md-sys-color-on-secondary; +} + +/* Secondary Container */ +.secondary-container { + -fx-background-color: -md-sys-color-secondary-container; +} +.secondary-container-text { + -fx-text-fill: -md-sys-color-secondary-container; +} +.secondary-container-fill { + -fx-fill: -md-sys-color-secondary-container; +} + +/* Secondary */ +.secondary { + -fx-background-color: -md-sys-color-secondary; +} +.secondary-text { + -fx-text-fill: -md-sys-color-secondary; +} +.secondary-fill { + -fx-fill: -md-sys-color-secondary; +} + +/* -- Tertiary -- */ +/* On Tertiary Container */ +.on-tertiary-container { + -fx-background-color: -md-sys-color-on-tertiary-container; +} +.on-tertiary-container-text { + -fx-text-fill: -md-sys-color-on-tertiary-container; +} +.on-tertiary-container-fill { + -fx-fill: -md-sys-color-on-tertiary-container; +} + +/* On Tertiary */ +.on-tertiary { + -fx-background-color: -md-sys-color-on-tertiary; +} +.on-tertiary-text { + -fx-text-fill: -md-sys-color-on-tertiary; +} +.on-tertiary-fill { + -fx-fill: -md-sys-color-on-tertiary; +} + +/* Tertiary Container */ +.tertiary-container { + -fx-background-color: -md-sys-color-tertiary-container; +} +.tertiary-container-text { + -fx-text-fill: -md-sys-color-tertiary-container; +} +.tertiary-container-text { + -fx-fill: -md-sys-color-tertiary-container; +} + +/* Tertiary */ +.tertiary { + -fx-background-color: -md-sys-color-tertiary; +} +.tertiary-text { + -fx-text-fill: -md-sys-color-tertiary; +} +.tertiary-fill { + -fx-text-fill: -md-sys-color-tertiary; +} + +/* -- Error -- */ +/* On Error Container */ +.on-error-container { + -fx-background-color: -md-sys-color-on-error-container; +} +.on-error-container-text { + -fx-text-fill: -md-sys-color-on-error-container; +} +.on-error-container-fill { + -fx-fill: -md-sys-color-on-error-container; +} + +/* On Error */ +.on-error { + -fx-background-color: -md-sys-color-on-error; +} +.on-error-text { + -fx-text-fill: -md-sys-color-on-error; +} +.on-error-fill { + -fx-fill: -md-sys-color-on-error; +} + +/* Error Container */ +.error-container { + -fx-background-color: -md-sys-color-error-container; +} +.error-container-text { + -fx-text-fill: -md-sys-color-error-container; +} +.error-container-fill { + -fx-fill: -md-sys-color-error-container; +} + +/* Error */ +.error { + -fx-background-color: -md-sys-color-error; +} +.error-text { + -fx-text-fill: -md-sys-color-error; +} +.error-fill { + -fx-fill: -md-sys-color-error; +} + +/* -- Surface -- */ +/* Inverse On Surface */ +.inverse-on-surface { + -fx-background-color: -md-sys-color-inverse-on-surface; +} +.inverse-on-surface-text { + -fx-text-fill: -md-sys-color-inverse-on-surface; +} +.inverse-on-surface-fill { + -fx-fill: -md-sys-color-inverse-on-surface; +} + +/* Inverse surface */ +.inverse-surface { + -fx-background-color: -md-sys-color-inverse-surface; +} +.inverse-surface-text { + -fx-text-fill: -md-sys-color-inverse-surface; +} +.inverse-surface-fill { + -fx-fill: -md-sys-color-inverse-surface; +} + +/* Inverse On Surface Variant */ +.on-surface-variant { + -fx-background-color: -md-sys-color-on-surface-variant; +} +.on-surface-variant-text { + -fx-text-fill: -md-sys-color-on-surface-variant; +} +.on-surface-variant-fill { + -fx-fill: -md-sys-color-on-surface-variant; +} + +/* On Surface */ +.on-surface { + -fx-background-color: -md-sys-color-on-surface; +} +.on-surface-text { + -fx-text-fill: -md-sys-color-on-surface; +} +.on-surface-fill { + -fx-fill: -md-sys-color-on-surface; +} + +/* Surface Variant */ +.surface-variant { + -fx-background-color: -md-sys-color-surface-variant; +} +.surface-variant-text { + -fx-text-fill: -md-sys-color-surface-variant; +} +.surface-variant-fill { + -fx-fill: -md-sys-color-surface-variant; +} + +/* Surface */ +.surface { + -fx-background-color: -md-sys-color-surface; +} +.surface-text { + -fx-text-fill: -md-sys-color-surface; +} +.surface-fill { + -fx-fill: -md-sys-color-surface; +} + +/* Surface Tint */ +.surface-tint { + -fx-background-color: -md-sys-color-surface-tint; +} +.surface-tint-text { + -fx-text-fill: -md-sys-color-surface-tint; +} +.surface-tint-fill { + -fx-fill: -md-sys-color-surface-tint; +} + +/* Surface Tint Color */ +.surface-tint-color { + -fx-background-color: -md-sys-color-surface-tint-color; +} +.surface-tint-color-text { + -fx-text-fill: -md-sys-color-surface-tint-color; +} +.surface-tint-color-fill { + -fx-fill: -md-sys-color-surface-tint-color; +} + +/* -- Background -- */ +.on-background { + -fx-background-color: -md-sys-color-on-background; +} +.on-background-text { + -fx-text-fill: -md-sys-color-on-background; +} +.background { + -fx-background-color: -md-sys-color-background; +} +.background-text { + -fx-text-fill: -md-sys-color-background; +} +.background-fill { + -fx-text-fill: -md-sys-color-background; +} + +/* -- Outline -- */ +.outline { + -fx-background-color: -md-sys-color-outline; +} +.outline-text { + -fx-text-fill: -md-sys-color-outline; +} +.outline-fill { + -fx--fill: -md-sys-color-outline; +} + +/* -- Shadow -- */ +.shadow { + -fx-background-color: -md-sys-color-shadow; +} +.shadow-text { + -fx-text-fill: -md-sys-color-shadow; +} +.shadow-fill { + -fx-fill: -md-sys-color-shadow; +} diff --git a/src/main/resources/nl/isygameclient/css/modules/typography.module.css b/src/main/resources/nl/isygameclient/css/modules/typography.module.css @@ -0,0 +1,109 @@ +/* Lable */ +.lable-small { + -fx-font-family: sans-serif; + -fx-font-size: 11; + -fx-font-style: normal; + -fx-font-weight: 500; +} + +.lable-medium { + -fx-font-family: sans-serif; + -fx-font-size: 13; + -fx-font-style: normal; + -fx-font-weight: 500; +} + +.lable-large { + -fx-font-family: sans-serif; + -fx-font-size: 14; + -fx-font-style: normal; + -fx-font-weight: 500; +} + +/* Body */ +.body-small { + -fx-font-family: sans-serif; + -fx-font-size: 12; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +.body-medium { + -fx-font-family: sans-serif; + -fx-font-size: 14; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +.body-large { + -fx-font-family: sans-serif; + -fx-font-size: 16; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +/* Headline */ +.headline-small { + -fx-font-family: sans-serif; + -fx-font-size: 24; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +.headline-medium { + -fx-font-family: sans-serif; + -fx-font-size: 28; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +.headline-large { + -fx-font-family: sans-serif; + -fx-font-size: 32; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +/* Display */ +.display-small { + -fx-font-family: sans-serif; + -fx-font-size: 36; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +.display-medium { + -fx-font-family: sans-serif; + -fx-font-size: 45; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +.display-large { + -fx-font-family: sans-serif; + -fx-font-size: 58; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +/* Title */ +.title-small { + -fx-font-family: sans-serif; + -fx-font-size: 14; + -fx-font-style: normal; + -fx-font-weight: 500; +} + +.title-medium { + -fx-font-family: sans-serif; + -fx-font-size: 16; + -fx-font-style: normal; + -fx-font-weight: 500; +} + +.title-large { + -fx-font-family: sans-serif; + -fx-font-size: 22; + -fx-font-style: normal; + -fx-font-weight: 400; +} +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/css/old_tokens.css b/src/main/resources/nl/isygameclient/css/old_tokens.css @@ -0,0 +1,122 @@ +* { + -md-source: #4caf50; + -md-color-black: #000000; + -md-color-white: #ffffff; + + /* error */ + -md-error50: #ffebee; + -md-error100: #ffcdd2; + -md-error200: #ef9a9a; + -md-error300: #e57373; + -md-error400: #ef5350; + -md-error500: #f44336; + -md-error600: #e53935; + -md-error700: #d32f2f; + -md-error800: #c62828; + -md-error900: #b71c1c; + + /* primary */ + -md-primary50: #e8f5e9; + -md-primary100: #c8e6c9; + -md-primary200: #a5d6a7; + -md-primary300: #81c784; + -md-primary400: #66bb6a; + -md-primary500: #4caf50; + -md-primary600: #43a047; + -md-primary700: #388e3c; + -md-primary800: #2e7d32; + -md-primary900: #1b5e20; + + /* secondary */ + -md-secondary50: #fff3e0; + -md-secondary100: #ffe0b2; + -md-secondary200: #ffcc80; + -md-secondary300: #ffb74d; + -md-secondary400: #ffa726; + -md-secondary500: #ff9800; + -md-secondary600: #fb8c00; + -md-secondary700: #f57c00; + -md-secondary800: #ef6c00; + -md-secondary900: #e65100; + + /* tertiary */ + -md-tertiary50: #e0f2f1; + -md-tertiary100: #b2dfdb; + -md-tertiary200: #80cbc4; + -md-tertiary300: #4db6ac; + -md-tertiary400: #26a69a; + -md-tertiary500: #009688; + -md-tertiary600: #00897b; + -md-tertiary700: #00796b; + -md-tertiary800: #00695c; + -md-tertiary900: #004d40; + + /* neutral */ + -md-neutral50: #fafafa; + -md-neutral100: #f5f5f5; + -md-neutral200: #eeeeee; + -md-neutral300: #e0e0e0; + -md-neutral400: #bdbdbd; + -md-neutral500: #787579; + -md-neutral600: #757575; + -md-neutral700: #616161; + -md-neutral800: #424242; + -md-neutral900: #212121; + + /* neutral-variant */ + -md-neutral-variant50: #eceff1; + -md-neutral-variant100: #cfd8dc; + -md-neutral-variant200: #b0bec5; + -md-neutral-variant300: #90a4ae; + -md-neutral-variant400: #78909c; + -md-neutral-variant500: #607d8b; + -md-neutral-variant600: #546e7a; + -md-neutral-variant700: #455a64; + -md-neutral-variant800: #37474f; + -md-neutral-variant900: #263238; + + /* Primary */ + -md-color-inverse-primary: -md-primary200; + -md-color-on-primary-container: -md-primary100; + -md-color-on-primary: -md-primary500; + -md-color-primary-container: -md-primary400; + -md-color-primary: -md-primary300; + + /* Secondary */ + -md-color-on-secondary-container: -md-secondary200; + -md-color-on-secondary: -md-secondary500; + -md-color-secondary-container: -md-secondary400; + -md-color-secondary: -md-secondary300; + + /* Tertiary */ + -md-color-on-tertiary-container: -md-error200; + -md-color-on-tertiary: -md-error400; + -md-color-tertiary-container: -md-error500; + -md-color-tertiary: -md-error300; + + /* Error */ + -md-color-on-error-container: -md-error300; + -md-color-on-error: -md-error500; + -md-color-error-container: -md-error600; + -md-color-error: -md-error300; + + /* Surface */ + -md-color-inverse-on-surface: -md-neutral500; + -md-color-inverse-surface: -md-neutral100; + + -md-color-on-surface-variant: -md-neutral-variant300; + -md-color-on-surface: -md-neutral300; + -md-color-surface-variant: -md-neutral700; + -md-color-surface: -md-neutral800; + + /* Background */ + -md-color-shadow: -md-color-black; + -md-color-outline: -md-neutral600; + -md-color-on-background: -md-neutral300; + -md-color-background: -md-neutral800; + + /* Text Colors */ + -md-color-text-highlight: -md-color-white; + -md-color-text-sub: -md-neutral400; + -md-color-text: -md-neutral200; +} diff --git a/src/main/resources/nl/isygameclient/css/style.css b/src/main/resources/nl/isygameclient/css/style.css @@ -0,0 +1,6 @@ +@import url(./views/game-select-menu.css); +@import url(./views/tictactoe.css); + +.transparent { + -fx-background-color: transparent; +} +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/css/theme.css b/src/main/resources/nl/isygameclient/css/theme.css @@ -0,0 +1,8 @@ +@import url(tokens.css); + +/* Modules */ +@import url(./modules/colors.module.css); +@import url(./modules/typography.module.css); + +@import url(./themes/theme.dark.css); +@import url(style.css); +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/css/themes/theme.dark.css b/src/main/resources/nl/isygameclient/css/themes/theme.dark.css @@ -0,0 +1,31 @@ +* { + -md-sys-color-surface-tint: -md-sys-color-surface-tint-dark; + -md-sys-color-surface-tint-color: -md-sys-color-surface-tint-color-dark; + -md-sys-color-on-error-container: -md-sys-color-on-error-container-dark; + -md-sys-color-on-error: -md-sys-color-on-error-dark; + -md-sys-color-error-container: -md-sys-color-error-container-dark; + -md-sys-color-on-tertiary-container: -md-sys-color-on-tertiary-container-dark; + -md-sys-color-on-tertiary: -md-sys-color-on-tertiary-dark; + -md-sys-color-tertiary-container: -md-sys-color-tertiary-container-dark; + -md-sys-color-tertiary: -md-sys-color-tertiary-dark; + -md-sys-color-shadow: -md-sys-color-shadow-dark; + -md-sys-color-error: -md-sys-color-error-dark; + -md-sys-color-outline: -md-sys-color-outline-dark; + -md-sys-color-on-background: -md-sys-color-on-background-dark; + -md-sys-color-background: -md-sys-color-background-dark; + -md-sys-color-inverse-on-surface: -md-sys-color-inverse-on-surface-dark; + -md-sys-color-inverse-surface: -md-sys-color-inverse-surface-dark; + -md-sys-color-on-surface-variant: -md-sys-color-on-surface-variant-dark; + -md-sys-color-on-surface: -md-sys-color-on-surface-dark; + -md-sys-color-surface-variant: -md-sys-color-surface-variant-dark; + -md-sys-color-surface: -md-sys-color-surface-dark; + -md-sys-color-on-secondary-container: -md-sys-color-on-secondary-container-dark; + -md-sys-color-on-secondary: -md-sys-color-on-secondary-dark; + -md-sys-color-secondary-container: -md-sys-color-secondary-container-dark; + -md-sys-color-secondary: -md-sys-color-secondary-dark; + -md-sys-color-inverse-primary: -md-sys-color-inverse-primary-dark; + -md-sys-color-on-primary-container: -md-sys-color-on-primary-container-dark; + -md-sys-color-on-primary: -md-sys-color-on-primary-dark; + -md-sys-color-primary-container: -md-sys-color-primary-container-dark; + -md-sys-color-primary: -md-sys-color-primary-dark; +} diff --git a/src/main/resources/nl/isygameclient/css/themes/theme.light.css b/src/main/resources/nl/isygameclient/css/themes/theme.light.css @@ -0,0 +1,31 @@ +* { + -md-sys-color-surface-tint: -md-sys-color-surface-tint-light; + -md-sys-color-surface-tint-color: -md-sys-color-surface-tint-color-light; + -md-sys-color-on-error-container: -md-sys-color-on-error-container-light; + -md-sys-color-on-error: -md-sys-color-on-error-light; + -md-sys-color-error-container: -md-sys-color-error-container-light; + -md-sys-color-on-tertiary-container: -md-sys-color-on-tertiary-container-light; + -md-sys-color-on-tertiary: -md-sys-color-on-tertiary-light; + -md-sys-color-tertiary-container: -md-sys-color-tertiary-container-light; + -md-sys-color-tertiary: -md-sys-color-tertiary-light; + -md-sys-color-shadow: -md-sys-color-shadow-light; + -md-sys-color-error: -md-sys-color-error-light; + -md-sys-color-outline: -md-sys-color-outline-light; + -md-sys-color-on-background: -md-sys-color-on-background-light; + -md-sys-color-background: -md-sys-color-background-light; + -md-sys-color-inverse-on-surface: -md-sys-color-inverse-on-surface-light; + -md-sys-color-inverse-surface: -md-sys-color-inverse-surface-light; + -md-sys-color-on-surface-variant: -md-sys-color-on-surface-variant-light; + -md-sys-color-on-surface: -md-sys-color-on-surface-light; + -md-sys-color-surface-variant: -md-sys-color-surface-variant-light; + -md-sys-color-surface: -md-sys-color-surface-light; + -md-sys-color-on-secondary-container: -md-sys-color-on-secondary-container-light; + -md-sys-color-on-secondary: -md-sys-color-on-secondary-light; + -md-sys-color-secondary-container: -md-sys-color-secondary-container-light; + -md-sys-color-secondary: -md-sys-color-secondary-light; + -md-sys-color-inverse-primary: -md-sys-color-inverse-primary-light; + -md-sys-color-on-primary-container: -md-sys-color-on-primary-container-light; + -md-sys-color-on-primary: -md-sys-color-on-primary-light; + -md-sys-color-primary-container: -md-sys-color-primary-container-light; + -md-sys-color-primary: -md-sys-color-primary-light; +} diff --git a/src/main/resources/nl/isygameclient/css/tokens.css b/src/main/resources/nl/isygameclient/css/tokens.css @@ -0,0 +1,223 @@ +* { + /* Primary */ + -md-ref-palette-primary0: #000000; + -md-ref-palette-primary5: #0a1400; + -md-ref-palette-primary10: #131f00; + -md-ref-palette-primary15: #1b2a00; + -md-ref-palette-primary20: #233600; + -md-ref-palette-primary25: #2c4200; + -md-ref-palette-primary30: #354e00; + -md-ref-palette-primary35: #3e5b00; + -md-ref-palette-primary40: #49680d; + -md-ref-palette-primary50: #618127; + -md-ref-palette-primary60: #7a9c3e; + -md-ref-palette-primary70: #93b756; + -md-ref-palette-primary80: #aed36e; + -md-ref-palette-primary90: #c9f087; + -md-ref-palette-primary95: #d7fe94; + -md-ref-palette-primary98: #f1ffd3; + -md-ref-palette-primary99: #f9ffe6; + -md-ref-palette-primary100: #ffffff; + + /* Secondary */ + -md-ref-palette-secondary0: #000000; + -md-ref-palette-secondary5: #0c1303; + -md-ref-palette-secondary10: #171e0a; + -md-ref-palette-secondary15: #212813; + -md-ref-palette-secondary20: #2b331d; + -md-ref-palette-secondary25: #363e27; + -md-ref-palette-secondary30: #414a32; + -md-ref-palette-secondary35: #4d563d; + -md-ref-palette-secondary40: #596248; + -md-ref-palette-secondary50: #727a5f; + -md-ref-palette-secondary60: #8b9478; + -md-ref-palette-secondary70: #a6af91; + -md-ref-palette-secondary80: #c1caab; + -md-ref-palette-secondary90: #dde6c6; + -md-ref-palette-secondary95: #ebf5d4; + -md-ref-palette-secondary98: #f4fddc; + -md-ref-palette-secondary99: #f9ffe6; + -md-ref-palette-secondary100: #ffffff; + + /* Tertiary */ + -md-ref-palette-tertiary0: #000000; + -md-ref-palette-tertiary5: #001412; + -md-ref-palette-tertiary10: #00201d; + -md-ref-palette-tertiary15: #002b28; + -md-ref-palette-tertiary20: #013733; + -md-ref-palette-tertiary25: #11423e; + -md-ref-palette-tertiary30: #1f4e49; + -md-ref-palette-tertiary35: #2c5a55; + -md-ref-palette-tertiary40: #396661; + -md-ref-palette-tertiary50: #527f7a; + -md-ref-palette-tertiary60: #6b9993; + -md-ref-palette-tertiary70: #85b4ae; + -md-ref-palette-tertiary80: #a0d0c9; + -md-ref-palette-tertiary90: #bcece5; + -md-ref-palette-tertiary95: #cafaf3; + -md-ref-palette-tertiary98: #e4fffa; + -md-ref-palette-tertiary99: #f2fffc; + -md-ref-palette-tertiary100: #ffffff; + + /* Error */ + -md-ref-palette-error0: #000000; + -md-ref-palette-error5: #2d0001; + -md-ref-palette-error10: #410002; + -md-ref-palette-error15: #540003; + -md-ref-palette-error20: #690005; + -md-ref-palette-error25: #7e0007; + -md-ref-palette-error30: #93000a; + -md-ref-palette-error35: #a80710; + -md-ref-palette-error40: #ba1a1a; + -md-ref-palette-error50: #de3730; + -md-ref-palette-error60: #ff5449; + -md-ref-palette-error70: #ff897d; + -md-ref-palette-error80: #ffb4ab; + -md-ref-palette-error90: #ffdad6; + -md-ref-palette-error95: #ffedea; + -md-ref-palette-error98: #fff8f7; + -md-ref-palette-error99: #fffbff; + -md-ref-palette-error100: #ffffff; + + /* Neutral */ + -md-ref-palette-neutral0: #000000; + -md-ref-palette-neutral5: #10110d; + -md-ref-palette-neutral10: #1b1c18; + -md-ref-palette-neutral15: #252621; + -md-ref-palette-neutral20: #30312c; + -md-ref-palette-neutral25: #3b3c37; + -md-ref-palette-neutral30: #464742; + -md-ref-palette-neutral35: #52534d; + -md-ref-palette-neutral40: #5e5f59; + -md-ref-palette-neutral50: #777771; + -md-ref-palette-neutral60: #91918a; + -md-ref-palette-neutral70: #acaba5; + -md-ref-palette-neutral80: #c7c7bf; + -md-ref-palette-neutral90: #e4e3db; + -md-ref-palette-neutral95: #f2f1e9; + -md-ref-palette-neutral98: #fbf9f2; + -md-ref-palette-neutral99: #fefcf4; + -md-ref-palette-neutral100: #ffffff; + + /* Neutral Variant */ + -md-ref-palette-neutral-variant0: #000000; + -md-ref-palette-neutral-variant5: #0f1209; + -md-ref-palette-neutral-variant10: #1a1d13; + -md-ref-palette-neutral-variant15: #24271d; + -md-ref-palette-neutral-variant20: #2e3227; + -md-ref-palette-neutral-variant25: #393d32; + -md-ref-palette-neutral-variant30: #45483d; + -md-ref-palette-neutral-variant35: #515448; + -md-ref-palette-neutral-variant40: #5d6054; + -md-ref-palette-neutral-variant50: #75786c; + -md-ref-palette-neutral-variant60: #8f9284; + -md-ref-palette-neutral-variant70: #aaad9e; + -md-ref-palette-neutral-variant80: #c5c8b9; + -md-ref-palette-neutral-variant90: #e2e4d4; + -md-ref-palette-neutral-variant95: #f0f2e2; + -md-ref-palette-neutral-variant98: #f9fbeb; + -md-ref-palette-neutral-variant99: #fcfeee; + -md-ref-palette-neutral-variant100: #ffffff; + + + /* System Colors */ + -md-sys-color-primary: #bdcca0; + -md-sys-color-on-primary: #293414; + -md-sys-color-primary-container: #3f4b29; + -md-sys-color-on-primary-container: #d9e8ba; + -md-sys-color-secondary: #c6c8ba; + -md-sys-color-on-secondary: #2f3228; + -md-sys-color-secondary-container: #45483d; + -md-sys-color-on-secondary-container: #e2e4d5; + -md-sys-color-tertiary: #b9cac7; + -md-sys-color-on-tertiary: #243331; + -md-sys-color-tertiary-container: #3a4a48; + -md-sys-color-on-tertiary-container: #d5e6e3; + -md-sys-color-error: #ffb4ab; + -md-sys-color-on-error: #690005; + -md-sys-color-error-container: #93000a; + -md-sys-color-on-error-container: #ffb4ab; + -md-sys-color-background: #1c1c1a; + -md-sys-color-on-background: #e5e2de; + -md-sys-color-surface: #1c1c1a; + -md-sys-color-on-surface: #e5e2de; + -md-sys-color-surface-variant: #464742; + -md-sys-color-on-surface-variant: #c7c7c0; + -md-sys-color-outline: #91918b; + -md-sys-color-outline-variant: #464742; + -md-sys-color-shadow: #000000; + -md-sys-color-scrim: #000000; + -md-sys-color-inverse-surface: #e5e2de; + -md-sys-color-inverse-on-surface: #31302e; + -md-sys-color-inverse-primary: #56633e; + -md-sys-color-surface-tint: #bdcca0; + -md-sys-color-surface-tint-color: #bdcca0; + + /* Light Colors */ + -md-sys-color-primary-light: #56633e; + -md-sys-color-on-primary-light: #ffffff; + -md-sys-color-primary-container-light: #d9e8ba; + -md-sys-color-on-primary-container-light: #141f03; + -md-sys-color-secondary-light: #5d6054; + -md-sys-color-on-secondary-light: #ffffff; + -md-sys-color-secondary-container-light: #e2e4d5; + -md-sys-color-on-secondary-container-light: #1a1d14; + -md-sys-color-tertiary-light: #52625f; + -md-sys-color-on-tertiary-light: #ffffff; + -md-sys-color-tertiary-container-light: #d5e6e3; + -md-sys-color-on-tertiary-container-light: #0f1e1c; + -md-sys-color-error-light: #ba1a1a; + -md-sys-color-on-error-light: #ffffff; + -md-sys-color-error-container-light: #ffdad6; + -md-sys-color-on-error-container-light: #410002; + -md-sys-color-background-light: #fffcf8; + -md-sys-color-on-background-light: #1c1c1a; + -md-sys-color-surface-light: #fffcf8; + -md-sys-color-on-surface-light: #1c1c1a; + -md-sys-color-surface-variant-light: #e4e3db; + -md-sys-color-on-surface-variant-light: #464742; + -md-sys-color-outline-light: #777772; + -md-sys-color-outline-variant-light: #c7c7c0; + -md-sys-color-shadow-light: #000000; + -md-sys-color-scrim-light: #000000; + -md-sys-color-inverse-surface-light: #31302e; + -md-sys-color-inverse-on-surface-light: #f3f0ec; + -md-sys-color-inverse-primary-light: #bdcca0; + -md-sys-color-surface-tint-light: #56633e; + -md-sys-color-surface-tint-color-light: #56633e; + + /* Dark Colors */ + -md-sys-color-primary-dark: #bdcca0; + -md-sys-color-on-primary-dark: #293414; + -md-sys-color-primary-container-dark: #3f4b29; + -md-sys-color-on-primary-container-dark: #d9e8ba; + -md-sys-color-secondary-dark: #c6c8ba; + -md-sys-color-on-secondary-dark: #2f3228; + -md-sys-color-secondary-container-dark: #45483d; + -md-sys-color-on-secondary-container-dark: #e2e4d5; + -md-sys-color-tertiary-dark: #b9cac7; + -md-sys-color-on-tertiary-dark: #243331; + -md-sys-color-tertiary-container-dark: #3a4a48; + -md-sys-color-on-tertiary-container-dark: #d5e6e3; + -md-sys-color-error-dark: #ffb4ab; + -md-sys-color-on-error-dark: #690005; + -md-sys-color-error-container-dark: #93000a; + -md-sys-color-on-error-container-dark: #ffb4ab; + -md-sys-color-background-dark: #1c1c1a; + -md-sys-color-on-background-dark: #e5e2de; + -md-sys-color-surface-dark: #1c1c1a; + -md-sys-color-on-surface-dark: #e5e2de; + -md-sys-color-surface-variant-dark: #464742; + -md-sys-color-on-surface-variant-dark: #c7c7c0; + -md-sys-color-outline-dark: #91918b; + -md-sys-color-outline-variant-dark: #464742; + -md-sys-color-shadow-dark: #000000; + -md-sys-color-scrim-dark: #000000; + -md-sys-color-inverse-surface-dark: #e5e2de; + -md-sys-color-inverse-on-surface-dark: #31302e; + -md-sys-color-inverse-primary-dark: #56633e; + -md-sys-color-surface-tint-dark: #bdcca0; + -md-sys-color-surface-tint-color-dark: #bdcca0; + + -md-ref-palette-neutralNaN: #FFFFFF; +} +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/css/views/game-select-menu.css b/src/main/resources/nl/isygameclient/css/views/game-select-menu.css @@ -0,0 +1,60 @@ +/* Menu Bar */ +.menu-bar { + -fx-effect: dropshadow(three-pass-box, black, 10, 0, 0, 0); +} +.menu-bar-settings { + -fx-fill: -md-sys-color-inverse-surface; +} + +/* Search Bar */ +.search-bar { + -fx-border-color: -md-sys-color-on-surface; + -fx-border-width: 1; + -fx-border-radius: 2; +} + +.search-field { + -fx-text-fill: -md-sys-color-inverse-surface; +} + +/* Game List */ +#gamesList { + -fx-background-radius: 0; + -fx-vbar-policy: never; +} + +#gamesList .list-cell { + -fx-background-color: transparent; + -fx-text-fill: -md-sys-color-inverse-surface; + + /* Large Body Type Style */ + -fx-font-size: 16; + -fx-font-style: normal; + -fx-font-weight: 400; +} + +#gamesList .list-cell:filled:selected:focused, +.list-cell:filled:selected { + -fx-background-color: -md-ref-palette-tertiary50; + -fx-text-fill: white; +} + +#gamesList .list-cell:even { + -fx-background-color: -md-sys-color-surface-variant; +} + +#gamesList .list-cell:odd { + -fx-background-color: -md-sys-color-inverse-on-surface; +} + +#gamesList .list-cell:filled:hover { + -fx-background-color: -md-ref-palette-tertiary60; + -fx-text-fill: white; +} + +/* Game Detail */ + +#gameDetail { + -fx-border-color: -md-sys-color-surface; + -fx-border-width: 0 5 0 0; +} diff --git a/src/main/resources/nl/isygameclient/css/views/tictactoe.css b/src/main/resources/nl/isygameclient/css/views/tictactoe.css @@ -0,0 +1,10 @@ +.ttt-grid { + -fx-border-color: -md-sys-color-surface-variant; + -fx-border-width: 5; + -fx-border-radius: 2; + } + + .ttt-button { + -fx-background-color: -md-sys-color-surface-variant; + -fx-text-fill: -md-sys-color-on-surface-variant + } +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/images/icons/svg/icons8_close.svg b/src/main/resources/nl/isygameclient/images/icons/svg/icons8_close.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?><s +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="480" height="480"> + <path d="M4.7070312 3.2929688L3.2929688 4.7070312L10.585938 12L3.2929688 19.292969L4.7070312 20.707031L12 13.414062L19.292969 20.707031L20.707031 19.292969L13.414062 12L20.707031 4.7070312L19.292969 3.2929688L12 10.585938L4.7070312 3.2929688 z" fill="#5B5B5B" /> +</svg> +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/images/icons/svg/icons8_search_1.svg b/src/main/resources/nl/isygameclient/images/icons/svg/icons8_search_1.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?><s +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="480" height="480"> + <path d="M9 2C5.1458514 2 2 5.1458514 2 9C2 12.854149 5.1458514 16 9 16C10.747998 16 12.345009 15.348024 13.574219 14.28125L14 14.707031L14 16L20 22L22 20L16 14L14.707031 14L14.28125 13.574219C15.348024 12.345009 16 10.747998 16 9C16 5.1458514 12.854149 2 9 2 z M 9 4C11.773268 4 14 6.2267316 14 9C14 11.773268 11.773268 14 9 14C6.2267316 14 4 11.773268 4 9C4 6.2267316 6.2267316 4 9 4 z" fill="#5B5B5B" /> +</svg> +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/images/icons/svg/icons8_settings.svg b/src/main/resources/nl/isygameclient/images/icons/svg/icons8_settings.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?><s +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="480" height="480"> + <path d="M11.423828 2C11.179828 2 10.969688 2.1769687 10.929688 2.4179688L10.646484 4.1230469C10.159736 4.2067166 9.689176 4.3360771 9.2363281 4.5039062L8.1347656 3.1679688C7.9797656 2.9789688 7.7100469 2.9297344 7.4980469 3.0527344L6.5019531 3.6289062C6.2899531 3.7509062 6.1972031 4.0083281 6.2832031 4.2363281L6.8886719 5.8535156C6.513238 6.1663963 6.1663963 6.513238 5.8535156 6.8886719L4.2363281 6.2832031C4.0083281 6.1972031 3.7509062 6.2899531 3.6289062 6.5019531L3.0527344 7.4980469C2.9297344 7.7100469 2.9789688 7.9797656 3.1679688 8.1347656L4.5039062 9.2363281C4.3360771 9.689176 4.2067166 10.159736 4.1230469 10.646484L2.4179688 10.929688C2.1769687 10.970688 2 11.178828 2 11.423828L2 12.576172C2 12.820172 2.1769687 13.030312 2.4179688 13.070312L4.1230469 13.353516C4.2067166 13.840264 4.3360771 14.310824 4.5039062 14.763672L3.1679688 15.865234C2.9789687 16.020234 2.9307344 16.289953 3.0527344 16.501953L3.6289062 17.498047C3.7509062 17.710047 4.0083281 17.802797 4.2363281 17.716797L5.8535156 17.111328C6.1663963 17.486762 6.513238 17.833604 6.8886719 18.146484L6.2832031 19.763672C6.1972031 19.992672 6.2909531 20.249094 6.5019531 20.371094L7.4980469 20.947266C7.7100469 21.069266 7.9797656 21.020031 8.1347656 20.832031L9.234375 19.496094C9.6877476 19.664236 10.15912 19.793178 10.646484 19.876953L10.929688 21.582031C10.970688 21.823031 11.178828 22 11.423828 22L12.576172 22C12.820172 22 13.030312 21.823031 13.070312 21.582031L13.353516 19.876953C13.840264 19.793283 14.310824 19.663923 14.763672 19.496094L15.865234 20.832031C16.020234 21.021031 16.289953 21.069266 16.501953 20.947266L17.498047 20.371094C17.710047 20.249094 17.802797 19.991672 17.716797 19.763672L17.111328 18.146484C17.486762 17.833604 17.833604 17.486762 18.146484 17.111328L19.763672 17.716797C19.992672 17.802797 20.249094 17.709047 20.371094 17.498047L20.947266 16.501953C21.069266 16.289953 21.020031 16.020234 20.832031 15.865234L19.496094 14.765625C19.664236 14.312252 19.793178 13.84088 19.876953 13.353516L21.582031 13.070312C21.823031 13.029312 22 12.821172 22 12.576172L22 11.423828C22 11.179828 21.823031 10.969688 21.582031 10.929688L19.876953 10.646484C19.793283 10.159736 19.663923 9.689176 19.496094 9.2363281L20.832031 8.1347656C21.021031 7.9797656 21.069266 7.7100469 20.947266 7.4980469L20.371094 6.5019531C20.249094 6.2899531 19.991672 6.1972031 19.763672 6.2832031L18.146484 6.8886719C17.833604 6.513238 17.486762 6.1663963 17.111328 5.8535156L17.716797 4.2363281C17.802797 4.0073281 17.709047 3.7509062 17.498047 3.6289062L16.501953 3.0527344C16.289953 2.9307344 16.020234 2.9799687 15.865234 3.1679688L14.765625 4.5039062C14.312252 4.3357635 13.84088 4.2068225 13.353516 4.1230469L13.070312 2.4179688C13.029312 2.1769687 12.821172 2 12.576172 2L11.423828 2 z M 11 6.0898438L11 9.1738281 A 3 3 0 0 0 9 12 A 3 3 0 0 0 9.0507812 12.548828L6.3789062 14.089844C6.1382306 13.438833 6 12.736987 6 12C6 9.0161425 8.1553612 6.5637988 11 6.0898438 z M 13 6.0898438C15.844639 6.5637988 18 9.0161425 18 12C18 12.737875 17.86037 13.440133 17.619141 14.091797L14.947266 12.546875 A 3 3 0 0 0 15 12 A 3 3 0 0 0 13 9.1757812L13 6.0898438 z M 13.947266 14.277344L16.628906 15.826172C15.530388 17.156023 13.868625 18 12 18C10.131375 18 8.4696124 17.156023 7.3710938 15.826172L10.050781 14.279297 A 3 3 0 0 0 12 15 A 3 3 0 0 0 13.947266 14.277344 z" fill="#5B5B5B" /> +</svg> +\ No newline at end of file diff --git a/src/main/resources/nl/isygameclient/images/reversie_logo.png b/src/main/resources/nl/isygameclient/images/othello_logo.png Binary files differ. diff --git a/src/main/resources/nl/isygameclient/views/GameSelectorMenu/GameCard.fxml b/src/main/resources/nl/isygameclient/views/GameSelectorMenu/GameCard.fxml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import java.lang.*?> +<?import javafx.scene.control.*?> +<?import javafx.scene.image.*?> +<?import javafx.scene.layout.*?> + +<VBox prefHeight="350.0" prefWidth="250.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" + alignment="CENTER" fx:controller="nl.isygameclient.controllers.GameSeletorMenu.GameCardController" onMouseClicked="#onCardMouseClick"> + <styleClass> + <String fx:value="surface"/> + </styleClass> + <AnchorPane> + <ImageView fx:id="gameImage" fitWidth="250.0" preserveRatio="true"/> + </AnchorPane> + <Separator/> + <Label fx:id="gameTitle" alignment="TOP_RIGHT" text="Title"> + <styleClass> + <String fx:value="title-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </Label> +</VBox> diff --git a/src/main/resources/nl/isygameclient/views/GameSelectorMenu/GameSelectorMenu.fxml b/src/main/resources/nl/isygameclient/views/GameSelectorMenu/GameSelectorMenu.fxml @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="UTF-8"?> + + +<?import com.jfoenix.controls.*?> +<?import de.jensd.fx.glyphs.materialicons.MaterialIconView?> +<?import javafx.geometry.*?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ListView?> +<?import javafx.scene.control.ScrollPane?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.*?> +<?import java.lang.*?> +<AnchorPane minHeight="480.0" minWidth="600.0" prefHeight="1080.0" prefWidth="1920.0" styleClass="white" + stylesheets="@../../css/theme.css" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" + fx:controller="nl.isygameclient.controllers.GameSeletorMenu.GameSelectorMenuController"> + <BorderPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" + AnchorPane.topAnchor="0.0"> + <top> + <HBox prefHeight="50.0" prefWidth="Infinity"> + <styleClass> + <String fx:value="menu-bar"/> + <String fx:value="background"/> + </styleClass> + + <!-- Menu Buttons --> + <JFXButton text="Store" disable="true" prefHeight="Infinity"> + <styleClass> + <String fx:value="title-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </JFXButton> + + <JFXButton text="Games" prefHeight="Infinity"> + <styleClass> + <String fx:value="title-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </JFXButton> + + <JFXButton text="User" disable="true" prefHeight="Infinity"> + <styleClass> + <String fx:value="title-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </JFXButton> + + <Pane HBox.hgrow="ALWAYS"/> + <JFXButton textAlignment="RIGHT" text="Settings" prefHeight="Infinity"> + <styleClass> + <String fx:value="title-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </JFXButton> + </HBox> + </top> + <left> + <AnchorPane> + <HBox AnchorPane.topAnchor="0.0" AnchorPane.bottomAnchor="0.0"> + + <!-- Search Container --> + <VBox prefWidth="200.0" AnchorPane.topAnchor="0.0" AnchorPane.bottomAnchor="0.0" + AnchorPane.leftAnchor="0.0"> + <padding> + <Insets left="5" right="5"/> + </padding> + <styleClass> + <String fx:value="search-container"/> + <String fx:value="surface"/> + </styleClass> + + <!-- Search Field --> + <StackPane> + <styleClass> + <String fx:value="search-bar"/> + <String fx:value="inverse-on-surface"/> + </styleClass> + <VBox.margin> + <Insets bottom="5.0" top="10.0"/> + </VBox.margin> + + + <TextField fx:id="searchBox" promptText="Search"> + <padding> + <Insets left="35" right="35"/> + </padding> + <styleClass> + <String fx:value="search-field"/> + <String fx:value="transparent"/> + </styleClass> + </TextField> + + <JFXButton StackPane.alignment="CENTER_LEFT" disable="true"> + <graphic> + <MaterialIconView StackPane.alignment="CENTER_LEFT" glyphName="SEARCH" + glyphSize="20" styleClass="icon"> + <styleClass> + <String fx:value="on-surface-fill"/> + </styleClass> + </MaterialIconView> + </graphic> + </JFXButton> + + <JFXButton onAction="#onClearSearchButtonClick" StackPane.alignment="CENTER_RIGHT"> + <graphic> + <MaterialIconView glyphName="CLOSE" glyphSize="16" styleClass="icon"> + <styleClass> + <String fx:value="on-surface-fill"/> + </styleClass> + </MaterialIconView> + </graphic> + </JFXButton> + </StackPane> + + <!-- Search List --> + <ListView fx:id="gamesList" VBox.vgrow="ALWAYS" styleClass="surface"> + <padding> + <Insets topRightBottomLeft="1"/> + </padding> + </ListView> + </VBox> + + + <!-- Game Detail Container --> + <VBox fx:id="gameDetail" prefWidth="400" HBox.hgrow="ALWAYS" alignment="TOP_CENTER" + styleClass="inverse-on-surface"> + <padding> + <Insets topRightBottomLeft="20"/> + </padding> + <Label text="Game Title"> + <styleClass> + <String fx:value="display-small"/> + <String fx:value="on-surface-text"/> + <String fx:value="on-surface-fill"/> + </styleClass> + </Label> + </VBox> + </HBox> + </AnchorPane> + </left> + <center> + <ScrollPane fitToWidth="true" fitToHeight="true" styleClass="surface-variant"> + <FlowPane fx:id="gameContainer" hgap="5" vgap="10" > + <styleClass> + <String fx:value="surface-variant"/> + </styleClass> + <padding> + <Insets topRightBottomLeft="20"/> + </padding> + </FlowPane> + </ScrollPane> + </center> + </BorderPane> +</AnchorPane> diff --git a/src/main/resources/nl/isygameclient/views/TicTacToe/TicTacToeMainMenu.fxml b/src/main/resources/nl/isygameclient/views/TicTacToe/TicTacToeMainMenu.fxml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> + + +<?import com.jfoenix.controls.JFXButton?> +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.VBox?> +<?import java.lang.*?> +<AnchorPane stylesheets="@../../css/theme.css" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" + fx:controller="nl.isygameclient.controllers.TicTacToeGame.TicTacToeMainMenuController" + prefWidth="680" prefHeight="480"> + <VBox alignment="CENTER" AnchorPane.rightAnchor="0" AnchorPane.leftAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.topAnchor="0"> + <styleClass> + <String fx:value="surface"/> + </styleClass> + <Label text="Tic Tac Toe"> + <styleClass> + <String fx:value="display-large"/> + <String fx:value="on-surface-text"/> + </styleClass> + <padding> + <Insets topRightBottomLeft="50"/> + </padding> + </Label> + <VBox spacing="10" alignment="CENTER" maxWidth="200"> + <JFXButton text="Single Player" onAction="#onSinglePlayerButtonClick" prefWidth="Infinity"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="title-medium"/> + </styleClass> + </JFXButton> + <JFXButton text="Multiplayer" onAction="#onMultiplayerButtonClick" prefWidth="Infinity"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="title-medium"/> + </styleClass> + </JFXButton> + <JFXButton text="Exit" onAction="#onExitButtonClick" prefWidth="Infinity"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="title-medium"/> + </styleClass> + </JFXButton> + </VBox> + </VBox> +</AnchorPane> diff --git a/src/main/resources/nl/isygameclient/views/TicTacToe/TicTacToeSinglePlayer.fxml b/src/main/resources/nl/isygameclient/views/TicTacToe/TicTacToeSinglePlayer.fxml @@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import com.jfoenix.controls.*?> +<?import javafx.geometry.*?> +<?import javafx.scene.control.*?> +<?import javafx.scene.layout.*?> +<?import java.lang.*?> +<BorderPane stylesheets="@../../css/theme.css" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" + fx:controller="nl.isygameclient.controllers.TicTacToeGame.TicTacToeSinglePlayerController"> + <styleClass> + <String fx:value="surface"/> + </styleClass> + <left> + <VBox prefWidth="200" styleClass="surface-variant" spacing="20" > + <padding> + <Insets topRightBottomLeft="20"/> + </padding> + + <!-- Difficulty Controls --> + <VBox> + <Label text="Difficulty"> + <styleClass> + <String fx:value="headline-small"/> + <String fx:value="on-surface-variant-text"/> + </styleClass> + </Label> + <Separator styleClass="on-surface-variant-text"/> + <JFXComboBox fx:id="difficultyCombo" prefWidth="Infinity" onAction="#onDifficultyComboSelect"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="body-large"/> + </styleClass> + </JFXComboBox> + </VBox> + + <!-- Playing As Controls --> + <VBox> + <Label text="Playing As"> + <styleClass> + <String fx:value="headline-small"/> + <String fx:value="on-surface-variant-text"/> + </styleClass> + </Label> + <Separator styleClass="on-surface-variant-text"/> + <JFXComboBox fx:id="playingAsCombo" prefWidth="Infinity" onAction="#onPlayingAsComboSelect"> + <styleClass> + <String fx:value="primary"/>--> + <String fx:value="on-primary-text"/> + <String fx:value="body-large"/> + </styleClass> + </JFXComboBox> + </VBox> + + <!-- Opponents Controls --> + <VBox> + <Label text="Opponent"> + <styleClass> + <String fx:value="headline-small"/> + <String fx:value="on-surface-variant-text"/> + </styleClass> + </Label> + <Separator styleClass="on-surface-variant-text"/> + <JFXComboBox fx:id="opponentCombo" prefWidth="Infinity" onAction="#onOpponentComboSelect"> + <styleClass> + <String fx:value="primary"/>--> + <String fx:value="on-primary-text"/> + <String fx:value="body-large"/> + </styleClass> + </JFXComboBox> + </VBox> + + <!-- Best of Controls --> + <VBox> + <Label text="Best Of"> + <styleClass> + <String fx:value="headline-small"/> + <String fx:value="on-surface-variant-text"/> + </styleClass> + </Label> + <Separator styleClass="on-surface-variant-text"/> + </VBox> + + <!-- Window Controls --> + <Pane VBox.vgrow="ALWAYS"/> + <VBox spacing="10" alignment="CENTER"> + <JFXButton text="New Game" onAction="#onNewGameButtonClick" prefWidth="Infinity"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="title-medium"/> + </styleClass> + </JFXButton> + <JFXButton text="Main Menu" onAction="#onMainMenuButtonClick" prefWidth="Infinity"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="title-medium"/> + </styleClass> + </JFXButton> + <JFXButton text="Exit" onAction="#onExitButtonClick" prefWidth="Infinity"> + <styleClass> + <String fx:value="primary"/> + <String fx:value="on-primary-text"/> + <String fx:value="title-medium"/> + </styleClass> + </JFXButton> + </VBox> + </VBox> + </left> + <!-- Game Field --> + <center> + <HBox alignment="CENTER"> + <padding> + <Insets topRightBottomLeft="20"/> + </padding> + + <!-- Left --> + <VBox minWidth="150" maxHeight="800" alignment="TOP_CENTER"> + <Label text="Score Player"> + <styleClass> + <String fx:value="headline-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </Label> + <Label text="0"> + <styleClass> + <String fx:value="headline-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </Label> + <Separator maxWidth="100"/> + </VBox> + + <!-- Center --> + <VBox alignment="CENTER" minWidth="640"> + <HBox alignment="CENTER"> + <padding> + <Insets topRightBottomLeft="5"/> + </padding> + <Label text="Current Player: "> + <styleClass> + <String fx:value="on-surface-text"/> + <String fx:value="headline-small"/> + </styleClass> + </Label> + <Label fx:id="currentPlayer"> + <styleClass> + <String fx:value="on-surface-text"/> + <String fx:value="headline-small"/> + </styleClass> + </Label> + </HBox> + <Separator maxWidth="100"/> + + <StackPane> + <VBox.margin> + <Insets left="20" right="20" top="20"/> + </VBox.margin> + <GridPane fx:id="grid" styleClass="ttt-grid" vgap="10" hgap="10" maxWidth="640" maxHeight="640"> + <padding> + <Insets topRightBottomLeft="10"/> + </padding> + </GridPane> + <Label fx:id="gameOverText" visible="false"> + <padding> + <Insets top="10" bottom="10" left="20" right="20"/> + </padding> + <styleClass> + <String fx:value="surface"/> + <String fx:value="display-large"/> + <String fx:value="on-surface-text"/> + </styleClass> + </Label> + </StackPane> + </VBox> + + <!-- Right --> + <VBox minWidth="150" maxHeight="800" alignment="TOP_CENTER"> + <Label text="Score Opponent"> + <styleClass> + <String fx:value="headline-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </Label> + <Label text="0"> + <styleClass> + <String fx:value="headline-medium"/> + <String fx:value="on-surface-text"/> + </styleClass> + </Label> + <Separator maxWidth="100"/> + </VBox> + </HBox> + </center> +</BorderPane> diff --git a/src/main/resources/nl/isygameclient/views/hello-view.fxml b/src/main/resources/nl/isygameclient/views/hello-view.fxml @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.geometry.*?> -<?import javafx.scene.control.*?> -<?import javafx.scene.layout.*?> - -<VBox alignment="CENTER" spacing="20.0" stylesheets="@../css/hello.css" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/16" fx:controller="nl.isygameclient.controllers.HelloController"> - <padding> - <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> - </padding> - - <Label fx:id="welcomeText" /> - <Button fx:id="welcomeButton" onAction="#onHelloButtonClick" text="Hello!" /> -</VBox>