diff --git a/src/main/java/com/boredxgames/tictactoeclient/presentation/GameController.java b/src/main/java/com/boredxgames/tictactoeclient/presentation/GameController.java index fe84999..8ed23dd 100644 --- a/src/main/java/com/boredxgames/tictactoeclient/presentation/GameController.java +++ b/src/main/java/com/boredxgames/tictactoeclient/presentation/GameController.java @@ -9,12 +9,10 @@ import com.boredxgames.tictactoeclient.domain.model.Move; import com.boredxgames.tictactoeclient.domain.services.game.GameBoard; import com.boredxgames.tictactoeclient.domain.model.GameState; -import com.boredxgames.tictactoeclient.domain.services.AIService; import com.boredxgames.tictactoeclient.domain.services.game.GameService; import com.boredxgames.tictactoeclient.domain.services.game.OfflinePVEAIService; import com.boredxgames.tictactoeclient.domain.services.game.OfflinePVPService; import javafx.animation.PauseTransition; -import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; @@ -30,13 +28,11 @@ import javafx.scene.media.MediaView; import javafx.scene.text.Text; import javafx.util.Duration; + import java.net.URL; import java.util.Objects; import java.util.ResourceBundle; -/** - * @author Tasneem - */ public class GameController implements Initializable, NavigationParameterAware { public GridPane gameGrid; @@ -86,8 +82,6 @@ public class GameController implements Initializable, NavigationParameterAware { @FXML private Button playAgainButton; @FXML - private Button changeDifficultyButton; - @FXML private Button mainMenuButton; private GameBoard gameBoard; @@ -100,12 +94,14 @@ public class GameController implements Initializable, NavigationParameterAware { private GameService gameService; + private MediaPlayer currentMediaPlayer; + @Override public void initialize(URL url, ResourceBundle rb) { cells = new Button[][]{ - {cell00, cell01, cell02}, - {cell10, cell11, cell12}, - {cell20, cell21, cell22} + {cell00, cell01, cell02}, + {cell10, cell11, cell12}, + {cell20, cell21, cell22} }; gameBoard = new GameBoard(); setupCellHandlers(); @@ -119,15 +115,11 @@ public void setNavigationParameter(Object parameter) { this.player2Name = params.player2(); this.gameMode = params.mode(); } else { - // Default this.gameMode = GameMode.OFFLINE_PVP; - this.player1Name = "Player 1"; - this.player2Name = "Player 2"; } applyPlayerInfo(); applyGameModeSettings(); - resetGame(); } @@ -139,28 +131,21 @@ private void applyPlayerInfo() { private void applyGameModeSettings() { switch (gameMode) { case OFFLINE_PVP -> { - opponentTypeLabel.setText("PLAYER 2"); + opponentTypeLabel.setText(player2Name); difficultyBadge.setVisible(false); difficultyBadge.setManaged(false); - changeDifficultyButton.setVisible(false); - changeDifficultyButton.setManaged(false); gameService = new OfflinePVPService(); } case OFFLINE_PVE -> { opponentTypeLabel.setText("CPU (O)"); difficultyBadge.setVisible(true); difficultyBadge.setManaged(true); - changeDifficultyButton.setVisible(true); - changeDifficultyButton.setManaged(true); gameService = new OfflinePVEAIService(); } case ONLINE_PVP -> { - opponentTypeLabel.setText("ONLINE PLAYER"); + opponentTypeLabel.setText(player2Name); difficultyBadge.setVisible(false); difficultyBadge.setManaged(false); - changeDifficultyButton.setVisible(false); - changeDifficultyButton.setManaged(false); - // gameService = new OnlinePVPService(); // TODO: implement online pvp service } } } @@ -180,21 +165,17 @@ private void setupButtonHandlers() { playAgainButton.setOnAction(e -> resetGame()); mainMenuButton.setOnAction(e -> { - NavigationManager.navigate(Screens.PRIMARY, NavigationAction.REPLACE_ALL); // TODO: change to mode selection screen + NavigationManager.navigate(Screens.GAME_MODE, NavigationAction.REPLACE_ALL); }); backButton.setOnAction(e -> { - NavigationManager.pop(); + NavigationManager.navigate(Screens.GAME_MODE, NavigationAction.REPLACE_ALL); }); settingsButton.setOnAction(e -> { NavigationManager.navigate(Screens.SETTINGS, NavigationAction.REPLACE_ALL); }); - - changeDifficultyButton.setOnAction(e -> { - // TODO change difficulty - }); } private void handleCellClick(int row, int col) { @@ -316,32 +297,6 @@ private void setActiveCard(VBox activeCard, VBox inactiveCard) { inactiveCard.getStyleClass().remove("active-card"); } - private void makeCPUMove() { - if (gameMode != GameMode.OFFLINE_PVE) { - return; - } - - int[] aiMove = AIService.nextMove(gameBoard, AIService.getDiffiulty()); - if (aiMove == null) { - return; - } - - Platform.runLater(() -> { - if (gameBoard.makeMove(aiMove[0], aiMove[1], GameBoard.PLAYER_O)) { - updateCell(aiMove[0], aiMove[1], GameBoard.PLAYER_O); - - GameState state = gameBoard.getGameState(); - if (state != GameState.IN_PROGRESS) { - handleGameEnd(); - } else { - gameBoard.switchPlayer(); - updateTurnIndicator(); - enableBoard(); - } - } - }); - } - private void handleGameEnd() { GameState state = gameBoard.getGameState(); @@ -354,13 +309,18 @@ private void handleGameEnd() { } } - if (state == GameState.X_WINS) { - playerScore++; - playerScoreLabel.setText(String.valueOf(playerScore)); - playVictoryVideo(); - } else if (state == GameState.O_WINS) { - opponentScore++; - opponentScoreLabel.setText(String.valueOf(opponentScore)); + switch (state) { + case X_WINS -> { + playerScore++; + playerScoreLabel.setText(String.valueOf(playerScore)); + playVictoryVideo("you_win"); + } + case O_WINS -> { + opponentScore++; + opponentScoreLabel.setText(String.valueOf(opponentScore)); + playVictoryVideo("loser"); + } + case DRAW -> playVictoryVideo("handshake"); } PauseTransition pause = new PauseTransition(Duration.millis(800)); @@ -370,66 +330,68 @@ private void handleGameEnd() { private void showGameOverModal(GameState state) { switch (state) { - case X_WINS: - modalIcon.setText("emoji_events"); + case X_WINS -> { modalTitle.setText("Victory!"); - modalMessage.setText(gameMode == GameMode.OFFLINE_PVE - ? "The CPU didn't stand a chance against your moves." - : "Player 1 wins the game!"); - break; - - case O_WINS: - modalIcon.setText("sentiment_dissatisfied"); + modalMessage.setText(player1Name + " wins!"); + } + case O_WINS -> { modalTitle.setText("Defeat!"); - modalMessage.setText(gameMode == GameMode.OFFLINE_PVE - ? "The CPU outsmarted you this time." - : "Player 2 wins the game!"); - break; - - case DRAW: - modalIcon.setText("handshake"); + modalMessage.setText(player2Name + " wins!"); + } + case DRAW -> { modalTitle.setText("Draw!"); - modalMessage.setText("It's a tie! Both players played well."); - break; + modalMessage.setText("Both players played well."); + } } - modalOverlay.setVisible(true); } - private void playVictoryVideo() { + private void playVictoryVideo(String videoName) { try { String videoPath = Objects.requireNonNull( - getClass().getResource("/assets/videos/you_win.mp4") + getClass().getResource("/assets/videos/" + videoName + ".mp4") ).toExternalForm(); + if (currentMediaPlayer != null) { + currentMediaPlayer.stop(); + currentMediaPlayer.dispose(); + } + Media media = new Media(videoPath); - MediaPlayer player = new MediaPlayer(media); - victoryVideo.setMediaPlayer(player); + currentMediaPlayer = new MediaPlayer(media); + victoryVideo.setMediaPlayer(currentMediaPlayer); + currentMediaPlayer.setCycleCount(MediaPlayer.INDEFINITE); + currentMediaPlayer.setAutoPlay(true); victoryVideo.setVisible(true); - player.setAutoPlay(true); - - player.setOnEndOfMedia(() -> { - victoryVideo.setVisible(false); - }); } catch (Exception e) { - System.out.println("Error getting the video: " + e); + System.out.println("Video error: " + e.getMessage()); } } public void resetGame() { gameBoard.resetGame(); - for (int row = 0; row < 3; row++) { - for (int col = 0; col < 3; col++) { - cells[row][col].setGraphic(null); - cells[row][col].setDisable(false); - cells[row][col].getStyleClass().remove("cell-winning"); + for (Button[] row : cells) { + for (Button cell : row) { + cell.setGraphic(null); + cell.setDisable(false); + cell.getStyleClass().remove("cell-winning"); } } modalOverlay.setVisible(false); + + if (currentMediaPlayer != null) { + currentMediaPlayer.stop(); + currentMediaPlayer.dispose(); + currentMediaPlayer = null; + } + + victoryVideo.setVisible(false); + victoryVideo.setMediaPlayer(null); + updateTurnIndicator(); enableBoard(); } diff --git a/src/main/resources/assets/videos/handshake.gif b/src/main/resources/assets/videos/handshake.gif new file mode 100644 index 0000000..7322328 Binary files /dev/null and b/src/main/resources/assets/videos/handshake.gif differ diff --git a/src/main/resources/assets/videos/loser.mp4 b/src/main/resources/assets/videos/loser.mp4 new file mode 100644 index 0000000..0a5f0de Binary files /dev/null and b/src/main/resources/assets/videos/loser.mp4 differ diff --git a/src/main/resources/css/features/game_screen.css b/src/main/resources/css/features/game_screen.css index 49988c7..6277da4 100644 --- a/src/main/resources/css/features/game_screen.css +++ b/src/main/resources/css/features/game_screen.css @@ -235,8 +235,8 @@ } .trophy-container { - -fx-background-color: -surface-darker; - -fx-background-radius: 50%; + -fx-background-color: transparent; + -fx-background-radius: 0; -fx-padding: 16; -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.3), 10, 0, 0, 4); } diff --git a/src/main/resources/fxml/game_screen.fxml b/src/main/resources/fxml/game_screen.fxml index c87eb8d..c03b74b 100644 --- a/src/main/resources/fxml/game_screen.fxml +++ b/src/main/resources/fxml/game_screen.fxml @@ -41,8 +41,6 @@ - -