class AudioState(msgspec.Struct): music: MusicState sfx: SfxState music_volume: float = 0.5 sfx_volume: float = 0.5def update_audio(audio: AudioState, dt: float) -> None: """Update audio streams.""" # Update music stream if audio.music.theme_music is not None: rl.update_music_stream(audio.music.theme_music) if audio.music.game_tune_started: current_tune = audio.music.game_tunes[ audio.music.current_game_tune_index ] rl.update_music_stream(current_tune)def trigger_game_tune( audio: AudioState, rand: Callable[[], int] | None = None,) -> str | None: """Start game music (first hit in Survival).""" if audio.music.game_tune_started: return None # Stop theme music if audio.music.theme_music is not None: rl.stop_music_stream(audio.music.theme_music) # Pick random game tune if rand is not None: index = rand() % len(audio.music.game_tunes) else: index = random.randrange(len(audio.music.game_tunes)) audio.music.current_game_tune_index = index # Start game tune tune = audio.music.game_tunes[index] rl.play_music_stream(tune) rl.set_music_volume(tune, audio.music_volume) audio.music.game_tune_started = True return f"game{index + 1}"
def play_hit_sfx( self, hits: list[ProjectileHit], *, game_mode: GameMode, rand: Callable[[], int],) -> None: """Play hit sound effects.""" if self.audio is None or not hits: return # Cap at 4 hit SFX per frame end = min(len(hits), 4) for idx in range(end): # Trigger game tune on first hit (Survival only) if game_mode == GameMode.SURVIVAL: self.trigger_game_tune() # Play hit SFX type_id = hits[idx].type_id sfx_key = self._hit_sfx_for_type(type_id, rand=rand) self.play_sfx(sfx_key)def play_death_sfx( self, deaths: Sequence[CreatureDeath], *, rand: Callable[[], int],) -> None: """Play creature death sounds.""" if self.audio is None or not deaths: return # Cap at 5 death SFX per frame for idx in range(min(len(deaths), 5)): death = deaths[idx] variants = CREATURE_DEATH_SFX[death.type_id] sfx_key = variants[rand() % len(variants)] self.play_sfx(sfx_key)