mirror of
https://github.com/BreizhHardware/project_sanic.git
synced 2026-01-18 16:47:25 +01:00
Feat(Game Logic) - Refactor main game loop into a separate handler function for improved code organization and maintainability
This commit is contained in:
223
Test.py
223
Test.py
@@ -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
BIN
assets/player/dead.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
357
main.py
357
main.py
@@ -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__":
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": [],
|
||||
|
||||
@@ -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
385
src/handler.py
Normal 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)
|
||||
Reference in New Issue
Block a user