|
|
|
@ -22,22 +22,17 @@ import eu.jonahbauer.wizard.client.libgdx.actors.game.CardsGroup;
|
|
|
|
|
import eu.jonahbauer.wizard.client.libgdx.actors.game.PadOfTruth;
|
|
|
|
|
import eu.jonahbauer.wizard.client.libgdx.actors.game.overlay.*;
|
|
|
|
|
import eu.jonahbauer.wizard.client.libgdx.state.Game;
|
|
|
|
|
import eu.jonahbauer.wizard.client.libgdx.util.Triple;
|
|
|
|
|
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
|
|
|
|
|
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
|
|
|
|
import eu.jonahbauer.wizard.common.messages.player.ContinueMessage;
|
|
|
|
|
import eu.jonahbauer.wizard.common.messages.player.JuggleMessage;
|
|
|
|
|
import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage;
|
|
|
|
|
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
|
|
|
|
import eu.jonahbauer.wizard.common.model.Card;
|
|
|
|
|
import lombok.Getter;
|
|
|
|
|
import org.jetbrains.annotations.Nls;
|
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
|
|
|
|
import static eu.jonahbauer.wizard.client.libgdx.actions.MyActions.*;
|
|
|
|
|
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.*;
|
|
|
|
|
|
|
|
|
|
public class GameScreen extends MenuScreen {
|
|
|
|
|
@Getter
|
|
|
|
@ -46,11 +41,9 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
private Label.LabelStyle labelStyleDefault;
|
|
|
|
|
private Label.LabelStyle labelStyleActive;
|
|
|
|
|
|
|
|
|
|
private final Game state;
|
|
|
|
|
|
|
|
|
|
private final List<UUID> players;
|
|
|
|
|
|
|
|
|
|
private Triple<UUID, UserInputMessage.Action, Long> activePlayer;
|
|
|
|
|
private final UUID self;
|
|
|
|
|
private final LinkedHashMap<UUID, String> players;
|
|
|
|
|
private final List<UUID> orderedPlayers;
|
|
|
|
|
|
|
|
|
|
private CardsGroup handCards;
|
|
|
|
|
private CardStack cardStack;
|
|
|
|
@ -70,18 +63,16 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
private final Map<UUID, Seat> seats = new HashMap<>();
|
|
|
|
|
private final Map<UUID, Label> nameLabels = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
private final AtomicBoolean sending = new AtomicBoolean();
|
|
|
|
|
private final AtomicBoolean pendingSync = new AtomicBoolean();
|
|
|
|
|
|
|
|
|
|
private boolean juggling;
|
|
|
|
|
private Card juggledCard;
|
|
|
|
|
|
|
|
|
|
public GameScreen(WizardGame game) {
|
|
|
|
|
public GameScreen(WizardGame game, @NotNull UUID self, @NotNull LinkedHashMap<UUID, String> players) {
|
|
|
|
|
super(game);
|
|
|
|
|
this.state = (Game) game.getClient().getState();
|
|
|
|
|
this.players = new ArrayList<>(state.getPlayers().keySet());
|
|
|
|
|
this.self = self;
|
|
|
|
|
this.players = players;
|
|
|
|
|
this.orderedPlayers = players.keySet().stream().toList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//<editor-fold desc="Layout">
|
|
|
|
|
@Override
|
|
|
|
|
public void show() {
|
|
|
|
|
super.show();
|
|
|
|
@ -92,10 +83,9 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
labelStyleActive.fontColor = Color.RED;
|
|
|
|
|
|
|
|
|
|
seat();
|
|
|
|
|
prepareLabels();
|
|
|
|
|
|
|
|
|
|
handCards = new CardsGroup(Collections.emptyList(), atlas);
|
|
|
|
|
handCards.setOnClickListener(this::onCardClicked);
|
|
|
|
|
handCards.setOnClickListener(actor -> onCardClicked(actor.getCard()));
|
|
|
|
|
var container = new Container<>(handCards);
|
|
|
|
|
container.setPosition(360, 75);
|
|
|
|
|
container.setSize(1200, CardActor.PREF_HEIGHT);
|
|
|
|
@ -107,13 +97,13 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
messages.setTouchable(Touchable.disabled);
|
|
|
|
|
|
|
|
|
|
padOfTruth = new PadOfTruth(game.data.skin, new TextureRegionDrawable(atlas.findRegion(GameAtlas.PAD_OF_TRUTH)));
|
|
|
|
|
padOfTruth.setPosition(1910 - 636f, 10);
|
|
|
|
|
padOfTruth.setOrigin(636f, 0);
|
|
|
|
|
padOfTruth.setPosition(WizardGame.WIDTH - 10 - PadOfTruth.EXTENDED_WIDTH, 10);
|
|
|
|
|
padOfTruth.setOrigin(PadOfTruth.EXTENDED_WIDTH, 0);
|
|
|
|
|
|
|
|
|
|
cardStack = new CardStack();
|
|
|
|
|
cardStack.setHoverBounds(0.5f * WizardGame.WIDTH - 50, 0.5f * WizardGame.HEIGHT - 50, 100, 100);
|
|
|
|
|
|
|
|
|
|
setNames();
|
|
|
|
|
addLabels();
|
|
|
|
|
|
|
|
|
|
Gdx.input.setInputProcessor(game.data.stage);
|
|
|
|
|
game.data.stage.addActor(container);
|
|
|
|
@ -122,14 +112,53 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
game.data.stage.addActor(messages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void seat() {
|
|
|
|
|
var count = players.size();
|
|
|
|
|
Seat[] seats = switch (count) {
|
|
|
|
|
case 3 -> new Seat[] {Seat.BOTTOM, Seat.TOP_LEFT, Seat.TOP_RIGHT};
|
|
|
|
|
case 4 -> new Seat[] {Seat.BOTTOM, Seat.LEFT, Seat.TOP, Seat.RIGHT};
|
|
|
|
|
case 5 -> new Seat[] {Seat.BOTTOM, Seat.LEFT, Seat.TOP_LEFT, Seat.TOP_RIGHT, Seat.RIGHT};
|
|
|
|
|
case 6 -> new Seat[] {Seat.BOTTOM, Seat.LEFT, Seat.TOP_LEFT, Seat.TOP, Seat.TOP_RIGHT, Seat.RIGHT};
|
|
|
|
|
default -> throw new AssertionError();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int index = orderedPlayers.indexOf(self);
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
var player = orderedPlayers.get((index + i) % count);
|
|
|
|
|
var seat = seats[i];
|
|
|
|
|
this.seats.put(player, seat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addLabels() {
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (var entry : players.entrySet()) {
|
|
|
|
|
UUID uuid = entry.getKey();
|
|
|
|
|
String name = entry.getValue();
|
|
|
|
|
|
|
|
|
|
padOfTruth.setName(i++, name);
|
|
|
|
|
|
|
|
|
|
if (isSelf(uuid)) continue;
|
|
|
|
|
var label = new Label("", game.data.skin);
|
|
|
|
|
var seat = seats.get(uuid);
|
|
|
|
|
label.setX(seat.getLabelX());
|
|
|
|
|
label.setY(seat.getLabelY());
|
|
|
|
|
label.setAlignment(seat.getLabelAlign());
|
|
|
|
|
label.setText(name);
|
|
|
|
|
nameLabels.put(uuid, label);
|
|
|
|
|
game.data.stage.addActor(label);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//</editor-fold>
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void renderBackground(float delta) {
|
|
|
|
|
float scale = Math.max(
|
|
|
|
|
game.data.extendViewport.getWorldWidth() / WizardGame.WIDTH,
|
|
|
|
|
game.data.extendViewport.getWorldHeight() / WizardGame.HEIGHT
|
|
|
|
|
);
|
|
|
|
|
game.batch.setColor(1, 1, 1, 0.25f);
|
|
|
|
|
game.batch.draw(game.data.background, 0,0, scale * WizardGame.WIDTH, scale * WizardGame.HEIGHT);
|
|
|
|
|
game.batch.setColor(1, 1, 1, 0.25f);
|
|
|
|
|
game.batch.draw(
|
|
|
|
|
game.data.title,
|
|
|
|
|
(game.data.extendViewport.getWorldWidth() - game.data.title.getRegionWidth() * 0.75f) / 2f,
|
|
|
|
@ -158,7 +187,7 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
game.data.stage.addAction(currentAction);
|
|
|
|
|
}
|
|
|
|
|
} else if (pendingSync.getAndSet(false)) {
|
|
|
|
|
send(new ContinueMessage());
|
|
|
|
|
game.getClient().execute(Game.class, Game::sync);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -170,90 +199,25 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
pendingActions.add(run(runnable));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void seat() {
|
|
|
|
|
var count = players.size();
|
|
|
|
|
Seat[] seats = switch (count) {
|
|
|
|
|
case 3 -> new Seat[] {Seat.TOP_LEFT, Seat.TOP_RIGHT};
|
|
|
|
|
case 4 -> new Seat[] {Seat.LEFT, Seat.TOP, Seat.RIGHT};
|
|
|
|
|
case 5 -> new Seat[] {Seat.LEFT, Seat.TOP_LEFT, Seat.TOP_RIGHT, Seat.RIGHT};
|
|
|
|
|
case 6 -> new Seat[] {Seat.LEFT, Seat.TOP_LEFT, Seat.TOP, Seat.TOP_RIGHT, Seat.RIGHT};
|
|
|
|
|
default -> throw new AssertionError();
|
|
|
|
|
};
|
|
|
|
|
int index = players.indexOf(state.getSelf());
|
|
|
|
|
for (int i = 1; i < count; i++) {
|
|
|
|
|
var player = players.get((index + i) % count);
|
|
|
|
|
var seat = seats[i - 1];
|
|
|
|
|
this.seats.put(player, seat);
|
|
|
|
|
}
|
|
|
|
|
this.seats.put(state.getSelf(), Seat.BOTTOM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void prepareLabels() {
|
|
|
|
|
for (UUID player : players) {
|
|
|
|
|
if (isSelf(player)) continue;
|
|
|
|
|
var label = new Label("", game.data.skin);
|
|
|
|
|
var seat = seats.get(player);
|
|
|
|
|
label.setX(seat.getLabelX());
|
|
|
|
|
label.setY(seat.getLabelY());
|
|
|
|
|
label.setAlignment(seat.getLabelAlign());
|
|
|
|
|
this.nameLabels.put(player, label);
|
|
|
|
|
game.data.stage.addActor(label);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setNames() {
|
|
|
|
|
for (int i = 0; i < players.size(); i++) {
|
|
|
|
|
var player = players.get(i);
|
|
|
|
|
var name = state.getPlayers().get(player);
|
|
|
|
|
padOfTruth.setName(i, name);
|
|
|
|
|
if (!isSelf(player)) {
|
|
|
|
|
nameLabels.get(player).setText(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private boolean isSelf(UUID uuid) {
|
|
|
|
|
return self.equals(uuid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onCardClicked(CardActor actor) {
|
|
|
|
|
var card = actor.getCard();
|
|
|
|
|
|
|
|
|
|
if (checkAction(PLAY_CARD)) {
|
|
|
|
|
var timeout = activePlayer.third();
|
|
|
|
|
if (card == Card.CLOUD) {
|
|
|
|
|
execute(new PlayColoredCardOverlay(this,
|
|
|
|
|
timeout,
|
|
|
|
|
Card.CLOUD,
|
|
|
|
|
Card.CLOUD_RED,
|
|
|
|
|
Card.CLOUD_GREEN,
|
|
|
|
|
Card.CLOUD_BLUE,
|
|
|
|
|
Card.CLOUD_YELLOW
|
|
|
|
|
));
|
|
|
|
|
} else if (card == Card.JUGGLER) {
|
|
|
|
|
execute(new PlayColoredCardOverlay(this,
|
|
|
|
|
timeout,
|
|
|
|
|
Card.JUGGLER,
|
|
|
|
|
Card.JUGGLER_RED,
|
|
|
|
|
Card.JUGGLER_GREEN,
|
|
|
|
|
Card.JUGGLER_BLUE,
|
|
|
|
|
Card.JUGGLER_YELLOW
|
|
|
|
|
));
|
|
|
|
|
} else {
|
|
|
|
|
send(new PlayCardMessage(card));
|
|
|
|
|
}
|
|
|
|
|
} else if (checkAction(JUGGLE_CARD)) {
|
|
|
|
|
juggledCard = card;
|
|
|
|
|
send(new JuggleMessage(card));
|
|
|
|
|
} else {
|
|
|
|
|
addMessage("You cannot do that right now.");
|
|
|
|
|
}
|
|
|
|
|
public void onCardClicked(Card card) {
|
|
|
|
|
game.getClient().execute(Game.class, (s, c) -> s.onCardClicked(c, card));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean checkAction(UserInputMessage.Action action) {
|
|
|
|
|
return activePlayer != null && (activePlayer.first() == null || isSelf(activePlayer.first())) && activePlayer.second() == action;
|
|
|
|
|
public void onSuitClicked(Card.Suit suit) {
|
|
|
|
|
game.getClient().execute(Game.class, (s, c) -> s.onSuitClicked(c, suit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isSelf(UUID uuid) {
|
|
|
|
|
return state.getSelf().equals(uuid);
|
|
|
|
|
public void onPredictionMade(int prediction) {
|
|
|
|
|
game.getClient().execute(Game.class, (s, c) -> s.onPredictionMade(c, prediction));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resets all round-scoped and shows an {@linkplain StartRoundOverlay overlay}.
|
|
|
|
|
*/
|
|
|
|
|
public void startRound(int round) {
|
|
|
|
|
execute(parallel(
|
|
|
|
|
run(() -> {
|
|
|
|
@ -277,24 +241,8 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
execute(() -> cardStack.clearChildren());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Indicates that the next call to {@link #setHand(UUID, List)} should be animated.
|
|
|
|
|
*/
|
|
|
|
|
public void setJuggling(boolean juggling) {
|
|
|
|
|
execute(() -> {
|
|
|
|
|
this.juggling = juggling;
|
|
|
|
|
if (juggling) {
|
|
|
|
|
handCards.setSelectMode(CardsGroup.SelectMode.SINGLE);
|
|
|
|
|
} else {
|
|
|
|
|
handCards.setSelectMode(CardsGroup.SelectMode.NONE);
|
|
|
|
|
juggledCard = null;
|
|
|
|
|
handCards.setSelected(null);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void finishTrick(UUID player, List<Card> cards) {
|
|
|
|
|
var seat = seats.get(player);
|
|
|
|
|
public void finishTrick(UUID player) {
|
|
|
|
|
var seat = seats.getOrDefault(player, Seat.FALLBACK);
|
|
|
|
|
|
|
|
|
|
var action = parallel();
|
|
|
|
|
execute(sequence(
|
|
|
|
@ -321,15 +269,17 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setHand(UUID player, List<Card> cards) {
|
|
|
|
|
/**
|
|
|
|
|
* Updates the given players hand cards.
|
|
|
|
|
*/
|
|
|
|
|
public void setHand(UUID player, List<Card> cards, boolean juggle) {
|
|
|
|
|
if (isSelf(player)) {
|
|
|
|
|
var sequence = sequence();
|
|
|
|
|
sequence.addAction(run(() -> {
|
|
|
|
|
var changes = handCards.update(cards);
|
|
|
|
|
|
|
|
|
|
// animate card changes
|
|
|
|
|
if (juggling) {
|
|
|
|
|
setJuggling(false);
|
|
|
|
|
if (juggle) {
|
|
|
|
|
sequence.addAction(animateJuggle(changes.first(), changes.second()));
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
@ -337,62 +287,39 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setTrump(Card trumpCard, Card.Suit trumpSuit) {
|
|
|
|
|
if (trumpCardActor == null) {
|
|
|
|
|
trumpCardActor = new CardActor(Card.HIDDEN, atlas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trumpSuitActor == null) {
|
|
|
|
|
trumpSuitActor = new CardActor(Card.HIDDEN, atlas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String player = null;
|
|
|
|
|
if (activePlayer != null && activePlayer.second() == PICK_TRUMP) {
|
|
|
|
|
player = state.getPlayers().get(activePlayer.first());
|
|
|
|
|
clearActivePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
execute(new TrumpOverlay(this, player, trumpCard, trumpSuit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void addPrediction(int round, UUID player, int prediction) {
|
|
|
|
|
boolean changed = false;
|
|
|
|
|
|
|
|
|
|
if (activePlayer != null && activePlayer.first().equals(player) && (activePlayer.second() == CHANGE_PREDICTION || activePlayer.second() == MAKE_PREDICTION)) {
|
|
|
|
|
changed = activePlayer.second() == CHANGE_PREDICTION;
|
|
|
|
|
clearActivePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds the prediction for the given round and player to the {@linkplain #padOfTruth pad of truth} and
|
|
|
|
|
* shows a corresponding message.
|
|
|
|
|
*/
|
|
|
|
|
public void addPrediction(int round, UUID player, int prediction, boolean changed) {
|
|
|
|
|
if (isSelf(player)) {
|
|
|
|
|
addMessage(game.messages.format("game.action." + (changed ? "change" : "make") + "_prediction.self", prediction));
|
|
|
|
|
} else {
|
|
|
|
|
var name = state.getPlayers().get(player);
|
|
|
|
|
var name = players.get(player);
|
|
|
|
|
addMessage(game.messages.format("game.action." + (changed ? "change" : "make") + "_prediction.other", name, prediction));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
execute(() -> padOfTruth.setPrediction(players.indexOf(player), round, prediction));
|
|
|
|
|
execute(() -> padOfTruth.setPrediction(orderedPlayers.indexOf(player), round, prediction));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes the card from the players hand and puts it into the {@linkplain #cardStack stack}.
|
|
|
|
|
*/
|
|
|
|
|
public void playCard(UUID player, Card card) {
|
|
|
|
|
if (activePlayer != null && activePlayer.first().equals(player) && activePlayer.second() == PLAY_CARD) {
|
|
|
|
|
clearActivePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isSelf(player)) {
|
|
|
|
|
addMessage(game.messages.get("game.action.play_card.self"));
|
|
|
|
|
} else {
|
|
|
|
|
var name = state.getPlayers().get(player);
|
|
|
|
|
var name = players.get(player);
|
|
|
|
|
addMessage(game.messages.format("game.action.play_card.other", name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Seat seat = seats.get(player);
|
|
|
|
|
Seat seat = seats.getOrDefault(player, Seat.FALLBACK);
|
|
|
|
|
|
|
|
|
|
var sequence = sequence();
|
|
|
|
|
sequence.addAction(run(() -> {
|
|
|
|
|
CardActor actor = null;
|
|
|
|
|
if (isSelf(player)) {
|
|
|
|
|
actor = handCards.remove(card);
|
|
|
|
|
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (actor == null) {
|
|
|
|
@ -406,55 +333,35 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
execute(sequence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds the scores for a round to the corresponding row of the {@linkplain #padOfTruth pad of truth}.
|
|
|
|
|
*/
|
|
|
|
|
public void addScores(int round, Map<UUID, Integer> scores) {
|
|
|
|
|
execute(() -> {
|
|
|
|
|
for (int i = 0; i < players.size(); i++) {
|
|
|
|
|
UUID player = players.get(i);
|
|
|
|
|
for (int i = 0; i < orderedPlayers.size(); i++) {
|
|
|
|
|
UUID player = orderedPlayers.get(i);
|
|
|
|
|
padOfTruth.setScore(i, round, scores.get(player));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes all visual changes done by {@link #setActivePlayer(UUID, UserInputMessage.Action, long)}.
|
|
|
|
|
*/
|
|
|
|
|
public void clearActivePlayer() {
|
|
|
|
|
setActivePlayer(null, null, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Highlights the given players label and sets the {@linkplain #setPersistentMessage(String) persistent message}
|
|
|
|
|
* accordingly.
|
|
|
|
|
*/
|
|
|
|
|
public void setActivePlayer(UUID player, UserInputMessage.Action action, long timeout) {
|
|
|
|
|
if (action == SYNC) throw new IllegalArgumentException();
|
|
|
|
|
|
|
|
|
|
// reset label color
|
|
|
|
|
if (activePlayer != null && nameLabels.containsKey(activePlayer.first())) {
|
|
|
|
|
var label = nameLabels.get(activePlayer.first());
|
|
|
|
|
execute(() -> label.setStyle(labelStyleDefault));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (player == null && action == null) {
|
|
|
|
|
activePlayer = null;
|
|
|
|
|
setPersistentMessage(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
activePlayer = Triple.of(player, action, timeout);
|
|
|
|
|
// set label color
|
|
|
|
|
if (nameLabels.containsKey(player)) {
|
|
|
|
|
var label = nameLabels.get(player);
|
|
|
|
|
execute(() -> label.setStyle(labelStyleActive));
|
|
|
|
|
}
|
|
|
|
|
execute(() -> nameLabels.forEach((p, l) -> l.setStyle(p.equals(player) ? labelStyleActive : labelStyleDefault)));
|
|
|
|
|
|
|
|
|
|
boolean isSelf = state.getSelf().equals(player);
|
|
|
|
|
if (isSelf || player == null) {
|
|
|
|
|
// show interface
|
|
|
|
|
if (isSelf(player) || player == null && action == null) {
|
|
|
|
|
setPersistentMessage(null);
|
|
|
|
|
switch (action) {
|
|
|
|
|
case PICK_TRUMP -> execute(new PickTrumpOverlay(this, timeout, false));
|
|
|
|
|
case MAKE_PREDICTION -> execute(new MakePredictionOverlay(this, timeout, state.getRound()));
|
|
|
|
|
case CHANGE_PREDICTION -> execute(new ChangePredictionOverlay(this, timeout, state.getRound(), state.getPredictions().get(state.getSelf())));
|
|
|
|
|
case PLAY_CARD -> setPersistentMessage(game.messages.get("game.message.play_card.self"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isSelf) {
|
|
|
|
|
// show message
|
|
|
|
|
} else {
|
|
|
|
|
var key = switch (action) {
|
|
|
|
|
case CHANGE_PREDICTION -> "game.message.change_prediction.";
|
|
|
|
|
case JUGGLE_CARD -> "game.message.juggle_card.";
|
|
|
|
@ -464,15 +371,141 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
default -> throw new AssertionError();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (player == null) {
|
|
|
|
|
if (player != null) {
|
|
|
|
|
setPersistentMessage(game.messages.format(key + "other", players.get(player)));
|
|
|
|
|
} else {
|
|
|
|
|
setPersistentMessage(game.messages.get(key + "all"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Visually highlights the given card on the players hand.
|
|
|
|
|
*/
|
|
|
|
|
public void setSelectedCard(@Nullable Card card) {
|
|
|
|
|
handCards.setSelected(card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Swaps the current {@linkplain #trumpCardActor trump card} (if present) with a {@linkplain Card#WEREWOLF werewolf}
|
|
|
|
|
* card on the given players hand.
|
|
|
|
|
*/
|
|
|
|
|
public void swapTrumpCard(UUID player) {
|
|
|
|
|
var seat = seats.getOrDefault(player, Seat.FALLBACK);
|
|
|
|
|
|
|
|
|
|
var sequence = sequence();
|
|
|
|
|
sequence.addAction(run(() -> {
|
|
|
|
|
if (trumpCardActor == null || !trumpCardActor.hasParent()) return;
|
|
|
|
|
|
|
|
|
|
if (isSelf(player)) {
|
|
|
|
|
var handCard = handCards.find(Card.WEREWOLF);
|
|
|
|
|
if (handCard == null) return;
|
|
|
|
|
|
|
|
|
|
handCard.setCard(trumpCardActor.getCard());
|
|
|
|
|
trumpCardActor.setCard(Card.WEREWOLF);
|
|
|
|
|
|
|
|
|
|
float localHandX = handCard.getX(), localHandY = handCard.getY();
|
|
|
|
|
float stageTrumpX = 10, stageTrumpY = 10;
|
|
|
|
|
float localTrumpX, localTrumpY;
|
|
|
|
|
float stageHandX, stageHandY;
|
|
|
|
|
|
|
|
|
|
var pos = new Vector2(handCard.getX(), handCard.getY());
|
|
|
|
|
handCards.localToStageCoordinates(pos);
|
|
|
|
|
stageHandX = pos.x;
|
|
|
|
|
stageHandY = pos.y;
|
|
|
|
|
|
|
|
|
|
pos.set(stageTrumpX, stageTrumpY);
|
|
|
|
|
handCards.stageToLocalCoordinates(pos);
|
|
|
|
|
localTrumpX = pos.x;
|
|
|
|
|
localTrumpY = pos.y;
|
|
|
|
|
|
|
|
|
|
trumpCardActor.setPosition(stageHandX, stageHandY);
|
|
|
|
|
trumpCardActor.setRotation(0);
|
|
|
|
|
|
|
|
|
|
handCard.setPosition(localTrumpX, localTrumpY);
|
|
|
|
|
handCard.setRotation(0);
|
|
|
|
|
|
|
|
|
|
sequence.addAction(parallel(
|
|
|
|
|
targeting(trumpCardActor, moveTo(stageTrumpX, stageTrumpY, AnimationTimings.WEREWOLF_SWAP)),
|
|
|
|
|
targeting(handCard, moveTo(localHandX, localHandY, AnimationTimings.WEREWOLF_SWAP))
|
|
|
|
|
));
|
|
|
|
|
} else {
|
|
|
|
|
var name = state.getPlayers().get(player);
|
|
|
|
|
setPersistentMessage(game.messages.format(key + "other", name));
|
|
|
|
|
var handCard = new CardActor(trumpCardActor.getCard(), atlas);
|
|
|
|
|
handCard.setPosition(10, 10);
|
|
|
|
|
trumpCardActor.setCard(Card.WEREWOLF);
|
|
|
|
|
game.data.stage.addActor(handCard);
|
|
|
|
|
|
|
|
|
|
sequence.addAction(seat.moveToHand(trumpCardActor, 0));
|
|
|
|
|
sequence.addAction(parallel(
|
|
|
|
|
targeting(trumpCardActor, moveTo(10,10, AnimationTimings.WEREWOLF_SWAP)),
|
|
|
|
|
targeting(trumpCardActor, rotateTo(0, AnimationTimings.WEREWOLF_SWAP)),
|
|
|
|
|
seat.moveToHand(handCard, AnimationTimings.WEREWOLF_SWAP)
|
|
|
|
|
));
|
|
|
|
|
sequence.addAction(removeActor(handCard));
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
execute(sequence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//<editor-fold desc="Overlays" defaultstate="collapsed">
|
|
|
|
|
public void showTrumpOverlay(UUID player, Card trumpCard, Card.Suit trumpSuit) {
|
|
|
|
|
if (trumpCardActor == null) {
|
|
|
|
|
trumpCardActor = new CardActor(Card.HIDDEN, atlas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trumpSuitActor == null) {
|
|
|
|
|
trumpSuitActor = new CardActor(Card.HIDDEN, atlas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
execute(new TrumpOverlay(this, players.get(player), trumpCard, trumpSuit));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void showColoredCardOverlay(Card card, long timeout) {
|
|
|
|
|
if (card == Card.JUGGLER) {
|
|
|
|
|
execute(new PlayColoredCardOverlay(this, timeout, card, Card.JUGGLER_RED, Card.JUGGLER_GREEN, Card.JUGGLER_BLUE, Card.JUGGLER_YELLOW));
|
|
|
|
|
} else if (card == Card.CLOUD) {
|
|
|
|
|
execute(new PlayColoredCardOverlay(this, timeout, card, Card.CLOUD_RED, Card.CLOUD_GREEN, Card.CLOUD_BLUE, Card.CLOUD_YELLOW));
|
|
|
|
|
} else {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InteractionOverlay showPickTrumpOverlay(long timeout, boolean allowNone) {
|
|
|
|
|
var overlay = new PickTrumpOverlay(this, timeout, allowNone);
|
|
|
|
|
execute(overlay);
|
|
|
|
|
return overlay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void showScoreOverlay() {
|
|
|
|
|
execute(sequence(
|
|
|
|
|
run(() -> padOfTruth.setEnabled(false)),
|
|
|
|
|
parallel(
|
|
|
|
|
targeting(padOfTruth, scaleTo(1, 1, AnimationTimings.OVERLAY_SHARED_ELEMENT)),
|
|
|
|
|
targeting(padOfTruth, moveTo((WizardGame.WIDTH - padOfTruth.getWidth()) / 2, (WizardGame.HEIGHT - padOfTruth.getHeight()) / 2, AnimationTimings.OVERLAY_SHARED_ELEMENT))
|
|
|
|
|
),
|
|
|
|
|
delay(AnimationTimings.OVERLAY_HOLD),
|
|
|
|
|
parallel(
|
|
|
|
|
targeting(padOfTruth, scaleTo(PadOfTruth.COLLAPSED_SCALE, PadOfTruth.COLLAPSED_SCALE, AnimationTimings.OVERLAY_SHARED_ELEMENT)),
|
|
|
|
|
targeting(padOfTruth, moveTo(WizardGame.WIDTH - 10 - PadOfTruth.EXTENDED_WIDTH, 10))
|
|
|
|
|
),
|
|
|
|
|
run(() -> padOfTruth.setEnabled(true))
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InteractionOverlay showMakePredictionOverlay(int round, long timeout) {
|
|
|
|
|
var overlay = new MakePredictionOverlay(this, timeout, round);
|
|
|
|
|
execute(overlay);
|
|
|
|
|
return overlay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public InteractionOverlay showChangePredictionOverlay(int round, int oldPrediction, long timeout) {
|
|
|
|
|
var overlay = new ChangePredictionOverlay(this, timeout, round, oldPrediction);
|
|
|
|
|
execute(overlay);
|
|
|
|
|
return overlay;
|
|
|
|
|
}
|
|
|
|
|
//</editor-fold>
|
|
|
|
|
|
|
|
|
|
//<editor-fold desc="Messages" defaultstate="collapsed">
|
|
|
|
|
public void addMessage(@Nls String text) {
|
|
|
|
|
addMessage(text, false);
|
|
|
|
|
}
|
|
|
|
@ -512,44 +545,33 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (text != null) {
|
|
|
|
|
persistentMessage = new Label(text, getData().skin);
|
|
|
|
|
persistentMessage = new Label(text, game.data.skin);
|
|
|
|
|
messages.addActor(persistentMessage);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void send(PlayerMessage message) {
|
|
|
|
|
if (!sending.getAndSet(true) || (message instanceof ContinueMessage)) {
|
|
|
|
|
game.getClient().send(new InteractionMessage(message));
|
|
|
|
|
} else {
|
|
|
|
|
addMessage("Please slow down.", true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//</editor-fold>
|
|
|
|
|
|
|
|
|
|
public void ready(boolean success) {
|
|
|
|
|
if (!pendingSync.get()) {
|
|
|
|
|
sending.set(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (success && currentAction instanceof Overlay overlay && overlay instanceof InteractionOverlay) {
|
|
|
|
|
overlay.finish();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (checkAction(JUGGLE_CARD) && juggledCard != null) {
|
|
|
|
|
handCards.setSelected(juggledCard);
|
|
|
|
|
juggledCard = null;
|
|
|
|
|
}
|
|
|
|
|
if (success) closeInteractionOverlay();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void timeout() {
|
|
|
|
|
addMessage(game.messages.get("game.message.timeout"));
|
|
|
|
|
ready(true);
|
|
|
|
|
closeInteractionOverlay();
|
|
|
|
|
clearActivePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void sync() {
|
|
|
|
|
pendingSync.set(true);
|
|
|
|
|
sending.set(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void closeInteractionOverlay() {
|
|
|
|
|
execute(() -> {
|
|
|
|
|
if (currentAction instanceof Overlay overlay && overlay instanceof InteractionOverlay) {
|
|
|
|
|
overlay.close();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -579,8 +601,8 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
|
|
|
|
|
var animation = parallel();
|
|
|
|
|
removed.forEach(actor -> {
|
|
|
|
|
getData().stage.addActor(actor);
|
|
|
|
|
animation.addAction(targeting(actor, left.moveToHand(AnimationTimings.JUGGLE)));
|
|
|
|
|
game.data.stage.addActor(actor);
|
|
|
|
|
animation.addAction(left.moveToHand(actor, AnimationTimings.JUGGLE));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
added.forEach(actor -> {
|
|
|
|
@ -600,7 +622,6 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
// apply start values
|
|
|
|
|
var startPos = new Vector2(right.getHandX(), right.getHandY());
|
|
|
|
|
handCards.stageToLocalCoordinates(startPos);
|
|
|
|
|
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
|
|
|
|
|
animation.addAction(targeting(actor, moveTo(startPos.x, startPos.y)));
|
|
|
|
|
animation.addAction(targeting(actor, rotateTo(right.getHandAngle())));
|
|
|
|
|
|
|
|
|
@ -617,6 +638,7 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
|
|
|
|
|
@Getter
|
|
|
|
|
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),
|
|
|
|
@ -655,14 +677,13 @@ public class GameScreen extends MenuScreen {
|
|
|
|
|
var actor = new CardActor(card, atlas);
|
|
|
|
|
actor.setPosition(getHandX() - actor.getWidth() / 2, getHandY() - actor.getHeight() / 2);
|
|
|
|
|
actor.setRotation(getHandAngle());
|
|
|
|
|
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
|
|
|
|
|
return actor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Action moveToHand(float duration) {
|
|
|
|
|
public Action moveToHand(CardActor actor, float duration) {
|
|
|
|
|
return parallel(
|
|
|
|
|
moveTo(handX, handY, duration),
|
|
|
|
|
rotateTo(handAngle, duration)
|
|
|
|
|
targeting(actor, moveTo(handX, handY, duration)),
|
|
|
|
|
targeting(actor, rotateTo(handAngle, duration))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|