Moved the high score stuff to the scoreboard because it makes more sense for it to be there even though it is used in the menu.

This commit is contained in:
Storm Dragon
2025-03-15 18:18:22 -04:00
parent 3f8385599b
commit 1dc0ac2a7f
3 changed files with 114 additions and 256 deletions

250
menu.py
View File

@ -22,6 +22,7 @@ from inspect import isfunction
from .speech import messagebox, Speech
from .sound import adjust_master_volume, adjust_bgm_volume, adjust_sfx_volume, play_bgm
from .display import display_text
from .scoreboard import Scoreboard
from .services import PathService, ConfigService
def game_menu(sounds, playCallback=None, *customOptions):
@ -60,7 +61,7 @@ def game_menu(sounds, playCallback=None, *customOptions):
allOptions = ["play"]
# Add high scores option if scores exist
if has_high_scores():
if Scoreboard.hasHighScores():
allOptions.append("high_scores")
# Add custom options (other menu items, etc.)
@ -203,7 +204,7 @@ def game_menu(sounds, playCallback=None, *customOptions):
elif selectedOption == "learn_sounds":
learn_sounds(sounds)
elif selectedOption == "high_scores":
high_scores()
Scoreboard.displayHighScores()
elif selectedOption == "donate":
donate()
@ -243,246 +244,5 @@ def game_menu(sounds, playCallback=None, *customOptions):
event = pygame.event.clear()
time.sleep(0.001)
def learn_sounds(sounds):
"""Interactive menu for learning game sounds.
Allows users to:
- Navigate through available sounds
- Play selected sounds
- Return to menu with escape key
Args:
sounds (dict): Dictionary of available sound objects
Returns:
str: "menu" if user exits with escape
"""
# Get speech instance
speech = Speech.get_instance()
currentIndex = 0
# Get list of available sounds, excluding special sounds
soundFiles = [f for f in listdir("sounds/")
if isfile(join("sounds/", f))
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
# Flag to track when to exit the loop
returnToMenu = False
while not returnToMenu:
if currentIndex != lastSpoken:
speech.speak(soundFiles[currentIndex][:-4])
lastSpoken = currentIndex
event = pygame.event.wait()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
returnToMenu = True
if event.key in [pygame.K_DOWN, pygame.K_s] and currentIndex < len(soundFiles) - 1:
pygame.mixer.stop()
currentIndex += 1
if event.key in [pygame.K_UP, pygame.K_w] and currentIndex > 0:
pygame.mixer.stop()
currentIndex -= 1
if event.key == pygame.K_RETURN:
try:
soundName = soundFiles[currentIndex][:-4]
pygame.mixer.stop()
sounds[soundName].play()
except:
lastSpoken = -1
speech.speak("Could not play sound.")
event = pygame.event.clear()
time.sleep(0.001)
return "menu"
def has_high_scores():
"""Check if the current game has any high scores.
Returns:
bool: True if at least one high score exists, False otherwise
"""
try:
# Get PathService to access game name
pathService = PathService.get_instance()
gameName = pathService.gameName
# Ensure path service is properly initialized
if not pathService.gamePath:
pathService.initialize(gameName)
# Get the config file path
configPath = os.path.join(pathService.gamePath, "config.ini")
# If config file doesn't exist, there are no scores
if not os.path.exists(configPath):
return False
# Ensure config service is properly connected to path service
configService = ConfigService.get_instance()
configService.set_game_info(gameName, pathService)
# Create scoreboard using the properly initialized services
from .scoreboard import Scoreboard
board = Scoreboard(0, configService)
# Force a read of local config to ensure fresh data
configService.read_local_config()
# Get high scores
scores = board.get_high_scores()
# Check if any score is greater than zero
return any(score['score'] > 0 for score in scores)
except Exception as e:
print(f"Error checking high scores: {e}")
return False
def high_scores():
"""Display high scores for the current game.
Reads the high scores from Scoreboard class.
Shows the game name at the top followed by the available scores.
"""
try:
# Get PathService to access game name
pathService = PathService.get_instance()
gameName = pathService.gameName
# Ensure path service is properly initialized
if not pathService.gamePath:
pathService.initialize(gameName)
# Ensure config service is properly connected to path service
configService = ConfigService.get_instance()
configService.set_game_info(gameName, pathService)
# Create scoreboard using the properly initialized services
from .scoreboard import Scoreboard
board = Scoreboard(0, configService)
# Force a read of local config to ensure fresh data
configService.read_local_config()
# Get high scores
scores = board.get_high_scores()
# Filter out scores with zero points
validScores = [score for score in scores if score['score'] > 0]
# Prepare the lines to display
lines = [f"High Scores for {gameName}:"]
# Add scores to the display list
if validScores:
for i, entry in enumerate(validScores, 1):
scoreStr = f"{i}. {entry['name']}: {entry['score']}"
lines.append(scoreStr)
else:
lines.append("No high scores yet.")
# Display the high scores
display_text(lines)
except Exception as e:
print(f"Error displaying high scores: {e}")
info = ["Could not display high scores."]
display_text(info)
def instructions():
"""Display game instructions from file.
Reads and displays instructions from 'files/instructions.txt'.
If file is missing, displays an error message.
"""
try:
with open('files/instructions.txt', 'r') as f:
info = f.readlines()
except:
info = ["Instructions file is missing."]
display_text(info)
def credits():
"""Display game credits from file.
Reads and displays credits from 'files/credits.txt'.
Adds game name header before displaying.
If file is missing, displays an error message.
"""
try:
with open('files/credits.txt', 'r') as f:
info = f.readlines()
pathService = PathService.get_instance()
info.insert(0, pathService.gameName + "\n")
except Exception as e:
print(f"Error in credits: {e}")
info = ["Credits file is missing."]
display_text(info)
def donate():
"""Open the donation webpage.
Opens the Ko-fi donation page.
"""
webbrowser.open('https://ko-fi.com/stormux')
messagebox("The donation page has been opened in your browser.")
def exit_game(fade=0):
"""Clean up and exit the game properly.
Args:
fade (int): Milliseconds to fade out music before exiting.
0 means stop immediately (default)
"""
# Force clear any pending events to prevent hanging
pygame.event.clear()
# Stop all mixer channels first
try:
pygame.mixer.stop()
except Exception as e:
print(f"Warning: Could not stop mixer channels: {e}")
# Get speech instance and handle all providers
try:
speech = Speech.get_instance()
# Try to close speech regardless of provider type
try:
speech.close()
except Exception as e:
print(f"Warning: Could not close speech: {e}")
except Exception as e:
print(f"Warning: Could not get speech instance: {e}")
# Handle music based on fade parameter
try:
if fade > 0 and pygame.mixer.music.get_busy():
pygame.mixer.music.fadeout(fade)
# Wait for fade to start but don't wait for full completion
pygame.time.wait(min(250, fade))
else:
pygame.mixer.music.stop()
except Exception as e:
print(f"Warning: Could not handle music during exit: {e}")
# Clean up pygame
try:
pygame.quit()
except Exception as e:
print(f"Warning: Error during pygame.quit(): {e}")
# Use os._exit for immediate termination
import os
os._exit(0)
# Rest of menu.py functions here...
# (learn_sounds, instructions, credits, donate, exit_game)