kgames

KGames — free keyboard games for kids
git clone https://www.keyboard.games/code/kgames.git
Log | Files | Refs | README | LICENSE

commit ed650a88a4639f1b2b544b431f5f6e21203abba1
parent c4578d57ea48bb50f909851757590f8bf6e9eb0f
Author: Kyle Barlow <kb@kylebarlow.com>
Date:   Wed, 22 Apr 2026 11:20:37 -0700

fix(fire-truck): refactor animations to use Phaser timers

Diffstat:
Mgames/fire-truck/game.js | 84++++++++++++++++++++++++++++++-------------------------------------------------
1 file changed, 32 insertions(+), 52 deletions(-)

diff --git a/games/fire-truck/game.js b/games/fire-truck/game.js @@ -99,9 +99,10 @@ class FireTruckScene extends Phaser.Scene { this.sirenOsc1 = null; this.sirenOsc2 = null; this.sirenGain = null; - this.sirenInterval = null; + this.sirenTimer = null; + this.sirenHi = true; this.lightPhase = 0; - this.lightInterval = null; + this.lightTimer = null; } // ── Preload ──────────────────────────────────────────────────────────────── @@ -429,41 +430,6 @@ class FireTruckScene extends Phaser.Scene { gfx.fillStyle(bldg.fill); - // Draw facade as stacked horizontal slices for correct fill - // Use polygon approach: 4 corners of the facade face - const topY = Math.min(yNearTop, yFarTop, 0); - const bottomY = Math.max(yNearBottom, yFarBottom, horizY); - - if (bottomY <= topY) continue; - - // Interpolate x-left/right at each screen row between horizY and bottomY - // Near edge: at y approaching horizY, x → horizon vanishing point; at yNearBottom x = xNearBottom - // Far edge: similar but for far Z - // Simpler: just draw a filled trapezoid using fillPoints - if (side < 0) { - // Left side: facade from sidewalk edge to screen left - // Near wall x = xNearBottom (at bottom) to xNearTop (at top) - // Fill screen left (x=0) to wall face - const pts = [ - { x: 0, y: topY }, - { x: xFarTop, y: topY }, - { x: xFarBottom, y: horizY }, - { x: xNearBottom, y: yNearBottom }, - { x: 0, y: yNearBottom }, - ]; - gfx.fillPoints(pts.map(p => new Phaser.Geom.Point(p.x, p.y)), true); - } else { - // Right side - const pts = [ - { x: xFarTop, y: topY }, - { x: W, y: topY }, - { x: W, y: yNearBottom }, - { x: xNearBottom, y: yNearBottom }, - { x: xFarBottom, y: horizY }, - ]; - gfx.fillPoints(pts.map(p => new Phaser.Geom.Point(p.x, p.y)), true); - } - // Windows — draw a grid on the facade for the nearest segment if (pNearB.visible && pNearB.scale > 0.015) { this._drawWindows(gfx, bldg.win, side, xNearBottom, yNearBottom, pNearB.scale, H); @@ -830,13 +796,20 @@ class FireTruckScene extends Phaser.Scene { if (this.lightsOn) { this.lightBarL.setVisible(true); this.lightBarR.setVisible(true); - this.lightInterval = setInterval(() => { - this.lightPhase = 1 - this.lightPhase; - this.lightBarL.setFillStyle(this.lightPhase ? 0xE63946 : 0x220000); - this.lightBarR.setFillStyle(this.lightPhase ? 0x220022 : 0x1D7CF2); - }, 280); + this.lightTimer = this.time.addEvent({ + delay: 280, + callback: () => { + this.lightPhase = 1 - this.lightPhase; + this.lightBarL.setFillStyle(this.lightPhase ? 0xE63946 : 0x220000); + this.lightBarR.setFillStyle(this.lightPhase ? 0x220022 : 0x1D7CF2); + }, + loop: true + }); } else { - clearInterval(this.lightInterval); + if (this.lightTimer) { + this.lightTimer.remove(); + this.lightTimer = null; + } this.lightBarL.setVisible(false); this.lightBarR.setVisible(false); } @@ -861,19 +834,26 @@ class FireTruckScene extends Phaser.Scene { this.sirenOsc1.frequency.value = 770; this.sirenOsc1.connect(this.sirenGain); this.sirenOsc1.start(); this.sirenOsc2 = ctx.createOscillator(); this.sirenOsc2.type = 'sawtooth'; this.sirenOsc2.frequency.value = 570; this.sirenOsc2.connect(this.sirenGain); this.sirenOsc2.start(); - let hi = true; - this.sirenInterval = setInterval(() => { - if (!this.sirenOsc1) return; - const t = this.getAudioCtx().currentTime; - this.sirenOsc1.frequency.linearRampToValueAtTime(hi ? 960 : 770, t + 0.45); - this.sirenOsc2.frequency.linearRampToValueAtTime(hi ? 700 : 570, t + 0.45); - hi = !hi; - }, 450); + this.sirenHi = true; + this.sirenTimer = this.time.addEvent({ + delay: 450, + callback: () => { + if (!this.sirenOsc1) return; + const t = this.getAudioCtx().currentTime; + this.sirenOsc1.frequency.linearRampToValueAtTime(this.sirenHi ? 960 : 770, t + 0.45); + this.sirenOsc2.frequency.linearRampToValueAtTime(this.sirenHi ? 700 : 570, t + 0.45); + this.sirenHi = !this.sirenHi; + }, + loop: true + }); } catch (_) {} } _stopSiren() { - clearInterval(this.sirenInterval); + if (this.sirenTimer) { + this.sirenTimer.remove(); + this.sirenTimer = null; + } try { if (this.sirenOsc1) { this.sirenOsc1.stop(); this.sirenOsc1 = null; } if (this.sirenOsc2) { this.sirenOsc2.stop(); this.sirenOsc2 = null; }