package inf112.fiasko.roborally.objects; 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; import inf112.fiasko.roborally.networking.RoboRallyServer; import inf112.fiasko.roborally.networking.containers.PowerDownContainer; 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 playerList; private final boolean host; private final String playerName; private final RoboRallyServer server; private Board gameBoard; private List> repairTiles; private Deck mainDeck; private GameState gameState = GameState.BEGINNING_OF_GAME; private String winningPlayerName; private List program; private ProgrammingCardDeck playerHand; private Phase phase; 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 playerList, String boardName, boolean host, String playerName, RoboRallyServer server, boolean debug) { this.playerName = playerName; this.host = host; this.playerList = playerList; this.server = server; if (debug) { initializeDebugMode(); } else { initializeGame(boardName); } this.phase = new Phase(gameBoard, playerList, 600, this); } /** * 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 playerList, String boardName, boolean host, String playerName, RoboRallyServer server) { this.playerName = playerName; this.host = host; this.playerList = playerList; this.server = server; initializeGame(boardName); this.phase = new Phase(gameBoard, playerList, 600, this); } @Override public int getWidth() { return gameBoard.getBoardWidth(); } @Override public int getHeight() { return gameBoard.getBoardHeight(); } @Override public List getTilesToDraw() { return gameBoard.getTiles(); } @Override public List getWallsToDraw() { return gameBoard.getWalls(); } @Override public List getParticlesToDraw() { return gameBoard.getParticles(); } @Override public List 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 getProgram() { return program; } @Override public void setProgram(List 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> programMap = programs.getProgram(); Map powerDown = programs.getPowerdown(); String playerName; for (Player player : playerList) { playerName = player.getName(); 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 if (host) { updateLockedProgrammingCardsForAllPlayers(); removeNonLockedProgrammingCardsFromPlayers(); } if (gameBoard.getPowerDown(Objects.requireNonNull(getPlayerFromName(this.playerName)).getRobotID())) { 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(); setGameState(GameState.BEGINNING_OF_GAME); runTurn(); } @Override public String getWinningPlayerName() { return winningPlayerName; } @Override public void setWinningPlayerName(String winningPlayerName) { this.winningPlayerName = winningPlayerName; } /** * Initializes the game with a debugging board */ private void initializeDebugMode() { List robots = new ArrayList<>(); 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))); try { gameBoard = BoardLoaderUtil.loadBoard("boards/all_tiles_test_board.txt", robots); } catch (IOException e) { e.printStackTrace(); } } /** * Initializes the game with a playable board */ private void initializeGame(String boardName) { try { List robots = new ArrayList<>(); int posX = 1; for (Player player : playerList) { Position spawn = new Position(posX, 1); robots.add(new Robot(player.getRobotID(), spawn)); posX++; } gameBoard = BoardLoaderUtil.loadBoard("boards/" + boardName, robots); moveRobotsToSpawn(); 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> spawnTileContainerList = gameBoard.getPositionsOfTileOnBoard(robotSpawn); if (spawnTileContainerList.size() != 1) { throw new IllegalArgumentException("The chosen board seems to be missing a robot spawn"); } BoardElementContainer spawnTileContainer = spawnTileContainerList.get(0); gameBoard.teleportRobot(robotID, spawnTileContainer.getPosition()); gameBoard.setBackupPositionOfRobot(robotID, spawnTileContainer.getPosition()); } } /** * 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 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); Player player = getPlayerFromName(playerName); if (player != null && player.getProgrammingCardDeck() != null) { server.sendToClient(connection, player.getProgrammingCardDeck()); } } } } /** * 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 playerProgram = player.getProgram(); 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) { 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) { 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(); 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(); } /** * repair all robots standing on a repair tile */ private void repairAllRobotsOnRepairTiles() { for (BoardElementContainer 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; } }