Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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();
}

Expand All @@ -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
}
}
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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();

Expand All @@ -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");
Comment thread
tasneem-hakeem marked this conversation as resolved.
}

PauseTransition pause = new PauseTransition(Duration.millis(800));
Expand All @@ -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();
}
Expand Down
Binary file added src/main/resources/assets/videos/handshake.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/resources/assets/videos/loser.mp4
Binary file not shown.
4 changes: 2 additions & 2 deletions src/main/resources/css/features/game_screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
16 changes: 3 additions & 13 deletions src/main/resources/fxml/game_screen.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@
<padding>
<Insets bottom="6" left="12" right="16" top="6"/>
</padding>
<Text styleClass="material-icon, difficulty-icon" text="smart_toy"/>
<Label fx:id="difficultyLabel" styleClass="difficulty-text" text="Hard Difficulty"/>
</HBox>
<Button fx:id="settingsButton" styleClass="icon-button">
<graphic>
Expand Down Expand Up @@ -147,9 +145,9 @@
<VBox alignment="CENTER">
<StackPane styleClass="trophy-container">
<MediaView fx:id="victoryVideo"
fitWidth="600"
visible="false"/>
<Text fx:id="modalIcon" styleClass="material-icon, trophy-icon" text="emoji_events"/>
visible="false"
fitWidth="450"
/>
Comment thread
tasneem-hakeem marked this conversation as resolved.
</StackPane>
</VBox>
</StackPane>
Expand All @@ -172,14 +170,6 @@
<Text styleClass="material-icon, button-icon" text="refresh"/>
</graphic>
</Button>

<Button fx:id="changeDifficultyButton" managed="false" maxWidth="Infinity"
styleClass="secondary-button" text="Change Difficulty" visible="false">
<graphic>
<Text styleClass="material-icon, button-icon" text="tune"/>
</graphic>
</Button>

<Button fx:id="mainMenuButton" maxWidth="Infinity" styleClass="text-button"
text="Back to Main Menu"/>
</VBox>
Expand Down
Loading