/** * GameClock — continuous game-time cycle (GTA-style). * * gameTime: 0..1 represents the full 24-hour cycle. * 0.00 / 1.00 → midnight * 0.22 → sunrise * 0.50 → noon * 0.78 → sunset * * dayDurationSeconds controls how fast time passes (default 120s = 2 minutes per game day, * a bit faster than GTA so kids see day → night within one play session). * * Optional jumpToLevel(t01) snaps the clock to a sensible time-of-day for the current grade: * level low → bright morning * level mid → noon → afternoon * level high → sunset → night * After a snap, time keeps progressing forward — so the BG never goes static. */ export class GameClock { constructor({ dayDurationSeconds = 120, startTime = 0.30 } = {}) { this.dayDurationSeconds = dayDurationSeconds; this.gameTime = startTime; // 0..1 } update(dt) { this.gameTime = (this.gameTime + dt / this.dayDurationSeconds) % 1; } /** * Jump time forward so the look matches the current level intensity. * Only nudges forward (no rewind) so the cycle keeps marching. */ jumpToLevel(t01) { // Map level intensity to a "preferred" time-of-day: // t=0 → 0.30 (mid-morning), t=0.5 → 0.55 (mid-afternoon), t=1 → 0.92 (deep night) const target = 0.30 + t01 * 0.62; // Take the shortest forward jump let delta = target - this.gameTime; if (delta < 0) delta += 1; if (delta > 0.02) this.gameTime = target; } // 50/50 split — 10 minutes of day and 10 minutes of night for a 20-minute cycle. // Sunrise is at gameTime 0.25, sunset at 0.75. isDay() { return this.gameTime >= 0.25 && this.gameTime < 0.75; } isNight() { return !this.isDay(); } /** Returns 0..1 fraction of the daylight arc (only valid during day). */ dayPhase() { if (!this.isDay()) return -1; return (this.gameTime - 0.25) / 0.5; // 0.25..0.75 → 0..1 } /** Returns 0..1 fraction of the night arc (wraps over midnight). */ nightPhase() { if (this.isDay()) return -1; const t = this.gameTime >= 0.75 ? this.gameTime - 0.75 : (1 - 0.75) + this.gameTime; return t / 0.5; // 0.75..1.25 → 0..1 } }