mirror of
https://github.com/BreizhHardware/project_sanic.git
synced 2026-03-18 21:50:33 +01:00
Feat(Infinite Mode) - Implement infinite level generation and management; add InfiniteMapGenerator and InfiniteMapManager for procedural map creation
This commit is contained in:
BIN
assets/sound/Death.mp3
Normal file
BIN
assets/sound/Death.mp3
Normal file
Binary file not shown.
285
map/infinite/2e9b8d03.json
Normal file
285
map/infinite/2e9b8d03.json
Normal file
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"name": "Niveau Infini 1",
|
||||
"width": 2400,
|
||||
"height": 800,
|
||||
"background": "assets/map/background/desert_bg.jpg",
|
||||
"gravity": 1.0,
|
||||
"platforms": [
|
||||
{
|
||||
"id": "platform_start",
|
||||
"x": 180,
|
||||
"y": 260,
|
||||
"width": 540,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform2",
|
||||
"x": 694,
|
||||
"y": 259,
|
||||
"width": 241,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform3",
|
||||
"x": 1080,
|
||||
"y": 167,
|
||||
"width": 163,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform4",
|
||||
"x": 1399,
|
||||
"y": 255,
|
||||
"width": 184,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 1399,
|
||||
"y": 255
|
||||
},
|
||||
{
|
||||
"x": 1539,
|
||||
"y": 255
|
||||
}
|
||||
],
|
||||
"speed": 2,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform5",
|
||||
"x": 1684,
|
||||
"y": 189,
|
||||
"width": 197,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform6",
|
||||
"x": 2030,
|
||||
"y": 337,
|
||||
"width": 162,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 2030,
|
||||
"y": 337
|
||||
},
|
||||
{
|
||||
"x": 2030,
|
||||
"y": 462
|
||||
}
|
||||
],
|
||||
"speed": 1,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform7",
|
||||
"x": 2291,
|
||||
"y": 241,
|
||||
"width": 234,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 2291,
|
||||
"y": 241
|
||||
},
|
||||
{
|
||||
"x": 2291,
|
||||
"y": 395
|
||||
}
|
||||
],
|
||||
"speed": 3,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform8",
|
||||
"x": 2671,
|
||||
"y": 336,
|
||||
"width": 243,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform9",
|
||||
"x": 3021,
|
||||
"y": 388,
|
||||
"width": 233,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 3021,
|
||||
"y": 388
|
||||
},
|
||||
{
|
||||
"x": 3021,
|
||||
"y": 498
|
||||
}
|
||||
],
|
||||
"speed": 1,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform10",
|
||||
"x": 3380,
|
||||
"y": 347,
|
||||
"width": 198,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 3380,
|
||||
"y": 347
|
||||
},
|
||||
{
|
||||
"x": 3380,
|
||||
"y": 475
|
||||
}
|
||||
],
|
||||
"speed": 2,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform11",
|
||||
"x": 3704,
|
||||
"y": 394,
|
||||
"width": 218,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 3704,
|
||||
"y": 394
|
||||
},
|
||||
{
|
||||
"x": 3826,
|
||||
"y": 394
|
||||
}
|
||||
],
|
||||
"speed": 2,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform12",
|
||||
"x": 4059,
|
||||
"y": 198,
|
||||
"width": 270,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform13",
|
||||
"x": 4426,
|
||||
"y": 254,
|
||||
"width": 156,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"id": "enemy1",
|
||||
"type": "turret",
|
||||
"x": 951,
|
||||
"y": 376,
|
||||
"patrol_distance": 291
|
||||
},
|
||||
{
|
||||
"id": "enemy2",
|
||||
"type": "turret",
|
||||
"x": 2173,
|
||||
"y": 377,
|
||||
"patrol_distance": 150
|
||||
}
|
||||
],
|
||||
"checkpoints": [],
|
||||
"exits": [
|
||||
{
|
||||
"x": 2300,
|
||||
"y": 200,
|
||||
"width": 50,
|
||||
"height": 80,
|
||||
"next_level": "NEXT_INFINITE_LEVEL",
|
||||
"sprite": "assets/map/exit/door.png"
|
||||
}
|
||||
],
|
||||
"collectibles": [
|
||||
{
|
||||
"id": "collectible1",
|
||||
"type": "shield",
|
||||
"x": 1013,
|
||||
"y": 101
|
||||
},
|
||||
{
|
||||
"id": "collectible2",
|
||||
"type": "shield",
|
||||
"x": 1749,
|
||||
"y": 183
|
||||
},
|
||||
{
|
||||
"id": "collectible3",
|
||||
"type": "coin",
|
||||
"x": 2070,
|
||||
"y": 196
|
||||
},
|
||||
{
|
||||
"id": "collectible4",
|
||||
"type": "health",
|
||||
"x": 758,
|
||||
"y": 375
|
||||
},
|
||||
{
|
||||
"id": "collectible5",
|
||||
"type": "shield",
|
||||
"x": 681,
|
||||
"y": 353
|
||||
},
|
||||
{
|
||||
"id": "collectible6",
|
||||
"type": "shield",
|
||||
"x": 581,
|
||||
"y": 163
|
||||
}
|
||||
],
|
||||
"spawn_point": {
|
||||
"x": 260.0,
|
||||
"y": 200.0
|
||||
}
|
||||
}
|
||||
225
map/infinite/822377c7.json
Normal file
225
map/infinite/822377c7.json
Normal file
@@ -0,0 +1,225 @@
|
||||
{
|
||||
"name": "Niveau Infini 1",
|
||||
"width": 2400,
|
||||
"height": 800,
|
||||
"background": "assets/map/background/desert_bg.jpg",
|
||||
"gravity": 1.0,
|
||||
"platforms": [
|
||||
{
|
||||
"id": "platform_start",
|
||||
"x": 180,
|
||||
"y": 260,
|
||||
"width": 540,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform2",
|
||||
"x": 762,
|
||||
"y": 316,
|
||||
"width": 238,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform3",
|
||||
"x": 1184,
|
||||
"y": 317,
|
||||
"width": 131,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 1184,
|
||||
"y": 317
|
||||
},
|
||||
{
|
||||
"x": 1326,
|
||||
"y": 317
|
||||
}
|
||||
],
|
||||
"speed": 1,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform4",
|
||||
"x": 1400,
|
||||
"y": 225,
|
||||
"width": 256,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform5",
|
||||
"x": 1736,
|
||||
"y": 282,
|
||||
"width": 178,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform6",
|
||||
"x": 2025,
|
||||
"y": 170,
|
||||
"width": 159,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform7",
|
||||
"x": 2347,
|
||||
"y": 367,
|
||||
"width": 150,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform8",
|
||||
"x": 2632,
|
||||
"y": 385,
|
||||
"width": 256,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform9",
|
||||
"x": 2984,
|
||||
"y": 227,
|
||||
"width": 221,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform10",
|
||||
"x": 3326,
|
||||
"y": 313,
|
||||
"width": 273,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/stone_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform11",
|
||||
"x": 3715,
|
||||
"y": 330,
|
||||
"width": 189,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform12",
|
||||
"x": 4007,
|
||||
"y": 304,
|
||||
"width": 112,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/wood_texture.jpg",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{
|
||||
"x": 4007,
|
||||
"y": 304
|
||||
},
|
||||
{
|
||||
"x": 4007,
|
||||
"y": 407
|
||||
}
|
||||
],
|
||||
"speed": 3,
|
||||
"wait_time": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform13",
|
||||
"x": 4216,
|
||||
"y": 383,
|
||||
"width": 285,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.jpg",
|
||||
"is_moving": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"id": "enemy1",
|
||||
"type": "turret",
|
||||
"x": 776,
|
||||
"y": 356,
|
||||
"patrol_distance": 121
|
||||
},
|
||||
{
|
||||
"id": "enemy2",
|
||||
"type": "walker",
|
||||
"x": 1080,
|
||||
"y": 305,
|
||||
"patrol_distance": 254
|
||||
}
|
||||
],
|
||||
"checkpoints": [],
|
||||
"exits": [
|
||||
{
|
||||
"x": 2300,
|
||||
"y": 200,
|
||||
"width": 50,
|
||||
"height": 80,
|
||||
"next_level": "map/infinite/2e9b8d03.json",
|
||||
"sprite": "assets/map/exit/door.png"
|
||||
}
|
||||
],
|
||||
"collectibles": [
|
||||
{
|
||||
"id": "collectible1",
|
||||
"type": "health",
|
||||
"x": 1672,
|
||||
"y": 119
|
||||
},
|
||||
{
|
||||
"id": "collectible2",
|
||||
"type": "coin",
|
||||
"x": 775,
|
||||
"y": 251
|
||||
},
|
||||
{
|
||||
"id": "collectible3",
|
||||
"type": "health",
|
||||
"x": 814,
|
||||
"y": 207
|
||||
},
|
||||
{
|
||||
"id": "collectible4",
|
||||
"type": "coin",
|
||||
"x": 1578,
|
||||
"y": 278
|
||||
},
|
||||
{
|
||||
"id": "collectible5",
|
||||
"type": "health",
|
||||
"x": 2069,
|
||||
"y": 209
|
||||
},
|
||||
{
|
||||
"id": "collectible6",
|
||||
"type": "shield",
|
||||
"x": 1163,
|
||||
"y": 304
|
||||
}
|
||||
],
|
||||
"spawn_point": {
|
||||
"x": 260.0,
|
||||
"y": 200.0
|
||||
}
|
||||
}
|
||||
157
src/Map/Infinite/InfiniteMapGenerator.py
Normal file
157
src/Map/Infinite/InfiniteMapGenerator.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import random
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
|
||||
class InfiniteMapGenerator:
|
||||
"""Procedural map generator for infinite levels."""
|
||||
|
||||
def __init__(self, game_resources):
|
||||
self.game_resources = game_resources
|
||||
self.width = 2400
|
||||
self.height = 800
|
||||
self.backgrounds = [
|
||||
"assets/map/background/forest_bg.jpg",
|
||||
"assets/map/background/desert_bg.jpg",
|
||||
"assets/map/background/mountain_bg.jpg",
|
||||
]
|
||||
self.platform_textures = [
|
||||
"assets/map/platform/grass_texture.jpg",
|
||||
"assets/map/platform/stone_texture.jpg",
|
||||
"assets/map/platform/wood_texture.jpg",
|
||||
]
|
||||
|
||||
# Create the directory for infinite maps if it doesn't exist
|
||||
os.makedirs("map/infinite", exist_ok=True)
|
||||
|
||||
def generate_map(self, difficulty=1):
|
||||
"""Generate a new infinite map with the specified difficulty level."""
|
||||
map_data = {
|
||||
"name": f"Niveau Infini {difficulty}",
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"background": random.choice(self.backgrounds),
|
||||
"gravity": 1.0,
|
||||
"platforms": self._generate_platforms(difficulty),
|
||||
"enemies": self._generate_enemies(difficulty),
|
||||
"checkpoints": [],
|
||||
"exits": [self._generate_exit()],
|
||||
"collectibles": self._generate_collectibles(difficulty),
|
||||
"spawn_point": {"x": 260.0, "y": 200.0},
|
||||
}
|
||||
|
||||
# Save the map data to a JSON file
|
||||
map_id = str(uuid.uuid4())[:8]
|
||||
map_path = f"map/infinite/{map_id}.json"
|
||||
|
||||
with open(map_path, "w") as f:
|
||||
json.dump(map_data, f, indent=2)
|
||||
|
||||
return map_path
|
||||
|
||||
def _generate_platforms(self, difficulty):
|
||||
platforms = []
|
||||
|
||||
# Starting platform
|
||||
platforms.append(
|
||||
{
|
||||
"id": "platform_start",
|
||||
"x": 180,
|
||||
"y": 260,
|
||||
"width": 540,
|
||||
"height": 60,
|
||||
"texture": random.choice(self.platform_textures),
|
||||
"is_moving": False,
|
||||
}
|
||||
)
|
||||
|
||||
# Generate additional platforms
|
||||
num_platforms = 10 + difficulty * 2
|
||||
last_x = 600
|
||||
for i in range(num_platforms):
|
||||
width = random.randint(
|
||||
max(40, 100 - difficulty * 5), max(120, 300 - difficulty * 10)
|
||||
)
|
||||
gap = random.randint(80, 200)
|
||||
x = last_x + gap
|
||||
y = random.randint(150, 400)
|
||||
|
||||
is_moving = random.random() < min(0.1 + difficulty * 0.05, 0.5)
|
||||
|
||||
platform = {
|
||||
"id": f"platform{i+2}",
|
||||
"x": x,
|
||||
"y": y,
|
||||
"width": width,
|
||||
"height": random.choice([20, 40, 60]),
|
||||
"texture": random.choice(self.platform_textures),
|
||||
"is_moving": is_moving,
|
||||
}
|
||||
|
||||
if is_moving:
|
||||
move_direction = random.choice(["horizontal", "vertical"])
|
||||
distance = random.randint(100, 200)
|
||||
|
||||
if move_direction == "horizontal":
|
||||
platform["movement"] = {
|
||||
"type": "linear",
|
||||
"points": [{"x": x, "y": y}, {"x": x + distance, "y": y}],
|
||||
"speed": random.randint(1, 3),
|
||||
"wait_time": 0.5,
|
||||
}
|
||||
else:
|
||||
platform["movement"] = {
|
||||
"type": "linear",
|
||||
"points": [{"x": x, "y": y}, {"x": x, "y": y + distance}],
|
||||
"speed": random.randint(1, 3),
|
||||
"wait_time": 0.5,
|
||||
}
|
||||
|
||||
platforms.append(platform)
|
||||
last_x = x + width
|
||||
|
||||
return platforms
|
||||
|
||||
def _generate_enemies(self, difficulty):
|
||||
enemies = []
|
||||
num_enemies = difficulty * 2
|
||||
enemy_types = ["walker", "flyer", "turret"]
|
||||
|
||||
for i in range(num_enemies):
|
||||
enemy = {
|
||||
"id": f"enemy{i+1}",
|
||||
"type": random.choice(enemy_types),
|
||||
"x": random.randint(600, self.width - 200),
|
||||
"y": random.randint(100, 400),
|
||||
"patrol_distance": random.randint(100, 300),
|
||||
}
|
||||
enemies.append(enemy)
|
||||
|
||||
return enemies
|
||||
|
||||
def _generate_collectibles(self, difficulty):
|
||||
collectibles = []
|
||||
num_collectibles = 5 + difficulty
|
||||
collectible_types = ["coin", "health", "shield"]
|
||||
|
||||
for i in range(num_collectibles):
|
||||
collectible = {
|
||||
"id": f"collectible{i+1}",
|
||||
"type": random.choice(collectible_types),
|
||||
"x": random.randint(400, self.width - 100),
|
||||
"y": random.randint(100, 400),
|
||||
}
|
||||
collectibles.append(collectible)
|
||||
|
||||
return collectibles
|
||||
|
||||
def _generate_exit(self):
|
||||
return {
|
||||
"x": self.width - 100,
|
||||
"y": 200,
|
||||
"width": 50,
|
||||
"height": 80,
|
||||
"next_level": "NEXT_INFINITE_LEVEL",
|
||||
"sprite": "assets/map/exit/door.png",
|
||||
}
|
||||
82
src/Map/Infinite/InfiniteMapManager.py
Normal file
82
src/Map/Infinite/InfiniteMapManager.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import os
|
||||
import json
|
||||
import glob
|
||||
from src.Map.Infinite.InfiniteMapGenerator import InfiniteMapGenerator
|
||||
|
||||
|
||||
class InfiniteMapManager:
|
||||
"""Handle infinite map generation and management."""
|
||||
|
||||
def __init__(self, game_resources):
|
||||
self.game_resources = game_resources
|
||||
self.map_generator = InfiniteMapGenerator(game_resources)
|
||||
self.current_level = 0
|
||||
self.active_maps = []
|
||||
self.difficulty = 1
|
||||
|
||||
def start_infinite_mode(self):
|
||||
"""Start the infinite mode by generating the first two maps."""
|
||||
self._clean_old_maps()
|
||||
|
||||
# Generate the first two maps
|
||||
first_map = self.map_generator.generate_map(difficulty=self.difficulty)
|
||||
second_map = self.map_generator.generate_map(difficulty=self.difficulty)
|
||||
|
||||
# Configure the first map to point to the second map
|
||||
self._update_exit_target(first_map, second_map)
|
||||
|
||||
self.active_maps = [first_map, second_map]
|
||||
self.current_level = 1
|
||||
|
||||
return first_map
|
||||
|
||||
def advance_to_next_level(self):
|
||||
"""Progress to the next level in infinite mode and delete the previous one."""
|
||||
# Delete the oldest map
|
||||
if self.active_maps:
|
||||
old_map = self.active_maps.pop(0)
|
||||
try:
|
||||
os.remove(old_map)
|
||||
except:
|
||||
print(f"Erreur: Impossible de supprimer {old_map}")
|
||||
|
||||
# Up the difficulty every 3 levels
|
||||
self.current_level += 1
|
||||
if self.current_level % 3 == 0:
|
||||
self.difficulty = min(10, self.difficulty + 1)
|
||||
|
||||
# Generate a new map
|
||||
new_map = self.map_generator.generate_map(difficulty=self.difficulty)
|
||||
|
||||
# Update the exit target of the last map to point to the new one
|
||||
if self.active_maps:
|
||||
self._update_exit_target(self.active_maps[0], new_map)
|
||||
|
||||
self.active_maps.append(new_map)
|
||||
|
||||
return self.active_maps[0]
|
||||
|
||||
def _update_exit_target(self, map_path, next_map_path):
|
||||
"""Update the exit of the current map to point to the next map."""
|
||||
try:
|
||||
with open(map_path, "r") as f:
|
||||
map_data = json.load(f)
|
||||
|
||||
if "exits" in map_data and map_data["exits"]:
|
||||
for exit_obj in map_data["exits"]:
|
||||
exit_obj["next_level"] = next_map_path
|
||||
|
||||
with open(map_path, "w") as f:
|
||||
json.dump(map_data, f, indent=2)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error while updating exit: {e}")
|
||||
|
||||
def _clean_old_maps(self):
|
||||
"""Delete all old infinite maps."""
|
||||
map_files = glob.glob("map/infinite/*.json")
|
||||
for file in map_files:
|
||||
try:
|
||||
os.remove(file)
|
||||
except:
|
||||
print(f"Error: Unable to delete {file}")
|
||||
@@ -27,6 +27,9 @@ class GameResources:
|
||||
pygame.display.set_caption("Project Sanic")
|
||||
self.FramePerSec = pygame.time.Clock()
|
||||
|
||||
self.infinite_manager = None
|
||||
self.infinite_mode = False
|
||||
|
||||
# Font
|
||||
try:
|
||||
self.font = pygame.font.SysFont("Arial", 20)
|
||||
|
||||
34
src/game.py
34
src/game.py
@@ -9,6 +9,7 @@ from src.Entity.Platform import Platform
|
||||
from src.Entity.Player import Player
|
||||
from src.Map.parser import MapParser
|
||||
from src.Database.CheckpointDB import CheckpointDB
|
||||
from src.Map.Infinite.InfiniteMapManager import InfiniteMapManager
|
||||
|
||||
|
||||
def initialize_game(game_resources, map_file="map/levels/1.json"):
|
||||
@@ -53,12 +54,17 @@ def initialize_game(game_resources, map_file="map/levels/1.json"):
|
||||
for exit_obj in exits:
|
||||
exit_obj.set_player(map_objects["player"])
|
||||
|
||||
background = map_objects.get("background", None)
|
||||
if background is None:
|
||||
background = pygame.Surface((game_resources.WIDTH, game_resources.HEIGHT))
|
||||
background.fill((0, 0, 0))
|
||||
|
||||
return (
|
||||
map_objects["player"],
|
||||
None,
|
||||
map_objects["platforms"],
|
||||
map_objects["all_sprites"],
|
||||
parser.background,
|
||||
background,
|
||||
map_objects["checkpoints"],
|
||||
exits,
|
||||
)
|
||||
@@ -117,5 +123,31 @@ def clear_level_progress():
|
||||
print(f"Error clearing level progress: {e}")
|
||||
|
||||
|
||||
def start_infinite_mode(game_resources):
|
||||
"""Start the infinite mode of the game"""
|
||||
# Create a new InfiniteMapManager
|
||||
infinite_manager = InfiniteMapManager(game_resources)
|
||||
game_resources.infinite_manager = infinite_manager
|
||||
game_resources.infinite_mode = True
|
||||
|
||||
# Generate the first level
|
||||
first_level = infinite_manager.start_infinite_mode()
|
||||
|
||||
# Initialize the game with the generated level
|
||||
return initialize_game(game_resources, first_level)
|
||||
|
||||
|
||||
def handle_exit_collision(exit_obj, game_resources, level_file):
|
||||
"""Handle exit collision and transition to next level, including infinite mode"""
|
||||
next_level = exit_obj.next_level
|
||||
|
||||
# Mod infinite: if the next level is "NEXT_INFINITE_LEVEL", generate a new level
|
||||
if hasattr(game_resources, "infinite_mode") and game_resources.infinite_mode:
|
||||
if next_level == "NEXT_INFINITE_LEVEL":
|
||||
next_level = game_resources.infinite_manager.advance_to_next_level()
|
||||
|
||||
return initialize_game(game_resources, next_level)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Please run the game using main.py")
|
||||
|
||||
@@ -12,6 +12,8 @@ from src.game import (
|
||||
initialize_game,
|
||||
reset_game_with_checkpoint,
|
||||
clear_checkpoint_database,
|
||||
start_infinite_mode,
|
||||
handle_exit_collision,
|
||||
)
|
||||
from src.constant import GameResources
|
||||
from src.Menu.Menu import Menu
|
||||
@@ -56,6 +58,15 @@ def handler():
|
||||
print(f"Erreur de chargement de l'image: {e}")
|
||||
death_image = None
|
||||
|
||||
try:
|
||||
death_sound = pygame.mixer.Sound("assets/sound/Death.mp3")
|
||||
death_display_time = death_sound.get_length()
|
||||
print(f"Son Death.mp3 chargé avec succès, durée: {death_display_time} secondes")
|
||||
except Exception as e:
|
||||
print(f"Erreur de chargement du son Death.mp3: {e}")
|
||||
death_sound = None
|
||||
death_display_time = 2
|
||||
|
||||
# Initialize game state and objects
|
||||
current_state = MENU
|
||||
main_menu = Menu(game_resources)
|
||||
@@ -131,6 +142,8 @@ def handler():
|
||||
if event.dict.get("action") == "player_death":
|
||||
current_state = DEATH_SCREEN
|
||||
death_timer = 0
|
||||
if death_sound:
|
||||
death_sound.play()
|
||||
|
||||
db = CheckpointDB()
|
||||
checkpoint_data = db.get_checkpoint(level_file)
|
||||
@@ -323,20 +336,30 @@ def handler():
|
||||
|
||||
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
|
||||
if (
|
||||
hasattr(game_resources, "infinite_mode")
|
||||
and game_resources.infinite_mode
|
||||
):
|
||||
# Mod infinit : load the next level without the menu
|
||||
P1, P1T, platforms, all_sprites, background, checkpoints, exits = (
|
||||
handle_exit_collision(exit, game_resources, level_file)
|
||||
)
|
||||
else:
|
||||
# Mod normal : unlock the next level and return to the menu
|
||||
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()
|
||||
# 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)
|
||||
# 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())
|
||||
@@ -354,22 +377,23 @@ def handler():
|
||||
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))
|
||||
P1, P1T, platforms, all_sprites, background, checkpoints, exits = (
|
||||
start_infinite_mode(game_resources)
|
||||
)
|
||||
current_state = PLAYING
|
||||
|
||||
elif current_state == LEADERBOARD:
|
||||
leaderboard.draw(displaysurface)
|
||||
|
||||
elif current_state == DEATH_SCREEN:
|
||||
displaysurface.fill((0, 0, 0)) # Fond rouge foncé
|
||||
displaysurface.fill((0, 0, 0))
|
||||
|
||||
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
|
||||
# Timer for death screen
|
||||
death_timer += dt
|
||||
if death_timer >= death_display_time:
|
||||
if checkpoint_data:
|
||||
|
||||
Reference in New Issue
Block a user