Skip to content

Commit

Permalink
Add GameState.java
Browse files Browse the repository at this point in the history
  • Loading branch information
Kiyotoko committed Jun 12, 2024
1 parent 41d7e5a commit 949b92b
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 70 deletions.
99 changes: 57 additions & 42 deletions src/main/java/org/seekers/game/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@

package org.seekers.game;

import io.grpc.stub.StreamObserver;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
Expand All @@ -33,15 +30,16 @@
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.util.Duration;
import javafx.util.Pair;
import org.ini4j.Ini;
import org.seekers.grpc.Corresponding;
import org.seekers.grpc.service.CommandResponse;
import org.seekers.plugin.GameMap;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Consumer;

/**
* The Game class represents a game environment where players, seekers, goals,
Expand All @@ -55,6 +53,7 @@ public class Game extends Scene {

// Game objects
private final @Nonnull List<Entity> entities = new ArrayList<>();
private GameState gameState;
private GameMap gameMap;
private long tick = 0;

Expand All @@ -64,11 +63,7 @@ public class Game extends Scene {
private final @Nonnull List<Goal> goals = new ArrayList<>();
private final @Nonnull List<Camp> camps = new ArrayList<>();

// List of all observers for sending the next status update
private final @Nonnull Map<String, Pair<StreamObserver<CommandResponse>, Integer>> observers = new HashMap<>();

// Graphics
private final @Nonnull BooleanProperty finished = new SimpleBooleanProperty(false);
private final @Nonnull Label time = new Label();
private final @Nonnull VBox info = new VBox();
private final @Nonnull Group front = new Group();
Expand All @@ -80,6 +75,10 @@ public class Game extends Scene {
private final @Nonnull Seeker.Properties seekerProperties;
private final @Nonnull Goal.Properties goalProperties;

// Events
private @Nullable Consumer<Game> onGameStarted;
private @Nullable Consumer<Game> onGameFinished;

/**
* Constructs a new Game object. Initializes the game environment, creates the
* game rendering components, and starts the game timeline.
Expand Down Expand Up @@ -146,22 +145,21 @@ public Properties(Ini ini) {
*/
private Timeline getTimeline() {
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(getGameProperties().tickDuration), e -> {
if (hasOpenSlots())
if (hasOpenSlots()) {
return;
if (tick > gameProperties.playtime) {
finished.set(true);
} else if (getGameState() == GameState.PREPARING) {
if (getOnGameStarted() != null) getOnGameStarted().accept(this);
setGameState(GameState.RUNNING);
} else if (getGameState() == GameState.RUNNING && tick > getGameProperties().playtime) {
if (getOnGameFinished() != null)
getOnGameFinished().accept(this);
setGameState(GameState.FINISHED);
return;
}

for (Entity entity : List.copyOf(getEntities())) {
entity.update();
}
var response = getCommandResponse();
for (var pair : getObservers().values()) {
var observer = pair.getKey();
observer.onNext(response.setSeekersChanged(pair.getValue()).build());
observer.onCompleted();
}
getObservers().clear();
time.setText("[ " + (++tick) + " ]");
}));
timeline.setCycleCount(javafx.animation.Animation.INDEFINITE);
Expand All @@ -182,21 +180,20 @@ public synchronized void reset() {
players.clear();
seekers.clear();
camps.clear();
observers.clear();

// Clear scene content
getBack().getChildren().clear();
getFront().getChildren().clear();
getInfo().getChildren().clear();

// Add goals back
for (Goal goal : goals) {
for (Goal goal : getGoals()) {
getFront().getChildren().add(goal);
getEntities().add(goal);
}

// Reset property
finished.set(false);
setGameState(GameState.PREPARING);
tick = 0;
}

Expand All @@ -210,13 +207,14 @@ public boolean hasOpenSlots() {
}

public void addToTournament(Tournament tournament) {
int sum = 0;
double sum = 0;
for (Player player : players) {
tournament.getResults().computeIfAbsent(player.getName(), n -> new ArrayList<>());
sum += player.getScore();
}
for (Player player : players) {
tournament.getResults().get(player.getName()).add(100 * (sum == 0 ? 1 / players.size() : player.getScore() / sum));
int score = (int) Math.round(100 * (sum == 0 ? 1.0 / players.size() : player.getScore() / sum));
tournament.getResults().get(player.getName()).add(score);
}
}

Expand Down Expand Up @@ -271,25 +269,6 @@ public List<Camp> getCamps() {
return camps;
}

@Nonnull
public Map<String, Pair<StreamObserver<CommandResponse>, Integer>> getObservers() {
return observers;
}

@CheckReturnValue
public GameMap getGameMap() {
return gameMap;
}

public void setGameMap(@Nonnull GameMap gameMap) {
this.gameMap = gameMap;
}

@Nonnull
public BooleanProperty finishedProperty() {
return finished;
}

@Nonnull
public VBox getInfo() {
return info;
Expand Down Expand Up @@ -331,4 +310,40 @@ public Goal.Properties getGoalProperties() {
public Camp.Properties getCampProperties() {
return campProperties;
}

@CheckReturnValue
public GameMap getGameMap() {
return gameMap;
}

public void setGameMap(@Nonnull GameMap gameMap) {
this.gameMap = gameMap;
}

@CheckReturnValue
public GameState getGameState() {
return gameState;
}

public void setGameState(@Nonnull GameState gameState) {
this.gameState = gameState;
}

public void setOnGameStarted(@Nonnull Consumer<Game> onGameStarted) {
this.onGameStarted = onGameStarted;
}

@Nullable
public Consumer<Game> getOnGameStarted() {
return onGameStarted;
}

public void setOnGameFinished(@Nonnull Consumer<Game> onGameFinished) {
this.onGameFinished = onGameFinished;
}

@Nullable
public Consumer<Game> getOnGameFinished() {
return onGameFinished;
}
}
35 changes: 35 additions & 0 deletions src/main/java/org/seekers/game/GameState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 Seekers Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package org.seekers.game;

public enum GameState {
/**
* Game waits for all players, the state of the game was newly generated.
*/
PREPARING,

/**
* Game is running.
*/
RUNNING,

/**
* Game has finished, all results were logged.
*/
FINISHED
}
25 changes: 23 additions & 2 deletions src/main/java/org/seekers/game/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,27 @@
import java.util.Map;
import java.util.Random;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import io.grpc.stub.StreamObserver;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.util.Pair;
import org.seekers.grpc.Corresponding;
import org.seekers.grpc.game.PlayerOuterClass;
import org.seekers.grpc.service.CommandResponse;

/**
* The Player class represents a player in the game.
*
* @author karlz
game.getFront().getChildren().add(this);
*/
public class Player extends Label implements Corresponding<PlayerOuterClass.Player> {
public class Player extends Label implements Entity, Corresponding<PlayerOuterClass.Player> {

private static final @Nonnull Random rand = new Random();

Expand All @@ -49,6 +53,8 @@ public class Player extends Label implements Corresponding<PlayerOuterClass.Play
private @Nonnull String name;
private int score;

private @CheckForNull Pair<StreamObserver<CommandResponse>, Integer> observer;

/**
* Constructs a new instance of the Player class.
*
Expand All @@ -62,8 +68,18 @@ public Player(@Nonnull Game game) {
setPadding(new Insets(2.0));
setFont(Font.font("Ubuntu", 24.0));
setTextFill(color);
getGame().getEntities().add(this);
getGame().getInfo().getChildren().add(this);
game.getPlayers().add(this);
getGame().getPlayers().add(this);
}

@Override
public void update() {
if (observer != null) {
observer.getKey().onNext(getGame().getCommandResponse().setSeekersChanged(observer.getValue()).build());
observer.getKey().onCompleted();
observer = null;
}
}

/**
Expand Down Expand Up @@ -181,6 +197,11 @@ public void putUp() {
setText(name + ": " + score);
}

public void setObserver(
@CheckForNull Pair<StreamObserver<CommandResponse>, Integer> observer) {
this.observer = observer;
}

@Override
public PlayerOuterClass.Player associated() {
return PlayerOuterClass.Player.newBuilder().setId(getIdentifier()).addAllSeekerIds(seekers.keySet())
Expand Down
43 changes: 31 additions & 12 deletions src/main/java/org/seekers/game/Seeker.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
import org.seekers.grpc.game.SeekerOuterClass;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.List;

/**
* The Seeker class represents a seeker in the game.
Expand Down Expand Up @@ -221,14 +223,17 @@ public void setColor(final @Nonnull Color color) {
this.activated = color;
this.disabled = color.darker().darker();
setFill(color);
animation.indicator.setStroke(color);
for (var indicator : animation.indicators)
indicator.setStroke(color);
}

@Override
public void setPosition(@Nonnull Point2D position) {
super.setPosition(position);
animation.indicator.setCenterX(getPosition().getX());
animation.indicator.setCenterY(getPosition().getY());
for (var indicator : animation.indicators) {
indicator.setCenterX(getPosition().getX());
indicator.setCenterY(getPosition().getY());
}
}

@Override
Expand All @@ -240,26 +245,40 @@ public SeekerOuterClass.Seeker associated() {

public class SeekerAnimation extends Animation {

private final @Nonnull Circle indicator = new Circle(properties.radius + 0.25 * getAnimationRange());
private final @Nonnull List<Circle> indicators = List.of(
new Circle(properties.radius + getAnimationRange() / 3),
new Circle(properties.radius + getAnimationRange() * 2 / 3) ,
new Circle(properties.radius + getAnimationRange())
);
private final @Nonnull List<Double> expansions = new ArrayList<>(List.of(
getAnimationRange() / 3, getAnimationRange() * 2 / 3, getAnimationRange()
));

protected SeekerAnimation(@Nonnull Game game) {
super(game);
indicator.setFill(Color.TRANSPARENT);
indicator.setStrokeWidth(3);
indicator.setStroke(player.getColor());
getChildren().add(indicator);
for (var indicator : indicators) {
indicator.setFill(Color.TRANSPARENT);
indicator.setStrokeWidth(2);
indicator.setStroke(player.getColor());
getChildren().add(indicator);
}
setVisible(false);
}

private int frameTime = 30;
private int frameTime = 8;

@Override
public void update() {
frameTime--;
if (frameTime < 0) {
double expansion = (indicator.getRadius() + Math.signum(magnet)) % getAnimationRange();
indicator.setRadius(expansion + properties.radius);
frameTime = 30;
for (int i = 0; i < expansions.size(); i++) {
var range = expansions.get(i) - Math.signum(getMagnet());
if (range > getAnimationRange()) range -= getAnimationRange();
if (range < 0) range += getAnimationRange();
expansions.set(i, range);
indicators.get(i).setRadius(range + properties.radius);
frameTime = 8;
}
}
}

Expand Down
Loading

0 comments on commit 949b92b

Please sign in to comment.