Compare commits

...

4 Commits

Author SHA1 Message Date
Storm Dragon f2079261d1 More event handling to help with pyinstaller compilation. 2025-09-10 12:57:09 -04:00
Storm Dragon 0190fa3a06 Add the ability to add an optional graphical logo for games. Tested with .png file. 2025-09-10 00:59:21 -04:00
Storm Dragon 09421c4bda Updated donation link. 2025-09-08 11:14:42 -04:00
Storm Dragon dcd204e476 Added the ability to create submenus with instructions. 2025-09-07 02:38:27 -04:00
6 changed files with 199 additions and 10 deletions
+2 -2
View File
@@ -51,7 +51,7 @@ from .input import get_input, check_for_exit, pause_game
from .display import display_text, initialize_gui
# Import menu functions
from .menu import game_menu, learn_sounds, instructions, credits, donate, exit_game
from .menu import game_menu, instruction_menu, learn_sounds, instructions, credits, donate, exit_game
# Update imports to reference Scoreboard methods
high_scores = Scoreboard.display_high_scores
@@ -113,7 +113,7 @@ __all__ = [
'display_text', 'initialize_gui',
# Menu
'game_menu', 'learn_sounds', 'instructions', 'credits', 'donate', 'exit_game', 'high_scores', 'has_high_scores',
'game_menu', 'instruction_menu', 'learn_sounds', 'instructions', 'credits', 'donate', 'exit_game', 'high_scores', 'has_high_scores',
# Game class
'Game',
+49 -3
View File
@@ -88,13 +88,55 @@ def initialize_gui(gameTitle):
Speech.get_instance().speak("Error loading sounds.", False)
soundData = {}
# Play intro sound if available
# Play intro sound if available, optionally with visual logo
from .sound import cut_scene
if 'game-intro' in soundData:
cut_scene(soundData, 'game-intro')
_show_logo_with_audio(soundData, 'game-intro')
return soundData
def _show_logo_with_audio(soundData, audioKey):
"""Show visual logo while playing audio intro.
Args:
soundData (dict): Dictionary of loaded sounds
audioKey (str): Key of the audio to play
"""
# Look for logo image files in common formats
logoFiles = ['logo.png', 'logo.jpg', 'logo.jpeg', 'logo.gif', 'logo.bmp']
logoImage = None
for logoFile in logoFiles:
if os.path.exists(logoFile):
try:
logoImage = pygame.image.load(logoFile)
break
except pygame.error:
continue
if logoImage:
# Display logo while audio plays
screen = pygame.display.get_surface()
screenRect = screen.get_rect()
logoRect = logoImage.get_rect(center=screenRect.center)
# Clear screen to black
screen.fill((0, 0, 0))
screen.blit(logoImage, logoRect)
pygame.display.flip()
# Play audio and wait for it to finish
from .sound import cut_scene
cut_scene(soundData, audioKey)
# Clear screen after audio finishes
screen.fill((0, 0, 0))
pygame.display.flip()
else:
# No logo image found, just play audio
from .sound import cut_scene
cut_scene(soundData, audioKey)
def display_text(text):
"""Display and speak text with navigation controls.
@@ -135,6 +177,9 @@ def display_text(text):
currentIndex = 0
speech.speak(navText[currentIndex])
# Clear any pending events
pygame.event.clear()
while True:
event = pygame.event.wait()
@@ -188,5 +233,6 @@ def display_text(text):
except:
speech.speak("Failed to copy the text to the clipboard.")
event = pygame.event.clear()
pygame.event.pump()
pygame.event.clear()
time.sleep(0.001)
+9
View File
@@ -43,6 +43,9 @@ def get_input(prompt="Enter text:", text=""):
initial_message = f"{prompt} Empty text field"
speak(initial_message)
# Clear any pending events
pygame.event.clear()
# Main input loop
while True:
event = pygame.event.wait()
@@ -199,6 +202,8 @@ def get_input(prompt="Enter text:", text=""):
# Allow other events to be processed
pygame.event.pump()
pygame.event.clear()
time.sleep(0.001)
def pause_game():
"""Pauses the game until user presses backspace."""
@@ -218,6 +223,10 @@ def pause_game():
event = pygame.event.wait()
if event.type == pygame.KEYDOWN and event.key == pygame.K_BACKSPACE:
break
pygame.event.pump()
pygame.event.clear()
time.sleep(0.001)
try:
pygame.mixer.unpause()
+94 -3
View File
@@ -25,6 +25,94 @@ from .display import display_text
from .scoreboard import Scoreboard
from .services import PathService, ConfigService
def instruction_menu(sounds, instruction_text, *options):
"""Display a menu with an instruction announcement at the top.
The instruction text is announced as item 0 but is not selectable.
The actual menu items start at index 1, and navigation skips the instruction.
Args:
sounds (dict): Dictionary of sound objects
instruction_text (str): The instruction/context text to announce
*options: Menu options to display
Returns:
str: Selected menu option or None if cancelled
"""
# Get speech instance
speech = Speech.get_instance()
# Create combined list with instruction at index 0
all_items = [instruction_text] + list(options)
loop = True
pygame.mixer.stop()
current_index = 0 # Start at instruction
last_spoken = -1
# Clear any pending events
pygame.event.clear()
while loop:
if current_index != last_spoken:
speech.speak(all_items[current_index])
last_spoken = current_index
event = pygame.event.wait()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return None
elif event.key in [pygame.K_DOWN, pygame.K_s]:
moved = False
if current_index == 0: # On instruction, go to first menu item
current_index = 1
moved = True
elif current_index < len(all_items) - 1: # Normal navigation
current_index += 1
moved = True
if moved:
try:
sounds['menu-move'].play()
except:
pass
elif event.key in [pygame.K_UP, pygame.K_w]:
if current_index > 1: # Can move up from menu items (but not to instruction)
current_index -= 1
try:
sounds['menu-move'].play()
except:
pass
elif event.key == pygame.K_HOME:
target_index = 1 if current_index != 1 else current_index # Go to first menu item
if target_index != current_index:
current_index = target_index
try:
sounds['menu-move'].play()
except:
pass
elif event.key == pygame.K_END and current_index != len(all_items) - 1:
current_index = len(all_items) - 1
try:
sounds['menu-move'].play()
except:
pass
elif event.key == pygame.K_RETURN:
if current_index == 0: # Can't select the instruction
continue
try:
sounds['menu-select'].play()
time.sleep(sounds['menu-select'].get_length())
except:
pass
return options[current_index - 1] # Adjust for instruction offset
elif event.type == pygame.QUIT:
return None
pygame.event.pump()
pygame.event.clear()
time.sleep(0.001)
def game_menu(sounds, playCallback=None, *customOptions):
"""Display and handle the main game menu with standard and custom options.
@@ -96,6 +184,7 @@ def game_menu(sounds, playCallback=None, *customOptions):
pygame.mixer.stop()
currentIndex = 0
lastSpoken = -1 # Track last spoken index
pygame.event.clear()
while loop:
if currentIndex != lastSpoken:
@@ -249,7 +338,8 @@ def game_menu(sounds, playCallback=None, *customOptions):
pass
return allOptions[currentIndex]
event = pygame.event.clear()
pygame.event.pump()
pygame.event.clear()
time.sleep(0.001)
def learn_sounds(sounds):
@@ -334,6 +424,7 @@ def learn_sounds(sounds):
# Flag to track when to exit the loop
returnToMenu = False
pygame.event.clear()
while not returnToMenu:
# Announce current sound
@@ -442,9 +533,9 @@ def credits():
def donate():
"""Open the donation webpage.
Opens the Ko-fi donation page.
Opens the donation page.
"""
webbrowser.open('https://ko-fi.com/stormux')
webbrowser.open('https://www.paypal.com/donate/?business=stormdragon2976@gmail.com&no_recurring=0&currency_code=USD')
messagebox("The donation page has been opened in your browser.")
def exit_game(fade=0):
+4
View File
@@ -73,6 +73,8 @@ class Sound:
if event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_RETURN, pygame.K_SPACE]:
pygame.mixer.stop()
return None
pygame.event.pump()
pygame.event.clear()
pygame.time.delay(10)
return None
@@ -335,6 +337,8 @@ def _play_cutscene(sound, sounds=None):
if event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_RETURN, pygame.K_SPACE]:
pygame.mixer.stop()
return None
pygame.event.pump()
pygame.event.clear()
pygame.time.delay(10)
return None
+41 -2
View File
@@ -111,12 +111,51 @@ class Game:
# Load sound effects
self.sound
# Play intro sound if available
# Play intro sound if available, optionally with visual logo
if 'game-intro' in self.sound.sounds:
self.sound.cut_scene('game-intro')
self._show_logo_with_audio('game-intro')
return self
def _show_logo_with_audio(self, audioKey):
"""Show visual logo while playing audio intro.
Args:
audioKey (str): Key of the audio to play
"""
# Look for logo image files in common formats
logoFiles = ['logo.png', 'logo.jpg', 'logo.jpeg', 'logo.gif', 'logo.bmp']
logoImage = None
for logoFile in logoFiles:
if os.path.exists(logoFile):
try:
logoImage = pygame.image.load(logoFile)
break
except pygame.error:
continue
if logoImage:
# Display logo while audio plays
screen = pygame.display.get_surface()
screenRect = screen.get_rect()
logoRect = logoImage.get_rect(center=screenRect.center)
# Clear screen to black
screen.fill((0, 0, 0))
screen.blit(logoImage, logoRect)
pygame.display.flip()
# Play audio and wait for it to finish
self.sound.cut_scene(audioKey)
# Clear screen after audio finishes
screen.fill((0, 0, 0))
pygame.display.flip()
else:
# No logo image found, just play audio
self.sound.cut_scene(audioKey)
def speak(self, text, interrupt=True):
"""Speak text using the speech system.