Allow naming savegames

This commit is contained in:
tobspr 2020-08-28 22:15:12 +02:00
parent a095cd6324
commit 728f4ae253
5 changed files with 1383 additions and 1297 deletions

View File

@ -285,13 +285,13 @@
@include S(border-radius, $globalBorderRadius); @include S(border-radius, $globalBorderRadius);
@include S(padding, 5px); @include S(padding, 5px);
display: grid; display: grid;
grid-template-columns: 1fr auto auto; grid-template-columns: 1fr 1fr auto auto;
grid-template-rows: auto auto; grid-template-rows: auto auto;
@include S(grid-column-gap, 4px); @include S(grid-column-gap, 4px);
@include S(grid-row-gap, 1px); @include S(grid-row-gap, 1px);
.playtime { .playtime {
grid-column: 1 / 2; grid-column: 2 / 3;
grid-row: 2 / 3; grid-row: 2 / 3;
@include SuperSmallText; @include SuperSmallText;
opacity: 0.5; opacity: 0.5;
@ -299,13 +299,23 @@
.level { .level {
grid-column: 1 / 2; grid-column: 1 / 2;
grid-row: 2 / 3;
@include SuperSmallText;
opacity: 0.5;
}
.name {
grid-column: 1 / 3;
grid-row: 1 / 2; grid-row: 1 / 2;
@include PlainText; @include PlainText;
display: inline-flex;
align-items: center;
} }
button.resumeGame, button.resumeGame,
button.downloadGame, button.downloadGame,
button.deleteGame { button.deleteGame,
button.renameGame {
padding: 0; padding: 0;
align-self: center; align-self: center;
justify-self: center; justify-self: center;
@ -314,7 +324,7 @@
} }
button.downloadGame { button.downloadGame {
grid-column: 2 / 3; grid-column: 3 / 4;
grid-row: 1 / 2; grid-row: 1 / 2;
background-color: $colorBlueBright; background-color: $colorBlueBright;
background-image: uiResource("icons/download.png"); background-image: uiResource("icons/download.png");
@ -326,7 +336,7 @@
} }
button.deleteGame { button.deleteGame {
grid-column: 2 / 3; grid-column: 3 / 4;
grid-row: 2 / 3; grid-row: 2 / 3;
background-color: $colorRedBright; background-color: $colorRedBright;
@include IncreasedClickArea(0px); @include IncreasedClickArea(0px);
@ -337,8 +347,26 @@
background-size: 60%; background-size: 60%;
} }
button.renameGame {
background-color: transparent;
@include IncreasedClickArea(2px);
background-image: uiResource("icons/edit_key.png");
@include S(width, 10px);
@include S(height, 10px);
align-self: center;
justify-self: center;
background-size: 90%;
opacity: 0.25;
@include S(margin-left, 4px);
&:hover {
opacity: 0.35;
}
}
button.resumeGame { button.resumeGame {
grid-column: 3 / 4; grid-column: 4 / 5;
grid-row: 1 / 3; grid-row: 1 / 3;
margin: 0; margin: 0;
@include S(width, 32px); @include S(width, 32px);

View File

@ -37,7 +37,7 @@ export class SavegameManager extends ReadWriteProxy {
} }
getCurrentVersion() { getCurrentVersion() {
return 1001; return 1002;
} }
/** /**
@ -64,6 +64,13 @@ export class SavegameManager extends ReadWriteProxy {
data.version = 1001; data.version = 1001;
} }
if (data.version < 1002) {
data.savegames.forEach(savegame => {
savegame.name = null;
});
data.version = 1002;
}
return ExplainedResult.good(); return ExplainedResult.good();
} }

View File

@ -27,6 +27,7 @@
* version: number, * version: number,
* internalId: string, * internalId: string,
* level: number * level: number
* name: string|null
* }} SavegameMetadata * }} SavegameMetadata
* *
* @typedef {{ * @typedef {{

View File

@ -14,6 +14,8 @@ import { ReadWriteProxy } from "../core/read_write_proxy";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { T } from "../translations"; import { T } from "../translations";
import { getApplicationSettingById } from "../profile/application_settings"; import { getApplicationSettingById } from "../profile/application_settings";
import { FormElementInput } from "../core/modal_dialog_forms";
import { DialogWithForm } from "../core/modal_dialog_elements";
/** /**
* @typedef {import("../savegame/savegame_typedefs").SavegameMetadata} SavegameMetadata * @typedef {import("../savegame/savegame_typedefs").SavegameMetadata} SavegameMetadata
@ -392,6 +394,13 @@ export class MainMenuState extends GameState {
: T.mainMenu.savegameLevelUnknown : T.mainMenu.savegameLevelUnknown
); );
const name = makeDiv(
elem,
null,
["name"],
games[i].name ? games[i].name : T.mainMenu.savegameUnnamed
);
const deleteButton = document.createElement("button"); const deleteButton = document.createElement("button");
deleteButton.classList.add("styledButton", "deleteGame"); deleteButton.classList.add("styledButton", "deleteGame");
elem.appendChild(deleteButton); elem.appendChild(deleteButton);
@ -400,6 +409,10 @@ export class MainMenuState extends GameState {
downloadButton.classList.add("styledButton", "downloadGame"); downloadButton.classList.add("styledButton", "downloadGame");
elem.appendChild(downloadButton); elem.appendChild(downloadButton);
const renameButton = document.createElement("button");
renameButton.classList.add("styledButton", "renameGame");
name.appendChild(renameButton);
const resumeButton = document.createElement("button"); const resumeButton = document.createElement("button");
resumeButton.classList.add("styledButton", "resumeGame"); resumeButton.classList.add("styledButton", "resumeGame");
elem.appendChild(resumeButton); elem.appendChild(resumeButton);
@ -407,10 +420,41 @@ export class MainMenuState extends GameState {
this.trackClicks(deleteButton, () => this.deleteGame(games[i])); this.trackClicks(deleteButton, () => this.deleteGame(games[i]));
this.trackClicks(downloadButton, () => this.downloadGame(games[i])); this.trackClicks(downloadButton, () => this.downloadGame(games[i]));
this.trackClicks(resumeButton, () => this.resumeGame(games[i])); this.trackClicks(resumeButton, () => this.resumeGame(games[i]));
this.trackClicks(renameButton, () => this.requestRenameSavegame(games[i]));
} }
} }
} }
/**
* @param {SavegameMetadata} game
*/
requestRenameSavegame(game) {
const regex = /^[a-zA-Z0-9_\- ]{1,20}$/;
const nameInput = new FormElementInput({
id: "nameInput",
label: null,
placeholder: "",
defaultValue: game.name || "",
validator: val => val.match(regex),
});
const dialog = new DialogWithForm({
app: this.app,
title: T.dialogs.renameSavegame.title,
desc: T.dialogs.renameSavegame.desc,
formElements: [nameInput],
buttons: ["cancel:bad:escape", "ok:good:enter"],
});
this.dialogs.internalShowDialog(dialog);
// When confirmed, save the name
dialog.buttonSignals.ok.add(() => {
game.name = nameInput.getValue();
this.app.savegameMgr.writeAsync();
this.renderSavegames();
});
}
/** /**
* @param {SavegameMetadata} game * @param {SavegameMetadata} game
*/ */
@ -473,7 +517,8 @@ export class MainMenuState extends GameState {
const savegame = this.app.savegameMgr.getSavegameById(game.internalId); const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
savegame.readAsync().then(() => { savegame.readAsync().then(() => {
const data = ReadWriteProxy.serializeObject(savegame.currentData); const data = ReadWriteProxy.serializeObject(savegame.currentData);
generateFileDownload(savegame.filename, data); const filename = (game.name || "unnamed") + ".bin";
generateFileDownload(filename, data);
}); });
} }

View File

@ -157,6 +157,7 @@ mainMenu:
savegameLevel: Level <x> savegameLevel: Level <x>
savegameLevelUnknown: Unknown Level savegameLevelUnknown: Unknown Level
savegameUnnamed: Unnamed
dialogs: dialogs:
buttons: buttons:
@ -274,6 +275,10 @@ dialogs:
title: Export screenshot title: Export screenshot
desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game! desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game!
renameSavegame:
title: Rename Savegame
desc: You can rename your savegame here.
ingame: ingame:
# This is shown in the top left corner and displays useful keybindings in # This is shown in the top left corner and displays useful keybindings in
# every situation # every situation