diff --git a/__init__.py b/__init__.py index 5cdd582..a3be4bc 100644 --- a/__init__.py +++ b/__init__.py @@ -89,43 +89,44 @@ class pygstormgames: self.speech.cleanup() pyglet.app.exit() - def wait(self, valid_keys=None): + def wait(self, validKeys=None): """Wait for key press(es). - + Args: - valid_keys (list, optional): List of pyglet.window.key values to wait for. + validKeys (list, optional): List of pyglet.window.key values to wait for. If None, accepts any key press. - + Returns: - int: The key that was pressed - """ - result = [None] # Use list to allow modification in closure + tuple: (key, modifiers) that were pressed + """ + keyResult = [None, None] # Use list to allow modification in closure def on_key_press(symbol, modifiers): - if valid_keys is None or symbol in valid_keys: - result[0] = symbol + if validKeys is None or symbol in validKeys: + keyResult[0] = symbol + keyResult[1] = modifiers return pyglet.event.EVENT_HANDLED - + # Register temporary handler self.display.window.push_handlers(on_key_press=on_key_press) - + # Wait for valid key press - while result[0] is None: + while keyResult[0] is None: self.display.window.dispatch_events() pyglet.clock.tick() - + # Clean up self.display.window.remove_handlers() - return result[0] + return tuple(keyResult) def wait_for_completion(self, condition, validKeys=None): """Wait for either a condition to be met or valid keys to be pressed. - + Args: condition: Function that returns True when waiting should end validKeys (list, optional): List of pyglet.window.key values that can interrupt If None, uses ESCAPE, RETURN, and SPACE - + Returns: bool: True if interrupted by key press, False if condition was met """ @@ -133,19 +134,19 @@ class pygstormgames: validKeys = [pyglet.window.key.ESCAPE, pyglet.window.key.RETURN, pyglet.window.key.SPACE] - - wasInterrupted = [False] # Use list to allow modification in closure - + + interrupted = [False] # Use list to allow modification in closure + def on_key_press(symbol, modifiers): if symbol in validKeys: - wasInterrupted[0] = True + interrupted[0] = True return pyglet.event.EVENT_HANDLED - + self.display.window.push_handlers(on_key_press=on_key_press) - - while not condition() and not wasInterrupted[0]: + + while not condition() and not interrupted[0]: self.display.window.dispatch_events() pyglet.clock.tick() - + self.display.window.remove_handlers() - return wasInterrupted[0] + return interrupted[0] diff --git a/display.py b/display.py index ec8f26e..9f89330 100644 --- a/display.py +++ b/display.py @@ -25,6 +25,7 @@ class Display: """ self.game = game self.window = pyglet.window.Window(800, 600, caption=game.gameTitle) + self.displayedInstructions = False self.currentText = [] self.currentIndex = 0 self.gameTitle = game.gameTitle @@ -43,10 +44,12 @@ class Display: self.navText = [line for line in text if line.strip()] # Add instructions at start - 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.") - self.navText.insert(0, instructions) + if not self.displayedInstructions: + 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.") + self.navText.insert(0, instructions) + self.displayedInstructions = True # Add end marker self.navText.append("End of text.") @@ -68,7 +71,7 @@ class Display: ] while True: - key = self.game.wait(validKeys) + key, _ = self.game.wait(validKeys) if key in (pyglet.window.key.ESCAPE, pyglet.window.key.RETURN): break @@ -142,7 +145,7 @@ def messagebox(self, text): self.game.speech.speak(text) # Wait for any key - key = self.game.wait() + key, _ = self.game.wait() # Exit if enter/escape pressed if key in (pyglet.window.key.RETURN, pyglet.window.key.ESCAPE): diff --git a/menu.py b/menu.py index 68ef609..208c8fe 100644 --- a/menu.py +++ b/menu.py @@ -32,73 +32,81 @@ class Menu: self.currentIndex = 0 lastSpoken = -1 - selection = None # Add this to store the selection - + if title: self.game.speech.speak(title) - - def key_handler(symbol, modifiers): # Define handler outside event - nonlocal selection, lastSpoken - # Handle Alt+volume controls - if modifiers & key.MOD_ALT: - if symbol == key.PAGEUP: - self.game.sound.adjust_master_volume(0.1) - elif symbol == key.PAGEDOWN: - self.game.sound.adjust_master_volume(-0.1) - elif symbol == key.HOME: - self.game.sound.adjust_bgm_volume(0.1) - elif symbol == key.END: - self.game.sound.adjust_bgm_volume(-0.1) - elif symbol == key.INSERT: - self.game.sound.adjust_sfx_volume(0.1) - elif symbol == key.DELETE: - self.game.sound.adjust_sfx_volume(-0.1) - return - - if symbol == key.ESCAPE: - selection = "exit" - return pyglet.event.EVENT_HANDLED - - if symbol == key.HOME and self.currentIndex != 0: - self.currentIndex = 0 - self.game.sound.play_sound('menu-move') - lastSpoken = -1 # Force speech - - elif symbol == key.END and self.currentIndex != len(options) - 1: - self.currentIndex = len(options) - 1 - self.game.sound.play_sound('menu-move') - lastSpoken = -1 # Force speech - - elif symbol in (key.DOWN, key.S) and self.currentIndex < len(options) - 1: - self.currentIndex += 1 - self.game.sound.play_sound('menu-move') - lastSpoken = -1 # Force speech - - elif symbol in (key.UP, key.W) and self.currentIndex > 0: - self.currentIndex -= 1 - self.game.sound.play_sound('menu-move') - lastSpoken = -1 # Force speech - - elif symbol == key.RETURN: - self.game.sound.play_sound('menu-select') - selection = options[self.currentIndex] - return pyglet.event.EVENT_HANDLED - - return pyglet.event.EVENT_HANDLED - # Register the handler - self.game.display.window.push_handlers(on_key_press=key_handler) - - # Main menu loop - while selection is None: + validKeys = [ + pyglet.window.key.ESCAPE, + pyglet.window.key.RETURN, + pyglet.window.key.UP, + pyglet.window.key.DOWN, + pyglet.window.key.W, + pyglet.window.key.S, + pyglet.window.key.HOME, + pyglet.window.key.END, + pyglet.window.key.PAGEUP, + pyglet.window.key.PAGEDOWN, + pyglet.window.key.INSERT, + pyglet.window.key.DELETE + ] + + while True: + # Speak current option if changed if self.currentIndex != lastSpoken: self.game.speech.speak(options[self.currentIndex]) lastSpoken = self.currentIndex - self.game.display.window.dispatch_events() - - # Clean up - self.game.display.window.remove_handlers() - return selection + + key, keyModifiers = self.game.wait(validKeys) + + # Handle Alt+volume controls + if keyModifiers & pyglet.window.key.MOD_ALT: + if key == pyglet.window.key.PAGEUP: + self.game.sound.adjust_master_volume(0.1) + elif key == pyglet.window.key.PAGEDOWN: + self.game.sound.adjust_master_volume(-0.1) + elif key == pyglet.window.key.HOME: + self.game.sound.adjust_bgm_volume(0.1) + elif key == pyglet.window.key.END: + self.game.sound.adjust_bgm_volume(-0.1) + elif key == pyglet.window.key.INSERT: + self.game.sound.adjust_sfx_volume(0.1) + elif key == pyglet.window.key.DELETE: + self.game.sound.adjust_sfx_volume(-0.1) + continue + + 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" + + if key == pyglet.window.key.HOME and self.currentIndex != 0: + self.currentIndex = 0 + self.game.sound.play_sound('menu-move') + lastSpoken = -1 # Force speech + + elif key == pyglet.window.key.END and self.currentIndex != len(options) - 1: + self.currentIndex = len(options) - 1 + self.game.sound.play_sound('menu-move') + lastSpoken = -1 # Force speech + + elif key in (pyglet.window.key.DOWN, pyglet.window.key.S) and self.currentIndex < len(options) - 1: + self.currentIndex += 1 + self.game.sound.play_sound('menu-move') + lastSpoken = -1 # Force speech + + elif key in (pyglet.window.key.UP, pyglet.window.key.W) and self.currentIndex > 0: + self.currentIndex -= 1 + self.game.sound.play_sound('menu-move') + lastSpoken = -1 # Force speech + + elif key == pyglet.window.key.RETURN: + self.game.sound.play_sound('menu-select') + return options[self.currentIndex] def game_menu(self): """Show main game menu.""" @@ -153,14 +161,20 @@ class Menu: if self.game.sound.currentBgm: self.game.sound.currentBgm.play() + 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): """Interactive menu for learning game sounds. - + Allows users to: - Navigate through available sounds - Play selected sounds - Return to menu with escape key - + Returns: str: "menu" if user exits with escape """ @@ -177,40 +191,46 @@ class Menu: and (f.split('.')[1].lower() in ["ogg", "wav"]) and (f.split('.')[0].lower() not in ["game-intro", "music_menu"]) and (not f.lower().startswith("_"))] - - # Track last spoken index to avoid repetition - lastSpoken = -1 - - while True: - if self.currentIndex != lastSpoken: - self.game.speech.speak(soundFiles[self.currentIndex][:-4]) - lastSpoken = self.currentIndex - - event = self.game.display.window.dispatch_events() - @self.game.display.window.event - def on_key_press(symbol, modifiers): - if symbol == key.ESCAPE: + if not soundFiles: + self.game.speech.speak("No sounds available to learn.") + return "menu" + + validKeys = [ + pyglet.window.key.ESCAPE, + pyglet.window.key.RETURN, + pyglet.window.key.UP, + pyglet.window.key.DOWN, + pyglet.window.key.W, + pyglet.window.key.S + ] + + # Speak initial sound name + self.game.speech.speak(soundFiles[self.currentIndex][:-4]) + + while True: + key, _ = self.game.wait(validKeys) + + if key == pyglet.window.key.ESCAPE: try: - self.game.sound.currentBgm.unpause() + self.game.sound.currentBgm.play() except: pass - self.game.display.window.remove_handler('on_key_press', on_key_press) return "menu" - - if symbol in [key.DOWN, key.S] and self.currentIndex < len(soundFiles) - 1: - self.game.sound.stop_all_sounds() - self.currentIndex += 1 - - if symbol in [key.UP, key.W] and self.currentIndex > 0: - self.game.sound.stop_all_sounds() - self.currentIndex -= 1 - - if symbol == key.RETURN: - try: - soundName = soundFiles[self.currentIndex][:-4] + + if key in [pyglet.window.key.DOWN, pyglet.window.key.S]: + if self.currentIndex < len(soundFiles) - 1: self.game.sound.stop_all_sounds() - self.game.sound.play_sound(soundName) - except: - lastSpoken = -1 - self.game.speech.speak("Could not play sound.") + self.currentIndex += 1 + self.game.speech.speak(soundFiles[self.currentIndex][:-4]) + + if key in [pyglet.window.key.UP, pyglet.window.key.W]: + if self.currentIndex > 0: + self.game.sound.stop_all_sounds() + self.currentIndex -= 1 + self.game.speech.speak(soundFiles[self.currentIndex][:-4]) + + if key == pyglet.window.key.RETURN: + soundName = soundFiles[self.currentIndex][:-4] + self.game.sound.stop_all_sounds() + self.game.sound.play_sound(soundName)