diff --git a/.idea/misc.xml b/.idea/misc.xml index dc377ec..44185bb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/src/main/java/com/dinosaur/dinosaurexploder/model/Difficulty.java b/src/main/java/com/dinosaur/dinosaurexploder/model/Difficulty.java new file mode 100644 index 0000000..6916c04 --- /dev/null +++ b/src/main/java/com/dinosaur/dinosaurexploder/model/Difficulty.java @@ -0,0 +1,27 @@ +package com.dinosaur.dinosaurexploder.model; + +import java.util.Random; + +public class Difficulty { + private final double speed; + private final double minAngleOffset; + private final double maxAngleOffset; + private final Random random = new Random(); + + public Difficulty(double speed, double min, double max) { + assert max > min; + this.speed = speed; + this.minAngleOffset = min; + this.maxAngleOffset = max; + } + public double getSpeed() { + return speed; + } + + public double getAngleOffset() { + if (minAngleOffset == maxAngleOffset) { + return minAngleOffset; + } + return minAngleOffset + (maxAngleOffset - minAngleOffset) * random.nextDouble(); + } +} diff --git a/src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java b/src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java new file mode 100644 index 0000000..67bf8c9 --- /dev/null +++ b/src/main/java/com/dinosaur/dinosaurexploder/model/GameSettings.java @@ -0,0 +1,69 @@ +package com.dinosaur.dinosaurexploder.model; + +public class GameSettings { + // Declare private static instance of the class + private static GameSettings instance; + + // Global variables + private int difficultyLevel; + private Difficulty difficulty; + + // Private constructor to prevent instantiation from other classes + private GameSettings() { + difficultyLevel = 1; + difficulty = createDifficulty(); + } + + 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 static method to provide access to the instance + public static GameSettings getInstance() { + if (instance == null) { + instance = new GameSettings(); + } + return instance; + } + + // Getters and setters for the global variables + + public Difficulty getDifficulty() { + return difficulty; + } + + public void setDifficultyLevel(int level) { + this.difficultyLevel = level; + difficulty = createDifficulty(); + } +} diff --git a/src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java b/src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java index f644f51..f24450e 100644 --- a/src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java +++ b/src/main/java/com/dinosaur/dinosaurexploder/model/GreenDinoComponent.java @@ -14,8 +14,8 @@ * This class extends Component and Implements the Dinosaur Classes and Handles the Shooting and Updating the Dino */ public class GreenDinoComponent extends Component implements Dinosaur{ - double verticalSpeed = 1.5; - private LocalTimer timer = FXGL.newLocalTimer(); + public Difficulty difficulty = GameSettings.getInstance().getDifficulty(); + private final LocalTimer timer = FXGL.newLocalTimer(); /** * Summary : * This method runs for every frame like a continues flow , without any stop until we put stop to it. @@ -24,7 +24,7 @@ public class GreenDinoComponent extends Component implements Dinosaur{ */ @Override public void onUpdate(double ptf) { - entity.translateY(verticalSpeed); + entity.translateY(difficulty.getSpeed()); //The dinosaur shoots every 2 seconds if (timer.elapsed(Duration.seconds(1.5)) && entity.getPosition().getY() > 0) @@ -41,9 +41,9 @@ public void onUpdate(double ptf) { public void shoot() { FXGL.play(GameConstants.ENEMYSHOOT_SOUND); Point2D center = entity.getCenter(); - Vec2 direction = Vec2.fromAngle(entity.getRotation() +90); + Vec2 direction = Vec2.fromAngle(entity.getRotation() + difficulty.getAngleOffset()); spawn("basicEnemyProjectile", - new SpawnData(center.getX() + 50 +3, center.getY()) + new SpawnData(center.getX(), center.getY()) .put("direction", direction.toPoint2D() ) ); } diff --git a/src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java b/src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java index 28016a1..4cf306c 100644 --- a/src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java +++ b/src/main/java/com/dinosaur/dinosaurexploder/model/LifeComponent.java @@ -39,7 +39,7 @@ public void onUpdate(double ptf) lifeText.setFill(Color.RED); lifeText.setFont(Font.font(GameConstants.ARCADECLASSIC_FONTNAME, 20)); - // Adjusting Hearts with respect to text and eachother + // Adjusting Hearts with respect to text and each other test1.setLayoutY(10); test2.setLayoutY(10); test3.setLayoutY(10); diff --git a/src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java b/src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java index 8075ee3..0d929d5 100644 --- a/src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java +++ b/src/main/java/com/dinosaur/dinosaurexploder/view/DinosaurMenu.java @@ -4,164 +4,143 @@ import com.almasb.fxgl.app.scene.MenuType; import com.almasb.fxgl.dsl.FXGL; import com.almasb.fxgl.scene.Scene; -import com.almasb.fxgl.ui.FontType; import com.dinosaur.dinosaurexploder.model.GameConstants; +import com.dinosaur.dinosaurexploder.model.GameSettings; +import javafx.geometry.Insets; +import javafx.geometry.Pos; import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.scene.media.Media; import javafx.scene.media.MediaPlayer; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; -import java.io.InputStream; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.geometry.Pos; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.Region; + import java.io.FileNotFoundException; -import java.util.Objects; -import javafx.scene.layout.StackPane; +import java.io.IOException; +import java.io.InputStream; public class DinosaurMenu extends FXGLMenu { - private MediaPlayer mainMenuSound; + private final MediaPlayer mainMenuSound; public DinosaurMenu() { super(MenuType.MAIN_MENU); + // Initialize background music Media media = new Media(getClass().getResource(GameConstants.MAINMENU_SOUND).toExternalForm()); mainMenuSound = new MediaPlayer(media); mainMenuSound.play(); mainMenuSound.setCycleCount(MediaPlayer.INDEFINITE); + // Create the background var bg = new Rectangle(getAppWidth(), getAppHeight(), Color.BLACK); - var title = FXGL.getUIFactoryService().newText(GameConstants.GAME_NAME, Color.LIME, FontType.MONO, 35); + // Create buttons var startButton = new Button("Start Game"); var quitButton = new Button("Quit"); + // Create volume slider and label Slider volumeSlider = new Slider(0, 1, 1); volumeSlider.setBlockIncrement(0.01); - - volumeSlider.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/styles/styles.css")).toExternalForm()); - - //Sets the volume label Label volumeLabel = new Label("100%"); - volumeSlider.valueProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Number oldValue, Number newValue) { - mainMenuSound.setVolume(newValue.doubleValue()); - volumeLabel.setText(String.format("%.0f%%", newValue.doubleValue() * 100)); - } + volumeSlider.valueProperty().addListener((observable, oldValue, newValue) -> { + mainMenuSound.setVolume(newValue.doubleValue()); + volumeLabel.setText(String.format("%.0f%%", newValue.doubleValue() * 100)); }); + ComboBox 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)); + }); - title.setTranslateY(100); - title.setTranslateX(getAppWidth() / 2 - 145); + // Set uniform size for buttons and dropdowns + startButton.setPrefSize(200, 50); + quitButton.setPrefSize(200, 50); - startButton.setTranslateY(400); - startButton.setTranslateX(getAppWidth() / 2 - 50); - startButton.setStyle("-fx-font-size:20"); + difficultyBox.setPrefSize(200, 50); - quitButton.setTranslateY(500); - quitButton.setTranslateX(getAppWidth() / 2 - 50); - quitButton.setStyle("-fx-font-size:20"); + startButton.setStyle("-fx-font-size: 18px;"); + quitButton.setStyle("-fx-font-size: 18px;"); + difficultyBox.setStyle("-fx-font-size: 18px;"); - BorderPane root = new BorderPane(); - root.setTop(title); - BorderPane.setAlignment(title, Pos.CENTER); + // Set volume slider width + volumeSlider.setPrefWidth(150); + // Create root layout container + BorderPane root = new BorderPane(); + root.setPrefSize(getAppWidth(), getAppHeight()); - BorderPane volumePane = new BorderPane(); - volumePane.setLeft(volumeLabel); - BorderPane.setAlignment(volumeLabel, Pos.CENTER); - volumePane.setCenter(volumeSlider); - volumeSlider.setStyle("-fx-padding: 10px;"); - volumeSlider.setTranslateY(25); - volumeSlider.setTranslateX(10); - volumeLabel.setTranslateX(20); - volumeLabel.setTranslateY(20); - volumeLabel.setStyle("-fx-text-fill: #61C181;"); + // Top layout for title, difficulty, and volume + VBox settingsBox = new VBox(10, new Label("Difficulty:"), difficultyBox); + settingsBox.setAlignment(Pos.TOP_LEFT); + settingsBox.setPadding(new Insets(10)); + HBox volumeBox = new HBox(10, volumeLabel, volumeSlider); + volumeBox.setAlignment(Pos.TOP_RIGHT); + volumeBox.setPadding(new Insets(10)); + BorderPane topPane = new BorderPane(); + topPane.setLeft(settingsBox); + topPane.setRight(volumeBox); - root.setCenter(volumePane); - root.setBottom(new BorderPane(startButton, null, quitButton, null, null)); - BorderPane.setAlignment(startButton, Pos.CENTER); - BorderPane.setAlignment(quitButton, Pos.BOTTOM_CENTER); + root.setTop(topPane); + // Center layout for dinosaur image + ImageView imageView = createImageView("assets/textures/dinomenu.png", 300, 400); + StackPane centerPane = new StackPane(imageView); + centerPane.setAlignment(Pos.CENTER); + root.setCenter(centerPane); - startButton.setOnAction(event -> { - fireNewGame(); - mainMenuSound.stop(); - }); + // Bottom layout for buttons + VBox buttonBox = new VBox(20, startButton, quitButton); + buttonBox.setAlignment(Pos.CENTER); + buttonBox.setPadding(new Insets(10)); + root.setBottom(buttonBox); - imageViewPlaying.setOnMouseClicked(mouseEvent -> { - if (mainMenuSound.isMute()){ - mainMenuSound.setMute(false); //False later - imageViewPlaying.setImage(audioOn); - } else { - mainMenuSound.setMute(true); - imageViewPlaying.setImage(mute); - } - }); + // Set button actions + startButton.setOnAction(event -> { + fireNewGame(); + mainMenuSound.stop(); + }); - quitButton.setOnAction(event -> fireExit()); + quitButton.setOnAction(event -> fireExit()); + // Add everything to the scene + getContentRoot().getChildren().addAll(bg, root); + } - getContentRoot().getChildren().addAll( - bg, title, startButton, quitButton, imageView, imageViewPlaying, volumeLabel, volumeSlider - ); - } - catch (FileNotFoundException e){ - System.out.println("File not found" + e.getMessage()); + /** + * Helper method to create an ImageView with specific width and height. + */ + private ImageView createImageView(String path, double width, double height) { + try (InputStream stream = getClass().getClassLoader().getResourceAsStream(path)) { + if (stream == null) { + throw new FileNotFoundException("Resource not found: " + path); + } + Image image = new Image(stream); + ImageView imageView = new ImageView(image); + imageView.setFitWidth(width); + imageView.setFitHeight(height); + imageView.setPreserveRatio(true); + return imageView; + } catch (IOException e) { + e.printStackTrace(); + return new ImageView(); } } + + @Override public void onEnteredFrom(Scene prevState) { super.onEnteredFrom(prevState);