From a6702edf1699658639381b9b11b804f921eb252e Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 23 Feb 2025 16:25:57 -0500 Subject: [PATCH] Added exit_game method that handles cleanup. A few other small modifications. --- __init__.py | 38 +++++++++++++++++++++++++++++++++ display.py | 41 +++++++++++++++++++---------------- menu.py | 28 +++++++----------------- sound.py | 61 +++++++++++++++++------------------------------------ speech.py | 20 ++++++++++++------ 5 files changed, 102 insertions(+), 86 deletions(-) diff --git a/__init__.py b/__init__.py index a3be4bc..b6733f8 100644 --- a/__init__.py +++ b/__init__.py @@ -150,3 +150,41 @@ class pygstormgames: self.display.window.remove_handlers() return interrupted[0] + + def exit_game(self): + """Closes the game cleanly""" + try: + self.sound.cleanup() + except Exception as e: + print(f"Error with sound cleanup: {e}") + pass + + try: + self.display.window.remove_handlers() + except Exception as e: + print(f"Error with handler cleanup: {e}") + pass + + try: + self.speech.cleanup() + except Exception as e: + print(f"Error with speech cleanup: {e}") + pass + + try: + # Try to close the window explicitly + if hasattr(self.display, 'window'): + self.display.window.close() + except Exception as e: + print(f"Error closing window: {e}") + pass + + try: + pyglet.app.exit() + except Exception as e: + print(f"Error while exiting: {e}") + pass + + # Force exit if pyglet.app.exit() didn't work + import sys + sys.exit(0) diff --git a/display.py b/display.py index 9f89330..2692b8a 100644 --- a/display.py +++ b/display.py @@ -57,7 +57,6 @@ class Display: self.currentIndex = 0 speech.speak(self.navText[self.currentIndex]) - # Use the game's existing input handlers validKeys = [ pyglet.window.key.ESCAPE, pyglet.window.key.RETURN, @@ -131,25 +130,25 @@ class Display: self.display_text(info, speech) -def messagebox(self, text): - """Display a simple message box with text. + def messagebox(self, text): + """Display a simple message box with text. - Shows a message that can be repeated until the user chooses to continue. - - Args: - text (str): Message to display - """ - text = text + "\nPress any key to repeat or enter to continue." - while True: - # Speak the text - self.game.speech.speak(text) + Shows a message that can be repeated until the user chooses to continue. - # Wait for any key - key, _ = self.game.wait() + Args: + text (str): Message to display + """ + text = text + "\nPress any key to repeat or enter to continue." + while True: + # Speak the text + self.game.speech.speak(text) - # Exit if enter/escape pressed - if key in (pyglet.window.key.RETURN, pyglet.window.key.ESCAPE): - break + # Wait for any key + key, _ = self.game.wait() + + # Exit if enter/escape pressed + if key in (pyglet.window.key.RETURN, pyglet.window.key.ESCAPE): + break def get_input(self, prompt="Enter text:", defaultText=""): """Display a dialog box for text input. @@ -173,5 +172,11 @@ def messagebox(self, text): def donate(self, speech): """Open the donation webpage.""" - speech.speak("Opening donation page.") + speech.speak("Opening donation page. Press space or enter to continue.") webbrowser.open('https://ko-fi.com/stormux') + validKeys = [ + pyglet.window.key.ESCAPE, + pyglet.window.key.RETURN, + pyglet.window.key.SPACE + ] + key, _ = self.game.wait(validKeys) diff --git a/menu.py b/menu.py index 208c8fe..6e700a8 100644 --- a/menu.py +++ b/menu.py @@ -78,11 +78,8 @@ class Menu: if key == pyglet.window.key.ESCAPE: if "exit" in options: # Handle exit cleanup immediately - self.game.sound.cleanup() - self.game.display.window.remove_handlers() - self.game.speech.cleanup() - pyglet.app.exit() - return "exit" + self.game.exit_game() + return if key == pyglet.window.key.HOME and self.currentIndex != 0: self.currentIndex = 0 @@ -126,14 +123,12 @@ class Menu: return selection elif selection == "instructions": # Pause menu music - if self.game.sound.currentBgm: - self.game.sound.currentBgm.pause() + self.game.sound.pause_bgm() self.game.display.instructions(self.game.speech) # Resume menu music - if self.game.sound.currentBgm: - self.game.sound.currentBgm.play() + self.game.sound.resume_bgm() elif selection == "learn_sounds": if self.learn_sounds() == "menu": @@ -141,30 +136,23 @@ class Menu: elif selection == "credits": # Pause menu music - if self.game.sound.currentBgm: - self.game.sound.currentBgm.pause() + self.game.sound.pause_bgm() self.game.display.credits(self.game.speech) # Resume menu music - if self.game.sound.currentBgm: - self.game.sound.currentBgm.play() + self.game.sound.resume_bgm() elif selection == "donate": # Pause menu music - if self.game.sound.currentBgm: - self.game.sound.currentBgm.pause() + self.game.sound.pause_bgm() self.game.display.donate(self.game.speech) # Resume menu music - if self.game.sound.currentBgm: - self.game.sound.currentBgm.play() + self.game.sound.resume_bgm() elif selection == "exit": - self.game.sound.cleanup() # Stop all sounds first - self.game.display.window.remove_handlers() # Remove handlers before closing speech - self.game.speech.cleanup() # Clean up speech last self.game.exit_game() def learn_sounds(self): diff --git a/sound.py b/sound.py index af47058..931638b 100644 --- a/sound.py +++ b/sound.py @@ -82,6 +82,16 @@ class Sound: except Exception as e: print(f"Error playing background music: {e}") + def pause_bgm(self): + """Pause background music.""" + if self.currentBgm and self.currentBgm.playing: + self.currentBgm.pause() + + def resume_bgm(self): + """Resume background music from paused state.""" + if self.currentBgm and not self.currentBgm.playing: + self.currentBgm.play() + def play_sound(self, soundName, volume=1.0): """Play a sound effect with volume settings. @@ -220,15 +230,7 @@ class Sound: player.cone_orientation = direction def cut_scene(self, soundName): - """Play a sound as a cut scene, stopping other sounds and waiting for completion. - - Args: - soundName (str): Name of sound to play - - The method will block until either: - - The sound finishes playing - - The user presses ESC/RETURN/SPACE (if window is provided) - """ + """Play a sound as a cut scene, stopping other sounds and waiting for completion.""" # Stop all current sounds self.stop_all_sounds() if self.currentBgm: @@ -241,44 +243,19 @@ class Sound: player = pyglet.media.Player() player.queue(self.sounds[soundName]) player.volume = self.sfxVolume * self.masterVolume - - # Flag to track if we should continue waiting - shouldContinue = True - - def on_player_eos(): - nonlocal shouldContinue - shouldContinue = False - - # Set up completion callback - player.push_handlers(on_eos=on_player_eos) - - # Get window from game display - window = self.game.display.window - - # If we have a window, set up key handler for skipping - if window: - skipKeys = [key.ESCAPE, key.RETURN, key.SPACE] - - @window.event - def on_key_press(symbol, modifiers): - nonlocal shouldContinue - if symbol in skipKeys: - shouldContinue = False - return True - + # Start playback player.play() - - # Wait for completion or skip - while shouldContinue and player.playing: - if window: - window.dispatch_events() - pyglet.clock.tick() - + + # Wait for completion or skip using game's wait_for_completion method + interrupted = self.game.wait_for_completion( + lambda: not player.playing + ) + # Ensure cleanup player.pause() player.delete() - + # Resume background music if it was playing if self.currentBgm: self.currentBgm.play() diff --git a/speech.py b/speech.py index 5a036c1..2be966c 100644 --- a/speech.py +++ b/speech.py @@ -15,6 +15,7 @@ class Speech: def __init__(self): """Initialize speech system with fallback providers.""" self._lastSpoken = {"text": None, "time": 0} + self._active = False self._speechDelay = 250 # ms delay between identical messages # Try to initialize speech providers in order of preference @@ -22,11 +23,13 @@ class Speech: import speechd self._speech = speechd.Client() self._provider = "speechd" + self._active = True except ImportError: try: import accessible_output2.outputs.auto self._speech = accessible_output2.outputs.auto.Auto() self._provider = "accessible_output2" + self._active = True except ImportError: raise RuntimeError("No speech providers found. Install either speechd or accessible_output2.") @@ -60,12 +63,15 @@ class Speech: self._lastSpoken["time"] = currentTime # Handle speech output based on provider - if self._provider == "speechd": - if interrupt: - self._speech.cancel() - self._speech.speak(text) - else: - self._speech.speak(text, interrupt=interrupt) + try: + if self._provider == "speechd": + if interrupt: + self._speech.cancel() + self._speech.speak(text) + else: + self._speech.speak(text, interrupt=interrupt) + except Exception as e: + self._active = False # Update display text self._font.text = text @@ -76,6 +82,8 @@ class Speech: def cleanup(self): """Clean up speech system resources.""" + if not self._active: + return if self._provider == "speechd": self._speech.close()