diff --git a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/WizardGame.java b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/WizardGame.java index 16aa5a4..a62ea67 100644 --- a/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/WizardGame.java +++ b/wizard-client/wizard-client-libgdx/core/src/main/java/eu/jonahbauer/wizard/client/libgdx/WizardGame.java @@ -7,16 +7,13 @@ import com.badlogic.gdx.Input; import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.utils.I18NBundle; import eu.jonahbauer.wizard.client.libgdx.screens.MainMenuScreen; import eu.jonahbauer.wizard.client.libgdx.util.SavedData; import eu.jonahbauer.wizard.client.libgdx.util.WizardAssetManager; import lombok.Getter; -import java.util.Locale; - public class WizardGame extends Game { - public static final boolean DEBUG = true; + public static final boolean DEBUG = false; public static final int WIDTH = 1920; public static final int HEIGHT = 1080; 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 d37493c..9fdd9c3 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 @@ -111,11 +111,22 @@ public class GameScreen extends WizardScreen { dimBehind.setColor(0, 0, 0, 0.5f); dimBehind.setVisible(false); dimBehind.setTouchable(Touchable.enabled); + dimBehind.setName("dim_behind"); + + contentRoot = new Group(); + contentRoot.setName("content_root"); + var contentRootWrapper = new Container<>(contentRoot); + contentRootWrapper.setSize(WizardGame.WIDTH, WizardGame.HEIGHT); + contentRootWrapper.fill().clip(); + contentRootWrapper.setName("content_root_wrapper"); + + overlayRoot = new Group(); + overlayRoot.setName("overlay_root"); stage.addActor(dimBehind); stage.addActor(createBackground()); - stage.addActor(contentRoot = new Group()); - stage.addActor(overlayRoot = new Group()); + stage.addActor(contentRootWrapper); + stage.addActor(overlayRoot); stage.addActor(menu = createMenu()); labelStyleDefault = skin.get(Label.LabelStyle.class); @@ -135,6 +146,7 @@ public class GameScreen extends WizardScreen { messageStack.setPosition(360, 85 + CardActor.PREF_HEIGHT); messageStack.setSize(1200, 0); messageStack.setTouchable(Touchable.disabled); + messageStack.setName("message_stack"); padOfTruth = new PadOfTruth(skin, new TextureRegionDrawable(atlas.findRegion(GameAtlas.PAD_OF_TRUTH))); padOfTruth.setPosition(PAD_OF_TRUTH_POSITION.x, PAD_OF_TRUTH_POSITION.y); @@ -193,6 +205,7 @@ public class GameScreen extends WizardScreen { (WizardGame.HEIGHT - title.getHeight()) / 2 ); group.addActor(title); + group.setName("background"); return group; } @@ -264,6 +277,7 @@ public class GameScreen extends WizardScreen { menu.add(returnToMenu).row(); menu.setVisible(false); + menu.setName("menu"); return menu; } @@ -398,28 +412,34 @@ public class GameScreen extends WizardScreen { public void finishTrick(@NotNull UUID player) { var seat = seats.getOrDefault(player, Seat.FALLBACK); - var action = parallel(); + var animation = parallel(); + var group = new Group(); execute(sequence( - run(() -> cardStack.removeAll().forEach(card -> { - var angle = (card.getRotation() % 360 + 360) % 360; - var rotation = rotateTo(angle < 90 || angle > 270 ? 0 : 180, AnimationTimings.STACK_FINISH_ROTATE); - rotation.setUseShortestDirection(true); - - action.addAction(sequence( - targeting(card, changeParent(stage.getRoot())), - parallel( - targeting(card, rotation), - targeting(card, moveTo( - seat.getFrontX() - card.getWidth() / 2, - seat.getFrontY() - card.getHeight() / 2, - AnimationTimings.STACK_FINISH_MOVE - )) - ), - targeting(card, alpha(0, AnimationTimings.STACK_FINISH_FADE)), - removeActor(card) - )); - })), - action + run(() -> { + contentRoot.addActorAfter(cardStack, group); + + cardStack.removeAll().forEach(card -> { + var angle = (card.getRotation() % 360 + 360) % 360; + var rotation = rotateTo(angle < 90 || angle > 270 ? 0 : 180, AnimationTimings.STACK_FINISH_ROTATE); + rotation.setUseShortestDirection(true); + + + animation.addAction(sequence( + targeting(card, changeParent(group)), + parallel( + targeting(card, rotation), + targeting(card, moveTo( + seat.getFrontX() - card.getWidth() / 2, + seat.getFrontY() - card.getHeight() / 2, + AnimationTimings.STACK_FINISH_MOVE + )) + ), + targeting(card, alpha(0, AnimationTimings.STACK_FINISH_FADE)) + )); + }); + }), + animation, + removeActor(group) )); } @@ -814,10 +834,13 @@ public class GameScreen extends WizardScreen { private void initTrumpCards() { if (trumpCardActor == null) { trumpCardActor = new CardActor(Card.HIDDEN, atlas); + trumpCardActor.setName("trump_card"); } if (trumpSuitActor == null) { trumpSuitActor = new CardActor(Card.HIDDEN, atlas); + trumpSuitActor.setOrigin(0, 0); + trumpSuitActor.setName("trump_suit"); } } @@ -825,11 +848,11 @@ public class GameScreen extends WizardScreen { public enum Seat { FALLBACK(WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT * 0.5f, 0, 0, 0, WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT * 0.5f), BOTTOM(WizardGame.WIDTH * 0.5f, 0, 0, 0, Align.bottom, WizardGame.WIDTH * 0.5f, 300), - LEFT(0, WizardGame.HEIGHT * 0.5f, 50, WizardGame.HEIGHT * 0.5f + 110f, Align.bottomLeft, 117.5f, WizardGame.HEIGHT * 0.5f), - TOP_LEFT(WizardGame.WIDTH * 0.25f, WizardGame.HEIGHT, WizardGame.WIDTH * 0.25f, WizardGame.HEIGHT - 50, Align.top, WizardGame.WIDTH * 0.25f, WizardGame.HEIGHT - 200), - TOP(WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT, WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT - 50, Align.top, WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT - 200), - TOP_RIGHT(WizardGame.WIDTH * 0.75f, WizardGame.HEIGHT, WizardGame.WIDTH * 0.75f, WizardGame.HEIGHT - 50, Align.top, WizardGame.WIDTH * 0.75f, WizardGame.HEIGHT - 200), - RIGHT(WizardGame.WIDTH, WizardGame.HEIGHT * 0.5f, WizardGame.WIDTH - 50, WizardGame.HEIGHT * 0.5f + 110f, Align.bottomRight, WizardGame.WIDTH - 117.5f, WizardGame.HEIGHT * 0.5f); + LEFT(-324, WizardGame.HEIGHT * 0.5f, 50, WizardGame.HEIGHT * 0.5f + 110f, Align.bottomLeft, 117.5f, WizardGame.HEIGHT * 0.5f), + TOP_LEFT(WizardGame.WIDTH * 0.25f - 144, WizardGame.HEIGHT + 289, WizardGame.WIDTH * 0.25f, WizardGame.HEIGHT - 50, Align.top, WizardGame.WIDTH * 0.25f, WizardGame.HEIGHT - 200), + TOP(WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT + 324, WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT - 50, Align.top, WizardGame.WIDTH * 0.5f, WizardGame.HEIGHT - 200), + TOP_RIGHT(WizardGame.WIDTH * 0.75f + 144, WizardGame.HEIGHT + 289, WizardGame.WIDTH * 0.75f, WizardGame.HEIGHT - 50, Align.top, WizardGame.WIDTH * 0.75f, WizardGame.HEIGHT - 200), + RIGHT(WizardGame.WIDTH + 324, WizardGame.HEIGHT * 0.5f, WizardGame.WIDTH - 50, WizardGame.HEIGHT * 0.5f + 110f, Align.bottomRight, WizardGame.WIDTH - 117.5f, WizardGame.HEIGHT * 0.5f); // position of the hand, should be offscreen private final float handX; 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 29953fb..0aeec35 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 @@ -18,7 +18,7 @@ menu.lobby.session_configuration.label=Configuration menu.create_game.player_name.label=Player Name menu.create_game.session_name.label=Session Name menu.create_game.session_name.default=%s's Session -menu.create_game.session_timeout.label=Timeout (s) +menu.create_game.session_timeout.label=Time Limit (s) menu.create_game.session_configuration.label=Configuration menu.create_game.back=Back menu.create_game.create=Create 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 68574f4..1700930 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 @@ -18,7 +18,7 @@ menu.lobby.session_configuration.label=Spielvariante menu.create_game.player_name.label=Spielername menu.create_game.session_name.label=Session Name menu.create_game.session_name.default=%s's Session -menu.create_game.session_timeout.label=Timeout (s) +menu.create_game.session_timeout.label=Bedenkzeit (s) menu.create_game.session_configuration.label=Spielvariante menu.create_game.back=Zurück menu.create_game.create=Erstellen diff --git a/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Lobby.java b/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Lobby.java index ea1dc97..6423853 100644 --- a/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Lobby.java +++ b/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Lobby.java @@ -13,7 +13,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; public class Lobby { @Language("RegExp") - private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_ ]{1,20}"; + private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}"; private static final Lobby INSTANCE = new Lobby(); public static Lobby getInstance() { return INSTANCE; diff --git a/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Session.java b/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Session.java index 41bf777..809fd77 100644 --- a/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Session.java +++ b/wizard-server/src/main/java/eu/jonahbauer/wizard/server/Session.java @@ -1,8 +1,11 @@ package eu.jonahbauer.wizard.server; +import eu.jonahbauer.wizard.common.messages.client.InteractionMessage; import eu.jonahbauer.wizard.common.messages.data.PlayerData; import eu.jonahbauer.wizard.common.messages.data.SessionData; import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage; +import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; +import eu.jonahbauer.wizard.common.messages.player.ContinueMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.server.*; import eu.jonahbauer.wizard.common.model.Configuration; @@ -28,7 +31,7 @@ import java.util.concurrent.ThreadLocalRandom; @EqualsAndHashCode(of = "uuid") public class Session implements Observer { @Language("RegExp") - private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_ ]{1,20}"; + private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}"; protected static final int MIN_PLAYERS = 3; protected static final int MAX_PLAYERS = 6; @@ -240,6 +243,7 @@ public class Session implements Observer { var gameMessage = new GameMessage(message); notifyPlayers(gameMessage); messages.add(Pair.of(null, gameMessage)); + autoSync(null, message); } @Override @@ -247,6 +251,23 @@ public class Session implements Observer { var gameMessage = new GameMessage(message); players.get(player).send(gameMessage); messages.add(Pair.of(player, gameMessage)); + autoSync(player, message); + } + + /** + * Automatically sends {@link ContinueMessage}s for disconnected players. + */ + private void autoSync(UUID player, ObserverMessage observerMessage) { + if (observerMessage instanceof UserInputMessage userInput) { + if (userInput.getAction() == UserInputMessage.Action.SYNC) { + for (var p : (player != null ? List.of(player) : players.keySet())) { + var sessionPlayer = players.get(p); + if (sessionPlayer != null && !sessionPlayer.isConnected()) { + game.onMessage(p, new ContinueMessage()); + } + } + } + } } @Data