Compare commits
	
		
			10 Commits
		
	
	
		
			fe772cbb1e
			...
			27765e62bc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 27765e62bc | ||
|  | 23aea6badf | ||
|  | af38d5af76 | ||
|  | 4d0436c5a9 | ||
|  | f51bd6dee4 | ||
|  | 3b2bcd928d | ||
|  | 91f39aad88 | ||
|  | 1dc0ac2a7f | ||
|  | 3f8385599b | ||
|  | 468c663cc1 | 
							
								
								
									
										12
									
								
								__init__.py
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								__init__.py
									
									
									
									
									
								
							| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								menu.py
									
									
									
									
									
								
							| @@ -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() | ||||
|                              | ||||
|   | ||||
							
								
								
									
										176
									
								
								scoreboard.py
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								scoreboard.py
									
									
									
									
									
								
							| @@ -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 | ||||
| @@ -71,6 +76,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.""" | ||||
|         return self.currentScore | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user