import { System } from '../core/System.ts'; import { SystemName, ComponentType, GameState } from '../core/Constants.ts'; import type { Entity } from '../core/Entity.ts'; import type { Engine } from '../core/Engine.ts'; import type { Music } from '../components/Music.ts'; import type { MenuSystem } from './MenuSystem.ts'; /** * System responsible for managing background music playback. */ export class MusicSystem extends System { private audioContext: AudioContext | null; private wasPaused: boolean; constructor() { super(SystemName.MUSIC); this.requiredComponents = [ComponentType.MUSIC]; this.priority = 5; this.audioContext = null; this.wasPaused = false; } /** * Initialize the audio context when system is added to engine. */ init(engine: Engine): void { super.init(engine); } /** * Process music entities - ensures audio context exists and handles pause/resume. */ process(_deltaTime: number, entities: Entity[]): void { const menuSystem = this.engine.systems.find((s) => s.name === SystemName.MENU) as | MenuSystem | undefined; const gameState = menuSystem ? menuSystem.getGameState() : GameState.PLAYING; const isPaused = gameState === GameState.PAUSED; entities.forEach((entity) => { const music = entity.getComponent(ComponentType.MUSIC); if (!music) return; if (!this.audioContext) { this.audioContext = new AudioContext(); } if (isPaused && !this.wasPaused) { music.pause(); this.wasPaused = true; } else if (!isPaused && this.wasPaused) { music.resume(); this.wasPaused = false; } }); } /** * Get or create the shared audio context. */ getAudioContext(): AudioContext { if (!this.audioContext) { this.audioContext = new AudioContext(); } return this.audioContext; } /** * Resume audio context (required after user interaction). */ resumeAudioContext(): void { if (this.audioContext && this.audioContext.state === 'suspended') { this.audioContext.resume(); } } }