Compare commits

...

10 Commits

Author SHA1 Message Date
Storm Dragon
27765e62bc Finally, a working scoreboard! 2025-03-15 19:52:16 -04:00
Storm Dragon
23aea6badf More fixes for scoreboard. 2025-03-15 19:06:29 -04:00
Storm Dragon
af38d5af76 Fixed another error in scoreboard. 2025-03-15 18:41:35 -04:00
Storm Dragon
4d0436c5a9 Fixed typo in menu.py. 2025-03-15 18:34:30 -04:00
Storm Dragon
f51bd6dee4 Fixed errors in __init__ 2025-03-15 18:31:43 -04:00
Storm Dragon
3b2bcd928d Fixed errors in scoreboard. 2025-03-15 18:29:55 -04:00
Storm Dragon
91f39aad88 Oops, I accidently committed my scrp file for menu instead of the actual file. 2025-03-15 18:22:21 -04:00
Storm Dragon
1dc0ac2a7f 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. 2025-03-15 18:18:22 -04:00
Storm Dragon
3f8385599b Work on fixing the path read for scores. 2025-03-15 17:28:32 -04:00
Storm Dragon
468c663cc1 Integrate scoreboard with game_menu() 2025-03-15 17:07:51 -04:00
3 changed files with 191 additions and 23 deletions

View File

