Erste Grundlage für den Spielscreen (#17)

main
Johannes Hochwart 3 years ago
parent 9c64f0397a
commit 8847e70e26

@ -0,0 +1,35 @@
package eu.jonahbauer.wizard.client.libgdx.actors.game;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import eu.jonahbauer.wizard.client.libgdx.GameAtlas;
import eu.jonahbauer.wizard.common.model.Card;
import lombok.Getter;
@Getter
public class CardActor extends Actor {
private final Card card;
private final TextureRegion texture;
float offset;
float baseY;
public CardActor(Card card, TextureAtlas atlas) {
this.card = card;
this.texture = atlas.findRegion(switch ((int)(Math.random() * 4)) {
case 0 -> GameAtlas.CARDS_BLUE;
case 1 -> GameAtlas.CARDS_GREEN;
case 2 -> GameAtlas.CARDS_RED;
case 3 -> GameAtlas.CARDS_YELLOW;
default -> throw new AssertionError();
});
setWidth(100);
setHeight(100);
}
@Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture, getX(), getY() + getHeight() - 100, getWidth(), 100);
}
}

@ -0,0 +1,88 @@
package eu.jonahbauer.wizard.client.libgdx.actors.game;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup;
import eu.jonahbauer.wizard.common.model.Card;
import java.util.List;
public class CardsGroup extends HorizontalGroup {
private TextureAtlas atlas;
private List<CardActor> actors;
private int hash;
private CardActor target;
public CardsGroup(List<Card> cards, TextureAtlas atlas) {
this.atlas = atlas;
update(cards);
this.addListener(new InputListener() {
@Override
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
if (event.getTarget() instanceof CardActor card) {
CardsGroup.this.target = card;
}
}
@Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
if (event.getTarget() == CardsGroup.this.target) {
CardsGroup.this.target = null;
}
}
});
}
@Override
public float getMinWidth() {
return 0;
}
@Override
public void act(float delta) {
super.act(delta);
float speed = 10;
float offset = 100;
for (Actor child : getChildren()) {
if (child instanceof CardActor card) {
if (card == target) {
card.offset += speed * delta;
} else {
card.offset -= speed * delta;
}
card.offset = Math.max(0, Math.min(1, card.offset));
card.setHeight(100 + offset * card.offset);
}
}
}
@Override
public void layout() {
super.layout();
var space = getWidth();
for (Actor child : getChildren()) {
space -= child.getWidth();
if (child instanceof CardActor card) {
card.baseY = card.getY();
}
}
space(getChildren().size > 1 ? space / (getChildren().size - 1) : 0);
super.layout();
}
public void update(List<Card> cards) {
var hash = cards.hashCode();
if (this.hash != hash) {
this.hash = hash;
this.actors = cards.stream().map(card -> new CardActor(card, atlas)).toList();
this.clearChildren();
this.actors.forEach(this::addActor);
this.layout();
}
}
}

@ -0,0 +1,33 @@
package eu.jonahbauer.wizard.client.libgdx.actors.game;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.common.model.Card;
public class ChosenCardActor extends Actor {
private final Card card;
private final TextureRegion texture;
private final Action toCenter;
public ChosenCardActor(Card card, TextureRegion region) {
this.card = card;
setWidth(100);
setHeight(100);
texture = region;
toCenter = Actions.moveTo(WizardGame.WIDTH * 0.75f * 0.5f - 0.5f * getWidth(), WizardGame.HEIGHT * 0.35f, 0.5f);
this.addAction(toCenter);
}
@Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
public void action() {
this.addAction(toCenter);
}
}

