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:
parent
3f8385599b
commit
1dc0ac2a7f
@ -51,7 +51,11 @@ 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, high_scores, has_high_scores
|
||||
from .menu import game_menu, learn_sounds, instructions, credits, donate, exit_game
|
||||
|
||||
# Update imports to reference Scoreboard methods
|
||||
high_scores = Scoreboard.displayHighScores
|
||||
has_high_scores = Scoreboard.hasHighScores
|
||||
|
||||
# Import utility functions and Game class
|
||||
from .utils import (
|
||||
|
250
menu.py
250
menu.py
@ -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)
|
||||
|
114
scoreboard.py
114
scoreboard.py
@ -8,8 +8,10 @@ Provides functionality for:
|
||||
"""
|
||||
|
||||
import time
|
||||
from .services import ConfigService
|
||||
import os
|
||||
from .services import ConfigService, PathService
|
||||
from .speech import Speech
|
||||
from .display import display_text
|
||||
|
||||
# For backward compatibility
|
||||
from .config import localConfig, write_config, read_config
|
||||
@ -71,35 +73,35 @@ class Scoreboard:
|
||||
# Sort high scores by score value in descending order
|
||||
self.highScores.sort(key=lambda x: x['score'], reverse=True)
|
||||
|
||||
def get_score(self):
|
||||
def getScore(self):
|
||||
"""Get current score."""
|
||||
return self.currentScore
|
||||
|
||||
def get_high_scores(self):
|
||||
def getHighScores(self):
|
||||
"""Get list of high scores."""
|
||||
return self.highScores
|
||||
|
||||
def decrease_score(self, points=1):
|
||||
def decreaseScore(self, points=1):
|
||||
"""Decrease the current score."""
|
||||
self.currentScore -= int(points)
|
||||
return self
|
||||
|
||||
def increase_score(self, points=1):
|
||||
def increaseScore(self, points=1):
|
||||
"""Increase the current score."""
|
||||
self.currentScore += int(points)
|
||||
return self
|
||||
|
||||
def set_score(self, score):
|
||||
def setScore(self, score):
|
||||
"""Set the current score to a specific value."""
|
||||
self.currentScore = int(score)
|
||||
return self
|
||||
|
||||
def reset_score(self):
|
||||
def resetScore(self):
|
||||
"""Reset the current score to zero."""
|
||||
self.currentScore = 0
|
||||
return self
|
||||
|
||||
def check_high_score(self):
|
||||
def checkHighScore(self):
|
||||
"""Check if current score qualifies as a high score.
|
||||
|
||||
Returns:
|
||||
@ -110,7 +112,7 @@ class Scoreboard:
|
||||
return i + 1
|
||||
return None
|
||||
|
||||
def add_high_score(self, name=None):
|
||||
def addHighScore(self, name=None):
|
||||
"""Add current score to high scores if it qualifies.
|
||||
|
||||
Args:
|
||||
@ -119,7 +121,7 @@ class Scoreboard:
|
||||
Returns:
|
||||
bool: True if score was added, False if not
|
||||
"""
|
||||
position = self.check_high_score()
|
||||
position = self.checkHighScore()
|
||||
if position is None:
|
||||
return False
|
||||
|
||||
@ -174,3 +176,95 @@ class Scoreboard:
|
||||
|
||||
time.sleep(1)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def hasHighScores():
|
||||
"""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
|
||||
board = Scoreboard(0, configService)
|
||||
|
||||
# Force a read of local config to ensure fresh data
|
||||
configService.read_local_config()
|
||||
|
||||
# Get high scores
|
||||
scores = board.getHighScores()
|
||||
|
||||
# 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
|
||||
|
||||
@staticmethod
|
||||
def displayHighScores():
|
||||
"""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
|
||||
board = Scoreboard(0, configService)
|
||||
|
||||
# Force a read of local config to ensure fresh data
|
||||
configService.read_local_config()
|
||||
|
||||
# Get high scores
|
||||
scores = board.getHighScores()
|
||||
|
||||
# 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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user