-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin' into 126-min-vindu-størelse
- Loading branch information
Showing
8 changed files
with
1,002 additions
and
351 deletions.
There are no files selected for viewing
407 changes: 213 additions & 194 deletions
407
src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java
Large diffs are not rendered by default.
Oops, something went wrong.
23 changes: 23 additions & 0 deletions
23
src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogActions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package edu.ntnu.idi.idatt2003.g40.mappe.view.ingame.quit; | ||
|
|
||
| /** | ||
| * User-triggered actions available in the in-game quit dialog overlay. | ||
| * | ||
| * <p>The quit dialog is shown when the player clicks the "Quit" button | ||
| * from the in-game dashboard, and lets them choose between continuing, | ||
| * saving in place, saving and returning to the main menu, or saving | ||
| * and exiting the application.</p> | ||
| * */ | ||
| public enum QuitDialogActions { | ||
| /** Closes the dialog and returns the player to the game. */ | ||
| CONTINUE, | ||
|
|
||
| /** Saves the current game state without leaving the in-game session. */ | ||
| SAVE, | ||
|
|
||
| /** Saves the current game state and returns to the main menu. */ | ||
| SAVE_AND_QUIT_TO_MAIN_MENU, | ||
|
|
||
| /** Saves the current game state and exits the application. */ | ||
| SAVE_AND_QUIT_GAME; | ||
| } |
188 changes: 188 additions & 0 deletions
188
src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| package edu.ntnu.idi.idatt2003.g40.mappe.view.ingame.quit; | ||
|
|
||
| import edu.ntnu.idi.idatt2003.g40.mappe.model.SaveGame; | ||
| import edu.ntnu.idi.idatt2003.g40.mappe.service.GameStateLoader; | ||
| 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.view.ViewController; | ||
| import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewEnum; | ||
| import edu.ntnu.idi.idatt2003.g40.mappe.view.ingame.InGameView; | ||
|
|
||
| /** | ||
| * Controller for {@link QuitDialogView}. | ||
| * | ||
| * <p>Wires the four dialog buttons to the appropriate save / scene-change | ||
| * / application-exit actions:</p> | ||
| * <ul> | ||
| * <li>{@code CONTINUE} - hides the overlay; the player stays in the | ||
| * current game session.</li> | ||
| * <li>{@code SAVE} - snapshots and writes the active save to disk, but | ||
| * keeps the overlay open so the player sees the confirmation message. | ||
| * They can then choose to continue, quit, or exit.</li> | ||
| * <li>{@code SAVE_AND_QUIT_TO_MAIN_MENU} - snapshots, writes to disk, | ||
| * hides the overlay, and changes the scene back to the main menu.</li> | ||
| * <li>{@code SAVE_AND_QUIT_GAME} - snapshots, writes to disk, then | ||
| * invokes the configured exit-application runnable to close the JVM.</li> | ||
| * </ul> | ||
| * | ||
| * <p>The save logic mirrors the auto-save hook in {@code Main}: a missing | ||
| * active save (e.g. before any game has been loaded) is treated as a | ||
| * no-op rather than an error, since there's nothing meaningful to write.</p> | ||
| * */ | ||
| public final class QuitDialogController | ||
| extends ViewController<QuitDialogView> { | ||
|
|
||
| /** The in-game view hosting this overlay. */ | ||
| private final InGameView inGameView; | ||
|
|
||
| /** Builds {@link SaveGame} snapshots of the current player/exchange. */ | ||
| private final GameStateLoader gameStateLoader; | ||
|
|
||
| /** Persists save games to disk. */ | ||
| private final SaveGameService saveGameService; | ||
|
|
||
| /** Runnable invoked to close the application. */ | ||
| private Runnable onExitApplication = () -> { }; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * | ||
| * @param view the {@link QuitDialogView} this controller is | ||
| * attached to. | ||
| * @param eventManager the active {@link EventManager}, used to | ||
| * publish scene-change events. | ||
| * @param inGameView the in-game view that hosts this overlay. | ||
| * @param gameStateLoader the loader used to snapshot the active save. | ||
| * @param saveGameService the service used to persist saves to disk. | ||
| * | ||
| * @throws IllegalArgumentException if any constructor argument is null. | ||
| * */ | ||
| public QuitDialogController(final QuitDialogView view, | ||
| final EventManager eventManager, | ||
| final InGameView inGameView, | ||
| final GameStateLoader gameStateLoader, | ||
| final SaveGameService saveGameService) | ||
| throws IllegalArgumentException { | ||
| this.inGameView = inGameView; | ||
| this.gameStateLoader = gameStateLoader; | ||
| this.saveGameService = saveGameService; | ||
| super(view, eventManager); | ||
| if (inGameView == null | ||
| || gameStateLoader == null | ||
| || saveGameService == null) { | ||
| throw new IllegalArgumentException( | ||
| "Invalid QuitDialogController arguments!"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Installs the runnable used to close the application when the player | ||
| * chooses "Save and quit game". | ||
| * | ||
| * <p>Typically this is wired to {@code Platform.exit()} (plus a | ||
| * {@code System.exit(0)} fallback) from {@code Main}, so the controller | ||
| * itself stays decoupled from the JavaFX runtime.</p> | ||
| * | ||
| * @param exitRunnable runnable invoked after the save completes; a null | ||
| * value resets the hook to a no-op. | ||
| * */ | ||
| public void setOnExitApplication(final Runnable exitRunnable) { | ||
| this.onExitApplication = | ||
| (exitRunnable != null) ? exitRunnable : () -> { }; | ||
| } | ||
|
|
||
| /** {@inheritDoc} */ | ||
| @Override | ||
| protected void initInteractions() { | ||
| getViewElement().setOnAction(QuitDialogActions.CONTINUE, this::close); | ||
|
|
||
| getViewElement().setOnAction(QuitDialogActions.SAVE, () -> { | ||
| boolean ok = performSave(); | ||
| if (ok) { | ||
| getViewElement().setStatus("Game saved.", false); | ||
| } | ||
| // Failures already wrote an error to the status label inside | ||
| // performSave(); nothing more to do here. | ||
| }); | ||
|
|
||
| getViewElement().setOnAction( | ||
| QuitDialogActions.SAVE_AND_QUIT_TO_MAIN_MENU, () -> { | ||
| if (performSave()) { | ||
| close(); | ||
| changeScene(ViewEnum.MAIN_MENU); | ||
| } | ||
| }); | ||
|
|
||
| getViewElement().setOnAction(QuitDialogActions.SAVE_AND_QUIT_GAME, () -> { | ||
| if (performSave()) { | ||
| close(); | ||
| onExitApplication.run(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Shows the quit dialog overlay on the host {@link InGameView}. | ||
| * | ||
| * <p>Clears any stale status message left over from a previous opening | ||
| * so the player doesn't see "Game saved." from a save they made hours | ||
| * ago.</p> | ||
| * */ | ||
| public void show() { | ||
| getViewElement().setStatus(null, false); | ||
| inGameView.showSettingsOverlay(getViewElement().getRootPane()); | ||
| } | ||
|
|
||
| /** | ||
| * Hides the overlay if it's currently visible. | ||
| * */ | ||
| private void close() { | ||
| inGameView.hideSettingsOverlay(); | ||
| } | ||
|
|
||
| /** | ||
| * Snapshots the active save and writes it to disk. | ||
| * | ||
| * <p>If no save is currently active (e.g. the player got into the | ||
| * in-game view through a path that doesn't load a save), this is | ||
| * treated as a successful no-op so the quit / exit flows can proceed. | ||
| * Any I/O failure is reported back to the player via the dialog's | ||
| * status label.</p> | ||
| * | ||
| * @return {@code true} when the operation can be considered successful | ||
| * (a real save was written, or no save was needed); {@code false} | ||
| * when an actual error occurred. | ||
| * */ | ||
| private boolean performSave() { | ||
| SaveGame snapshot; | ||
| try { | ||
| snapshot = gameStateLoader.snapshotActiveSave(); | ||
| } catch (Exception e) { | ||
| System.err.println("[quit-dialog] Snapshot failed: " | ||
| + e.getMessage()); | ||
| getViewElement().setStatus( | ||
| "Could not prepare save: " + e.getMessage(), true); | ||
| return false; | ||
| } | ||
|
|
||
| if (snapshot == null) { | ||
| System.out.println( | ||
| "[quit-dialog] No active save - nothing to write."); | ||
| // Nothing to persist, but the user's intent (continue past this | ||
| // step) is still valid - treat as success. | ||
| return true; | ||
| } | ||
|
|
||
| try { | ||
| saveGameService.saveGame(snapshot); | ||
| System.out.println("[quit-dialog] Wrote save '" | ||
| + snapshot.getName() + "' to disk."); | ||
| return true; | ||
| } catch (Exception e) { | ||
| System.err.println("[quit-dialog] Save failed: " + e.getMessage()); | ||
| getViewElement().setStatus( | ||
| "Save failed: " + e.getMessage(), true); | ||
| return false; | ||
| } | ||
| } | ||
| } |
185 changes: 185 additions & 0 deletions
185
src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ingame/quit/QuitDialogView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| package edu.ntnu.idi.idatt2003.g40.mappe.view.ingame.quit; | ||
|
|
||
| import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewElement; | ||
| import javafx.geometry.Insets; | ||
| import javafx.geometry.Pos; | ||
| import javafx.scene.control.Button; | ||
| import javafx.scene.control.Label; | ||
| import javafx.scene.layout.HBox; | ||
| import javafx.scene.layout.Priority; | ||
| import javafx.scene.layout.Region; | ||
| import javafx.scene.layout.StackPane; | ||
| import javafx.scene.layout.VBox; | ||
|
|
||
| /** | ||
| * Overlay shown when the player clicks the "Quit" button in the in-game | ||
| * top bar. | ||
| * | ||
| * <p>The root of this view is a dimmer {@link StackPane} that covers the | ||
| * current center widget. Inside, a centered panel hosts four action buttons | ||
| * that let the player choose how to leave (or stay in) the game.</p> | ||
| * | ||
| * <p>Layout: a header row with the title and a close (X) button, a short | ||
| * description, and a vertical stack of four buttons: Continue, Save, | ||
| * Save and quit to main menu, Save and quit game.</p> | ||
| * | ||
| * <p>This view is purely presentational - the {@link QuitDialogController} | ||
| * wires the buttons to the actual save/quit logic.</p> | ||
| * */ | ||
| public final class QuitDialogView | ||
| extends ViewElement<StackPane, QuitDialogActions> { | ||
|
|
||
| /** Close button shown in the panel's top-right corner. */ | ||
| private Button closeButton; | ||
|
|
||
| /** Closes the dialog without saving. */ | ||
| private Button continueButton; | ||
|
|
||
| /** Saves the game in place, without leaving. */ | ||
| private Button saveButton; | ||
|
|
||
| /** Saves and returns to the main menu. */ | ||
| private Button saveAndQuitMainMenuButton; | ||
|
|
||
| /** Saves and exits the application. */ | ||
| private Button saveAndQuitGameButton; | ||
|
|
||
| /** Label used to communicate save status to the player. */ | ||
| private Label statusLabel; | ||
|
|
||
| /** Modal-style card hosting the dialog controls. */ | ||
| private VBox panel; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * */ | ||
| public QuitDialogView() { | ||
| super(new StackPane(), QuitDialogActions.class); | ||
| } | ||
|
|
||
| /** | ||
| * Updates the status text shown below the action buttons. | ||
| * | ||
| * <p>Used by the controller to tell the player whether the last save | ||
| * succeeded or failed.</p> | ||
| * | ||
| * @param text message to display, or {@code null} / empty to hide. | ||
| * @param errorStyle when {@code true}, the message is styled as an error. | ||
| * */ | ||
| public void setStatus(final String text, final boolean errorStyle) { | ||
| if (statusLabel == null) { | ||
| return; | ||
| } | ||
| statusLabel.getStyleClass().remove("quit-dialog-status-error"); | ||
| if (text == null || text.isEmpty()) { | ||
| statusLabel.setText(""); | ||
| statusLabel.setVisible(false); | ||
| statusLabel.setManaged(false); | ||
| return; | ||
| } | ||
| statusLabel.setText(text); | ||
| statusLabel.setVisible(true); | ||
| statusLabel.setManaged(true); | ||
| if (errorStyle) { | ||
| statusLabel.getStyleClass().add("quit-dialog-status-error"); | ||
| } | ||
| } | ||
|
|
||
| /** {@inheritDoc} */ | ||
| @Override | ||
| protected void initLayout() { | ||
| // Header row: title + close button. | ||
| Label title = new Label("Quit game?"); | ||
| title.getStyleClass().add("quit-dialog-title"); | ||
|
|
||
| Region headerSpacer = new Region(); | ||
| HBox.setHgrow(headerSpacer, Priority.ALWAYS); | ||
|
|
||
| closeButton = new Button("X"); | ||
| closeButton.setFocusTraversable(false); | ||
| closeButton.getStyleClass().add("quit-dialog-close-button"); | ||
|
|
||
| HBox header = new HBox(12, title, headerSpacer, closeButton); | ||
| header.setAlignment(Pos.CENTER_LEFT); | ||
|
|
||
| Label description = new Label( | ||
| "Choose what to do with your current session."); | ||
| description.getStyleClass().add("quit-dialog-description"); | ||
| description.setWrapText(true); | ||
|
|
||
| // The four action buttons. Each is full-width inside the panel so the | ||
| // longer labels do not overflow. | ||
| continueButton = new Button("Continue"); | ||
| saveButton = new Button("Save"); | ||
| saveAndQuitMainMenuButton = | ||
| new Button("Save and quit to main menu"); | ||
| saveAndQuitGameButton = new Button("Save and quit game"); | ||
|
|
||
| continueButton.setFocusTraversable(false); | ||
| saveButton.setFocusTraversable(false); | ||
| saveAndQuitMainMenuButton.setFocusTraversable(false); | ||
| saveAndQuitGameButton.setFocusTraversable(false); | ||
|
|
||
| continueButton.getStyleClass().addAll( | ||
| "quit-dialog-button", "quit-dialog-button-primary"); | ||
| saveButton.getStyleClass().add("quit-dialog-button"); | ||
| saveAndQuitMainMenuButton.getStyleClass().add("quit-dialog-button"); | ||
| saveAndQuitGameButton.getStyleClass().addAll( | ||
| "quit-dialog-button", "quit-dialog-button-danger"); | ||
|
|
||
| continueButton.setMaxWidth(Double.MAX_VALUE); | ||
| saveButton.setMaxWidth(Double.MAX_VALUE); | ||
| saveAndQuitMainMenuButton.setMaxWidth(Double.MAX_VALUE); | ||
| saveAndQuitGameButton.setMaxWidth(Double.MAX_VALUE); | ||
|
|
||
| VBox buttonColumn = new VBox(10, | ||
| continueButton, | ||
| saveButton, | ||
| saveAndQuitMainMenuButton, | ||
| saveAndQuitGameButton); | ||
| buttonColumn.setAlignment(Pos.CENTER); | ||
|
|
||
| // Status label - hidden until the controller writes something into it. | ||
| statusLabel = new Label(""); | ||
| statusLabel.getStyleClass().add("quit-dialog-status"); | ||
| statusLabel.setWrapText(true); | ||
| statusLabel.setVisible(false); | ||
| statusLabel.setManaged(false); | ||
|
|
||
| // Assemble panel. | ||
| panel = new VBox(18, header, description, buttonColumn, statusLabel); | ||
| panel.setAlignment(Pos.TOP_LEFT); | ||
| panel.setPadding(new Insets(26, 30, 28, 30)); | ||
| panel.setMaxWidth(440); | ||
| panel.setPrefWidth(440); | ||
| // Lock the panel to a fixed height so it doesn't visibly grow or | ||
| // shrink when the status label appears/disappears, and so it stays | ||
| // visually aligned with the settings overlay (which also has a | ||
| // fixed height). | ||
| panel.setMinHeight(420); | ||
| panel.setPrefHeight(420); | ||
| panel.setMaxHeight(420); | ||
| panel.getStyleClass().add("quit-dialog-panel"); | ||
|
|
||
| // Dimmer wrapper. | ||
| getRootPane().getStyleClass().add("quit-dialog-overlay-dimmer"); | ||
| getRootPane().getChildren().add(panel); | ||
| StackPane.setAlignment(panel, Pos.CENTER); | ||
|
|
||
| registerButton(QuitDialogActions.CONTINUE, continueButton); | ||
| registerButton(QuitDialogActions.SAVE, saveButton); | ||
| registerButton(QuitDialogActions.SAVE_AND_QUIT_TO_MAIN_MENU, | ||
| saveAndQuitMainMenuButton); | ||
| registerButton(QuitDialogActions.SAVE_AND_QUIT_GAME, | ||
| saveAndQuitGameButton); | ||
| // The X button reuses the CONTINUE action so we don't need a | ||
| // dedicated enum entry just for "close". | ||
| closeButton.setOnAction(e -> continueButton.fire()); | ||
| } | ||
|
|
||
| /** {@inheritDoc} */ | ||
| @Override | ||
| protected void initStyling() { | ||
| // Styling is applied via style classes assigned in initLayout(). | ||
| } | ||
| } |
Oops, something went wrong.