Feat(Game Logic) - Refactor main game loop into a separate handler function for improved code organization and maintainability

This commit is contained in:
Félix MARQUET
2025-04-01 13:09:15 +02:00
parent cb27727e7a
commit ee54b5d272
9 changed files with 402 additions and 667 deletions

223
Test.py
View File

@@ -1,223 +0,0 @@
import pygame
from pygame.locals import *
import sys
import os
import time
pygame.init()
vec = pygame.math.Vector2
HEIGHT = 450
WIDTH = 400
ACC = 0.5
FRIC = -0.12
FPS = 60
FramePerSec = pygame.time.Clock()
displaysurface = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
WIDTH, HEIGHT = displaysurface.get_size()
pygame.display.set_caption("Project Sanic")
# Initialize font for FPS counter
try:
font = pygame.font.SysFont("Arial", 24)
except:
font = pygame.font.Font(None, 24) # Default font if Arial is not available
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
# Animation variables
self.animation_frames = []
self.current_frame = 0
self.animation_speed = 0.1
self.last_update = time.time()
self.static_image = None
self.moving = False # Track if player is moving
self.dashing = False
# Load static image and animation frames
self.load_images()
# Set initial surface
if self.static_image:
self.surf = self.static_image
elif self.animation_frames:
self.surf = self.animation_frames[0]
else:
# Fallback to a colored rectangle
self.surf = pygame.Surface((30, 30))
self.surf.fill((128, 255, 40))
self.rect = self.surf.get_rect()
self.pos = vec((10, 385))
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.jumping = False
def load_images(self):
try:
# Load static image
if os.path.isfile("assets/player/Sanic Base.png"):
self.static_image = pygame.image.load("assets/player/Sanic Base.png").convert_alpha()
self.static_image = pygame.transform.scale(self.static_image, (160, 160))
# Load animation sprite sheet
if os.path.isfile("assets/player/Sanic Annimate.png"):
sprite_sheet = pygame.image.load("assets/player/Sanic Annimate.png").convert_alpha()
# Extract the 4 frames
frame_width = sprite_sheet.get_height()
for i in range(4):
# Cut out a region of the sprite sheet
frame = sprite_sheet.subsurface((i * 2207, 0, frame_width, frame_width))
# Resize the frame
frame = pygame.transform.scale(frame, (160, 160))
self.animation_frames.append(frame)
except Exception as e:
print(f"Error loading player images: {e}")
def update_animation(self):
if self.moving:
# Only animate when moving
if self.animation_frames and time.time() - self.last_update > self.animation_speed:
self.current_frame = (self.current_frame + 1) % len(self.animation_frames)
self.surf = self.animation_frames[self.current_frame]
self.last_update = time.time()
else:
# Use static image when not moving
if self.static_image:
self.surf = self.static_image
def dash(self, acc):
self.acc.x = 5 * acc
if acc<0:
self.acc.y = 5 * acc
else:
self.acc.y = -5 * acc
def move(self):
self.acc = vec(0, 1) # Gravity
# Reset moving flag
self.moving = False
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_q]:
self.acc.x = -ACC
self.moving = True # Set moving to True when moving left
if pressed_keys[K_a] :
self.dash(-ACC)
if pressed_keys[K_d]:
self.acc.x = ACC
self.moving = True # Set moving to True when moving right
if pressed_keys[K_a] :
self.dash(ACC)
# Also consider the player moving if they have significant horizontal velocity
if abs(self.vel.x) > 0.5:
self.moving = True
# Jumping logic
if pressed_keys[K_SPACE] and not self.jumping:
self.vel.y = -30
self.jumping = True
# Apply friction
self.acc.y += self.vel.y * FRIC
self.acc.x += self.vel.x * FRIC
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# Prevent the player from moving off-screen horizontally
if self.pos.x > WIDTH - self.rect.width / 2:
self.pos.x = WIDTH - self.rect.width / 2
self.vel.x = 0
if self.pos.x < self.rect.width / 2:
self.pos.x = self.rect.width / 2
self.vel.x = 0
self.rect.midbottom = self.pos
# Update animation frame
self.update_animation()
def update(self):
hits = pygame.sprite.spritecollide(self, platforms, False)
if hits:
if self.vel.y > 0:
self.pos.y = hits[0].rect.top
self.vel.y = 0
self.jumping = False
class platform(pygame.sprite.Sprite):
def __init__(self, Long, R, G, B):
super().__init__()
self.surf = pygame.Surface((Long, 20))
self.surf.fill((R, G, B))
self.rect = self.surf.get_rect(center=(WIDTH / 2, HEIGHT - 10))
PT1 = platform(WIDTH, 255, 0, 0)
a = 200
PT2 = platform(a, 0, 0, 255)
PT2.rect.y=(HEIGHT-50)
P1 = Player()
platforms = pygame.sprite.Group()
platforms.add(PT1)
platforms.add(PT2)
all_sprites = pygame.sprite.Group()
all_sprites.add(PT1)
all_sprites.add(PT2)
all_sprites.add(P1)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
displaysurface.fill((0, 0, 0))
P1.move()
P1.update()
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
# Gestion déplacement plateforme PT2
if PT2.rect.x < 50:
a = 1
if PT2.rect.x > 1000:
a=-1
PT2.rect.x += 2*a
# Le rectangle P1 reste sur la plateforme mouvante
if P1.rect.colliderect(PT2.rect) and P1.pos.y == PT2.rect.y:
P1.pos.x += 2 * a
# Display FPS
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))
# Display player coordinates
pos_text = font.render(f"X: {int(P1.pos.x)}, Y: {int(P1.pos.y)}", True, (255, 255, 255))
displaysurface.blit(pos_text, (10, 40))
pygame.display.update()
FramePerSec.tick(FPS)

