333 lines
12 KiB
Python
333 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Modified Scoreboard class with integrated fixes.
|
|
|
|
This code should replace the existing Scoreboard class in scoreboard.py.
|
|
The modifications ensure proper path handling and config operations.
|
|
"""
|
|
|
|
import time
|
|
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
|
|
|
|
class Scoreboard:
|
|
"""Handles high score tracking with player names."""
|
|
|
|
def __init__(self, score=0, configService=None, speech=None):
|
|
"""Initialize scoreboard.
|
|
|
|
Args:
|
|
score (int): Initial score (default: 0)
|
|
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
|
|
self.highScores = []
|
|
|
|
# For backward compatibility
|
|
read_config()
|
|
|
|
try:
|
|
# Try to use configService
|
|
self.configService.localConfig.add_section("scoreboard")
|
|
except:
|
|
# Fallback to old method
|
|
try:
|
|
localConfig.add_section("scoreboard")
|
|
except:
|
|
pass
|
|
|
|
# Load existing high scores
|
|
for i in range(1, 11):
|
|
try:
|
|
# Try to use configService
|
|
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
|
|
})
|
|
except:
|
|
# Fallback to old method
|
|
try:
|
|
score = localConfig.getint("scoreboard", f"score_{i}")
|
|
name = localConfig.get("scoreboard", f"name_{i}")
|
|
self.highScores.append({
|
|
'name': name,
|
|
'score': score
|
|
})
|
|
except:
|
|
self.highScores.append({
|
|
'name': "Player",
|
|
'score': 0
|
|
})
|
|
|
|
# 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
|
|
|
|
def get_high_scores(self):
|
|
"""Get list of high scores."""
|
|
return self.highScores
|
|
|
|
def decrease_score(self, points=1):
|
|
"""Decrease the current score."""
|
|
self.currentScore -= int(points)
|
|
return self
|
|
|
|
def increase_score(self, points=1):
|
|
"""Increase the current score."""
|
|
self.currentScore += int(points)
|
|
return self
|
|
|
|
def set_score(self, score):
|
|
"""Set the current score to a specific value."""
|
|
self.currentScore = int(score)
|
|
return self
|
|
|
|
def reset_score(self):
|
|
"""Reset the current score to zero."""
|
|
self.currentScore = 0
|
|
return self
|
|
|
|
def check_high_score(self):
|
|
"""Check if current score qualifies as a high score.
|
|
|
|
Returns:
|
|
int: Position (1-10) if high score, None if not
|
|
"""
|
|
for i, entry in enumerate(self.highScores):
|
|
if self.currentScore > entry['score']:
|
|
return i + 1
|
|
return None
|
|
|
|
def add_high_score(self, name=None):
|
|
"""Add current score to high scores if it qualifies.
|
|
|
|
Args:
|
|
name (str): Player name (if None, will prompt user)
|
|
|
|
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
|
|
|
|
# Get player name
|
|
if name is None:
|
|
# Import get_input here to avoid circular imports
|
|
from .input import get_input
|
|
name = get_input("New high score! Enter your name:", "Player")
|
|
if name is None: # User cancelled
|
|
name = "Player"
|
|
|
|
# Insert new score at correct position
|
|
self.highScores.insert(position - 1, {
|
|
'name': name,
|
|
'score': self.currentScore
|
|
})
|
|
|
|
# Keep only top 10
|
|
self.highScores = self.highScores[:10]
|
|
|
|
# Save to config - try both methods for maximum compatibility
|
|
try:
|
|
# Try new method first
|
|
for i, entry in enumerate(self.highScores):
|
|
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']))
|
|
localConfig.set("scoreboard", f"name_{i+1}", entry['name'])
|
|
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']))
|
|
localConfig.set("scoreboard", f"name_{i+1}", entry['name'])
|
|
write_config()
|
|
|
|
# Announce success
|
|
try:
|
|
self.speech.messagebox(f"Congratulations {name}! You got position {position} on the scoreboard!")
|
|
except:
|
|
# Fallback to global speak function
|
|
from .speech import speak
|
|
speak(f"Congratulations {name}! You got position {position} on the 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
|