Merge remote-tracking branch 'origin/Development' into Development
# Conflicts: # app/src/main/java/org/toop/app/view/views/GameView.java
1
.idea/dictionaries/project.xml
generated
@@ -5,6 +5,7 @@
|
||||
<w>clid</w>
|
||||
<w>dcompile</w>
|
||||
<w>errorprone</w>
|
||||
<w>español</w>
|
||||
<w>flushnl</w>
|
||||
<w>gaaf</w>
|
||||
<w>gamelist</w>
|
||||
|
||||
@@ -36,4 +36,5 @@ public final class Main {
|
||||
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,17 @@ public class GameInformation {
|
||||
public int getMaxDepth() {
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
public String getTypeToString() {
|
||||
String name = this.name();
|
||||
return switch (name) {
|
||||
case "TICTACTOE" -> "TicTacToe";
|
||||
case "REVERSI" -> "Reversi";
|
||||
case "CONNECT4" -> "Connect4";
|
||||
case "BATTLESHIP" -> "Battleship";
|
||||
default -> name;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class Player {
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
package org.toop.app;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Pos;
|
||||
import org.toop.app.game.Connect4Game;
|
||||
import org.toop.app.game.ReversiGame;
|
||||
import org.toop.app.game.TicTacToeGame;
|
||||
import org.toop.app.widget.WidgetContainer;
|
||||
import org.toop.app.widget.complex.LoadingWidget;
|
||||
import org.toop.app.widget.popup.ChallengePopup;
|
||||
import org.toop.app.widget.popup.ErrorPopup;
|
||||
import org.toop.app.widget.popup.SendChallengePopup;
|
||||
import org.toop.app.widget.view.ServerView;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.eventbus.ListenerHandler;
|
||||
import org.toop.framework.networking.clients.TournamentNetworkingClient;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
import org.toop.framework.networking.types.NetworkingConnector;
|
||||
import org.toop.local.AppContext;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -69,12 +74,18 @@ public final class Server {
|
||||
return;
|
||||
}
|
||||
|
||||
new EventFlow()
|
||||
final int reconnectAttempts = 10;
|
||||
|
||||
var a = new EventFlow()
|
||||
.addPostEvent(NetworkEvents.StartClient.class,
|
||||
new TournamentNetworkingClient(),
|
||||
new NetworkingConnector(ip, parsedPort, 10, 1, TimeUnit.SECONDS)
|
||||
)
|
||||
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
||||
new NetworkingConnector(ip, parsedPort, reconnectAttempts, 1, TimeUnit.SECONDS)
|
||||
);
|
||||
|
||||
a.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
||||
|
||||
a.unsubscribe("startclient");
|
||||
|
||||
this.user = user;
|
||||
clientId = e.clientId();
|
||||
|
||||
@@ -87,9 +98,10 @@ public final class Server {
|
||||
populateGameList();
|
||||
|
||||
}).postEvent();
|
||||
|
||||
new EventFlow().listen(this::handleReceivedChallenge)
|
||||
.listen(this::handleMatchResponse);
|
||||
new EventFlow().listen(NetworkEvents.ChallengeResponse.class, this::handleReceivedChallenge, false)
|
||||
.listen(NetworkEvents.GameMatchResponse.class, this::handleMatchResponse, false);
|
||||
startPopulateScheduler();
|
||||
populateGameList();
|
||||
}
|
||||
|
||||
private void sendChallenge(String opponent) {
|
||||
|
||||
@@ -48,16 +48,17 @@ public abstract class BaseGameThread<TGame extends Game, TAI, TCanvas> {
|
||||
this.game = gameSupplier.get();
|
||||
this.ai = aiSupplier.get();
|
||||
|
||||
String type = information.type.getTypeToString();
|
||||
if (onForfeit == null || onExit == null) {
|
||||
primary = new GameView(null, () -> {
|
||||
isRunning.set(false);
|
||||
WidgetContainer.getCurrentView().transitionPrevious();
|
||||
}, null);
|
||||
}, null, type);
|
||||
} else {
|
||||
primary = new GameView(onForfeit, () -> {
|
||||
isRunning.set(false);
|
||||
onExit.run();
|
||||
}, onMessage);
|
||||
}, onMessage, type);
|
||||
}
|
||||
|
||||
this.canvas = canvasFactory.apply(this::onCellClicked);
|
||||
@@ -70,8 +71,8 @@ public abstract class BaseGameThread<TGame extends Game, TAI, TCanvas> {
|
||||
new Thread(this::localGameThread).start();
|
||||
else
|
||||
new EventFlow()
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse, false)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse, false);
|
||||
|
||||
setGameLabels(myTurn == 0);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@ import javafx.scene.paint.Color;
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.canvas.Connect4Canvas;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.views.GameView;
|
||||
import org.toop.app.view.views.LocalMultiplayerView;
|
||||
import org.toop.app.widget.view.GameView;
|
||||
import org.toop.app.widget.WidgetContainer;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
import org.toop.game.Connect4.Connect4;
|
||||
@@ -32,7 +31,7 @@ public class Connect4Game {
|
||||
private final int columnSize = 7;
|
||||
private final int rowSize = 6;
|
||||
|
||||
private final GameView view;
|
||||
private final GameView primary;
|
||||
private final Connect4Canvas canvas;
|
||||
|
||||
private final AtomicBoolean isRunning;
|
||||
@@ -50,15 +49,15 @@ public class Connect4Game {
|
||||
isRunning = new AtomicBoolean(true);
|
||||
|
||||
if (onForfeit == null || onExit == null) {
|
||||
view = new GameView(null, () -> {
|
||||
primary = new GameView(null, () -> {
|
||||
isRunning.set(false);
|
||||
ViewStack.push(new LocalMultiplayerView(information));
|
||||
}, null);
|
||||
WidgetContainer.getCurrentView().transitionPrevious();
|
||||
}, null, "Connect4");
|
||||
} else {
|
||||
view = new GameView(onForfeit, () -> {
|
||||
primary = new GameView(onForfeit, () -> {
|
||||
isRunning.set(false);
|
||||
onExit.run();
|
||||
}, onMessage);
|
||||
}, onMessage, "Connect4");
|
||||
}
|
||||
|
||||
canvas = new Connect4Canvas(Color.GRAY,
|
||||
@@ -83,17 +82,16 @@ public class Connect4Game {
|
||||
}
|
||||
});
|
||||
|
||||
view.add(Pos.CENTER, canvas.getCanvas());
|
||||
ViewStack.push(view);
|
||||
primary.add(Pos.CENTER, canvas.getCanvas());
|
||||
WidgetContainer.getCurrentView().transitionNext(primary);
|
||||
|
||||
if (onForfeit == null || onExit == null) {
|
||||
new Thread(this::localGameThread).start();
|
||||
setGameLabels(information.players[0].isHuman);
|
||||
} else {
|
||||
new EventFlow()
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse)
|
||||
.listen(NetworkEvents.ReceivedMessage.class, this::onReceivedMessage);
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse, false)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse, false);
|
||||
|
||||
setGameLabels(myTurn == 0);
|
||||
}
|
||||
@@ -109,7 +107,7 @@ public class Connect4Game {
|
||||
final String currentValue = currentTurn == 0? "RED" : "BLUE";
|
||||
final int nextTurn = (currentTurn + 1) % information.type.getPlayerCount();
|
||||
|
||||
view.nextPlayer(information.players[currentTurn].isHuman,
|
||||
primary.nextPlayer(information.players[currentTurn].isHuman,
|
||||
information.players[currentTurn].name,
|
||||
currentValue,
|
||||
information.players[nextTurn].name);
|
||||
@@ -159,9 +157,9 @@ public class Connect4Game {
|
||||
*/
|
||||
if (state != GameState.NORMAL) {
|
||||
if (state == GameState.WIN) {
|
||||
view.gameOver(true, information.players[currentTurn].name);
|
||||
primary.gameOver(true, information.players[currentTurn].name);
|
||||
} else if (state == GameState.DRAW) {
|
||||
view.gameOver(false, "");
|
||||
primary.gameOver(false, "");
|
||||
}
|
||||
|
||||
isRunning.set(false);
|
||||
@@ -188,14 +186,14 @@ public class Connect4Game {
|
||||
if (state != GameState.NORMAL) {
|
||||
if (state == GameState.WIN) {
|
||||
if (response.player().equalsIgnoreCase(information.players[0].name)) {
|
||||
view.gameOver(true, information.players[0].name);
|
||||
primary.gameOver(true, information.players[0].name);
|
||||
gameOver();
|
||||
} else {
|
||||
view.gameOver(false, information.players[1].name);
|
||||
primary.gameOver(false, information.players[1].name);
|
||||
gameOver();
|
||||
}
|
||||
} else if (state == GameState.DRAW) {
|
||||
view.gameOver(false, "");
|
||||
primary.gameOver(false, "");
|
||||
gameOver();
|
||||
}
|
||||
}
|
||||
@@ -243,14 +241,6 @@ public class Connect4Game {
|
||||
.postEvent();
|
||||
}
|
||||
|
||||
private void onReceivedMessage(NetworkEvents.ReceivedMessage msg) {
|
||||
if (!isRunning.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
view.updateChat(msg.message());
|
||||
}
|
||||
|
||||
private void updateCanvas() {
|
||||
canvas.clearAll();
|
||||
|
||||
@@ -267,7 +257,7 @@ public class Connect4Game {
|
||||
final int currentTurn = game.getCurrentTurn();
|
||||
final String currentValue = currentTurn == 0? "RED" : "BLUE";
|
||||
|
||||
view.nextPlayer(isMe,
|
||||
primary.nextPlayer(isMe,
|
||||
information.players[isMe? 0 : 1].name,
|
||||
currentValue,
|
||||
information.players[isMe? 1 : 0].name);
|
||||
|
||||
@@ -55,12 +55,12 @@ public final class ReversiGame {
|
||||
primary = new GameView(null, () -> {
|
||||
isRunning.set(false);
|
||||
WidgetContainer.getCurrentView().transitionPrevious();
|
||||
}, null);
|
||||
}, null, "Reversi");
|
||||
} else {
|
||||
primary = new GameView(onForfeit, () -> {
|
||||
isRunning.set(false);
|
||||
onExit.run();
|
||||
}, onMessage);
|
||||
}, onMessage, "Reversi");
|
||||
}
|
||||
|
||||
canvas = new ReversiCanvas(Color.BLACK,
|
||||
@@ -95,8 +95,8 @@ public final class ReversiGame {
|
||||
setGameLabels(information.players[0].isHuman);
|
||||
} else {
|
||||
new EventFlow()
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse, false)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse, false);
|
||||
|
||||
setGameLabels(myTurn == 0);
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ public final class TicTacToeGame {
|
||||
primary = new GameView(null, () -> {
|
||||
isRunning.set(false);
|
||||
WidgetContainer.getCurrentView().transitionPrevious();
|
||||
}, null);
|
||||
}, null, "TicTacToe");
|
||||
} else {
|
||||
primary = new GameView(onForfeit, () -> {
|
||||
isRunning.set(false);
|
||||
onExit.run();
|
||||
}, onMessage);
|
||||
}, onMessage, "TicTacToe");
|
||||
}
|
||||
|
||||
canvas = new TicTacToeCanvas(Color.GRAY,
|
||||
@@ -88,8 +88,8 @@ public final class TicTacToeGame {
|
||||
new Thread(this::localGameThread).start();
|
||||
} else {
|
||||
new EventFlow()
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse);
|
||||
.listen(NetworkEvents.GameMoveResponse.class, this::onMoveResponse, false)
|
||||
.listen(NetworkEvents.YourTurnResponse.class, this::onYourTurnResponse, false);
|
||||
|
||||
setGameLabels(myTurn == 0);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ import org.toop.game.enumerators.GameState;
|
||||
import org.toop.game.records.Move;
|
||||
import org.toop.game.tictactoe.TicTacToe;
|
||||
import org.toop.game.tictactoe.TicTacToeAI;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
|
||||
@@ -1,400 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package org.toop.app.view.displays;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ProgressBar;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.toop.app.widget.Widget;
|
||||
import org.toop.framework.audio.AudioEventListener;
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.framework.eventbus.GlobalEventBus;
|
||||
|
||||
public class SongDisplay extends VBox implements Widget {
|
||||
|
||||
private final Text songTitle;
|
||||
private final ProgressBar progressBar;
|
||||
private final Text progressText;
|
||||
private boolean paused = false;
|
||||
|
||||
public SongDisplay() {
|
||||
new EventFlow()
|
||||
.listen(this::updateTheSong);
|
||||
|
||||
setAlignment(Pos.CENTER);
|
||||
getStyleClass().add("song-display");
|
||||
|
||||
songTitle = new Text("song playing");
|
||||
songTitle.getStyleClass().add("song-title");
|
||||
|
||||
progressBar = new ProgressBar(0);
|
||||
progressBar.getStyleClass().add("progress-bar");
|
||||
|
||||
progressText = new Text("0:00/0:00");
|
||||
progressText.getStyleClass().add("progress-text");
|
||||
|
||||
Button skipButton = new Button(">>");
|
||||
Button pauseButton = new Button("⏸");
|
||||
Button previousButton = new Button("<<");
|
||||
|
||||
skipButton.getStyleClass().setAll("skip-button");
|
||||
pauseButton.getStyleClass().setAll("pause-button");
|
||||
previousButton.getStyleClass().setAll("previous-button");
|
||||
|
||||
skipButton.setOnAction( event -> {
|
||||
GlobalEventBus.post(new AudioEvents.SkipMusic());
|
||||
paused = false;
|
||||
pauseButton.setText(getPlayString(paused));
|
||||
});
|
||||
|
||||
pauseButton.setOnAction(event -> {
|
||||
GlobalEventBus.post(new AudioEvents.PauseMusic());
|
||||
paused = !paused;
|
||||
pauseButton.setText(getPlayString(paused));
|
||||
});
|
||||
|
||||
previousButton.setOnAction( event -> {
|
||||
GlobalEventBus.post(new AudioEvents.PreviousMusic());
|
||||
paused = false;
|
||||
pauseButton.setText(getPlayString(paused));
|
||||
});
|
||||
|
||||
HBox control = new HBox(10, previousButton, pauseButton, skipButton);
|
||||
control.setAlignment(Pos.CENTER);
|
||||
control.getStyleClass().add("controls");
|
||||
|
||||
getChildren().addAll(songTitle, progressBar, progressText, control);
|
||||
}
|
||||
|
||||
private void updateTheSong(AudioEvents.PlayingMusic event) {
|
||||
Platform.runLater(() -> {
|
||||
String text = event.name();
|
||||
text = text.substring(0, text.length() - 4);
|
||||
songTitle.setText(text);
|
||||
double currentPos = event.currentPosition();
|
||||
double duration = event.duration();
|
||||
if (currentPos / duration > 0.05) {
|
||||
double progress = currentPos / duration;
|
||||
progressBar.setProgress(progress);
|
||||
}
|
||||
else if (currentPos / duration < 0.05) {
|
||||
progressBar.setProgress(0.05);
|
||||
}
|
||||
progressText.setText(getTimeString(event.currentPosition(), event.duration()));
|
||||
});
|
||||
}
|
||||
|
||||
private String getTimeString(long position, long duration) {
|
||||
long positionMinutes = position / 60;
|
||||
long durationMinutes = duration / 60;
|
||||
long positionSeconds = position % 60;
|
||||
long durationSeconds = duration % 60;
|
||||
String positionSecondsStr = String.valueOf(positionSeconds);
|
||||
String durationSecondsStr = String.valueOf(durationSeconds);
|
||||
|
||||
if (positionSeconds < 10) {
|
||||
positionSecondsStr = "0" + positionSeconds;
|
||||
}
|
||||
if (durationSeconds < 10) {
|
||||
durationSecondsStr = "0" + durationSeconds;
|
||||
}
|
||||
|
||||
String time = positionMinutes + ":" + positionSecondsStr + " / " + durationMinutes + ":" + durationSecondsStr;
|
||||
return time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
private String getPlayString(boolean paused) {
|
||||
if (paused) {
|
||||
return "▶";
|
||||
}
|
||||
else {
|
||||
return "⏸";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
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 org.toop.app.view.displays.SongDisplay;
|
||||
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.text.Text;
|
||||
|
||||
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(Server.gameToType(game).getMaxDepth());
|
||||
computerDifficultySlider.setValue(playerInformation.computerDifficulty);
|
||||
computerDifficultySlider.valueProperty().addListener((_, _, newValue) -> {
|
||||
playerInformation.computerDifficulty = newValue.intValue();
|
||||
});
|
||||
|
||||
nodes.add(vbox(computerDifficultyText, computerDifficultySlider));
|
||||
}
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(hboxFill(
|
||||
vboxFill(
|
||||
challengeText,
|
||||
challengerHeader,
|
||||
gameText,
|
||||
separator(),
|
||||
|
||||
hboxFill(
|
||||
acceptButton,
|
||||
denyButton
|
||||
)
|
||||
),
|
||||
|
||||
vboxFill(
|
||||
nodes.toArray(new Node[0])
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
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.app.view.displays.SongDisplay;
|
||||
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");
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.displays.SongDisplay;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
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;
|
||||
|
||||
private final ListView<Text> chatListView;
|
||||
private final TextField chatInput;
|
||||
|
||||
public GameView(Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
||||
assert onExit != null;
|
||||
|
||||
super(true, "bg-primary");
|
||||
|
||||
if (onForfeit != null) {
|
||||
forfeitButton = button();
|
||||
forfeitButton.setText(AppContext.getString("forfeit"));
|
||||
forfeitButton.setOnAction(_ -> onForfeit.run());
|
||||
} else {
|
||||
forfeitButton = null;
|
||||
}
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
if (onMessage != null) {
|
||||
chatListView = new ListView<Text>();
|
||||
|
||||
chatInput = input();
|
||||
chatInput.setOnAction(_ -> {
|
||||
onMessage.accept(chatInput.getText());
|
||||
chatInput.setText("");
|
||||
});
|
||||
} else {
|
||||
chatListView = null;
|
||||
chatInput = 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
|
||||
)
|
||||
);
|
||||
|
||||
if (chatListView != null) {
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
chatListView,
|
||||
chatInput
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) {
|
||||
Platform.runLater(() -> {
|
||||
currentPlayerHeader.setText(currentPlayer);
|
||||
currentMoveHeader.setText(currentMove);
|
||||
|
||||
nextPlayerHeader.setText(nextPlayer);
|
||||
|
||||
if (isMe) {
|
||||
currentPlayerHeader.getStyleClass().add("my-turn");
|
||||
} else {
|
||||
currentPlayerHeader.getStyleClass().remove("my-turn");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void updateChat(String message) {
|
||||
if (chatListView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Text messageText = text();
|
||||
messageText.setText(message);
|
||||
|
||||
chatListView.getItems().add(messageText);
|
||||
}
|
||||
|
||||
public void gameOver(boolean iWon, String winner) {
|
||||
ViewStack.push(new GameOverView(iWon, winner));
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.game.Connect4Game;
|
||||
import org.toop.app.game.ReversiGame;
|
||||
import org.toop.app.game.TicTacToeGameThread;
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.displays.SongDisplay;
|
||||
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 TicTacToeGameThread(information); break;
|
||||
case REVERSI: new ReversiGame(information); break;
|
||||
case CONNECT4: new Connect4Game(information); break;
|
||||
// case BATTLESHIP: new BattleshipGame(information); break;
|
||||
}
|
||||
});
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
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[information.type.getPlayerCount()];
|
||||
|
||||
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(information.type.getMaxDepth());
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
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.app.view.displays.SongDisplay;
|
||||
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(_ -> { ViewStack.push(new LocalMultiplayerView(GameInformation.Type.REVERSI)); });
|
||||
|
||||
final Button connect4Button = button();
|
||||
connect4Button.setText(AppContext.getString("connect4"));
|
||||
connect4Button.setOnAction(_ -> { ViewStack.push(new LocalMultiplayerView(GameInformation.Type.CONNECT4)); });
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
ticTacToeButton,
|
||||
reversiButton,
|
||||
connect4Button
|
||||
))
|
||||
);
|
||||
|
||||
final Button backButton = button();
|
||||
backButton.setText(AppContext.getString("back"));
|
||||
backButton.setOnAction(_ -> { ViewStack.push(new MainView()); });
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
add(Pos.BOTTOM_LEFT,
|
||||
vboxFill(
|
||||
backButton
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
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 org.toop.app.view.displays.SongDisplay;
|
||||
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(); });
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
add(Pos.CENTER,
|
||||
fit(vboxFill(
|
||||
localButton,
|
||||
onlineButton,
|
||||
creditsButton,
|
||||
optionsButton,
|
||||
quitButton
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
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.app.view.displays.SongDisplay;
|
||||
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());
|
||||
});
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
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.app.view.displays.SongDisplay;
|
||||
import org.toop.framework.audio.VolumeControl;
|
||||
import org.toop.framework.audio.events.AudioEvents;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.local.AppContext;
|
||||
import org.toop.local.AppSettings;
|
||||
|
||||
import 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(); });
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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(), VolumeControl.MASTERVOLUME)).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.ChangeVolume(newValue.doubleValue(), VolumeControl.FX)).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.ChangeVolume(newValue.doubleValue(), VolumeControl.MUSIC)).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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
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
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
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 org.toop.local.AppContext;
|
||||
|
||||
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 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(false, "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.getGameList());
|
||||
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(Server.gameToType(gamesCombobox.getValue()).getMaxDepth());
|
||||
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])
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package org.toop.app.view.views;
|
||||
|
||||
import org.toop.app.view.View;
|
||||
import org.toop.app.view.ViewStack;
|
||||
import org.toop.app.view.displays.SongDisplay;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
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>();
|
||||
|
||||
final SongDisplay songdisplay = new SongDisplay();
|
||||
|
||||
|
||||
add(Pos.BOTTOM_RIGHT,
|
||||
fit(vboxFill(
|
||||
songdisplay
|
||||
)));
|
||||
|
||||
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,7 +1,10 @@
|
||||
package org.toop.app.widget;
|
||||
|
||||
import javafx.scene.image.ImageView;
|
||||
import org.toop.framework.resource.resources.ImageAsset;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
@@ -19,37 +22,54 @@ import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public final class Primitive {
|
||||
public static Text header(String key) {
|
||||
public static Text header(String key, boolean localize) {
|
||||
var header = new Text();
|
||||
header.getStyleClass().add("header");
|
||||
|
||||
if (!key.isEmpty()) {
|
||||
header.setText(AppContext.getString(key));
|
||||
header.textProperty().bind(AppContext.bindToKey(key));
|
||||
if (localize) header.setText(AppContext.getString(key)); else header.setText(key);
|
||||
header.textProperty().bind(AppContext.bindToKey(key, localize));
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
public static Text text(String key) {
|
||||
public static Text header(String key) {
|
||||
return header(key, true);
|
||||
}
|
||||
|
||||
public static Text text(String key, boolean localize) {
|
||||
var text = new Text();
|
||||
text.getStyleClass().add("text");
|
||||
|
||||
if (!key.isEmpty()) {
|
||||
text.setText(AppContext.getString(key));
|
||||
text.textProperty().bind(AppContext.bindToKey(key));
|
||||
if (localize) text.setText(AppContext.getString(key)); else text.setText(key);
|
||||
text.textProperty().bind(AppContext.bindToKey(key, localize));
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public static Button button(String key, Runnable onAction) {
|
||||
public static Text text(String key) {
|
||||
return text(key, true);
|
||||
}
|
||||
|
||||
public static ImageView image(ImageAsset imageAsset) {
|
||||
ImageView imageView = new ImageView(imageAsset.getImage());
|
||||
imageView.getStyleClass().add("image");
|
||||
imageView.setPreserveRatio(true);
|
||||
imageView.setFitWidth(400);
|
||||
imageView.setFitHeight(400);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
public static Button button(String key, Runnable onAction, boolean localize) {
|
||||
var button = new Button();
|
||||
button.getStyleClass().add("button");
|
||||
|
||||
if (!key.isEmpty()) {
|
||||
button.setText(AppContext.getString(key));
|
||||
button.textProperty().bind(AppContext.bindToKey(key));
|
||||
if (localize) button.setText(AppContext.getString(key)); else button.setText(key);
|
||||
button.textProperty().bind(AppContext.bindToKey(key, localize));
|
||||
}
|
||||
|
||||
if (onAction != null) {
|
||||
@@ -60,13 +80,17 @@ public final class Primitive {
|
||||
return button;
|
||||
}
|
||||
|
||||
public static TextField input(String promptKey, String text, Consumer<String> onValueChanged) {
|
||||
public static Button button(String key, Runnable onAction) {
|
||||
return button(key, onAction, true);
|
||||
}
|
||||
|
||||
public static TextField input(String promptKey, String text, Consumer<String> onValueChanged, boolean localize) {
|
||||
var input = new TextField();
|
||||
input.getStyleClass().add("input");
|
||||
|
||||
if (!promptKey.isEmpty()) {
|
||||
input.setPromptText(AppContext.getString(promptKey));
|
||||
input.promptTextProperty().bind(AppContext.bindToKey(promptKey));
|
||||
if (localize) input.setPromptText(AppContext.getString(promptKey)); else input.setPromptText(promptKey);
|
||||
input.promptTextProperty().bind(AppContext.bindToKey(promptKey, localize));
|
||||
}
|
||||
|
||||
input.setText(text);
|
||||
@@ -79,6 +103,10 @@ public final class Primitive {
|
||||
return input;
|
||||
}
|
||||
|
||||
public static TextField input(String promptKey, String text, Consumer<String> onValueChanged) {
|
||||
return input(promptKey, text, onValueChanged, true);
|
||||
}
|
||||
|
||||
public static Slider slider(int min, int max, int value, Consumer<Integer> onValueChanged) {
|
||||
var slider = new Slider();
|
||||
slider.getStyleClass().add("slider");
|
||||
|
||||
5
app/src/main/java/org/toop/app/widget/Updatable.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package org.toop.app.widget;
|
||||
|
||||
public interface Updatable {
|
||||
void update();
|
||||
}
|
||||
@@ -62,4 +62,16 @@ public final class WidgetContainer {
|
||||
public static ViewWidget getCurrentView() {
|
||||
return currentView;
|
||||
}
|
||||
|
||||
public static void setCurrentView(ViewWidget view) {
|
||||
if (root == null || view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
root.getChildren().clear();
|
||||
root.getChildren().add(view.getNode());
|
||||
currentView = view;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.toop.app.widget.complex;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ProgressBar;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
public class LoadingWidget extends ViewWidget implements Update { // TODO make of widget type
|
||||
private final ProgressBar progressBar;
|
||||
|
||||
private Runnable success = () -> {};
|
||||
private Runnable failure = () -> {};
|
||||
private int maxAmount;
|
||||
private int amount;
|
||||
private float percentage = 0.0f;
|
||||
|
||||
|
||||
public LoadingWidget(int startAmount, int maxAmount) {
|
||||
|
||||
amount = startAmount;
|
||||
this.maxAmount = maxAmount;
|
||||
|
||||
progressBar = new ProgressBar();
|
||||
|
||||
HBox box = new HBox(10, progressBar);
|
||||
add(Pos.CENTER, box);
|
||||
}
|
||||
|
||||
public void setMaxAmount(int maxAmount) {
|
||||
this.maxAmount = maxAmount;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
update();
|
||||
}
|
||||
|
||||
public void setAmount() {
|
||||
setAmount(this.amount+1);
|
||||
}
|
||||
|
||||
public void setOnSuccess(Runnable onSuccess) {
|
||||
success = onSuccess;
|
||||
}
|
||||
|
||||
public void setOnFailure(Runnable onFailure) {
|
||||
failure = onFailure;
|
||||
}
|
||||
|
||||
public void triggerSuccess() {
|
||||
success.run();
|
||||
}
|
||||
|
||||
public void triggerFailure() {
|
||||
failure.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (amount >= maxAmount) {
|
||||
triggerSuccess();
|
||||
System.out.println("triggered");
|
||||
this.hide();
|
||||
return;
|
||||
} else if (amount < 0) {
|
||||
triggerFailure();
|
||||
System.out.println("triggerFailure");
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
percentage = (float) amount / maxAmount;
|
||||
progressBar.setProgress(percentage);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,21 @@
|
||||
package org.toop.app.widget.complex;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
|
||||
public abstract class PopupWidget extends StackWidget {
|
||||
private final Button popButton;
|
||||
|
||||
public PopupWidget() {
|
||||
super("bg-popup");
|
||||
|
||||
popButton = new Button("X");
|
||||
popButton.setOnAction(_ -> hide());
|
||||
|
||||
add(Pos.TOP_RIGHT, popButton);
|
||||
}
|
||||
|
||||
protected void setOnPop(Runnable onPop) {
|
||||
popButton.setOnAction(_ -> onPop.run());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.toop.app.widget.complex;
|
||||
|
||||
public interface Update {
|
||||
void update();
|
||||
}
|
||||
@@ -22,7 +22,7 @@ public class SongDisplay extends VBox implements Widget {
|
||||
|
||||
public SongDisplay() {
|
||||
new EventFlow()
|
||||
.listen(this::updateTheSong);
|
||||
.listen(AudioEvents.PlayingMusic.class, this::updateTheSong, false);
|
||||
|
||||
setAlignment(Pos.CENTER);
|
||||
setMaxHeight(Region.USE_PREF_SIZE);
|
||||
|
||||
@@ -20,5 +20,10 @@ public class QuitPopup extends PopupWidget {
|
||||
});
|
||||
|
||||
add(Pos.CENTER, confirmWidget);
|
||||
|
||||
setOnPop(() -> {
|
||||
App.stopQuit();
|
||||
hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.toop.app.widget.tutorial;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.text.Text;
|
||||
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
|
||||
import org.toop.app.widget.Primitive;
|
||||
import org.toop.app.widget.Updatable;
|
||||
import org.toop.app.widget.WidgetContainer;
|
||||
import org.toop.app.widget.complex.PopupWidget;
|
||||
|
||||
import org.toop.framework.resource.resources.ImageAsset;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A widget base for all the tutorial widgets.
|
||||
*
|
||||
* <p>Usage example:
|
||||
*
|
||||
* <pre>{@code
|
||||
* public class Connect4TutorialWidget extends BaseTutorialWidget {
|
||||
* public Connect4TutorialWidget(Runnable nextScreen) {
|
||||
* super(List.of(
|
||||
* new ImmutablePair<>("connect4.1", ResourceManager.get("connect41.png")),
|
||||
* new ImmutablePair<>("connect4.2", ResourceManager.get("connect42.png"))
|
||||
* ), nextScreen);
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public class BaseTutorialWidget extends PopupWidget implements Updatable {
|
||||
|
||||
private final Text tutorialText;
|
||||
private final ImageView imagery;
|
||||
private final Button previousButton;
|
||||
private final Button nextButton;
|
||||
private final List<ImmutablePair<String, ImageAsset>> pages;
|
||||
private final Runnable nextScreen;
|
||||
|
||||
private int pageIndex = 0;
|
||||
|
||||
public BaseTutorialWidget(List<ImmutablePair<String, ImageAsset>> pages, Runnable nextScreen) {
|
||||
this.tutorialText = Primitive.text(pages.getFirst().getKey());
|
||||
this.imagery = Primitive.image(pages.getFirst().getValue());
|
||||
|
||||
this.pages = pages;
|
||||
this.nextScreen = nextScreen;
|
||||
|
||||
previousButton = Primitive.button("goback", () -> { update(false); this.hide(); });
|
||||
nextButton = Primitive.button(">", () -> update(true));
|
||||
|
||||
var w = Primitive.hbox(
|
||||
previousButton,
|
||||
nextButton
|
||||
);
|
||||
|
||||
var x = Primitive.vbox(imagery, tutorialText);
|
||||
|
||||
add(Pos.CENTER, Primitive.vbox(x, w));
|
||||
|
||||
WidgetContainer.add(Pos.CENTER, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
update(true);
|
||||
}
|
||||
|
||||
// TODO Refactor if statements to make code easier to read.
|
||||
public void update(boolean next) {
|
||||
pageIndex = next ? pageIndex + 1 : pageIndex - 1;
|
||||
|
||||
if (pageIndex >= pages.size()) {
|
||||
pageIndex--;
|
||||
return;
|
||||
} else if (pageIndex < 0) {
|
||||
pageIndex++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pageIndex == pages.size()-1) {
|
||||
nextButton.textProperty().unbind();
|
||||
nextButton.setText(AppContext.getString("startgame"));
|
||||
nextButton.setOnAction((_) -> {
|
||||
this.hide();
|
||||
nextScreen.run();
|
||||
});
|
||||
} else {
|
||||
nextButton.textProperty().unbind();
|
||||
nextButton.setText(AppContext.getString(">"));
|
||||
nextButton.setOnAction((_) -> this.update(true));
|
||||
}
|
||||
|
||||
if (pageIndex == 0) {
|
||||
previousButton.textProperty().unbind();
|
||||
previousButton.setText(AppContext.getString("goback"));
|
||||
previousButton.setOnAction((_) -> this.hide());
|
||||
} else {
|
||||
previousButton.textProperty().unbind();
|
||||
previousButton.setText(AppContext.getString("<"));
|
||||
previousButton.setOnAction((_) -> this.update(false));
|
||||
}
|
||||
|
||||
var currentPage = pages.get(pageIndex);
|
||||
|
||||
var text = currentPage.getKey();
|
||||
var image = currentPage.getValue();
|
||||
|
||||
tutorialText.textProperty().unbind();
|
||||
tutorialText.setText(AppContext.getString(text));
|
||||
imagery.setImage(Primitive.image(image).getImage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.toop.app.widget.tutorial;
|
||||
|
||||
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
|
||||
import org.toop.framework.resource.ResourceManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Connect4TutorialWidget extends BaseTutorialWidget {
|
||||
public Connect4TutorialWidget(Runnable nextScreen) {
|
||||
super(List.of(
|
||||
new ImmutablePair<>("connect4.1", ResourceManager.get("connect41.png")),
|
||||
new ImmutablePair<>("connect4.2", ResourceManager.get("connect42.png"))
|
||||
), nextScreen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.toop.app.widget.tutorial;
|
||||
|
||||
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
|
||||
import org.toop.framework.resource.ResourceManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ReversiTutorialWidget extends BaseTutorialWidget {
|
||||
public ReversiTutorialWidget(Runnable nextScreen) {
|
||||
super(List.of(
|
||||
new ImmutablePair<>("reversi1", ResourceManager.get("reversi1.png")),
|
||||
new ImmutablePair<>("reversi2", ResourceManager.get("reversi2.png")),
|
||||
new ImmutablePair<>("reversi3", ResourceManager.get("cat.jpg")),
|
||||
new ImmutablePair<>("reversi4", ResourceManager.get("cat.jpg"))
|
||||
), nextScreen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.toop.app.widget.tutorial;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import org.toop.app.widget.Primitive;
|
||||
import org.toop.app.widget.WidgetContainer;
|
||||
import org.toop.app.widget.complex.PopupWidget;
|
||||
import org.toop.local.AppSettings;
|
||||
|
||||
public class ShowEnableTutorialWidget extends PopupWidget {
|
||||
|
||||
public ShowEnableTutorialWidget(Runnable tutorial, Runnable nextScreen, Runnable appSettingsSetter) {
|
||||
var a = Primitive.hbox(
|
||||
Primitive.button("ok", () -> { appSettingsSetter.run(); tutorial.run(); this.hide(); }),
|
||||
Primitive.button("no", () -> { appSettingsSetter.run(); nextScreen.run(); this.hide(); }),
|
||||
Primitive.button("never", () -> { AppSettings.getSettings().setTutorialFlag(false); nextScreen.run(); this.hide(); })
|
||||
);
|
||||
|
||||
var txt = Primitive.text("tutorial");
|
||||
add(Pos.CENTER, Primitive.vbox(txt, a));
|
||||
WidgetContainer.add(Pos.CENTER, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.toop.app.widget.tutorial;
|
||||
|
||||
import org.apache.maven.surefire.shared.lang3.tuple.ImmutablePair;
|
||||
import org.toop.framework.resource.ResourceManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TicTacToeTutorialWidget extends BaseTutorialWidget {
|
||||
public TicTacToeTutorialWidget(Runnable nextScreen) {
|
||||
super(List.of(
|
||||
new ImmutablePair<>("tictactoe1", ResourceManager.get("tictactoe1.png")),
|
||||
new ImmutablePair<>("tictactoe2", ResourceManager.get("tictactoe2.png"))
|
||||
), nextScreen);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,18 +11,21 @@ import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.text.Text;
|
||||
import org.toop.app.widget.tutorial.BaseTutorialWidget;
|
||||
import org.toop.app.widget.tutorial.Connect4TutorialWidget;
|
||||
import org.toop.app.widget.tutorial.ReversiTutorialWidget;
|
||||
import org.toop.app.widget.tutorial.TicTacToeTutorialWidget;
|
||||
|
||||
public final class GameView extends ViewWidget {
|
||||
private final Text currentPlayerHeader;
|
||||
private final Text currentMoveHeader;
|
||||
private final Text nextPlayerHeader;
|
||||
|
||||
private final Button forfeitButton;
|
||||
private final Button exitButton;
|
||||
|
||||
private final Button tutorialButton;
|
||||
private final TextField chatInput;
|
||||
|
||||
public GameView(Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
||||
public GameView(Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, String gameType) {
|
||||
currentPlayerHeader = Primitive.header("");
|
||||
currentMoveHeader = Primitive.header("");
|
||||
nextPlayerHeader = Primitive.header("");
|
||||
@@ -48,6 +51,17 @@ public final class GameView extends ViewWidget {
|
||||
chatInput = null;
|
||||
}
|
||||
|
||||
switch(gameType) {
|
||||
case "TicTacToe":
|
||||
this.tutorialButton = Primitive.button("tutorialstring", () -> new TicTacToeTutorialWidget(() -> {})); break;
|
||||
case "Reversi":
|
||||
this.tutorialButton = Primitive.button("tutorialstring", () -> new ReversiTutorialWidget(() -> {})); break;
|
||||
case "Connect4":
|
||||
this.tutorialButton = Primitive.button("tutorialstring", () -> new Connect4TutorialWidget(() -> {})); break;
|
||||
default:
|
||||
this.tutorialButton = null; break;
|
||||
}
|
||||
|
||||
setupLayout();
|
||||
}
|
||||
|
||||
@@ -74,6 +88,10 @@ public final class GameView extends ViewWidget {
|
||||
if (chatInput != null) {
|
||||
add(Pos.BOTTOM_RIGHT, Primitive.vbox(chatInput));
|
||||
}
|
||||
|
||||
if (tutorialButton != null) {
|
||||
add(Pos.TOP_LEFT, tutorialButton);
|
||||
}
|
||||
}
|
||||
|
||||
public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package org.toop.app.widget.view;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.game.Connect4Game;
|
||||
import org.toop.app.game.ReversiGame;
|
||||
import org.toop.app.game.TicTacToeGameThread;
|
||||
import org.toop.app.widget.Primitive;
|
||||
import org.toop.app.widget.WidgetContainer;
|
||||
import org.toop.app.widget.complex.PlayerInfoWidget;
|
||||
import org.toop.app.widget.complex.ViewWidget;
|
||||
import org.toop.app.widget.popup.ErrorPopup;
|
||||
import org.toop.app.widget.tutorial.*;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import org.toop.local.AppSettings;
|
||||
public class LocalMultiplayerView extends ViewWidget {
|
||||
private final GameInformation information;
|
||||
|
||||
@@ -32,11 +35,41 @@ public class LocalMultiplayerView extends ViewWidget {
|
||||
}
|
||||
|
||||
switch (information.type) {
|
||||
case TICTACTOE -> new TicTacToeGameThread(information);
|
||||
case REVERSI -> new ReversiGame(information);
|
||||
case CONNECT4 -> new Connect4Game(information);
|
||||
// case BATTLESHIP -> new BattleshipGame(information);
|
||||
case TICTACTOE:
|
||||
if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstTTT()) {
|
||||
new ShowEnableTutorialWidget(
|
||||
() -> new TicTacToeTutorialWidget(() -> new TicTacToeGameThread(information)),
|
||||
() -> Platform.runLater(() -> new TicTacToeGameThread(information)),
|
||||
() -> AppSettings.getSettings().setFirstTTT(false)
|
||||
);
|
||||
} else {
|
||||
new TicTacToeGameThread(information);
|
||||
}
|
||||
break;
|
||||
case REVERSI:
|
||||
if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstReversi()) {
|
||||
new ShowEnableTutorialWidget(
|
||||
() -> new ReversiTutorialWidget(() -> new ReversiGame(information)),
|
||||
() -> Platform.runLater(() -> new ReversiGame(information)),
|
||||
() -> AppSettings.getSettings().setFirstReversi(false)
|
||||
);
|
||||
} else {
|
||||
new ReversiGame(information);
|
||||
}
|
||||
break;
|
||||
case CONNECT4:
|
||||
if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstConnect4()) {
|
||||
new ShowEnableTutorialWidget(
|
||||
() -> new Connect4TutorialWidget(() -> new Connect4Game(information)),
|
||||
() -> Platform.runLater(() -> new Connect4Game(information)),
|
||||
() -> AppSettings.getSettings().setFirstConnect4(false)
|
||||
);
|
||||
} else {
|
||||
new Connect4Game(information);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// case BATTLESHIP -> new BattleshipGame(information);
|
||||
});
|
||||
|
||||
var playerSection = setupPlayerSections();
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.toop.app.widget.view;
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.widget.Primitive;
|
||||
import org.toop.app.widget.complex.ViewWidget;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
public class MainView extends ViewWidget {
|
||||
|
||||
@@ -29,7 +29,7 @@ public final class ServerView extends ViewWidget {
|
||||
}
|
||||
|
||||
private void setupLayout() {
|
||||
var playerHeader = Primitive.header(user);
|
||||
var playerHeader = Primitive.header(user, false);
|
||||
|
||||
var playerListSection = Primitive.vbox(
|
||||
playerHeader,
|
||||
@@ -52,7 +52,7 @@ public final class ServerView extends ViewWidget {
|
||||
listView.getItems().clear();
|
||||
|
||||
for (String player : players) {
|
||||
var playerButton = Primitive.button(player, () -> onPlayerClicked.accept(player));
|
||||
var playerButton = Primitive.button(player, () -> onPlayerClicked.accept(player), false);
|
||||
listView.getItems().add(playerButton);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,10 +58,19 @@ public class AppContext {
|
||||
return "MISSING RESOURCE";
|
||||
}
|
||||
|
||||
public static StringBinding bindToKey(String key) {
|
||||
return Bindings.createStringBinding(
|
||||
public static StringBinding bindToKey(String key, boolean localize) {
|
||||
if (localize) return Bindings.createStringBinding(
|
||||
() -> localization.getString(key, locale),
|
||||
localeProperty
|
||||
);
|
||||
|
||||
return Bindings.createStringBinding(
|
||||
() -> key
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public static StringBinding bindToKey(String key) {
|
||||
return bindToKey(key, true);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ public class AppSettings {
|
||||
settingsAsset.load();
|
||||
}
|
||||
|
||||
checkSettings();
|
||||
|
||||
Settings settingsData = settingsAsset.getContent();
|
||||
|
||||
AppContext.setLocale(Locale.of(settingsData.locale));
|
||||
@@ -64,4 +66,43 @@ public class AppSettings {
|
||||
public static SettingsAsset getSettings() {
|
||||
return settingsAsset;
|
||||
}
|
||||
|
||||
public static void checkSettings() {
|
||||
Settings s = settingsAsset.getContent();
|
||||
boolean changed = false;
|
||||
|
||||
if (s.showTutorials == null) {
|
||||
settingsAsset.setTutorialFlag(true);
|
||||
changed = true;
|
||||
}
|
||||
if (s.firstReversi == null) {
|
||||
settingsAsset.setFirstReversi(true);
|
||||
changed = true;
|
||||
}
|
||||
if (s.firstTTT == null) {
|
||||
settingsAsset.setFirstTTT(true);
|
||||
changed = true;
|
||||
}
|
||||
if (s.firstConnect4 == null) {
|
||||
settingsAsset.setFirstConnect4(true);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
getSettings().save();
|
||||
}
|
||||
}
|
||||
|
||||
public static void doDefaultSettings() {
|
||||
settingsAsset.setFirstConnect4(true);
|
||||
settingsAsset.setFirstTTT(true);
|
||||
settingsAsset.setFirstReversi(true);
|
||||
settingsAsset.setLocale("en");
|
||||
settingsAsset.setTheme("dark");
|
||||
settingsAsset.setFullscreen(false);
|
||||
settingsAsset.setVolume(100);
|
||||
settingsAsset.setFxVolume(20);
|
||||
settingsAsset.setMusicVolume(15);
|
||||
settingsAsset.setTutorialFlag(true);
|
||||
settingsAsset.setLayoutSize("medium");
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/resources/assets/images/cat.jpg
Normal file
|
After Width: | Height: | Size: 596 KiB |
BIN
app/src/main/resources/assets/images/connect41.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
app/src/main/resources/assets/images/connect42.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
app/src/main/resources/assets/images/reversi1.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/src/main/resources/assets/images/reversi2.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
app/src/main/resources/assets/images/tictactoe1.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
app/src/main/resources/assets/images/tictactoe2.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
@@ -5,6 +5,7 @@ are-you-sure=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f?
|
||||
back=\u0627\u0644\u0631\u062c\u0648\u0639
|
||||
cancel=\u0625\u0644\u063a\u0627\u0621
|
||||
challenge=\u062a\u062d\u062f\u064a
|
||||
connect4=Connect 4
|
||||
computer-difficulty=\u0635\u0639\u0648\u0628 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631
|
||||
computer-think-time=\u0648\u0642\u062a \u062a\u0641\u0643\u064a\u0631 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631
|
||||
computer=\u0643\u0645\u0628\u064a\u0648\u062a\u0631
|
||||
@@ -41,6 +42,7 @@ merge-commander=Merge Commander
|
||||
moral-support=\u062f\u0639\u0645 \u0623\u0639\u0635\u0627\u0628\u064a
|
||||
music-volume=\u0635\u0648\u062a \u0627\u0644\u0645\u0648\u0633\u064a\u0642\u0649
|
||||
name=\u0627\u0633\u0645
|
||||
never=\u0644\u0627\u060c \u0644\u0627 \u0623\u0631\u064A\u062F \u0623\u0646 \u0623\u0631\u0649 \u0623\u064A \u062F\u0631\u0648\u0633 \u0639\u0644\u0649 \u0627\u0644\u0625\u0637\u0644\u0627\u0642.
|
||||
no=\u0644\u0627
|
||||
ok=\u0645\u0648\u0627\u0641\u0642
|
||||
online=\u0645\u062a\u0635\u0644
|
||||
@@ -62,13 +64,27 @@ style=\u0623\u0633\u0644\u0648\u0628
|
||||
the-game-ended-in-a-draw=\u0627\u0646\u062a\u0647\u062a \u0627\u0644\u0644\u0639\u0628\u0629 \u0628\u062a\u0639\u0627\u062f\u0644
|
||||
theme=\u0645\u0648\u0636\u0648\u0639
|
||||
tic-tac-toe=\u062a\u064a\u0643 \u062a\u0627\u0643 \u062a\u0648
|
||||
tictactoe1 =\u0645\u0631\u062d\u0628\u064b\u0627 \u0628\u0643 \u0641\u064a \u0644\u0639\u0628\u0629 \u0625\u0643\u0633-\u0623\u0648! \u064a\u0645\u0643\u0646\u0643 \u0627\u0644\u0642\u064a\u0627\u0645 \u0628\u062d\u0631\u0643\u062a\u0643 \u0628\u0627\u0644\u0646\u0642\u0631 \u0639\u0644\u0649 \u0623\u064a \u0645\u0646 \u0627\u0644\u0645\u0631\u0628\u0639\u0627\u062a \u0627\u0644\u062a\u0633\u0639\u0629 \u0639\u0644\u0649 \u0627\u0644\u0644\u0648\u062d\u0629. \u0627\u0644\u0645\u062b\u0627\u0644 \u0623\u0639\u0644\u0627\u0647:
|
||||
tictactoe2 =\u064a\u0641\u0648\u0632 \u0627\u0644\u0644\u0627\u0639\u0628 \u0639\u0646\u062f\u0645\u0627 \u064a\u062d\u0635\u0644 \u0639\u0644\u0649 \u062b\u0644\u0627\u062b \u0642\u0637\u0639 \u0645\u062a\u062a\u0627\u0644\u064a\u0629 ? \u0623\u0641\u0642\u064a\u064b\u0627 \u0623\u0648 \u0639\u0645\u0648\u062f\u064a\u064b\u0627 \u0623\u0648 \u0642\u0637\u0631\u064a\u064b\u0627. \u0641\u064a \u0627\u0644\u0645\u062b\u0627\u0644 \u0623\u0639\u0644\u0627\u0647\u060c \u064a\u0641\u0648\u0632 \u0627\u0644\u0644\u0627\u0639\u0628 \u0628\u0635\u0641 \u0642\u0637\u0631\u064a \u0645\u0646 \u062b\u0644\u0627\u062b \u0642\u0637\u0639
|
||||
to-a-game-of=\u0625\u0644\u0649 \u0644\u0639\u0628\u0629 \u0645\u0646
|
||||
tutorial=\u0647\u0644 \u062a\u0631\u064a\u062f \u0645\u0634\u0627\u0647\u062f\u0629 \u062f\u0644\u064a\u0644 \u0644\u0647\u0630\u0627 \u0627\u0644\u0644\u0639\u0628\u0629\u061f
|
||||
volume=\u0635\u0648\u062a
|
||||
windowed=\u0641\u064a \u0646\u0641\u0630\u0629
|
||||
yes=\u0646\u0639\u0645
|
||||
you-lost-against=\u0623\u0646\u062a \u062e\u0633\u0631\u062a \u0636\u062f
|
||||
you-were-challenged-by=\u062a\u062d\u062f\u064a\u062a \u0645\u0646
|
||||
you-win=\u0623\u0646\u062a \u062a\u0641\u0648\u0632
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=\u0645\u0631\u062d\u0628\u0627 \u0628\u0643 \u0641\u064a \u0644\u0639\u0628\u0629 Connect 4! \u064a\u0645\u0643\u0646\u0643 \u062a\u0633\u0645\u064a\u0629 \u0627\u0644\u0644\u0639\u0628 \u0645\u0646 \u0637\u0631\u0641 \u0645\u0646 \u0627\u0644\u0639\u0645\u0648\u062f\u0627\u062a. \u0633\u064a\u062a\u0645 \u0648\u0636\u0639 \u0627\u0644\u0644\u062d\u0629 \u0627\u0644\u0623\u0642\u0644 \u0641\u064a \u062a\u0644\u0643 \u0627\u0644\u0639\u0645\u0648\u062f\u0629 \u0627\u0644\u0645\u0644\u0623\u0629 \u0627\u0644\u062a\u064a \u0644\u064a\u0633\u062a\u0645\u0644\u0623 \u0627\u0644\u0645\u0644\u0623\u0629.
|
||||
connect4.2=\u064a\u0645\u0643\u0646\u0643 \u0627\u0644\u0641\u0648\u0632 \u0628\u0647\u0632\u0645 \u0623\u0631\u062c\u0639 \u0645\u0646 \u0627\u0644\u0642\u0637\u0639 \u0627\u0644\u0641\u064a \u0627\u0644\u0623\u0641\u0642 \u0623\u0648 \u0627\u0644\u0645\u064a\u0646 \u0623\u0648 \u0623\u0641\u0642 \u0639\u0644\u0649 \u0627\u0644\u0645\u0644\u0623\u0629!
|
||||
reversi1=\u0645\u0631\u062d\u0628\u0627 \u0628\u0643 \u0641\u064a \u0644\u0639\u0628\u0629 Reversi! \u064a\u0645\u0643\u0646\u0643 \u0627\u0644\u0644\u0639\u0628 \u0628\u0627\u0644\u0646\u0642\u0631 \u0639\u0644\u0649 \u0623\u064a \u0646\u0642\u0637\u0629 \u0645\u0634\u0631\u0641\u0629.
|
||||
reversi2=\u0639\u0646\u062f\u0645\u0627 \u062a\u0646\u0642\u0631 \u0639\u0644\u0649 \u0646\u0642\u0637\u0629 \u0633\u064a\u062a\u063a\u064a\u0631 \u062c\u0645\u064a\u0639 \u0627\u0644\u0644\u0648\u0639\u0627\u0628 \u0628\u064a\u0646 \u0645\u0643\u0627\u0646 \u062a\u0636\u0639 \u0627\u0644\u0646\u0642\u0637\u0629 \u0648\u0646\u0642\u0637\u0629 \u0627\u0644\u0644\u0648\u0639 \u0627\u0644\u062a\u0627\u0644\u064a \u0627\u0644\u0645\u0648\u062c\u0648\u062f.
|
||||
reversi3=\u0645\u0631\u062a\u0643 \u0642\u062f \u064a\u062a\u063a\u0627\u0637 \u0625\u0630\u0627 \u0644\u0645 \u064a\u0643\u0646 \u0647\u0646\u0627\u0643 \u062d\u0631\u0643 \u0642\u0627\u0646\u0648\u0646\u064a.
|
||||
reversi4=\u0627\u0644\u0644\u0627\u0639\u0628 \u0627\u0644\u0630\u064a \u064a\u0641\u0648\u0632 \u0641\u064a \u0646\u0647\u0627\u064a\u0629 \u0627\u0644\u0644\u0639\u0628 \u0647\u0648 \u0627\u0644\u0630\u064a \u064a\u0643\u0648\u0646 \u0644\u062f\u064a\u0647 \u0627\u0644\u0623\u0643\u062b\u0631 \u0645\u0646 \u0627\u0644\u0644\u0648\u0639\u0627\u0628 \u0639\u0644\u0649 \u0627\u0644\u0644\u0648\u062d\u0629.
|
||||
tutorialstring=\u0627\u0644\u062f\u0631\u0633 \u0627\u0644\u062a\u0648\u0636\u064a\u062d\u064a
|
||||
startgame=\u0627\u0628\u062f\u0623 \u0627\u0644\u0644\u0639\u0628\u0629!
|
||||
goback=\u0627\u0631\u062c\u0639
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629
|
||||
chinese=\u4e2d\u6587 (\u0627\u0644\u0635\u064a\u0646\u064a\u0629)
|
||||
|
||||
@@ -9,6 +9,8 @@ computer-difficulty=Computer Schwierigkeit
|
||||
computer-think-time=Computer Denkzeit
|
||||
computer=Computer
|
||||
connect=Verbinden
|
||||
connect4=Connect 4
|
||||
|
||||
credits=Credits
|
||||
dark=Dunkel
|
||||
deny=Ablehnen
|
||||
@@ -41,6 +43,7 @@ merge-commander=Merge Commander
|
||||
moral-support=Mentale Unterst\u00fctzung
|
||||
music-volume=Musiklautst\u00e4rke
|
||||
name=Name
|
||||
never=Nein, ich m\u00f6chte niemals irgendwelche Tutorials sehen.
|
||||
no=Nein
|
||||
ok=Ok
|
||||
online=Online
|
||||
@@ -62,13 +65,28 @@ style=Stil
|
||||
the-game-ended-in-a-draw=Das Spiel endete unentschieden
|
||||
theme=Thema
|
||||
tic-tac-toe=Tic Tac Toe
|
||||
tictactoe1 =Willkommen beim Spiel Tic Tac Toe! Du kannst deinen Zug machen, indem du auf eines der 9 Felder auf dem Spielfeld klickst. Beispiel oben:
|
||||
tictactoe2 =Ein Spieler gewinnt, wenn er drei Steine in einer Reihe hat ? horizontal, vertikal oder diagonal. Im obigen Beispiel gewinnt der Spieler mit einer diagonalen Reihe von drei Steinen.
|
||||
to-a-game-of=zu einem Spiel von
|
||||
tutorial=M\u00f6chtest du ein Tutorial f\u00fcr dieses Spiel sehen?
|
||||
|
||||
volume=Lautst\u00e4rke
|
||||
windowed=Fenstermodus
|
||||
yes=Ja
|
||||
you-lost-against=Sie haben gegen ... verloren
|
||||
you-were-challenged-by=Sie wurden herausgefordert von ...
|
||||
you-win=Sie gewinnen
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=Willkommen beim Spiel Connect 4! Du kannst einen Zug machen, indem du auf eine der Spalten klickst. Der Zug wird in der niedrigsten noch freien Reihe dieser Spalte platziert.
|
||||
connect4.2=Du kannst gewinnen, indem du 4 Spielsteine deiner Farbe horizontal, diagonal oder vertikal verbindest! Siehe das obige Beispiel.
|
||||
reversi1=Willkommen beim Spiel Reversi! Du kannst einen Zug machen, indem du auf einen der leicht transparenten Punkte klickst.
|
||||
reversi2=Wenn du auf einen Punkt klickst, werden alle Spielsteine dazwischen umgedreht, bis zum n<>chsten Punkt. Siehe das Beispiel oben, wo Gelb die zu drehenden Steine ist.
|
||||
reversi3=Dein Zug kann <20>bersprungen werden, wenn es keinen legalen Zug gibt. Dein Gegner spielt dann weiter, bis du einen legalen Zug machen kannst.
|
||||
reversi4=Der Spieler, der am Ende die meisten Steine auf dem Brett hat, gewinnt.
|
||||
tutorialstring=Tutorial
|
||||
startgame=Spiel starten!
|
||||
goback=Zur<EFBFBD>ck
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabisch)
|
||||
chinese=\u4e2d\u6587 (Chinesisch)
|
||||
|
||||
@@ -42,6 +42,7 @@ moral-support=Moral Support
|
||||
music-volume=Music Volume
|
||||
name=Name
|
||||
no=No
|
||||
never=No, I never want to see any tutorials
|
||||
ok=Ok
|
||||
online=Online
|
||||
opengl=OpenGL
|
||||
@@ -62,6 +63,9 @@ style=Style
|
||||
the-game-ended-in-a-draw=The game ended in a draw
|
||||
theme=Theme
|
||||
tic-tac-toe=Tic Tac Toe
|
||||
tictactoe1 =Welcome to the game of Tic Tac Toe! You can make your move by clicking on any of the 9 squares on the board. Example above:
|
||||
tictactoe2 =A player wins by getting 3 pieces in a row - horizontally, vertically or diagonally. In the example above, the player wins with a diagonal row of three pieces.
|
||||
tutorial=Do you want a tutorial for this game?
|
||||
connect4=Connect 4
|
||||
to-a-game-of=to a game of
|
||||
volume=Volume
|
||||
@@ -70,6 +74,19 @@ yes=Yes
|
||||
you-lost-against=You lost against
|
||||
you-were-challenged-by=You were challenged by
|
||||
you-win=You win
|
||||
>=>
|
||||
<=<
|
||||
// tutorial
|
||||
connect4.1=Welcome to the game of Connect 4! You can make a move by clicking one of the columns. The move will be placed in the lowest row of that column that is not filled yet.
|
||||
connect4.2=You can win by getting 4 pieces of your row horizontally, diagonally or vertically! For an example, see above.
|
||||
reversi1=Welcome to the game of Reversi! You can make a move by clicking on one of the slightly transparent dots.
|
||||
reversi2=Clicking on a dot will flip all the moves between where you place the dot and the next dot it finds. See the example above, where yellow is the moves to be flipped.
|
||||
reversi3=Your turn may be skipped if there is no legal move. This will let your opponent play again until you get an opportunity at a legal move.
|
||||
reversi4=The player who wins at the end of the game is the one who has the most pieces on the board.
|
||||
tutorialstring=Tutorial
|
||||
startgame=Start game!
|
||||
goback=Go back
|
||||
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabic)
|
||||
chinese=\u4e2d\u6587 (Chinese)
|
||||
|
||||
@@ -9,6 +9,8 @@ computer-difficulty=Dificultad del ordenador
|
||||
computer-think-time=Tiempo de pensamiento del ordenador
|
||||
computer=Ordenador
|
||||
connect=Conectar
|
||||
connect4=Connect 4
|
||||
|
||||
credits=Cr\u00e9ditos
|
||||
dark=Oscuro
|
||||
deny=Denegar
|
||||
@@ -41,6 +43,7 @@ merge-commander=Merge Commander
|
||||
moral-support=Apoyo moral
|
||||
music-volume=Volumen de m\u00fasica
|
||||
name=Nombre
|
||||
never=No, no quiero ver ning\u00fan tutorial nunca.
|
||||
no=No
|
||||
ok=OK
|
||||
online=En l\u00ednea
|
||||
@@ -62,13 +65,28 @@ style=Estilo
|
||||
the-game-ended-in-a-draw=El juego termin\u00f3 en empate
|
||||
theme=Tema
|
||||
tic-tac-toe=Tres en raya
|
||||
tictactoe1 =\u00a1Bienvenido al juego del Tres en Raya! Puedes hacer tu jugada haciendo clic en cualquiera de los 9 cuadros del tablero. Ejemplo arriba:
|
||||
tictactoe2 =Un jugador gana al conseguir 3 fichas en l\u00ednea, ya sea horizontal, vertical o diagonalmente. En el ejemplo de arriba, el jugador gana con una fila diagonal de tres fichas.
|
||||
to-a-game-of=a un juego de
|
||||
tutorial=\u00bfQuieres ver un tutorial para este juego?
|
||||
volume=Volumen
|
||||
windowed=En ventana
|
||||
yes=S\u00ed
|
||||
you-lost-against=Perdiste contra
|
||||
you-were-challenged-by=Fuiste desafiado por
|
||||
you-win=Ganaste
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=\u00a1Bienvenido al juego de Connect 4! Puedes hacer un movimiento haciendo clic en una de las columnas. El movimiento se colocar<61> en la fila m<>s baja de esa columna que no est<73> llena.
|
||||
connect4.2=\u00a1Puedes ganar consiguiendo 4 fichas de tu color horizontal, diagonal o verticalmente! Mira el ejemplo de arriba.
|
||||
reversi1=\u00a1Bienvenido al juego de Reversi! Puedes hacer un movimiento haciendo clic en uno de los puntos ligeramente transparentes.
|
||||
reversi2=Al hacer clic en un punto, se voltear<61>n todas las fichas entre donde colocas el punto y el siguiente punto que encuentre. Mira el ejemplo de arriba, donde amarillo son las fichas a voltear.
|
||||
reversi3=Tu turno puede ser saltado si no hay un movimiento legal. Esto permitir<69> que tu oponente juegue nuevamente hasta que tengas una oportunidad legal.
|
||||
reversi4=El jugador que gane al final del juego es quien tenga m<>s fichas en el tablero.
|
||||
tutorialstring=Tutorial
|
||||
startgame=\u00a1Iniciar juego!
|
||||
goback=Volver
|
||||
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Ar\u00e1bigo)
|
||||
chinese=\u4e2d\u6587 (Chino)
|
||||
|
||||
@@ -9,6 +9,8 @@ computer-difficulty=Difficult\u00e9 de l'ordinateur
|
||||
computer-think-time=Temps de r\u00e9flexion de l'ordinateur
|
||||
computer=Ordinateur
|
||||
connect=Connexion
|
||||
connect4=Connect 4
|
||||
|
||||
credits=Cr\u00e9dits
|
||||
dark=Sombre
|
||||
deny=Refuser
|
||||
@@ -41,6 +43,7 @@ merge-commander=Merge Commander
|
||||
moral-support=Soutien moral
|
||||
music-volume=Volume de la musique
|
||||
name=Nom
|
||||
never=Non, je ne veux jamais voir de tutoriels.
|
||||
no=Non
|
||||
ok=OK
|
||||
online=En ligne
|
||||
@@ -62,13 +65,27 @@ style=Style
|
||||
the-game-ended-in-a-draw=La partie s'est termin\u00e9e par un match nul
|
||||
theme=Th\u00e8me
|
||||
tic-tac-toe=Morpion
|
||||
tictactoe1 =Bienvenue dans le jeu du Tic Tac Toe ! Vous pouvez jouer en cliquant sur l\u2019une des 9 cases du plateau. Exemple ci-dessus :
|
||||
tictactoe2 =Un joueur gagne en alignant 3 pi\u00e8ces \u2013 horizontalement, verticalement ou en diagonale. Dans l\u2019exemple ci-dessus, le joueur gagne avec une diagonale de trois pi\u00e8ces.
|
||||
to-a-game-of=\u00e0 une partie de
|
||||
tutorial=Veux-tu voir un tutoriel pour ce jeu\u00a0?
|
||||
volume=Volume
|
||||
windowed=Fen\u00eatrr\u00e9
|
||||
yes=Oui
|
||||
you-lost-against=Vous avez perdu contre
|
||||
you-were-challenged-by=Vous avez \u00e9t\u00e9 d\u00e9fi\u00e9 par
|
||||
you-win=Vous avez gagn\u00e9
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=Bienvenue dans le jeu Connect 4 ! Vous pouvez effectuer un mouvement en cliquant sur l'une des colonnes. Le mouvement sera plac<61> dans la ligne la plus basse de cette colonne qui n'est pas encore remplie.
|
||||
connect4.2=Vous pouvez gagner en alignant 4 pions de votre couleur horizontalement, diagonalement ou verticalement ! Voir l'exemple ci-dessus.
|
||||
reversi1=Bienvenue dans le jeu Reversi ! Vous pouvez jouer en cliquant sur l'un des points l<>g<EFBFBD>rement transparents.
|
||||
reversi2=Cliquer sur un point retournera tous les pions entre le point plac<61> et le prochain point trouv<75>. Voir l'exemple ci-dessus, o<> le jaune indique les pions <20> retourner.
|
||||
reversi3=Votre tour peut <20>tre saut<75> s'il n'y a pas de coup l<>gal. Cela permettra <20> votre adversaire de jouer jusqu'<27> ce que vous ayez un coup l<>gal.
|
||||
reversi4=Le joueur qui a le plus de pions <20> la fin du jeu gagne.
|
||||
tutorialstring=Tutoriel
|
||||
startgame=D\u00e9marrer le jeu!
|
||||
goback=Retour
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabe)
|
||||
chinese=\u4e2d\u6587 (Chinois)
|
||||
|
||||
@@ -9,6 +9,8 @@ computer-difficulty=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930 \u0915\u094
|
||||
computer-think-time=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930 \u091a\u093f\u0902\u0924\u0928 \u0938\u092e\u092f
|
||||
computer=\u0915\u0902\u092a\u094d\u092f\u0942\u091f\u0930
|
||||
connect=\u091c\u094b\u095c\u0947\u0902
|
||||
connect4=Connect 4
|
||||
|
||||
credits=\u0915\u094d\u0930\u0947\u0921\u093f\u091f\u094d\u0938
|
||||
dark=\u0917\u0939\u0930\u093e
|
||||
deny=\u0907\u0902\u0915\u093e\u0930
|
||||
@@ -41,6 +43,7 @@ merge-commander=\u092e\u0930\u094d\u091c \u0915\u092e\u093e\u0902\u0921\u0930
|
||||
moral-support=\u0928\u0948\u0924\u093f\u0915 \u0938\u0939\u093e\u0930\u093e
|
||||
music-volume=\u0938\u0902\u0917\u0940\u0924 \u0935\u0949\u0932\u094d\u092f\u0942\u092e
|
||||
name=\u0928\u093e\u092e
|
||||
never=\u0928\u0939\u0940\u0902, \u092e\u0948\u0902 \u0915\u092c\u094d\u0939\u0940 \u092d\u0940 \u0915\u094b\u0908 \u091f\u094d\u092f\u0942\u091f\u094b\u0930\u093f\u092f\u0932 \u0928\u0939\u0940\u0902 \u0926\u0947\u0916\u0928\u093e \u091a\u093e\u0939\u0924\u093e/\u091a\u093e\u0939\u0924\u0940\u0964
|
||||
no=\u0928\u0939\u0940\u0902
|
||||
ok=\u0920\u0940\u0915 \u0939\u0948
|
||||
online=\u0911\u0928\u0932\u093e\u0907\u0928
|
||||
@@ -62,13 +65,27 @@ style=\u0936\u0948\u0932\u0940
|
||||
the-game-ended-in-a-draw=\u0916\u0947\u0932 \u091f\u0940\u0915 \u0939\u094b \u0917\u092f\u093e
|
||||
theme=\u0925\u0940\u092e
|
||||
tic-tac-toe=\u091f\u093f\u0915-\u091f\u0948\u0915-\u091f\u094b
|
||||
tictactoe1 =\u091f\u093f\u0915-\u091f\u0948\u0915-\u091f\u094b \u0915\u0947 \u0916\u0947\u0932 \u092e\u0947\u0902 \u0906\u092a\u0915\u093e \u0938\u094d\u0935\u093e\u0917\u0924 \u0939\u0948! \u0906\u092a \u092c\u094b\u0930\u094d\u0921 \u092a\u0930 \u0915\u093f\u0938\u0940 \u092d\u0940 9 \u0916\u093e\u0928\u0947 \u092a\u0930 \u0915\u094d\u0932\u093f\u0915 \u0915\u0930\u0915\u0947 \u0905\u092a\u0928\u0940 \u091a\u093e\u0932 \u091a\u0932 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u090a\u092a\u0930 \u0926\u093f\u092f\u093e \u0917\u092f\u093e \u0909\u0926\u093e\u0939\u0930\u0923 \u0926\u0947\u0916\u0947\u0902:
|
||||
tictactoe2 =\u0915\u094b\u0908 \u0916\u093f\u0932\u093e\u0921\u093c\u0940 \u0924\u092c \u091c\u0940\u0924\u0924\u093e \u0939\u0948 \u091c\u092c \u0935\u0939 \u092a\u0948 \u092a\u0902\u0915\u094d\u0924\u093f \u092e\u0947\u0902 \u0924\u0940\u0928 \u0928\u093f\u0936\u093e\u0928 \u090f\u0915 \u092a\u0902\u0915\u094d\u0924\u093f \u092e\u0947\u0902 \u0932\u0917\u093e \u0926\u0947\u0924\u093e \u0939\u0948 ? \u0915\u094d\u0937\u0948\u0924\u093f\u091c, \u090a\u0930\u094d\u0927\u094d\u0935\u093e\u0927\u0930 \u092f\u093e \u0924\u093f\u0930\u091b\u0947\u0964 \u090a\u092a\u0930 \u0909\u0926\u093e\u0939\u0930\u0923 \u092e\u0947\u0902, \u0916\u093f\u0932\u093e\u0921\u093c\u0940 \u0924\u093f\u0930\u091b\u0940 \u092a\u0902\u0915\u094d\u0924\u093f \u092e\u0947\u0902 \u0924\u093f\u0930\u091b\u0940 \u092a\u0902\u0915\u094d\u0924\u093f \u0938\u0947 \u091c\u0940\u0924\u0924\u093e \u0939\u0948\u0964
|
||||
to-a-game-of=\u0916\u0947\u0932 \u0915\u0940 \u090f\u0915 \u0916\u0947\u0932 \u0915\u0940 \u0913\u0930
|
||||
tutorial=\u0915\u094d\u092f\u093e \u0906\u092a \u0907\u0938 \u0917\u0947\u092e \u0915\u0947 \u0932\u093f\u090f \u0915\u094b\u0908 \u091f\u094d\u092f\u0942\u091f\u094b\u0930\u093f\u092f\u0932 \u0926\u0947\u0916\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?
|
||||
volume=\u0935\u0949\u0932\u094d\u092f\u0942\u092e
|
||||
windowed=\u0935\u093f\u0902\u0921\u094b \u092e\u094b\u0921
|
||||
yes=\u0939\u093e\u0901
|
||||
you-lost-against=\u0906\u092a \u0939\u093e\u0930 \u0917\u090f
|
||||
you-were-challenged-by=\u0906\u092a\u0915\u094b \u091a\u0941\u0928\u094c\u0924\u0940 \u0926\u0940 \u0917\u0908
|
||||
you-win=\u0906\u092a \u091c\u0940\u0924 \u0917\u090f
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=\u0915\u0928\u094d\u0928\u0947 \u0915\u0940 \u091c\u0948 \u0915\u0947 \u091c\u094c\u0915 \u0915\u0928\u0947\u0915\u094d\u091f 4 \u092e\u0947\u0902! \u0906\u092a \u0915\u0940 \u091a\u0948\u0928 \u092a\u0932\u0938 \u092a\u0928\u094d\u0928 \u0928\u093e\u092e\u094d\u092c\u0921\u093e\u0928\u0947 \u0915\u0940 \u092f\u094b\u0917\u0924\u0940 \u092e\u0947\u0902 \u0915\u0940 \u0928\u093f\u091a\u094d\u091a\u0942\u0928 \u0915\u0940 \u0924\u0939 \u091a\u093f\u0928 \u092a\u0924\u093f \u0928\u0939\u0940\u0902 \u0926\u0947 \u092c\u0924\u0940.
|
||||
connect4.2=\u092a\u093e\u0902\u091a \u092e\u0946\u092c\u0921 \u0915\u0940 \u092a\u0930\u092a\u0930\u092f\u094d\u0928 \u092d\u093e\u0935\u0940 \u0938\u0947 4 \u092a\u093f\u0938 \u0924\u093e\u0924\u093e \u0915\u0940 \u0930\u094f\u0936\u0942 \u0938\u0940\u0926\u094d\u0927 \u092e\u0947\u0902 \u092a\u0940\u0928\u094d\u0928\u094b\u0902 \u0915\u0940 \u092e\u093e\u0928\u094d\u0926\u0930 \u0939\u0940.
|
||||
reversi1=\u0915\u0928\u094d\u0928\u0947 \u0915\u0940 \u091c\u0948 \u0910 \u0930\u0947\u0935\u0930\u094d\u0938\u0940 \u092e\u0947\u0902 \u0926\u0948\u091a \u092c\u0922\u093e\u0928 \u0915\u0940 \u092a\u0924\u094d\u0928 \u092a\u0930 \u092a\u094d\u0932\u0947 \u0916\u0942\u0928\u0947 \u0915\u0940 \u092a\u094d\u0930\u092f\u0948\u0915\u0944 \u0915\u0930\u0948\u0902.
|
||||
reversi2=\u0915\u093f\u0938 \u092a\u0930 \u092a\u093f\u0938 \u092a\u0948\u0918 \u0915\u0940 \u091a\u0928 \u092e\u0947\u0902 \u0938\u092e\u093e\u0930\u094e \u092a\u093f\u0938 \u092a\u0940\u0918 \u092e\u0947\u0902 \u092a\u0930\u093f\u0923\u0924 \u0915\u093e \u092a\u0930\u093f\u0928\u0942\u0924\u0940 \u0915\u0940 \u092a\u094d\u0930\u0924\u093f \u092a\u0941\u0928\u0940 \u092a\u0930\u093f\u0928\u094d\u0924 \u0915\u0940 \u092a\u0940\u0938 \u092a\u0930\u094d\u092f\u0928\u0947 \u091c\u093e\u0902\u0918\u0942.
|
||||
reversi3=\u092f\u0939 \u092a\u0930\u094d\u092f \u0938\u0947 \u0938\u0947\u091a \u0915\u0940 \u091c\u093e\u0902\u091c \u0928\u0939\u0940\u0902 \u0939\u0948 \u0914\u0938\u0924\u0947 \u0915\u0948 \u092a\u0948\u0928 \u092a\u0930\u094d\u092f \u0939\u0948 \u0914\u092a\u0915\u0940 \u0915\u0940 \u092a\u0932\u0947 \u092d\u0942\u0924 \u0915\u0940 \u0906\u0927\u093e \u092a\u0948\u0928 \u091c\u093e\u0902\u091c \u0915\u0930 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902.
|
||||
reversi4=\u0916\u0941\u092f \u0915\u093f \u0915\u0940 \u0928\u093f\u092e\u0940 \u092e\u0947\u0902 \u091a\u093e\u0932 \u0938\u092c\u0938\u0947 \u091a\u0942\u0928\u094d\u0928\u0947 \u0939\u0948, \u0935\u0949 \u0915\u0947 \u092e\u093e\u0924\u094d\u0930 \u091c\u0940\u0924\u0947 \u0939\u0948.
|
||||
tutorialstring=\u0924\u0942\u091f\u0949\u0930\u093f\u092f\u0932
|
||||
startgame=\u0916\u0947\u0932 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902!
|
||||
goback=\u0935\u093e\u092a\u0938 \u091c\u093e\u090f\u0901
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0905\u0930\u092c\u0940)
|
||||
chinese=\u4e2d\u6587 (\u091a\u0940\u0928\u0940)
|
||||
|
||||
@@ -9,6 +9,7 @@ computer-difficulty=Difficolt\u00e0 del computer
|
||||
computer-think-time=Tempo di riflessione del computer
|
||||
computer=Computer
|
||||
connect=Connetti
|
||||
connect4=Connect 4
|
||||
credits=Crediti
|
||||
dark=Scuro
|
||||
deny=Nega
|
||||
@@ -41,6 +42,7 @@ merge-commander=Merge Commander
|
||||
moral-support=Supporto morale
|
||||
music-volume=Volume della musica
|
||||
name=Nome
|
||||
never=No, non voglio mai vedere alcun tutorial.
|
||||
no=No
|
||||
ok=OK
|
||||
online=Online
|
||||
@@ -62,13 +64,27 @@ style=Stile
|
||||
the-game-ended-in-a-draw=La partita \u00e8 terminata in parit\u00e0
|
||||
theme=Tema
|
||||
tic-tac-toe=Tris
|
||||
tictactoe1 =Benvenuto nel gioco del Tris! Puoi fare la tua mossa cliccando su uno dei 9 quadrati della griglia. Esempio sopra:
|
||||
tictactoe2 =Un giocatore vince mettendo 3 simboli in fila ? orizzontalmente, verticalmente o diagonalmente. Nell?esempio sopra, il giocatore vince con una fila diagonale di tre simboli.
|
||||
to-a-game-of=a una partita di
|
||||
tutorial=Vuoi vedere un tutorial per questo gioco?
|
||||
volume=Volume
|
||||
windowed=Finestra
|
||||
yes=S\u00ec
|
||||
you-lost-against=Hai perso contro
|
||||
you-were-challenged-by=Sei stato sfidato da
|
||||
you-win=Hai vinto
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=Benvenuto nel gioco Connect 4! Puoi fare una mossa cliccando su una delle colonne. La mossa sar<61> posizionata nella riga pi<70> bassa di quella colonna che non <20> ancora piena.
|
||||
connect4.2=Puoi vincere ottenendo 4 pedine del tuo colore in orizzontale, diagonale o verticale! Guarda l'esempio sopra.
|
||||
reversi1=Benvenuto nel gioco Reversi! Puoi fare una mossa cliccando su uno dei punti leggermente trasparenti.
|
||||
reversi2=Cliccando su un punto, tutti i pezzi tra dove metti il punto e il prossimo punto trovato verranno girati. Guarda l'esempio sopra, dove il giallo indica i pezzi da girare.
|
||||
reversi3=Il tuo turno pu<70> essere saltato se non ci sono mosse legali. Questo permetter<65> al tuo avversario di giocare fino a quando non avrai un'opportunit<69> legale.
|
||||
reversi4=Il giocatore che alla fine del gioco ha pi<70> pezzi sulla scacchiera vince.
|
||||
tutorialstring=Tutorial
|
||||
startgame=Avvia il gioco!
|
||||
goback=Indietro
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabo)
|
||||
chinese=\u4e2d\u6587 (Cinese)
|
||||
|
||||
@@ -9,6 +9,7 @@ computer-difficulty=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u306e\u96e3\u6613\u5ea6
|
||||
computer-think-time=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u306e\u601d\u8003\u6642\u9593
|
||||
computer=\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf
|
||||
connect=\u63a5\u7d9a
|
||||
connect4=Connect 4
|
||||
credits=\u30af\u30ec\u30b8\u30c3\u30c8
|
||||
dark=\u30c0\u30fc\u30af
|
||||
deny=\u62d2\u5426
|
||||
@@ -41,6 +42,7 @@ merge-commander=\u30de\u30fc\u30b8\u30b3\u30de\u30f3\u30c0\u30fc
|
||||
moral-support=\u30e1\u30f3\u30bf\u30eb\u30b5\u30dd\u30fc\u30c8
|
||||
music-volume=\u97f3\u697d\u97f3\u91cf
|
||||
name=\u540d\u524d
|
||||
never=\u3044\u3044\u3048\u3001\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u306f\u4e8c\u5ea6\u3068\u898b\u305f\u304f\u3042\u308a\u307e\u305b\u3093\u3002
|
||||
no=\u3044\u3044\u3048
|
||||
ok=OK
|
||||
online=\u30aa\u30f3\u30e9\u30a4\u30f3
|
||||
@@ -62,13 +64,27 @@ style=\u30b9\u30bf\u30a4\u30eb
|
||||
the-game-ended-in-a-draw=\u30b2\u30fc\u30e0\u306f\u5f15\u304d\u5206\u3051\u306b\u7d42\u308f\u308a\u307e\u3057\u305f
|
||||
theme=\u30c6\u30fc\u30de
|
||||
tic-tac-toe=\u4e09\u76ee\u4e26\u3079
|
||||
tictactoe1 =\u4e09\u76ee\u4e26\u3079\u306e\u30b2\u30fc\u30e0\u3078\u3088\u3046\u3053\u305d\uff01\u30dc\u30fc\u30c9\u4e0a\u306e9\u3064\u306e\u30de\u30b9\u306e\u3044\u305a\u308c\u304b\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u624b\u3092\u6253\u3061\u307e\u3057\u3087\u3046\u3002\u4e0a\u306e\u4f8b\u3092\u53c2\u7167\uff1a
|
||||
tictactoe2 =\u30d7\u30ec\u30a4\u30e4\u30fc\u306f\u3001\u6a2a\u30fb\u7e26\u30fb\u65b9\u5411\u306e\u3044\u305a\u308c\u304b\u3067\u30de\u30fc\u30af\u30923\u3064\u4e26\u3079\u308b\u3068\u52dd\u3061\u3067\u3059\u3002\u4e0a\u306e\u4f8b\u3067\u306f\u3001\u30d7\u30ec\u30a4\u30e4\u30fc\u304c\u659c\u3081\u306b3\u3064\u4e26\u3079\u3066\u52dd\u3063\u3066\u3044\u307e\u3059\u3002
|
||||
to-a-game-of=\u30b2\u30fc\u30e0\u306b
|
||||
tutorial=\u3053\u306e\u30b2\u30fc\u30e0\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3092\u898b\u305f\u3044\u3067\u3059\u304b\uff1f
|
||||
volume=\u97f3\u91cf
|
||||
windowed=\u30a6\u30a3\u30f3\u30c9\u30a6\u8868\u793a
|
||||
yes=\u306f\u3044
|
||||
you-lost-against=\u3042\u306a\u305f\u306f ... \u306b\u6557\u308c\u307e\u3057\u305f
|
||||
you-were-challenged-by=\u3042\u306a\u305f\u306f ... \u304b\u3089\u6311\u6226\u3055\u308c\u307e\u3057\u305f
|
||||
you-win=\u52dd\u5229\u3067\u3059
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=\u30b3\u30cd\u30af\u30c84\u306e\u30b2\u30fc\u30e0\u3078\u3088\u3046\u3053\u305d! \u30ab\u30e9\u30e0\u306e\u4e0a\u306e\u30ab\u30e9\u30e0\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u52d5\u304b\u3057\u3092\u884c\u3048\u307e\u3059\u3002\u52d5\u304b\u3057\u306f\u3001\u3060\u307e\u308a\u306f\u307e\u3067\u5869\u3067\u306a\u3044\u884c\u306b\u8a2d\u7f6e\u3055\u308c\u307e\u3059\u3002
|
||||
connect4.2=\u6a2a\u7dda\u3001\u65b9\u5411\u306e\u307f\u3082\u306a\u3057\u3067\u30014\u3064\u306e\u8ca0\u3051\u3092\u7d50\u5408\u3055\u305b\u308b\u3068\u52dd\u3061\u307e\u3059! \u4e0a\u306e\u4f8b\u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
|
||||
reversi1=\u30ea\u30d0\u30fc\u30b7\u30fb\u30b2\u30fc\u30e0\u3078\u3088\u3046\u3053\u305d! \u30ab\u30e9\u30e0\u306e\u30b9\u30dd\u30c3\u30c8\u30c9\u30c3\u30c8\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u52d5\u304b\u3057\u306e\u30d7\u30ec\u30a4\u304c\u3067\u304d\u307e\u3059\u3002
|
||||
reversi2=\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u3001\u3064\u306a\u304c\u308a\u3092\u542b\u3081\u305f\u8ca0\u3051\u304c\u307e\u3067\u306e\u8ca0\u3051\u304c\u5909\u308f\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002
|
||||
reversi3=\u6b21\u306e\u52d5\u304b\u3057\u304c\u306a\u3044\u5834\u5408\u3001\u8a8d\u5b9a\u3055\u308c\u305f\u52d5\u304b\u3057\u306e\u6642\u9593\u306f\u62d2\u7d76\u3055\u308c\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002
|
||||
reversi4=\u672c\u6b21\u306b\u30dc\u30fc\u30c9\u4e0a\u3067\u6700\u591a\u306e\u8ca0\u3051\u3092\u6301\u3064\u30d7\u30ec\u30a4\u30e4\u30fc\u304c\u52dd\u3061\u307e\u3059\u3002
|
||||
tutorialstring=\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb
|
||||
startgame=\u30b2\u30fc\u30e0\u3092\u958b\u59cb\uff01
|
||||
goback=\u623b\u308b
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u30a2\u30e9\u30d3\u30a2\u8a9e)
|
||||
chinese=\u4e2d\u6587 (\u4e2d\u6587)
|
||||
|
||||
@@ -9,6 +9,7 @@ computer-difficulty=\uCEF4\uD4E8\uD130 \uC5B4\uB9AC\uAE30
|
||||
computer-think-time=\uCEF4\uD4E8\uD130 \uC0DD\uAC01 \uC2DC\uAC04
|
||||
computer=\uCEF4\uD4E8\uD130
|
||||
connect=\uC5F0\uACB0
|
||||
connect4=Connect 4
|
||||
credits=\uD06C\uB808\uB527
|
||||
dark=\uC5B4\uB460
|
||||
deny=\uAC70\uBD80
|
||||
@@ -42,6 +43,7 @@ moral-support=\uC815\uC2E0\uC801 \uC9C0\uC6D0
|
||||
music-volume=\uC74C\uC545 \uBCFC\uB968
|
||||
name=\uC774\uB984
|
||||
no=\uC544\uB2C8\uC624
|
||||
never=\uc544\ub2c8\uc694, \uc800\ub294 \ud29c\ud1a0\ub9ac\uc5bc\uc744 \ub2e4\uc2dc\ub294 \ubcf4\uace0 \uc2f6\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.
|
||||
ok=\uD655\uC778
|
||||
online=\uC628\uB77C\uC778
|
||||
opengl=OpenGL
|
||||
@@ -62,13 +64,27 @@ style=\uC2A4\uD0C0\uC77C
|
||||
the-game-ended-in-a-draw=\uAC8C\uC784\uC774 \uBB34\uC2B9\uBD80\uB85C \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4
|
||||
theme=\uC8FC\uC81C
|
||||
tic-tac-toe=\uD2F0\uD06C\uD0D1\uD1A0
|
||||
tictactoe1 =\ud2f1\ud0dd\ud1a0 \uac8c\uc784\uc5d0 \uc624\uc2e0 \uac83\uc744 \ud658\uc601\ud569\ub2c8\ub2e4! \ubcf4\ub4dc\uc758 9\uac1c \uce78 \uc911 \ud558\ub098\ub97c \ud074\ub9ad\ud558\uc5ec \uc6c0\uc9c1\uc77c \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc704\uc758 \uc608\uc2dc\ub97c \ucc38\uace0\ud558\uc138\uc694:
|
||||
tictactoe2 =\ud50c\ub808\uc774\uc5b4\ub294 \uac00\ub85c, \uc138\ub85c \ub610\ub294 \ub300\uac01\uc120\uc73c\ub85c \ub9d0 3\uac1c\ub97c \uc77c\ub82c\ub85c \ub193\uc73c\uba74 \uc2b9\ub9ac\ud569\ub2c8\ub2e4. \uc704\uc758 \uc608\uc5d0\uc11c \ud50c\ub808\uc774\uc5b4\ub294 \ub300\uac01\uc120\uc73c\ub85c \uc138 \uac1c\ub97c \uc5f0\uacb0\ud558\uc5ec \uc774\uacbc\uc2b5\ub2c8\ub2e4.
|
||||
to-a-game-of=... \uAC8C\uC784\uC5D0
|
||||
tutorial=\uc774 \uac8c\uc784\uc758 \ud29c\ud1a0\ub9ac\uc5bc\uc744 \ubcf4\uace0 \uc2f6\ub098\uc694?
|
||||
volume=\uBCFC\uB968
|
||||
windowed=\uCC3D \uBAA9\uB85D
|
||||
yes=\uB124
|
||||
you-lost-against=... \uC5D0\uAC8C \uC9C0\uC600\uC2B5\uB2C8\uB2E4
|
||||
you-were-challenged-by=... \uB85C\uBD80\uD130 \uCC38\uC5EC \uC694\uCCAD\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4
|
||||
you-win=\uC774\uACBC\uC2B5\uB2C8\uB2E4
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=Connect 4 \uacbd\uc6b0\uc5d0 \uc81c\uc2dc\ud569\ub2c8\ub2e4! \ud648\ub825\uc744 \ub2e4\ub978 \uc0c1\uc704\ub85c \ud074\ub9ad\ud558\uc2dc\uba70 \ub2e4\uc74c \uc815\uc758 \ud648\ub825\uc744 \ub610\ub294 \uc704\ub85c \uc0ac\uc6a9\ud558\uc2dc\uba70 \ud648\ub825\uc744 \uc124\uc815\ud569\ub2c8\ub2e4.
|
||||
connect4.2=\uc0ac\uc6a9\uc790\uc758 \ud648\uc744 \uc54c\ub824 \ud574\uc8fc\uba70 \ud574\ub2f9 \ud648\uc758 4\uae38\uc744 \ud574\uc8fc\uba70 \ud655\uc9c0, \ub354\ub7ec \ubc29\uacfc \ub610\ub294 \uc0ac\uc6a9\uc790 \ud648\uc758 \uc5f4\ub9b0 \ucd5c\ub300 \ubc29\ud574\uc5d0 \uc5c6\uc74c\uc774\ub2e4!
|
||||
reversi1=Reversi \uacbd\uc6b0\uc5d0 \uc81c\uc2dc\ud569\ub2c8\ub2e4! \ub2e4\ub978 \ud615\uc2dd\uc758 \ud648\uc744 \ud074\ub9ad\ud558\uc2dc\uba70 \ub2e4\uc74c \uc815\uc758 \ud648\uc744 \uc124\uc815\ud569\ub2c8\ub2e4.
|
||||
reversi2=\ud074\ub9ad \ud558\uba70, \ub2e4\ub978 \ud648 \uc704\ub85c \ucd5c\uc2e0 \ubc1b\ub294 \ud648\uc5d0 \ub300\ud574 \ub2e4\uc774\ubc84\ub77c\uc758 \ud648\uc744 \ubcc0\uacbd\ud569\ub2c8\ub2e4.
|
||||
reversi3=\uc0ac\uc6a9\uc790\uc758 \ud648\uc744 \ud074 \uc218 \uc5c6\uc2b5\uc2b5\ub2c8\ub2e4. \uc0ac\uc6a9\uc790 \ub2f5\uc5d0 \ub300\ud574 \uc811\ub2c8\ub2e4.
|
||||
reversi4=\uacbd\uc6b0 \uc5d0\uc11c \ucd5c\ub300 \ud648\uc744 \uac00\uc838\ub294 \uc0ac\uc6a9\uc790\uc774 \uc52c\uc544\uc624\uba70 \uc0ac\uc6a9\uc790\uc758 \ud648\uc744 \uc54c\ub824\ud569\ub2c8\ub2e4.
|
||||
tutorialstring=\ud14c\ud2b8\ub9ad
|
||||
startgame=\uac8c\uc784 \uc2dc\uc791!
|
||||
goback=\ub4a4\ub85c \uac00\uae30
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0639\u0631\u0628\u064a\u0629)
|
||||
chinese=\u4e2d\u6587 (\u4e2d\u6587)
|
||||
|
||||
@@ -41,6 +41,7 @@ merge-commander=Merge Commander
|
||||
moral-support=Morele steun
|
||||
music-volume=Muziekvolume
|
||||
name=Naam
|
||||
never=Nee, ik wil nooit tutorials zien.
|
||||
no=Nee
|
||||
ok=Ok<EFBFBD>
|
||||
online=Online
|
||||
@@ -62,14 +63,28 @@ style=Stijl
|
||||
the-game-ended-in-a-draw=Het spel eindigde in een gelijkspel
|
||||
theme=Thema
|
||||
tic-tac-toe=Boter Kaas en Eieren
|
||||
tictactoe1 =Welkom bij het spel Boter, Kaas en Eieren! Je kunt je zet doen door op een van de 9 vakjes op het bord te klikken. Voorbeeld hierboven:
|
||||
tictactoe2 =Een speler wint door 3 stukken op een rij te krijgen ? horizontaal, verticaal of diagonaal. In het voorbeeld hierboven wint de speler met een diagonale rij van drie stukken.
|
||||
connect4=Vier op een rij
|
||||
to-a-game-of=voor een spelletje
|
||||
tutorial=Wil je een tutorial voor dit spel zien?
|
||||
volume=Volume
|
||||
windowed=Venstermodus
|
||||
yes=Ja
|
||||
you-lost-against=Je hebt verloren van
|
||||
you-were-challenged-by=Je bent uitgedaagd door
|
||||
you-win=Je wint
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=Welkom bij het spel Connect 4! Je kunt een zet doen door op een van de kolommen te klikken. De zet wordt geplaatst in de laagste nog lege rij van die kolom.
|
||||
connect4.2=Je kunt winnen door 4 van je stukken horizontaal, diagonaal of verticaal op een rij te krijgen! Zie het voorbeeld hierboven.
|
||||
reversi1=Welkom bij het spel Reversi! Je kunt een zet doen door op een van de licht transparante stippen te klikken.
|
||||
reversi2=Door op een stip te klikken draai je alle stukken om tussen de plaats waar je de stip zet en de volgende stip die wordt gevonden. Zie het voorbeeld hierboven, waar geel de stukken zijn die omgedraaid worden.
|
||||
reversi3=Je beurt kan worden overgeslagen als er geen legale zet is. Hierdoor kan je tegenstander doorgaan tot jij een legale zet kunt doen.
|
||||
reversi4=De speler die aan het einde van het spel de meeste stukken op het bord heeft, wint.
|
||||
tutorialstring=Tutorial
|
||||
startgame=Spel starten!
|
||||
goback=Ga terug
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabisch)
|
||||
chinese=\u4e2d\u6587 (Chinees)
|
||||
|
||||
@@ -5,6 +5,7 @@ are-you-sure=\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b?
|
||||
back=\u041d\u0430\u0437\u0430\u0434
|
||||
cancel=\u041e\u0442\u043c\u0435\u043d\u0430
|
||||
challenge=\u0412\u044b\u0437\u043e\u0432
|
||||
connect4=Connect 4
|
||||
computer-difficulty=\u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430
|
||||
computer-think-time=\u0412\u0440\u0435\u043c\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430
|
||||
computer=\u041a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440
|
||||
@@ -41,6 +42,7 @@ merge-commander=Merge Commander
|
||||
moral-support=\u041c\u043e\u0440\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430
|
||||
music-volume=\u0413\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u043c\u0443\u0437\u044b\u043a\u0438
|
||||
name=\u0418\u043c\u044f
|
||||
never=\u041d\u0435\u0442, \u044f \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0445\u043e\u0447\u0443 \u0432\u0438\u0434\u0435\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u0443\u0447\u0435\u0431\u043d\u0438\u043a\u0438.
|
||||
no=\u041d\u0435\u0442
|
||||
ok=OK
|
||||
online=\u0412 \u0441\u0435\u0442\u0438
|
||||
@@ -62,13 +64,27 @@ style=\u0421\u0442\u0438\u043b\u044c
|
||||
the-game-ended-in-a-draw=\u0418\u0433\u0440\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u043d\u0438\u0447\u044c\u0435\u0439
|
||||
theme=\u0422\u0435\u043c\u0430
|
||||
tic-tac-toe=\u041a\u0440\u0435\u0441\u0442\u0438\u043a\u043e
|
||||
tictactoe1 =\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0438\u0433\u0440\u0443 \u041a\u0440\u0435\u0441\u0442\u0438\u043a\u0438-\u043d\u043e\u043b\u0438\u043a\u0438! \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0434, \u043d\u0430\u0436\u0430\u0432 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 9 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u043e\u0432 \u043d\u0430 \u043f\u043e\u043b\u0435. \u041f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0448\u0435:
|
||||
tictactoe2 =\u0418\u0433\u0440\u043e\u043a \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0441\u0442\u0430\u0432\u0438\u0442 3 \u0441\u0438\u043c\u0432\u043e\u043b\u0430 \u043f\u043e\u0434\u0440\u044f\u0434 ? \u043f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438, \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438 \u0438\u043b\u0438 \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u0438. \u0412 \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u043c \u0432\u044b\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0433\u0440\u043e\u043a \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442 \u043f\u043e \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u0438
|
||||
to-a-game-of=\u043a \u0438\u0433\u0440\u0435 \u0432
|
||||
tutorial=\u0425\u043e\u0447\u0435\u0448\u044c \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0443\u0447\u0435\u0431\u043d\u0438\u043a \u043f\u043e \u044d\u0442\u043e\u0439 \u0438\u0433\u0440\u0435?
|
||||
volume=\u0413\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c
|
||||
windowed=\u041e\u043a\u043d\u043e
|
||||
yes=\u0414\u0430
|
||||
you-lost-against=\u0412\u044b \u043f\u0440\u043e\u0438\u0433\u0440\u0430\u043b\u0438 \u043a\u043e\u043c\u0443
|
||||
you-were-challenged-by=\u0412\u0430\u0441 \u0432\u044b\u0437\u0432\u0430\u043b \u043d\u0430 \u0441\u043e\u0440\u0435\u0432\u043d\u0438\u043a
|
||||
you-win=\u0412\u044b \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442\u0435
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0438\u0433\u0440\u0443 Connect 4! \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0434, \u043a\u043b\u0438\u043a\u043d\u0443\u044f \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432. \u0425\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d \u0432 \u043d\u0438\u0436\u0430\u0439 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0432 \u044d\u0442\u043e\u0439 \u043a\u043e\u043b\u043e\u043d\u043a\u0435.
|
||||
connect4.2=\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0432 4 \u0444\u0438\u0448\u043a\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u0446\u0432\u0435\u0442\u0430 \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e, \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u044c\u043d\u043e \u0438\u043b\u0438 \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e!
|
||||
reversi1=\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0438\u0433\u0440\u0443 Reversi! \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0434, \u043a\u043b\u0438\u043a\u043d\u0443\u044f \u043f043 \u043f043 \u043d043 \u043e043 \u043d043 \u043e043 \u043a043 \u0430043 \u043a043 \u0430043.
|
||||
reversi2=\u041d043 \u043d043 \u0430043 \u043a043 \u0430043 \u043a043 \u043e043 \u043c043 \u0435043 \u0436043 \u0435043 \u0434043 \u0435043 \u043d043 \u0430043.
|
||||
reversi3=\u0412043 \u0430043 \u0436043 \u0434043 \u0430043 \u043d043 \u0438043 \u043d043 \u0435043 \u0435043 \u0432043 \u0430043.
|
||||
reversi4=\u0418043 \u0433043 \u0440043 \u043e043 \u043a043 \u043e043 \u0442043 \u043e043 \u0442043 \u043e043 \u0435043 \u0435043 \u0430043 \u0435043 \u043d043 \u0438043 \u0435043 \u0435043 \u043c043 \u0430043.
|
||||
tutorialstring=\u0423\u0447\u0435\u0431\u043d\u0438\u043a
|
||||
startgame=\u041d\u0430\u0447\u0430\u0442\u044c \u0438\u0433\u0440\u0443!
|
||||
goback=\u041d\u0430\u0437\u0430\u0434
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0410\u0440\u0430\u0431\u0441\u043a\u0438\u0439)
|
||||
chinese=\u4e2d\u6587 (\u041a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0439)
|
||||
|
||||
@@ -9,6 +9,7 @@ computer-difficulty=\u8ba1\u7b97\u673a\u96be\u5ea6
|
||||
computer-think-time=\u8ba1\u7b97\u673a\u601d\u8003\u65f6\u95f4
|
||||
computer=\u8ba1\u7b97\u673a
|
||||
connect=\u8fde\u63a5
|
||||
connect4=Connect 4
|
||||
credits=\u81f4\u8c22
|
||||
dark=\u6697\u8272
|
||||
deny=\u62d2\u7edd
|
||||
@@ -41,6 +42,7 @@ merge-commander=\u5408\u5e76\u6307\u6325
|
||||
moral-support=\u7cbe\u795e\u652f\u6301
|
||||
music-volume=\u97f3\u4e50\u97f3\u91cf
|
||||
name=\u540d\u5b57
|
||||
never=\u4e0d\uff0c\u6211\u5b8c\u5168\u4e0d\u60f3\u518d\u770b\u5230\u4efb\u4f55\u6559\u7a0b\u3002
|
||||
no=\u4e0d
|
||||
ok=\u786e\u5b9a
|
||||
online=\u5728\u7ebf
|
||||
@@ -62,13 +64,27 @@ style=\u98ce\u683c
|
||||
the-game-ended-in-a-draw=\u6e38\u620f\u4ee5\u548c\u5c40\u7ed3\u675f
|
||||
theme=\u4e3b\u9898
|
||||
tic-tac-toe=\u4e09\u5b9a\u7ebf
|
||||
tictactoe1 =\u6b22\u8fce\u6765\u5230\u4e95\u5b57\u68cb\u6e38\u620f\uff01\u4f60\u53ef\u4ee5\u901a\u8fc7\u70b9\u51fb\u68cb\u76d8\u4e0a\u4efb\u610f\u4e00\u4e2a\u4e5d\u4e2a\u65b9\u683c\u6765\u843d\u5b50\u3002\u4e0a\u65b9\u793a\u4f8b\uff1a
|
||||
tictactoe2 =\u73a9\u5bb6\u5728\u6a2a\u3001\u7ad6\u6216\u659c\u7ebf\u4e0a\u8fde\u7eed\u653e\u7f6e\u4e09\u4e2a\u68cb\u5b50\u5373\u53ef\u83b7\u80dc\u3002\u5728\u4e0a\u65b9\u7684\u793a\u4f8b\u4e2d\uff0c\u73a9\u5bb6\u901a\u8fc7\u659c\u7ebf\u4e0a\u7684\u4e09\u4e2a\u68cb\u5b50\u83b7\u80dc\u4e86\u6bd4\u8d5b\u3002
|
||||
to-a-game-of=\u6311\u6218\u4e00\u573a
|
||||
tutorial=\u4f60\u60f3\u770b\u8fd9\u4e2a\u6e38\u620f\u7684\u6559\u7a0b\u5417\uff1f
|
||||
volume=\u97f3\u91cf
|
||||
windowed=\u7a97\u53e3\u6a21\u5f0f
|
||||
yes=\u662f
|
||||
you-lost-against=\u60a8\u8f93\u7ed9\u4e86
|
||||
you-were-challenged-by=\u60a8\u88ab\u6311\u6218\u81ea
|
||||
you-win=\u60a8\u83b7\u80dc\u4e86
|
||||
>=>
|
||||
<=<
|
||||
connect4.1=\u6b22\u8fce\u6765\u5230 Connect 4 \u6e38\u620f! \u4f60\u53ef\u4ee5\u70b9\u51fb\u4e00\u5217\u6761\u76ee\u64cd\u4f5c. \u64cd\u4f5c\u5c06\u88c5\u7f6e\u5728\u672a\u88c5\u5165\u7684\u6700\u4f4e\u884c.
|
||||
connect4.2=\u5982\u679c\u5f97\u52304\u4e2a\u5bf9\u5e94\u7684\u4ee3\u7406\u7ec4\u6210\u6c34\u5e73\u3001\u5347\u5e26\u6216\u5782\u76f4\u5373\u53ef\u80dc. \u770b\u4e0a\u65b9\u793a\u4f8b.
|
||||
reversi1=\u6b22\u8fce\u6765\u5230 Reversi \u6e38\u620f! \u4f60\u53ef\u4ee5\u70b9\u51fb\u4e00\u4e2a\u9ed8\u8272\u5149\u900f\u7a7a\u70b9\u64cd\u4f5c.
|
||||
reversi2=\u70b9\u51fb\u4e00\u4e2a\u70b9\u65f6\u5c06\u5c06\u6240\u6709\u4e2d\u95f4\u7684\u4ee3\u7406\u7ffb\u8f6c\u3002 \u770b\u4e0a\u65b9\u793a\u4f8b\uff0c\u9ec4\u8272\u662f\u5bf9\u4ee3\u7406\u9700\u64ad\u7684\u4ee3\u7406.
|
||||
reversi3=\u5982\u679c\u6ca1\u6709\u5408\u6cd5\u64cd\u4f5c\u4f60\u7684\u8fdb\u6b65\u53ef\u80fd\u88ab\u5ffd\u7565. \u8fd9\u4f1a\u8ba9\u5bf9\u624b\u518d\u6b21\u64cd\u4f5c\u5230\u4f60\u6709\u5408\u6cd5\u64cd\u4f5c\u65f6.
|
||||
reversi4=\u672c\u6e38\u620f\u7ed3\u675f\u65f6\u8d62\u5f97\u6ee1\u8fc7\u76d8\u9762\u7684\u4ee3\u7406\u6570\u6700\u591a\u7684\u4eba\u5c31\u80dc.
|
||||
tutorialstring=\u6559\u7a0b
|
||||
startgame=\u5f00\u59cb\u6e38\u620f\uff01
|
||||
goback=\u8fd4\u56de
|
||||
|
||||
arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u963f\u62c9\u4f2f\u8bed)
|
||||
chinese=\u4e2d\u6587
|
||||
|
||||
@@ -49,3 +49,8 @@
|
||||
-fx-font-size: 22px;
|
||||
-fx-font-weight: normal;
|
||||
}
|
||||
|
||||
.image {
|
||||
-fx-max-width: 200px;
|
||||
-fx-max-height: 200px;
|
||||
}
|
||||
@@ -49,3 +49,8 @@
|
||||
-fx-font-size: 16px;
|
||||
-fx-font-weight: normal;
|
||||
}
|
||||
|
||||
.image {
|
||||
-fx-max-width: 200px;
|
||||
-fx-max-height: 200px;
|
||||
}
|
||||
@@ -49,3 +49,8 @@
|
||||
-fx-font-size: 12px;
|
||||
-fx-font-weight: normal;
|
||||
}
|
||||
|
||||
.image {
|
||||
-fx-max-width: 200px;
|
||||
-fx-max-height: 200px;
|
||||
}
|
||||
|
||||
@@ -25,17 +25,16 @@ public class AudioEventListener<T extends AudioResource, K extends AudioResource
|
||||
|
||||
public AudioEventListener<?, ?> initListeners(String buttonSoundToPlay) {
|
||||
new EventFlow()
|
||||
.listen(this::handleStopMusicManager)
|
||||
.listen(this::handlePlaySound)
|
||||
.listen(this::handleSkipSong)
|
||||
.listen(this::handlePauseSong)
|
||||
.listen(this::handlePreviousSong)
|
||||
.listen(this::handleStopSound)
|
||||
.listen(this::handleMusicStart)
|
||||
.listen(this::handleVolumeChange)
|
||||
.listen(this::handleGetVolume)
|
||||
.listen(AudioEvents.ClickButton.class, _ ->
|
||||
soundEffectManager.play(buttonSoundToPlay, false));
|
||||
.listen(AudioEvents.StopAudioManager.class, this::handleStopMusicManager, false)
|
||||
.listen(AudioEvents.PlayEffect.class, this::handlePlaySound, false)
|
||||
.listen(AudioEvents.SkipMusic.class, this::handleSkipSong, false)
|
||||
.listen(AudioEvents.PauseMusic.class, this::handlePauseSong, false)
|
||||
.listen(AudioEvents.PreviousMusic.class, this::handlePreviousSong, false)
|
||||
.listen(AudioEvents.StopEffect.class, this::handleStopSound, false)
|
||||
.listen(AudioEvents.StartBackgroundMusic.class, this::handleMusicStart, false)
|
||||
.listen(AudioEvents.ChangeVolume.class, this::handleVolumeChange, false)
|
||||
.listen(AudioEvents.GetVolume.class, this::handleGetVolume,false)
|
||||
.listen(AudioEvents.ClickButton.class, _ -> soundEffectManager.play(buttonSoundToPlay, false), false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class EventFlow {
|
||||
private EventType event = null;
|
||||
|
||||
/** The listener returned by GlobalEventBus subscription. Used for unsubscription. */
|
||||
private final List<ListenerHandler> listeners = new ArrayList<>();
|
||||
private final List<ListenerHandler<?>> listeners = new ArrayList<>();
|
||||
|
||||
/** Holds the results returned from the subscribed event, if any. */
|
||||
private Map<String, ?> result = null;
|
||||
@@ -46,16 +46,15 @@ public class EventFlow {
|
||||
/** Empty constructor (event must be added via {@link #addPostEvent(Class, Object...)}). */
|
||||
public EventFlow() {}
|
||||
|
||||
public EventFlow addPostEvent(EventType event) {
|
||||
this.event = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventFlow addPostEvent(Supplier<? extends EventType> eventSupplier) {
|
||||
this.event = eventSupplier.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add an event that will be triggered when {@link #postEvent()} or {@link #asyncPostEvent()} is called.
|
||||
*
|
||||
* @param eventClass The event that will be posted.
|
||||
* @param args The event arguments, see the added event record for more information.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <T extends EventType> EventFlow addPostEvent(Class<T> eventClass, Object... args) {
|
||||
try {
|
||||
boolean isUuidEvent = UniqueEvent.class.isAssignableFrom(eventClass);
|
||||
@@ -98,54 +97,138 @@ public class EventFlow {
|
||||
}
|
||||
}
|
||||
|
||||
/** Subscribe by ID: only fires if UUID matches this publisher's eventId. */
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(
|
||||
Class<TT> eventClass, Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
eventClass,
|
||||
event -> {
|
||||
if (event.getIdentifier() != this.eventSnowflake) return;
|
||||
|
||||
action.accept(event);
|
||||
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
|
||||
this.result = event.result();
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
/**
|
||||
*
|
||||
* Add an event that will be triggered when {@link #postEvent()} or {@link #asyncPostEvent()} is called.
|
||||
*
|
||||
* @param event The event to be posted.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public EventFlow addPostEvent(EventType event) {
|
||||
this.event = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Subscribe by ID: only fires if UUID matches this publisher's eventId. */
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(Class<TT> eventClass, Consumer<TT> action) {
|
||||
return this.onResponse(eventClass, action, true);
|
||||
/**
|
||||
*
|
||||
* Add an event that will be triggered when {@link #postEvent()} or {@link #asyncPostEvent()} is called.
|
||||
*
|
||||
* @param eventSupplier The event that will be posted through a Supplier.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public EventFlow addPostEvent(Supplier<? extends EventType> eventSupplier) {
|
||||
this.event = eventSupplier.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Subscribe by ID without explicit class. */
|
||||
/**
|
||||
*
|
||||
* Start listening for an event and trigger when ID correlates.
|
||||
*
|
||||
* @param event The {@link ResponseToUniqueEvent} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @param unsubscribeAfterSuccess Enable/disable auto unsubscribing to event after being triggered.
|
||||
* @param name A name given to the event, can later be used to unsubscribe.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(
|
||||
Class<TT> event, Consumer<TT> action, boolean unsubscribeAfterSuccess, String name
|
||||
) {
|
||||
|
||||
final long id = SnowflakeGenerator.nextId();
|
||||
|
||||
Consumer<TT> newAction = eventClass -> {
|
||||
if (eventClass.getIdentifier() != this.eventSnowflake) return;
|
||||
|
||||
action.accept(eventClass);
|
||||
|
||||
if (unsubscribeAfterSuccess) unsubscribe(id);
|
||||
|
||||
this.result = eventClass.result();
|
||||
};
|
||||
|
||||
// TODO Remove casts
|
||||
var listener = new ListenerHandler<>(
|
||||
id,
|
||||
name,
|
||||
(Class<ResponseToUniqueEvent>) event,
|
||||
(Consumer<ResponseToUniqueEvent>) newAction
|
||||
);
|
||||
|
||||
GlobalEventBus.subscribe(listener);
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening for an event and trigger when ID correlates, auto unsubscribes after being triggered and adds an empty name.
|
||||
*
|
||||
* @param event The {@link ResponseToUniqueEvent} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(Class<TT> event, Consumer<TT> action) {
|
||||
return this.onResponse(event, action, true, "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening for an event and trigger when ID correlates, auto adds an empty name.
|
||||
*
|
||||
* @param event The {@link ResponseToUniqueEvent} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @param unsubscribeAfterSuccess Enable/disable auto unsubscribing to event after being triggered.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(Class<TT> event, Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
return this.onResponse(event, action, unsubscribeAfterSuccess, "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening for an event and trigger when ID correlates, auto unsubscribes after being triggered.
|
||||
*
|
||||
* @param event The {@link ResponseToUniqueEvent} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @param name A name given to the event, can later be used to unsubscribe.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(Class<TT> event, Consumer<TT> action, String name) {
|
||||
return this.onResponse(event, action, true, name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Subscribe by ID without explicit class.
|
||||
*
|
||||
* @param action The lambda to run when triggered.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
* @deprecated use {@link #onResponse(Class, Consumer, boolean, String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(
|
||||
Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
event -> {
|
||||
Consumer<TT> action, boolean unsubscribeAfterSuccess, String name) {
|
||||
|
||||
final long id = SnowflakeGenerator.nextId();
|
||||
|
||||
Consumer<TT> newAction = event -> {
|
||||
if (!(event instanceof UniqueEvent uuidEvent)) return;
|
||||
if (uuidEvent.getIdentifier() == this.eventSnowflake) {
|
||||
try {
|
||||
TT typedEvent = (TT) uuidEvent;
|
||||
action.accept(typedEvent);
|
||||
if (unsubscribeAfterSuccess
|
||||
&& listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
|
||||
if (unsubscribeAfterSuccess) unsubscribe(id);
|
||||
|
||||
this.result = typedEvent.result();
|
||||
} catch (ClassCastException _) {
|
||||
throw new ClassCastException(
|
||||
@@ -154,99 +237,281 @@ public class EventFlow {
|
||||
+ " to UniqueEvent");
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
};
|
||||
|
||||
var listener = new ListenerHandler<>(
|
||||
id,
|
||||
name,
|
||||
(Class<TT>) action.getClass().getDeclaredMethods()[0].getParameterTypes()[0],
|
||||
newAction
|
||||
);
|
||||
|
||||
GlobalEventBus.subscribe(listener);
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Subscribe by ID without explicit class.
|
||||
*
|
||||
* @param action The lambda to run when triggered.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
* @deprecated use {@link #onResponse(Class, Consumer)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public <TT extends ResponseToUniqueEvent> EventFlow onResponse(Consumer<TT> action) {
|
||||
return this.onResponse(action, true);
|
||||
return this.onResponse(action, true, "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening for an event, and run a lambda when triggered.
|
||||
*
|
||||
* @param event The {@link EventType} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @param unsubscribeAfterSuccess Enable/disable auto unsubscribing to event after being triggered.
|
||||
* @param name A name given to the event, can later be used to unsubscribe.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends EventType> EventFlow listen(
|
||||
Class<TT> eventClass, Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
eventClass,
|
||||
event -> {
|
||||
action.accept(event);
|
||||
Class<TT> event, Consumer<TT> action, boolean unsubscribeAfterSuccess, String name) {
|
||||
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
long id = SnowflakeGenerator.nextId();
|
||||
|
||||
Consumer<TT> newAction = eventc -> {
|
||||
action.accept(eventc);
|
||||
|
||||
if (unsubscribeAfterSuccess) unsubscribe(id);
|
||||
};
|
||||
|
||||
var listener = new ListenerHandler<>(
|
||||
id,
|
||||
name,
|
||||
event,
|
||||
newAction
|
||||
);
|
||||
|
||||
GlobalEventBus.subscribe(listener);
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <TT extends EventType> EventFlow listen(Class<TT> eventClass, Consumer<TT> action) {
|
||||
return this.listen(eventClass, action, true);
|
||||
/**
|
||||
*
|
||||
* Start listening for an event, and run a lambda when triggered, auto unsubscribes.
|
||||
*
|
||||
* @param event The {@link EventType} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @param name A name given to the event, can later be used to unsubscribe.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends EventType> EventFlow listen(Class<TT> event, Consumer<TT> action, String name) {
|
||||
return this.listen(event, action, true, name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening for an event, and run a lambda when triggered, auto unsubscribe and gives it an empty name.
|
||||
*
|
||||
* @param event The {@link EventType} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends EventType> EventFlow listen(Class<TT> event, Consumer<TT> action) {
|
||||
return this.listen(event, action, true, "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening for an event, and run a lambda when triggered, adds an empty name.
|
||||
*
|
||||
* @param event The {@link EventType} to trigger the lambda.
|
||||
* @param action The lambda to run when triggered.
|
||||
* @param unsubscribeAfterSuccess Enable/disable auto unsubscribing to event after being triggered.
|
||||
* @return {@link #EventFlow}
|
||||
*
|
||||
*/
|
||||
public <TT extends EventType> EventFlow listen(Class<TT> event, Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
return this.listen(event, action, unsubscribeAfterSuccess, "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening to an event.
|
||||
*
|
||||
* @param action The lambda to run when triggered.
|
||||
* @return {@link EventFlow}
|
||||
*
|
||||
* @deprecated use {@link #listen(Class, Consumer, boolean, String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TT extends EventType> EventFlow listen(
|
||||
Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
event -> {
|
||||
Consumer<TT> action, boolean unsubscribeAfterSuccess, String name) {
|
||||
long id = SnowflakeGenerator.nextId();
|
||||
|
||||
Class<TT> eventClass = (Class<TT>) action.getClass().getDeclaredMethods()[0].getParameterTypes()[0];
|
||||
|
||||
Consumer<TT> newAction = event -> {
|
||||
if (!(event instanceof EventType nonUuidEvent)) return;
|
||||
try {
|
||||
TT typedEvent = (TT) nonUuidEvent;
|
||||
action.accept(typedEvent);
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
if (unsubscribeAfterSuccess) unsubscribe(id);
|
||||
} catch (ClassCastException _) {
|
||||
throw new ClassCastException(
|
||||
"Cannot cast "
|
||||
+ event.getClass().getName()
|
||||
+ " to UniqueEvent");
|
||||
}
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
};
|
||||
|
||||
var listener = new ListenerHandler<>(
|
||||
id,
|
||||
name,
|
||||
eventClass,
|
||||
newAction
|
||||
);
|
||||
|
||||
GlobalEventBus.subscribe(listener);
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start listening to an event.
|
||||
*
|
||||
* @param action The lambda to run when triggered.
|
||||
* @return {@link EventFlow}
|
||||
*
|
||||
* @deprecated use {@link #listen(Class, Consumer)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public <TT extends EventType> EventFlow listen(Consumer<TT> action) {
|
||||
return this.listen(action, true);
|
||||
return this.listen(action, true, "");
|
||||
}
|
||||
|
||||
/** Post synchronously */
|
||||
/**
|
||||
* Posts the event added through {@link #addPostEvent}.
|
||||
*/
|
||||
public EventFlow postEvent() {
|
||||
GlobalEventBus.post(this.event);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Post asynchronously */
|
||||
/**
|
||||
* Posts the event added through {@link #addPostEvent} asynchronously.
|
||||
*/
|
||||
public EventFlow asyncPostEvent() {
|
||||
GlobalEventBus.postAsync(this.event);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Unsubscribe from an event.
|
||||
*
|
||||
* @param listenerObject The listener object to remove and unsubscribe.
|
||||
*/
|
||||
public void unsubscribe(Object listenerObject) {
|
||||
this.listeners.removeIf(handler -> {
|
||||
if (handler.getListener() == listenerObject) {
|
||||
GlobalEventBus.unsubscribe(handler);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Unsubscribe from an event.
|
||||
*
|
||||
* @param listenerId The id given to the {@link ListenerHandler}.
|
||||
*/
|
||||
public void unsubscribe(long listenerId) {
|
||||
this.listeners.removeIf(handler -> {
|
||||
if (handler.getId() == listenerId) {
|
||||
GlobalEventBus.unsubscribe(handler);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from an event.
|
||||
*
|
||||
* @param name The name given to the listener.
|
||||
*/
|
||||
public void unsubscribe(String name) {
|
||||
this.listeners.removeIf(handler -> {
|
||||
if (handler.getName().equals(name)) {
|
||||
GlobalEventBus.unsubscribe(handler);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe all events.
|
||||
*/
|
||||
public void unsubscribeAll() {
|
||||
listeners.removeIf(handler -> {
|
||||
GlobalEventBus.unsubscribe(handler);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean and remove everything inside {@link EventFlow}.
|
||||
*/
|
||||
private void clean() {
|
||||
this.listeners.clear();
|
||||
unsubscribeAll();
|
||||
this.event = null;
|
||||
this.result = null;
|
||||
} // TODO
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @return TODO
|
||||
*/
|
||||
public Map<String, ?> getResult() {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @return TODO
|
||||
*/
|
||||
public EventType getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a copy of the list of listeners.
|
||||
*
|
||||
* @return Copy of the list of listeners.
|
||||
*/
|
||||
public ListenerHandler[] getListeners() {
|
||||
return listeners.toArray(new ListenerHandler[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generated snowflake for the {@link EventFlow}
|
||||
*
|
||||
* @return The generated snowflake for this {@link EventFlow}
|
||||
*/
|
||||
public long getEventSnowflake() {
|
||||
return eventSnowflake;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import com.lmax.disruptor.dsl.ProducerType;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.framework.eventbus.events.EventType;
|
||||
import org.toop.framework.eventbus.events.UniqueEvent;
|
||||
|
||||
@@ -14,9 +17,10 @@ import org.toop.framework.eventbus.events.UniqueEvent;
|
||||
* publishing.
|
||||
*/
|
||||
public final class GlobalEventBus {
|
||||
private static final Logger logger = LogManager.getLogger(GlobalEventBus.class);
|
||||
|
||||
/** Map of event class to type-specific listeners. */
|
||||
private static final Map<Class<?>, CopyOnWriteArrayList<Consumer<? super EventType>>>
|
||||
private static final Map<Class<?>, CopyOnWriteArrayList<ListenerHandler<?>>>
|
||||
LISTENERS = new ConcurrentHashMap<>();
|
||||
|
||||
/** Map of event class to Snowflake-ID-specific listeners. */
|
||||
@@ -49,7 +53,6 @@ public final class GlobalEventBus {
|
||||
ProducerType.MULTI,
|
||||
new BusySpinWaitStrategy());
|
||||
|
||||
// Single consumer that dispatches to subscribers
|
||||
DISRUPTOR.handleEventsWith(
|
||||
(holder, seq, endOfBatch) -> {
|
||||
if (holder.event != null) {
|
||||
@@ -73,23 +76,12 @@ public final class GlobalEventBus {
|
||||
// ------------------------------------------------------------------------
|
||||
// Subscription
|
||||
// ------------------------------------------------------------------------
|
||||
public static <T extends EventType> Consumer<? super EventType> subscribe(
|
||||
Class<T> eventClass, Consumer<T> listener) {
|
||||
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> list =
|
||||
LISTENERS.computeIfAbsent(eventClass, k -> new CopyOnWriteArrayList<>());
|
||||
|
||||
Consumer<? super EventType> wrapper = event -> listener.accept(eventClass.cast(event));
|
||||
list.add(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static Consumer<? super EventType> subscribe(Consumer<Object> listener) {
|
||||
Consumer<? super EventType> wrapper = event -> listener.accept(event);
|
||||
LISTENERS.computeIfAbsent(Object.class, _ -> new CopyOnWriteArrayList<>()).add(wrapper);
|
||||
return wrapper;
|
||||
public static <T extends EventType> void subscribe(ListenerHandler<T> listener) {
|
||||
logger.debug("Subscribing to {}: {}", listener.getListenerClass().getSimpleName(), listener.getListener().getClass().getSimpleName());
|
||||
LISTENERS.computeIfAbsent(listener.getListenerClass(), _ -> new CopyOnWriteArrayList<>()).add(listener);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public static <T extends UniqueEvent> void subscribeById(
|
||||
Class<T> eventClass, long eventId, Consumer<T> listener) {
|
||||
UUID_LISTENERS
|
||||
@@ -97,10 +89,14 @@ public final class GlobalEventBus {
|
||||
.put(eventId, listener);
|
||||
}
|
||||
|
||||
public static void unsubscribe(Object listener) {
|
||||
LISTENERS.values().forEach(list -> list.remove(listener));
|
||||
public static void unsubscribe(ListenerHandler<?> listener) {
|
||||
logger.debug("Unsubscribing from {}: {}", listener.getListenerClass().getSimpleName(), listener.getListener().getClass().getSimpleName());
|
||||
LISTENERS.getOrDefault(listener.getListenerClass(), new CopyOnWriteArrayList<>())
|
||||
.remove(listener);
|
||||
LISTENERS.entrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
}
|
||||
|
||||
// TODO
|
||||
public static <T extends UniqueEvent> void unsubscribeById(
|
||||
Class<T> eventClass, long eventId) {
|
||||
Map<Long, Consumer<? extends UniqueEvent>> map = UUID_LISTENERS.get(eventClass);
|
||||
@@ -124,36 +120,44 @@ public final class GlobalEventBus {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends EventType> void callListener(ListenerHandler<?> raw, EventType event) {
|
||||
ListenerHandler<T> handler = (ListenerHandler<T>) raw;
|
||||
Consumer<T> listener = handler.getListener();
|
||||
|
||||
T casted = (T) event;
|
||||
|
||||
listener.accept(casted);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void dispatchEvent(EventType event) {
|
||||
Class<?> clazz = event.getClass();
|
||||
|
||||
// class-specific listeners
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> classListeners = LISTENERS.get(clazz);
|
||||
logger.debug("Triggered event: {}", event.getClass().getSimpleName());
|
||||
|
||||
CopyOnWriteArrayList<ListenerHandler<?>> classListeners = LISTENERS.get(clazz);
|
||||
if (classListeners != null) {
|
||||
for (Consumer<? super EventType> listener : classListeners) {
|
||||
for (ListenerHandler<?> listener : classListeners) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
callListener(listener, event);
|
||||
} catch (Throwable e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generic listeners
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> genericListeners =
|
||||
LISTENERS.get(Object.class);
|
||||
CopyOnWriteArrayList<ListenerHandler<?>> genericListeners = LISTENERS.get(Object.class);
|
||||
if (genericListeners != null) {
|
||||
for (Consumer<? super EventType> listener : genericListeners) {
|
||||
for (ListenerHandler<?> listener : genericListeners) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
callListener(listener, event);
|
||||
} catch (Throwable e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// snowflake listeners
|
||||
if (event instanceof UniqueEvent snowflakeEvent) {
|
||||
Map<Long, Consumer<? extends UniqueEvent>> map = UUID_LISTENERS.get(clazz);
|
||||
if (map != null) {
|
||||
@@ -182,4 +186,8 @@ public final class GlobalEventBus {
|
||||
LISTENERS.clear();
|
||||
UUID_LISTENERS.clear();
|
||||
}
|
||||
|
||||
public static Map<Class<?>, CopyOnWriteArrayList<ListenerHandler<?>>> getAllListeners() {
|
||||
return LISTENERS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,48 @@
|
||||
package org.toop.framework.eventbus;
|
||||
|
||||
public class ListenerHandler {
|
||||
private Object listener;
|
||||
import org.toop.framework.SnowflakeGenerator;
|
||||
import org.toop.framework.eventbus.events.EventType;
|
||||
|
||||
// private boolean unsubscribeAfterSuccess = true;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// public ListenerHandler(Object listener, boolean unsubAfterSuccess) {
|
||||
// this.listener = listener;
|
||||
// this.unsubscribeAfterSuccess = unsubAfterSuccess;
|
||||
// }
|
||||
public class ListenerHandler<T extends EventType> {
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final Class<T> clazz;
|
||||
private final Consumer<T> listener;
|
||||
|
||||
public ListenerHandler(Object listener) {
|
||||
public ListenerHandler(long id, String name, Class<T> clazz, Consumer<T> listener) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.clazz = clazz;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public Object getListener() {
|
||||
return this.listener;
|
||||
public ListenerHandler(String name, Class<T> clazz, Consumer<T> listener) {
|
||||
this(SnowflakeGenerator.nextId(), name, clazz, listener);
|
||||
}
|
||||
|
||||
// public boolean isUnsubscribeAfterSuccess() {
|
||||
// return this.unsubscribeAfterSuccess;
|
||||
// }
|
||||
|
||||
public ListenerHandler(long id, Class<T> clazz, Consumer<T> listener) {
|
||||
this(id, String.valueOf(id), clazz, listener);
|
||||
}
|
||||
|
||||
public ListenerHandler(Class<T> clazz, Consumer<T> listener) {
|
||||
this(SnowflakeGenerator.nextId(), clazz, listener);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Consumer<T> getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public Class<T> getListenerClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,25 +17,25 @@ public class NetworkingClientEventListener {
|
||||
public NetworkingClientEventListener(NetworkingClientManager clientManager) {
|
||||
this.clientManager = clientManager;
|
||||
new EventFlow()
|
||||
.listen(this::handleStartClient)
|
||||
.listen(this::handleCommand)
|
||||
.listen(this::handleSendLogin)
|
||||
.listen(this::handleSendLogout)
|
||||
.listen(this::handleSendGetPlayerlist)
|
||||
.listen(this::handleSendGetGamelist)
|
||||
.listen(this::handleSendSubscribe)
|
||||
.listen(this::handleSendMove)
|
||||
.listen(this::handleSendChallenge)
|
||||
.listen(this::handleSendAcceptChallenge)
|
||||
.listen(this::handleSendForfeit)
|
||||
.listen(this::handleSendMessage)
|
||||
.listen(this::handleSendHelp)
|
||||
.listen(this::handleSendHelpForCommand)
|
||||
.listen(this::handleCloseClient)
|
||||
.listen(this::handleReconnect)
|
||||
.listen(this::handleChangeAddress)
|
||||
.listen(this::handleGetAllConnections)
|
||||
.listen(this::handleShutdownAll);
|
||||
.listen(NetworkEvents.StartClient.class, this::handleStartClient, false)
|
||||
.listen(NetworkEvents.SendCommand.class, this::handleCommand, false)
|
||||
.listen(NetworkEvents.SendLogin.class, this::handleSendLogin, false)
|
||||
.listen(NetworkEvents.SendLogout.class, this::handleSendLogout, false)
|
||||
.listen(NetworkEvents.SendGetPlayerlist.class, this::handleSendGetPlayerlist, false)
|
||||
.listen(NetworkEvents.SendGetGamelist.class, this::handleSendGetGamelist, false)
|
||||
.listen(NetworkEvents.SendSubscribe.class, this::handleSendSubscribe, false)
|
||||
.listen(NetworkEvents.SendMove.class, this::handleSendMove, false)
|
||||
.listen(NetworkEvents.SendChallenge.class, this::handleSendChallenge, false)
|
||||
.listen(NetworkEvents.SendAcceptChallenge.class, this::handleSendAcceptChallenge, false)
|
||||
.listen(NetworkEvents.SendForfeit.class, this::handleSendForfeit, false)
|
||||
.listen(NetworkEvents.SendMessage.class, this::handleSendMessage, false)
|
||||
.listen(NetworkEvents.SendHelp.class, this::handleSendHelp, false)
|
||||
.listen(NetworkEvents.SendHelpForCommand.class, this::handleSendHelpForCommand, false)
|
||||
.listen(NetworkEvents.CloseClient.class, this::handleCloseClient, false)
|
||||
.listen(NetworkEvents.Reconnect.class, this::handleReconnect, false)
|
||||
.listen(NetworkEvents.ChangeAddress.class, this::handleChangeAddress, false)
|
||||
.listen(NetworkEvents.RequestsAllClients.class, this::handleGetAllConnections, false)
|
||||
.listen(NetworkEvents.ForceCloseAllClients.class, this::handleShutdownAll, false);
|
||||
}
|
||||
|
||||
void handleStartClient(NetworkEvents.StartClient event) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.framework.eventbus.GlobalEventBus;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
import org.toop.framework.networking.exceptions.ClientNotFoundException;
|
||||
import org.toop.framework.networking.exceptions.CouldNotConnectException;
|
||||
import org.toop.framework.networking.interfaces.NetworkingClient;
|
||||
@@ -44,6 +46,7 @@ public class NetworkingClientManager implements org.toop.framework.networking.in
|
||||
nClient.connect(id, nConnector.host(), nConnector.port());
|
||||
networkClients.put(id, nClient);
|
||||
logger.info("New client started successfully for {}:{}", nConnector.host(), nConnector.port());
|
||||
GlobalEventBus.post(new NetworkEvents.ConnectTry(id, attempts, nConnector.reconnectAttempts(), true));
|
||||
onSuccess.run();
|
||||
scheduler.shutdown();
|
||||
} catch (CouldNotConnectException e) {
|
||||
@@ -51,14 +54,17 @@ public class NetworkingClientManager implements org.toop.framework.networking.in
|
||||
if (attempts < nConnector.reconnectAttempts()) {
|
||||
logger.warn("Could not connect to {}:{}. Retrying in {} {}",
|
||||
nConnector.host(), nConnector.port(), nConnector.timeout(), nConnector.timeUnit());
|
||||
GlobalEventBus.post(new NetworkEvents.ConnectTry(id, attempts, nConnector.reconnectAttempts(), false));
|
||||
scheduler.schedule(this, nConnector.timeout(), nConnector.timeUnit());
|
||||
} else {
|
||||
logger.error("Failed to start client for {}:{} after {} attempts", nConnector.host(), nConnector.port(), attempts);
|
||||
GlobalEventBus.post(new NetworkEvents.ConnectTry(id, -1, nConnector.reconnectAttempts(), false));
|
||||
onFailure.run();
|
||||
scheduler.shutdown();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Unexpected exception during startClient", e);
|
||||
GlobalEventBus.post(new NetworkEvents.ConnectTry(id, -1, nConnector.reconnectAttempts(), false));
|
||||
onFailure.run();
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
@@ -181,6 +181,8 @@ public class NetworkEvents extends EventsBase {
|
||||
public record StartClientResponse(long clientId, boolean successful, long identifier)
|
||||
implements ResponseToUniqueEvent {}
|
||||
|
||||
public record ConnectTry(long clientId, int amount, int maxAmount, boolean success) implements GenericEvent {}
|
||||
|
||||
/**
|
||||
* Requests reconnection of an existing client using its previous configuration.
|
||||
* <p>
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.toop.framework.resource.types.LoadableResource;
|
||||
|
||||
@FileExtension({"png", "jpg", "jpeg"})
|
||||
public class ImageAsset extends BaseResource implements LoadableResource {
|
||||
private Image image;
|
||||
private Image image = null;
|
||||
|
||||
public ImageAsset(final File file) {
|
||||
super(file);
|
||||
@@ -40,8 +40,7 @@ public class ImageAsset extends BaseResource implements LoadableResource {
|
||||
public Image getImage() {
|
||||
if (!this.isLoaded) {
|
||||
this.load();
|
||||
}
|
||||
return image;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class JsonAsset<T> extends BaseResource implements LoadableResource {
|
||||
|
||||
private T content;
|
||||
private Class<T> type;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
|
||||
|
||||
public JsonAsset(File file, Class<T> type) {
|
||||
super(file);
|
||||
|
||||
@@ -38,6 +38,22 @@ public class SettingsAsset extends JsonAsset<Settings> {
|
||||
return getContent().layoutSize;
|
||||
}
|
||||
|
||||
public Boolean getTutorialFlag() {
|
||||
return getContent().showTutorials;
|
||||
}
|
||||
|
||||
public Boolean getFirstTTT() {
|
||||
return getContent().firstTTT;
|
||||
}
|
||||
|
||||
public Boolean getFirstConnect4() {
|
||||
return getContent().firstConnect4;
|
||||
}
|
||||
|
||||
public Boolean getFirstReversi() {
|
||||
return getContent().firstReversi;
|
||||
}
|
||||
|
||||
public void setVolume(int volume) {
|
||||
getContent().volume = volume;
|
||||
save();
|
||||
@@ -72,4 +88,24 @@ public class SettingsAsset extends JsonAsset<Settings> {
|
||||
getContent().layoutSize = layoutSize;
|
||||
save();
|
||||
}
|
||||
|
||||
public void setTutorialFlag(boolean tutorialFlag) {
|
||||
getContent().showTutorials = tutorialFlag;
|
||||
save();
|
||||
}
|
||||
|
||||
public void setFirstTTT(boolean firstTTT) {
|
||||
getContent().firstTTT = firstTTT;
|
||||
save();
|
||||
}
|
||||
|
||||
public void setFirstConnect4(boolean firstConnect4) {
|
||||
getContent().firstConnect4 = firstConnect4;
|
||||
save();
|
||||
}
|
||||
|
||||
public void setFirstReversi(boolean firstReversi) {
|
||||
getContent().firstReversi = firstReversi;
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,9 @@ public class Settings {
|
||||
public int volume = 100;
|
||||
public int fxVolume = 20;
|
||||
public int musicVolume = 15;
|
||||
public Boolean showTutorials;
|
||||
public Boolean firstReversi;
|
||||
public Boolean firstTTT;
|
||||
public Boolean firstConnect4;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,235 +1,222 @@
|
||||
// package org.toop.framework.eventbus;
|
||||
//
|
||||
// import org.junit.jupiter.api.Tag;
|
||||
// import org.junit.jupiter.api.Test;
|
||||
// import org.toop.framework.eventbus.events.UniqueEvent;
|
||||
//
|
||||
// import java.math.BigInteger;
|
||||
// import java.util.concurrent.*;
|
||||
// import java.util.concurrent.atomic.LongAdder;
|
||||
//
|
||||
// import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
//
|
||||
// class EventFlowStressTest {
|
||||
//
|
||||
// /** Top-level record to ensure runtime type matches subscription */
|
||||
// public record HeavyEvent(String payload, long eventSnowflake) implements UniqueEvent {
|
||||
// @Override
|
||||
// public java.util.Map<String, Object> result() {
|
||||
// return java.util.Map.of("payload", payload, "eventId", eventSnowflake);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long eventSnowflake() {
|
||||
// return this.eventSnowflake;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public record HeavyEventSuccess(String payload, long eventSnowflake) implements
|
||||
// UniqueEvent {
|
||||
// @Override
|
||||
// public java.util.Map<String, Object> result() {
|
||||
// return java.util.Map.of("payload", payload, "eventId", eventSnowflake);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long eventSnowflake() {
|
||||
// return eventSnowflake;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static final int THREADS = 32;
|
||||
// private static final long EVENTS_PER_THREAD = 10_000_000;
|
||||
//
|
||||
// @Tag("stress")
|
||||
// @Test
|
||||
// void extremeConcurrencySendTest_progressWithMemory() throws InterruptedException {
|
||||
// LongAdder counter = new LongAdder();
|
||||
// ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
//
|
||||
// BigInteger totalEvents = BigInteger.valueOf(THREADS)
|
||||
// .multiply(BigInteger.valueOf(EVENTS_PER_THREAD));
|
||||
//
|
||||
// long startTime = System.currentTimeMillis();
|
||||
//
|
||||
// // Monitor thread for EPS and memory
|
||||
// Thread monitor = new Thread(() -> {
|
||||
// long lastCount = 0;
|
||||
// long lastTime = System.currentTimeMillis();
|
||||
// Runtime runtime = Runtime.getRuntime();
|
||||
//
|
||||
// while (counter.sum() < totalEvents.longValue()) {
|
||||
// try { Thread.sleep(200); } catch (InterruptedException ignored) {}
|
||||
//
|
||||
// long now = System.currentTimeMillis();
|
||||
// long completed = counter.sum();
|
||||
// long eventsThisPeriod = completed - lastCount;
|
||||
// double eps = eventsThisPeriod / ((now - lastTime) / 1000.0);
|
||||
//
|
||||
// long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
// double usedPercent = usedMemory * 100.0 / runtime.maxMemory();
|
||||
//
|
||||
// System.out.printf(
|
||||
// "Progress: %d/%d (%.2f%%), EPS: %.0f, Memory Used: %.2f MB (%.2f%%)%n",
|
||||
// completed,
|
||||
// totalEvents.longValue(),
|
||||
// completed * 100.0 / totalEvents.doubleValue(),
|
||||
// eps,
|
||||
// usedMemory / 1024.0 / 1024.0,
|
||||
// usedPercent
|
||||
// );
|
||||
//
|
||||
// lastCount = completed;
|
||||
// lastTime = now;
|
||||
// }
|
||||
// });
|
||||
// monitor.setDaemon(true);
|
||||
// monitor.start();
|
||||
//
|
||||
// var listener = new EventFlow().listen(HeavyEvent.class, _ -> counter.increment());
|
||||
//
|
||||
// // Submit events asynchronously
|
||||
// for (int t = 0; t < THREADS; t++) {
|
||||
// executor.submit(() -> {
|
||||
// for (int i = 0; i < EVENTS_PER_THREAD; i++) {
|
||||
// var _ = new EventFlow().addPostEvent(HeavyEvent.class, "payload-" + i)
|
||||
// .asyncPostEvent();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// executor.shutdown();
|
||||
// executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
//
|
||||
// listener.getResult();
|
||||
//
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// double durationSeconds = (endTime - startTime) / 1000.0;
|
||||
//
|
||||
// System.out.println("Posted " + totalEvents + " events in " + durationSeconds + "
|
||||
// seconds");
|
||||
// double averageEps = totalEvents.doubleValue() / durationSeconds;
|
||||
// System.out.printf("Average EPS: %.0f%n", averageEps);
|
||||
//
|
||||
// assertEquals(totalEvents.longValue(), counter.sum());
|
||||
// }
|
||||
//
|
||||
// @Tag("stress")
|
||||
// @Test
|
||||
// void extremeConcurrencySendAndReturnTest_progressWithMemory() throws InterruptedException {
|
||||
// LongAdder counter = new LongAdder();
|
||||
// ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
//
|
||||
// BigInteger totalEvents = BigInteger.valueOf(THREADS)
|
||||
// .multiply(BigInteger.valueOf(EVENTS_PER_THREAD));
|
||||
//
|
||||
// long startTime = System.currentTimeMillis();
|
||||
//
|
||||
// // Monitor thread for EPS and memory
|
||||
// Thread monitor = new Thread(() -> {
|
||||
// long lastCount = 0;
|
||||
// long lastTime = System.currentTimeMillis();
|
||||
// Runtime runtime = Runtime.getRuntime();
|
||||
//
|
||||
// while (counter.sum() < totalEvents.longValue()) {
|
||||
// try { Thread.sleep(200); } catch (InterruptedException ignored) {}
|
||||
//
|
||||
// long now = System.currentTimeMillis();
|
||||
// long completed = counter.sum();
|
||||
// long eventsThisPeriod = completed - lastCount;
|
||||
// double eps = eventsThisPeriod / ((now - lastTime) / 1000.0);
|
||||
//
|
||||
// long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
// double usedPercent = usedMemory * 100.0 / runtime.maxMemory();
|
||||
//
|
||||
// System.out.printf(
|
||||
// "Progress: %d/%d (%.2f%%), EPS: %.0f, Memory Used: %.2f MB (%.2f%%)%n",
|
||||
// completed,
|
||||
// totalEvents.longValue(),
|
||||
// completed * 100.0 / totalEvents.doubleValue(),
|
||||
// eps,
|
||||
// usedMemory / 1024.0 / 1024.0,
|
||||
// usedPercent
|
||||
// );
|
||||
//
|
||||
// lastCount = completed;
|
||||
// lastTime = now;
|
||||
// }
|
||||
// });
|
||||
// monitor.setDaemon(true);
|
||||
// monitor.start();
|
||||
//
|
||||
// // Submit events asynchronously
|
||||
// for (int t = 0; t < THREADS; t++) {
|
||||
// executor.submit(() -> {
|
||||
// for (int i = 0; i < EVENTS_PER_THREAD; i++) {
|
||||
// var a = new EventFlow().addPostEvent(HeavyEvent.class, "payload-" + i)
|
||||
// .onResponse(HeavyEventSuccess.class, _ -> counter.increment())
|
||||
// .postEvent();
|
||||
//
|
||||
// new EventFlow().addPostEvent(HeavyEventSuccess.class, "payload-" + i,
|
||||
// a.getEventSnowflake())
|
||||
// .postEvent();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// executor.shutdown();
|
||||
// executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
//
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// double durationSeconds = (endTime - startTime) / 1000.0;
|
||||
//
|
||||
// System.out.println("Posted " + totalEvents + " events in " + durationSeconds + "
|
||||
// seconds");
|
||||
// double averageEps = totalEvents.doubleValue() / durationSeconds;
|
||||
// System.out.printf("Average EPS: %.0f%n", averageEps);
|
||||
//
|
||||
// assertEquals(totalEvents.longValue(), counter.sum());
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Tag("stress")
|
||||
// @Test
|
||||
// void efficientExtremeConcurrencyTest() throws InterruptedException {
|
||||
// final int THREADS = Runtime.getRuntime().availableProcessors();
|
||||
// final int EVENTS_PER_THREAD = 5000;
|
||||
//
|
||||
// ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
// ConcurrentLinkedQueue<HeavyEvent> processedEvents = new ConcurrentLinkedQueue<>();
|
||||
//
|
||||
// long start = System.nanoTime();
|
||||
//
|
||||
// for (int t = 0; t < THREADS; t++) {
|
||||
// executor.submit(() -> {
|
||||
// for (int i = 0; i < EVENTS_PER_THREAD; i++) {
|
||||
// new EventFlow().addPostEvent(HeavyEvent.class, "payload-" + i)
|
||||
// .onResponse(HeavyEvent.class, processedEvents::add)
|
||||
// .postEvent();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// executor.shutdown();
|
||||
// executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
//
|
||||
// long end = System.nanoTime();
|
||||
// double durationSeconds = (end - start) / 1_000_000_000.0;
|
||||
//
|
||||
// BigInteger totalEvents = BigInteger.valueOf((long)
|
||||
// THREADS).multiply(BigInteger.valueOf(EVENTS_PER_THREAD));
|
||||
// double eps = totalEvents.doubleValue() / durationSeconds;
|
||||
//
|
||||
// System.out.printf("Posted %s events in %.3f seconds%n", totalEvents, durationSeconds);
|
||||
// System.out.printf("Throughput: %.0f events/sec%n", eps);
|
||||
//
|
||||
// Runtime rt = Runtime.getRuntime();
|
||||
// System.out.printf("Used memory: %.2f MB%n", (rt.totalMemory() - rt.freeMemory()) / 1024.0
|
||||
// / 1024.0);
|
||||
//
|
||||
// assertEquals(totalEvents.intValue(), processedEvents.size());
|
||||
// }
|
||||
//
|
||||
package org.toop.framework.eventbus;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.toop.framework.eventbus.events.ResponseToUniqueEvent;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class EventFlowStressTest {
|
||||
|
||||
public record HeavyEvent(String payload, long eventSnowflake) implements ResponseToUniqueEvent {
|
||||
@Override
|
||||
public long getIdentifier() {
|
||||
return eventSnowflake;
|
||||
}
|
||||
}
|
||||
|
||||
public record HeavyEventSuccess(String payload, long eventSnowflake) implements ResponseToUniqueEvent {
|
||||
@Override
|
||||
public long getIdentifier() {
|
||||
return eventSnowflake;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int THREADS = 32;
|
||||
private static final long EVENTS_PER_THREAD = 10_000_000;
|
||||
|
||||
@Tag("stress")
|
||||
@Test
|
||||
void extremeConcurrencySendTest_progressWithMemory() throws InterruptedException {
|
||||
LongAdder counter = new LongAdder();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
|
||||
BigInteger totalEvents = BigInteger.valueOf(THREADS)
|
||||
.multiply(BigInteger.valueOf(EVENTS_PER_THREAD));
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
Thread monitor = new Thread(() -> {
|
||||
long lastCount = 0;
|
||||
long lastTime = System.currentTimeMillis();
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
while (counter.sum() < totalEvents.longValue()) {
|
||||
try { Thread.sleep(200); } catch (InterruptedException ignored) {}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long completed = counter.sum();
|
||||
long eventsThisPeriod = completed - lastCount;
|
||||
double eps = eventsThisPeriod / ((now - lastTime) / 1000.0);
|
||||
|
||||
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
double usedPercent = usedMemory * 100.0 / runtime.maxMemory();
|
||||
|
||||
System.out.printf(
|
||||
"Progress: %d/%d (%.2f%%), EPS: %.0f, Memory Used: %.2f MB (%.2f%%)%n",
|
||||
completed,
|
||||
totalEvents.longValue(),
|
||||
completed * 100.0 / totalEvents.doubleValue(),
|
||||
eps,
|
||||
usedMemory / 1024.0 / 1024.0,
|
||||
usedPercent
|
||||
);
|
||||
|
||||
lastCount = completed;
|
||||
lastTime = now;
|
||||
}
|
||||
});
|
||||
monitor.setDaemon(true);
|
||||
monitor.start();
|
||||
|
||||
var listener = new EventFlow().listen(HeavyEvent.class, _ -> counter.increment());
|
||||
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
executor.submit(() -> {
|
||||
for (int i = 0; i < EVENTS_PER_THREAD; i++) {
|
||||
var _ = new EventFlow().addPostEvent(HeavyEvent.class, "payload-" + i)
|
||||
.asyncPostEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
|
||||
listener.getResult();
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
double durationSeconds = (endTime - startTime) / 1000.0;
|
||||
|
||||
System.out.println("Posted " + totalEvents + " events in " + durationSeconds + " seconds");
|
||||
double averageEps = totalEvents.doubleValue() / durationSeconds;
|
||||
System.out.printf("Average EPS: %.0f%n", averageEps);
|
||||
|
||||
assertEquals(totalEvents.longValue(), counter.sum());
|
||||
}
|
||||
|
||||
@Tag("stress")
|
||||
@Test
|
||||
void extremeConcurrencySendAndReturnTest_progressWithMemory() throws InterruptedException {
|
||||
LongAdder counter = new LongAdder();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
|
||||
BigInteger totalEvents = BigInteger.valueOf(THREADS)
|
||||
.multiply(BigInteger.valueOf(EVENTS_PER_THREAD));
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
Thread monitor = new Thread(() -> {
|
||||
long lastCount = 0;
|
||||
long lastTime = System.currentTimeMillis();
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
while (counter.sum() < totalEvents.longValue()) {
|
||||
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long completed = counter.sum();
|
||||
long eventsThisPeriod = completed - lastCount;
|
||||
double eps = eventsThisPeriod / ((now - lastTime) / 1000.0);
|
||||
|
||||
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
double usedPercent = usedMemory * 100.0 / runtime.maxMemory();
|
||||
|
||||
System.out.printf(
|
||||
"Progress: %d/%d (%.2f%%), EPS: %.0f, Memory Used: %.2f MB (%.2f%%)%n",
|
||||
completed,
|
||||
totalEvents.longValue(),
|
||||
completed * 100.0 / totalEvents.doubleValue(),
|
||||
eps,
|
||||
usedMemory / 1024.0 / 1024.0,
|
||||
usedPercent
|
||||
);
|
||||
|
||||
lastCount = completed;
|
||||
lastTime = now;
|
||||
}
|
||||
});
|
||||
monitor.setDaemon(true);
|
||||
monitor.start();
|
||||
|
||||
EventFlow sharedFlow = new EventFlow();
|
||||
sharedFlow.listen(HeavyEventSuccess.class, _ -> counter.increment(), false, "heavyEventSuccessListener");
|
||||
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
executor.submit(() -> {
|
||||
EventFlow threadFlow = new EventFlow();
|
||||
|
||||
for (int i = 0; i < EVENTS_PER_THREAD; i++) {
|
||||
var heavyEvent = threadFlow.addPostEvent(HeavyEvent.class, "payload-" + i)
|
||||
.postEvent();
|
||||
|
||||
threadFlow.addPostEvent(HeavyEventSuccess.class, "payload-" + i, heavyEvent.getEventSnowflake())
|
||||
.postEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
double durationSeconds = (endTime - startTime) / 1000.0;
|
||||
|
||||
System.out.println("Posted " + totalEvents + " events in " + durationSeconds + " seconds");
|
||||
double averageEps = totalEvents.doubleValue() / durationSeconds;
|
||||
System.out.printf("Average EPS: %.0f%n", averageEps);
|
||||
|
||||
assertEquals(totalEvents.longValue(), counter.sum());
|
||||
}
|
||||
|
||||
@Tag("stress")
|
||||
@Test
|
||||
void efficientExtremeConcurrencyTest() throws InterruptedException {
|
||||
final int THREADS = Runtime.getRuntime().availableProcessors();
|
||||
final int EVENTS_PER_THREAD = 1_000_000;
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
ConcurrentLinkedQueue<HeavyEvent> processedEvents = new ConcurrentLinkedQueue<>();
|
||||
|
||||
long start = System.nanoTime();
|
||||
|
||||
EventFlow sharedFlow = new EventFlow();
|
||||
sharedFlow.listen(HeavyEvent.class, processedEvents::add, false, "heavyEventListener");
|
||||
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
executor.submit(() -> {
|
||||
EventFlow threadFlow = new EventFlow();
|
||||
|
||||
for (int i = 0; i < EVENTS_PER_THREAD; i++) {
|
||||
threadFlow.addPostEvent(HeavyEvent.class, "payload-" + i)
|
||||
.postEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
|
||||
long end = System.nanoTime();
|
||||
double durationSeconds = (end - start) / 1_000_000_000.0;
|
||||
|
||||
BigInteger totalEvents = BigInteger.valueOf(THREADS)
|
||||
.multiply(BigInteger.valueOf(EVENTS_PER_THREAD));
|
||||
double eps = totalEvents.doubleValue() / durationSeconds;
|
||||
|
||||
System.out.printf("Posted %s events in %.3f seconds%n", totalEvents, durationSeconds);
|
||||
System.out.printf("Throughput: %.0f events/sec%n", eps);
|
||||
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
System.out.printf("Used memory: %.2f MB%n", (rt.totalMemory() - rt.freeMemory()) / 1024.0 / 1024.0);
|
||||
|
||||
assertEquals(totalEvents.intValue(), processedEvents.size());
|
||||
}
|
||||
|
||||
// @Tag("stress")
|
||||
// @Test
|
||||
// void constructorCacheVsReflection() throws Throwable {
|
||||
@@ -247,7 +234,6 @@
|
||||
// long endHandle = System.nanoTime();
|
||||
//
|
||||
// System.out.println("Reflection: " + (endReflect - startReflect) / 1_000_000 + " ms");
|
||||
// System.out.println("MethodHandle Cache: " + (endHandle - startHandle) / 1_000_000 + "
|
||||
// ms");
|
||||
// }
|
||||
// System.out.println("MethodHandle Cache: " + (endHandle - startHandle) / 1_000_000 + " ms");
|
||||
// }
|
||||
}
|
||||
|
||||