phase 10: schools — Mechanic, Naturalist, Navigator with unlock system and bonuses

Add 3 new schools with real scientific principles (Lever & Moment, Photosynthesis,
Angular Measurement). Data-driven unlock conditions check codex elements, creature
discoveries, and completed runs. Each school provides passive gameplay bonuses
(projectile damage, movement speed, creature aggro range, reaction efficiency)
applied through system multiplier parameters. CradleScene shows all 4 schools with
locked ones grayed out and showing unlock hints. 24 new tests (511 total).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Денис Шкабатур
2026-02-12 18:35:15 +03:00
parent 1b2cc0cd86
commit 0cd995c817
10 changed files with 573 additions and 76 deletions

View File

@@ -45,8 +45,9 @@ import {
import { query } from 'bitecs';
// Run cycle imports
import type { MetaState, SchoolData, RunState } from '../run/types';
import type { MetaState, SchoolData, RunState, ResolvedSchoolBonuses } from '../run/types';
import { RunPhase, RUN_PHASE_NAMES_RU, PHASE_DURATIONS } from '../run/types';
import { getSchoolBonuses } from '../run/meta';
import { createRunState, advancePhase, updateEscalation, recordDiscovery } from '../run/state';
import {
createCrisisState,
@@ -138,6 +139,9 @@ export class GameScene extends Phaser.Scene {
// Biome selection
private biomeId = 'catalytic-wastes';
// School bonuses (resolved from school data)
private schoolBonuses!: ResolvedSchoolBonuses;
init(data: { meta: MetaState; schoolId: string; runId: number; purchasedEffects?: import('../mycelium/types').BonusEffect[]; biomeId?: string }): void {
this.meta = data.meta;
this.runState = createRunState(data.runId, data.schoolId);
@@ -148,6 +152,7 @@ export class GameScene extends Phaser.Scene {
this.memoryFlashTimer = 0;
this.purchasedEffects = data.purchasedEffects ?? [];
this.biomeId = data.biomeId ?? 'catalytic-wastes';
this.schoolBonuses = getSchoolBonuses(data.schoolId);
}
create(): void {
@@ -354,7 +359,7 @@ export class GameScene extends Phaser.Scene {
/** Give the player their school's starting elements */
private giveStartingKit(): void {
const schools = schoolsData as SchoolData[];
const schools = schoolsData as unknown as SchoolData[];
const school = schools.find(s => s.id === this.runState.schoolId);
if (!school) return;
@@ -389,8 +394,8 @@ export class GameScene extends Phaser.Scene {
interact: this.keys.E.isDown,
};
// 3. Player input → velocity
playerInputSystem(this.gameWorld.world, input);
// 3. Player input → velocity (Navigator school gets speed bonus)
playerInputSystem(this.gameWorld.world, input, this.schoolBonuses.movementSpeed);
// 4. Movement (all entities including projectiles)
movementSystem(this.gameWorld.world, delta);
@@ -454,10 +459,11 @@ export class GameScene extends Phaser.Scene {
this.tryLaunchProjectile();
}
// 9a. Creature AI — adjust aggression based on escalation
// 9a. Creature AI — aggro range affected by Naturalist school bonus
aiSystem(
this.gameWorld.world, delta,
this.speciesLookup, this.gameWorld.time.tick,
this.schoolBonuses.creatureAggroRange,
);
// 9b. Creature metabolism (feeding, energy drain)
@@ -486,9 +492,10 @@ export class GameScene extends Phaser.Scene {
}
}
// 9e. Projectile-creature collision
// 9e. Projectile-creature collision (Mechanic school gets damage bonus)
creatureProjectileSystem(
this.gameWorld.world, this.projectileData, this.speciesLookup,
this.schoolBonuses.projectileDamage,
);
// 9f. Creature attacks on player