6
.gitignore
vendored
@@ -47,6 +47,7 @@ shelf/
|
|||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
misc.xml
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
## Eclipse
|
## Eclipse
|
||||||
@@ -102,3 +103,8 @@ newgamesver-release-V1.jar
|
|||||||
server.properties
|
server.properties
|
||||||
gameserver.log.*
|
gameserver.log.*
|
||||||
gameserver.log
|
gameserver.log
|
||||||
|
|
||||||
|
##############################
|
||||||
|
## JPackage
|
||||||
|
##############################
|
||||||
|
jpackage-input
|
||||||
38
app/pom.xml
@@ -9,7 +9,7 @@
|
|||||||
<main-class>org.toop.Main</main-class>
|
<main-class>org.toop.Main</main-class>
|
||||||
<maven.compiler.source>25</maven.compiler.source>
|
<maven.compiler.source>25</maven.compiler.source>
|
||||||
<maven.compiler.target>25</maven.compiler.target>
|
<maven.compiler.target>25</maven.compiler.target>
|
||||||
|
<maven.compiler.release>25</maven.compiler.release>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -44,14 +44,34 @@
|
|||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.14.1</version>
|
<version>3.6.1</version>
|
||||||
<configuration>
|
<executions>
|
||||||
<source>25</source>
|
<execution>
|
||||||
<target>25</target>
|
<phase>package</phase>
|
||||||
<release>25</release>
|
<goals>
|
||||||
<encoding>UTF-8</encoding>
|
<goal>shade</goal>
|
||||||
</configuration>
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<artifact>*:*</artifact>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/*.SF</exclude>
|
||||||
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
|
</excludes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>org.toop.Main</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
<finalName>pism</finalName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.diffplug.spotless</groupId>
|
<groupId>com.diffplug.spotless</groupId>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package org.toop;
|
package org.toop;
|
||||||
|
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.framework.asset.AssetLoader;
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
import org.toop.framework.asset.AssetManager;
|
import org.toop.framework.asset.ResourceManager;
|
||||||
import org.toop.framework.audio.SoundManager;
|
import org.toop.framework.audio.SoundManager;
|
||||||
import org.toop.framework.networking.NetworkingClientManager;
|
import org.toop.framework.networking.NetworkingClientManager;
|
||||||
import org.toop.framework.networking.NetworkingInitializationException;
|
import org.toop.framework.networking.NetworkingInitializationException;
|
||||||
|
|
||||||
public class Main {
|
public final class Main {
|
||||||
static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
initSystems();
|
initSystems();
|
||||||
App.run(args);
|
App.run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initSystems() throws NetworkingInitializationException {
|
private static void initSystems() throws NetworkingInitializationException {
|
||||||
AssetManager.loadAssets(new AssetLoader("app/src/main/resources/assets"));
|
ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets"));
|
||||||
new Thread(NetworkingClientManager::new).start();
|
new Thread(NetworkingClientManager::new).start();
|
||||||
new Thread(SoundManager::new).start();
|
new Thread(SoundManager::new).start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,54 @@ package org.toop.app;
|
|||||||
|
|
||||||
import org.toop.app.menu.MainMenu;
|
import org.toop.app.menu.MainMenu;
|
||||||
import org.toop.app.menu.Menu;
|
import org.toop.app.menu.Menu;
|
||||||
import org.toop.app.menu.QuitMenu;
|
|
||||||
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.toop.framework.asset.AssetManager;
|
import org.toop.framework.asset.ResourceManager;
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
import org.toop.framework.asset.resources.CssAsset;
|
||||||
import org.toop.framework.audio.events.AudioEvents;
|
import org.toop.framework.audio.events.AudioEvents;
|
||||||
import org.toop.framework.eventbus.EventFlow;
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
import org.toop.local.AppContext;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
public final class App extends Application {
|
||||||
|
|
||||||
public class App extends Application {
|
|
||||||
private static Stage stage;
|
private static Stage stage;
|
||||||
private static Scene scene;
|
|
||||||
private static StackPane root;
|
private static StackPane root;
|
||||||
|
|
||||||
private Locale currentLocale = AppContext.getLocale();
|
private static int width;
|
||||||
private LocalizationAsset loc = AssetManager.get("localization.properties");
|
private static int height;
|
||||||
|
|
||||||
|
private static boolean isQuitting;
|
||||||
|
|
||||||
|
private static class QuitMenu extends Menu {
|
||||||
|
public QuitMenu() {
|
||||||
|
final Region background = createBackground("quit_background");
|
||||||
|
|
||||||
|
final Text sure = createText("Are you sure?");
|
||||||
|
|
||||||
|
final Button yes = createButton("Yes", () -> { stage.close(); });
|
||||||
|
final Button no = createButton("No", () -> { pop(); isQuitting = false; });
|
||||||
|
|
||||||
|
final HBox buttons = new HBox(50, yes, no);
|
||||||
|
buttons.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
final VBox box = new VBox(35, sure, buttons);
|
||||||
|
box.getStyleClass().add("quit_box");
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
box.setMaxWidth(350);
|
||||||
|
box.setMaxHeight(200);
|
||||||
|
|
||||||
|
pane = new StackPane(background, box);
|
||||||
|
pane.getStylesheets().add(ResourceManager.get(CssAsset.class, "quit.css").getUrl());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void run(String[] args) {
|
public static void run(String[] args) {
|
||||||
launch(args);
|
launch(args);
|
||||||
@@ -31,15 +58,20 @@ public class App extends Application {
|
|||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws Exception {
|
public void start(Stage stage) throws Exception {
|
||||||
final StackPane root = new StackPane(new MainMenu().getPane());
|
final StackPane root = new StackPane(new MainMenu().getPane());
|
||||||
final Scene scene = new Scene(root);
|
|
||||||
|
|
||||||
stage.setTitle(loc.getString("windowTitle", currentLocale));
|
final Scene scene = new Scene(root);
|
||||||
|
scene.getStylesheets().add(((CssAsset) ResourceManager.get("app.css")).getUrl());
|
||||||
|
|
||||||
|
stage.setTitle("pism");
|
||||||
stage.setMinWidth(1080);
|
stage.setMinWidth(1080);
|
||||||
stage.setMinHeight(720);
|
stage.setMinHeight(720);
|
||||||
|
|
||||||
stage.setOnCloseRequest(event -> {
|
stage.setOnCloseRequest(event -> {
|
||||||
event.consume();
|
event.consume();
|
||||||
push(new QuitMenu());
|
|
||||||
|
if (!isQuitting) {
|
||||||
|
quitPopup();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
@@ -48,28 +80,35 @@ public class App extends Application {
|
|||||||
stage.show();
|
stage.show();
|
||||||
|
|
||||||
App.stage = stage;
|
App.stage = stage;
|
||||||
App.scene = scene;
|
|
||||||
App.root = root;
|
App.root = root;
|
||||||
|
|
||||||
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).postEvent();
|
App.width = (int)stage.getWidth();
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(0.3)).postEvent();
|
App.height = (int)stage.getHeight();
|
||||||
|
|
||||||
|
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
|
||||||
|
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(0.1)).asyncPostEvent();
|
||||||
|
|
||||||
|
App.isQuitting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void quitPopup() {
|
||||||
|
isQuitting = true;
|
||||||
|
push(new QuitMenu());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void activate(Menu menu) {
|
public static void activate(Menu menu) {
|
||||||
scene.setRoot(menu.getPane());
|
pop();
|
||||||
|
push(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void push(Menu menu) {
|
public static void push(Menu menu) {
|
||||||
root.getChildren().add(menu.getPane());
|
root.getChildren().addLast(menu.getPane());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void pop() {
|
public static void pop() {
|
||||||
root.getChildren().removeLast();
|
root.getChildren().removeLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void quit() {
|
public static int getWidth() { return width; }
|
||||||
stage.close();
|
public static int getHeight() { return height; }
|
||||||
}
|
|
||||||
|
|
||||||
public static StackPane getRoot() { return root; }
|
|
||||||
}
|
}
|
||||||
21
app/src/main/java/org/toop/app/GameType.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package org.toop.app;
|
||||||
|
|
||||||
|
public enum GameType {
|
||||||
|
TICTACTOE, REVERSI;
|
||||||
|
|
||||||
|
public static String toName(GameType type) {
|
||||||
|
return switch (type) {
|
||||||
|
case TICTACTOE -> "Tic Tac Toe";
|
||||||
|
case REVERSI -> "Reversi";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameType toType(String name) {
|
||||||
|
return switch (name) {
|
||||||
|
case "Tic Tac Toe" -> TICTACTOE;
|
||||||
|
case "Reversi" -> REVERSI;
|
||||||
|
|
||||||
|
default -> TICTACTOE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
package org.toop.app.menu;
|
package org.toop.app.menu;
|
||||||
|
|
||||||
import org.toop.framework.asset.AssetManager;
|
import org.toop.framework.asset.ResourceManager;
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
import org.toop.framework.asset.resources.LocalizationAsset;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public final class CreditsMenu extends Menu {
|
public final class CreditsMenu extends Menu {
|
||||||
private Locale currentLocale = AppContext.getLocale();
|
private Locale currentLocale = AppContext.getLocale();
|
||||||
private LocalizationAsset loc = AssetManager.get("localization.properties");
|
private LocalizationAsset loc = ResourceManager.get("localization.properties");
|
||||||
public CreditsMenu() {
|
public CreditsMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
33
app/src/main/java/org/toop/app/menu/GameSelectMenu.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package org.toop.app.menu;
|
||||||
|
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.toop.app.GameType;
|
||||||
|
|
||||||
|
public class GameSelectMenu extends Menu {
|
||||||
|
public GameSelectMenu(GameType type) {
|
||||||
|
final Region background = createBackground();
|
||||||
|
|
||||||
|
final ComboBox<String> selectedGame = new ComboBox<>();
|
||||||
|
selectedGame.getItems().add(GameType.toName(GameType.TICTACTOE));
|
||||||
|
selectedGame.getItems().add(GameType.toName(GameType.REVERSI));
|
||||||
|
selectedGame.setValue(GameType.toName(type));
|
||||||
|
|
||||||
|
final ComboBox<String> selectedMode = new ComboBox<>();
|
||||||
|
selectedMode.getItems().add("Local");
|
||||||
|
selectedMode.getItems().add("Online");
|
||||||
|
selectedMode.setValue("Local");
|
||||||
|
|
||||||
|
final HBox selectedContainer = new HBox(10, selectedGame, selectedMode);
|
||||||
|
|
||||||
|
final TextField serverIpField = new TextField();
|
||||||
|
serverIpField.setPromptText("Enter here your server ip address");
|
||||||
|
|
||||||
|
VBox box = new VBox(selectedContainer, serverIpField);
|
||||||
|
pane = new StackPane(background, box);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +1,37 @@
|
|||||||
package org.toop.app.menu;
|
package org.toop.app.menu;
|
||||||
|
|
||||||
|
import org.toop.app.App;
|
||||||
|
import org.toop.app.GameType;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.text.Font;
|
import org.toop.app.menu.game.TicTacToeMenu;
|
||||||
import org.toop.framework.asset.resources.FontAsset;
|
import org.toop.game.tictactoe.TicTacToe;
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import org.toop.framework.asset.AssetManager;
|
|
||||||
import org.toop.framework.asset.resources.CssAsset;
|
|
||||||
import org.toop.framework.asset.resources.ImageAsset;
|
|
||||||
import org.toop.local.AppContext;
|
|
||||||
|
|
||||||
public final class MainMenu extends Menu {
|
public final class MainMenu extends Menu {
|
||||||
private final Locale currentLocale = AppContext.getLocale();
|
public MainMenu() {
|
||||||
private final LocalizationAsset loc = AssetManager.get("localization.properties");
|
final Region background = createBackground();
|
||||||
|
|
||||||
public MainMenu() {
|
final Button tictactoe = createButton("Tic Tac Toe", () -> { App.activate(new TicTacToeMenu(new TicTacToe("player 1", true, "player 2", true))); });
|
||||||
|
final Button reversi = createButton("Reversi", () -> { App.activate(new GameSelectMenu(GameType.REVERSI)); });
|
||||||
|
|
||||||
final Button tictactoe = createButton(loc.getString("mainMenuSelectTicTacToe", currentLocale), () -> {});
|
final VBox gamesBox = new VBox(10, tictactoe, reversi);
|
||||||
final Button reversi = createButton(loc.getString("mainMenuSelectReversi", currentLocale), () -> {});
|
gamesBox.setAlignment(Pos.TOP_LEFT);
|
||||||
final Button sudoku = createButton(loc.getString("mainMenuSelectSudoku", currentLocale), () -> {});
|
gamesBox.setPickOnBounds(false);
|
||||||
final Button battleship = createButton(loc.getString("mainMenuSelectBattleship", currentLocale), () -> {});
|
gamesBox.setTranslateY(50);
|
||||||
final Button other = createButton(loc.getString("mainMenuSelectOther", currentLocale), () -> {});
|
gamesBox.setTranslateX(25);
|
||||||
|
|
||||||
final VBox gamesBox = new VBox(tictactoe, reversi, sudoku, battleship, other);
|
final Button credits = createButton("Credits", () -> { App.push(new CreditsMenu()); });
|
||||||
gamesBox.setAlignment(Pos.TOP_CENTER);
|
final Button options = createButton("Options", () -> { App.push(new OptionsMenu()); });
|
||||||
|
final Button quit = createButton("Quit", () -> { App.quitPopup(); });
|
||||||
|
|
||||||
final Button credits = createButton(loc.getString("mainMenuSelectCredits", currentLocale), () -> {});
|
final VBox controlBox = new VBox(10, credits, options, quit);
|
||||||
final Button options = createButton(loc.getString("mainMenuSelectOptions", currentLocale), () -> {});
|
controlBox.setAlignment(Pos.BOTTOM_LEFT);
|
||||||
final Button quit = createButton(loc.getString("mainMenuSelectQuit", currentLocale), () -> {});
|
controlBox.setPickOnBounds(false);
|
||||||
|
controlBox.setTranslateY(-50);
|
||||||
|
controlBox.setTranslateX(25);
|
||||||
|
|
||||||
final VBox creditsBox = new VBox(credits, options, quit);
|
pane = new StackPane(background, gamesBox, controlBox);
|
||||||
creditsBox.setAlignment(Pos.BOTTOM_CENTER);
|
|
||||||
|
|
||||||
VBox grid = new VBox(20, gamesBox, creditsBox);
|
|
||||||
grid.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
ImageAsset backgroundImage = (ImageAsset) AssetManager.getByName("background.jpg").getResource();
|
|
||||||
ImageView background = new ImageView(backgroundImage.getImage());
|
|
||||||
background.setPreserveRatio(false);
|
|
||||||
background.fitWidthProperty().bind(grid.widthProperty());
|
|
||||||
background.fitHeightProperty().bind(grid.heightProperty());
|
|
||||||
|
|
||||||
pane = new StackPane(background, grid);
|
|
||||||
CssAsset css = AssetManager.get("main.css");
|
|
||||||
pane.getStylesheets().add(css.getUrl());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,47 @@
|
|||||||
package org.toop.app.menu;
|
package org.toop.app.menu;
|
||||||
|
|
||||||
import org.toop.app.App;
|
import javafx.scene.Node;
|
||||||
|
|
||||||
import javafx.animation.FadeTransition;
|
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.util.Duration;
|
import javafx.scene.layout.Region;
|
||||||
import org.toop.framework.asset.Asset;
|
import javafx.scene.text.Text;
|
||||||
import org.toop.framework.asset.AssetManager;
|
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
|
||||||
import org.toop.local.AppContext;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public abstract class Menu {
|
public abstract class Menu {
|
||||||
protected Pane pane;
|
protected Pane pane;
|
||||||
public Pane getPane() { return pane; }
|
public Pane getPane() { return pane; }
|
||||||
private Locale currentLocale = AppContext.getLocale();
|
|
||||||
private LocalizationAsset loc = AssetManager.get("localization.properties");
|
|
||||||
|
|
||||||
public void fadeBackgroundImage(String imagePath, float from, float to, float milliseconds) {
|
public Region createBackground(String css) {
|
||||||
final FadeTransition fade = new FadeTransition(Duration.millis(milliseconds), App.getRoot());
|
final Region background = new Region();
|
||||||
fade.setFromValue(from);
|
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||||
fade.setToValue(to);
|
background.getStyleClass().add(css);
|
||||||
fade.play();
|
|
||||||
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Button createButton(String text, Runnable runnable) {
|
public Region createBackground() {
|
||||||
final Button button = new Button(text);
|
return createBackground("background");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Text createText(String css, String x) {
|
||||||
|
final Text text = new Text(x);
|
||||||
|
text.getStyleClass().add(css);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Text createText(String x) {
|
||||||
|
return createText("text", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Button createButton(String css, String x, Runnable runnable) {
|
||||||
|
final Button button = new Button(x);
|
||||||
button.setOnAction(_ -> runnable.run());
|
button.setOnAction(_ -> runnable.run());
|
||||||
button.getStyleClass().add("button");
|
button.getStyleClass().add(css);
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Button createButton(String x, Runnable runnable) {
|
||||||
|
return createButton("button", x, runnable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
package org.toop.app.menu;
|
package org.toop.app.menu;
|
||||||
|
|
||||||
import org.toop.framework.asset.AssetManager;
|
import org.toop.framework.asset.ResourceManager;
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
import org.toop.framework.asset.resources.LocalizationAsset;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public final class OptionsMenu extends Menu {
|
public final class OptionsMenu extends Menu {
|
||||||
private Locale currentLocale = AppContext.getLocale();
|
private Locale currentLocale = AppContext.getLocale();
|
||||||
private LocalizationAsset loc = AssetManager.get("localization.properties");
|
private LocalizationAsset loc = ResourceManager.get("localization.properties");
|
||||||
public OptionsMenu() {
|
public OptionsMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package org.toop.app.menu;
|
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import org.toop.app.App;
|
|
||||||
import org.toop.framework.asset.AssetManager;
|
|
||||||
import org.toop.framework.asset.resources.CssAsset;
|
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
|
||||||
import org.toop.local.AppContext;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public final class QuitMenu extends Menu {
|
|
||||||
private Locale currentLocale = AppContext.getLocale();
|
|
||||||
private LocalizationAsset loc = AssetManager.get("localization.properties");
|
|
||||||
public QuitMenu() {
|
|
||||||
final Region background = new Region();
|
|
||||||
background.getStyleClass().add("quit-background");
|
|
||||||
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
|
||||||
|
|
||||||
final Text sure = new Text(loc.getString("quitMenuTextSure", currentLocale));
|
|
||||||
sure.getStyleClass().add("quit-text");
|
|
||||||
|
|
||||||
final Button yes = new Button(loc.getString("quitMenuButtonYes", currentLocale));
|
|
||||||
yes.getStyleClass().add("quit-button");
|
|
||||||
yes.setOnAction(_ -> {
|
|
||||||
App.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
final Button no = new Button(loc.getString("quitMenuButtonNo", currentLocale));
|
|
||||||
no.getStyleClass().add("quit-button");
|
|
||||||
no.setOnAction(_ -> {
|
|
||||||
App.pop();
|
|
||||||
});
|
|
||||||
|
|
||||||
final HBox buttons = new HBox(10, yes, no);
|
|
||||||
buttons.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
VBox box = new VBox(43, sure, buttons);
|
|
||||||
box.setAlignment(Pos.CENTER);
|
|
||||||
box.getStyleClass().add("quit-box");
|
|
||||||
box.setMaxWidth(350);
|
|
||||||
box.setMaxHeight(200);
|
|
||||||
|
|
||||||
StackPane modalContainer = new StackPane(background, box);
|
|
||||||
StackPane.setAlignment(box, Pos.CENTER);
|
|
||||||
|
|
||||||
pane = modalContainer;
|
|
||||||
CssAsset css = AssetManager.get("quit.css");
|
|
||||||
pane.getStylesheets().add(css.getUrl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
99
app/src/main/java/org/toop/app/menu/game/GameMenu.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package org.toop.app.menu.game;
|
||||||
|
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.canvas.Canvas;
|
||||||
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import org.toop.app.App;
|
||||||
|
import org.toop.app.menu.MainMenu;
|
||||||
|
import org.toop.app.menu.Menu;
|
||||||
|
|
||||||
|
public abstract class GameMenu extends Menu {
|
||||||
|
protected final class Cell {
|
||||||
|
public float x;
|
||||||
|
public float y;
|
||||||
|
|
||||||
|
public float width;
|
||||||
|
public float height;
|
||||||
|
|
||||||
|
public Cell(float x, float y, float width, float height) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean check(float x, float y) {
|
||||||
|
return x >= this.x && y >= this.y && x <= this.x + width && y <= this.y + height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int size;
|
||||||
|
|
||||||
|
protected final Canvas canvas;
|
||||||
|
protected final GraphicsContext graphics;
|
||||||
|
|
||||||
|
protected final int rows;
|
||||||
|
protected final int columns;
|
||||||
|
|
||||||
|
protected final int gapSize;
|
||||||
|
|
||||||
|
protected final Cell[] cells;
|
||||||
|
|
||||||
|
protected GameMenu(int rows, int columns, int gapSize) {
|
||||||
|
final int size = Math.min(App.getWidth(), App.getHeight()) / 5 * 4;
|
||||||
|
|
||||||
|
final Canvas canvas = new Canvas(size, size);
|
||||||
|
|
||||||
|
final GraphicsContext graphics = canvas.getGraphicsContext2D();
|
||||||
|
|
||||||
|
this.size = size;
|
||||||
|
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.graphics = graphics;
|
||||||
|
|
||||||
|
this.rows = rows;
|
||||||
|
this.columns = columns;
|
||||||
|
|
||||||
|
this.gapSize = gapSize;
|
||||||
|
|
||||||
|
cells = new Cell[rows * columns];
|
||||||
|
|
||||||
|
final float cellWidth = ((float)size - (rows - 1) * gapSize) / rows;
|
||||||
|
final float cellHeight = ((float)size - (columns - 1) * gapSize) / rows;
|
||||||
|
|
||||||
|
for (int y = 0; y < columns; y++) {
|
||||||
|
final float startY = y * cellHeight + y * gapSize;
|
||||||
|
|
||||||
|
for (int x = 0; x < rows; x++) {
|
||||||
|
final float startX = x * cellWidth + x * gapSize;
|
||||||
|
cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Region background = createBackground();
|
||||||
|
|
||||||
|
final Text player1 = createText("player_1", "Player 1");
|
||||||
|
final Text player2 = createText("player_2", "Player 2");
|
||||||
|
|
||||||
|
final HBox playersContainer = new HBox(100, player1, player2);
|
||||||
|
playersContainer.setAlignment(Pos.TOP_CENTER);
|
||||||
|
playersContainer.setPickOnBounds(false);
|
||||||
|
playersContainer.setTranslateY(50);
|
||||||
|
|
||||||
|
final Button hint = createButton("Hint", () -> {});
|
||||||
|
final Button back = createButton("Back", () -> { App.activate(new MainMenu()); });
|
||||||
|
|
||||||
|
final VBox controlContainer = new VBox(hint, back);
|
||||||
|
StackPane.setAlignment(controlContainer, Pos.BOTTOM_LEFT);
|
||||||
|
controlContainer.setPickOnBounds(false);
|
||||||
|
|
||||||
|
pane = new StackPane(background, canvas, playersContainer, controlContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
133
app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package org.toop.app.menu.game;
|
||||||
|
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import org.toop.game.Game;
|
||||||
|
import org.toop.game.Player;
|
||||||
|
import org.toop.game.tictactoe.TicTacToe;
|
||||||
|
import org.toop.game.tictactoe.TicTacToeAI;
|
||||||
|
|
||||||
|
import javax.management.RuntimeErrorException;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
public final class TicTacToeMenu extends GameMenu {
|
||||||
|
private final TicTacToe game;
|
||||||
|
private final TicTacToeAI ai;
|
||||||
|
|
||||||
|
// private final ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||||
|
private final BlockingQueue<Game.Move> moveQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
public TicTacToeMenu(TicTacToe game) {
|
||||||
|
super(3, 3, 10);
|
||||||
|
|
||||||
|
graphics.setFill(Color.CYAN);
|
||||||
|
|
||||||
|
for (int x = 1; x < rows; x++) {
|
||||||
|
graphics.fillRect(cells[x].x - gapSize, 0, gapSize, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 1; y < columns; y++) {
|
||||||
|
graphics.fillRect(0, cells[y * rows].y - gapSize, size, gapSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.game = game;
|
||||||
|
ai = new TicTacToeAI();
|
||||||
|
|
||||||
|
canvas.setOnMouseClicked(event -> {
|
||||||
|
for (int i = 0; i < cells.length; i++) {
|
||||||
|
if (cells[i].check((float) event.getX(), (float) event.getY())) {
|
||||||
|
final Game.Move move = new Game.Move(i, game.getCurrentPlayer().values()[0]);
|
||||||
|
play(move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new Thread(this::gameThread).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void play(Game.Move move) {
|
||||||
|
final Game.Move[] legalMoves = game.getLegalMoves();
|
||||||
|
|
||||||
|
boolean isLegal = false;
|
||||||
|
|
||||||
|
for (final Game.Move legalMove : legalMoves) {
|
||||||
|
if (legalMove.position() == move.position() && legalMove.value() == move.value()) {
|
||||||
|
isLegal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLegal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try { moveQueue.put(move); }
|
||||||
|
catch (InterruptedException _) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void placeX(int cell) {
|
||||||
|
graphics.setStroke(Color.ORANGERED);
|
||||||
|
graphics.setLineWidth(gapSize);
|
||||||
|
|
||||||
|
final float x = cells[cell].x + gapSize;
|
||||||
|
final float y = cells[cell].y + gapSize;
|
||||||
|
|
||||||
|
final float width = cells[cell].width - gapSize * 2;
|
||||||
|
final float height = cells[cell].height - gapSize * 2;
|
||||||
|
|
||||||
|
graphics.strokeLine(x, y, x + width, y + height);
|
||||||
|
graphics.strokeLine(x + width, y, x, y + height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void placeO(int cell) {
|
||||||
|
graphics.setStroke(Color.DEEPSKYBLUE);
|
||||||
|
graphics.setLineWidth(gapSize);
|
||||||
|
|
||||||
|
final float x = cells[cell].x + gapSize;
|
||||||
|
final float y = cells[cell].y + gapSize;
|
||||||
|
|
||||||
|
final float width = cells[cell].width - gapSize * 2;
|
||||||
|
final float height = cells[cell].height - gapSize * 2;
|
||||||
|
|
||||||
|
graphics.strokeOval(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gameThread() {
|
||||||
|
boolean running = true;
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
final Player currentPlayer = game.getCurrentPlayer();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Game.Move move;
|
||||||
|
|
||||||
|
if (!currentPlayer.isAI()) {
|
||||||
|
try { move = moveQueue.take(); }
|
||||||
|
catch (InterruptedException _) { return; }
|
||||||
|
} else {
|
||||||
|
move = ai.findBestMove(game, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert move != null;
|
||||||
|
final Game.State state = game.play(move);
|
||||||
|
|
||||||
|
if (move.value() == 'X') {
|
||||||
|
placeX(move.position());
|
||||||
|
} else {
|
||||||
|
placeO(move.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case NORMAL: break;
|
||||||
|
|
||||||
|
case DRAW:
|
||||||
|
case LOSE:
|
||||||
|
case WIN:
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 956 B |
BIN
app/src/main/resources/assets/image/lowpoly.png
Normal file
|
After Width: | Height: | Size: 357 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 15 MiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
37
app/src/main/resources/assets/style/app.css
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
.background {
|
||||||
|
-fx-background-color: linear-gradient(to bottom right, #21a7b2, #8f32b9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
-fx-fill: white;
|
||||||
|
|
||||||
|
-fx-font-family: "Segoe UI", sans-serif;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button, .text-field, .combo-box, .combo-box-popup .list-cell {
|
||||||
|
-fx-padding: 10 20;
|
||||||
|
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combo-box-popup .list-view {
|
||||||
|
-fx-background-color: #1d1d1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-field {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover, .text-field:hover, .combo-box:hover, .combo-box-popup .list-cell:hover {
|
||||||
|
-fx-cursor: hand;
|
||||||
|
|
||||||
|
-fx-effect: dropshadow(gaussian, #00ffff7f, 10, 0.5, 0, 0);
|
||||||
|
-fx-border-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-field:focused, .combo-box:focused {
|
||||||
|
-fx-border-color: white;
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
.main-button {
|
|
||||||
-fx-background-color: transparent;
|
|
||||||
-fx-background-size: cover;
|
|
||||||
-fx-background-position: center;
|
|
||||||
-fx-pref-width: 250px;
|
|
||||||
-fx-pref-height: 180px;
|
|
||||||
-fx-border-radius: 15;
|
|
||||||
-fx-background-radius: 15;
|
|
||||||
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 15, 0.4, 0, 4);
|
|
||||||
-fx-cursor: hand;
|
|
||||||
-fx-padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-label {
|
|
||||||
-fx-background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
-fx-font-size: 20px;
|
|
||||||
-fx-font-weight: bold;
|
|
||||||
-fx-text-fill: white;
|
|
||||||
-fx-padding: 10px;
|
|
||||||
-fx-alignment: top-center;
|
|
||||||
-fx-background-radius: 15 15 0 0;
|
|
||||||
-fx-opacity: 0;
|
|
||||||
-fx-transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-button:hover {
|
|
||||||
-fx-effect: dropshadow(gaussian, #00ffff, 15, 0.5, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-button:hover .card-label {
|
|
||||||
-fx-opacity: 1;
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,8 @@
|
|||||||
.quit-background {
|
.quit_background {
|
||||||
-fx-background-color: rgba(0, 0, 0, 0.6);
|
-fx-background-color: rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quit-box {
|
.quit_box {
|
||||||
-fx-background-color: rgba(30, 30, 30, 0.95);
|
-fx-background-color: rgba(30, 30, 30, 0.5);
|
||||||
-fx-background-radius: 15;
|
-fx-background-radius: 15;
|
||||||
-fx-padding: 30;
|
|
||||||
-fx-effect: dropshadow(gaussian, black, 20, 0.6, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.quit-text {
|
|
||||||
-fx-fill: white;
|
|
||||||
-fx-font-size: 28px;
|
|
||||||
-fx-font-weight: 600;
|
|
||||||
-fx-font-family: "Groovy Maniac Demo", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quit-button {
|
|
||||||
-fx-font-size: 16px;
|
|
||||||
-fx-text-fill: white;
|
|
||||||
-fx-background-color: transparent;
|
|
||||||
-fx-border-color: white;
|
|
||||||
-fx-border-radius: 5;
|
|
||||||
-fx-padding: 8 20;
|
|
||||||
-fx-cursor: hand;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quit-button:hover {
|
|
||||||
-fx-text-fill: #00ffff;
|
|
||||||
-fx-border-color: #00ffff;
|
|
||||||
-fx-effect: dropshadow(gaussian, #00ffff, 8, 0.5, 0, 0);
|
|
||||||
}
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
.root {
|
|
||||||
-fx-background-color: #2d2d2d;
|
|
||||||
|
|
||||||
-fx-font-size: 28px;
|
|
||||||
-fx-font-weight: 600;
|
|
||||||
-fx-font-family: "Segoe UI", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
-fx-background-color: transparent;
|
|
||||||
-fx-text-fill: white;
|
|
||||||
-fx-border-color: transparent;
|
|
||||||
-fx-padding: 10 20;
|
|
||||||
-fx-cursor: hand;
|
|
||||||
-fx-effect: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
-fx-effect: dropshadow(gaussian, #00ffff, 10, 0.3, 0, 0);
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,9 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.toop.framework.asset.events.AssetLoaderEvents;
|
import org.toop.framework.asset.events.AssetLoaderEvents;
|
||||||
import org.toop.framework.asset.resources.*;
|
import org.toop.framework.asset.resources.*;
|
||||||
|
import org.toop.framework.asset.types.BundledResource;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.PreloadResource;
|
||||||
import org.toop.framework.eventbus.EventFlow;
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
@@ -17,7 +20,7 @@ import java.util.function.Function;
|
|||||||
/**
|
/**
|
||||||
* Responsible for loading assets from a file system directory into memory.
|
* Responsible for loading assets from a file system directory into memory.
|
||||||
* <p>
|
* <p>
|
||||||
* The {@code AssetLoader} scans a root folder recursively, identifies files,
|
* The {@code ResourceLoader} scans a root folder recursively, identifies files,
|
||||||
* and maps them to registered resource types based on file extensions and
|
* and maps them to registered resource types based on file extensions and
|
||||||
* {@link FileExtension} annotations.
|
* {@link FileExtension} annotations.
|
||||||
* It supports multiple resource types including {@link PreloadResource} (automatically loaded)
|
* It supports multiple resource types including {@link PreloadResource} (automatically loaded)
|
||||||
@@ -25,7 +28,7 @@ import java.util.function.Function;
|
|||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>Assets are stored in a static, thread-safe list and can be retrieved
|
* <p>Assets are stored in a static, thread-safe list and can be retrieved
|
||||||
* through {@link AssetManager}.</p>
|
* through {@link ResourceManager}.</p>
|
||||||
*
|
*
|
||||||
* <p>Features:</p>
|
* <p>Features:</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -38,24 +41,24 @@ import java.util.function.Function;
|
|||||||
*
|
*
|
||||||
* <p>Usage example:</p>
|
* <p>Usage example:</p>
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* AssetLoader loader = new AssetLoader("assets");
|
* ResourceLoader loader = new ResourceLoader("assets");
|
||||||
* double progress = loader.getProgress();
|
* double progress = loader.getProgress();
|
||||||
* List<Asset<? extends BaseResource>> loadedAssets = loader.getAssets();
|
* List<Asset<? extends BaseResource>> loadedAssets = loader.getAssets();
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
public class AssetLoader {
|
public class ResourceLoader {
|
||||||
private static final Logger logger = LogManager.getLogger(AssetLoader.class);
|
private static final Logger logger = LogManager.getLogger(ResourceLoader.class);
|
||||||
private static final List<Asset<? extends BaseResource>> assets = new CopyOnWriteArrayList<>();
|
private static final List<ResourceMeta<? extends BaseResource>> assets = new CopyOnWriteArrayList<>();
|
||||||
private final Map<String, Function<File, ? extends BaseResource>> registry = new ConcurrentHashMap<>();
|
private final Map<String, Function<File, ? extends BaseResource>> registry = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final AtomicInteger loadedCount = new AtomicInteger(0);
|
private final AtomicInteger loadedCount = new AtomicInteger(0);
|
||||||
private int totalCount = 0;
|
private int totalCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an AssetLoader and loads assets from the given root folder.
|
* Constructs an ResourceLoader and loads assets from the given root folder.
|
||||||
* @param rootFolder the folder containing asset files
|
* @param rootFolder the folder containing asset files
|
||||||
*/
|
*/
|
||||||
public AssetLoader(File rootFolder) {
|
public ResourceLoader(File rootFolder) {
|
||||||
autoRegisterResources();
|
autoRegisterResources();
|
||||||
List<File> foundFiles = new ArrayList<>();
|
List<File> foundFiles = new ArrayList<>();
|
||||||
fileSearcher(rootFolder, foundFiles);
|
fileSearcher(rootFolder, foundFiles);
|
||||||
@@ -80,10 +83,10 @@ public class AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an AssetLoader from a folder path.
|
* Constructs an ResourceLoader from a folder path.
|
||||||
* @param rootFolder the folder path containing assets
|
* @param rootFolder the folder path containing assets
|
||||||
*/
|
*/
|
||||||
public AssetLoader(String rootFolder) {
|
public ResourceLoader(String rootFolder) {
|
||||||
this(new File(rootFolder));
|
this(new File(rootFolder));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +118,7 @@ public class AssetLoader {
|
|||||||
* Returns a snapshot list of all assets loaded by this loader.
|
* Returns a snapshot list of all assets loaded by this loader.
|
||||||
* @return list of loaded assets
|
* @return list of loaded assets
|
||||||
*/
|
*/
|
||||||
public List<Asset<? extends BaseResource>> getAssets() {
|
public List<ResourceMeta<? extends BaseResource>> getAssets() {
|
||||||
return new ArrayList<>(assets);
|
return new ArrayList<>(assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +182,7 @@ public class AssetLoader {
|
|||||||
boolean alreadyAdded = assets.stream()
|
boolean alreadyAdded = assets.stream()
|
||||||
.anyMatch(a -> a.getResource() == finalResource);
|
.anyMatch(a -> a.getResource() == finalResource);
|
||||||
if (!alreadyAdded) {
|
if (!alreadyAdded) {
|
||||||
assets.add(new Asset<>(file.getName(), resource));
|
assets.add(new ResourceMeta<>(file.getName(), resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Loaded {} from {}", resource.getClass().getSimpleName(), file.getAbsolutePath());
|
logger.info("Loaded {} from {}", resource.getClass().getSimpleName(), file.getAbsolutePath());
|
||||||
@@ -8,9 +8,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
/**
|
/**
|
||||||
* Centralized manager for all loaded assets in the application.
|
* Centralized manager for all loaded assets in the application.
|
||||||
* <p>
|
* <p>
|
||||||
* {@code AssetManager} maintains a thread-safe registry of {@link Asset} objects
|
* {@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects
|
||||||
* and provides utility methods to retrieve assets by name, ID, or type.
|
* and provides utility methods to retrieve assets by name, ID, or type.
|
||||||
* It works together with {@link AssetLoader} to register assets automatically
|
* It works together with {@link ResourceLoader} to register assets automatically
|
||||||
* when they are loaded from the file system.
|
* when they are loaded from the file system.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
@@ -25,47 +25,47 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* <p>Example usage:</p>
|
* <p>Example usage:</p>
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* // Load assets from a loader
|
* // Load assets from a loader
|
||||||
* AssetLoader loader = new AssetLoader(new File("RootFolder"));
|
* ResourceLoader loader = new ResourceLoader(new File("RootFolder"));
|
||||||
* AssetManager.loadAssets(loader);
|
* ResourceManager.loadAssets(loader);
|
||||||
*
|
*
|
||||||
* // Retrieve a single resource
|
* // Retrieve a single resource
|
||||||
* ImageAsset background = AssetManager.get("background.jpg");
|
* ImageAsset background = ResourceManager.get("background.jpg");
|
||||||
*
|
*
|
||||||
* // Retrieve all fonts
|
* // Retrieve all fonts
|
||||||
* List<Asset<FontAsset>> fonts = AssetManager.getAllOfType(FontAsset.class);
|
* List<Asset<FontAsset>> fonts = ResourceManager.getAllOfType(FontAsset.class);
|
||||||
*
|
*
|
||||||
* // Retrieve by asset name or optional lookup
|
* // Retrieve by asset name or optional lookup
|
||||||
* Optional<Asset<? extends BaseResource>> maybeAsset = AssetManager.findByName("menu.css");
|
* Optional<Asset<? extends BaseResource>> maybeAsset = ResourceManager.findByName("menu.css");
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>Notes:</p>
|
* <p>Notes:</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>All retrieval methods are static and thread-safe.</li>
|
* <li>All retrieval methods are static and thread-safe.</li>
|
||||||
* <li>The {@link #get(String)} method may require casting if the asset type is not known at compile time.</li>
|
* <li>The {@link #get(String)} method may require casting if the asset type is not known at compile time.</li>
|
||||||
* <li>Assets should be loaded via {@link AssetLoader} before retrieval.</li>
|
* <li>Assets should be loaded via {@link ResourceLoader} before retrieval.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class AssetManager {
|
public class ResourceManager {
|
||||||
private static final AssetManager INSTANCE = new AssetManager();
|
private static final ResourceManager INSTANCE = new ResourceManager();
|
||||||
private static final Map<String, Asset<? extends BaseResource>> assets = new ConcurrentHashMap<>();
|
private static final Map<String, ResourceMeta<? extends BaseResource>> assets = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private AssetManager() {}
|
private ResourceManager() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the singleton instance of {@code AssetManager}.
|
* Returns the singleton instance of {@code ResourceManager}.
|
||||||
*
|
*
|
||||||
* @return the shared instance
|
* @return the shared instance
|
||||||
*/
|
*/
|
||||||
public static AssetManager getInstance() {
|
public static ResourceManager getInstance() {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all assets from a given {@link AssetLoader} into the manager.
|
* Loads all assets from a given {@link ResourceLoader} into the manager.
|
||||||
*
|
*
|
||||||
* @param loader the loader that has already loaded assets
|
* @param loader the loader that has already loaded assets
|
||||||
*/
|
*/
|
||||||
public synchronized static void loadAssets(AssetLoader loader) {
|
public synchronized static void loadAssets(ResourceLoader loader) {
|
||||||
for (var asset : loader.getAssets()) {
|
for (var asset : loader.getAssets()) {
|
||||||
assets.put(asset.getName(), asset);
|
assets.put(asset.getName(), asset);
|
||||||
}
|
}
|
||||||
@@ -80,11 +80,23 @@ public class AssetManager {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends BaseResource> T get(String name) {
|
public static <T extends BaseResource> T get(String name) {
|
||||||
Asset<T> asset = (Asset<T>) assets.get(name);
|
ResourceMeta<T> asset = (ResourceMeta<T>) assets.get(name);
|
||||||
if (asset == null) return null;
|
if (asset == null) return null;
|
||||||
return asset.getResource();
|
return asset.getResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the resource of a given name, cast to the expected type.
|
||||||
|
*
|
||||||
|
* @param name the asset name
|
||||||
|
* @param <T> the expected resource type
|
||||||
|
* @return the resource, or null if not found
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends BaseResource> T get(Class<T> type, String name) {
|
||||||
|
return type.cast(assets.get(name).getResource());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all assets of a specific resource type.
|
* Retrieve all assets of a specific resource type.
|
||||||
*
|
*
|
||||||
@@ -92,12 +104,12 @@ public class AssetManager {
|
|||||||
* @param <T> the resource type
|
* @param <T> the resource type
|
||||||
* @return a list of assets matching the type
|
* @return a list of assets matching the type
|
||||||
*/
|
*/
|
||||||
public static <T extends BaseResource> ArrayList<Asset<T>> getAllOfType(Class<T> type) {
|
public static <T extends BaseResource> ArrayList<ResourceMeta<T>> getAllOfType(Class<T> type) {
|
||||||
ArrayList<Asset<T>> list = new ArrayList<>();
|
ArrayList<ResourceMeta<T>> list = new ArrayList<>();
|
||||||
for (Asset<? extends BaseResource> asset : assets.values()) {
|
for (ResourceMeta<? extends BaseResource> asset : assets.values()) {
|
||||||
if (type.isInstance(asset.getResource())) {
|
if (type.isInstance(asset.getResource())) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Asset<T> typed = (Asset<T>) asset;
|
ResourceMeta<T> typed = (ResourceMeta<T>) asset;
|
||||||
list.add(typed);
|
list.add(typed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,8 +122,8 @@ public class AssetManager {
|
|||||||
* @param id the asset ID
|
* @param id the asset ID
|
||||||
* @return the asset, or null if not found
|
* @return the asset, or null if not found
|
||||||
*/
|
*/
|
||||||
public static Asset<? extends BaseResource> getById(String id) {
|
public static ResourceMeta<? extends BaseResource> getById(String id) {
|
||||||
for (Asset<? extends BaseResource> asset : assets.values()) {
|
for (ResourceMeta<? extends BaseResource> asset : assets.values()) {
|
||||||
if (asset.getId().toString().equals(id)) {
|
if (asset.getId().toString().equals(id)) {
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
@@ -125,7 +137,7 @@ public class AssetManager {
|
|||||||
* @param name the asset name
|
* @param name the asset name
|
||||||
* @return the asset, or null if not found
|
* @return the asset, or null if not found
|
||||||
*/
|
*/
|
||||||
public static Asset<? extends BaseResource> getByName(String name) {
|
public static ResourceMeta<? extends BaseResource> getByName(String name) {
|
||||||
return assets.get(name);
|
return assets.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +147,7 @@ public class AssetManager {
|
|||||||
* @param name the asset name
|
* @param name the asset name
|
||||||
* @return an Optional containing the asset if found
|
* @return an Optional containing the asset if found
|
||||||
*/
|
*/
|
||||||
public static Optional<Asset<? extends BaseResource>> findByName(String name) {
|
public static Optional<ResourceMeta<? extends BaseResource>> findByName(String name) {
|
||||||
return Optional.ofNullable(assets.get(name));
|
return Optional.ofNullable(assets.get(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +156,7 @@ public class AssetManager {
|
|||||||
*
|
*
|
||||||
* @param asset the asset to add
|
* @param asset the asset to add
|
||||||
*/
|
*/
|
||||||
public static void addAsset(Asset<? extends BaseResource> asset) {
|
public static void addAsset(ResourceMeta<? extends BaseResource> asset) {
|
||||||
assets.put(asset.getName(), asset);
|
assets.put(asset.getName(), asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,12 @@ package org.toop.framework.asset;
|
|||||||
import org.toop.framework.SnowflakeGenerator;
|
import org.toop.framework.SnowflakeGenerator;
|
||||||
import org.toop.framework.asset.resources.BaseResource;
|
import org.toop.framework.asset.resources.BaseResource;
|
||||||
|
|
||||||
public class Asset <T extends BaseResource> {
|
public class ResourceMeta<T extends BaseResource> {
|
||||||
private final Long id;
|
private final Long id;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final T resource;
|
private final T resource;
|
||||||
|
|
||||||
public Asset(String name, T resource) {
|
public ResourceMeta(String name, T resource) {
|
||||||
this.id = new SnowflakeGenerator().nextId();
|
this.id = new SnowflakeGenerator().nextId();
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@FileExtension({"css"})
|
@FileExtension({"css"})
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.PreloadResource;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.types.BundledResource;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
import javafx.scene.media.Media;
|
import javafx.scene.media.Media;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
import javafx.scene.media.Media;
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
import javax.sound.sampled.*;
|
import javax.sound.sampled.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
@FileExtension({"wav"})
|
@FileExtension({"wav"})
|
||||||
public class SoundEffectAsset extends BaseResource implements LoadableResource {
|
public class SoundEffectAsset extends BaseResource implements LoadableResource {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.types;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -6,7 +8,7 @@ import java.io.File;
|
|||||||
* Represents a resource that can be composed of multiple files, or "bundled" together
|
* Represents a resource that can be composed of multiple files, or "bundled" together
|
||||||
* under a common base name.
|
* under a common base name.
|
||||||
*
|
*
|
||||||
* <p>Implementing classes allow an {@link org.toop.framework.asset.AssetLoader}
|
* <p>Implementing classes allow an {@link ResourceLoader}
|
||||||
* to automatically merge multiple related files into a single resource instance.</p>
|
* to automatically merge multiple related files into a single resource instance.</p>
|
||||||
*
|
*
|
||||||
* <p>Typical use cases include:</p>
|
* <p>Typical use cases include:</p>
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.types;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
import org.toop.framework.asset.resources.BaseResource;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@@ -9,7 +12,7 @@ import java.lang.annotation.ElementType;
|
|||||||
* Annotation to declare which file extensions a {@link BaseResource} subclass
|
* Annotation to declare which file extensions a {@link BaseResource} subclass
|
||||||
* can handle.
|
* can handle.
|
||||||
*
|
*
|
||||||
* <p>This annotation is processed by the {@link org.toop.framework.asset.AssetLoader}
|
* <p>This annotation is processed by the {@link ResourceLoader}
|
||||||
* to automatically register resource types for specific file extensions.
|
* to automatically register resource types for specific file extensions.
|
||||||
* Each extension listed will be mapped to the annotated resource class,
|
* Each extension listed will be mapped to the annotated resource class,
|
||||||
* allowing the loader to instantiate the correct type when scanning files.</p>
|
* allowing the loader to instantiate the correct type when scanning files.</p>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.types;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a resource that can be explicitly loaded and unloaded.
|
* Represents a resource that can be explicitly loaded and unloaded.
|
||||||
@@ -40,7 +42,7 @@ package org.toop.framework.asset.resources;
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>This interface is commonly used with {@link PreloadResource} to allow automatic
|
* <p>This interface is commonly used with {@link PreloadResource} to allow automatic
|
||||||
* loading by an {@link org.toop.framework.asset.AssetLoader} if desired.</p>
|
* loading by an {@link ResourceLoader} if desired.</p>
|
||||||
*/
|
*/
|
||||||
public interface LoadableResource {
|
public interface LoadableResource {
|
||||||
/**
|
/**
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.types;
|
||||||
|
|
||||||
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marker interface for resources that should be **automatically loaded** by the {@link org.toop.framework.asset.AssetLoader}.
|
* Marker interface for resources that should be **automatically loaded** by the {@link ResourceLoader}.
|
||||||
*
|
*
|
||||||
* <p>Extends {@link LoadableResource}, so any implementing class must provide the standard
|
* <p>Extends {@link LoadableResource}, so any implementing class must provide the standard
|
||||||
* {@link LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the
|
* {@link LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the
|
||||||
* {@link LoadableResource#isLoaded()} check.</p>
|
* {@link LoadableResource#isLoaded()} check.</p>
|
||||||
*
|
*
|
||||||
* <p>When a resource implements {@code PreloadResource}, the {@code AssetLoader} will invoke
|
* <p>When a resource implements {@code PreloadResource}, the {@code ResourceLoader} will invoke
|
||||||
* {@link LoadableResource#load()} automatically after the resource is discovered and instantiated,
|
* {@link LoadableResource#load()} automatically after the resource is discovered and instantiated,
|
||||||
* without requiring manual loading by the user.</p>
|
* without requiring manual loading by the user.</p>
|
||||||
*
|
*
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package org.toop.framework.audio;
|
package org.toop.framework.audio;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.media.MediaPlayer;
|
|
||||||
import org.toop.framework.SnowflakeGenerator;
|
import org.toop.framework.SnowflakeGenerator;
|
||||||
import org.toop.framework.asset.Asset;
|
import org.toop.framework.asset.ResourceManager;
|
||||||
import org.toop.framework.asset.AssetManager;
|
import org.toop.framework.asset.ResourceMeta;
|
||||||
import org.toop.framework.asset.resources.MusicAsset;
|
import org.toop.framework.asset.resources.MusicAsset;
|
||||||
import org.toop.framework.asset.resources.SoundEffectAsset;
|
import org.toop.framework.asset.resources.SoundEffectAsset;
|
||||||
import org.toop.framework.audio.events.AudioEvents;
|
import org.toop.framework.audio.events.AudioEvents;
|
||||||
import org.toop.framework.eventbus.EventFlow;
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
|
|
||||||
|
import javafx.scene.media.MediaPlayer;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import javax.sound.sampled.*;
|
import javax.sound.sampled.*;
|
||||||
@@ -25,7 +25,7 @@ public class SoundManager {
|
|||||||
|
|
||||||
public SoundManager() {
|
public SoundManager() {
|
||||||
// Get all Audio Resources and add them to a list.
|
// Get all Audio Resources and add them to a list.
|
||||||
for (Asset<SoundEffectAsset> asset : AssetManager.getAllOfType(SoundEffectAsset.class)) {
|
for (ResourceMeta<SoundEffectAsset> asset : ResourceManager.getAllOfType(SoundEffectAsset.class)) {
|
||||||
try {
|
try {
|
||||||
this.addAudioResource(asset);
|
this.addAudioResource(asset);
|
||||||
} catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
|
} catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
|
||||||
@@ -58,7 +58,7 @@ public class SoundManager {
|
|||||||
this.stopSound(event.clipId());
|
this.stopSound(event.clipId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAudioResource(Asset<SoundEffectAsset> audioAsset)
|
private void addAudioResource(ResourceMeta<SoundEffectAsset> audioAsset)
|
||||||
throws IOException, UnsupportedAudioFileException, LineUnavailableException {
|
throws IOException, UnsupportedAudioFileException, LineUnavailableException {
|
||||||
|
|
||||||
this.audioResources.put(audioAsset.getName(), audioAsset.getResource());
|
this.audioResources.put(audioAsset.getName(), audioAsset.getResource());
|
||||||
@@ -74,15 +74,15 @@ public class SoundManager {
|
|||||||
|
|
||||||
private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
|
private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
|
||||||
backgroundMusicQueue.clear();
|
backgroundMusicQueue.clear();
|
||||||
Platform.runLater(() -> {
|
List<MusicAsset> shuffledArray = new ArrayList<>(ResourceManager.getAllOfType(MusicAsset.class)
|
||||||
backgroundMusicQueue.addAll(
|
.stream()
|
||||||
AssetManager.getAllOfType(MusicAsset.class).stream()
|
.map(ResourceMeta::getResource)
|
||||||
.map(Asset::getResource)
|
.toList());
|
||||||
.toList()
|
Collections.shuffle(shuffledArray);
|
||||||
);
|
backgroundMusicQueue.addAll(
|
||||||
backgroundMusicPlayer();
|
shuffledArray
|
||||||
});
|
);
|
||||||
|
backgroundMusicPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBackgroundMusic(MusicAsset musicAsset) {
|
private void addBackgroundMusic(MusicAsset musicAsset) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public abstract class Game {
|
public abstract class Game {
|
||||||
public enum State {
|
public enum State {
|
||||||
NORMAL, LOSE, DRAW, WIN,
|
NORMAL, DRAW, LOSE, WIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Move(int position, char value) {}
|
public record Move(int position, char value) {}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package org.toop.game;
|
package org.toop.game;
|
||||||
|
|
||||||
public record Player(String name, char... values) {}
|
public record Player(String name, boolean isAI, char... values) {}
|
||||||
@@ -8,8 +8,8 @@ import java.util.ArrayList;
|
|||||||
public final class TicTacToe extends Game {
|
public final class TicTacToe extends Game {
|
||||||
private int movesLeft;
|
private int movesLeft;
|
||||||
|
|
||||||
public TicTacToe(String player1, String player2) {
|
public TicTacToe(String player1, boolean isPlayer1AI, String player2, boolean isPlayer2AI) {
|
||||||
super(3, 3, new Player(player1, 'X'), new Player(player2, 'O'));
|
super(3, 3, new Player(player1, isPlayer1AI, 'X'), new Player(player2, isPlayer2AI, 'O'));
|
||||||
movesLeft = board.length;
|
movesLeft = board.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ public final class TicTacToe extends Game {
|
|||||||
return State.DRAW;
|
return State.DRAW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return State.NORMAL;
|
return State.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
package org.toop.game;
|
//package org.toop.game;
|
||||||
|
//
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
//import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
//import org.junit.jupiter.api.Test;
|
||||||
|
//
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
//
|
||||||
class PlayerTest {
|
//class PlayerTest {
|
||||||
private Player playerA;
|
// private Player playerA;
|
||||||
private Player playerB;
|
// private Player playerB;
|
||||||
private Player playerC;
|
// private Player playerC;
|
||||||
|
//
|
||||||
@BeforeEach
|
// @BeforeEach
|
||||||
void setup() {
|
// void setup() {
|
||||||
playerA = new Player("test A", 'x', 'Z', 'i');
|
// playerA = new Player("test A", 'x', 'Z', 'i');
|
||||||
playerB = new Player("test B", 'O', (char)12, (char)-34, 's');
|
// playerB = new Player("test B", 'O', (char)12, (char)-34, 's');
|
||||||
playerC = new Player("test C", (char)9, '9', (char)-9, '0', 'X', 'O');
|
// playerC = new Player("test C", (char)9, '9', (char)-9, '0', 'X', 'O');
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
void testNameGetter_returnsTrueForValidName() {
|
// void testNameGetter_returnsTrueForValidName() {
|
||||||
assertEquals("test A", playerA.name());
|
// assertEquals("test A", playerA.name());
|
||||||
assertEquals("test B", playerB.name());
|
// assertEquals("test B", playerB.name());
|
||||||
assertEquals("test C", playerC.name());
|
// assertEquals("test C", playerC.name());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
void testValuesGetter_returnsTrueForValidValues() {
|
// void testValuesGetter_returnsTrueForValidValues() {
|
||||||
final char[] valuesA = playerA.values();
|
// final char[] valuesA = playerA.values();
|
||||||
assertEquals('x', valuesA[0]);
|
// assertEquals('x', valuesA[0]);
|
||||||
assertEquals('Z', valuesA[1]);
|
// assertEquals('Z', valuesA[1]);
|
||||||
assertEquals('i', valuesA[2]);
|
// assertEquals('i', valuesA[2]);
|
||||||
|
//
|
||||||
final char[] valuesB = playerB.values();
|
// final char[] valuesB = playerB.values();
|
||||||
assertEquals('O', valuesB[0]);
|
// assertEquals('O', valuesB[0]);
|
||||||
assertEquals(12, valuesB[1]);
|
// assertEquals(12, valuesB[1]);
|
||||||
assertEquals((char)-34, valuesB[2]);
|
// assertEquals((char)-34, valuesB[2]);
|
||||||
assertEquals('s', valuesB[3]);
|
// assertEquals('s', valuesB[3]);
|
||||||
|
//
|
||||||
final char[] valuesC = playerC.values();
|
// final char[] valuesC = playerC.values();
|
||||||
assertEquals((char)9, valuesC[0]);
|
// assertEquals((char)9, valuesC[0]);
|
||||||
assertEquals('9', valuesC[1]);
|
// assertEquals('9', valuesC[1]);
|
||||||
assertEquals((char)-9, valuesC[2]);
|
// assertEquals((char)-9, valuesC[2]);
|
||||||
assertEquals('0', valuesC[3]);
|
// assertEquals('0', valuesC[3]);
|
||||||
assertEquals('X', valuesC[4]);
|
// assertEquals('X', valuesC[4]);
|
||||||
assertEquals('O', valuesC[5]);
|
// assertEquals('O', valuesC[5]);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
@@ -1,83 +1,83 @@
|
|||||||
package org.toop.game.tictactoe;
|
//package org.toop.game.tictactoe;
|
||||||
|
//
|
||||||
import org.toop.game.Game;
|
//import org.toop.game.Game;
|
||||||
|
//
|
||||||
import java.util.Set;
|
//import java.util.Set;
|
||||||
|
//
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
//import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
//import org.junit.jupiter.api.Test;
|
||||||
|
//
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
//import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
//
|
||||||
class TicTacToeAITest {
|
//class TicTacToeAITest {
|
||||||
private TicTacToe game;
|
// private TicTacToe game;
|
||||||
private TicTacToeAI ai;
|
// private TicTacToeAI ai;
|
||||||
|
//
|
||||||
@BeforeEach
|
// @BeforeEach
|
||||||
void setup() {
|
// void setup() {
|
||||||
game = new TicTacToe("AI", "AI");
|
// game = new TicTacToe("AI", "AI");
|
||||||
ai = new TicTacToeAI();
|
// ai = new TicTacToeAI();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
void testBestMove_returnWinningMoveWithDepth1() {
|
// void testBestMove_returnWinningMoveWithDepth1() {
|
||||||
// X X -
|
// // X X -
|
||||||
// O O -
|
// // O O -
|
||||||
// - - -
|
// // - - -
|
||||||
game.play(new Game.Move(0, 'X'));
|
// game.play(new Game.Move(0, 'X'));
|
||||||
game.play(new Game.Move(3, 'O'));
|
// game.play(new Game.Move(3, 'O'));
|
||||||
game.play(new Game.Move(1, 'X'));
|
// game.play(new Game.Move(1, 'X'));
|
||||||
game.play(new Game.Move(4, 'O'));
|
// game.play(new Game.Move(4, 'O'));
|
||||||
|
//
|
||||||
final Game.Move move = ai.findBestMove(game, 1);
|
// final Game.Move move = ai.findBestMove(game, 1);
|
||||||
|
//
|
||||||
assertNotNull(move);
|
// assertNotNull(move);
|
||||||
assertEquals('X', move.value());
|
// assertEquals('X', move.value());
|
||||||
assertEquals(2, move.position());
|
// assertEquals(2, move.position());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
void testBestMove_blockOpponentWinDepth1() {
|
// void testBestMove_blockOpponentWinDepth1() {
|
||||||
// - - -
|
// // - - -
|
||||||
// O - -
|
// // O - -
|
||||||
// X X -
|
// // X X -
|
||||||
game.play(new Game.Move(6, 'X'));
|
// game.play(new Game.Move(6, 'X'));
|
||||||
game.play(new Game.Move(3, 'O'));
|
// game.play(new Game.Move(3, 'O'));
|
||||||
game.play(new Game.Move(7, 'X'));
|
// game.play(new Game.Move(7, 'X'));
|
||||||
|
//
|
||||||
final Game.Move move = ai.findBestMove(game, 1);
|
// final Game.Move move = ai.findBestMove(game, 1);
|
||||||
|
//
|
||||||
assertNotNull(move);
|
// assertNotNull(move);
|
||||||
assertEquals('O', move.value());
|
// assertEquals('O', move.value());
|
||||||
assertEquals(8, move.position());
|
// assertEquals(8, move.position());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
void testBestMove_preferCornerOnEmpty() {
|
// void testBestMove_preferCornerOnEmpty() {
|
||||||
final Game.Move move = ai.findBestMove(game, 0);
|
// final Game.Move move = ai.findBestMove(game, 0);
|
||||||
|
//
|
||||||
assertNotNull(move);
|
// assertNotNull(move);
|
||||||
assertEquals('X', move.value());
|
// assertEquals('X', move.value());
|
||||||
assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
|
// assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
void testBestMove_findBestMoveDraw() {
|
// void testBestMove_findBestMoveDraw() {
|
||||||
// O X -
|
// // O X -
|
||||||
// - O X
|
// // - O X
|
||||||
// X O X
|
// // X O X
|
||||||
game.play(new Game.Move(1, 'X'));
|
// game.play(new Game.Move(1, 'X'));
|
||||||
game.play(new Game.Move(0, 'O'));
|
// game.play(new Game.Move(0, 'O'));
|
||||||
game.play(new Game.Move(5, 'X'));
|
// game.play(new Game.Move(5, 'X'));
|
||||||
game.play(new Game.Move(4, 'O'));
|
// game.play(new Game.Move(4, 'O'));
|
||||||
game.play(new Game.Move(6, 'X'));
|
// game.play(new Game.Move(6, 'X'));
|
||||||
game.play(new Game.Move(7, 'O'));
|
// game.play(new Game.Move(7, 'O'));
|
||||||
game.play(new Game.Move(8, 'X'));
|
// game.play(new Game.Move(8, 'X'));
|
||||||
|
//
|
||||||
final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
|
// final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
|
||||||
|
//
|
||||||
assertNotNull(move);
|
// assertNotNull(move);
|
||||||
assertEquals('O', move.value());
|
// assertEquals('O', move.value());
|
||||||
assertEquals(2, move.position());
|
// assertEquals(2, move.position());
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
76
pom.xml
@@ -107,71 +107,19 @@
|
|||||||
</java>
|
</java>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- <plugin>-->
|
||||||
|
<!-- <groupId>org.apache.maven.plugins</groupId>-->
|
||||||
|
<!-- <artifactId>maven-compiler-plugin</artifactId>-->
|
||||||
|
<!-- <version>3.14.1</version>-->
|
||||||
|
<!-- <configuration>-->
|
||||||
|
<!-- <outputDirectory>${project.build.directory}/custom</outputDirectory>-->
|
||||||
|
<!-- <source>25</source>-->
|
||||||
|
<!-- <target>25</target>-->
|
||||||
|
<!-- <release>25</release>-->
|
||||||
|
<!-- <encoding>UTF-8</encoding>-->
|
||||||
|
<!-- </configuration>-->
|
||||||
|
<!-- </plugin>-->
|
||||||
</plugins>
|
</plugins>
|
||||||
<pluginManagement>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<version>3.5.4</version>
|
|
||||||
<configuration>
|
|
||||||
<excludedGroups>stress</excludedGroups>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
|
||||||
<version>3.5.4</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.14.1</version>
|
|
||||||
<configuration>
|
|
||||||
<source>25</source>
|
|
||||||
<target>25</target>
|
|
||||||
<release>25</release>
|
|
||||||
<encoding>UTF-8</encoding>
|
|
||||||
<!-- <compilerArgs>-->
|
|
||||||
<!-- <arg>-XDcompilePolicy=simple</arg>-->
|
|
||||||
<!-- <arg>--should-stop=ifError=FLOW</arg>-->
|
|
||||||
<!-- <arg>-Xplugin:ErrorProne</arg>-->
|
|
||||||
<!-- </compilerArgs>-->
|
|
||||||
<!-- <annotationProcessorPaths>-->
|
|
||||||
<!-- <path>-->
|
|
||||||
<!-- <groupId>com.google.errorprone</groupId>-->
|
|
||||||
<!-- <artifactId>error_prone_core</artifactId>-->
|
|
||||||
<!-- <version>2.42.0</version>-->
|
|
||||||
<!-- </path>-->
|
|
||||||
<!-- <!– Other annotation processors go here.-->
|
|
||||||
|
|
||||||
<!-- If 'annotationProcessorPaths' is set, processors will no longer be-->
|
|
||||||
<!-- discovered on the regular -classpath; see also 'Using Error Prone-->
|
|
||||||
<!-- together with other annotation processors' below. –>-->
|
|
||||||
<!-- </annotationProcessorPaths>-->
|
|
||||||
<!-- <fork>true</fork>-->
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>3.6.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<transformers>
|
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
|
||||||
<mainClass>app.src.java.org.toop.Main</mainClass>
|
|
||||||
</transformer>
|
|
||||||
</transformers>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</pluginManagement>
|
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
|||||||