feat: CradleScene + UIScene + GameScene cycle integration

Scene integration for the Great Cycle system:
- CradleScene: shows "Великий Цикл N: Тема | Ран X/7", narrative quote
- UIScene: cycle info bar and run phase display below health
- GameScene: world trace spawning (ruins/markers from past runs),
  trace glow rendering, death position recording, cycle info to registry,
  biomeId + worldSeed passed to RunState
- Scene flow: Fractal → RenewalScene (on 7th run) → Cradle

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Денис Шкабатур
2026-02-12 18:51:19 +03:00
parent 91d4e4d730
commit d9213b6be0
3 changed files with 115 additions and 3 deletions

View File

@@ -72,6 +72,10 @@ import { createMycosisState, updateMycosis, getMycosisVisuals } from '../myceliu
import type { FungalNodeInfo, MycosisState, MemoryFlash } from '../mycelium/types';
import { FUNGAL_NODE_CONFIG, MYCOSIS_CONFIG } from '../mycelium/types';
// World traces (Great Cycle)
import { spawnWorldTraces, updateTraceGlow, type WorldTraceInfo } from '../world/traces';
import { CYCLE_THEME_NAMES_RU } from '../run/types';
export class GameScene extends Phaser.Scene {
private gameWorld!: GameWorld;
private bridge!: PhaserBridge;
@@ -129,6 +133,10 @@ export class GameScene extends Phaser.Scene {
private fungalNodeGlowGraphics!: Phaser.GameObjects.Graphics;
private hasDepositedThisRun = false;
// World traces from past runs
private worldTraceData: WorldTraceInfo[] = [];
private worldTraceGraphics!: Phaser.GameObjects.Graphics;
constructor() {
super({ key: 'GameScene' });
}
@@ -144,14 +152,14 @@ export class GameScene extends Phaser.Scene {
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);
this.biomeId = data.biomeId ?? 'catalytic-wastes';
this.runState = createRunState(data.runId, data.schoolId, this.biomeId);
this.crisisState = null;
this.playerDead = false;
this.mycosisState = createMycosisState();
this.hasDepositedThisRun = false;
this.memoryFlashTimer = 0;
this.purchasedEffects = data.purchasedEffects ?? [];
this.biomeId = data.biomeId ?? 'catalytic-wastes';
this.schoolBonuses = getSchoolBonuses(data.schoolId);
}
@@ -164,6 +172,7 @@ export class GameScene extends Phaser.Scene {
// 2. Generate world — use selected biome
const biome = (biomeDataArray as BiomeData[]).find(b => b.id === this.biomeId) ?? biomeDataArray[0] as BiomeData;
this.worldSeed = Date.now() % 1000000;
this.runState.worldSeed = this.worldSeed;
const worldData = generateWorld(biome, this.worldSeed);
// 3. Create tilemap
@@ -184,6 +193,13 @@ export class GameScene extends Phaser.Scene {
this.gameWorld.world, worldData.grid, biome, this.worldSeed,
);
// 5c. Spawn world traces from past runs (Great Cycle)
this.worldTraceData = spawnWorldTraces(
this.gameWorld.world, this.meta.greatCycle, this.biomeId, biome,
);
this.worldTraceGraphics = this.add.graphics();
this.worldTraceGraphics.setDepth(4); // above tiles, below entities
// 6. Initialize creature systems — filter by biome
const allSpecies = speciesDataArray as SpeciesData[];
const biomeSpecies = allSpecies.filter(s => s.biome === biome.id);
@@ -581,6 +597,12 @@ export class GameScene extends Phaser.Scene {
}
this.registry.set('invCounts', counts);
// 14. Push cycle info to registry for UIScene
this.registry.set('cycleNumber', this.meta.greatCycle.cycleNumber);
this.registry.set('runInCycle', this.meta.greatCycle.runInCycle);
this.registry.set('cycleThemeRu', CYCLE_THEME_NAMES_RU[this.meta.greatCycle.theme]);
this.registry.set('runPhaseRu', RUN_PHASE_NAMES_RU[this.runState.phase]);
// 15. Creature observation for UIScene
const nearbyCreatures = getObservableCreatures(this.gameWorld.world);
if (nearbyCreatures.length > 0) {
@@ -698,6 +720,25 @@ export class GameScene extends Phaser.Scene {
);
}
// Render world trace glows (past run markers)
this.worldTraceGraphics.clear();
updateTraceGlow(this.worldTraceData, delta);
for (const trace of this.worldTraceData) {
const eid = trace.eid;
const phase = (this.worldTraceGraphics as unknown as { _phase?: number })._phase ?? 0;
const glowPhase = phase + Position.x[eid] * 0.01; // offset per position
const pulse = 0.3 + 0.7 * Math.sin(glowPhase + this.runState.elapsed * 0.001);
const color = trace.traceType === 'death_site' ? 0xaa3333 : 0x4488aa;
const alpha = 0.1 + 0.1 * pulse;
const radius = 8 * (0.7 + 0.3 * pulse);
this.worldTraceGraphics.fillStyle(color, alpha);
this.worldTraceGraphics.fillCircle(Position.x[eid], Position.y[eid], radius);
// Inner core
this.worldTraceGraphics.fillStyle(color, alpha * 2);
this.worldTraceGraphics.fillCircle(Position.x[eid], Position.y[eid], radius * 0.4);
}
// Fade memory flash text
if (this.memoryFlashTimer > 0) {
this.memoryFlashTimer -= delta;
@@ -797,6 +838,14 @@ export class GameScene extends Phaser.Scene {
this.playerDead = true;
this.runState.alive = false;
// Record death position for great cycle traces
const px = Position.x[this.playerEid] ?? 0;
const py = Position.y[this.playerEid] ?? 0;
this.runState.deathPosition = {
tileX: Math.floor(px / this.tileSize),
tileY: Math.floor(py / this.tileSize),
};
// Auto-deposit all discoveries into Mycelium on death
if (!this.hasDepositedThisRun) {
depositKnowledge(this.meta.mycelium, this.runState);