diff --git a/README.md b/README.md index 48951a0..621037b 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,8 @@ You will need the following software installed: * [Stacked-Success Versions](https://github.com/Stacked-Success/Softeng310/releases) **View individual versions**: -* [Stacked-Success v1.0.4](https://github.com/Stacked-Success/Softeng310/releases/tag/v.1.0.4) (Latest) +* [Stacked-Success Final v1.0.5](https://github.com/Stacked-Success/Softeng310/releases/tag/v.1.0.5) (Latest) +* [Stacked-Success v1.0.4](https://github.com/Stacked-Success/Softeng310/releases/tag/v.1.0.4) * [Stacked-Success v1.0.3](https://github.com/Stacked-Success/Softeng310/releases/tag/v1.0.3) * [Stacked-Success v1.0.2](https://github.com/Stacked-Success/Softeng310/releases/tag/v1.0.2) * [Stacked-Success v1.0.1](https://github.com/Stacked-Success/Softeng310/releases/tag/v1.0.1) diff --git a/marathon_score.txt b/marathon_score.txt index 7e3d5e2..e69de29 100644 --- a/marathon_score.txt +++ b/marathon_score.txt @@ -1,2 +0,0 @@ -10|20|300 -0|21|5 diff --git a/src/main/java/com/stackedsuccess/Main.java b/src/main/java/com/stackedsuccess/Main.java index 231650c..9d2be6d 100644 --- a/src/main/java/com/stackedsuccess/Main.java +++ b/src/main/java/com/stackedsuccess/Main.java @@ -6,7 +6,6 @@ import com.stackedsuccess.managers.SceneManager.AppUI; import javafx.application.Application; -import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; diff --git a/src/main/java/com/stackedsuccess/ScoreRecorder.java b/src/main/java/com/stackedsuccess/ScoreRecorder.java index 018904a..0ecf981 100644 --- a/src/main/java/com/stackedsuccess/ScoreRecorder.java +++ b/src/main/java/com/stackedsuccess/ScoreRecorder.java @@ -27,28 +27,22 @@ private ScoreRecorder() { * @throws IOException if an I/O error occurs */ public static void saveScore(String score) throws IOException { - HashMap scores = getAllScores(); // Get current scores as a HashMap + HashMap scores = getAllScores(); String playerName; if(NameEntryController.name == null || NameEntryController.name.equals("")){ playerName = "Anonymous"; } else { playerName = NameEntryController.name; } - // Add or update the score for the player scores.put(playerName, Integer.parseInt(score)); - // Sort the scores in descending order and keep only the top MAX_SCORES entries List> sortedScores = new ArrayList<>(scores.entrySet()); sortedScores.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue())); - - // Clear the map and add back only the top scores scores.clear(); for (int i = 0; i < Math.min(MAX_SCORES, sortedScores.size()); i++) { Map.Entry entry = sortedScores.get(i); scores.put(entry.getKey(), entry.getValue()); } - - // Write the sorted scores back to the file writeScores(scores); } @@ -64,8 +58,6 @@ public static String getHighScore() throws IOException { if (scores.isEmpty()) { return "No high score available."; } - - // Find the player with the highest score return Collections.max(scores.entrySet(), Map.Entry.comparingByValue()).toString(); } @@ -85,7 +77,7 @@ public static HashMap getAllScores() throws IOException { String line; while ((line = reader.readLine()) != null) { if (!line.trim().isEmpty()) { - String[] parts = line.split(" "); // Assuming scores are formatted as "Name Score" + String[] parts = line.split(" "); if (parts.length == 2) { String playerName = parts[0]; Integer playerScore = Integer.parseInt(parts[1]); @@ -106,7 +98,7 @@ public static HashMap getAllScores() throws IOException { static void writeScores(HashMap scores) throws IOException { try (BufferedWriter writer = new BufferedWriter(new FileWriter(SCOREFILE))) { for (HashMap.Entry entry : scores.entrySet()) { - writer.write(entry.getKey() + " " + entry.getValue()); // Write name and score + writer.write(entry.getKey() + " " + entry.getValue()); writer.newLine(); } } @@ -125,7 +117,6 @@ public static void createScoreFile() { try { boolean isFileCreated = scoreFile.createNewFile(); if (!isFileCreated) { - // Retry creating the file isFileCreated = scoreFile.createNewFile(); if (!isFileCreated) { throw new IOException("Failed to create score file after retrying."); @@ -151,7 +142,7 @@ public static void saveMarathonScore(int linesCleared, int targetLines, int time scores.add(newScore); if (scores.size() > MAX_SCORES) { - scores.remove(scores.size() - 1); // Keep only the top MAX_SCORES entries + scores.remove(scores.size() - 1); } writeMarathonScores(scores); } diff --git a/src/main/java/com/stackedsuccess/controllers/GameBoardController.java b/src/main/java/com/stackedsuccess/controllers/GameBoardController.java index 0202697..6d53400 100644 --- a/src/main/java/com/stackedsuccess/controllers/GameBoardController.java +++ b/src/main/java/com/stackedsuccess/controllers/GameBoardController.java @@ -89,14 +89,14 @@ public class GameBoardController implements GameStateManager { private static final int SOLID_BLOCK_VALUE = -2; - private int targetLines; // Stores the target number of lines in Marathon Mode - private boolean isMarathonMode; // Stores whether Marathon Mode is active + private int targetLines; + private boolean isMarathonMode; private GameInstance gameInstance; private final TetriminoImageManager imageManager; - private Timeline gameTimer; // JavaFX Timeline to schedule timer updates - private int elapsedSeconds; // Track the total number of elapsed seconds + private Timeline gameTimer; + private int elapsedSeconds; public GameBoardController() { imageManager = new TetriminoImageManager(); @@ -109,8 +109,8 @@ public GameBoardController() { */ public void setGameInstance(GameInstance gameInstance) { this.gameInstance = gameInstance; - this.isMarathonMode = gameInstance.getIsMarathonMode(); // Store whether it is Marathon Mode - this.targetLines = gameInstance.getTargetLines(); // Get the target lines if Marathon Mode is active + this.isMarathonMode = gameInstance.getIsMarathonMode(); + this.targetLines = gameInstance.getTargetLines(); } private TutorialController tutorialController; @@ -136,7 +136,6 @@ public void setGameInstance(GameInstance gameInstance) { */ @FXML public void initialize() { - // resets score, line, and level to initial state resetLabels(); basePane.requestFocus(); @@ -144,17 +143,12 @@ public void initialize() { if (gameInstance != null) { gameInstance.start(); - // Update next piece view nextPieceView .setImage(imageManager.getTetriminoImage(gameInstance.getGameBoard().getNextTetrimino().getClass())); - // Set window close handler setWindowCloseHandler(getStage()); - - // Initialize lineLabel for Marathon Mode if (isMarathonMode) { lineLabel.setText("0/" + targetLines); - // start timer startTimer(); } else { timerVbox.setVisible(false); @@ -163,7 +157,6 @@ public void initialize() { } }); - //sets the tutorial controller to return to the game tutorialController = new TutorialController(); tutorialController.setDestinationAppUI(AppUI.GAME); tutorialController.setHasTutorialBeenViewed(true); @@ -225,7 +218,6 @@ public void updateDisplay(int[][] board) { @FXML @Override public void gameOver() throws IOException { - // Stop the game timer if it is running if (gameTimer != null) { gameTimer.stop(); } @@ -233,19 +225,16 @@ public void gameOver() throws IOException { try { if (gameInstance.getIsMarathonMode()) { - // Save Marathon Mode Score int targetLines = gameInstance.getTargetLines(); - int timeTakenInSeconds = elapsedSeconds; // Assuming elapsedSeconds is tracking the timer + int timeTakenInSeconds = elapsedSeconds; ScoreRecorder.saveMarathonScore(linesCleared, targetLines, timeTakenInSeconds); } else { - // Save Basic Mode Score int finalScore = Integer.parseInt(scoreLabel.getText()); ScoreRecorder.saveScore(String.valueOf(finalScore)); } } catch (IOException e) { - // If there is an issue saving the score, throw an exception } @@ -280,7 +269,6 @@ public void onKeyPressed(KeyEvent event) { if (action == Action.PAUSE) { togglePauseScreen(); } - // otherwise passes the players input to the game instance gameInstance.handleInput(event); } @@ -364,6 +352,7 @@ public void onClickMainMenu(ActionEvent event) throws IOException { elapsedSeconds = 0; updateTimerLabel(); } + Platform.runLater(() -> SoundManager.getInstance().playBackgroundMusic("mainmenu")); } /** @@ -604,7 +593,6 @@ private void playGameOverAnimation() { }); animationTimeline.getKeyFrames().add(actionsKeyFrame); - // Remove solid blocks for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { int delay = 2000 + (row * 50); @@ -658,12 +646,10 @@ private void enableGameOverElements() { int targetLines = gameInstance.getTargetLines(); if (linesCleared >= targetLines) { - // Player won Marathon Mode gameOverLabel.setText("Victory!"); gameOverScoreLabel.setText("Score: " + linesCleared + "/" + targetLines); - gameOverHighScoreLabel.setVisible(false); // Hide high score for winning Marathon Mode + gameOverHighScoreLabel.setVisible(false); } else { - // Player lost Marathon Mode gameOverLabel.setText("Game Over"); gameOverScoreLabel.setText("Score: " + linesCleared + "/" + targetLines); try { @@ -674,7 +660,6 @@ private void enableGameOverElements() { } } } else { - // Basic Mode gameOverLabel.setText("Game Over"); gameOverScoreLabel.setText("Score: " + scoreLabel.getText()); try { diff --git a/src/main/java/com/stackedsuccess/controllers/HomeScreenController.java b/src/main/java/com/stackedsuccess/controllers/HomeScreenController.java index 52c8f59..cc3c158 100644 --- a/src/main/java/com/stackedsuccess/controllers/HomeScreenController.java +++ b/src/main/java/com/stackedsuccess/controllers/HomeScreenController.java @@ -8,10 +8,7 @@ import com.stackedsuccess.managers.SceneManager.AppUI; import com.stackedsuccess.managers.sound.SoundManager; import java.io.IOException; - -import java.util.ArrayList; import java.util.HashMap; - import java.util.List; import javafx.application.Platform; @@ -68,9 +65,8 @@ public class HomeScreenController { @FXML public void initialize() throws IOException { difficultySlider.requestFocus(); - // Optionally load scores during initialization - loadBasicModeScores(); // Load and set scores for Basic Mode - loadMarathonModeScores(); // Load and set scores for Marathon Mode + loadBasicModeScores(); + loadMarathonModeScores(); pastScoresButton.setFocusTraversable(false); tutorialController = new TutorialController(); @@ -82,18 +78,20 @@ public void initialize() throws IOException { basicBtn.setSelected(true); marathonBtn.setSelected(false); - // Ensure files exist ScoreRecorder.createMarathonScoreFile(); ScoreRecorder.createScoreFile(); - // Load the tutorial screen FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/Tutorial.fxml")); loader.setController(tutorialController); Parent root = loader.load(); SceneManager.addScene(AppUI.HOME_TUTORIAL, root); } - // Method to set the GameInstance + /** + * Sets the game instance for this controller. + * + * @param gameInstance the GameInstance to be set. + */ public void setGameInstance(GameInstance gameInstance) { this.gameInstance = gameInstance; } @@ -166,12 +164,18 @@ public void toggleMusic() { } } + /** + * Sets Marathon mode as selected and disables Basic mode selection. + */ @FXML public void toggleMarathon() { marathonBtn.setSelected(true); basicBtn.setSelected(false); } + /** + * Sets Basic mode as selected and disables Marathon mode selection. + */ @FXML void toggleBasic() { basicBtn.setSelected(true); @@ -196,21 +200,33 @@ public void onSettings() { mainPane.setDisable(true); } + /** + * Opens the difficulty settings by displaying the difficulty pane and disabling the main pane. + */ public void onDifficulty() { difficultyPane.setVisible(true); mainPane.setDisable(true); } + /** + * Closes the settings menu and re-enables the main pane. + */ public void onSettingsBack() { settingsPane.setVisible(false); mainPane.setDisable(false); } + /** + * Closes the difficulty settings pane and re-enables the main pane. + */ public void onDifficultyBack() { difficultyPane.setVisible(false); mainPane.setDisable(false); } + /** + * Closes the past scores pane and re-enables the main pane. + */ public void onPastScoreBack() { pastScorePane.setVisible(false); mainPane.setDisable(false); @@ -273,21 +289,14 @@ private void runGame() throws IOException { setGameConditions(); loadGame(); } else { - // Tell the turorial that it needs to create a new game when the user is - // finished tutorialController.setCreateGame(true); - - // Load the tutorial goToTutorial(); - - // Creates the game when the tutorial is completed Runnable onTutorialCompleted = () -> { try { setGameConditions(); loadGame(); } catch (IOException e) { - // Do nothing for now } }; tutorialController.setOnTutorialCompleted(onTutorialCompleted); @@ -301,11 +310,7 @@ private void runGame() throws IOException { * the player selected Marathon Mode or Basic Mode. */ private void setGameConditions() { - - // Get the level from the slider initialLevel = (int) difficultySlider.getValue(); - - // Determine which mode the player selected isMarathonMode = marathonBtn.isSelected(); } @@ -321,26 +326,17 @@ private void setGameConditions() { */ @FXML public void loadGame() throws IOException { - - // Calculate target lines based on difficulty level (for Marathon Mode) int targetLines = - calculateTargetLines(initialLevel); // Higher difficulty means more lines to clear + calculateTargetLines(initialLevel); - // loads the game board fxml file FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/GameBoard.fxml")); Parent root = loader.load(); GameBoardController controller = loader.getController(); - - // Create a GameStateManager implementation to handle the state updates GameStateManager gameStateManager = controller; - - // Create a new GameInstance with the state manager this.gameInstance = new GameInstance(gameStateManager, isMarathonMode, targetLines); - - // Pass the GameInstance to the controller for reference controller.setGameInstance(gameInstance); - // Set the initial level + controller.updateLevel(initialLevel); SceneManager.addScene(AppUI.GAME, root); Main.setUi(AppUI.GAME); @@ -380,6 +376,7 @@ public void onKeyPressed(KeyEvent event) { */ @FXML public void showPastScores() { + mainPane.setDisable(true); pastScorePane.setVisible(true); MarathonPastScores.setVisible(true); @@ -416,7 +413,7 @@ public void goToTutorial() { */ private int calculateTargetLines(int difficultyLevel) { return difficultyLevel - + 10; // Adjust the multiplier as needed, e.g., 10 lines per difficulty level. + + 10; } /** @@ -426,8 +423,6 @@ private int calculateTargetLines(int difficultyLevel) { */ @FXML public void onClickBasicMode() { - - // Hide the Marathon Mode scores and related image, show Basic Mode scores and related image MarathonPastScores.setVisible(false); marathonScoreImageView.setVisible(true); @@ -440,7 +435,6 @@ public void onClickBasicMode() { clickBasic.toFront(); clickMarathon.toFront(); - // Load and display Basic Mode scores loadBasicModeScores(); } @@ -453,7 +447,6 @@ public void onClickBasicMode() { public void onClickMarathonMode() { System.out.println("Marathon Mode clicked"); - // Hide the Basic Mode scores and related image, show Marathon Mode scores and related image basicPastScores.setVisible(false); basicScoreImageView.setVisible(true); @@ -465,16 +458,15 @@ public void onClickMarathonMode() { clickBasic.toFront(); clickMarathon.toFront(); - // Load and display Marathon Mode scores loadMarathonModeScores(); } private void loadBasicModeScores() { try { - HashMap basicScores = ScoreRecorder.getAllScores(); // Now using HashMap + HashMap basicScores = ScoreRecorder.getAllScores(); if (!basicScores.isEmpty()) { - for (HashMap.Entry entry : basicScores.entrySet()) { // Using HashMap.Entry - String formattedScore = entry.getKey() + " " + entry.getValue(); // "Name Score" formatted string + for (HashMap.Entry entry : basicScores.entrySet()) { + String formattedScore = entry.getKey() + " " + entry.getValue(); basicPastScores.getItems().add(formattedScore); } } else { diff --git a/src/main/java/com/stackedsuccess/controllers/NameEntryController.java b/src/main/java/com/stackedsuccess/controllers/NameEntryController.java index 9421721..56aea65 100644 --- a/src/main/java/com/stackedsuccess/controllers/NameEntryController.java +++ b/src/main/java/com/stackedsuccess/controllers/NameEntryController.java @@ -15,8 +15,18 @@ public class NameEntryController { private Runnable onNameEntryCompleted; public static String name; + /** + * Initializes the controller after the FXML file has been loaded. Currently, this method does not + * perform any actions. + */ public void initialize() {} + /** + * Handles the event when the user clicks the advance button. Retrieves the entered name from the + * text field and runs the callback (onNameEntryCompleted) to proceed in the game. + * + * @throws IOException if an I/O error occurs during name entry processing + */ @FXML private void onAdvance() throws IOException { name = textEntryField.getText(); @@ -25,6 +35,11 @@ private void onAdvance() throws IOException { } } + /** + * Sets the callback to be executed when the name entry is completed. + * + * @param onNameEntryCompleted a Runnable to execute when the name is entered and submitted + */ public void setOnNameCompleted(Runnable onNameEntryCompleted) { this.onNameEntryCompleted = onNameEntryCompleted; } diff --git a/src/main/java/com/stackedsuccess/controllers/SkinShopController.java b/src/main/java/com/stackedsuccess/controllers/SkinShopController.java index 66a08cf..ceae520 100644 --- a/src/main/java/com/stackedsuccess/controllers/SkinShopController.java +++ b/src/main/java/com/stackedsuccess/controllers/SkinShopController.java @@ -8,8 +8,6 @@ import java.io.IOException; import java.util.List; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -47,12 +45,28 @@ public void initialize() { /** Updates the high score label with the current high score. */ private void updateHighScoreLabel() { try { - String highScore = ScoreRecorder.getHighScore(); - highScoreLabel.setText("High Score: " + highScore); + String highScore = ScoreRecorder.getHighScore(); + + if (highScore.equals("No high score available.")) { + highScoreLabel.setText("High Score: 0"); + } else { + String[] parts = highScore.split("="); + + if (parts.length == 2) { + try { + int parsedScore = Integer.parseInt(parts[1]); + highScoreLabel.setText("High Score: " + parsedScore); + } catch (NumberFormatException e) { + highScoreLabel.setText("High Score: 0"); + } + } else { + highScoreLabel.setText("High Score: 0"); + } + } } catch (IOException e) { - highScoreLabel.setText("High Score: 0"); + highScoreLabel.setText("High Score: 0"); } - } +} /** Populates the skin selection display with available skins. */ private void populateSkinDisplay() { diff --git a/src/main/java/com/stackedsuccess/controllers/TutorialController.java b/src/main/java/com/stackedsuccess/controllers/TutorialController.java index d577f77..4619c99 100644 --- a/src/main/java/com/stackedsuccess/controllers/TutorialController.java +++ b/src/main/java/com/stackedsuccess/controllers/TutorialController.java @@ -8,101 +8,135 @@ public class TutorialController { - @FXML - public ImageView exitBtn; - - //Used to check if the tutorial has been viewed - //If not the tutorial will be displayed when the game is started - private boolean hasTutorialBeenViewed = false; - private boolean createGame = false; - private AppUI destinationAppUI = AppUI.MAIN_MENU; - public Runnable onTutorialCompleted; - - - public void initialize() { - //Utilize the deafault constructor build into javafx - } - - /** - * This method expands the size of the exit cross when it is hovered - * over to help users recognise that it is a clickable object - * - * @param event the action of the user hovering their mouse over the exit cross - */ - @FXML - public void onExitHoveredOver(MouseEvent event) { - exitBtn.scaleXProperty().set(1.2); - exitBtn.scaleYProperty().set(1.2); - } - - /** - * Retruns the size of the exit cross to its original size when the user - * is no longer hovering over it - * - * @param event the users mouse being moved away from the exit cross - */ - @FXML - public void onExitNotHoveredOver(MouseEvent event) { - exitBtn.scaleXProperty().set(1); - exitBtn.scaleYProperty().set(1); - } - - /** - * This method closes the tutorial screen and returns the user to their game - * when the user clicks on the exit cross. If appearing before the first game, - * we have to create the game here to stop it starting in the background - * - * @param event the action of clicking the exit xross - */ - @FXML - public void onClickExit(MouseEvent event) { - - hasTutorialBeenViewed = true; - - //If the tutorial is being displayed before the first game, create the game - if (createGame) { - onTutorialCompleted.run(); - createGame = false; - } else { - //Return to the desired screen - Main.setUi(destinationAppUI); - } - } - - /** - * Getters/Setters to check if Tutorial has been viewed and completed, To create the game, to get the Destination scene. - * @return - */ - public boolean getHasTutorialBeenViewed() { - return hasTutorialBeenViewed; - } - - public void setHasTutorialBeenViewed(boolean hasTutorialBeenViewed) { - this.hasTutorialBeenViewed = hasTutorialBeenViewed; - } - - public boolean getCreateGame() { - return createGame; - } - - public void setCreateGame(boolean createGame) { - this.createGame = createGame; - } - - public AppUI getDestinationAppUI() { - return destinationAppUI; - } - - public void setDestinationAppUI(AppUI destinationAppUI) { - this.destinationAppUI = destinationAppUI; - } - - public Runnable getOnTutorialCompleted() { - return onTutorialCompleted; - } - - public void setOnTutorialCompleted(Runnable onTutorialCompleted) { - this.onTutorialCompleted = onTutorialCompleted; + @FXML public ImageView exitBtn; + + // Used to check if the tutorial has been viewed + // If not the tutorial will be displayed when the game is started + private boolean hasTutorialBeenViewed = false; + private boolean createGame = false; + private AppUI destinationAppUI = AppUI.MAIN_MENU; + public Runnable onTutorialCompleted; + + public void initialize() { + // Utilize the deafault constructor build into javafx + } + + /** + * This method expands the size of the exit cross when it is hovered over to help users recognise + * that it is a clickable object + * + * @param event the action of the user hovering their mouse over the exit cross + */ + @FXML + public void onExitHoveredOver(MouseEvent event) { + exitBtn.scaleXProperty().set(1.2); + exitBtn.scaleYProperty().set(1.2); + } + + /** + * Retruns the size of the exit cross to its original size when the user is no longer hovering + * over it + * + * @param event the users mouse being moved away from the exit cross + */ + @FXML + public void onExitNotHoveredOver(MouseEvent event) { + exitBtn.scaleXProperty().set(1); + exitBtn.scaleYProperty().set(1); + } + + /** + * This method closes the tutorial screen and returns the user to their game when the user clicks + * on the exit cross. If appearing before the first game, we have to create the game here to stop + * it starting in the background + * + * @param event the action of clicking the exit xross + */ + @FXML + public void onClickExit(MouseEvent event) { + + hasTutorialBeenViewed = true; + + // If the tutorial is being displayed before the first game, create the game + if (createGame) { + onTutorialCompleted.run(); + createGame = false; + } else { + // Return to the desired screen + Main.setUi(destinationAppUI); } - + } + + /** + * Returns whether the tutorial has been viewed or not. + * + * @return true if the tutorial has been viewed, false otherwise. + */ + public boolean getHasTutorialBeenViewed() { + return hasTutorialBeenViewed; + } + + /** + * Sets whether the tutorial has been viewed or not. + * + * @param hasTutorialBeenViewed true if the tutorial has been viewed, false otherwise. + */ + public void setHasTutorialBeenViewed(boolean hasTutorialBeenViewed) { + this.hasTutorialBeenViewed = hasTutorialBeenViewed; + } + + /** + * Returns whether a new game should be created after the tutorial is completed. + * + * @return true if the game should be created, false otherwise. + */ + public boolean getCreateGame() { + return createGame; + } + + /** + * Sets whether a new game should be created after the tutorial is completed. + * + * @param createGame true if the game should be created, false otherwise. + */ + public void setCreateGame(boolean createGame) { + this.createGame = createGame; + } + + /** + * Returns the destination UI to navigate to after the tutorial is closed. + * + * @return The destination AppUI enum representing the screen to navigate to. + */ + public AppUI getDestinationAppUI() { + return destinationAppUI; + } + + /** + * Sets the destination UI to navigate to after the tutorial is closed. + * + * @param destinationAppUI The AppUI enum representing the screen to navigate to. + */ + public void setDestinationAppUI(AppUI destinationAppUI) { + this.destinationAppUI = destinationAppUI; + } + + /** + * Returns the runnable action that will be executed when the tutorial is completed. + * + * @return A Runnable representing the action to execute when the tutorial is completed. + */ + public Runnable getOnTutorialCompleted() { + return onTutorialCompleted; + } + + /** + * Sets the runnable action to be executed when the tutorial is completed. + * + * @param onTutorialCompleted A Runnable representing the action to execute when the tutorial is + * completed. + */ + public void setOnTutorialCompleted(Runnable onTutorialCompleted) { + this.onTutorialCompleted = onTutorialCompleted; + } } diff --git a/src/main/java/com/stackedsuccess/managers/SkinDisplayMaker.java b/src/main/java/com/stackedsuccess/managers/SkinDisplayMaker.java index 909b6df..517175d 100644 --- a/src/main/java/com/stackedsuccess/managers/SkinDisplayMaker.java +++ b/src/main/java/com/stackedsuccess/managers/SkinDisplayMaker.java @@ -20,7 +20,7 @@ public class SkinDisplayMaker { private final int paneHeight = 250; private final int imageWidth = 120; private final int imageHeight = 120; - + private int pointsIncrement = 0; /** @@ -31,10 +31,10 @@ public class SkinDisplayMaker { * @return the pane containing the skin preview, name, points label, and selection/locked button */ public VBox createSkinPane(String themeName, EventHandler selectSkinHandler) { - VBox pane = new VBox(10); // Add spacing between elements + VBox pane = new VBox(10); pane.setPrefSize(paneWidth, paneHeight); pane.setAlignment(Pos.CENTER); - + ImageView skinImageView = createImageView(themeName); String displayName = themeName.replace("Skin", ""); @@ -48,8 +48,14 @@ public VBox createSkinPane(String themeName, EventHandler selectSki int highScore; try { - highScore = Integer.parseInt(ScoreRecorder.getHighScore()); - } catch (IOException e) { + String highScoreStr = ScoreRecorder.getHighScore(); + + if (highScoreStr.equals("No high score available.")) { + highScore = 0; + } else { + highScore = Integer.parseInt(highScoreStr.split("=")[1].trim()); + } + } catch (IOException | NumberFormatException e) { highScore = 0; } @@ -67,7 +73,7 @@ public VBox createSkinPane(String themeName, EventHandler selectSki pane.getChildren().addAll(skinImageView, skinName, pointsLabel, skinButton); VBox.setVgrow(skinImageView, Priority.ALWAYS); - + return pane; } @@ -96,7 +102,7 @@ public VBox createComingSoonPane() { Button lockedButton = new Button("Locked"); lockedButton.setFont(new Font(16)); - lockedButton.setDisable(true); + lockedButton.setDisable(true); pane.getChildren().addAll(skinImageView, comingSoonText, pointsLabel, lockedButton); VBox.setVgrow(skinImageView, Priority.ALWAYS); @@ -115,12 +121,12 @@ public VBox createComingSoonPane() { private ImageView createImageView(String themeName) { String imagePath = "file:src/main/resources/images/" + themeName + "/block.png"; Image image = new Image(imagePath, imageWidth, imageHeight, true, true); - + ImageView imageView = new ImageView(image); imageView.setFitWidth(imageWidth); imageView.setFitHeight(imageHeight); imageView.setPreserveRatio(true); - + return imageView; } } diff --git a/src/main/resources/fxml/HomeScreen.fxml b/src/main/resources/fxml/HomeScreen.fxml index 21147da..8cfc08a 100644 --- a/src/main/resources/fxml/HomeScreen.fxml +++ b/src/main/resources/fxml/HomeScreen.fxml @@ -87,11 +87,6 @@ - @@ -102,6 +97,11 @@ + diff --git a/src/test/java/com/stackedsuccess/MarathonModeTest.java b/src/test/java/com/stackedsuccess/MarathonModeTest.java index 2bacbc4..8837bc7 100644 --- a/src/test/java/com/stackedsuccess/MarathonModeTest.java +++ b/src/test/java/com/stackedsuccess/MarathonModeTest.java @@ -19,9 +19,6 @@ import com.stackedsuccess.managers.GameStateManager; import com.stackedsuccess.tetriminos.Tetrimino; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; - public class MarathonModeTest { @Mock @@ -46,32 +43,21 @@ public void setup() { @Test public void testSaveBasicScore() throws IOException { - // Clear existing scores - ScoreRecorder.writeScores(new HashMap<>()); // Assuming writeScores accepts a HashMap - - // Save a new score + ScoreRecorder.writeScores(new HashMap<>()); String score = "15"; ScoreRecorder.saveScore(score); - - // Verify the score is saved correctly HashMap scoresMap = ScoreRecorder.getAllScores(); assertEquals(1, scoresMap.size()); - assertEquals(15, scoresMap.get("Anonymous")); } @Test void testSaveMarathonScore() throws IOException { - // Clear existing scores ScoreRecorder.writeMarathonScores(List.of()); - - // Save a new Marathon Mode score int linesCleared = 10; int targetLines = 20; - int timeTaken = 300; // 5 minutes + int timeTaken = 300; ScoreRecorder.saveMarathonScore(linesCleared, targetLines, timeTaken); - - // Verify the score is saved correctly List marathonScores = ScoreRecorder.getAllMarathonScores(); assertEquals(1, marathonScores.size()); assertEquals("10|20|300", marathonScores.get(0)); @@ -87,7 +73,6 @@ public void testInitialBoardSetup() { @Test public void testCollisionDetection() { - // Test collision with the board boundaries assertTrue(gameBoard.isOutOfBounds(-1, 0)); assertTrue(gameBoard.isOutOfBounds(10, 0)); assertTrue(gameBoard.isOutOfBounds(0, 22)); @@ -106,7 +91,6 @@ public void testHoldTetrimino() { Tetrimino currentTetrimino = gameBoard.getCurrentTetrimino(); gameBoard.holdTetrimino(); Tetrimino holdTetrimino = gameBoard.getHoldTetrimino(); - assertEquals(currentTetrimino, holdTetrimino); assertTrue(gameBoard.getHoldTetrimino() != null); assertFalse(gameBoard.getHoldTetrimino() == gameBoard.getCurrentTetrimino()); // Confirm swap @@ -116,7 +100,6 @@ public void testHoldTetrimino() { public void testPauseGame() { gameInstance.togglePause(); assertTrue(gameInstance.isPaused()); - gameInstance.togglePause(); assertFalse(gameInstance.isPaused()); } @@ -158,7 +141,6 @@ public void testSaveAndRetrieveScore() throws IOException { public void testHighScore() throws IOException { ScoreRecorder.saveScore("1000"); ScoreRecorder.saveScore("2000"); - String highScore = ScoreRecorder.getHighScore(); assertEquals("Anonymous=2000", highScore); } diff --git a/src/test/java/com/stackedsuccess/TetriminoImageManagerTest.java b/src/test/java/com/stackedsuccess/TetriminoImageManagerTest.java index 6c5dac6..0cf752c 100644 --- a/src/test/java/com/stackedsuccess/TetriminoImageManagerTest.java +++ b/src/test/java/com/stackedsuccess/TetriminoImageManagerTest.java @@ -3,9 +3,6 @@ import com.stackedsuccess.managers.TetriminoImageManager; import com.stackedsuccess.tetriminos.*; import javafx.scene.image.Image; -import javafx.application.Platform; - -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test;