Advertisement

← tower-of-time-game

src/systems/SoundSystem.ts

5.0 KB Download file
/**
 * SoundSystem - Centralized sound effect management for the game
 *
 * Features:
 * - Multiple simultaneous sound playback
 * - Volume control and muting
 * - Easy sound effect triggering
 * - Configurable sound effects
 */

export interface SoundEffect {
  key: string;
  volume?: number;
  loop?: boolean;
}

export interface SoundConfig {
  masterVolume: number;
  muted: boolean;
  effects: Record<string, SoundEffect>;
}

export class SoundSystem {
  private scene: Phaser.Scene;
  private config: SoundConfig;
  private activeSounds: Phaser.Sound.BaseSound[] = [];

  constructor(scene: Phaser.Scene, config?: Partial<SoundConfig>) {
    this.scene = scene;
    this.config = {
      masterVolume: 0.7,
      muted: false,
      effects: {
        "menu-highlight": {
          key: "menu-highlight",
          volume: 0.5,
          loop: false,
        },
        "menu-select": {
          key: "menu-select",
          volume: 0.8,
          loop: false,
        },
        explosion: {
          key: "explosion",
          volume: 0.7,
          loop: false,
        },
        shoot: {
          key: "shoot",
          volume: 0.4,
          loop: false,
        },
        slow: {
          key: "slow",
          volume: 0.5,
          loop: false,
        },
        blast: {
          key: "blast",
          volume: 0.6,
          loop: false,
        },
        "red-alert": {
          key: "red-alert",
          volume: 0.8,
          loop: false,
        },
      },
      ...config,
    };

    console.log("SoundSystem initialized");
  }

  /**
   * Play a sound effect by name
   */
  public playSound(soundName: string): void {
    const soundConfig = this.config.effects[soundName];

    if (!soundConfig) {
      console.warn(`Sound effect not found: ${soundName}`);
      return;
    }

    if (this.config.muted) {
      console.log(`Sound is muted, not playing: ${soundName}`);
      return;
    }

    this.playSoundEffect(soundConfig);
  }

  /**
   * Play a sound effect directly with config
   */
  public playSoundEffect(soundConfig: SoundEffect): void {
    if (this.config.muted) {
      console.log("Sound is muted, not playing sound effect");
      return;
    }

    try {
      // Calculate final volume
      const finalVolume =
        (soundConfig.volume || 1.0) * this.config.masterVolume;

      // Create and play the sound
      const sound = this.scene.sound.add(soundConfig.key, {
        loop: soundConfig.loop || false,
        volume: finalVolume,
      });

      // Track active sounds for cleanup
      this.activeSounds.push(sound);

      // Clean up when sound completes
      sound.once("complete", () => {
        this.removeActiveSound(sound);
      });

      // Play the sound
      sound.play();

      console.log(
        `✅ Played sound effect: ${soundConfig.key} (volume: ${finalVolume})`
      );
    } catch (error) {
      console.error(`Failed to play sound effect ${soundConfig.key}:`, error);
    }
  }

  /**
   * Stop all currently playing sounds
   */
  public stopAllSounds(): void {
    this.activeSounds.forEach((sound) => {
      if (sound.isPlaying) {
        sound.stop();
      }
    });
    this.activeSounds = [];
    console.log("Stopped all sound effects");
  }

  /**
   * Set master volume for all sound effects
   */
  public setMasterVolume(volume: number): void {
    this.config.masterVolume = Math.max(0, Math.min(1, volume));
    console.log(`Sound master volume set to: ${this.config.masterVolume}`);
  }

  /**
   * Get current master volume
   */
  public getMasterVolume(): number {
    return this.config.masterVolume;
  }

  /**
   * Mute or unmute all sound effects
   */
  public setMuted(muted: boolean): void {
    this.config.muted = muted;
    if (muted) {
      this.stopAllSounds();
    }
    console.log(`Sound effects ${muted ? "muted" : "unmuted"}`);
  }

  /**
   * Check if sound effects are muted
   */
  public isMuted(): boolean {
    return this.config.muted;
  }

  /**
   * Add a new sound effect configuration
   */
  public addSoundEffect(name: string, soundConfig: SoundEffect): void {
    this.config.effects[name] = soundConfig;
    console.log(`Added sound effect: ${name}`);
  }

  /**
   * Remove a sound effect configuration
   */
  public removeSoundEffect(name: string): void {
    delete this.config.effects[name];
    console.log(`Removed sound effect: ${name}`);
  }

  /**
   * Get all available sound effect names
   */
  public getAvailableSounds(): string[] {
    return Object.keys(this.config.effects);
  }

  /**
   * Clean up and destroy the sound system
   */
  public destroy(): void {
    this.stopAllSounds();
    console.log("SoundSystem destroyed");
  }

  /**
   * Remove a sound from the active sounds list
   */
  private removeActiveSound(sound: Phaser.Sound.BaseSound): void {
    const index = this.activeSounds.indexOf(sound);
    if (index !== -1) {
      this.activeSounds.splice(index, 1);
    }
  }
}