From aa7c4968d1caa4ab69d32832a7b3beff15e3aa32 Mon Sep 17 00:00:00 2001 From: Kevin Bunn <45328239+Pseudonian@users.noreply.github.com> Date: Fri, 7 Feb 2025 14:27:30 -0500 Subject: [PATCH] Address PR Comments --- biome.json | 3 + src/Campaign.ts | 122 +++++++++++++++++------------ src/Corruptions.ts | 57 ++++++++++---- src/History.ts | 4 +- src/Reset.ts | 2 +- src/Synergism.ts | 37 ++++----- src/UpdateVisuals.ts | 14 ++-- src/saves/PlayerJsonSchema.ts | 16 ++-- src/saves/PlayerSchema.ts | 52 ++++++------ src/saves/PlayerUpdateVarSchema.ts | 14 +--- src/types/Synergism.d.ts | 2 +- 11 files changed, 182 insertions(+), 141 deletions(-) diff --git a/biome.json b/biome.json index 51ed0903f..7db761e4d 100644 --- a/biome.json +++ b/biome.json @@ -22,6 +22,9 @@ "complexity": { "noUselessTypeConstraint": "off", "noForEach": "off" + }, + "performance": { + "noDelete": "off" } }, "ignore": ["./dist/*", "**/node_modules/*"] diff --git a/src/Campaign.ts b/src/Campaign.ts index 9b4856383..2b315be4f 100644 --- a/src/Campaign.ts +++ b/src/Campaign.ts @@ -1,12 +1,12 @@ import i18next from 'i18next' -import { CorruptionLoadout, type Corruptions } from './Corruptions' +import { CorruptionLoadout, corruptionsSchema, type Corruptions } from './Corruptions' import { player } from './Synergism' import { DOMCacheGetOrSet } from './Cache/DOM' import { IconSets } from './Themes' export type AscensionModifiers = 'GlobalSpeed' -export type CampaignLoadout = Partial +export type CampaignLoadout = Corruptions export type CampaignModifiers = Partial> export type CampaignKeys = 'test1' | 'test2' | 'test3' @@ -24,96 +24,118 @@ export interface ICampaignData { } export class CampaignManager { - private totalCampaignTokens: number - private currentCampaign: Campaign | undefined - private campaigns!: Record + #totalCampaignTokens: number + #currentCampaign: Campaign | undefined + #campaigns!: Record constructor (campaignManagerData?: ICampaignManagerData) { - this.campaigns = {} as Record - if (campaignManagerData !== undefined) { - for (const campaignKey of Object.keys(campaignDatas)) { - const key = campaignKey as keyof typeof campaignDatas - this.campaigns[key] = new Campaign(campaignDatas[key], key, campaignManagerData.campaigns?.[key]) - } + if (campaignManagerData !== undefined) { + this.#campaigns = Object.fromEntries( + Object.entries(campaignDatas).map(([key, data]) => { + return [key, new Campaign(data, key, campaignManagerData.campaigns?.[key as CampaignKeys])] + } + )) as Record const currentKey = campaignManagerData.currentCampaign if (currentKey !== undefined) { - this.currentCampaign = this.campaigns[currentKey] - player.corruptions.used = new CorruptionLoadout(this.currentCampaign.campaignCorruptions) + this.#currentCampaign = this.#campaigns[currentKey] + player.corruptions.used = new CorruptionLoadout(this.#currentCampaign.campaignCorruptions) } else { - this.currentCampaign = undefined + this.#currentCampaign = undefined } } else { - for (const campaignKey of Object.keys(campaignDatas)) { - const key = campaignKey as keyof typeof campaignDatas - this.campaigns[key] = new Campaign(campaignDatas[key], key, 0) - } - this.currentCampaign = undefined + this.#campaigns = Object.fromEntries( + Object.entries(campaignDatas).map(([key, data]) => { + return [key, new Campaign(data, key)] + } + )) as Record + this.#currentCampaign = undefined } - this.totalCampaignTokens = this.computeTotalCampaignTokens() + this.#totalCampaignTokens = this.computeTotalCampaignTokens() } - computeTotalCampaignTokens = () => { + computeTotalCampaignTokens () { let sum = 0 - for (const campaign in this.campaigns) { - const key = campaign as keyof typeof this.campaigns - sum += this.campaigns[key].computeTokenValue() + for (const campaign of Object.values(this.#campaigns)) { + sum += campaign.computeTokenValue() } return sum } get tokens () { - return this.totalCampaignTokens + return this.#totalCampaignTokens } get current () { - return this.currentCampaign + return this.#currentCampaign } // Store as this in player get c10Completions (): Record { return Object.fromEntries( - Object.entries(this.campaigns).map(([key, value]) => [key, value.c10Completions]) + Object.entries(this.#campaigns).map(([key, value]) => [key, value.c10Completions]) ) as Record } } export class Campaign { - // Stored as variable out of scope - name: string - description: string - campaignCorruptions: CampaignLoadout - campaignModifiers: CampaignModifiers - limit: number - isMeta: boolean - - // Saved as a variable - _c10Completions = 0 + #name: string + #description: string + #campaignCorruptions: CampaignLoadout + #campaignModifiers: CampaignModifiers + #limit: number + #isMeta: boolean + #c10Completions = 0 constructor (campaignData: ICampaignData, key: string, c10?: number) { - this.name = i18next.t(`campaigns.data.${key}.name`) - this.description = i18next.t(`campaigns.data.${key}.description`) - this.campaignCorruptions = campaignData.campaignCorruptions - this.campaignModifiers = campaignData.campaignModifiers - this.limit = campaignData.limit - this.isMeta = campaignData.isMeta - this._c10Completions = c10 ?? 0 + this.#name = i18next.t(`campaigns.data.${key}.name`) + this.#description = i18next.t(`campaigns.data.${key}.description`) + this.#campaignCorruptions = corruptionsSchema.parse(campaignData.campaignCorruptions) + this.#campaignModifiers = campaignData.campaignModifiers + this.#limit = campaignData.limit + this.#isMeta = campaignData.isMeta + this.#c10Completions = c10 ?? 0 } public computeTokenValue = () => { - const metaMultiplier = this.isMeta ? 2 : 1 - return metaMultiplier * Math.min(this.c10Completions, this.limit) + const metaMultiplier = this.#isMeta ? 2 : 1 + return metaMultiplier * Math.min(this.c10Completions, this.#limit) } public createUsableLoadout = (): CorruptionLoadout => { - return new CorruptionLoadout(this.campaignCorruptions) + return new CorruptionLoadout(this.#campaignCorruptions) } public set c10Completions (value: number) { - this._c10Completions = Math.min(value, this.limit) + this.#c10Completions = Math.min(value, this.#limit) + } + + public get campaignCorruptions () { + return this.#campaignCorruptions } + public get c10Completions () { - return this._c10Completions + return this.#c10Completions + } + + public get name () { + return this.#name + } + + public get description () { + return this.#description + } + + public get limit () { + return this.#limit + } + + public get campaignModifiers () { + return this.#campaignModifiers + } + + public get isMeta () { + return this.#isMeta } } @@ -142,7 +164,7 @@ export const campaignDatas: Record = { test3: { campaignCorruptions: { viscosity: 1, - deflation: 1, + deflation: 1, dilation: 1 }, campaignModifiers: { diff --git a/src/Corruptions.ts b/src/Corruptions.ts index e927a4d1a..27773ba55 100644 --- a/src/Corruptions.ts +++ b/src/Corruptions.ts @@ -7,20 +7,43 @@ import { Alert, Prompt } from './UpdateHTML' import { getElementById, productContents, sumContents, validateNonnegativeInteger } from './Utility' import { Globals as G } from './Variables' import { PCoinUpgradeEffects } from './PseudoCoinUpgrades' +import { z } from 'zod' + +export enum CorruptionIndices { + 'viscosity' = 0, + 'dilation' = 1, + 'hyperchallenge' = 2, + 'illiteracy' = 3, + 'deflation' = 4, + 'extinction' = 5, + 'drought' = 6, + 'recession' = 7 +} export const convertInputToCorruption = (array: number[]): Corruptions => { return { - viscosity: array[0], - drought: array[6], - deflation: array[4], - extinction: array[5], - illiteracy: array[3], - recession: array[7], - dilation: array[1], - hyperchallenge: array[2] + viscosity: array[CorruptionIndices.viscosity], + drought: array[CorruptionIndices.drought], + deflation: array[CorruptionIndices.deflation], + extinction: array[CorruptionIndices.extinction], + illiteracy: array[CorruptionIndices.illiteracy], + recession: array[CorruptionIndices.recession], + dilation: array[CorruptionIndices.dilation], + hyperchallenge: array[CorruptionIndices.hyperchallenge] } } +export const corruptionsSchema = z.object({ + viscosity: z.number().default(0), + drought: z.number().default(0), + deflation: z.number().default(0), + extinction: z.number().default(0), + illiteracy: z.number().default(0), + recession: z.number().default(0), + dilation: z.number().default(0), + hyperchallenge: z.number().default(0), +}) + export type Corruptions = { viscosity: number drought: number @@ -58,8 +81,10 @@ export class CorruptionLoadout { } #bonusLevels = 0 - constructor (p: Partial) { - Object.assign(this.#levels, p) + constructor (p: Corruptions) { + Object.entries(p).forEach(([key, value]) => { + this.#levels[key as keyof Corruptions] = value + }) } public setCorruptionLevels (corruptions: Partial) { @@ -325,13 +350,13 @@ export type SavedCorruption = { export class CorruptionSaves { #saves: Array = [] - constructor (corrSaveData: Record>) { + constructor (corrSaveData: Record) { for (const saveKey of Object.keys(corrSaveData).slice(0, 16)) { this.#saves.push({ name: saveKey, loadout: new CorruptionLoadout(corrSaveData[saveKey]) }) } } - addSave (loadoutName: string, loadoutValues: Partial) { + addSave (loadoutName: string, loadoutValues: Corruptions) { this.#saves.push({ name: loadoutName, loadout: new CorruptionLoadout(loadoutValues) }) } @@ -648,9 +673,10 @@ export const applyCorruptions = (corruptions: string) => { console.log(corruptions) if (corruptions.includes('/') && corruptions.split('/').length === 8) { + // Supports legacy format corr = convertInputToCorruption(corruptions.split('/').map(Number)) } else { - corr = JSON.parse(corruptions) as Corruptions + corr = corruptionsSchema.parse(corruptions) } if (corr) { @@ -665,7 +691,7 @@ export const applyCorruptions = (corruptions: string) => { async function importCorruptionsPrompt () { const input = await Prompt(i18next.t('corruptions.importCorruptionsPrompt.import')) - if (!applyCorruptions(input as string)) { + if (input === null || !applyCorruptions(input)) { void Alert(i18next.t('corruptions.importCorruptionsPrompt.importError')) } } @@ -695,7 +721,8 @@ async function corruptionLoadoutGetNewName (loadout = 0) { export const updateCorruptionLoadoutNames = () => { const rows = getElementById('corruptionLoadoutTable').rows - for (let i = 0; i < 8; i++) { + const totalSlots = 8 + PCoinUpgradeEffects.CORRUPTION_LOADOUT_SLOT_QOL + for (let i = 0; i < totalSlots; i++) { const cells = rows[i + 2].cells // start changes on 2nd row if (cells[0].textContent!.length === 0) { // first time setup cells[0].addEventListener('click', () => void corruptionLoadoutGetNewName(i)) // get name function handles -1 for array diff --git a/src/History.ts b/src/History.ts index 752e0596f..0e0cb04da 100644 --- a/src/History.ts +++ b/src/History.ts @@ -558,10 +558,10 @@ const resetHistoryFormatCorruptions = (data: ResetHistoryEntryAscend): [string, corrs++ } - const corr_str = JSON.stringify(corrToLoad) + const corrStr = JSON.stringify(corrToLoad) if (corruptions) { - loadout += `` + loadout += `` } if (data.currentChallenge !== undefined) { score += ` / C${data.currentChallenge}` diff --git a/src/Reset.ts b/src/Reset.ts index 270f6dfa5..7bcecc05a 100644 --- a/src/Reset.ts +++ b/src/Reset.ts @@ -299,7 +299,7 @@ const resetAddHistoryEntry = (input: resetNames, from = 'unknown') => { seconds: player.ascensionCounter, date: Date.now(), c10Completions: player.challengecompletions[10], - usedCorruptions: player.corruptions.used, // shallow copy, + usedCorruptions: player.corruptions.used.loadout, corruptionScore: corruptionMetaData[3], wowCubes: corruptionMetaData[4], wowTesseracts: corruptionMetaData[5], diff --git a/src/Synergism.ts b/src/Synergism.ts index b7b5c24d3..c49d6bd9d 100644 --- a/src/Synergism.ts +++ b/src/Synergism.ts @@ -64,6 +64,7 @@ import { corruptionLoadoutTableCreate, corruptionLoadoutTableUpdate, CorruptionSaves, + corruptionsSchema, corruptionStatsUpdate, updateCorruptionLoadoutNames, updateUndefinedLoadouts, @@ -881,25 +882,25 @@ export const player: Player = { }, corruptions: { - next: new CorruptionLoadout({}), - used: new CorruptionLoadout({}), + next: new CorruptionLoadout(corruptionsSchema.parse({})), + used: new CorruptionLoadout(corruptionsSchema.parse({})), saves: new CorruptionSaves({ - 'Loadout 1': {}, - 'Loadout 2': {}, - 'Loadout 3': {}, - 'Loadout 4': {}, - 'Loadout 5': {}, - 'Loadout 6': {}, - 'Loadout 7': {}, - 'Loadout 8': {}, - 'Loadout 9': {}, - 'Loadout 10': {}, - 'Loadout 11': {}, - 'Loadout 12': {}, - 'Loadout 13': {}, - 'Loadout 14': {}, - 'Loadout 15': {}, - 'Loadout 16': {}, + 'Loadout 1': corruptionsSchema.parse({}), + 'Loadout 2': corruptionsSchema.parse({}), + 'Loadout 3': corruptionsSchema.parse({}), + 'Loadout 4': corruptionsSchema.parse({}), + 'Loadout 5': corruptionsSchema.parse({}), + 'Loadout 6': corruptionsSchema.parse({}), + 'Loadout 7': corruptionsSchema.parse({}), + 'Loadout 8': corruptionsSchema.parse({}), + 'Loadout 9': corruptionsSchema.parse({}), + 'Loadout 10': corruptionsSchema.parse({}), + 'Loadout 11': corruptionsSchema.parse({}), + 'Loadout 12': corruptionsSchema.parse({}), + 'Loadout 13': corruptionsSchema.parse({}), + 'Loadout 14': corruptionsSchema.parse({}), + 'Loadout 15': corruptionsSchema.parse({}), + 'Loadout 16': corruptionsSchema.parse({}), }), showStats: true }, diff --git a/src/UpdateVisuals.ts b/src/UpdateVisuals.ts index 68ee64d58..eeb301ff5 100644 --- a/src/UpdateVisuals.ts +++ b/src/UpdateVisuals.ts @@ -41,7 +41,7 @@ import { format, formatTimeShort, player } from './Synergism' import { Tabs } from './Tabs' import { calculateMaxTalismanLevel } from './Talismans' import type { Player, ZeroToFour } from './types/Synergism' -import { sumContents, timeReminingHours } from './Utility' +import { memoize, sumContents, timeReminingHours } from './Utility' import { Globals as G } from './Variables' export const visualUpdateBuildings = () => { @@ -1866,15 +1866,11 @@ export const visualUpdateEvent = () => { export const visualUpdatePurchase = () => {} -// Experimental: Some things in the Campaign need only loaded once. -// What if I just made it so that it loads exactly once and then -// Never again? - 2025 Platonic -let cachedVisualCampaignUpdates = false - export const visualUpdateCampaign = () => { - if (!cachedVisualCampaignUpdates) { + + memoize(() => { DOMCacheGetOrSet('campaignsIntroduction').innerHTML = i18next.t('campaigns.intro') DOMCacheGetOrSet('campaignsInfo1').innerHTML = i18next.t('campaigns.campaignInfo') - cachedVisualCampaignUpdates = true - } + }) + } \ No newline at end of file diff --git a/src/saves/PlayerJsonSchema.ts b/src/saves/PlayerJsonSchema.ts index a38117465..1b198889e 100644 --- a/src/saves/PlayerJsonSchema.ts +++ b/src/saves/PlayerJsonSchema.ts @@ -1,9 +1,9 @@ import { z } from 'zod' import type { Player } from '../types/Synergism' -import { playerSchema } from './PlayerSchema' +import { playerCampaignSchema, playerCorruptionSchema, playerSchema } from './PlayerSchema' import type { CorruptionLoadout, Corruptions } from '../Corruptions' -const convertArrayToCorruption = (array: number[]): Corruptions => { +export const convertArrayToCorruption = (array: number[]): Corruptions => { return { viscosity: array[2], drought: array[8], @@ -24,11 +24,11 @@ export const playerJsonSchema = playerSchema.extend({ wowHypercubes: z.any().transform((hypercubes: Player['wowHypercubes']) => Number(hypercubes)), wowPlatonicCubes: z.any().transform((cubes: Player['wowPlatonicCubes']) => Number(cubes)), - campaigns: z.any().transform((campaignManager: Player['campaigns']) => { + campaigns: playerCampaignSchema.transform((campaignManager: Player['campaigns']) => { return campaignManager.c10Completions }), - corruptions: z.any().transform((stuff: Player['corruptions']) => { + corruptions: playerCorruptionSchema.transform((stuff: Player['corruptions']) => { return { used: stuff.used.loadout, next: stuff.next.loadout, @@ -132,10 +132,10 @@ export const playerJsonSchema = playerSchema.extend({ player.corruptions.saves = corruptionSaveStuff } - player.usedCorruptions = undefined - player.prototypeCorruptions = undefined - player.corruptionLoadoutNames = undefined - player.corruptionLoadouts = undefined + delete player.usedCorruptions + delete player.prototypeCorruptions + delete player.corruptionLoadoutNames + delete player.corruptionLoadouts return player }) diff --git a/src/saves/PlayerSchema.ts b/src/saves/PlayerSchema.ts index 0ea193b36..b0f38f86a 100644 --- a/src/saves/PlayerSchema.ts +++ b/src/saves/PlayerSchema.ts @@ -83,16 +83,34 @@ const hepteractCraftSchema = (k: keyof Player['hepteractCrafts']) => }) const optionalCorruptionSchema = z.object({ - viscosity: z.number().optional(), - drought: z.number().optional(), - deflation: z.number().optional(), - extinction: z.number().optional(), - illiteracy: z.number().optional(), - recession: z.number().optional(), - dilation: z.number().optional(), - hyperchallenge: z.number().optional() + viscosity: z.number().optional().default(0), + drought: z.number().optional().default(0), + deflation: z.number().optional().default(0), + extinction: z.number().optional().default(0), + illiteracy: z.number().optional().default(0), + recession: z.number().optional().default(0), + dilation: z.number().optional().default(0), + hyperchallenge: z.number().optional().default(0) }) +export const playerCorruptionSchema = z.object({ + used: optionalCorruptionSchema.transform((value) => { + return new CorruptionLoadout(value) + }), + next: optionalCorruptionSchema.transform((value) => { + console.log(Object.values(value)) + return new CorruptionLoadout(value) + }), + saves: z.record(z.string(), optionalCorruptionSchema).transform((value) => { + return new CorruptionSaves(value) + }), + showStats: z.boolean() +}).default(() => JSON.parse(JSON.stringify(blankSave.corruptions))) + +export const playerCampaignSchema = z.record(z.string(), z.number()).transform((campaigns => { + return new CampaignManager(campaigns) +})).default(() => JSON.parse(JSON.stringify(blankSave.campaigns))) + export const playerSchema = z.object({ firstPlayed: z.string().datetime().optional().default(() => new Date().toISOString()), worlds: z.number().transform((quarks) => new QuarkHandler(quarks)), @@ -532,23 +550,9 @@ export const playerSchema = z.object({ roombaResearchIndex: z.number().default(() => blankSave.roombaResearchIndex), ascStatToggles: z.record(integerStringSchema, z.boolean()).default(() => ({ ...blankSave.ascStatToggles })), - campaigns: z.record(z.string(), z.number()).transform((campaigns => { - return new CampaignManager(campaigns) - })).default(() => JSON.parse(JSON.stringify(blankSave.campaigns))), + campaigns: playerCampaignSchema, - corruptions: z.object({ - used: optionalCorruptionSchema.transform((value) => { - return new CorruptionLoadout(value) - }), - next: optionalCorruptionSchema.transform((value) => { - console.log(Object.values(value)) - return new CorruptionLoadout(value) - }), - saves: z.record(z.string(), optionalCorruptionSchema).transform((value) => { - return new CorruptionSaves(value) - }), - showStats: z.boolean() - }).default(() => JSON.parse(JSON.stringify(blankSave.corruptions))), + corruptions: playerCorruptionSchema, prototypeCorruptions: z.number().array().optional(), usedCorruptions: z.number().array().optional(), diff --git a/src/saves/PlayerUpdateVarSchema.ts b/src/saves/PlayerUpdateVarSchema.ts index 7108dc1e9..57930be41 100644 --- a/src/saves/PlayerUpdateVarSchema.ts +++ b/src/saves/PlayerUpdateVarSchema.ts @@ -1,19 +1,7 @@ import { CorruptionLoadout, type Corruptions, CorruptionSaves } from '../Corruptions' +import { convertArrayToCorruption } from './PlayerJsonSchema' import { playerSchema } from './PlayerSchema' -const convertArrayToCorruption = (array: number[]): Corruptions => { - return { - viscosity: array[2], - drought: array[8], - deflation: array[6], - extinction: array[7], - illiteracy: array[5], - recession: array[9], - dilation: array[3], - hyperchallenge: array[4] - } -} - export const playerUpdateVarSchema = playerSchema.transform((player) => { if (player.usedCorruptions !== undefined) { const corrLoadout = convertArrayToCorruption(player.usedCorruptions) diff --git a/src/types/Synergism.d.ts b/src/types/Synergism.d.ts index 5bca3fa9f..c7047e7db 100644 --- a/src/types/Synergism.d.ts +++ b/src/types/Synergism.d.ts @@ -16,7 +16,7 @@ import type { } from '../StatCache' import type { Tabs } from '../Tabs' import type { Corruptions, CorruptionLoadout } from '../Corruptions' -import { CampaignManager } from '../Campaign' +import type { CampaignManager } from '../Campaign' type ArrayStartingWithNull = [null, ...T[]]