BIN
assets/player/dead.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

357
main.py
View File

@@ -1,361 +1,8 @@
import re
import pygame
import sys
from pygame.locals import *
import numpy as np
from src.Database.LevelDB import LevelDB
from src.Entity.Enemy import Enemy
from src.Menu.LevelSelectMenu import LevelSelectMenu
from src.game import (
initialize_game,
reset_game_with_checkpoint,
clear_checkpoint_database,
)
from src.constant import GameResources
from src.Menu.Menu import Menu
from src.Menu.Leaderboard import Leaderboard
from src.Camera import Camera
from src.Database.CheckpointDB import CheckpointDB
from src.Map.Editor.LevelEditor import LevelEditor
from src.Menu.LevelEditorSelectionMenu import LevelEditorSelectionMenu
from src.handler import handler
def main():
# Initialize Pygame and game resources
game_resources = GameResources()
displaysurface = game_resources.displaysurface
FramePerSec = game_resources.FramePerSec
font = game_resources.font
FPS = game_resources.FPS
WIDTH = game_resources.WIDTH
HEIGHT = game_resources.HEIGHT
ORIGINAL_WIDTH = game_resources.ORIGINAL_WIDTH
ORIGINAL_HEIGHT = game_resources.ORIGINAL_HEIGHT
fullscreen = game_resources.fullscreen
# Add camera initialization
camera = Camera(WIDTH, HEIGHT, game_resources)
# Game states
MENU = 0
PLAYING = 1
INFINITE = 2
LEADERBOARD = 3
# Initialize game state and objects
current_state = MENU
main_menu = Menu(game_resources)
level_select_menu = None
level_file = "map/levels/1.json"
current_menu = "main"
leaderboard = Leaderboard(WIDTH, HEIGHT, font)
clear_checkpoint_database()
projectiles = pygame.sprite.Group()
pygame.joystick.quit()
pygame.joystick.init()
joysticks = []
try:
for i in range(pygame.joystick.get_count()):
joystick = pygame.joystick.Joystick(i)
joystick.init()
joysticks.append(joystick)
except pygame.error:
print("Error while initializing joysticks")
# Main game loop
running = True
while running:
try:
events = []
try:
events = pygame.event.get()
except Exception as e:
print(f"Error while getting events: {e}")
pygame.joystick.quit()
pygame.joystick.init()
continue
for event in events:
if event.type == QUIT:
running = False
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
if current_state in [PLAYING, INFINITE]:
current_state = MENU
else:
pygame.quit()
sys.exit()
elif event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
# Store current window size before going fullscreen
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = displaysurface.get_size()
displaysurface = pygame.display.set_mode(
(0, 0), pygame.FULLSCREEN
)
else:
# Return to windowed mode with previous size
displaysurface = pygame.display.set_mode(
(ORIGINAL_WIDTH, ORIGINAL_HEIGHT), pygame.RESIZABLE
)
elif (
event.type == VIDEORESIZE
): # Fixed indentation - moved out of K_F11 condition
if not fullscreen:
displaysurface = pygame.display.set_mode(
(event.w, event.h), pygame.RESIZABLE
)
# Update window dimensions
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h
elif event.type == USEREVENT:
if event.action == "player_death":
db = CheckpointDB()
checkpoint_pos = db.get_checkpoint(level_file)
if checkpoint_pos:
# Respawn player at checkpoint
P1, platforms, all_sprites, background, checkpoints = (
reset_game_with_checkpoint(level_file, game_resources)
)
projectiles.empty()
else:
# No checkpoint found, return to menu
current_state = MENU
if event.dict.get("action") == "create_projectile":
projectile = event.dict.get("projectile")
projectiles.add(projectile)
# Handle menu events
if current_state == MENU:
if current_menu == "main":
action = main_menu.handle_event(event)
if action == "level_select":
level_select_menu = LevelSelectMenu(game_resources)
current_menu = "level_select"
elif action == "infinite":
current_state = INFINITE
elif action == "leaderboard":
current_state = LEADERBOARD
elif action == "quit":
pygame.quit()
sys.exit()
elif current_menu == "level_select":
action = level_select_menu.handle_event(event)
if action == "back_to_main":
current_menu = "main"
elif (
isinstance(action, dict)
and action.get("action") == "select_level"
):
level_file = action.get("level_file")
print(level_file)
(
P1,
PT1,
platforms,
all_sprites,
background,
checkpoints,
exits,
) = initialize_game(game_resources, level_file)
projectiles.empty()
current_state = PLAYING
elif action == "open_editor":
editor_select_menu = LevelEditorSelectionMenu(
game_resources
)
current_state = "editor_select"
# Handle leaderboard events
elif current_state == LEADERBOARD:
action = leaderboard.handle_event(event)
if action == "menu":
current_state = MENU
elif current_state == "editor_select":
action = editor_select_menu.handle_event(event)
if action == "back_to_levels":
current_state = MENU
current_menu = "level_select"
elif isinstance(action, dict):
if action["action"] == "edit_level":
level_editor = LevelEditor(
game_resources, action["level_file"]
)
current_state = "level_editor"
elif action["action"] == "new_level":
level_editor = LevelEditor(game_resources)
current_state = "level_editor"
elif current_state == "level_editor":
result = level_editor.handle_event(event)
if result == "back_to_levels":
current_state = "editor_select"
except Exception as e:
print(f"Error while processing events: {e}")
continue
# Clear screen
displaysurface.fill((0, 0, 0))
# Draw appropriate screen based on state
if current_state == MENU:
if current_menu == "main":
main_menu.draw(displaysurface)
elif current_menu == "level_select":
level_select_menu.draw(displaysurface)
elif current_state == "editor_select":
editor_select_menu.draw(displaysurface)
elif current_state == "level_editor":
level_editor.draw(displaysurface)
elif current_state == LEADERBOARD:
leaderboard.draw(displaysurface)
elif current_state == PLAYING:
# Regular game code
P1.move()
P1.update()
P1.attack()
# Update camera to follow player
camera.update(P1)
# Clear screen
displaysurface.fill((0, 0, 0))
for platform in platforms:
if platform.is_moving and platform.movement_type == "linear":
if (
platform.movement_points[0]["x"]
- platform.movement_points[1]["x"]
== 0
):
dir = 0
else:
dir = 1
if (
P1.rect.colliderect(platform.rect)
and P1.pos.y == platform.rect.y
):
P1.pos.x += platform.movement_speed * platform.coeff
platform.move_linear(
dir,
platform.movement_points,
platform.movement_speed,
platform.wait_time,
platform.coeff,
)
if platform.is_moving and platform.movement_type == "circular":
if (
P1.rect.colliderect(platform.rect)
and P1.pos.y == platform.rect.y
and platform.clockwise
):
P1.pos.x = P1.pos.x + platform.radius * np.cos(platform.angle)
P1.pos.y = P1.pos.y + platform.radius * np.sin(platform.angle)
if (
P1.rect.colliderect(platform.rect)
and P1.pos.y == platform.rect.y
and not platform.clockwise
):
P1.pos.x = P1.pos.x + platform.radius * np.cos(platform.angle)
P1.pos.y = P1.pos.y + platform.radius * np.sin(-platform.angle)
platform.move_circular(
platform.center,
platform.angular_speed,
platform.radius,
platform.clockwise,
)
if background:
parallax_factor = 0.3
bg_x = camera.camera.x * parallax_factor
bg_y = camera.camera.y * parallax_factor
displaysurface.blit(background, (bg_x, bg_y))
# Draw all sprites with camera offset applied
for entity in all_sprites:
# Calculate position adjusted for camera
camera_adjusted_rect = entity.rect.copy()
camera_adjusted_rect.x += camera.camera.x
camera_adjusted_rect.y += camera.camera.y
displaysurface.blit(entity.surf, camera_adjusted_rect)
for sprite in all_sprites:
if isinstance(sprite, Enemy):
sprite.update(P1)
else:
sprite.update()
projectiles.update(WIDTH, HEIGHT, P1, camera)
for projectile in projectiles:
# Calculate position adjusted for camera (comme pour les autres sprites)
camera_adjusted_rect = projectile.rect.copy()
camera_adjusted_rect.x += camera.camera.x
camera_adjusted_rect.y += camera.camera.y
displaysurface.blit(projectile.surf, camera_adjusted_rect)
if checkpoints is not None:
checkpoints_hit = pygame.sprite.spritecollide(P1, checkpoints, False)
else:
checkpoints_hit = []
for checkpoint in checkpoints_hit:
checkpoint.activate()
exits_hit = pygame.sprite.spritecollide(P1, exits, False) if exits else []
for exit in exits_hit:
current_level_match = re.search(r"(\d+)\.json$", level_file)
if current_level_match:
current_level = int(current_level_match.group(1))
next_level = current_level + 1
# Unlock next level
db = LevelDB()
db.unlock_level(next_level)
db.close()
# Return to level select menu
current_state = MENU
current_menu = "level_select"
level_select_menu = LevelSelectMenu(game_resources)
# Display FPS and coordinates (fixed position UI elements)
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))
P1.draw_dash_cooldown_bar(displaysurface)
pos_text = font.render(
f"X: {int(P1.pos.x)}, Y: {int(P1.pos.y)}", True, (255, 255, 255)
)
displaysurface.blit(pos_text, (10, 40))
P1.draw_dash_cooldown_bar(displaysurface)
P1.draw_lives(displaysurface)
elif current_state == INFINITE:
# Placeholder for infinite mode
text = font.render("Mode Infini - À implémenter", True, (255, 255, 255))
displaysurface.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2))
elif current_state == LEADERBOARD:
leaderboard.draw(displaysurface)
pygame.display.update()
FramePerSec.tick(FPS)
handler()
if __name__ == "__main__":