@@ -53,6 +53,10 @@ from .display import display_text, initialize_gui
# Import menu functions
from .menu import game_menu, learn_sounds, instructions, credits, donate, exit_game
# Update imports to reference Scoreboard methods
high_scores = Scoreboard.display_high_scores
has_high_scores = Scoreboard.has_high_scores
# Import utility functions and Game class
from .utils import (
Game,
@@ -109,15 +113,15 @@ __all__ = [
'display_text', 'initialize_gui',
# Menu
'game_menu', 'learn_sounds', 'instructions', 'credits', 'donate', 'exit_game',
'game_menu', 'learn_sounds', 'instructions', 'credits', 'donate', 'exit_game', 'high_scores', 'has_high_scores',
# Game class
'Game',
# Utils
'check_for_updates', 'get_version_tuple', 'check_compatibility',
'sanitize_filename', 'lerp', 'smooth_step', 'distance_2d',
'x_powerbar', 'y_powerbar', 'generate_tone',
'check_for_updates', 'get_version_tuple', 'check_compatibility',
'sanitize_filename', 'lerp', 'smooth_step', 'distance_2d',
'x_powerbar', 'y_powerbar', 'generate_tone',
# Re-exported functions from pygame, math, random
'get_ticks', 'delay', 'wait',

26
menu.py
View File

@@ -22,19 +22,21 @@ 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 .services import PathService
from .scoreboard import Scoreboard
from .services import PathService, ConfigService
def game_menu(sounds, playCallback=None, *customOptions):
"""Display and handle the main game menu with standard and custom options.
Standard menu structure:
1. Play (always first)
2. Custom options (high scores, etc.)
3. Learn Sounds
4. Instructions (if available)
5. Credits (if available)
6. Donate
7. Exit
2. High Scores
3. Custom options (if provided)
4. Learn Sounds
5. Instructions (if available)
6. Credits (if available)
7. Donate
8. Exit
Handles navigation with:
- Up/Down arrows for selection
@@ -58,7 +60,11 @@ def game_menu(sounds, playCallback=None, *customOptions):
# Start with Play option
allOptions = ["play"]
# Add custom options (high scores, etc.)
# Add high scores option if scores exist
if Scoreboard.has_high_scores():
allOptions.append("high_scores")
# Add custom options (other menu items, etc.)
allOptions.extend(customOptions)
# Add standard options in preferred order
@@ -183,7 +189,7 @@ def game_menu(sounds, playCallback=None, *customOptions):
# Otherwise return "play" to the caller
return "play"
# Handle standard options directly
elif selectedOption in ["instructions", "credits", "learn_sounds", "donate"]:
elif selectedOption in ["instructions", "credits", "learn_sounds", "high_scores", "donate"]:
# Pause music before calling the selected function
try:
pygame.mixer.music.pause()
@@ -197,6 +203,8 @@ def game_menu(sounds, playCallback=None, *customOptions):
credits()
elif selectedOption == "learn_sounds":
learn_sounds(sounds)
elif selectedOption == "high_scores":
Scoreboard.display_high_scores()
elif selectedOption == "donate":
donate()

View File

@@ -1,15 +1,17 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Scoreboard handling for Storm Games.
"""
Modified Scoreboard class with integrated fixes.
Provides functionality for:
- Tracking high scores with player names
- Saving/loading high scores from configuration
This code should replace the existing Scoreboard class in scoreboard.py.
The modifications ensure proper path handling and config operations.
"""
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
@@ -25,6 +27,9 @@ class Scoreboard:
configService (ConfigService): Config service (default: global instance)
speech (Speech): Speech system (default: global instance)
"""
# Ensure services are properly initialized
self._ensure_services()
self.configService = configService or ConfigService.get_instance()
self.speech = speech or Speech.get_instance()
self.currentScore = score
@@ -35,7 +40,7 @@ class Scoreboard:
try:
# Try to use configService
self.configService.local_config.add_section("scoreboard")
self.configService.localConfig.add_section("scoreboard")
except:
# Fallback to old method
try:
@@ -47,8 +52,8 @@ class Scoreboard:
for i in range(1, 11):
try:
# Try to use configService
score = self.configService.local_config.getint("scoreboard", f"score_{i}")
name = self.configService.local_config.get("scoreboard", f"name_{i}")
score = self.configService.localConfig.getint("scoreboard", f"score_{i}")
name = self.configService.localConfig.get("scoreboard", f"name_{i}")
self.highScores.append({
'name': name,
'score': score
@@ -70,6 +75,37 @@ class Scoreboard:
# Sort high scores by score value in descending order
self.highScores.sort(key=lambda x: x['score'], reverse=True)
def _ensure_services(self):
"""Ensure PathService and ConfigService are properly initialized."""
# Get PathService and make sure it has a game name
pathService = PathService.get_instance()
# If no game name yet, try to get from pygame window title
if not pathService.gameName:
try:
import pygame
if pygame.display.get_caption()[0]:
pathService.gameName = pygame.display.get_caption()[0]
except:
pass
# Initialize path service if we have a game name but no paths set up
if pathService.gameName and not pathService.gamePath:
pathService.initialize(pathService.gameName)
# Get ConfigService and connect to PathService
configService = ConfigService.get_instance()
if not hasattr(configService, 'pathService') or not configService.pathService:
if pathService.gameName:
configService.set_game_info(pathService.gameName, pathService)
# Ensure the game directory exists
if pathService.gamePath and not os.path.exists(pathService.gamePath):
try:
os.makedirs(pathService.gamePath)
except Exception as e:
print(f"Error creating game directory: {e}")
def get_score(self):
"""Get current score."""
@@ -119,6 +155,9 @@ class Scoreboard:
Returns:
bool: True if score was added, False if not
"""
# Ensure services are properly set up
self._ensure_services()
position = self.check_high_score()
if position is None:
return False
@@ -144,13 +183,14 @@ class Scoreboard:
try:
# Try new method first
for i, entry in enumerate(self.highScores):
self.configService.local_config.set("scoreboard", f"score_{i+1}", str(entry['score']))
self.configService.local_config.set("scoreboard", f"name_{i+1}", entry['name'])
self.configService.localConfig.set("scoreboard", f"score_{i+1}", str(entry['score']))
self.configService.localConfig.set("scoreboard", f"name_{i+1}", entry['name'])
# Try to write with configService
try:
self.configService.write_local_config()
except Exception as e:
print(f"Error writing config with configService: {e}")
# Fallback to old method if configService fails
for i, entry in enumerate(self.highScores):
localConfig.set("scoreboard", f"score_{i+1}", str(entry['score']))
@@ -158,6 +198,7 @@ class Scoreboard:
write_config()
except Exception as e:
print(f"Error writing high scores: {e}")
# If all else fails, try direct old method
for i, entry in enumerate(self.highScores):
localConfig.set("scoreboard", f"score_{i+1}", str(entry['score']))
@@ -174,3 +215,118 @@ class Scoreboard:
time.sleep(1)
return True
@staticmethod
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
# If no game name, try to get from window title
if not gameName:
try:
import pygame
if pygame.display.get_caption()[0]:
gameName = pygame.display.get_caption()[0]
pathService.gameName = gameName
except:
pass
# Ensure path service is properly initialized
if gameName and 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.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
@staticmethod
def display_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
# If no game name, try to get from window title
if not gameName:
try:
import pygame
if pygame.display.get_caption()[0]:
gameName = pygame.display.get_caption()[0]
pathService.gameName = gameName
except:
pass
# Ensure path service is properly initialized
if gameName and 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.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)
# For backward compatibility with older code that might call displayHigh_scores
displayHigh_scores = display_high_scores