diff --git a/src/runtime/SaveInfo.ts b/src/runtime/SaveInfo.ts new file mode 100644 index 0000000..9451ee5 --- /dev/null +++ b/src/runtime/SaveInfo.ts @@ -0,0 +1,10 @@ +import { NameInfoMap } from "../view/model/NameInfoMap"; + +export class SaveInfo { + public time: Date; + public position: number; + public variables: {[key: string]: any}; + public characterNames: NameInfoMap; + public backgroundType: string; + public backgroundName: string; +} \ No newline at end of file diff --git a/src/runtime/Variables.ts b/src/runtime/Variables.ts index 7bb2ab9..0e76a4a 100644 --- a/src/runtime/Variables.ts +++ b/src/runtime/Variables.ts @@ -1,7 +1,7 @@ import { VariablesHolder } from "./VariablesHolder"; export class Variables { - private static holder: VariablesHolder = new VariablesHolder(); + private static readonly holder: VariablesHolder = new VariablesHolder(); public static getVariable(name: string): any { return Variables.holder.getVariable(name); @@ -10,4 +10,12 @@ export class Variables { public static setVariable(name: string, value: any): void { Variables.holder.setVariable(name, value); } + + public static getVariables(): {[key: string]: any} { + return Variables.holder.getVariables(); + } + + public static setVariables(dump: {[key: string]: any}): void { + Variables.holder.setVariables(dump); + } } \ No newline at end of file diff --git a/src/runtime/VariablesHolder.ts b/src/runtime/VariablesHolder.ts index b9fb3a7..c05018c 100644 --- a/src/runtime/VariablesHolder.ts +++ b/src/runtime/VariablesHolder.ts @@ -1,12 +1,7 @@ export class VariablesHolder { - - private variables: object + private variables: {[key: string]: any}; constructor() { - this.variables = {}; - } - - public init(): void { this.variables = { "True": 1, "False": 0 @@ -23,4 +18,12 @@ export class VariablesHolder { public setVariable(name: string, value: any): void { this.variables[name] = value; } + + public getVariables(): {[key: string]: any} { + return this.variables; + } + + public setVariables(dump: {[key: string]: any}): void { + this.variables = dump; + } } \ No newline at end of file diff --git a/src/view/MainView.ts b/src/view/MainView.ts index f521382..3b978db 100644 --- a/src/view/MainView.ts +++ b/src/view/MainView.ts @@ -1,6 +1,8 @@ import { PathResolver } from "../utils/PathResolver"; import { TextUtils } from "../utils/TextUtils"; import { UIUtils } from "../utils/UIUtils"; +import { SaveInfo } from "../runtime/SaveInfo"; +import { Variables } from "../runtime/Variables"; import { Characters } from "./model/Characters"; import { FadeInfo } from "./model/FadeInfo"; import { MapPlaces } from "./model/MapPlaces"; @@ -307,23 +309,101 @@ export class MainView implements ViewModel { }); this.addMainMenuItem("Previous scene", () => this.navigable.prevScene()); this.addMainMenuItem("Next scene", () => this.navigable.nextScene()); - // TODO this.addMainMenuItem("Save", () => { this.elMenu.style.display = 'none'; - // this.saveState(); + this.saveState(); }); - const saves = JSON.parse(localStorage.saves || null) || {}; - //saves[rpyscript] = saves[rpyscript] || []; - //if (saves[rpyscript].length > 0) { - // this.addMainMenuItem("Load…", () => this.showLoadStateMenu()); - // this.addMainMenuItem("Delete…", () => this.showRemoveStateMenu()); - //} + const saves = this.loadCurrentStatesList(); + if (saves.length > 0) { + this.addMainMenuItem("Load…", () => this.showLoadStateMenu()); + this.addMainMenuItem("Delete…", () => this.showRemoveStateMenu()); + } this.addMainMenuItem("Close", () => { this.elMenu.style.display = 'none'; }); this.elMenu.style.display = 'block'; } + private showLoadStateMenu(): void { + document.getElementById('menuTitle').textContent = 'Load'; + document.getElementById('menuChoose').innerHTML = ''; + this.addMainMenuItem("Close", () => { + this.elMenu.style.display = 'none'; + }); + + const saves = this.loadCurrentStatesList(); + for (let i = saves.length - 1; i >= 0; i--) { + const save = saves[i]; + this.addMainMenuItem(save.time.toLocaleString(), () => { + this.loadState(save); + this.elMenu.style.display = 'none'; + }); + } + this.elMenu.style.display = 'block'; + } + + private showRemoveStateMenu(): void { + document.getElementById('menuTitle').textContent = 'Delete'; + document.getElementById('menuChoose').innerHTML = ''; + this.addMainMenuItem("Close", () => { + this.elMenu.style.display = 'none'; + }); + + const saves = this.loadCurrentStatesList(); + for (let i = saves.length - 1; i >= 0; i--) { + this.addMainMenuItem(saves[i].time.toLocaleString(), () => { + this.deleteState(i); + this.showRemoveStateMenu(); + }); + } + this.elMenu.style.display = 'block'; + } + + private loadCurrentStatesList(): SaveInfo[] { + const rpyscript = window['rpyscript'] || 'default'; + const saves = JSON.parse(window.localStorage.getItem('saves') || null) || {}; + const items = saves[rpyscript] || []; + return items as SaveInfo[]; + } + + private saveState(): void { + this.modifyCurrentScenarioStates(items => items.push(this.createSave())) + } + + private deleteState(i: number): void { + this.modifyCurrentScenarioStates(items => items.splice(i, 1)) + } + + private modifyCurrentScenarioStates(func: (items: SaveInfo[]) => void): void { + const rpyscript = window['rpyscript'] || 'default'; + const saves = JSON.parse(window.localStorage.getItem('saves') || null) || {}; + if (!(rpyscript in saves)) { + saves[rpyscript] = []; + } + func(saves[rpyscript] as SaveInfo[]); + window.localStorage.setItem('saves', JSON.stringify(saves)); + } + + private createSave(): SaveInfo { + const save = new SaveInfo(); + save.position = this.navigable.getLastPosition(); + save.time = new Date(); + save.variables = Variables.getVariables(); + save.characterNames = this.characters.getNames(); + save.backgroundType = this.backgroundType; + save.backgroundName = this.backgroundName; + return save; + } + + private loadState(save: SaveInfo): void { + Variables.setVariables(save.variables); + this.characters.setNames(save.characterNames); + if (!TextUtils.isEmpty(save.backgroundName)) { + this.background(save.backgroundType, save.backgroundName, ""); + } + this.navigable.setPosition(save.position); + } + private addMainMenuItem(name: string, func: () => void) { const li = document.createElement('li'); li.textContent = name; @@ -382,7 +462,6 @@ export class MainView implements ViewModel { public music(name: string, fade: FadeInfo): void { try { - console.log("Music: " + name, this.pathResolver.music(name)); this.stopMusic(this.musicPlayerAudio.fade); this.musicPlayerAudio.src = this.pathResolver.music(name); this.musicPlayerAudio.fade = fade; @@ -589,9 +668,9 @@ export class MainView implements ViewModel { this.stopSoundLoop(FadeInfo.NO_FADE); this.stopAmbience(FadeInfo.NO_FADE); this.spritesClear(); - this.text('{center}{html}Сценарий завершён'); + this.text('{center}{html}Scenario completed'); } else { - this.text('{center}{html}Вернуться на главную'); + this.text('{center}{html}Back to main page'); } } diff --git a/src/view/model/Characters.ts b/src/view/model/Characters.ts index 2cf4b23..e47c5f6 100644 --- a/src/view/model/Characters.ts +++ b/src/view/model/Characters.ts @@ -1,8 +1,5 @@ import { NameInfo } from "./NameInfo"; - -type NameInfoMap = { - [key: string]: NameInfo; -}; +import { NameInfoMap } from "./NameInfoMap"; export class Characters { private static readonly DEFAULT_COLOR: number = 0xFFC0C0C0; @@ -34,6 +31,14 @@ export class Characters { return shortName in this.names; } + public getNames(): NameInfoMap { + return this.names; + } + + public setNames(dump: NameInfoMap): void { + this.names = dump; + } + public makeNamesUnknown(): void { } public makeNamesKnown(): void { } diff --git a/src/view/model/NameInfoMap.ts b/src/view/model/NameInfoMap.ts new file mode 100644 index 0000000..eefaadb --- /dev/null +++ b/src/view/model/NameInfoMap.ts @@ -0,0 +1,5 @@ +import { NameInfo } from "./NameInfo"; + +export type NameInfoMap = { + [key: string]: NameInfo; +}; \ No newline at end of file