Skip to content

5 difficulty levels added for issue #96 #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

arvi18
Copy link

@arvi18 arvi18 commented Apr 15, 2025

👉 Checklist

Please make sure to check off the following before submitting:

  • I have reviewed my submission thoroughly.
  • I have tested my code (if submission is related to coding) and run the game before pushing (to make sure the project compile).
  • I have run the JUnit tests (if submission is related to coding).
  • I have read the Code of Conduct and Contribution Guidelines.

✍ Description of the Pull Request

Please concisely describe the changes you have made.

  • classes Difficulty.java and GameSettings.java added
  • GreenDinoComponent.java and DinosaurMenu.java modified
  • Modifications to other files are unnecessary

🔗 Issue link

Issue reference number and link goes here e.g. 'Fixes/Closes jvondermarck#96'.

  • This Pull Request fixes the issue : 'Closes #'
  • This Pull Request does not fix an issue.

Thanks for taking the time to fill out this Pull Request! ❤️ Thanks for contributing to this project 🦖

Summary by CodeRabbit

  • New Features

    • Added a difficulty selection option (levels 1–5) to the main menu, allowing users to adjust game challenge.
    • Game difficulty now dynamically affects enemy movement speed and projectile angles during gameplay.
  • Improvements

    • Main menu layout redesigned for a cleaner, more organized appearance using structured containers.
    • Volume control simplified and made more intuitive with a slider and real-time label updates.
    • Centralized and improved image loading with better error handling.
    • Projectiles now spawn from the center of the enemy for more consistent visuals.
  • Bug Fixes

    • Corrected a minor typo in an in-code comment (no impact on gameplay).

@arvi18
Copy link
Author

arvi18 commented Apr 15, 2025

The user can choose a difficulty level at the start of the game.
image
As the difficulty increments, the dinosaur can move faster and send bullets in a wider range of angle.
image

@visz11
Copy link
Collaborator

visz11 commented Apr 23, 2025

@coderabbitai full review

Copy link

coderabbitai bot commented Apr 23, 2025

✅ Actions performed

Full review triggered.

Copy link

coderabbitai bot commented Apr 23, 2025

Walkthrough

This update introduces a difficulty management system to the project, including a new Difficulty class and a singleton GameSettings class to manage game difficulty globally. The GreenDinoComponent now uses difficulty-dependent parameters for movement speed and shooting angle, and the projectile spawn position is centered. The main menu UI in DinosaurMenu is refactored to use JavaFX layout containers, includes a difficulty selection combo box, and simplifies image handling with a new helper method. Additionally, a comment typo is corrected in LifeComponent, and the Java language level setting in the project configuration is changed.

Changes

File(s) Change Summary
.idea/misc.xml Changed Java language level from "JDK_21" to "JDK_X".
src/main/java/com/dinosaur/dinosaurexploder/model/Difficulty.java Added new public class Difficulty for encapsulating speed and angle offset logic based on difficulty.
src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java Added new singleton class GameSettings to manage and provide global access to difficulty settings.
src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java Refactored to use Difficulty for movement speed and projectile angle; centered projectile spawn position; made timer field final.
src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java Fixed comment typo ("eachother" → "each other").
src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java Refactored UI to use JavaFX containers, added difficulty combo box, centralized image loading, removed mute toggle, and standardized control layout.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DinosaurMenu
    participant GameSettings
    participant GreenDinoComponent
    participant Difficulty

    User->>DinosaurMenu: Selects difficulty from combo box
    DinosaurMenu->>GameSettings: setDifficultyLevel(level)
    GameSettings->>Difficulty: Create new Difficulty instance

    GreenDinoComponent->>GameSettings: getInstance()
    GreenDinoComponent->>GameSettings: getDifficulty()
    GameSettings->>Difficulty: Return Difficulty instance

    GreenDinoComponent->>Difficulty: getSpeed()/getAngleOffset()
    Difficulty-->>GreenDinoComponent: Returns speed/angle for game logic
