1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-20 11:30:19 +08:00
parent 50759f3c1e
commit 8a7bd90458
85 changed files with 7290 additions and 1903 deletions

View File

@@ -2,6 +2,7 @@ import {
buildInitialNpcState,
createNpcBattleMonster,
} from '../data/npcInteractions';
import { normalizePlayerProgressionState } from '../data/playerProgression';
import type {
Encounter,
GameState,
@@ -18,9 +19,7 @@ import type {
SnapshotState,
} from './runtimeSnapshotTypes';
function normalizeBottomTab(
bottomTab: string | null | undefined,
): BottomTab {
function normalizeBottomTab(bottomTab: string | null | undefined): BottomTab {
return bottomTab === 'character' || bottomTab === 'inventory'
? bottomTab
: 'adventure';
@@ -106,6 +105,8 @@ function normalizeRuntimeBattleEncounter(
typeof encounter.npcAvatar === 'string' ? encounter.npcAvatar : '',
context: typeof encounter.context === 'string' ? encounter.context : '',
hostile: true,
levelProfile: encounter.levelProfile,
experienceReward: encounter.experienceReward,
} satisfies Encounter;
}
@@ -126,9 +127,7 @@ function resolveRuntimeNpcBattleState(
}
const npcStateKey =
gameState.currentBattleNpcId ??
encounter.id ??
encounter.npcName;
gameState.currentBattleNpcId ?? encounter.id ?? encounter.npcName;
const npcState =
gameState.npcStates[npcStateKey] ??
buildInitialNpcState(
@@ -161,9 +160,13 @@ function hydrateRuntimeNpcBattleMonster(params: {
);
const candidate = params.hostileNpc as Partial<SceneHostileNpc>;
const xMeters =
typeof candidate.xMeters === 'number' ? candidate.xMeters : template.xMeters;
typeof candidate.xMeters === 'number'
? candidate.xMeters
: template.xMeters;
const yOffset =
typeof candidate.yOffset === 'number' ? candidate.yOffset : template.yOffset;
typeof candidate.yOffset === 'number'
? candidate.yOffset
: template.yOffset;
return {
...template,
@@ -198,6 +201,11 @@ function hydrateRuntimeNpcBattleMonster(params: {
: template.attackRange,
speed:
typeof candidate.speed === 'number' ? candidate.speed : template.speed,
levelProfile: candidate.levelProfile ?? template.levelProfile,
experienceReward:
typeof candidate.experienceReward === 'number'
? candidate.experienceReward
: template.experienceReward,
encounter: {
...template.encounter,
xMeters,
@@ -263,6 +271,9 @@ export function normalizeSavedGameState(gameState: GameState) {
return hydrateRuntimeNpcBattleGameState({
...hydratableState,
playerProgression: normalizePlayerProgressionState(
hydratableState.playerProgression ?? null,
),
playerMaxHp,
playerHp: Math.min(hydratableState.playerHp, playerMaxHp),
playerMaxMana,
@@ -305,14 +316,19 @@ export function isHydratedSnapshotState(
(gameState.runtimeSessionId === null ||
typeof gameState.runtimeSessionId === 'string') &&
(!gameState.playerCharacter ||
Boolean(gameState.playerEquipment && typeof gameState.playerEquipment === 'object')),
Boolean(
gameState.playerEquipment &&
typeof gameState.playerEquipment === 'object',
)),
);
}
export function rehydrateSavedSnapshot<T extends HydratedSnapshotState>(
snapshot: T,
): T {
const hydratedGameState = hydrateRuntimeNpcBattleGameState(snapshot.gameState);
const hydratedGameState = hydrateRuntimeNpcBattleGameState(
snapshot.gameState,
);
if (hydratedGameState === snapshot.gameState) {
return snapshot;