I made a grave mistake in not committing some of these things separately
But now we both have to live with this, because I can't really be bothered to fix it Summary: +Save resources *Restructure Handlers -Remove StaticClass +Add Resource data files ~Change loading
This commit is contained in:
parent
69932a28f7
commit
0c107bc849
25 changed files with 368 additions and 302 deletions
|
@ -20,4 +20,7 @@ module.exports = {
|
||||||
es2021: true,
|
es2021: true,
|
||||||
node: false,
|
node: false,
|
||||||
},
|
},
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-unused-vars": ["warn", { args: "all", argsIgnorePattern: "^_", ignoreRestSiblings: false }],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
root = document.documentElement;
|
root = document.documentElement;
|
||||||
|
|
||||||
SharkGame.Settings.settings.subscribe((settings) => {
|
SharkGame.SettingsHandler.settings.subscribe((settings) => {
|
||||||
root.classList.toggle("no-theme", !settings.appearance.enableThemes.current);
|
root.classList.toggle("no-theme", !settings.appearance.enableThemes.current);
|
||||||
settings.appearance.theme.options.forEach((theme) => {
|
settings.appearance.theme.options.forEach((theme) => {
|
||||||
root.classList.toggle(theme, theme === settings.appearance.theme.current);
|
root.classList.toggle(theme, theme === settings.appearance.theme.current);
|
||||||
|
@ -33,12 +33,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBeforeUnload(event: BeforeUnloadEvent) {
|
function handleBeforeUnload(event: BeforeUnloadEvent) {
|
||||||
console.debug(
|
|
||||||
"beforeUnload",
|
|
||||||
Date.now() - SharkGame.SaveHandler.lastSaved >= 60 * 1000,
|
|
||||||
Date.now() - SharkGame.SaveHandler.lastSaved,
|
|
||||||
60 * 1000
|
|
||||||
);
|
|
||||||
// If last save is over a minute old
|
// If last save is over a minute old
|
||||||
if (Date.now() - SharkGame.SaveHandler.lastSaved >= 60 * 1000) {
|
if (Date.now() - SharkGame.SaveHandler.lastSaved >= 60 * 1000) {
|
||||||
// Annotyingly, the standardized way isn't supported, so both outdated ones will have to suffice
|
// Annotyingly, the standardized way isn't supported, so both outdated ones will have to suffice
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
return game.SaveHandler.save(game);
|
return game.SaveHandler.save(game);
|
||||||
},
|
},
|
||||||
settings() {
|
settings() {
|
||||||
openModal(SettingsModal, { settings: game.Settings.settings }, { replace: true });
|
openModal(SettingsModal, { settings: game.SettingsHandler.settings }, { replace: true });
|
||||||
},
|
},
|
||||||
help() {
|
help() {
|
||||||
openModal(HelpModal, { discordLink }, { replace: true });
|
openModal(HelpModal, { discordLink }, { replace: true });
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
import { slide } from "svelte/transition";
|
import { slide } from "svelte/transition";
|
||||||
|
|
||||||
import type { AddMessageEvent, ResetLogEvent, Message } from "../shark/Message";
|
import type { AddMessageEvent, ResetLogEvent, Message } from "../shark/helperTypes/Message";
|
||||||
import { MessageType } from "../shark/Message";
|
import { MessageType } from "../shark/helperTypes/Message";
|
||||||
|
|
||||||
export let messages: Writable<Message[]>;
|
export let messages: Writable<Message[]>;
|
||||||
export let logLength: number;
|
export let logLength: number;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
{isOpen}
|
{isOpen}
|
||||||
confirm={() => {
|
confirm={() => {
|
||||||
game.SaveHandler.reset();
|
game.SaveHandler.resetSave();
|
||||||
}}
|
}}
|
||||||
deny={closeAllModals}>Do you want to reset your save?</ConfirmModal
|
deny={closeAllModals}>Do you want to reset your save?</ConfirmModal
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Settings } from "../../shark/Settings";
|
import type { SettingsHandler } from "../../shark/handlers/SettingsHandler";
|
||||||
|
|
||||||
import BaseModal from "./Base/Modal.svelte";
|
import BaseModal from "./Base/Modal.svelte";
|
||||||
|
|
||||||
export let isOpen: boolean;
|
export let isOpen: boolean;
|
||||||
|
|
||||||
export let settings: typeof Settings["settings"];
|
export let settings: typeof SettingsHandler["settings"];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal {isOpen}>
|
<BaseModal {isOpen}>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<svelte:options immutable />
|
<svelte:options immutable />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Resource } from "../../shark/data/Resources";
|
import type { Resource } from "../../shark/helperTypes/Resource";
|
||||||
|
|
||||||
export let categoryName: string;
|
export let categoryName: string;
|
||||||
export let collapsed: boolean;
|
export let collapsed: boolean;
|
||||||
export let resources: [string, Resource][];
|
export let resources: Resource[];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<tr on:click class="subhead" tabindex="0" role="button">
|
<tr on:click class="subhead" tabindex="0" role="button">
|
||||||
|
@ -13,9 +13,9 @@
|
||||||
<td colspan="3"><h4>{categoryName}</h4> </td>
|
<td colspan="3"><h4>{categoryName}</h4> </td>
|
||||||
</tr>
|
</tr>
|
||||||
{#if !collapsed}
|
{#if !collapsed}
|
||||||
{#each resources as [resourceName, resource], index}
|
{#each resources as resource, index}
|
||||||
<tr class:even={index % 2 === 0} class:odd={index % 2 === 1}>
|
<tr style="--resource-color: {resource.color}" class:even={index % 2 === 0} class:odd={index % 2 === 1}>
|
||||||
<td colspan="2">{resourceName}</td>
|
<td colspan="2">{resource.humanName}</td>
|
||||||
<td>{resource.amount}</td>
|
<td>{resource.amount}</td>
|
||||||
<td>{Math.round(100 * (resource.change + Number.EPSILON)) / 100}/s</td>
|
<td>{Math.round(100 * (resource.change + Number.EPSILON)) / 100}/s</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -30,8 +30,12 @@
|
||||||
background-color: var(--color-lighter);
|
background-color: var(--color-lighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text-shadow: -1px -1px 2px var(--color-med), 1px -1px 2px var(--color-med), -1px 1px 2px var(--color-med),
|
||||||
|
1px 1px 2px var(--color-med);
|
||||||
|
|
||||||
> td:first-child {
|
> td:first-child {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
color: var(--resource-color);
|
||||||
}
|
}
|
||||||
> td {
|
> td {
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
|
|
||||||
import type { Resource } from "../../shark/data/Resources";
|
import type { Resource } from "../../shark/helperTypes/Resource";
|
||||||
import ResourceGroup from "./ResourceGroup.svelte";
|
import ResourceGroup from "./ResourceGroup.svelte";
|
||||||
|
|
||||||
let collapsed: string[] = [];
|
let collapsed: string[] = [];
|
||||||
|
@ -15,16 +15,14 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function resourceGroups(resources: Record<string, Resource>) {
|
function resourceGroups(resources: Record<string, Resource>) {
|
||||||
return Object.entries(
|
const result: Record<string, Resource[]> = {};
|
||||||
Object.entries(resources).reduce((reduced, [resourceName, resource]) => {
|
for (const resource of Object.values(resources)) {
|
||||||
if (reduced[resource.category] === undefined) {
|
if (result[resource.category] === undefined) {
|
||||||
reduced[resource.category] = [];
|
result[resource.category] = [];
|
||||||
}
|
}
|
||||||
reduced[resource.category].push([resourceName, resource]);
|
result[resource.category].push(resource);
|
||||||
|
}
|
||||||
return reduced;
|
return Object.entries(result);
|
||||||
}, {} as Record<string, [string, Resource][]>)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleCollapsed(categoryName: string) {
|
function toggleCollapsed(categoryName: string) {
|
||||||
|
|
|
@ -2,24 +2,34 @@
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { HomeAction, HomeActions } from "../../shark/data/HomeActions";
|
import type { HomeAction, HomeActions } from "../../shark/data/HomeActions";
|
||||||
import type { AddMessageEvent } from "../../shark/Message";
|
import type { ResourceHandler } from "../../shark/handlers/ResourceHandler";
|
||||||
|
import { AddMessageEvent, MessageType } from "../../shark/helperTypes/Message";
|
||||||
import { Resources } from "../../shark/data/Resources";
|
|
||||||
|
|
||||||
export let homeActions: ReturnType<typeof HomeActions["getActionTable"]>;
|
export let homeActions: ReturnType<typeof HomeActions["getActionTable"]>;
|
||||||
|
export let resourceHandler: typeof ResourceHandler;
|
||||||
export let showIcons: boolean;
|
export let showIcons: boolean;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ addMessage: AddMessageEvent }>();
|
const dispatch = createEventDispatcher<{ addMessage: AddMessageEvent }>();
|
||||||
|
|
||||||
function homeActionClick(homeAction: HomeAction) {
|
function homeActionClick(homeAction: HomeAction) {
|
||||||
if (homeAction.effect.resource) {
|
if (homeAction.effect.addResources !== undefined) {
|
||||||
const resources = Resources.getResources(Object.keys(homeAction.effect.resource));
|
const addResources = homeAction.effect.addResources;
|
||||||
|
// Don't use ResourceHandlers's increaseResource method
|
||||||
if (resources !== null) {
|
// it would call update once per resource instead of once total
|
||||||
for (const [resourceName, resource] of Object.entries(resources)) {
|
resourceHandler.allResources.update((allResources) => {
|
||||||
resource.amount += homeAction.effect.resource[resourceName];
|
for (const [resourceId, delta] of Object.entries(addResources)) {
|
||||||
|
if (resourceId in addResources) {
|
||||||
|
if (!(resourceId in allResources)) {
|
||||||
|
dispatch("addMessage", {
|
||||||
|
message: `Unknown resourceId '${resourceId}'`,
|
||||||
|
messageType: MessageType.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
allResources[resourceId].amount += delta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return allResources;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (homeAction.outcomes) {
|
if (homeAction.outcomes) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { AddMessageEvent } from "../shark/Message";
|
import type { AddMessageEvent } from "../shark/helperTypes/Message";
|
||||||
import type { SharkGame } from "../shark/SharkGame";
|
import type { SharkGame } from "../shark/SharkGame";
|
||||||
import Log from "./Log.svelte";
|
import Log from "./Log.svelte";
|
||||||
import ResourceTable from "./ResourceTable/ResourceTable.svelte";
|
import ResourceTable from "./ResourceTable/ResourceTable.svelte";
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
let logLength: number;
|
let logLength: number;
|
||||||
let showIcons: boolean;
|
let showIcons: boolean;
|
||||||
game.Settings.settings.subscribe((settings) => {
|
game.SettingsHandler.settings.subscribe((settings) => {
|
||||||
logLength = settings.layout.logLength.current;
|
logLength = settings.layout.logLength.current;
|
||||||
showIcons = settings.appearance.showIcons.current;
|
showIcons = settings.appearance.showIcons.current;
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<div id="left-column" class:expanded={sidebarExpanded} class:collapsed={!sidebarExpanded}>
|
<div id="left-column" class:expanded={sidebarExpanded} class:collapsed={!sidebarExpanded}>
|
||||||
<ResourceTable resources={game.Resources.Resources} />
|
<ResourceTable resources={game.ResourceHandler.allResources} />
|
||||||
<Log
|
<Log
|
||||||
{logLength}
|
{logLength}
|
||||||
messages={game.MessageHandler.messages}
|
messages={game.MessageHandler.messages}
|
||||||
|
@ -63,6 +63,7 @@
|
||||||
on:addMessage={handleAddMessage}
|
on:addMessage={handleAddMessage}
|
||||||
{showIcons}
|
{showIcons}
|
||||||
homeActions={game.Data.HomeActions.getActionTable()}
|
homeActions={game.Data.HomeActions.getActionTable()}
|
||||||
|
resourceHandler={game.ResourceHandler}
|
||||||
/>
|
/>
|
||||||
{:else if game.TabHandler.currentTab === game.TabHandler.AllTabs.Lab}
|
{:else if game.TabHandler.currentTab === game.TabHandler.AllTabs.Lab}
|
||||||
<svelte:component this={game.TabHandler.AllTabs.Lab} />
|
<svelte:component this={game.TabHandler.AllTabs.Lab} />
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { StaticClass } from "./StaticClass";
|
export const LZString = new (class LZString {
|
||||||
|
readonly #KeyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
export class LZString extends StaticClass {
|
readonly #KeyStrBase64Dict = this.#createBaseDict(this.#KeyStrBase64);
|
||||||
static readonly #KeyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
||||||
|
|
||||||
static readonly #KeyStrBase64Dict = LZString.#createBaseDict(LZString.#KeyStrBase64);
|
#createBaseDict(alphabet: string): Record<string, number> {
|
||||||
|
|
||||||
static #createBaseDict(alphabet: string): Record<string, number> {
|
|
||||||
const dict: Record<string, number> = {};
|
const dict: Record<string, number> = {};
|
||||||
for (let i = 0; i < alphabet.length; i++) {
|
for (let i = 0; i < alphabet.length; i++) {
|
||||||
dict[alphabet[i]] = i;
|
dict[alphabet[i]] = i;
|
||||||
|
@ -13,21 +11,21 @@ export class LZString extends StaticClass {
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
static compressToBase64(input: string): string {
|
compressToBase64(input: string): string {
|
||||||
const result = LZString.#compress(input, 6, (a) => LZString.#KeyStrBase64.charAt(a));
|
const result = this.#compress(input, 6, (a) => this.#KeyStrBase64.charAt(a));
|
||||||
return result + "=".repeat((4 - (result.length % 4)) % 4);
|
return result + "=".repeat((4 - (result.length % 4)) % 4);
|
||||||
}
|
}
|
||||||
static decompressFromBase64(input: string | null): string | null {
|
decompressFromBase64(input: string | null): string | null {
|
||||||
if (input === null || input === "") return null;
|
if (input === null || input === "") return null;
|
||||||
return LZString.#decompress(input.length, 32, (index) => LZString.#KeyStrBase64Dict[input.charAt(index)]);
|
return this.#decompress(input.length, 32, (index) => this.#KeyStrBase64Dict[input.charAt(index)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static compress(uncompressed: string): string {
|
compress(uncompressed: string): string {
|
||||||
return LZString.#compress(uncompressed);
|
return this.#compress(uncompressed);
|
||||||
}
|
}
|
||||||
static #compress(uncompressed: string): string;
|
#compress(uncompressed: string): string;
|
||||||
static #compress(uncompressed: string, bitsPerChar: number, getCharFromInt: (index: number) => string): string;
|
#compress(uncompressed: string, bitsPerChar: number, getCharFromInt: (index: number) => string): string;
|
||||||
static #compress(
|
#compress(
|
||||||
uncompressed: string,
|
uncompressed: string,
|
||||||
bitsPerChar = 16,
|
bitsPerChar = 16,
|
||||||
getCharFromNumber = (code: number) => String.fromCharCode(code)
|
getCharFromNumber = (code: number) => String.fromCharCode(code)
|
||||||
|
@ -240,11 +238,11 @@ export class LZString extends StaticClass {
|
||||||
return contextData;
|
return contextData;
|
||||||
}
|
}
|
||||||
|
|
||||||
static decompress(compressed: string | null): string | null {
|
decompress(compressed: string | null): string | null {
|
||||||
if (compressed === null || compressed === "") return null;
|
if (compressed === null || compressed === "") return null;
|
||||||
return LZString.#decompress(compressed.length, 32768, (index) => compressed.charCodeAt(index));
|
return this.#decompress(compressed.length, 32768, (index) => compressed.charCodeAt(index));
|
||||||
}
|
}
|
||||||
static #decompress(length: number, resetValue: number, getNextValue: (index: number) => number): string | null {
|
#decompress(length: number, resetValue: number, getNextValue: (index: number) => number): string | null {
|
||||||
const dictionary: (number | string)[] = [];
|
const dictionary: (number | string)[] = [];
|
||||||
|
|
||||||
let enlargeIn = 4;
|
let enlargeIn = 4;
|
||||||
|
@ -411,6 +409,6 @@ export class LZString extends StaticClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})();
|
||||||
|
|
||||||
window.LZString = LZString;
|
window.LZString = LZString;
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
// 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 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,
|
|
||||||
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"] as const,
|
|
||||||
},
|
|
||||||
showIcons: {
|
|
||||||
current: true,
|
|
||||||
default: true as const,
|
|
||||||
name: "Show icons" as const,
|
|
||||||
description: "Whether to show decorative icons in various places" as const,
|
|
||||||
options: [true, false] as const,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
behavior: {
|
|
||||||
autoSave: {
|
|
||||||
current: 1 as "Off" | number,
|
|
||||||
default: 1 as const,
|
|
||||||
name: "Autosave" as const,
|
|
||||||
description: "How many seconds to wait between each autosave" as const,
|
|
||||||
options: ["Off", 1, 5, 10, 15, 60] as const,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
static readonly settings = writable(Settings.#settings);
|
|
||||||
|
|
||||||
static getSettings(): SettingsRecord {
|
|
||||||
return Object.seal(Object.assign({}, Settings.#settings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { Resources } from "./data/Resources";
|
import { ResourceHandler } from "./handlers/ResourceHandler";
|
||||||
import { MessageHandler } from "./handlers/MessageHandler";
|
import { MessageHandler } from "./handlers/MessageHandler";
|
||||||
import { SaveHandler } from "./handlers/SaveHandler";
|
import { SaveHandler } from "./handlers/SaveHandler";
|
||||||
import { Settings } from "./Settings";
|
|
||||||
import { StaticClass } from "./StaticClass";
|
|
||||||
import { TabHandler } from "./handlers/TabHandler";
|
import { TabHandler } from "./handlers/TabHandler";
|
||||||
|
import { SettingsHandler } from "./handlers/SettingsHandler";
|
||||||
|
|
||||||
import { HomeActions } from "./data/HomeActions";
|
import { HomeActions } from "./data/HomeActions";
|
||||||
|
import { ResourceData } from "./data/Resources";
|
||||||
|
import { SettingsData } from "./data/Settings";
|
||||||
|
|
||||||
export class SharkGame extends StaticClass {
|
export const SharkGame = new (class SharkGame {
|
||||||
static readonly #GAME_NAMES = [
|
readonly #GAME_NAMES = [
|
||||||
"Five Seconds A Shark",
|
"Five Seconds A Shark",
|
||||||
"Next Shark Game",
|
"Next Shark Game",
|
||||||
"Next Shark Game: Barkfest",
|
"Next Shark Game: Barkfest",
|
||||||
|
@ -57,28 +58,35 @@ export class SharkGame extends StaticClass {
|
||||||
"what the crab doin",
|
"what the crab doin",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
static title: string;
|
title: string = this.#GAME_NAMES[Math.floor(Math.random() * this.#GAME_NAMES.length)];
|
||||||
static readonly Settings = Settings;
|
readonly SettingsHandler = SettingsHandler;
|
||||||
static readonly MessageHandler = MessageHandler;
|
readonly MessageHandler = MessageHandler;
|
||||||
static readonly Resources = Resources;
|
readonly ResourceHandler = ResourceHandler;
|
||||||
static readonly TabHandler = TabHandler;
|
readonly TabHandler = TabHandler;
|
||||||
static readonly SaveHandler = SaveHandler;
|
readonly SaveHandler = SaveHandler;
|
||||||
static readonly Data = Object.seal({
|
readonly Data = Object.seal({
|
||||||
HomeActions,
|
HomeActions,
|
||||||
|
ResourceData,
|
||||||
|
SettingsData,
|
||||||
});
|
});
|
||||||
|
|
||||||
static async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
|
MessageHandler.initialize(this);
|
||||||
|
|
||||||
|
SaveHandler.reset();
|
||||||
|
TabHandler.reset();
|
||||||
|
ResourceHandler.reset();
|
||||||
|
SettingsHandler.reset();
|
||||||
|
MessageHandler.reset();
|
||||||
|
|
||||||
await SaveHandler.load(this);
|
await SaveHandler.load(this);
|
||||||
|
|
||||||
SharkGame.title = SharkGame.#GAME_NAMES[Math.floor(Math.random() * SharkGame.#GAME_NAMES.length)];
|
SaveHandler.initialize(this);
|
||||||
|
|
||||||
MessageHandler.init();
|
|
||||||
SaveHandler.init(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static save(): Promise<void> {
|
save(): Promise<void> {
|
||||||
return SaveHandler.save(this);
|
return SaveHandler.save(this);
|
||||||
}
|
}
|
||||||
}
|
})();
|
||||||
|
|
||||||
window.SharkGame = SharkGame;
|
window.SharkGame = SharkGame;
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
export class StaticClass {
|
|
||||||
constructor() {
|
|
||||||
if (this instanceof StaticClass) {
|
|
||||||
throw new Error("A static class cannot be instantiated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { StaticClass } from "../StaticClass";
|
|
||||||
|
|
||||||
export type HomeAction = {
|
export type HomeAction = {
|
||||||
name: string;
|
name: string;
|
||||||
effect: {
|
effect: Partial<{
|
||||||
resource: Record<string, number>;
|
addResources: Record<string, number>;
|
||||||
};
|
}>;
|
||||||
outcomes: string[];
|
outcomes: string[];
|
||||||
multiOutcomes?: string[];
|
multiOutcomes?: string[];
|
||||||
image?: string;
|
image?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class HomeActions extends StaticClass {
|
export class HomeActions {
|
||||||
static getActionTable(): Record<string, HomeAction> {
|
static getActionTable(): Record<string, HomeAction> {
|
||||||
return HomeActions.actionTable;
|
return HomeActions.actionTable;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +18,7 @@ export class HomeActions extends StaticClass {
|
||||||
image: "catchFish.webp",
|
image: "catchFish.webp",
|
||||||
name: "Steal fish",
|
name: "Steal fish",
|
||||||
effect: {
|
effect: {
|
||||||
resource: {
|
addResources: {
|
||||||
fish: 1,
|
fish: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -71,7 +69,7 @@ export class HomeActions extends StaticClass {
|
||||||
image: "getShark.webp",
|
image: "getShark.webp",
|
||||||
name: "Kidnap shark",
|
name: "Kidnap shark",
|
||||||
effect: {
|
effect: {
|
||||||
resource: {
|
addResources: {
|
||||||
shark: 1,
|
shark: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -81,7 +79,7 @@ export class HomeActions extends StaticClass {
|
||||||
image: "getManta.webp",
|
image: "getManta.webp",
|
||||||
name: "Summon ray",
|
name: "Summon ray",
|
||||||
effect: {
|
effect: {
|
||||||
resource: {
|
addResources: {
|
||||||
get ray() {
|
get ray() {
|
||||||
return Math.random() < 0.01 ? 1e9 : 1;
|
return Math.random() < 0.01 ? 1e9 : 1;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,68 +1,23 @@
|
||||||
import { writable } from "svelte/store";
|
export type ResourceRaw = {
|
||||||
|
color: string;
|
||||||
import { StaticClass } from "../StaticClass";
|
humanName: string;
|
||||||
|
|
||||||
export class Resource {
|
|
||||||
category: string;
|
category: string;
|
||||||
amount: number;
|
};
|
||||||
change: number;
|
|
||||||
|
|
||||||
constructor(category: string) {
|
export const ResourceData = {
|
||||||
this.category = category;
|
fish: {
|
||||||
this.amount = 0;
|
color: "yellow",
|
||||||
this.change = 0;
|
humanName: "fish",
|
||||||
}
|
category: "animals",
|
||||||
}
|
},
|
||||||
|
shark: {
|
||||||
export class Resources extends StaticClass {
|
color: "#92C1E0",
|
||||||
static readonly Resources = writable<Record<string, Resource>>({
|
humanName: "shark",
|
||||||
shark: new Resource("Frenzy"),
|
category: "frenzy",
|
||||||
ray: new Resource("Frenzy"),
|
},
|
||||||
fish: new Resource("Animals"),
|
ray: {
|
||||||
});
|
color: "#8080FF",
|
||||||
|
humanName: "ray",
|
||||||
static createResource(name: string, category: string): void {
|
category: "frenzy",
|
||||||
Resources.Resources.update((res) => {
|
},
|
||||||
res[name] = new Resource(category);
|
} as const; // as Record<string, ResourceRaw>
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static setResource(resourceName: string, amount: number): void {
|
|
||||||
Resources.Resources.update((res) => {
|
|
||||||
res[resourceName].amount = amount;
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static getResource(resourceName: string): Resource | null {
|
|
||||||
let result: null | Resource = null;
|
|
||||||
Resources.Resources.update((res) => {
|
|
||||||
if (Object.hasOwnProperty.call(res, resourceName)) result = res[resourceName];
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getResources(resourceNames: string[]): Record<string, Resource> | null;
|
|
||||||
static getResources(...resourceNames: string[]): Record<string, Resource> | null;
|
|
||||||
static getResources(...resourceNames: string[] | string[][]): Record<string, Resource> | null {
|
|
||||||
if (Array.isArray(resourceNames[0])) {
|
|
||||||
resourceNames = resourceNames[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let result: null | [string, Resource][] = null;
|
|
||||||
Resources.Resources.update((res) => {
|
|
||||||
for (const resourceName of resourceNames as string[]) {
|
|
||||||
if (Object.hasOwnProperty.call(res, resourceName)) {
|
|
||||||
if (result === null) {
|
|
||||||
result = [];
|
|
||||||
}
|
|
||||||
result.push([resourceName, res[resourceName]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
return result === null ? null : Object.fromEntries(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
53
src/shark/data/Settings.ts
Normal file
53
src/shark/data/Settings.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// How to make current just be undefined here, but keep the type of default?
|
||||||
|
|
||||||
|
export type Setting = {
|
||||||
|
current: unknown;
|
||||||
|
readonly default: unknown;
|
||||||
|
readonly name: unknown;
|
||||||
|
readonly description: unknown;
|
||||||
|
readonly options: readonly unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsData = {
|
||||||
|
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,
|
||||||
|
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"] as const,
|
||||||
|
},
|
||||||
|
showIcons: {
|
||||||
|
current: true,
|
||||||
|
default: true as const,
|
||||||
|
name: "Show icons" as const,
|
||||||
|
description: "Whether to show decorative icons in various places" as const,
|
||||||
|
options: [true, false] as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
behavior: {
|
||||||
|
autoSave: {
|
||||||
|
current: 1 as "Off" | 1 | 5 | 10 | 15 | 60,
|
||||||
|
default: 1 as const,
|
||||||
|
name: "Autosave" as const,
|
||||||
|
description: "How many seconds to wait between each autosave" as const,
|
||||||
|
options: ["Off", 1, 5, 10, 15, 60] as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
3
src/shark/handlers/BaseHandler.ts
Normal file
3
src/shark/handlers/BaseHandler.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export interface BaseHandler {
|
||||||
|
reset(): void;
|
||||||
|
}
|
|
@ -1,27 +1,25 @@
|
||||||
import { writable } from "svelte/store";
|
import { get, writable } from "svelte/store";
|
||||||
|
|
||||||
import { Message, MessageType } from "../Message";
|
import { Message, MessageType } from "../helperTypes/Message";
|
||||||
import { Settings } from "../Settings";
|
import type { SharkGame } from "../SharkGame";
|
||||||
import { StaticClass } from "../StaticClass";
|
import type { BaseHandler } from "./BaseHandler";
|
||||||
|
|
||||||
export class MessageHandler extends StaticClass {
|
export const MessageHandler = new (class MessageHandler implements BaseHandler {
|
||||||
static messages = writable<Message[]>([]);
|
readonly messages = writable<Message[]>([]);
|
||||||
static #maxLogLength: number;
|
#maxLogLength = Infinity;
|
||||||
|
|
||||||
static init(): void {
|
initialize(game: typeof SharkGame): void {
|
||||||
Settings.settings.subscribe((settings) => {
|
this.#maxLogLength = Math.max(...get(game.SettingsHandler.settings).layout.logLength.options);
|
||||||
MessageHandler.#maxLogLength = Math.max(...settings.layout.logLength.options);
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static addMessage(message: string, messageType: MessageType = MessageType.message): void {
|
addMessage(message: string, messageType: MessageType = MessageType.message): void {
|
||||||
this.messages.update((oldMessages) =>
|
this.messages.update((oldMessages) =>
|
||||||
[...oldMessages, new Message(message, messageType)].slice(-MessageHandler.#maxLogLength)
|
[...oldMessages, new Message(message, messageType)].slice(-this.#maxLogLength)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static reset(): void {
|
reset(): void {
|
||||||
Message.even = true;
|
Message.even = true;
|
||||||
this.messages.set([]);
|
this.messages.set([]);
|
||||||
}
|
}
|
||||||
}
|
})();
|
||||||
|
|
32
src/shark/handlers/ResourceHandler.ts
Normal file
32
src/shark/handlers/ResourceHandler.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
import { ResourceData } from "../data/Resources";
|
||||||
|
|
||||||
|
import { Resource } from "../helperTypes/Resource";
|
||||||
|
|
||||||
|
import type { BaseHandler } from "./BaseHandler";
|
||||||
|
|
||||||
|
type ResourceMap = Record<string, Resource>;
|
||||||
|
|
||||||
|
export const ResourceHandler = new (class ResourceHandler implements BaseHandler {
|
||||||
|
allResources = writable<ResourceMap>({});
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.allResources.update((allResources) => {
|
||||||
|
for (const [resourceId, resourceRaw] of Object.entries(ResourceData)) {
|
||||||
|
allResources[resourceId] = new Resource(resourceRaw);
|
||||||
|
}
|
||||||
|
return allResources;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
increaseResource(resourceId: string, amount: number) {
|
||||||
|
this.allResources.update((resources) => {
|
||||||
|
if (!(resourceId in resources)) {
|
||||||
|
throw new Error(`Unknown resourceId '${resourceId}'`);
|
||||||
|
}
|
||||||
|
resources[resourceId].amount += amount;
|
||||||
|
return resources;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
|
@ -1,9 +1,18 @@
|
||||||
import type { Message, MessageType } from "../Message";
|
|
||||||
import type { SharkGame } from "../SharkGame";
|
import type { SharkGame } from "../SharkGame";
|
||||||
import { StaticClass } from "../StaticClass";
|
|
||||||
import type { TabHandler } from "./TabHandler";
|
import type { TabHandler } from "./TabHandler";
|
||||||
|
import type { BaseHandler } from "./BaseHandler";
|
||||||
|
|
||||||
|
import { SettingsHandler } from "./SettingsHandler";
|
||||||
|
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
|
import { Resource } from "../helperTypes/Resource";
|
||||||
|
import { Message, MessageType } from "../helperTypes/Message";
|
||||||
|
|
||||||
|
import { ResourceData } from "../data/Resources";
|
||||||
|
|
||||||
import { LZString } from "../LZString";
|
import { LZString } from "../LZString";
|
||||||
import { Settings } from "../Settings";
|
import type { SettingsData } from "../data/Settings";
|
||||||
|
|
||||||
const __EMPTY_OBJECT = {};
|
const __EMPTY_OBJECT = {};
|
||||||
type Version0Save = typeof __EMPTY_OBJECT;
|
type Version0Save = typeof __EMPTY_OBJECT;
|
||||||
|
@ -15,46 +24,50 @@ type Version1Save = Version0Save & {
|
||||||
}[];
|
}[];
|
||||||
selectedTab: keyof typeof TabHandler["AllTabs"];
|
selectedTab: keyof typeof TabHandler["AllTabs"];
|
||||||
settings: Record<string, Record<string, unknown>>;
|
settings: Record<string, Record<string, unknown>>;
|
||||||
|
resources: Record<string, { amount: number; total: number }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CurrentVersionSave = Version1Save;
|
type CurrentVersionSave = Version1Save;
|
||||||
type Save = Version0Save | Version1Save;
|
type Save = Version0Save | Version1Save;
|
||||||
|
|
||||||
export class SaveHandler extends StaticClass {
|
export const SaveHandler = new (class SaveHandler implements BaseHandler {
|
||||||
static readonly saveName = "sharg-save";
|
readonly saveName = "sharg-save";
|
||||||
static #lastSaved = Date.now();
|
#lastSaved = Date.now();
|
||||||
static get lastSaved(): number {
|
get lastSaved(): number {
|
||||||
return SaveHandler.#lastSaved;
|
return this.#lastSaved;
|
||||||
}
|
}
|
||||||
private static set lastSaved(val) {
|
private set lastSaved(val) {
|
||||||
SaveHandler.#lastSaved = val;
|
this.#lastSaved = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #saveInterval: ReturnType<typeof setInterval> | undefined = undefined;
|
#saveInterval: ReturnType<typeof setInterval> | undefined = undefined;
|
||||||
|
|
||||||
static init(game: typeof SharkGame): void {
|
initialize(game: typeof SharkGame) {
|
||||||
Settings.settings.subscribe((settings) => {
|
let previousSetting: typeof SettingsData["behavior"]["autoSave"]["options"][number];
|
||||||
if (SaveHandler.#saveInterval !== undefined) {
|
SettingsHandler.settings.subscribe((settings) => {
|
||||||
clearInterval(SaveHandler.#saveInterval);
|
const currentSetting = settings.behavior.autoSave.current;
|
||||||
}
|
if (previousSetting !== currentSetting) {
|
||||||
if (settings.behavior.autoSave.current !== "Off") {
|
this.reset();
|
||||||
SaveHandler.#saveInterval = setInterval(
|
if (settings.behavior.autoSave.current !== "Off") {
|
||||||
() => SaveHandler.save(game),
|
this.#saveInterval = setInterval(() => this.save(game), settings.behavior.autoSave.current * 1000);
|
||||||
settings.behavior.autoSave.current * 1000
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
previousSetting = currentSetting;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async save(game: typeof SharkGame): Promise<void> {
|
reset(): void {
|
||||||
|
if (this.#saveInterval !== undefined) {
|
||||||
|
clearInterval(this.#saveInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(game: typeof SharkGame): Promise<void> {
|
||||||
console.debug("Saving");
|
console.debug("Saving");
|
||||||
console.time("Done saving");
|
console.time("Done saving");
|
||||||
const messages = await new Promise<Message[]>((resolve) => {
|
const messages = get(game.MessageHandler.messages);
|
||||||
game.MessageHandler.messages.subscribe((messages) => {
|
const resources = get(game.ResourceHandler.allResources);
|
||||||
resolve(messages);
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Save tab
|
||||||
const allTabsEntries = Object.entries(game.TabHandler.AllTabs) as unknown as [
|
const allTabsEntries = Object.entries(game.TabHandler.AllTabs) as unknown as [
|
||||||
keyof typeof TabHandler["AllTabs"],
|
keyof typeof TabHandler["AllTabs"],
|
||||||
typeof TabHandler["AllTabs"][keyof typeof TabHandler["AllTabs"]]
|
typeof TabHandler["AllTabs"][keyof typeof TabHandler["AllTabs"]]
|
||||||
|
@ -62,7 +75,8 @@ export class SaveHandler extends StaticClass {
|
||||||
const selectedTabIndex = allTabsEntries.findIndex(([, tab]) => tab === game.TabHandler.currentTab);
|
const selectedTabIndex = allTabsEntries.findIndex(([, tab]) => tab === game.TabHandler.currentTab);
|
||||||
const selectedTabName = allTabsEntries[selectedTabIndex][0];
|
const selectedTabName = allTabsEntries[selectedTabIndex][0];
|
||||||
|
|
||||||
const currentSettings = game.Settings.getSettings();
|
// Save settings
|
||||||
|
const currentSettings = get(game.SettingsHandler.settings);
|
||||||
const saveSettings: CurrentVersionSave["settings"] = {};
|
const saveSettings: CurrentVersionSave["settings"] = {};
|
||||||
for (const [categoryId, categorySettings] of Object.entries(currentSettings)) {
|
for (const [categoryId, categorySettings] of Object.entries(currentSettings)) {
|
||||||
const categorySave: CurrentVersionSave["settings"][string] = {};
|
const categorySave: CurrentVersionSave["settings"][string] = {};
|
||||||
|
@ -76,16 +90,25 @@ export class SaveHandler extends StaticClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save resources
|
||||||
|
const saveResources: CurrentVersionSave["resources"] = {};
|
||||||
|
for (const [resourceId, resource] of Object.entries(resources)) {
|
||||||
|
if (resource.amount > 0 || resource.total > 0)
|
||||||
|
saveResources[resourceId] = { amount: resource.amount, total: resource.total };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save messages
|
||||||
|
const saveMessages: CurrentVersionSave["messages"] = messages.map((message) => ({
|
||||||
|
content: message.content,
|
||||||
|
type: message.type,
|
||||||
|
}));
|
||||||
|
|
||||||
const save: CurrentVersionSave = {
|
const save: CurrentVersionSave = {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
messages: saveMessages,
|
||||||
selectedTab: selectedTabName,
|
selectedTab: selectedTabName,
|
||||||
settings: saveSettings,
|
settings: saveSettings,
|
||||||
messages: messages.map((message) => {
|
resources: saveResources,
|
||||||
return {
|
|
||||||
content: message.content,
|
|
||||||
type: message.type,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
const stringifiedSave = JSON.stringify(save);
|
const stringifiedSave = JSON.stringify(save);
|
||||||
const encodedSave = LZString.compressToBase64(stringifiedSave);
|
const encodedSave = LZString.compressToBase64(stringifiedSave);
|
||||||
|
@ -95,22 +118,22 @@ export class SaveHandler extends StaticClass {
|
||||||
}% size`
|
}% size`
|
||||||
);
|
);
|
||||||
|
|
||||||
localStorage.setItem(SaveHandler.saveName, encodedSave);
|
localStorage.setItem(this.saveName, encodedSave);
|
||||||
SaveHandler.#lastSaved = Date.now();
|
this.#lastSaved = Date.now();
|
||||||
console.timeEnd("Done saving");
|
console.timeEnd("Done saving");
|
||||||
}
|
}
|
||||||
|
|
||||||
static async load(game: typeof SharkGame): Promise<void> {
|
async load(game: typeof SharkGame): Promise<void> {
|
||||||
console.debug("Loading");
|
console.debug("Loading");
|
||||||
console.time("Done loading");
|
console.time("Done loading");
|
||||||
const localSave = LZString.decompressFromBase64(localStorage.getItem(SaveHandler.saveName));
|
const localSave = LZString.decompressFromBase64(localStorage.getItem(this.saveName));
|
||||||
const loadedSave = JSON.parse(localSave ?? "{}");
|
const loadedSave = JSON.parse(localSave ?? "{}");
|
||||||
const saveVersion = loadedSave.version ?? 0;
|
const saveVersion = loadedSave.version ?? 0;
|
||||||
|
|
||||||
const migrators = SaveHandler.migrators.slice(saveVersion);
|
const migrators = this.migrators.slice(saveVersion);
|
||||||
|
|
||||||
if (migrators.length > 0 && localSave !== null) {
|
if (migrators.length > 0 && localSave !== null) {
|
||||||
localStorage.setItem(SaveHandler.saveName + "-backup", localSave);
|
localStorage.setItem(this.saveName + "-backup", localSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
let save = loadedSave;
|
let save = loadedSave;
|
||||||
|
@ -121,44 +144,64 @@ export class SaveHandler extends StaticClass {
|
||||||
|
|
||||||
const fullSave = save as CurrentVersionSave;
|
const fullSave = save as CurrentVersionSave;
|
||||||
|
|
||||||
game.Settings.settings.update((settings) => {
|
// Load settings
|
||||||
Object.entries(settings).forEach(([categoryName, categorySettings]) => {
|
game.SettingsHandler.reset();
|
||||||
Object.entries(categorySettings).forEach(([settingId, setting]) => {
|
game.SettingsHandler.settings.update((settings) => {
|
||||||
|
for (const [categoryName, categorySettings] of Object.entries(settings)) {
|
||||||
|
for (const [settingId, setting] of Object.entries(categorySettings)) {
|
||||||
setting.current = fullSave.settings[categoryName]?.[settingId] ?? setting.default;
|
setting.current = fullSave.settings[categoryName]?.[settingId] ?? setting.default;
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
return settings;
|
return settings;
|
||||||
});
|
});
|
||||||
|
|
||||||
game.MessageHandler.reset();
|
// Load resources
|
||||||
|
game.ResourceHandler.reset();
|
||||||
|
game.ResourceHandler.allResources.update((allResources) => {
|
||||||
|
for (const [resourceId, resourceInfo] of Object.entries(fullSave.resources)) {
|
||||||
|
if (resourceId in ResourceData) {
|
||||||
|
const resource = new Resource(ResourceData[resourceId as keyof typeof ResourceData]);
|
||||||
|
|
||||||
fullSave.messages.forEach((message) => {
|
resource.amount = resourceInfo.amount;
|
||||||
game.MessageHandler.addMessage(message.content, message.type);
|
resource.total = resourceInfo.total;
|
||||||
|
|
||||||
|
allResources[resourceId] = resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allResources;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Load messages
|
||||||
|
game.MessageHandler.reset();
|
||||||
|
game.MessageHandler.messages.set(
|
||||||
|
fullSave.messages.map((saveMessage) => new Message(saveMessage.content, saveMessage.type))
|
||||||
|
);
|
||||||
|
|
||||||
game.TabHandler.currentTab = game.TabHandler.AllTabs[fullSave.selectedTab];
|
game.TabHandler.currentTab = game.TabHandler.AllTabs[fullSave.selectedTab];
|
||||||
console.timeEnd("Done loading");
|
console.timeEnd("Done loading");
|
||||||
}
|
}
|
||||||
|
|
||||||
static reset(): void {
|
resetSave(): void {
|
||||||
const localSave = localStorage.getItem(SaveHandler.saveName);
|
const localSave = localStorage.getItem(this.saveName);
|
||||||
if (localSave !== null) {
|
if (localSave !== null) {
|
||||||
localStorage.setItem(SaveHandler.saveName + "-backup", localSave);
|
localStorage.setItem(this.saveName + "-backup", localSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.removeItem(SaveHandler.saveName);
|
localStorage.removeItem(this.saveName);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
static migrators: ((save: Save) => Save)[] = [
|
migrators: ((save: Save) => Save)[] = [
|
||||||
(): Version1Save => {
|
(): Version1Save => {
|
||||||
const newSave = {
|
const newSave = {
|
||||||
version: 1 as const,
|
version: 1 as const,
|
||||||
messages: [],
|
messages: [],
|
||||||
selectedTab: "Home" as const,
|
selectedTab: "Home" as const,
|
||||||
settings: {},
|
settings: {},
|
||||||
|
resources: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
return newSave;
|
return newSave;
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
})();
|
||||||
|
|
19
src/shark/handlers/SettingsHandler.ts
Normal file
19
src/shark/handlers/SettingsHandler.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import type { BaseHandler } from "./BaseHandler";
|
||||||
|
|
||||||
|
import { SettingsData } from "../data/Settings";
|
||||||
|
|
||||||
|
export const SettingsHandler = new (class Settings implements BaseHandler {
|
||||||
|
readonly settings = writable(SettingsData);
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
for (const category of Object.values(SettingsData)) {
|
||||||
|
for (const setting of Object.values(category)) {
|
||||||
|
setting.current = setting.default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.settings.set(SettingsData);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
export type SettingsRecord = typeof SettingsHandler["settings"] extends Writable<infer X> ? X : never;
|
|
@ -1,11 +1,15 @@
|
||||||
import { StaticClass } from "../StaticClass";
|
|
||||||
import Home from "../../components/Tabs/Home.svelte";
|
import Home from "../../components/Tabs/Home.svelte";
|
||||||
import Lab from "../../components/Tabs/Lab.svelte";
|
import Lab from "../../components/Tabs/Lab.svelte";
|
||||||
|
import type { BaseHandler } from "./BaseHandler";
|
||||||
|
|
||||||
export class TabHandler extends StaticClass {
|
export const TabHandler = new (class TabHandler implements BaseHandler {
|
||||||
static readonly AllTabs = {
|
reset() {
|
||||||
|
this.currentTab = this.AllTabs.Home;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly AllTabs = {
|
||||||
Home,
|
Home,
|
||||||
Lab,
|
Lab,
|
||||||
} as const;
|
} as const;
|
||||||
static currentTab: typeof TabHandler.AllTabs[keyof typeof TabHandler.AllTabs] = TabHandler.AllTabs.Home;
|
currentTab: typeof this.AllTabs[keyof typeof this.AllTabs] = this.AllTabs.Home;
|
||||||
}
|
})();
|
||||||
|
|
17
src/shark/helperTypes/Resource.ts
Normal file
17
src/shark/helperTypes/Resource.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import type { ResourceRaw } from "../data/Resources";
|
||||||
|
|
||||||
|
export class Resource implements ResourceRaw {
|
||||||
|
humanName: string;
|
||||||
|
category: string;
|
||||||
|
color: string;
|
||||||
|
|
||||||
|
amount = 0;
|
||||||
|
total = 0;
|
||||||
|
change = 0;
|
||||||
|
|
||||||
|
constructor(raw: ResourceRaw) {
|
||||||
|
this.humanName = raw.humanName;
|
||||||
|
this.category = raw.category;
|
||||||
|
this.color = raw.color;
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue