diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java index a4a757c..2ca8688 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java @@ -3,6 +3,7 @@ import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock; import edu.ntnu.idi.idatt2003.g40.mappe.service.FileConverter; import edu.ntnu.idi.idatt2003.g40.mappe.service.FileParser; +import edu.ntnu.idi.idatt2003.g40.mappe.service.SaveGameService; import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager; import edu.ntnu.idi.idatt2003.g40.mappe.utils.ConfigValues; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewManager; @@ -11,6 +12,8 @@ import edu.ntnu.idi.idatt2003.g40.mappe.view.mainmenu.MainMenuView; import edu.ntnu.idi.idatt2003.g40.mappe.view.playgame.PlayGameController; import edu.ntnu.idi.idatt2003.g40.mappe.view.playgame.PlayGameView; +import edu.ntnu.idi.idatt2003.g40.mappe.view.settings.SettingsController; +import edu.ntnu.idi.idatt2003.g40.mappe.view.settings.SettingsView; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.Pane; @@ -54,10 +57,13 @@ public void start(final Stage stage) throws Exception { PlayGameView playGameView = new PlayGameView(); new PlayGameController(playGameView, eventManager); - // Midlertidig dummy-data — bytt ut med ekte saver fra disk senere. - playGameView.setSaves(List.of( - new PlayGameView.SaveEntry("fuck", -1_000_000_000.00), - new PlayGameView.SaveEntry("Halleluja", 1_000_650_901.43))); + // Last lagrede spill fra disk. + SaveGameService saveGameService = new SaveGameService(); + playGameView.setSaves(saveGameService.loadSaves()); + + // Settings + SettingsView settingsView = new SettingsView(); + new SettingsController(settingsView, eventManager); // In-game InGameView inGameView = new InGameView(); @@ -65,6 +71,7 @@ public void start(final Stage stage) throws Exception { // Registrer alle views og start på hovedmenyen viewManager.addView(mainMenuView); viewManager.addView(playGameView); + viewManager.addView(settingsView); viewManager.addView(inGameView); viewManager.setScene(mainMenuView); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java new file mode 100644 index 0000000..e22273f --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java @@ -0,0 +1,47 @@ +package edu.ntnu.idi.idatt2003.g40.mappe.model; + +/** + * Represents one save game entry. + * + *
+ * Holds the display name and the current balance for a single + * saved game. + *
+ */ +public class SaveGame { + + /** Display name of the save. */ + private final String name; + + /** Current balance in the save. */ + private final double balance; + + /** + * Constructor. + * + * @param name the display name of the save. + * @param balance the current balance value. + */ + public SaveGame(final String name, final double balance) { + this.name = name; + this.balance = balance; + } + + /** + * Getter method for the name. + * + * @return the save name. + */ + public String getName() { + return name; + } + + /** + * Getter method for the balance. + * + * @return the balance value. + */ + public double getBalance() { + return balance; + } +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java new file mode 100644 index 0000000..82b310d --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java @@ -0,0 +1,107 @@ +package edu.ntnu.idi.idatt2003.g40.mappe.service; + +import edu.ntnu.idi.idatt2003.g40.mappe.model.SaveGame; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +/** + * Service for loading and saving {@link SaveGame} entries from disk. + * + *+ * Save file format (one entry per line): + *
+ * + *+ * # Comment lines start with hash + * SaveName, 1234567.89 + *+ * + *
+ * Lines that don't match the expected format are skipped. + *
+ */ +public class SaveGameService { + + /** Default location of the save file. */ + private static final String DEFAULT_PATH = "src/main/resources/saves.txt"; + + /** Path to the save file. */ + private final String filePath; + + /** + * Constructor with default path. + */ + public SaveGameService() { + this(DEFAULT_PATH); + } + + /** + * Constructor with custom path. + * + * @param filePath the path to the save file. + */ + public SaveGameService(final String filePath) { + this.filePath = filePath; + } + + /** + * Loads all save games from the file. + * + *+ * Returns an empty list if the file cannot be read or is empty. + *
+ * + * @return the loaded {@link SaveGame} entries. + */ + public List+ * Returns null for invalid lines, comment lines, and blank lines. + *
+ * + * @param line the raw line from the file. + * @return the parsed {@link SaveGame}, or null if the line is invalid. + */ + private SaveGame parseLine(final String line) { + if (line == null || line.isBlank() || line.trim().startsWith("#")) { + return null; + } + String[] parts = line.split(","); + if (parts.length != 2) { + return null; + } + try { + String name = parts[0].trim(); + double balance = Double.parseDouble(parts[1].trim()); + if (name.isEmpty()) { + return null; + } + return new SaveGame(name, balance); + } catch (NumberFormatException e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/mainmenu/MainMenuController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/mainmenu/MainMenuController.java index 51c287d..7d9b1dc 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/mainmenu/MainMenuController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/mainmenu/MainMenuController.java @@ -5,6 +5,7 @@ import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController; import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewData; +import javafx.application.Platform; /** * Controller for the {@link MainMenuView}. @@ -31,7 +32,7 @@ public MainMenuController(final MainMenuView view, * {@inheritDoc} * *- * Sets play game button functionality + * Sets play game, settings, and exit button functionality. *
*/ @Override @@ -41,5 +42,13 @@ protected void initInteractions() { EventData- * Wires up the two buttons and the save-row click handler. Publishes - * scene-change events through the {@link EventManager}. + * Extends {@link ViewController} *
*/ public class PlayGameController extends ViewController+ * Sets create new game, back, and save-row click functionality. + *
+ */ @Override protected void initInteractions() { - // Create new game → go to in-game view (placeholder; later: name picker). - getViewElement().getCreateNewGameButton().setOnAction(e -> changeScene("InGameView")); + getViewElement().getCreateNewGameButton().setOnAction(e -> { + ViewData viewData = new ViewData("InGameView"); + EventData- * Null-safe — kan kalles før feltinitialisererne har kjørt. - *
*/ private void rebuildSaveList() { if (saveListContainer == null || saves == null) { return; } saveListContainer.getChildren().clear(); - for (SaveEntry save : saves) { + for (SaveGame save : saves) { saveListContainer.getChildren().add(buildSaveRow(save)); } } @@ -169,16 +162,16 @@ private void rebuildSaveList() { * Builds a single row displaying the save name on the left and the * balance (with sign and currency suffix) on the right. */ - private HBox buildSaveRow(final SaveEntry save) { - Text nameText = new Text(save.name()); + private HBox buildSaveRow(final SaveGame save) { + Text nameText = new Text(save.getName()); nameText.getStyleClass().add("save-name"); Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS); - Text balanceText = new Text(formatBalance(save.balance())); + Text balanceText = new Text(formatBalance(save.getBalance())); balanceText.getStyleClass().add( - save.balance() >= 0 ? "save-balance-positive" : "save-balance-negative"); + save.getBalance() >= 0 ? "save-balance-positive" : "save-balance-negative"); HBox row = new HBox(20, nameText, spacer, balanceText); row.setAlignment(Pos.CENTER_LEFT); @@ -200,13 +193,4 @@ private String formatBalance(final double balance) { DecimalFormat df = new DecimalFormat("+#,##0.00;-#,##0.00", symbols); return df.format(balance) + "kr"; } - - /** - * Simple data record for one save entry. - * - * @param name display name of the save. - * @param balance current balance value. - */ - public record SaveEntry(String name, double balance) { - } } \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/settings/SettingsController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/settings/SettingsController.java new file mode 100644 index 0000000..c364624 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/settings/SettingsController.java @@ -0,0 +1,50 @@ +package edu.ntnu.idi.idatt2003.g40.mappe.view.settings; + +import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData; +import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager; +import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType; +import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController; +import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewData; + +/** + * Controller for {@link SettingsView}. + * + *+ * Wires up the category buttons to switch the right-hand content, + * and the Back button to return to the main menu. + *
+ */ +public class SettingsController extends ViewController+ * Layout: left sidebar with category buttons (audio, video, hot keys) + * and a Back button at the bottom. Right pane shows the content for the + * currently selected category. + *
+ */ +public class SettingsView extends ViewElement+ * Constructs with name "SettingsView" + *
+ */ + public SettingsView() { + super(new StackPane(), "SettingsView"); + } + + public Button getAudioButton() { + return audioButton; + } + + public Button getVideoButton() { + return videoButton; + } + + public Button getHotKeysButton() { + return hotKeysButton; + } + + public Button getBackButton() { + return backButton; + } + + public Slider getMasterVolumeSlider() { + return masterVolumeSlider; + } + + /** + * Replaces the content area with the audio section (master volume). + */ + public void showAudioSection() { + Label title = new Label("master volume"); + title.getStyleClass().add("settings-title"); + + masterVolumeSlider = new Slider(0, 100, 50); + masterVolumeSlider.setMaxWidth(360); + masterVolumeSlider.getStyleClass().add("settings-slider"); + + VBox audioBox = new VBox(20, title, masterVolumeSlider); + audioBox.setAlignment(Pos.TOP_CENTER); + audioBox.setPadding(new Insets(30)); + + contentArea.getChildren().setAll(audioBox); + } + + /** + * Replaces the content area with the video section placeholder. + */ + public void showVideoSection() { + Label title = new Label("video"); + title.getStyleClass().add("settings-title"); + Label placeholder = new Label("(video settings coming soon)"); + placeholder.getStyleClass().add("settings-placeholder"); + + VBox videoBox = new VBox(20, title, placeholder); + videoBox.setAlignment(Pos.TOP_CENTER); + videoBox.setPadding(new Insets(30)); + + contentArea.getChildren().setAll(videoBox); + } + + /** + * Replaces the content area with the hot keys section placeholder. + */ + public void showHotKeysSection() { + Label title = new Label("hot keys"); + title.getStyleClass().add("settings-title"); + Label placeholder = new Label("(hot key bindings coming soon)"); + placeholder.getStyleClass().add("settings-placeholder"); + + VBox hotKeysBox = new VBox(20, title, placeholder); + hotKeysBox.setAlignment(Pos.TOP_CENTER); + hotKeysBox.setPadding(new Insets(30)); + + contentArea.getChildren().setAll(hotKeysBox); + } + + /** {@inheritDoc} */ + @Override + protected void initLayout() { + backgroundImage = new ImageView(new Image(Objects.requireNonNull( + getClass().getResourceAsStream("/millionsbackground.png")))); + backgroundImage.setPreserveRatio(false); + + audioButton = new Button("audio"); + videoButton = new Button("video"); + hotKeysButton = new Button("hot keys"); + backButton = new Button("Back"); + + // Spacer pushes the Back button to the bottom of the sidebar. + Region spacer = new Region(); + VBox.setVgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + VBox sidebar = new VBox(15, + audioButton, videoButton, hotKeysButton, spacer, backButton); + sidebar.setAlignment(Pos.TOP_LEFT); + sidebar.setPadding(new Insets(30)); + sidebar.setPrefWidth(180); + + contentArea = new StackPane(); + contentArea.setPadding(new Insets(30)); + + mainPanel = new BorderPane(); + mainPanel.setLeft(sidebar); + mainPanel.setCenter(contentArea); + mainPanel.setMaxWidth(800); + mainPanel.setMaxHeight(500); + + // Center the BorderPane on top of the background. + HBox centerWrapper = new HBox(mainPanel); + centerWrapper.setAlignment(Pos.CENTER); + + getRootPane().getChildren().addAll(backgroundImage, centerWrapper); + + // Make the background fill the root pane. + backgroundImage.fitWidthProperty().bind(getRootPane().widthProperty()); + backgroundImage.fitHeightProperty().bind(getRootPane().heightProperty()); + + // Default section shown when settings opens. + showAudioSection(); + } + + /** {@inheritDoc} */ + @Override + protected void initStyling() { + getRootPane().getStyleClass().add("main-menu-bg"); + mainPanel.getStyleClass().add("settings-panel"); + audioButton.getStyleClass().add("settings-tab-button"); + videoButton.getStyleClass().add("settings-tab-button"); + hotKeysButton.getStyleClass().add("settings-tab-button"); + backButton.getStyleClass().add("settings-tab-button"); + } + + /** {@inheritDoc} */ + @Override + public void onUpdate() { + // Reset til audio-section hver gang viewet vises. + if (contentArea != null) { + showAudioSection(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/saves.txt b/src/main/resources/saves.txt new file mode 100644 index 0000000..fec18df --- /dev/null +++ b/src/main/resources/saves.txt @@ -0,0 +1,5 @@ +# Save file format: name, balance +# Lines starting with # are comments + +fuck, -1000000000.00 +Halleluja, 1000650901.43 \ No newline at end of file diff --git a/src/main/resources/styles.css b/src/main/resources/styles.css index 85ab14b..d435134 100644 --- a/src/main/resources/styles.css +++ b/src/main/resources/styles.css @@ -6,7 +6,8 @@ /* Base button styling */ .menu-button { - -fx-background-color: rgba(255, 255, 255, 0.7); /* Translucent white */ + -fx-background-color: rgba(255, 255, 255, 0.7); + /* Translucent white */ -fx-background-radius: 15; -fx-min-width: 350; -fx-min-height: 60; @@ -27,7 +28,111 @@ /* Background image styling */ .main-menu-bg { - -fx-background-image: url("millionsbackground.png"); /* Replace with your image path */ + -fx-background-image: url("millionsbackground.png"); + /* Replace with your image path */ -fx-background-size: cover; -fx-background-position: center; +} + + +/* The translucent grey panel containing all save rows */ +.save-list { + -fx-background-color: rgba(220, 220, 220, 0.85); + -fx-background-radius: 12; + -fx-min-width: 600; + -fx-spacing: 8; +} + +/* Each clickable save row */ +.save-row { + -fx-background-color: rgba(173, 216, 230, 0.85); + /* Light blue */ + -fx-background-radius: 8; + -fx-cursor: hand; +} + +.save-row:hover { + -fx-background-color: rgba(135, 206, 235, 0.95); +} + +/* Save name (left side of the row) */ +.save-name { + -fx-font-family: "System"; + -fx-font-weight: bold; + -fx-font-style: italic; + -fx-font-size: 18px; + -fx-fill: black; +} + +/* Save balance, color-coded by sign */ +.save-balance-positive { + -fx-font-family: "System"; + -fx-font-weight: bold; + -fx-font-style: italic; + -fx-font-size: 18px; + -fx-fill: black; +} + +.save-balance-negative { + -fx-font-family: "System"; + -fx-font-weight: bold; + -fx-font-style: italic; + -fx-font-size: 18px; + -fx-fill: black; +} + + + +/* The translucent main panel containing sidebar + content */ +.settings-panel { + -fx-background-color: rgba(220, 220, 220, 0.85); + -fx-background-radius: 12; + -fx-border-color: rgba(135, 206, 235, 0.9); + -fx-border-width: 3; + -fx-border-radius: 12; +} + +/* Sidebar buttons (audio, video, hot keys, back) */ +.settings-tab-button { + -fx-background-color: transparent; + -fx-font-family: "System"; + -fx-font-weight: bold; + -fx-font-style: italic; + -fx-font-size: 22px; + -fx-text-fill: black; + -fx-cursor: hand; + -fx-padding: 4 8 4 8; +} + +.settings-tab-button:hover { + -fx-text-fill: #1976d2; +} + +/* Title in the right content area, e.g. "master volume" */ +.settings-title { + -fx-font-family: "System"; + -fx-font-weight: bold; + -fx-font-style: italic; + -fx-font-size: 22px; + -fx-text-fill: black; +} + +/* Placeholder text in incomplete sections */ +.settings-placeholder { + -fx-font-family: "System"; + -fx-font-style: italic; + -fx-font-size: 14px; + -fx-text-fill: #555; +} + +/* Slider styling to match the cyan thumb */ +.settings-slider .thumb { + -fx-background-color: #4dd0e1; + -fx-background-radius: 50%; + -fx-padding: 8; +} + +.settings-slider .track { + -fx-background-color: rgba(0, 0, 0, 0.4); + -fx-pref-height: 2; } \ No newline at end of file