123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- /// <reference path="World/Classes/Save/StoredVariable.ts" />
- /// <reference path="World/TurnSequence.ts" />
- /// <reference path="World/Classes/Rule.ts" />
- /// <reference path="Controls/Classes/StoredMemory.ts" />
- interface SavedThing {
- Name : string;
- Unique : boolean;
- Changes : {[id : string] : any}
- }
- // The player is always a humanoid
- interface SavedPlayer {
- Parts : Array<SavedThing>;
- PlayerChanges : {[id : string] : any};
- Name : string; // Are we going to do names? I Mean it'd be fun
- }
- interface SaveStructure {
- Variables : {[id : string] : any};
- Wielded : Array<SavedThing>;
- Worn : Array<SavedThing>;
- Carried : Array<SavedThing>;
- Player : SavedPlayer;
- Rounds : number;
- Date : string;
- UniqueThings : Array<SavedThing>;
- }
- module SaveHandler {
- let saveName = "Obelisk_Save"
- let saveExtension = "obsav";
- let storagePrefix = "save_";
- let saveSlot = 0;
- let errors : Array<string> = [];
- let erasing = false;
- let virgin = new StoredMemory("First time saving", true);
- export let AfterLoad = new Rulebook<SaveStructure>("After loading");
- export async function readFile () : Promise<string> {
- let element = <HTMLInputElement> document.createElement("input");
- element.type = "file";
- element.accept = "." + saveExtension;
- let promise : Promise<string> = new Promise((resolve, reject) => {
- element.onchange = () => {
- if (element.files.length == 0) {
- resolve(undefined);
- } else {
- var fr = new FileReader();
- fr.onload = (ev) => {
- resolve(ev.target['result']);
- }
- fr.readAsText(element.files[0]);
- }
- }
- });
- element.click();
- return promise;
- }
- function download(filename, text) {
- var element = document.createElement('a');
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
- element.setAttribute('download', filename);
- element.style.display = 'none';
- document.body.appendChild(element);
- element.click();
- document.body.removeChild(element);
- }
- export function loadSave () {
- let input = <HTMLInputElement> document.createElement("input");
- input.type = "file";
- input.accept = "." + saveExtension;
- document.body.appendChild(input);
- input.addEventListener("change", () => {
- let reader = new FileReader();
- reader.onload = () => {
- let text = reader.result;
- // TODO: Check if this is actually working. Typescript 3 suggested it wouldn't.
- SaveHandler.loadGame(<SaveStructure> JSON.parse(<string> text));
- };
- reader.readAsText(input.files[0]);
- });
- input.click();
- document.body.removeChild(input);
- }
- function getItem (thing : SavedThing) : Thing {
- let item : Thing;
- if (thing.Unique) {
- item = Thing.getUnique(thing.Name);
- } else {
- let items = Thing.getNonUnique(thing.Name);
- if (items.length > 0) {
- item = items[0];
- }
- if (item == undefined) {
- let error = thing.Name + " no longer exists.";
- console.error("[SaveHandler] " + error);
- errors.push(error)
- return undefined;
- } else {
- item = item.clone(true);
- }
- }
- item.setChanges(thing.Changes);
- return item;
- }
- export function loadGame (saveStruc : SaveStructure) {
- // interface SaveStructure {
- // Variables : {[id : string] : any};
- // Wielded : Array<SavedThing>;
- // Worn : Array<SavedThing>;
- // Carried : Array<SavedThing>;
- // Player : SavedPlayer;
- // Rounds : number;
- // }
- let player = WorldState.player;
- StoredVariable.updateFromObject(saveStruc.Variables);
- WorldState.setCurrentTurn(saveStruc.Rounds);
- Thing.WearRelation.getRight(player).forEach((thing : Thing) => {
- Thing.WearRelation.unsetRight(thing);
- });
- Thing.WieldRelation.getRight(player).forEach((thing : Thing) => {
- Thing.WieldRelation.unsetRight(thing);
- });
- Thing.CarryRelation.getRight(player).forEach((thing : Thing) => {
- Thing.CarryRelation.unsetRight(thing);
- });
- saveStruc.Wielded.forEach((thing : SavedThing) => {
- let item = getItem(thing);
- if (item != undefined) WorldState.player.setWielded(item);
- });
- saveStruc.Worn.forEach((thing : SavedThing) => {
- let item = getItem(thing);
- if (item != undefined) WorldState.player.setWorn(item);
- });
- saveStruc.Carried.forEach((thing : SavedThing) => {
- let item = getItem(thing);
- if (item != undefined) WorldState.player.setCarried(item);
- });
- let savedPlayer = saveStruc.Player;
- player.setName(savedPlayer.Name);
- player.setChanges(savedPlayer.PlayerChanges); // this adds the right gendered parts
- savedPlayer.Parts.forEach((part : SavedThing) => {
- let bpList = <Array<Bodypart>> player.getPartsByName(part.Name);
- if (bpList != undefined) {
- bpList[0].setChanges(part.Changes);
- }
- });
- saveStruc.UniqueThings.forEach((savedThing : SavedThing) => {
- let thing = Thing.getUnique(savedThing.Name);
- if (thing != undefined) {
- thing.setChanges(savedThing.Changes);
- }
- });
- }
- function exportPlayer () : SavedPlayer {
- return {
- Name : WorldState.player.getName(),
- PlayerChanges : WorldState.player.getChanges(),
- Parts : exportThings(WorldState.player.getParts())
- }
- }
- export function exportThings (arr : Array<Thing>, changedOnly? : boolean) : Array<SavedThing> {
- let obj = [];
- for (let i = 0; i < arr.length; i++) {
- let thing = <Thing> arr[i];
- let savedThing = {
- Unique : thing.isUnique(),
- Name : thing.getName(),
- Changes : thing.getChanges()
- };
- if (!changedOnly || Object.keys(savedThing.Changes).length > 0) {
- obj.push(savedThing);
- }
- }
- return obj;
- }
- export function getSaveStructure () : SaveStructure {
- let variables = StoredVariable.getVariables();
- let savedVariables = {};
- for (let i = 0; i < variables.length; i++) {
- savedVariables[variables[i].id] = variables[i].exportAsObject();
- }
- let wielded = Thing.WieldRelation.getRight(WorldState.player);
- let worn = Thing.WearRelation.getRight(WorldState.player);
- let carried = Thing.CarryRelation.getRight(WorldState.player);
- let saveStruc : SaveStructure = {
- Variables : savedVariables,
- UniqueThings : exportThings(Thing.getUniques()),
- Wielded : exportThings(wielded),
- Worn : exportThings(worn),
- Carried : exportThings(carried),
- Player : exportPlayer(),
- Rounds : WorldState.getCurrentTurn(),
- Date : new Date().toLocaleString()
- };
- console.debug("[SaveHandler] Created Save Structure", saveStruc);
- return saveStruc;
- }
- export function setSlot (slotN : number) {
- saveSlot = slotN;
- }
- export function saveToStorage () {
- Controls.Memory.setValue(storagePrefix + saveSlot, getSaveStructure());
- }
- export async function loadFromStorage () {
- if (!erasing) {
- let saveStruct = (Controls.Memory.getValue(storagePrefix + saveSlot, undefined));
- if (saveStruct != undefined) {
- loadGame(saveStruct);
- return await AfterLoad.execute({noun : saveStruct});
- }
- }
- // this is a new game!
- await CharacterCreation.rulebook.execute({});
- }
- export async function loadFromFile () {
- PlayBegins.LOAD_FAILED = false;
- let promise = readFile();
- let finishedAny;
- let realPromise = new Promise((resolve) => {
- finishedAny = resolve;
- });
- let say = new Say("No save file was loaded.", Say.PARAGRAPH_BREAK, Say.CENTERED, new SayBold("Press any key to return."));
- let sayElements = await Elements.CurrentTurnHandler.getSayElementsAsContent(say);
- Elements.CurrentTurnHandler.print(...sayElements);
- let nextKey = Controls.KeyHandler.getNextKey();
- promise.then((file) => {
- Controls.KeyHandler.stopGivingNextKey(nextKey);
- Elements.CurrentTurnHandler.unprint(...sayElements);
- loadGame(getFromFile(file)); // If no file was chosen then change isn't triggered, so it's never undefined...
- finishedAny();
- });
- nextKey.then((keyCode : KeyCode) => {
- PlayBegins.LOAD_FAILED = true;
- finishedAny();
- });
- await realPromise;
- }
- export function getSayForSlot (slotNumber : number) {
- let saveStruct : SaveStructure = (Controls.Memory.getValue(storagePrefix + slotNumber, undefined));
- if (saveStruct == undefined) {
- return new Say("New Game");
- } else {
- let erasingText = erasing ? "(ERASE) - " : "";
- return new Say(erasingText, saveStruct.Player.Name + " - Turns: " + saveStruct.Rounds + " - Last Played: " + saveStruct.Date);
- }
- }
- export function getFromFile (saveText) : SaveStructure {
- return JSON.parse(decodeURIComponent(atob(saveText)));
- }
- export function saveToFile () {
- // It's okay if you want to cheat, but if you tamper with the save file, please be mindful when reporting "bugs".
- download(saveName + "." + saveExtension, btoa(unescape(encodeURIComponent((JSON.stringify(getSaveStructure()))))));
- }
- export function isErasing () {
- return erasing;
- }
- export function toggleErasing () {
- erasing = !erasing;
- }
- export function isVirgin () {
- let was = virgin.getValue();
- virgin.storeValue(false);
- return was;
- }
- }
- // document.getElementById("SaveGameButton").addEventListener("click", () => {
- // SaveHandler.saveToFile();
- // });
- TurnSequence.rulebook.createAndAddRule({
- name : "Save game to Storage",
- priority : Rule.PRIORITY_LOWEST,
- firstPriority : Rule.PRIORITY_LOWEST,
- code : () => {
- SaveHandler.saveToStorage();
- }
- })
|