Added exit_game method that handles cleanup. A few other small modifications.

This commit is contained in:
Storm Dragon 2025-02-23 16:25:57 -05:00
parent 576f4cc87a
commit a6702edf16
5 changed files with 102 additions and 86 deletions

View File

@ -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)

View File

@ -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)

28
menu.py
View File

@ -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):

View File

@ -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()

View File

@ -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()