@ -0,0 +1,96 @@
package eu.jonahbauer.wizard.client.libgdx.actors.game;
import com.badlogic.gdx.scenes.scene2d.ui.Cell;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import eu.jonahbauer.wizard.common.model.Card;
import lombok.AccessLevel;
import lombok.Setter;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class InfoTable extends Table {
private final UUID[] players;
private final Skin skin;
@Setter(AccessLevel.MODULE)
private int round = -1;
private int playercount; //optional
private Cell cells[][];
private static final int TABLELENGTH = 4;
private static final int PLAYERNAMES = 0;
private static final int PREDICTIONS = 1;
private static final int TRICKS = 2;
private static final int SCORES = 3;
private final String headers[] = {"Spieler", "Vorhersage", "Stiche", "Punktzahl"};
public InfoTable(Skin skin, UUID[] players) {
super(skin);
this.skin = skin;
this.players = players;
playercount = players.length;
cells = new Cell[TABLELENGTH][playercount];
createHeader();
//this.columnDefaults(1).width(5);
}
public void createHeader() {
for (int i = 0; i < headers.length; i++) {
Label header = new Label(headers[i], skin);
//header.setWidth(5);
//header.setEllipsis(true);
add(header).padRight(5);
}
row();
}
public void fill(Map<UUID, String> names, Map<UUID, Integer> predictions, Map<UUID, List<List<Card>>> tricks,
Map<Integer, Map<UUID, Integer>> scores) {
for(int i = 0; i < players.length; i++) {
//Name
cells[PLAYERNAMES][i] = add(names.get(players[i]));
((Label) cells[PLAYERNAMES][i].getActor()).setWidth(10);
((Label) cells[PLAYERNAMES][i].getActor()).setEllipsis(true);
//Prediction
cells[PREDICTIONS][i] = add(String.valueOf(predictions.get(players[i])));
((Label) cells[PLAYERNAMES][i].getActor()).setEllipsis(true);
//Tricks
cells[TRICKS][i] = add(String.valueOf(tricks.get(players[i]).size()));
((Label) cells[PLAYERNAMES][i].getActor()).setEllipsis(true);
//Scores
//TODO Rundencheck - Rundenupdate
cells[SCORES][i] = add(String.valueOf(scores.get(round).get(players[i])));
((Label) cells[PLAYERNAMES][i].getActor()).setEllipsis(true);
row();
}
}
public void updatePrediction(Map<UUID, Integer> predictions) {
for(int i = 0; i < players.length; i++) {
((Label) cells[PREDICTIONS][i].getActor()).setText(String.valueOf(predictions.get(players[i])));
}
}
public void updateTricks(Map<UUID, List<List<Card>>> tricks) {
for(int i = 0; i < players.length; i++) {
((Label) cells[TRICKS][i].getActor()).setText(String.valueOf(tricks.get(players[i]).size()));
}
}
public void updateScores(Map<Integer, Map<UUID, Integer>> scores) {
for(int i = 0; i < players.length; i++) {
((Label) cells[SCORES][i].getActor()).setText(String.valueOf(scores.get(round).get(players[i])));
}
}
//TODO - update/removePlayer
}

@ -0,0 +1,54 @@
package eu.jonahbauer.wizard.client.libgdx.actors.game;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import eu.jonahbauer.wizard.client.libgdx.GameAtlas;
import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.screens.GameScreen;
import eu.jonahbauer.wizard.common.model.Card;
public class PlayedCardActor extends Actor {
private final Card playedCard;
private final TextureRegion texture;
public PlayedCardActor(Card playedCard, TextureAtlas atlas) {
//TODO Texture zur Karte bestimmen
this.playedCard = playedCard;
this.texture = atlas.findRegion(GameAtlas.CARDS_RED);
setWidth(100);
setHeight(100);
}
@Override
public void setPosition(float x, float y) {
super.setPosition(x, y);
}
public void setPositionOne() {
setPosition(WizardGame.WIDTH * GameScreen.BACKGROUND_WIDTH_PORTION * 0.25f, WizardGame.HEIGHT * 0.5f - 0.5f * getHeight());
}
public void setPositionTwo() {
setPosition(WizardGame.WIDTH * GameScreen.BACKGROUND_WIDTH_PORTION * 0.75f, WizardGame.HEIGHT * 0.5f - 0.5f * getHeight());
}
public void setPositionThree() {
setPosition(WizardGame.WIDTH * GameScreen.BACKGROUND_WIDTH_PORTION * 0.25f, WizardGame.HEIGHT * 0.7f - 0.5f * getHeight());
}
public void setPositionFour() {
setPosition(2 * WizardGame.WIDTH * GameScreen.BACKGROUND_WIDTH_PORTION * 0.25f, WizardGame.HEIGHT * 0.75f - 0.5f * getHeight());
}
public void setPositionFive() {
setPosition(3 * WizardGame.WIDTH * GameScreen.BACKGROUND_WIDTH_PORTION * 0.25f, WizardGame.HEIGHT * 0.7f - 0.5f * getHeight());
}
@Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
}