Loading

Poem

In a field of code where dinos roam,
Difficulty now calls this place home.
Menus are neat, with choices to make,
Speed and angles shift with each step we take.
A rabbit hops by, with a wink and a cheer—
“Level up, little dino, your challenge is here!”
🦖🐇

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
src/main/java/com/dinosaur/dinosaurexploder/model/Difficulty.java (1)

5-27: Well-structured Difficulty class with room for improvement.

The class effectively encapsulates difficulty parameters (speed and angle offsets) with clean getters. However, consider these improvements:

  1. Replace the assertion with a proper exception:
-        assert max > min;
+        if (max <= min) {
+            throw new IllegalArgumentException("Maximum angle offset must be greater than minimum angle offset");
+        }
  1. Add JavaDoc comments to document the class purpose and method behaviors:
+/**
+ * Encapsulates game difficulty parameters including movement speed and projectile angle range.
+ */
 public class Difficulty {
     private final double speed;
     private final double minAngleOffset;
     private final double maxAngleOffset;
     private final Random random = new Random();

+    /**
+     * Creates a difficulty configuration with specified parameters.
+     * 
+     * @param speed The movement speed for this difficulty
+     * @param min The minimum angle offset for projectiles in degrees
+     * @param max The maximum angle offset for projectiles in degrees
+     * @throws IllegalArgumentException if max is not greater than min
+     */
     public Difficulty(double speed, double min, double max) {
src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java (1)

59-68: Add missing getter for difficultyLevel.

The class has a getter for difficulty but is missing a getter for difficultyLevel.

     public Difficulty getDifficulty() {
         return difficulty;
     }
 
+    public int getDifficultyLevel() {
+        return difficultyLevel;
+    }
+
     public void setDifficultyLevel(int level) {
         this.difficultyLevel = level;
         difficulty = createDifficulty();
     }
src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java (3)

100-103: Avoid hard‑coding image path – reuse a constant

The texture path is duplicated here while similar paths live in GameConstants.
This increases the risk of typos or future drift.

-ImageView imageView = createImageView("assets/textures/dinomenu.png", 300, 400);
+ImageView imageView = createImageView(GameConstants.GREENDINO_IMAGEPATH /* or introduce DINOMENU constant */, 300, 400);

Consider adding a DINOMENU_IMAGEPATH constant to GameConstants and referencing it to keep all resource paths in one place.


117-118: Stop menu music when quitting to release audio resources cleanly

Exiting the application without stopping the media player can keep native audio handles open on some platforms.

-quitButton.setOnAction(event -> fireExit());
+quitButton.setOnAction(event -> {
+    mainMenuSound.stop();
+    fireExit();
+});

Minor, but prevents potential “application still using audio device” warnings.


126-140: Improve image loading fallback & error signalling

Returning a blank ImageView on any IOException hides the root cause and leaves an empty UI element that might go unnoticed.

} catch (IOException e) {
-    e.printStackTrace();
-    return new ImageView();
+    // Log once through FXGL’s logger, then propagate as unchecked to fail fast.
+    FXGL.getLogger(getClass()).error("Failed to load resource: {}", path, e);
+    throw new UncheckedIOException(e);
 }

Fail‑fast behavior surfaces broken resource packaging during development instead of silently degrading in production.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 623d77b and 38f4065.

📒 Files selected for processing (6)
  • .idea/misc.xml (1 hunks)
  • src/main/java/com/dinosaur/dinosaurexploder/model/Difficulty.java (1 hunks)
  • src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java (1 hunks)
  • src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java (3 hunks)
  • src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java (1 hunks)
  • src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java (1)
src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java (1)
  • GameSettings (3-69)
src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java (2)
src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java (1)
  • GameSettings (3-69)
src/main/java/com/dinosaur/dinosaurexploder/model/GameConstants.java (1)
  • GameConstants (11-44)
🔇 Additional comments (2)
src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java (1)

42-42: Corrected comment typo for clarity.

Updating “eachother” to “each other” improves readability and maintains professional code comments.

.idea/misc.xml (1)

11-11: Check Java language level configuration.

The languageLevel attribute has been changed to "JDK_X", which appears to be a placeholder rather than a valid Java version. Meanwhile, the project JDK name is still set to "19". This inconsistency might cause compilation or IDE configuration issues.

If this was an intentional change to make the project more portable, consider using a more standard approach such as setting the language level to match the project JDK or using a specific version like "JDK_19".

Comment on lines +3 to +9
public class GameSettings {
// Declare private static instance of the class
private static GameSettings instance;

// Global variables
private int difficultyLevel;
private Difficulty difficulty;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Singleton implementation lacks thread safety.

The singleton pattern is implemented properly for single-threaded scenarios, but lacks thread safety for multi-threaded environments.

Consider using the initialization-on-demand holder idiom for thread-safe lazy initialization:

 public class GameSettings {
-    // Declare private static instance of the class
-    private static GameSettings instance;
+    // Private static holder class for lazy initialization
+    private static class InstanceHolder {
+        private static final GameSettings INSTANCE = new GameSettings();
+    }

     // Global variables
     private int difficultyLevel;
     private Difficulty difficulty;

Then update the getInstance() method:

     // Public static method to provide access to the instance
     public static GameSettings getInstance() {
-        if (instance == null) {
-            instance = new GameSettings();
-        }
-        return instance;
+        return InstanceHolder.INSTANCE;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public class GameSettings {
// Declare private static instance of the class
private static GameSettings instance;
// Global variables
private int difficultyLevel;
private Difficulty difficulty;
public class GameSettings {
// Private static holder class for lazy initialization
private static class InstanceHolder {
private static final GameSettings INSTANCE = new GameSettings();
}
// Global variables
private int difficultyLevel;
private Difficulty difficulty;
// Public static method to provide access to the instance
public static GameSettings getInstance() {
return InstanceHolder.INSTANCE;
}
// ... other methods ...
}

Comment on lines +17 to +49
private Difficulty createDifficulty() {
double speed, min, max;
switch (difficultyLevel) {
case 1:
speed = 1.0;
min = 90;
max = 90;
break;
case 2:
speed = 2.0;
min = 90;
max = 90;
break;
case 3:
speed = 2.5;
min = 90;
max = 90;
break;
case 4:
speed = 2.5;
min = 22.5;
max = 112.5;
break;
case 5:
speed = 3.0;
min = 45;
max = 135;
break;
default:
throw new IllegalArgumentException("Unknown difficulty level!");
}
return new Difficulty(speed, min, max);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation and improve difficulty configurations.

The createDifficulty() method handles unknown difficulty levels with an exception, but the input is not validated in the setter.

  1. Add validation in setDifficultyLevel:
     public void setDifficultyLevel(int level) {
+        if (level < 1 || level > 5) {
+            throw new IllegalArgumentException("Difficulty level must be between 1 and 5");
+        }
         this.difficultyLevel = level;
         difficulty = createDifficulty();
     }
  1. Consider using constants for difficulty parameters to improve readability:
+    // Difficulty constants
+    private static final double[] DIFFICULTY_SPEEDS = {1.0, 2.0, 2.5, 2.5, 3.0};
+    private static final double[] MIN_ANGLE_OFFSETS = {90, 90, 90, 22.5, 45};
+    private static final double[] MAX_ANGLE_OFFSETS = {90, 90, 90, 112.5, 135};
+
     private Difficulty createDifficulty() {
-        double speed, min, max;
-        switch (difficultyLevel) {
-            case 1:
-                speed = 1.0;
-                min = 90;
-                max = 90;
-                break;
-            case 2:
-                speed = 2.0;
-                min = 90;
-                max = 90;
-                break;
-            case 3:
-                speed = 2.5;
-                min = 90;
-                max = 90;
-                break;
-            case 4:
-                speed = 2.5;
-                min = 22.5;
-                max = 112.5;
-                break;
-            case 5:
-                speed = 3.0;
-                min = 45;
-                max = 135;
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown difficulty level!");
+        if (difficultyLevel < 1 || difficultyLevel > 5) {
+            throw new IllegalArgumentException("Unknown difficulty level!");
         }
-        return new Difficulty(speed, min, max);
+        int index = difficultyLevel - 1;
+        return new Difficulty(
+            DIFFICULTY_SPEEDS[index],
+            MIN_ANGLE_OFFSETS[index],
+            MAX_ANGLE_OFFSETS[index]
+        );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private Difficulty createDifficulty() {
double speed, min, max;
switch (difficultyLevel) {
case 1:
speed = 1.0;
min = 90;
max = 90;
break;
case 2:
speed = 2.0;
min = 90;
max = 90;
break;
case 3:
speed = 2.5;
min = 90;
max = 90;
break;
case 4:
speed = 2.5;
min = 22.5;
max = 112.5;
break;
case 5:
speed = 3.0;
min = 45;
max = 135;
break;
default:
throw new IllegalArgumentException("Unknown difficulty level!");
}
return new Difficulty(speed, min, max);
}
public void setDifficultyLevel(int level) {
if (level < 1 || level > 5) {
throw new IllegalArgumentException("Difficulty level must be between 1 and 5");
}
this.difficultyLevel = level;
difficulty = createDifficulty();
}
// Difficulty constants
private static final double[] DIFFICULTY_SPEEDS = {1.0, 2.0, 2.5, 2.5, 3.0};
private static final double[] MIN_ANGLE_OFFSETS = {90, 90, 90, 22.5, 45};
private static final double[] MAX_ANGLE_OFFSETS = {90, 90, 90, 112.5, 135};
private Difficulty createDifficulty() {
if (difficultyLevel < 1 || difficultyLevel > 5) {
throw new IllegalArgumentException("Unknown difficulty level!");
}
int index = difficultyLevel - 1;
return new Difficulty(
DIFFICULTY_SPEEDS[index],
MIN_ANGLE_OFFSETS[index],
MAX_ANGLE_OFFSETS[index]
);
}

Comment on lines +17 to +18
public Difficulty difficulty = GameSettings.getInstance().getDifficulty();
private final LocalTimer timer = FXGL.newLocalTimer();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Difficulty should be dynamically accessed, not stored.

The current implementation initializes the difficulty field only once, which means it won't reflect changes to the difficulty level during gameplay.

-    public Difficulty difficulty = GameSettings.getInstance().getDifficulty();
+    // Don't store the difficulty directly, access it when needed
     private final LocalTimer timer = FXGL.newLocalTimer();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +44 to +46
Vec2 direction = Vec2.fromAngle(entity.getRotation() + difficulty.getAngleOffset());
spawn("basicEnemyProjectile",
new SpawnData(center.getX() + 50 +3, center.getY())
new SpawnData(center.getX(), center.getY())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Access current difficulty settings dynamically.

Similar to the issue with movement speed, the angle offset calculation should also dynamically access the current difficulty settings.

-        Vec2 direction = Vec2.fromAngle(entity.getRotation() + difficulty.getAngleOffset());
+        Vec2 direction = Vec2.fromAngle(entity.getRotation() + GameSettings.getInstance().getDifficulty().getAngleOffset());
         spawn("basicEnemyProjectile",
                 new SpawnData(center.getX(), center.getY())

The change to use entity's center position for spawning projectiles is a good improvement over the previous offset approach.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Vec2 direction = Vec2.fromAngle(entity.getRotation() + difficulty.getAngleOffset());
spawn("basicEnemyProjectile",
new SpawnData(center.getX() + 50 +3, center.getY())
new SpawnData(center.getX(), center.getY())
Vec2 direction = Vec2.fromAngle(
entity.getRotation() + GameSettings.getInstance().getDifficulty().getAngleOffset()
);
spawn("basicEnemyProjectile",
new SpawnData(center.getX(), center.getY())

@@ -24,7 +24,7 @@ public class GreenDinoComponent extends Component implements Dinosaur{
*/
@Override
public void onUpdate(double ptf) {
entity.translateY(verticalSpeed);
entity.translateY(difficulty.getSpeed());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update to dynamically access difficulty settings.

Since the difficulty field is initialized only once and won't update when changed in GameSettings, you should directly access the current difficulty.

-        entity.translateY(difficulty.getSpeed());
+        entity.translateY(GameSettings.getInstance().getDifficulty().getSpeed());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
entity.translateY(difficulty.getSpeed());
entity.translateY(GameSettings.getInstance().getDifficulty().getSpeed());

Comment on lines 37 to 41
Media media = new Media(getClass().getResource(GameConstants.MAINMENU_SOUND).toExternalForm());
mainMenuSound = new MediaPlayer(media);
mainMenuSound.play();
mainMenuSound.setCycleCount(MediaPlayer.INDEFINITE);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against missing audio resource to avoid NPE at startup

getResource() may return null if the WAV file is not on the runtime class‑path.
Attempting toExternalForm() on a null URL throws a NullPointerException, which crashes the menu before it appears.

-Media media = new Media(getClass().getResource(GameConstants.MAINMENU_SOUND).toExternalForm());
+URL url = getClass().getResource(GameConstants.MAINMENU_SOUND);
+if (url == null) {
+    throw new IllegalStateException(
+        "Main‑menu sound not found on class‑path: " + GameConstants.MAINMENU_SOUND);
+}
+Media media = new Media(url.toExternalForm());

Fail fast with a descriptive exception (or log + fallback) so the problem can be diagnosed instead of surfacing as a generic NPE later.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Media media = new Media(getClass().getResource(GameConstants.MAINMENU_SOUND).toExternalForm());
mainMenuSound = new MediaPlayer(media);
mainMenuSound.play();
mainMenuSound.setCycleCount(MediaPlayer.INDEFINITE);
URL url = getClass().getResource(GameConstants.MAINMENU_SOUND);
if (url == null) {
throw new IllegalStateException(
"Main‑menu sound not found on class‑path: " + GameConstants.MAINMENU_SOUND);
}
Media media = new Media(url.toExternalForm());
mainMenuSound = new MediaPlayer(media);
mainMenuSound.play();
mainMenuSound.setCycleCount(MediaPlayer.INDEFINITE);

Comment on lines +58 to +65
ComboBox<String> difficultyBox = new ComboBox<>();
difficultyBox.getItems().addAll("1", "2", "3", "4", "5");
difficultyBox.setValue("1");

try {

//Using InputStream for efficient fetching of images
InputStream menuImage = getClass().getClassLoader().getResourceAsStream("assets/textures/dinomenu.png");
if (menuImage == null) {
throw new FileNotFoundException("Resource not found: assets/textures/dinomenu.png");
}
InputStream muteButton = getClass().getClassLoader().getResourceAsStream("assets/textures/silent.png");
if (muteButton == null) {
throw new FileNotFoundException("Resource not found: assets/textures/silent.png");
}
InputStream soundButton = getClass().getClassLoader().getResourceAsStream("assets/textures/playing.png");
if (soundButton == null) {
throw new FileNotFoundException("Resource not found: assets/textures/playing.png");
}

// image for dino in main menu
Image image = new Image(menuImage);
ImageView imageView = new ImageView(image);
imageView.setFitHeight(250);
imageView.setFitWidth(200);
imageView.setX(200);
imageView.setY(190);
imageView.setPreserveRatio(true);

//adding image to manually mute music
Image mute = new Image(muteButton);


Image audioOn = new Image(soundButton);
ImageView imageViewPlaying = new ImageView(audioOn);
imageViewPlaying.setFitHeight(50);
imageViewPlaying.setFitWidth(60);
imageViewPlaying.setX(470);
imageViewPlaying.setY(20);
imageViewPlaying.setPreserveRatio(true);


startButton.setMinSize(50, 50);
startButton.setPrefSize(140,60);

quitButton.setMinSize(140, 60);
difficultyBox.valueProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Selected Difficulty: " + newValue);
GameSettings.getInstance().setDifficultyLevel(Integer.parseInt(newValue));
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace String‑based ComboBox with typed values & drop debug print

The difficulty picker currently holds Strings, which forces an unnecessary Integer.parseInt() and leaves room for runtime format errors.
A typed combo box eliminates parsing, and the System.out debug line should be removed (or replaced with proper logging).

-ComboBox<String> difficultyBox = new ComboBox<>();
-difficultyBox.getItems().addAll("1", "2", "3", "4", "5");
-difficultyBox.setValue("1");
+ComboBox<Integer> difficultyBox = new ComboBox<>();
+difficultyBox.getItems().addAll(1, 2, 3, 4, 5);
+difficultyBox.setValue(1);
 ...
-difficultyBox.valueProperty().addListener((observable, oldValue, newValue) -> {
-    System.out.println("Selected Difficulty: " + newValue);
-    GameSettings.getInstance().setDifficultyLevel(Integer.parseInt(newValue));
+difficultyBox.valueProperty().addListener((obs, oldVal, newVal) ->
+    GameSettings.getInstance().setDifficultyLevel(newVal)
 );

Benefits: type‑safety, one less parse, and a cleaner console.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ComboBox<String> difficultyBox = new ComboBox<>();
difficultyBox.getItems().addAll("1", "2", "3", "4", "5");
difficultyBox.setValue("1");
try {
//Using InputStream for efficient fetching of images
InputStream menuImage = getClass().getClassLoader().getResourceAsStream("assets/textures/dinomenu.png");
if (menuImage == null) {
throw new FileNotFoundException("Resource not found: assets/textures/dinomenu.png");
}
InputStream muteButton = getClass().getClassLoader().getResourceAsStream("assets/textures/silent.png");
if (muteButton == null) {
throw new FileNotFoundException("Resource not found: assets/textures/silent.png");
}
InputStream soundButton = getClass().getClassLoader().getResourceAsStream("assets/textures/playing.png");
if (soundButton == null) {
throw new FileNotFoundException("Resource not found: assets/textures/playing.png");
}
// image for dino in main menu
Image image = new Image(menuImage);
ImageView imageView = new ImageView(image);
imageView.setFitHeight(250);
imageView.setFitWidth(200);
imageView.setX(200);
imageView.setY(190);
imageView.setPreserveRatio(true);
//adding image to manually mute music
Image mute = new Image(muteButton);
Image audioOn = new Image(soundButton);
ImageView imageViewPlaying = new ImageView(audioOn);
imageViewPlaying.setFitHeight(50);
imageViewPlaying.setFitWidth(60);
imageViewPlaying.setX(470);
imageViewPlaying.setY(20);
imageViewPlaying.setPreserveRatio(true);
startButton.setMinSize(50, 50);
startButton.setPrefSize(140,60);
quitButton.setMinSize(140, 60);
difficultyBox.valueProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Selected Difficulty: " + newValue);
GameSettings.getInstance().setDifficultyLevel(Integer.parseInt(newValue));
});
ComboBox<Integer> difficultyBox = new ComboBox<>();
difficultyBox.getItems().addAll(1, 2, 3, 4, 5);
difficultyBox.setValue(1);
difficultyBox.valueProperty().addListener((obs, oldVal, newVal) ->
GameSettings.getInstance().setDifficultyLevel(newVal)
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: adding different levels from 1 to n and making the difficulty higher
3 participants