View File

@@ -4,8 +4,7 @@
"height": 800,
"background": "assets/map/background/forest_bg.jpg",
"gravity": 1.0,
"ground": [
"platforms": [
{
"id": "main_ground",
"x": -1000,
@@ -14,14 +13,6 @@
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
},
{
"id": "pit",
"x": 800,
"y": 780,
"width": 200,
"height": 20,
"is_hole": true
},
{
"id": "main_ground_2",
"x": 1000,
@@ -29,10 +20,7 @@
"width": 1800,
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
}
],
"platforms": [
},
{
"id": "platform1",
"x": 300,

View File

@@ -4,8 +4,7 @@
"height": 800,
"background": "assets/map/background/forest_bg.jpg",
"gravity": 1.0,
"ground": [
"platforms": [
{
"id": "main_ground",
"x": -1000,
@@ -14,14 +13,6 @@
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
},
{
"id": "pit",
"x": 800,
"y": 780,
"width": 200,
"height": 20,
"is_hole": true
},
{
"id": "main_ground_2",
"x": 1000,
@@ -29,10 +20,7 @@
"width": 1800,
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
}
],
"platforms": [
},
{
"id": "platform1",
"x": 300,

View File

@@ -4,21 +4,11 @@
"height": 800,
"background": "assets/map/background/forest_bg.jpg",
"gravity": 1.0,
"ground": [
{
"id": "main_ground",
"x": -1000,
"y": 780,
"width": 1800,
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
}
],
"platforms": [
{
"id": "platform1",
"x": 220,
"y": 280,
"y": 160,
"width": 540,
"height": 160,
"texture": "assets/map/platform/grass_texture.jpg",
@@ -27,46 +17,31 @@
{
"id": "platform2",
"x": 320,
"y": 140,
"y": 120,
"width": 200,
"height": 20,
"texture": "assets/map/platform/grass_texture.jpg",
"is_moving": true,
"movement": {
"type": "linear",
"points": [
{
"x": 320,
"y": 140
},
{
"x": 420,
"y": 140
}
],
"speed": 2,
"wait_time": 1.0
}
"is_moving": false
}
],
"enemies": [
{
"id": "enemy1",
"type": "walker",
"x": 380,
"y": 220,
"x": 340,
"y": 180,
"health": 1,
"damage": 1,
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
"behavior": "patrol",
"patrol_points": [
{
"x": 280,
"y": 220
"x": 240,
"y": 180
},
{
"x": 480,
"y": 220
"x": 440,
"y": 180
}
],
"speed": 1.5
@@ -75,8 +50,8 @@
"checkpoints": [],
"exits": [
{
"x": 680,
"y": 200,
"x": 630,
"y": 40,
"width": 50,
"height": 80,
"next_level": "map/levels/1.json",

View File

@@ -216,16 +216,6 @@ class LevelEditor:
"height": 800,
"background": "assets/map/background/forest_bg.jpg",
"gravity": 1.0,
"ground": [
{
"id": "main_ground",
"x": -1000,
"y": 780,
"width": 1800,
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg",
}
],
"platforms": [],
"enemies": [],
"checkpoints": [],

View File

@@ -56,21 +56,6 @@ class MapParser:
self.checkpoints.empty()
self.exits.empty()
# Create ground elements
if "ground" in map_data:
for ground in map_data["ground"]:
if not ground.get("is_hole", False):
platform = Platform(
ground["width"],
ground["height"],
ground["x"] + ground["width"] / 2,
ground["y"],
(255, 0, 0),
ground["texture"],
)
self.platforms.add(platform)
self.all_sprites.add(platform)
# Create enemies
if "enemies" in map_data:
# Create enemies

385
src/handler.py Normal file
View File

@@ -0,0 +1,385 @@
import re
import pygame
import sys
from pygame.locals import *
import numpy as np
from src.Database.LevelDB import LevelDB
from src.Entity.Enemy import Enemy
from src.Menu.LevelSelectMenu import LevelSelectMenu
from src.game import (
initialize_game,
reset_game_with_checkpoint,
clear_checkpoint_database,
)
from src.constant import GameResources
from src.Menu.Menu import Menu
from src.Menu.Leaderboard import Leaderboard
from src.Camera import Camera
from src.Database.CheckpointDB import CheckpointDB
from src.Map.Editor.LevelEditor import LevelEditor
from src.Menu.LevelEditorSelectionMenu import LevelEditorSelectionMenu
def handler():
# Initialize Pygame and game resources
game_resources = GameResources()
displaysurface = game_resources.displaysurface
FramePerSec = game_resources.FramePerSec
font = game_resources.font
FPS = game_resources.FPS
WIDTH = game_resources.WIDTH
HEIGHT = game_resources.HEIGHT
ORIGINAL_WIDTH = game_resources.ORIGINAL_WIDTH
ORIGINAL_HEIGHT = game_resources.ORIGINAL_HEIGHT
fullscreen = game_resources.fullscreen
# Add camera initialization
camera = Camera(WIDTH, HEIGHT, game_resources)
# Game states
MENU = 0
PLAYING = 1
INFINITE = 2
LEADERBOARD = 3
DEATH_SCREEN = 4
# Initialize death screen
death_timer = 0
death_display_time = 2
checkpoint_data = None
try:
death_image = pygame.image.load("assets/player/dead.jpg")
print("Image dead.jpg chargée avec succès")
except Exception as e:
print(f"Erreur de chargement de l'image: {e}")
death_image = None
# Initialize game state and objects
current_state = MENU
main_menu = Menu(game_resources)
level_select_menu = None
level_file = "map/levels/1.json"
current_menu = "main"
leaderboard = Leaderboard(WIDTH, HEIGHT, font)
clear_checkpoint_database()
projectiles = pygame.sprite.Group()
pygame.joystick.quit()
pygame.joystick.init()
joysticks = []
try:
for i in range(pygame.joystick.get_count()):
joystick = pygame.joystick.Joystick(i)
joystick.init()
joysticks.append(joystick)
except pygame.error:
print("Error while initializing joysticks")
# Main game loop
running = True
while running:
try:
events = []
dt = FramePerSec.get_time() / 1000.0
try:
events = pygame.event.get()
except Exception as e:
print(f"Error while getting events: {e}")
pygame.joystick.quit()
pygame.joystick.init()
continue
for event in events:
if event.type == QUIT:
running = False
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
if current_state in [PLAYING, INFINITE]:
current_state = MENU
else:
pygame.quit()
sys.exit()
elif event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
# Store current window size before going fullscreen
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = displaysurface.get_size()
displaysurface = pygame.display.set_mode(
(0, 0), pygame.FULLSCREEN
)
else:
# Return to windowed mode with previous size
displaysurface = pygame.display.set_mode(
(ORIGINAL_WIDTH, ORIGINAL_HEIGHT), pygame.RESIZABLE
)
elif (
event.type == VIDEORESIZE
): # Fixed indentation - moved out of K_F11 condition
if not fullscreen:
displaysurface = pygame.display.set_mode(
(event.w, event.h), pygame.RESIZABLE
)
# Update window dimensions
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h
elif event.type == USEREVENT:
if event.dict.get("action") == "player_death":
current_state = DEATH_SCREEN
death_timer = 0
db = CheckpointDB()
checkpoint_data = db.get_checkpoint(level_file)
if event.dict.get("action") == "create_projectile":
projectile = event.dict.get("projectile")
projectiles.add(projectile)
# Handle menu events
if current_state == MENU:
if current_menu == "main":
action = main_menu.handle_event(event)
if action == "level_select":
level_select_menu = LevelSelectMenu(game_resources)
current_menu = "level_select"
elif action == "infinite":
current_state = INFINITE
elif action == "leaderboard":
current_state = LEADERBOARD
elif action == "quit":
pygame.quit()
sys.exit()
elif current_menu == "level_select":
action = level_select_menu.handle_event(event)
if action == "back_to_main":
current_menu = "main"
elif (
isinstance(action, dict)
and action.get("action") == "select_level"
):
level_file = action.get("level_file")
print(level_file)
(
P1,
PT1,
platforms,
all_sprites,
background,
checkpoints,
exits,
) = initialize_game(game_resources, level_file)
projectiles.empty()
current_state = PLAYING
elif action == "open_editor":
editor_select_menu = LevelEditorSelectionMenu(
game_resources
)
current_state = "editor_select"
# Handle leaderboard events
elif current_state == LEADERBOARD:
action = leaderboard.handle_event(event)
if action == "menu":
current_state = MENU
elif current_state == "editor_select":
action = editor_select_menu.handle_event(event)
if action == "back_to_levels":
current_state = MENU
current_menu = "level_select"
elif isinstance(action, dict):
if action["action"] == "edit_level":
level_editor = LevelEditor(
game_resources, action["level_file"]
)
current_state = "level_editor"
elif action["action"] == "new_level":
level_editor = LevelEditor(game_resources)
current_state = "level_editor"
elif current_state == "level_editor":
result = level_editor.handle_event(event)
if result == "back_to_levels":
current_state = "editor_select"
except Exception as e:
print(f"Error while processing events: {e}")
continue
# Clear screen
displaysurface.fill((0, 0, 0))
# Draw appropriate screen based on state
if current_state == MENU:
if current_menu == "main":
main_menu.draw(displaysurface)
elif current_menu == "level_select":
level_select_menu.draw(displaysurface)
elif current_state == "editor_select":
editor_select_menu.draw(displaysurface)
elif current_state == "level_editor":
level_editor.draw(displaysurface)
elif current_state == LEADERBOARD:
leaderboard.draw(displaysurface)
elif current_state == PLAYING:
# Regular game code
P1.move()
P1.update()
P1.attack()
# Update camera to follow player
camera.update(P1)
# Clear screen
displaysurface.fill((0, 0, 0))
for platform in platforms:
if platform.is_moving and platform.movement_type == "linear":
if (
platform.movement_points[0]["x"]
- platform.movement_points[1]["x"]
== 0
):
dir = 0
else:
dir = 1
if (
P1.rect.colliderect(platform.rect)
and P1.pos.y == platform.rect.y
):
P1.pos.x += platform.movement_speed * platform.coeff
platform.move_linear(
dir,
platform.movement_points,
platform.movement_speed,
platform.wait_time,
platform.coeff,
)
if platform.is_moving and platform.movement_type == "circular":
if (
P1.rect.colliderect(platform.rect)
and P1.pos.y == platform.rect.y
and platform.clockwise
):
P1.pos.x = P1.pos.x + platform.radius * np.cos(platform.angle)
P1.pos.y = P1.pos.y + platform.radius * np.sin(platform.angle)
if (
P1.rect.colliderect(platform.rect)
and P1.pos.y == platform.rect.y
and not platform.clockwise
):
P1.pos.x = P1.pos.x + platform.radius * np.cos(platform.angle)
P1.pos.y = P1.pos.y + platform.radius * np.sin(-platform.angle)
platform.move_circular(
platform.center,
platform.angular_speed,
platform.radius,
platform.clockwise,
)
if background:
parallax_factor = 0.3
bg_x = camera.camera.x * parallax_factor
bg_y = camera.camera.y * parallax_factor
displaysurface.blit(background, (bg_x, bg_y))
# Draw all sprites with camera offset applied
for entity in all_sprites:
# Calculate position adjusted for camera
camera_adjusted_rect = entity.rect.copy()
camera_adjusted_rect.x += camera.camera.x
camera_adjusted_rect.y += camera.camera.y
displaysurface.blit(entity.surf, camera_adjusted_rect)
for sprite in all_sprites:
if isinstance(sprite, Enemy):
sprite.update(P1)
else:
sprite.update()
projectiles.update(WIDTH, HEIGHT, P1, camera)
for projectile in projectiles:
# Calculate position adjusted for camera (comme pour les autres sprites)
camera_adjusted_rect = projectile.rect.copy()
camera_adjusted_rect.x += camera.camera.x
camera_adjusted_rect.y += camera.camera.y
displaysurface.blit(projectile.surf, camera_adjusted_rect)
if checkpoints is not None:
checkpoints_hit = pygame.sprite.spritecollide(P1, checkpoints, False)
else:
checkpoints_hit = []
for checkpoint in checkpoints_hit:
checkpoint.activate()
exits_hit = pygame.sprite.spritecollide(P1, exits, False) if exits else []
for exit in exits_hit:
current_level_match = re.search(r"(\d+)\.json$", level_file)
if current_level_match:
current_level = int(current_level_match.group(1))
next_level = current_level + 1
# Unlock next level
db = LevelDB()
db.unlock_level(next_level)
db.close()
# Return to level select menu
current_state = MENU
current_menu = "level_select"
level_select_menu = LevelSelectMenu(game_resources)
# Display FPS and coordinates (fixed position UI elements)
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))
P1.draw_dash_cooldown_bar(displaysurface)
pos_text = font.render(
f"X: {int(P1.pos.x)}, Y: {int(P1.pos.y)}", True, (255, 255, 255)
)
displaysurface.blit(pos_text, (10, 40))
P1.draw_dash_cooldown_bar(displaysurface)
P1.draw_lives(displaysurface)
elif current_state == INFINITE:
# Placeholder for infinite mode
text = font.render("Mode Infini - À implémenter", True, (255, 255, 255))
displaysurface.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2))
elif current_state == LEADERBOARD:
leaderboard.draw(displaysurface)
elif current_state == DEATH_SCREEN:
displaysurface.fill((0, 0, 0)) # Fond rouge foncé
if death_image:
scaled_image = pygame.transform.scale(death_image, (WIDTH, HEIGHT))
image_rect = scaled_image.get_rect(center=(WIDTH // 2, HEIGHT // 2))
displaysurface.blit(scaled_image, image_rect)
# Gestion du timer
death_timer += dt
if death_timer >= death_display_time:
if checkpoint_data:
P1, platforms, all_sprites, background, checkpoints = (
reset_game_with_checkpoint(level_file, game_resources)
)
projectiles.empty()
current_state = PLAYING
else:
current_state = MENU
pygame.display.update()
FramePerSec.tick(FPS)