FEAT [Leaderboard] - Add infinite mode points counter : using DB it's displayed in the Leaderboard too

This commit is contained in:
ClementHVT
2025-04-10 10:21:44 +02:00
parent 50d4b5f82c
commit 86ca9332a7
5 changed files with 103 additions and 88 deletions

View File

@@ -5,24 +5,20 @@ import os
class InfiniteModeDB:
def __init__(self, db_file="game.db"):
"""
Initialize database connection for infinite game mode points management
Initialize database connection for infinite game mode points management.
Args:
db_file: SQLite database file path
db_file: SQLite database file path.
"""
# Create database directory if it doesn't exist
os.makedirs(
os.path.dirname(db_file) if os.path.dirname(db_file) else ".", exist_ok=True
)
self.conn = sqlite3.connect(db_file)
self.cursor = self.conn.cursor()
self._create_tables()
self.clear_InfiniteModeDB()
def _create_tables(self):
print("Creating infinite mode table if it doesn't exist")
"""Create required tables if they don't exist"""
"""Create required tables if they don't exist."""
self.cursor.execute(
"""
CREATE TABLE IF NOT EXISTS InfiniteMode (
@@ -32,24 +28,14 @@ class InfiniteModeDB:
"""
)
self.conn.commit()
def get_all(self):
"""
Get all scores from the table
Returns:
List of tuples containing player name and score
"""
def get_all(self):
"""Get all scores from the table."""
self.cursor.execute("SELECT * FROM InfiniteMode")
return self.cursor.fetchall()
def add_score(self, player_name, score):
"""
Add a new score to the InfiniteMode
Args:
player_name: Name of the player
score: Score to be added
"""
"""Add a new score to the InfiniteMode."""
self.cursor.execute(
"INSERT INTO InfiniteMode (player_name, score) VALUES (?, ?)",
(player_name, score),
@@ -57,13 +43,14 @@ class InfiniteModeDB:
self.conn.commit()
def clear_InfiniteModeDB(self):
"""
Clear all scores from the leaderboard
"""
self.cursor.execute("DELETE FROM InfiniteMode")
self.conn.commit()
"""Clear all scores from the InfiniteMode table."""
try:
self.cursor.execute("DELETE FROM InfiniteMode")
self.conn.commit()
except sqlite3.Error as e:
print(f"Error clearing InfiniteMode table: {e}")
def close(self):
"""Close database connection"""
"""Close database connection."""
if self.conn:
self.conn.close()

View File

@@ -5,23 +5,20 @@ import os
class LeaderboardDB:
def __init__(self, db_file="game.db"):
"""
Initialize database connection for infinite game mode points management
Initialize database connection for leaderboard management.
Args:
db_file: SQLite database file path
db_file: SQLite database file path.
"""
# Create database directory if it doesn't exist
os.makedirs(
os.path.dirname(db_file) if os.path.dirname(db_file) else ".", exist_ok=True
)
self.conn = sqlite3.connect(db_file)
self.cursor = self.conn.cursor()
self._create_tables()
def _create_tables(self):
print("Ensuring the Leaderboard table has the correct schema")
# Create the table with the correct schema
"""Ensure the Leaderboard table has the correct schema."""
self.cursor.execute(
"""
CREATE TABLE IF NOT EXISTS Leaderboard (
@@ -32,36 +29,16 @@ class LeaderboardDB:
"""
)
self.conn.commit()
def get_all(self):
"""
Get all scores from the table limited at 10 rows
Returns:
List of tuples containing player name and score
"""
self.cursor.execute("SELECT score, date FROM Leaderboard LIMIT 10")
return self.cursor.fetchall()
def get_top_3_scores(self):
"""
Get top 3 scores from the leaderboard
Returns:
List of tuples containing player name and score
"""
def get_top_10_scores(self):
"""Get top 10 scores from the leaderboard."""
self.cursor.execute(
"SELECT score, date FROM Leaderboard ORDER BY score DESC LIMIT 3"
"SELECT score, date FROM Leaderboard ORDER BY score DESC LIMIT 10"
)
return self.cursor.fetchall()
def add_score(self, player_name, score):
"""
Add a new score to the InfiniteMode
Args:
player_name: Name of the player
score: Score to be added
"""
"""Add a new score to the leaderboard."""
self.cursor.execute(
"INSERT INTO Leaderboard (player_name, score) VALUES (?, ?)",
(player_name, score),
@@ -69,13 +46,11 @@ class LeaderboardDB:
self.conn.commit()
def clear_leaderboard(self):
"""
Clear all scores from the leaderboard
"""
"""Clear all scores from the leaderboard."""
self.cursor.execute("DELETE FROM Leaderboard")
self.conn.commit()
def close(self):
"""Close database connection"""
"""Close database connection."""
if self.conn:
self.conn.close()

View File

@@ -11,11 +11,12 @@ from src.Database.LevelDB import LevelDB
class Leaderboard:
"""This class represents the leaderboard menu for the game."""
def __init__(self, WIDTH, HEIGHT, font, db_path="game.db"):
def __init__(self, WIDTH, HEIGHT, font, leaderboard_db, db_path="game.db"):
self.WIDTH = WIDTH
self.HEIGHT = HEIGHT
self.font = font
self.db_path = db_path
self.leaderboard_db = leaderboard_db
self.levels = self.get_available_levels()
self.level_tabs = [f"Level {level}" for level in self.levels]
@@ -58,8 +59,23 @@ class Leaderboard:
for i, level in enumerate(self.levels):
self.scores[i] = self.get_level_scores(str(level))
# TO DO: Load scores for infinite mode
self.scores[len(self.levels)] = []
# Load scores for infinite mode
try:
# Get the TOP 10 scores for infinite mode
all_scores = self.leaderboard_db.get_top_10_scores()
# Format the scores for display
formatted_scores = []
for score, date in all_scores:
date_obj = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
formatted_date = date_obj.strftime("%d/%m/%Y")
formatted_scores.append((formatted_date, score))
# Assign the formatted scores to the infinite mode tab
self.scores[len(self.levels)] = formatted_scores
except Exception as e:
print(f"Error loading infinite mode scores: {e}")
self.scores[len(self.levels)] = []
def get_level_scores(self, level_id):
"""Get the top 10 scores for a specific level from the database."""
@@ -104,6 +120,9 @@ class Leaderboard:
def draw(self, surface):
"""Draw the leaderboard on the given surface."""
# Refresh scores to ensure the latest data is displayed
self.load_scores()
self.bg_manager.draw(surface)
# Draw a semi-transparent panel
@@ -159,7 +178,7 @@ class Leaderboard:
(self.WIDTH // 2 - no_scores_text.get_width() // 2, y_pos + 40),
)
else:
for i, (date, time, collected, total) in enumerate(scores_for_tab):
for i, score_data in enumerate(scores_for_tab):
row_bg = (30, 30, 60, 150) if i % 2 == 0 else (40, 40, 80, 150)
row_rect = pygame.Rect(self.WIDTH // 2 - 200, y_pos - 5, 400, 30)
row_surface = pygame.Surface(
@@ -169,28 +188,40 @@ class Leaderboard:
surface.blit(row_surface, row_rect)
# Rank
rank_text = self.font.render(f"{i+1}.", True, (255, 255, 255))
rank_text = self.font.render(f"{i + 1}.", True, (255, 255, 255))
surface.blit(rank_text, (header_positions[0], y_pos))
# Date
date_text = self.font.render(date, True, (255, 255, 255))
surface.blit(date_text, (header_positions[1], y_pos))
if self.current_tab == len(self.levels): # Infinite mode
date, score = score_data
# Date
date_text = self.font.render(date, True, (255, 255, 255))
surface.blit(date_text, (header_positions[1], y_pos))
# Time
time_text = self.font.render(
self.format_time(time), True, (255, 255, 255)
)
surface.blit(time_text, (header_positions[2], y_pos))
# Time (score)
score_text = self.font.render(str(score), True, (255, 255, 255))
surface.blit(score_text, (header_positions[2], y_pos))
else: # Level scores
date, time, collected, total = score_data
# Collected items
collected_color = (255, 255, 255)
if collected == total:
collected_color = (0, 255, 0)
# Date
date_text = self.font.render(date, True, (255, 255, 255))
surface.blit(date_text, (header_positions[1], y_pos))
collected_text = self.font.render(
f"{collected}/{total}", True, collected_color
)
surface.blit(collected_text, (header_positions[3], y_pos))
# Time
time_text = self.font.render(
self.format_time(time), True, (255, 255, 255)
)
surface.blit(time_text, (header_positions[2], y_pos))
# Collected items
collected_color = (255, 255, 255)
if collected == total:
collected_color = (0, 255, 0)
collected_text = self.font.render(
f"{collected}/{total}", True, collected_color
)
surface.blit(collected_text, (header_positions[3], y_pos))
y_pos += 40

View File

@@ -138,13 +138,8 @@ def start_infinite_mode(game_resources):
game_resources.infinite_mode = True
# Open the temporary database
print("Creating leaderboard database")
game_resources.infinite_mode_db = InfiniteModeDB()
# Open the leaderboard database
print("Creating leaderboard database")
game_resources.leaderboard_db = LeaderboardDB()
# Generate the first level
first_level = infinite_manager.start_infinite_mode()

View File

@@ -4,6 +4,7 @@ import sys
from pygame.locals import *
import numpy as np
from src.Database.LeaderboardDB import LeaderboardDB
from src.Database.LevelDB import LevelDB
from src.Entity.Enemy import Enemy
from src.Menu.LevelSelectMenu import LevelSelectMenu
@@ -68,8 +69,9 @@ def initialize_game_resources():
level_select_menu = None
editor_select_menu = None
level_file = "map/levels/1.json"
leaderboard_db = LeaderboardDB()
leaderboard = Leaderboard(
game_resources.WIDTH, game_resources.HEIGHT, game_resources.font
game_resources.WIDTH, game_resources.HEIGHT, game_resources.font, leaderboard_db
)
return (
@@ -88,7 +90,8 @@ def initialize_game_resources():
leaderboard,
projectiles,
joysticks,
editor_select_menu, # Added editor_select_menu to the return tuple
editor_select_menu,
leaderboard_db,
)
@@ -498,6 +501,11 @@ def handle_exits(
speedrun_timer.save_time(collected_coins, total_coins)
if hasattr(game_resources, "infinite_mode") and game_resources.infinite_mode:
# Infinite mode: load the next level without going back to menu
if hasattr(game_resources, "infinite_mode_db"):
# Zeldo : add 100 points
game_resources.infinite_mode_db.add_score("player", 100)
# Add coins points also
game_resources.infinite_mode_db.add_score("player", P1.coins * 10)
result = handle_exit_collision(exit, game_resources, level_file)
return {"action": "continue_infinite", "result": result}
else:
@@ -545,6 +553,7 @@ def draw_ui_elements(displaysurface, P1, FramePerSec, font, speedrun_timer=None)
def handle_death_screen(
P1,
displaysurface,
death_timer,
dt,
@@ -555,6 +564,7 @@ def handle_death_screen(
game_resources,
WIDTH,
HEIGHT,
leaderboard_db,
):
"""Handle player death screen"""
# Fill background
@@ -590,6 +600,20 @@ def handle_death_screen(
"projectiles": projectiles,
}
else:
if hasattr(game_resources, "infinite_mode_db"):
# Save score to database
game_resources.infinite_mode_db.add_score("player", P1.coins * 10)
# Get all scores from the database
all_scores = game_resources.infinite_mode_db.get_all()
game_resources.infinite_mode_db.clear_InfiniteModeDB()
game_resources.infinite_mode_db.close()
# Calculate total points, add them to leaderboard table
if(leaderboard_db):
total = 0
for i in range(len(all_scores)):
total += all_scores[i][1]
leaderboard_db.add_score("player", total)
# Return to menu
if hasattr(game_resources, "infinite_mode"):
game_resources.infinite_mode = False
@@ -626,6 +650,7 @@ def handler():
projectiles,
joysticks,
editor_select_menu,
leaderboard_db,
) = initialize_game_resources()
# Initialize editor variables
@@ -859,6 +884,7 @@ def handler():
elif current_state == DEATH_SCREEN:
# Handle death screen
death_result = handle_death_screen(
P1,
displaysurface,
death_timer,
dt,
@@ -869,6 +895,7 @@ def handler():
game_resources,
game_resources.WIDTH,
game_resources.HEIGHT,
leaderboard_db
)
death_timer = death_result["death_timer"]