@ -0,0 +1,27 @@
package eu.jonahbauer.wizard.client.libgdx.actors.game;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import eu.jonahbauer.wizard.client.libgdx.GameAtlas;
import eu.jonahbauer.wizard.common.model.Card;
public class TrumpCardActor extends Actor {
private final Card trumpCard;
private final TextureRegion texture;
public TrumpCardActor(Card trumpCard, TextureAtlas atlas) {
//TODO Texture zur Karte bestimmen
this.trumpCard = trumpCard;
this.texture = atlas.findRegion(GameAtlas.CARDS_RED);
setWidth(200);
setHeight(200);
}
@Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
}

@ -0,0 +1,339 @@
package eu.jonahbauer.wizard.client.libgdx.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Container;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.FitViewport;
import eu.jonahbauer.wizard.client.libgdx.GameAtlas;
import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.actors.game.*;
import eu.jonahbauer.wizard.client.libgdx.util.Pair;
import eu.jonahbauer.wizard.common.messages.observer.*;
import eu.jonahbauer.wizard.common.messages.server.GameMessage;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.common.model.Card;
import java.util.*;
public class GameScreen implements Screen {
private final WizardGame game;
private ExtendViewport extendViewport;
private FitViewport viewport;
private Stage stage;
private Skin skin;
private TextureAtlas atlas;
private TextureRegion background;
public static final float BACKGROUND_WIDTH_PORTION = 0.7f;
private final UUID[] players;
//wahrscheinlich eine Map<UUID, Integer> für Sitzpositionen einfügen
private final Map<UUID, Integer> seats = new HashMap<>();
private final UUID self;
private final UUID session;
private final Map<UUID, String> names;
private final Map<Integer, Map<UUID, Integer>> scores = new HashMap<>();
private int round = - 1;
private final Map<UUID, List<Card>> hands = new HashMap<>();
private final Map<UUID, Integer> predictions = new HashMap<>();
private final Map<UUID, List<List<Card>>> tricks = new HashMap<>();
private Card.Suit trumpSuit;
private Card trumpCard;
private Label[] playerNames;
private int trick;
private final List<Pair<UUID, Card>> stack = new ArrayList<>();
private Pair<UUID, UserInputMessage.Action> activePlayer;
private ChosenCardActor chosenCardActor;
ClickListener clickListener = new ClickListener() {
//TODO - Fehler bei Click auf Objekt während der Bewegung - Behebung durch Entfernung des Click-Listeners
@Override
public void clicked(InputEvent event, float x, float y) {
super.clicked(event, x, y);
var target = event.getTarget();
if(target instanceof CardActor cardTarget) {
chosenCardActor = new ChosenCardActor(cardTarget.getCard(), cardTarget.getTexture());
//TODO falsche Koordinaten
chosenCardActor.setPosition(cardTarget.getX(), cardTarget.getParent().getY());
stage.addActor(chosenCardActor);
target.remove();
//Todo Message - Server-Antwort abwarten - moveMethode in chosenCardActor aufrufen
}
}
};
public GameScreen(WizardGame game) {
this.game = game;
int playerCount = 6;
this.session = UUID.randomUUID();
this.players = new UUID[playerCount];
this.names = new HashMap<>();
HashMap<UUID, Integer> score = new HashMap();
for (int i = 0; i < playerCount; i++) {
players[i] = UUID.randomUUID();
names.put(players[i], "Player " + (i + 1));
predictions.put(players[i], i + 5);
score.put(players[i], i);
}
scores.put(round, score);
this.self = players[0];
ArrayList<List<Card>> trickList = new ArrayList<>();
ArrayList<Card> trick = new ArrayList();
trick.add(Card.BLUE_3);
trick.add(Card.YELLOW_1);
trick.add(Card.GREEN_1);
trick.add(Card.RED_5);
trickList.add(trick);
trickList.add(trick);
trickList.add(trick);
for(int i = 0; i < playerCount; i++) {
tricks.put(players[i], trickList);
var cards = new ArrayList<Card>(20);
for(int j = 0; j < 20; j++) {
cards.add(Arrays.stream(Card.values()).toList().get(j));
}
hands.put(players[i], cards);
}
//List<Pair<UUID, Card>> stack
stack.add(new Pair<>(players[0], Card.BLUE_11));
stack.add(new Pair<>(players[1], Card.RED_1));
stack.add(new Pair<>(players[2], Card.YELLOW_11));
stack.add(new Pair<>(players[3], Card.BLUE_10));
stack.add(new Pair<>(players[4], Card.BLUE_9));
stack.add(new Pair<>(players[5], Card.BLUE_8));
determineSeats(players);
}
@Override
public void show() {
viewport = new FitViewport(WizardGame.WIDTH, WizardGame.HEIGHT);
extendViewport = new ExtendViewport(WizardGame.WIDTH, WizardGame.HEIGHT);
stage = new Stage(viewport);
skin = new Skin(Gdx.files.internal("uiskin.json"));
atlas = new TextureAtlas(Gdx.files.internal(GameAtlas.$PATH));
background = atlas.findRegion(GameAtlas.BACKGROUND);
this.stage.addListener(clickListener);
var container = new Container<>(new CardsGroup(hands.get(players[0]), atlas).wrap(false).grow());
container.setPosition(100, 75);
container.setSize(1200, 200);
container.fillX();
InfoTable infoTable = new InfoTable(skin, players);
infoTable.fill(names, predictions, tricks, scores);
infoTable.setPosition(WizardGame.WIDTH * 0.85f, WizardGame.HEIGHT * 0.8f);
trumpCard = Card.BLUE_1;
TrumpCardActor trumpCardActor = new TrumpCardActor(trumpCard, atlas);
trumpCardActor.setPosition(WizardGame.WIDTH * 0.85f, WizardGame.HEIGHT * 0.1f);
this.stage.addListener(clickListener);
createLabels();
cardPlayed(stack);
Gdx.input.setInputProcessor(stage);
stage.addActor(container);
stage.addActor(infoTable);
stage.addActor(trumpCardActor);
stage.setDebugAll(true);
}
@Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//viewport.apply(true);
extendViewport.apply(true);
game.batch.setProjectionMatrix(extendViewport.getCamera().combined);
game.batch.begin();
float scale = Math.max(
extendViewport.getWorldWidth() / WizardGame.WIDTH,
extendViewport.getWorldHeight() / WizardGame.HEIGHT
);
game.batch.draw(background, 0,0, scale * WizardGame.WIDTH * BACKGROUND_WIDTH_PORTION, scale * WizardGame.HEIGHT);
game.batch.end();
stage.act(delta);
stage.draw();
}
@Override
public void resize(int width, int height) {
extendViewport.update(width, height);
viewport.update(width, height);
}
@Override
public void pause() {}
@Override
public void resume() {}
@Override
public void hide() {}
@Override
public void dispose() {
if (stage != null) stage.dispose();
if (atlas != null) atlas.dispose();
if (skin != null) skin.dispose();
}
private void createLabels() {
//TODO ggf. Labels für Vorhersage und Stichanzahl einbauen
playerNames = new Label[players.length];
int count = 0;
for(int i = 0; i < players.length; i++) {
playerNames[i] = new Label(names.get(players[i]), skin);
if(players[i] != self) {
switch (count) {
case 0 -> {
playerNames[i].setPosition(10, WizardGame.HEIGHT * 0.5f - 0.5f * playerNames[i].getHeight());
stage.addActor(playerNames[i]);
}
case 1 -> {
playerNames[i].setPosition(WizardGame.WIDTH * BACKGROUND_WIDTH_PORTION - playerNames[i].getWidth() - 10, WizardGame.HEIGHT * 0.5f - 0.5f * playerNames[i].getHeight());
stage.addActor(playerNames[i]);
}
case 2 -> {
playerNames[i].setPosition(WizardGame.WIDTH * BACKGROUND_WIDTH_PORTION / (players.length - 2) - playerNames[i].getWidth() * 0.5f, WizardGame.HEIGHT * 0.8f - 0.5f * playerNames[i].getHeight());
stage.addActor(playerNames[i]);
}
case 3 -> {
playerNames[i].setPosition(2 * WizardGame.WIDTH * BACKGROUND_WIDTH_PORTION / (players.length - 2) - playerNames[i].getWidth() * 0.5f, WizardGame.HEIGHT * 0.85f - 0.5f * playerNames[i].getHeight());
stage.addActor(playerNames[i]);
}
case 4 -> {
playerNames[i].setPosition(3 * WizardGame.WIDTH * BACKGROUND_WIDTH_PORTION / (players.length - 2) - playerNames[i].getWidth() * 0.5f, WizardGame.HEIGHT * 0.8f - 0.5f * playerNames[i].getHeight());
stage.addActor(playerNames[i]);
}
default -> throw new IllegalArgumentException();
}
count++;
}
else {
playerNames[i].setPosition(WizardGame.WIDTH * 0.35f - 0.5f * playerNames[i].getWidth(), WizardGame.HEIGHT * 0.05f);
stage.addActor(playerNames[i]);
}
}
}
private void cardPlayed(List<Pair<UUID, Card>> stack) {
while(stack.size() > 0) {
Pair<UUID, Card> pair = stack.get(stack.size() - 1);
if(pair.first() == self) {
//TODO Action
//chosenCardActor.action()
stack.remove(pair);
}
else {
for(UUID player : players) {
if(player == pair.first()) {
drawPlayedCard(seats.get(player), pair.second());
stack.remove(pair);
}
}
}
}
}
private void drawPlayedCard(int pos, Card card) {
PlayedCardActor playedCardActor = new PlayedCardActor(card, atlas);
switch (pos) {
case 1 -> playedCardActor.setPositionOne();
case 2 -> playedCardActor.setPositionTwo();
case 3 -> playedCardActor.setPositionThree();
case 4 -> playedCardActor.setPositionFour();
case 5 -> playedCardActor.setPositionFive();
}
stage.addActor(playedCardActor);
}
private void determineSeats(UUID[] players) {
for(int i = 0; i < players.length; i++) {
if(self == players[i]) {
seats.put(self, 0);
}
else {
seats.put(players[i], i);
}
}
}
public void onMessage(ServerMessage serverMessage) {
if (serverMessage instanceof GameMessage gameMessage) {
var observerMessage = gameMessage.getObserverMessage();
if (observerMessage instanceof CardMessage card) {
stack.add(Pair.of(card.getPlayer(), card.getCard()));
} else if (observerMessage instanceof TrickMessage trick) {
stack.clear();
tricks.computeIfAbsent(trick.getPlayer(), p -> new ArrayList<>()).add(trick.getCards());
} else if (observerMessage instanceof HandMessage hand) {
var cards = hands.computeIfAbsent(hand.getPlayer(), p -> new ArrayList<>());
cards.clear();
cards.addAll(hand.getHand());
} else if (observerMessage instanceof ScoreMessage score) {
var roundScores = scores.computeIfAbsent(round, r -> new HashMap<>());
roundScores.putAll(score.getPoints());
} else if (observerMessage instanceof TrumpMessage trump) {
trumpCard = trump.getCard();
trumpSuit = trump.getSuit();
//Todo update TrumpCardActor if trumpSuit != null and trumpCard != null
} else if (observerMessage instanceof PredictionMessage prediction) {
predictions.put(prediction.getPlayer(), prediction.getPrediction());
//TODO update table and label
} else if (observerMessage instanceof UserInputMessage userInput) {
activePlayer = Pair.of(userInput.getPlayer(), userInput.getAction());
if (self.equals(userInput.getPlayer())) {
// TODO do something
}
} else if (observerMessage instanceof StateMessage state) {
switch (state.getState()) {
case "starting_round" -> {
round++;
trick = -1;
hands.clear();
predictions.clear();
tricks.clear();
trumpSuit = null;
trumpCard = null;
}
case "starting_trick" -> {
trick++;
stack.clear();
activePlayer = null;
}
case "finished", "error" -> {
// TODO do something
}
}
}
}
}
}

@ -0,0 +1,25 @@
package eu.jonahbauer.wizard.client.libgdx.util;
import java.util.Map;
public record Pair<F,S>(F first, S second) implements Map.Entry<F,S> {
public static <F,S> Pair<F,S> of(F first, S second) {
return new Pair<>(first, second);
}
@Override
public F getKey() {
return first();
}
@Override
public S getValue() {
return second();
}
@Override
public S setValue(S value) {
throw new UnsupportedOperationException();
}
}
Loading…
Cancel
Save