Skip to content

upload save #117

Merged
merged 1 commit into from
May 23, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public enum PlayGameActions {
NEW_GAME,
BACK,
UPLOAD_SAVE,
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
package edu.ntnu.idi.idatt2003.g40.mappe.view.playgame;

import edu.ntnu.idi.idatt2003.g40.mappe.model.SaveGame;
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 java.io.File;
import java.util.List;

import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.FileChooser;
import javafx.stage.Window;

/**
* Controller for the {@link PlayGameView}.
*
* <p>Extends {@link ViewController}</p>
*
* <p>
* Handles three user actions: starting a new game, going back to the
* main menu, and uploading a custom save file from disk.
* </p>
*/
public class PlayGameController extends ViewController<PlayGameView> {

Expand All @@ -24,19 +40,88 @@ public PlayGameController(final PlayGameView view,
}

/**
* {@inheritDoc}-
* {@inheritDoc}
*
* <p>Sets create new game, back, and save-row click functionality. </p>
* <p>Sets create new game, upload save, back, and save-row click
* functionality.</p>
*/
@Override
protected void initInteractions() {
getViewElement().setOnAction(PlayGameActions.NEW_GAME, () ->
changeScene(ViewEnum.IN_GAME));
changeScene(ViewEnum.IN_GAME));

getViewElement().setOnAction(PlayGameActions.BACK, () ->
changeScene(ViewEnum.MAIN_MENU));
changeScene(ViewEnum.MAIN_MENU));

getViewElement().setOnAction(PlayGameActions.UPLOAD_SAVE,
this::handleUploadSave);

getViewElement().setOnSaveSelected(save ->
changeScene(ViewEnum.IN_GAME));
changeScene(ViewEnum.IN_GAME));
}

/**
* Opens a {@link FileChooser} for the user to pick a save file from
* disk. Parses it using {@link SaveGameService} and appends the
* loaded saves to the view's current list.
*
* <p>
* Shows an alert if the user picks a file that contains no valid
* save entries.
* </p>
*/
private void handleUploadSave() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose save file");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("Save files (*.txt)", "*.txt"),
new FileChooser.ExtensionFilter("All files", "*.*")
);

Window window = getOwnerWindow();
File selectedFile = fileChooser.showOpenDialog(window);
if (selectedFile == null) {
// User cancelled the dialog.
return;
}

SaveGameService service =
new SaveGameService(selectedFile.getAbsolutePath());
List<SaveGame> uploadedSaves = service.loadSaves();

if (uploadedSaves.isEmpty()) {
showAlert(AlertType.WARNING,
"No saves found",
"The selected file did not contain any valid save entries.\n\n"
+ "Expected format (one entry per line):\n"
+ " SaveName, 1234567.89");
return;
}

getViewElement().addSaves(uploadedSaves);
}

/**
* Looks up the {@link Window} that hosts the view, so we can parent
* the file chooser to it. Returns null if the view is not yet
* attached to a scene (in which case the FileChooser opens
* un-parented, which still works).
*/
private Window getOwnerWindow() {
Scene scene = getViewElement().getRootPane().getScene();
return (scene != null) ? scene.getWindow() : null;
}

/**
* Shows a modal {@link Alert} with the given type, header and content.
*/
private void showAlert(final AlertType type,
final String header,
final String content) {
Alert alert = new Alert(type);
alert.setTitle("Upload save");
alert.setHeaderText(header);
alert.setContentText(content);
alert.showAndWait();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
* View shown after the player clicks "Play" on the main menu.
*
* <p>
* Displays a list of existing save games and two action buttons:
* "Create new game" and "Back".
* Displays a list of existing save games and three action buttons:
* "Create new game", "Upload save" and "Back".
* </p>
*
* <p>
Expand All @@ -41,13 +41,16 @@ public class PlayGameView extends ViewElement<StackPane, PlayGameActions> {
/** "Create new game" button. */
private Button createNewGameButton;

/** "Upload save" button - lets the user pick a save file from disk. */
private Button uploadSaveButton;

/** "Back" button. */
private Button backButton;

/** Vertical container holding the save rows. Rebuilt on every update. */
private VBox saveListContainer;

/** Bottom row with the two action buttons. */
/** Bottom row with the three action buttons. */
private HBox bottomButtonRow;

/** Centered panel: save list + bottom buttons stacked. */
Expand Down Expand Up @@ -77,13 +80,33 @@ public PlayGameView() {
/**
* Sets the list of saves to display and rebuilds the rows.
*
* <p>Replaces any saves currently displayed.</p>
*
* @param saves saves to display.
*/
public void setSaves(final List<SaveGame> saves) {
this.saves = new ArrayList<>(saves);
rebuildSaveList();
}

/**
* Appends additional saves to the displayed list and rebuilds the rows.
*
* <p>
* Used by the controller after the user uploads a save file from disk.
* Existing saves are kept; the new ones are appended at the bottom.
* </p>
*
* @param newSaves saves to append. Null or empty lists are ignored.
*/
public void addSaves(final List<SaveGame> newSaves) {
if (newSaves == null || newSaves.isEmpty()) {
return;
}
this.saves.addAll(newSaves);
rebuildSaveList();
}

/**
* Sets the handler called when a save row is clicked.
*
Expand All @@ -106,9 +129,11 @@ protected void initLayout() {
saveListContainer.setPadding(new Insets(20));

createNewGameButton = new Button("Create new game");
uploadSaveButton = new Button("Upload save");
backButton = new Button("Back");

bottomButtonRow = new HBox(40, createNewGameButton, backButton);
bottomButtonRow = new HBox(20,
createNewGameButton, uploadSaveButton, backButton);
bottomButtonRow.setAlignment(Pos.CENTER);

mainPanel = new VBox(30, saveListContainer, bottomButtonRow);
Expand All @@ -123,6 +148,7 @@ protected void initLayout() {
backgroundImage.fitHeightProperty().bind(getRootPane().heightProperty());

registerButton(PlayGameActions.NEW_GAME, createNewGameButton);
registerButton(PlayGameActions.UPLOAD_SAVE, uploadSaveButton);
registerButton(PlayGameActions.BACK, backButton);
}

Expand All @@ -132,6 +158,7 @@ protected void initStyling() {
getRootPane().getStyleClass().add("main-menu-bg");
saveListContainer.getStyleClass().add("save-list");
createNewGameButton.getStyleClass().add("menu-button");
uploadSaveButton.getStyleClass().add("menu-button");
backButton.getStyleClass().add("menu-button");
}

Expand Down Expand Up @@ -189,4 +216,4 @@ private String formatBalance(final double balance) {
DecimalFormat df = new DecimalFormat("+#,##0.00;-#,##0.00", symbols);
return df.format(balance) + "kr";
}
}
}