mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 19:04:49 +00:00
Formatting
This commit is contained in:
@@ -8,14 +8,14 @@ import org.toop.framework.networking.NetworkingClientManager;
|
|||||||
import org.toop.framework.networking.NetworkingInitializationException;
|
import org.toop.framework.networking.NetworkingInitializationException;
|
||||||
|
|
||||||
public final class Main {
|
public final class Main {
|
||||||
public 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 {
|
||||||
ResourceManager.loadAssets(new ResourceLoader("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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
package org.toop.app;
|
package org.toop.app;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
import javafx.application.Application;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.stage.Stage;
|
||||||
import org.toop.app.layer.Layer;
|
import org.toop.app.layer.Layer;
|
||||||
import org.toop.app.layer.layers.MainLayer;
|
import org.toop.app.layer.layers.MainLayer;
|
||||||
import org.toop.app.layer.layers.QuitPopup;
|
import org.toop.app.layer.layers.QuitPopup;
|
||||||
@@ -9,155 +14,154 @@ 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 org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import org.toop.local.AppSettings;
|
import org.toop.local.AppSettings;
|
||||||
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
public final class App extends Application {
|
public final class App extends Application {
|
||||||
private static Stage stage;
|
private static Stage stage;
|
||||||
private static Scene scene;
|
private static Scene scene;
|
||||||
private static StackPane root;
|
private static StackPane root;
|
||||||
|
|
||||||
private static Stack<Layer> stack;
|
private static Stack<Layer> stack;
|
||||||
private static int height;
|
private static int height;
|
||||||
private static int width;
|
private static int width;
|
||||||
|
|
||||||
private static boolean isQuitting;
|
private static boolean isQuitting;
|
||||||
|
|
||||||
public static void run(String[] args) {
|
public static void run(String[] args) {
|
||||||
launch(args);
|
launch(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws Exception {
|
public void start(Stage stage) throws Exception {
|
||||||
final StackPane root = new StackPane();
|
final StackPane root = new StackPane();
|
||||||
final Scene scene = new Scene(root);
|
final Scene scene = new Scene(root);
|
||||||
|
|
||||||
stage.setTitle(AppContext.getString("appTitle"));
|
stage.setTitle(AppContext.getString("appTitle"));
|
||||||
stage.setWidth(1080);
|
stage.setWidth(1080);
|
||||||
stage.setHeight(720);
|
stage.setHeight(720);
|
||||||
|
|
||||||
stage.setOnCloseRequest(event -> {
|
stage.setOnCloseRequest(
|
||||||
event.consume();
|
event -> {
|
||||||
|
event.consume();
|
||||||
|
|
||||||
if (!isQuitting) {
|
if (!isQuitting) {
|
||||||
quitPopup();
|
quitPopup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
|
|
||||||
stage.show();
|
stage.show();
|
||||||
|
|
||||||
App.stage = stage;
|
App.stage = stage;
|
||||||
App.scene = scene;
|
App.scene = scene;
|
||||||
App.root = root;
|
App.root = root;
|
||||||
|
|
||||||
App.stack = new Stack<>();
|
App.stack = new Stack<>();
|
||||||
|
|
||||||
App.width = (int) stage.getWidth();
|
App.width = (int) stage.getWidth();
|
||||||
App.height = (int) stage.getHeight();
|
App.height = (int) stage.getHeight();
|
||||||
|
|
||||||
App.isQuitting = false;
|
App.isQuitting = false;
|
||||||
|
|
||||||
final AppSettings settings = new AppSettings();
|
final AppSettings settings = new AppSettings();
|
||||||
settings.applySettings();
|
settings.applySettings();
|
||||||
|
|
||||||
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
|
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
|
||||||
activate(new MainLayer());
|
activate(new MainLayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void activate(Layer layer) {
|
public static void activate(Layer layer) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(
|
||||||
popAll();
|
() -> {
|
||||||
push(layer);
|
popAll();
|
||||||
});
|
push(layer);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void push(Layer layer) {
|
public static void push(Layer layer) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(
|
||||||
root.getChildren().addLast(layer.getLayer());
|
() -> {
|
||||||
stack.push(layer);
|
root.getChildren().addLast(layer.getLayer());
|
||||||
});
|
stack.push(layer);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void pop() {
|
public static void pop() {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(
|
||||||
root.getChildren().removeLast();
|
() -> {
|
||||||
stack.pop();
|
root.getChildren().removeLast();
|
||||||
|
stack.pop();
|
||||||
|
|
||||||
isQuitting = false;
|
isQuitting = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void popAll() {
|
public static void popAll() {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(
|
||||||
final int childrenCount = root.getChildren().size();
|
() -> {
|
||||||
|
final int childrenCount = root.getChildren().size();
|
||||||
|
|
||||||
for (int i = 0; i < childrenCount; i++) {
|
for (int i = 0; i < childrenCount; i++) {
|
||||||
try {
|
try {
|
||||||
root.getChildren().removeLast();
|
root.getChildren().removeLast();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
IO.println(e);
|
IO.println(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.removeAllElements();
|
stack.removeAllElements();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void quitPopup() {
|
public static void quitPopup() {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(
|
||||||
push(new QuitPopup());
|
() -> {
|
||||||
isQuitting = true;
|
push(new QuitPopup());
|
||||||
});
|
isQuitting = true;
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void quit() {
|
public static void quit() {
|
||||||
stage.close();
|
stage.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void reloadAll() {
|
public static void reloadAll() {
|
||||||
stage.setTitle(AppContext.getString("appTitle"));
|
stage.setTitle(AppContext.getString("appTitle"));
|
||||||
|
|
||||||
for (final Layer layer : stack) {
|
for (final Layer layer : stack) {
|
||||||
layer.reload();
|
layer.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setFullscreen(boolean fullscreen) {
|
public static void setFullscreen(boolean fullscreen) {
|
||||||
stage.setFullScreen(fullscreen);
|
stage.setFullScreen(fullscreen);
|
||||||
|
|
||||||
width = (int) stage.getWidth();
|
width = (int) stage.getWidth();
|
||||||
height = (int) stage.getHeight();
|
height = (int) stage.getHeight();
|
||||||
|
|
||||||
reloadAll();
|
reloadAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setStyle(String theme, String layoutSize) {
|
public static void setStyle(String theme, String layoutSize) {
|
||||||
final int stylesCount = scene.getStylesheets().size();
|
final int stylesCount = scene.getStylesheets().size();
|
||||||
|
|
||||||
for (int i = 0; i < stylesCount; i++) {
|
for (int i = 0; i < stylesCount; i++) {
|
||||||
scene.getStylesheets().removeLast();
|
scene.getStylesheets().removeLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
scene.getStylesheets().add(ResourceManager.<CssAsset>get(theme + ".css").getUrl());
|
scene.getStylesheets().add(ResourceManager.<CssAsset>get(theme + ".css").getUrl());
|
||||||
scene.getStylesheets().add(ResourceManager.<CssAsset>get(layoutSize + ".css").getUrl());
|
scene.getStylesheets().add(ResourceManager.<CssAsset>get(layoutSize + ".css").getUrl());
|
||||||
|
|
||||||
reloadAll();
|
reloadAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getWidth() {
|
public static int getWidth() {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getHeight() {
|
public static int getHeight() {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
package org.toop.app;
|
package org.toop.app;
|
||||||
|
|
||||||
public record GameInformation(String[] playerName, boolean[] isPlayerHuman,
|
public record GameInformation(
|
||||||
int[] computerDifficulty, int[] computerThinkTime,
|
String[] playerName,
|
||||||
boolean isConnectionLocal, String serverIP, String serverPort) {
|
boolean[] isPlayerHuman,
|
||||||
}
|
int[] computerDifficulty,
|
||||||
|
int[] computerThinkTime,
|
||||||
|
boolean isConnectionLocal,
|
||||||
|
String serverIP,
|
||||||
|
String serverPort) {}
|
||||||
|
|||||||
@@ -1,123 +1,130 @@
|
|||||||
package org.toop.app.canvas;
|
package org.toop.app.canvas;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import javafx.scene.canvas.Canvas;
|
import javafx.scene.canvas.Canvas;
|
||||||
import javafx.scene.canvas.GraphicsContext;
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
import javafx.scene.input.MouseButton;
|
import javafx.scene.input.MouseButton;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public abstract class GameCanvas {
|
public abstract class GameCanvas {
|
||||||
protected record Cell(float x, float y, float width, float height) {
|
protected record Cell(float x, float y, float width, float height) {}
|
||||||
}
|
|
||||||
|
|
||||||
protected final Canvas canvas;
|
protected final Canvas canvas;
|
||||||
protected final GraphicsContext graphics;
|
protected final GraphicsContext graphics;
|
||||||
|
|
||||||
protected final Color color;
|
protected final Color color;
|
||||||
|
|
||||||
protected int width;
|
protected int width;
|
||||||
protected int height;
|
protected int height;
|
||||||
|
|
||||||
protected final int rows;
|
protected final int rows;
|
||||||
protected final int columns;
|
protected final int columns;
|
||||||
|
|
||||||
protected final int gapSize;
|
protected final int gapSize;
|
||||||
protected final boolean edges;
|
protected final boolean edges;
|
||||||
|
|
||||||
protected final Cell[] cells;
|
protected final Cell[] cells;
|
||||||
|
|
||||||
protected GameCanvas(Color color, int width, int height, int rows, int columns, int gapSize, boolean edges, Consumer<Integer> onCellClicked) {
|
protected GameCanvas(
|
||||||
canvas = new Canvas(width, height);
|
Color color,
|
||||||
graphics = canvas.getGraphicsContext2D();
|
int width,
|
||||||
|
int height,
|
||||||
|
int rows,
|
||||||
|
int columns,
|
||||||
|
int gapSize,
|
||||||
|
boolean edges,
|
||||||
|
Consumer<Integer> onCellClicked) {
|
||||||
|
canvas = new Canvas(width, height);
|
||||||
|
graphics = canvas.getGraphicsContext2D();
|
||||||
|
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
|
||||||
this.rows = rows;
|
this.rows = rows;
|
||||||
this.columns = columns;
|
this.columns = columns;
|
||||||
|
|
||||||
this.gapSize = gapSize;
|
this.gapSize = gapSize;
|
||||||
this.edges = edges;
|
this.edges = edges;
|
||||||
|
|
||||||
cells = new Cell[rows * columns];
|
cells = new Cell[rows * columns];
|
||||||
|
|
||||||
final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows;
|
final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows;
|
||||||
final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns;
|
final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns;
|
||||||
|
|
||||||
for (int y = 0; y < columns; y++) {
|
for (int y = 0; y < columns; y++) {
|
||||||
final float startY = y * cellHeight + y * gapSize;
|
final float startY = y * cellHeight + y * gapSize;
|
||||||
|
|
||||||
for (int x = 0; x < rows; x++) {
|
for (int x = 0; x < rows; x++) {
|
||||||
final float startX = x * cellWidth + x * gapSize;
|
final float startX = x * cellWidth + x * gapSize;
|
||||||
cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
|
cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.setOnMouseClicked(event -> {
|
canvas.setOnMouseClicked(
|
||||||
if (event.getButton() != MouseButton.PRIMARY) {
|
event -> {
|
||||||
return;
|
if (event.getButton() != MouseButton.PRIMARY) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final int column = (int) ((event.getX() / width) * rows);
|
final int column = (int) ((event.getX() / width) * rows);
|
||||||
final int row = (int) ((event.getY() / height) * columns);
|
final int row = (int) ((event.getY() / height) * columns);
|
||||||
|
|
||||||
event.consume();
|
event.consume();
|
||||||
onCellClicked.accept(row * rows + column);
|
onCellClicked.accept(row * rows + column);
|
||||||
});
|
});
|
||||||
|
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
graphics.clearRect(0, 0, width, height);
|
graphics.clearRect(0, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
public void render() {
|
||||||
graphics.setFill(color);
|
graphics.setFill(color);
|
||||||
|
|
||||||
for (int x = 1; x < rows; x++) {
|
for (int x = 1; x < rows; x++) {
|
||||||
graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height);
|
graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 1; y < columns; y++) {
|
for (int y = 1; y < columns; y++) {
|
||||||
graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize);
|
graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edges) {
|
if (edges) {
|
||||||
graphics.fillRect(-gapSize, 0, gapSize, height);
|
graphics.fillRect(-gapSize, 0, gapSize, height);
|
||||||
graphics.fillRect(0, -gapSize, width, gapSize);
|
graphics.fillRect(0, -gapSize, width, gapSize);
|
||||||
|
|
||||||
graphics.fillRect(width - gapSize, 0, gapSize, height);
|
graphics.fillRect(width - gapSize, 0, gapSize, height);
|
||||||
graphics.fillRect(0, height - gapSize, width, gapSize);
|
graphics.fillRect(0, height - gapSize, width, gapSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(Color color, int cell) {
|
public void draw(Color color, int cell) {
|
||||||
final float x = cells[cell].x() + gapSize;
|
final float x = cells[cell].x() + gapSize;
|
||||||
final float y = cells[cell].y() + gapSize;
|
final float y = cells[cell].y() + gapSize;
|
||||||
|
|
||||||
final float width = cells[cell].width() - gapSize * 2;
|
final float width = cells[cell].width() - gapSize * 2;
|
||||||
final float height = cells[cell].height() - gapSize * 2;
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
graphics.setFill(color);
|
graphics.setFill(color);
|
||||||
graphics.fillRect(x, y, width, height);
|
graphics.fillRect(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resize(int width, int height) {
|
public void resize(int width, int height) {
|
||||||
canvas.setWidth(width);
|
canvas.setWidth(width);
|
||||||
canvas.setHeight(height);
|
canvas.setHeight(height);
|
||||||
|
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Canvas getCanvas() {
|
public Canvas getCanvas() {
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,37 @@
|
|||||||
package org.toop.app.canvas;
|
package org.toop.app.canvas;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class TicTacToeCanvas extends GameCanvas {
|
public class TicTacToeCanvas extends GameCanvas {
|
||||||
public TicTacToeCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
|
public TicTacToeCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
|
||||||
super(color, width, height, 3, 3, 10, false, onCellClicked);
|
super(color, width, height, 3, 3, 10, false, onCellClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawX(Color color, int cell) {
|
public void drawX(Color color, int cell) {
|
||||||
graphics.setStroke(color);
|
graphics.setStroke(color);
|
||||||
graphics.setLineWidth(gapSize);
|
graphics.setLineWidth(gapSize);
|
||||||
|
|
||||||
final float x = cells[cell].x() + gapSize;
|
final float x = cells[cell].x() + gapSize;
|
||||||
final float y = cells[cell].y() + gapSize;
|
final float y = cells[cell].y() + gapSize;
|
||||||
|
|
||||||
final float width = cells[cell].width() - gapSize * 2;
|
final float width = cells[cell].width() - gapSize * 2;
|
||||||
final float height = cells[cell].height() - gapSize * 2;
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
graphics.strokeLine(x, y, x + width, y + height);
|
graphics.strokeLine(x, y, x + width, y + height);
|
||||||
graphics.strokeLine(x + width, y, x, y + height);
|
graphics.strokeLine(x + width, y, x, y + height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawO(Color color, int cell) {
|
public void drawO(Color color, int cell) {
|
||||||
graphics.setStroke(color);
|
graphics.setStroke(color);
|
||||||
graphics.setLineWidth(gapSize);
|
graphics.setLineWidth(gapSize);
|
||||||
|
|
||||||
final float x = cells[cell].x() + gapSize;
|
final float x = cells[cell].x() + gapSize;
|
||||||
final float y = cells[cell].y() + gapSize;
|
final float y = cells[cell].y() + gapSize;
|
||||||
|
|
||||||
final float width = cells[cell].width() - gapSize * 2;
|
final float width = cells[cell].width() - gapSize * 2;
|
||||||
final float height = cells[cell].height() - gapSize * 2;
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
graphics.strokeOval(x, y, width, height);
|
graphics.strokeOval(x, y, width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,9 @@ import javafx.scene.Node;
|
|||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
|
||||||
public abstract class Container {
|
public abstract class Container {
|
||||||
public abstract Region getContainer();
|
public abstract Region getContainer();
|
||||||
|
|
||||||
public abstract void addNodes(Node... nodes);
|
public abstract void addNodes(Node... nodes);
|
||||||
public abstract void addContainer(Container container, boolean fill);
|
|
||||||
|
public abstract void addContainer(Container container, boolean fill);
|
||||||
}
|
}
|
||||||
@@ -1,81 +1,86 @@
|
|||||||
package org.toop.app.layer;
|
package org.toop.app.layer;
|
||||||
|
|
||||||
import org.toop.app.App;
|
|
||||||
import org.toop.app.canvas.GameCanvas;
|
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
import org.toop.app.App;
|
||||||
|
import org.toop.app.canvas.GameCanvas;
|
||||||
|
|
||||||
public abstract class Layer {
|
public abstract class Layer {
|
||||||
protected StackPane layer;
|
protected StackPane layer;
|
||||||
protected Region background;
|
protected Region background;
|
||||||
|
|
||||||
protected Layer(String... backgroundStyles) {
|
protected Layer(String... backgroundStyles) {
|
||||||
layer = new StackPane();
|
layer = new StackPane();
|
||||||
|
|
||||||
background = new Region();
|
background = new Region();
|
||||||
background.getStyleClass().addAll(backgroundStyles);
|
background.getStyleClass().addAll(backgroundStyles);
|
||||||
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||||
|
|
||||||
layer.getChildren().addLast(background);
|
layer.getChildren().addLast(background);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addContainer(Container container, Pos position, int xOffset, int yOffset, int widthPercent, int heightPercent) {
|
protected void addContainer(
|
||||||
StackPane.setAlignment(container.getContainer(), position);
|
Container container,
|
||||||
|
Pos position,
|
||||||
|
int xOffset,
|
||||||
|
int yOffset,
|
||||||
|
int widthPercent,
|
||||||
|
int heightPercent) {
|
||||||
|
StackPane.setAlignment(container.getContainer(), position);
|
||||||
|
|
||||||
final double widthUnit = App.getWidth() / 100.0;
|
final double widthUnit = App.getWidth() / 100.0;
|
||||||
final double heightUnit = App.getHeight() / 100.0;
|
final double heightUnit = App.getHeight() / 100.0;
|
||||||
|
|
||||||
if (widthPercent > 0) {
|
if (widthPercent > 0) {
|
||||||
container.getContainer().setMaxWidth(widthPercent * widthUnit);
|
container.getContainer().setMaxWidth(widthPercent * widthUnit);
|
||||||
} else {
|
} else {
|
||||||
container.getContainer().setMaxWidth(Region.USE_PREF_SIZE);
|
container.getContainer().setMaxWidth(Region.USE_PREF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heightPercent > 0) {
|
if (heightPercent > 0) {
|
||||||
container.getContainer().setMaxHeight(heightPercent * heightUnit);
|
container.getContainer().setMaxHeight(heightPercent * heightUnit);
|
||||||
} else {
|
} else {
|
||||||
container.getContainer().setMaxHeight(Region.USE_PREF_SIZE);
|
container.getContainer().setMaxHeight(Region.USE_PREF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
container.getContainer().setTranslateX(xOffset * widthUnit);
|
container.getContainer().setTranslateX(xOffset * widthUnit);
|
||||||
container.getContainer().setTranslateY(yOffset * heightUnit);
|
container.getContainer().setTranslateY(yOffset * heightUnit);
|
||||||
|
|
||||||
layer.getChildren().addLast(container.getContainer());
|
layer.getChildren().addLast(container.getContainer());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) {
|
protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) {
|
||||||
StackPane.setAlignment(canvas.getCanvas(), position);
|
StackPane.setAlignment(canvas.getCanvas(), position);
|
||||||
|
|
||||||
final double widthUnit = App.getWidth() / 100.0;
|
final double widthUnit = App.getWidth() / 100.0;
|
||||||
final double heightUnit = App.getHeight() / 100.0;
|
final double heightUnit = App.getHeight() / 100.0;
|
||||||
|
|
||||||
canvas.getCanvas().setTranslateX(xOffset * widthUnit);
|
canvas.getCanvas().setTranslateX(xOffset * widthUnit);
|
||||||
canvas.getCanvas().setTranslateY(yOffset * heightUnit);
|
canvas.getCanvas().setTranslateY(yOffset * heightUnit);
|
||||||
|
|
||||||
layer.getChildren().addLast(canvas.getCanvas());
|
layer.getChildren().addLast(canvas.getCanvas());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void pop() {
|
protected void pop() {
|
||||||
if (layer.getChildren().size() <= 1) {
|
if (layer.getChildren().size() <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
layer.getChildren().removeLast();
|
layer.getChildren().removeLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void popAll() {
|
protected void popAll() {
|
||||||
final int containers = layer.getChildren().size();
|
final int containers = layer.getChildren().size();
|
||||||
|
|
||||||
for (int i = 1; i < containers; i++) {
|
for (int i = 1; i < containers; i++) {
|
||||||
layer.getChildren().removeLast();
|
layer.getChildren().removeLast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public StackPane getLayer() {
|
public StackPane getLayer() {
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void reload();
|
public abstract void reload();
|
||||||
}
|
}
|
||||||
@@ -1,131 +1,140 @@
|
|||||||
package org.toop.app.layer;
|
package org.toop.app.layer;
|
||||||
|
|
||||||
import org.toop.framework.audio.events.AudioEvents;
|
import java.util.function.Consumer;
|
||||||
import org.toop.framework.eventbus.EventFlow;
|
|
||||||
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
|
import org.toop.framework.audio.events.AudioEvents;
|
||||||
import java.util.function.Consumer;
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
|
|
||||||
public final class NodeBuilder {
|
public final class NodeBuilder {
|
||||||
public static void addCss(Node node, String... cssClasses) {
|
public static void addCss(Node node, String... cssClasses) {
|
||||||
node.getStyleClass().addAll(cssClasses);
|
node.getStyleClass().addAll(cssClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setCss(Node node, String... cssClasses) {
|
public static void setCss(Node node, String... cssClasses) {
|
||||||
node.getStyleClass().removeAll();
|
node.getStyleClass().removeAll();
|
||||||
node.getStyleClass().addAll(cssClasses);
|
node.getStyleClass().addAll(cssClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Text header(String x) {
|
public static Text header(String x) {
|
||||||
final Text element = new Text(x);
|
final Text element = new Text(x);
|
||||||
setCss(element, "text-primary", "text-header");
|
setCss(element, "text-primary", "text-header");
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Text text(String x) {
|
public static Text text(String x) {
|
||||||
final Text element = new Text(x);
|
final Text element = new Text(x);
|
||||||
setCss(element, "text-secondary", "text-normal");
|
setCss(element, "text-secondary", "text-normal");
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Label button(String x, Runnable runnable) {
|
public static Label button(String x, Runnable runnable) {
|
||||||
final Label element = new Label(x);
|
final Label element = new Label(x);
|
||||||
setCss(element, "button", "text-normal");
|
setCss(element, "button", "text-normal");
|
||||||
|
|
||||||
element.setOnMouseClicked(_ -> {
|
element.setOnMouseClicked(
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
_ -> {
|
||||||
runnable.run();
|
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||||
});
|
runnable.run();
|
||||||
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Label toggle(String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
|
public static Label toggle(String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
|
||||||
final Label element = new Label(toggled ? x2 : x1);
|
final Label element = new Label(toggled ? x2 : x1);
|
||||||
setCss(element, "toggle", "text-normal");
|
setCss(element, "toggle", "text-normal");
|
||||||
|
|
||||||
final BooleanProperty checked = new SimpleBooleanProperty(toggled);
|
final BooleanProperty checked = new SimpleBooleanProperty(toggled);
|
||||||
|
|
||||||
element.setOnMouseClicked(_ -> {
|
element.setOnMouseClicked(
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
_ -> {
|
||||||
checked.set(!checked.get());
|
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||||
|
checked.set(!checked.get());
|
||||||
|
|
||||||
if (checked.get()) {
|
if (checked.get()) {
|
||||||
element.setText(x1);
|
element.setText(x1);
|
||||||
} else {
|
} else {
|
||||||
element.setText(x2);
|
element.setText(x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
consumer.accept(checked.get());
|
consumer.accept(checked.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Slider slider(int max, int initial, Consumer<Integer> consumer) {
|
public static Slider slider(int max, int initial, Consumer<Integer> consumer) {
|
||||||
final Slider element = new Slider(0, max, initial);
|
final Slider element = new Slider(0, max, initial);
|
||||||
setCss(element, "bg-slider-track");
|
setCss(element, "bg-slider-track");
|
||||||
|
|
||||||
element.setMinorTickCount(0);
|
element.setMinorTickCount(0);
|
||||||
element.setMajorTickUnit(1);
|
element.setMajorTickUnit(1);
|
||||||
element.setBlockIncrement(1);
|
element.setBlockIncrement(1);
|
||||||
|
|
||||||
element.setSnapToTicks(true);
|
element.setSnapToTicks(true);
|
||||||
element.setShowTickLabels(true);
|
element.setShowTickLabels(true);
|
||||||
|
|
||||||
element.setOnMouseClicked(_ -> {
|
element.setOnMouseClicked(
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
_ -> {
|
||||||
});
|
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||||
|
});
|
||||||
|
|
||||||
element.valueProperty().addListener((_, _, newValue) -> {
|
element.valueProperty()
|
||||||
consumer.accept(newValue.intValue());
|
.addListener(
|
||||||
});
|
(_, _, newValue) -> {
|
||||||
|
consumer.accept(newValue.intValue());
|
||||||
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextField input(String x, Consumer<String> consumer) {
|
public static TextField input(String x, Consumer<String> consumer) {
|
||||||
final TextField element = new TextField(x);
|
final TextField element = new TextField(x);
|
||||||
setCss(element, "input", "text-normal");
|
setCss(element, "input", "text-normal");
|
||||||
|
|
||||||
element.setOnMouseClicked(_ -> {
|
element.setOnMouseClicked(
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
_ -> {
|
||||||
});
|
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||||
|
});
|
||||||
|
|
||||||
element.textProperty().addListener((_, _, newValue) -> {
|
element.textProperty()
|
||||||
consumer.accept(newValue);
|
.addListener(
|
||||||
});
|
(_, _, newValue) -> {
|
||||||
|
consumer.accept(newValue);
|
||||||
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ChoiceBox<T> choiceBox(Consumer<T> consumer) {
|
public static <T> ChoiceBox<T> choiceBox(Consumer<T> consumer) {
|
||||||
final ChoiceBox<T> element = new ChoiceBox<>();
|
final ChoiceBox<T> element = new ChoiceBox<>();
|
||||||
setCss(element, "choice-box", "text-normal");
|
setCss(element, "choice-box", "text-normal");
|
||||||
|
|
||||||
element.setOnMouseClicked(_ -> {
|
element.setOnMouseClicked(
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
_ -> {
|
||||||
});
|
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||||
|
});
|
||||||
|
|
||||||
element.valueProperty().addListener((_, _, newValue) -> {
|
element.valueProperty()
|
||||||
consumer.accept(newValue);
|
.addListener(
|
||||||
});
|
(_, _, newValue) -> {
|
||||||
|
consumer.accept(newValue);
|
||||||
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Separator separator() {
|
public static Separator separator() {
|
||||||
final Separator element = new Separator(Orientation.HORIZONTAL);
|
final Separator element = new Separator(Orientation.HORIZONTAL);
|
||||||
setCss(element, "separator");
|
setCss(element, "separator");
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,17 +3,18 @@ package org.toop.app.layer;
|
|||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
|
|
||||||
public abstract class Popup extends Layer {
|
public abstract class Popup extends Layer {
|
||||||
protected Popup(boolean popOnBackground, String... backgroundStyles) {
|
protected Popup(boolean popOnBackground, String... backgroundStyles) {
|
||||||
super(backgroundStyles);
|
super(backgroundStyles);
|
||||||
|
|
||||||
if (popOnBackground) {
|
if (popOnBackground) {
|
||||||
background.setOnMouseClicked(_ -> {
|
background.setOnMouseClicked(
|
||||||
App.pop();
|
_ -> {
|
||||||
});
|
App.pop();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected Popup(boolean popOnBackground) {
|
protected Popup(boolean popOnBackground) {
|
||||||
this(popOnBackground, "bg-popup");
|
this(popOnBackground, "bg-popup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,60 +1,59 @@
|
|||||||
package org.toop.app.layer.containers;
|
package org.toop.app.layer.containers;
|
||||||
|
|
||||||
import org.toop.app.layer.Container;
|
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import org.toop.app.layer.Container;
|
||||||
|
|
||||||
public final class HorizontalContainer extends Container {
|
public final class HorizontalContainer extends Container {
|
||||||
private final HBox container;
|
private final HBox container;
|
||||||
|
|
||||||
public HorizontalContainer(int spacing, String... cssClasses) {
|
public HorizontalContainer(int spacing, String... cssClasses) {
|
||||||
container = new HBox(spacing);
|
container = new HBox(spacing);
|
||||||
container.getStyleClass().addAll(cssClasses);
|
container.getStyleClass().addAll(cssClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HorizontalContainer(int spacing) {
|
public HorizontalContainer(int spacing) {
|
||||||
this(spacing, "container");
|
this(spacing, "container");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Region getContainer() {
|
public Region getContainer() {
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addNodes(Node... nodes) {
|
public void addNodes(Node... nodes) {
|
||||||
container.getChildren().addAll(nodes);
|
container.getChildren().addAll(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addContainer(Container container, boolean fill) {
|
public void addContainer(Container container, boolean fill) {
|
||||||
if (fill) {
|
if (fill) {
|
||||||
container.getContainer().setMinSize(0, 0);
|
container.getContainer().setMinSize(0, 0);
|
||||||
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||||
HBox.setHgrow(container.getContainer(), Priority.ALWAYS);
|
HBox.setHgrow(container.getContainer(), Priority.ALWAYS);
|
||||||
} else {
|
} else {
|
||||||
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.getChildren().add(container.getContainer());
|
this.container.getChildren().add(container.getContainer());
|
||||||
|
|
||||||
if (fill) {
|
if (fill) {
|
||||||
balanceChildWidths();
|
balanceChildWidths();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void balanceChildWidths() {
|
private void balanceChildWidths() {
|
||||||
final ObservableList<Node> children = container.getChildren();
|
final ObservableList<Node> children = container.getChildren();
|
||||||
final double widthPerChild = container.getWidth() / children.size();
|
final double widthPerChild = container.getWidth() / children.size();
|
||||||
|
|
||||||
for (final Node child : children) {
|
for (final Node child : children) {
|
||||||
if (child instanceof Region) {
|
if (child instanceof Region) {
|
||||||
((Region) child).setPrefWidth(widthPerChild);
|
((Region) child).setPrefWidth(widthPerChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,60 +1,59 @@
|
|||||||
package org.toop.app.layer.containers;
|
package org.toop.app.layer.containers;
|
||||||
|
|
||||||
import org.toop.app.layer.Container;
|
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.toop.app.layer.Container;
|
||||||
|
|
||||||
public final class VerticalContainer extends Container {
|
public final class VerticalContainer extends Container {
|
||||||
private final VBox container;
|
private final VBox container;
|
||||||
|
|
||||||
public VerticalContainer(int spacing, String... cssClasses) {
|
public VerticalContainer(int spacing, String... cssClasses) {
|
||||||
container = new VBox(spacing);
|
container = new VBox(spacing);
|
||||||
container.getStyleClass().addAll(cssClasses);
|
container.getStyleClass().addAll(cssClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VerticalContainer(int spacing) {
|
public VerticalContainer(int spacing) {
|
||||||
this(spacing, "container");
|
this(spacing, "container");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Region getContainer() {
|
public Region getContainer() {
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addNodes(Node... nodes) {
|
public void addNodes(Node... nodes) {
|
||||||
container.getChildren().addAll(nodes);
|
container.getChildren().addAll(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addContainer(Container container, boolean fill) {
|
public void addContainer(Container container, boolean fill) {
|
||||||
if (fill) {
|
if (fill) {
|
||||||
container.getContainer().setMinSize(0, 0);
|
container.getContainer().setMinSize(0, 0);
|
||||||
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||||
VBox.setVgrow(container.getContainer(), Priority.ALWAYS);
|
VBox.setVgrow(container.getContainer(), Priority.ALWAYS);
|
||||||
} else {
|
} else {
|
||||||
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.getChildren().add(container.getContainer());
|
this.container.getChildren().add(container.getContainer());
|
||||||
|
|
||||||
if (fill) {
|
if (fill) {
|
||||||
balanceChildHeights();
|
balanceChildHeights();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void balanceChildHeights() {
|
private void balanceChildHeights() {
|
||||||
final ObservableList<Node> children = container.getChildren();
|
final ObservableList<Node> children = container.getChildren();
|
||||||
final double heightPerChild = container.getHeight() / children.size();
|
final double heightPerChild = container.getHeight() / children.size();
|
||||||
|
|
||||||
for (final Node child : children) {
|
for (final Node child : children) {
|
||||||
if (child instanceof Region) {
|
if (child instanceof Region) {
|
||||||
((Region) child).setPrefHeight(heightPerChild);
|
((Region) child).setPrefHeight(heightPerChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
package org.toop.app.layer.layers;
|
package org.toop.app.layer.layers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.GameInformation;
|
import org.toop.app.GameInformation;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
@@ -14,169 +22,214 @@ import org.toop.framework.eventbus.EventFlow;
|
|||||||
import org.toop.framework.networking.events.NetworkEvents;
|
import org.toop.framework.networking.events.NetworkEvents;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ListView;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public final class ConnectedLayer extends Layer {
|
public final class ConnectedLayer extends Layer {
|
||||||
private static Timer pollTimer = new Timer();
|
private static Timer pollTimer = new Timer();
|
||||||
|
|
||||||
private static class ChallengePopup extends Popup {
|
private static class ChallengePopup extends Popup {
|
||||||
private final GameInformation information;
|
private final GameInformation information;
|
||||||
|
|
||||||
private final String challenger;
|
private final String challenger;
|
||||||
private final String game;
|
private final String game;
|
||||||
|
|
||||||
private final long clientID;
|
private final long clientID;
|
||||||
private final int challengeID;
|
private final int challengeID;
|
||||||
|
|
||||||
public ChallengePopup(GameInformation information, String challenger, String game, long clientID, String challengeID) {
|
public ChallengePopup(
|
||||||
super(false, "bg-popup");
|
GameInformation information,
|
||||||
|
String challenger,
|
||||||
|
String game,
|
||||||
|
long clientID,
|
||||||
|
String challengeID) {
|
||||||
|
super(false, "bg-popup");
|
||||||
|
|
||||||
this.information = information;
|
this.information = information;
|
||||||
|
|
||||||
this.challenger = challenger;
|
this.challenger = challenger;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
|
||||||
this.clientID = clientID;
|
this.clientID = clientID;
|
||||||
this.challengeID = Integer.parseInt(challengeID.substring(18, challengeID.length() - 2));
|
this.challengeID =
|
||||||
|
Integer.parseInt(challengeID.substring(18, challengeID.length() - 2));
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final var challengeText = NodeBuilder.header(AppContext.getString("challengeText"));
|
final var challengeText = NodeBuilder.header(AppContext.getString("challengeText"));
|
||||||
final var challengerNameText = NodeBuilder.header(challenger);
|
final var challengerNameText = NodeBuilder.header(challenger);
|
||||||
|
|
||||||
final var gameText = NodeBuilder.text(AppContext.getString("gameIsText"));
|
final var gameText = NodeBuilder.text(AppContext.getString("gameIsText"));
|
||||||
final var gameNameText = NodeBuilder.text(game);
|
final var gameNameText = NodeBuilder.text(game);
|
||||||
|
|
||||||
final var acceptButton = NodeBuilder.button(AppContext.getString("accept"), () -> {
|
final var acceptButton =
|
||||||
pollTimer.cancel();
|
NodeBuilder.button(
|
||||||
|
AppContext.getString("accept"),
|
||||||
|
() -> {
|
||||||
|
pollTimer.cancel();
|
||||||
|
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientID, challengeID)).postEvent();
|
new EventFlow()
|
||||||
App.activate(new TicTacToeLayer(information, clientID));
|
.addPostEvent(
|
||||||
});
|
new NetworkEvents.SendAcceptChallenge(
|
||||||
|
clientID, challengeID))
|
||||||
|
.postEvent();
|
||||||
|
App.activate(new TicTacToeLayer(information, clientID));
|
||||||
|
});
|
||||||
|
|
||||||
final var denyButton = NodeBuilder.button(AppContext.getString("deny"), () -> {
|
final var denyButton =
|
||||||
App.pop();
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("deny"),
|
||||||
|
() -> {
|
||||||
|
App.pop();
|
||||||
|
});
|
||||||
|
|
||||||
final Container controlContainer = new HorizontalContainer(30);
|
final Container controlContainer = new HorizontalContainer(30);
|
||||||
controlContainer.addNodes(acceptButton, denyButton);
|
controlContainer.addNodes(acceptButton, denyButton);
|
||||||
|
|
||||||
final Container mainContainer = new VerticalContainer(30);
|
final Container mainContainer = new VerticalContainer(30);
|
||||||
mainContainer.addNodes(challengeText, challengerNameText);
|
mainContainer.addNodes(challengeText, challengerNameText);
|
||||||
mainContainer.addNodes(gameText, gameNameText);
|
mainContainer.addNodes(gameText, gameNameText);
|
||||||
|
|
||||||
mainContainer.addContainer(controlContainer, false);
|
mainContainer.addContainer(controlContainer, false);
|
||||||
|
|
||||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameInformation information;
|
GameInformation information;
|
||||||
long clientId;
|
long clientId;
|
||||||
String user;
|
String user;
|
||||||
List<String> onlinePlayers = new CopyOnWriteArrayList<>();
|
List<String> onlinePlayers = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
public ConnectedLayer(GameInformation information) {
|
public ConnectedLayer(GameInformation information) {
|
||||||
super("bg-primary");
|
super("bg-primary");
|
||||||
|
|
||||||
this.information = information;
|
this.information = information;
|
||||||
|
|
||||||
new EventFlow()
|
new EventFlow()
|
||||||
.addPostEvent(NetworkEvents.StartClient.class, information.serverIP(), Integer.parseInt(information.serverPort()))
|
.addPostEvent(
|
||||||
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
NetworkEvents.StartClient.class,
|
||||||
clientId = e.clientId();
|
information.serverIP(),
|
||||||
user = information.playerName()[0].replaceAll("\\s+", "");
|
Integer.parseInt(information.serverPort()))
|
||||||
|
.onResponse(
|
||||||
|
NetworkEvents.StartClientResponse.class,
|
||||||
|
e -> {
|
||||||
|
clientId = e.clientId();
|
||||||
|
user = information.playerName()[0].replaceAll("\\s+", "");
|
||||||
|
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendLogin(this.clientId, this.user)).postEvent();
|
new EventFlow()
|
||||||
|
.addPostEvent(
|
||||||
|
new NetworkEvents.SendLogin(this.clientId, this.user))
|
||||||
|
.postEvent();
|
||||||
|
|
||||||
Thread popThread = new Thread(this::populatePlayerList);
|
Thread popThread = new Thread(this::populatePlayerList);
|
||||||
popThread.setDaemon(false);
|
popThread.setDaemon(false);
|
||||||
popThread.start();
|
popThread.start();
|
||||||
}).postEvent();
|
})
|
||||||
|
.postEvent();
|
||||||
|
|
||||||
new EventFlow().listen(this::handleReceivedChallenge);
|
new EventFlow().listen(this::handleReceivedChallenge);
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populatePlayerList() {
|
private void populatePlayerList() {
|
||||||
EventFlow sendGetPlayerList = new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId));
|
EventFlow sendGetPlayerList =
|
||||||
new EventFlow().listen(NetworkEvents.PlayerlistResponse.class, e -> {
|
new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId));
|
||||||
if (e.clientId() == this.clientId) {
|
new EventFlow()
|
||||||
List<String> playerList = new java.util.ArrayList<>(List.of(e.playerlist())); // TODO: Garbage, but works
|
.listen(
|
||||||
playerList.removeIf(name -> name.equalsIgnoreCase(user));
|
NetworkEvents.PlayerlistResponse.class,
|
||||||
if (this.onlinePlayers != playerList) {
|
e -> {
|
||||||
this.onlinePlayers.clear();
|
if (e.clientId() == this.clientId) {
|
||||||
this.onlinePlayers.addAll(playerList);
|
List<String> playerList =
|
||||||
}
|
new java.util.ArrayList<>(
|
||||||
}
|
List.of(e.playerlist())); // TODO: Garbage,
|
||||||
});
|
// but works
|
||||||
|
playerList.removeIf(name -> name.equalsIgnoreCase(user));
|
||||||
|
if (this.onlinePlayers != playerList) {
|
||||||
|
this.onlinePlayers.clear();
|
||||||
|
this.onlinePlayers.addAll(playerList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
TimerTask task = new TimerTask() {
|
TimerTask task =
|
||||||
public void run() {
|
new TimerTask() {
|
||||||
sendGetPlayerList.postEvent();
|
public void run() {
|
||||||
Platform.runLater(() -> reload());
|
sendGetPlayerList.postEvent();
|
||||||
}
|
Platform.runLater(() -> reload());
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later
|
pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendChallenge(String oppUsername, String gameType) {
|
private void sendChallenge(String oppUsername, String gameType) {
|
||||||
final AtomicInteger challengeId = new AtomicInteger(-1);
|
final AtomicInteger challengeId = new AtomicInteger(-1);
|
||||||
|
|
||||||
if (onlinePlayers.contains(oppUsername)) {
|
if (onlinePlayers.contains(oppUsername)) {
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType))
|
new EventFlow()
|
||||||
.listen(NetworkEvents.ChallengeResponse.class, e -> {
|
.addPostEvent(
|
||||||
challengeId.set(Integer.parseInt(e.challengeId().substring(18, e.challengeId().length() - 2)));
|
new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType))
|
||||||
})
|
.listen(
|
||||||
.listen(NetworkEvents.GameMatchResponse.class, e -> {
|
NetworkEvents.ChallengeResponse.class,
|
||||||
if (e.clientId() == this.clientId) {
|
e -> {
|
||||||
pollTimer.cancel();
|
challengeId.set(
|
||||||
App.activate(new TicTacToeLayer(information, this.clientId));
|
Integer.parseInt(
|
||||||
}
|
e.challengeId()
|
||||||
}, false).postEvent();
|
.substring(
|
||||||
// ^
|
18, e.challengeId().length() - 2)));
|
||||||
// |
|
})
|
||||||
// |
|
.listen(
|
||||||
// |
|
NetworkEvents.GameMatchResponse.class,
|
||||||
}
|
e -> {
|
||||||
}
|
if (e.clientId() == this.clientId) {
|
||||||
|
pollTimer.cancel();
|
||||||
|
App.activate(new TicTacToeLayer(information, this.clientId));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false)
|
||||||
|
.postEvent();
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
|
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
|
||||||
App.push(new ChallengePopup(information, response.challengerName(), response.gameType(), clientId, response.challengeId()));
|
App.push(
|
||||||
}
|
new ChallengePopup(
|
||||||
|
information,
|
||||||
|
response.challengerName(),
|
||||||
|
response.gameType(),
|
||||||
|
clientId,
|
||||||
|
response.challengeId()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
ListView<Label> players = new ListView<>();
|
ListView<Label> players = new ListView<>();
|
||||||
|
|
||||||
for (int i = 0; i < onlinePlayers.size(); i++) {
|
for (int i = 0; i < onlinePlayers.size(); i++) {
|
||||||
int finalI = i;
|
int finalI = i;
|
||||||
players.getItems().add(NodeBuilder.button(onlinePlayers.get(i), () -> {
|
players.getItems()
|
||||||
String clickedPlayer = onlinePlayers.get(finalI);
|
.add(
|
||||||
sendChallenge(clickedPlayer, "tic-tac-toe");
|
NodeBuilder.button(
|
||||||
}));
|
onlinePlayers.get(i),
|
||||||
}
|
() -> {
|
||||||
|
String clickedPlayer = onlinePlayers.get(finalI);
|
||||||
|
sendChallenge(clickedPlayer, "tic-tac-toe");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
final Container playersContainer = new VerticalContainer(10);
|
final Container playersContainer = new VerticalContainer(10);
|
||||||
playersContainer.addNodes(players);
|
playersContainer.addNodes(players);
|
||||||
|
|
||||||
addContainer(playersContainer, Pos.CENTER, 0, 0, 0, 0);
|
addContainer(playersContainer, Pos.CENTER, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
package org.toop.app.layer.layers;
|
package org.toop.app.layer.layers;
|
||||||
|
|
||||||
|
import javafx.animation.PauseTransition;
|
||||||
|
import javafx.animation.TranslateTransition;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.util.Duration;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
import org.toop.app.layer.NodeBuilder;
|
import org.toop.app.layer.NodeBuilder;
|
||||||
@@ -8,65 +13,61 @@ import org.toop.app.layer.containers.HorizontalContainer;
|
|||||||
import org.toop.app.layer.containers.VerticalContainer;
|
import org.toop.app.layer.containers.VerticalContainer;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.animation.PauseTransition;
|
|
||||||
import javafx.animation.TranslateTransition;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
|
|
||||||
public final class CreditsPopup extends Popup {
|
public final class CreditsPopup extends Popup {
|
||||||
private final int lineHeight = 100;
|
private final int lineHeight = 100;
|
||||||
|
|
||||||
public CreditsPopup() {
|
public CreditsPopup() {
|
||||||
super(true, "bg-primary");
|
super(true, "bg-primary");
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final String[] credits = {
|
final String[] credits = {
|
||||||
AppContext.getString("scrumMaster") + ": Stef",
|
AppContext.getString("scrumMaster") + ": Stef",
|
||||||
AppContext.getString("productOwner") + ": Omar",
|
AppContext.getString("productOwner") + ": Omar",
|
||||||
AppContext.getString("mergeCommander") + ": Bas",
|
AppContext.getString("mergeCommander") + ": Bas",
|
||||||
AppContext.getString("localization") + ": Ticho",
|
AppContext.getString("localization") + ": Ticho",
|
||||||
AppContext.getString("ai") + ": Michiel",
|
AppContext.getString("ai") + ": Michiel",
|
||||||
AppContext.getString("developers") + ": Michiel, Bas, Stef, Omar, Ticho",
|
AppContext.getString("developers") + ": Michiel, Bas, Stef, Omar, Ticho",
|
||||||
AppContext.getString("moralSupport") + ": Wesley",
|
AppContext.getString("moralSupport") + ": Wesley",
|
||||||
AppContext.getString("opengl") + ": Omar"
|
AppContext.getString("opengl") + ": Omar"
|
||||||
};
|
};
|
||||||
|
|
||||||
final Text[] creditsHeaders = new Text[credits.length];
|
final Text[] creditsHeaders = new Text[credits.length];
|
||||||
|
|
||||||
for (int i = 0; i < credits.length; i++) {
|
for (int i = 0; i < credits.length; i++) {
|
||||||
creditsHeaders[i] = NodeBuilder.header(credits[i]);
|
creditsHeaders[i] = NodeBuilder.header(credits[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Container creditsContainer = new HorizontalContainer(0);
|
final Container creditsContainer = new HorizontalContainer(0);
|
||||||
|
|
||||||
final Container animatedContainer = new VerticalContainer(lineHeight);
|
final Container animatedContainer = new VerticalContainer(lineHeight);
|
||||||
creditsContainer.addContainer(animatedContainer, true);
|
creditsContainer.addContainer(animatedContainer, true);
|
||||||
|
|
||||||
animatedContainer.addNodes(creditsHeaders);
|
animatedContainer.addNodes(creditsHeaders);
|
||||||
addContainer(creditsContainer, Pos.CENTER, 0, 0, 50, 100);
|
addContainer(creditsContainer, Pos.CENTER, 0, 0, 50, 100);
|
||||||
|
|
||||||
playCredits(animatedContainer, App.getHeight());
|
playCredits(animatedContainer, App.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playCredits(Container container, double sceneLength) {
|
private void playCredits(Container container, double sceneLength) {
|
||||||
container.getContainer().setTranslateY(-sceneLength);
|
container.getContainer().setTranslateY(-sceneLength);
|
||||||
|
|
||||||
final TranslateTransition scrollCredits = new TranslateTransition(Duration.seconds(20), container.getContainer());
|
final TranslateTransition scrollCredits =
|
||||||
scrollCredits.setFromY(-sceneLength - lineHeight);
|
new TranslateTransition(Duration.seconds(20), container.getContainer());
|
||||||
scrollCredits.setToY(sceneLength + lineHeight);
|
scrollCredits.setFromY(-sceneLength - lineHeight);
|
||||||
|
scrollCredits.setToY(sceneLength + lineHeight);
|
||||||
|
|
||||||
scrollCredits.setOnFinished(_ -> {
|
scrollCredits.setOnFinished(
|
||||||
final PauseTransition pauseCredits = new PauseTransition(Duration.seconds(3));
|
_ -> {
|
||||||
pauseCredits.setOnFinished(_ -> playCredits(container, sceneLength));
|
final PauseTransition pauseCredits = new PauseTransition(Duration.seconds(3));
|
||||||
pauseCredits.play();
|
pauseCredits.setOnFinished(_ -> playCredits(container, sceneLength));
|
||||||
});
|
pauseCredits.play();
|
||||||
|
});
|
||||||
|
|
||||||
scrollCredits.play();
|
scrollCredits.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.toop.app.layer.layers;
|
package org.toop.app.layer.layers;
|
||||||
|
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
import org.toop.app.layer.Layer;
|
import org.toop.app.layer.Layer;
|
||||||
@@ -7,45 +8,58 @@ import org.toop.app.layer.NodeBuilder;
|
|||||||
import org.toop.app.layer.containers.VerticalContainer;
|
import org.toop.app.layer.containers.VerticalContainer;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
|
|
||||||
public final class MainLayer extends Layer {
|
public final class MainLayer extends Layer {
|
||||||
public MainLayer() {
|
public MainLayer() {
|
||||||
super("bg-primary");
|
super("bg-primary");
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final var tictactoeButton = NodeBuilder.button(AppContext.getString("tictactoe"), () -> {
|
final var tictactoeButton =
|
||||||
App.activate(new MultiplayerLayer());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("tictactoe"),
|
||||||
|
() -> {
|
||||||
|
App.activate(new MultiplayerLayer());
|
||||||
|
});
|
||||||
|
|
||||||
final var othelloButton = NodeBuilder.button(AppContext.getString("othello"), () -> {
|
final var othelloButton =
|
||||||
App.activate(new MultiplayerLayer());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("othello"),
|
||||||
|
() -> {
|
||||||
|
App.activate(new MultiplayerLayer());
|
||||||
|
});
|
||||||
|
|
||||||
final var creditsButton = NodeBuilder.button(AppContext.getString("credits"), () -> {
|
final var creditsButton =
|
||||||
App.push(new CreditsPopup());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("credits"),
|
||||||
|
() -> {
|
||||||
|
App.push(new CreditsPopup());
|
||||||
|
});
|
||||||
|
|
||||||
final var optionsButton = NodeBuilder.button(AppContext.getString("options"), () -> {
|
final var optionsButton =
|
||||||
App.push(new OptionsPopup());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("options"),
|
||||||
|
() -> {
|
||||||
|
App.push(new OptionsPopup());
|
||||||
|
});
|
||||||
|
|
||||||
final var quitButton = NodeBuilder.button(AppContext.getString("quit"), () -> {
|
final var quitButton =
|
||||||
App.quitPopup();
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("quit"),
|
||||||
|
() -> {
|
||||||
|
App.quitPopup();
|
||||||
|
});
|
||||||
|
|
||||||
final Container gamesContainer = new VerticalContainer(5);
|
final Container gamesContainer = new VerticalContainer(5);
|
||||||
gamesContainer.addNodes(tictactoeButton, othelloButton);
|
gamesContainer.addNodes(tictactoeButton, othelloButton);
|
||||||
|
|
||||||
final Container controlContainer = new VerticalContainer(5);
|
final Container controlContainer = new VerticalContainer(5);
|
||||||
controlContainer.addNodes(creditsButton, optionsButton, quitButton);
|
controlContainer.addNodes(creditsButton, optionsButton, quitButton);
|
||||||
|
|
||||||
addContainer(gamesContainer, Pos.TOP_LEFT, 2, 2, 20, 0);
|
addContainer(gamesContainer, Pos.TOP_LEFT, 2, 2, 20, 0);
|
||||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 20, 0);
|
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 20, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.toop.app.layer.layers;
|
package org.toop.app.layer.layers;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.GameInformation;
|
import org.toop.app.GameInformation;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
@@ -10,167 +12,230 @@ import org.toop.app.layer.containers.VerticalContainer;
|
|||||||
import org.toop.app.layer.layers.game.TicTacToeLayer;
|
import org.toop.app.layer.layers.game.TicTacToeLayer;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
public final class MultiplayerLayer extends Layer {
|
public final class MultiplayerLayer extends Layer {
|
||||||
private boolean isConnectionLocal = true;
|
private boolean isConnectionLocal = true;
|
||||||
|
|
||||||
private boolean isPlayer1Human = true;
|
private boolean isPlayer1Human = true;
|
||||||
private String player1Name = "";
|
private String player1Name = "";
|
||||||
private int computer1Difficulty = 0;
|
private int computer1Difficulty = 0;
|
||||||
private int computer1ThinkTime = 0;
|
private int computer1ThinkTime = 0;
|
||||||
|
|
||||||
private boolean isPlayer2Human = true;
|
private boolean isPlayer2Human = true;
|
||||||
private String player2Name = "";
|
private String player2Name = "";
|
||||||
private int computer2Difficulty = 0;
|
private int computer2Difficulty = 0;
|
||||||
private int computer2ThinkTime = 0;
|
private int computer2ThinkTime = 0;
|
||||||
|
|
||||||
private String serverIP = "";
|
private String serverIP = "";
|
||||||
private String serverPort = "";
|
private String serverPort = "";
|
||||||
|
|
||||||
public MultiplayerLayer() {
|
public MultiplayerLayer() {
|
||||||
super("bg-primary");
|
super("bg-primary");
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final Container player1Container = new VerticalContainer(20);
|
final Container player1Container = new VerticalContainer(20);
|
||||||
final Container player2Container = new VerticalContainer(20);
|
final Container player2Container = new VerticalContainer(20);
|
||||||
|
|
||||||
final var isPlayer1HumanToggle = NodeBuilder.toggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer1Human, (computer) -> {
|
final var isPlayer1HumanToggle =
|
||||||
isPlayer1Human = !computer;
|
NodeBuilder.toggle(
|
||||||
reload();
|
AppContext.getString("human"),
|
||||||
});
|
AppContext.getString("computer"),
|
||||||
|
!isPlayer1Human,
|
||||||
|
(computer) -> {
|
||||||
|
isPlayer1Human = !computer;
|
||||||
|
reload();
|
||||||
|
});
|
||||||
|
|
||||||
player1Container.addNodes(isPlayer1HumanToggle);
|
player1Container.addNodes(isPlayer1HumanToggle);
|
||||||
|
|
||||||
if (isPlayer1Human) {
|
if (isPlayer1Human) {
|
||||||
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
|
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
|
||||||
final var playerNameInput = NodeBuilder.input(player1Name, (name) -> {
|
final var playerNameInput =
|
||||||
player1Name = name;
|
NodeBuilder.input(
|
||||||
});
|
player1Name,
|
||||||
|
(name) -> {
|
||||||
|
player1Name = name;
|
||||||
|
});
|
||||||
|
|
||||||
player1Container.addNodes(playerNameText, playerNameInput);
|
player1Container.addNodes(playerNameText, playerNameInput);
|
||||||
} else {
|
} else {
|
||||||
player1Name = "Pism Bot V" + LocalDateTime.now().getSecond();
|
player1Name = "Pism Bot V" + LocalDateTime.now().getSecond();
|
||||||
|
|
||||||
final var computerNameText = NodeBuilder.text(player1Name);
|
final var computerNameText = NodeBuilder.text(player1Name);
|
||||||
final var computerNameSeparator = NodeBuilder.separator();
|
final var computerNameSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var computerDifficultyText = NodeBuilder.text(AppContext.getString("computerDifficulty"));
|
final var computerDifficultyText =
|
||||||
final var computerDifficultySeparator = NodeBuilder.separator();
|
NodeBuilder.text(AppContext.getString("computerDifficulty"));
|
||||||
final var computerDifficultySlider = NodeBuilder.slider(10, computer1Difficulty, (difficulty) ->
|
final var computerDifficultySeparator = NodeBuilder.separator();
|
||||||
computer1Difficulty = difficulty);
|
final var computerDifficultySlider =
|
||||||
|
NodeBuilder.slider(
|
||||||
|
10,
|
||||||
|
computer1Difficulty,
|
||||||
|
(difficulty) -> computer1Difficulty = difficulty);
|
||||||
|
|
||||||
final var computerThinkTimeText = NodeBuilder.text(AppContext.getString("computerThinkTime"));
|
final var computerThinkTimeText =
|
||||||
final var computerThinkTimeSlider = NodeBuilder.slider(5, computer1ThinkTime, (thinkTime) ->
|
NodeBuilder.text(AppContext.getString("computerThinkTime"));
|
||||||
computer1ThinkTime = thinkTime);
|
final var computerThinkTimeSlider =
|
||||||
|
NodeBuilder.slider(
|
||||||
|
5, computer1ThinkTime, (thinkTime) -> computer1ThinkTime = thinkTime);
|
||||||
|
|
||||||
player1Container.addNodes(computerNameText, computerNameSeparator,
|
player1Container.addNodes(
|
||||||
computerDifficultyText, computerDifficultySlider, computerDifficultySeparator,
|
computerNameText,
|
||||||
computerThinkTimeText, computerThinkTimeSlider);
|
computerNameSeparator,
|
||||||
}
|
computerDifficultyText,
|
||||||
|
computerDifficultySlider,
|
||||||
|
computerDifficultySeparator,
|
||||||
|
computerThinkTimeText,
|
||||||
|
computerThinkTimeSlider);
|
||||||
|
}
|
||||||
|
|
||||||
if (isConnectionLocal) {
|
if (isConnectionLocal) {
|
||||||
final var isPlayer2HumanToggle = NodeBuilder.toggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer2Human, (computer) -> {
|
final var isPlayer2HumanToggle =
|
||||||
isPlayer2Human = !computer;
|
NodeBuilder.toggle(
|
||||||
reload();
|
AppContext.getString("human"),
|
||||||
});
|
AppContext.getString("computer"),
|
||||||
|
!isPlayer2Human,
|
||||||
|
(computer) -> {
|
||||||
|
isPlayer2Human = !computer;
|
||||||
|
reload();
|
||||||
|
});
|
||||||
|
|
||||||
player2Container.addNodes(isPlayer2HumanToggle);
|
player2Container.addNodes(isPlayer2HumanToggle);
|
||||||
|
|
||||||
if (isPlayer2Human) {
|
if (isPlayer2Human) {
|
||||||
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
|
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
|
||||||
final var playerNameInput = NodeBuilder.input(player2Name, (name) -> {
|
final var playerNameInput =
|
||||||
player2Name = name;
|
NodeBuilder.input(
|
||||||
});
|
player2Name,
|
||||||
|
(name) -> {
|
||||||
|
player2Name = name;
|
||||||
|
});
|
||||||
|
|
||||||
player2Container.addNodes(playerNameText, playerNameInput);
|
player2Container.addNodes(playerNameText, playerNameInput);
|
||||||
} else {
|
} else {
|
||||||
player2Name = "Pism Bot V" + LocalDateTime.now().getSecond();
|
player2Name = "Pism Bot V" + LocalDateTime.now().getSecond();
|
||||||
|
|
||||||
final var computerNameText = NodeBuilder.text(player2Name);
|
final var computerNameText = NodeBuilder.text(player2Name);
|
||||||
final var computerNameSeparator = NodeBuilder.separator();
|
final var computerNameSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var computerDifficultyText = NodeBuilder.text(AppContext.getString("computerDifficulty"));
|
final var computerDifficultyText =
|
||||||
final var computerDifficultySeparator = NodeBuilder.separator();
|
NodeBuilder.text(AppContext.getString("computerDifficulty"));
|
||||||
final var computerDifficultySlider = NodeBuilder.slider(10, computer2Difficulty, (difficulty) ->
|
final var computerDifficultySeparator = NodeBuilder.separator();
|
||||||
computer2Difficulty = difficulty);
|
final var computerDifficultySlider =
|
||||||
|
NodeBuilder.slider(
|
||||||
|
10,
|
||||||
|
computer2Difficulty,
|
||||||
|
(difficulty) -> computer2Difficulty = difficulty);
|
||||||
|
|
||||||
final var computerThinkTimeText = NodeBuilder.text(AppContext.getString("computerThinkTime"));
|
final var computerThinkTimeText =
|
||||||
final var computerThinkTimeSlider = NodeBuilder.slider(5, computer2ThinkTime, (thinkTime) ->
|
NodeBuilder.text(AppContext.getString("computerThinkTime"));
|
||||||
computer2ThinkTime = thinkTime);
|
final var computerThinkTimeSlider =
|
||||||
|
NodeBuilder.slider(
|
||||||
|
5,
|
||||||
|
computer2ThinkTime,
|
||||||
|
(thinkTime) -> computer2ThinkTime = thinkTime);
|
||||||
|
|
||||||
player2Container.addNodes(computerNameText, computerNameSeparator,
|
player2Container.addNodes(
|
||||||
computerDifficultyText, computerDifficultySlider, computerDifficultySeparator,
|
computerNameText,
|
||||||
computerThinkTimeText, computerThinkTimeSlider);
|
computerNameSeparator,
|
||||||
}
|
computerDifficultyText,
|
||||||
} else {
|
computerDifficultySlider,
|
||||||
final var serverIPText = NodeBuilder.text(AppContext.getString("serverIP"));
|
computerDifficultySeparator,
|
||||||
final var serverIPSeparator = NodeBuilder.separator();
|
computerThinkTimeText,
|
||||||
final var serverIPInput = NodeBuilder.input(serverIP, (ip) -> {
|
computerThinkTimeSlider);
|
||||||
serverIP = ip;
|
}
|
||||||
});
|
} else {
|
||||||
|
final var serverIPText = NodeBuilder.text(AppContext.getString("serverIP"));
|
||||||
|
final var serverIPSeparator = NodeBuilder.separator();
|
||||||
|
final var serverIPInput =
|
||||||
|
NodeBuilder.input(
|
||||||
|
serverIP,
|
||||||
|
(ip) -> {
|
||||||
|
serverIP = ip;
|
||||||
|
});
|
||||||
|
|
||||||
final var serverPortText = NodeBuilder.text(AppContext.getString("serverPort"));
|
final var serverPortText = NodeBuilder.text(AppContext.getString("serverPort"));
|
||||||
final var serverPortInput = NodeBuilder.input(serverPort, (port) -> {
|
final var serverPortInput =
|
||||||
serverPort = port;
|
NodeBuilder.input(
|
||||||
});
|
serverPort,
|
||||||
|
(port) -> {
|
||||||
|
serverPort = port;
|
||||||
|
});
|
||||||
|
|
||||||
player2Container.addNodes(serverIPText, serverIPInput, serverIPSeparator,
|
player2Container.addNodes(
|
||||||
serverPortText, serverPortInput);
|
serverIPText,
|
||||||
}
|
serverIPInput,
|
||||||
|
serverIPSeparator,
|
||||||
|
serverPortText,
|
||||||
|
serverPortInput);
|
||||||
|
}
|
||||||
|
|
||||||
final var versusText = NodeBuilder.header("VS");
|
final var versusText = NodeBuilder.header("VS");
|
||||||
|
|
||||||
final var connectionTypeText = NodeBuilder.text(AppContext.getString("connectionType") + ":");
|
final var connectionTypeText =
|
||||||
final var connectionTypeToggle = NodeBuilder.toggle(AppContext.getString("local"), AppContext.getString("server"), !isConnectionLocal, (server) -> {
|
NodeBuilder.text(AppContext.getString("connectionType") + ":");
|
||||||
isConnectionLocal = !server;
|
final var connectionTypeToggle =
|
||||||
reload();
|
NodeBuilder.toggle(
|
||||||
});
|
AppContext.getString("local"),
|
||||||
|
AppContext.getString("server"),
|
||||||
|
!isConnectionLocal,
|
||||||
|
(server) -> {
|
||||||
|
isConnectionLocal = !server;
|
||||||
|
reload();
|
||||||
|
});
|
||||||
|
|
||||||
final var playButton = NodeBuilder.button(isConnectionLocal ? AppContext.getString("start") : AppContext.getString("connect"), () -> {
|
final var playButton =
|
||||||
final var information = new GameInformation(
|
NodeBuilder.button(
|
||||||
new String[]{player1Name, player2Name},
|
isConnectionLocal
|
||||||
new boolean[]{isPlayer1Human, isPlayer2Human},
|
? AppContext.getString("start")
|
||||||
new int[]{computer1Difficulty, computer2Difficulty},
|
: AppContext.getString("connect"),
|
||||||
new int[]{computer1ThinkTime, computer2ThinkTime},
|
() -> {
|
||||||
isConnectionLocal, serverIP, serverPort);
|
final var information =
|
||||||
|
new GameInformation(
|
||||||
|
new String[] {player1Name, player2Name},
|
||||||
|
new boolean[] {isPlayer1Human, isPlayer2Human},
|
||||||
|
new int[] {computer1Difficulty, computer2Difficulty},
|
||||||
|
new int[] {computer1ThinkTime, computer2ThinkTime},
|
||||||
|
isConnectionLocal,
|
||||||
|
serverIP,
|
||||||
|
serverPort);
|
||||||
|
|
||||||
if (isConnectionLocal) {
|
if (isConnectionLocal) {
|
||||||
App.activate(new TicTacToeLayer(information));
|
App.activate(new TicTacToeLayer(information));
|
||||||
} else {
|
} else {
|
||||||
App.activate(new ConnectedLayer(information));
|
App.activate(new ConnectedLayer(information));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final Container mainContainer = new VerticalContainer(10);
|
final Container mainContainer = new VerticalContainer(10);
|
||||||
final Container playersContainer = new HorizontalContainer(5);
|
final Container playersContainer = new HorizontalContainer(5);
|
||||||
final Container connectionTypeContainer = new HorizontalContainer(10);
|
final Container connectionTypeContainer = new HorizontalContainer(10);
|
||||||
|
|
||||||
mainContainer.addContainer(playersContainer, true);
|
mainContainer.addContainer(playersContainer, true);
|
||||||
mainContainer.addContainer(connectionTypeContainer, false);
|
mainContainer.addContainer(connectionTypeContainer, false);
|
||||||
mainContainer.addNodes(playButton);
|
mainContainer.addNodes(playButton);
|
||||||
|
|
||||||
connectionTypeContainer.addNodes(connectionTypeText, connectionTypeToggle);
|
connectionTypeContainer.addNodes(connectionTypeText, connectionTypeToggle);
|
||||||
|
|
||||||
playersContainer.addContainer(player1Container, true);
|
playersContainer.addContainer(player1Container, true);
|
||||||
playersContainer.addNodes(versusText);
|
playersContainer.addNodes(versusText);
|
||||||
playersContainer.addContainer(player2Container, true);
|
playersContainer.addContainer(player2Container, true);
|
||||||
|
|
||||||
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
|
final var backButton =
|
||||||
App.activate(new MainLayer());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("back"),
|
||||||
|
() -> {
|
||||||
|
App.activate(new MainLayer());
|
||||||
|
});
|
||||||
|
|
||||||
final Container controlContainer = new VerticalContainer(0);
|
final Container controlContainer = new VerticalContainer(0);
|
||||||
controlContainer.addNodes(backButton);
|
controlContainer.addNodes(backButton);
|
||||||
|
|
||||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 75, 75);
|
addContainer(mainContainer, Pos.CENTER, 0, 0, 75, 75);
|
||||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
package org.toop.app.layer.layers;
|
package org.toop.app.layer.layers;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.ChoiceBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Slider;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
import org.toop.app.layer.NodeBuilder;
|
import org.toop.app.layer.NodeBuilder;
|
||||||
@@ -11,32 +16,25 @@ import org.toop.framework.eventbus.EventFlow;
|
|||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
import org.toop.local.AppSettings;
|
import org.toop.local.AppSettings;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.ChoiceBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.Slider;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public final class OptionsPopup extends Popup {
|
public final class OptionsPopup extends Popup {
|
||||||
AppSettings appSettings = new AppSettings();
|
AppSettings appSettings = new AppSettings();
|
||||||
SettingsAsset settings = appSettings.getPath();
|
SettingsAsset settings = appSettings.getPath();
|
||||||
private boolean isWindowed = !(settings.getFullscreen());
|
private boolean isWindowed = !(settings.getFullscreen());
|
||||||
|
|
||||||
public OptionsPopup() {
|
public OptionsPopup() {
|
||||||
super(true, "bg-primary");
|
super(true, "bg-primary");
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final var languageHeader = NodeBuilder.header(AppContext.getString("language"));
|
final var languageHeader = NodeBuilder.header(AppContext.getString("language"));
|
||||||
final var languageSeparator = NodeBuilder.separator();
|
final var languageSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var volumeHeader = NodeBuilder.header(AppContext.getString("volume"));
|
final var volumeHeader = NodeBuilder.header(AppContext.getString("volume"));
|
||||||
final var volumeSeparator = NodeBuilder.separator();
|
final var volumeSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var fxVolumeHeader = NodeBuilder.header(AppContext.getString("effectsVolume"));
|
final var fxVolumeHeader = NodeBuilder.header(AppContext.getString("effectsVolume"));
|
||||||
final var fxVolumeSeparator = NodeBuilder.separator();
|
final var fxVolumeSeparator = NodeBuilder.separator();
|
||||||
@@ -44,151 +42,181 @@ public final class OptionsPopup extends Popup {
|
|||||||
final var musicVolumeHeader = NodeBuilder.header(AppContext.getString("musicVolume"));
|
final var musicVolumeHeader = NodeBuilder.header(AppContext.getString("musicVolume"));
|
||||||
final var musicVolumeSeparator = NodeBuilder.separator();
|
final var musicVolumeSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var themeHeader = NodeBuilder.header(AppContext.getString("theme"));
|
final var themeHeader = NodeBuilder.header(AppContext.getString("theme"));
|
||||||
final var themeSeparator = NodeBuilder.separator();
|
final var themeSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var layoutSizeHeader = NodeBuilder.header(AppContext.getString("layoutSize"));
|
final var layoutSizeHeader = NodeBuilder.header(AppContext.getString("layoutSize"));
|
||||||
final var layoutSizeSeparator = NodeBuilder.separator();
|
final var layoutSizeSeparator = NodeBuilder.separator();
|
||||||
|
|
||||||
final var optionsContainer = new VerticalContainer(5);
|
final var optionsContainer = new VerticalContainer(5);
|
||||||
optionsContainer.addNodes(languageHeader, languageChoiceBox(), languageSeparator);
|
optionsContainer.addNodes(languageHeader, languageChoiceBox(), languageSeparator);
|
||||||
optionsContainer.addNodes(volumeHeader, volumeSlider(), volumeSeparator);
|
optionsContainer.addNodes(volumeHeader, volumeSlider(), volumeSeparator);
|
||||||
optionsContainer.addNodes(fxVolumeHeader, fxVolumeSlider(), fxVolumeSeparator);
|
optionsContainer.addNodes(fxVolumeHeader, fxVolumeSlider(), fxVolumeSeparator);
|
||||||
optionsContainer.addNodes(musicVolumeHeader, musicVolumeSlider(), musicVolumeSeparator);
|
optionsContainer.addNodes(musicVolumeHeader, musicVolumeSlider(), musicVolumeSeparator);
|
||||||
optionsContainer.addNodes(themeHeader, themeChoiceBox(), themeSeparator);
|
optionsContainer.addNodes(themeHeader, themeChoiceBox(), themeSeparator);
|
||||||
optionsContainer.addNodes(layoutSizeHeader, layoutSizeChoiceBox(), layoutSizeSeparator);
|
optionsContainer.addNodes(layoutSizeHeader, layoutSizeChoiceBox(), layoutSizeSeparator);
|
||||||
optionsContainer.addNodes(fullscreenToggle());
|
optionsContainer.addNodes(fullscreenToggle());
|
||||||
|
|
||||||
final Container mainContainer = new VerticalContainer(50, "");
|
final Container mainContainer = new VerticalContainer(50, "");
|
||||||
mainContainer.addContainer(optionsContainer, true);
|
mainContainer.addContainer(optionsContainer, true);
|
||||||
|
|
||||||
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
|
final var backButton =
|
||||||
App.pop();
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("back"),
|
||||||
|
() -> {
|
||||||
|
App.pop();
|
||||||
|
});
|
||||||
|
|
||||||
final Container controlContainer = new VerticalContainer(5);
|
final Container controlContainer = new VerticalContainer(5);
|
||||||
controlContainer.addNodes(backButton);
|
controlContainer.addNodes(backButton);
|
||||||
|
|
||||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 0, 0);
|
addContainer(mainContainer, Pos.CENTER, 0, 0, 0, 0);
|
||||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChoiceBox<Locale> languageChoiceBox() {
|
private ChoiceBox<Locale> languageChoiceBox() {
|
||||||
assert AppContext.getLocalization() != null;
|
assert AppContext.getLocalization() != null;
|
||||||
|
|
||||||
final ChoiceBox<Locale> languageChoiceBox = NodeBuilder.choiceBox((locale) -> {
|
final ChoiceBox<Locale> languageChoiceBox =
|
||||||
if (locale == AppContext.getLocale()) {
|
NodeBuilder.choiceBox(
|
||||||
return;
|
(locale) -> {
|
||||||
}
|
if (locale == AppContext.getLocale()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
settings.setLocale(locale.toString());
|
settings.setLocale(locale.toString());
|
||||||
AppContext.setLocale(locale);
|
AppContext.setLocale(locale);
|
||||||
|
|
||||||
App.reloadAll();
|
App.reloadAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
languageChoiceBox.setConverter(new javafx.util.StringConverter<>() {
|
languageChoiceBox.setConverter(
|
||||||
@Override
|
new javafx.util.StringConverter<>() {
|
||||||
public String toString(Locale locale) {
|
@Override
|
||||||
return AppContext.getString(locale.getDisplayName().toLowerCase());
|
public String toString(Locale locale) {
|
||||||
}
|
return AppContext.getString(locale.getDisplayName().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Locale fromString(String string) {
|
public Locale fromString(String string) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
languageChoiceBox.getItems().addAll(AppContext.getLocalization().getAvailableLocales());
|
languageChoiceBox.getItems().addAll(AppContext.getLocalization().getAvailableLocales());
|
||||||
languageChoiceBox.setValue(AppContext.getLocale());
|
languageChoiceBox.setValue(AppContext.getLocale());
|
||||||
|
|
||||||
return languageChoiceBox;
|
return languageChoiceBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Slider volumeSlider() {
|
private Slider volumeSlider() {
|
||||||
return NodeBuilder.slider(100, settings.getVolume(), (volume) -> {
|
return NodeBuilder.slider(
|
||||||
settings.setVolume(volume);
|
100,
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(volume.doubleValue())).asyncPostEvent();
|
settings.getVolume(),
|
||||||
});
|
(volume) -> {
|
||||||
}
|
settings.setVolume(volume);
|
||||||
|
new EventFlow()
|
||||||
|
.addPostEvent(new AudioEvents.ChangeVolume(volume.doubleValue()))
|
||||||
|
.asyncPostEvent();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Slider fxVolumeSlider() {
|
private Slider fxVolumeSlider() {
|
||||||
return NodeBuilder.slider(100, settings.getFxVolume(), (volume) -> {
|
return NodeBuilder.slider(
|
||||||
settings.setFxVolume(volume);
|
100,
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeFxVolume(volume.doubleValue())).asyncPostEvent();
|
settings.getFxVolume(),
|
||||||
});
|
(volume) -> {
|
||||||
|
settings.setFxVolume(volume);
|
||||||
|
new EventFlow()
|
||||||
|
.addPostEvent(new AudioEvents.ChangeFxVolume(volume.doubleValue()))
|
||||||
|
.asyncPostEvent();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Slider musicVolumeSlider() {
|
private Slider musicVolumeSlider() {
|
||||||
return NodeBuilder.slider(100, settings.getMusicVolume(), (volume) -> {
|
return NodeBuilder.slider(
|
||||||
settings.setMusicVolume(volume);
|
100,
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeMusicVolume(volume.doubleValue())).asyncPostEvent();
|
settings.getMusicVolume(),
|
||||||
});
|
(volume) -> {
|
||||||
|
settings.setMusicVolume(volume);
|
||||||
|
new EventFlow()
|
||||||
|
.addPostEvent(new AudioEvents.ChangeMusicVolume(volume.doubleValue()))
|
||||||
|
.asyncPostEvent();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Label fullscreenToggle() {
|
||||||
|
return NodeBuilder.toggle(
|
||||||
|
AppContext.getString("windowed"),
|
||||||
|
AppContext.getString("fullscreen"),
|
||||||
|
!isWindowed,
|
||||||
|
(fullscreen) -> {
|
||||||
|
isWindowed = !fullscreen;
|
||||||
|
|
||||||
private Label fullscreenToggle() {
|
settings.setFullscreen(fullscreen);
|
||||||
return NodeBuilder.toggle(AppContext.getString("windowed"), AppContext.getString("fullscreen"), !isWindowed, (fullscreen) -> {
|
App.setFullscreen(fullscreen);
|
||||||
isWindowed = !fullscreen;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
settings.setFullscreen(fullscreen);
|
private ChoiceBox<String> themeChoiceBox() {
|
||||||
App.setFullscreen(fullscreen);
|
final ChoiceBox<String> themeChoiceBox =
|
||||||
});
|
NodeBuilder.choiceBox(
|
||||||
}
|
(theme) -> {
|
||||||
|
if (theme.equalsIgnoreCase(settings.getTheme())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private ChoiceBox<String> themeChoiceBox() {
|
settings.setTheme(theme);
|
||||||
final ChoiceBox<String> themeChoiceBox = NodeBuilder.choiceBox((theme) -> {
|
App.setStyle(theme, settings.getLayoutSize());
|
||||||
if (theme.equalsIgnoreCase(settings.getTheme())) {
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.setTheme(theme);
|
themeChoiceBox.setConverter(
|
||||||
App.setStyle(theme, settings.getLayoutSize());
|
new javafx.util.StringConverter<>() {
|
||||||
});
|
@Override
|
||||||
|
public String toString(String theme) {
|
||||||
|
return AppContext.getString(theme);
|
||||||
|
}
|
||||||
|
|
||||||
themeChoiceBox.setConverter(new javafx.util.StringConverter<>() {
|
@Override
|
||||||
@Override
|
public String fromString(String string) {
|
||||||
public String toString(String theme) {
|
return null;
|
||||||
return AppContext.getString(theme);
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
@Override
|
themeChoiceBox.getItems().addAll("dark", "light", "dark-hc", "light-hc");
|
||||||
public String fromString(String string) {
|
themeChoiceBox.setValue(settings.getTheme());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
themeChoiceBox.getItems().addAll("dark", "light", "dark-hc", "light-hc");
|
return themeChoiceBox;
|
||||||
themeChoiceBox.setValue(settings.getTheme());
|
}
|
||||||
|
|
||||||
return themeChoiceBox;
|
private ChoiceBox<String> layoutSizeChoiceBox() {
|
||||||
}
|
final ChoiceBox<String> layoutSizeChoiceBox =
|
||||||
|
NodeBuilder.choiceBox(
|
||||||
|
(layoutSize) -> {
|
||||||
|
if (layoutSize.equalsIgnoreCase(settings.getLayoutSize())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private ChoiceBox<String> layoutSizeChoiceBox() {
|
settings.setLayoutSize(layoutSize);
|
||||||
final ChoiceBox<String> layoutSizeChoiceBox = NodeBuilder.choiceBox((layoutSize) -> {
|
App.setStyle(settings.getTheme(), layoutSize);
|
||||||
if (layoutSize.equalsIgnoreCase(settings.getLayoutSize())) {
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.setLayoutSize(layoutSize);
|
layoutSizeChoiceBox.setConverter(
|
||||||
App.setStyle(settings.getTheme(), layoutSize);
|
new javafx.util.StringConverter<>() {
|
||||||
});
|
@Override
|
||||||
|
public String toString(String layoutSize) {
|
||||||
|
return AppContext.getString(layoutSize);
|
||||||
|
}
|
||||||
|
|
||||||
layoutSizeChoiceBox.setConverter(new javafx.util.StringConverter<>() {
|
@Override
|
||||||
@Override
|
public String fromString(String string) {
|
||||||
public String toString(String layoutSize) {
|
return null;
|
||||||
return AppContext.getString(layoutSize);
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
@Override
|
layoutSizeChoiceBox.getItems().addAll("small", "medium", "large");
|
||||||
public String fromString(String string) {
|
layoutSizeChoiceBox.setValue(settings.getLayoutSize());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
layoutSizeChoiceBox.getItems().addAll("small", "medium", "large");
|
return layoutSizeChoiceBox;
|
||||||
layoutSizeChoiceBox.setValue(settings.getLayoutSize());
|
}
|
||||||
|
|
||||||
return layoutSizeChoiceBox;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.toop.app.layer.layers;
|
package org.toop.app.layer.layers;
|
||||||
|
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
import org.toop.app.layer.NodeBuilder;
|
import org.toop.app.layer.NodeBuilder;
|
||||||
@@ -8,35 +9,39 @@ import org.toop.app.layer.containers.HorizontalContainer;
|
|||||||
import org.toop.app.layer.containers.VerticalContainer;
|
import org.toop.app.layer.containers.VerticalContainer;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
|
|
||||||
public final class QuitPopup extends Popup {
|
public final class QuitPopup extends Popup {
|
||||||
public QuitPopup() {
|
public QuitPopup() {
|
||||||
super(true);
|
super(true);
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final var sureText = NodeBuilder.header(AppContext.getString("quitSure"));
|
final var sureText = NodeBuilder.header(AppContext.getString("quitSure"));
|
||||||
|
|
||||||
final var yesButton = NodeBuilder.button(AppContext.getString("yes"), () -> {
|
final var yesButton =
|
||||||
App.quit();
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("yes"),
|
||||||
|
() -> {
|
||||||
|
App.quit();
|
||||||
|
});
|
||||||
|
|
||||||
final var noButton = NodeBuilder.button(AppContext.getString("no"), () -> {
|
final var noButton =
|
||||||
App.pop();
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("no"),
|
||||||
|
() -> {
|
||||||
|
App.pop();
|
||||||
|
});
|
||||||
|
|
||||||
final Container controlContainer = new HorizontalContainer(30);
|
final Container controlContainer = new HorizontalContainer(30);
|
||||||
controlContainer.addNodes(yesButton, noButton);
|
controlContainer.addNodes(yesButton, noButton);
|
||||||
|
|
||||||
final Container mainContainer = new VerticalContainer(30);
|
final Container mainContainer = new VerticalContainer(30);
|
||||||
mainContainer.addNodes(sureText);
|
mainContainer.addNodes(sureText);
|
||||||
mainContainer.addContainer(controlContainer, false);
|
mainContainer.addContainer(controlContainer, false);
|
||||||
|
|
||||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.toop.app.layer.layers.game;
|
package org.toop.app.layer.layers.game;
|
||||||
|
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.layer.Container;
|
import org.toop.app.layer.Container;
|
||||||
import org.toop.app.layer.NodeBuilder;
|
import org.toop.app.layer.NodeBuilder;
|
||||||
@@ -8,45 +9,47 @@ import org.toop.app.layer.containers.VerticalContainer;
|
|||||||
import org.toop.app.layer.layers.MainLayer;
|
import org.toop.app.layer.layers.MainLayer;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
|
|
||||||
public class GameFinishedPopup extends Popup {
|
public class GameFinishedPopup extends Popup {
|
||||||
private final boolean isDraw;
|
private final boolean isDraw;
|
||||||
private final String winner;
|
private final String winner;
|
||||||
|
|
||||||
public GameFinishedPopup(boolean isDraw, String winner) {
|
public GameFinishedPopup(boolean isDraw, String winner) {
|
||||||
super(true, "bg-popup");
|
super(true, "bg-popup");
|
||||||
|
|
||||||
this.isDraw = isDraw;
|
this.isDraw = isDraw;
|
||||||
this.winner = winner;
|
this.winner = winner;
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
final Container mainContainer = new VerticalContainer(30);
|
final Container mainContainer = new VerticalContainer(30);
|
||||||
|
|
||||||
if (isDraw) {
|
if (isDraw) {
|
||||||
final var drawHeader = NodeBuilder.header(AppContext.getString("drawText"));
|
final var drawHeader = NodeBuilder.header(AppContext.getString("drawText"));
|
||||||
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
|
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
|
||||||
|
|
||||||
mainContainer.addNodes(drawHeader, goodGameText);
|
mainContainer.addNodes(drawHeader, goodGameText);
|
||||||
} else {
|
} else {
|
||||||
final var winHeader = NodeBuilder.header(AppContext.getString("congratulations") + ": " + winner);
|
final var winHeader =
|
||||||
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
|
NodeBuilder.header(AppContext.getString("congratulations") + ": " + winner);
|
||||||
|
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
|
||||||
|
|
||||||
mainContainer.addNodes(winHeader, goodGameText);
|
mainContainer.addNodes(winHeader, goodGameText);
|
||||||
}
|
}
|
||||||
|
|
||||||
final var backToMainMenuButton = NodeBuilder.button(AppContext.getString("backToMainMenu"), () -> {
|
final var backToMainMenuButton =
|
||||||
App.activate(new MainLayer());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("backToMainMenu"),
|
||||||
|
() -> {
|
||||||
|
App.activate(new MainLayer());
|
||||||
|
});
|
||||||
|
|
||||||
mainContainer.addNodes(backToMainMenuButton);
|
mainContainer.addNodes(backToMainMenuButton);
|
||||||
|
|
||||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
package org.toop.app.layer.layers.game;
|
package org.toop.app.layer.layers.game;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.GameInformation;
|
import org.toop.app.GameInformation;
|
||||||
@@ -17,280 +23,307 @@ import org.toop.game.tictactoe.TicTacToe;
|
|||||||
import org.toop.game.tictactoe.TicTacToeAI;
|
import org.toop.game.tictactoe.TicTacToeAI;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public final class TicTacToeLayer extends Layer {
|
public final class TicTacToeLayer extends Layer {
|
||||||
private TicTacToeCanvas canvas;
|
private TicTacToeCanvas canvas;
|
||||||
|
|
||||||
private AtomicReference<TicTacToe> ticTacToe;
|
private AtomicReference<TicTacToe> ticTacToe;
|
||||||
private TicTacToeAI ticTacToeAI;
|
private TicTacToeAI ticTacToeAI;
|
||||||
|
|
||||||
private GameInformation information;
|
private GameInformation information;
|
||||||
|
|
||||||
private final Text currentPlayerNameText;
|
private final Text currentPlayerNameText;
|
||||||
private final Text currentPlayerMoveText;
|
private final Text currentPlayerMoveText;
|
||||||
|
|
||||||
private final BlockingQueue<Game.Move> playerMoveQueue = new LinkedBlockingQueue<>();
|
private final BlockingQueue<Game.Move> playerMoveQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
// Todo: set these from the server
|
// Todo: set these from the server
|
||||||
private char currentPlayerMove = Game.EMPTY;
|
private char currentPlayerMove = Game.EMPTY;
|
||||||
private String player2Name = "";
|
private String player2Name = "";
|
||||||
|
|
||||||
final AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true);
|
final AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true);
|
||||||
|
|
||||||
public TicTacToeLayer(GameInformation information) {
|
public TicTacToeLayer(GameInformation information) {
|
||||||
super("bg-primary");
|
super("bg-primary");
|
||||||
|
|
||||||
canvas = new TicTacToeCanvas(Color.LIME, (App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> {
|
canvas =
|
||||||
try {
|
new TicTacToeCanvas(
|
||||||
if (information.isConnectionLocal()) {
|
Color.LIME,
|
||||||
if (ticTacToe.get().getCurrentTurn() == 0) {
|
(App.getHeight() / 100) * 75,
|
||||||
playerMoveQueue.put(new Game.Move(cell, 'X'));
|
(App.getHeight() / 100) * 75,
|
||||||
} else {
|
(cell) -> {
|
||||||
playerMoveQueue.put(new Game.Move(cell, 'O'));
|
try {
|
||||||
}
|
if (information.isConnectionLocal()) {
|
||||||
} else {
|
if (ticTacToe.get().getCurrentTurn() == 0) {
|
||||||
if (information.isPlayerHuman()[0] && currentPlayerMove != Game.EMPTY) {
|
playerMoveQueue.put(new Game.Move(cell, 'X'));
|
||||||
playerMoveQueue.put(new Game.Move(cell, firstPlayerIsMe.get()? 'X' : 'O'));
|
} else {
|
||||||
}
|
playerMoveQueue.put(new Game.Move(cell, 'O'));
|
||||||
}
|
}
|
||||||
} catch (InterruptedException _) {}
|
} else {
|
||||||
});
|
if (information.isPlayerHuman()[0]
|
||||||
|
&& currentPlayerMove != Game.EMPTY) {
|
||||||
|
playerMoveQueue.put(
|
||||||
|
new Game.Move(
|
||||||
|
cell, firstPlayerIsMe.get() ? 'X' : 'O'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException _) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ticTacToe = new AtomicReference<>(new TicTacToe());
|
ticTacToe = new AtomicReference<>(new TicTacToe());
|
||||||
ticTacToeAI = new TicTacToeAI();
|
ticTacToeAI = new TicTacToeAI();
|
||||||
|
|
||||||
this.information = information;
|
this.information = information;
|
||||||
|
|
||||||
if (information.isConnectionLocal()) {
|
if (information.isConnectionLocal()) {
|
||||||
new Thread(this::localGameThread).start();
|
new Thread(this::localGameThread).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPlayerNameText = NodeBuilder.header("");
|
currentPlayerNameText = NodeBuilder.header("");
|
||||||
currentPlayerMoveText = NodeBuilder.header("");
|
currentPlayerMoveText = NodeBuilder.header("");
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TicTacToeLayer(GameInformation information, long clientID) {
|
public TicTacToeLayer(GameInformation information, long clientID) {
|
||||||
this(information);
|
this(information);
|
||||||
|
|
||||||
Thread a = new Thread(this::serverGameThread);
|
Thread a = new Thread(this::serverGameThread);
|
||||||
a.setDaemon(false);
|
a.setDaemon(false);
|
||||||
a.start();
|
a.start();
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
popAll();
|
popAll();
|
||||||
|
|
||||||
canvas.resize((App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75);
|
canvas.resize((App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75);
|
||||||
|
|
||||||
for (int i = 0; i < ticTacToe.get().board.length; i++) {
|
for (int i = 0; i < ticTacToe.get().board.length; i++) {
|
||||||
final char value = ticTacToe.get().board[i];
|
final char value = ticTacToe.get().board[i];
|
||||||
|
|
||||||
if (value == 'X') {
|
if (value == 'X') {
|
||||||
canvas.drawX(Color.RED, i);
|
canvas.drawX(Color.RED, i);
|
||||||
} else if (value == 'O') {
|
} else if (value == 'O') {
|
||||||
canvas.drawO(Color.BLUE, i);
|
canvas.drawO(Color.BLUE, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
|
final var backButton =
|
||||||
App.activate(new MainLayer());
|
NodeBuilder.button(
|
||||||
});
|
AppContext.getString("back"),
|
||||||
|
() -> {
|
||||||
|
App.activate(new MainLayer());
|
||||||
|
});
|
||||||
|
|
||||||
final Container controlContainer = new VerticalContainer(5);
|
final Container controlContainer = new VerticalContainer(5);
|
||||||
controlContainer.addNodes(backButton);
|
controlContainer.addNodes(backButton);
|
||||||
|
|
||||||
final Container informationContainer = new HorizontalContainer(15);
|
final Container informationContainer = new HorizontalContainer(15);
|
||||||
informationContainer.addNodes(currentPlayerNameText, currentPlayerMoveText);
|
informationContainer.addNodes(currentPlayerNameText, currentPlayerMoveText);
|
||||||
|
|
||||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
||||||
addContainer(informationContainer, Pos.TOP_LEFT, 2, 2, 0, 0);
|
addContainer(informationContainer, Pos.TOP_LEFT, 2, 2, 0, 0);
|
||||||
addGameCanvas(canvas, Pos.CENTER, 0, 0);
|
addGameCanvas(canvas, Pos.CENTER, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int compurterDifficultyToDepth(int maxDifficulty, int difficulty) {
|
private int compurterDifficultyToDepth(int maxDifficulty, int difficulty) {
|
||||||
return (int) (((float) maxDifficulty / difficulty) * 9);
|
return (int) (((float) maxDifficulty / difficulty) * 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void localGameThread() {
|
private void localGameThread() {
|
||||||
boolean running = true;
|
boolean running = true;
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
final int currentPlayer = ticTacToe.get().getCurrentTurn();
|
final int currentPlayer = ticTacToe.get().getCurrentTurn();
|
||||||
|
|
||||||
currentPlayerNameText.setText(information.playerName()[currentPlayer]);
|
currentPlayerNameText.setText(information.playerName()[currentPlayer]);
|
||||||
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0? "X" : "O");
|
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0 ? "X" : "O");
|
||||||
|
|
||||||
Game.Move move = null;
|
Game.Move move = null;
|
||||||
|
|
||||||
if (information.isPlayerHuman()[currentPlayer]) {
|
if (information.isPlayerHuman()[currentPlayer]) {
|
||||||
try {
|
try {
|
||||||
final Game.Move wants = playerMoveQueue.take();
|
final Game.Move wants = playerMoveQueue.take();
|
||||||
final Game.Move[] legalMoves = ticTacToe.get().getLegalMoves();
|
final Game.Move[] legalMoves = ticTacToe.get().getLegalMoves();
|
||||||
|
|
||||||
for (final Game.Move legalMove : legalMoves) {
|
for (final Game.Move legalMove : legalMoves) {
|
||||||
if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) {
|
if (legalMove.position() == wants.position()
|
||||||
move = wants;
|
&& legalMove.value() == wants.value()) {
|
||||||
}
|
move = wants;
|
||||||
}
|
}
|
||||||
} catch (InterruptedException _) {}
|
}
|
||||||
} else {
|
} catch (InterruptedException _) {
|
||||||
final long start = System.currentTimeMillis();
|
}
|
||||||
|
} else {
|
||||||
|
final long start = System.currentTimeMillis();
|
||||||
|
|
||||||
move = ticTacToeAI.findBestMove(ticTacToe.get(), compurterDifficultyToDepth(10,
|
move =
|
||||||
information.computerDifficulty()[currentPlayer]));
|
ticTacToeAI.findBestMove(
|
||||||
|
ticTacToe.get(),
|
||||||
|
compurterDifficultyToDepth(
|
||||||
|
10, information.computerDifficulty()[currentPlayer]));
|
||||||
|
|
||||||
if (information.computerThinkTime()[currentPlayer] > 0) {
|
if (information.computerThinkTime()[currentPlayer] > 0) {
|
||||||
final long elapsedTime = System.currentTimeMillis() - start;
|
final long elapsedTime = System.currentTimeMillis() - start;
|
||||||
final long sleepTime = information.computerThinkTime()[currentPlayer] * 1000L - elapsedTime;
|
final long sleepTime =
|
||||||
|
information.computerThinkTime()[currentPlayer] * 1000L - elapsedTime;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(sleepTime);
|
Thread.sleep(sleepTime);
|
||||||
} catch (InterruptedException _) {}
|
} catch (InterruptedException _) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (move == null) {
|
if (move == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Game.State state = ticTacToe.get().play(move);
|
final Game.State state = ticTacToe.get().play(move);
|
||||||
|
|
||||||
if (move.value() == 'X') {
|
if (move.value() == 'X') {
|
||||||
canvas.drawX(Color.RED, move.position());
|
canvas.drawX(Color.RED, move.position());
|
||||||
} else if (move.value() == 'O') {
|
} else if (move.value() == 'O') {
|
||||||
canvas.drawO(Color.BLUE, move.position());
|
canvas.drawO(Color.BLUE, move.position());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state != Game.State.NORMAL) {
|
if (state != Game.State.NORMAL) {
|
||||||
if (state == Game.State.WIN) {
|
if (state == Game.State.WIN) {
|
||||||
App.push(new GameFinishedPopup(false, information.playerName()[ticTacToe.get().getCurrentTurn()]));
|
App.push(
|
||||||
} else if (state == Game.State.DRAW) {
|
new GameFinishedPopup(
|
||||||
App.push(new GameFinishedPopup(true, ""));
|
false,
|
||||||
}
|
information.playerName()[ticTacToe.get().getCurrentTurn()]));
|
||||||
|
} else if (state == Game.State.DRAW) {
|
||||||
|
App.push(new GameFinishedPopup(true, ""));
|
||||||
|
}
|
||||||
|
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void serverGameThread() {
|
private void serverGameThread() {
|
||||||
new EventFlow()
|
new EventFlow()
|
||||||
.listen(this::handleServerGameStart) // <-----------
|
.listen(this::handleServerGameStart) // <-----------
|
||||||
.listen(this::yourTurnResponse)
|
.listen(this::yourTurnResponse)
|
||||||
.listen(this::onMoveResponse)
|
.listen(this::onMoveResponse)
|
||||||
.listen(this::handleReceivedMessage);
|
.listen(this::handleReceivedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) {
|
private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) {
|
||||||
// Meneer Bas de Jong. Dit functie wordt niet aangeroepen als je de challenger bent.
|
// Meneer Bas de Jong. Dit functie wordt niet aangeroepen als je de challenger bent.
|
||||||
// Ik heb veel dingen geprobeert. FUCKING veel dingen. Hij doet het niet.
|
// Ik heb veel dingen geprobeert. FUCKING veel dingen. Hij doet het niet.
|
||||||
// Ik heb zelfs in jou code gekeken en unsubscribeAfterSuccess op false gezet. (zie ConnectedLayer).
|
// Ik heb zelfs in jou code gekeken en unsubscribeAfterSuccess op false gezet. (zie
|
||||||
// Alle andere functies worden wel gecalt. Behalve dit.
|
// ConnectedLayer).
|
||||||
|
// Alle andere functies worden wel gecalt. Behalve dit.
|
||||||
|
|
||||||
// Ben jij gehandicapt of ik? Want het moet 1 van de 2 zijn. Ik ben dit al 2 uur aan het debuggen.
|
// Ben jij gehandicapt of ik? Want het moet 1 van de 2 zijn. Ik ben dit al 2 uur aan het
|
||||||
// Ik ga nu slapen (04:46).
|
// debuggen.
|
||||||
|
// Ik ga nu slapen (04:46).
|
||||||
|
|
||||||
// ⠀⠀⠀⠀⠀⠀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⠀⠀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⢀⣴⣿⣿⠿⣟⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⢀⣴⣿⣿⠿⣟⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⢸⣏⡏⠀⠀⠀⢣⢻⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⢸⣏⡏⠀⠀⠀⢣⢻⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⢸⣟⠧⠤⠤⠔⠋⠀⢿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⢸⣟⠧⠤⠤⠔⠋⠀⢿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠸⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠸⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⠘⣿⡀⢀⣶⠤⠒⠀⢻⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⠘⣿⡀⢀⣶⠤⠒⠀⢻⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⠀⢹⣧⠀⠀⠀⠀⠀⠈⢿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⠀⢹⣧⠀⠀⠀⠀⠀⠈⢿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠈⢿⣆⣠⣤⣤⣤⣤⣴⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠈⢿⣆⣠⣤⣤⣤⣤⣴⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⢀⣾⢿⢿⠀⠀⠀⢀⣀⣀⠘⣿⠋⠁⠀⠙⢇⠀⠀⠙⢿⣦⡀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⢀⣾⢿⢿⠀⠀⠀⢀⣀⣀⠘⣿⠋⠁⠀⠙⢇⠀⠀⠙⢿⣦⡀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⢀⣾⢇⡞⠘⣧⠀⢖⡭⠞⢛⡄⠘⣆⠀⠀⠀⠈⢧⠀⠀⠀⠙⢿⣄⠀⠀⠀⠀
|
// ⠀⠀⠀⢀⣾⢇⡞⠘⣧⠀⢖⡭⠞⢛⡄⠘⣆⠀⠀⠀⠈⢧⠀⠀⠀⠙⢿⣄⠀⠀⠀⠀
|
||||||
// ⠀⠀⣠⣿⣛⣥⠤⠤⢿⡄⠀⠀⠈⠉⠀⠀⠹⡄⠀⠀⠀⠈⢧⠀⠀⠀⠈⠻⣦⠀⠀⠀
|
// ⠀⠀⣠⣿⣛⣥⠤⠤⢿⡄⠀⠀⠈⠉⠀⠀⠹⡄⠀⠀⠀⠈⢧⠀⠀⠀⠈⠻⣦⠀⠀⠀
|
||||||
// ⠀⣼⡟⡱⠛⠙⠀⠀⠘⢷⡀⠀⠀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠈⣧⠀⠀⠀⠀⠹⣧⡀⠀
|
// ⠀⣼⡟⡱⠛⠙⠀⠀⠘⢷⡀⠀⠀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠈⣧⠀⠀⠀⠀⠹⣧⡀⠀
|
||||||
// ⢸⡏⢠⠃⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠘⣧⠀⠀⠀⠀⠸⣷⡀
|
// ⢸⡏⢠⠃⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠘⣧⠀⠀⠀⠀⠸⣷⡀
|
||||||
// ⠸⣧⠘⡇⠀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⢹⡇⠀⠀⠀⠀⣿⠇
|
// ⠸⣧⠘⡇⠀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⢹⡇⠀⠀⠀⠀⣿⠇
|
||||||
// ⠀⣿⡄⢳⠀⠀⠀⠀⠀⠀⠀⠈⣷⠀⠀⠀⠀⠀⠀⠈⠆⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠀
|
// ⠀⣿⡄⢳⠀⠀⠀⠀⠀⠀⠀⠈⣷⠀⠀⠀⠀⠀⠀⠈⠆⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠀
|
||||||
// ⠀⢹⡇⠘⣇⠀⠀⠀⠀⠀⠀⠰⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⣼⡟⠀⠀
|
// ⠀⢹⡇⠘⣇⠀⠀⠀⠀⠀⠀⠰⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⣼⡟⠀⠀
|
||||||
// ⠀⢸⡇⠀⢹⡆⠀⠀⠀⠀⠀⠀⠙⠁⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⢳⣼⠟⠀⠀⠀
|
// ⠀⢸⡇⠀⢹⡆⠀⠀⠀⠀⠀⠀⠙⠁⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⢳⣼⠟⠀⠀⠀
|
||||||
// ⠀⠸⣧⣀⠀⢳⡀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⢃⠀⢀⣴⡿⠁⠀⠀⠀⠀
|
// ⠀⠸⣧⣀⠀⢳⡀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⢃⠀⢀⣴⡿⠁⠀⠀⠀⠀
|
||||||
// ⠀⠀⠈⠙⢷⣄⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⣠⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠈⠙⢷⣄⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⣠⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⠈⠻⢿⣷⣦⣄⣀⣀⣠⣤⠾⠷⣦⣤⣤⡶⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⠈⠻⢿⣷⣦⣄⣀⣀⣠⣤⠾⠷⣦⣤⣤⡶⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
// ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
// ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||||
|
|
||||||
player2Name = resp.opponent();
|
player2Name = resp.opponent();
|
||||||
System.out.println(player2Name);
|
System.out.println(player2Name);
|
||||||
|
|
||||||
currentPlayerMoveText.setText("X");
|
currentPlayerMoveText.setText("X");
|
||||||
|
|
||||||
if(!resp.playerToMove().equalsIgnoreCase(resp.opponent())) {
|
if (!resp.playerToMove().equalsIgnoreCase(resp.opponent())) {
|
||||||
currentPlayerNameText.setText(information.playerName()[0]);
|
currentPlayerNameText.setText(information.playerName()[0]);
|
||||||
firstPlayerIsMe.set(true);
|
firstPlayerIsMe.set(true);
|
||||||
|
|
||||||
System.out.printf("I am starting: My client id is %d\n", resp.clientId());
|
System.out.printf("I am starting: My client id is %d\n", resp.clientId());
|
||||||
} else {
|
} else {
|
||||||
currentPlayerNameText.setText(player2Name);
|
currentPlayerNameText.setText(player2Name);
|
||||||
firstPlayerIsMe.set(false);
|
firstPlayerIsMe.set(false);
|
||||||
|
|
||||||
System.out.printf("I am NOT starting: My client id is %d\n", resp.clientId());
|
System.out.printf("I am NOT starting: My client id is %d\n", resp.clientId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMoveResponse(NetworkEvents.GameMoveResponse resp) {
|
private void onMoveResponse(NetworkEvents.GameMoveResponse resp) {
|
||||||
char playerChar;
|
char playerChar;
|
||||||
|
|
||||||
if (!resp.player().equalsIgnoreCase(player2Name)) {
|
if (!resp.player().equalsIgnoreCase(player2Name)) {
|
||||||
playerChar = firstPlayerIsMe.get()? 'X' : 'O';
|
playerChar = firstPlayerIsMe.get() ? 'X' : 'O';
|
||||||
} else {
|
} else {
|
||||||
playerChar = firstPlayerIsMe.get()? 'O' : 'X';
|
playerChar = firstPlayerIsMe.get() ? 'O' : 'X';
|
||||||
}
|
}
|
||||||
|
|
||||||
final Game.Move move = new Game.Move(Integer.parseInt(resp.move()), playerChar);
|
final Game.Move move = new Game.Move(Integer.parseInt(resp.move()), playerChar);
|
||||||
final Game.State state = ticTacToe.get().play(move);
|
final Game.State state = ticTacToe.get().play(move);
|
||||||
|
|
||||||
if (state != Game.State.NORMAL) { //todo differentiate between future draw guaranteed and is currently a draw
|
if (state
|
||||||
if (state == Game.State.WIN) {
|
!= Game.State.NORMAL) { // todo differentiate between future draw guaranteed and is
|
||||||
App.push(new GameFinishedPopup(false, information.playerName()[ticTacToe.get().getCurrentTurn()]));
|
// currently a draw
|
||||||
} else if (state == Game.State.DRAW) {
|
if (state == Game.State.WIN) {
|
||||||
App.push(new GameFinishedPopup(true, ""));
|
App.push(
|
||||||
}
|
new GameFinishedPopup(
|
||||||
}
|
false, information.playerName()[ticTacToe.get().getCurrentTurn()]));
|
||||||
|
} else if (state == Game.State.DRAW) {
|
||||||
|
App.push(new GameFinishedPopup(true, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (move.value() == 'X') {
|
if (move.value() == 'X') {
|
||||||
canvas.drawX(Color.RED, move.position());
|
canvas.drawX(Color.RED, move.position());
|
||||||
} else if (move.value() == 'O') {
|
} else if (move.value() == 'O') {
|
||||||
canvas.drawO(Color.BLUE, move.position());
|
canvas.drawO(Color.BLUE, move.position());
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPlayerNameText.setText(ticTacToe.get().getCurrentTurn() == (firstPlayerIsMe.get()? 0 : 1)? information.playerName()[0] : player2Name);
|
currentPlayerNameText.setText(
|
||||||
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0? "X" : "O");
|
ticTacToe.get().getCurrentTurn() == (firstPlayerIsMe.get() ? 0 : 1)
|
||||||
}
|
? information.playerName()[0]
|
||||||
|
: player2Name);
|
||||||
|
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0 ? "X" : "O");
|
||||||
|
}
|
||||||
|
|
||||||
private void yourTurnResponse(NetworkEvents.YourTurnResponse response) {
|
private void yourTurnResponse(NetworkEvents.YourTurnResponse response) {
|
||||||
int position = -1;
|
int position = -1;
|
||||||
|
|
||||||
if (information.isPlayerHuman()[0]) {
|
if (information.isPlayerHuman()[0]) {
|
||||||
try {
|
try {
|
||||||
position = playerMoveQueue.take().position();
|
position = playerMoveQueue.take().position();
|
||||||
} catch (InterruptedException _) {}
|
} catch (InterruptedException _) {
|
||||||
} else {
|
}
|
||||||
final Game.Move move = ticTacToeAI.findBestMove(ticTacToe.get(), compurterDifficultyToDepth(10,
|
} else {
|
||||||
information.computerDifficulty()[0]));
|
final Game.Move move =
|
||||||
|
ticTacToeAI.findBestMove(
|
||||||
|
ticTacToe.get(),
|
||||||
|
compurterDifficultyToDepth(10, information.computerDifficulty()[0]));
|
||||||
|
|
||||||
position = move.position();
|
position = move.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short)position))
|
new EventFlow()
|
||||||
.postEvent();
|
.addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short) position))
|
||||||
}
|
.postEvent();
|
||||||
|
}
|
||||||
|
|
||||||
private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) {
|
private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) {
|
||||||
System.out.println("Received Message: " + msg.message()); //todo add chat window
|
System.out.println("Received Message: " + msg.message()); // todo add chat window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,27 @@
|
|||||||
package org.toop.local;
|
package org.toop.local;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
import org.toop.framework.asset.ResourceManager;
|
import org.toop.framework.asset.ResourceManager;
|
||||||
import org.toop.framework.asset.resources.LocalizationAsset;
|
import org.toop.framework.asset.resources.LocalizationAsset;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class AppContext {
|
public class AppContext {
|
||||||
private static final LocalizationAsset localization = ResourceManager.get("localization");
|
private static final LocalizationAsset localization = ResourceManager.get("localization");
|
||||||
private static Locale locale = Locale.forLanguageTag("en");
|
private static Locale locale = Locale.forLanguageTag("en");
|
||||||
|
|
||||||
public static LocalizationAsset getLocalization() {
|
public static LocalizationAsset getLocalization() {
|
||||||
return localization;
|
return localization;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setLocale(Locale locale) {
|
public static void setLocale(Locale locale) {
|
||||||
AppContext.locale = locale;
|
AppContext.locale = locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Locale getLocale() {
|
public static Locale getLocale() {
|
||||||
return locale;
|
return locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getString(String key) {
|
public static String getString(String key) {
|
||||||
assert localization != null;
|
assert localization != null;
|
||||||
return localization.getString(key, locale);
|
return localization.getString(key, locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
package org.toop.local;
|
package org.toop.local;
|
||||||
|
|
||||||
import jdk.jfr.Event;
|
import java.io.File;
|
||||||
|
import java.util.Locale;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.framework.asset.resources.SettingsAsset;
|
import org.toop.framework.asset.resources.SettingsAsset;
|
||||||
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.framework.settings.Settings;
|
import org.toop.framework.settings.Settings;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class AppSettings {
|
public class AppSettings {
|
||||||
|
|
||||||
private SettingsAsset settingsAsset;
|
private SettingsAsset settingsAsset;
|
||||||
@@ -23,10 +21,16 @@ public class AppSettings {
|
|||||||
|
|
||||||
AppContext.setLocale(Locale.of(settingsData.locale));
|
AppContext.setLocale(Locale.of(settingsData.locale));
|
||||||
App.setFullscreen(settingsData.fullScreen);
|
App.setFullscreen(settingsData.fullScreen);
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(settingsData.volume)).asyncPostEvent();
|
new EventFlow()
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeFxVolume(settingsData.fxVolume)).asyncPostEvent();
|
.addPostEvent(new AudioEvents.ChangeVolume(settingsData.volume))
|
||||||
new EventFlow().addPostEvent(new AudioEvents.ChangeMusicVolume(settingsData.musicVolume)).asyncPostEvent();
|
.asyncPostEvent();
|
||||||
App.setStyle(settingsAsset.getTheme(), settingsAsset.getLayoutSize());
|
new EventFlow()
|
||||||
|
.addPostEvent(new AudioEvents.ChangeFxVolume(settingsData.fxVolume))
|
||||||
|
.asyncPostEvent();
|
||||||
|
new EventFlow()
|
||||||
|
.addPostEvent(new AudioEvents.ChangeMusicVolume(settingsData.musicVolume))
|
||||||
|
.asyncPostEvent();
|
||||||
|
App.setStyle(settingsAsset.getTheme(), settingsAsset.getLayoutSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettingsAsset getPath() {
|
public SettingsAsset getPath() {
|
||||||
@@ -45,7 +49,8 @@ public class AppSettings {
|
|||||||
basePath = System.getProperty("user.home") + "/.config";
|
basePath = System.getProperty("user.home") + "/.config";
|
||||||
}
|
}
|
||||||
|
|
||||||
File settingsFile = new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
|
File settingsFile =
|
||||||
|
new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
|
||||||
this.settingsAsset = new SettingsAsset(settingsFile);
|
this.settingsAsset = new SettingsAsset(settingsFile);
|
||||||
}
|
}
|
||||||
return this.settingsAsset;
|
return this.settingsAsset;
|
||||||
|
|||||||
@@ -7,26 +7,27 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A thread-safe, distributed unique ID generator following the Snowflake pattern.
|
* A thread-safe, distributed unique ID generator following the Snowflake pattern.
|
||||||
* <p>
|
*
|
||||||
* Each generated 64-bit ID encodes:
|
* <p>Each generated 64-bit ID encodes:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>41-bit timestamp (milliseconds since custom epoch)</li>
|
* <li>41-bit timestamp (milliseconds since custom epoch)
|
||||||
* <li>10-bit machine identifier</li>
|
* <li>10-bit machine identifier
|
||||||
* <li>12-bit sequence number for IDs generated in the same millisecond</li>
|
* <li>12-bit sequence number for IDs generated in the same millisecond
|
||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>This implementation ensures:
|
* <p>This implementation ensures:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>IDs are unique per machine.</li>
|
* <li>IDs are unique per machine.
|
||||||
* <li>Monotonicity within the same machine.</li>
|
* <li>Monotonicity within the same machine.
|
||||||
* <li>Safe concurrent generation via synchronized {@link #nextId()}.</li>
|
* <li>Safe concurrent generation via synchronized {@link #nextId()}.
|
||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>Custom epoch is set to {@code 2025-01-01T00:00:00Z}.</p>
|
* <p>Custom epoch is set to {@code 2025-01-01T00:00:00Z}.
|
||||||
|
*
|
||||||
|
* <p>Usage example:
|
||||||
*
|
*
|
||||||
* <p>Usage example:</p>
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* SnowflakeGenerator generator = new SnowflakeGenerator();
|
* SnowflakeGenerator generator = new SnowflakeGenerator();
|
||||||
* long id = generator.nextId();
|
* long id = generator.nextId();
|
||||||
@@ -34,9 +35,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
*/
|
*/
|
||||||
public class SnowflakeGenerator {
|
public class SnowflakeGenerator {
|
||||||
|
|
||||||
/**
|
/** Custom epoch in milliseconds (2025-01-01T00:00:00Z). */
|
||||||
* Custom epoch in milliseconds (2025-01-01T00:00:00Z).
|
|
||||||
*/
|
|
||||||
private static final long EPOCH = Instant.parse("2025-01-01T00:00:00Z").toEpochMilli();
|
private static final long EPOCH = Instant.parse("2025-01-01T00:00:00Z").toEpochMilli();
|
||||||
|
|
||||||
// Bit allocations
|
// Bit allocations
|
||||||
@@ -53,17 +52,15 @@ public class SnowflakeGenerator {
|
|||||||
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
|
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
|
||||||
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
|
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
|
||||||
|
|
||||||
/**
|
/** Unique machine identifier derived from network interfaces (10 bits). */
|
||||||
* Unique machine identifier derived from network interfaces (10 bits).
|
|
||||||
*/
|
|
||||||
private static final long machineId = SnowflakeGenerator.genMachineId();
|
private static final long machineId = SnowflakeGenerator.genMachineId();
|
||||||
|
|
||||||
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
|
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
|
||||||
private long sequence = 0L;
|
private long sequence = 0L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a 10-bit machine identifier based on MAC addresses of network interfaces.
|
* Generates a 10-bit machine identifier based on MAC addresses of network interfaces. Falls
|
||||||
* Falls back to a random value if MAC cannot be determined.
|
* back to a random value if MAC cannot be determined.
|
||||||
*/
|
*/
|
||||||
private static long genMachineId() {
|
private static long genMachineId() {
|
||||||
try {
|
try {
|
||||||
@@ -82,6 +79,7 @@ public class SnowflakeGenerator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* For testing: manually set the last generated timestamp.
|
* For testing: manually set the last generated timestamp.
|
||||||
|
*
|
||||||
* @param l timestamp in milliseconds
|
* @param l timestamp in milliseconds
|
||||||
*/
|
*/
|
||||||
void setTime(long l) {
|
void setTime(long l) {
|
||||||
@@ -89,8 +87,8 @@ public class SnowflakeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a SnowflakeGenerator.
|
* Constructs a SnowflakeGenerator. Validates that the machine ID is within allowed range.
|
||||||
* Validates that the machine ID is within allowed range.
|
*
|
||||||
* @throws IllegalArgumentException if machine ID is invalid
|
* @throws IllegalArgumentException if machine ID is invalid
|
||||||
*/
|
*/
|
||||||
public SnowflakeGenerator() {
|
public SnowflakeGenerator() {
|
||||||
@@ -102,10 +100,9 @@ public class SnowflakeGenerator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the next unique ID.
|
* Generates the next unique ID.
|
||||||
* <p>
|
*
|
||||||
* If multiple IDs are generated in the same millisecond, a sequence number
|
* <p>If multiple IDs are generated in the same millisecond, a sequence number is incremented.
|
||||||
* is incremented. If the sequence overflows, waits until the next millisecond.
|
* If the sequence overflows, waits until the next millisecond.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @return a unique 64-bit ID
|
* @return a unique 64-bit ID
|
||||||
* @throws IllegalStateException if clock moves backwards or timestamp exceeds 41-bit limit
|
* @throws IllegalStateException if clock moves backwards or timestamp exceeds 41-bit limit
|
||||||
@@ -139,6 +136,7 @@ public class SnowflakeGenerator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until the next millisecond if sequence overflows.
|
* Waits until the next millisecond if sequence overflows.
|
||||||
|
*
|
||||||
* @param lastTimestamp previous timestamp
|
* @param lastTimestamp previous timestamp
|
||||||
* @return new timestamp
|
* @return new timestamp
|
||||||
*/
|
*/
|
||||||
@@ -150,9 +148,7 @@ public class SnowflakeGenerator {
|
|||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns current system timestamp in milliseconds. */
|
||||||
* Returns current system timestamp in milliseconds.
|
|
||||||
*/
|
|
||||||
private long timestamp() {
|
private long timestamp() {
|
||||||
return System.currentTimeMillis();
|
return System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,44 @@
|
|||||||
package org.toop.framework.asset;
|
package org.toop.framework.asset;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.toop.framework.asset.events.AssetLoaderEvents;
|
|
||||||
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.reflections.Reflections;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.reflections.Reflections;
|
||||||
|
import org.toop.framework.asset.events.AssetLoaderEvents;
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for loading assets from a file system directory into memory.
|
* Responsible for loading assets from a file system directory into memory.
|
||||||
* <p>
|
|
||||||
* The {@code ResourceLoader} scans a root folder recursively, identifies files,
|
|
||||||
* and maps them to registered resource types based on file extensions and
|
|
||||||
* {@link FileExtension} annotations.
|
|
||||||
* It supports multiple resource types including {@link PreloadResource} (automatically loaded)
|
|
||||||
* and {@link BundledResource} (merged across multiple files).
|
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>Assets are stored in a static, thread-safe list and can be retrieved
|
* <p>The {@code ResourceLoader} scans a root folder recursively, identifies files, and maps them to
|
||||||
* through {@link ResourceManager}.</p>
|
* registered resource types based on file extensions and {@link FileExtension} annotations. It
|
||||||
|
* supports multiple resource types including {@link PreloadResource} (automatically loaded) and
|
||||||
|
* {@link BundledResource} (merged across multiple files).
|
||||||
|
*
|
||||||
|
* <p>Assets are stored in a static, thread-safe list and can be retrieved through {@link
|
||||||
|
* ResourceManager}.
|
||||||
|
*
|
||||||
|
* <p>Features:
|
||||||
*
|
*
|
||||||
* <p>Features:</p>
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Recursive directory scanning for assets.</li>
|
* <li>Recursive directory scanning for assets.
|
||||||
* <li>Automatic registration of resource classes via reflection.</li>
|
* <li>Automatic registration of resource classes via reflection.
|
||||||
* <li>Bundled resource support: multiple files merged into a single resource instance.</li>
|
* <li>Bundled resource support: multiple files merged into a single resource instance.
|
||||||
* <li>Preload resources automatically invoke {@link PreloadResource#load()}.</li>
|
* <li>Preload resources automatically invoke {@link PreloadResource#load()}.
|
||||||
* <li>Progress tracking via {@link AssetLoaderEvents.LoadingProgressUpdate} events.</li>
|
* <li>Progress tracking via {@link AssetLoaderEvents.LoadingProgressUpdate} events.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Usage example:</p>
|
* <p>Usage example:
|
||||||
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* ResourceLoader loader = new ResourceLoader("assets");
|
* ResourceLoader loader = new ResourceLoader("assets");
|
||||||
* double progress = loader.getProgress();
|
* double progress = loader.getProgress();
|
||||||
@@ -48,14 +47,17 @@ import java.util.function.Function;
|
|||||||
*/
|
*/
|
||||||
public class ResourceLoader {
|
public class ResourceLoader {
|
||||||
private static final Logger logger = LogManager.getLogger(ResourceLoader.class);
|
private static final Logger logger = LogManager.getLogger(ResourceLoader.class);
|
||||||
private static final List<ResourceMeta<? extends BaseResource>> assets = new CopyOnWriteArrayList<>();
|
private static final List<ResourceMeta<? extends BaseResource>> assets =
|
||||||
private final Map<String, Function<File, ? extends BaseResource>> registry = new ConcurrentHashMap<>();
|
new CopyOnWriteArrayList<>();
|
||||||
|
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 ResourceLoader 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 ResourceLoader(File rootFolder) {
|
public ResourceLoader(File rootFolder) {
|
||||||
@@ -84,6 +86,7 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an ResourceLoader 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 ResourceLoader(String rootFolder) {
|
public ResourceLoader(String rootFolder) {
|
||||||
@@ -92,6 +95,7 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current progress of loading assets (0.0 to 1.0).
|
* Returns the current progress of loading assets (0.0 to 1.0).
|
||||||
|
*
|
||||||
* @return progress as a double
|
* @return progress as a double
|
||||||
*/
|
*/
|
||||||
public double getProgress() {
|
public double getProgress() {
|
||||||
@@ -100,6 +104,7 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of assets loaded so far.
|
* Returns the number of assets loaded so far.
|
||||||
|
*
|
||||||
* @return loaded count
|
* @return loaded count
|
||||||
*/
|
*/
|
||||||
public int getLoadedCount() {
|
public int getLoadedCount() {
|
||||||
@@ -108,6 +113,7 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of files found to load.
|
* Returns the total number of files found to load.
|
||||||
|
*
|
||||||
* @return total asset count
|
* @return total asset count
|
||||||
*/
|
*/
|
||||||
public int getTotalCount() {
|
public int getTotalCount() {
|
||||||
@@ -116,6 +122,7 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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<ResourceMeta<? extends BaseResource>> getAssets() {
|
public List<ResourceMeta<? extends BaseResource>> getAssets() {
|
||||||
@@ -124,6 +131,7 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a factory for a specific file extension.
|
* Registers a factory for a specific file extension.
|
||||||
|
*
|
||||||
* @param extension the file extension (without dot)
|
* @param extension the file extension (without dot)
|
||||||
* @param factory a function mapping a File to a resource instance
|
* @param factory a function mapping a File to a resource instance
|
||||||
* @param <T> the type of resource
|
* @param <T> the type of resource
|
||||||
@@ -132,9 +140,7 @@ public class ResourceLoader {
|
|||||||
this.registry.put(extension, factory);
|
this.registry.put(extension, factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Maps a file to a resource instance based on its extension and registered factories. */
|
||||||
* Maps a file to a resource instance based on its extension and registered factories.
|
|
||||||
*/
|
|
||||||
private <T extends BaseResource> T resourceMapper(File file, Class<T> type) {
|
private <T extends BaseResource> T resourceMapper(File file, Class<T> type) {
|
||||||
String ext = getExtension(file.getName());
|
String ext = getExtension(file.getName());
|
||||||
Function<File, ? extends BaseResource> factory = registry.get(ext);
|
Function<File, ? extends BaseResource> factory = registry.get(ext);
|
||||||
@@ -144,16 +150,13 @@ public class ResourceLoader {
|
|||||||
|
|
||||||
if (!type.isInstance(resource)) {
|
if (!type.isInstance(resource)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"File " + file.getName() + " is not of type " + type.getSimpleName()
|
"File " + file.getName() + " is not of type " + type.getSimpleName());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return type.cast(resource);
|
return type.cast(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Loads the given list of files into assets, handling bundled and preload resources. */
|
||||||
* Loads the given list of files into assets, handling bundled and preload resources.
|
|
||||||
*/
|
|
||||||
private void loader(List<File> files) {
|
private void loader(List<File> files) {
|
||||||
Map<String, BundledResource> bundledResources = new HashMap<>();
|
Map<String, BundledResource> bundledResources = new HashMap<>();
|
||||||
|
|
||||||
@@ -166,35 +169,38 @@ public class ResourceLoader {
|
|||||||
}
|
}
|
||||||
case BundledResource br -> {
|
case BundledResource br -> {
|
||||||
String key = resource.getClass().getName() + ":" + br.getBaseName();
|
String key = resource.getClass().getName() + ":" + br.getBaseName();
|
||||||
if (!bundledResources.containsKey(key)) {bundledResources.put(key, br);}
|
if (!bundledResources.containsKey(key)) {
|
||||||
|
bundledResources.put(key, br);
|
||||||
|
}
|
||||||
bundledResources.get(key).loadFile(file);
|
bundledResources.get(key).loadFile(file);
|
||||||
resource = (BaseResource) bundledResources.get(key);
|
resource = (BaseResource) bundledResources.get(key);
|
||||||
assets.add(new ResourceMeta<>(br.getBaseName(), resource));
|
assets.add(new ResourceMeta<>(br.getBaseName(), resource));
|
||||||
skipAdd = true;
|
skipAdd = true;
|
||||||
}
|
}
|
||||||
case PreloadResource pr -> pr.load();
|
case PreloadResource pr -> pr.load();
|
||||||
default -> {
|
default -> {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseResource finalResource = resource;
|
BaseResource finalResource = resource;
|
||||||
boolean alreadyAdded = assets.stream()
|
boolean alreadyAdded = assets.stream().anyMatch(a -> a.getResource() == finalResource);
|
||||||
.anyMatch(a -> a.getResource() == finalResource);
|
|
||||||
if (!alreadyAdded && !skipAdd) {
|
if (!alreadyAdded && !skipAdd) {
|
||||||
assets.add(new ResourceMeta<>(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());
|
||||||
loadedCount.incrementAndGet();
|
loadedCount.incrementAndGet();
|
||||||
new EventFlow()
|
new EventFlow()
|
||||||
.addPostEvent(new AssetLoaderEvents.LoadingProgressUpdate(loadedCount.get(), totalCount))
|
.addPostEvent(
|
||||||
|
new AssetLoaderEvents.LoadingProgressUpdate(
|
||||||
|
loadedCount.get(), totalCount))
|
||||||
.postEvent();
|
.postEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Recursively searches a folder and adds all files to the foundFiles list. */
|
||||||
* Recursively searches a folder and adds all files to the foundFiles list.
|
|
||||||
*/
|
|
||||||
private void fileSearcher(final File folder, List<File> foundFiles) {
|
private void fileSearcher(final File folder, List<File> foundFiles) {
|
||||||
for (File fileEntry : Objects.requireNonNull(folder.listFiles())) {
|
for (File fileEntry : Objects.requireNonNull(folder.listFiles())) {
|
||||||
if (fileEntry.isDirectory()) {
|
if (fileEntry.isDirectory()) {
|
||||||
@@ -206,8 +212,8 @@ public class ResourceLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses reflection to automatically register all {@link BaseResource} subclasses
|
* Uses reflection to automatically register all {@link BaseResource} subclasses annotated with
|
||||||
* annotated with {@link FileExtension}.
|
* {@link FileExtension}.
|
||||||
*/
|
*/
|
||||||
private void autoRegisterResources() {
|
private void autoRegisterResources() {
|
||||||
Reflections reflections = new Reflections("org.toop.framework.asset.resources");
|
Reflections reflections = new Reflections("org.toop.framework.asset.resources");
|
||||||
@@ -217,20 +223,20 @@ public class ResourceLoader {
|
|||||||
if (!cls.isAnnotationPresent(FileExtension.class)) continue;
|
if (!cls.isAnnotationPresent(FileExtension.class)) continue;
|
||||||
FileExtension annotation = cls.getAnnotation(FileExtension.class);
|
FileExtension annotation = cls.getAnnotation(FileExtension.class);
|
||||||
for (String ext : annotation.value()) {
|
for (String ext : annotation.value()) {
|
||||||
registry.put(ext, file -> {
|
registry.put(
|
||||||
try {
|
ext,
|
||||||
return cls.getConstructor(File.class).newInstance(file);
|
file -> {
|
||||||
} catch (Exception e) {
|
try {
|
||||||
throw new RuntimeException(e);
|
return cls.getConstructor(File.class).newInstance(file);
|
||||||
}
|
} catch (Exception e) {
|
||||||
});
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Extracts the base name from a file name, used for bundling multiple files. */
|
||||||
* Extracts the base name from a file name, used for bundling multiple files.
|
|
||||||
*/
|
|
||||||
private static String getBaseName(String fileName) {
|
private static String getBaseName(String fileName) {
|
||||||
int underscoreIndex = fileName.indexOf('_');
|
int underscoreIndex = fileName.indexOf('_');
|
||||||
int dotIndex = fileName.lastIndexOf('.');
|
int dotIndex = fileName.lastIndexOf('.');
|
||||||
@@ -238,9 +244,7 @@ public class ResourceLoader {
|
|||||||
return fileName.substring(0, dotIndex);
|
return fileName.substring(0, dotIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the file extension of a given file name (without dot). */
|
||||||
* Returns the file extension of a given file name (without dot).
|
|
||||||
*/
|
|
||||||
public static String getExtension(String name) {
|
public static String getExtension(String name) {
|
||||||
int i = name.lastIndexOf('.');
|
int i = name.lastIndexOf('.');
|
||||||
return (i > 0) ? name.substring(i + 1) : "";
|
return (i > 0) ? name.substring(i + 1) : "";
|
||||||
|
|||||||
@@ -1,30 +1,29 @@
|
|||||||
package org.toop.framework.asset;
|
package org.toop.framework.asset;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.toop.framework.asset.resources.*;
|
import org.toop.framework.asset.resources.*;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Centralized manager for all loaded assets in the application.
|
* Centralized manager for all loaded assets in the application.
|
||||||
* <p>
|
|
||||||
* {@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects
|
|
||||||
* and provides utility methods to retrieve assets by name, ID, or type.
|
|
||||||
* It works together with {@link ResourceLoader} to register assets automatically
|
|
||||||
* when they are loaded from the file system.
|
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>Key responsibilities:</p>
|
* <p>{@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects and provides
|
||||||
|
* utility methods to retrieve assets by name, ID, or type. It works together with {@link
|
||||||
|
* ResourceLoader} to register assets automatically when they are loaded from the file system.
|
||||||
|
*
|
||||||
|
* <p>Key responsibilities:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Storing all loaded assets in a concurrent map.</li>
|
* <li>Storing all loaded assets in a concurrent map.
|
||||||
* <li>Providing typed access to asset resources.</li>
|
* <li>Providing typed access to asset resources.
|
||||||
* <li>Allowing lookup by asset name or ID.</li>
|
* <li>Allowing lookup by asset name or ID.
|
||||||
* <li>Supporting retrieval of all assets of a specific {@link BaseResource} subclass.</li>
|
* <li>Supporting retrieval of all assets of a specific {@link BaseResource} subclass.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Example usage:</p>
|
* <p>Example usage:
|
||||||
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* // Load assets from a loader
|
* // Load assets from a loader
|
||||||
* ResourceLoader loader = new ResourceLoader(new File("RootFolder"));
|
* ResourceLoader loader = new ResourceLoader(new File("RootFolder"));
|
||||||
@@ -40,17 +39,20 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* Optional<Asset<? extends BaseResource>> maybeAsset = ResourceManager.findByName("menu.css");
|
* Optional<Asset<? extends BaseResource>> maybeAsset = ResourceManager.findByName("menu.css");
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>Notes:</p>
|
* <p>Notes:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>All retrieval methods are static and thread-safe.</li>
|
* <li>All retrieval methods are static and thread-safe.
|
||||||
* <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
|
||||||
* <li>Assets should be loaded via {@link ResourceLoader} before retrieval.</li>
|
* compile time.
|
||||||
|
* <li>Assets should be loaded via {@link ResourceLoader} before retrieval.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class ResourceManager {
|
public class ResourceManager {
|
||||||
private static final Logger logger = LogManager.getLogger(ResourceManager.class);
|
private static final Logger logger = LogManager.getLogger(ResourceManager.class);
|
||||||
private static final ResourceManager INSTANCE = new ResourceManager();
|
private static final ResourceManager INSTANCE = new ResourceManager();
|
||||||
private static final Map<String, ResourceMeta<? extends BaseResource>> assets = new ConcurrentHashMap<>();
|
private static final Map<String, ResourceMeta<? extends BaseResource>> assets =
|
||||||
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ResourceManager() {}
|
private ResourceManager() {}
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ public class ResourceManager {
|
|||||||
*
|
*
|
||||||
* @param loader the loader that has already loaded assets
|
* @param loader the loader that has already loaded assets
|
||||||
*/
|
*/
|
||||||
public synchronized static void loadAssets(ResourceLoader loader) {
|
public static synchronized void loadAssets(ResourceLoader loader) {
|
||||||
for (var asset : loader.getAssets()) {
|
for (var asset : loader.getAssets()) {
|
||||||
assets.put(asset.getName(), asset);
|
assets.put(asset.getName(), asset);
|
||||||
}
|
}
|
||||||
@@ -85,15 +87,20 @@ public class ResourceManager {
|
|||||||
public static <T extends BaseResource> T get(String name) {
|
public static <T extends BaseResource> T get(String name) {
|
||||||
ResourceMeta<T> asset = (ResourceMeta<T>) assets.get(name);
|
ResourceMeta<T> asset = (ResourceMeta<T>) assets.get(name);
|
||||||
if (asset == null) {
|
if (asset == null) {
|
||||||
throw new TypeNotPresentException(name, new RuntimeException(String.format("Type %s not present", name))); // TODO: Create own exception, BAM
|
throw new TypeNotPresentException(
|
||||||
|
name,
|
||||||
|
new RuntimeException(
|
||||||
|
String.format(
|
||||||
|
"Type %s not present",
|
||||||
|
name))); // TODO: Create own exception, BAM
|
||||||
}
|
}
|
||||||
return asset.getResource();
|
return asset.getResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @SuppressWarnings("unchecked")
|
// @SuppressWarnings("unchecked")
|
||||||
// public static <T extends BaseResource> ArrayList<ResourceMeta<T>> getAllOfType() {
|
// public static <T extends BaseResource> ArrayList<ResourceMeta<T>> getAllOfType() {
|
||||||
// return (ArrayList<ResourceMeta<T>>) (ArrayList<?>) new ArrayList<>(assets.values());
|
// return (ArrayList<ResourceMeta<T>>) (ArrayList<?>) new ArrayList<>(assets.values());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all assets of a specific resource type.
|
* Retrieve all assets of a specific resource type.
|
||||||
|
|||||||
@@ -25,5 +25,4 @@ public class ResourceMeta<T extends BaseResource> {
|
|||||||
public T getResource() {
|
public T getResource() {
|
||||||
return this.resource;
|
return this.resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ package org.toop.framework.asset.events;
|
|||||||
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
|
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
|
||||||
|
|
||||||
public class AssetLoaderEvents {
|
public class AssetLoaderEvents {
|
||||||
public record LoadingProgressUpdate(int hasLoadedAmount, int isLoadingAmount) implements EventWithoutSnowflake {}
|
public record LoadingProgressUpdate(int hasLoadedAmount, int isLoadingAmount)
|
||||||
|
implements EventWithoutSnowflake {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,4 @@ public abstract class BaseResource {
|
|||||||
public File getFile() {
|
public File getFile() {
|
||||||
return this.file;
|
return this.file;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +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;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
|
||||||
@FileExtension({"css"})
|
@FileExtension({"css"})
|
||||||
public class CssAsset extends BaseResource {
|
public class CssAsset extends BaseResource {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
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;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.PreloadResource;
|
||||||
|
|
||||||
@FileExtension({"ttf", "otf"})
|
@FileExtension({"ttf", "otf"})
|
||||||
public class FontAsset extends BaseResource implements PreloadResource {
|
public class FontAsset extends BaseResource implements PreloadResource {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
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;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
@FileExtension({"png", "jpg", "jpeg"})
|
@FileExtension({"png", "jpg", "jpeg"})
|
||||||
public class ImageAsset extends BaseResource implements LoadableResource {
|
public class ImageAsset extends BaseResource implements LoadableResource {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
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.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
@FileExtension({"json"})
|
@FileExtension({"json"})
|
||||||
public class JsonAsset<T> extends BaseResource implements LoadableResource {
|
public class JsonAsset<T> extends BaseResource implements LoadableResource {
|
||||||
@@ -25,7 +26,8 @@ public class JsonAsset<T> extends BaseResource implements LoadableResource {
|
|||||||
File file = getFile();
|
File file = getFile();
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
try {
|
try {
|
||||||
// make a new file with the declared constructor (example: settings) if it doesn't exist
|
// make a new file with the declared constructor (example: settings) if it doesn't
|
||||||
|
// exist
|
||||||
content = type.getDeclaredConstructor().newInstance();
|
content = type.getDeclaredConstructor().newInstance();
|
||||||
save();
|
save();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -36,7 +38,7 @@ public class JsonAsset<T> extends BaseResource implements LoadableResource {
|
|||||||
try (FileReader reader = new FileReader(file)) {
|
try (FileReader reader = new FileReader(file)) {
|
||||||
content = gson.fromJson(reader, type);
|
content = gson.fromJson(reader, type);
|
||||||
this.isLoaded = true;
|
this.isLoaded = true;
|
||||||
} catch(Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to load JSON asset" + getFile(), e);
|
throw new RuntimeException("Failed to load JSON asset" + getFile(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,7 +64,7 @@ public class JsonAsset<T> extends BaseResource implements LoadableResource {
|
|||||||
parent.mkdirs();
|
parent.mkdirs();
|
||||||
}
|
}
|
||||||
try (FileWriter writer = new FileWriter(file)) {
|
try (FileWriter writer = new FileWriter(file)) {
|
||||||
gson.toJson(content, writer);
|
gson.toJson(content, writer);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Failed to save JSON asset" + getFile(), e);
|
throw new RuntimeException("Failed to save JSON asset" + getFile(), e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,29 @@
|
|||||||
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.*;
|
||||||
|
import org.toop.framework.asset.types.BundledResource;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a localization resource asset that loads and manages property files
|
* Represents a localization resource asset that loads and manages property files containing
|
||||||
* containing key-value pairs for different locales.
|
* key-value pairs for different locales.
|
||||||
* <p>
|
*
|
||||||
* This class implements {@link LoadableResource} to support loading/unloading
|
* <p>This class implements {@link LoadableResource} to support loading/unloading and {@link
|
||||||
* and {@link BundledResource} to represent resources that can contain multiple
|
* BundledResource} to represent resources that can contain multiple localized bundles.
|
||||||
* localized bundles.
|
*
|
||||||
* </p>
|
* <p>Files handled by this class must have the {@code .properties} extension, optionally with a
|
||||||
* <p>
|
* locale suffix, e.g., {@code messages_en_US.properties}.
|
||||||
* Files handled by this class must have the {@code .properties} extension,
|
*
|
||||||
* optionally with a locale suffix, e.g., {@code messages_en_US.properties}.
|
* <p>Example usage:
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>
|
|
||||||
* Example usage:
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* LocalizationAsset asset = new LocalizationAsset(new File("messages_en_US.properties"));
|
* LocalizationAsset asset = new LocalizationAsset(new File("messages_en_US.properties"));
|
||||||
* asset.load();
|
* asset.load();
|
||||||
* String greeting = asset.getString("hello", Locale.US);
|
* String greeting = asset.getString("hello", Locale.US);
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
@FileExtension({"properties"})
|
@FileExtension({"properties"})
|
||||||
public class LocalizationAsset extends BaseResource implements LoadableResource, BundledResource {
|
public class LocalizationAsset extends BaseResource implements LoadableResource, BundledResource {
|
||||||
@@ -54,18 +49,14 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
|
|||||||
super(file);
|
super(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Loads the resource file into memory and prepares localized bundles. */
|
||||||
* Loads the resource file into memory and prepares localized bundles.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
loadFile(getFile());
|
loadFile(getFile());
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Unloads all loaded resource bundles, freeing memory. */
|
||||||
* Unloads all loaded resource bundles, freeing memory.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void unload() {
|
public void unload() {
|
||||||
bundles.clear();
|
bundles.clear();
|
||||||
@@ -83,11 +74,10 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a localized string for the given key and locale.
|
* Retrieves a localized string for the given key and locale. If an exact match for the locale
|
||||||
* If an exact match for the locale is not found, a fallback
|
* is not found, a fallback matching the language or the default locale will be used.
|
||||||
* matching the language or the default locale will be used.
|
|
||||||
*
|
*
|
||||||
* @param key the key of the string
|
* @param key the key of the string
|
||||||
* @param locale the desired locale
|
* @param locale the desired locale
|
||||||
* @return the localized string
|
* @return the localized string
|
||||||
* @throws MissingResourceException if no resource bundle is available for the locale
|
* @throws MissingResourceException if no resource bundle is available for the locale
|
||||||
@@ -95,14 +85,15 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
|
|||||||
public String getString(String key, Locale locale) {
|
public String getString(String key, Locale locale) {
|
||||||
Locale target = findBestLocale(locale);
|
Locale target = findBestLocale(locale);
|
||||||
ResourceBundle bundle = bundles.get(target);
|
ResourceBundle bundle = bundles.get(target);
|
||||||
if (bundle == null) throw new MissingResourceException(
|
if (bundle == null)
|
||||||
"No bundle for locale: " + target, getClass().getName(), key);
|
throw new MissingResourceException(
|
||||||
|
"No bundle for locale: " + target, getClass().getName(), key);
|
||||||
return bundle.getString(key);
|
return bundle.getString(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the best matching locale among loaded bundles.
|
* Finds the best matching locale among loaded bundles. Prefers an exact match, then
|
||||||
* Prefers an exact match, then language-only match, then fallback.
|
* language-only match, then fallback.
|
||||||
*
|
*
|
||||||
* @param locale the desired locale
|
* @param locale the desired locale
|
||||||
* @return the best matching locale
|
* @return the best matching locale
|
||||||
@@ -125,8 +116,8 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a specific property file as a resource bundle.
|
* Loads a specific property file as a resource bundle. The locale is extracted from the file
|
||||||
* The locale is extracted from the file name if present.
|
* name if present.
|
||||||
*
|
*
|
||||||
* @param file the property file to load
|
* @param file the property file to load
|
||||||
* @throws RuntimeException if the file cannot be read
|
* @throws RuntimeException if the file cannot be read
|
||||||
@@ -134,7 +125,7 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
|
|||||||
@Override
|
@Override
|
||||||
public void loadFile(File file) {
|
public void loadFile(File file) {
|
||||||
try (InputStreamReader reader =
|
try (InputStreamReader reader =
|
||||||
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
|
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
|
||||||
Locale locale = extractLocale(file.getName(), baseName);
|
Locale locale = extractLocale(file.getName(), baseName);
|
||||||
bundles.put(locale, new PropertyResourceBundle(reader));
|
bundles.put(locale, new PropertyResourceBundle(reader));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -153,22 +144,23 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
|
|||||||
return this.baseName;
|
return this.baseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Extracts the base name from a file name.
|
// * Extracts the base name from a file name.
|
||||||
// *
|
// *
|
||||||
// * @param fileName the file name
|
// * @param fileName the file name
|
||||||
// * @return base name without locale or extension
|
// * @return base name without locale or extension
|
||||||
// */
|
// */
|
||||||
// private String getBaseName(String fileName) {
|
// private String getBaseName(String fileName) {
|
||||||
// int dotIndex = fileName.lastIndexOf('.');
|
// int dotIndex = fileName.lastIndexOf('.');
|
||||||
// String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) : fileName;
|
// String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) :
|
||||||
//
|
// fileName;
|
||||||
// int underscoreIndex = nameWithoutExtension.indexOf('_');
|
//
|
||||||
// if (underscoreIndex > 0) {
|
// int underscoreIndex = nameWithoutExtension.indexOf('_');
|
||||||
// return nameWithoutExtension.substring(0, underscoreIndex);
|
// if (underscoreIndex > 0) {
|
||||||
// }
|
// return nameWithoutExtension.substring(0, underscoreIndex);
|
||||||
// return nameWithoutExtension;
|
// }
|
||||||
// }
|
// return nameWithoutExtension;
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts a locale from a file name based on the pattern "base_LOCALE.properties".
|
* Extracts a locale from a file name based on the pattern "base_LOCALE.properties".
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
import javafx.scene.media.Media;
|
import javafx.scene.media.Media;
|
||||||
import org.toop.framework.asset.types.FileExtension;
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
import org.toop.framework.asset.types.LoadableResource;
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
@FileExtension({"mp3"})
|
@FileExtension({"mp3"})
|
||||||
public class MusicAsset extends BaseResource implements LoadableResource {
|
public class MusicAsset extends BaseResource implements LoadableResource {
|
||||||
private Media media;
|
private Media media;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.toop.framework.asset.resources;
|
package org.toop.framework.asset.resources;
|
||||||
|
|
||||||
|
|
||||||
import org.toop.framework.settings.Settings;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import org.toop.framework.settings.Settings;
|
||||||
|
|
||||||
public class SettingsAsset extends JsonAsset<Settings> {
|
public class SettingsAsset extends JsonAsset<Settings> {
|
||||||
|
|
||||||
@@ -32,13 +30,13 @@ public class SettingsAsset extends JsonAsset<Settings> {
|
|||||||
return getContent().fullScreen;
|
return getContent().fullScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTheme() {
|
public String getTheme() {
|
||||||
return getContent().theme;
|
return getContent().theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLayoutSize() {
|
public String getLayoutSize() {
|
||||||
return getContent().layoutSize;
|
return getContent().layoutSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVolume(int volume) {
|
public void setVolume(int volume) {
|
||||||
getContent().volume = volume;
|
getContent().volume = volume;
|
||||||
@@ -65,13 +63,13 @@ public class SettingsAsset extends JsonAsset<Settings> {
|
|||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTheme(String theme) {
|
public void setTheme(String theme) {
|
||||||
getContent().theme = theme;
|
getContent().theme = theme;
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLayoutSize(String layoutSize) {
|
public void setLayoutSize(String layoutSize) {
|
||||||
getContent().layoutSize = layoutSize;
|
getContent().layoutSize = layoutSize;
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
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 javax.sound.sampled.*;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import javax.sound.sampled.*;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
@FileExtension({"wav"})
|
@FileExtension({"wav"})
|
||||||
public class SoundEffectAsset extends BaseResource implements LoadableResource {
|
public class SoundEffectAsset extends BaseResource implements LoadableResource {
|
||||||
@@ -16,22 +15,25 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets a new clip to play
|
// Gets a new clip to play
|
||||||
public Clip getNewClip() throws LineUnavailableException, UnsupportedAudioFileException, IOException {
|
public Clip getNewClip()
|
||||||
|
throws LineUnavailableException, UnsupportedAudioFileException, IOException {
|
||||||
// Get a new clip from audio system
|
// Get a new clip from audio system
|
||||||
Clip clip = AudioSystem.getClip();
|
Clip clip = AudioSystem.getClip();
|
||||||
|
|
||||||
// Insert a new audio stream into the clip
|
// Insert a new audio stream into the clip
|
||||||
AudioInputStream inputStream = this.getAudioStream();
|
AudioInputStream inputStream = this.getAudioStream();
|
||||||
AudioFormat baseFormat = inputStream.getFormat();
|
AudioFormat baseFormat = inputStream.getFormat();
|
||||||
if (baseFormat.getSampleSizeInBits() > 16) inputStream = downSampleAudio(inputStream, baseFormat);
|
if (baseFormat.getSampleSizeInBits() > 16)
|
||||||
clip.open(inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
|
inputStream = downSampleAudio(inputStream, baseFormat);
|
||||||
|
clip.open(
|
||||||
|
inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
|
||||||
return clip;
|
return clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a new audio stream from byte array
|
// Generates a new audio stream from byte array
|
||||||
private AudioInputStream getAudioStream() throws UnsupportedAudioFileException, IOException {
|
private AudioInputStream getAudioStream() throws UnsupportedAudioFileException, IOException {
|
||||||
// Check if raw data is loaded into memory
|
// Check if raw data is loaded into memory
|
||||||
if(!this.isLoaded()){
|
if (!this.isLoaded()) {
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,16 +41,18 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource {
|
|||||||
return AudioSystem.getAudioInputStream(new ByteArrayInputStream(this.rawData));
|
return AudioSystem.getAudioInputStream(new ByteArrayInputStream(this.rawData));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) {
|
private AudioInputStream downSampleAudio(
|
||||||
AudioFormat decodedFormat = new AudioFormat(
|
AudioInputStream audioInputStream, AudioFormat baseFormat) {
|
||||||
AudioFormat.Encoding.PCM_SIGNED,
|
AudioFormat decodedFormat =
|
||||||
baseFormat.getSampleRate(),
|
new AudioFormat(
|
||||||
16, // force 16-bit
|
AudioFormat.Encoding.PCM_SIGNED,
|
||||||
baseFormat.getChannels(),
|
baseFormat.getSampleRate(),
|
||||||
baseFormat.getChannels() * 2,
|
16, // force 16-bit
|
||||||
baseFormat.getSampleRate(),
|
baseFormat.getChannels(),
|
||||||
false // little-endian
|
baseFormat.getChannels() * 2,
|
||||||
);
|
baseFormat.getSampleRate(),
|
||||||
|
false // little-endian
|
||||||
|
);
|
||||||
|
|
||||||
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
|
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
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;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import org.toop.framework.asset.types.FileExtension;
|
||||||
|
import org.toop.framework.asset.types.LoadableResource;
|
||||||
|
|
||||||
@FileExtension({"txt", "json", "xml"})
|
@FileExtension({"txt", "json", "xml"})
|
||||||
public class TextAsset extends BaseResource implements LoadableResource {
|
public class TextAsset extends BaseResource implements LoadableResource {
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
package org.toop.framework.asset.types;
|
package org.toop.framework.asset.types;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import org.toop.framework.asset.ResourceLoader;
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
|
||||||
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
|
||||||
* under a common base name.
|
* common base name.
|
||||||
*
|
*
|
||||||
* <p>Implementing classes allow an {@link ResourceLoader}
|
* <p>Implementing classes allow an {@link ResourceLoader} to automatically merge multiple related
|
||||||
* to automatically merge multiple related files into a single resource instance.</p>
|
* files into a single resource instance.
|
||||||
|
*
|
||||||
|
* <p>Typical use cases include:
|
||||||
*
|
*
|
||||||
* <p>Typical use cases include:</p>
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Localization assets, where multiple `.properties` files (e.g., `messages_en.properties`,
|
* <li>Localization assets, where multiple `.properties` files (e.g., `messages_en.properties`,
|
||||||
* `messages_nl.properties`) are grouped under the same logical resource.</li>
|
* `messages_nl.properties`) are grouped under the same logical resource.
|
||||||
* <li>Sprite sheets, tile sets, or other multi-file resources that logically belong together.</li>
|
* <li>Sprite sheets, tile sets, or other multi-file resources that logically belong together.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Implementing classes must provide:</p>
|
* <p>Implementing classes must provide:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #loadFile(File)}: Logic to load or merge an individual file into the resource.</li>
|
* <li>{@link #loadFile(File)}: Logic to load or merge an individual file into the resource.
|
||||||
* <li>{@link #getBaseName()}: A consistent base name used to group multiple files into this resource.</li>
|
* <li>{@link #getBaseName()}: A consistent base name used to group multiple files into this
|
||||||
|
* resource.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Example usage:</p>
|
* <p>Example usage:
|
||||||
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* public class LocalizationAsset extends BaseResource implements BundledResource {
|
* public class LocalizationAsset extends BaseResource implements BundledResource {
|
||||||
* private final String baseName;
|
* private final String baseName;
|
||||||
@@ -47,8 +50,8 @@ import java.io.File;
|
|||||||
* }
|
* }
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>When used with an asset loader, all files sharing the same base name are
|
* <p>When used with an asset loader, all files sharing the same base name are automatically merged
|
||||||
* automatically merged into a single resource instance.</p>
|
* into a single resource instance.
|
||||||
*/
|
*/
|
||||||
public interface BundledResource {
|
public interface BundledResource {
|
||||||
|
|
||||||
@@ -60,15 +63,15 @@ public interface BundledResource {
|
|||||||
void loadFile(File file);
|
void loadFile(File file);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a base name for grouping multiple files into this single resource.
|
* Return a base name for grouping multiple files into this single resource. Files with the same
|
||||||
* Files with the same base name are automatically merged by the loader.
|
* base name are automatically merged by the loader.
|
||||||
*
|
*
|
||||||
* @return the base name used to identify this bundled resource
|
* @return the base name used to identify this bundled resource
|
||||||
*/
|
*/
|
||||||
String getBaseName();
|
String getBaseName();
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// Returns the name
|
// Returns the name
|
||||||
// */
|
// */
|
||||||
// String getDefaultName();
|
// String getDefaultName();
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,21 @@
|
|||||||
package org.toop.framework.asset.types;
|
package org.toop.framework.asset.types;
|
||||||
|
|
||||||
import org.toop.framework.asset.ResourceLoader;
|
import java.lang.annotation.ElementType;
|
||||||
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;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.lang.annotation.ElementType;
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
import org.toop.framework.asset.resources.BaseResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 ResourceLoader}
|
* <p>This annotation is processed by the {@link ResourceLoader} to automatically register resource
|
||||||
* to automatically register resource types for specific file extensions.
|
* types for specific file extensions. Each extension listed will be mapped to the annotated
|
||||||
* Each extension listed will be mapped to the annotated resource class,
|
* resource class, allowing the loader to instantiate the correct type when scanning files.
|
||||||
* allowing the loader to instantiate the correct type when scanning files.</p>
|
*
|
||||||
|
* <p>Usage example:
|
||||||
*
|
*
|
||||||
* <p>Usage example:</p>
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* @FileExtension({"png", "jpg"})
|
* @FileExtension({"png", "jpg"})
|
||||||
* public class ImageAsset extends BaseResource implements LoadableResource {
|
* public class ImageAsset extends BaseResource implements LoadableResource {
|
||||||
@@ -25,18 +23,20 @@ import java.lang.annotation.ElementType;
|
|||||||
* }
|
* }
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>Key points:</p>
|
* <p>Key points:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The annotation is retained at runtime for reflection-based registration.</li>
|
* <li>The annotation is retained at runtime for reflection-based registration.
|
||||||
* <li>Can only be applied to types (classes) that extend {@link BaseResource}.</li>
|
* <li>Can only be applied to types (classes) that extend {@link BaseResource}.
|
||||||
* <li>Multiple extensions can be specified in the {@code value()} array.</li>
|
* <li>Multiple extensions can be specified in the {@code value()} array.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
public @interface FileExtension {
|
public @interface FileExtension {
|
||||||
/**
|
/**
|
||||||
* The list of file extensions (without leading dot) that the annotated resource class can handle.
|
* The list of file extensions (without leading dot) that the annotated resource class can
|
||||||
|
* handle.
|
||||||
*
|
*
|
||||||
* @return array of file extensions
|
* @return array of file extensions
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,20 +4,23 @@ 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.
|
||||||
* <p>
|
|
||||||
* Any class implementing {@code LoadableResource} is responsible for managing its own
|
|
||||||
* loading and unloading logic, such as reading files, initializing data structures,
|
|
||||||
* or allocating external resources.
|
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>Implementing classes must define the following behaviors:</p>
|
* <p>Any class implementing {@code LoadableResource} is responsible for managing its own loading
|
||||||
|
* and unloading logic, such as reading files, initializing data structures, or allocating external
|
||||||
|
* resources.
|
||||||
|
*
|
||||||
|
* <p>Implementing classes must define the following behaviors:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #load()}: Load the resource into memory or perform necessary initialization.</li>
|
* <li>{@link #load()}: Load the resource into memory or perform necessary initialization.
|
||||||
* <li>{@link #unload()}: Release any held resources or memory when the resource is no longer needed.</li>
|
* <li>{@link #unload()}: Release any held resources or memory when the resource is no longer
|
||||||
* <li>{@link #isLoaded()}: Return {@code true} if the resource has been successfully loaded and is ready for use, {@code false} otherwise.</li>
|
* needed.
|
||||||
|
* <li>{@link #isLoaded()}: Return {@code true} if the resource has been successfully loaded and
|
||||||
|
* is ready for use, {@code false} otherwise.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Typical usage:</p>
|
* <p>Typical usage:
|
||||||
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* public class MyFontAsset extends BaseResource implements LoadableResource {
|
* public class MyFontAsset extends BaseResource implements LoadableResource {
|
||||||
* private boolean loaded = false;
|
* private boolean loaded = false;
|
||||||
@@ -41,19 +44,19 @@ import org.toop.framework.asset.ResourceLoader;
|
|||||||
* }
|
* }
|
||||||
* }</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
|
||||||
* loading by an {@link ResourceLoader} if desired.</p>
|
* {@link ResourceLoader} if desired.
|
||||||
*/
|
*/
|
||||||
public interface LoadableResource {
|
public interface LoadableResource {
|
||||||
/**
|
/**
|
||||||
* Load the resource into memory or initialize it.
|
* Load the resource into memory or initialize it. This method may throw runtime exceptions if
|
||||||
* This method may throw runtime exceptions if loading fails.
|
* loading fails.
|
||||||
*/
|
*/
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unload the resource and free any associated resources.
|
* Unload the resource and free any associated resources. After this call, {@link #isLoaded()}
|
||||||
* After this call, {@link #isLoaded()} should return false.
|
* should return false.
|
||||||
*/
|
*/
|
||||||
void unload();
|
void unload();
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,19 @@ package org.toop.framework.asset.types;
|
|||||||
import org.toop.framework.asset.ResourceLoader;
|
import org.toop.framework.asset.ResourceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marker interface for resources that should be **automatically loaded** by the {@link ResourceLoader}.
|
* 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
|
||||||
* {@link LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the
|
* LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the {@link
|
||||||
* {@link LoadableResource#isLoaded()} check.</p>
|
* LoadableResource#isLoaded()} check.
|
||||||
*
|
*
|
||||||
* <p>When a resource implements {@code PreloadResource}, the {@code ResourceLoader} 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>Typical usage:
|
||||||
*
|
*
|
||||||
* <p>Typical usage:</p>
|
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* public class MyFontAsset extends BaseResource implements PreloadResource {
|
* public class MyFontAsset extends BaseResource implements PreloadResource {
|
||||||
* @Override
|
* @Override
|
||||||
@@ -34,6 +36,6 @@ import org.toop.framework.asset.ResourceLoader;
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>Note: Only use this interface for resources that are safe to load at startup, as it may
|
* <p>Note: Only use this interface for resources that are safe to load at startup, as it may
|
||||||
* increase memory usage or startup time.</p>
|
* increase memory usage or startup time.
|
||||||
*/
|
*/
|
||||||
public interface PreloadResource extends LoadableResource {}
|
public interface PreloadResource extends LoadableResource {}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package org.toop.framework.audio;
|
package org.toop.framework.audio;
|
||||||
|
|
||||||
import com.sun.scenario.Settings;
|
|
||||||
import javafx.scene.media.MediaPlayer;
|
import javafx.scene.media.MediaPlayer;
|
||||||
import org.toop.framework.audio.events.AudioEvents;
|
|
||||||
import org.toop.framework.eventbus.EventFlow;
|
|
||||||
|
|
||||||
import javax.sound.sampled.Clip;
|
import javax.sound.sampled.Clip;
|
||||||
import javax.sound.sampled.FloatControl;
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import org.toop.framework.audio.events.AudioEvents;
|
||||||
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
|
|
||||||
public class AudioVolumeManager {
|
public class AudioVolumeManager {
|
||||||
private final SoundManager sM;
|
private final SoundManager sM;
|
||||||
@@ -15,7 +13,7 @@ public class AudioVolumeManager {
|
|||||||
private double fxVolume = 1.0;
|
private double fxVolume = 1.0;
|
||||||
private double musicVolume = 1.0;
|
private double musicVolume = 1.0;
|
||||||
|
|
||||||
public AudioVolumeManager(SoundManager soundManager){
|
public AudioVolumeManager(SoundManager soundManager) {
|
||||||
this.sM = soundManager;
|
this.sM = soundManager;
|
||||||
|
|
||||||
new EventFlow()
|
new EventFlow()
|
||||||
@@ -25,19 +23,22 @@ public class AudioVolumeManager {
|
|||||||
.listen(this::handleGetCurrentVolume)
|
.listen(this::handleGetCurrentVolume)
|
||||||
.listen(this::handleGetCurrentFxVolume)
|
.listen(this::handleGetCurrentFxVolume)
|
||||||
.listen(this::handleGetCurrentMusicVolume);
|
.listen(this::handleGetCurrentMusicVolume);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMusicVolume(MediaPlayer mediaPlayer){
|
public void updateMusicVolume(MediaPlayer mediaPlayer) {
|
||||||
mediaPlayer.setVolume(this.musicVolume * this.volume);
|
mediaPlayer.setVolume(this.musicVolume * this.volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSoundEffectVolume(Clip clip){
|
public void updateSoundEffectVolume(Clip clip) {
|
||||||
if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)){
|
if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
|
||||||
FloatControl volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
|
FloatControl volumeControl =
|
||||||
|
(FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
float min = volumeControl.getMinimum();
|
float min = volumeControl.getMinimum();
|
||||||
float max = volumeControl.getMaximum();
|
float max = volumeControl.getMaximum();
|
||||||
float dB = (float) (Math.log10(Math.max(this.fxVolume * this.volume, 0.0001)) * 20.0); // convert linear to dB
|
float dB =
|
||||||
|
(float)
|
||||||
|
(Math.log10(Math.max(this.fxVolume * this.volume, 0.0001))
|
||||||
|
* 20.0); // convert linear to dB
|
||||||
dB = Math.max(min, Math.min(max, dB));
|
dB = Math.max(min, Math.min(max, dB));
|
||||||
volumeControl.setValue(dB);
|
volumeControl.setValue(dB);
|
||||||
}
|
}
|
||||||
@@ -50,7 +51,7 @@ public class AudioVolumeManager {
|
|||||||
|
|
||||||
private void handleFxVolumeChange(AudioEvents.ChangeFxVolume event) {
|
private void handleFxVolumeChange(AudioEvents.ChangeFxVolume event) {
|
||||||
this.fxVolume = limitVolume(event.newVolume() / 100);
|
this.fxVolume = limitVolume(event.newVolume() / 100);
|
||||||
for (Clip clip : sM.getActiveSoundEffects().values()){
|
for (Clip clip : sM.getActiveSoundEffects().values()) {
|
||||||
updateSoundEffectVolume(clip);
|
updateSoundEffectVolume(clip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,32 +61,40 @@ public class AudioVolumeManager {
|
|||||||
for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
|
for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
|
||||||
this.updateMusicVolume(mediaPlayer);
|
this.updateMusicVolume(mediaPlayer);
|
||||||
}
|
}
|
||||||
for (Clip clip : sM.getActiveSoundEffects().values()){
|
for (Clip clip : sM.getActiveSoundEffects().values()) {
|
||||||
updateSoundEffectVolume(clip);
|
updateSoundEffectVolume(clip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event){
|
private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event) {
|
||||||
this.musicVolume = limitVolume(event.newVolume() / 100);
|
this.musicVolume = limitVolume(event.newVolume() / 100);
|
||||||
System.out.println(this.musicVolume);
|
System.out.println(this.musicVolume);
|
||||||
System.out.println(this.volume);
|
System.out.println(this.volume);
|
||||||
for (MediaPlayer mediaPlayer : sM.getActiveMusic()){
|
for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
|
||||||
this.updateMusicVolume(mediaPlayer);
|
this.updateMusicVolume(mediaPlayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) {
|
private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) {
|
||||||
new EventFlow().addPostEvent(new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId()))
|
new EventFlow()
|
||||||
|
.addPostEvent(
|
||||||
|
new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId()))
|
||||||
.asyncPostEvent();
|
.asyncPostEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetCurrentFxVolume(AudioEvents.GetCurrentFxVolume event) {
|
private void handleGetCurrentFxVolume(AudioEvents.GetCurrentFxVolume event) {
|
||||||
new EventFlow().addPostEvent(new AudioEvents.GetCurrentFxVolumeResponse(fxVolume * 100, event.snowflakeId()))
|
new EventFlow()
|
||||||
|
.addPostEvent(
|
||||||
|
new AudioEvents.GetCurrentFxVolumeResponse(
|
||||||
|
fxVolume * 100, event.snowflakeId()))
|
||||||
.asyncPostEvent();
|
.asyncPostEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event){
|
private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event) {
|
||||||
new EventFlow().addPostEvent(new AudioEvents.GetCurrentMusicVolumeResponse(musicVolume * 100, event.snowflakeId()))
|
new EventFlow()
|
||||||
|
.addPostEvent(
|
||||||
|
new AudioEvents.GetCurrentMusicVolumeResponse(
|
||||||
|
musicVolume * 100, event.snowflakeId()))
|
||||||
.asyncPostEvent();
|
.asyncPostEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package org.toop.framework.audio;
|
package org.toop.framework.audio;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import javafx.scene.media.MediaPlayer;
|
||||||
|
import javax.sound.sampled.*;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.toop.framework.SnowflakeGenerator;
|
import org.toop.framework.SnowflakeGenerator;
|
||||||
@@ -10,25 +14,20 @@ 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.util.*;
|
|
||||||
import javax.sound.sampled.*;
|
|
||||||
|
|
||||||
public class SoundManager {
|
public class SoundManager {
|
||||||
private static final Logger logger = LogManager.getLogger(SoundManager.class);
|
private static final Logger logger = LogManager.getLogger(SoundManager.class);
|
||||||
private final List<MediaPlayer> activeMusic = new ArrayList<>();
|
private final List<MediaPlayer> activeMusic = new ArrayList<>();
|
||||||
private final Queue<MusicAsset> backgroundMusicQueue = new LinkedList<>();
|
private final Queue<MusicAsset> backgroundMusicQueue = new LinkedList<>();
|
||||||
private final Map<Long, Clip> activeSoundEffects = new HashMap<>();
|
private final Map<Long, Clip> activeSoundEffects = new HashMap<>();
|
||||||
private final HashMap<String, SoundEffectAsset> audioResources = new HashMap<>();
|
private final HashMap<String, SoundEffectAsset> audioResources = new HashMap<>();
|
||||||
private final SnowflakeGenerator idGenerator = new SnowflakeGenerator(); // TODO: Don't create a new generator
|
private final SnowflakeGenerator idGenerator =
|
||||||
|
new SnowflakeGenerator(); // TODO: Don't create a new generator
|
||||||
private final AudioVolumeManager audioVolumeManager = new AudioVolumeManager(this);
|
private final AudioVolumeManager audioVolumeManager = new AudioVolumeManager(this);
|
||||||
|
|
||||||
|
|
||||||
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 (ResourceMeta<SoundEffectAsset> asset : ResourceManager.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) {
|
||||||
@@ -39,13 +38,17 @@ public class SoundManager {
|
|||||||
.listen(this::handlePlaySound)
|
.listen(this::handlePlaySound)
|
||||||
.listen(this::handleStopSound)
|
.listen(this::handleStopSound)
|
||||||
.listen(this::handleMusicStart)
|
.listen(this::handleMusicStart)
|
||||||
.listen(AudioEvents.ClickButton.class, _ -> {
|
.listen(
|
||||||
try {
|
AudioEvents.ClickButton.class,
|
||||||
playSound("medium-button-click.wav", false);
|
_ -> {
|
||||||
} catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
|
try {
|
||||||
logger.error(e);
|
playSound("medium-button-click.wav", false);
|
||||||
}
|
} catch (UnsupportedAudioFileException
|
||||||
});
|
| LineUnavailableException
|
||||||
|
| IOException e) {
|
||||||
|
logger.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePlaySound(AudioEvents.PlayEffect event) {
|
private void handlePlaySound(AudioEvents.PlayEffect event) {
|
||||||
@@ -68,14 +71,13 @@ public class SoundManager {
|
|||||||
|
|
||||||
private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
|
private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
|
||||||
backgroundMusicQueue.clear();
|
backgroundMusicQueue.clear();
|
||||||
List<MusicAsset> shuffledArray = new ArrayList<>(ResourceManager.getAllOfType(MusicAsset.class)
|
List<MusicAsset> shuffledArray =
|
||||||
.stream()
|
new ArrayList<>(
|
||||||
.map(ResourceMeta::getResource)
|
ResourceManager.getAllOfType(MusicAsset.class).stream()
|
||||||
.toList());
|
.map(ResourceMeta::getResource)
|
||||||
|
.toList());
|
||||||
Collections.shuffle(shuffledArray);
|
Collections.shuffle(shuffledArray);
|
||||||
backgroundMusicQueue.addAll(
|
backgroundMusicQueue.addAll(shuffledArray);
|
||||||
shuffledArray
|
|
||||||
);
|
|
||||||
backgroundMusicPlayer();
|
backgroundMusicPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,34 +91,40 @@ public class SoundManager {
|
|||||||
|
|
||||||
MediaPlayer mediaPlayer = new MediaPlayer(ma.getMedia());
|
MediaPlayer mediaPlayer = new MediaPlayer(ma.getMedia());
|
||||||
|
|
||||||
mediaPlayer.setOnEndOfMedia(() -> {
|
mediaPlayer.setOnEndOfMedia(
|
||||||
addBackgroundMusic(ma);
|
() -> {
|
||||||
activeMusic.remove(mediaPlayer);
|
addBackgroundMusic(ma);
|
||||||
mediaPlayer.dispose();
|
activeMusic.remove(mediaPlayer);
|
||||||
ma.unload();
|
mediaPlayer.dispose();
|
||||||
backgroundMusicPlayer(); // play next
|
ma.unload();
|
||||||
});
|
backgroundMusicPlayer(); // play next
|
||||||
|
});
|
||||||
|
|
||||||
mediaPlayer.setOnStopped(() -> {
|
mediaPlayer.setOnStopped(
|
||||||
addBackgroundMusic(ma);
|
() -> {
|
||||||
activeMusic.remove(mediaPlayer);
|
addBackgroundMusic(ma);
|
||||||
ma.unload();
|
activeMusic.remove(mediaPlayer);
|
||||||
});
|
ma.unload();
|
||||||
|
});
|
||||||
|
|
||||||
mediaPlayer.setOnError(() -> {
|
mediaPlayer.setOnError(
|
||||||
addBackgroundMusic(ma);
|
() -> {
|
||||||
activeMusic.remove(mediaPlayer);
|
addBackgroundMusic(ma);
|
||||||
ma.unload();
|
activeMusic.remove(mediaPlayer);
|
||||||
});
|
ma.unload();
|
||||||
|
});
|
||||||
|
|
||||||
audioVolumeManager.updateMusicVolume(mediaPlayer);
|
audioVolumeManager.updateMusicVolume(mediaPlayer);
|
||||||
mediaPlayer.play();
|
mediaPlayer.play();
|
||||||
activeMusic.add(mediaPlayer);
|
activeMusic.add(mediaPlayer);
|
||||||
logger.info("Playing background music: {}", ma.getFile().getName());
|
logger.info("Playing background music: {}", ma.getFile().getName());
|
||||||
logger.info("Background music next in line: {}", backgroundMusicQueue.peek().getFile().getName());
|
logger.info(
|
||||||
|
"Background music next in line: {}",
|
||||||
|
backgroundMusicQueue.peek().getFile().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private long playSound(String audioFileName, boolean loop) throws UnsupportedAudioFileException, LineUnavailableException, IOException {
|
private long playSound(String audioFileName, boolean loop)
|
||||||
|
throws UnsupportedAudioFileException, LineUnavailableException, IOException {
|
||||||
SoundEffectAsset asset = audioResources.get(audioFileName);
|
SoundEffectAsset asset = audioResources.get(audioFileName);
|
||||||
|
|
||||||
// Return -1 which indicates resource wasn't available
|
// Return -1 which indicates resource wasn't available
|
||||||
@@ -134,8 +142,7 @@ public class SoundManager {
|
|||||||
// If supposed to loop make it loop, else just start it once
|
// If supposed to loop make it loop, else just start it once
|
||||||
if (loop) {
|
if (loop) {
|
||||||
clip.loop(Clip.LOOP_CONTINUOUSLY);
|
clip.loop(Clip.LOOP_CONTINUOUSLY);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
clip.start();
|
clip.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,12 +155,13 @@ public class SoundManager {
|
|||||||
activeSoundEffects.put(clipId, clip); // TODO: Do on snowflake for specific sound to stop
|
activeSoundEffects.put(clipId, clip); // TODO: Do on snowflake for specific sound to stop
|
||||||
|
|
||||||
// remove when finished (only for non-looping sounds)
|
// remove when finished (only for non-looping sounds)
|
||||||
clip.addLineListener(event -> {
|
clip.addLineListener(
|
||||||
if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
|
event -> {
|
||||||
activeSoundEffects.remove(clipId);
|
if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
|
||||||
clip.close();
|
activeSoundEffects.remove(clipId);
|
||||||
}
|
clip.close();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Return id so it can be stopped
|
// Return id so it can be stopped
|
||||||
return clipId;
|
return clipId;
|
||||||
@@ -179,7 +187,11 @@ public class SoundManager {
|
|||||||
activeSoundEffects.clear();
|
activeSoundEffects.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Long, Clip> getActiveSoundEffects(){ return this.activeSoundEffects; }
|
public Map<Long, Clip> getActiveSoundEffects() {
|
||||||
|
return this.activeSoundEffects;
|
||||||
|
}
|
||||||
|
|
||||||
public List<MediaPlayer> getActiveMusic() { return activeMusic; }
|
public List<MediaPlayer> getActiveMusic() {
|
||||||
|
return activeMusic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
package org.toop.framework.audio.events;
|
package org.toop.framework.audio.events;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
||||||
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
|
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
|
||||||
import org.toop.framework.eventbus.events.EventsBase;
|
import org.toop.framework.eventbus.events.EventsBase;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class AudioEvents extends EventsBase {
|
public class AudioEvents extends EventsBase {
|
||||||
/** Starts playing a sound. */
|
/** Starts playing a sound. */
|
||||||
public record PlayEffect(String fileName, boolean loop)
|
public record PlayEffect(String fileName, boolean loop) implements EventWithoutSnowflake {}
|
||||||
implements EventWithoutSnowflake {}
|
|
||||||
|
|
||||||
public record StopEffect(long clipId) implements EventWithoutSnowflake {}
|
public record StopEffect(long clipId) implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record StartBackgroundMusic() implements EventWithoutSnowflake {}
|
public record StartBackgroundMusic() implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record ChangeVolume(double newVolume) implements EventWithoutSnowflake {}
|
public record ChangeVolume(double newVolume) implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record ChangeFxVolume(double newVolume) implements EventWithoutSnowflake {}
|
public record ChangeFxVolume(double newVolume) implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record ChangeMusicVolume(double newVolume) implements EventWithoutSnowflake {}
|
public record ChangeMusicVolume(double newVolume) implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record GetCurrentVolume(long snowflakeId) implements EventWithSnowflake {
|
public record GetCurrentVolume(long snowflakeId) implements EventWithSnowflake {
|
||||||
@@ -29,7 +30,9 @@ public class AudioEvents extends EventsBase {
|
|||||||
return snowflakeId;
|
return snowflakeId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public record GetCurrentVolumeResponse(double currentVolume, long snowflakeId) implements EventWithSnowflake {
|
|
||||||
|
public record GetCurrentVolumeResponse(double currentVolume, long snowflakeId)
|
||||||
|
implements EventWithSnowflake {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> result() {
|
public Map<String, Object> result() {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
@@ -65,7 +68,8 @@ public class AudioEvents extends EventsBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record GetCurrentFxVolumeResponse(double currentVolume, long snowflakeId) implements EventWithSnowflake {
|
public record GetCurrentFxVolumeResponse(double currentVolume, long snowflakeId)
|
||||||
|
implements EventWithSnowflake {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> result() {
|
public Map<String, Object> result() {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
@@ -77,7 +81,8 @@ public class AudioEvents extends EventsBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record GetCurrentMusicVolumeResponse(double currentVolume, long snowflakeId) implements EventWithSnowflake {
|
public record GetCurrentMusicVolumeResponse(double currentVolume, long snowflakeId)
|
||||||
|
implements EventWithSnowflake {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> result() {
|
public Map<String, Object> result() {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
@@ -90,5 +95,4 @@ public class AudioEvents extends EventsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record ClickButton() implements EventWithoutSnowflake {}
|
public record ClickButton() implements EventWithoutSnowflake {}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package org.toop.framework.settings;
|
|||||||
public class Settings {
|
public class Settings {
|
||||||
public boolean fullScreen = false;
|
public boolean fullScreen = false;
|
||||||
public String locale = "en";
|
public String locale = "en";
|
||||||
public String theme = "dark";
|
public String theme = "dark";
|
||||||
public String layoutSize = "medium";
|
public String layoutSize = "medium";
|
||||||
public int volume = 100;
|
public int volume = 100;
|
||||||
public int fxVolume = 20;
|
public int fxVolume = 20;
|
||||||
public int musicVolume = 15;
|
public int musicVolume = 15;
|
||||||
|
|||||||
@@ -3,34 +3,37 @@ package org.toop.game;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public abstract class Game {
|
public abstract class Game {
|
||||||
public enum State {
|
public enum State {
|
||||||
NORMAL, DRAW, WIN,
|
NORMAL,
|
||||||
}
|
DRAW,
|
||||||
|
WIN,
|
||||||
|
}
|
||||||
|
|
||||||
public record Move(int position, char value) {}
|
public record Move(int position, char value) {}
|
||||||
|
|
||||||
public static final char EMPTY = (char)0;
|
public static final char EMPTY = (char) 0;
|
||||||
|
|
||||||
public final int rowSize;
|
public final int rowSize;
|
||||||
public final int columnSize;
|
public final int columnSize;
|
||||||
public final char[] board;
|
public final char[] board;
|
||||||
|
|
||||||
protected Game(int rowSize, int columnSize) {
|
protected Game(int rowSize, int columnSize) {
|
||||||
assert rowSize > 0 && columnSize > 0;
|
assert rowSize > 0 && columnSize > 0;
|
||||||
|
|
||||||
this.rowSize = rowSize;
|
this.rowSize = rowSize;
|
||||||
this.columnSize = columnSize;
|
this.columnSize = columnSize;
|
||||||
|
|
||||||
board = new char[rowSize * columnSize];
|
board = new char[rowSize * columnSize];
|
||||||
Arrays.fill(board, EMPTY);
|
Arrays.fill(board, EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Game(Game other) {
|
protected Game(Game other) {
|
||||||
rowSize = other.rowSize;
|
rowSize = other.rowSize;
|
||||||
columnSize = other.columnSize;
|
columnSize = other.columnSize;
|
||||||
board = Arrays.copyOf(other.board, other.board.length);
|
board = Arrays.copyOf(other.board, other.board.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Move[] getLegalMoves();
|
public abstract Move[] getLegalMoves();
|
||||||
public abstract State play(Move move);
|
|
||||||
|
public abstract State play(Move move);
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,27 @@
|
|||||||
package org.toop.game;
|
package org.toop.game;
|
||||||
|
|
||||||
public abstract class TurnBasedGame extends Game {
|
public abstract class TurnBasedGame extends Game {
|
||||||
public final int turns;
|
public final int turns;
|
||||||
|
|
||||||
protected int currentTurn;
|
protected int currentTurn;
|
||||||
|
|
||||||
protected TurnBasedGame(int rowSize, int columnSize, int turns) {
|
protected TurnBasedGame(int rowSize, int columnSize, int turns) {
|
||||||
super(rowSize, columnSize);
|
super(rowSize, columnSize);
|
||||||
assert turns >= 2;
|
assert turns >= 2;
|
||||||
this.turns = turns;
|
this.turns = turns;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TurnBasedGame(TurnBasedGame other) {
|
protected TurnBasedGame(TurnBasedGame other) {
|
||||||
super(other);
|
super(other);
|
||||||
turns = other.turns;
|
turns = other.turns;
|
||||||
currentTurn = other.currentTurn;
|
currentTurn = other.currentTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void nextTurn() {
|
protected void nextTurn() {
|
||||||
currentTurn = (currentTurn + 1) % turns;
|
currentTurn = (currentTurn + 1) % turns;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentTurn() { return currentTurn; }
|
public int getCurrentTurn() {
|
||||||
|
return currentTurn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,17 +3,17 @@ package org.toop.game.othello;
|
|||||||
import org.toop.game.TurnBasedGame;
|
import org.toop.game.TurnBasedGame;
|
||||||
|
|
||||||
public final class Othello extends TurnBasedGame {
|
public final class Othello extends TurnBasedGame {
|
||||||
Othello() {
|
Othello() {
|
||||||
super(8, 8, 2);
|
super(8, 8, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Move[] getLegalMoves() {
|
public Move[] getLegalMoves() {
|
||||||
return new Move[0];
|
return new Move[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public State play(Move move) {
|
public State play(Move move) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@ import org.toop.game.AI;
|
|||||||
import org.toop.game.Game;
|
import org.toop.game.Game;
|
||||||
|
|
||||||
public final class OthelloAI extends AI<Othello> {
|
public final class OthelloAI extends AI<Othello> {
|
||||||
@Override
|
@Override
|
||||||
public Game.Move findBestMove(Othello game, int depth) {
|
public Game.Move findBestMove(Othello game, int depth) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.toop.game.tictactoe;
|
package org.toop.game.tictactoe;
|
||||||
|
|
||||||
import org.toop.game.TurnBasedGame;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import org.toop.game.TurnBasedGame;
|
||||||
|
|
||||||
public final class TicTacToe extends TurnBasedGame {
|
public final class TicTacToe extends TurnBasedGame {
|
||||||
private int movesLeft;
|
private int movesLeft;
|
||||||
@@ -19,8 +18,8 @@ public final class TicTacToe extends TurnBasedGame {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Move[] getLegalMoves() {
|
public Move[] getLegalMoves() {
|
||||||
final ArrayList<Move> legalMoves = new ArrayList<>();
|
final ArrayList<Move> legalMoves = new ArrayList<>();
|
||||||
final char currentValue = getCurrentValue();
|
final char currentValue = getCurrentValue();
|
||||||
|
|
||||||
for (int i = 0; i < board.length; i++) {
|
for (int i = 0; i < board.length; i++) {
|
||||||
if (board[i] == EMPTY) {
|
if (board[i] == EMPTY) {
|
||||||
@@ -44,12 +43,12 @@ public final class TicTacToe extends TurnBasedGame {
|
|||||||
return State.WIN;
|
return State.WIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTurn();
|
nextTurn();
|
||||||
|
|
||||||
if (movesLeft <= 2) {
|
if (movesLeft <= 2) {
|
||||||
if (movesLeft <= 0 || checkForEarlyDraw(this)) {
|
if (movesLeft <= 0 || checkForEarlyDraw(this)) {
|
||||||
return State.DRAW;
|
return State.DRAW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return State.NORMAL;
|
return State.NORMAL;
|
||||||
@@ -60,7 +59,9 @@ public final class TicTacToe extends TurnBasedGame {
|
|||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
final int index = i * 3;
|
final int index = i * 3;
|
||||||
|
|
||||||
if (board[index] != EMPTY && board[index] == board[index + 1] && board[index] == board[index + 2]) {
|
if (board[index] != EMPTY
|
||||||
|
&& board[index] == board[index + 1]
|
||||||
|
&& board[index] == board[index + 2]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +84,7 @@ public final class TicTacToe extends TurnBasedGame {
|
|||||||
|
|
||||||
private boolean checkForEarlyDraw(TicTacToe game) {
|
private boolean checkForEarlyDraw(TicTacToe game) {
|
||||||
for (final Move move : game.getLegalMoves()) {
|
for (final Move move : game.getLegalMoves()) {
|
||||||
final TicTacToe copy = new TicTacToe(game);
|
final TicTacToe copy = new TicTacToe(game);
|
||||||
|
|
||||||
if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) {
|
if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -93,7 +94,7 @@ public final class TicTacToe extends TurnBasedGame {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private char getCurrentValue() {
|
private char getCurrentValue() {
|
||||||
return currentTurn == 0? 'X' : 'O';
|
return currentTurn == 0 ? 'X' : 'O';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,83 +1,81 @@
|
|||||||
package org.toop.game.tictactoe;
|
package org.toop.game.tictactoe;
|
||||||
|
|
||||||
import org.toop.game.Game;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.toop.game.Game;
|
||||||
|
|
||||||
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();
|
game = new TicTacToe();
|
||||||
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user