diff --git a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/Overlay.java b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/Overlay.java index 90d9bcb..8a9636c 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/Overlay.java +++ b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/Overlay.java @@ -1,23 +1,19 @@ package eu.jonahbauer.wizard.client.libgdx.actions.overlay; -import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.Interpolation; -import com.badlogic.gdx.scenes.scene2d.*; +import com.badlogic.gdx.scenes.scene2d.Action; +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.ui.Container; import com.badlogic.gdx.scenes.scene2d.ui.Skin; -import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.utils.I18NBundle; -import eu.jonahbauer.wizard.client.libgdx.util.AnimationTimings; -import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas; import eu.jonahbauer.wizard.client.libgdx.WizardGame; import eu.jonahbauer.wizard.client.libgdx.screens.GameScreen; import lombok.AccessLevel; import lombok.Getter; import org.jetbrains.annotations.NotNull; -import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*; +import static com.badlogic.gdx.scenes.scene2d.actions.Actions.run; public abstract class Overlay extends Action { protected final GameScreen screen; diff --git a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/ScoreOverlay.java b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/ScoreOverlay.java index 14095e2..3409380 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/ScoreOverlay.java +++ b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/actions/overlay/ScoreOverlay.java @@ -2,6 +2,8 @@ package eu.jonahbauer.wizard.client.libgdx.actions.overlay; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Group; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import eu.jonahbauer.wizard.client.libgdx.WizardGame; import eu.jonahbauer.wizard.client.libgdx.actors.PadOfTruth; import eu.jonahbauer.wizard.client.libgdx.screens.GameScreen; @@ -16,17 +18,38 @@ import static eu.jonahbauer.wizard.client.libgdx.screens.GameScreen.PAD_OF_TRUTH import static eu.jonahbauer.wizard.client.libgdx.util.AnimationTimings.OVERLAY_SHARED_ELEMENT; public class ScoreOverlay extends Overlay { + private final boolean finalScores; + private final PadOfTruth padOfTruth; + private TextButton back; - public ScoreOverlay(@NotNull GameScreen gameScreen) { + public ScoreOverlay(@NotNull GameScreen gameScreen, boolean finalScores) { super(gameScreen, Long.MAX_VALUE); - padOfTruth = Objects.requireNonNull(gameScreen.getPadOfTruth()); + this.finalScores = finalScores; + this.padOfTruth = Objects.requireNonNull(gameScreen.getPadOfTruth()); } @Override protected Actor createContent() { var group = new Group(); group.addActor(padOfTruth); + + if (finalScores) { + back = new TextButton(messages.get("game.overlay.scores.return_to_session"), skin); + back.setPosition( + (WizardGame.WIDTH - back.getWidth()) / 2, + (WizardGame.HEIGHT - padOfTruth.getHeight()) / 2 + 30 - back.getHeight() + ); + back.setVisible(false); + back.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + screen.showSessionScreen(); + } + }); + group.addActor(back); + } + return group; } @@ -35,20 +58,32 @@ public class ScoreOverlay extends Overlay { var root = getRoot().fill(); super.show(parent); - root.addAction(sequence( - run(() -> padOfTruth.setEnabled(false)), - parallel( - targeting(padOfTruth, scaleTo(1, 1, OVERLAY_SHARED_ELEMENT)), - targeting(padOfTruth, moveTo((WizardGame.WIDTH - padOfTruth.getWidth()) / 2, (WizardGame.HEIGHT - padOfTruth.getHeight()) / 2, OVERLAY_SHARED_ELEMENT)) - ), - delay(AnimationTimings.OVERLAY_HOLD), - parallel( - targeting(padOfTruth, scaleTo(PadOfTruth.COLLAPSED_SCALE, PadOfTruth.COLLAPSED_SCALE, OVERLAY_SHARED_ELEMENT)), - targeting(padOfTruth, moveTo(PAD_OF_TRUTH_POSITION.x, PAD_OF_TRUTH_POSITION.y, OVERLAY_SHARED_ELEMENT)) - ), - targeting(padOfTruth, changeParent(screen.getContentRoot())), - run(() -> padOfTruth.setEnabled(true)), - run(this::close) - )); + if (finalScores) { + root.addAction(sequence( + run(() -> padOfTruth.setEnabled(false)), + parallel( + targeting(padOfTruth, scaleTo(1, 1, OVERLAY_SHARED_ELEMENT)), + targeting(padOfTruth, moveTo((WizardGame.WIDTH - padOfTruth.getWidth()) / 2,(WizardGame.HEIGHT - padOfTruth.getHeight()) / 2 + 50, OVERLAY_SHARED_ELEMENT)) + ), + delay(AnimationTimings.OVERLAY_HOLD), + targeting(back, visible(true)) + )); + } else { + root.addAction(sequence( + run(() -> padOfTruth.setEnabled(false)), + parallel( + targeting(padOfTruth, scaleTo(1, 1, OVERLAY_SHARED_ELEMENT)), + targeting(padOfTruth, moveTo((WizardGame.WIDTH - padOfTruth.getWidth()) / 2,(WizardGame.HEIGHT - padOfTruth.getHeight()) / 2, OVERLAY_SHARED_ELEMENT)) + ), + delay(AnimationTimings.OVERLAY_HOLD), + parallel( + targeting(padOfTruth, scaleTo(PadOfTruth.COLLAPSED_SCALE, PadOfTruth.COLLAPSED_SCALE, OVERLAY_SHARED_ELEMENT)), + targeting(padOfTruth, moveTo(PAD_OF_TRUTH_POSITION.x, PAD_OF_TRUTH_POSITION.y, OVERLAY_SHARED_ELEMENT) ) + ), + targeting(padOfTruth, changeParent(screen.getContentRoot())), + run(() -> padOfTruth.setEnabled(true)), + run(this::close) + )); + } } } diff --git a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/screens/GameScreen.java b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/screens/GameScreen.java index 2f28feb..9d02302 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/screens/GameScreen.java +++ b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/screens/GameScreen.java @@ -21,6 +21,7 @@ import eu.jonahbauer.wizard.client.libgdx.actors.CardStack; import eu.jonahbauer.wizard.client.libgdx.actors.CardsGroup; import eu.jonahbauer.wizard.client.libgdx.actors.PadOfTruth; import eu.jonahbauer.wizard.client.libgdx.state.Game; +import eu.jonahbauer.wizard.client.libgdx.state.Session; import eu.jonahbauer.wizard.client.libgdx.util.AnimationTimings; import eu.jonahbauer.wizard.client.libgdx.util.CardUtil; import eu.jonahbauer.wizard.client.libgdx.util.Pair; @@ -357,6 +358,10 @@ public class GameScreen extends WizardScreen { game.getClient().execute(Game.class, (s, c) -> s.onPredictionMade(c, prediction)); } + public void showSessionScreen() { + game.getClient().execute(Session.class, Session::showInfoScreen); + } + // public void dim(int layer, boolean dim) { var old = dimLayer; @@ -773,8 +778,8 @@ public class GameScreen extends WizardScreen { return overlay; } - public void showScoreOverlay() { - execute(new ScoreOverlay(this)); + public void showScoreOverlay(boolean finalScores) { + execute(new ScoreOverlay(this, finalScores)); } public InteractionOverlay showMakePredictionOverlay(int round, long timeout) { diff --git a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Game.java b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Game.java index 4c8e34a..f5977a3 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Game.java +++ b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Game.java @@ -68,6 +68,8 @@ public final class Game extends BaseState { private Card juggleCard; private boolean werewolf; + private int finishing = 0; + public void init(List messages) { pendingMessages = messages; } @@ -104,11 +106,16 @@ public final class Game extends BaseState { } private Optional onMessage(ObserverMessage message) { + if (finishing != 0) return onMessageWhileFinishing(message); + if (message instanceof StateMessage state) { switch (state.getState()) { - case "starting_round" -> onStartRound(); + case "starting_round" -> { + return onStartRound(); + } case "starting_trick" -> onStartTrick(); case "juggling" -> onJuggle(); + case "finishing_round" -> onFinishingRound(); case "finished" -> { onFinished(); return returnToSession(); @@ -129,20 +136,44 @@ public final class Game extends BaseState { } else if (message instanceof CardMessage card) { onCardMessage(card.getPlayer(), card.getCard()); } else if (message instanceof ScoreMessage score) { - onScoreMessage(score.getPoints()); + onScoreMessage(score.getPoints(), true); } else if (message instanceof UserInputMessage input) { onUserInputMessage(input.getPlayer(), input.getAction(), input.getTimeout()); } else if (message instanceof TimeoutMessage) { onTimeoutMessage(); } else { - log.fatal("Unknown observer message type {}.", message.getClass()); - // TODO user feedback - return Optional.of(new Menu()); + return unexpectedMessage(new GameMessage(message)); } return Optional.empty(); } - private void onStartRound() { + private Optional onMessageWhileFinishing(ObserverMessage message) { + if (finishing == 1) { // last "finishing_round" has been received + if (message instanceof ScoreMessage score) { + onScoreMessage(score.getPoints(), false); + return Optional.empty(); + } else if (message instanceof StateMessage state && "finishing".equals(state.getState())) { + finishing++; + return Optional.empty(); + } + } else if (finishing == 2) { // "finishing" has been received + if (message instanceof ScoreMessage) { + return Optional.empty(); + } else if (message instanceof StateMessage state && "finished".equals(state.getState())) { + onFinished(); + finishing++; + return returnToSession(); + } + } + + return unexpectedMessage(new GameMessage(message)); + } + + private Optional onStartRound() { + if (isLastRound()) { + log.fatal("Cannot start round {} with {} players", round + 1, players.size()); + return unexpectedMessage(new GameMessage(new StateMessage("starting_round"))); + } log.info("Round {} is starting...", round + 1); round ++; @@ -153,6 +184,7 @@ public final class Game extends BaseState { trick = -1; if (gameScreen != null) gameScreen.startRound(round); + return Optional.empty(); } private void onStartTrick() { @@ -168,8 +200,15 @@ public final class Game extends BaseState { juggleCard = null; } + private void onFinishingRound() { + if (isLastRound()) finishing = 1; // start finish procedure + } + private void onFinished() { log.info("The game has finished."); + if (gameScreen != null) { + gameScreen.showScoreOverlay(true); + } } private void onError() { @@ -256,12 +295,12 @@ public final class Game extends BaseState { if (gameScreen != null) gameScreen.playCard(player, handCard, card); } - private void onScoreMessage(@Unmodifiable Map<@NotNull UUID, @NotNull Integer> points) { + private void onScoreMessage(@Unmodifiable Map<@NotNull UUID, @NotNull Integer> points, boolean showOverlay) { log.info("The scores are as follows: " + points); scores.put(round, points); if (gameScreen != null) { gameScreen.addScores(round, points); - gameScreen.showScoreOverlay(); + if (showOverlay) gameScreen.showScoreOverlay(false); } } @@ -392,7 +431,8 @@ public final class Game extends BaseState { players.entrySet().stream() .map(entry -> new PlayerData(entry.getKey(), entry.getValue(), false)) .toList(), - self + self, + true )); } @@ -509,6 +549,10 @@ public final class Game extends BaseState { } } + private boolean isLastRound() { + return round + 1 >= 60 / getPlayers().size(); + } + // /** diff --git a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Session.java b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Session.java index 0b4e7a8..4063793 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Session.java +++ b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/state/Session.java @@ -24,27 +24,29 @@ public final class Session extends BaseState { private final String sessionName; private final Configuration configuration; private final LinkedHashMap players = new LinkedHashMap<>(); + private final boolean dontSwitchScreen; private boolean sending; public Session(SessionData session, Collection players, UUID self) { + this(session, players, self, false); + } + + public Session(SessionData session, Collection players, UUID self, boolean dontSwitchScreen) { this.session = session.getUuid(); this.sessionName = session.getName(); this.configuration = session.getConfiguration(); players.forEach(p -> this.players.put(p.getUuid(), p)); + this.dontSwitchScreen = dontSwitchScreen; this.self = self; } @Override public Optional onEnter(Client client) { - sessionScreen = new WaitingScreen(client.getGame()); - client.getGame().setScreen(sessionScreen); - sessionScreen.setPlayers(players.values().toArray(new PlayerData[0])); - sessionScreen.setReady(false); - sessionScreen.setPlayerName(getName()); - sessionScreen.setSession(session, sessionName, configuration); - + if (!dontSwitchScreen) { + showInfoScreen(client); + } return super.onEnter(client); } @@ -112,6 +114,16 @@ public final class Session extends BaseState { return Optional.of(new AwaitingJoinLobby()); } + public Optional showInfoScreen(Client client) { + sessionScreen = new WaitingScreen(client.getGame()); + client.getGame().setScreen(sessionScreen); + sessionScreen.setPlayers(players.values().toArray(new PlayerData[0])); + sessionScreen.setReady(players.get(self).isReady()); + sessionScreen.setPlayerName(getName()); + sessionScreen.setSession(session, sessionName, configuration); + return Optional.empty(); + } + private boolean isReady() { return players.get(self).isReady(); } diff --git a/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages.properties b/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages.properties index f024c69..3f879c9 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages.properties +++ b/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages.properties @@ -236,4 +236,6 @@ game.overlay.play_colored_card.prompt=Pick a color game.overlay.play_colored_card.cancel=Cancel game.overlay.play_changeling.prompt=Do you want to play the changeling as a wizard or a jester? -game.overlay.play_changeling.cancel=Cancel \ No newline at end of file +game.overlay.play_changeling.cancel=Cancel + +game.overlay.scores.return_to_session=Done \ No newline at end of file diff --git a/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages_de.properties b/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages_de.properties index f80b2a5..176f33b 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages_de.properties +++ b/wizard-client/wizard-client-libgdx/core/src/main/resources/i18n/messages_de.properties @@ -226,4 +226,6 @@ game.overlay.play_colored_card.prompt=Wähle eine Farbe game.overlay.play_colored_card.cancel=Abbrechen game.overlay.play_changeling.prompt=Möchtest du den Gestaltwandler als Zauberer oder als Narr spielen? -game.overlay.play_changeling.cancel=Abbrechen \ No newline at end of file +game.overlay.play_changeling.cancel=Abbrechen + +game.overlay.scores.return_to_session=Fertig \ No newline at end of file