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

114
utils.py
View File

@ -36,10 +36,10 @@ class Game:
self.title = title
# Initialize services
self.path_service = PathService.get_instance().initialize(title)
self.config_service = ConfigService.get_instance()
self.config_service.set_game_info(title, self.path_service)
self.volume_service = VolumeService.get_instance()
self.pathService = PathService.get_instance().initialize(title)
self.configService = ConfigService.get_instance()
self.configService.set_game_info(title, self.pathService)
self.volumeService = VolumeService.get_instance()
# Initialize game components (lazy loaded)
self._speech = None
@ -47,7 +47,7 @@ class Game:
self._scoreboard = None
# Display text instructions flag
self.display_text_usage_instructions = False
self.displayTextUsageInstructions = False
@property
def speech(self):
@ -68,7 +68,7 @@ class Game:
Sound: Sound system instance
"""
if not self._sound:
self._sound = Sound("sounds/", self.volume_service)
self._sound = Sound("sounds/", self.volumeService)
return self._sound
@property
@ -79,7 +79,7 @@ class Game:
Scoreboard: Scoreboard instance
"""
if not self._scoreboard:
self._scoreboard = Scoreboard(self.config_service)
self._scoreboard = Scoreboard(self.configService)
return self._scoreboard
def initialize(self):
@ -130,89 +130,89 @@ class Game:
self.speech.speak(text, interrupt)
return self
def play_bgm(self, music_file):
def play_bgm(self, musicFile):
"""Play background music.
Args:
music_file (str): Path to music file
musicFile (str): Path to music file
Returns:
Game: Self for method chaining
"""
self.sound.play_bgm(music_file)
self.sound.play_bgm(musicFile)
return self
def display_text(self, text_lines):
def display_text(self, textLines):
"""Display text with navigation controls.
Args:
text_lines (list): List of text lines
textLines (list): List of text lines
Returns:
Game: Self for method chaining
"""
# Store original text with blank lines for copying
original_text = text_lines.copy()
originalText = textLines.copy()
# Create navigation text by filtering out blank lines
nav_text = [line for line in text_lines if line.strip()]
navText = [line for line in textLines if line.strip()]
# Add instructions at the start on the first display
if not self.display_text_usage_instructions:
if not self.displayTextUsageInstructions:
instructions = ("Press space to read the whole text. Use up and down arrows to navigate "
"the text line by line. Press c to copy the current line to the clipboard "
"or t to copy the entire text. Press enter or escape when you are done reading.")
nav_text.insert(0, instructions)
self.display_text_usage_instructions = True
navText.insert(0, instructions)
self.displayTextUsageInstructions = True
# Add end marker
nav_text.append("End of text.")
navText.append("End of text.")
current_index = 0
self.speech.speak(nav_text[current_index])
currentIndex = 0
self.speech.speak(navText[currentIndex])
while True:
event = pygame.event.wait()
if event.type == pygame.KEYDOWN:
# Check for Alt modifier
mods = pygame.key.get_mods()
alt_pressed = mods & pygame.KMOD_ALT
altPressed = mods & pygame.KMOD_ALT
# Volume controls (require Alt)
if alt_pressed:
if altPressed:
if event.key == pygame.K_PAGEUP:
self.volume_service.adjust_master_volume(0.1, pygame.mixer)
self.volumeService.adjust_master_volume(0.1, pygame.mixer)
elif event.key == pygame.K_PAGEDOWN:
self.volume_service.adjust_master_volume(-0.1, pygame.mixer)
self.volumeService.adjust_master_volume(-0.1, pygame.mixer)
elif event.key == pygame.K_HOME:
self.volume_service.adjust_bgm_volume(0.1, pygame.mixer)
self.volumeService.adjust_bgm_volume(0.1, pygame.mixer)
elif event.key == pygame.K_END:
self.volume_service.adjust_bgm_volume(-0.1, pygame.mixer)
self.volumeService.adjust_bgm_volume(-0.1, pygame.mixer)
elif event.key == pygame.K_INSERT:
self.volume_service.adjust_sfx_volume(0.1, pygame.mixer)
self.volumeService.adjust_sfx_volume(0.1, pygame.mixer)
elif event.key == pygame.K_DELETE:
self.volume_service.adjust_sfx_volume(-0.1, pygame.mixer)
self.volumeService.adjust_sfx_volume(-0.1, pygame.mixer)
else:
if event.key in (pygame.K_ESCAPE, pygame.K_RETURN):
return self
if event.key in [pygame.K_DOWN, pygame.K_s] and current_index < len(nav_text) - 1:
current_index += 1
self.speech.speak(nav_text[current_index])
if event.key in [pygame.K_DOWN, pygame.K_s] and currentIndex < len(navText) - 1:
currentIndex += 1
self.speech.speak(navText[currentIndex])
if event.key in [pygame.K_UP, pygame.K_w] and current_index > 0:
current_index -= 1
self.speech.speak(nav_text[current_index])
if event.key in [pygame.K_UP, pygame.K_w] and currentIndex > 0:
currentIndex -= 1
self.speech.speak(navText[currentIndex])
if event.key == pygame.K_SPACE:
# Join with newlines to preserve spacing in speech
self.speech.speak('\n'.join(original_text[1:-1]))
self.speech.speak('\n'.join(originalText[1:-1]))
if event.key == pygame.K_c:
try:
import pyperclip
pyperclip.copy(nav_text[current_index])
self.speech.speak("Copied " + nav_text[current_index] + " to the clipboard.")
pyperclip.copy(navText[currentIndex])
self.speech.speak("Copied " + navText[currentIndex] + " to the clipboard.")
except:
self.speech.speak("Failed to copy the text to the clipboard.")
@ -220,7 +220,7 @@ class Game:
try:
import pyperclip
# Join with newlines to preserve blank lines in full text
pyperclip.copy(''.join(original_text[2:-1]))
pyperclip.copy(''.join(originalText[2:-1]))
self.speech.speak("Copied entire message to the clipboard.")
except:
self.speech.speak("Failed to copy the text to the clipboard.")
@ -239,12 +239,12 @@ class Game:
# Utility functions
def check_for_updates(current_version, game_name, url):
def check_for_updates(currentVersion, gameName, url):
"""Check for game updates.
Args:
current_version (str): Current version string (e.g. "1.0.0")
game_name (str): Name of the game
currentVersion (str): Current version string (e.g. "1.0.0")
gameName (str): Name of the game
url (str): URL to check for updates
Returns:
@ -254,7 +254,7 @@ def check_for_updates(current_version, game_name, url):
response = requests.get(url, timeout=5)
if response.status_code == 200:
data = response.json()
if 'version' in data and data['version'] > current_version:
if 'version' in data and data['version'] > currentVersion:
return {
'version': data['version'],
'url': data.get('url', ''),
@ -264,29 +264,29 @@ def check_for_updates(current_version, game_name, url):
print(f"Error checking for updates: {e}")
return None
def get_version_tuple(version_str):
def get_version_tuple(versionStr):
"""Convert version string to comparable tuple.
Args:
version_str (str): Version string (e.g. "1.0.0")
versionStr (str): Version string (e.g. "1.0.0")
Returns:
tuple: Version as tuple of integers
"""
return tuple(map(int, version_str.split('.')))
return tuple(map(int, versionStr.split('.')))
def check_compatibility(required_version, current_version):
def check_compatibility(requiredVersion, currentVersion):
"""Check if current version meets minimum required version.
Args:
required_version (str): Minimum required version string
current_version (str): Current version string
requiredVersion (str): Minimum required version string
currentVersion (str): Current version string
Returns:
bool: True if compatible, False otherwise
"""
req = get_version_tuple(required_version)
cur = get_version_tuple(current_version)
req = get_version_tuple(requiredVersion)
cur = get_version_tuple(currentVersion)
return cur >= req
def sanitize_filename(filename):
@ -350,25 +350,25 @@ def distance_2d(x1, y1, x2, y2):
"""
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
def generate_tone(frequency, duration=0.1, sample_rate=44100, volume=0.2):
def generate_tone(frequency, duration=0.1, sampleRate=44100, volume=0.2):
"""Generate a tone at the specified frequency.
Args:
frequency (float): Frequency in Hz
duration (float): Duration in seconds (default: 0.1)
sample_rate (int): Sample rate in Hz (default: 44100)
sampleRate (int): Sample rate in Hz (default: 44100)
volume (float): Volume from 0.0 to 1.0 (default: 0.2)
Returns:
pygame.mixer.Sound: Sound object with the generated tone
"""
t = np.linspace(0, duration, int(sample_rate * duration), False)
t = np.linspace(0, duration, int(sampleRate * duration), False)
tone = np.sin(2 * np.pi * frequency * t)
stereo_tone = np.vstack((tone, tone)).T # Create a 2D array for stereo
stereo_tone = (stereo_tone * 32767 * volume).astype(np.int16) # Apply volume
stereo_tone = np.ascontiguousarray(stereo_tone) # Ensure C-contiguous array
return pygame.sndarray.make_sound(stereo_tone)
stereoTone = np.vstack((tone, tone)).T # Create a 2D array for stereo
stereoTone = (stereoTone * 32767 * volume).astype(np.int16) # Apply volume
stereoTone = np.ascontiguousarray(stereoTone) # Ensure C-contiguous array
return pygame.sndarray.make_sound(stereoTone)
def x_powerbar():
"""Sound based horizontal power bar