diff --git a/.gitignore b/.gitignore index 9c881c5..a96374f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .venv/* .idea -.idea/* \ No newline at end of file +.idea/* + +checkpoint.db \ No newline at end of file diff --git a/main.py b/main.py index 285a3df..1d90a46 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ import sys from pygame.locals import * from src.Entity.Enemy import Enemy -from src.game import initialize_game, reset_game +from src.game import initialize_game, reset_game, reset_game_with_checkpoint from src.constant import ( displaysurface, FramePerSec, @@ -18,7 +18,7 @@ from src.constant import ( from src.Menu.Menu import Menu from src.Menu.Leaderboard import Leaderboard from src.Camera import Camera -from src.Entity.Projectile import Projectile +from src.Database.CheckpointDB import CheckpointDB def main(): @@ -40,7 +40,9 @@ def main(): leaderboard = Leaderboard() # Initialize game components - P1, PT1, platforms, all_sprites, background = initialize_game("map_test.json") + P1, PT1, platforms, all_sprites, background, checkpoints = initialize_game( + "map_test.json" + ) projectiles = pygame.sprite.Group() # Main game loop @@ -80,7 +82,20 @@ def main(): ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h elif event.type == USEREVENT: if event.action == "player_death": - current_state = MENU + db = CheckpointDB() + checkpoint_pos = db.get_checkpoint("map_test.json") + + if checkpoint_pos: + # Respawn player at checkpoint + P1, platforms, all_sprites, background, checkpoints = ( + reset_game_with_checkpoint("map_test.json") + ) + projectiles.empty() + print("Joueur réanimé au checkpoint") + else: + # No checkpoint found, return to menu + current_state = MENU + print("Game over - retour au menu") if event.dict.get("action") == "create_projectile": projectile = event.dict.get("projectile") projectiles.add(projectile) @@ -89,7 +104,7 @@ def main(): if current_state == MENU: action = menu.handle_event(event) if action == "play": - P1, platforms, all_sprites, background = reset_game() + P1, platforms, all_sprites, background, checkpoints = reset_game() current_state = PLAYING elif action == "infinite": current_state = INFINITE @@ -156,6 +171,10 @@ def main(): f"Projectile: pos={projectile.pos}, rect={projectile.rect}, camera={camera.camera}" ) + checkpoints_hit = pygame.sprite.spritecollide(P1, checkpoints, False) + for checkpoint in checkpoints_hit: + checkpoint.activate() + # Display FPS and coordinates (fixed position UI elements) fps = int(FramePerSec.get_fps()) fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255)) diff --git a/src/Database/CheckpointDB.py b/src/Database/CheckpointDB.py new file mode 100644 index 0000000..a0b2825 --- /dev/null +++ b/src/Database/CheckpointDB.py @@ -0,0 +1,70 @@ +import sqlite3 +import os + + +class CheckpointDB: + def __init__(self, db_file="checkpoint.db"): + """ + Initialize database connection for checkpoint management + + Args: + 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): + """Create required tables if they don't exist""" + self.cursor.execute( + """ + CREATE TABLE IF NOT EXISTS checkpoints ( + map_name TEXT PRIMARY KEY, + pos_x REAL, + pos_y REAL, + timestamp INTEGER + ) + """ + ) + self.conn.commit() + + def save_checkpoint(self, map_name, pos_x, pos_y): + """ + Save or update checkpoint position + + Args: + map_name: Name of the current map + pos_x: X coordinate + pos_y: Y coordinate + """ + self.cursor.execute( + "INSERT OR REPLACE INTO checkpoints (map_name, pos_x, pos_y, timestamp) VALUES (?, ?, ?, strftime('%s'))", + (map_name, pos_x, pos_y), + ) + self.conn.commit() + + def get_checkpoint(self, map_name): + """ + Get saved checkpoint position for a map + + Args: + map_name: Map name to query + + Returns: + Tuple (x, y) if checkpoint exists, None otherwise + """ + self.cursor.execute( + "SELECT pos_x, pos_y FROM checkpoints WHERE map_name = ?", (map_name,) + ) + result = self.cursor.fetchone() + return result if result else None + + def close(self): + """Close database connection""" + if self.conn: + self.conn.close() diff --git a/src/Entity/Checkpoint.py b/src/Entity/Checkpoint.py new file mode 100644 index 0000000..2e46dad --- /dev/null +++ b/src/Entity/Checkpoint.py @@ -0,0 +1,48 @@ +from src.Entity.Entity import Entity +import pygame +from pygame.math import Vector2 as vec +from src.Database.CheckpointDB import CheckpointDB + + +class Checkpoint(Entity): + def __init__( + self, pos, size=(50, 50), color=(0, 255, 0), texture_path="", map_name="default" + ): + """ + Initialize a checkpoint entity + + Args: + pos (tuple): Position of the checkpoint (x, y) + size (tuple): Size of the checkpoint (width, height) + color (tuple): Default color if no texture is provided + texture_path (str): Path to the checkpoint texture + map_name (str): Name of the current map + """ + super().__init__(pos, size, color, texture_path) + self.activated = False + self.map_name = map_name + self.default_color = color + self.activated_color = (0, 100, 0) # Dark green + self.db = CheckpointDB() + + def activate(self): + """ + Activate the checkpoint if not already activated. + Save coordinates to database and change color. + + Returns: + bool: True if newly activated, False if already activated + """ + if not self.activated: + self.activated = True + # Change color to dark green + if not hasattr(self, "original_surf"): + self.original_surf = self.surf.copy() + self.surf.fill(self.activated_color) + + # Save checkpoint to database + self.db.save_checkpoint(self.map_name, self.pos.x, self.pos.y) + + print("checkpoint") + return True + return False diff --git a/src/Map/parser.py b/src/Map/parser.py index 223ca53..8bc0e3d 100644 --- a/src/Map/parser.py +++ b/src/Map/parser.py @@ -4,6 +4,7 @@ import os from src.Entity.Platform import Platform from src.Entity.Player import Player from src.Entity.Enemy import Enemy +from src.Entity.Checkpoint import Checkpoint from src.constant import WIDTH, HEIGHT, all_sprites, platforms @@ -13,6 +14,7 @@ class MapParser: self.platforms = platforms self.enemies = pygame.sprite.Group() self.collectibles = pygame.sprite.Group() + self.checkpoints = pygame.sprite.Group() self.player = None def load_map(self, map_file): @@ -22,7 +24,7 @@ class MapParser: map_data = json.load(file) # Create all game objects from map data - self.create_map_objects(map_data) + self.create_map_objects(map_data, map_file) return { "player": self.player, @@ -35,18 +37,20 @@ class MapParser: "width": map_data.get("width", WIDTH), "height": map_data.get("height", HEIGHT), }, + "checkpoints": self.checkpoints, } except Exception as e: print(f"Error loading map: {e}") return None - def create_map_objects(self, map_data): + def create_map_objects(self, map_data, map_file): """Create all game objects from map data""" # Clear existing sprites self.all_sprites.empty() self.platforms.empty() self.enemies.empty() self.collectibles.empty() + self.checkpoints.empty() # Create ground elements if "ground" in map_data: @@ -131,3 +135,14 @@ class MapParser: print(f"Background image not found: {map_data['background']}") else: self.background = None + + if "checkpoints" in map_data: + for checkpoint_data in map_data["checkpoints"]: + pos = (checkpoint_data["x"], checkpoint_data["y"]) + size = (checkpoint_data["width"], checkpoint_data["height"]) + sprite = checkpoint_data["sprite"] + checkpoint = Checkpoint( + pos, size, texture_path=sprite, map_name=map_file + ) + self.checkpoints.add(checkpoint) + self.all_sprites.add(checkpoint) diff --git a/src/game.py b/src/game.py index 72201a3..2384d22 100644 --- a/src/game.py +++ b/src/game.py @@ -1,7 +1,16 @@ from src.Entity.Platform import Platform from src.Entity.Player import Player -from src.constant import displaysurface, FramePerSec, font, FPS, platforms, all_sprites +from src.constant import ( + displaysurface, + FramePerSec, + font, + FPS, + platforms, + all_sprites, + vec, +) from src.Map.parser import MapParser +from src.Database.CheckpointDB import CheckpointDB def initialize_game(map_file="map_test.json"): @@ -29,6 +38,7 @@ def initialize_game(map_file="map_test.json"): map_objects["platforms"], map_objects["all_sprites"], parser.background, # Return the loaded background + map_objects["checkpoints"], ) @@ -41,9 +51,34 @@ def reset_game(): all_sprites.empty() # Reload game objects - player, _, platforms, all_sprites, background = initialize_game("map_test.json") + player, _, platforms, all_sprites, background, checkpoints = initialize_game( + "map_test.json" + ) - return player, platforms, all_sprites, background + return player, platforms, all_sprites, background, checkpoints + + +def reset_game_with_checkpoint(map_name="map_test.json"): + """ + Reset the game and respawn player at checkpoint if available + + Args: + map_name: Name of the current map + """ + # Initialize game normally + player, platforms, all_sprites, background, checkpoints = reset_game() + + # Check if there's a saved checkpoint + db = CheckpointDB() + checkpoint_pos = db.get_checkpoint(map_name) + + # If checkpoint exists, teleport player there + if checkpoint_pos: + player.pos = vec(checkpoint_pos[0], checkpoint_pos[1]) + player.update_rect() + print(f"Player respawned at checkpoint: {checkpoint_pos}") + + return player, platforms, all_sprites, background, checkpoints if __name__ == "__main__":