Files
synthesis/src/run/state.ts
Денис Шкабатур 5b7dbb4df3 phase 6: school data, run state, meta-progression, crisis system
- Add school data (Alchemist: H, O, C, Na, S, Fe starting kit)
- Add run cycle types: phases, escalation, crisis, body composition
- Implement run state management (create, advance phase, discoveries, spores)
- Implement meta-progression (codex, spore accumulation, run history)
- Implement crisis system (Chemical Plague with neutralization)
- Add IndexedDB persistence for meta state
- 42 new tests (335 total)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 15:10:05 +03:00

94 lines
2.8 KiB
TypeScript

/**
* Run State Management — creates and updates the state of a single run.
*
* A "run" is one cycle: Awakening → Exploration → Escalation → Crisis → Resolution.
* Each run tracks discoveries, escalation level, and crisis status.
*/
import {
type RunState,
type RunDiscoveries,
RunPhase,
ESCALATION_RATE,
SPORE_REWARDS,
} from './types';
/** Create a fresh run state for a new run */
export function createRunState(runId: number, schoolId: string): RunState {
return {
runId,
schoolId,
phase: RunPhase.Awakening,
phaseTimer: 0,
elapsed: 0,
escalation: 0,
crisisActive: false,
crisisResolved: false,
discoveries: {
elements: new Set<string>(),
reactions: new Set<string>(),
compounds: new Set<string>(),
creatures: new Set<string>(),
},
alive: true,
};
}
/** Advance to the next phase. Does nothing if already at Resolution. */
export function advancePhase(state: RunState): void {
if (state.phase < RunPhase.Resolution) {
state.phase = (state.phase + 1) as RunPhase;
state.phaseTimer = 0;
}
}
/** Update escalation level based on elapsed delta (ms). Only active during Escalation and Crisis. */
export function updateEscalation(state: RunState, deltaMs: number): void {
if (state.phase < RunPhase.Escalation) return;
if (state.phase > RunPhase.Crisis) return;
const deltaSec = deltaMs / 1000;
state.escalation = Math.min(1.0, state.escalation + ESCALATION_RATE * deltaSec);
}
/** Discovery type names used in API (singular) */
export type DiscoveryType = 'element' | 'reaction' | 'compound' | 'creature';
/** Map singular type to plural key in RunDiscoveries */
const DISCOVERY_KEY: Record<DiscoveryType, keyof RunDiscoveries> = {
element: 'elements',
reaction: 'reactions',
compound: 'compounds',
creature: 'creatures',
};
/** Record a discovery during the current run */
export function recordDiscovery(
state: RunState,
type: DiscoveryType,
id: string,
): void {
state.discoveries[DISCOVERY_KEY[type]].add(id);
}
/** Count total unique discoveries in a run */
export function countDiscoveries(state: RunState): number {
return (
state.discoveries.elements.size +
state.discoveries.reactions.size +
state.discoveries.compounds.size +
state.discoveries.creatures.size
);
}
/** Calculate spores earned from a run */
export function calculateSpores(state: RunState): number {
let total = 0;
total += state.discoveries.elements.size * SPORE_REWARDS.element;
total += state.discoveries.reactions.size * SPORE_REWARDS.reaction;
total += state.discoveries.compounds.size * SPORE_REWARDS.compound;
total += state.discoveries.creatures.size * SPORE_REWARDS.creature;
if (state.crisisResolved) total += SPORE_REWARDS.crisisResolved;
return total;
}