471 lines
16 KiB
Java
Raw Normal View History

package inf112.fiasko.roborally.objects;
2020-04-14 14:47:17 +02:00
import com.esotericsoftware.kryonet.Connection;
import inf112.fiasko.roborally.elementproperties.GameState;
import inf112.fiasko.roborally.elementproperties.Position;
import inf112.fiasko.roborally.elementproperties.RobotID;
import inf112.fiasko.roborally.elementproperties.TileType;
2020-04-14 14:47:17 +02:00
import inf112.fiasko.roborally.networking.RoboRallyServer;
import inf112.fiasko.roborally.networking.containers.PowerDownContainer;
2020-04-16 14:50:45 +02:00
import inf112.fiasko.roborally.networking.containers.ProgamsContainer;
import inf112.fiasko.roborally.utility.BoardLoaderUtil;
import inf112.fiasko.roborally.utility.DeckLoaderUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* This class represent a game which is drawable using libgdx
*/
public class RoboRallyGame implements DrawableGame, InteractableGame {
private final List<Player> playerList;
private final boolean host;
private final String playerName;
private final RoboRallyServer server;
private Board gameBoard;
private List<BoardElementContainer<Tile>> repairTiles;
private Deck<ProgrammingCard> mainDeck;
private GameState gameState = GameState.BEGINNING_OF_GAME;
private String winningPlayerName;
2020-04-16 14:50:45 +02:00
private List<ProgrammingCard> program;
private ProgrammingCardDeck playerHand;
2020-04-17 11:13:08 +02:00
private Phase phase;
2020-04-14 14:47:17 +02:00
public Boolean getRobotPowerdown(){
if(getPlayerFromName(this.playerName)!=null){
return gameBoard.getPowerDown(Objects.requireNonNull(getPlayerFromName(this.playerName)).getRobotID());
}
return false;
}
/**
* Instantiates a new Robo Rally game
*
* @param playerList A list of all the players participating in the game
* @param boardName The playerName of the board to use
* @param host Whether this player is the host
* @param playerName The name of the player of this instance of the game
* @param server The server if this player is host. Should be null otherwise
* @param debug Whether this game is to use the debugging board
*/
public RoboRallyGame(List<Player> playerList, String boardName, boolean host, String playerName,
RoboRallyServer server, boolean debug) {
this.playerName = playerName;
this.host = host;
this.playerList = playerList;
this.server = server;
2020-02-27 16:44:06 +01:00
if (debug) {
initializeDebugMode();
} else {
2020-04-06 17:09:05 +02:00
initializeGame(boardName);
2020-02-27 16:44:06 +01:00
}
2020-04-17 11:13:08 +02:00
this.phase = new Phase(gameBoard, playerList, 600, this);
2020-02-27 16:44:06 +01:00
}
/**
* Instantiates a new Robo Rally game
*
* @param playerList A list of all the players participating in the game
* @param boardName The playerName of the board to use
* @param host Whether this player is the host
* @param playerName The name of the player of this instance of the game
* @param server The server if this player is host. Should be null otherwise
*/
public RoboRallyGame(List<Player> playerList, String boardName, boolean host, String playerName,
RoboRallyServer server) {
this.playerName = playerName;
this.host = host;
this.playerList = playerList;
this.server = server;
2020-04-06 17:09:05 +02:00
initializeGame(boardName);
2020-04-17 11:13:08 +02:00
this.phase = new Phase(gameBoard, playerList, 600, this);
2020-02-27 16:44:06 +01:00
}
@Override
public int getWidth() {
return gameBoard.getBoardWidth();
}
@Override
public int getHeight() {
return gameBoard.getBoardHeight();
}
@Override
public List<Tile> getTilesToDraw() {
return gameBoard.getTiles();
}
@Override
public List<Wall> getWallsToDraw() {
return gameBoard.getWalls();
}
@Override
public List<Particle> getParticlesToDraw() {
return gameBoard.getParticles();
}
@Override
public List<Robot> getRobotsToDraw() {
return gameBoard.getAliveRobots();
}
@Override
public GameState getGameState() {
return gameState;
}
@Override
public void setGameState(GameState gameState) {
this.gameState = gameState;
}
@Override
public ProgrammingCardDeck getPlayerHand() {
return playerHand;
}
@Override
public void setPlayerHand(ProgrammingCardDeck playerHand) {
this.playerHand = playerHand;
}
@Override
public List<ProgrammingCard> getProgram() {
return program;
}
@Override
public void setProgram(List<ProgrammingCard> program) {
this.program = program;
}
@Override
public int getProgramSize() {
Player player = getPlayerFromName(playerName);
if (player != null) {
return Math.min(5, 5 - gameBoard.getRobotDamage(player.getRobotID()) + 4);
}
return -1;
}
@Override
public void receiveAllPrograms(ProgamsContainer programs) throws InterruptedException {
//Reads data from server and updates player objects
Map<String, List<ProgrammingCard>> programMap = programs.getProgram();
Map<String, Boolean> powerDown = programs.getPowerdown();
String playerName;
for (Player player : playerList) {
playerName = player.getName();
2020-04-20 14:21:03 +02:00
player.setProgram(programMap.get(playerName));
player.setPowerDownNextRound(powerDown.get(playerName));
}
//Runs 5 phases
setGameState(GameState.RUNNING_PROGRAMS);
phase.runPhase(1);
phase.runPhase(2);
phase.runPhase(3);
phase.runPhase(4);
phase.runPhase(5);
// Repair robots on repair tiles
repairAllRobotsOnRepairTiles();
//Updates the host's card deck
2020-04-20 18:04:34 +02:00
if (host) {
updateLockedProgrammingCardsForAllPlayers();
removeNonLockedProgrammingCardsFromPlayers();
}
2020-04-20 18:04:34 +02:00
if (gameBoard.getPowerDown(Objects.requireNonNull(getPlayerFromName(this.playerName)).getRobotID())) {
2020-04-20 18:04:34 +02:00
setGameState(GameState.CHOOSING_STAY_IN_POWER_DOWN);
} else {
setGameState(GameState.LOADING);
}
}
@Override
public void receiveStayInPowerDown(PowerDownContainer powerDowns) {
for (Player player : playerList) {
if(gameBoard.getPowerDown(player.getRobotID())) {
player.setPowerDownNextRound(powerDowns.getPowerDown().get(player.getName()));
}
}
respawnRobots();
sendAllDeadPlayersToServer();
resetHasTouchedFlagThisTurnForAllRobots();
2020-04-20 18:04:34 +02:00
setGameState(GameState.BEGINNING_OF_GAME);
runTurn();
}
2020-04-20 14:21:03 +02:00
@Override
public String getWinningPlayerName() {
return winningPlayerName;
}
2020-04-20 14:21:03 +02:00
@Override
public void setWinningPlayerName(String winningPlayerName) {
this.winningPlayerName = winningPlayerName;
}
/**
* Initializes the game with a debugging board
*/
2020-02-27 16:44:06 +01:00
private void initializeDebugMode() {
List<Robot> robots = new ArrayList<>();
2020-03-19 13:46:45 +01:00
robots.add(new Robot(RobotID.ROBOT_1, new Position(0, 18)));
robots.add(new Robot(RobotID.ROBOT_2, new Position(1, 18)));
robots.add(new Robot(RobotID.ROBOT_3, new Position(2, 18)));
robots.add(new Robot(RobotID.ROBOT_4, new Position(3, 18)));
robots.add(new Robot(RobotID.ROBOT_5, new Position(4, 18)));
robots.add(new Robot(RobotID.ROBOT_6, new Position(5, 18)));
robots.add(new Robot(RobotID.ROBOT_7, new Position(6, 18)));
robots.add(new Robot(RobotID.ROBOT_8, new Position(7, 18)));
2020-02-27 16:44:06 +01:00
try {
gameBoard = BoardLoaderUtil.loadBoard("boards/all_tiles_test_board.txt", robots);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Initializes the game with a playable board
*/
2020-04-06 17:09:05 +02:00
private void initializeGame(String boardName) {
try {
List<Robot> robots = new ArrayList<>();
int posX = 1;
for (Player player : playerList) {
Position spawn = new Position(posX, 1);
robots.add(new Robot(player.getRobotID(), spawn));
posX++;
2020-04-06 17:09:05 +02:00
}
gameBoard = BoardLoaderUtil.loadBoard("boards/" + boardName, robots);
moveRobotsToSpawn();
2020-04-20 18:04:34 +02:00
2020-04-17 11:13:08 +02:00
repairTiles = gameBoard.getPositionsOfTileOnBoard(TileType.FLAG_1, TileType.FLAG_2, TileType.FLAG_3,
TileType.FLAG_4, TileType.WRENCH, TileType.WRENCH_AND_HAMMER);
if (host) {
mainDeck = DeckLoaderUtil.loadProgrammingCardsDeck();
}
new Thread(this::runTurn).start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Teleports all robots to their respective spawn positions
*/
private void moveRobotsToSpawn() {
for (Player player : playerList) {
RobotID robotID = player.getRobotID();
TileType robotSpawn = TileType.getTileTypeFromID(robotID.getRobotIDID() + 22);
List<BoardElementContainer<Tile>> spawnTileContainerList = gameBoard.getPositionsOfTileOnBoard(robotSpawn);
if (spawnTileContainerList.size() != 1) {
throw new IllegalArgumentException("The chosen board seems to be missing a robot spawn");
}
BoardElementContainer<Tile> spawnTileContainer = spawnTileContainerList.get(0);
gameBoard.teleportRobot(robotID, spawnTileContainer.getPosition());
2020-04-20 18:04:34 +02:00
gameBoard.setBackupPositionOfRobot(robotID, spawnTileContainer.getPosition());
}
2020-04-20 18:04:34 +02:00
}
/**
* Runs all the steps of one turn in the game
*/
private void runTurn() {
// Sets the power down status to true on robots that have players who planned one this turn.
// Resets players power down for next turn to false.
updateRobotPowerDown();
// Set damage of robots in power down to 0
2020-04-20 14:21:03 +02:00
gameBoard.executePowerDown();
setGameState(GameState.LOADING);
if (host) {
//Distributes programming cards for all players, and sends a deck to each player
distributeProgrammingCardsToPlayers();
for (Connection connection : server.getPlayerNames().keySet()) {
String playerName = server.getPlayerNames().get(connection);
2020-04-14 14:47:17 +02:00
Player player = getPlayerFromName(playerName);
2020-04-20 14:21:03 +02:00
if (player != null && player.getProgrammingCardDeck() != null) {
server.sendToClient(connection, player.getProgrammingCardDeck());
2020-04-14 14:47:17 +02:00
}
}
}
}
/**
* Sends information about players no longer part of the game to the server
*/
private void sendAllDeadPlayersToServer() {
if (host) {
server.setDeadPlayers(gameBoard.getRealDeadRobots());
}
//Removes dead players from playerList
playerList.removeIf((player) -> gameBoard.getRealDeadRobots().contains(player.getRobotID()));
if (playerList.isEmpty()) {
setGameState(GameState.EXITED);
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Resets the boolean for if the robot has touched a flag this turn, to set up the next turn.
*/
private void resetHasTouchedFlagThisTurnForAllRobots() {
for (Robot robot : gameBoard.getAliveRobots()) {
robot.setHasTouchedFlagThisTurn(false);
}
}
/**
* Locks the players programming cards in relation to the robots damage
*/
private void updateLockedProgrammingCardsForAllPlayers() {
for (Player player : playerList) {
List<ProgrammingCard> playerProgram = player.getProgram();
2020-04-20 14:21:03 +02:00
ProgrammingCardDeck playerDeck = player.getProgrammingCardDeck();
ProgrammingCardDeck lockedPlayerDeck = player.getLockedProgrammingCardDeck();
int robotDamage;
if (!gameBoard.isRobotAlive(player.getRobotID())) {
robotDamage = 0;
} else {
robotDamage = gameBoard.getRobotDamage(player.getRobotID());
}
//The player has no locked cards. All previously locked cards should go into the free deck
if (robotDamage <= 4) {
2020-04-20 14:21:03 +02:00
lockedPlayerDeck.emptyInto(player.getProgrammingCardDeck());
continue;
}
//Goes through locked cards and moves them to the locked player deck
for (int i = 1; i <= (robotDamage - 4); i++) {
ProgrammingCard card = playerProgram.get(playerProgram.size() - i);
moveProgrammingCardToLockedDeck(card, playerDeck, lockedPlayerDeck);
}
}
}
/**
* Moves a card from the player's deck to the player's locked deck if found
*
* @param card The card to move to the locked deck
* @param playerDeck The deck containing the player's cards
* @param lockedPlayerDeck The deck containing the player's locked cards
*/
private void moveProgrammingCardToLockedDeck(ProgrammingCard card, ProgrammingCardDeck playerDeck,
ProgrammingCardDeck lockedPlayerDeck) {
for (int i = 0; i < playerDeck.size(); i++) {
if (card.equals(playerDeck.peekTop())) {
//Found the card. Add to the locked deck
lockedPlayerDeck.draw(playerDeck);
break;
} else {
//Move the card to the bottom of the deck
playerDeck.draw(playerDeck);
}
}
}
/**
* Moves non-locked player programming cards from their hand back to the main deck
*/
private void removeNonLockedProgrammingCardsFromPlayers() {
for (Player player : playerList) {
2020-04-20 14:21:03 +02:00
player.getProgrammingCardDeck().emptyInto(mainDeck);
}
}
/**
* Deals correct amount of cards to active players, based on their robots damage
*/
private void distributeProgrammingCardsToPlayers() {
mainDeck.shuffle();
for (Player player : playerList) {
RobotID robot = player.getRobotID();
2020-04-20 14:21:03 +02:00
ProgrammingCardDeck playerDeck = player.getProgrammingCardDeck();
int robotDamage = gameBoard.getRobotDamage(robot);
//Powered down or heavily damaged robots don't get any cards
if (gameBoard.getPowerDown(robot) || robotDamage >= 9) {
continue;
}
if (!playerDeck.isEmpty()) {
throw new IllegalStateException("Player deck must be empty when dealing new cards!");
}
//Gives the player the correct amount of cards
playerDeck.draw(mainDeck, 9 - robotDamage);
}
}
/**
* Respawn all the dead robots with more lives and places them on the game board
*/
private void respawnRobots() {
gameBoard.respawnRobots();
}
/**
2020-03-31 13:45:35 +02:00
* repair all robots standing on a repair tile
*/
private void repairAllRobotsOnRepairTiles() {
for (BoardElementContainer<Tile> repairTile : repairTiles) {
Position robotOnTilePosition = repairTile.getPosition();
if (!gameBoard.hasRobotOnPosition(robotOnTilePosition)) {
continue;
}
gameBoard.repairRobotOnTile(gameBoard.getRobotOnPosition(robotOnTilePosition));
}
}
/**
* Sets the robot's power down status to the player's "power down next round" status and sets the players status to false
*/
private void updateRobotPowerDown() {
for (Player player : playerList) {
setRobotPowerDown(player, player.getPowerDownNextRound());
player.setPowerDownNextRound(false);
}
}
/**
* Sets the power down status of a robot
*
* @param player The player that owns the robot
* @param powerDownStatus The new power down status
*/
private void setRobotPowerDown(Player player, Boolean powerDownStatus) {
gameBoard.setPowerDown(player.getRobotID(), powerDownStatus);
}
/**
* Gets a player object given a player name
*
* @param name The name of the player to get
* @return The corresponding player object or null if no such object exists
*/
private Player getPlayerFromName(String name) {
for (Player player : playerList) {
if (player.getName().equals(name)) {
return player;
}
}
return null;
}
}