package lect78_impl.controller; import java.util.ArrayList; import java.util.List; import lect78_impl.model.*; import lect78_impl.view.GamePanel; import utils.NotPossibleException; /** * @overview Represents the main game controller for the 2D Tank Battle Game * This is a real-time game where both players can move freely * * @attributes * board Board The game board * players List List of players * gameState GameState Current state of the game (SETUP, RUNNING, GAME_OVER) * winningPlayer Player The player who won the game (null if not over) * projectiles List Active projectiles in the game */ public class Game { // Game state enum public enum GameState { SETUP, RUNNING, GAME_OVER } private Board board; private List players; private GameState gameState; private Player winningPlayer; private List projectiles; private GamePanel gamePanel; // Constants private static final int DEFAULT_BOARD_SIZE = 8; private static final int DEFAULT_TANKS_PER_PLAYER = 1; /** * @effects Initializes the game with default settings */ public Game(GamePanel gamePanel) { this.gamePanel = gamePanel; this.board = new Board(DEFAULT_BOARD_SIZE); this.players = new ArrayList<>(); this.gameState = GameState.SETUP; this.winningPlayer = null; this.projectiles = new ArrayList<>(); } /** * @effects Sets up the game with players and tanks */ public void setupGame() { // Create players with default controls Player player1 = new Player(1, Controls.createPlayer1Controls(), DEFAULT_TANKS_PER_PLAYER); Player player2 = new Player(2, Controls.createPlayer2Controls(), DEFAULT_TANKS_PER_PLAYER); players.add(player1); players.add(player2); try { // Create tanks for player 1 Tank tank1 = new Tank(100, 20, 10, 1000, "lect78_impl/assets/images/tank1.png"); player1.addTank(tank1); board.placeTank(tank1, new Position(0, 0)); // Top-left // Create tanks for player 2 Tank tank2 = new Tank(100, 20, 10, 1000, "lect78_impl/assets/images/tank2.png"); player2.addTank(tank2); board.placeTank(tank2, new Position(7, 7)); // Bottom-right // Set different ranges for tanks tank1.setRange(3); // Tank 1 can shoot up to 3 cells away tank2.setRange(4); // Tank 2 can shoot up to 4 cells away } catch (NotPossibleException e) { System.err.println("Failed to create tanks: " + e.getMessage()); } } /** * @effects Starts the game, setting state to RUNNING * @throws NotPossibleException if game setup is incomplete */ public void startGame() throws NotPossibleException { if (players.isEmpty()) { throw new NotPossibleException("Game.startGame: No players available"); } for (Player player : players) { if (player.getTankSet().isEmpty()) { throw new NotPossibleException("Game.startGame: Player " + player.getId() + " has no tanks"); } } gameState = GameState.RUNNING; } /** * @effects Updates the game state for a frame */ public void update() { if (gameState != GameState.RUNNING) { return; } // Update all projectiles List projectilesToRemove = new ArrayList<>(); for (Projectile proj : projectiles) { proj.update(); // Check collisions with tanks for (Player player : players) { for (Tank tank : player.getTankSet().getAllTanks()) { if (!tank.isDestroyed() && proj.checkCollision(tank)) { // Apply damage proj.applyDamage(tank); // Handle HE area damage if (proj instanceof ProjectileHE) { ((ProjectileHE) proj).applyAreaDamage(board, tank.getPosition()); } // Mark projectile for removal projectilesToRemove.add(proj); // Check if tank was destroyed if (tank.isDestroyed()) { removeDestroyedTank(player, tank); // Check if this ended the game if (checkGameOver()) { return; // Game is over, stop processing } } break; } } } // Add inactive projectiles to removal list if (!proj.isActive()) { projectilesToRemove.add(proj); } } // Remove inactive projectiles projectiles.removeAll(projectilesToRemove); } /** * @effects Processes movement for a specific player's tank to a new position * @return true if movement was successful, false otherwise */ public boolean processMovement(Player player, Position newPosition) { if (gameState != GameState.RUNNING) { return false; } Tank activeTank = player.getActiveTank(); if (activeTank == null || activeTank.isDestroyed()) { return false; } return board.moveTank(activeTank, newPosition); } /** * @effects Processes a shot from a specific player * @return true if shot was fired, false otherwise */ public boolean processShot(Player player, String projectileType) { if (gameState != GameState.RUNNING) { return false; } Tank activeTank = player.getActiveTank(); if (activeTank == null || activeTank.isDestroyed() || !activeTank.canFire()) { return false; } Projectile projectile = activeTank.fireProjectile(projectileType); if (projectile != null) { projectiles.add(projectile); return true; } return false; } /** * @effects Removes a destroyed tank from the board and player */ private void removeDestroyedTank(Player player, Tank tank) { // Remove from board Cell cell = board.getCell(tank.getPosition()); if (cell != null) { cell.setTank(null); } // Remove from player's tank set player.removeTank(tank); } /** * @effects Checks if the game is over (a player has no tanks left) * @return true if game is over, false otherwise */ public boolean checkGameOver() { if (gameState != GameState.RUNNING) { return gameState == GameState.GAME_OVER; } // Count players with tanks int playersWithTanks = 0; Player lastPlayerWithTanks = null; for (Player player : players) { if (!player.hasLost()) { playersWithTanks++; lastPlayerWithTanks = player; } } // If only one player has tanks, they win if (playersWithTanks == 1) { endGame(lastPlayerWithTanks); return true; } // If no players have tanks, it's a draw if (playersWithTanks == 0) { endGame(null); return true; } return false; } /** * @effects Ends the game and sets the winning player */ public void endGame(Player winner) { gameState = GameState.GAME_OVER; winningPlayer = winner; announceGameResult(); } /** * @effects Returns the winning player if game is over, null otherwise */ public Player getWinner() { return winningPlayer; } /** * @effects Announces game result to players */ public void announceGameResult() { if (winningPlayer != null) { System.out.println("Game Over! Player " + winningPlayer.getId() + " wins!"); } else { System.out.println("Game Over! It's a draw!"); } } /** * @effects Gets the current game state */ public GameState getGameState() { return gameState; } /** * @effects Gets the board */ public Board getBoard() { return board; } /** * @effects Gets all players */ public List getPlayers() { return players; } /** * @effects Gets the player by ID */ public Player getPlayer(int playerId) { for (Player player : players) { if (player.getId() == playerId) { return player; } } return null; } /** * @effects Gets all active projectiles */ public List getProjectiles() { return new ArrayList<>(projectiles); } }