Attempt to fix traceback on game exit with some older games.

This commit is contained in:
Storm Dragon
2025-03-14 23:10:14 -04:00
parent 2ad22ff1ae
commit be6dfdf53a
9 changed files with 483 additions and 457 deletions

362
sound.py
View File

@ -17,21 +17,21 @@ from os.path import isfile, join
from .services import VolumeService
# Global instance for backward compatibility
volume_service = VolumeService.get_instance()
volumeService = VolumeService.get_instance()
class Sound:
"""Handles sound loading and playback."""
def __init__(self, sound_dir="sounds/", volume_service=None):
def __init__(self, soundDir="sounds/", volumeService=None):
"""Initialize sound system.
Args:
sound_dir (str): Directory containing sound files (default: "sounds/")
volume_service (VolumeService): Volume service (default: global instance)
soundDir (str): Directory containing sound files (default: "sounds/")
volumeService (VolumeService): Volume service (default: global instance)
"""
self.sound_dir = sound_dir
self.soundDir = soundDir
self.sounds = {}
self.volume_service = volume_service or VolumeService.get_instance()
self.volumeService = volumeService or VolumeService.get_instance()
# Initialize pygame mixer if not already done
if not pygame.mixer.get_init():
@ -46,13 +46,13 @@ class Sound:
def load_sounds(self):
"""Load all sound files from the sound directory."""
try:
sound_files = [f for f in listdir(self.sound_dir)
if isfile(join(self.sound_dir, f))
soundFiles = [f for f in listdir(self.soundDir)
if isfile(join(self.soundDir, f))
and (f.split('.')[1].lower() in ["ogg", "wav"])]
# Create dictionary of sound objects
for f in sound_files:
self.sounds[f.split('.')[0]] = pygame.mixer.Sound(join(self.sound_dir, f))
for f in soundFiles:
self.sounds[f.split('.')[0]] = pygame.mixer.Sound(join(self.soundDir, f))
except Exception as e:
print(f"Error loading sounds: {e}")
@ -70,109 +70,109 @@ class Sound:
"""
return self.sounds
def play_bgm(self, music_file):
def play_bgm(self, musicFile):
"""Play background music with proper volume settings.
Args:
music_file (str): Path to the music file to play
musicFile (str): Path to the music file to play
"""
try:
pygame.mixer.music.stop()
pygame.mixer.music.load(music_file)
pygame.mixer.music.set_volume(self.volume_service.get_bgm_volume())
pygame.mixer.music.load(musicFile)
pygame.mixer.music.set_volume(self.volumeService.get_bgm_volume())
pygame.mixer.music.play(-1) # Loop indefinitely
except Exception as e:
pass
def play_sound(self, sound_name, volume=1.0):
def play_sound(self, soundName, volume=1.0):
"""Play a sound with current volume settings applied.
Args:
sound_name (str): Name of sound to play
soundName (str): Name of sound to play
volume (float): Base volume for the sound (0.0-1.0, default: 1.0)
Returns:
pygame.mixer.Channel: The channel the sound is playing on
"""
if sound_name not in self.sounds:
if soundName not in self.sounds:
return None
sound = self.sounds[sound_name]
sound = self.sounds[soundName]
channel = sound.play()
if channel:
channel.set_volume(volume * self.volume_service.get_sfx_volume())
channel.set_volume(volume * self.volumeService.get_sfx_volume())
return channel
def calculate_volume_and_pan(self, player_pos, obj_pos, max_distance=12):
def calculate_volume_and_pan(self, playerPos, objPos, maxDistance=12):
"""Calculate volume and stereo panning based on relative positions.
Args:
player_pos (float): Player's position on x-axis
obj_pos (float): Object's position on x-axis
max_distance (float): Maximum audible distance (default: 12)
playerPos (float): Player's position on x-axis
objPos (float): Object's position on x-axis
maxDistance (float): Maximum audible distance (default: 12)
Returns:
tuple: (volume, left_vol, right_vol) values between 0 and 1
"""
distance = abs(player_pos - obj_pos)
distance = abs(playerPos - objPos)
if distance > max_distance:
if distance > maxDistance:
return 0, 0, 0 # No sound if out of range
# Calculate volume (non-linear scaling for more noticeable changes)
# Apply masterVolume as the maximum possible volume
volume = (((max_distance - distance) / max_distance) ** 1.5) * self.volume_service.master_volume
volume = (((maxDistance - distance) / maxDistance) ** 1.5) * self.volumeService.masterVolume
# Determine left/right based on relative position
if player_pos < obj_pos:
if playerPos < objPos:
# Object is to the right
left = max(0, 1 - (obj_pos - player_pos) / max_distance)
left = max(0, 1 - (objPos - playerPos) / maxDistance)
right = 1
elif player_pos > obj_pos:
elif playerPos > objPos:
# Object is to the left
left = 1
right = max(0, 1 - (player_pos - obj_pos) / max_distance)
right = max(0, 1 - (playerPos - objPos) / maxDistance)
else:
# Player is on the object
left = right = 1
return volume, left, right
def obj_play(self, sound_name, player_pos, obj_pos, loop=True):
def obj_play(self, soundName, playerPos, objPos, loop=True):
"""Play a sound with positional audio.
Args:
sound_name (str): Name of sound to play
player_pos (float): Player's position for audio panning
obj_pos (float): Object's position for audio panning
soundName (str): Name of sound to play
playerPos (float): Player's position for audio panning
objPos (float): Object's position for audio panning
loop (bool): Whether to loop the sound (default: True)
Returns:
pygame.mixer.Channel: Sound channel object, or None if out of range
"""
if sound_name not in self.sounds:
if soundName not in self.sounds:
return None
volume, left, right = self.calculate_volume_and_pan(player_pos, obj_pos)
volume, left, right = self.calculate_volume_and_pan(playerPos, objPos)
if volume == 0:
return None # Don't play if out of range
# Play the sound on a new channel
channel = self.sounds[sound_name].play(-1 if loop else 0)
channel = self.sounds[soundName].play(-1 if loop else 0)
if channel:
channel.set_volume(
volume * left * self.volume_service.sfx_volume,
volume * right * self.volume_service.sfx_volume
volume * left * self.volumeService.sfxVolume,
volume * right * self.volumeService.sfxVolume
)
return channel
def obj_update(self, channel, player_pos, obj_pos):
def obj_update(self, channel, playerPos, objPos):
"""Update positional audio for a playing sound.
Args:
channel: Sound channel to update
player_pos (float): New player position
obj_pos (float): New object position
playerPos (float): New player position
objPos (float): New object position
Returns:
pygame.mixer.Channel: Updated channel, or None if sound should stop
@ -180,15 +180,15 @@ class Sound:
if channel is None:
return None
volume, left, right = self.calculate_volume_and_pan(player_pos, obj_pos)
volume, left, right = self.calculate_volume_and_pan(playerPos, objPos)
if volume == 0:
channel.stop()
return None
# Apply the volume and pan
channel.set_volume(
volume * left * self.volume_service.sfx_volume,
volume * right * self.volume_service.sfx_volume
volume * left * self.volumeService.sfxVolume,
volume * right * self.volumeService.sfxVolume
)
return channel
@ -207,19 +207,19 @@ class Sound:
except:
return channel
def play_ambiance(self, sound_names, probability, random_location=False):
def play_ambiance(self, soundNames, probability, randomLocation=False):
"""Play random ambient sounds with optional positional audio.
Args:
sound_names (list): List of possible sound names to choose from
soundNames (list): List of possible sound names to choose from
probability (int): Chance to play (1-100)
random_location (bool): Whether to randomize stereo position
randomLocation (bool): Whether to randomize stereo position
Returns:
pygame.mixer.Channel: Sound channel if played, None otherwise
"""
# Check if any of the sounds in the list is already playing
for sound_name in sound_names:
for soundName in soundNames:
if pygame.mixer.find_channel(True) and pygame.mixer.find_channel(True).get_busy():
return None
@ -227,124 +227,124 @@ class Sound:
return None
# Choose a random sound from the list
ambiance_sound = random.choice(sound_names)
if ambiance_sound not in self.sounds:
ambianceSound = random.choice(soundNames)
if ambianceSound not in self.sounds:
return None
channel = self.sounds[ambiance_sound].play()
channel = self.sounds[ambianceSound].play()
if random_location and channel:
left_volume = random.random() * self.volume_service.get_sfx_volume()
right_volume = random.random() * self.volume_service.get_sfx_volume()
channel.set_volume(left_volume, right_volume)
if randomLocation and channel:
leftVolume = random.random() * self.volumeService.get_sfx_volume()
rightVolume = random.random() * self.volumeService.get_sfx_volume()
channel.set_volume(leftVolume, rightVolume)
return channel
def play_random(self, sound_prefix, pause=False, interrupt=False):
def play_random(self, soundPrefix, pause=False, interrupt=False):
"""Play a random variation of a sound.
Args:
sound_prefix (str): Base name of sound (will match all starting with this)
soundPrefix (str): Base name of sound (will match all starting with this)
pause (bool): Whether to pause execution until sound finishes
interrupt (bool): Whether to interrupt other sounds
"""
keys = []
for i in self.sounds.keys():
if re.match("^" + sound_prefix + ".*", i):
if re.match("^" + soundPrefix + ".*", i):
keys.append(i)
if not keys: # No matching sounds found
return None
random_key = random.choice(keys)
randomKey = random.choice(keys)
if interrupt:
self.cut_scene(random_key)
self.cut_scene(randomKey)
return
channel = self.sounds[random_key].play()
sfx_volume = self.volume_service.get_sfx_volume()
channel = self.sounds[randomKey].play()
sfxVolume = self.volumeService.get_sfx_volume()
if channel:
channel.set_volume(sfx_volume, sfx_volume)
channel.set_volume(sfxVolume, sfxVolume)
if pause:
time.sleep(self.sounds[random_key].get_length())
time.sleep(self.sounds[randomKey].get_length())
return channel
def play_random_positional(self, sound_prefix, player_x, object_x):
def play_random_positional(self, soundPrefix, playerX, objectX):
"""Play a random variation of a sound with positional audio.
Args:
sound_prefix (str): Base name of sound to match
player_x (float): Player's x position
object_x (float): Object's x position
soundPrefix (str): Base name of sound to match
playerX (float): Player's x position
objectX (float): Object's x position
Returns:
pygame.mixer.Channel: Sound channel if played, None otherwise
"""
keys = [k for k in self.sounds.keys() if k.startswith(sound_prefix)]
keys = [k for k in self.sounds.keys() if k.startswith(soundPrefix)]
if not keys:
return None
random_key = random.choice(keys)
volume, left, right = self.calculate_volume_and_pan(player_x, object_x)
randomKey = random.choice(keys)
volume, left, right = self.calculate_volume_and_pan(playerX, objectX)
if volume == 0:
return None
channel = self.sounds[random_key].play()
channel = self.sounds[randomKey].play()
if channel:
channel.set_volume(
volume * left * self.volume_service.sfx_volume,
volume * right * self.volume_service.sfx_volume
volume * left * self.volumeService.sfxVolume,
volume * right * self.volumeService.sfxVolume
)
return channel
def play_directional_sound(self, sound_name, player_pos, obj_pos, center_distance=3, volume=1.0):
def play_directional_sound(self, soundName, playerPos, objPos, centerDistance=3, volume=1.0):
"""Play a sound with simplified directional audio.
For sounds that need to be heard clearly regardless of distance, but still provide
directional feedback. Sound plays at full volume but pans left/right based on relative position.
Args:
sound_name (str): Name of sound to play
player_pos (float): Player's x position
obj_pos (float): Object's x position
center_distance (float): Distance within which sound plays center (default: 3)
soundName (str): Name of sound to play
playerPos (float): Player's x position
objPos (float): Object's x position
centerDistance (float): Distance within which sound plays center (default: 3)
volume (float): Base volume multiplier (0.0-1.0, default: 1.0)
Returns:
pygame.mixer.Channel: The channel the sound is playing on
"""
if sound_name not in self.sounds:
if soundName not in self.sounds:
return None
channel = self.sounds[sound_name].play()
channel = self.sounds[soundName].play()
if channel:
# Apply volume settings
final_volume = volume * self.volume_service.get_sfx_volume()
finalVolume = volume * self.volumeService.get_sfx_volume()
# If player is within centerDistance tiles of object, play in center
if abs(player_pos - obj_pos) <= center_distance:
if abs(playerPos - objPos) <= centerDistance:
# Equal volume in both speakers (center)
channel.set_volume(final_volume, final_volume)
elif player_pos > obj_pos:
channel.set_volume(finalVolume, finalVolume)
elif playerPos > objPos:
# Object is to the left of player
channel.set_volume(final_volume, (final_volume + 0.01) / 2)
channel.set_volume(finalVolume, (finalVolume + 0.01) / 2)
else:
# Object is to the right of player
channel.set_volume((final_volume + 0.01) / 2, final_volume)
channel.set_volume((finalVolume + 0.01) / 2, finalVolume)
return channel
def cut_scene(self, sound_name):
def cut_scene(self, soundName):
"""Play a sound as a cut scene, stopping other sounds.
Args:
sound_name (str): Name of sound to play
soundName (str): Name of sound to play
"""
if sound_name not in self.sounds:
if soundName not in self.sounds:
return
pygame.event.clear()
@ -354,11 +354,11 @@ class Sound:
channel = pygame.mixer.Channel(0)
# Apply the appropriate volume settings
sfx_volume = self.volume_service.get_sfx_volume()
channel.set_volume(sfx_volume, sfx_volume)
sfxVolume = self.volumeService.get_sfx_volume()
channel.set_volume(sfxVolume, sfxVolume)
# Play the sound
channel.play(self.sounds[sound_name])
channel.play(self.sounds[soundName])
while pygame.mixer.get_busy():
for event in pygame.event.get():
@ -367,74 +367,74 @@ class Sound:
return
pygame.time.delay(10)
def play_random_falling(self, sound_prefix, player_x, object_x, start_y,
current_y=0, max_y=20, existing_channel=None):
def play_random_falling(self, soundPrefix, playerX, objectX, startY,
currentY=0, maxY=20, existingChannel=None):
"""Play or update a falling sound with positional audio and volume based on height.
Args:
sound_prefix (str): Base name of sound to match
player_x (float): Player's x position
object_x (float): Object's x position
start_y (float): Starting Y position (0-20, higher = quieter start)
current_y (float): Current Y position (0 = ground level) (default: 0)
max_y (float): Maximum Y value (default: 20)
existing_channel: Existing sound channel to update (default: None)
soundPrefix (str): Base name of sound to match
playerX (float): Player's x position
objectX (float): Object's x position
startY (float): Starting Y position (0-20, higher = quieter start)
currentY (float): Current Y position (0 = ground level) (default: 0)
maxY (float): Maximum Y value (default: 20)
existingChannel: Existing sound channel to update (default: None)
Returns:
pygame.mixer.Channel: Sound channel for updating position/volume,
or None if sound should stop
"""
# Calculate horizontal positioning
volume, left, right = self.calculate_volume_and_pan(player_x, object_x)
volume, left, right = self.calculate_volume_and_pan(playerX, objectX)
# Calculate vertical fall volume multiplier (0 at max_y, 1 at y=0)
fall_multiplier = 1 - (current_y / max_y)
# Calculate vertical fall volume multiplier (0 at maxY, 1 at y=0)
fallMultiplier = 1 - (currentY / maxY)
# Adjust final volumes
final_volume = volume * fall_multiplier
final_left = left * final_volume
final_right = right * final_volume
finalVolume = volume * fallMultiplier
finalLeft = left * finalVolume
finalRight = right * finalVolume
if existing_channel is not None:
if existingChannel is not None:
if volume == 0: # Out of audible range
existing_channel.stop()
existingChannel.stop()
return None
existing_channel.set_volume(
final_left * self.volume_service.sfx_volume,
final_right * self.volume_service.sfx_volume
existingChannel.set_volume(
finalLeft * self.volumeService.sfxVolume,
finalRight * self.volumeService.sfxVolume
)
return existing_channel
return existingChannel
else: # Need to create new channel
if volume == 0: # Don't start if out of range
return None
# Find matching sound files
keys = [k for k in self.sounds.keys() if k.startswith(sound_prefix)]
keys = [k for k in self.sounds.keys() if k.startswith(soundPrefix)]
if not keys:
return None
random_key = random.choice(keys)
channel = self.sounds[random_key].play()
randomKey = random.choice(keys)
channel = self.sounds[randomKey].play()
if channel:
channel.set_volume(
final_left * self.volume_service.sfx_volume,
final_right * self.volume_service.sfx_volume
finalLeft * self.volumeService.sfxVolume,
finalRight * self.volumeService.sfxVolume
)
return channel
# Global functions for backward compatibility
def play_bgm(music_file):
def play_bgm(musicFile):
"""Play background music with proper volume settings.
Args:
music_file (str): Path to the music file to play
musicFile (str): Path to the music file to play
"""
try:
pygame.mixer.music.stop()
pygame.mixer.music.load(music_file)
pygame.mixer.music.set_volume(volume_service.get_bgm_volume())
pygame.mixer.music.load(musicFile)
pygame.mixer.music.set_volume(volumeService.get_bgm_volume())
pygame.mixer.music.play(-1) # Loop indefinitely
except Exception as e:
pass
@ -445,7 +445,7 @@ def adjust_master_volume(change):
Args:
change (float): Amount to change volume by (positive or negative)
"""
volume_service.adjust_master_volume(change, pygame.mixer)
volumeService.adjust_master_volume(change, pygame.mixer)
def adjust_bgm_volume(change):
"""Adjust only the background music volume.
@ -453,7 +453,7 @@ def adjust_bgm_volume(change):
Args:
change (float): Amount to change volume by (positive or negative)
"""
volume_service.adjust_bgm_volume(change, pygame.mixer)
volumeService.adjust_bgm_volume(change, pygame.mixer)
def adjust_sfx_volume(change):
"""Adjust volume for sound effects only.
@ -461,37 +461,37 @@ def adjust_sfx_volume(change):
Args:
change (float): Amount to change volume by (positive or negative)
"""
volume_service.adjust_sfx_volume(change, pygame.mixer)
volumeService.adjust_sfx_volume(change, pygame.mixer)
def calculate_volume_and_pan(player_pos, obj_pos):
def calculate_volume_and_pan(playerPos, objPos):
"""Calculate volume and stereo panning based on relative positions.
Args:
player_pos (float): Player's position on x-axis
obj_pos (float): Object's position on x-axis
playerPos (float): Player's position on x-axis
objPos (float): Object's position on x-axis
Returns:
tuple: (volume, left_vol, right_vol) values between 0 and 1
"""
distance = abs(player_pos - obj_pos)
max_distance = 12 # Maximum audible distance
distance = abs(playerPos - objPos)
maxDistance = 12 # Maximum audible distance
if distance > max_distance:
if distance > maxDistance:
return 0, 0, 0 # No sound if out of range
# Calculate volume (non-linear scaling for more noticeable changes)
# Apply masterVolume as the maximum possible volume
volume = (((max_distance - distance) / max_distance) ** 1.5) * volume_service.master_volume
volume = (((maxDistance - distance) / maxDistance) ** 1.5) * volumeService.masterVolume
# Determine left/right based on relative position
if player_pos < obj_pos:
if playerPos < objPos:
# Object is to the right
left = max(0, 1 - (obj_pos - player_pos) / max_distance)
left = max(0, 1 - (objPos - playerPos) / maxDistance)
right = 1
elif player_pos > obj_pos:
elif playerPos > objPos:
# Object is to the left
left = 1
right = max(0, 1 - (player_pos - obj_pos) / max_distance)
right = max(0, 1 - (playerPos - objPos) / maxDistance)
else:
# Player is on the object
left = right = 1
@ -510,40 +510,40 @@ def play_sound(sound, volume=1.0):
"""
channel = sound.play()
if channel:
channel.set_volume(volume * volume_service.get_sfx_volume())
channel.set_volume(volume * volumeService.get_sfx_volume())
return channel
def obj_play(sounds, soundName, player_pos, obj_pos, loop=True):
def obj_play(sounds, soundName, playerPos, objPos, loop=True):
"""Play a sound with positional audio.
Args:
sounds (dict): Dictionary of sound objects
soundName (str): Name of sound to play
player_pos (float): Player's position for audio panning
obj_pos (float): Object's position for audio panning
playerPos (float): Player's position for audio panning
objPos (float): Object's position for audio panning
loop (bool): Whether to loop the sound (default: True)
Returns:
pygame.mixer.Channel: Sound channel object, or None if out of range
"""
volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
volume, left, right = calculate_volume_and_pan(playerPos, objPos)
if volume == 0:
return None # Don't play if out of range
# Play the sound on a new channel
channel = sounds[soundName].play(-1 if loop else 0)
if channel:
channel.set_volume(volume * left * volume_service.sfx_volume,
volume * right * volume_service.sfx_volume)
channel.set_volume(volume * left * volumeService.sfxVolume,
volume * right * volumeService.sfxVolume)
return channel
def obj_update(channel, player_pos, obj_pos):
def obj_update(channel, playerPos, objPos):
"""Update positional audio for a playing sound.
Args:
channel: Sound channel to update
player_pos (float): New player position
obj_pos (float): New object position
playerPos (float): New player position
objPos (float): New object position
Returns:
pygame.mixer.Channel: Updated channel, or None if sound should stop
@ -551,14 +551,14 @@ def obj_update(channel, player_pos, obj_pos):
if channel is None:
return None
volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
volume, left, right = calculate_volume_and_pan(playerPos, objPos)
if volume == 0:
channel.stop()
return None
# Apply the volume and pan
channel.set_volume(volume * left * volume_service.sfx_volume,
volume * right * volume_service.sfx_volume)
channel.set_volume(volume * left * volumeService.sfxVolume,
volume * right * volumeService.sfxVolume)
return channel
def obj_stop(channel):
@ -601,8 +601,8 @@ def play_ambiance(sounds, soundNames, probability, randomLocation=False):
channel = sounds[ambianceSound].play()
if randomLocation and channel:
leftVolume = random.random() * volume_service.get_sfx_volume()
rightVolume = random.random() * volume_service.get_sfx_volume()
leftVolume = random.random() * volumeService.get_sfx_volume()
rightVolume = random.random() * volumeService.get_sfx_volume()
channel.set_volume(leftVolume, rightVolume)
return channel
@ -632,20 +632,20 @@ def play_random(sounds, soundName, pause=False, interrupt=False):
channel = sounds[randomKey].play()
if channel:
sfx_volume = volume_service.get_sfx_volume()
channel.set_volume(sfx_volume, sfx_volume)
sfxVolume = volumeService.get_sfx_volume()
channel.set_volume(sfxVolume, sfxVolume)
if pause:
time.sleep(sounds[randomKey].get_length())
def play_random_positional(sounds, soundName, player_x, object_x):
def play_random_positional(sounds, soundName, playerX, objectX):
"""Play a random variation of a sound with positional audio.
Args:
sounds (dict): Dictionary of sound objects
soundName (str): Base name of sound to match
player_x (float): Player's x position
object_x (float): Object's x position
playerX (float): Player's x position
objectX (float): Object's x position
Returns:
pygame.mixer.Channel: Sound channel if played, None otherwise
@ -655,15 +655,15 @@ def play_random_positional(sounds, soundName, player_x, object_x):
return None
randomKey = random.choice(keys)
volume, left, right = calculate_volume_and_pan(player_x, object_x)
volume, left, right = calculate_volume_and_pan(playerX, objectX)
if volume == 0:
return None
channel = sounds[randomKey].play()
if channel:
channel.set_volume(volume * left * volume_service.sfx_volume,
volume * right * volume_service.sfx_volume)
channel.set_volume(volume * left * volumeService.sfxVolume,
volume * right * volumeService.sfxVolume)
return channel
def play_directional_sound(sounds, soundName, playerPos, objPos, centerDistance=3, volume=1.0):
@ -686,7 +686,7 @@ def play_directional_sound(sounds, soundName, playerPos, objPos, centerDistance=
channel = sounds[soundName].play()
if channel:
# Apply volume settings
finalVolume = volume * volume_service.get_sfx_volume()
finalVolume = volume * volumeService.get_sfx_volume()
# If player is within centerDistance tiles of object, play in center
if abs(playerPos - objPos) <= centerDistance:
@ -714,8 +714,8 @@ def cut_scene(sounds, soundName):
channel = pygame.mixer.Channel(0)
# Apply the appropriate volume settings
sfx_volume = volume_service.get_sfx_volume()
channel.set_volume(sfx_volume, sfx_volume)
sfxVolume = volumeService.get_sfx_volume()
channel.set_volume(sfxVolume, sfxVolume)
# Play the sound
channel.play(sounds[soundName])
@ -727,42 +727,42 @@ def cut_scene(sounds, soundName):
return
pygame.time.delay(10)
def play_random_falling(sounds, soundName, player_x, object_x, start_y,
currentY=0, max_y=20, existing_channel=None):
def play_random_falling(sounds, soundName, playerX, objectX, startY,
currentY=0, maxY=20, existingChannel=None):
"""Play or update a falling sound with positional audio and volume based on height.
Args:
sounds (dict): Dictionary of sound objects
soundName (str): Base name of sound to match
player_x (float): Player's x position
object_x (float): Object's x position
start_y (float): Starting Y position (0-20, higher = quieter start)
playerX (float): Player's x position
objectX (float): Object's x position
startY (float): Starting Y position (0-20, higher = quieter start)
currentY (float): Current Y position (0 = ground level) (default: 0)
max_y (float): Maximum Y value (default: 20)
existing_channel: Existing sound channel to update (default: None)
maxY (float): Maximum Y value (default: 20)
existingChannel: Existing sound channel to update (default: None)
Returns:
pygame.mixer.Channel: Sound channel for updating position/volume,
or None if sound should stop
"""
# Calculate horizontal positioning
volume, left, right = calculate_volume_and_pan(player_x, object_x)
volume, left, right = calculate_volume_and_pan(playerX, objectX)
# Calculate vertical fall volume multiplier (0 at max_y, 1 at y=0)
fallMultiplier = 1 - (currentY / max_y)
# Calculate vertical fall volume multiplier (0 at maxY, 1 at y=0)
fallMultiplier = 1 - (currentY / maxY)
# Adjust final volumes
finalVolume = volume * fallMultiplier
finalLeft = left * finalVolume
finalRight = right * finalVolume
if existing_channel is not None:
if existingChannel is not None:
if volume == 0: # Out of audible range
existing_channel.stop()
existingChannel.stop()
return None
existing_channel.set_volume(finalLeft * volume_service.sfx_volume,
finalRight * volume_service.sfx_volume)
return existing_channel
existingChannel.set_volume(finalLeft * volumeService.sfxVolume,
finalRight * volumeService.sfxVolume)
return existingChannel
else: # Need to create new channel
if volume == 0: # Don't start if out of range
return None
@ -775,6 +775,6 @@ def play_random_falling(sounds, soundName, player_x, object_x, start_y,
randomKey = random.choice(keys)
channel = sounds[randomKey].play()
if channel:
channel.set_volume(finalLeft * volume_service.sfx_volume,
finalRight * volume_service.sfx_volume)
channel.set_volume(finalLeft * volumeService.sfxVolume,
finalRight * volumeService.sfxVolume)
return channel