Moved properties from player data to new settings and state objects
This commit is contained in:
parent
bc808098ff
commit
eaf47bb946
25 changed files with 367 additions and 285 deletions
10
src/App.vue
10
src/App.vue
|
@ -11,11 +11,11 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import themes from "./data/themes";
|
|
||||||
import player from "./game/player";
|
|
||||||
import modInfo from "./data/modInfo.json";
|
import modInfo from "./data/modInfo.json";
|
||||||
import { mapState } from "./util/vue";
|
import themes from "./data/themes";
|
||||||
|
import settings from "./game/settings";
|
||||||
import "./main.css";
|
import "./main.css";
|
||||||
|
import { mapSettings } from "./util/vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "App",
|
name: "App",
|
||||||
|
@ -23,9 +23,9 @@ export default defineComponent({
|
||||||
return { useHeader: modInfo.useHeader };
|
return { useHeader: modInfo.useHeader };
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["showTPS"]),
|
...mapSettings(["showTPS"]),
|
||||||
theme() {
|
theme() {
|
||||||
return themes[player.theme].variables;
|
return themes[settings.theme].variables;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -178,6 +178,7 @@ import themes from "@/data/themes";
|
||||||
import { ProgressDisplay, Shape } from "@/game/enums";
|
import { ProgressDisplay, Shape } from "@/game/enums";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
import { BoardNode, BoardNodeAction, NodeLabel, NodeType } from "@/typings/features/board";
|
import { BoardNode, BoardNodeAction, NodeLabel, NodeType } from "@/typings/features/board";
|
||||||
import { getNodeTypeProperty } from "@/util/features";
|
import { getNodeTypeProperty } from "@/util/features";
|
||||||
import { defineComponent, PropType } from "vue";
|
import { defineComponent, PropType } from "vue";
|
||||||
|
@ -279,18 +280,18 @@ export default defineComponent({
|
||||||
return getNodeTypeProperty(this.nodeType, this.node, "progress") || 0;
|
return getNodeTypeProperty(this.nodeType, this.node, "progress") || 0;
|
||||||
},
|
},
|
||||||
backgroundColor(): string {
|
backgroundColor(): string {
|
||||||
return themes[player.theme].variables["--background"];
|
return themes[settings.theme].variables["--background"];
|
||||||
},
|
},
|
||||||
outlineColor(): string {
|
outlineColor(): string {
|
||||||
return (
|
return (
|
||||||
getNodeTypeProperty(this.nodeType, this.node, "outlineColor") ||
|
getNodeTypeProperty(this.nodeType, this.node, "outlineColor") ||
|
||||||
themes[player.theme].variables["--outline"]
|
themes[settings.theme].variables["--outline"]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
fillColor(): string {
|
fillColor(): string {
|
||||||
return (
|
return (
|
||||||
getNodeTypeProperty(this.nodeType, this.node, "fillColor") ||
|
getNodeTypeProperty(this.nodeType, this.node, "fillColor") ||
|
||||||
themes[player.theme].variables["--raised-background"]
|
themes[settings.theme].variables["--raised-background"]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
progressColor(): string {
|
progressColor(): string {
|
||||||
|
@ -299,7 +300,7 @@ export default defineComponent({
|
||||||
titleColor(): string {
|
titleColor(): string {
|
||||||
return (
|
return (
|
||||||
getNodeTypeProperty(this.nodeType, this.node, "titleColor") ||
|
getNodeTypeProperty(this.nodeType, this.node, "titleColor") ||
|
||||||
themes[player.theme].variables["--foreground"]
|
themes[settings.theme].variables["--foreground"]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
progressDisplay(): ProgressDisplay {
|
progressDisplay(): ProgressDisplay {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import themes from "@/data/themes";
|
import themes from "@/data/themes";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
import { Infobox } from "@/typings/features/infobox";
|
import { Infobox } from "@/typings/features/infobox";
|
||||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||||
import { Component, defineComponent } from "vue";
|
import { Component, defineComponent } from "vue";
|
||||||
|
@ -67,7 +68,7 @@ export default defineComponent({
|
||||||
return player.layers[this.layer].infoboxes[this.id];
|
return player.layers[this.layer].infoboxes[this.id];
|
||||||
},
|
},
|
||||||
stacked(): boolean {
|
stacked(): boolean {
|
||||||
return themes[player.theme].stackedInfoboxes;
|
return themes[settings.theme].stackedInfoboxes;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<input type="checkbox" class="toggle" :checked="value" @input="handleInput" />
|
<input type="checkbox" class="toggle" :checked="value" @input="handleInput" />
|
||||||
<span>{{ title }}</span>
|
<component :is="display" />
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { coerceComponent } from "@/util/vue";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
// Reference: https://codepen.io/finnhvman/pen/pOeyjE
|
// Reference: https://codepen.io/finnhvman/pen/pOeyjE
|
||||||
|
@ -16,6 +17,11 @@ export default defineComponent({
|
||||||
value: Boolean
|
value: Boolean
|
||||||
},
|
},
|
||||||
emits: ["change"],
|
emits: ["change"],
|
||||||
|
computed: {
|
||||||
|
display() {
|
||||||
|
return coerceComponent(this.title || "", "span");
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleInput(e: InputEvent) {
|
handleInput(e: InputEvent) {
|
||||||
this.$emit("change", (e.target as HTMLInputElement).checked);
|
this.$emit("change", (e.target as HTMLInputElement).checked);
|
||||||
|
|
|
@ -49,6 +49,7 @@ import modInfo from "@/data/modInfo.json";
|
||||||
import themes from "@/data/themes";
|
import themes from "@/data/themes";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
import { Subtab } from "@/typings/features/subtab";
|
import { Subtab } from "@/typings/features/subtab";
|
||||||
import { coerceComponent } from "@/util/vue";
|
import { coerceComponent } from "@/util/vue";
|
||||||
import { Component, defineComponent } from "vue";
|
import { Component, defineComponent } from "vue";
|
||||||
|
@ -76,7 +77,7 @@ export default defineComponent({
|
||||||
return layers[this.layer].name || this.layer;
|
return layers[this.layer].name || this.layer;
|
||||||
},
|
},
|
||||||
floating(): boolean {
|
floating(): boolean {
|
||||||
return themes[player.theme].floatingTabs;
|
return themes[settings.theme].floatingTabs;
|
||||||
},
|
},
|
||||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||||
const style = [];
|
const style = [];
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
import themes from "@/data/themes";
|
import themes from "@/data/themes";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
import { Microtab, MicrotabFamily } from "@/typings/features/subtab";
|
import settings from "@/game/settings";
|
||||||
|
import { Microtab } from "@/typings/features/subtab";
|
||||||
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
import { coerceComponent, InjectLayerMixin } from "@/util/vue";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
@ -40,9 +41,9 @@ export default defineComponent({
|
||||||
inject: ["tab"],
|
inject: ["tab"],
|
||||||
computed: {
|
computed: {
|
||||||
floating() {
|
floating() {
|
||||||
return themes[player.theme].floatingTabs;
|
return themes[settings.theme].floatingTabs;
|
||||||
},
|
},
|
||||||
tabFamily(): MicrotabFamily {
|
tabFamily() {
|
||||||
return layers[this.layer].microtabs![this.family];
|
return layers[this.layer].microtabs![this.family];
|
||||||
},
|
},
|
||||||
microtabs() {
|
microtabs() {
|
||||||
|
@ -61,13 +62,13 @@ export default defineComponent({
|
||||||
activeMicrotab() {
|
activeMicrotab() {
|
||||||
return this.id != undefined
|
return this.id != undefined
|
||||||
? this.tabFamily.data[this.id]
|
? this.tabFamily.data[this.id]
|
||||||
: this.tabFamily.activeMicrotab;
|
: this.tabFamily.activeMicrotab!;
|
||||||
},
|
},
|
||||||
embed() {
|
embed() {
|
||||||
return this.activeMicrotab!.embedLayer;
|
return this.activeMicrotab!.embedLayer;
|
||||||
},
|
},
|
||||||
display() {
|
display() {
|
||||||
return this.activeMicrotab!.display && coerceComponent(this.activeMicrotab!.display!);
|
return this.activeMicrotab!.display && coerceComponent(this.activeMicrotab!.display);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -46,8 +46,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import modInfo from "@/data/modInfo.json";
|
import modInfo from "@/data/modInfo.json";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import state from "@/game/state";
|
||||||
import Decimal, { format } from "@/util/bignum";
|
import Decimal, { format } from "@/util/bignum";
|
||||||
import { mapState } from "@/util/vue";
|
import { mapPlayer, mapState } from "@/util/vue";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -57,13 +58,14 @@ export default defineComponent({
|
||||||
return { discordName, discordLink, format, showSaves: false };
|
return { discordName, discordLink, format, showSaves: false };
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["hasNaN", "autosave"]),
|
...mapPlayer(["autosave"]),
|
||||||
|
...mapState(["hasNaN"]),
|
||||||
path(): string | undefined {
|
path(): string | undefined {
|
||||||
return player.NaNPath?.join(".");
|
return state.NaNPath?.join(".");
|
||||||
},
|
},
|
||||||
previous(): any {
|
previous(): unknown {
|
||||||
if (player.NaNReceiver && this.property) {
|
if (state.NaNReceiver && this.property) {
|
||||||
return player.NaNReceiver[this.property];
|
return state.NaNReceiver[this.property];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
@ -71,29 +73,29 @@ export default defineComponent({
|
||||||
return player.devSpeed === 0;
|
return player.devSpeed === 0;
|
||||||
},
|
},
|
||||||
property(): string | undefined {
|
property(): string | undefined {
|
||||||
return player.NaNPath?.slice(-1)[0];
|
return state.NaNPath?.slice(-1)[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setZero() {
|
setZero() {
|
||||||
if (player.NaNReceiver && this.property) {
|
if (state.NaNReceiver && this.property) {
|
||||||
player.NaNReceiver[this.property] = new Decimal(0);
|
state.NaNReceiver[this.property] = new Decimal(0);
|
||||||
player.hasNaN = false;
|
state.hasNaN = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setOne() {
|
setOne() {
|
||||||
if (player.NaNReceiver && this.property) {
|
if (state.NaNReceiver && this.property) {
|
||||||
player.NaNReceiver[this.property] = new Decimal(1);
|
state.NaNReceiver[this.property] = new Decimal(1);
|
||||||
player.hasNaN = false;
|
state.hasNaN = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setPrev() {
|
setPrev() {
|
||||||
player.hasNaN = false;
|
state.hasNaN = false;
|
||||||
},
|
},
|
||||||
ignore() {
|
ignore() {
|
||||||
if (player.NaNReceiver && this.property) {
|
if (state.NaNReceiver && this.property) {
|
||||||
player.NaNReceiver[this.property] = new Decimal(NaN);
|
state.NaNReceiver[this.property] = new Decimal(NaN);
|
||||||
player.hasNaN = false;
|
state.hasNaN = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setAutosave(autosave: boolean) {
|
setAutosave(autosave: boolean) {
|
||||||
|
|
|
@ -20,18 +20,26 @@
|
||||||
@change="setMSDisplay"
|
@change="setMSDisplay"
|
||||||
default="all"
|
default="all"
|
||||||
/>
|
/>
|
||||||
<Toggle
|
<Toggle title="Show TPS" :value="showTPS" @change="toggleSettingsOption('showTPS')" />
|
||||||
title="Offline Production"
|
|
||||||
:value="offlineProd"
|
|
||||||
@change="toggleOption('offlineProd')"
|
|
||||||
/>
|
|
||||||
<Toggle title="Autosave" :value="autosave" @change="toggleOption('autosave')" />
|
|
||||||
<Toggle title="Pause game" :value="paused" @change="togglePaused" />
|
|
||||||
<Toggle title="Show TPS" :value="showTPS" @change="toggleOption('showTPS')" />
|
|
||||||
<Toggle
|
<Toggle
|
||||||
title="Hide Maxed Challenges"
|
title="Hide Maxed Challenges"
|
||||||
:value="hideChallenges"
|
:value="hideChallenges"
|
||||||
@change="toggleOption('hideChallenges')"
|
@change="toggleSettingsOption('hideChallenges')"
|
||||||
|
/>
|
||||||
|
<Toggle
|
||||||
|
title="Offline Production<tooltip display='Save-specific'>*</tooltip>"
|
||||||
|
:value="offlineProd"
|
||||||
|
@change="togglePlayerOption('offlineProd')"
|
||||||
|
/>
|
||||||
|
<Toggle
|
||||||
|
title="Autosave<tooltip display='Save-specific'>*</tooltip>"
|
||||||
|
:value="autosave"
|
||||||
|
@change="togglePlayerOption('autosave')"
|
||||||
|
/>
|
||||||
|
<Toggle
|
||||||
|
title="Pause game<tooltip display='Save-specific'>*</tooltip>"
|
||||||
|
:value="paused"
|
||||||
|
@change="togglePaused"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -41,9 +49,12 @@
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import themes, { Themes } from "@/data/themes";
|
import themes, { Themes } from "@/data/themes";
|
||||||
import { camelToTitle } from "@/util/common";
|
import { camelToTitle } from "@/util/common";
|
||||||
import { mapState } from "@/util/vue";
|
import { mapPlayer, mapSettings } from "@/util/vue";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
import { MilestoneDisplay } from "@/game/enums";
|
import { MilestoneDisplay } from "@/game/enums";
|
||||||
|
import { PlayerData } from "@/typings/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
|
import { Settings } from "@/typings/settings";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "Options",
|
name: "Options",
|
||||||
|
@ -57,27 +68,31 @@ export default defineComponent({
|
||||||
label: camelToTitle(theme),
|
label: camelToTitle(theme),
|
||||||
value: theme
|
value: theme
|
||||||
})),
|
})),
|
||||||
msDisplayOptions: ["all", "last", "configurable", "incomplete", "none"].map(option => ({
|
msDisplayOptions: Object.values(MilestoneDisplay).map(option => ({
|
||||||
label: camelToTitle(option),
|
label: camelToTitle(option),
|
||||||
value: option
|
value: option
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["autosave", "offlineProd", "showTPS", "hideChallenges", "theme", "msDisplay"]),
|
...mapSettings(["showTPS", "hideChallenges", "theme", "msDisplay"]),
|
||||||
|
...mapPlayer(["autosave", "offlineProd"]),
|
||||||
paused() {
|
paused() {
|
||||||
return player.devSpeed === 0;
|
return player.devSpeed === 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleOption(option: string) {
|
togglePlayerOption(option: keyof PlayerData) {
|
||||||
player[option] = !player[option];
|
player[option] = !player[option];
|
||||||
},
|
},
|
||||||
|
toggleSettingsOption(option: keyof Settings) {
|
||||||
|
settings[option] = !settings[option];
|
||||||
|
},
|
||||||
setTheme(theme: Themes) {
|
setTheme(theme: Themes) {
|
||||||
player.theme = theme;
|
settings.theme = theme;
|
||||||
},
|
},
|
||||||
setMSDisplay(msDisplay: MilestoneDisplay) {
|
setMSDisplay(msDisplay: MilestoneDisplay) {
|
||||||
player.msDisplay = msDisplay;
|
settings.msDisplay = msDisplay;
|
||||||
},
|
},
|
||||||
togglePaused() {
|
togglePaused() {
|
||||||
player.devSpeed = this.paused ? 1 : 0;
|
player.devSpeed = this.paused ? 1 : 0;
|
||||||
|
@ -90,4 +105,9 @@ export default defineComponent({
|
||||||
.header {
|
.header {
|
||||||
margin-bottom: -10px;
|
margin-bottom: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* >>> .tooltip-container {
|
||||||
|
display: inline;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<h2>Saves Manager</h2>
|
<h2>Saves Manager</h2>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:body v-sortable="{ update, handle: '.handle' }">
|
<template v-slot:body>
|
||||||
|
<div v-sortable="{ update, handle: '.handle' }">
|
||||||
<save
|
<save
|
||||||
v-for="(save, index) in saves"
|
v-for="(save, index) in saves"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
@duplicate="duplicateSave(save.id)"
|
@duplicate="duplicateSave(save.id)"
|
||||||
@delete="deleteSave(save.id)"
|
@delete="deleteSave(save.id)"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:footer>
|
<template v-slot:footer>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -55,8 +57,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import modInfo from "@/data/modInfo.json";
|
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
|
import state from "@/game/state";
|
||||||
import { PlayerData } from "@/typings/player";
|
import { PlayerData } from "@/typings/player";
|
||||||
import { getUniqueID, loadSave, newSave, save } from "@/util/save";
|
import { getUniqueID, loadSave, newSave, save } from "@/util/save";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
@ -86,7 +89,10 @@ export default defineComponent({
|
||||||
bank
|
bank
|
||||||
} as {
|
} as {
|
||||||
importingFailed: boolean;
|
importingFailed: boolean;
|
||||||
saves: Record<string, Partial<PlayerData>>;
|
saves: Record<
|
||||||
|
string,
|
||||||
|
Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown }
|
||||||
|
>;
|
||||||
saveToImport: string;
|
saveToImport: string;
|
||||||
bank: Array<{ label: string; value: string }>;
|
bank: Array<{ label: string; value: string }>;
|
||||||
};
|
};
|
||||||
|
@ -100,17 +106,22 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadSaveData() {
|
loadSaveData() {
|
||||||
|
this.saves = settings.saves.reduce(
|
||||||
|
(
|
||||||
|
acc: Record<
|
||||||
|
string,
|
||||||
|
Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown }
|
||||||
|
>,
|
||||||
|
curr: string
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const { saves } = JSON.parse(
|
const save = localStorage.getItem(curr);
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
if (save == null) {
|
||||||
);
|
acc[curr] = { error: `Save with id "${curr}" doesn't exist`, id: curr };
|
||||||
this.saves = saves.reduce(
|
} else {
|
||||||
(acc: Record<string, Partial<PlayerData>>, curr: string) => {
|
acc[curr] = JSON.parse(decodeURIComponent(escape(atob(save))));
|
||||||
try {
|
|
||||||
acc[curr] = JSON.parse(
|
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(curr)!)))
|
|
||||||
);
|
|
||||||
acc[curr].id = curr;
|
acc[curr].id = curr;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Can't load save with id "${curr}"`, error);
|
console.warn(`Can't load save with id "${curr}"`, error);
|
||||||
acc[curr] = { error, id: curr };
|
acc[curr] = { error, id: curr };
|
||||||
|
@ -119,20 +130,12 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
} catch (e) {
|
|
||||||
this.saves = { [player.id]: player };
|
|
||||||
const modData = { active: player.id, saves: [player.id] };
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
exportSave(id: string) {
|
exportSave(id: string) {
|
||||||
let saveToExport;
|
let saveToExport;
|
||||||
if (player.id === id) {
|
if (player.id === id) {
|
||||||
save();
|
save();
|
||||||
saveToExport = player.saveToExport;
|
saveToExport = state.saveToExport;
|
||||||
} else {
|
} else {
|
||||||
saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(this.saves[id]))));
|
saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(this.saves[id]))));
|
||||||
}
|
}
|
||||||
|
@ -157,40 +160,18 @@ export default defineComponent({
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||||
);
|
);
|
||||||
|
|
||||||
const modData = JSON.parse(
|
settings.saves.push(playerData.id);
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
|
||||||
);
|
|
||||||
modData.saves.push(playerData.id);
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
this.saves[playerData.id] = playerData;
|
this.saves[playerData.id] = playerData;
|
||||||
},
|
},
|
||||||
deleteSave(id: string) {
|
deleteSave(id: string) {
|
||||||
const modData = JSON.parse(
|
settings.saves = settings.saves.filter((save: string) => save !== id);
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
|
||||||
);
|
|
||||||
modData.saves = modData.saves.filter((save: string) => save !== id);
|
|
||||||
localStorage.removeItem(id);
|
localStorage.removeItem(id);
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
delete this.saves[id];
|
delete this.saves[id];
|
||||||
},
|
},
|
||||||
openSave(id: string) {
|
openSave(id: string) {
|
||||||
this.saves[player.id].time = player.time;
|
this.saves[player.id].time = player.time;
|
||||||
save();
|
save();
|
||||||
loadSave(this.saves[id]);
|
loadSave(this.saves[id]);
|
||||||
const modData = JSON.parse(
|
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
|
||||||
);
|
|
||||||
modData.active = id;
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
newSave() {
|
newSave() {
|
||||||
const playerData = newSave();
|
const playerData = newSave();
|
||||||
|
@ -204,14 +185,7 @@ export default defineComponent({
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||||
);
|
);
|
||||||
|
|
||||||
const modData = JSON.parse(
|
settings.saves.push(playerData.id);
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
|
||||||
);
|
|
||||||
modData.saves.push(playerData.id);
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
this.saves[playerData.id] = playerData;
|
this.saves[playerData.id] = playerData;
|
||||||
},
|
},
|
||||||
editSave(id: string, newName: string) {
|
editSave(id: string, newName: string) {
|
||||||
|
@ -227,7 +201,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
importSave(text: string) {
|
importSave(text: string) {
|
||||||
console.log(text);
|
|
||||||
this.saveToImport = text;
|
this.saveToImport = text;
|
||||||
if (text) {
|
if (text) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -247,14 +220,7 @@ export default defineComponent({
|
||||||
this.saveToImport = "";
|
this.saveToImport = "";
|
||||||
this.importingFailed = false;
|
this.importingFailed = false;
|
||||||
|
|
||||||
const modData = JSON.parse(
|
settings.saves.push(id);
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
|
||||||
);
|
|
||||||
modData.saves.push(id);
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.importingFailed = true;
|
this.importingFailed = true;
|
||||||
}
|
}
|
||||||
|
@ -264,14 +230,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update(e: { newIndex: number; oldIndex: number }) {
|
update(e: { newIndex: number; oldIndex: number }) {
|
||||||
const modData = JSON.parse(
|
settings.saves.splice(e.newIndex, 0, settings.saves.splice(e.oldIndex, 1)[0]);
|
||||||
decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!)))
|
|
||||||
);
|
|
||||||
modData.saves.splice(e.newIndex, 0, modData.saves.splice(e.oldIndex, 1)[0]);
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import player from "@/game/player";
|
import state from "@/game/state";
|
||||||
import Decimal, { formatWhole } from "@/util/bignum";
|
import Decimal, { formatWhole } from "@/util/bignum";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ export default defineComponent({
|
||||||
tps() {
|
tps() {
|
||||||
return formatWhole(
|
return formatWhole(
|
||||||
Decimal.div(
|
Decimal.div(
|
||||||
player.lastTenTicks.length,
|
state.lastTenTicks.length,
|
||||||
player.lastTenTicks.reduce((acc, curr) => acc + curr, 0)
|
state.lastTenTicks.reduce((acc, curr) => acc + curr, 0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
import themes from "@/data/themes";
|
import themes from "@/data/themes";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
import { Subtab } from "@/typings/features/subtab";
|
import { Subtab } from "@/typings/features/subtab";
|
||||||
import { InjectLayerMixin } from "@/util/vue";
|
import { InjectLayerMixin } from "@/util/vue";
|
||||||
import { defineComponent, PropType } from "vue";
|
import { defineComponent, PropType } from "vue";
|
||||||
|
@ -36,7 +37,7 @@ export default defineComponent({
|
||||||
emits: ["selectTab"],
|
emits: ["selectTab"],
|
||||||
computed: {
|
computed: {
|
||||||
floating(): boolean {
|
floating(): boolean {
|
||||||
return themes[player.theme].floatingTabs;
|
return themes[settings.theme].floatingTabs;
|
||||||
},
|
},
|
||||||
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
style(): Array<Partial<CSSStyleDeclaration> | undefined> {
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import modInfo from "@/data/modInfo.json";
|
import modInfo from "@/data/modInfo.json";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import { coerceComponent, mapState } from "@/util/vue";
|
import { coerceComponent, mapPlayer } from "@/util/vue";
|
||||||
import { Component, defineComponent } from "vue";
|
import { Component, defineComponent } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -36,7 +36,7 @@ export default defineComponent({
|
||||||
return { useHeader: modInfo.useHeader };
|
return { useHeader: modInfo.useHeader };
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["tabs"]),
|
...mapPlayer(["tabs"]),
|
||||||
components() {
|
components() {
|
||||||
return Object.keys(layers).reduce(
|
return Object.keys(layers).reduce(
|
||||||
(acc: Record<string, Component | string | false>, curr) => {
|
(acc: Record<string, Component | string | false>, curr) => {
|
||||||
|
|
|
@ -1230,11 +1230,11 @@ export default {
|
||||||
},
|
},
|
||||||
effect() {
|
effect() {
|
||||||
if (!hasChallenge(this.layer, this.id)) return 0;
|
if (!hasChallenge(this.layer, this.id)) return 0;
|
||||||
if (player.layers[this.layer].challenges![this.id] == new Decimal(1))
|
if (Decimal.eq(player.layers[this.layer].challenges![this.id], 1))
|
||||||
return 50;
|
return 50;
|
||||||
if (player.layers[this.layer].challenges![this.id] == new Decimal(2))
|
if (Decimal.eq(player.layers[this.layer].challenges![this.id], 2))
|
||||||
return 60;
|
return 60;
|
||||||
if (player.layers[this.layer].challenges![this.id] == new Decimal(3))
|
if (Decimal.eq(player.layers[this.layer].challenges![this.id], 3))
|
||||||
return 70;
|
return 70;
|
||||||
},
|
},
|
||||||
completionLimit() {
|
completionLimit() {
|
||||||
|
|
|
@ -81,7 +81,7 @@ const main = {
|
||||||
<span v-if="player.points.lt('1e1e6')"> points</span>
|
<span v-if="player.points.lt('1e1e6')"> points</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="Decimal.gt(pointGain, 0)">
|
<div v-if="Decimal.gt(pointGain, 0)">
|
||||||
({{ player.oompsMag != 0 ? format(player.oomps) + " OOM" + (player.oompsMag < 0 ? "^OOM" : player.oompsMag > 1 ? "^" + player.oompsMag : "") + "s" : formatSmall(pointGain) }}/sec)
|
({{ state.oompsMag != 0 ? format(state.oomps) + " OOM" + (state.oompsMag < 0 ? "^OOM" : state.oompsMag > 1 ? "^" + state.oompsMag : "") + "s" : formatSmall(pointGain) }}/sec)
|
||||||
</div>
|
</div>
|
||||||
<spacer />
|
<spacer />
|
||||||
<modal :show="false">
|
<modal :show="false">
|
||||||
|
|
|
@ -3,11 +3,7 @@ import modInfo from "@/data/modInfo.json";
|
||||||
import Decimal, { DecimalSource } from "@/util/bignum";
|
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||||
import { layers } from "./layers";
|
import { layers } from "./layers";
|
||||||
import player from "./player";
|
import player from "./player";
|
||||||
|
import state from "./state";
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
|
||||||
function updatePopups(diff: number) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||||
function updateParticles(diff: number) {
|
function updateParticles(diff: number) {
|
||||||
|
@ -16,21 +12,21 @@ function updateParticles(diff: number) {
|
||||||
|
|
||||||
function updateOOMPS(diff: DecimalSource) {
|
function updateOOMPS(diff: DecimalSource) {
|
||||||
if (player.points != undefined) {
|
if (player.points != undefined) {
|
||||||
player.oompsMag = 0;
|
state.oompsMag = 0;
|
||||||
if (player.points.lte(new Decimal(1e100))) {
|
if (player.points.lte(new Decimal(1e100))) {
|
||||||
player.lastPoints = player.points;
|
state.lastPoints = player.points;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let curr = player.points;
|
let curr = player.points;
|
||||||
let prev = (player.lastPoints as Decimal) || new Decimal(0);
|
let prev = (state.lastPoints as Decimal) || new Decimal(0);
|
||||||
player.lastPoints = curr;
|
state.lastPoints = curr;
|
||||||
if (curr.gt(prev)) {
|
if (curr.gt(prev)) {
|
||||||
if (curr.gte("10^^8")) {
|
if (curr.gte("10^^8")) {
|
||||||
curr = curr.slog(1e10);
|
curr = curr.slog(1e10);
|
||||||
prev = prev.slog(1e10);
|
prev = prev.slog(1e10);
|
||||||
player.oomps = curr.sub(prev).div(diff);
|
state.oomps = curr.sub(prev).div(diff);
|
||||||
player.oompsMag = -1;
|
state.oompsMag = -1;
|
||||||
} else {
|
} else {
|
||||||
while (
|
while (
|
||||||
curr
|
curr
|
||||||
|
@ -38,13 +34,13 @@ function updateOOMPS(diff: DecimalSource) {
|
||||||
.log(10)
|
.log(10)
|
||||||
.div(diff)
|
.div(diff)
|
||||||
.gte("100") &&
|
.gte("100") &&
|
||||||
player.oompsMag <= 5 &&
|
state.oompsMag <= 5 &&
|
||||||
prev.gt(0)
|
prev.gt(0)
|
||||||
) {
|
) {
|
||||||
curr = curr.log(10);
|
curr = curr.log(10);
|
||||||
prev = prev.log(10);
|
prev = prev.log(10);
|
||||||
player.oomps = curr.sub(prev).div(diff);
|
state.oomps = curr.sub(prev).div(diff);
|
||||||
player.oompsMag++;
|
state.oompsMag++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,11 +114,10 @@ function update() {
|
||||||
const trueDiff = diff;
|
const trueDiff = diff;
|
||||||
|
|
||||||
// Always update UI
|
// Always update UI
|
||||||
updatePopups(trueDiff);
|
|
||||||
updateParticles(trueDiff);
|
updateParticles(trueDiff);
|
||||||
player.lastTenTicks.push(trueDiff);
|
state.lastTenTicks.push(trueDiff);
|
||||||
if (player.lastTenTicks.length > 10) {
|
if (state.lastTenTicks.length > 10) {
|
||||||
player.lastTenTicks = player.lastTenTicks.slice(1);
|
state.lastTenTicks = state.lastTenTicks.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop here if the game is paused on the win screen
|
// Stop here if the game is paused on the win screen
|
||||||
|
@ -130,7 +125,7 @@ function update() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Stop here if the player had a NaN value
|
// Stop here if the player had a NaN value
|
||||||
if (player.hasNaN) {
|
if (state.hasNaN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import clone from "lodash.clonedeep";
|
||||||
import { isRef } from "vue";
|
import { isRef } from "vue";
|
||||||
import { ProgressDisplay, Shape } from "./enums";
|
import { ProgressDisplay, Shape } from "./enums";
|
||||||
import { default as playerProxy } from "./player";
|
import { default as playerProxy } from "./player";
|
||||||
|
import settings from "./settings";
|
||||||
|
|
||||||
export const layers: Record<string, Readonly<Layer>> = {};
|
export const layers: Record<string, Readonly<Layer>> = {};
|
||||||
export const hotkeys: Hotkey[] = [];
|
export const hotkeys: Hotkey[] = [];
|
||||||
|
@ -217,7 +218,7 @@ export function addLayer(layer: RawLayer, player?: Partial<PlayerData>): void {
|
||||||
for (const id in layer.challenges.data) {
|
for (const id in layer.challenges.data) {
|
||||||
layer.challenges.data[id].shown = function() {
|
layer.challenges.data[id].shown = function() {
|
||||||
return (
|
return (
|
||||||
this.unlocked !== false && (playerProxy.hideChallenges === false || !this.maxed)
|
this.unlocked !== false && (settings.hideChallenges === false || !this.maxed)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
layer.challenges.data[id].completed = function() {
|
layer.challenges.data[id].completed = function() {
|
||||||
|
@ -376,7 +377,7 @@ export function addLayer(layer: RawLayer, player?: Partial<PlayerData>): void {
|
||||||
if (!this.unlocked) {
|
if (!this.unlocked) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch (playerProxy.msDisplay) {
|
switch (settings.msDisplay) {
|
||||||
default:
|
default:
|
||||||
case "all":
|
case "all":
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import { Themes } from "@/data/themes";
|
|
||||||
import { PlayerData } from "@/typings/player";
|
import { PlayerData } from "@/typings/player";
|
||||||
import Decimal from "@/util/bignum";
|
import Decimal from "@/util/bignum";
|
||||||
import { isPlainObject } from "@/util/common";
|
import { isPlainObject } from "@/util/common";
|
||||||
import { reactive } from "vue";
|
import { reactive } from "vue";
|
||||||
import { ImportingStatus, MilestoneDisplay } from "./enums";
|
import transientState from "./state";
|
||||||
|
|
||||||
const state = reactive<PlayerData>({
|
const state = reactive<PlayerData>({
|
||||||
id: "",
|
id: "",
|
||||||
points: new Decimal(0),
|
points: new Decimal(0),
|
||||||
oomps: new Decimal(0),
|
|
||||||
oompsMag: 0,
|
|
||||||
name: "",
|
name: "",
|
||||||
tabs: [],
|
tabs: [],
|
||||||
time: -1,
|
time: -1,
|
||||||
|
@ -18,22 +15,11 @@ const state = reactive<PlayerData>({
|
||||||
offlineTime: null,
|
offlineTime: null,
|
||||||
timePlayed: new Decimal(0),
|
timePlayed: new Decimal(0),
|
||||||
keepGoing: false,
|
keepGoing: false,
|
||||||
lastTenTicks: [],
|
|
||||||
showTPS: true,
|
|
||||||
msDisplay: MilestoneDisplay.All,
|
|
||||||
hideChallenges: false,
|
|
||||||
theme: Themes.Nordic,
|
|
||||||
subtabs: {},
|
subtabs: {},
|
||||||
minimized: {},
|
minimized: {},
|
||||||
modID: "",
|
modID: "",
|
||||||
modVersion: "",
|
modVersion: "",
|
||||||
justLoaded: false,
|
justLoaded: false,
|
||||||
hasNaN: false,
|
|
||||||
NaNPath: [],
|
|
||||||
NaNReceiver: null,
|
|
||||||
importing: ImportingStatus.NotImporting,
|
|
||||||
saveToImport: "",
|
|
||||||
saveToExport: "",
|
|
||||||
layers: {}
|
layers: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,7 +51,7 @@ const playerHandler: ProxyHandler<Record<string, any>> = {
|
||||||
receiver: ProxyConstructor
|
receiver: ProxyConstructor
|
||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
!state.hasNaN &&
|
!transientState.hasNaN &&
|
||||||
((typeof value === "number" && isNaN(value)) ||
|
((typeof value === "number" && isNaN(value)) ||
|
||||||
(value instanceof Decimal &&
|
(value instanceof Decimal &&
|
||||||
(isNaN(value.sign) || isNaN(value.layer) || isNaN(value.mag))))
|
(isNaN(value.sign) || isNaN(value.layer) || isNaN(value.mag))))
|
||||||
|
@ -81,9 +67,9 @@ const playerHandler: ProxyHandler<Record<string, any>> = {
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
state.autosave = false;
|
state.autosave = false;
|
||||||
state.hasNaN = true;
|
transientState.hasNaN = true;
|
||||||
state.NaNPath = [...target.__path, property];
|
transientState.NaNPath = [...target.__path, property];
|
||||||
state.NaNReceiver = (receiver as unknown) as Record<string, unknown>;
|
transientState.NaNReceiver = (receiver as unknown) as Record<string, unknown>;
|
||||||
console.error(
|
console.error(
|
||||||
`Attempted to set NaN value`,
|
`Attempted to set NaN value`,
|
||||||
[...target.__path, property],
|
[...target.__path, property],
|
||||||
|
|
72
src/game/settings.ts
Normal file
72
src/game/settings.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import modInfo from "@/data/modInfo.json";
|
||||||
|
import { Themes } from "@/data/themes";
|
||||||
|
import { Settings } from "@/typings/settings";
|
||||||
|
import { isPlainObject } from "@/util/common";
|
||||||
|
import { hardReset } from "@/util/save";
|
||||||
|
import { reactive } from "vue";
|
||||||
|
import { MilestoneDisplay } from "./enums";
|
||||||
|
|
||||||
|
const state = reactive<Settings>({
|
||||||
|
active: "",
|
||||||
|
saves: [],
|
||||||
|
showTPS: true,
|
||||||
|
msDisplay: MilestoneDisplay.All,
|
||||||
|
hideChallenges: false,
|
||||||
|
theme: Themes.Nordic
|
||||||
|
});
|
||||||
|
|
||||||
|
const settingsHandler: ProxyHandler<Record<string, any>> = {
|
||||||
|
get(target: Record<string, any>, key: string): any {
|
||||||
|
if (key === "__state") {
|
||||||
|
return target[key];
|
||||||
|
}
|
||||||
|
if (target.__state[key] == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isPlainObject(target.__state[key])) {
|
||||||
|
if (target.__state[key] !== target[key]?.__state) {
|
||||||
|
target[key] = new Proxy({ __state: target.__state[key] }, settingsHandler);
|
||||||
|
}
|
||||||
|
return target[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return target.__state[key];
|
||||||
|
},
|
||||||
|
set(target: Record<string, any>, property: string, value: any): boolean {
|
||||||
|
target.__state[property] = value;
|
||||||
|
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(state)))));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ownKeys(target: Record<string, any>) {
|
||||||
|
return Reflect.ownKeys(target.__state);
|
||||||
|
},
|
||||||
|
has(target: Record<string, any>, key: string) {
|
||||||
|
return Reflect.has(target.__state, key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default window.settings = new Proxy({ __state: state }, settingsHandler) as Settings;
|
||||||
|
|
||||||
|
export function loadSettings(): void {
|
||||||
|
try {
|
||||||
|
const item: string | null = localStorage.getItem(modInfo.id);
|
||||||
|
if (item != null && item !== "") {
|
||||||
|
const settings = JSON.parse(decodeURIComponent(escape(atob(item))));
|
||||||
|
if (typeof settings === "object") {
|
||||||
|
Object.assign(state, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-empty
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hardResetSettings = (window.hardResetSettings = () => {
|
||||||
|
Object.assign(state, {
|
||||||
|
active: "",
|
||||||
|
saves: [],
|
||||||
|
showTPS: true,
|
||||||
|
msDisplay: MilestoneDisplay.All,
|
||||||
|
hideChallenges: false,
|
||||||
|
theme: Themes.Nordic
|
||||||
|
});
|
||||||
|
hardReset();
|
||||||
|
});
|
44
src/game/state.ts
Normal file
44
src/game/state.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { Transient } from "@/typings/transient";
|
||||||
|
import Decimal from "@/util/bignum";
|
||||||
|
import { isPlainObject } from "@/util/common";
|
||||||
|
import { reactive } from "vue";
|
||||||
|
|
||||||
|
const state = reactive<Transient>({
|
||||||
|
lastTenTicks: [],
|
||||||
|
hasNaN: false,
|
||||||
|
NaNPath: [],
|
||||||
|
lastPoints: new Decimal(0),
|
||||||
|
saveToExport: "",
|
||||||
|
oomps: new Decimal(0),
|
||||||
|
oompsMag: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const stateHandler: ProxyHandler<Record<string, any>> = {
|
||||||
|
get(target: Record<string, any>, key: string): any {
|
||||||
|
if (key === "__state") {
|
||||||
|
return target[key];
|
||||||
|
}
|
||||||
|
if (target.__state[key] == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isPlainObject(target.__state[key])) {
|
||||||
|
if (target.__state[key] !== target[key]?.__state) {
|
||||||
|
target[key] = new Proxy({ __state: target.__state[key] }, stateHandler);
|
||||||
|
}
|
||||||
|
return target[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return target.__state[key];
|
||||||
|
},
|
||||||
|
set(target: Record<string, any>, property: string, value: any): boolean {
|
||||||
|
target.__state[property] = value;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ownKeys(target: Record<string, any>) {
|
||||||
|
return Reflect.ownKeys(target.__state);
|
||||||
|
},
|
||||||
|
has(target: Record<string, any>, key: string) {
|
||||||
|
return Reflect.has(target.__state, key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default window.state = new Proxy({ __state: state }, stateHandler) as Transient;
|
5
src/typings/global.d.ts
vendored
5
src/typings/global.d.ts
vendored
|
@ -1,14 +1,19 @@
|
||||||
import Decimal, { DecimalSource } from "@/util/bignum";
|
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||||
import { App } from "vue";
|
import { App } from "vue";
|
||||||
import { PlayerData } from "./player";
|
import { PlayerData } from "./player";
|
||||||
|
import { Settings } from "./settings";
|
||||||
|
import { Transient } from "./transient";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
vue: App;
|
vue: App;
|
||||||
save: () => void;
|
save: () => void;
|
||||||
hardReset: () => void;
|
hardReset: () => void;
|
||||||
|
hardResetSettings: () => void;
|
||||||
layers: Dictionary<typeof Proxy>;
|
layers: Dictionary<typeof Proxy>;
|
||||||
player: PlayerData;
|
player: PlayerData;
|
||||||
|
state: Transient;
|
||||||
|
settings: Settings;
|
||||||
Decimal: typeof Decimal;
|
Decimal: typeof Decimal;
|
||||||
exponentialFormat: (
|
exponentialFormat: (
|
||||||
num: DecimalSource,
|
num: DecimalSource,
|
||||||
|
|
23
src/typings/player.d.ts
vendored
23
src/typings/player.d.ts
vendored
|
@ -1,21 +1,11 @@
|
||||||
import { Themes } from "@/data/themes";
|
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||||
import { DecimalSource } from "@/lib/break_eternity";
|
|
||||||
import Decimal from "@/util/bignum";
|
|
||||||
import { BoardData } from "./features/board";
|
import { BoardData } from "./features/board";
|
||||||
import { MilestoneDisplay } from "./features/milestone";
|
|
||||||
import { State } from "./state";
|
import { State } from "./state";
|
||||||
|
|
||||||
export interface ModSaveData {
|
|
||||||
active?: string;
|
|
||||||
saves?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlayerData {
|
export interface PlayerData {
|
||||||
id: string;
|
id: string;
|
||||||
devSpeed?: DecimalSource;
|
devSpeed?: DecimalSource;
|
||||||
points: Decimal;
|
points: Decimal;
|
||||||
oomps: Decimal;
|
|
||||||
oompsMag: number;
|
|
||||||
name: string;
|
name: string;
|
||||||
tabs: Array<string>;
|
tabs: Array<string>;
|
||||||
time: number;
|
time: number;
|
||||||
|
@ -24,11 +14,6 @@ export interface PlayerData {
|
||||||
offlineTime: Decimal | null;
|
offlineTime: Decimal | null;
|
||||||
timePlayed: Decimal;
|
timePlayed: Decimal;
|
||||||
keepGoing: boolean;
|
keepGoing: boolean;
|
||||||
lastTenTicks: Array<number>;
|
|
||||||
showTPS: boolean;
|
|
||||||
msDisplay: MilestoneDisplay;
|
|
||||||
hideChallenges: boolean;
|
|
||||||
theme: Themes;
|
|
||||||
subtabs: {
|
subtabs: {
|
||||||
[index: string]: {
|
[index: string]: {
|
||||||
mainTabs?: string;
|
mainTabs?: string;
|
||||||
|
@ -39,12 +24,6 @@ export interface PlayerData {
|
||||||
modID: string;
|
modID: string;
|
||||||
modVersion: string;
|
modVersion: string;
|
||||||
justLoaded: boolean;
|
justLoaded: boolean;
|
||||||
hasNaN: boolean;
|
|
||||||
NaNPath?: Array<string>;
|
|
||||||
NaNReceiver?: Record<string, unknown> | null;
|
|
||||||
importing: ImportingStatus;
|
|
||||||
saveToImport: string;
|
|
||||||
saveToExport: string;
|
|
||||||
layers: Record<string, LayerSaveData>;
|
layers: Record<string, LayerSaveData>;
|
||||||
[index: string]: unknown;
|
[index: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
13
src/typings/settings.d.ts
vendored
Normal file
13
src/typings/settings.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { Themes } from "@/data/themes";
|
||||||
|
import { MilestoneDisplay } from "@/game/enums";
|
||||||
|
|
||||||
|
// This is the global save data, persists between individual saves
|
||||||
|
export interface Settings {
|
||||||
|
active: string;
|
||||||
|
saves: string[];
|
||||||
|
showTPS: boolean;
|
||||||
|
msDisplay: MilestoneDisplay;
|
||||||
|
hideChallenges: boolean;
|
||||||
|
theme: Themes;
|
||||||
|
[index: string]: unknown;
|
||||||
|
}
|
13
src/typings/transient.d.ts
vendored
Normal file
13
src/typings/transient.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Decimal from "@/lib/break_eternity";
|
||||||
|
|
||||||
|
// Save data that doesn't persist between reloads
|
||||||
|
export interface Transient {
|
||||||
|
lastTenTicks: number[];
|
||||||
|
hasNaN: bool;
|
||||||
|
NaNPath?: string[];
|
||||||
|
NaNReceiver?: Record<string, unknown>;
|
||||||
|
saveToExport: string;
|
||||||
|
lastPoints: Decimal;
|
||||||
|
oomps: Decimal;
|
||||||
|
oompsMag: number;
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
import { fixOldSave, getInitialLayers, getStartingData } from "@/data/mod";
|
import { fixOldSave, getInitialLayers, getStartingData } from "@/data/mod";
|
||||||
import modInfo from "@/data/modInfo.json";
|
import modInfo from "@/data/modInfo.json";
|
||||||
import { Themes } from "@/data/themes";
|
|
||||||
import { ImportingStatus, MilestoneDisplay } from "@/game/enums";
|
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
import { ModSaveData, PlayerData } from "@/typings/player";
|
import settings, { loadSettings } from "@/game/settings";
|
||||||
|
import state from "@/game/state";
|
||||||
|
import { PlayerData } from "@/typings/player";
|
||||||
import Decimal from "./bignum";
|
import Decimal from "./bignum";
|
||||||
|
|
||||||
export function getInitialStore(playerData: Partial<PlayerData> = {}): PlayerData {
|
export function getInitialStore(playerData: Partial<PlayerData> = {}): PlayerData {
|
||||||
|
@ -11,8 +11,6 @@ export function getInitialStore(playerData: Partial<PlayerData> = {}): PlayerDat
|
||||||
{
|
{
|
||||||
id: `${modInfo.id}-0`,
|
id: `${modInfo.id}-0`,
|
||||||
points: new Decimal(0),
|
points: new Decimal(0),
|
||||||
oomps: new Decimal(0),
|
|
||||||
oompsMag: 0,
|
|
||||||
name: "Default Save",
|
name: "Default Save",
|
||||||
tabs: modInfo.initialTabs.slice(),
|
tabs: modInfo.initialTabs.slice(),
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
|
@ -21,61 +19,29 @@ export function getInitialStore(playerData: Partial<PlayerData> = {}): PlayerDat
|
||||||
offlineTime: new Decimal(0),
|
offlineTime: new Decimal(0),
|
||||||
timePlayed: new Decimal(0),
|
timePlayed: new Decimal(0),
|
||||||
keepGoing: false,
|
keepGoing: false,
|
||||||
lastTenTicks: [],
|
|
||||||
showTPS: true,
|
|
||||||
msDisplay: MilestoneDisplay.All,
|
|
||||||
hideChallenges: false,
|
|
||||||
theme: Themes.Nordic,
|
|
||||||
subtabs: {},
|
subtabs: {},
|
||||||
minimized: {},
|
minimized: {},
|
||||||
modID: modInfo.id,
|
modID: modInfo.id,
|
||||||
modVersion: modInfo.versionNumber,
|
modVersion: modInfo.versionNumber,
|
||||||
layers: {},
|
layers: {},
|
||||||
justLoaded: false,
|
justLoaded: false,
|
||||||
...getStartingData(),
|
...getStartingData()
|
||||||
|
|
||||||
// Values that don't get loaded/saved
|
|
||||||
hasNaN: false,
|
|
||||||
NaNPath: [],
|
|
||||||
NaNReceiver: null,
|
|
||||||
importing: ImportingStatus.NotImporting,
|
|
||||||
saveToImport: "",
|
|
||||||
saveToExport: ""
|
|
||||||
},
|
},
|
||||||
playerData
|
playerData
|
||||||
) as PlayerData;
|
) as PlayerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function save(): void {
|
export function save(): void {
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
state.saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(player.__state))));
|
||||||
const {
|
localStorage.setItem(player.id, state.saveToExport);
|
||||||
hasNaN,
|
|
||||||
NaNPath,
|
|
||||||
NaNReceiver,
|
|
||||||
importing,
|
|
||||||
saveToImport,
|
|
||||||
saveToExport,
|
|
||||||
...playerData
|
|
||||||
} = player.__state as PlayerData;
|
|
||||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
||||||
player.saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(playerData))));
|
|
||||||
|
|
||||||
localStorage.setItem(player.id, player.saveToExport);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function load(): Promise<void> {
|
export async function load(): Promise<void> {
|
||||||
|
// Load global settings
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let modData: string | ModSaveData | null = localStorage.getItem(modInfo.id);
|
const save = localStorage.getItem(settings.active);
|
||||||
if (modData == null) {
|
|
||||||
await loadSave(newSave());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
modData = JSON.parse(decodeURIComponent(escape(atob(modData)))) as ModSaveData;
|
|
||||||
if (modData?.active == null) {
|
|
||||||
await loadSave(newSave());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const save = localStorage.getItem(modData.active);
|
|
||||||
if (save == null) {
|
if (save == null) {
|
||||||
await loadSave(newSave());
|
await loadSave(newSave());
|
||||||
return;
|
return;
|
||||||
|
@ -85,7 +51,7 @@ export async function load(): Promise<void> {
|
||||||
await loadSave(newSave());
|
await loadSave(newSave());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerData.id = modData.active;
|
playerData.id = settings.active;
|
||||||
await loadSave(playerData);
|
await loadSave(playerData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await loadSave(newSave());
|
await loadSave(newSave());
|
||||||
|
@ -97,21 +63,7 @@ export function newSave(): PlayerData {
|
||||||
const playerData = getInitialStore({ id });
|
const playerData = getInitialStore({ id });
|
||||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(playerData)))));
|
localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(playerData)))));
|
||||||
|
|
||||||
const rawModData = localStorage.getItem(modInfo.id);
|
settings.saves.push(id);
|
||||||
if (rawModData == null) {
|
|
||||||
const modData = { active: id, saves: [id] };
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const modData = JSON.parse(decodeURIComponent(escape(atob(rawModData))));
|
|
||||||
modData.saves.push(id);
|
|
||||||
localStorage.setItem(
|
|
||||||
modInfo.id,
|
|
||||||
btoa(unescape(encodeURIComponent(JSON.stringify(modData))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return playerData;
|
return playerData;
|
||||||
}
|
}
|
||||||
|
@ -150,6 +102,7 @@ export async function loadSave(playerData: Partial<PlayerData>): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
player.justLoaded = true;
|
player.justLoaded = true;
|
||||||
|
settings.active = player.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyPlayerData<T extends Record<string, any>>(
|
export function applyPlayerData<T extends Record<string, any>>(
|
||||||
|
@ -191,7 +144,4 @@ window.onbeforeunload = () => {
|
||||||
window.save = save;
|
window.save = save;
|
||||||
export const hardReset = (window.hardReset = async () => {
|
export const hardReset = (window.hardReset = async () => {
|
||||||
await loadSave(newSave());
|
await loadSave(newSave());
|
||||||
const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)!))));
|
|
||||||
modData.active = player.id;
|
|
||||||
localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import { hasWon, pointGain } from "@/data/mod";
|
import { hasWon, pointGain } from "@/data/mod";
|
||||||
import { layers } from "@/game/layers";
|
import { layers } from "@/game/layers";
|
||||||
import player from "@/game/player";
|
import player from "@/game/player";
|
||||||
|
import settings from "@/game/settings";
|
||||||
|
import state from "@/game/state";
|
||||||
import { Feature, Features, GridFeatures } from "@/typings/features/feature";
|
import { Feature, Features, GridFeatures } from "@/typings/features/feature";
|
||||||
import { Layer } from "@/typings/layer";
|
import { Layer } from "@/typings/layer";
|
||||||
|
import { PlayerData } from "@/typings/player";
|
||||||
|
import { Settings } from "@/typings/settings";
|
||||||
|
import { Transient } from "@/typings/transient";
|
||||||
import { App, Component, ComponentOptions, defineComponent, inject, PropType } from "vue";
|
import { App, Component, ComponentOptions, defineComponent, inject, PropType } from "vue";
|
||||||
import Decimal, * as numberUtils from "./bignum";
|
import Decimal, * as numberUtils from "./bignum";
|
||||||
import {
|
import {
|
||||||
|
@ -34,7 +39,7 @@ export function setVue(vm: App): void {
|
||||||
|
|
||||||
// Pass in various data that the template could potentially use
|
// Pass in various data that the template could potentially use
|
||||||
const data = function(): Record<string, unknown> {
|
const data = function(): Record<string, unknown> {
|
||||||
return { Decimal, player, layers, hasWon, pointGain, ...numberUtils };
|
return { Decimal, player, state, settings, layers, hasWon, pointGain, ...numberUtils };
|
||||||
};
|
};
|
||||||
export function coerceComponent(
|
export function coerceComponent(
|
||||||
component: string | ComponentOptions | Component,
|
component: string | ComponentOptions | Component,
|
||||||
|
@ -95,14 +100,41 @@ export function getFiltered<T>(
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapState(properties: Array<string> = []): Record<string, unknown> {
|
type OmitIndex<T> = {
|
||||||
return properties.reduce((acc: Record<string, unknown>, curr: string): Record<
|
[K in keyof T as unknown extends Record<K, 1> ? never : K]: T[K];
|
||||||
string,
|
};
|
||||||
unknown
|
|
||||||
> => {
|
export function mapPlayer<K extends keyof OmitIndex<PlayerData>>(
|
||||||
|
properties: K[] = []
|
||||||
|
): {
|
||||||
|
[P in K]: () => PlayerData[P];
|
||||||
|
} {
|
||||||
|
return properties.reduce((acc, curr: keyof PlayerData) => {
|
||||||
acc[curr] = () => player[curr];
|
acc[curr] = () => player[curr];
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {} as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapSettings<K extends keyof OmitIndex<Settings>>(
|
||||||
|
properties: K[] = []
|
||||||
|
): {
|
||||||
|
[P in K]: () => Settings[P];
|
||||||
|
} {
|
||||||
|
return properties.reduce((acc, curr: keyof Settings) => {
|
||||||
|
acc[curr] = () => settings[curr];
|
||||||
|
return acc;
|
||||||
|
}, {} as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapState<K extends keyof OmitIndex<Transient>>(
|
||||||
|
properties: K[] = []
|
||||||
|
): {
|
||||||
|
[P in K]: () => Transient[P];
|
||||||
|
} {
|
||||||
|
return properties.reduce((acc, curr: keyof Transient) => {
|
||||||
|
acc[curr] = () => state[curr];
|
||||||
|
return acc;
|
||||||
|
}, {} as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InjectLayerMixin = {
|
export const InjectLayerMixin = {
|
||||||
|
@ -126,9 +158,9 @@ export function FilteredFeaturesMixin<T extends Feature>(
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
computed: {
|
computed: {
|
||||||
filtered: () => Record<string, T> | undefined;
|
filtered: (this: { layer: string; [feature]: string[] }) => Record<string, T> | undefined;
|
||||||
rows: () => number | undefined;
|
rows: (this: { layer: string }) => number | undefined;
|
||||||
cols: () => number | undefined;
|
cols: (this: { layer: string }) => number | undefined;
|
||||||
};
|
};
|
||||||
} {
|
} {
|
||||||
return {
|
return {
|
||||||
|
@ -139,19 +171,19 @@ export function FilteredFeaturesMixin<T extends Feature>(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
filtered(this: { layer: string; [feature]: string[] }) {
|
filtered() {
|
||||||
return (
|
return (
|
||||||
(layers[this.layer][feature] as Features<T> | undefined) &&
|
(layers[this.layer][feature] as Features<T> | undefined) &&
|
||||||
getFiltered((layers[this.layer][feature] as Features<T>).data, this[feature])
|
getFiltered((layers[this.layer][feature] as Features<T>).data, this[feature])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
rows(this: { layer: string }) {
|
rows() {
|
||||||
return (
|
return (
|
||||||
(layers[this.layer][feature] as Features<T> | undefined) &&
|
(layers[this.layer][feature] as Features<T> | undefined) &&
|
||||||
(layers[this.layer][feature] as Features<T> | GridFeatures<T>).rows
|
(layers[this.layer][feature] as Features<T> | GridFeatures<T>).rows
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
cols(this: { layer: string }) {
|
cols() {
|
||||||
return (
|
return (
|
||||||
(layers[this.layer][feature] as Features<T> | undefined) &&
|
(layers[this.layer][feature] as Features<T> | undefined) &&
|
||||||
(layers[this.layer][feature] as Features<T> | GridFeatures<T>).cols
|
(layers[this.layer][feature] as Features<T> | GridFeatures<T>).cols
|
||||||
|
|
Loading…
Reference in a new issue