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>
This commit is contained in:
101
src/run/meta.ts
Normal file
101
src/run/meta.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Meta-Progression — persists between runs.
|
||||
*
|
||||
* Manages the Codex (permanent knowledge), spores (currency),
|
||||
* unlocked schools, and run history.
|
||||
*/
|
||||
|
||||
import type { MetaState, CodexEntry, RunSummary } from './types';
|
||||
import type { RunState } from './types';
|
||||
import { calculateSpores, countDiscoveries } from './state';
|
||||
|
||||
/** Create a fresh meta state (first time playing) */
|
||||
export function createMetaState(): MetaState {
|
||||
return {
|
||||
spores: 0,
|
||||
codex: [],
|
||||
totalRuns: 0,
|
||||
totalDeaths: 0,
|
||||
unlockedSchools: ['alchemist'],
|
||||
bestRunTime: 0,
|
||||
bestRunDiscoveries: 0,
|
||||
runHistory: [],
|
||||
};
|
||||
}
|
||||
|
||||
/** Apply run results to meta state after a run ends (death or completion) */
|
||||
export function applyRunResults(meta: MetaState, run: RunState): void {
|
||||
// 1. Calculate and add spores
|
||||
const sporesEarned = calculateSpores(run);
|
||||
meta.spores += sporesEarned;
|
||||
|
||||
// 2. Add new codex entries (skip duplicates)
|
||||
const existingIds = new Set(meta.codex.map(e => `${e.type}:${e.id}`));
|
||||
|
||||
for (const elemId of run.discoveries.elements) {
|
||||
const key = `element:${elemId}`;
|
||||
if (!existingIds.has(key)) {
|
||||
meta.codex.push({ id: elemId, type: 'element', discoveredOnRun: run.runId });
|
||||
}
|
||||
}
|
||||
for (const reactionId of run.discoveries.reactions) {
|
||||
const key = `reaction:${reactionId}`;
|
||||
if (!existingIds.has(key)) {
|
||||
meta.codex.push({ id: reactionId, type: 'reaction', discoveredOnRun: run.runId });
|
||||
}
|
||||
}
|
||||
for (const compoundId of run.discoveries.compounds) {
|
||||
const key = `compound:${compoundId}`;
|
||||
if (!existingIds.has(key)) {
|
||||
meta.codex.push({ id: compoundId, type: 'compound', discoveredOnRun: run.runId });
|
||||
}
|
||||
}
|
||||
for (const creatureId of run.discoveries.creatures) {
|
||||
const key = `creature:${creatureId}`;
|
||||
if (!existingIds.has(key)) {
|
||||
meta.codex.push({ id: creatureId, type: 'creature', discoveredOnRun: run.runId });
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Update stats
|
||||
meta.totalRuns += 1;
|
||||
meta.totalDeaths += 1; // every run ends in death (for now)
|
||||
|
||||
const discoveryCount = countDiscoveries(run);
|
||||
if (run.elapsed > meta.bestRunTime) {
|
||||
meta.bestRunTime = run.elapsed;
|
||||
}
|
||||
if (discoveryCount > meta.bestRunDiscoveries) {
|
||||
meta.bestRunDiscoveries = discoveryCount;
|
||||
}
|
||||
|
||||
// 4. Add to run history
|
||||
const summary: RunSummary = {
|
||||
runId: run.runId,
|
||||
schoolId: run.schoolId,
|
||||
duration: run.elapsed,
|
||||
phase: run.phase,
|
||||
discoveries: discoveryCount,
|
||||
sporesEarned,
|
||||
crisisResolved: run.crisisResolved,
|
||||
};
|
||||
meta.runHistory.push(summary);
|
||||
}
|
||||
|
||||
/** Check if a school is unlocked */
|
||||
export function isSchoolUnlocked(meta: MetaState, schoolId: string): boolean {
|
||||
return meta.unlockedSchools.includes(schoolId);
|
||||
}
|
||||
|
||||
/** Get codex entries filtered by type */
|
||||
export function getCodexEntries(
|
||||
meta: MetaState,
|
||||
type: CodexEntry['type'],
|
||||
): CodexEntry[] {
|
||||
return meta.codex.filter(e => e.type === type);
|
||||
}
|
||||
|
||||
/** Get total codex entry count */
|
||||
export function getCodexCount(meta: MetaState): number {
|
||||
return meta.codex.length;
|
||||
}
|
||||
Reference in New Issue
Block a user