From d30cfe857fb0e6e0ac51a45c94857b69c716d25b Mon Sep 17 00:00:00 2001 From: thepaperpilot Date: Wed, 22 Feb 2023 20:48:04 -0600 Subject: [PATCH] Fix NaN checks --- src/data/common.tsx | 4 +-- src/features/achievements/achievement.tsx | 2 +- src/features/boards/board.ts | 13 +++++---- src/features/challenges/challenge.tsx | 2 +- src/features/grids/grid.ts | 2 +- src/features/infoboxes/infobox.ts | 2 +- src/features/milestones/milestone.tsx | 2 +- src/features/tabs/tabFamily.ts | 2 +- src/features/tooltips/tooltip.ts | 2 +- src/features/upgrades/upgrade.ts | 2 +- src/game/layers.tsx | 2 +- src/game/persistence.ts | 32 ++++++++++++++++++----- 12 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/data/common.tsx b/src/data/common.tsx index 9f1436f..b0becca 100644 --- a/src/data/common.tsx +++ b/src/data/common.tsx @@ -288,7 +288,7 @@ export function createCollapsibleModifierSections( return sections; } - const collapsed = persistent>({}); + const collapsed = persistent>({}, false); const jsxFunc = jsx(() => { const sections = calculateSections(); @@ -389,7 +389,7 @@ export function colorText(textToColor: string, color = "var(--accent2)"): JSX.El export function createCollapsibleMilestones(milestones: Record) { // Milestones are typically defined from easiest to hardest, and we want to show hardest first const orderedMilestones = Object.values(milestones).reverse(); - const collapseMilestones = persistent(true); + const collapseMilestones = persistent(true, false); const lockedMilestones = computed(() => orderedMilestones.filter(m => m.earned.value === false) ); diff --git a/src/features/achievements/achievement.tsx b/src/features/achievements/achievement.tsx index 500e93f..7daaf7e 100644 --- a/src/features/achievements/achievement.tsx +++ b/src/features/achievements/achievement.tsx @@ -74,7 +74,7 @@ export type GenericAchievement = Replace< export function createAchievement( optionsFunc?: OptionsFunc ): Achievement { - const earned = persistent(false); + const earned = persistent(false, false); return createLazyProxy(() => { const achievement = optionsFunc?.() ?? ({} as ReturnType>); achievement.id = getUniqueID("achievement-"); diff --git a/src/features/boards/board.ts b/src/features/boards/board.ts index 7f79110..cc17d2e 100644 --- a/src/features/boards/board.ts +++ b/src/features/boards/board.ts @@ -208,11 +208,14 @@ export type GenericBoard = Replace< export function createBoard( optionsFunc: OptionsFunc ): Board { - const state = persistent({ - nodes: [], - selectedNode: null, - selectedAction: null - }); + const state = persistent( + { + nodes: [], + selectedNode: null, + selectedAction: null + }, + false + ); return createLazyProxy(() => { const board = optionsFunc(); diff --git a/src/features/challenges/challenge.tsx b/src/features/challenges/challenge.tsx index ef4c77d..58a804c 100644 --- a/src/features/challenges/challenge.tsx +++ b/src/features/challenges/challenge.tsx @@ -101,7 +101,7 @@ export function createChallenge( optionsFunc: OptionsFunc ): Challenge { const completions = persistent(0); - const active = persistent(false); + const active = persistent(false, false); return createLazyProxy(() => { const challenge = optionsFunc(); diff --git a/src/features/grids/grid.ts b/src/features/grids/grid.ts index eda80da..53fd7fb 100644 --- a/src/features/grids/grid.ts +++ b/src/features/grids/grid.ts @@ -238,7 +238,7 @@ export type GenericGrid = Replace< export function createGrid( optionsFunc: OptionsFunc ): Grid { - const cellState = persistent>({}); + const cellState = persistent>({}, false); return createLazyProxy(() => { const grid = optionsFunc(); grid.id = getUniqueID("grid-"); diff --git a/src/features/infoboxes/infobox.ts b/src/features/infoboxes/infobox.ts index 43e3ffa..81d7bf6 100644 --- a/src/features/infoboxes/infobox.ts +++ b/src/features/infoboxes/infobox.ts @@ -58,7 +58,7 @@ export type GenericInfobox = Replace< export function createInfobox( optionsFunc: OptionsFunc ): Infobox { - const collapsed = persistent(false); + const collapsed = persistent(false, false); return createLazyProxy(() => { const infobox = optionsFunc(); infobox.id = getUniqueID("infobox-"); diff --git a/src/features/milestones/milestone.tsx b/src/features/milestones/milestone.tsx index 6ea736b..30cd243 100644 --- a/src/features/milestones/milestone.tsx +++ b/src/features/milestones/milestone.tsx @@ -94,7 +94,7 @@ export type GenericMilestone = Replace< export function createMilestone( optionsFunc?: OptionsFunc ): Milestone { - const earned = persistent(false); + const earned = persistent(false, false); return createLazyProxy(() => { const milestone = optionsFunc?.() ?? ({} as ReturnType>); milestone.id = getUniqueID("milestone-"); diff --git a/src/features/tabs/tabFamily.ts b/src/features/tabs/tabFamily.ts index 51d298e..5f5c666 100644 --- a/src/features/tabs/tabFamily.ts +++ b/src/features/tabs/tabFamily.ts @@ -101,7 +101,7 @@ export function createTabFamily( throw "Cannot create tab family with 0 tabs"; } - const selected = persistent(Object.keys(tabs)[0]); + const selected = persistent(Object.keys(tabs)[0], false); return createLazyProxy(() => { const tabFamily = optionsFunc?.() ?? ({} as ReturnType>); diff --git a/src/features/tooltips/tooltip.ts b/src/features/tooltips/tooltip.ts index dcc618f..13cb931 100644 --- a/src/features/tooltips/tooltip.ts +++ b/src/features/tooltips/tooltip.ts @@ -78,7 +78,7 @@ export function addTooltip( options.pinnable = false; } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any - (element as any).pinned = options.pinned = persistent(false); + (element as any).pinned = options.pinned = persistent(false, false); } } diff --git a/src/features/upgrades/upgrade.ts b/src/features/upgrades/upgrade.ts index 5eda0e0..924e28f 100644 --- a/src/features/upgrades/upgrade.ts +++ b/src/features/upgrades/upgrade.ts @@ -87,7 +87,7 @@ export type GenericUpgrade = Replace< export function createUpgrade( optionsFunc: OptionsFunc ): Upgrade { - const bought = persistent(false); + const bought = persistent(false, false); return createLazyProxy(() => { const upgrade = optionsFunc(); upgrade.id = getUniqueID("upgrade-"); diff --git a/src/game/layers.tsx b/src/game/layers.tsx index c689235..532daba 100644 --- a/src/game/layers.tsx +++ b/src/game/layers.tsx @@ -219,7 +219,7 @@ export function createLayer( addingLayers.push(id); persistentRefs[id] = new Set(); - layer.minimized = persistent(false); + layer.minimized = persistent(false, false); Object.assign(layer, optionsFunc.call(layer as BaseLayer)); if ( addingLayers[addingLayers.length - 1] == null || diff --git a/src/game/persistence.ts b/src/game/persistence.ts index 293140a..bf3afa9 100644 --- a/src/game/persistence.ts +++ b/src/game/persistence.ts @@ -40,6 +40,11 @@ export const NonPersistent = Symbol("NonPersistent"); * @see {@link Persistent[SaveDataPath]} */ export const SaveDataPath = Symbol("SaveDataPath"); +/** + * A symbol used in {@link Persistent} objects. + * @see {@link Persistent[CheckNaN]} + */ +export const CheckNaN = Symbol("CheckNaN"); /** * This is a union of things that should be safely stringifiable without needing special processes or knowing what to load them in as. @@ -78,6 +83,10 @@ export type Persistent = Ref & { * The path this persistent appears in within the save data object. Predominantly used to ensure it's only placed in there one time. */ [SaveDataPath]: string[] | undefined; + /** + * Whether or not to NaN-check this ref. Should only be true on values expected to always be DecimalSources. + */ + [CheckNaN]: boolean; }; export type NonPersistent = WritableComputedRef & { [DefaultValue]: T }; @@ -94,10 +103,7 @@ function getStackTrace() { function checkNaNAndWrite(persistent: Persistent, value: T) { // Decimal is smart enough to return false on things that aren't supposed to be numbers - if ( - Decimal.isNaN(value as DecimalSource) && - !Decimal.isNaN(persistent.value as DecimalSource) - ) { + if (Decimal.isNaN(value as DecimalSource)) { if (!state.hasNaN) { player.autosave = false; state.hasNaN = true; @@ -118,8 +124,12 @@ function checkNaNAndWrite(persistent: Persistent, value: T) * Create a persistent ref, which can be saved and loaded. * All (non-deleted) persistent refs must be included somewhere within the layer object returned by that layer's options func. * @param defaultValue The value the persistent ref should start at on fresh saves or when reset. + * @param checkNaN Whether or not to check this ref for being NaN on set. Only use on refs that should always be DecimalSources. */ -export function persistent(defaultValue: T | Ref): Persistent { +export function persistent( + defaultValue: T | Ref, + checkNaN = true +): Persistent { const persistentState: Ref = isRef(defaultValue) ? defaultValue : (ref(defaultValue) as Ref); @@ -133,7 +143,11 @@ export function persistent(defaultValue: T | Ref): Persisten return persistentState.value; }, set(value) { - checkNaNAndWrite(persistent, value); + if (checkNaN) { + checkNaNAndWrite(persistent, value); + } else { + persistent[PersistentState].value = value; + } } }) as NonPersistent; nonPersistent[DefaultValue] = defaultValue; @@ -144,7 +158,11 @@ export function persistent(defaultValue: T | Ref): Persisten return persistentState.value as T; }, set value(value: T) { - checkNaNAndWrite(persistent, value); + if (checkNaN) { + checkNaNAndWrite(persistent, value); + } else { + persistent[PersistentState].value = value; + } }, __v_isRef: true, [PersistentState]: persistentState,