mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 19:04:49 +00:00
gui refactor
This commit is contained in:
@@ -1,29 +1,24 @@
|
||||
package org.toop.app;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import org.toop.app.layer.Layer;
|
||||
import org.toop.app.layer.layers.MainLayer;
|
||||
import org.toop.app.layer.layers.QuitPopup;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.views.MainView;
|
||||
import org.toop.app.view.views.QuitView;
|
||||
import org.toop.framework.asset.ResourceManager;
|
||||
import org.toop.framework.asset.resources.CssAsset;
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.local.AppContext;
|
||||
import org.toop.local.AppSettings;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Stage;
|
||||
import org.toop.local.AppSettings;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
public final class App extends Application {
|
||||
private static Stage stage;
|
||||
private static Scene scene;
|
||||
private static StackPane root;
|
||||
|
||||
private static Stack<Layer> stack;
|
||||
private static int height;
|
||||
private static int width;
|
||||
|
||||
@@ -37,17 +32,15 @@ public final class App extends Application {
|
||||
public void start(Stage stage) throws Exception {
|
||||
final StackPane root = new StackPane();
|
||||
final Scene scene = new Scene(root);
|
||||
ViewStack.setup(scene);
|
||||
|
||||
stage.setTitle(AppContext.getString("appTitle"));
|
||||
stage.setTitle(AppContext.getString("app-title"));
|
||||
stage.setWidth(1080);
|
||||
stage.setHeight(720);
|
||||
|
||||
stage.setOnCloseRequest(event -> {
|
||||
event.consume();
|
||||
|
||||
if (!isQuitting) {
|
||||
quitPopup();
|
||||
}
|
||||
startQuit();
|
||||
});
|
||||
|
||||
stage.setScene(scene);
|
||||
@@ -57,78 +50,40 @@ public final class App extends Application {
|
||||
|
||||
App.stage = stage;
|
||||
App.scene = scene;
|
||||
App.root = root;
|
||||
|
||||
App.stack = new Stack<>();
|
||||
|
||||
App.width = (int) stage.getWidth();
|
||||
App.height = (int) stage.getHeight();
|
||||
App.width = (int)stage.getWidth();
|
||||
App.height = (int)stage.getHeight();
|
||||
|
||||
App.isQuitting = false;
|
||||
|
||||
final AppSettings settings = new AppSettings();
|
||||
settings.applySettings();
|
||||
|
||||
AppSettings.applySettings();
|
||||
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
|
||||
activate(new MainLayer());
|
||||
|
||||
ViewStack.push(new MainView());
|
||||
}
|
||||
|
||||
public static void activate(Layer layer) {
|
||||
Platform.runLater(() -> {
|
||||
popAll();
|
||||
push(layer);
|
||||
});
|
||||
public static void startQuit() {
|
||||
if (isQuitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewStack.push(new QuitView());
|
||||
isQuitting = true;
|
||||
}
|
||||
|
||||
public static void push(Layer layer) {
|
||||
Platform.runLater(() -> {
|
||||
root.getChildren().addLast(layer.getLayer());
|
||||
stack.push(layer);
|
||||
});
|
||||
}
|
||||
|
||||
public static void pop() {
|
||||
Platform.runLater(() -> {
|
||||
root.getChildren().removeLast();
|
||||
stack.pop();
|
||||
|
||||
isQuitting = false;
|
||||
});
|
||||
}
|
||||
|
||||
public static void popAll() {
|
||||
Platform.runLater(() -> {
|
||||
final int childrenCount = root.getChildren().size();
|
||||
|
||||
for (int i = 0; i < childrenCount; i++) {
|
||||
try {
|
||||
root.getChildren().removeLast();
|
||||
} catch (Exception e) {
|
||||
IO.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
stack.removeAllElements();
|
||||
});
|
||||
}
|
||||
|
||||
public static void quitPopup() {
|
||||
Platform.runLater(() -> {
|
||||
push(new QuitPopup());
|
||||
isQuitting = true;
|
||||
});
|
||||
public static void stopQuit() {
|
||||
ViewStack.pop();
|
||||
isQuitting = false;
|
||||
}
|
||||
|
||||
public static void quit() {
|
||||
ViewStack.cleanup();
|
||||
stage.close();
|
||||
}
|
||||
|
||||
public static void reloadAll() {
|
||||
stage.setTitle(AppContext.getString("appTitle"));
|
||||
|
||||
for (final Layer layer : stack) {
|
||||
layer.reload();
|
||||
}
|
||||
public static void reload() {
|
||||
stage.setTitle(AppContext.getString("app-title"));
|
||||
ViewStack.reload();
|
||||
}
|
||||
|
||||
public static void setFullscreen(boolean fullscreen) {
|
||||
@@ -137,7 +92,7 @@ public final class App extends Application {
|
||||
width = (int) stage.getWidth();
|
||||
height = (int) stage.getHeight();
|
||||
|
||||
reloadAll();
|
||||
reload();
|
||||
}
|
||||
|
||||
public static void setStyle(String theme, String layoutSize) {
|
||||
@@ -147,10 +102,11 @@ public final class App extends Application {
|
||||
scene.getStylesheets().removeLast();
|
||||
}
|
||||
|
||||
scene.getStylesheets().add(ResourceManager.<CssAsset>get("general.css").getUrl());
|
||||
scene.getStylesheets().add(ResourceManager.<CssAsset>get(theme + ".css").getUrl());
|
||||
scene.getStylesheets().add(ResourceManager.<CssAsset>get(layoutSize + ".css").getUrl());
|
||||
|
||||
reloadAll();
|
||||
reload();
|
||||
}
|
||||
|
||||
public static int getWidth() {
|
||||
|
||||
@@ -1,6 +1,41 @@
|
||||
package org.toop.app;
|
||||
|
||||
public record GameInformation(String[] playerName, boolean[] isPlayerHuman,
|
||||
int[] computerDifficulty, int[] computerThinkTime,
|
||||
boolean isConnectionLocal, String serverIP, String serverPort) {
|
||||
}
|
||||
public class GameInformation {
|
||||
public enum Type {
|
||||
TICTACTOE,
|
||||
REVERSI;
|
||||
|
||||
public static int playerCount(Type type) {
|
||||
return switch (type) {
|
||||
case TICTACTOE -> 2;
|
||||
case REVERSI -> 2;
|
||||
};
|
||||
}
|
||||
|
||||
public static int maxDepth(Type type) {
|
||||
return switch (type) {
|
||||
case TICTACTOE -> 5;
|
||||
case REVERSI -> 0; // Todo
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class Player {
|
||||
public String name = "";
|
||||
public boolean isHuman = true;
|
||||
public int computerDifficulty = 0;
|
||||
public int computerThinkTime = 1;
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
public final Player[] players;
|
||||
|
||||
public GameInformation(Type type) {
|
||||
this.type = type;
|
||||
players = new Player[Type.playerCount(type)];
|
||||
|
||||
for (int i = 0; i < players.length; i++) {
|
||||
players[i] = new Player();
|
||||
}
|
||||
}
|
||||
}
|
||||
188
app/src/main/java/org/toop/app/Server.java
Normal file
188
app/src/main/java/org/toop/app/Server.java
Normal file
@@ -0,0 +1,188 @@
|
||||
package org.toop.app;
|
||||
|
||||
import org.toop.app.game.TicTacToeGame;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.views.ChallengeView;
|
||||
import org.toop.app.view.views.ErrorView;
|
||||
import org.toop.app.view.views.SendChallengeView;
|
||||
import org.toop.app.view.views.ServerView;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public final class Server {
|
||||
private String user = "";
|
||||
|
||||
private long clientId = -1;
|
||||
private List<String> onlinePlayers = new CopyOnWriteArrayList<String>();
|
||||
|
||||
private ServerView view;
|
||||
|
||||
private boolean isPolling = true;
|
||||
|
||||
public static GameInformation.Type gameToType(String game) {
|
||||
if (game.equalsIgnoreCase("tic-tac-toe")) {
|
||||
return GameInformation.Type.TICTACTOE;
|
||||
} else if (game.equalsIgnoreCase("reversi")) {
|
||||
return GameInformation.Type.REVERSI;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Server(String ip, String port, String user) {
|
||||
if (ip.split("\\.").length < 4) {
|
||||
ViewStack.push(new ErrorView("\"" + ip + "\" " + AppContext.getString("is-not-a-valid-ip-address")));
|
||||
return;
|
||||
}
|
||||
|
||||
int parsedPort = -1;
|
||||
|
||||
try {
|
||||
parsedPort = Integer.parseInt(port);
|
||||
} catch (NumberFormatException _) {
|
||||
ViewStack.push(new ErrorView("\"" + port + "\" " + AppContext.getString("is-not-a-valid-port")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.isEmpty()) {
|
||||
ViewStack.push(new ErrorView(AppContext.getString("invalid-username")));
|
||||
return;
|
||||
}
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(NetworkEvents.StartClient.class, ip, parsedPort)
|
||||
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
||||
this.user = user;
|
||||
clientId = e.clientId();
|
||||
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendLogin(clientId, user)).postEvent();
|
||||
|
||||
view = new ServerView(user, this::sendChallenge, this::disconnect);
|
||||
ViewStack.push(view);
|
||||
|
||||
startPopulateThread();
|
||||
}).postEvent();
|
||||
|
||||
new EventFlow().listen(this::handleReceivedChallenge);
|
||||
}
|
||||
|
||||
private void populatePlayerList() {
|
||||
new EventFlow().listen(NetworkEvents.PlayerlistResponse.class, e -> {
|
||||
if (e.clientId() == clientId) {
|
||||
onlinePlayers = new ArrayList<String>(List.of(e.playerlist()));
|
||||
onlinePlayers.removeIf(name -> name.equalsIgnoreCase(user));
|
||||
|
||||
view.update(onlinePlayers);
|
||||
}
|
||||
});
|
||||
|
||||
final EventFlow sendGetPlayerList = new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(clientId));
|
||||
|
||||
while (isPolling) {
|
||||
sendGetPlayerList.postEvent();
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException _) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendChallenge(String opponent) {
|
||||
ViewStack.push(new SendChallengeView(this, opponent, (playerInformation, gameType) -> {
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType))
|
||||
.listen(NetworkEvents.GameMatchResponse.class, e -> {
|
||||
if (e.clientId() == clientId) {
|
||||
isPolling = false;
|
||||
onlinePlayers.clear();
|
||||
|
||||
final GameInformation.Type type = gameToType(gameType);
|
||||
final int myTurn = e.playerToMove().equalsIgnoreCase(e.opponent())? 1 : 0;
|
||||
|
||||
final GameInformation information = new GameInformation(type);
|
||||
information.players[0] = playerInformation;
|
||||
information.players[0].name = user;
|
||||
information.players[1].name = opponent;
|
||||
|
||||
new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame);
|
||||
}
|
||||
}).postEvent();
|
||||
}));
|
||||
}
|
||||
|
||||
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
|
||||
String challengerName = response.challengerName();
|
||||
challengerName = challengerName.substring(challengerName.indexOf("\"") + 1);
|
||||
challengerName = challengerName.substring(0, challengerName.indexOf("\""));
|
||||
|
||||
String gameType = response.gameType();
|
||||
gameType = gameType.substring(gameType.indexOf("\"") + 1);
|
||||
gameType = gameType.substring(0, gameType.indexOf("\""));
|
||||
|
||||
final String finalGameType = gameType;
|
||||
|
||||
ViewStack.push(new ChallengeView(challengerName, gameType, (playerInformation) -> {
|
||||
final int challengeId = Integer.parseInt(response.challengeId().substring(18, response.challengeId().length() - 2));
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent();
|
||||
|
||||
ViewStack.pop();
|
||||
|
||||
new EventFlow().listen(NetworkEvents.GameMatchResponse.class, e -> {
|
||||
if (e.clientId() == clientId) {
|
||||
isPolling = false;
|
||||
onlinePlayers.clear();
|
||||
|
||||
final GameInformation.Type type = gameToType(finalGameType);
|
||||
final int myTurn = e.playerToMove().equalsIgnoreCase(e.opponent())? 1 : 0;
|
||||
|
||||
final GameInformation information = new GameInformation(type);
|
||||
information.players[0] = playerInformation;
|
||||
information.players[0].name = user;
|
||||
information.players[1].name = e.opponent();
|
||||
|
||||
switch (type) {
|
||||
case TICTACTOE:
|
||||
new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame);
|
||||
break;
|
||||
case REVERSI:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
// Todo
|
||||
}
|
||||
|
||||
private void forfeitGame() {
|
||||
// Todo
|
||||
}
|
||||
|
||||
private void exitGame() {
|
||||
forfeitGame();
|
||||
|
||||
ViewStack.push(view);
|
||||
startPopulateThread();
|
||||
}
|
||||
|
||||
private void startPopulateThread() {
|
||||
isPolling = true;
|
||||
|
||||
final Thread populateThread = new Thread(this::populatePlayerList);
|
||||
populateThread.setDaemon(false);
|
||||
populateThread.start();
|
||||
}
|
||||
|
||||
public List<String> getGamesList() {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
list.add("tic-tac-toe"); // Todo: get games list from server and check if the game is supported
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ public abstract class GameCanvas {
|
||||
|
||||
protected final Color color;
|
||||
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected final int width;
|
||||
protected final int height;
|
||||
|
||||
protected final int rows;
|
||||
protected final int columns;
|
||||
@@ -28,6 +28,9 @@ public abstract class GameCanvas {
|
||||
protected final Cell[] cells;
|
||||
|
||||
protected GameCanvas(Color color, int width, int height, int rows, int columns, int gapSize, boolean edges, Consumer<Integer> onCellClicked) {
|
||||
width += gapSize * 2;
|
||||
height += gapSize * 2;
|
||||
|
||||
canvas = new Canvas(width, height);
|
||||
graphics = canvas.getGraphicsContext2D();
|
||||
|
||||
@@ -44,14 +47,14 @@ public abstract class GameCanvas {
|
||||
|
||||
cells = new Cell[rows * columns];
|
||||
|
||||
final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows;
|
||||
final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns;
|
||||
final float cellWidth = ((float) width - gapSize * rows) / rows;
|
||||
final float cellHeight = ((float) height - gapSize * columns) / columns;
|
||||
|
||||
for (int y = 0; y < columns; y++) {
|
||||
final float startY = y * cellHeight + y * gapSize;
|
||||
final float startY = gapSize + y * cellHeight + y * gapSize;
|
||||
|
||||
for (int x = 0; x < rows; x++) {
|
||||
final float startX = x * cellWidth + x * gapSize;
|
||||
final float startX = gapSize + x * cellWidth + x * gapSize;
|
||||
cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
|
||||
}
|
||||
}
|
||||
@@ -61,11 +64,11 @@ public abstract class GameCanvas {
|
||||
return;
|
||||
}
|
||||
|
||||
final int column = (int) ((event.getX() / width) * rows);
|
||||
final int row = (int) ((event.getY() / height) * columns);
|
||||
final int column = (int)((event.getX() / this.width) * rows);
|
||||
final int row = (int)((event.getY() / this.height) * columns);
|
||||
|
||||
event.consume();
|
||||
onCellClicked.accept(row * rows + column);
|
||||
onCellClicked.accept(column + row * rows);
|
||||
});
|
||||
|
||||
render();
|
||||
@@ -79,44 +82,33 @@ public abstract class GameCanvas {
|
||||
graphics.setFill(color);
|
||||
|
||||
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 + gapSize);
|
||||
}
|
||||
|
||||
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, gapSize);
|
||||
}
|
||||
|
||||
if (edges) {
|
||||
graphics.fillRect(-gapSize, 0, gapSize, height);
|
||||
graphics.fillRect(0, -gapSize, width, gapSize);
|
||||
graphics.fillRect(0, 0, gapSize, height + gapSize);
|
||||
graphics.fillRect(0, 0, width + gapSize, gapSize);
|
||||
|
||||
graphics.fillRect(width - gapSize, 0, gapSize, height);
|
||||
graphics.fillRect(0, height - gapSize, width, gapSize);
|
||||
graphics.fillRect(width + gapSize, 0, gapSize, height + gapSize * 2);
|
||||
graphics.fillRect(0, height + gapSize, width + gapSize * 2, gapSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(Color color, int cell) {
|
||||
final float x = cells[cell].x() + gapSize;
|
||||
final float y = cells[cell].y() + gapSize;
|
||||
public void fill(Color color, int cell) {
|
||||
final float x = cells[cell].x();
|
||||
final float y = cells[cell].y();
|
||||
|
||||
final float width = cells[cell].width() - gapSize * 2;
|
||||
final float height = cells[cell].height() - gapSize * 2;
|
||||
final float width = cells[cell].width();
|
||||
final float height = cells[cell].height();
|
||||
|
||||
graphics.setFill(color);
|
||||
graphics.fillRect(x, y, width, height);
|
||||
}
|
||||
|
||||
public void resize(int width, int height) {
|
||||
canvas.setWidth(width);
|
||||
canvas.setHeight(height);
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
clear();
|
||||
render();
|
||||
}
|
||||
|
||||
public Canvas getCanvas() {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import javafx.scene.paint.Color;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TicTacToeCanvas extends GameCanvas {
|
||||
public final class TicTacToeCanvas extends GameCanvas {
|
||||
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, 30, false, onCellClicked);
|
||||
}
|
||||
|
||||
public void drawX(Color color, int cell) {
|
||||
|
||||
220
app/src/main/java/org/toop/app/game/TicTacToeGame.java
Normal file
220
app/src/main/java/org/toop/app/game/TicTacToeGame.java
Normal file
@@ -0,0 +1,220 @@
|
||||
package org.toop.app.game;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.canvas.TicTacToeCanvas;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.views.GameView;
|
||||
import org.toop.app.view.views.LocalMultiplayerView;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
import org.toop.game.Game;
|
||||
import org.toop.game.tictactoe.TicTacToe;
|
||||
import org.toop.game.tictactoe.TicTacToeAI;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public final class TicTacToeGame {
|
||||
private final GameInformation information;
|
||||
|
||||
private final int myTurn;
|
||||
private final BlockingQueue<Game.Move> moveQueue;
|
||||
|
||||
private final TicTacToe game;
|
||||
private final TicTacToeAI ai;
|
||||
|
||||
private final GameView view;
|
||||
private final TicTacToeCanvas canvas;
|
||||
|
||||
public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit) {
|
||||
this.information = information;
|
||||
|
||||
this.myTurn = myTurn;
|
||||
moveQueue = new LinkedBlockingQueue<Game.Move>();
|
||||
|
||||
game = new TicTacToe();
|
||||
ai = new TicTacToeAI();
|
||||
|
||||
if (onForfeit == null || onExit == null) {
|
||||
view = new GameView(null, () -> {
|
||||
ViewStack.push(new LocalMultiplayerView(information));
|
||||
});
|
||||
} else {
|
||||
view = new GameView(onForfeit, onExit);
|
||||
}
|
||||
|
||||
canvas = new TicTacToeCanvas(Color.GRAY,
|
||||
(App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,
|
||||
(cell) -> {
|
||||
if (onForfeit == null || onExit == null) {
|
||||
if (information.players[game.getCurrentTurn()].isHuman) {
|
||||
final char value = game.getCurrentTurn() == 0? 'X' : 'O';
|
||||
|
||||
try {
|
||||
moveQueue.put(new Game.Move(cell, value));
|
||||
} catch (InterruptedException _) {}
|
||||
}
|
||||
} else {
|
||||
if (information.players[0].isHuman) {
|
||||
final char value = myTurn == 0? 'X' : 'O';
|
||||
|
||||
try {
|
||||
moveQueue.put(new Game.Move(cell, value));
|
||||
} catch (InterruptedException _) {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
view.add(Pos.CENTER, canvas.getCanvas());
|
||||
ViewStack.push(view);
|
||||
|
||||
if (onForfeit == null || onExit == null) {
|
||||
new Thread(this::localGameThread).start();
|
||||
} else {
|
||||
new EventFlow()
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse)
|
||||
.listen(NetworkEvents.ReceivedMessage.class, this::onReceivedMessage);
|
||||
|
||||
setGameLabels(myTurn == 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void localGameThread() {
|
||||
boolean isRunning = true;
|
||||
|
||||
while (isRunning) {
|
||||
final int currentTurn = game.getCurrentTurn();
|
||||
final char currentValue = currentTurn == 0? 'X' : 'O';
|
||||
final int nextTurn = (currentTurn + 1) % GameInformation.Type.playerCount(information.type);
|
||||
|
||||
view.nextPlayer(information.players[currentTurn].isHuman,
|
||||
information.players[currentTurn].name,
|
||||
String.valueOf(currentValue),
|
||||
information.players[nextTurn].name);
|
||||
|
||||
Game.Move move = null;
|
||||
|
||||
if (information.players[currentTurn].isHuman) {
|
||||
try {
|
||||
final Game.Move wants = moveQueue.take();
|
||||
final Game.Move[] legalMoves = game.getLegalMoves();
|
||||
|
||||
for (final Game.Move legalMove : legalMoves) {
|
||||
if (legalMove.position() == wants.position() &&
|
||||
legalMove.value() == wants.value()) {
|
||||
move = wants;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException _) {}
|
||||
} else {
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
move = ai.findBestMove(game, information.players[currentTurn].computerDifficulty);
|
||||
|
||||
if (information.players[currentTurn].computerThinkTime > 0) {
|
||||
final long elapsedTime = System.currentTimeMillis() - start;
|
||||
final long sleepTime = information.players[currentTurn].computerThinkTime * 1000L - elapsedTime;
|
||||
|
||||
try {
|
||||
Thread.sleep((long)(sleepTime * Math.random()));
|
||||
} catch (InterruptedException _) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (move == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Game.State state = game.play(move);
|
||||
|
||||
if (move.value() == 'X') {
|
||||
canvas.drawX(Color.INDIANRED, move.position());
|
||||
} else if (move.value() == 'O') {
|
||||
canvas.drawO(Color.ROYALBLUE, move.position());
|
||||
}
|
||||
|
||||
if (state != Game.State.NORMAL) {
|
||||
if (state == Game.State.WIN) {
|
||||
view.gameOver(true, information.players[currentTurn].name);
|
||||
} else if (state == Game.State.DRAW) {
|
||||
view.gameOver(false, "");
|
||||
}
|
||||
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onMoveResponse(NetworkEvents.GameMoveResponse response) {
|
||||
char playerChar;
|
||||
|
||||
if (response.player().equalsIgnoreCase(information.players[0].name)) {
|
||||
playerChar = myTurn == 0? 'X' : 'O';
|
||||
} else {
|
||||
playerChar = myTurn == 0? 'O' : 'X';
|
||||
}
|
||||
|
||||
final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar);
|
||||
final Game.State state = game.play(move);
|
||||
|
||||
if (state != Game.State.NORMAL) {
|
||||
if (state == Game.State.WIN) {
|
||||
if (response.player().equalsIgnoreCase(information.players[0].name)) {
|
||||
view.gameOver(true, information.players[0].name);
|
||||
} else {
|
||||
view.gameOver(false, information.players[1].name);
|
||||
}
|
||||
} else if (state == Game.State.DRAW) {
|
||||
view.gameOver(false, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (move.value() == 'X') {
|
||||
canvas.drawX(Color.RED, move.position());
|
||||
} else if (move.value() == 'O') {
|
||||
canvas.drawO(Color.BLUE, move.position());
|
||||
}
|
||||
|
||||
setGameLabels(game.getCurrentTurn() == myTurn);
|
||||
}
|
||||
|
||||
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
|
||||
moveQueue.clear();
|
||||
|
||||
int position = -1;
|
||||
|
||||
if (information.players[0].isHuman) {
|
||||
try {
|
||||
position = moveQueue.take().position();
|
||||
} catch (InterruptedException _) {}
|
||||
} else {
|
||||
final Game.Move move = ai.findBestMove(game, information.players[0].computerDifficulty);
|
||||
|
||||
assert move != null;
|
||||
position = move.position();
|
||||
}
|
||||
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short)position))
|
||||
.postEvent();
|
||||
}
|
||||
|
||||
private void onReceivedMessage(NetworkEvents.ReceivedMessage msg) {
|
||||
view.updateChat("anon", msg.message());
|
||||
}
|
||||
|
||||
private void setGameLabels(boolean isMe) {
|
||||
final int currentTurn = game.getCurrentTurn();
|
||||
final char currentValue = currentTurn == 0? 'X' : 'O';
|
||||
|
||||
view.nextPlayer(isMe,
|
||||
information.players[isMe? 0 : 1].name,
|
||||
String.valueOf(currentValue),
|
||||
information.players[isMe? 1 : 0].name);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.toop.app.layer;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
public abstract class Container {
|
||||
public abstract Region getContainer();
|
||||
|
||||
public abstract void addNodes(Node... nodes);
|
||||
public abstract void addContainer(Container container, boolean fill);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.toop.app.layer;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.canvas.GameCanvas;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
public abstract class Layer {
|
||||
protected StackPane layer;
|
||||
protected Region background;
|
||||
|
||||
protected Layer(String... backgroundStyles) {
|
||||
layer = new StackPane();
|
||||
|
||||
background = new Region();
|
||||
background.getStyleClass().addAll(backgroundStyles);
|
||||
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||
|
||||
layer.getChildren().addLast(background);
|
||||
}
|
||||
|
||||
protected void addContainer(Container container, Pos position, int xOffset, int yOffset, int widthPercent, int heightPercent) {
|
||||
StackPane.setAlignment(container.getContainer(), position);
|
||||
|
||||
final double widthUnit = App.getWidth() / 100.0;
|
||||
final double heightUnit = App.getHeight() / 100.0;
|
||||
|
||||
if (widthPercent > 0) {
|
||||
container.getContainer().setMaxWidth(widthPercent * widthUnit);
|
||||
} else {
|
||||
container.getContainer().setMaxWidth(Region.USE_PREF_SIZE);
|
||||
}
|
||||
|
||||
if (heightPercent > 0) {
|
||||
container.getContainer().setMaxHeight(heightPercent * heightUnit);
|
||||
} else {
|
||||
container.getContainer().setMaxHeight(Region.USE_PREF_SIZE);
|
||||
}
|
||||
|
||||
container.getContainer().setTranslateX(xOffset * widthUnit);
|
||||
container.getContainer().setTranslateY(yOffset * heightUnit);
|
||||
|
||||
layer.getChildren().addLast(container.getContainer());
|
||||
}
|
||||
|
||||
protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) {
|
||||
StackPane.setAlignment(canvas.getCanvas(), position);
|
||||
|
||||
final double widthUnit = App.getWidth() / 100.0;
|
||||
final double heightUnit = App.getHeight() / 100.0;
|
||||
|
||||
canvas.getCanvas().setTranslateX(xOffset * widthUnit);
|
||||
canvas.getCanvas().setTranslateY(yOffset * heightUnit);
|
||||
|
||||
layer.getChildren().addLast(canvas.getCanvas());
|
||||
}
|
||||
|
||||
protected void pop() {
|
||||
if (layer.getChildren().size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
layer.getChildren().removeLast();
|
||||
}
|
||||
|
||||
protected void popAll() {
|
||||
final int containers = layer.getChildren().size();
|
||||
|
||||
for (int i = 1; i < containers; i++) {
|
||||
layer.getChildren().removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
public StackPane getLayer() {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public abstract void reload();
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package org.toop.app.layer;
|
||||
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class NodeBuilder {
|
||||
public static void addCss(Node node, String... cssClasses) {
|
||||
node.getStyleClass().addAll(cssClasses);
|
||||
}
|
||||
|
||||
public static void setCss(Node node, String... cssClasses) {
|
||||
node.getStyleClass().removeAll();
|
||||
node.getStyleClass().addAll(cssClasses);
|
||||
}
|
||||
|
||||
public static Text header(String x) {
|
||||
final Text element = new Text(x);
|
||||
setCss(element, "text-primary", "text-header");
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static Text text(String x) {
|
||||
final Text element = new Text(x);
|
||||
setCss(element, "text-secondary", "text-normal");
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static Label button(String x, Runnable runnable) {
|
||||
final Label element = new Label(x);
|
||||
setCss(element, "button", "text-normal");
|
||||
|
||||
element.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
runnable.run();
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static Label toggle(String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
|
||||
final Label element = new Label(toggled ? x2 : x1);
|
||||
setCss(element, "toggle", "text-normal");
|
||||
|
||||
final BooleanProperty checked = new SimpleBooleanProperty(toggled);
|
||||
|
||||
element.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
checked.set(!checked.get());
|
||||
|
||||
if (checked.get()) {
|
||||
element.setText(x1);
|
||||
} else {
|
||||
element.setText(x2);
|
||||
}
|
||||
|
||||
consumer.accept(checked.get());
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static Slider slider(int max, int initial, Consumer<Integer> consumer) {
|
||||
final Slider element = new Slider(0, max, initial);
|
||||
setCss(element, "bg-slider-track");
|
||||
|
||||
element.setMinorTickCount(0);
|
||||
element.setMajorTickUnit(1);
|
||||
element.setBlockIncrement(1);
|
||||
|
||||
element.setSnapToTicks(true);
|
||||
element.setShowTickLabels(true);
|
||||
|
||||
element.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
element.valueProperty().addListener((_, _, newValue) -> {
|
||||
consumer.accept(newValue.intValue());
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static TextField input(String x, Consumer<String> consumer) {
|
||||
final TextField element = new TextField(x);
|
||||
setCss(element, "input", "text-normal");
|
||||
|
||||
element.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
element.textProperty().addListener((_, _, newValue) -> {
|
||||
consumer.accept(newValue);
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static <T> ChoiceBox<T> choiceBox(Consumer<T> consumer) {
|
||||
final ChoiceBox<T> element = new ChoiceBox<>();
|
||||
setCss(element, "choice-box", "text-normal");
|
||||
|
||||
element.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
element.valueProperty().addListener((_, _, newValue) -> {
|
||||
consumer.accept(newValue);
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public static Separator separator() {
|
||||
final Separator element = new Separator(Orientation.HORIZONTAL);
|
||||
setCss(element, "separator");
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.toop.app.layer;
|
||||
|
||||
import org.toop.app.App;
|
||||
|
||||
public abstract class Popup extends Layer {
|
||||
protected Popup(boolean popOnBackground, String... backgroundStyles) {
|
||||
super(backgroundStyles);
|
||||
|
||||
if (popOnBackground) {
|
||||
background.setOnMouseClicked(_ -> {
|
||||
App.pop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected Popup(boolean popOnBackground) {
|
||||
this(popOnBackground, "bg-popup");
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.toop.app.layer.containers;
|
||||
|
||||
import org.toop.app.layer.Container;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
public final class HorizontalContainer extends Container {
|
||||
private final HBox container;
|
||||
|
||||
public HorizontalContainer(int spacing, String... cssClasses) {
|
||||
container = new HBox(spacing);
|
||||
container.getStyleClass().addAll(cssClasses);
|
||||
}
|
||||
|
||||
public HorizontalContainer(int spacing) {
|
||||
this(spacing, "container");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNodes(Node... nodes) {
|
||||
container.getChildren().addAll(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContainer(Container container, boolean fill) {
|
||||
if (fill) {
|
||||
container.getContainer().setMinSize(0, 0);
|
||||
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||
HBox.setHgrow(container.getContainer(), Priority.ALWAYS);
|
||||
} else {
|
||||
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||
}
|
||||
|
||||
this.container.getChildren().add(container.getContainer());
|
||||
|
||||
if (fill) {
|
||||
balanceChildWidths();
|
||||
}
|
||||
}
|
||||
|
||||
private void balanceChildWidths() {
|
||||
final ObservableList<Node> children = container.getChildren();
|
||||
final double widthPerChild = container.getWidth() / children.size();
|
||||
|
||||
for (final Node child : children) {
|
||||
if (child instanceof Region) {
|
||||
((Region) child).setPrefWidth(widthPerChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.toop.app.layer.containers;
|
||||
|
||||
import org.toop.app.layer.Container;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
public final class VerticalContainer extends Container {
|
||||
private final VBox container;
|
||||
|
||||
public VerticalContainer(int spacing, String... cssClasses) {
|
||||
container = new VBox(spacing);
|
||||
container.getStyleClass().addAll(cssClasses);
|
||||
}
|
||||
|
||||
public VerticalContainer(int spacing) {
|
||||
this(spacing, "container");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNodes(Node... nodes) {
|
||||
container.getChildren().addAll(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContainer(Container container, boolean fill) {
|
||||
if (fill) {
|
||||
container.getContainer().setMinSize(0, 0);
|
||||
container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||
VBox.setVgrow(container.getContainer(), Priority.ALWAYS);
|
||||
} else {
|
||||
container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||
}
|
||||
|
||||
this.container.getChildren().add(container.getContainer());
|
||||
|
||||
if (fill) {
|
||||
balanceChildHeights();
|
||||
}
|
||||
}
|
||||
|
||||
private void balanceChildHeights() {
|
||||
final ObservableList<Node> children = container.getChildren();
|
||||
final double heightPerChild = container.getHeight() / children.size();
|
||||
|
||||
for (final Node child : children) {
|
||||
if (child instanceof Region) {
|
||||
((Region) child).setPrefHeight(heightPerChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
package org.toop.app.layer.layers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.Layer;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.Popup;
|
||||
import org.toop.app.layer.containers.HorizontalContainer;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.app.layer.layers.game.TicTacToeLayer;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListView;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
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 {
|
||||
private static Timer pollTimer = new Timer();
|
||||
|
||||
private static class ChallengePopup extends Popup {
|
||||
private final GameInformation information;
|
||||
|
||||
private final String challenger;
|
||||
private final String game;
|
||||
|
||||
private final long clientID;
|
||||
private final int challengeID;
|
||||
|
||||
public ChallengePopup(GameInformation information, String challenger, String game, long clientID, String challengeID) {
|
||||
super(false, "bg-popup");
|
||||
|
||||
this.information = information;
|
||||
|
||||
this.challenger = challenger;
|
||||
this.game = game;
|
||||
|
||||
this.clientID = clientID;
|
||||
this.challengeID = Integer.parseInt(challengeID.substring(18, challengeID.length() - 2));
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final var challengeText = NodeBuilder.header(AppContext.getString("challengeText"));
|
||||
final var challengerNameText = NodeBuilder.header(challenger);
|
||||
|
||||
final var gameText = NodeBuilder.text(AppContext.getString("gameIsText"));
|
||||
final var gameNameText = NodeBuilder.text(game);
|
||||
|
||||
final var acceptButton = NodeBuilder.button(AppContext.getString("accept"), () -> {
|
||||
pollTimer.cancel();
|
||||
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientID, challengeID)).postEvent();
|
||||
App.activate(new TicTacToeLayer(information, clientID));
|
||||
});
|
||||
|
||||
final var denyButton = NodeBuilder.button(AppContext.getString("deny"), () -> {
|
||||
App.pop();
|
||||
});
|
||||
|
||||
final Container controlContainer = new HorizontalContainer(30);
|
||||
controlContainer.addNodes(acceptButton, denyButton);
|
||||
|
||||
final Container mainContainer = new VerticalContainer(30);
|
||||
mainContainer.addNodes(challengeText, challengerNameText);
|
||||
mainContainer.addNodes(gameText, gameNameText);
|
||||
|
||||
mainContainer.addContainer(controlContainer, false);
|
||||
|
||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
||||
}
|
||||
}
|
||||
|
||||
GameInformation information;
|
||||
long clientId;
|
||||
String user;
|
||||
List<String> onlinePlayers = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ConnectedLayer(GameInformation information) {
|
||||
super("bg-primary");
|
||||
|
||||
this.information = information;
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(NetworkEvents.StartClient.class, information.serverIP(), Integer.parseInt(information.serverPort()))
|
||||
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
||||
clientId = e.clientId();
|
||||
user = information.playerName()[0].replaceAll("\\s+", "");
|
||||
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendLogin(this.clientId, this.user)).postEvent();
|
||||
|
||||
Thread popThread = new Thread(this::populatePlayerList);
|
||||
popThread.setDaemon(false);
|
||||
popThread.start();
|
||||
}).postEvent();
|
||||
|
||||
new EventFlow().listen(this::handleReceivedChallenge);
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
private void populatePlayerList() {
|
||||
EventFlow sendGetPlayerList = new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId));
|
||||
new EventFlow().listen(NetworkEvents.PlayerlistResponse.class, e -> {
|
||||
if (e.clientId() == this.clientId) {
|
||||
List<String> playerList = new java.util.ArrayList<>(List.of(e.playerlist())); // TODO: Garbage, but works
|
||||
playerList.removeIf(name -> name.equalsIgnoreCase(user));
|
||||
if (this.onlinePlayers != playerList) {
|
||||
this.onlinePlayers.clear();
|
||||
this.onlinePlayers.addAll(playerList);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
TimerTask task = new TimerTask() {
|
||||
public void run() {
|
||||
sendGetPlayerList.postEvent();
|
||||
Platform.runLater(() -> reload());
|
||||
}
|
||||
};
|
||||
|
||||
pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later
|
||||
}
|
||||
|
||||
private void sendChallenge(String oppUsername, String gameType) {
|
||||
final AtomicInteger challengeId = new AtomicInteger(-1);
|
||||
|
||||
if (onlinePlayers.contains(oppUsername)) {
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType))
|
||||
.listen(NetworkEvents.ChallengeResponse.class, e -> {
|
||||
challengeId.set(Integer.parseInt(e.challengeId().substring(18, e.challengeId().length() - 2)));
|
||||
})
|
||||
.listen(NetworkEvents.GameMatchResponse.class, e -> {
|
||||
if (e.clientId() == this.clientId) {
|
||||
pollTimer.cancel();
|
||||
App.activate(new TicTacToeLayer(information, this.clientId, e));
|
||||
}
|
||||
}, false).postEvent();
|
||||
// ^
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
|
||||
App.push(new ChallengePopup(information, response.challengerName(), response.gameType(), clientId, response.challengeId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
ListView<Label> players = new ListView<>();
|
||||
|
||||
for (int i = 0; i < onlinePlayers.size(); i++) {
|
||||
int finalI = i;
|
||||
players.getItems().add(NodeBuilder.button(onlinePlayers.get(i), () -> {
|
||||
String clickedPlayer = onlinePlayers.get(finalI);
|
||||
sendChallenge(clickedPlayer, "tic-tac-toe");
|
||||
}));
|
||||
}
|
||||
|
||||
final Container playersContainer = new VerticalContainer(10);
|
||||
playersContainer.addNodes(players);
|
||||
|
||||
addContainer(playersContainer, Pos.CENTER, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.toop.app.layer.layers;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.Popup;
|
||||
import org.toop.app.layer.containers.HorizontalContainer;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
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 {
|
||||
private final int lineHeight = 100;
|
||||
|
||||
public CreditsPopup() {
|
||||
super(true, "bg-primary");
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final String[] credits = {
|
||||
AppContext.getString("scrumMaster") + ": Stef",
|
||||
AppContext.getString("productOwner") + ": Omar",
|
||||
AppContext.getString("mergeCommander") + ": Bas",
|
||||
AppContext.getString("localization") + ": Ticho",
|
||||
AppContext.getString("ai") + ": Michiel",
|
||||
AppContext.getString("developers") + ": Michiel, Bas, Stef, Omar, Ticho",
|
||||
AppContext.getString("moralSupport") + ": Wesley",
|
||||
AppContext.getString("opengl") + ": Omar"
|
||||
};
|
||||
|
||||
final Text[] creditsHeaders = new Text[credits.length];
|
||||
|
||||
for (int i = 0; i < credits.length; i++) {
|
||||
creditsHeaders[i] = NodeBuilder.header(credits[i]);
|
||||
}
|
||||
|
||||
final Container creditsContainer = new HorizontalContainer(0);
|
||||
|
||||
final Container animatedContainer = new VerticalContainer(lineHeight);
|
||||
creditsContainer.addContainer(animatedContainer, true);
|
||||
|
||||
animatedContainer.addNodes(creditsHeaders);
|
||||
addContainer(creditsContainer, Pos.CENTER, 0, 0, 50, 100);
|
||||
|
||||
playCredits(animatedContainer, App.getHeight());
|
||||
}
|
||||
|
||||
private void playCredits(Container container, double sceneLength) {
|
||||
container.getContainer().setTranslateY(-sceneLength);
|
||||
|
||||
final TranslateTransition scrollCredits = new TranslateTransition(Duration.seconds(20), container.getContainer());
|
||||
scrollCredits.setFromY(-sceneLength - lineHeight);
|
||||
scrollCredits.setToY(sceneLength + lineHeight);
|
||||
|
||||
scrollCredits.setOnFinished(_ -> {
|
||||
final PauseTransition pauseCredits = new PauseTransition(Duration.seconds(3));
|
||||
pauseCredits.setOnFinished(_ -> playCredits(container, sceneLength));
|
||||
pauseCredits.play();
|
||||
});
|
||||
|
||||
scrollCredits.play();
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.toop.app.layer.layers;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.Layer;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
public final class MainLayer extends Layer {
|
||||
public MainLayer() {
|
||||
super("bg-primary");
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final var tictactoeButton = NodeBuilder.button(AppContext.getString("tictactoe"), () -> {
|
||||
App.activate(new MultiplayerLayer());
|
||||
});
|
||||
|
||||
final var othelloButton = NodeBuilder.button(AppContext.getString("othello"), () -> {
|
||||
App.activate(new MultiplayerLayer());
|
||||
});
|
||||
|
||||
final var creditsButton = NodeBuilder.button(AppContext.getString("credits"), () -> {
|
||||
App.push(new CreditsPopup());
|
||||
});
|
||||
|
||||
final var optionsButton = NodeBuilder.button(AppContext.getString("options"), () -> {
|
||||
App.push(new OptionsPopup());
|
||||
});
|
||||
|
||||
final var quitButton = NodeBuilder.button(AppContext.getString("quit"), () -> {
|
||||
App.quitPopup();
|
||||
});
|
||||
|
||||
final Container gamesContainer = new VerticalContainer(5);
|
||||
gamesContainer.addNodes(tictactoeButton, othelloButton);
|
||||
|
||||
final Container controlContainer = new VerticalContainer(5);
|
||||
controlContainer.addNodes(creditsButton, optionsButton, quitButton);
|
||||
|
||||
addContainer(gamesContainer, Pos.TOP_LEFT, 2, 2, 20, 0);
|
||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 20, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
package org.toop.app.layer.layers;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.Layer;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.containers.HorizontalContainer;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.app.layer.layers.game.TicTacToeLayer;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public final class MultiplayerLayer extends Layer {
|
||||
private boolean isConnectionLocal = true;
|
||||
|
||||
private boolean isPlayer1Human = true;
|
||||
private String player1Name = "";
|
||||
private int computer1Difficulty = 0;
|
||||
private int computer1ThinkTime = 0;
|
||||
|
||||
private boolean isPlayer2Human = true;
|
||||
private String player2Name = "";
|
||||
private int computer2Difficulty = 0;
|
||||
private int computer2ThinkTime = 0;
|
||||
|
||||
private String serverIP = "";
|
||||
private String serverPort = "";
|
||||
|
||||
public MultiplayerLayer() {
|
||||
super("bg-primary");
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final Container player1Container = new VerticalContainer(20);
|
||||
final Container player2Container = new VerticalContainer(20);
|
||||
|
||||
final var isPlayer1HumanToggle = NodeBuilder.toggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer1Human, (computer) -> {
|
||||
isPlayer1Human = !computer;
|
||||
reload();
|
||||
});
|
||||
|
||||
player1Container.addNodes(isPlayer1HumanToggle);
|
||||
|
||||
if (isPlayer1Human) {
|
||||
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
|
||||
final var playerNameInput = NodeBuilder.input(player1Name, (name) -> {
|
||||
player1Name = name;
|
||||
});
|
||||
|
||||
player1Container.addNodes(playerNameText, playerNameInput);
|
||||
} else {
|
||||
player1Name = "Pism Bot V" + LocalDateTime.now().getSecond();
|
||||
|
||||
final var computerNameText = NodeBuilder.text(player1Name);
|
||||
final var computerNameSeparator = NodeBuilder.separator();
|
||||
|
||||
final var computerDifficultyText = NodeBuilder.text(AppContext.getString("computerDifficulty"));
|
||||
final var computerDifficultySeparator = NodeBuilder.separator();
|
||||
final var computerDifficultySlider = NodeBuilder.slider(10, computer1Difficulty, (difficulty) ->
|
||||
computer1Difficulty = difficulty);
|
||||
|
||||
final var computerThinkTimeText = NodeBuilder.text(AppContext.getString("computerThinkTime"));
|
||||
final var computerThinkTimeSlider = NodeBuilder.slider(5, computer1ThinkTime, (thinkTime) ->
|
||||
computer1ThinkTime = thinkTime);
|
||||
|
||||
player1Container.addNodes(computerNameText, computerNameSeparator,
|
||||
computerDifficultyText, computerDifficultySlider, computerDifficultySeparator,
|
||||
computerThinkTimeText, computerThinkTimeSlider);
|
||||
}
|
||||
|
||||
if (isConnectionLocal) {
|
||||
final var isPlayer2HumanToggle = NodeBuilder.toggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer2Human, (computer) -> {
|
||||
isPlayer2Human = !computer;
|
||||
reload();
|
||||
});
|
||||
|
||||
player2Container.addNodes(isPlayer2HumanToggle);
|
||||
|
||||
if (isPlayer2Human) {
|
||||
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
|
||||
final var playerNameInput = NodeBuilder.input(player2Name, (name) -> {
|
||||
player2Name = name;
|
||||
});
|
||||
|
||||
player2Container.addNodes(playerNameText, playerNameInput);
|
||||
} else {
|
||||
player2Name = "Pism Bot V" + LocalDateTime.now().getSecond();
|
||||
|
||||
final var computerNameText = NodeBuilder.text(player2Name);
|
||||
final var computerNameSeparator = NodeBuilder.separator();
|
||||
|
||||
final var computerDifficultyText = NodeBuilder.text(AppContext.getString("computerDifficulty"));
|
||||
final var computerDifficultySeparator = NodeBuilder.separator();
|
||||
final var computerDifficultySlider = NodeBuilder.slider(10, computer2Difficulty, (difficulty) ->
|
||||
computer2Difficulty = difficulty);
|
||||
|
||||
final var computerThinkTimeText = NodeBuilder.text(AppContext.getString("computerThinkTime"));
|
||||
final var computerThinkTimeSlider = NodeBuilder.slider(5, computer2ThinkTime, (thinkTime) ->
|
||||
computer2ThinkTime = thinkTime);
|
||||
|
||||
player2Container.addNodes(computerNameText, computerNameSeparator,
|
||||
computerDifficultyText, computerDifficultySlider, computerDifficultySeparator,
|
||||
computerThinkTimeText, computerThinkTimeSlider);
|
||||
}
|
||||
} else {
|
||||
final var serverIPText = NodeBuilder.text(AppContext.getString("serverIP"));
|
||||
final var serverIPSeparator = NodeBuilder.separator();
|
||||
final var serverIPInput = NodeBuilder.input(serverIP, (ip) -> {
|
||||
serverIP = ip;
|
||||
});
|
||||
|
||||
final var serverPortText = NodeBuilder.text(AppContext.getString("serverPort"));
|
||||
final var serverPortInput = NodeBuilder.input(serverPort, (port) -> {
|
||||
serverPort = port;
|
||||
});
|
||||
|
||||
player2Container.addNodes(serverIPText, serverIPInput, serverIPSeparator,
|
||||
serverPortText, serverPortInput);
|
||||
}
|
||||
|
||||
final var versusText = NodeBuilder.header("VS");
|
||||
|
||||
final var connectionTypeText = NodeBuilder.text(AppContext.getString("connectionType") + ":");
|
||||
final var connectionTypeToggle = NodeBuilder.toggle(AppContext.getString("local"), AppContext.getString("server"), !isConnectionLocal, (server) -> {
|
||||
isConnectionLocal = !server;
|
||||
reload();
|
||||
});
|
||||
|
||||
final var playButton = NodeBuilder.button(isConnectionLocal ? AppContext.getString("start") : AppContext.getString("connect"), () -> {
|
||||
final var information = new GameInformation(
|
||||
new String[]{player1Name, player2Name},
|
||||
new boolean[]{isPlayer1Human, isPlayer2Human},
|
||||
new int[]{computer1Difficulty, computer2Difficulty},
|
||||
new int[]{computer1ThinkTime, computer2ThinkTime},
|
||||
isConnectionLocal, serverIP, serverPort);
|
||||
|
||||
if (isConnectionLocal) {
|
||||
App.activate(new TicTacToeLayer(information));
|
||||
} else {
|
||||
App.activate(new ConnectedLayer(information));
|
||||
}
|
||||
});
|
||||
|
||||
final Container mainContainer = new VerticalContainer(10);
|
||||
final Container playersContainer = new HorizontalContainer(5);
|
||||
final Container connectionTypeContainer = new HorizontalContainer(10);
|
||||
|
||||
mainContainer.addContainer(playersContainer, true);
|
||||
mainContainer.addContainer(connectionTypeContainer, false);
|
||||
mainContainer.addNodes(playButton);
|
||||
|
||||
connectionTypeContainer.addNodes(connectionTypeText, connectionTypeToggle);
|
||||
|
||||
playersContainer.addContainer(player1Container, true);
|
||||
playersContainer.addNodes(versusText);
|
||||
playersContainer.addContainer(player2Container, true);
|
||||
|
||||
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
|
||||
App.activate(new MainLayer());
|
||||
});
|
||||
|
||||
final Container controlContainer = new VerticalContainer(0);
|
||||
controlContainer.addNodes(backButton);
|
||||
|
||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 75, 75);
|
||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
package org.toop.app.layer.layers;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.Popup;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.framework.asset.resources.SettingsAsset;
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.local.AppContext;
|
||||
import org.toop.local.AppSettings;
|
||||
|
||||
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 {
|
||||
AppSettings appSettings = new AppSettings();
|
||||
SettingsAsset settings = appSettings.getPath();
|
||||
private boolean isWindowed = !(settings.getFullscreen());
|
||||
|
||||
public OptionsPopup() {
|
||||
super(true, "bg-primary");
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final var languageHeader = NodeBuilder.header(AppContext.getString("language"));
|
||||
final var languageSeparator = NodeBuilder.separator();
|
||||
|
||||
final var volumeHeader = NodeBuilder.header(AppContext.getString("volume"));
|
||||
final var volumeSeparator = NodeBuilder.separator();
|
||||
|
||||
final var fxVolumeHeader = NodeBuilder.header(AppContext.getString("effectsVolume"));
|
||||
final var fxVolumeSeparator = NodeBuilder.separator();
|
||||
|
||||
final var musicVolumeHeader = NodeBuilder.header(AppContext.getString("musicVolume"));
|
||||
final var musicVolumeSeparator = NodeBuilder.separator();
|
||||
|
||||
final var themeHeader = NodeBuilder.header(AppContext.getString("theme"));
|
||||
final var themeSeparator = NodeBuilder.separator();
|
||||
|
||||
final var layoutSizeHeader = NodeBuilder.header(AppContext.getString("layoutSize"));
|
||||
final var layoutSizeSeparator = NodeBuilder.separator();
|
||||
|
||||
final var optionsContainer = new VerticalContainer(5);
|
||||
optionsContainer.addNodes(languageHeader, languageChoiceBox(), languageSeparator);
|
||||
optionsContainer.addNodes(volumeHeader, volumeSlider(), volumeSeparator);
|
||||
optionsContainer.addNodes(fxVolumeHeader, fxVolumeSlider(), fxVolumeSeparator);
|
||||
optionsContainer.addNodes(musicVolumeHeader, musicVolumeSlider(), musicVolumeSeparator);
|
||||
optionsContainer.addNodes(themeHeader, themeChoiceBox(), themeSeparator);
|
||||
optionsContainer.addNodes(layoutSizeHeader, layoutSizeChoiceBox(), layoutSizeSeparator);
|
||||
optionsContainer.addNodes(fullscreenToggle());
|
||||
|
||||
final Container mainContainer = new VerticalContainer(50, "");
|
||||
mainContainer.addContainer(optionsContainer, true);
|
||||
|
||||
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
|
||||
App.pop();
|
||||
});
|
||||
|
||||
final Container controlContainer = new VerticalContainer(5);
|
||||
controlContainer.addNodes(backButton);
|
||||
|
||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 0, 0);
|
||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
||||
}
|
||||
|
||||
private ChoiceBox<Locale> languageChoiceBox() {
|
||||
assert AppContext.getLocalization() != null;
|
||||
|
||||
final ChoiceBox<Locale> languageChoiceBox = NodeBuilder.choiceBox((locale) -> {
|
||||
if (locale == AppContext.getLocale()) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings.setLocale(locale.toString());
|
||||
AppContext.setLocale(locale);
|
||||
|
||||
App.reloadAll();
|
||||
});
|
||||
|
||||
languageChoiceBox.setConverter(new javafx.util.StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Locale locale) {
|
||||
return AppContext.getString(locale.getDisplayName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
languageChoiceBox.getItems().addAll(AppContext.getLocalization().getAvailableLocales());
|
||||
languageChoiceBox.setValue(AppContext.getLocale());
|
||||
|
||||
return languageChoiceBox;
|
||||
}
|
||||
|
||||
private Slider volumeSlider() {
|
||||
return NodeBuilder.slider(100, settings.getVolume(), (volume) -> {
|
||||
settings.setVolume(volume);
|
||||
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(volume.doubleValue())).asyncPostEvent();
|
||||
});
|
||||
}
|
||||
|
||||
private Slider fxVolumeSlider() {
|
||||
return NodeBuilder.slider(100, settings.getFxVolume(), (volume) -> {
|
||||
settings.setFxVolume(volume);
|
||||
new EventFlow().addPostEvent(new AudioEvents.ChangeFxVolume(volume.doubleValue())).asyncPostEvent();
|
||||
});
|
||||
}
|
||||
|
||||
private Slider musicVolumeSlider() {
|
||||
return NodeBuilder.slider(100, settings.getMusicVolume(), (volume) -> {
|
||||
settings.setMusicVolume(volume);
|
||||
new EventFlow().addPostEvent(new AudioEvents.ChangeMusicVolume(volume.doubleValue())).asyncPostEvent();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private Label fullscreenToggle() {
|
||||
return NodeBuilder.toggle(AppContext.getString("windowed"), AppContext.getString("fullscreen"), !isWindowed, (fullscreen) -> {
|
||||
isWindowed = !fullscreen;
|
||||
|
||||
settings.setFullscreen(fullscreen);
|
||||
App.setFullscreen(fullscreen);
|
||||
});
|
||||
}
|
||||
|
||||
private ChoiceBox<String> themeChoiceBox() {
|
||||
final ChoiceBox<String> themeChoiceBox = NodeBuilder.choiceBox((theme) -> {
|
||||
if (theme.equalsIgnoreCase(settings.getTheme())) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings.setTheme(theme);
|
||||
App.setStyle(theme, settings.getLayoutSize());
|
||||
});
|
||||
|
||||
themeChoiceBox.setConverter(new javafx.util.StringConverter<>() {
|
||||
@Override
|
||||
public String toString(String theme) {
|
||||
return AppContext.getString(theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
themeChoiceBox.getItems().addAll("dark", "light", "dark-hc", "light-hc");
|
||||
themeChoiceBox.setValue(settings.getTheme());
|
||||
|
||||
return themeChoiceBox;
|
||||
}
|
||||
|
||||
private ChoiceBox<String> layoutSizeChoiceBox() {
|
||||
final ChoiceBox<String> layoutSizeChoiceBox = NodeBuilder.choiceBox((layoutSize) -> {
|
||||
if (layoutSize.equalsIgnoreCase(settings.getLayoutSize())) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings.setLayoutSize(layoutSize);
|
||||
App.setStyle(settings.getTheme(), layoutSize);
|
||||
});
|
||||
|
||||
layoutSizeChoiceBox.setConverter(new javafx.util.StringConverter<>() {
|
||||
@Override
|
||||
public String toString(String layoutSize) {
|
||||
return AppContext.getString(layoutSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
layoutSizeChoiceBox.getItems().addAll("small", "medium", "large");
|
||||
layoutSizeChoiceBox.setValue(settings.getLayoutSize());
|
||||
|
||||
return layoutSizeChoiceBox;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.toop.app.layer.layers;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.Popup;
|
||||
import org.toop.app.layer.containers.HorizontalContainer;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
public final class QuitPopup extends Popup {
|
||||
public QuitPopup() {
|
||||
super(true);
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final var sureText = NodeBuilder.header(AppContext.getString("quitSure"));
|
||||
|
||||
final var yesButton = NodeBuilder.button(AppContext.getString("yes"), () -> {
|
||||
App.quit();
|
||||
});
|
||||
|
||||
final var noButton = NodeBuilder.button(AppContext.getString("no"), () -> {
|
||||
App.pop();
|
||||
});
|
||||
|
||||
final Container controlContainer = new HorizontalContainer(30);
|
||||
controlContainer.addNodes(yesButton, noButton);
|
||||
|
||||
final Container mainContainer = new VerticalContainer(30);
|
||||
mainContainer.addNodes(sureText);
|
||||
mainContainer.addContainer(controlContainer, false);
|
||||
|
||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.toop.app.layer.layers.game;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.Popup;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.app.layer.layers.MainLayer;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
public class GameFinishedPopup extends Popup {
|
||||
private final boolean isDraw;
|
||||
private final String winner;
|
||||
|
||||
public GameFinishedPopup(boolean isDraw, String winner) {
|
||||
super(true, "bg-popup");
|
||||
|
||||
this.isDraw = isDraw;
|
||||
this.winner = winner;
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
final Container mainContainer = new VerticalContainer(30);
|
||||
|
||||
if (isDraw) {
|
||||
final var drawHeader = NodeBuilder.header(AppContext.getString("drawText"));
|
||||
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
|
||||
|
||||
mainContainer.addNodes(drawHeader, goodGameText);
|
||||
} else {
|
||||
final var winHeader = NodeBuilder.header(AppContext.getString("congratulations") + ": " + winner);
|
||||
final var goodGameText = NodeBuilder.text(AppContext.getString("goodGameText"));
|
||||
|
||||
mainContainer.addNodes(winHeader, goodGameText);
|
||||
}
|
||||
|
||||
final var backToMainMenuButton = NodeBuilder.button(AppContext.getString("backToMainMenu"), () -> {
|
||||
App.activate(new MainLayer());
|
||||
});
|
||||
|
||||
mainContainer.addNodes(backToMainMenuButton);
|
||||
|
||||
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
package org.toop.app.layer.layers.game;
|
||||
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.canvas.TicTacToeCanvas;
|
||||
import org.toop.app.layer.Container;
|
||||
import org.toop.app.layer.Layer;
|
||||
import org.toop.app.layer.NodeBuilder;
|
||||
import org.toop.app.layer.containers.HorizontalContainer;
|
||||
import org.toop.app.layer.containers.VerticalContainer;
|
||||
import org.toop.app.layer.layers.MainLayer;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
import org.toop.game.Game;
|
||||
import org.toop.game.tictactoe.TicTacToe;
|
||||
import org.toop.game.tictactoe.TicTacToeAI;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
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 {
|
||||
private TicTacToeCanvas canvas;
|
||||
|
||||
private AtomicReference<TicTacToe> ticTacToe;
|
||||
private TicTacToeAI ticTacToeAI;
|
||||
|
||||
private GameInformation information;
|
||||
|
||||
private final Text currentPlayerNameText;
|
||||
private final Text currentPlayerMoveText;
|
||||
|
||||
private final BlockingQueue<Game.Move> playerMoveQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
// Todo: set these from the server
|
||||
private String player2Name = "";
|
||||
|
||||
final AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true);
|
||||
|
||||
public TicTacToeLayer(GameInformation information) {
|
||||
super("bg-primary");
|
||||
|
||||
canvas = new TicTacToeCanvas(Color.LIME, (App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> {
|
||||
try {
|
||||
if (information.isConnectionLocal()) {
|
||||
if (ticTacToe.get().getCurrentTurn() == 0) {
|
||||
playerMoveQueue.put(new Game.Move(cell, 'X'));
|
||||
} else {
|
||||
playerMoveQueue.put(new Game.Move(cell, 'O'));
|
||||
}
|
||||
} else {
|
||||
if (information.isPlayerHuman()[0]) {
|
||||
if (ticTacToe.get().getCurrentTurn() == 0) {
|
||||
if (firstPlayerIsMe.get()) {
|
||||
playerMoveQueue.put(new Game.Move(cell, 'X'));
|
||||
}
|
||||
} else {
|
||||
if (!firstPlayerIsMe.get()) {
|
||||
playerMoveQueue.put(new Game.Move(cell, 'O'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException _) {}
|
||||
});
|
||||
|
||||
ticTacToe = new AtomicReference<>(new TicTacToe());
|
||||
ticTacToeAI = new TicTacToeAI();
|
||||
|
||||
this.information = information;
|
||||
|
||||
if (information.isConnectionLocal()) {
|
||||
new Thread(this::localGameThread).start();
|
||||
}
|
||||
|
||||
currentPlayerNameText = NodeBuilder.header("");
|
||||
currentPlayerMoveText = NodeBuilder.header("");
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
public TicTacToeLayer(GameInformation information, long clientID) {
|
||||
this(information);
|
||||
|
||||
Thread a = new Thread(this::serverGameThread);
|
||||
a.setDaemon(false);
|
||||
a.start();
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
public TicTacToeLayer(GameInformation information, long clientID, NetworkEvents.GameMatchResponse resp) {
|
||||
this(information);
|
||||
|
||||
Thread a = new Thread(this::serverGameThread);
|
||||
a.setDaemon(false);
|
||||
a.start();
|
||||
|
||||
handleServerGameStart(resp);
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
popAll();
|
||||
|
||||
canvas.resize((App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75);
|
||||
|
||||
for (int i = 0; i < ticTacToe.get().board.length; i++) {
|
||||
final char value = ticTacToe.get().board[i];
|
||||
|
||||
if (value == 'X') {
|
||||
canvas.drawX(Color.RED, i);
|
||||
} else if (value == 'O') {
|
||||
canvas.drawO(Color.BLUE, i);
|
||||
}
|
||||
}
|
||||
|
||||
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
|
||||
App.activate(new MainLayer());
|
||||
});
|
||||
|
||||
final Container controlContainer = new VerticalContainer(5);
|
||||
controlContainer.addNodes(backButton);
|
||||
|
||||
final Container informationContainer = new HorizontalContainer(15);
|
||||
informationContainer.addNodes(currentPlayerNameText, currentPlayerMoveText);
|
||||
|
||||
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
|
||||
addContainer(informationContainer, Pos.TOP_LEFT, 2, 2, 0, 0);
|
||||
addGameCanvas(canvas, Pos.CENTER, 0, 0);
|
||||
}
|
||||
|
||||
private int compurterDifficultyToDepth(int maxDifficulty, int difficulty) {
|
||||
return (int) (((float) maxDifficulty / difficulty) * 9);
|
||||
}
|
||||
|
||||
private void localGameThread() {
|
||||
boolean running = true;
|
||||
|
||||
while (running) {
|
||||
final int currentPlayer = ticTacToe.get().getCurrentTurn();
|
||||
|
||||
currentPlayerNameText.setText(information.playerName()[currentPlayer]);
|
||||
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0? "X" : "O");
|
||||
|
||||
Game.Move move = null;
|
||||
|
||||
if (information.isPlayerHuman()[currentPlayer]) {
|
||||
try {
|
||||
final Game.Move wants = playerMoveQueue.take();
|
||||
final Game.Move[] legalMoves = ticTacToe.get().getLegalMoves();
|
||||
|
||||
for (final Game.Move legalMove : legalMoves) {
|
||||
if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) {
|
||||
move = wants;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException _) {}
|
||||
} else {
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
move = ticTacToeAI.findBestMove(ticTacToe.get(), compurterDifficultyToDepth(10,
|
||||
information.computerDifficulty()[currentPlayer]));
|
||||
|
||||
if (information.computerThinkTime()[currentPlayer] > 0) {
|
||||
final long elapsedTime = System.currentTimeMillis() - start;
|
||||
final long sleepTime = information.computerThinkTime()[currentPlayer] * 1000L - elapsedTime;
|
||||
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException _) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (move == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Game.State state = ticTacToe.get().play(move);
|
||||
|
||||
if (move.value() == 'X') {
|
||||
canvas.drawX(Color.RED, move.position());
|
||||
} else if (move.value() == 'O') {
|
||||
canvas.drawO(Color.BLUE, move.position());
|
||||
}
|
||||
|
||||
if (state != Game.State.NORMAL) {
|
||||
if (state == Game.State.WIN) {
|
||||
App.push(new GameFinishedPopup(false, information.playerName()[ticTacToe.get().getCurrentTurn()]));
|
||||
} else if (state == Game.State.DRAW) {
|
||||
App.push(new GameFinishedPopup(true, ""));
|
||||
}
|
||||
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void serverGameThread() {
|
||||
new EventFlow()
|
||||
.listen(this::handleServerGameStart) // <-----------
|
||||
.listen(this::yourTurnResponse)
|
||||
.listen(this::onMoveResponse)
|
||||
.listen(this::handleReceivedMessage);
|
||||
}
|
||||
|
||||
private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) {
|
||||
// Meneer Bas de Jong. Dit functie wordt niet aangeroepen als je de challenger bent.
|
||||
// Ik heb veel dingen geprobeert. FUCKING veel dingen. Hij doet het niet.
|
||||
// Ik heb zelfs in jou code gekeken en unsubscribeAfterSuccess op false gezet. (zie ConnectedLayer).
|
||||
// Alle andere functies worden wel gecalt. Behalve dit.
|
||||
|
||||
// Ben jij gehandicapt of ik? Want het moet 1 van de 2 zijn. Ik ben dit al 2 uur aan het debuggen.
|
||||
// Ik ga nu slapen (04:46).
|
||||
|
||||
// ⠀⠀⠀⠀⠀⠀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⢀⣴⣿⣿⠿⣟⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⢸⣏⡏⠀⠀⠀⢣⢻⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⢸⣟⠧⠤⠤⠔⠋⠀⢿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠸⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠘⣿⡀⢀⣶⠤⠒⠀⢻⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⢹⣧⠀⠀⠀⠀⠀⠈⢿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⣿⡆⠀⠀⠀⠀⠀⠈⢿⣆⣠⣤⣤⣤⣤⣴⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⢀⣾⢿⢿⠀⠀⠀⢀⣀⣀⠘⣿⠋⠁⠀⠙⢇⠀⠀⠙⢿⣦⡀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⢀⣾⢇⡞⠘⣧⠀⢖⡭⠞⢛⡄⠘⣆⠀⠀⠀⠈⢧⠀⠀⠀⠙⢿⣄⠀⠀⠀⠀
|
||||
// ⠀⠀⣠⣿⣛⣥⠤⠤⢿⡄⠀⠀⠈⠉⠀⠀⠹⡄⠀⠀⠀⠈⢧⠀⠀⠀⠈⠻⣦⠀⠀⠀
|
||||
// ⠀⣼⡟⡱⠛⠙⠀⠀⠘⢷⡀⠀⠀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠈⣧⠀⠀⠀⠀⠹⣧⡀⠀
|
||||
// ⢸⡏⢠⠃⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠘⣧⠀⠀⠀⠀⠸⣷⡀
|
||||
// ⠸⣧⠘⡇⠀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⢹⡇⠀⠀⠀⠀⣿⠇
|
||||
// ⠀⣿⡄⢳⠀⠀⠀⠀⠀⠀⠀⠈⣷⠀⠀⠀⠀⠀⠀⠈⠆⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠀
|
||||
// ⠀⢹⡇⠘⣇⠀⠀⠀⠀⠀⠀⠰⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⣼⡟⠀⠀
|
||||
// ⠀⢸⡇⠀⢹⡆⠀⠀⠀⠀⠀⠀⠙⠁⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⢳⣼⠟⠀⠀⠀
|
||||
// ⠀⠸⣧⣀⠀⢳⡀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⢃⠀⢀⣴⡿⠁⠀⠀⠀⠀
|
||||
// ⠀⠀⠈⠙⢷⣄⢳⡀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⣠⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠈⠻⢿⣷⣦⣄⣀⣀⣠⣤⠾⠷⣦⣤⣤⡶⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
|
||||
player2Name = resp.opponent();
|
||||
System.out.println(player2Name);
|
||||
|
||||
currentPlayerMoveText.setText("X");
|
||||
|
||||
if(!resp.playerToMove().equalsIgnoreCase(resp.opponent())) {
|
||||
currentPlayerNameText.setText(information.playerName()[0]);
|
||||
firstPlayerIsMe.set(true);
|
||||
|
||||
System.out.printf("I am starting: My client id is %d\n", resp.clientId());
|
||||
} else {
|
||||
currentPlayerNameText.setText(player2Name);
|
||||
firstPlayerIsMe.set(false);
|
||||
|
||||
System.out.printf("I am NOT starting: My client id is %d\n", resp.clientId());
|
||||
}
|
||||
}
|
||||
|
||||
private void onMoveResponse(NetworkEvents.GameMoveResponse resp) {
|
||||
char playerChar;
|
||||
|
||||
if (!resp.player().equalsIgnoreCase(player2Name)) {
|
||||
playerChar = firstPlayerIsMe.get()? 'X' : 'O';
|
||||
} else {
|
||||
playerChar = firstPlayerIsMe.get()? 'O' : 'X';
|
||||
}
|
||||
|
||||
final Game.Move move = new Game.Move(Integer.parseInt(resp.move()), playerChar);
|
||||
final Game.State state = ticTacToe.get().play(move);
|
||||
|
||||
if (state != Game.State.NORMAL) { //todo differentiate between future draw guaranteed and is currently a draw
|
||||
if (state == Game.State.WIN) {
|
||||
if (!resp.player().equalsIgnoreCase(player2Name)) {
|
||||
App.push(new GameFinishedPopup(false, information.playerName()[0]));
|
||||
} else {
|
||||
App.push(new GameFinishedPopup(false, player2Name));
|
||||
}
|
||||
} else if (state == Game.State.DRAW) {
|
||||
App.push(new GameFinishedPopup(true, ""));
|
||||
}
|
||||
}
|
||||
|
||||
if (move.value() == 'X') {
|
||||
canvas.drawX(Color.RED, move.position());
|
||||
} else if (move.value() == 'O') {
|
||||
canvas.drawO(Color.BLUE, move.position());
|
||||
}
|
||||
|
||||
currentPlayerNameText.setText(ticTacToe.get().getCurrentTurn() == (firstPlayerIsMe.get()? 0 : 1)? information.playerName()[0] : player2Name);
|
||||
currentPlayerMoveText.setText(ticTacToe.get().getCurrentTurn() == 0? "X" : "O");
|
||||
}
|
||||
|
||||
private void yourTurnResponse(NetworkEvents.YourTurnResponse response) {
|
||||
int position = -1;
|
||||
|
||||
if (information.isPlayerHuman()[0]) {
|
||||
try {
|
||||
position = playerMoveQueue.take().position();
|
||||
System.out.println("TEST" + position);
|
||||
} catch (InterruptedException _) {}
|
||||
} else {
|
||||
final Game.Move move = ticTacToeAI.findBestMove(ticTacToe.get(), compurterDifficultyToDepth(10,
|
||||
information.computerDifficulty()[0]));
|
||||
|
||||
position = move.position();
|
||||
}
|
||||
|
||||
new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(), (short)position))
|
||||
.postEvent();
|
||||
}
|
||||
|
||||
private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) {
|
||||
System.out.println("Received Message: " + msg.message()); //todo add chat window
|
||||
}
|
||||
}
|
||||
400
app/src/main/java/org/toop/app/view/View.java
Normal file
400
app/src/main/java/org/toop/app/view/View.java
Normal file
@@ -0,0 +1,400 @@
|
||||
package org.toop.app.view;
|
||||
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class View {
|
||||
private final boolean mainView;
|
||||
|
||||
private final StackPane view;
|
||||
private final Map<String, Node> nodeMap;
|
||||
|
||||
protected View(boolean mainView, String cssClass) {
|
||||
this.mainView = mainView;
|
||||
|
||||
view = new StackPane();
|
||||
view.getStyleClass().add(cssClass);
|
||||
|
||||
nodeMap = new HashMap<String, Node>();
|
||||
}
|
||||
|
||||
public void add(Pos position, Node node) {
|
||||
assert node != null;
|
||||
|
||||
StackPane.setAlignment(node, position);
|
||||
view.getChildren().add(node);
|
||||
}
|
||||
|
||||
protected Region hspacer() {
|
||||
final Region hspacer = new Region();
|
||||
hspacer.getStyleClass().add("hspacer");
|
||||
|
||||
return hspacer;
|
||||
}
|
||||
|
||||
protected Region vspacer() {
|
||||
final Region vspacer = new Region();
|
||||
vspacer.getStyleClass().add("vspacer");
|
||||
|
||||
return vspacer;
|
||||
}
|
||||
|
||||
protected ScrollPane fit(String identifier, String cssClass, Node node) {
|
||||
assert node != null;
|
||||
|
||||
final ScrollPane fit = new ScrollPane(node);
|
||||
fit.getStyleClass().add(cssClass);
|
||||
|
||||
fit.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||
fit.setFitToWidth(true);
|
||||
fit.setFitToHeight(true);
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, fit);
|
||||
}
|
||||
|
||||
return fit;
|
||||
}
|
||||
|
||||
protected ScrollPane fit(String identifier, Node node) {
|
||||
return fit(identifier, "fit", node);
|
||||
}
|
||||
|
||||
protected ScrollPane fit(Node node) {
|
||||
return fit("", node);
|
||||
}
|
||||
|
||||
protected HBox hbox(String identifier, String cssClass, Node... nodes) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final HBox hbox = new HBox();
|
||||
hbox.getStyleClass().add(cssClass);
|
||||
hbox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||
|
||||
for (final Node node : nodes) {
|
||||
if (node != null) {
|
||||
hbox.getChildren().add(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, hbox);
|
||||
}
|
||||
|
||||
return hbox;
|
||||
}
|
||||
|
||||
protected HBox hbox(String identifier, Node... nodes) {
|
||||
return hbox(identifier, "container", nodes);
|
||||
}
|
||||
|
||||
protected HBox hbox(Node... nodes) {
|
||||
return hbox("", nodes);
|
||||
}
|
||||
|
||||
protected HBox hboxFill(String identifier, String cssClass, Node... nodes) {
|
||||
final HBox hbox = hbox(identifier, cssClass, nodes);
|
||||
|
||||
for (final Node node : hbox.getChildren()) {
|
||||
if (node instanceof Region) {
|
||||
((Region)node).setMaxHeight(Double.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return hbox;
|
||||
}
|
||||
|
||||
protected HBox hboxFill(String identifier, Node... nodes) {
|
||||
final HBox hbox = hbox(identifier, nodes);
|
||||
|
||||
for (final Node node : hbox.getChildren()) {
|
||||
if (node instanceof Region) {
|
||||
((Region)node).setMaxHeight(Double.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return hbox;
|
||||
}
|
||||
|
||||
protected HBox hboxFill(Node... nodes) {
|
||||
final HBox hbox = hbox(nodes);
|
||||
|
||||
for (final Node node : hbox.getChildren()) {
|
||||
if (node instanceof Region) {
|
||||
((Region)node).setMaxHeight(Double.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return hbox;
|
||||
}
|
||||
|
||||
protected VBox vbox(String identifier, String cssClass, Node... nodes) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final VBox vbox = new VBox();
|
||||
vbox.getStyleClass().add(cssClass);
|
||||
vbox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
|
||||
|
||||
for (final Node node : nodes) {
|
||||
if (node != null) {
|
||||
vbox.getChildren().add(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, vbox);
|
||||
}
|
||||
|
||||
return vbox;
|
||||
}
|
||||
|
||||
protected VBox vbox(String identifier, Node... nodes) {
|
||||
return vbox(identifier, "container", nodes);
|
||||
}
|
||||
|
||||
protected VBox vbox(Node... nodes) {
|
||||
return vbox("", nodes);
|
||||
}
|
||||
|
||||
protected VBox vboxFill(String identifier, String cssClass, Node... nodes) {
|
||||
final VBox vbox = vbox(identifier, cssClass, nodes);
|
||||
|
||||
for (final Node node : vbox.getChildren()) {
|
||||
if (node instanceof Region) {
|
||||
((Region)node).setMaxWidth(Double.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return vbox;
|
||||
}
|
||||
|
||||
protected VBox vboxFill(String identifier, Node... nodes) {
|
||||
final VBox vbox = vbox(identifier, nodes);
|
||||
|
||||
for (final Node node : vbox.getChildren()) {
|
||||
if (node instanceof Region) {
|
||||
((Region)node).setMaxWidth(Double.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return vbox;
|
||||
}
|
||||
|
||||
protected VBox vboxFill(Node... nodes) {
|
||||
final VBox vbox = vbox(nodes);
|
||||
|
||||
for (final Node node : vbox.getChildren()) {
|
||||
if (node instanceof Region) {
|
||||
((Region)node).setMaxWidth(Double.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return vbox;
|
||||
}
|
||||
|
||||
protected Separator separator(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final Separator separator = new Separator();
|
||||
separator.getStyleClass().add(cssClass);
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, separator);
|
||||
}
|
||||
|
||||
return separator;
|
||||
}
|
||||
|
||||
protected Separator separator(String identifier) {
|
||||
return separator(identifier, "separator");
|
||||
}
|
||||
|
||||
protected Separator separator() {
|
||||
return separator("");
|
||||
}
|
||||
|
||||
protected Text header(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final Text header = new Text();
|
||||
header.getStyleClass().add(cssClass);
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, header);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
protected Text header(String identifier) {
|
||||
return header(identifier, "header");
|
||||
}
|
||||
|
||||
protected Text header() {
|
||||
return header("");
|
||||
}
|
||||
|
||||
protected Text text(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final Text text = new Text();
|
||||
text.getStyleClass().add(cssClass);
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, text);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
protected Text text(String identifier) {
|
||||
return text(identifier, "text");
|
||||
}
|
||||
|
||||
protected Text text() {
|
||||
return text("");
|
||||
}
|
||||
|
||||
protected Button button(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final Button button = new Button();
|
||||
button.getStyleClass().add(cssClass);
|
||||
|
||||
button.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, button);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
protected Button button(String identifier) {
|
||||
return button(identifier, "button");
|
||||
}
|
||||
|
||||
protected Button button() {
|
||||
return button("");
|
||||
}
|
||||
|
||||
protected Slider slider(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final Slider slider = new Slider();
|
||||
slider.getStyleClass().add(cssClass);
|
||||
|
||||
slider.setMinorTickCount(0);
|
||||
slider.setMajorTickUnit(1);
|
||||
slider.setBlockIncrement(1);
|
||||
|
||||
slider.setSnapToTicks(true);
|
||||
slider.setShowTickLabels(true);
|
||||
|
||||
slider.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, slider);
|
||||
}
|
||||
|
||||
return slider;
|
||||
}
|
||||
|
||||
protected Slider slider(String identifier) {
|
||||
return slider(identifier, "slider");
|
||||
}
|
||||
|
||||
protected Slider slider() {
|
||||
return slider("");
|
||||
}
|
||||
|
||||
protected TextField input(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final TextField input = new TextField();
|
||||
input.getStyleClass().add(cssClass);
|
||||
|
||||
input.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, input);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
protected TextField input(String identifier) {
|
||||
return input(identifier, "input");
|
||||
}
|
||||
|
||||
protected TextField input() {
|
||||
return input("");
|
||||
}
|
||||
|
||||
protected <T> ComboBox<T> combobox(String identifier, String cssClass) {
|
||||
assert !nodeMap.containsKey(identifier);
|
||||
|
||||
final ComboBox<T> combobox = new ComboBox<T>();
|
||||
combobox.getStyleClass().add(cssClass);
|
||||
|
||||
combobox.setOnMouseClicked(_ -> {
|
||||
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
|
||||
});
|
||||
|
||||
if (!identifier.isEmpty()) {
|
||||
nodeMap.put(identifier, combobox);
|
||||
}
|
||||
|
||||
return combobox;
|
||||
}
|
||||
|
||||
protected <T> ComboBox<T> combobox(String identifier) {
|
||||
return combobox(identifier, "combo-box");
|
||||
}
|
||||
|
||||
protected <T> ComboBox<T> combobox() {
|
||||
return combobox("");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends Node> T get(String identifier) {
|
||||
assert nodeMap.containsKey(identifier);
|
||||
return (T) nodeMap.get(identifier);
|
||||
}
|
||||
|
||||
protected void clear() {
|
||||
view.getChildren().clear();
|
||||
nodeMap.clear();
|
||||
}
|
||||
|
||||
public boolean isMainView() { return mainView; }
|
||||
public Region getView() { return view; }
|
||||
|
||||
public abstract void setup();
|
||||
public void cleanup() {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
105
app/src/main/java/org/toop/app/view/ViewStack.java
Normal file
105
app/src/main/java/org/toop/app/view/ViewStack.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package org.toop.app.view;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
public final class ViewStack {
|
||||
private static boolean setup = false;
|
||||
|
||||
private static StackPane root;
|
||||
|
||||
private static View active;
|
||||
private static Stack<View> stack;
|
||||
|
||||
public static void setup(Scene scene) {
|
||||
assert scene != null;
|
||||
|
||||
if (setup) {
|
||||
return;
|
||||
}
|
||||
|
||||
root = new StackPane();
|
||||
|
||||
active = null;
|
||||
stack = new Stack<View>();
|
||||
|
||||
scene.setRoot(root);
|
||||
|
||||
setup = true;
|
||||
}
|
||||
|
||||
public static void cleanup() {
|
||||
assert setup;
|
||||
|
||||
final var count = stack.size();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
pop();
|
||||
}
|
||||
|
||||
if (active != null) {
|
||||
active.cleanup();
|
||||
}
|
||||
|
||||
setup = false;
|
||||
}
|
||||
|
||||
public static void reload() {
|
||||
assert setup;
|
||||
|
||||
for (final var view : stack) {
|
||||
view.cleanup();
|
||||
}
|
||||
|
||||
if (active != null) {
|
||||
active.cleanup();
|
||||
active.setup();
|
||||
}
|
||||
|
||||
for (final var view : stack) {
|
||||
view.setup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void push(View view) {
|
||||
assert setup;
|
||||
assert view != null;
|
||||
|
||||
if (view.isMainView()) {
|
||||
Platform.runLater(() -> {
|
||||
if (active != null) {
|
||||
root.getChildren().removeFirst();
|
||||
active.cleanup();
|
||||
}
|
||||
|
||||
root.getChildren().addFirst(view.getView());
|
||||
view.setup();
|
||||
|
||||
active = view;
|
||||
});
|
||||
} else {
|
||||
Platform.runLater(() -> {
|
||||
stack.push(view);
|
||||
root.getChildren().addLast(view.getView());
|
||||
view.setup();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void pop() {
|
||||
assert setup;
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
final var last = stack.pop();
|
||||
root.getChildren().removeLast();
|
||||
last.cleanup();
|
||||
});
|
||||
}
|
||||
}
|
||||
118
app/src/main/java/org/toop/app/view/views/ChallengeView.java
Normal file
118
app/src/main/java/org/toop/app/view/views/ChallengeView.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.Server;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ChallengeView extends View {
|
||||
private final GameInformation.Player playerInformation;
|
||||
|
||||
private final String challenger;
|
||||
private final String game;
|
||||
|
||||
private final Consumer<GameInformation.Player> onAccept;
|
||||
|
||||
public ChallengeView(String challenger, String game, Consumer<GameInformation.Player> onAccept) {
|
||||
super(false, "bg-popup");
|
||||
|
||||
playerInformation = new GameInformation.Player();
|
||||
|
||||
this.challenger = challenger;
|
||||
this.game = game;
|
||||
|
||||
this.onAccept = onAccept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text challengeText = text();
|
||||
challengeText.setText(AppContext.getString("you-were-challenged-by"));
|
||||
|
||||
final Text challengerHeader = header();
|
||||
challengerHeader.setText(challenger);
|
||||
|
||||
final Text gameText = text();
|
||||
gameText.setText(AppContext.getString("to-a-game-of") + " " + game);
|
||||
|
||||
final Button acceptButton = button();
|
||||
acceptButton.setText(AppContext.getString("accept"));
|
||||
acceptButton.setOnAction(_ -> {
|
||||
onAccept.accept(playerInformation);
|
||||
});
|
||||
|
||||
final Button denyButton = button();
|
||||
denyButton.setText(AppContext.getString("deny"));
|
||||
denyButton.setOnAction(_ -> {
|
||||
ViewStack.pop();
|
||||
});
|
||||
|
||||
final List<Node> nodes = new ArrayList<>();
|
||||
|
||||
if (playerInformation.isHuman) {
|
||||
final Button playerToggle = button();
|
||||
playerToggle.setText(AppContext.getString("player"));
|
||||
playerToggle.setOnAction(_ -> {
|
||||
playerInformation.isHuman = false;
|
||||
cleanup();
|
||||
setup();
|
||||
});
|
||||
|
||||
nodes.add(vbox(playerToggle));
|
||||
} else {
|
||||
final Button computerToggle = button();
|
||||
computerToggle.setText(AppContext.getString("computer"));
|
||||
computerToggle.setOnAction(_ -> {
|
||||
playerInformation.isHuman = true;
|
||||
cleanup();
|
||||
setup();
|
||||
});
|
||||
|
||||
nodes.add(vbox(computerToggle));
|
||||
|
||||
final Text computerDifficultyText = text();
|
||||
computerDifficultyText.setText(AppContext.getString("computer-difficulty"));
|
||||
|
||||
final Slider computerDifficultySlider = slider();
|
||||
computerDifficultySlider.setMin(0);
|
||||
computerDifficultySlider.setMax(GameInformation.Type.maxDepth(Server.gameToType(game)));
|
||||
computerDifficultySlider.setValue(playerInformation.computerDifficulty);
|
||||
computerDifficultySlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
playerInformation.computerDifficulty = newValue.intValue();
|
||||
});
|
||||
|
||||
nodes.add(vbox(computerDifficultyText, computerDifficultySlider));
|
||||
}
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(hboxFill(
|
||||
vboxFill(
|
||||
challengeText,
|
||||
challengerHeader,
|
||||
gameText,
|
||||
separator(),
|
||||
|
||||
hboxFill(
|
||||
acceptButton,
|
||||
denyButton
|
||||
)
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
nodes.toArray(new Node[0])
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
101
app/src/main/java/org/toop/app/view/views/CreditsView.java
Normal file
101
app/src/main/java/org/toop/app/view/views/CreditsView.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.Duration;
|
||||
|
||||
public final class CreditsView extends View {
|
||||
public CreditsView() {
|
||||
super(false, "bg-primary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text scrumMasterHeader = header();
|
||||
scrumMasterHeader.setText(AppContext.getString("scrum-master") + ": Stef");
|
||||
|
||||
final Text productOwnerHeader = header();
|
||||
productOwnerHeader.setText(AppContext.getString("product-owner") + ": Omar");
|
||||
|
||||
final Text mergeCommanderHeader = header();
|
||||
mergeCommanderHeader.setText(AppContext.getString("merge-commander") + ": Bas");
|
||||
|
||||
final Text localizationHeader = header();
|
||||
localizationHeader.setText(AppContext.getString("localization") + ": Ticho");
|
||||
|
||||
final Text aiHeader = header();
|
||||
aiHeader.setText(AppContext.getString("ai") + ": Michiel");
|
||||
|
||||
final Text developersHeader = header();
|
||||
developersHeader.setText(AppContext.getString("developers") + ": Michiel, Bas, Stef, Omar, Ticho");
|
||||
|
||||
final Text moralSupportHeader = header();
|
||||
moralSupportHeader.setText(AppContext.getString("moral-support") + ": Wesley");
|
||||
|
||||
final Text openglHeader = header();
|
||||
openglHeader.setText(AppContext.getString("opengl") + ": Omar");
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit("credits-fit", vboxFill("credits-container", "credits-container",
|
||||
vbox("credits-spacer-top", ""),
|
||||
|
||||
scrumMasterHeader,
|
||||
productOwnerHeader,
|
||||
mergeCommanderHeader,
|
||||
localizationHeader,
|
||||
aiHeader,
|
||||
developersHeader,
|
||||
moralSupportHeader,
|
||||
openglHeader,
|
||||
|
||||
vbox("credits-spacer-bottom", "")
|
||||
))
|
||||
);
|
||||
|
||||
final Button backButton = button();
|
||||
backButton.setText(AppContext.getString("back"));
|
||||
backButton.setOnAction(_ -> { ViewStack.pop(); });
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
backButton
|
||||
)
|
||||
);
|
||||
|
||||
playCredits(100, 20);
|
||||
}
|
||||
|
||||
private void playCredits(int lineHeight, int length) {
|
||||
final ScrollPane creditsFit = get("credits-fit");
|
||||
creditsFit.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
creditsFit.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
|
||||
final VBox creditsContainer = get("credits-container");
|
||||
creditsContainer.setSpacing(lineHeight);
|
||||
|
||||
final VBox creditsSpacerTop = get("credits-spacer-top");
|
||||
creditsSpacerTop.setMinHeight(App.getHeight() - lineHeight);
|
||||
|
||||
final VBox creditsSpacerBottom = get("credits-spacer-bottom");
|
||||
creditsSpacerBottom.setMinHeight(App.getHeight() - lineHeight);
|
||||
|
||||
final Timeline timeline = new Timeline(
|
||||
new KeyFrame(Duration.seconds(0), new KeyValue(creditsFit.vvalueProperty(), 0.0)),
|
||||
new KeyFrame(Duration.seconds(length), new KeyValue(creditsFit.vvalueProperty(), 1.0))
|
||||
);
|
||||
|
||||
timeline.setCycleCount(Timeline.INDEFINITE);
|
||||
timeline.play();
|
||||
}
|
||||
}
|
||||
45
app/src/main/java/org/toop/app/view/views/ErrorView.java
Normal file
45
app/src/main/java/org/toop/app/view/views/ErrorView.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
public final class ErrorView extends View {
|
||||
private final String error;
|
||||
|
||||
public ErrorView(String error) {
|
||||
super(false, "bg-popup");
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text errorHeader = header();
|
||||
errorHeader.setText(AppContext.getString("error"));
|
||||
|
||||
final Text errorText = text();
|
||||
errorText.setText(error);
|
||||
|
||||
final Button okButton = button();
|
||||
okButton.setText(AppContext.getString("ok"));
|
||||
okButton.setOnAction(_ -> { ViewStack.pop(); });
|
||||
|
||||
add(Pos.CENTER,
|
||||
vboxFill(
|
||||
errorHeader,
|
||||
separator(),
|
||||
|
||||
vspacer(),
|
||||
errorText,
|
||||
vspacer(),
|
||||
|
||||
separator(),
|
||||
okButton
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
135
app/src/main/java/org/toop/app/view/views/GameView.java
Normal file
135
app/src/main/java/org/toop/app/view/views/GameView.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
public final class GameView extends View {
|
||||
private static class GameOverView extends View {
|
||||
private final boolean iWon;
|
||||
private final String winner;
|
||||
|
||||
public GameOverView(boolean iWon, String winner) {
|
||||
super(false, "bg-popup");
|
||||
|
||||
this.iWon = iWon;
|
||||
this.winner = winner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text gameOverHeader = header();
|
||||
gameOverHeader.setText(AppContext.getString("game-over"));
|
||||
|
||||
final Button okButton = button();
|
||||
okButton.setText(AppContext.getString("ok"));
|
||||
okButton.setOnAction(_ -> { ViewStack.pop(); });
|
||||
|
||||
Text gameOverText = text();
|
||||
|
||||
if (winner.isEmpty()) {
|
||||
gameOverText.setText(AppContext.getString("the-game-ended-in-a-draw"));
|
||||
} else {
|
||||
if (iWon) {
|
||||
gameOverText.setText(AppContext.getString("you-win") + " " + winner);
|
||||
} else {
|
||||
gameOverText.setText(AppContext.getString("you-lost-against") + " " + winner);
|
||||
}
|
||||
}
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
gameOverHeader,
|
||||
separator(),
|
||||
|
||||
vspacer(),
|
||||
gameOverText,
|
||||
vspacer(),
|
||||
|
||||
separator(),
|
||||
okButton
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private final Button forfeitButton;
|
||||
private final Button exitButton;
|
||||
|
||||
private final Text currentPlayerHeader;
|
||||
private final Text currentMoveHeader;
|
||||
|
||||
private final Text nextPlayerHeader;
|
||||
|
||||
public GameView(Runnable onForfeit, Runnable onExit) {
|
||||
assert onExit != null;
|
||||
|
||||
super(true, "bg-primary");
|
||||
|
||||
if (onForfeit != null) {
|
||||
forfeitButton = button();
|
||||
forfeitButton.setText(AppContext.getString("forfeit"));
|
||||
forfeitButton.setOnAction(_ -> onForfeit.run());
|
||||
} else {
|
||||
forfeitButton = null;
|
||||
}
|
||||
|
||||
exitButton = button();
|
||||
exitButton.setText(AppContext.getString("exit"));
|
||||
exitButton.setOnAction(_ -> onExit.run());
|
||||
|
||||
currentPlayerHeader = header("", "current-player");
|
||||
currentMoveHeader = header();
|
||||
|
||||
nextPlayerHeader = header();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
add(Pos.TOP_RIGHT,
|
||||
fit(vboxFill(
|
||||
currentPlayerHeader,
|
||||
|
||||
hboxFill(
|
||||
separator(),
|
||||
currentMoveHeader,
|
||||
separator()
|
||||
),
|
||||
|
||||
nextPlayerHeader
|
||||
))
|
||||
);
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
forfeitButton,
|
||||
exitButton
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) {
|
||||
currentPlayerHeader.setText(currentPlayer);
|
||||
currentMoveHeader.setText(currentMove);
|
||||
|
||||
nextPlayerHeader.setText(nextPlayer);
|
||||
|
||||
if (isMe) {
|
||||
currentPlayerHeader.getStyleClass().add("my-turn");
|
||||
} else {
|
||||
currentPlayerHeader.getStyleClass().remove("my-turn");
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChat(String player, String message) {
|
||||
// Todo
|
||||
}
|
||||
|
||||
public void gameOver(boolean iWon, String winner) {
|
||||
ViewStack.push(new GameOverView(iWon, winner));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.game.TicTacToeGame;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class LocalMultiplayerView extends View {
|
||||
private final GameInformation information;
|
||||
|
||||
public LocalMultiplayerView(GameInformation information) {
|
||||
super(true, "bg-primary");
|
||||
this.information = information;
|
||||
}
|
||||
|
||||
public LocalMultiplayerView(GameInformation.Type type) {
|
||||
this(new GameInformation(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Button playButton = button();
|
||||
playButton.setText(AppContext.getString("play"));
|
||||
playButton.setOnAction(_ -> {
|
||||
for (final GameInformation.Player player : information.players) {
|
||||
if (player.name.isEmpty()) {
|
||||
ViewStack.push(new ErrorView(AppContext.getString("please-enter-your-name")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (information.type) {
|
||||
case TICTACTOE: new TicTacToeGame(information, 0, null, null);
|
||||
case REVERSI: break;
|
||||
}
|
||||
});
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
hbox(
|
||||
setupPlayers()
|
||||
),
|
||||
|
||||
separator(),
|
||||
playButton
|
||||
))
|
||||
);
|
||||
|
||||
final Button backButton = button();
|
||||
backButton.setText(AppContext.getString("back"));
|
||||
backButton.setOnAction(_ -> { ViewStack.push(new MainView()); });
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
backButton
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private VBox[] setupPlayers() {
|
||||
final VBox[] playerBoxes = new VBox[GameInformation.Type.playerCount(information.type)];
|
||||
|
||||
for (int i = 0; i < playerBoxes.length; i++) {
|
||||
final int index = i;
|
||||
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
|
||||
final Text playerHeader = header();
|
||||
playerHeader.setText(AppContext.getString("player") + " #" + (i + 1));
|
||||
|
||||
nodes.add(playerHeader);
|
||||
nodes.add(separator());
|
||||
|
||||
final Text nameText = text();
|
||||
nameText.setText(AppContext.getString("name"));
|
||||
|
||||
if (information.players[i].isHuman) {
|
||||
final Button playerToggle = button();
|
||||
playerToggle.setText(AppContext.getString("player"));
|
||||
playerToggle.setOnAction(_ -> {
|
||||
information.players[index].isHuman = false;
|
||||
cleanup();
|
||||
setup();
|
||||
});
|
||||
|
||||
nodes.add(vboxFill(playerToggle));
|
||||
|
||||
final TextField playerNameInput = input();
|
||||
playerNameInput.setPromptText(AppContext.getString("enter-your-name"));
|
||||
playerNameInput.setText(information.players[i].name);
|
||||
playerNameInput.textProperty().addListener((_, _, newValue) -> {
|
||||
information.players[index].name = newValue;
|
||||
});
|
||||
|
||||
nodes.add(vboxFill(nameText, playerNameInput));
|
||||
} else {
|
||||
final Button computerToggle = button();
|
||||
computerToggle.setText(AppContext.getString("computer"));
|
||||
computerToggle.setOnAction(_ -> {
|
||||
information.players[index].isHuman = true;
|
||||
cleanup();
|
||||
setup();
|
||||
});
|
||||
|
||||
nodes.add(vboxFill(computerToggle));
|
||||
|
||||
information.players[i].name = "Pism Bot V" + i;
|
||||
|
||||
final Text computerNameText = text();
|
||||
computerNameText.setText(information.players[index].name);
|
||||
|
||||
nodes.add(vboxFill(nameText, computerNameText));
|
||||
|
||||
final Text computerDifficultyText = text();
|
||||
computerDifficultyText.setText(AppContext.getString("computer-difficulty"));
|
||||
|
||||
final Slider computerDifficultySlider = slider();
|
||||
computerDifficultySlider.setMin(0);
|
||||
computerDifficultySlider.setMax(GameInformation.Type.maxDepth(information.type));
|
||||
computerDifficultySlider.setValue(information.players[i].computerDifficulty);
|
||||
computerDifficultySlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
information.players[index].computerDifficulty = newValue.intValue();
|
||||
});
|
||||
|
||||
nodes.add(vboxFill(computerDifficultyText, computerDifficultySlider));
|
||||
|
||||
final Text computerThinkTimeText = text();
|
||||
computerThinkTimeText.setText(AppContext.getString("computer-think-time"));
|
||||
|
||||
final Slider computerThinkTimeSlider = slider();
|
||||
computerThinkTimeSlider.setMin(0);
|
||||
computerThinkTimeSlider.setMax(5);
|
||||
computerThinkTimeSlider.setValue(information.players[i].computerThinkTime);
|
||||
computerThinkTimeSlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
information.players[index].computerThinkTime = newValue.intValue();
|
||||
});
|
||||
|
||||
nodes.add(vboxFill(computerThinkTimeText, computerThinkTimeSlider));
|
||||
}
|
||||
|
||||
playerBoxes[i] = vboxFill(nodes.toArray(new Node[0]));
|
||||
}
|
||||
|
||||
return playerBoxes;
|
||||
}
|
||||
}
|
||||
43
app/src/main/java/org/toop/app/view/views/LocalView.java
Normal file
43
app/src/main/java/org/toop/app/view/views/LocalView.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
|
||||
public final class LocalView extends View {
|
||||
public LocalView() {
|
||||
super(true, "bg-primary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Button ticTacToeButton = button();
|
||||
ticTacToeButton.setText(AppContext.getString("tic-tac-toe"));
|
||||
ticTacToeButton.setOnAction(_ -> { ViewStack.push(new LocalMultiplayerView(GameInformation.Type.TICTACTOE)); });
|
||||
|
||||
final Button reversiButton = button();
|
||||
reversiButton.setText(AppContext.getString("reversi"));
|
||||
reversiButton.setOnAction(_ -> {});
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
ticTacToeButton,
|
||||
reversiButton
|
||||
))
|
||||
);
|
||||
|
||||
final Button backButton = button();
|
||||
backButton.setText(AppContext.getString("back"));
|
||||
backButton.setOnAction(_ -> { ViewStack.push(new MainView()); });
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
backButton
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
48
app/src/main/java/org/toop/app/view/views/MainView.java
Normal file
48
app/src/main/java/org/toop/app/view/views/MainView.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
|
||||
public final class MainView extends View {
|
||||
public MainView() {
|
||||
super(true, "bg-primary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Button localButton = button();
|
||||
localButton.setText(AppContext.getString("local"));
|
||||
localButton.setOnAction(_ -> { ViewStack.push(new LocalView()); });
|
||||
|
||||
final Button onlineButton = button();
|
||||
onlineButton.setText(AppContext.getString("online"));
|
||||
onlineButton.setOnAction(_ -> { ViewStack.push(new OnlineView()); });
|
||||
|
||||
final Button creditsButton = button();
|
||||
creditsButton.setText(AppContext.getString("credits"));
|
||||
creditsButton.setOnAction(_ -> { ViewStack.push(new CreditsView()); });
|
||||
|
||||
final Button optionsButton = button();
|
||||
optionsButton.setText(AppContext.getString("options"));
|
||||
optionsButton.setOnAction(_ -> { ViewStack.push(new OptionsView()); });
|
||||
|
||||
final Button quitButton = button();
|
||||
quitButton.setText(AppContext.getString("quit"));
|
||||
quitButton.setOnAction(_ -> { App.startQuit(); });
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
localButton,
|
||||
onlineButton,
|
||||
creditsButton,
|
||||
optionsButton,
|
||||
quitButton
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
83
app/src/main/java/org/toop/app/view/views/OnlineView.java
Normal file
83
app/src/main/java/org/toop/app/view/views/OnlineView.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.Server;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
public class OnlineView extends View {
|
||||
public OnlineView() {
|
||||
super(true, "bg-primary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text serverInformationHeader = header();
|
||||
serverInformationHeader.setText(AppContext.getString("server-information"));
|
||||
|
||||
final Text serverIPText = text();
|
||||
serverIPText.setText(AppContext.getString("ip-address"));
|
||||
|
||||
final TextField serverIPInput = input();
|
||||
serverIPInput.setPromptText(AppContext.getString("enter-the-server-ip"));
|
||||
|
||||
final Text serverPortText = text();
|
||||
serverPortText.setText(AppContext.getString("port"));
|
||||
|
||||
final TextField serverPortInput = input();
|
||||
serverPortInput.setPromptText(AppContext.getString("enter-the-server-port"));
|
||||
|
||||
final Text playerNameText = text();
|
||||
playerNameText.setText(AppContext.getString("player-name"));
|
||||
|
||||
final TextField playerNameInput = input();
|
||||
playerNameInput.setPromptText(AppContext.getString("enter-your-name"));
|
||||
|
||||
final Button connectButton = button();
|
||||
connectButton.setText(AppContext.getString("connect"));
|
||||
connectButton.setOnAction(_ -> {
|
||||
new Server(serverIPInput.getText(), serverPortInput.getText(), playerNameInput.getText());
|
||||
});
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
serverInformationHeader,
|
||||
separator(),
|
||||
|
||||
vboxFill(
|
||||
serverIPText,
|
||||
serverIPInput
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
serverPortText,
|
||||
serverPortInput
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
playerNameText,
|
||||
playerNameInput
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
connectButton
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
final Button backButton = button();
|
||||
backButton.setText(AppContext.getString("back"));
|
||||
backButton.setOnAction(_ -> { ViewStack.push(new MainView()); });
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
backButton
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
249
app/src/main/java/org/toop/app/view/views/OptionsView.java
Normal file
249
app/src/main/java/org/toop/app/view/views/OptionsView.java
Normal file
@@ -0,0 +1,249 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.local.AppContext;
|
||||
import org.toop.local.AppSettings;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class OptionsView extends View {
|
||||
public OptionsView() {
|
||||
super(false, "bg-secondary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text generalHeader = header();
|
||||
generalHeader.setText(AppContext.getString("general"));
|
||||
|
||||
final Text volumeHeader = header();
|
||||
volumeHeader.setText(AppContext.getString("volume"));
|
||||
|
||||
final Text styleHeader = header();
|
||||
styleHeader.setText(AppContext.getString("style"));
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(hboxFill(
|
||||
vboxFill(
|
||||
generalHeader,
|
||||
separator(),
|
||||
|
||||
vboxFill(
|
||||
text("language-text"),
|
||||
combobox("language-combobox")
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
button("fullscreen-button")
|
||||
)
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
volumeHeader,
|
||||
separator(),
|
||||
|
||||
vboxFill(
|
||||
text("master-volume-text"),
|
||||
slider("master-volume-slider")
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
text("effects-volume-text"),
|
||||
slider("effects-volume-slider")
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
text("music-volume-text"),
|
||||
slider("music-volume-slider")
|
||||
)
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
styleHeader,
|
||||
separator(),
|
||||
|
||||
vboxFill(
|
||||
text("theme-text"),
|
||||
combobox("theme-combobox")
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
text("layout-text"),
|
||||
combobox("layout-combobox")
|
||||
)
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
setupLanguageOption();
|
||||
setupMasterVolumeOption();
|
||||
setupEffectsVolumeOption();
|
||||
setupMusicVolumeOption();
|
||||
setupThemeOption();
|
||||
setupLayoutOption();
|
||||
setupFullscreenOption();
|
||||
|
||||
final Button backButton = button();
|
||||
backButton.setText(AppContext.getString("back"));
|
||||
backButton.setOnAction(_ -> { ViewStack.pop(); });
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
backButton
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void setupLanguageOption() {
|
||||
final Text languageText = get("language-text");
|
||||
languageText.setText(AppContext.getString("language"));
|
||||
|
||||
final ComboBox<Locale> languageCombobox = get("language-combobox");
|
||||
languageCombobox.getItems().addAll(AppContext.getLocalization().getAvailableLocales());
|
||||
languageCombobox.setValue(AppContext.getLocale());
|
||||
|
||||
languageCombobox.getSelectionModel().selectedItemProperty().addListener((_, _, newValue) -> {
|
||||
AppSettings.getSettings().setLocale(newValue.toString());
|
||||
AppContext.setLocale(newValue);
|
||||
App.reload();
|
||||
});
|
||||
|
||||
languageCombobox.setConverter(new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Locale locale) {
|
||||
return AppContext.getString(locale.getDisplayName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupMasterVolumeOption() {
|
||||
final Text masterVolumeText = get("master-volume-text");
|
||||
masterVolumeText.setText(AppContext.getString("master-volume"));
|
||||
|
||||
final Slider masterVolumeSlider = get("master-volume-slider");
|
||||
masterVolumeSlider.setMin(0);
|
||||
masterVolumeSlider.setMax(100);
|
||||
masterVolumeSlider.setValue(AppSettings.getSettings().getVolume());
|
||||
|
||||
masterVolumeSlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
AppSettings.getSettings().setVolume(newValue.intValue());
|
||||
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(newValue.doubleValue())).asyncPostEvent();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupEffectsVolumeOption() {
|
||||
final Text effectsVolumeText = get("effects-volume-text");
|
||||
effectsVolumeText.setText(AppContext.getString("effects-volume"));
|
||||
|
||||
final Slider effectsVolumeSlider = get("effects-volume-slider");
|
||||
effectsVolumeSlider.setMin(0);
|
||||
effectsVolumeSlider.setMax(100);
|
||||
effectsVolumeSlider.setValue(AppSettings.getSettings().getFxVolume());
|
||||
|
||||
effectsVolumeSlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
AppSettings.getSettings().setFxVolume(newValue.intValue());
|
||||
new EventFlow().addPostEvent(new AudioEvents.ChangeFxVolume(newValue.doubleValue())).asyncPostEvent();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupMusicVolumeOption() {
|
||||
final Text musicVolumeText = get("music-volume-text");
|
||||
musicVolumeText.setText(AppContext.getString("music-volume"));
|
||||
|
||||
final Slider musicVolumeSlider = get("music-volume-slider");
|
||||
musicVolumeSlider.setMin(0);
|
||||
musicVolumeSlider.setMax(100);
|
||||
musicVolumeSlider.setValue(AppSettings.getSettings().getMusicVolume());
|
||||
|
||||
musicVolumeSlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
AppSettings.getSettings().setMusicVolume(newValue.intValue());
|
||||
new EventFlow().addPostEvent(new AudioEvents.ChangeMusicVolume(newValue.doubleValue())).asyncPostEvent();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupThemeOption() {
|
||||
final Text themeText = get("theme-text");
|
||||
themeText.setText(AppContext.getString("theme"));
|
||||
|
||||
final ComboBox<String> themeCombobox = get("theme-combobox");
|
||||
themeCombobox.getItems().addAll("dark", "light", "high-contrast");
|
||||
themeCombobox.setValue(AppSettings.getSettings().getTheme());
|
||||
|
||||
themeCombobox.getSelectionModel().selectedItemProperty().addListener((_, _, newValue) -> {
|
||||
AppSettings.getSettings().setTheme(newValue);
|
||||
App.setStyle(newValue, AppSettings.getSettings().getLayoutSize());
|
||||
});
|
||||
|
||||
themeCombobox.setConverter(new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(String theme) {
|
||||
return AppContext.getString(theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupLayoutOption() {
|
||||
final Text layoutText = get("layout-text");
|
||||
layoutText.setText(AppContext.getString("layout-size"));
|
||||
|
||||
final ComboBox<String> layoutCombobox = get("layout-combobox");
|
||||
layoutCombobox.getItems().addAll("small", "medium", "large");
|
||||
layoutCombobox.setValue(AppSettings.getSettings().getLayoutSize());
|
||||
|
||||
layoutCombobox.getSelectionModel().selectedItemProperty().addListener((_, _, newValue) -> {
|
||||
AppSettings.getSettings().setLayoutSize(newValue);
|
||||
App.setStyle(AppSettings.getSettings().getTheme(), newValue);
|
||||
});
|
||||
|
||||
layoutCombobox.setConverter(new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(String layout) {
|
||||
return AppContext.getString(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupFullscreenOption() {
|
||||
final Button fullscreenButton = get("fullscreen-button");
|
||||
|
||||
if (AppSettings.getSettings().getFullscreen()) {
|
||||
fullscreenButton.setText(AppContext.getString("windowed"));
|
||||
fullscreenButton.setOnAction(_ -> {
|
||||
AppSettings.getSettings().setFullscreen(false);
|
||||
App.setFullscreen(false);
|
||||
});
|
||||
} else {
|
||||
fullscreenButton.setText(AppContext.getString("fullscreen"));
|
||||
fullscreenButton.setOnAction(_ -> {
|
||||
AppSettings.getSettings().setFullscreen(true);
|
||||
App.setFullscreen(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
40
app/src/main/java/org/toop/app/view/views/QuitView.java
Normal file
40
app/src/main/java/org/toop/app/view/views/QuitView.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
public final class QuitView extends View {
|
||||
public QuitView() {
|
||||
super(false, "bg-popup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text sureHeader = header();
|
||||
sureHeader.setText(AppContext.getString("are-you-sure"));
|
||||
|
||||
final Button yesButton = button();
|
||||
yesButton.setText(AppContext.getString("yes"));
|
||||
yesButton.setOnAction(_ -> { App.quit(); });
|
||||
|
||||
final Button noButton = button();
|
||||
noButton.setText(AppContext.getString("no"));
|
||||
noButton.setOnAction(_ -> { App.stopQuit(); });
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vbox(
|
||||
sureHeader,
|
||||
|
||||
hbox(
|
||||
yesButton,
|
||||
noButton
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
118
app/src/main/java/org/toop/app/view/views/SendChallengeView.java
Normal file
118
app/src/main/java/org/toop/app/view/views/SendChallengeView.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.Server;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public final class SendChallengeView extends View {
|
||||
private final Server server;
|
||||
private final String opponent;
|
||||
private final BiConsumer<GameInformation.Player, String> onSend;
|
||||
|
||||
private final GameInformation.Player playerInformation;
|
||||
|
||||
public SendChallengeView(Server server, String opponent, BiConsumer<GameInformation.Player, String> onSend) {
|
||||
super(true, "bg-popup");
|
||||
|
||||
this.server = server;
|
||||
this.opponent = opponent;
|
||||
this.onSend = onSend;
|
||||
|
||||
playerInformation = new GameInformation.Player();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text challengeText = text();
|
||||
challengeText.setText(AppContext.getString("challenge"));
|
||||
|
||||
final Text opponentHeader = header();
|
||||
opponentHeader.setText(opponent);
|
||||
|
||||
final Text gameText = text();
|
||||
gameText.setText(AppContext.getString("to-a-game-of"));
|
||||
|
||||
final ComboBox<String> gamesCombobox = combobox();
|
||||
gamesCombobox.getItems().addAll(server.getGamesList());
|
||||
gamesCombobox.setValue(gamesCombobox.getItems().getFirst());
|
||||
|
||||
final Button sendButton = button();
|
||||
sendButton.setText(AppContext.getString("send"));
|
||||
sendButton.setOnAction(_ -> { onSend.accept(playerInformation, gamesCombobox.getValue()); });
|
||||
|
||||
final Button cancelButton = button();
|
||||
cancelButton.setText(AppContext.getString("cancel"));
|
||||
cancelButton.setOnAction(_ -> { ViewStack.pop(); });
|
||||
|
||||
final List<Node> nodes = new ArrayList<>();
|
||||
|
||||
if (playerInformation.isHuman) {
|
||||
final Button playerToggle = button();
|
||||
playerToggle.setText(AppContext.getString("player"));
|
||||
playerToggle.setOnAction(_ -> {
|
||||
playerInformation.isHuman = false;
|
||||
cleanup();
|
||||
setup();
|
||||
});
|
||||
|
||||
nodes.add(vbox(playerToggle));
|
||||
} else {
|
||||
final Button computerToggle = button();
|
||||
computerToggle.setText(AppContext.getString("computer"));
|
||||
computerToggle.setOnAction(_ -> {
|
||||
playerInformation.isHuman = true;
|
||||
cleanup();
|
||||
setup();
|
||||
});
|
||||
|
||||
nodes.add(vbox(computerToggle));
|
||||
|
||||
final Text computerDifficultyText = text();
|
||||
computerDifficultyText.setText(AppContext.getString("computer-difficulty"));
|
||||
|
||||
final Slider computerDifficultySlider = slider();
|
||||
computerDifficultySlider.setMin(0);
|
||||
computerDifficultySlider.setMax(GameInformation.Type.maxDepth(Server.gameToType(gamesCombobox.getValue())));
|
||||
computerDifficultySlider.setValue(playerInformation.computerDifficulty);
|
||||
computerDifficultySlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
playerInformation.computerDifficulty = newValue.intValue();
|
||||
});
|
||||
|
||||
nodes.add(vbox(computerDifficultyText, computerDifficultySlider));
|
||||
}
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(hboxFill(
|
||||
vboxFill(
|
||||
challengeText,
|
||||
opponentHeader,
|
||||
gameText,
|
||||
gamesCombobox,
|
||||
separator(),
|
||||
|
||||
hboxFill(
|
||||
sendButton,
|
||||
cancelButton
|
||||
)
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
nodes.toArray(new Node[0])
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
80
app/src/main/java/org/toop/app/view/views/ServerView.java
Normal file
80
app/src/main/java/org/toop/app/view/views/ServerView.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ServerView extends View {
|
||||
private final String user;
|
||||
private final Consumer<String> onPlayerClicked;
|
||||
private final Runnable onDisconnect;
|
||||
|
||||
private ListView<Button> listView;
|
||||
|
||||
public ServerView(String user, Consumer<String> onPlayerClicked, Runnable onDisconnect) {
|
||||
super(true, "bg-primary");
|
||||
|
||||
this.user = user;
|
||||
this.onPlayerClicked = onPlayerClicked;
|
||||
this.onDisconnect = onDisconnect;
|
||||
}
|
||||
|
||||
public void update(List<String> players) {
|
||||
Platform.runLater(() -> {
|
||||
listView.getItems().clear();
|
||||
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
final int finalI = i;
|
||||
|
||||
final Button button = button();
|
||||
button.setText(players.get(i));
|
||||
button.setOnAction(_ -> {
|
||||
onPlayerClicked.accept(players.get(finalI));
|
||||
});
|
||||
|
||||
listView.getItems().add(button);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
final Text playerHeader = header();
|
||||
playerHeader.setText(user);
|
||||
|
||||
listView = new ListView<Button>();
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
vbox(
|
||||
playerHeader,
|
||||
separator()
|
||||
),
|
||||
|
||||
listView
|
||||
))
|
||||
);
|
||||
|
||||
final Button disconnectButton = button();
|
||||
disconnectButton.setText(AppContext.getString("disconnect"));
|
||||
disconnectButton.setOnAction(_ -> {
|
||||
onDisconnect.run();
|
||||
ViewStack.push(new OnlineView());
|
||||
});
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
disconnectButton
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.toop.local;
|
||||
|
||||
import jdk.jfr.Event;
|
||||
import org.toop.app.App;
|
||||
import org.toop.framework.asset.resources.SettingsAsset;
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
@@ -11,10 +10,9 @@ import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
public class AppSettings {
|
||||
private static SettingsAsset settingsAsset;
|
||||
|
||||
private SettingsAsset settingsAsset;
|
||||
|
||||
public void applySettings() {
|
||||
public static void applySettings() {
|
||||
SettingsAsset settings = getPath();
|
||||
if (!settings.isLoaded()) {
|
||||
settings.load();
|
||||
@@ -29,8 +27,8 @@ public class AppSettings {
|
||||
App.setStyle(settingsAsset.getTheme(), settingsAsset.getLayoutSize());
|
||||
}
|
||||
|
||||
public SettingsAsset getPath() {
|
||||
if (this.settingsAsset == null) {
|
||||
public static SettingsAsset getPath() {
|
||||
if (settingsAsset == null) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String basePath;
|
||||
|
||||
@@ -46,8 +44,13 @@ public class AppSettings {
|
||||
}
|
||||
|
||||
File settingsFile = new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
|
||||
this.settingsAsset = new SettingsAsset(settingsFile);
|
||||
settingsAsset = new SettingsAsset(settingsFile);
|
||||
}
|
||||
return this.settingsAsset;
|
||||
return settingsAsset;
|
||||
}
|
||||
|
||||
public static SettingsAsset getSettings() {
|
||||
assert settingsAsset != null;
|
||||
return settingsAsset;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user