diff --git a/src/components/TabSelector.svelte b/src/components/TabSelector.svelte
index ee5e65d..76ac0da 100644
--- a/src/components/TabSelector.svelte
+++ b/src/components/TabSelector.svelte
@@ -1,8 +1,8 @@
diff --git a/src/components/Wrapper.svelte b/src/components/Wrapper.svelte
index 403664f..9686bbc 100644
--- a/src/components/Wrapper.svelte
+++ b/src/components/Wrapper.svelte
@@ -1,5 +1,5 @@
@@ -57,13 +57,13 @@
{
+ Settings.settings.subscribe((settings) => {
Log.#maxLogLength = Math.max(...settings.layout.logLength.options);
})();
}
@@ -25,9 +25,9 @@ export class Log extends StaticClass {
);
}
- static reset(): void {
+ static reset(notify = true): void {
Message.even = true;
this.messages.set([]);
- this.addMessage("Log cleared");
+ if (notify) this.addMessage("Log cleared");
}
}
diff --git a/src/shark/Message.ts b/src/shark/Message.ts
index be6e161..2679302 100644
--- a/src/shark/Message.ts
+++ b/src/shark/Message.ts
@@ -1,5 +1,5 @@
export type AddMessageEvent = { message: string; messageType?: MessageType };
-export type ResetMessagesEvent = { reset: true };
+export type ResetMessagesEvent = { notify?: boolean };
export enum MessageType {
message = "message",
diff --git a/src/shark/SaveHandler.ts b/src/shark/SaveHandler.ts
new file mode 100644
index 0000000..9041a72
--- /dev/null
+++ b/src/shark/SaveHandler.ts
@@ -0,0 +1,111 @@
+import type { Message, MessageType } from "./Message";
+import type { Settings } from "./Settings";
+import type { SharkGame } from "./SharkGame";
+import { StaticClass } from "./StaticClass";
+import type { TabHandler } from "./TabHandler";
+
+const __EMPTY_OBJECT = {};
+type Version0Save = typeof __EMPTY_OBJECT;
+type Version1Save = Version0Save & {
+ version: 1;
+ messages: {
+ message: string;
+ type: MessageType;
+ }[];
+ selectedTab: keyof typeof TabHandler["AllTabs"];
+ settings: ReturnType;
+};
+
+type CurrentVersionSave = Version1Save;
+type Save = Version0Save | Version1Save;
+
+export class SaveHandler extends StaticClass {
+ static readonly saveName = "sharg-save";
+
+ static async save(game: typeof SharkGame): Promise {
+ const messages = await new Promise((resolve) => {
+ game.Log.messages.subscribe((messages) => {
+ resolve(messages);
+ })();
+ });
+
+ const allTabsEntries = Object.entries(
+ game.TabHandler.AllTabs
+ ) as unknown as [
+ keyof typeof TabHandler["AllTabs"],
+ typeof TabHandler["AllTabs"][keyof typeof TabHandler["AllTabs"]]
+ ][];
+ const selectedTabIndex = allTabsEntries.findIndex(
+ ([, tab]) => tab === game.TabHandler.currentTab
+ );
+ const selectedTabName = allTabsEntries[selectedTabIndex][0];
+
+ const save: CurrentVersionSave = {
+ version: 1,
+ selectedTab: selectedTabName,
+ settings: game.Settings.getCurrent(),
+ messages: messages.map((message) => {
+ return {
+ message: message.message,
+ type: message.type,
+ };
+ }),
+ };
+ const encodedSave = JSON.stringify(save);
+
+ localStorage.setItem(SaveHandler.saveName, encodedSave);
+ }
+
+ static async load(game: typeof SharkGame): Promise {
+ const localSave = localStorage.getItem(SaveHandler.saveName);
+ const loadedSave = JSON.parse(localSave ?? "{}");
+ const saveVersion = loadedSave.version ?? 0;
+
+ const migrators = SaveHandler.migrators.slice(saveVersion);
+
+ let save = loadedSave;
+ for (let i = 0; i < migrators.length; i++) {
+ console.debug(
+ `Executing save migrator ${i + 1} / ${migrators.length} (${
+ saveVersion + i + 1
+ })`
+ );
+ save = migrators[i](save);
+ }
+
+ const fullSave = save as CurrentVersionSave;
+
+ game.Settings.settings.update((settings) => {
+ Object.entries(settings).forEach(([categoryName, categorySettings]) => {
+ Object.entries(categorySettings).forEach(([settingName, setting]) => {
+ setting.current =
+ fullSave.settings[`${categoryName};${settingName}`] ??
+ setting.default;
+ });
+ });
+ return settings;
+ });
+
+ game.Log.reset(false);
+
+ fullSave.messages.forEach((message) => {
+ game.Log.addMessage(message.message, message.type);
+ });
+ game.TabHandler.currentTab = game.TabHandler.AllTabs[fullSave.selectedTab];
+
+ return game;
+ }
+
+ static migrators: ((save: Save) => Save)[] = [
+ (): Version1Save => {
+ const newSave = {
+ version: 1 as const,
+ messages: [],
+ selectedTab: "Home" as const,
+ settings: {},
+ };
+
+ return newSave;
+ },
+ ];
+}
diff --git a/src/shark/Settings.ts b/src/shark/Settings.ts
index bdcd40e..ac8b8a2 100644
--- a/src/shark/Settings.ts
+++ b/src/shark/Settings.ts
@@ -1,156 +1,188 @@
-// How to make current just be undefined here, but keep the type of defaultValue?
+// How to make current just be undefined here, but keep the type of default?
import { writable } from "svelte/store";
+import type { Writable } from "svelte/store";
+import { StaticClass } from "./StaticClass";
-export const Settings = writable({
- layout: {
- logLength: {
- current: 30,
- defaultValue: 30 as const,
- name: "Log Messages" as const,
- description: "The number of messages shown in the log" as const,
- options: [5, 10, 15, 20, 30, 60] as const,
+export class Settings extends StaticClass {
+ static readonly #settings = {
+ layout: {
+ logLength: {
+ current: 30,
+ default: 30 as const,
+ name: "Log Messages" as const,
+ description: "The number of messages shown in the log" as const,
+ options: [5, 10, 15, 20, 30, 60] as const,
+ },
},
- },
- appearance: {
- enableThemes: {
- current: true,
- defaultValue: true as const,
- name: "Enable Planet-dependent Styles" as const,
- description:
- "Whether page colors should change for different planets" as const,
- options: [true, false] as const,
+ appearance: {
+ enableThemes: {
+ current: true,
+ default: true as const,
+ name: "Enable Planet-dependent Styles" as const,
+ description:
+ "Whether page colors should change for different planets" as const,
+ options: [true, false] as const,
+ },
+ theme: {
+ current: "marine",
+ default: "marine" as const,
+ name: "Currently enabled theme" as const,
+ description:
+ "Only applied if planet-dependent styles are enabled" as const,
+ options: [
+ "abandoned",
+ "chaotic",
+ "frigid",
+ "haven",
+ "marine",
+ "shrouded",
+ "tempestuous",
+ "violent",
+ ],
+ },
},
- theme: {
- current: "marine",
- defaultValue: "marine" as const,
- name: "Currently enabled theme" as const,
- description:
- "Only applied if planet-dependent styles are enabled" as const,
- options: [
- "abandoned",
- "chaotic",
- "frigid",
- "haven",
- "marine",
- "shrouded",
- "tempestuous",
- "violent",
- ],
+ other: {
+ updateCheck: {
+ current: true,
+ default: true as const,
+ name: "Check for updates" as const,
+ description: "Whether to notify you of new updates" as const,
+ options: [true, false] as const,
+ },
},
- },
- other: {
- updateCheck: {
- current: true,
- defaultValue: true as const,
- name: "Check for updates" as const,
- description: "Whether to notify you of new updates" as const,
- options: [true, false] as const,
+ // To test scrolling of the settings modal (gonna use them to test saving later, too)
+ nil: {
+ nil1: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil2: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil3: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil4: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil5: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil6: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil7: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil8: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil9: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil10: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil11: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil12: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil13: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil14: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil15: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil16: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
+ nil17: {
+ default: true as const,
+ name: "placeholder" as const,
+ description: "placeholder." as const,
+ options: [true, false] as const,
+ },
},
- },
- // To test scrolling of the settings modal (gonna use them to test saving later, too)
- nil: {
- nil1: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil2: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil3: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil4: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil5: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil6: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil7: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil8: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil9: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil10: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil11: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil12: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil13: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil14: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil15: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil16: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- nil17: {
- defaultValue: true as const,
- name: "placeholder" as const,
- description: "placeholder." as const,
- options: [true, false] as const,
- },
- },
-});
+ };
+ static readonly settings = writable(Settings.#settings);
+
+ static getSettings(): SettingsRecord {
+ return Object.seal(Object.assign({}, Settings.#settings));
+ }
+ static getCurrent(): Record<`${string};${string}`, Setting> {
+ const current = this.getSettings();
+ const result: Record<`${string};${string}`, Setting> = {};
+ for (const [categoryName, category] of Object.entries(current)) {
+ for (const [settingName, setting] of Object.entries(category)) {
+ result[`${categoryName};${settingName}`] = setting.current;
+ }
+ }
+ return result;
+ }
+}
+
+export type Setting = {
+ current: unknown;
+ readonly default: unknown;
+ readonly name: unknown;
+ readonly description: unknown;
+ readonly options: readonly unknown[];
+};
+export type SettingsRecord = typeof Settings["settings"] extends Writable<
+ infer X
+>
+ ? X
+ : never;
diff --git a/src/shark/SharkGame.ts b/src/shark/SharkGame.ts
index 8825c08..1476904 100644
--- a/src/shark/SharkGame.ts
+++ b/src/shark/SharkGame.ts
@@ -1,8 +1,9 @@
import { Resources } from "./data/Resources";
import { Log } from "./Log";
+import { SaveHandler } from "./SaveHandler";
import { Settings } from "./Settings";
import { StaticClass } from "./StaticClass";
-import { Tabs } from "./Tabs";
+import { TabHandler } from "./TabHandler";
export class SharkGame extends StaticClass {
static readonly #GAME_NAMES = [
@@ -58,19 +59,22 @@ export class SharkGame extends StaticClass {
static readonly Settings = Settings;
static readonly Log = Log;
static readonly Resources = Resources;
- static readonly Tabs = Tabs;
+ static readonly TabHandler = TabHandler;
+ static readonly SaveHandler = SaveHandler;
static init(): void {
- Settings.update((settings) => {
+ Settings.settings.update((settings) => {
for (const settingsCategory of Object.values(settings)) {
for (const setting of Object.values(settingsCategory)) {
- setting.current = setting.defaultValue;
+ setting.current = setting.default;
}
}
return settings;
});
Log.init();
+ SaveHandler.load(this);
+
SharkGame.title =
SharkGame.#GAME_NAMES[
Math.floor(Math.random() * SharkGame.#GAME_NAMES.length)
diff --git a/src/shark/Tabs.ts b/src/shark/TabHandler.ts
similarity index 57%
rename from src/shark/Tabs.ts
rename to src/shark/TabHandler.ts
index 508b138..0fc0bb0 100644
--- a/src/shark/Tabs.ts
+++ b/src/shark/TabHandler.ts
@@ -2,11 +2,11 @@ import Home from "../components/Tabs/Home.svelte";
import Lab from "../components/Tabs/Lab.svelte";
import { StaticClass } from "./StaticClass";
-export class Tabs extends StaticClass {
+export class TabHandler extends StaticClass {
static readonly AllTabs = {
Home,
Lab,
} as const;
- static currentTab: typeof Tabs.AllTabs[keyof typeof Tabs.AllTabs] =
- Tabs.AllTabs.Home;
+ static currentTab: typeof TabHandler.AllTabs[keyof typeof TabHandler.AllTabs] =
+ TabHandler.AllTabs.Home;
}