mirror of
https://github.com/BreizhHardware/project_sanic.git
synced 2026-01-18 16:47:25 +01:00
7
.gitignore
vendored
7
.gitignore
vendored
@@ -10,4 +10,9 @@
|
||||
checkpoint.db
|
||||
checkpoint.db-journal
|
||||
game.db
|
||||
map/infinite/*
|
||||
map/infinite/*
|
||||
|
||||
temp_audio.mp3
|
||||
output.prof
|
||||
|
||||
**/*.pyc
|
||||
2
CHATGPT.MD
Normal file
2
CHATGPT.MD
Normal file
@@ -0,0 +1,2 @@
|
||||
# Utilisation de LLM
|
||||
Dans ce projet les LLM (GPT 4o et Claude 3.7 Thinking) ont été utilisés pour du debug (impossible de trouver tous les endroits précisément) ainsi que pour écrire les commits messages.
|
||||
@@ -93,3 +93,6 @@ Power-ups Disponibles : Affichés de manière visible pour que le joueur sache q
|
||||
|
||||
## Structure du Projet
|
||||
La structure du projet est disponible dans le fichier [PROJECT_STRUCTURE](PROJECT_STRUCTURE.MD). Ce fichier contient une description détaillée de l'organisation des fichiers et des dossiers du projet.
|
||||
|
||||
## Utilisation des LLM
|
||||
L'utilisation est détaillée dans le fichier [CHATGPT](CHATGPT.MD).
|
||||
BIN
assets/map/collectibles/jump.png
Normal file
BIN
assets/map/collectibles/jump.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
BIN
assets/map/collectibles/speed.png
Normal file
BIN
assets/map/collectibles/speed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 51 KiB |
BIN
assets/player/Sanic.gif
Normal file
BIN
assets/player/Sanic.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
BIN
assets/sound/Boule de feu.mp3
Normal file
BIN
assets/sound/Boule de feu.mp3
Normal file
Binary file not shown.
BIN
assets/sound/Coin.mp3
Normal file
BIN
assets/sound/Coin.mp3
Normal file
Binary file not shown.
BIN
assets/sound/Jump.mp3
Normal file
BIN
assets/sound/Jump.mp3
Normal file
Binary file not shown.
BIN
assets/sound/main_music.mp3
Normal file
BIN
assets/sound/main_music.mp3
Normal file
Binary file not shown.
@@ -8,22 +8,14 @@
|
||||
{
|
||||
"id": "main_ground",
|
||||
"x": -1000,
|
||||
"y": 780,
|
||||
"y": 800,
|
||||
"width": 1800,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/grass_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "main_ground_2",
|
||||
"x": 1000,
|
||||
"y": 900,
|
||||
"width": 1800,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform1",
|
||||
"x": 300,
|
||||
"id": "platform1_01",
|
||||
"x": 200,
|
||||
"y": 600,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
@@ -31,80 +23,140 @@
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform2",
|
||||
"x": 700,
|
||||
"id": "platform1_02",
|
||||
"x": 500,
|
||||
"y": 500,
|
||||
"width": 150,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform1_03",
|
||||
"x": 900,
|
||||
"y": 650,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 700, "y": 300},
|
||||
{"x": 700, "y": 600}
|
||||
{"x": 800, "y": 650},
|
||||
{"x": 1600, "y": 650}
|
||||
],
|
||||
"speed": 2.0,
|
||||
"speed": 3.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform21",
|
||||
"x": 1200,
|
||||
"y": 750,
|
||||
"width": 150,
|
||||
"id": "main_ground_2",
|
||||
"x": 1700,
|
||||
"y": 800,
|
||||
"width": 700,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform2_01",
|
||||
"x": 2500,
|
||||
"y": 700,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 1200, "y": 550},
|
||||
{"x": 1500, "y": 550}
|
||||
{"x": 2500, "y": 700},
|
||||
{"x": 2500, "y": 1200}
|
||||
],
|
||||
"speed": 2.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform3",
|
||||
"x": 1200,
|
||||
"y": 400,
|
||||
"id": "platform2_02",
|
||||
"x": 3300,
|
||||
"y": 1200,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 2800, "y": 1200},
|
||||
{"x": 3300, "y": 1200}
|
||||
],
|
||||
"speed": 2.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_ground_3",
|
||||
"x": 3600,
|
||||
"y": 1400,
|
||||
"width": 700,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform3_01",
|
||||
"x": 4600,
|
||||
"y": 1100,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "circular",
|
||||
"center": {"x": 1200, "y": 400},
|
||||
"center": {"x": 4600, "y": 1100},
|
||||
"radius": 3,
|
||||
"speed": 0.02,
|
||||
"clockwise": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform3_02",
|
||||
"x": 3900,
|
||||
"y": 1200,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "main_ground_4",
|
||||
"x": 4900,
|
||||
"y": 1300,
|
||||
"width": 600,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
}
|
||||
],
|
||||
|
||||
"enemies": [
|
||||
{
|
||||
"id": "enemy1",
|
||||
"id": "enemy1_01",
|
||||
"type": "walker",
|
||||
"x": 500,
|
||||
"y": 660,
|
||||
"y": 700,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "patrol",
|
||||
"patrol_points": [
|
||||
{"x": 400, "y": 660},
|
||||
{"x": 600, "y": 660}
|
||||
{"x": 400, "y": 700},
|
||||
{"x": 600, "y": 700}
|
||||
],
|
||||
"speed": 1.5,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy2",
|
||||
"id": "enemy1_02",
|
||||
"type": "flyer",
|
||||
"x": 1000,
|
||||
"x": 700,
|
||||
"y": 400,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
@@ -115,9 +167,9 @@
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy3",
|
||||
"id": "enemy2_01",
|
||||
"type": "turret",
|
||||
"x": 1500,
|
||||
"x": 2000,
|
||||
"y": 700,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
@@ -126,6 +178,35 @@
|
||||
"attack_range": 300,
|
||||
"sprite_sheet": "assets/map/enemy/turret.gif",
|
||||
"size": [50,100]
|
||||
},
|
||||
{
|
||||
"id": "enemy3_01",
|
||||
"type": "walker",
|
||||
"x": 3600,
|
||||
"y": 1300,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "patrol",
|
||||
"patrol_points": [
|
||||
{"x": 3600, "y": 1300},
|
||||
{"x": 3900, "y": 1300}
|
||||
],
|
||||
"speed": 1.5,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy3_02",
|
||||
"type": "turret",
|
||||
"x": 4000,
|
||||
"y": 1150,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "stationary",
|
||||
"attack_interval": 2.0,
|
||||
"attack_range": 300,
|
||||
"sprite_sheet": "assets/map/enemy/turret.gif",
|
||||
"size": [50,100]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -133,15 +214,8 @@
|
||||
{
|
||||
"id": "coin1",
|
||||
"type": "coin",
|
||||
"x": 1220,
|
||||
"y": 320,
|
||||
"sprite": "assets/map/collectibles/Sanic_Coin.png"
|
||||
},
|
||||
{
|
||||
"id": "coin2",
|
||||
"type": "coin",
|
||||
"x": 400,
|
||||
"y": 540,
|
||||
"x": 600,
|
||||
"y": 300,
|
||||
"sprite": "assets/map/collectibles/Sanic_Coin.png"
|
||||
}
|
||||
],
|
||||
@@ -149,10 +223,10 @@
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "checkpoint1",
|
||||
"x": 1200,
|
||||
"y": 760,
|
||||
"x": 2200,
|
||||
"y": 600,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"height": 125,
|
||||
"sprite": "assets/map/checkpoints/checkpoint.png"
|
||||
}
|
||||
],
|
||||
@@ -164,8 +238,8 @@
|
||||
|
||||
"exits": [
|
||||
{
|
||||
"x": 2300,
|
||||
"y": 700,
|
||||
"x": 5100,
|
||||
"y": 1200,
|
||||
"width": 50,
|
||||
"height": 80,
|
||||
"next_level": "Level 2",
|
||||
|
||||
@@ -1,174 +1,456 @@
|
||||
{
|
||||
"name": "Level 2",
|
||||
"width": 2400,
|
||||
"height": 800,
|
||||
"width": 10500,
|
||||
"height": 1500,
|
||||
"background": "assets/map/background/forest_bg.jpg",
|
||||
"gravity": 1.0,
|
||||
"platforms": [
|
||||
{
|
||||
"id": "platform1",
|
||||
"id": "main_ground",
|
||||
"x": -1000,
|
||||
"y": 520,
|
||||
"width": 1800,
|
||||
"y": 800,
|
||||
"width": 1700,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
"texture": "assets/map/platform/grass_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform2",
|
||||
"id": "platform1_01",
|
||||
"x": 1000,
|
||||
"y": 600,
|
||||
"width": 1800,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform3",
|
||||
"x": 300,
|
||||
"y": 570,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform4",
|
||||
"x": 700,
|
||||
"y": 470,
|
||||
"width": 150,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform5",
|
||||
"x": 900,
|
||||
"y": 470,
|
||||
"width": 150,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform6",
|
||||
"x": 1200,
|
||||
"y": 370,
|
||||
"y": 700,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "circular",
|
||||
"center": {"x": 1000, "y": 700},
|
||||
"radius": 2,
|
||||
"speed": 0.02,
|
||||
"clockwise": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform7",
|
||||
"x": 300,
|
||||
"y": 240,
|
||||
"width": 260,
|
||||
"height": 40,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
"id": "platform1_02",
|
||||
"x": 1300,
|
||||
"y": 700,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 1300, "y": 700},
|
||||
{"x": 1800, "y": 700}
|
||||
],
|
||||
"speed": 3.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform8",
|
||||
"x": 720,
|
||||
"y": 220,
|
||||
"width": 240,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"is_moving": false
|
||||
"id": "platform1_03",
|
||||
"x": 2100,
|
||||
"y": 700,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 2100, "y": 100},
|
||||
{"x": 2100, "y": 700}
|
||||
],
|
||||
"speed": 2.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform9",
|
||||
"x": 520,
|
||||
"id": "main_ground_2",
|
||||
"x": 2300,
|
||||
"y": 200,
|
||||
"width": 1000,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform2_01",
|
||||
"x": 2500,
|
||||
"y": 0,
|
||||
"width": 240,
|
||||
"height": 60,
|
||||
"texture": "assets/map/platform/grass_texture.png",
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform2_02",
|
||||
"x": 3400,
|
||||
"y": 100,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform2_03",
|
||||
"x": 3600,
|
||||
"y": 300,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "main_ground_3",
|
||||
"x": 3700,
|
||||
"y": 600,
|
||||
"width": 1000,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform3_01",
|
||||
"x": 4800,
|
||||
"y": 600,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 4800, "y": 600},
|
||||
{"x": 4800, "y": 1100}
|
||||
],
|
||||
"speed": 2.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform3_02",
|
||||
"x": 5100,
|
||||
"y": 1200,
|
||||
"width": 500,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "main_ground_4",
|
||||
"x": 5700,
|
||||
"y": 1200,
|
||||
"width": 900,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform4_01",
|
||||
"x": 5900,
|
||||
"y": 1000,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform4_02",
|
||||
"x": 6000,
|
||||
"y": 900,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform4_03",
|
||||
"x": 6700,
|
||||
"y": 1300,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "linear",
|
||||
"points": [
|
||||
{"x": 6700, "y": 1300},
|
||||
{"x": 7300, "y": 1300}
|
||||
],
|
||||
"speed": 3.0,
|
||||
"wait_time": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform4_04",
|
||||
"x": 7600,
|
||||
"y": 1150,
|
||||
"width": 200,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "main_ground_5",
|
||||
"x": 7900,
|
||||
"y": 1200,
|
||||
"width": 700,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
},
|
||||
{
|
||||
"id": "platform5_01",
|
||||
"x": 8900,
|
||||
"y": 1200,
|
||||
"width": 300,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": false
|
||||
},
|
||||
{
|
||||
"id": "platform5_02",
|
||||
"x": 9500,
|
||||
"y": 1200,
|
||||
"width": 100,
|
||||
"height": 20,
|
||||
"texture": "assets/map/platform/wood_texture.png",
|
||||
"is_moving": true,
|
||||
"movement": {
|
||||
"type": "circular",
|
||||
"center": {"x": 9500, "y": 1200},
|
||||
"radius": 2,
|
||||
"speed": 0.02,
|
||||
"clockwise": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "main_ground_6",
|
||||
"x": 9800,
|
||||
"y": 1300,
|
||||
"width": 700,
|
||||
"height": 200,
|
||||
"texture": "assets/map/platform/stone_texture.png"
|
||||
}
|
||||
],
|
||||
|
||||
"enemies": [
|
||||
{
|
||||
"id": "enemy1",
|
||||
"type": "turret",
|
||||
"x": 260,
|
||||
"y": 100,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"sprite_sheet": "assets/map/enemy/turret.gif",
|
||||
"size": [
|
||||
50,
|
||||
100
|
||||
],
|
||||
"behavior": "stationary",
|
||||
"attack_interval": 2.0,
|
||||
"attack_range": 300
|
||||
},
|
||||
{
|
||||
"id": "enemy2",
|
||||
"id": "enemy1_01",
|
||||
"type": "walker",
|
||||
"x": 770,
|
||||
"y": 140,
|
||||
"x": 300,
|
||||
"y": 700,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [
|
||||
50,
|
||||
100
|
||||
],
|
||||
"behavior": "patrol",
|
||||
"patrol_points": [
|
||||
{
|
||||
"x": 670,
|
||||
"y": 140
|
||||
},
|
||||
{
|
||||
"x": 870,
|
||||
"y": 140
|
||||
}
|
||||
{"x": 300, "y": 700},
|
||||
{"x": 600, "y": 700}
|
||||
],
|
||||
"speed": 1.5
|
||||
"speed": 1.5,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy3",
|
||||
"id": "enemy2_01",
|
||||
"type": "flyer",
|
||||
"x": 420,
|
||||
"y": 520,
|
||||
"x": 1600,
|
||||
"y": 600,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"sprite_sheet": "assets/map/enemy/flying_enemy.png",
|
||||
"size": [
|
||||
50,
|
||||
100
|
||||
],
|
||||
"behavior": "chase",
|
||||
"detection_radius": 200,
|
||||
"speed": 2.0
|
||||
"speed": 2.0,
|
||||
"sprite_sheet": "assets/map/enemy/flying_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy1_02",
|
||||
"type": "turret",
|
||||
"x": 2900,
|
||||
"y": 75,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "stationary",
|
||||
"attack_interval": 2.0,
|
||||
"attack_range": 300,
|
||||
"sprite_sheet": "assets/map/enemy/turret.gif",
|
||||
"size": [50,100]
|
||||
},
|
||||
{
|
||||
"id": "enemy2_02",
|
||||
"type": "flyer",
|
||||
"x": 3700,
|
||||
"y": 200,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "chase",
|
||||
"detection_radius": 200,
|
||||
"speed": 2.0,
|
||||
"sprite_sheet": "assets/map/enemy/flying_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy1_03",
|
||||
"type": "walker",
|
||||
"x": 3800,
|
||||
"y": 450,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "patrol",
|
||||
"patrol_points": [
|
||||
{"x": 3800, "y": 450},
|
||||
{"x": 4100, "y": 450}
|
||||
],
|
||||
"speed": 1.5,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy2_03",
|
||||
"type": "walker",
|
||||
"x": 4200,
|
||||
"y": 450,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "patrol",
|
||||
"patrol_points": [
|
||||
{"x": 4200, "y": 450},
|
||||
{"x": 4600, "y": 450}
|
||||
],
|
||||
"speed": 1.5,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy1_04",
|
||||
"type": "flyer",
|
||||
"x": 6100,
|
||||
"y": 800,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "chase",
|
||||
"detection_radius": 200,
|
||||
"speed": 2.0,
|
||||
"sprite_sheet": "assets/map/enemy/flying_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy2_04",
|
||||
"type": "turret",
|
||||
"x": 6300,
|
||||
"y": 1050,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "stationary",
|
||||
"attack_interval": 2.0,
|
||||
"attack_range": 300,
|
||||
"sprite_sheet": "assets/map/enemy/turret.gif",
|
||||
"size": [50,100]
|
||||
},
|
||||
{
|
||||
"id": "enemy1_05",
|
||||
"type": "turret",
|
||||
"x": 8300,
|
||||
"y": 1050,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "stationary",
|
||||
"attack_interval": 2.0,
|
||||
"attack_range": 300,
|
||||
"sprite_sheet": "assets/map/enemy/turret.gif",
|
||||
"size": [50,100]
|
||||
},
|
||||
{
|
||||
"id": "enemy1_06",
|
||||
"type": "flyer",
|
||||
"x": 9800,
|
||||
"y": 1100,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "chase",
|
||||
"detection_radius": 200,
|
||||
"speed": 2.0,
|
||||
"sprite_sheet": "assets/map/enemy/flying_enemy.png",
|
||||
"size": [50,50]
|
||||
},
|
||||
{
|
||||
"id": "enemy2_06",
|
||||
"type": "walker",
|
||||
"x": 9900,
|
||||
"y": 1200,
|
||||
"health": 1,
|
||||
"damage": 1,
|
||||
"behavior": "patrol",
|
||||
"patrol_points": [
|
||||
{"x": 9900, "y": 1200},
|
||||
{"x": 10200, "y": 1200}
|
||||
],
|
||||
"speed": 1.5,
|
||||
"sprite_sheet": "assets/map/enemy/walker_enemy.png",
|
||||
"size": [50,50]
|
||||
}
|
||||
],
|
||||
|
||||
"collectibles": [
|
||||
{
|
||||
"id": "coin1",
|
||||
"type": "coin",
|
||||
"x": 2500,
|
||||
"y": -100,
|
||||
"sprite": "assets/map/collectibles/Sanic_Coin.png"
|
||||
},
|
||||
{
|
||||
"id": "coin2",
|
||||
"type": "coin",
|
||||
"x": 4600,
|
||||
"y": 800,
|
||||
"sprite": "assets/map/collectibles/Sanic_Coin.png"
|
||||
},
|
||||
{
|
||||
"id": "coin3",
|
||||
"type": "coin",
|
||||
"x": 6000,
|
||||
"y": 800,
|
||||
"sprite": "assets/map/collectibles/Sanic_Coin.png"
|
||||
},
|
||||
{
|
||||
"id": "jump1",
|
||||
"type": "jump",
|
||||
"x": 6500,
|
||||
"y": 1000,
|
||||
"sprite": "assets/map/collectibles/jump.png"
|
||||
},
|
||||
{
|
||||
"id": "speed1",
|
||||
"type": "speed",
|
||||
"x": 8000,
|
||||
"y": 1000,
|
||||
"sprite": "assets/map/collectibles/speed.png"
|
||||
}
|
||||
],
|
||||
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "checkpoint1",
|
||||
"x": 1080,
|
||||
"y": 450,
|
||||
"x": 5300,
|
||||
"y": 1075,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"sprite": "assets/map/checkpoints/checkpoint_uncheck.png"
|
||||
"height": 125,
|
||||
"sprite": "assets/map/checkpoints/checkpoint.png"
|
||||
}
|
||||
],
|
||||
|
||||
"spawn_point": {
|
||||
"x": 50,
|
||||
"y": 650
|
||||
},
|
||||
|
||||
"exits": [
|
||||
{
|
||||
"x": 2225,
|
||||
"y": 500,
|
||||
"x": 10450,
|
||||
"y": 1000,
|
||||
"width": 50,
|
||||
"height": 80,
|
||||
"next_level": "map/levels/1.json",
|
||||
"next_level": "Level 2",
|
||||
"sprite": "assets/map/exit/Zeldo.png"
|
||||
}
|
||||
],
|
||||
"collectibles": [],
|
||||
"spawn_point": {
|
||||
"x": 50.0,
|
||||
"y": 350.0
|
||||
}
|
||||
]
|
||||
}
|
||||
9
profiler.py
Normal file
9
profiler.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import cProfile
|
||||
from src.handler import handler
|
||||
|
||||
|
||||
def main():
|
||||
handler()
|
||||
|
||||
|
||||
cProfile.run("main()", "output.prof")
|
||||
@@ -81,3 +81,18 @@ class CheckpointDB:
|
||||
self.conn.commit()
|
||||
except Exception as e:
|
||||
print(f"Error clearing checkpoint database: {e}")
|
||||
|
||||
def reset_level(self, map_name):
|
||||
"""
|
||||
Reset the checkpoint for a specific map
|
||||
|
||||
Args:
|
||||
map_name: Map name to reset
|
||||
"""
|
||||
try:
|
||||
self.cursor.execute(
|
||||
"DELETE FROM checkpoints WHERE map_name = ?", (map_name,)
|
||||
)
|
||||
self.conn.commit()
|
||||
except Exception as e:
|
||||
print(f"Error resetting checkpoint for {map_name}: {e}")
|
||||
|
||||
56
src/Database/InfiniteModeDB.py
Normal file
56
src/Database/InfiniteModeDB.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
|
||||
class InfiniteModeDB:
|
||||
def __init__(self, db_file="game.db"):
|
||||
"""
|
||||
Initialize database connection for infinite game mode points management.
|
||||
|
||||
Args:
|
||||
db_file: SQLite database file path.
|
||||
"""
|
||||
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 InfiniteMode (
|
||||
player_name TEXT,
|
||||
score INTEGER
|
||||
)
|
||||
"""
|
||||
)
|
||||
self.conn.commit()
|
||||
|
||||
def get_all(self):
|
||||
"""Get all scores from the table."""
|
||||
self.cursor.execute("SELECT * FROM InfiniteMode")
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def add_score(self, player_name, score):
|
||||
"""Add a new score to the InfiniteMode."""
|
||||
self.cursor.execute(
|
||||
"INSERT INTO InfiniteMode (player_name, score) VALUES (?, ?)",
|
||||
(player_name, score),
|
||||
)
|
||||
self.conn.commit()
|
||||
|
||||
def clear_InfiniteModeDB(self):
|
||||
"""Clear all scores from the InfiniteMode table."""
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM InfiniteMode")
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(f"Error clearing InfiniteMode table: {e}")
|
||||
|
||||
def close(self):
|
||||
"""Close database connection."""
|
||||
if self.conn:
|
||||
self.conn.close()
|
||||
56
src/Database/LeaderboardDB.py
Normal file
56
src/Database/LeaderboardDB.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
|
||||
class LeaderboardDB:
|
||||
def __init__(self, db_file="game.db"):
|
||||
"""
|
||||
Initialize database connection for leaderboard management.
|
||||
|
||||
Args:
|
||||
db_file: SQLite database file path.
|
||||
"""
|
||||
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):
|
||||
"""Ensure the Leaderboard table has the correct schema."""
|
||||
self.cursor.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS Leaderboard (
|
||||
player_name TEXT,
|
||||
score INTEGER,
|
||||
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
)
|
||||
self.conn.commit()
|
||||
|
||||
def get_top_10_scores(self):
|
||||
"""Get top 10 scores from the leaderboard."""
|
||||
self.cursor.execute(
|
||||
"SELECT score, date FROM Leaderboard ORDER BY score DESC LIMIT 10"
|
||||
)
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def add_score(self, player_name, score):
|
||||
"""Add a new score to the leaderboard."""
|
||||
self.cursor.execute(
|
||||
"INSERT INTO Leaderboard (player_name, score) VALUES (?, ?)",
|
||||
(player_name, score),
|
||||
)
|
||||
self.conn.commit()
|
||||
|
||||
def clear_leaderboard(self):
|
||||
"""Clear all scores from the leaderboard."""
|
||||
self.cursor.execute("DELETE FROM Leaderboard")
|
||||
self.conn.commit()
|
||||
|
||||
def close(self):
|
||||
"""Close database connection."""
|
||||
if self.conn:
|
||||
self.conn.close()
|
||||
BIN
src/Database/__pycache__/CheckpointDB.cpython-313.pyc
Normal file
BIN
src/Database/__pycache__/CheckpointDB.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Database/__pycache__/CoinDB.cpython-313.pyc
Normal file
BIN
src/Database/__pycache__/CoinDB.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Database/__pycache__/InfiniteModeDB.cpython-313.pyc
Normal file
BIN
src/Database/__pycache__/InfiniteModeDB.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Database/__pycache__/LeaderboardDB.cpython-313.pyc
Normal file
BIN
src/Database/__pycache__/LeaderboardDB.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Database/__pycache__/LevelDB.cpython-313.pyc
Normal file
BIN
src/Database/__pycache__/LevelDB.cpython-313.pyc
Normal file
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
import pygame
|
||||
import os
|
||||
from src.Entity.Entity import Entity
|
||||
from moviepy import VideoFileClip
|
||||
import moviepy as mp
|
||||
@@ -75,9 +76,16 @@ class Exit(Entity):
|
||||
# Extract audio from the video
|
||||
audio = mp.AudioFileClip(video_path)
|
||||
audio.write_audiofile("temp_audio.mp3")
|
||||
pygame.mixer.init()
|
||||
pygame.mixer.music.load("temp_audio.mp3")
|
||||
pygame.mixer.music.play()
|
||||
|
||||
# Pause the main music without stopping it
|
||||
main_music_pos = (
|
||||
pygame.mixer.music.get_pos() / 1000 if pygame.mixer.get_init() else 0
|
||||
)
|
||||
pygame.mixer.music.pause()
|
||||
|
||||
# Load and play the audio on a separate channel
|
||||
temp_sound = pygame.mixer.Sound("temp_audio.mp3")
|
||||
sound_channel = temp_sound.play()
|
||||
|
||||
for frame in clip.iter_frames(fps=24, dtype="uint8"):
|
||||
frame_surface = pygame.surfarray.make_surface(frame.swapaxes(0, 1))
|
||||
@@ -85,11 +93,22 @@ class Exit(Entity):
|
||||
pygame.display.flip()
|
||||
clock.tick(24)
|
||||
|
||||
clip.close()
|
||||
pygame.mixer.music.stop()
|
||||
pygame.mixer.quit()
|
||||
# Check if the sound channel is still playing
|
||||
if sound_channel and not sound_channel.get_busy():
|
||||
break
|
||||
|
||||
# Create and post a return to menu event
|
||||
clip.close()
|
||||
|
||||
# Play the main music again from the last position
|
||||
pygame.mixer.music.unpause()
|
||||
|
||||
# Remove the temporary audio file
|
||||
try:
|
||||
os.remove("temp_audio.mp3")
|
||||
except Exception as e:
|
||||
print(f"Error removing temporary audio file: {e}")
|
||||
|
||||
# Return to the menu
|
||||
return_event = pygame.event.Event(
|
||||
pygame.USEREVENT, {"action": "return_to_menu"}
|
||||
)
|
||||
|
||||
112
src/Entity/JumpBoost.py
Normal file
112
src/Entity/JumpBoost.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import os
|
||||
import pygame
|
||||
import time
|
||||
from src.Entity.Entity import Entity
|
||||
|
||||
|
||||
class JumpBoost(Entity):
|
||||
"""
|
||||
A collectible that temporarily increases the player's jump power
|
||||
for 3 seconds when collected.
|
||||
"""
|
||||
|
||||
def __init__(self, pos, size=(30, 30), color=(0, 255, 0), texturePath=""):
|
||||
super().__init__(pos=pos, size=size, color=color, texturePath=texturePath)
|
||||
self.collected = False
|
||||
|
||||
# Jump boost properties
|
||||
self.boost_factor = 1.5
|
||||
self.boost_duration = 10
|
||||
|
||||
# Create initial surface
|
||||
self.surf = pygame.Surface(size, pygame.SRCALPHA)
|
||||
|
||||
# Load and scale texture
|
||||
if texturePath:
|
||||
try:
|
||||
if os.path.exists(texturePath):
|
||||
texture = pygame.image.load(texturePath).convert_alpha()
|
||||
textureSize = (size[0] * 1.5, size[1] * 1.5)
|
||||
self.surf = pygame.transform.scale(texture, textureSize)
|
||||
else:
|
||||
self.draw_fallback(color, size)
|
||||
except Exception as e:
|
||||
self.draw_fallback(color, size)
|
||||
else:
|
||||
self.draw_fallback(color, size)
|
||||
|
||||
# Set rect
|
||||
self.rect = self.surf.get_rect()
|
||||
self.rect.topleft = pos
|
||||
|
||||
# Animation properties
|
||||
self.animation_frame = 0
|
||||
self.last_update = 0
|
||||
|
||||
def draw_fallback(self, color, size):
|
||||
"""Draw a green arrow pointing up as fallback"""
|
||||
self.surf.fill((0, 0, 0, 0))
|
||||
|
||||
# Draw an arrow pointing up
|
||||
half_width = size[0] // 2
|
||||
height = size[1]
|
||||
|
||||
# Arrow head
|
||||
head_points = [
|
||||
(half_width, 0),
|
||||
(half_width - 8, 10),
|
||||
(half_width + 8, 10),
|
||||
]
|
||||
|
||||
# Arrow body
|
||||
body_rect = pygame.Rect(half_width - 4, 10, 8, height - 10)
|
||||
|
||||
pygame.draw.polygon(self.surf, color, head_points)
|
||||
pygame.draw.rect(self.surf, color, body_rect)
|
||||
|
||||
def update(self):
|
||||
"""Update the jump boost animation"""
|
||||
now = pygame.time.get_ticks()
|
||||
if now - self.last_update > 200:
|
||||
self.last_update = now
|
||||
self.animation_frame = (self.animation_frame + 1) % 4
|
||||
# Simple floating animation
|
||||
self.rect.y += [-1, 0, 1, 0][self.animation_frame]
|
||||
|
||||
def on_collision(self, player):
|
||||
"""
|
||||
Handle jump boost collision with player
|
||||
|
||||
Args:
|
||||
player: The player object to apply the boost to
|
||||
"""
|
||||
if not self.collected:
|
||||
self.collected = True
|
||||
|
||||
# Store original jump power
|
||||
original_jump_power = player.jump_power
|
||||
|
||||
# Apply boost effect
|
||||
player.jump_power *= self.boost_factor
|
||||
|
||||
# Set visual feedback
|
||||
player.jump_boost_active = True
|
||||
|
||||
# Schedule effect removal
|
||||
pygame.time.set_timer(
|
||||
pygame.USEREVENT + 2, # Custom event ID for jump boost expiration
|
||||
self.boost_duration * 1000, # Convert to milliseconds
|
||||
1, # Only trigger once
|
||||
)
|
||||
|
||||
# Store reference to restore original jump power
|
||||
player.active_jump_boost = {
|
||||
"original_power": original_jump_power,
|
||||
"boost_object": self,
|
||||
}
|
||||
|
||||
# Remove the collectible from display
|
||||
self.kill()
|
||||
|
||||
return True
|
||||
return False
|
||||
@@ -2,6 +2,7 @@ from src.Entity.Entity import Entity
|
||||
from pygame import *
|
||||
import pygame
|
||||
import os
|
||||
from PIL import Image, ImageSequence
|
||||
from pygame.math import Vector2 as vec
|
||||
from src.Entity.Projectile import Projectile
|
||||
|
||||
@@ -45,10 +46,17 @@ class Player(Entity):
|
||||
self.dash_start_time = 0
|
||||
self.dash_duration = 500 # 1/2 second activation time
|
||||
self.dash_cooldown = 3000 # 3 seconds cooldown
|
||||
self.speed_boost_active = False
|
||||
self.active_speed_boost = None
|
||||
|
||||
# Jump mechanics
|
||||
self.jump_power = 30
|
||||
self.jump_boost_active = False
|
||||
self.active_jump_boost = None
|
||||
|
||||
# Life system
|
||||
self.max_lives = 2
|
||||
self.lives = 2
|
||||
self.max_lives = 5
|
||||
self.lives = 3
|
||||
self.invulnerable = False
|
||||
self.invulnerable_timer = 0
|
||||
self.invulnerable_duration = 1.5
|
||||
@@ -75,107 +83,174 @@ class Player(Entity):
|
||||
self.attack_start_time = 0
|
||||
self.attack_cooldown = 2000
|
||||
|
||||
self.facing_right = True
|
||||
|
||||
# Initilize mixer
|
||||
pygame.mixer.init()
|
||||
|
||||
def load_images(self):
|
||||
"""Load images for the player"""
|
||||
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, (100, 100)
|
||||
)
|
||||
|
||||
# Load regular 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_height = sprite_sheet.get_height()
|
||||
frame_width = sprite_sheet.get_width() // 4
|
||||
|
||||
for i in range(4):
|
||||
# Cut out a region of the sprite sheet
|
||||
frame = sprite_sheet.subsurface(
|
||||
(i * 2290, 0, frame_width, frame_height)
|
||||
)
|
||||
# Resize the frame
|
||||
frame = pygame.transform.scale(frame, (100, 100))
|
||||
self.animation_frames.append(frame)
|
||||
|
||||
# Load jump animation sprite sheet
|
||||
if os.path.isfile("assets/player/Sanic Boule.png"):
|
||||
self.jump_frames.append(
|
||||
pygame.transform.scale(
|
||||
pygame.image.load(
|
||||
"assets/player/Sanic Boule.png"
|
||||
).convert_alpha(),
|
||||
(80, 80),
|
||||
)
|
||||
)
|
||||
|
||||
# Load dash animation sprite sheet
|
||||
if os.path.isfile("assets/player/Sanic Boule Annimate.png"):
|
||||
dash_sheet = pygame.image.load(
|
||||
"assets/player/Sanic Boule Annimate.png"
|
||||
).convert_alpha()
|
||||
|
||||
dash_frame_height = dash_sheet.get_height()
|
||||
|
||||
for i in range(4):
|
||||
frame = dash_sheet.subsurface(
|
||||
(i * 2000, 0, dash_frame_height, dash_frame_height)
|
||||
)
|
||||
frame = pygame.transform.scale(frame, (80, 80))
|
||||
self.dash_frames.append(frame)
|
||||
|
||||
# Load life icon
|
||||
if os.path.isfile("assets/player/Sanic Head.png"):
|
||||
self.life_icon = pygame.image.load(
|
||||
"assets/player/Sanic Head.png"
|
||||
).convert_alpha()
|
||||
self.life_icon = pygame.transform.scale(
|
||||
self.life_icon,
|
||||
(
|
||||
self.game_resources.life_icon_width,
|
||||
self.game_resources.life_icon_width,
|
||||
),
|
||||
)
|
||||
# Load the 7 frames of the GIF
|
||||
if os.path.isfile("assets/player/Sanic.gif"):
|
||||
self.load_gif_frames("assets/player/Sanic.gif")
|
||||
self.animation_speed = 0.05
|
||||
else:
|
||||
# Backup: use a red square
|
||||
self.life_icon = pygame.Surface(
|
||||
(
|
||||
self.game_resources.life_icon_width,
|
||||
self.game_resources.life_icon_width,
|
||||
)
|
||||
)
|
||||
self.life_icon.fill((255, 0, 0))
|
||||
# Fallback to static image if GIF is not found
|
||||
self.load_static_and_sprite_sheets()
|
||||
|
||||
# Load special animations (jump, dash, ...)
|
||||
self.load_special_animations()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error loading player images: {e}")
|
||||
|
||||
def load_gif_frames(self, gif_path):
|
||||
"""Load frames from a GIF file"""
|
||||
try:
|
||||
gif = Image.open(gif_path)
|
||||
|
||||
self.animation_frames = []
|
||||
|
||||
for frame in ImageSequence.Iterator(gif):
|
||||
# Convert the frame to a format compatible with Pygame
|
||||
frame_rgb = frame.convert("RGBA")
|
||||
raw_str = frame_rgb.tobytes("raw", "RGBA")
|
||||
|
||||
pygame_surface = pygame.image.fromstring(
|
||||
raw_str, frame_rgb.size, "RGBA"
|
||||
)
|
||||
|
||||
pygame_surface = pygame.transform.scale(pygame_surface, (125, 125))
|
||||
|
||||
self.animation_frames.append(pygame_surface)
|
||||
|
||||
# Use the first frame as the static image
|
||||
if self.animation_frames:
|
||||
self.static_image = self.animation_frames[0]
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error while loading the GIF: {e}")
|
||||
|
||||
def load_static_and_sprite_sheets(self):
|
||||
"""Previous method to load static image and sprite sheets"""
|
||||
# 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, (100, 100))
|
||||
|
||||
# Load regular 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_height = sprite_sheet.get_height()
|
||||
frame_width = sprite_sheet.get_width() // 4
|
||||
|
||||
for i in range(4):
|
||||
# Cut out a region of the sprite sheet
|
||||
frame = sprite_sheet.subsurface(
|
||||
(i * 2290, 0, frame_width, frame_height)
|
||||
)
|
||||
# Resize the frame
|
||||
frame = pygame.transform.scale(frame, (100, 100))
|
||||
self.animation_frames.append(frame)
|
||||
|
||||
def load_special_animations(self):
|
||||
"""Load special animations for jump and dash"""
|
||||
# Load jump animation sprite sheet
|
||||
if os.path.isfile("assets/player/Sanic Boule.png"):
|
||||
self.jump_frames.append(
|
||||
pygame.transform.scale(
|
||||
pygame.image.load("assets/player/Sanic Boule.png").convert_alpha(),
|
||||
(80, 80),
|
||||
)
|
||||
)
|
||||
|
||||
# Load dash animation sprite sheet
|
||||
if os.path.isfile("assets/player/Sanic Boule Annimate.png"):
|
||||
dash_sheet = pygame.image.load(
|
||||
"assets/player/Sanic Boule Annimate.png"
|
||||
).convert_alpha()
|
||||
|
||||
dash_frame_height = dash_sheet.get_height()
|
||||
|
||||
for i in range(4):
|
||||
frame = dash_sheet.subsurface(
|
||||
(i * 2000, 0, dash_frame_height, dash_frame_height)
|
||||
)
|
||||
frame = pygame.transform.scale(frame, (80, 80))
|
||||
self.dash_frames.append(frame)
|
||||
|
||||
# Load life icon
|
||||
if os.path.isfile("assets/player/Sanic Head.png"):
|
||||
self.life_icon = pygame.image.load(
|
||||
"assets/player/Sanic Head.png"
|
||||
).convert_alpha()
|
||||
self.life_icon = pygame.transform.scale(
|
||||
self.life_icon,
|
||||
(
|
||||
self.game_resources.life_icon_width,
|
||||
self.game_resources.life_icon_width,
|
||||
),
|
||||
)
|
||||
else:
|
||||
# Backup: use a red square
|
||||
self.life_icon = pygame.Surface(
|
||||
(
|
||||
self.game_resources.life_icon_width,
|
||||
self.game_resources.life_icon_width,
|
||||
)
|
||||
)
|
||||
self.life_icon.fill((255, 0, 0))
|
||||
|
||||
def update_animation(self):
|
||||
current_time = pygame.time.get_ticks()
|
||||
|
||||
current_image = None
|
||||
|
||||
# Priority: Dashing > Jumping > Moving > Static
|
||||
if self.dashing and self.dash_frames:
|
||||
if self.dashing and self.dash_frames and len(self.dash_frames) > 0:
|
||||
if current_time - self.last_update > self.animation_speed * 1000:
|
||||
self.current_frame = (self.current_frame + 1) % len(self.dash_frames)
|
||||
self.surf = self.dash_frames[self.current_frame]
|
||||
self.last_update = current_time
|
||||
elif self.jumping and self.jump_frames:
|
||||
self.surf = self.jump_frames[0] # Use jump frame
|
||||
elif self.moving and self.animation_frames:
|
||||
|
||||
if 0 <= self.current_frame < len(self.dash_frames):
|
||||
current_image = self.dash_frames[self.current_frame]
|
||||
|
||||
elif self.jumping and self.jump_frames and len(self.jump_frames) > 0:
|
||||
if 0 < len(self.jump_frames):
|
||||
current_image = self.jump_frames[0]
|
||||
|
||||
elif self.moving and self.animation_frames and len(self.animation_frames) > 0:
|
||||
if current_time - self.last_update > self.animation_speed * 1000:
|
||||
self.current_frame = (self.current_frame + 1) % len(
|
||||
self.animation_frames
|
||||
)
|
||||
self.surf = self.animation_frames[self.current_frame]
|
||||
self.last_update = current_time
|
||||
|
||||
if 0 <= self.current_frame < len(self.animation_frames):
|
||||
current_image = self.animation_frames[self.current_frame]
|
||||
|
||||
elif self.static_image:
|
||||
self.surf = self.static_image
|
||||
current_image = self.static_image
|
||||
|
||||
# If no animation is found, use the static imagef available
|
||||
if not current_image:
|
||||
if self.static_image:
|
||||
current_image = self.static_image
|
||||
else:
|
||||
return
|
||||
|
||||
# Appliquer le retournement selon la direction
|
||||
if not self.facing_right:
|
||||
self.surf = pygame.transform.flip(current_image, True, False)
|
||||
else:
|
||||
self.surf = current_image
|
||||
|
||||
def dash(self, acc):
|
||||
current_time = pygame.time.get_ticks()
|
||||
@@ -209,7 +284,7 @@ class Player(Entity):
|
||||
|
||||
if self.has_joystick and self.joystick:
|
||||
try:
|
||||
# Joystick gauche pour mouvement
|
||||
# Left joystick for movement
|
||||
if self.joystick.get_numaxes() > 0:
|
||||
joystick_x = self.joystick.get_axis(0)
|
||||
if abs(joystick_x) > 0.2:
|
||||
@@ -218,7 +293,7 @@ class Player(Entity):
|
||||
elif joystick_x > 0:
|
||||
move_right = True
|
||||
|
||||
# Boutons pour sauter/dasher
|
||||
# Button for jumping and dashing
|
||||
if self.joystick.get_numbuttons() > self.jump_button:
|
||||
if self.joystick.get_button(self.jump_button):
|
||||
jump = True
|
||||
@@ -227,7 +302,7 @@ class Player(Entity):
|
||||
if self.joystick.get_button(self.dash_button):
|
||||
dash_key = True
|
||||
except pygame.error:
|
||||
pass # Ignorer les erreurs de manette
|
||||
pass
|
||||
|
||||
if move_left:
|
||||
# Check if X is > 0 to prevent player from going off screen
|
||||
@@ -248,7 +323,12 @@ class Player(Entity):
|
||||
|
||||
# Jumping logic
|
||||
if jump and not self.jumping:
|
||||
self.vel.y = -30
|
||||
try:
|
||||
jump_sound = pygame.mixer.Sound("assets/sound/Jump.mp3")
|
||||
jump_sound.play()
|
||||
except Exception as e:
|
||||
print(f"Error playing jump sound: {e}")
|
||||
self.vel.y = -self.jump_power
|
||||
self.jumping = True
|
||||
|
||||
# Apply friction
|
||||
@@ -350,6 +430,11 @@ class Player(Entity):
|
||||
if fall_distance > 500:
|
||||
self.death()
|
||||
|
||||
if self.vel.x > 0:
|
||||
self.facing_right = True
|
||||
elif self.vel.x < 0:
|
||||
self.facing_right = False
|
||||
|
||||
def take_damage(self, amount=1):
|
||||
"""Reduce life number if not invulnerable"""
|
||||
if not self.invulnerable:
|
||||
@@ -358,7 +443,7 @@ class Player(Entity):
|
||||
if self.lives <= 0:
|
||||
self.death()
|
||||
else:
|
||||
# Période d'invulnérabilité temporaire
|
||||
# Temporarily make the player invulnerable
|
||||
self.invulnerable = True
|
||||
self.invulnerable_timer = 0
|
||||
|
||||
@@ -377,7 +462,7 @@ class Player(Entity):
|
||||
|
||||
for i in range(self.max_lives):
|
||||
if i < self.lives:
|
||||
# Vie active: afficher l'icône normale
|
||||
# Active life: display the icon
|
||||
surface.blit(
|
||||
self.life_icon,
|
||||
(
|
||||
@@ -386,9 +471,9 @@ class Player(Entity):
|
||||
),
|
||||
)
|
||||
else:
|
||||
# Vie perdue: afficher l'icône grisée
|
||||
# Life lost: display a grayscale version of the icon
|
||||
grayscale_icon = self.life_icon.copy()
|
||||
# Appliquer un filtre gris
|
||||
# Apply grayscale effect
|
||||
for x in range(grayscale_icon.get_width()):
|
||||
for y in range(grayscale_icon.get_height()):
|
||||
color = grayscale_icon.get_at((x, y))
|
||||
@@ -433,9 +518,16 @@ class Player(Entity):
|
||||
|
||||
surface.blit(coin_text, (text_x, text_y))
|
||||
|
||||
def collect_coin(self, surface):
|
||||
def collect_coin(self, surface, speedrun_timer=None):
|
||||
"""Increment coin counter when collecting a coin"""
|
||||
coin_sound = pygame.mixer.Sound("assets/sound/Coin.mp3")
|
||||
coin_sound.play()
|
||||
self.coins += 1
|
||||
if self.lives < self.max_lives:
|
||||
self.lives += 1
|
||||
self.draw_lives(surface)
|
||||
if speedrun_timer:
|
||||
speedrun_timer.collected_items += 1
|
||||
|
||||
def attack(self):
|
||||
"""Do an attack action on the player"""
|
||||
@@ -491,6 +583,9 @@ class Player(Entity):
|
||||
|
||||
if pressed_keys[K_q] and pressed_keys[K_v]:
|
||||
if current_time - self.last_attack_time >= self.attack_cooldown:
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
@@ -539,6 +634,9 @@ class Player(Entity):
|
||||
|
||||
if pressed_keys[K_d] and pressed_keys[K_v]:
|
||||
if current_time - self.last_attack_time >= self.attack_cooldown:
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
|
||||
110
src/Entity/SpeedBoost.py
Normal file
110
src/Entity/SpeedBoost.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
import pygame
|
||||
import time
|
||||
from src.Entity.Entity import Entity
|
||||
|
||||
|
||||
class SpeedBoost(Entity):
|
||||
"""
|
||||
A collectible that temporarily increases the player's movement speed
|
||||
for 3 seconds when collected.
|
||||
"""
|
||||
|
||||
def __init__(self, pos, size=(30, 30), color=(0, 0, 255), texturePath=""):
|
||||
super().__init__(pos=pos, size=size, color=color, texturePath=texturePath)
|
||||
self.collected = False
|
||||
|
||||
# Speed boost properties
|
||||
self.boost_factor = 2
|
||||
self.boost_duration = 10
|
||||
|
||||
# Create initial surface
|
||||
self.surf = pygame.Surface(size, pygame.SRCALPHA)
|
||||
|
||||
# Load and scale texture
|
||||
if texturePath:
|
||||
try:
|
||||
if os.path.exists(texturePath):
|
||||
texture = pygame.image.load(texturePath).convert_alpha()
|
||||
textureSize = (size[0] * 3, size[1] * 3)
|
||||
self.surf = pygame.transform.scale(texture, textureSize)
|
||||
else:
|
||||
self.draw_fallback(color, size)
|
||||
except Exception as e:
|
||||
self.draw_fallback(color, size)
|
||||
else:
|
||||
self.draw_fallback(color, size)
|
||||
|
||||
# Set rect
|
||||
self.rect = self.surf.get_rect()
|
||||
self.rect.topleft = pos
|
||||
|
||||
# Animation properties
|
||||
self.animation_frame = 0
|
||||
self.last_update = 0
|
||||
|
||||
def draw_fallback(self, color, size):
|
||||
"""Draw a blue lightning bolt as fallback"""
|
||||
self.surf.fill((0, 0, 0, 0))
|
||||
|
||||
# Draw a lightning bolt symbol
|
||||
width, height = size
|
||||
points = [
|
||||
(width // 2, 0),
|
||||
(width // 4, height // 2),
|
||||
(width // 2 - 2, height // 2),
|
||||
(width // 3, height),
|
||||
(width // 2 + 5, height // 2 + 5),
|
||||
(width // 2 + 2, height // 2),
|
||||
(3 * width // 4, height // 2),
|
||||
]
|
||||
|
||||
pygame.draw.polygon(self.surf, color, points)
|
||||
|
||||
def update(self):
|
||||
"""Update the speed boost animation"""
|
||||
now = pygame.time.get_ticks()
|
||||
if now - self.last_update > 200:
|
||||
self.last_update = now
|
||||
self.animation_frame = (self.animation_frame + 1) % 4
|
||||
# Simple floating animation
|
||||
self.rect.y += [-1, 0, 1, 0][self.animation_frame]
|
||||
|
||||
def on_collision(self, player, game_resources):
|
||||
"""
|
||||
Handle speed boost collision with player
|
||||
|
||||
Args:
|
||||
player: The player object to apply the boost to
|
||||
game_resources: Game resources object containing player speed
|
||||
"""
|
||||
if not self.collected:
|
||||
self.collected = True
|
||||
|
||||
# Store original movement speed
|
||||
original_ACC = game_resources.ACC
|
||||
|
||||
# Apply boost effect
|
||||
game_resources.ACC *= self.boost_factor
|
||||
|
||||
# Set visual feedback
|
||||
player.speed_boost_active = True
|
||||
|
||||
# Schedule effect removal
|
||||
pygame.time.set_timer(
|
||||
pygame.USEREVENT + 3, # Custom event ID for speed boost expiration
|
||||
self.boost_duration * 1000, # Convert to milliseconds
|
||||
1, # Only trigger once
|
||||
)
|
||||
|
||||
# Store reference to restore original speed
|
||||
player.active_speed_boost = {
|
||||
"original_ACC": original_ACC,
|
||||
"boost_object": self,
|
||||
}
|
||||
|
||||
# Remove the collectible from display
|
||||
self.kill()
|
||||
|
||||
return True
|
||||
return False
|
||||
BIN
src/Entity/__pycache__/Checkpoint.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Checkpoint.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Coin.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Coin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Enemy.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Enemy.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Entity.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Entity.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Exit.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Exit.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/JumpBoost.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/JumpBoost.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Platform.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Platform.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Player.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Player.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/Projectile.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/Projectile.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Entity/__pycache__/SpeedBoost.cpython-313.pyc
Normal file
BIN
src/Entity/__pycache__/SpeedBoost.cpython-313.pyc
Normal file
Binary file not shown.
@@ -611,7 +611,7 @@ class LevelEditor:
|
||||
self.selected_object, EditorCollectible
|
||||
):
|
||||
if event.key == K_t:
|
||||
types = ["coin"]
|
||||
types = ["coin", "jump", "speed"]
|
||||
current_index = (
|
||||
types.index(self.selected_object.collectible_type)
|
||||
if self.selected_object.collectible_type in types
|
||||
@@ -623,6 +623,10 @@ class LevelEditor:
|
||||
# Update appearance based on type
|
||||
if self.selected_object.collectible_type == "coin":
|
||||
self.selected_object.image.fill((255, 215, 0))
|
||||
elif self.selected_object.collectible_type == "jump":
|
||||
self.selected_object.image.fill((0, 255, 0))
|
||||
elif self.selected_object.collectible_type == "speed":
|
||||
self.selected_object.image.fill((0, 0, 255))
|
||||
|
||||
elif self.selected_object and isinstance(self.selected_object, EditorExit):
|
||||
if event.key == K_n:
|
||||
|
||||
BIN
src/Map/Editor/__pycache__/EditorSprites.cpython-313.pyc
Normal file
BIN
src/Map/Editor/__pycache__/EditorSprites.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Map/Editor/__pycache__/LevelEditor.cpython-313.pyc
Normal file
BIN
src/Map/Editor/__pycache__/LevelEditor.cpython-313.pyc
Normal file
Binary file not shown.
@@ -70,13 +70,20 @@ class InfiniteMapGenerator:
|
||||
# Generate additional platforms
|
||||
num_platforms = 10 + difficulty * 2
|
||||
last_x = 600
|
||||
last_y = 260
|
||||
max_jump_height = 120
|
||||
|
||||
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)
|
||||
|
||||
min_y = max(150, last_y - max_jump_height)
|
||||
max_y = min(400, last_y + 80)
|
||||
|
||||
y = random.randint(min_y, max_y)
|
||||
|
||||
is_moving = random.random() < min(0.1 + difficulty * 0.05, 0.5)
|
||||
|
||||
@@ -111,6 +118,7 @@ class InfiniteMapGenerator:
|
||||
|
||||
platforms.append(platform)
|
||||
last_x = x + width
|
||||
last_y = y
|
||||
|
||||
return platforms
|
||||
|
||||
|
||||
Binary file not shown.
BIN
src/Map/Infinite/__pycache__/InfiniteMapManager.cpython-313.pyc
Normal file
BIN
src/Map/Infinite/__pycache__/InfiniteMapManager.cpython-313.pyc
Normal file
Binary file not shown.
109
src/Map/Speedrun/SpeedrunTimer.py
Normal file
109
src/Map/Speedrun/SpeedrunTimer.py
Normal file
@@ -0,0 +1,109 @@
|
||||
import pygame
|
||||
import time
|
||||
import sqlite3
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
class SpeedrunTimer:
|
||||
def __init__(self, level_id, db_path="game.db"):
|
||||
self.level_id = level_id
|
||||
self.db_path = db_path
|
||||
self.start_time = None
|
||||
self.current_time = 0
|
||||
self.is_running = False
|
||||
self.best_time = self._get_best_time()
|
||||
self.color = (0, 255, 0) # Green by default
|
||||
self.font = pygame.font.Font(None, 36)
|
||||
self.collected_items = 0
|
||||
self.total_items = 0
|
||||
|
||||
def start(self):
|
||||
"""Start the timer"""
|
||||
self.start_time = time.time()
|
||||
self.is_running = True
|
||||
|
||||
def stop(self):
|
||||
"""Stop the timer and return the final time"""
|
||||
if self.is_running:
|
||||
self.current_time = time.time() - self.start_time
|
||||
self.is_running = False
|
||||
return self.current_time
|
||||
return self.current_time
|
||||
|
||||
def update(self):
|
||||
"""Update the timer and its color"""
|
||||
if self.is_running:
|
||||
self.current_time = time.time() - self.start_time
|
||||
if self.best_time is not None:
|
||||
if self.current_time < self.best_time:
|
||||
self.color = (0, 255, 0) # Green if ahead
|
||||
else:
|
||||
self.color = (255, 0, 0) # Red if behind
|
||||
|
||||
def save_time(self, collected_items=0, total_items=0):
|
||||
"""Save the current time in the database"""
|
||||
if not self.is_running and self.current_time > 0:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Create the table if it doesn't exist
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS speedrun (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
level_id TEXT NOT NULL,
|
||||
time REAL NOT NULL,
|
||||
collected_items INTEGER DEFAULT 0,
|
||||
total_items INTEGER DEFAULT 0,
|
||||
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
# Insert the new time
|
||||
cursor.execute(
|
||||
"INSERT INTO speedrun (level_id, time, collected_items, total_items) VALUES (?, ?, ?, ?)",
|
||||
(self.level_id, self.current_time, collected_items, total_items),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Update the best time
|
||||
self.best_time = self._get_best_time()
|
||||
|
||||
def _get_best_time(self):
|
||||
"""Get the best time for this level from the database"""
|
||||
try:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"SELECT MIN(time) FROM speedrun WHERE level_id = ?", (self.level_id,)
|
||||
)
|
||||
|
||||
best_time = cursor.fetchone()[0]
|
||||
conn.close()
|
||||
|
||||
return best_time
|
||||
except (sqlite3.Error, TypeError):
|
||||
return None
|
||||
|
||||
def format_time(self, time_value):
|
||||
"""Format the time in minutes:seconds.milliseconds"""
|
||||
if time_value is None:
|
||||
return "00:00.000"
|
||||
|
||||
td = timedelta(seconds=time_value)
|
||||
minutes, seconds = divmod(td.seconds, 60)
|
||||
milliseconds = td.microseconds // 1000
|
||||
return f"{minutes:02}:{seconds:02}.{milliseconds:03}"
|
||||
|
||||
def draw(self, surface):
|
||||
"""Display the timer on the screen"""
|
||||
if self.level_id == "NEXT_INFINITE_LEVEL":
|
||||
return # No timer for infinite mode
|
||||
|
||||
formatted_time = self.format_time(self.current_time)
|
||||
text = self.font.render(formatted_time, True, self.color)
|
||||
surface.blit(text, (20, surface.get_height() - 50))
|
||||
BIN
src/Map/Speedrun/__pycache__/SpeedrunTimer.cpython-313.pyc
Normal file
BIN
src/Map/Speedrun/__pycache__/SpeedrunTimer.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Map/__pycache__/parser.cpython-313.pyc
Normal file
BIN
src/Map/__pycache__/parser.cpython-313.pyc
Normal file
Binary file not shown.
@@ -8,6 +8,8 @@ from src.Entity.Enemy import Enemy
|
||||
from src.Entity.Checkpoint import Checkpoint
|
||||
from src.Entity.Exit import Exit
|
||||
from src.Entity.Coin import Coin
|
||||
from src.Entity.JumpBoost import JumpBoost
|
||||
from src.Entity.SpeedBoost import SpeedBoost
|
||||
|
||||
|
||||
class MapParser:
|
||||
@@ -122,9 +124,7 @@ class MapParser:
|
||||
self.platforms.add(platform)
|
||||
self.all_sprites.add(platform)
|
||||
|
||||
# Create collectibles (requires Collectible class implementation)
|
||||
# In MapParser.create_map_objects()
|
||||
# In MapParser.create_map_objects()
|
||||
# Create collectibles
|
||||
if "collectibles" in map_data:
|
||||
for collectible_data in map_data["collectibles"]:
|
||||
if collectible_data["type"] == "coin":
|
||||
@@ -137,13 +137,31 @@ class MapParser:
|
||||
)
|
||||
self.collectibles.add(coin)
|
||||
self.all_sprites.add(coin)
|
||||
elif collectible_data["type"] == "jump":
|
||||
sprite_path = collectible_data.get("sprite", "")
|
||||
jump = JumpBoost(
|
||||
pos=(collectible_data["x"], collectible_data["y"]),
|
||||
texturePath=sprite_path,
|
||||
)
|
||||
self.collectibles.add(jump)
|
||||
self.all_sprites.add(jump)
|
||||
elif collectible_data["type"] == "speed":
|
||||
sprite_path = collectible_data.get("sprite", "")
|
||||
speed = SpeedBoost(
|
||||
pos=(collectible_data["x"], collectible_data["y"]),
|
||||
texturePath=sprite_path,
|
||||
)
|
||||
self.collectibles.add(speed)
|
||||
self.all_sprites.add(speed)
|
||||
|
||||
# Create background image
|
||||
if "background" in map_data:
|
||||
map_width = map_data.get("width", self.game_resources.WIDTH)
|
||||
map_height = map_data.get("height", self.game_resources.HEIGHT)
|
||||
|
||||
if os.path.isfile(map_data["background"]):
|
||||
background = pygame.image.load(map_data["background"]).convert_alpha()
|
||||
background = pygame.transform.scale(
|
||||
background, (self.game_resources.WIDTH, self.game_resources.HEIGHT)
|
||||
)
|
||||
background = pygame.transform.scale(background, (map_width, map_height))
|
||||
self.background = background
|
||||
else:
|
||||
print(f"Background image not found: {map_data['background']}")
|
||||
|
||||
45
src/Menu/BackgroundManager.py
Normal file
45
src/Menu/BackgroundManager.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import pygame
|
||||
import random
|
||||
import math
|
||||
|
||||
|
||||
class BackgroundManager:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.backgrounds = [
|
||||
"assets/map/background/forest_bg.jpg",
|
||||
"assets/map/background/desert_bg.jpg",
|
||||
"assets/map/background/mountain_bg.jpg",
|
||||
"assets/map/background/cave_bg.png",
|
||||
]
|
||||
|
||||
self.background_path = random.choice(self.backgrounds)
|
||||
self.init_time = pygame.time.get_ticks()
|
||||
|
||||
try:
|
||||
# Load the background image
|
||||
self.background = pygame.image.load(self.background_path).convert()
|
||||
bg_width = width * 3
|
||||
bg_height = height * 3
|
||||
self.background = pygame.transform.scale(
|
||||
self.background, (bg_width, bg_height)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Erreur lors du chargement du fond d'écran: {e}")
|
||||
self.background = None
|
||||
|
||||
def draw(self, surface):
|
||||
if self.background:
|
||||
parallax_factor = 0.4
|
||||
time_factor = pygame.time.get_ticks() / 1000
|
||||
|
||||
center_x = (self.background.get_width() - surface.get_width()) / 2
|
||||
center_y = (self.background.get_height() - surface.get_height()) / 2
|
||||
|
||||
bg_x = -center_x + math.sin(time_factor) * 50 * parallax_factor
|
||||
bg_y = -center_y + math.cos(time_factor) * 30 * parallax_factor
|
||||
|
||||
surface.blit(self.background, (bg_x, bg_y))
|
||||
else:
|
||||
surface.fill((0, 0, 0))
|
||||
@@ -1,33 +1,145 @@
|
||||
import pygame
|
||||
import sqlite3
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from src.Menu.BackgroundManager import BackgroundManager
|
||||
from src.Menu.Button import Button
|
||||
from src.Database.LevelDB import LevelDB
|
||||
|
||||
|
||||
class Leaderboard:
|
||||
def __init__(self, HEIGHT, WIDTH, font):
|
||||
self.HEIGHT = HEIGHT
|
||||
"""This class represents the leaderboard menu for the game."""
|
||||
|
||||
def __init__(self, WIDTH, HEIGHT, font, leaderboard_db, db_path="game.db"):
|
||||
self.WIDTH = WIDTH
|
||||
self.HEIGHT = HEIGHT
|
||||
self.font = font
|
||||
self.tabs = ["Mode Normal", "Mode Infini"]
|
||||
self.db_path = db_path
|
||||
self.leaderboard_db = leaderboard_db
|
||||
|
||||
self.levels = self.get_available_levels()
|
||||
self.level_tabs = [f"Level {level}" for level in self.levels]
|
||||
|
||||
self.bg_manager = BackgroundManager(WIDTH, HEIGHT)
|
||||
|
||||
# Define the tabs (levels + infinite mode)
|
||||
self.tabs = self.level_tabs + ["Infinite mode"]
|
||||
self.current_tab = 0
|
||||
self.scores = {
|
||||
0: [("Player1", 1000), ("Player2", 800), ("Player3", 600)],
|
||||
1: [("Player1", 2000), ("Player2", 1500), ("Player3", 1200)],
|
||||
}
|
||||
self.back_button = Button("Retour", 20, self.HEIGHT - 70, 120, 50, "menu")
|
||||
tab_width = 150
|
||||
self.tab_buttons = [
|
||||
Button(
|
||||
self.tabs[0], self.WIDTH // 2 - tab_width, 80, tab_width, 40, "tab_0"
|
||||
),
|
||||
Button(self.tabs[1], self.WIDTH // 2, 80, tab_width, 40, "tab_1"),
|
||||
]
|
||||
|
||||
self.back_button = Button("Back", 20, self.HEIGHT - 70, 120, 50, "menu")
|
||||
|
||||
# Add buttons for each tab
|
||||
self.tab_buttons = []
|
||||
tab_width = min(150, (self.WIDTH - 100) / len(self.tabs))
|
||||
|
||||
for i, tab_name in enumerate(self.tabs):
|
||||
x_pos = 50 + i * tab_width
|
||||
self.tab_buttons.append(
|
||||
Button(tab_name, x_pos, 80, tab_width, 40, f"tab_{i}")
|
||||
)
|
||||
|
||||
self.load_scores()
|
||||
|
||||
def get_available_levels(self):
|
||||
"""Get the list of available levels from the database."""
|
||||
try:
|
||||
db = LevelDB()
|
||||
levels = db.get_all_unlocked_levels()
|
||||
db.close()
|
||||
return sorted(set(levels)) # Remove duplicates and sort
|
||||
except:
|
||||
return [1]
|
||||
|
||||
def load_scores(self):
|
||||
"""Load scores from the database for each level."""
|
||||
self.scores = {}
|
||||
|
||||
# Load scores for each level
|
||||
for i, level in enumerate(self.levels):
|
||||
self.scores[i] = self.get_level_scores(str(level))
|
||||
|
||||
# Load scores for infinite mode
|
||||
try:
|
||||
# Get the TOP 10 scores for infinite mode
|
||||
all_scores = self.leaderboard_db.get_top_10_scores()
|
||||
|
||||
# Format the scores for display
|
||||
formatted_scores = []
|
||||
for score, date in all_scores:
|
||||
date_obj = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
||||
formatted_date = date_obj.strftime("%d/%m/%Y")
|
||||
formatted_scores.append((formatted_date, score))
|
||||
|
||||
# Assign the formatted scores to the infinite mode tab
|
||||
self.scores[len(self.levels)] = formatted_scores
|
||||
except Exception as e:
|
||||
print(f"Error loading infinite mode scores: {e}")
|
||||
self.scores[len(self.levels)] = []
|
||||
|
||||
def get_level_scores(self, level_id):
|
||||
"""Get the top 10 scores for a specific level from the database."""
|
||||
try:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT time, date, collected_items, total_items
|
||||
FROM speedrun
|
||||
WHERE level_id = ?
|
||||
ORDER BY time ASC
|
||||
LIMIT 10
|
||||
""",
|
||||
(level_id,),
|
||||
)
|
||||
|
||||
results = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
# Format results
|
||||
formatted_results = []
|
||||
for time, date, collected, total in results:
|
||||
date_obj = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
||||
formatted_date = date_obj.strftime("%d/%m/%Y")
|
||||
formatted_results.append((formatted_date, time, collected, total))
|
||||
|
||||
return formatted_results
|
||||
except (sqlite3.Error, Exception) as e:
|
||||
print(f"Error loading scores: {e}")
|
||||
return []
|
||||
|
||||
def format_time(self, time_value):
|
||||
"""Format the time in minutes:seconds.milliseconds."""
|
||||
from datetime import timedelta
|
||||
|
||||
td = timedelta(seconds=time_value)
|
||||
minutes, seconds = divmod(td.seconds, 60)
|
||||
milliseconds = int((time_value * 1000) % 1000)
|
||||
return f"{minutes:02}:{seconds:02}.{milliseconds:03}"
|
||||
|
||||
def draw(self, surface):
|
||||
# Draw title
|
||||
title = pygame.font.SysFont("Arial", 48).render(
|
||||
"Classement", True, (0, 191, 255)
|
||||
"""Draw the leaderboard on the given surface."""
|
||||
# Refresh scores to ensure the latest data is displayed
|
||||
self.load_scores()
|
||||
|
||||
self.bg_manager.draw(surface)
|
||||
|
||||
# Draw a semi-transparent panel
|
||||
panel_rect = pygame.Rect(self.WIDTH // 2 - 250, 130, 500, self.HEIGHT - 200)
|
||||
panel_surface = pygame.Surface(
|
||||
(panel_rect.width, panel_rect.height), pygame.SRCALPHA
|
||||
)
|
||||
panel_surface.fill((10, 10, 40, 180))
|
||||
surface.blit(panel_surface, panel_rect)
|
||||
|
||||
title_font = pygame.font.SysFont("Arial", 48, bold=True)
|
||||
title = title_font.render("Leaderboard", True, (255, 255, 255))
|
||||
title_shadow = title_font.render("Leaderboard", True, (0, 0, 0))
|
||||
|
||||
title_rect = title.get_rect(center=(self.WIDTH // 2, 40))
|
||||
shadow_rect = title_shadow.get_rect(center=(self.WIDTH // 2 + 2, 42))
|
||||
surface.blit(title_shadow, shadow_rect)
|
||||
surface.blit(title, title_rect)
|
||||
|
||||
font = pygame.font.SysFont("Arial", 20)
|
||||
@@ -35,27 +147,97 @@ class Leaderboard:
|
||||
# Draw tabs
|
||||
for i, button in enumerate(self.tab_buttons):
|
||||
if i == self.current_tab:
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
(100, 149, 237),
|
||||
(button.x, button.y, button.width, button.height),
|
||||
)
|
||||
# Highlight current tab
|
||||
pygame.draw.rect(surface, (100, 100, 255), button.rect)
|
||||
button.draw(surface, font)
|
||||
|
||||
# Determine headers and positions based on the current tab
|
||||
if self.current_tab == len(self.levels): # Infinite mode
|
||||
headers = ["Rank", "Date", "Score"]
|
||||
header_positions = [
|
||||
self.WIDTH // 2 - 150,
|
||||
self.WIDTH // 2 - 50,
|
||||
self.WIDTH // 2 + 100,
|
||||
]
|
||||
else: # Level scores
|
||||
headers = ["Rank", "Date", "Time", "Collected"]
|
||||
header_positions = [
|
||||
self.WIDTH // 2 - 150,
|
||||
self.WIDTH // 2 - 100,
|
||||
self.WIDTH // 2 + 50,
|
||||
self.WIDTH // 2 + 150,
|
||||
]
|
||||
|
||||
y_pos = 200
|
||||
|
||||
# Draw column headers
|
||||
for i, header in enumerate(headers):
|
||||
header_text = font.render(header, True, (200, 200, 200))
|
||||
surface.blit(header_text, (header_positions[i], y_pos - 30))
|
||||
|
||||
# Draw scores
|
||||
y_pos = 150
|
||||
for i, (name, score) in enumerate(self.scores[self.current_tab]):
|
||||
rank_text = self.font.render(
|
||||
f"{i+1}. {name}: {score}", True, (255, 255, 255)
|
||||
scores_for_tab = self.scores.get(self.current_tab, [])
|
||||
|
||||
if not scores_for_tab:
|
||||
no_scores_text = self.font.render(
|
||||
"No time saved for this level", True, (255, 255, 255)
|
||||
)
|
||||
surface.blit(
|
||||
rank_text, (self.WIDTH // 2 - rank_text.get_width() // 2, y_pos)
|
||||
no_scores_text,
|
||||
(self.WIDTH // 2 - no_scores_text.get_width() // 2, y_pos + 40),
|
||||
)
|
||||
y_pos += 40
|
||||
else:
|
||||
for i, score_data in enumerate(scores_for_tab):
|
||||
row_bg = (30, 30, 60, 150) if i % 2 == 0 else (40, 40, 80, 150)
|
||||
row_rect = pygame.Rect(self.WIDTH // 2 - 200, y_pos - 5, 400, 30)
|
||||
row_surface = pygame.Surface(
|
||||
(row_rect.width, row_rect.height), pygame.SRCALPHA
|
||||
)
|
||||
row_surface.fill(row_bg)
|
||||
surface.blit(row_surface, row_rect)
|
||||
|
||||
# Rank
|
||||
rank_text = self.font.render(f"{i + 1}.", True, (255, 255, 255))
|
||||
surface.blit(rank_text, (header_positions[0], y_pos))
|
||||
|
||||
if self.current_tab == len(self.levels): # Infinite mode
|
||||
date, score = score_data
|
||||
# Date
|
||||
date_text = self.font.render(date, True, (255, 255, 255))
|
||||
surface.blit(date_text, (header_positions[1], y_pos))
|
||||
|
||||
# Score
|
||||
score_text = self.font.render(str(score), True, (255, 255, 255))
|
||||
surface.blit(score_text, (header_positions[2], y_pos))
|
||||
else: # Level scores
|
||||
date, time, collected, total = score_data
|
||||
|
||||
# Date
|
||||
date_text = self.font.render(date, True, (255, 255, 255))
|
||||
surface.blit(date_text, (header_positions[1], y_pos))
|
||||
|
||||
# Time
|
||||
time_text = self.font.render(
|
||||
self.format_time(time), True, (255, 255, 255)
|
||||
)
|
||||
surface.blit(time_text, (header_positions[2], y_pos))
|
||||
|
||||
# Collected items
|
||||
collected_color = (255, 255, 255)
|
||||
if collected == total:
|
||||
collected_color = (0, 255, 0)
|
||||
|
||||
collected_text = self.font.render(
|
||||
f"{collected}/{total}", True, collected_color
|
||||
)
|
||||
surface.blit(collected_text, (header_positions[3], y_pos))
|
||||
|
||||
y_pos += 40
|
||||
|
||||
self.back_button.draw(surface, font)
|
||||
|
||||
def handle_event(self, event):
|
||||
"""Handle events for the leaderboard menu."""
|
||||
action = self.back_button.handle_event(event)
|
||||
if action:
|
||||
return action
|
||||
@@ -65,3 +247,25 @@ class Leaderboard:
|
||||
if action and action.startswith("tab_"):
|
||||
self.current_tab = int(action.split("_")[1])
|
||||
return None
|
||||
|
||||
def refresh_scores(self, previous_level=""):
|
||||
"""Refresh scores from the database."""
|
||||
if previous_level != "LEADERBOARD":
|
||||
self.scores = {}
|
||||
|
||||
# Get the list of levels from the directory
|
||||
level_dir = "map/levels/"
|
||||
try:
|
||||
levels = [
|
||||
f.replace(".json", "")
|
||||
for f in os.listdir(level_dir)
|
||||
if f.endswith(".json")
|
||||
]
|
||||
|
||||
# Get the scores for each level
|
||||
for level in levels:
|
||||
scores = self.get_level_scores(level)
|
||||
if scores:
|
||||
self.scores[int(level) - 1] = scores
|
||||
except Exception as e:
|
||||
print(f"Error while refreshing the score: {e}")
|
||||
|
||||
@@ -2,6 +2,7 @@ import pygame
|
||||
import os
|
||||
import re
|
||||
|
||||
from src.Menu.BackgroundManager import BackgroundManager
|
||||
from src.Menu.Button import Button
|
||||
|
||||
|
||||
@@ -21,6 +22,8 @@ class LevelEditorSelectionMenu:
|
||||
self.buttons = []
|
||||
self.levels = []
|
||||
|
||||
self.bg_manager = BackgroundManager(game_resources.WIDTH, game_resources.HEIGHT)
|
||||
|
||||
# Button dimensions
|
||||
self.button_width = 250
|
||||
self.button_height = 60
|
||||
@@ -131,6 +134,7 @@ class LevelEditorSelectionMenu:
|
||||
Args:
|
||||
surface: Pygame surface to draw on
|
||||
"""
|
||||
self.bg_manager.draw(surface)
|
||||
# Draw title
|
||||
title = pygame.font.SysFont("Arial", 48).render(
|
||||
"Level Editor", True, (0, 191, 255)
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
import re
|
||||
|
||||
from src.Database.LevelDB import LevelDB
|
||||
from src.Menu.BackgroundManager import BackgroundManager
|
||||
from src.Menu.Button import Button
|
||||
from src.game import clear_checkpoint_database, clear_level_progress
|
||||
|
||||
@@ -24,6 +25,8 @@ class LevelSelectMenu:
|
||||
self.buttons = []
|
||||
self.levels = []
|
||||
|
||||
self.bg_manager = BackgroundManager(game_resources.WIDTH, game_resources.HEIGHT)
|
||||
|
||||
# Button dimensions
|
||||
self.button_width = 250
|
||||
self.button_height = 60
|
||||
@@ -168,6 +171,7 @@ class LevelSelectMenu:
|
||||
Args:
|
||||
surface: Pygame surface to draw on
|
||||
"""
|
||||
self.bg_manager.draw(surface)
|
||||
# Draw title
|
||||
title = pygame.font.SysFont("Arial", 48).render(
|
||||
"Select Level", True, (0, 191, 255)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import pygame
|
||||
import random
|
||||
import math
|
||||
|
||||
from src.Menu.BackgroundManager import BackgroundManager
|
||||
from src.Menu.Button import Button
|
||||
|
||||
|
||||
@@ -13,32 +15,12 @@ class Menu:
|
||||
button_spacing = 20
|
||||
start_y = self.game_resources.HEIGHT // 2 - 100
|
||||
|
||||
self.backgrounds = [
|
||||
"assets/map/background/forest_bg.jpg",
|
||||
"assets/map/background/desert_bg.jpg",
|
||||
"assets/map/background/mountain_bg.jpg",
|
||||
"assets/map/background/cave_bg.png",
|
||||
]
|
||||
|
||||
self.background_path = random.choice(self.backgrounds)
|
||||
|
||||
try:
|
||||
# Load the background image
|
||||
self.background = pygame.image.load(self.background_path).convert()
|
||||
|
||||
bg_width = game_resources.WIDTH * 3
|
||||
bg_height = game_resources.HEIGHT * 3
|
||||
self.background = pygame.transform.scale(
|
||||
self.background, (bg_width, bg_height)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error while loading menu background: {e}")
|
||||
self.background = None
|
||||
self.bg_manager = BackgroundManager(game_resources.WIDTH, game_resources.HEIGHT)
|
||||
|
||||
# Create buttons centered horizontally
|
||||
self.buttons.append(
|
||||
Button(
|
||||
"Jouer",
|
||||
"Play",
|
||||
self.game_resources.WIDTH // 2 - button_width // 2,
|
||||
start_y,
|
||||
button_width,
|
||||
@@ -50,7 +32,7 @@ class Menu:
|
||||
start_y += button_height + button_spacing
|
||||
self.buttons.append(
|
||||
Button(
|
||||
"Jouer en mode infini",
|
||||
"Play in infinite mode",
|
||||
self.game_resources.WIDTH // 2 - button_width // 2,
|
||||
start_y,
|
||||
button_width,
|
||||
@@ -62,7 +44,7 @@ class Menu:
|
||||
start_y += button_height + button_spacing
|
||||
self.buttons.append(
|
||||
Button(
|
||||
"Classement",
|
||||
"Leaderboard",
|
||||
self.game_resources.WIDTH // 2 - button_width // 2,
|
||||
start_y,
|
||||
button_width,
|
||||
@@ -74,7 +56,7 @@ class Menu:
|
||||
start_y += button_height + button_spacing
|
||||
self.buttons.append(
|
||||
Button(
|
||||
"Quitter",
|
||||
"Quit",
|
||||
self.game_resources.WIDTH // 2 - button_width // 2,
|
||||
start_y,
|
||||
button_width,
|
||||
@@ -84,23 +66,11 @@ class Menu:
|
||||
)
|
||||
|
||||
def draw(self, surface):
|
||||
if self.background:
|
||||
parallax_factor = 0.4
|
||||
time_factor = pygame.time.get_ticks() / 1000
|
||||
|
||||
center_x = (self.background.get_width() - surface.get_width()) / 2
|
||||
center_y = (self.background.get_height() - surface.get_height()) / 2
|
||||
|
||||
bg_x = -center_x + math.sin(time_factor) * 50 * parallax_factor
|
||||
bg_y = -center_y + math.cos(time_factor) * 30 * parallax_factor
|
||||
|
||||
surface.blit(self.background, (bg_x, bg_y))
|
||||
else:
|
||||
surface.fill((0, 0, 0))
|
||||
self.bg_manager.draw(surface)
|
||||
|
||||
# Draw title
|
||||
title = pygame.font.SysFont("Arial", 72).render(
|
||||
"Sanic et la princesse Zeldo", True, (0, 191, 255)
|
||||
"Sanic and the princess Zeldo", True, (0, 191, 255)
|
||||
)
|
||||
title_rect = title.get_rect(
|
||||
center=(self.game_resources.WIDTH // 2, self.game_resources.HEIGHT // 4)
|
||||
|
||||
BIN
src/Menu/__pycache__/BackgroundManager.cpython-313.pyc
Normal file
BIN
src/Menu/__pycache__/BackgroundManager.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Menu/__pycache__/Button.cpython-313.pyc
Normal file
BIN
src/Menu/__pycache__/Button.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Menu/__pycache__/Leaderboard.cpython-313.pyc
Normal file
BIN
src/Menu/__pycache__/Leaderboard.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Menu/__pycache__/LevelEditorSelectionMenu.cpython-313.pyc
Normal file
BIN
src/Menu/__pycache__/LevelEditorSelectionMenu.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Menu/__pycache__/LevelSelectMenu.cpython-313.pyc
Normal file
BIN
src/Menu/__pycache__/LevelSelectMenu.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/Menu/__pycache__/Menu.cpython-313.pyc
Normal file
BIN
src/Menu/__pycache__/Menu.cpython-313.pyc
Normal file
Binary file not shown.
@@ -16,6 +16,12 @@ class GameResources:
|
||||
self.life_icon_width = 50
|
||||
self.fullscreen = False
|
||||
|
||||
try:
|
||||
icon = pygame.image.load("assets/player/Sanic Head.png")
|
||||
pygame.display.set_icon(icon)
|
||||
except Exception as e:
|
||||
print(f"Erreur lors du chargement de l'icône: {e}")
|
||||
|
||||
# Ressources
|
||||
self.platforms = pygame.sprite.Group()
|
||||
self.all_sprites = pygame.sprite.Group()
|
||||
|
||||
@@ -6,6 +6,8 @@ import sys
|
||||
from moviepy.decorators import preprocess_args
|
||||
from pygame.locals import *
|
||||
|
||||
from src.Database.InfiniteModeDB import InfiniteModeDB
|
||||
from src.Database.LeaderboardDB import LeaderboardDB
|
||||
from src.Database.LevelDB import LevelDB
|
||||
from src.Entity.Platform import Platform
|
||||
from src.Entity.Player import Player
|
||||
@@ -25,6 +27,9 @@ def initialize_game(game_resources, map_file="map/levels/1.json"):
|
||||
Returns:
|
||||
tuple: (player, platform, platforms_group, all_sprites, background, checkpoints, exits)
|
||||
"""
|
||||
checkpointDB = CheckpointDB()
|
||||
checkpointDB.reset_level(map_file)
|
||||
checkpointDB.close()
|
||||
parser = MapParser(game_resources)
|
||||
map_objects = parser.load_map(map_file)
|
||||
|
||||
@@ -135,6 +140,9 @@ def start_infinite_mode(game_resources):
|
||||
game_resources.infinite_manager = infinite_manager
|
||||
game_resources.infinite_mode = True
|
||||
|
||||
# Open the temporary database
|
||||
game_resources.infinite_mode_db = InfiniteModeDB()
|
||||
|
||||
# Generate the first level
|
||||
first_level = infinite_manager.start_infinite_mode()
|
||||
|
||||
|
||||
1255
src/handler.py
1255
src/handler.py
File diff suppressed because it is too large
Load Diff
BIN
temp_audio.mp3
BIN
temp_audio.mp3
Binary file not shown.
Reference in New Issue
Block a user