@jeremyckahn/farmhand
Version:
A farming game
108 lines (92 loc) • 3.67 kB
text/typescript
import { stageFocusType, toolLevel, toolType } from '../enums.js'
import { STANDARD_VIEW_LIST } from '../constants.js'
import { farmProductsSold } from './farmProductsSold.js'
import { getLevelEntitlements } from './getLevelEntitlements.js'
import { levelAchieved } from './levelAchieved.js'
export const transformStateDataForImport = (
state: farmhand.state
): farmhand.state => {
let sanitizedState: Record<string, unknown> = { ...state }
const rejectedKeys = ['version']
rejectedKeys.forEach(rejectedKey => delete sanitizedState[rejectedKey])
if (sanitizedState.experience === 0) {
sanitizedState.experience = farmProductsSold(sanitizedState.itemsSold || {})
}
if (
sanitizedState.showHomeScreen === false &&
sanitizedState.stageFocus === stageFocusType.HOME
) {
sanitizedState = {
...sanitizedState,
stageFocus: STANDARD_VIEW_LIST[0] as farmhand.stageFocusType,
}
}
// NOTE: This is a mitigation for
// https://github.com/jeremyckahn/farmhand/issues/546. There's no expected
// scenario where a cow would be present in cowBreedingPen but not
// cowInventory, but at least one player's game somehow got into that state.
// This block detects such an invalid state and corrects it.
{
// TODO: Add defensive check safeguards for sanitizedState.cowBreedingPen
// and sanitizedState.cowInventory to prevent TypeError crashes during
// corrupt/legacy state imports.
const {
cowId1,
cowId2,
} = sanitizedState.cowBreedingPen as farmhand.state['cowBreedingPen']
const cowPenIdMap = (sanitizedState.cowInventory as farmhand.cow[]).reduce(
(acc: Record<string, farmhand.cow>, cow: farmhand.cow) => {
acc[cow.id] = cow
return acc
},
{}
)
const isCowInBreedingPenMissingFromInventory = [cowId1, cowId2].some(
cowId => {
return cowId && !(cowId in cowPenIdMap)
}
)
if (isCowInBreedingPenMissingFromInventory) {
// Resets cowBreedingPen state
sanitizedState.cowBreedingPen = {
cowId1: null,
cowId2: null,
daysUntilBirth: -1,
}
}
}
// NOTE: Legacy data trasformation for https://github.com/jeremyckahn/farmhand/issues/387
if (sanitizedState.id) {
sanitizedState.playerId = sanitizedState.id
delete sanitizedState.id
}
// NOTE: Legacy data transformation for saves persisted before the AXE
// tool was added. toolLevels is merged as a whole object on import (see
// useFarmhand.ts), so an old save's toolLevels has no AXE key at all
// rather than an explicit UNAVAILABLE - leaving Toolbelt.tsx unable to
// resolve a tool image for it and crashing on tool.level.toLowerCase().
//
// A save whose player is already past the level that unlocks the axe
// (see levels.ts) never gets another chance to unlock it retroactively -
// processLevelUp only fires on a *new* level-up crossing that threshold,
// not on load - so backfill straight to DEFAULT for those players rather
// than leaving them permanently locked out of a tool they should already
// have.
if (
sanitizedState.toolLevels &&
!(sanitizedState.toolLevels as farmhand.state['toolLevels'])[toolType.AXE]
) {
const hasUnlockedAxe = Boolean(
getLevelEntitlements(
levelAchieved((sanitizedState.experience ?? 0) as number)
).tools[toolType.AXE]
)
sanitizedState.toolLevels = {
...(sanitizedState.toolLevels as farmhand.state['toolLevels']),
[toolType.AXE]: hasUnlockedAxe
? toolLevel.DEFAULT
: toolLevel.UNAVAILABLE,
}
}
return (sanitizedState as unknown) as farmhand.state
}