From 8eecbfc6130d2c4d4f214e71ecd243d6d332beb0 Mon Sep 17 00:00:00 2001 From: Shoyers Date: Mon, 31 Mar 2025 04:43:02 +0200 Subject: [PATCH 1/3] feat: Ajout de fichiers de configuration et de ressources pour le jeu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Création d'un fichier .gitignore pour exclure les fichiers __pycache__. - Ajout de fichiers JSON pour la sauvegarde des données de jeu (highscore, état des modules). - Mise à jour de pyproject.toml pour inclure des fichiers supplémentaires. - Ajout de nouveaux fichiers de ressources (images, sons, etc.) pour enrichir l'expérience de jeu. - Introduction de la structure de base pour les modules de jeu et les systèmes. --- .gitignore | 9 + pyproject.toml | 11 +- save_data.json | 1 + {survival-shooter/src => src}/__init__.py | 0 .../02459f64-4057-459c-b1a3-3f601195d141.ico | Bin .../src => src}/assets/__init__.py | 0 src/assets/achievements/first_kill.png | Bin 0 -> 28 bytes src/assets/achievements/rich.png | Bin 0 -> 28 bytes src/assets/achievements/survivor.png | Bin 0 -> 28 bytes .../assets/characters/default.png | Bin src/assets/characters/speeder.png | Bin 0 -> 57333 bytes src/assets/characters/tank.png | Bin 0 -> 57333 bytes src/assets/icons/chest.png | Bin 0 -> 51758 bytes src/assets/icons/coin.png | Bin 0 -> 20924 bytes src/assets/icons/coin_1.png | Bin 0 -> 28 bytes {survival-shooter/src => src}/assets/logo.ico | Bin src/assets/sounds/achievement.wav | Bin 0 -> 8864 bytes src/assets/sounds/coin.wav | Bin 0 -> 8864 bytes src/assets/sounds/enemy_death.wav | Bin 0 -> 17684 bytes .../src => src}/assets/sounds/enemy_shoot.wav | Bin .../src => src}/assets/sounds/game_music.mp3 | Bin src/assets/sounds/hit.wav | Bin 0 -> 8864 bytes .../src => src}/assets/sounds/player_hurt.wav | Bin src/assets/sounds/purchase.wav | Bin 0 -> 8864 bytes .../src => src}/assets/sounds/shoot.wav | Bin src/assets/sprites/background.png | 1 + src/assets/sprites/bullet.png | 1 + src/assets/sprites/enemy.png | 1 + src/assets/sprites/enemy_bullet.png | 1 + src/assets/sprites/player.png | Bin 0 -> 57333 bytes src/data/game_save.json | 1 + .../src => src}/data/highscore.json | 0 .../src => src}/game/__init__.py | 0 src/game/core/__init__.py | 0 .../src/game => src/game/core}/enemy.py | 17 +- .../src/main.py => src/game/core/game.py | 1057 ++++++++++------- src/game/core/pickup.py | 98 ++ src/game/core/player.py | 264 ++++ .../src/game => src/game/core}/weapon.py | 24 +- src/game/modes/__init__.py | 1 + src/game/modes/boss_rush_mode.py | 227 ++++ src/game/modes/classic_mode.py | 170 +++ src/game/modes/game_mode_base.py | 75 ++ src/game/modes/game_modes.py | 153 +++ src/game/modes/survival_mode.py | 150 +++ src/game/modules/__init__.py | 0 src/game/modules/module_manager.py | 200 ++++ src/game/modules/module_menu.py | 237 ++++ src/game/modules/module_selection_menu.py | 157 +++ src/game/modules/modules.py | 179 +++ src/game/systems/__init__.py | 0 src/game/systems/economy.py | 110 ++ src/game/ui/__init__.py | 0 src/game/ui/button.py | 66 + src/game/ui/character_shop.py | 304 +++++ src/game/ui/game_mode_menu.py | 152 +++ src/game/ui/menu_manager.py | 238 ++++ src/game/ui/menus.py | 366 ++++++ src/game/ui/module_menu.py | 227 ++++ src/game/ui/module_selection.py | 135 +++ src/game/ui/notification_manager.py | 143 +++ src/main.py | 42 + src/save/modules_state.json | 1 + .../src => src}/utils/__init__.py | 0 .../src => src}/utils/constants.py | 66 +- .../src => src}/utils/sound_generator.py | 53 +- .../src/assets/sounds/enemy_death.wav | Bin 17684 -> 0 bytes survival-shooter/src/assets/sounds/hit.wav | Bin 8864 -> 0 bytes .../game/__pycache__/__init__.cpython-312.pyc | Bin 185 -> 0 bytes .../game/__pycache__/enemy.cpython-312.pyc | Bin 11314 -> 0 bytes .../game/__pycache__/player.cpython-312.pyc | Bin 10143 -> 0 bytes .../game/__pycache__/weapon.cpython-312.pyc | Bin 4698 -> 0 bytes survival-shooter/src/game/player.py | 153 --- .../__pycache__/menu_manager.cpython-312.pyc | Bin 5135 -> 0 bytes survival-shooter/src/ui/menu_manager.py | 55 - .../__pycache__/__init__.cpython-312.pyc | Bin 186 -> 0 bytes .../__pycache__/constants.cpython-312.pyc | Bin 4950 -> 0 bytes .../sound_generator.cpython-312.pyc | Bin 7696 -> 0 bytes .../__pycache__/spritesheet.cpython-312.pyc | Bin 2470 -> 0 bytes 79 files changed, 4509 insertions(+), 637 deletions(-) create mode 100644 .gitignore create mode 100644 save_data.json rename {survival-shooter/src => src}/__init__.py (100%) rename {survival-shooter/src => src}/assets/02459f64-4057-459c-b1a3-3f601195d141.ico (100%) rename {survival-shooter/src => src}/assets/__init__.py (100%) create mode 100644 src/assets/achievements/first_kill.png create mode 100644 src/assets/achievements/rich.png create mode 100644 src/assets/achievements/survivor.png rename survival-shooter/src/assets/sprites/player.png => src/assets/characters/default.png (100%) create mode 100644 src/assets/characters/speeder.png create mode 100644 src/assets/characters/tank.png create mode 100644 src/assets/icons/chest.png create mode 100644 src/assets/icons/coin.png create mode 100644 src/assets/icons/coin_1.png rename {survival-shooter/src => src}/assets/logo.ico (100%) create mode 100644 src/assets/sounds/achievement.wav create mode 100644 src/assets/sounds/coin.wav create mode 100644 src/assets/sounds/enemy_death.wav rename {survival-shooter/src => src}/assets/sounds/enemy_shoot.wav (100%) rename {survival-shooter/src => src}/assets/sounds/game_music.mp3 (100%) create mode 100644 src/assets/sounds/hit.wav rename {survival-shooter/src => src}/assets/sounds/player_hurt.wav (100%) create mode 100644 src/assets/sounds/purchase.wav rename {survival-shooter/src => src}/assets/sounds/shoot.wav (100%) create mode 100644 src/assets/sprites/background.png create mode 100644 src/assets/sprites/bullet.png create mode 100644 src/assets/sprites/enemy.png create mode 100644 src/assets/sprites/enemy_bullet.png create mode 100644 src/assets/sprites/player.png create mode 100644 src/data/game_save.json rename {survival-shooter/src => src}/data/highscore.json (100%) rename {survival-shooter/src => src}/game/__init__.py (100%) create mode 100644 src/game/core/__init__.py rename {survival-shooter/src/game => src/game/core}/enemy.py (92%) rename survival-shooter/src/main.py => src/game/core/game.py (54%) create mode 100644 src/game/core/pickup.py create mode 100644 src/game/core/player.py rename {survival-shooter/src/game => src/game/core}/weapon.py (72%) create mode 100644 src/game/modes/__init__.py create mode 100644 src/game/modes/boss_rush_mode.py create mode 100644 src/game/modes/classic_mode.py create mode 100644 src/game/modes/game_mode_base.py create mode 100644 src/game/modes/game_modes.py create mode 100644 src/game/modes/survival_mode.py create mode 100644 src/game/modules/__init__.py create mode 100644 src/game/modules/module_manager.py create mode 100644 src/game/modules/module_menu.py create mode 100644 src/game/modules/module_selection_menu.py create mode 100644 src/game/modules/modules.py create mode 100644 src/game/systems/__init__.py create mode 100644 src/game/systems/economy.py create mode 100644 src/game/ui/__init__.py create mode 100644 src/game/ui/button.py create mode 100644 src/game/ui/character_shop.py create mode 100644 src/game/ui/game_mode_menu.py create mode 100644 src/game/ui/menu_manager.py create mode 100644 src/game/ui/menus.py create mode 100644 src/game/ui/module_menu.py create mode 100644 src/game/ui/module_selection.py create mode 100644 src/game/ui/notification_manager.py create mode 100644 src/main.py create mode 100644 src/save/modules_state.json rename {survival-shooter/src => src}/utils/__init__.py (100%) rename {survival-shooter/src => src}/utils/constants.py (70%) rename {survival-shooter/src => src}/utils/sound_generator.py (67%) delete mode 100644 survival-shooter/src/assets/sounds/enemy_death.wav delete mode 100644 survival-shooter/src/assets/sounds/hit.wav delete mode 100644 survival-shooter/src/game/__pycache__/__init__.cpython-312.pyc delete mode 100644 survival-shooter/src/game/__pycache__/enemy.cpython-312.pyc delete mode 100644 survival-shooter/src/game/__pycache__/player.cpython-312.pyc delete mode 100644 survival-shooter/src/game/__pycache__/weapon.cpython-312.pyc delete mode 100644 survival-shooter/src/game/player.py delete mode 100644 survival-shooter/src/ui/__pycache__/menu_manager.cpython-312.pyc delete mode 100644 survival-shooter/src/ui/menu_manager.py delete mode 100644 survival-shooter/src/utils/__pycache__/__init__.cpython-312.pyc delete mode 100644 survival-shooter/src/utils/__pycache__/constants.cpython-312.pyc delete mode 100644 survival-shooter/src/utils/__pycache__/sound_generator.cpython-312.pyc delete mode 100644 survival-shooter/src/utils/__pycache__/spritesheet.cpython-312.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e43f6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/src/game/__pycache__ +/src/game/modes/__pycache__ +/src/game/modules/__pycache__ +/src/game/ui/__pycache__ +/src/utils/__pycache__ +/survival-shooter/src/game/__pycache__ +/survival-shooter/src/game/__pycache__ +/src/game/systems/__pycache__ +/src/game/core/__pycache__ diff --git a/pyproject.toml b/pyproject.toml index b2c530d..eadb338 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,4 +32,13 @@ survival-shooter ├── README.md ├── plan.txt ├── prompt.txt -└── requirements.txt \ No newline at end of file +└── requirements.txt + + + + + + + + + diff --git a/save_data.json b/save_data.json new file mode 100644 index 0000000..2ccf42f --- /dev/null +++ b/save_data.json @@ -0,0 +1 @@ +{"coins": 71806, "achievements": {"first_kill": {"unlocked": true, "progress": 0}, "survivor": {"unlocked": false, "progress": 0}, "rich": {"unlocked": true, "progress": 100}, "collector": {"unlocked": false, "progress": 0}, "master": {"unlocked": false, "progress": 0}}} \ No newline at end of file diff --git a/survival-shooter/src/__init__.py b/src/__init__.py similarity index 100% rename from survival-shooter/src/__init__.py rename to src/__init__.py diff --git a/survival-shooter/src/assets/02459f64-4057-459c-b1a3-3f601195d141.ico b/src/assets/02459f64-4057-459c-b1a3-3f601195d141.ico similarity index 100% rename from survival-shooter/src/assets/02459f64-4057-459c-b1a3-3f601195d141.ico rename to src/assets/02459f64-4057-459c-b1a3-3f601195d141.ico diff --git a/survival-shooter/src/assets/__init__.py b/src/assets/__init__.py similarity index 100% rename from survival-shooter/src/assets/__init__.py rename to src/assets/__init__.py diff --git a/src/assets/achievements/first_kill.png b/src/assets/achievements/first_kill.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac0d1140cb8211fd1a3e126ab9953f8a4739c8a GIT binary patch literal 28 gcmezWFMuJ3A(0`OA(bJ6As@(20kVo1cp11D0DIg9cmMzZ literal 0 HcmV?d00001 diff --git a/src/assets/achievements/rich.png b/src/assets/achievements/rich.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac0d1140cb8211fd1a3e126ab9953f8a4739c8a GIT binary patch literal 28 gcmezWFMuJ3A(0`OA(bJ6As@(20kVo1cp11D0DIg9cmMzZ literal 0 HcmV?d00001 diff --git a/src/assets/achievements/survivor.png b/src/assets/achievements/survivor.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac0d1140cb8211fd1a3e126ab9953f8a4739c8a GIT binary patch literal 28 gcmezWFMuJ3A(0`OA(bJ6As@(20kVo1cp11D0DIg9cmMzZ literal 0 HcmV?d00001 diff --git a/survival-shooter/src/assets/sprites/player.png b/src/assets/characters/default.png similarity index 100% rename from survival-shooter/src/assets/sprites/player.png rename to src/assets/characters/default.png diff --git a/src/assets/characters/speeder.png b/src/assets/characters/speeder.png new file mode 100644 index 0000000000000000000000000000000000000000..18b40a8184fc5059fb30ed48889dfce41e5bb9d2 GIT binary patch literal 57333 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%0Apv`Ufuh?gnx`h&%0c%O>U_&ROa< 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 G2~7Yrj9B9U literal 0 HcmV?d00001 diff --git a/src/assets/icons/coin.png b/src/assets/icons/coin.png new file mode 100644 index 0000000000000000000000000000000000000000..db76b4174bc507e725a989f0d911652d4466d77f GIT binary patch literal 20924 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 literal 0 HcmV?d00001 diff --git a/src/assets/icons/coin_1.png b/src/assets/icons/coin_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac0d1140cb8211fd1a3e126ab9953f8a4739c8a GIT binary patch literal 28 gcmezWFMuJ3A(0`OA(bJ6As@(20kVo1cp11D0DIg9cmMzZ literal 0 HcmV?d00001 diff --git a/survival-shooter/src/assets/logo.ico b/src/assets/logo.ico similarity index 100% rename from survival-shooter/src/assets/logo.ico rename to src/assets/logo.ico diff --git a/src/assets/sounds/achievement.wav b/src/assets/sounds/achievement.wav new file mode 100644 index 0000000000000000000000000000000000000000..dc4b3f10f641fd4357236f753358403b0b44a734 GIT binary patch literal 8864 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 literal 0 HcmV?d00001 diff --git a/src/assets/sounds/coin.wav b/src/assets/sounds/coin.wav new file mode 100644 index 0000000000000000000000000000000000000000..dc4b3f10f641fd4357236f753358403b0b44a734 GIT binary patch literal 8864 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 literal 0 HcmV?d00001 diff --git a/src/assets/sounds/enemy_death.wav b/src/assets/sounds/enemy_death.wav new file mode 100644 index 0000000000000000000000000000000000000000..8c6ea913caedb75008ee1671e98a62d0de60cb1f GIT binary patch literal 17684 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 J!~S3Y1px5ke?tHO literal 0 HcmV?d00001 diff --git a/survival-shooter/src/assets/sounds/enemy_shoot.wav b/src/assets/sounds/enemy_shoot.wav similarity index 100% rename from survival-shooter/src/assets/sounds/enemy_shoot.wav rename to src/assets/sounds/enemy_shoot.wav diff --git a/survival-shooter/src/assets/sounds/game_music.mp3 b/src/assets/sounds/game_music.mp3 similarity index 100% rename from survival-shooter/src/assets/sounds/game_music.mp3 rename to src/assets/sounds/game_music.mp3 diff --git a/src/assets/sounds/hit.wav b/src/assets/sounds/hit.wav new file mode 100644 index 0000000000000000000000000000000000000000..6990ddf6321ac06de3dba23ff4ce7610f24ec312 GIT binary patch literal 8864 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 bXFIX0*hG%XWC52p+rA*ZD!bXR^N#0#ETl1| literal 0 HcmV?d00001 diff --git a/survival-shooter/src/assets/sounds/player_hurt.wav b/src/assets/sounds/player_hurt.wav similarity index 100% rename from survival-shooter/src/assets/sounds/player_hurt.wav rename to src/assets/sounds/player_hurt.wav diff --git a/src/assets/sounds/purchase.wav b/src/assets/sounds/purchase.wav new file mode 100644 index 0000000000000000000000000000000000000000..dc4b3f10f641fd4357236f753358403b0b44a734 GIT binary patch literal 8864 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 literal 0 HcmV?d00001 diff --git a/survival-shooter/src/assets/sounds/shoot.wav b/src/assets/sounds/shoot.wav similarity index 100% rename from survival-shooter/src/assets/sounds/shoot.wav rename to src/assets/sounds/shoot.wav diff --git a/src/assets/sprites/background.png b/src/assets/sprites/background.png new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/src/assets/sprites/background.png @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/src/assets/sprites/bullet.png b/src/assets/sprites/bullet.png new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/src/assets/sprites/bullet.png @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/src/assets/sprites/enemy.png b/src/assets/sprites/enemy.png new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/src/assets/sprites/enemy.png @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/src/assets/sprites/enemy_bullet.png b/src/assets/sprites/enemy_bullet.png new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/src/assets/sprites/enemy_bullet.png @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/src/assets/sprites/player.png b/src/assets/sprites/player.png new file mode 100644 index 0000000000000000000000000000000000000000..18b40a8184fc5059fb30ed48889dfce41e5bb9d2 GIT binary patch literal 57333 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 SCREEN_HEIGHT: + star['pos'][1] = 0 + star['pos'][0] = random.randint(0, SCREEN_WIDTH) + star['brightness'] = random.randint(100, 255) # Nouvelle luminosité + + # Garde les étoiles dans les limites horizontales + if star['pos'][0] < 0: + star['pos'][0] = SCREEN_WIDTH + elif star['pos'][0] > SCREEN_WIDTH: + star['pos'][0] = 0 + + def draw_stars(self): + """Dessine les étoiles""" + for star in self.stars: + color = (star['brightness'], star['brightness'], star['brightness']) + pygame.draw.circle(self.screen, color, + (int(star['pos'][0]), int(star['pos'][1])), + star['size']) + + def run(self): + running = True + while running: + # Gestion des événements + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_m: + self.toggle_sound() + elif event.key == pygame.K_ESCAPE: + if self.game_state == PLAYING: + self.game_state = PAUSED + elif self.game_state == PAUSED: + self.game_state = PLAYING + self.show_module_menu = False # Fermer le menu des modules + elif self.game_state == 'mode_selection': + self.game_state = MENU + elif event.key == pygame.K_SPACE: + if self.game_state == MENU: + self.game_mode_menu.show() + self.game_state = 'mode_selection' + elif self.game_state == GAME_OVER: + self.reset_game() + self.game_state = MENU + elif event.type == pygame.MOUSEBUTTONDOWN: + if event.button == 1: # Clic gauche + mouse_x, mouse_y = event.pos + button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING + button_y = SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING + if (button_x <= mouse_x <= button_x + SOUND_BUTTON_SIZE and + button_y <= mouse_y <= button_y + SOUND_BUTTON_SIZE): + self.toggle_sound() + elif event.button == 3: # Clic droit + if self.game_state == PLAYING: + self.player.activate_shield() + + # Gestion des événements du menu + if self.game_state == MENU: + action = self.menu_manager.handle_event(event) + if action == 'start_game': + self.game_mode_menu.show() + self.game_state = 'mode_selection' + elif action == 'quit': + running = False + elif action == 'restart_game': + self.current_game_mode.initialize() + self.game_state = PLAYING + elif action == 'resume_game': + self.game_state = PLAYING + elif action == 'go_to_main_menu': + self.game_state = MENU + elif self.game_state == 'mode_selection': + action = self.game_mode_menu.handle_event(event) + if action: + if action == 'back': + self.game_state = MENU + elif action.startswith('mode_'): + mode_id = action[5:] # Enlève le préfixe 'mode_' + if self.select_game_mode(mode_id): + self.game_state = PLAYING + self.current_game_mode.initialize() + # On n'affiche le menu des modules que s'il y en a de débloqués + unlocked_modules = [module for module in self.module_manager.modules.values() if module.is_unlocked] + if unlocked_modules: + self.module_selection.visible = True + self.module_selection.modules = unlocked_modules + elif self.game_state == PLAYING or self.game_state == PAUSED: + self.module_selection.handle_event(event) + + # Gestion des entrées continues (mouvement et tir) + if self.game_state == PLAYING: + # Mise à jour du mode de jeu actuel + self.current_game_mode.update() + + # Gestion du mouvement + keys = pygame.key.get_pressed() + dx = dy = 0 + if keys[pygame.K_z] or keys[pygame.K_UP]: + dy -= 1 + if keys[pygame.K_s] or keys[pygame.K_DOWN]: + dy += 1 + if keys[pygame.K_q] or keys[pygame.K_LEFT]: + dx -= 1 + if keys[pygame.K_d] or keys[pygame.K_RIGHT]: + dx += 1 + if dx != 0 or dy != 0: + self.player.move(dx, dy) + + # Gestion du tir continu + mouse_buttons = pygame.mouse.get_pressed() + if mouse_buttons[0]: # Clic gauche maintenu + self.player.shoot() + if keys[pygame.K_SPACE]: # Barre d'espace maintenue + self.player.shoot() + + # Effacement de l'écran + self.screen.fill(BG_COLOR) + + # Mise à jour et dessin des étoiles + self.update_stars() + self.draw_stars() + + # Gestion des états du jeu + if self.game_state == MENU: + self.menu_manager.draw() + elif self.game_state == 'mode_selection': + self.game_mode_menu.draw() + elif self.game_state == PLAYING: + # Mise à jour du jeu + self.player.update() + if not self.current_game_mode.wave_transition: + self.spawn_enemy() + self.update_enemies() + self.update_effects() + + # Mise à jour des pickups + self.pickups.update(self.player) + + # Rendu du jeu + self.current_game_mode.draw(self.screen) + self.pickups.draw(self.screen) # Affichage des objets à ramasser + self.draw_hud() + + # Dessiner le menu de sélection des modules s'il est visible + self.module_selection.draw(self.screen) + elif self.game_state == PAUSED: + # Afficher d'abord le jeu en arrière-plan + self.current_game_mode.draw(self.screen) + self.draw_hud() + + # Afficher un fond semi-transparent + overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + overlay.fill((0, 0, 0)) + overlay.set_alpha(128) + self.screen.blit(overlay, (0, 0)) + + # Si le menu des modules est visible, on l'affiche + if self.show_module_menu: + self.module_selection.draw(self.screen) + # Sinon on affiche le menu pause normal + else: + # Afficher le texte "PAUSE" + pause_text = self.big_font.render("PAUSE", True, WHITE) + pause_rect = pause_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3)) + self.screen.blit(pause_text, pause_rect) + + # Instructions + resume_text = self.font.render("Appuyez sur ÉCHAP pour reprendre", True, WHITE) + resume_rect = resume_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3)) + self.screen.blit(resume_text, resume_rect) + elif self.game_state == GAME_OVER: + self.draw_game_over() + self.handle_game_over_input() + + # Mise à jour de l'écran + pygame.display.flip() + self.clock.tick(FPS) + + # Nettoyage et fermeture + pygame.quit() + sys.exit() + + def toggle_sound(self): + """Active/désactive le son""" + self.sound_muted = not self.sound_muted + if self.sound_muted: + pygame.mixer.music.pause() + else: + pygame.mixer.music.unpause() + + def play_background_music(self): + """Joue la musique de fond en boucle""" + try: + pygame.mixer.music.play(-1) # -1 pour jouer en boucle + except Exception as e: + print(f"Erreur lors de la lecture de la musique de fond: {e}") + + def load_highscore(self): + """Charge le meilleur score""" + try: + if os.path.exists(SAVE_FILE): + with open(SAVE_FILE, 'r') as f: + data = json.load(f) + return data.get('highscore', 0) + except Exception as e: + print(f'Erreur lors du chargement du meilleur score: {e}') + return 0 + + def load_game_state(self): + """Charge l'état du jeu depuis un fichier""" + try: + if os.path.exists(SAVE_FILE): + with open(SAVE_FILE, 'r') as f: + data = json.load(f) + self.highscore = data.get('highscore', 0) + + # Chargement des records pour chaque mode + records = data.get('records', {}) + if 'classic' in records: + self.game_modes['classic'].best_survival_time = records['classic'].get('best_survival_time', 0) + if 'survival' in records: + self.game_modes['survival'].best_wave = records['survival'].get('best_wave', 0) + if 'boss_rush' in records: + self.game_modes['boss_rush'].best_bosses_killed = records['boss_rush'].get('best_bosses_killed', 0) + + print('État du jeu chargé avec succès') + except Exception as e: + print(f'Erreur lors du chargement de l\'état du jeu: {e}') + + def game_over(self): + """Gère la fin de partie""" + self.game_state = GAME_OVER + self.update_highscore() + def update_highscore(self): + """Met à jour le meilleur score""" + if self.player.score > self.highscore: + self.highscore = self.player.score + self.save_highscore() + + def save_highscore(self): + """Sauvegarde le meilleur score""" + try: + with open(SAVE_FILE, 'w') as f: + json.dump({'highscore': self.highscore}, f) + except Exception as e: + print(f"Erreur lors de la sauvegarde du meilleur score: {e}") + + def get_game_modes(self): + """Retourne les modes de jeu disponibles""" + return self.game_modes + + def select_game_mode(self, mode_id): + """Sélectionne un mode de jeu""" + if mode_id in self.game_modes: + self.current_game_mode = self.game_modes[mode_id] + return True + return False + + def reset_game(self): + """Réinitialise le jeu""" + self.current_game_mode.initialize() + self.game_state = MENU + self.player = Player(self) + self.enemies = pygame.sprite.Group() + self.pickups = pygame.sprite.Group() + self.wave = 1 + self.enemies_killed = 0 + self.last_spawn = 0 + self.spawn_cooldown = 2000 + self.highscore = self.load_highscore() + self.module_manager.reset_temp_modules() # Réinitialisation des modules temporaires + self.save_game_state() + def set_volume(self, volume): """Configure le volume pour tous les sons""" try: if hasattr(self, 'shoot_sound') and self.shoot_sound: self.shoot_sound.set_volume(volume) - print(f"Volume shoot_sound réglé à {volume}") if hasattr(self, 'enemy_shoot_sound') and self.enemy_shoot_sound: self.enemy_shoot_sound.set_volume(volume) if hasattr(self, 'hit_sound') and self.hit_sound: @@ -124,24 +472,240 @@ def set_volume(self, volume): self.enemy_death_sound.set_volume(volume) if hasattr(self, 'player_hurt_sound') and self.player_hurt_sound: self.player_hurt_sound.set_volume(volume) + if hasattr(self, 'coin_sound') and self.coin_sound: + self.coin_sound.set_volume(volume) + if hasattr(self, 'achievement_sound') and self.achievement_sound: + self.achievement_sound.set_volume(volume) + if hasattr(self, 'purchase_sound') and self.purchase_sound: + self.purchase_sound.set_volume(volume) except Exception as e: print(f"Erreur lors du réglage du volume: {e}") + + def play_sound(self, sound_type): + """Joue un son spécifique""" + if not self.sound_muted: + try: + sound = None + if sound_type == 'shoot': + sound = self.shoot_sound + elif sound_type == 'enemy_shoot': + sound = self.enemy_shoot_sound + elif sound_type == 'hit': + sound = self.hit_sound + elif sound_type == 'enemy_death': + sound = self.enemy_death_sound + elif sound_type == 'player_hurt': + sound = self.player_hurt_sound + elif sound_type == 'coin': + sound = self.coin_sound + elif sound_type == 'achievement': + sound = self.achievement_sound + elif sound_type == 'purchase': + sound = self.purchase_sound + + if sound: + channel = pygame.mixer.find_channel(True) + if channel: + channel.set_volume(0.3) + channel.play(sound, maxtime=1000) # Limite la durée à 1 seconde + except Exception as e: + print(f"Erreur lors de la lecture du son {sound_type}: {e}") + + def load_background_music(self): + """Charge la musique de fond""" + try: + pygame.mixer.music.load(GAME_MUSIC) + pygame.mixer.music.set_volume(0.1) + except Exception as e: + print(f"Erreur lors du chargement de la musique de fond: {e}") + + def pause_background_music(self): + """Met en pause la musique de fond""" + pygame.mixer.music.pause() + + def resume_background_music(self): + """Reprend la musique de fond""" + pygame.mixer.music.unpause() + + def stop_background_music(self): + """Arrête la musique de fond""" + pygame.mixer.music.stop() + + def set_background_music_volume(self, volume): + """Règle le volume de la musique de fond""" + pygame.mixer.music.set_volume(volume) + + def get_background_music_volume(self): + """Retourne le volume courant de la musique de fond""" + return pygame.mixer.music.get_volume() + + def update_effects(self): + """Mise à jour des effets visuels""" + if TRAIL_EFFECT: + self.player.trail.append((self.player.rect.center, 255)) + # Faire disparaître progressivement la traînée + self.player.trail = [(pos, alpha-10) for pos, alpha in self.player.trail if alpha > 0] + + def draw_wave_transition(self): + """Affiche la transition entre les vagues""" + if self.wave_transition: + overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + overlay.fill(BG_COLOR) + overlay.set_alpha(128) + self.screen.blit(overlay, (0, 0)) + + wave_text = self.big_font.render(f"VAGUE {self.wave}", True, CYAN) + wave_rect = wave_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) + self.screen.blit(wave_text, wave_rect) + + def draw_hud(self): + """Affiche l'interface utilisateur""" + # Affichage de la santé à gauche + health_text = self.font.render(f"Vie: {self.player.health}", True, WHITE) + health_rect = health_text.get_rect(topleft=(20, 20)) + self.screen.blit(health_text, health_rect) - def reset_game(self): - self.player = Player(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2) - self.player.game = self # Passer l'instance du jeu - self.enemies = pygame.sprite.Group() - self.wave = 1 - self.wave_enemies_left = ENEMIES_PER_WAVE - self.enemies_killed = 0 - self.wave_transition = False - self.wave_transition_start = 0 - self.last_spawn = 0 - self.spawn_cooldown = 2000 + # Barre de vie + health_bar_width = 200 + health_bar_height = 20 + health_ratio = max(0, self.player.health / 100) + pygame.draw.rect(self.screen, RED, (20, 50, health_bar_width, health_bar_height), 2) + pygame.draw.rect(self.screen, RED, (20, 50, health_bar_width * health_ratio, health_bar_height)) + + # Affichage du score actuel en haut à droite + score_text = self.font.render(f"Score: {self.player.score}", True, WHITE) + score_rect = score_text.get_rect(topright=(SCREEN_WIDTH - 20, 20)) + self.screen.blit(score_text, score_rect) + + # Affichage spécifique selon le mode de jeu + if isinstance(self.current_game_mode, ClassicMode): + # Mode Classique : Temps de survie + survival_text = self.font.render( + f"Temps de survie: {self.current_game_mode.format_time(self.current_game_mode.survival_time)}", + True, WHITE + ) + survival_rect = survival_text.get_rect(midtop=(SCREEN_WIDTH // 2, 20)) + self.screen.blit(survival_text, survival_rect) + + if self.current_game_mode.best_survival_time > 0: + best_time_text = self.font.render( + f"Record: {self.current_game_mode.format_time(self.current_game_mode.best_survival_time)}", + True, GOLD_COLOR + ) + best_time_rect = best_time_text.get_rect(midtop=(SCREEN_WIDTH // 2, 50)) + self.screen.blit(best_time_text, best_time_rect) + + elif isinstance(self.current_game_mode, SurvivalMode): + # Mode Survie : Affichage de la vague actuelle et du record + wave_text = self.font.render( + f"Vague actuelle: {self.current_game_mode.wave}", + True, CYAN + ) + wave_rect = wave_text.get_rect(midtop=(SCREEN_WIDTH // 2, 20)) + self.screen.blit(wave_text, wave_rect) + + if self.current_game_mode.best_wave > 0: + best_wave_text = self.font.render( + f"Record: Vague {self.current_game_mode.best_wave}", + True, GOLD_COLOR + ) + best_wave_rect = best_wave_text.get_rect(midtop=(SCREEN_WIDTH // 2, 50)) + self.screen.blit(best_wave_text, best_wave_rect) + + elif isinstance(self.current_game_mode, BossRushMode): + # Mode Boss Rush : Compteur de boss + boss_count_text = self.font.render( + f"Boss vaincus: {self.current_game_mode.bosses_killed}", + True, WHITE + ) + boss_count_rect = boss_count_text.get_rect(midtop=(SCREEN_WIDTH // 2, 20)) + self.screen.blit(boss_count_text, boss_count_rect) + + if self.current_game_mode.best_bosses_killed > 0: + record_text = self.font.render( + f"Record: {self.current_game_mode.best_bosses_killed} boss", + True, GOLD_COLOR + ) + record_rect = record_text.get_rect(midtop=(SCREEN_WIDTH // 2, 50)) + self.screen.blit(record_text, record_rect) + + # Affichage de la vague + wave_text = self.font.render(f"Vague {self.wave}", True, CYAN) + wave_rect = wave_text.get_rect(midtop=(SCREEN_WIDTH // 2, 80)) + self.screen.blit(wave_text, wave_rect) + + # Informations sur les ennemis à droite + y_offset = 60 + spacing = 35 + + # Nombre d'ennemis actifs + enemies_text = self.font.render(f"Ennemis: {len(self.enemies)}", True, WHITE) + enemies_rect = enemies_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset)) + self.screen.blit(enemies_text, enemies_rect) + + # Ennemis restants à faire apparaître + remaining = self.current_game_mode.get_remaining_enemies() + remaining_text = self.font.render(f"Restants: {remaining}", True, WHITE) + remaining_rect = remaining_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset + spacing)) + self.screen.blit(remaining_text, remaining_rect) + + # Bouton son en bas à droite + button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING + button_y = SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING + + # Dessiner le cercle du bouton + color = SOUND_ON_COLOR if not self.sound_muted else SOUND_OFF_COLOR + pygame.draw.circle(self.screen, color, (button_x + SOUND_BUTTON_SIZE//2, button_y + SOUND_BUTTON_SIZE//2), SOUND_BUTTON_SIZE//2) + + # Dessiner l'icône + if not self.sound_muted: + # Dessiner des ondes sonores + for i in range(3): + radius = (i + 1) * 5 + pygame.draw.arc(self.screen, WHITE, + (button_x + SOUND_BUTTON_SIZE//2 - radius, + button_y + SOUND_BUTTON_SIZE//2 - radius, + radius * 2, radius * 2), + -math.pi/4, math.pi/4, 2) + else: + # Dessiner une croix + start_x = button_x + SOUND_BUTTON_SIZE//4 + start_y = button_y + SOUND_BUTTON_SIZE//4 + end_x = button_x + SOUND_BUTTON_SIZE*3//4 + end_y = button_y + SOUND_BUTTON_SIZE*3//4 + pygame.draw.line(self.screen, WHITE, (start_x, start_y), (end_x, end_y), 2) + pygame.draw.line(self.screen, WHITE, (start_x, end_y), (end_x, start_y), 2) + + # Barre de cooldown du bouclier + shield_cooldown_width = 200 + shield_cooldown_height = 20 + current_time = pygame.time.get_ticks() + if self.player.shield_active: + shield_ratio = 1 - (current_time - self.player.last_shield_activation) / self.player.shield_duration + else: + shield_ratio = min(1, (current_time - self.player.last_shield_activation) / self.player.shield_cooldown) + pygame.draw.rect(self.screen, RED, (20, 80, shield_cooldown_width, shield_cooldown_height), 2) + pygame.draw.rect(self.screen, CYAN, (20, 80, shield_cooldown_width * shield_ratio, shield_cooldown_height)) + + # Texte du cooldown du bouclier + shield_text = self.font.render("Bouclier", True, WHITE) + shield_rect = shield_text.get_rect(topleft=(20, 110)) + self.screen.blit(shield_text, shield_rect) + def spawn_enemy(self): + """Spawn un ennemi""" + # Déléguer au mode de jeu actuel + if hasattr(self.current_game_mode, 'spawn_enemies'): + self.current_game_mode.spawn_enemies() + # Fallback sur la méthode originale si nécessaire + else: + self._legacy_spawn_enemy() + + def _legacy_spawn_enemy(self): + """Ancienne méthode de spawn d'ennemis (pour compatibilité)""" current_time = pygame.time.get_ticks() - if current_time - self.last_spawn >= self.spawn_cooldown and self.wave_enemies_left > 0: + if current_time - self.last_spawn >= self.spawn_cooldown and self.current_game_mode.wave_enemies_left > 0: health = 50 + (self.wave * 10) speed = 2 + (self.wave * 0.5) @@ -152,10 +716,11 @@ def spawn_enemy(self): enemy.game = self # Passer l'instance du jeu avec les sons self.enemies.add(enemy) - self.wave_enemies_left -= 1 + self.current_game_mode.wave_enemies_left -= 1 self.last_spawn = current_time - + def update_wave(self): + """Gestion des vagues d'ennemis""" # On change de vague uniquement si tous les ennemis prévus sont apparus ET qu'il n'y en a plus sur le terrain if self.wave_enemies_left <= 0 and len(self.enemies) == 0: if not self.wave_transition: @@ -166,12 +731,16 @@ def update_wave(self): # Calcul du nouveau nombre d'ennemis avec le coefficient self.wave_enemies_left = int(ENEMIES_PER_WAVE * (WAVE_ENEMY_MULTIPLIER ** (self.wave - 1))) self.spawn_cooldown = max(500, self.spawn_cooldown - WAVE_SPEEDUP) + print(f"Début de la transition de vague Game {self.wave}") # Log pour debug if self.wave_transition: - if pygame.time.get_ticks() - self.wave_start_time > WAVE_TRANSITION_TIME: + current_time = pygame.time.get_ticks() + if current_time - self.wave_start_time > WAVE_TRANSITION_TIME: self.wave_transition = False + print(f"Fin de la transition de vague Game {self.wave}") # Log pour debug def update_enemies(self): + """Met à jour les ennemis""" for enemy in self.enemies: if isinstance(enemy, ShootingEnemy): enemy.update((self.player.x, self.player.y)) @@ -193,7 +762,21 @@ def update_enemies(self): for bullet in bullet_hits: if enemy.take_damage(BULLET_DAMAGE): self.player.gain_score(ENEMY_SCORE) - self.enemies_killed += 1 # Compte les ennemis tués + self.enemies_killed += 1 + + # Lorsqu'un ennemi est tué, on décrémente le compteur + if hasattr(self.current_game_mode, 'wave_enemies_left'): + if self.current_game_mode.wave_enemies_left > 0: + self.current_game_mode.wave_enemies_left -= 1 + + # Chance de faire apparaître une pièce ou un coffre + if random.random() < 0.3: # 30% de chance + from game.core.pickup import Pickup + if random.random() < 0.8: # 80% de chance pour une pièce, 20% pour un coffre + pickup = Pickup(enemy.rect.centerx, enemy.rect.centery, 'coin') + else: + pickup = Pickup(enemy.rect.centerx, enemy.rect.centery, 'chest') + self.pickups.add(pickup) # Vérifie les collisions avec le joueur if enemy.attack(self.player): @@ -203,8 +786,9 @@ def update_enemies(self): pass if self.player.health <= 0: self.game_over() - + def draw_game_over(self): + """Affiche l'écran de game over""" # Fond semi-transparent overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) overlay.fill(BG_COLOR) @@ -235,382 +819,45 @@ def draw_game_over(self): quit_text = self.font.render("Appuyez sur ÉCHAP pour quitter", True, WHITE) quit_rect = quit_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 3 // 4)) self.screen.blit(quit_text, quit_rect) - - def game_over(self): - self.game_state = GAME_OVER - self.update_highscore() - + def handle_game_over_input(self): + """Gère les entrées dans l'écran de game over""" for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: self.reset_game() - self.game_state = PLAYING # Important : définir l'état à PLAYING + self.game_state = PLAYING elif event.key == pygame.K_ESCAPE: pygame.quit() sys.exit() - - def draw_hud(self): - # Affichage de la santé à gauche - health_text = self.font.render(f"Vie: {self.player.health}", True, WHITE) - health_rect = health_text.get_rect(topleft=(20, 20)) - self.screen.blit(health_text, health_rect) - - # Barre de vie - health_bar_width = 200 - health_bar_height = 20 - health_ratio = max(0, self.player.health / 100) - pygame.draw.rect(self.screen, RED, (20, 50, health_bar_width, health_bar_height), 2) - pygame.draw.rect(self.screen, RED, (20, 50, health_bar_width * health_ratio, health_bar_height)) - - # Affichage du score actuel en haut à droite - score_text = self.font.render(f"Score: {self.player.score}", True, WHITE) - score_rect = score_text.get_rect(topright=(SCREEN_WIDTH - 20, 20)) - self.screen.blit(score_text, score_rect) - - # Affichage de la vague au centre - wave_text = self.font.render(f"Vague {self.wave}", True, CYAN) - wave_rect = wave_text.get_rect(midtop=(SCREEN_WIDTH // 2, 20)) - self.screen.blit(wave_text, wave_rect) - - # Affichage du meilleur score sous la vague - highscore_text = self.font.render(f"Meilleur Score : {self.highscore}", True, CYAN) - highscore_rect = highscore_text.get_rect(midtop=(SCREEN_WIDTH // 2, 60)) - self.screen.blit(highscore_text, highscore_rect) - - # Informations sur les ennemis à droite (réorganisées) - y_offset = 60 # Commence plus haut pour combler l'espace - spacing = 35 # Espacement réduit entre les lignes - - # Nombre d'ennemis actifs - enemies_text = self.font.render(f"Ennemis: {len(self.enemies)}", True, WHITE) - enemies_rect = enemies_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset)) - self.screen.blit(enemies_text, enemies_rect) - - # Ennemis restants à faire apparaître - remaining_text = self.font.render(f"Restants: {self.wave_enemies_left}", True, WHITE) - remaining_rect = remaining_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset + spacing)) - self.screen.blit(remaining_text, remaining_rect) - - # Bouton son en bas à droite - button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - button_y = SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - - # Dessiner le cercle du bouton - color = SOUND_ON_COLOR if not self.sound_muted else SOUND_OFF_COLOR - pygame.draw.circle(self.screen, color, (button_x + SOUND_BUTTON_SIZE//2, button_y + SOUND_BUTTON_SIZE//2), SOUND_BUTTON_SIZE//2) - - # Dessiner l'icône - if not self.sound_muted: - # Dessiner des ondes sonores - for i in range(3): - radius = (i + 1) * 5 - pygame.draw.arc(self.screen, WHITE, - (button_x + SOUND_BUTTON_SIZE//2 - radius, - button_y + SOUND_BUTTON_SIZE//2 - radius, - radius * 2, radius * 2), - -math.pi/4, math.pi/4, 2) - else: - # Dessiner une croix - start_x = button_x + SOUND_BUTTON_SIZE//4 - start_y = button_y + SOUND_BUTTON_SIZE//4 - end_x = button_x + SOUND_BUTTON_SIZE*3//4 - end_y = button_y + SOUND_BUTTON_SIZE*3//4 - pygame.draw.line(self.screen, WHITE, (start_x, start_y), (end_x, end_y), 2) - pygame.draw.line(self.screen, WHITE, (start_x, end_y), (end_x, start_y), 2) - - # Barre de cooldown du bouclier - shield_cooldown_width = 200 - shield_cooldown_height = 20 - current_time = pygame.time.get_ticks() - if self.player.shield_active: - shield_ratio = 1 - (current_time - self.player.last_shield_activation) / self.player.shield_duration - else: - shield_ratio = min(1, (current_time - self.player.last_shield_activation) / self.player.shield_cooldown) - - pygame.draw.rect(self.screen, RED, (20, 80, shield_cooldown_width, shield_cooldown_height), 2) - pygame.draw.rect(self.screen, CYAN, (20, 80, shield_cooldown_width * shield_ratio, shield_cooldown_height)) - - # Texte du cooldown du bouclier - shield_text = self.font.render("Bouclier", True, WHITE) - shield_rect = shield_text.get_rect(topleft=(20, 110)) - self.screen.blit(shield_text, shield_rect) - def update_effects(self): - # Mise à jour des effets visuels - if TRAIL_EFFECT: - self.player.trail.append((self.player.rect.center, 255)) - # Faire disparaître progressivement la traînée - self.player.trail = [(pos, alpha-10) for pos, alpha in self.player.trail if alpha > 0] - - def draw_wave_transition(self): - if self.wave_transition: - overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) - overlay.fill(BG_COLOR) - overlay.set_alpha(128) - self.screen.blit(overlay, (0, 0)) - - wave_text = self.big_font.render(f"VAGUE {self.wave}", True, CYAN) - wave_rect = wave_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) - self.screen.blit(wave_text, wave_rect) - - def toggle_sound(self): - self.sound_muted = not self.sound_muted - - # Gestion des effets sonores - volume = 0 if self.sound_muted else 0.3 - for sound in [self.shoot_sound, self.enemy_shoot_sound, - self.hit_sound, self.enemy_death_sound, - self.player_hurt_sound]: - if sound: - sound.set_volume(volume) - - # Gestion de la musique de fond - if self.sound_muted: - pygame.mixer.music.pause() - else: - pygame.mixer.music.unpause() - if not pygame.mixer.music.get_busy(): - self.play_background_music() - - def play_sound(self, sound_type): - if not self.sound_muted: - try: - sound = None - if sound_type == 'shoot': - sound = self.shoot_sound - elif sound_type == 'enemy_shoot': - sound = self.enemy_shoot_sound - elif sound_type == 'hit': - sound = self.hit_sound - elif sound_type == 'enemy_death': - sound = self.enemy_death_sound - elif sound_type == 'player_hurt': - sound = self.player_hurt_sound - - if sound: - channel = pygame.mixer.find_channel(True) - if channel: - channel.set_volume(0.3) - channel.play(sound, maxtime=1000) # Limite la durée à 1 seconde - print(f"Son joué: {sound_type}") - except Exception as e: - print(f"Erreur lors de la lecture du son {sound_type}: {e}") - - def generate_stars(self): - """Génère les étoiles initiales""" - for _ in range(self.num_stars): - x = random.randint(0, SCREEN_WIDTH) - y = random.randint(0, SCREEN_HEIGHT) - speed = random.choice(self.star_speeds) - brightness = random.randint(100, 255) - size = random.randint(1, 3) - angle = random.uniform(0, 2 * math.pi) # Angle aléatoire pour chaque étoile - self.stars.append({ - 'pos': [x, y], - 'speed': speed, - 'brightness': brightness, - 'size': size, - 'angle': angle # Nouvelle propriété pour la direction - }) - - def update_stars(self): - """Met à jour la position des étoiles avec un mouvement constant""" - for star in self.stars: - # Mouvement constant vers le bas avec une légère dérive - star['pos'][1] += star['speed'] - star['pos'][0] += math.sin(star['angle']) * 0.3 - - # Fait réapparaître les étoiles en haut quand elles sortent de l'écran - if star['pos'][1] > SCREEN_HEIGHT: - star['pos'][1] = 0 - star['pos'][0] = random.randint(0, SCREEN_WIDTH) - star['brightness'] = random.randint(100, 255) # Nouvelle luminosité - - # Garde les étoiles dans les limites horizontales - if star['pos'][0] < 0: - star['pos'][0] = SCREEN_WIDTH - elif star['pos'][0] > SCREEN_WIDTH: - star['pos'][0] = 0 - - def draw_stars(self): - """Dessine les étoiles""" - for star in self.stars: - color = (star['brightness'], star['brightness'], star['brightness']) - pygame.draw.circle(self.screen, color, - (int(star['pos'][0]), int(star['pos'][1])), - star['size']) - - def load_background_music(self): - """Charge la musique de fond""" - try: - pygame.mixer.music.load(GAME_MUSIC) - pygame.mixer.music.set_volume(0.1) - except Exception as e: - print(f"Erreur lors du chargement de la musique de fond: {e}") - - def play_background_music(self): - """Joue la musique de fond en boucle""" - try: - pygame.mixer.music.play(-1) # -1 pour jouer en boucle - except Exception as e: - print(f"Erreur lors de la lecture de la musique de fond: {e}") - - def pause_background_music(self): - """Met en pause la musique de fond""" - pygame.mixer.music.pause() - - def resume_background_music(self): - """Reprend la musique de fond""" - pygame.mixer.music.unpause() - - def stop_background_music(self): - """Arrête la musique de fond""" - pygame.mixer.music.stop() - - def set_background_music_volume(self, volume): - """Règle le volume de la musique de fond""" - pygame.mixer.music.set_volume(volume) - - def get_background_music_volume(self): - """Retourne le volume courant de la musique de fond""" - return pygame.mixer.music.get_volume() - - def load_highscore(self): + def save_game_state(self): + """Sauvegarde l'état du jeu""" try: - if not os.path.exists(os.path.dirname(SCORE_FILE)): - os.makedirs(os.path.dirname(SCORE_FILE)) - if os.path.exists(SCORE_FILE): - with open(SCORE_FILE, 'r') as f: - data = json.load(f) - return data.get('highscore', 0) - return 0 - except Exception as e: - print(f"Erreur lors du chargement du meilleur score: {e}") - return 0 - - def save_highscore(self): - try: - with open(SCORE_FILE, 'w') as f: - json.dump({'highscore': self.highscore}, f) - except Exception as e: - print(f"Erreur lors de la sauvegarde du meilleur score: {e}") - - def update_highscore(self): - if self.player.score > self.highscore: - self.highscore = self.player.score - self.save_highscore() - - def draw_menu(self): - # Fond sombre semi-transparent - overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA) - overlay.fill((0, 0, 0, 180)) - self.screen.blit(overlay, (0, 0)) - - # Titre du jeu - title_text = self.big_font.render("SHOOTER SURVIVAL", True, CYAN) - title_rect = title_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3)) - self.screen.blit(title_text, title_rect) - - # Instructions - start_text = self.font.render("Appuyez sur ESPACE pour commencer", True, WHITE) - start_rect = start_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) - self.screen.blit(start_text, start_rect) - - # Meilleur score - highscore_text = self.font.render(f"Meilleur score : {self.highscore}", True, WHITE) - highscore_rect = highscore_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3)) - self.screen.blit(highscore_text, highscore_rect) - - def draw_pause(self): - # Fond sombre semi-transparent - overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA) - overlay.fill((0, 0, 0, 180)) - self.screen.blit(overlay, (0, 0)) - - # Texte de pause - pause_text = self.big_font.render("PAUSE", True, WHITE) - pause_rect = pause_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) - self.screen.blit(pause_text, pause_rect) - - # Instructions - resume_text = self.font.render("Appuyez sur ECHAP pour reprendre", True, WHITE) - resume_rect = resume_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3)) - self.screen.blit(resume_text, resume_rect) - - def run(self): - while True: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - pygame.quit() - sys.exit() - elif event.type == pygame.KEYDOWN: - if event.key == pygame.K_m: - self.toggle_sound() - elif event.key == pygame.K_ESCAPE: - if self.game_state == PLAYING: - self.game_state = PAUSED - elif self.game_state == PAUSED: - self.game_state = PLAYING - elif event.key == pygame.K_SPACE: - if self.game_state == MENU: - self.reset_game() - self.game_state = PLAYING - elif self.game_state == GAME_OVER: - self.reset_game() - self.game_state = PLAYING - elif event.type == pygame.MOUSEBUTTONDOWN: - if event.button == 1: # Clic gauche - mouse_x, mouse_y = event.pos - button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - button_y = SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - if (button_x <= mouse_x <= button_x + SOUND_BUTTON_SIZE and - button_y <= mouse_y <= button_y + SOUND_BUTTON_SIZE): - self.toggle_sound() - elif event.button == 3: # Clic droit - if self.game_state == PLAYING: - self.player.activate_shield() + if not os.path.exists(os.path.dirname(SAVE_FILE)): + os.makedirs(os.path.dirname(SAVE_FILE)) - # Fond étoilé toujours actif - self.screen.fill((5, 5, 15)) - self.update_stars() - self.draw_stars() + # Récupération des records pour chaque mode + records = { + 'classic': { + 'best_survival_time': self.game_modes['classic'].best_survival_time + }, + 'survival': { + 'best_wave': self.game_modes['survival'].best_wave + }, + 'boss_rush': { + 'best_bosses_killed': self.game_modes['boss_rush'].best_bosses_killed + } + } - if self.game_state == MENU: - self.menu_manager.draw_main_menu() - elif self.game_state == PLAYING: - self.player.update() - if not self.wave_transition: - self.spawn_enemy() - self.update_enemies() - self.update_effects() - self.update_wave() - - self.player.draw(self.screen) - for enemy in self.enemies: - enemy.draw(self.screen) - for enemy in self.enemies: - if isinstance(enemy, ShootingEnemy): - enemy.bullets.draw(self.screen) - self.draw_hud() - self.draw_wave_transition() - elif self.game_state == PAUSED: - # Afficher d'abord le jeu en arrière-plan - self.player.draw(self.screen) - for enemy in self.enemies: - enemy.draw(self.screen) - for enemy in self.enemies: - if isinstance(enemy, ShootingEnemy): - enemy.bullets.draw(self.screen) - self.draw_hud() - # Puis afficher le menu de pause par-dessus - self.menu_manager.draw_pause_menu() - elif self.game_state == GAME_OVER: - self.menu_manager.draw_game_over_menu() - - pygame.display.flip() - self.clock.tick(FPS) + with open(SAVE_FILE, 'w') as f: + json.dump({ + 'highscore': self.highscore, + 'wave': self.wave, + 'score': self.player.score if hasattr(self, 'player') else 0, + 'records': records + }, f) + except Exception as e: + print(f"Erreur lors de la sauvegarde de l'état du jeu: {e}") -if __name__ == '__main__': - game = Game() - game.run() \ No newline at end of file + # ... [Toutes les autres méthodes de la classe Game] \ No newline at end of file diff --git a/src/game/core/pickup.py b/src/game/core/pickup.py new file mode 100644 index 0000000..654dab4 --- /dev/null +++ b/src/game/core/pickup.py @@ -0,0 +1,98 @@ +import pygame +import os +import random +from utils.constants import * + +class Pickup(pygame.sprite.Sprite): + def __init__(self, x, y, pickup_type): + super().__init__() + self.pickup_type = pickup_type + + # Chargement de l'image en fonction du type + try: + if pickup_type == 'coin': + self.image = pygame.image.load(COIN_ICON).convert_alpha() + elif pickup_type == 'chest': + self.image = pygame.image.load(os.path.join(ICONS_DIR, 'chest.png')).convert_alpha() + + # Redimensionner l'image + self.image = pygame.transform.scale(self.image, (32, 32)) + except Exception as e: + print(f'Erreur lors du chargement de l\'image du {pickup_type}: {e}') + # Image par défaut si erreur + self.image = pygame.Surface((32, 32)) + if pickup_type == 'coin': + self.image.fill(GOLD_COLOR) + else: + self.image.fill((139, 69, 19)) # Marron pour le coffre + + self.rect = self.image.get_rect() + self.rect.center = (x, y) + + # Variables pour l'animation de flottement + self.original_y = y + self.float_offset = 0 + self.float_speed = 2 + self.float_direction = 1 + self.lifetime = 10000 # Durée de vie en ms + self.spawn_time = pygame.time.get_ticks() + + # Variables pour l'attraction vers le joueur + self.is_attracted = False + self.attraction_speed = 5 + self.x = float(x) + self.y = float(y) + + def update(self, player=None): + current_time = pygame.time.get_ticks() + + # Vérifier si le pickup doit disparaître + if current_time - self.spawn_time > self.lifetime: + self.kill() + return + + # Animation de flottement si pas attiré + if not self.is_attracted: + self.float_offset += self.float_speed * self.float_direction * 0.1 + if abs(self.float_offset) > 5: + self.float_direction *= -1 + self.rect.centery = self.original_y + self.float_offset + + # Vérifier si le joueur est assez proche pour être attiré + if player and self.rect.colliderect(player.rect.inflate(100, 100)): + self.is_attracted = True + + # Attraction vers le joueur + elif player: + dx = player.rect.centerx - self.x + dy = player.rect.centery - self.y + dist = (dx * dx + dy * dy) ** 0.5 + + if dist > 0: + self.x += (dx / dist) * self.attraction_speed + self.y += (dy / dist) * self.attraction_speed + + self.rect.centerx = int(self.x) + self.rect.centery = int(self.y) + + # Collision avec le joueur + if self.rect.colliderect(player.rect): + if self.pickup_type == 'coin': + player.game.economy_manager.add_coins(random.randint(5, 15)) + if player.game.coin_sound: + player.game.coin_sound.play() + elif self.pickup_type == 'chest': + # Obtenir 3 modules temporaires aléatoires + available_modules = player.game.module_manager.get_random_temp_modules(3) + if available_modules: + # Afficher le menu de sélection des modules + player.game.module_selection.show(available_modules) + player.game.show_module_menu = True + if player.game.achievement_sound: + player.game.achievement_sound.play() + else: + # Si aucun module n'est disponible, donner des pièces + player.game.economy_manager.add_coins(random.randint(10, 25)) + if player.game.coin_sound: + player.game.coin_sound.play() + self.kill() \ No newline at end of file diff --git a/src/game/core/player.py b/src/game/core/player.py new file mode 100644 index 0000000..c73b5cc --- /dev/null +++ b/src/game/core/player.py @@ -0,0 +1,264 @@ +import pygame +import math +import os +from utils.constants import * +from game.core.weapon import Bullet + +class Player(pygame.sprite.Sprite): + def __init__(self, game): + super().__init__() + self.game = game + + # Attributs pour les effets visuels + self.trail = [] + + # Chargement de l'image du joueur + try: + # Essayer d'abord de charger player.png + sprite_path = os.path.join(ASSETS_DIR, 'characters', 'player.png') + if os.path.exists(sprite_path): + self.original_image = pygame.image.load(sprite_path).convert_alpha() + else: + # Fallback sur default.png + sprite_path = os.path.join(ASSETS_DIR, 'characters', 'default.png') + if os.path.exists(sprite_path): + self.original_image = pygame.image.load(sprite_path).convert_alpha() + else: + # Créer une image par défaut si aucun sprite n'est trouvé + self.original_image = pygame.Surface((PLAYER_SIZE, PLAYER_SIZE), pygame.SRCALPHA) + # Dessiner un vaisseau triangulaire + points = [(PLAYER_SIZE//2, 0), (0, PLAYER_SIZE), (PLAYER_SIZE, PLAYER_SIZE)] + pygame.draw.polygon(self.original_image, PLAYER_COLOR, points) + # Ajouter des détails + pygame.draw.polygon(self.original_image, (255, 255, 255), + [(PLAYER_SIZE//2, PLAYER_SIZE//4), + (PLAYER_SIZE//3, PLAYER_SIZE//2), + (PLAYER_SIZE//2, PLAYER_SIZE//1.5), + (PLAYER_SIZE//1.5, PLAYER_SIZE//2)]) + print('Image par défaut créée pour le joueur') + + # Redimensionner l'image + self.original_image = pygame.transform.scale(self.original_image, (PLAYER_SIZE, PLAYER_SIZE)) + self.image = self.original_image.copy() + except Exception as e: + print(f'Erreur lors du chargement du sprite du joueur: {e}') + self.original_image = pygame.Surface((PLAYER_SIZE, PLAYER_SIZE), pygame.SRCALPHA) + # Dessiner un vaisseau triangulaire + points = [(PLAYER_SIZE//2, 0), (0, PLAYER_SIZE), (PLAYER_SIZE, PLAYER_SIZE)] + pygame.draw.polygon(self.original_image, PLAYER_COLOR, points) + self.image = self.original_image.copy() + + self.rect = self.image.get_rect() + self.angle = 0 # Angle de rotation initial + self.last_shot_effect = 0 # Pour l'effet de tir + + # Position initiale + self.x = SCREEN_WIDTH // 2 + self.y = SCREEN_HEIGHT // 2 + self.rect.center = (self.x, self.y) + + # Attributs de base + self.base_speed = 5 + self.base_shoot_cooldown = 250 + self.base_shield_cooldown = 5000 + self.base_health = 100 + self.base_bullet_damage = BULLET_DAMAGE + + # Attributs modifiés par les modules + self.speed = self.base_speed + self.shoot_cooldown = self.base_shoot_cooldown + self.shield_cooldown = self.base_shield_cooldown + self.max_health = self.base_health + self.health = self.max_health + self.bullet_damage = self.base_bullet_damage + self.health_regen = 0 + self.extra_bullets = 0 + + # État du joueur + self.score = 0 + self.last_shot = 0 + self.last_shield = 0 + self.last_shield_activation = 0 + self.shield_active = False + self.shield_duration = 2000 + + # Groupe de sprites pour les balles + self.bullets = pygame.sprite.Group() + + def move(self, dx, dy): + """Déplace le joueur""" + # Normalisation du vecteur de déplacement + length = math.sqrt(dx * dx + dy * dy) + if length > 0: + dx = dx / length * self.speed + dy = dy / length * self.speed + + # Mise à jour de la position + self.x = max(16, min(SCREEN_WIDTH - 16, self.x + dx)) + self.y = max(16, min(SCREEN_HEIGHT - 16, self.y + dy)) + self.rect.center = (self.x, self.y) + + def move_up(self): + """Déplace le joueur vers le haut""" + self.move(0, -1) + + def move_down(self): + """Déplace le joueur vers le bas""" + self.move(0, 1) + + def move_left(self): + """Déplace le joueur vers la gauche""" + self.move(-1, 0) + + def move_right(self): + """Déplace le joueur vers la droite""" + self.move(1, 0) + + def shoot(self): + """Tire une balle""" + current_time = pygame.time.get_ticks() + if current_time - self.last_shot > self.shoot_cooldown: + # Calcul de l'angle vers la souris + mouse_x, mouse_y = pygame.mouse.get_pos() + angle = math.atan2(mouse_y - self.y, mouse_x - self.x) + + # Calcul de la position de départ du projectile (à l'avant du vaisseau) + offset = PLAYER_SIZE // 2 # Distance depuis le centre du vaisseau + start_x = self.x + math.cos(angle) * offset + start_y = self.y + math.sin(angle) * offset + + # Tir multiple selon le nombre de balles supplémentaires + angles = [] + if self.extra_bullets == 0: + angles = [angle] + elif self.extra_bullets == 1: + angles = [angle - 0.1, angle + 0.1] + elif self.extra_bullets == 2: + angles = [angle - 0.2, angle, angle + 0.2] + + # Création des balles + for shoot_angle in angles: + bullet = Bullet(start_x, start_y, shoot_angle, self.bullet_damage) + self.bullets.add(bullet) + + # Effet visuel de tir + self.last_shot_effect = current_time + + # Son de tir + if hasattr(self.game, 'shoot_sound') and self.game.shoot_sound and not self.game.sound_muted: + self.game.shoot_sound.play() + + self.last_shot = current_time + + def activate_shield(self): + """Active le bouclier""" + current_time = pygame.time.get_ticks() + if current_time - self.last_shield > self.shield_cooldown: + self.shield_active = True + self.last_shield = current_time + self.last_shield_activation = current_time + + def update(self): + """Met à jour l'état du joueur""" + # Mise à jour de l'angle de rotation en fonction de la position de la souris + mouse_x, mouse_y = pygame.mouse.get_pos() + dx = mouse_x - self.x + dy = mouse_y - self.y + self.angle = -math.degrees(math.atan2(dy, dx)) + 90 # +90 au lieu de -90 pour pivoter de 180 degrés + + # Mise à jour du bouclier + current_time = pygame.time.get_ticks() + if self.shield_active and current_time - self.last_shield > self.shield_duration: + self.shield_active = False + + # Régénération de santé + if self.health_regen > 0 and self.health < self.max_health: + self.health = min(self.max_health, self.health + self.health_regen / 60) # 60 FPS + + # Mise à jour des balles + self.bullets.update() + + # Suppression des balles hors écran + for bullet in self.bullets: + if not bullet.is_on_screen(): + bullet.kill() + + def draw(self, screen): + """Dessine le joueur et ses balles""" + # Dessin des balles + self.bullets.draw(screen) + + # Rotation de l'image du joueur + rotated_image = pygame.transform.rotate(self.original_image, self.angle) + rotated_rect = rotated_image.get_rect(center=self.rect.center) + + # Effet de tir (flash à l'avant du vaisseau) + current_time = pygame.time.get_ticks() + if current_time - self.last_shot_effect < 100: # Effet pendant 100ms + # Calculer la position du flash + flash_angle_rad = math.radians(self.angle - 90) # Convertir en radians et ajuster + flash_offset = PLAYER_SIZE // 2 + flash_x = self.rect.centerx + math.cos(flash_angle_rad) * flash_offset + flash_y = self.rect.centery + math.sin(flash_angle_rad) * flash_offset + + # Dessiner le flash + flash_radius = 5 + (current_time - self.last_shot_effect) // 20 + flash_alpha = 255 - (current_time - self.last_shot_effect) * 2.5 + + # Créer une surface pour le flash avec transparence + flash_surface = pygame.Surface((flash_radius * 2, flash_radius * 2), pygame.SRCALPHA) + pygame.draw.circle(flash_surface, (255, 200, 100, flash_alpha), (flash_radius, flash_radius), flash_radius) + + # Dessiner le flash + flash_rect = flash_surface.get_rect(center=(flash_x, flash_y)) + screen.blit(flash_surface, flash_rect) + + # Dessin du joueur + screen.blit(rotated_image, rotated_rect) + + # Dessin du bouclier si actif + if self.shield_active: + shield_radius = PLAYER_SIZE + 10 # Bouclier légèrement plus grand que le joueur + pygame.draw.circle(screen, (0, 255, 255), self.rect.center, shield_radius, 2) + # Effet de lueur pour le bouclier + pygame.draw.circle(screen, (0, 200, 200, 50), self.rect.center, shield_radius - 2, 1) + + def take_damage(self, damage): + """Inflige des dégâts au joueur""" + if not self.shield_active: + self.health -= damage + return True + return False + + def gain_score(self, points): + """Augmente le score du joueur""" + self.score += points + + def reset_stats(self): + """Réinitialise les statistiques du joueur""" + self.speed = self.base_speed + self.shoot_cooldown = self.base_shoot_cooldown + self.shield_cooldown = self.base_shield_cooldown + self.max_health = self.base_health + self.health = self.max_health + self.bullet_damage = self.base_bullet_damage + self.health_regen = 0 + self.extra_bullets = 0 + + def play_sound(self, sound_type): + if hasattr(self, 'game'): + print(f"Game instance exists, sound_muted: {self.game.sound_muted}") + if not self.game.sound_muted: + if sound_type == 'shoot' and self.game.shoot_sound: + print("Playing shoot sound") + self.game.shoot_sound.play() + elif sound_type == 'hurt' and self.game.player_hurt_sound: + print("Playing hurt sound") + self.game.player_hurt_sound.play() + else: + print("No game instance found") + + def die(self): + # Logique pour la mort du joueur + if hasattr(self, 'game'): + self.game.game_over() \ No newline at end of file diff --git a/survival-shooter/src/game/weapon.py b/src/game/core/weapon.py similarity index 72% rename from survival-shooter/src/game/weapon.py rename to src/game/core/weapon.py index 9561091..ca735d2 100644 --- a/survival-shooter/src/game/weapon.py +++ b/src/game/core/weapon.py @@ -3,10 +3,14 @@ from utils.constants import * class Bullet(pygame.sprite.Sprite): - def __init__(self, x, y, direction, is_enemy=False): + def __init__(self, x, y, direction, damage=BULLET_DAMAGE, is_enemy=False): super().__init__() - self.direction = direction + # Convertir l'angle en vecteur de direction + self.direction_angle = direction + self.direction_x = math.cos(direction) + self.direction_y = math.sin(direction) self.speed = BULLET_SPEED + self.damage = damage self.is_enemy = is_enemy self.colors = BULLET_COLORS["ENEMY"] if is_enemy else BULLET_COLORS["PLAYER"] @@ -21,10 +25,17 @@ def __init__(self, x, y, direction, is_enemy=False): self.rect = self.image.get_rect(center=(x, y)) self.trail_positions = [] self.frame_count = 0 + + # Position précise pour éviter l'accumulation d'erreurs d'arrondi + self.x = float(x) + self.y = float(y) def update(self): - self.rect.x += self.direction[0] * self.speed - self.rect.y += self.direction[1] * self.speed + # Mise à jour de la position avec les coordonnées précises + self.x += self.direction_x * self.speed + self.y += self.direction_y * self.speed + self.rect.x = int(self.x) + self.rect.y = int(self.y) # Mise à jour de la traînée moins fréquente self.frame_count = (self.frame_count + 1) % TRAIL_UPDATE_FREQUENCY @@ -44,6 +55,11 @@ def draw(self, screen): screen.blit(self.image, self.rect) + def is_on_screen(self): + """Vérifie si la balle est toujours à l'écran""" + return (0 <= self.rect.x <= SCREEN_WIDTH and + 0 <= self.rect.y <= SCREEN_HEIGHT) + class Weapon: def __init__(self): self.bullets = pygame.sprite.Group() diff --git a/src/game/modes/__init__.py b/src/game/modes/__init__.py new file mode 100644 index 0000000..08c127d --- /dev/null +++ b/src/game/modes/__init__.py @@ -0,0 +1 @@ +# Package pour les différents modes de jeu \ No newline at end of file diff --git a/src/game/modes/boss_rush_mode.py b/src/game/modes/boss_rush_mode.py new file mode 100644 index 0000000..6628b1f --- /dev/null +++ b/src/game/modes/boss_rush_mode.py @@ -0,0 +1,227 @@ +import pygame +import random +from game.modes.game_mode_base import GameModeBase +from utils.constants import * +from game.core.enemy import Enemy, ShootingEnemy + +class BossRushMode(GameModeBase): + def __init__(self, game): + super().__init__(game, + id='boss_rush', + name='Mode Boss Rush', + description='Affrontez uniquement les boss les plus puissants') + self.boss = None + self.minions_count = 0 + self.boss_health = 0 + self.boss_max_health = 0 + self.bosses_killed = 0 + self.best_bosses_killed = 0 + + def initialize(self): + """Initialise le mode de jeu boss rush""" + self.wave = 1 + self.wave_enemies_left = 0 # Pas d'ennemis normaux, seulement des boss + self.wave_transition = False + self.wave_start_time = 0 + self.boss = None + self.minions_count = 0 + self.bosses_killed = 0 + self.best_bosses_killed = 0 + self.spawn_boss() + + def update(self): + """Met à jour la logique du mode boss rush""" + # Gestion de la transition de vague en priorité + if self.wave_transition: + current_time = pygame.time.get_ticks() + if current_time - self.wave_start_time > WAVE_TRANSITION_TIME: + self.wave_transition = False + self.spawn_boss() + print(f"Fin de la transition de vague {self.wave}, nouveau boss spawné") + return # Sortir de la fonction pendant la transition + + # Gérer les interactions normales hors transition + if self.boss and self.boss in self.game.enemies: + # Spawn des sbires si le boss est présent + if self.minions_count > 0: + self.spawn_minions() + # Vérifier si le boss est mort + elif self.boss and self.boss not in self.game.enemies: + self.boss = None + self.handle_wave_completion() + + self.update_wave() + + def spawn_boss(self): + """Spawn un boss""" + # Utiliser la configuration de la vague + wave_config = self.get_wave_config(self.wave) + self.boss_health = wave_config['boss_health'] + self.boss_max_health = self.boss_health + boss_speed = wave_config['boss_speed'] + + # Utiliser un ShootingEnemy comme boss pour l'instant + self.boss = ShootingEnemy(self.boss_health, boss_speed) + self.boss.size = PLAYER_SIZE * 2 # Boss plus grand + self.boss.color = (255, 0, 0) # Rouge vif + self.boss.shoot_cooldown = 500 # Tirs plus fréquents + self.boss.game = self.game + + # Positionner le boss en haut de l'écran + self.boss.x = SCREEN_WIDTH // 2 + self.boss.y = 100 + + self.game.enemies.add(self.boss) + + # Nombre de sbires à spawner + self.minions_count = wave_config['minions'] + + def spawn_minions(self): + """Spawn des sbires pour accompagner le boss""" + # Limiter le nombre d'ennemis actifs pour des performances optimales + if len(self.game.enemies) >= MAX_ACTIVE_ENEMIES: + return + + current_time = pygame.time.get_ticks() + if current_time - self.game.last_spawn >= self.game.spawn_cooldown and self.minions_count > 0: + # Sbires plus faibles que le boss + health = 50 + (self.wave * 10) + speed = 3 + (self.wave * 0.3) + + enemy = Enemy(health, speed) + enemy.game = self.game + + # Positionner le sbire près du boss + enemy.x = self.boss.x + random.randint(-200, 200) + enemy.y = self.boss.y + random.randint(50, 150) + + self.game.enemies.add(enemy) + self.minions_count -= 1 + self.game.last_spawn = current_time + + def update_wave(self): + """Gestion des vagues de boss""" + # Vérifier si la période de transition est terminée + if self.wave_transition: + current_time = pygame.time.get_ticks() + if current_time - self.wave_start_time > WAVE_TRANSITION_TIME: + self.wave_transition = False + self.spawn_boss() + print(f"Fin de la transition de vague {self.wave}, nouveau boss spawné") # Log pour debug + + def handle_wave_completion(self): + """Gestion de la fin d'une vague (boss vaincu)""" + self.wave_transition = True + self.wave_start_time = pygame.time.get_ticks() + self.wave += 1 + self.game.enemies_killed = 0 + self.bosses_killed += 1 + + # Mise à jour du record + if self.bosses_killed > self.best_bosses_killed: + self.best_bosses_killed = self.bosses_killed + # Jouer un son de réussite pour le nouveau record + if hasattr(self.game, 'achievement_sound') and self.game.achievement_sound and not self.game.sound_muted: + self.game.achievement_sound.play() + + # Récompenses importantes pour avoir vaincu un boss + coins_earned = self.wave * COINS_PER_BOSS + self.game.economy_manager.add_coins(coins_earned) + + # Notification + self.game.notification_manager.add_notification( + f'Boss vaincu ! ({self.bosses_killed} boss tués)', + f'+{coins_earned} pièces', + COIN_ICON + ) + + # Son de récompense + if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: + self.game.coin_sound.play() + + def handle_events(self, events): + """Gestion des événements spécifiques au mode boss rush""" + # Pas d'événements spécifiques pour le moment + pass + + def draw(self, screen): + """Affichage spécifique au mode boss rush""" + # Affichage du joueur + self.game.player.draw(screen) + + # Affichage des ennemis + for enemy in self.game.enemies: + enemy.draw(screen) + if isinstance(enemy, ShootingEnemy): + enemy.bullets.draw(screen) + + # Affichage de la barre de vie du boss + if self.boss and self.boss in self.game.enemies: + # Dimensions et position de la barre de vie + boss_health_bar_width = SCREEN_WIDTH * 0.5 + boss_health_bar_height = 25 + boss_health_bar_x = (SCREEN_WIDTH - boss_health_bar_width) // 2 + boss_health_bar_y = SCREEN_HEIGHT - 100 + + # Fond avec bordure + pygame.draw.rect(screen, (30, 0, 0), + (boss_health_bar_x - 4, boss_health_bar_y - 4, + boss_health_bar_width + 8, boss_health_bar_height + 8)) + + # Fond de la barre + pygame.draw.rect(screen, (70, 0, 0), + (boss_health_bar_x, boss_health_bar_y, + boss_health_bar_width, boss_health_bar_height)) + + # Barre de vie actuelle + health_ratio = max(0, self.boss.health / self.boss_max_health) + if health_ratio < 0.3: + bar_color = (255, 30, 30) + elif health_ratio < 0.6: + bar_color = (255, 120, 30) + else: + bar_color = (255, 180, 30) + + pygame.draw.rect(screen, bar_color, + (boss_health_bar_x, boss_health_bar_y, + boss_health_bar_width * health_ratio, boss_health_bar_height)) + + # Nom du boss + boss_font = pygame.font.Font(None, 36) + boss_name = f"BOSS ALIEN NIVEAU {self.wave}" + boss_text = boss_font.render(boss_name, True, (255, 200, 50)) + boss_text_rect = boss_text.get_rect(midbottom=(SCREEN_WIDTH // 2, boss_health_bar_y - 15)) + screen.blit(boss_text, boss_text_rect) + + # Effet de brillance + pygame.draw.rect(screen, (255, 150, 50), + (boss_health_bar_x, boss_health_bar_y, + boss_health_bar_width, 2)) + + # Affichage de la transition de vague + if self.wave_transition: + self.draw_wave_transition(screen) + + def spawn_enemies(self): + """Implémentation de la méthode abstraite spawn_enemies""" + # Dans ce mode, nous ne spawnons pas d'ennemis normaux + # mais plutôt un boss et ses sbires + if self.boss is None: + self.spawn_boss() + elif self.minions_count > 0: + self.spawn_minions() + + def get_wave_config(self, wave_number: int) -> dict: + """Retourne la configuration de la vague pour le mode boss rush""" + return { + 'boss': True, + 'boss_health': 1000 + wave_number * 500, + 'boss_speed': min(0.5 + wave_number * 0.1, 1.5), + 'minions': wave_number * 2 + } + + def get_record_text(self) -> str: + """Retourne le texte du record pour le mode boss rush""" + if self.best_bosses_killed > 0: + return f"Record: {self.best_bosses_killed} boss" + return "Pas encore de record" \ No newline at end of file diff --git a/src/game/modes/classic_mode.py b/src/game/modes/classic_mode.py new file mode 100644 index 0000000..e1c3813 --- /dev/null +++ b/src/game/modes/classic_mode.py @@ -0,0 +1,170 @@ +import pygame +import random +from game.modes.game_mode_base import GameModeBase +from utils.constants import * +from game.core.enemy import Enemy, ShootingEnemy + +class ClassicMode(GameModeBase): + def __init__(self, game): + super().__init__(game, + id='classic', + name='Mode Classique', + description='Survivez le plus longtemps possible') + self.survival_time = 0 + self.best_survival_time = 0 + self.start_time = pygame.time.get_ticks() + self.paused_time = 0 + self.is_paused = False + + def initialize(self): + """Initialise le mode de jeu classique""" + self.wave = 1 + self.wave_enemies_left = ENEMIES_PER_WAVE + self.wave_transition = False + self.wave_start_time = 0 + self.start_time = pygame.time.get_ticks() + self.survival_time = 0 + self.paused_time = 0 + self.is_paused = False + + def update(self): + """Met à jour la logique du mode classique""" + # Gestion de la transition de vague en priorité + if self.wave_transition: + current_time = pygame.time.get_ticks() + if current_time - self.wave_start_time > WAVE_TRANSITION_TIME: + self.wave_transition = False + # Calculer le nombre d'ennemis pour la nouvelle vague + self.wave_enemies_left = int(ENEMIES_PER_WAVE * (WAVE_ENEMY_MULTIPLIER ** (self.wave - 1))) + print(f"Fin de la transition de vague {self.wave}, nouveaux ennemis: {self.wave_enemies_left}") + # Pas de spawn/check pendant la transition + elif not self.wave_transition: + self.spawn_enemies() + self.check_wave_completion() + + # Mise à jour du temps de survie uniquement si le jeu est en cours + if self.game.game_state == PLAYING: + if self.is_paused: + # Ajuster le temps de départ pour compenser la pause + self.start_time = pygame.time.get_ticks() - (self.survival_time * 1000) + self.is_paused = False + + # Calculer le temps écoulé + self.survival_time = (pygame.time.get_ticks() - self.start_time) // 1000 + + # Mettre à jour le record si nécessaire + if self.survival_time > self.best_survival_time: + self.best_survival_time = self.survival_time + # Son de réussite pour le nouveau record + if hasattr(self.game, 'achievement_sound') and self.game.achievement_sound and not self.game.sound_muted: + self.game.achievement_sound.play() + + elif self.game.game_state == PAUSED and not self.is_paused: + self.is_paused = True + self.paused_time = pygame.time.get_ticks() + + def format_time(self, seconds): + """Formate le temps en minutes:secondes""" + minutes = seconds // 60 + seconds = seconds % 60 + return f"{minutes:02d}:{seconds:02d}" + + def spawn_enemies(self): + """Logique de spawn des ennemis pour le mode classique""" + # Limiter le nombre d'ennemis actifs pour des performances optimales + if len(self.game.enemies) >= MAX_ACTIVE_ENEMIES: + return + + current_time = pygame.time.get_ticks() + if current_time - self.game.last_spawn >= self.game.spawn_cooldown and self.wave_enemies_left > 0: + # Utiliser la configuration de la vague + wave_config = self.get_wave_config(self.wave) + health = wave_config['enemy_health'] + speed = wave_config['enemy_speed'] + + # Calcul du ratio d'ennemis tireurs vs normaux + normal_enemies = wave_config['normal_enemies'] + shooting_enemies = wave_config['shooting_enemies'] + total_enemies = normal_enemies + shooting_enemies + shooter_ratio = shooting_enemies / total_enemies if total_enemies > 0 else 0 + + # Création de l'ennemi + if random.random() < shooter_ratio: + enemy = ShootingEnemy(health * 1.5, speed * 0.8) + else: + enemy = Enemy(health, speed) + + enemy.game = self.game + self.game.enemies.add(enemy) + self.wave_enemies_left -= 1 + self.game.last_spawn = current_time + + def check_wave_completion(self): + """Vérifie si la vague actuelle est terminée""" + # Vérifier que tous les ennemis prévus sont apparus ET qu'il n'y en a plus sur le terrain + if self.wave_enemies_left <= 0 and len(self.game.enemies) == 0: + if not self.wave_transition: + self.handle_wave_completion() + return True + return False + + def handle_wave_completion(self): + """Gestion de la fin d'une vague""" + self.wave_transition = True + self.wave_start_time = pygame.time.get_ticks() + print(f"Début de la transition de vague {self.wave}") # Log pour debug + self.wave += 1 + self.game.enemies_killed = 0 + + # Le nombre d'ennemis est mis à zéro et sera recalculé à la fin de la transition + self.wave_enemies_left = 0 + self.game.spawn_cooldown = max(500, self.game.spawn_cooldown - WAVE_SPEEDUP) + + # Récompenses + coins_earned = self.wave * COINS_PER_WAVE + self.game.economy_manager.add_coins(coins_earned) + + # Notification + self.game.notification_manager.add_notification( + f'Vague {self.wave-1} terminée !', + f'+{coins_earned} pièces', + COIN_ICON + ) + + # Son + if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: + self.game.coin_sound.play() + + def handle_events(self, events): + """Gestion des événements spécifiques au mode classique""" + # Pas d'événements spécifiques pour le moment + pass + + def draw(self, screen): + """Affichage spécifique au mode classique""" + # Affichage du joueur et des ennemis + self.game.player.draw(screen) + for enemy in self.game.enemies: + enemy.draw(screen) + if isinstance(enemy, ShootingEnemy): + enemy.bullets.draw(screen) + + # Affichage de la transition de vague + if self.wave_transition: + self.draw_wave_transition(screen) + + def get_wave_config(self, wave_number: int) -> dict: + """Retourne la configuration de la vague pour le mode classique""" + return { + 'normal_enemies': wave_number * 2, + 'shooting_enemies': max(0, wave_number - 2), + 'enemy_speed': min(1 + wave_number * 0.1, 2.0), + 'enemy_health': 100 + wave_number * 10, + 'boss': False # Pas de boss en mode classique + } + + def get_record_text(self) -> str: + """Retourne le texte du record pour le mode classique""" + if self.best_survival_time > 0: + return f"Record: {self.format_time(self.best_survival_time)}" + return "Pas encore de record" \ No newline at end of file diff --git a/src/game/modes/game_mode_base.py b/src/game/modes/game_mode_base.py new file mode 100644 index 0000000..8c4629d --- /dev/null +++ b/src/game/modes/game_mode_base.py @@ -0,0 +1,75 @@ +import pygame +from abc import ABC, abstractmethod + +class GameModeBase(ABC): + def __init__(self, game, id='classic', name='Mode Classique', description='Mode de jeu classique'): + self.game = game # Référence à l'instance principale du jeu + self.id = id + self.name = name + self.description = description + self.high_score = 0 + self.wave = 1 + self.wave_enemies_left = 0 + self.wave_transition = False + self.wave_start_time = 0 + + @abstractmethod + def initialize(self): + """Initialise le mode de jeu""" + pass + + @abstractmethod + def update(self): + """Met à jour la logique du mode de jeu""" + pass + + @abstractmethod + def handle_events(self, events): + """Gère les événements spécifiques au mode""" + pass + + @abstractmethod + def draw(self, screen): + """Dessine les éléments spécifiques au mode""" + pass + + @abstractmethod + def spawn_enemies(self): + """Logique de spawn des ennemis spécifique au mode""" + pass + + @abstractmethod + def handle_wave_completion(self): + """Gestion de la fin d'une vague""" + pass + + def draw_wave_transition(self, screen): + """Affiche la transition entre les vagues""" + from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT, BG_COLOR, CYAN + + # Création d'une surface semi-transparente pour l'overlay + overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + overlay.fill(BG_COLOR) + overlay.set_alpha(128) + screen.blit(overlay, (0, 0)) + + # Affichage du texte de la vague + wave_text = self.game.big_font.render(f"VAGUE {self.wave}", True, CYAN) + wave_rect = wave_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) + screen.blit(wave_text, wave_rect) + + def get_record_text(self) -> str: + """Retourne le texte du record formaté pour l'affichage dans le menu""" + return "Pas de record" # Par défaut, à surcharger dans les modes spécifiques + + def format_time(self, seconds): + """Formate le temps en minutes:secondes""" + minutes = seconds // 60 + seconds = seconds % 60 + return f"{minutes:02d}:{seconds:02d}" + + def get_remaining_enemies(self): + """Retourne le nombre d'ennemis restants à spawner""" + if hasattr(self, 'wave_enemies_left'): + return max(0, self.wave_enemies_left) + return 0 \ No newline at end of file diff --git a/src/game/modes/game_modes.py b/src/game/modes/game_modes.py new file mode 100644 index 0000000..7a76115 --- /dev/null +++ b/src/game/modes/game_modes.py @@ -0,0 +1,153 @@ +from typing import Dict, List, Tuple +import random + +class GameMode: + def __init__(self, id: str, name: str, description: str): + self.id = id + self.name = name + self.description = description + self.is_unlocked = False + self.high_score = 0 + + def get_wave_config(self, wave_number: int) -> dict: + """Retourne la configuration de la vague""" + pass + + def get_record_text(self) -> str: + """Retourne le texte du record pour ce mode""" + pass + +class ClassicMode(GameMode): + def __init__(self): + super().__init__('classic', 'Mode Classique', 'Survivez le plus longtemps possible') + self.is_unlocked = True + self.best_survival_time = 0 + + def get_wave_config(self, wave_number: int) -> dict: + return { + 'normal_enemies': wave_number * 2, + 'shooting_enemies': max(0, wave_number - 2), + 'enemy_speed': min(1 + wave_number * 0.1, 2.0), + 'enemy_health': 100 + wave_number * 10 + } + + def get_record_text(self) -> str: + if self.best_survival_time > 0: + minutes = self.best_survival_time // 60 + seconds = self.best_survival_time % 60 + return f"Record: {minutes:02d}:{seconds:02d}" + return "Pas encore de record" + +class SurvivalMode(GameMode): + def __init__(self): + super().__init__('survival', 'Mode Survie', 'Affrontez des vagues infinies d\'ennemis de plus en plus difficiles') + self.is_unlocked = False + self.best_time = 0 + + def get_wave_config(self, wave_number: int) -> dict: + return { + 'normal_enemies': wave_number * 3, + 'shooting_enemies': wave_number, + 'enemy_speed': min(1 + wave_number * 0.15, 2.5), + 'enemy_health': 120 + wave_number * 15 + } + + def get_record_text(self) -> str: + if self.best_time > 0: + minutes = self.best_time // 60 + seconds = self.best_time % 60 + return f"Record: {minutes:02d}:{seconds:02d}" + return "Pas encore de record" + +class BossRushMode(GameMode): + def __init__(self): + super().__init__('boss_rush', 'Mode Boss Rush', 'Affrontez uniquement les boss les plus puissants') + self.is_unlocked = False + self.best_bosses_killed = 0 + + def get_wave_config(self, wave_number: int) -> dict: + return { + 'boss': True, + 'boss_health': 1000 + wave_number * 500, + 'boss_speed': min(0.5 + wave_number * 0.1, 1.5), + 'minions': wave_number * 2 + } + + def get_record_text(self) -> str: + if self.best_bosses_killed > 0: + return f"Record: {self.best_bosses_killed} boss" + return "Pas encore de record" + +class GameModeManager: + def __init__(self): + self.modes: Dict[str, GameMode] = { + 'classic': ClassicMode(), + 'survival': SurvivalMode(), + 'boss_rush': BossRushMode() + } + self.selected_mode = 'classic' + + def get_mode_info(self, mode_id: str) -> dict: + """Retourne les informations d'un mode, y compris son record""" + mode = self.modes.get(mode_id) + if mode: + return { + 'id': mode.id, + 'name': mode.name, + 'description': mode.description, + 'is_unlocked': mode.is_unlocked, + 'record': mode.get_record_text() + } + return None + + def unlock_mode(self, mode_id: str) -> bool: + if mode_id in self.modes and not self.modes[mode_id].is_unlocked: + self.modes[mode_id].is_unlocked = True + return True + return False + + def select_mode(self, mode_id: str) -> bool: + if mode_id in self.modes and self.modes[mode_id].is_unlocked: + self.selected_mode = mode_id + return True + return False + + def get_selected_mode(self) -> GameMode: + return self.modes[self.selected_mode] + + def update_high_score(self, mode_id: str, score: int): + if mode_id in self.modes: + self.modes[mode_id].high_score = max(self.modes[mode_id].high_score, score) + + def save_state(self) -> dict: + return { + 'selected': self.selected_mode, + 'unlocked': [id for id, mode in self.modes.items() if mode.is_unlocked], + 'high_scores': {id: mode.high_score for id, mode in self.modes.items()}, + 'records': { + 'classic': self.modes['classic'].best_survival_time if hasattr(self.modes['classic'], 'best_survival_time') else 0, + 'survival': self.modes['survival'].best_time if hasattr(self.modes['survival'], 'best_time') else 0, + 'boss_rush': self.modes['boss_rush'].best_bosses_killed if hasattr(self.modes['boss_rush'], 'best_bosses_killed') else 0 + } + } + + def load_state(self, state: dict): + if 'selected' in state and state['selected'] in self.modes: + self.selected_mode = state['selected'] + + for mode_id in state.get('unlocked', []): + if mode_id in self.modes: + self.modes[mode_id].is_unlocked = True + + for mode_id, score in state.get('high_scores', {}).items(): + if mode_id in self.modes: + self.modes[mode_id].high_score = score + + # Charger les records spécifiques à chaque mode + records = state.get('records', {}) + if 'classic' in records and hasattr(self.modes['classic'], 'best_survival_time'): + self.modes['classic'].best_survival_time = records['classic'] + if 'survival' in records and hasattr(self.modes['survival'], 'best_time'): + self.modes['survival'].best_time = records['survival'] + if 'boss_rush' in records and hasattr(self.modes['boss_rush'], 'best_bosses_killed'): + self.modes['boss_rush'].best_bosses_killed = records['boss_rush'] \ No newline at end of file diff --git a/src/game/modes/survival_mode.py b/src/game/modes/survival_mode.py new file mode 100644 index 0000000..a9d1b98 --- /dev/null +++ b/src/game/modes/survival_mode.py @@ -0,0 +1,150 @@ +import pygame +import random +import time +from game.modes.game_mode_base import GameModeBase +from utils.constants import * +from game.core.enemy import Enemy, ShootingEnemy + +class SurvivalMode(GameModeBase): + def __init__(self, game): + super().__init__(game, + id='survival', + name='Mode Survie', + description='Affrontez des vagues infinies d\'ennemis de plus en plus difficiles') + self.wave = 1 + self.best_wave = 0 # Record du nombre de vagues survécues + self.wave_enemies_left = 15 + self.wave_transition = False + self.wave_start_time = 0 + + def initialize(self): + """Initialise le mode de jeu survie""" + self.wave = 1 + self.wave_enemies_left = 15 # Plus d'ennemis que le mode classique + self.wave_transition = False + self.wave_start_time = 0 + + def update(self): + """Met à jour la logique du mode survie""" + # Mise à jour de la transition de vague en priorité + if self.wave_transition: + current_time = pygame.time.get_ticks() + if current_time - self.wave_start_time > WAVE_TRANSITION_TIME: + self.wave_transition = False + self.wave_enemies_left = int(15 * (1.7 ** (self.wave - 1))) + print(f"Fin de la transition de vague {self.wave}, nouveaux ennemis: {self.wave_enemies_left}") + # Pas de spawn d'ennemis pendant la transition + elif not self.wave_transition: + self.spawn_enemies() + self.check_wave_completion() + + def check_wave_completion(self): + """Vérifie si la vague actuelle est terminée""" + # Vérifier que tous les ennemis prévus sont apparus ET qu'il n'y en a plus sur le terrain + if self.wave_enemies_left <= 0 and len(self.game.enemies) == 0: + if not self.wave_transition: + self.handle_wave_completion() + return True + return False + + def spawn_enemies(self): + """Logique de spawn des ennemis pour le mode survie""" + # Limiter le nombre d'ennemis actifs pour des performances optimales + if len(self.game.enemies) >= MAX_ACTIVE_ENEMIES: + return + + current_time = pygame.time.get_ticks() + if current_time - self.game.last_spawn >= self.game.spawn_cooldown and self.wave_enemies_left > 0: + # Utiliser la configuration de la vague + wave_config = self.get_wave_config(self.wave) + health = wave_config['enemy_health'] + speed = wave_config['enemy_speed'] + + # Calcul du ratio d'ennemis tireurs vs normaux + normal_enemies = wave_config['normal_enemies'] + shooting_enemies = wave_config['shooting_enemies'] + total_enemies = normal_enemies + shooting_enemies + shooter_ratio = shooting_enemies / total_enemies if total_enemies > 0 else 0 + + # Création de l'ennemi + if random.random() < shooter_ratio: + enemy = ShootingEnemy(health * 1.5, speed * 0.8) + else: + enemy = Enemy(health, speed) + + enemy.game = self.game + self.game.enemies.add(enemy) + self.wave_enemies_left -= 1 + self.game.last_spawn = current_time + + def handle_wave_completion(self): + """Gestion de la fin d'une vague""" + self.wave_transition = True + self.wave_start_time = pygame.time.get_ticks() + print(f"Début de la transition de vague {self.wave} à {self.wave_start_time}") # Log pour debug + self.wave += 1 + self.game.enemies_killed = 0 + + # Mise à jour du record si nécessaire + if self.wave > self.best_wave: + self.best_wave = self.wave + # Son de réussite pour le nouveau record + if hasattr(self.game, 'achievement_sound') and self.game.achievement_sound and not self.game.sound_muted: + self.game.achievement_sound.play() + + # Le nombre d'ennemis est mis à zéro et sera recalculé à la fin de la transition + self.wave_enemies_left = 0 + + # Spawn plus rapide + self.game.spawn_cooldown = max(300, self.game.spawn_cooldown - WAVE_SPEEDUP * 1.5) + + # Récompenses plus importantes + coins_earned = self.wave * COINS_PER_WAVE * 1.5 + self.game.economy_manager.add_coins(int(coins_earned)) + + # Notification + self.game.notification_manager.add_notification( + f'Vague {self.wave-1} terminée !', + f'+{int(coins_earned)} pièces', + COIN_ICON + ) + + # Son + if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: + self.game.coin_sound.play() + + def handle_events(self, events): + """Gestion des événements spécifiques au mode survie""" + # Pas d'événements spécifiques pour le moment + pass + + def draw(self, screen): + """Affichage spécifique au mode survie""" + # Affichage du joueur + self.game.player.draw(screen) + + # Affichage des ennemis + for enemy in self.game.enemies: + enemy.draw(screen) + if isinstance(enemy, ShootingEnemy): + enemy.bullets.draw(screen) + + # Affichage de la transition de vague + if self.wave_transition: + self.draw_wave_transition(screen) + + def get_wave_config(self, wave_number: int) -> dict: + """Retourne la configuration de la vague pour le mode survie""" + return { + 'normal_enemies': wave_number * 3, + 'shooting_enemies': wave_number, + 'enemy_speed': min(1 + wave_number * 0.15, 2.5), + 'enemy_health': 120 + wave_number * 15, + 'boss': False # Pas de boss en mode survie + } + + def get_record_text(self) -> str: + """Retourne le texte du record pour le mode survie""" + if self.best_wave > 0: + return f"Record: Vague {self.best_wave}" + return "Pas encore de record" \ No newline at end of file diff --git a/src/game/modules/__init__.py b/src/game/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/game/modules/module_manager.py b/src/game/modules/module_manager.py new file mode 100644 index 0000000..dd823e6 --- /dev/null +++ b/src/game/modules/module_manager.py @@ -0,0 +1,200 @@ +import random + +class Module: + def __init__(self, id, name, description, max_level, cost): + """Initialise un module""" + self.id = id + self.name = name + self.description = description + self.max_level = max_level + self.cost = cost + self.level = 0 + self.is_unlocked = False + + def get_level_description(self): + """Retourne la description du niveau actuel""" + if not self.is_unlocked: + return f'Non débloqué - Coût: {self.cost} pièces' + return f'Niveau {self.level}/{self.max_level}' + + def get_effect_description(self): + """Retourne la description de l'effet selon le niveau""" + if not self.is_unlocked: + return 'Aucun effet' + + if self.id == 'rapid_fire': + return f'Vitesse de tir +{self.level * 20}%' + elif self.id == 'shield_boost': + return f'Cooldown du bouclier -{self.level * 15}%' + elif self.id == 'damage_boost': + return f'Dégâts +{self.level * 25}%' + elif self.id == 'speed_boost': + return f'Vitesse +{self.level * 15}%' + elif self.id == 'health_regen': + return f'Régénération de {self.level} PV/s' + elif self.id == 'multi_shot': + return f'{self.level + 1} projectiles' + + return 'Effet inconnu' + +class ModuleManager: + def __init__(self, game): + """Initialise le gestionnaire de modules""" + self.game = game + self.modules = {} # Modules permanents (achetés) + self.temp_modules = {} # Modules temporaires (coffres) + self.initialize_modules() + + def initialize_modules(self): + """Initialise la liste des modules disponibles""" + module_data = { + 'rapid_fire': { + 'name': 'Module de tir rapide', + 'description': 'Augmente la vitesse de tir', + 'max_level': 3, + 'cost': 100 + }, + 'shield_boost': { + 'name': 'Module de bouclier amélioré', + 'description': 'Réduit le temps de recharge du bouclier', + 'max_level': 3, + 'cost': 150 + }, + 'damage_boost': { + 'name': 'Module de puissance', + 'description': 'Augmente les dégâts infligés', + 'max_level': 3, + 'cost': 200 + }, + 'speed_boost': { + 'name': 'Module de vitesse', + 'description': 'Augmente la vitesse de déplacement', + 'max_level': 3, + 'cost': 100 + }, + 'health_regen': { + 'name': 'Module de régénération', + 'description': 'Régénère lentement la santé', + 'max_level': 3, + 'cost': 250 + }, + 'multi_shot': { + 'name': 'Module multi-tir', + 'description': 'Tire plusieurs projectiles à la fois', + 'max_level': 3, + 'cost': 300 + } + } + + # Création des modules permanents et temporaires + for module_id, data in module_data.items(): + # Module permanent (pour l'achat) + self.modules[module_id] = Module( + module_id, + data['name'], + data['description'], + data['max_level'], + data['cost'] + ) + + # Module temporaire (pour les coffres) + self.temp_modules[module_id] = Module( + module_id, + data['name'], + data['description'], + data['max_level'], + 0 # Pas de coût pour les modules temporaires + ) + + def reset_temp_modules(self): + """Réinitialise tous les modules temporaires""" + for module in self.temp_modules.values(): + module.is_unlocked = False + module.level = 0 + + def get_random_temp_modules(self, count=3): + """Retourne une liste aléatoire de modules temporaires disponibles""" + available_modules = [ + module for module in self.temp_modules.values() + if module.level < module.max_level + ] + return random.sample(available_modules, min(count, len(available_modules))) + + def upgrade_temp_module(self, module_id): + """Améliore un module temporaire""" + if module_id not in self.temp_modules: + return False + + module = self.temp_modules[module_id] + if module.level >= module.max_level: + return False + + if not module.is_unlocked: + module.is_unlocked = True + module.level = 1 + else: + module.level += 1 + + # Son et notification + if hasattr(self.game, 'achievement_sound') and self.game.achievement_sound and not self.game.sound_muted: + self.game.achievement_sound.play() + + self.game.notification_manager.add_notification( + 'Module amélioré !', + f'{module.name} niveau {module.level}', + None + ) + return True + + def apply_module_effects(self, player): + """Applique les effets des modules au joueur""" + # Réinitialisation des stats + player.reset_stats() + + # Application des effets des modules permanents et temporaires + for modules in [self.modules, self.temp_modules]: + for module in modules.values(): + if not module.is_unlocked: + continue + + if module.id == 'rapid_fire': + player.shoot_cooldown = int(player.base_shoot_cooldown * (1 - module.level * 0.2)) + elif module.id == 'shield_boost': + player.shield_cooldown = int(player.base_shield_cooldown * (1 - module.level * 0.15)) + elif module.id == 'damage_boost': + player.damage_multiplier = 1 + module.level * 0.25 + elif module.id == 'speed_boost': + player.speed = player.base_speed * (1 + module.level * 0.15) + elif module.id == 'health_regen': + player.health_regen = module.level + elif module.id == 'multi_shot': + player.multi_shot = module.level + 1 + + def unlock_module(self, module_id): + """Débloque un module permanent""" + if module_id not in self.modules: + return False + + module = self.modules[module_id] + if module.is_unlocked: + return False + + module.is_unlocked = True + module.level = 1 + + # Notification et son déjà gérés dans ModuleMenu.handle_click + return True + + def upgrade_module(self, module_id): + """Améliore un module permanent""" + if module_id not in self.modules: + return False + + module = self.modules[module_id] + if not module.is_unlocked or module.level >= module.max_level: + return False + + module.level += 1 + + # Notification et son déjà gérés dans ModuleMenu.handle_click + return True \ No newline at end of file diff --git a/src/game/modules/module_menu.py b/src/game/modules/module_menu.py new file mode 100644 index 0000000..b29725d --- /dev/null +++ b/src/game/modules/module_menu.py @@ -0,0 +1,237 @@ +import pygame +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT + +class ModuleMenu: + def __init__(self, game): + """Initialise le menu des modules""" + self.game = game + self.font = pygame.font.Font(None, 36) + self.small_font = pygame.font.Font(None, 24) + + # Dimensions pour l'affichage des modules + self.module_width = 200 + self.module_height = 150 + self.padding = 20 + self.modules_per_row = 3 + + # Couleurs + self.bg_color = (50, 50, 50) + self.text_color = (255, 255, 255) + self.locked_color = (100, 100, 100) + self.hover_color = (70, 70, 70) + self.selected_color = (0, 255, 0) + + # État + self.scroll_offset = 0 + self.selected_module = None + self.hovered_module = None + + def handle_event(self, event): + """Gère les événements du menu""" + if event.type == pygame.MOUSEBUTTONDOWN: + # Gestion du scroll + if event.button == 4: # Scroll vers le haut + self.scroll_offset = max(0, self.scroll_offset - 50) + elif event.button == 5: # Scroll vers le bas + self.scroll_offset = min( + self.get_max_scroll(), + self.scroll_offset + 50 + ) + # Clic sur un module + elif event.button == 1: # Clic gauche + if self.hovered_module: + if not self.hovered_module.is_unlocked: + self.game.module_manager.unlock_module(self.hovered_module.id) + else: + self.game.module_manager.upgrade_module(self.hovered_module.id) + + # Mise à jour du module survolé + mouse_pos = pygame.mouse.get_pos() + self.update_hovered_module(mouse_pos) + + def update_hovered_module(self, mouse_pos): + """Met à jour le module survolé""" + x, y = mouse_pos + + # Position de départ pour l'affichage des modules + start_x = (SCREEN_WIDTH - (self.module_width + self.padding) * self.modules_per_row + self.padding) // 2 + start_y = 150 - self.scroll_offset + + self.hovered_module = None + + # Parcours des modules + row = 0 + col = 0 + for module in self.game.module_manager.modules.values(): + # Calcul de la position du module + module_x = start_x + col * (self.module_width + self.padding) + module_y = start_y + row * (self.module_height + self.padding) + + # Vérification si la souris est sur le module + if (module_x <= x <= module_x + self.module_width and + module_y <= y <= module_y + self.module_height): + self.hovered_module = module + break + + # Passage à la ligne suivante si nécessaire + col += 1 + if col >= self.modules_per_row: + col = 0 + row += 1 + + def get_max_scroll(self): + """Retourne le scroll maximum possible""" + num_modules = len(self.game.module_manager.modules) + num_rows = (num_modules + self.modules_per_row - 1) // self.modules_per_row + total_height = num_rows * (self.module_height + self.padding) + visible_height = SCREEN_HEIGHT - 200 # Espace disponible pour les modules + return max(0, total_height - visible_height) + + def draw(self, screen): + """Dessine le menu des modules""" + # Fond semi-transparent + s = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + s.set_alpha(128) + s.fill((0, 0, 0)) + screen.blit(s, (0, 0)) + + # Titre + title = self.font.render('MODULES XENOTECH', True, self.text_color) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, 50)) + screen.blit(title, title_rect) + + # Affichage des pièces + coins_text = self.font.render(f'Pièces: {self.game.economy_manager.coins}', True, (255, 255, 0)) + coins_rect = coins_text.get_rect(center=(SCREEN_WIDTH // 2, 100)) + screen.blit(coins_text, coins_rect) + + # Position de départ pour l'affichage des modules + start_x = (SCREEN_WIDTH - (self.module_width + self.padding) * self.modules_per_row + self.padding) // 2 + start_y = 150 - self.scroll_offset + + # Affichage des modules + row = 0 + col = 0 + for module in self.game.module_manager.modules.values(): + # Calcul de la position du module + x = start_x + col * (self.module_width + self.padding) + y = start_y + row * (self.module_height + self.padding) + + # Vérification si le module est visible + if y + self.module_height > 0 and y < SCREEN_HEIGHT: + # Couleur de fond selon l'état + if module == self.hovered_module: + color = self.hover_color + elif module == self.selected_module: + color = self.selected_color + else: + color = self.locked_color if not module.is_unlocked else self.bg_color + + # Dessin du fond + pygame.draw.rect(screen, color, (x, y, self.module_width, self.module_height)) + pygame.draw.rect(screen, self.text_color, (x, y, self.module_width, self.module_height), 2) + + # Nom du module + name = self.font.render(module.name, True, self.text_color) + name_rect = name.get_rect(center=(x + self.module_width // 2, y + 30)) + screen.blit(name, name_rect) + + # Description + desc_lines = self.wrap_text(module.description, self.module_width - 20) + for i, line in enumerate(desc_lines): + desc = self.small_font.render(line, True, self.text_color) + desc_rect = desc.get_rect(center=(x + self.module_width // 2, y + 60 + i * 20)) + screen.blit(desc, desc_rect) + + # Niveau et effet + level = self.small_font.render(module.get_level_description(), True, self.text_color) + level_rect = level.get_rect(center=(x + self.module_width // 2, y + 100)) + screen.blit(level, level_rect) + + effect = self.small_font.render(module.get_effect_description(), True, self.text_color) + effect_rect = effect.get_rect(center=(x + self.module_width // 2, y + 120)) + screen.blit(effect, effect_rect) + + # Passage à la ligne suivante si nécessaire + col += 1 + if col >= self.modules_per_row: + col = 0 + row += 1 + + def wrap_text(self, text, width): + """Découpe le texte en lignes selon la largeur donnée""" + words = text.split() + lines = [] + current_line = [] + current_width = 0 + + for word in words: + word_surface = self.small_font.render(word + ' ', True, self.text_color) + word_width = word_surface.get_width() + + if current_width + word_width > width: + lines.append(' '.join(current_line)) + current_line = [word] + current_width = word_width + else: + current_line.append(word) + current_width += word_width + + if current_line: + lines.append(' '.join(current_line)) + + return lines + + def handle_click(self, mouse_pos): + """Gère les clics dans le menu des modules""" + # Met à jour le module survolé + self.update_hovered_module(mouse_pos) + + # Si un module est survolé, on tente de l'acheter ou de l'améliorer + if self.hovered_module: + if not self.hovered_module.is_unlocked: + if self.game.economy_manager.coins >= self.hovered_module.cost: + self.game.economy_manager.remove_coins(self.hovered_module.cost) + self.game.module_manager.unlock_module(self.hovered_module.id) + # Son et notification + try: + if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: + self.game.coin_sound.play() + except Exception as e: + print(f'Erreur lors de la lecture du son: {e}') + + self.game.notification_manager.add_notification( + 'Module débloqué !', + f'{self.hovered_module.name}', + None # Icône à ajouter plus tard + ) + else: + self.game.notification_manager.add_notification( + 'Pas assez de pièces !', + f'Il vous manque {self.hovered_module.cost - self.game.economy_manager.coins} pièces', + None # Icône à ajouter plus tard + ) + elif self.hovered_module.level < self.hovered_module.max_level: + upgrade_cost = self.hovered_module.cost * (self.hovered_module.level + 1) + if self.game.economy_manager.coins >= upgrade_cost: + self.game.economy_manager.remove_coins(upgrade_cost) + self.game.module_manager.upgrade_module(self.hovered_module.id) + # Son et notification + try: + if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: + self.game.coin_sound.play() + except Exception as e: + print(f'Erreur lors de la lecture du son: {e}') + + self.game.notification_manager.add_notification( + 'Module amélioré !', + f'{self.hovered_module.name} niveau {self.hovered_module.level}', + None # Icône à ajouter plus tard + ) + else: + self.game.notification_manager.add_notification( + 'Pas assez de pièces !', + f'Il vous manque {upgrade_cost - self.game.economy_manager.coins} pièces', + None # Icône à ajouter plus tard + ) + return True \ No newline at end of file diff --git a/src/game/modules/module_selection_menu.py b/src/game/modules/module_selection_menu.py new file mode 100644 index 0000000..f6ced31 --- /dev/null +++ b/src/game/modules/module_selection_menu.py @@ -0,0 +1,157 @@ +import pygame +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT, PAUSED, PLAYING + +class ModuleSelectionMenu: + def __init__(self, game): + """Initialise le menu de sélection des modules""" + self.game = game + self.font = pygame.font.Font(None, 36) + self.small_font = pygame.font.Font(None, 24) + + # Dimensions pour l'affichage des modules + self.module_width = 200 + self.module_height = 150 + self.padding = 20 + + # Couleurs + self.bg_color = (50, 50, 50) + self.text_color = (255, 255, 255) + self.hover_color = (70, 70, 70) + self.selected_color = (0, 255, 0) + + # État + self.visible = False + self.modules = [] + self.hovered_module = None + + def show(self, modules): + """Affiche le menu avec les modules donnés""" + self.visible = True + self.modules = modules + self.hovered_module = None + # On ne met en pause que si le jeu est déjà en cours + if self.game.game_state == PLAYING: + self.game.game_state = PAUSED + + def hide(self): + """Cache le menu""" + self.visible = False + self.modules = [] + self.hovered_module = None + self.game.game_state = PLAYING # Reprend le jeu + + def handle_event(self, event): + """Gère les événements du menu""" + if not self.visible: + return + + if event.type == pygame.MOUSEBUTTONDOWN: + if event.button == 1: # Clic gauche + if self.hovered_module: + # Amélioration du module temporaire sélectionné + self.game.module_manager.upgrade_temp_module(self.hovered_module.id) + self.hide() + self.game.game_state = PLAYING # Reprendre le jeu + self.game.show_module_menu = False + + # Mise à jour du module survolé + mouse_pos = pygame.mouse.get_pos() + self.update_hovered_module(mouse_pos) + + def update_hovered_module(self, mouse_pos): + """Met à jour le module survolé""" + x, y = mouse_pos + + # Position de départ pour l'affichage des modules + start_x = (SCREEN_WIDTH - (self.module_width + self.padding) * len(self.modules) + self.padding) // 2 + start_y = (SCREEN_HEIGHT - self.module_height) // 2 + + self.hovered_module = None + + # Parcours des modules + for i, module in enumerate(self.modules): + # Calcul de la position du module + module_x = start_x + i * (self.module_width + self.padding) + + # Vérification si la souris est sur le module + if (module_x <= x <= module_x + self.module_width and + start_y <= y <= start_y + self.module_height): + self.hovered_module = module + break + + def draw(self, screen): + """Dessine le menu de sélection""" + if not self.visible: + return + + # Fond semi-transparent + s = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + s.set_alpha(128) + s.fill((0, 0, 0)) + screen.blit(s, (0, 0)) + + # Titre + title = self.font.render('CHOISISSEZ UN MODULE', True, self.text_color) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)) + screen.blit(title, title_rect) + + # Position de départ pour l'affichage des modules + start_x = (SCREEN_WIDTH - (self.module_width + self.padding) * len(self.modules) + self.padding) // 2 + start_y = (SCREEN_HEIGHT - self.module_height) // 2 + + # Affichage des modules + for i, module in enumerate(self.modules): + x = start_x + i * (self.module_width + self.padding) + y = start_y + + # Couleur de fond selon l'état + color = self.hover_color if module == self.hovered_module else self.bg_color + + # Dessin du fond + pygame.draw.rect(screen, color, (x, y, self.module_width, self.module_height)) + pygame.draw.rect(screen, self.text_color, (x, y, self.module_width, self.module_height), 2) + + # Nom du module + name = self.font.render(module.name, True, self.text_color) + name_rect = name.get_rect(center=(x + self.module_width // 2, y + 30)) + screen.blit(name, name_rect) + + # Description + desc_lines = self.wrap_text(module.description, self.module_width - 20) + for j, line in enumerate(desc_lines): + desc = self.small_font.render(line, True, self.text_color) + desc_rect = desc.get_rect(center=(x + self.module_width // 2, y + 60 + j * 20)) + screen.blit(desc, desc_rect) + + # Niveau et effet + level = self.small_font.render(module.get_level_description(), True, self.text_color) + level_rect = level.get_rect(center=(x + self.module_width // 2, y + 100)) + screen.blit(level, level_rect) + + effect = self.small_font.render(module.get_effect_description(), True, self.text_color) + effect_rect = effect.get_rect(center=(x + self.module_width // 2, y + 120)) + screen.blit(effect, effect_rect) + + def wrap_text(self, text, width): + """Découpe le texte en lignes selon la largeur donnée""" + words = text.split() + lines = [] + current_line = [] + current_width = 0 + + for word in words: + word_surface = self.small_font.render(word + ' ', True, self.text_color) + word_width = word_surface.get_width() + + if current_width + word_width > width: + lines.append(' '.join(current_line)) + current_line = [word] + current_width = word_width + else: + current_line.append(word) + current_width += word_width + + if current_line: + lines.append(' '.join(current_line)) + + return lines \ No newline at end of file diff --git a/src/game/modules/modules.py b/src/game/modules/modules.py new file mode 100644 index 0000000..4e67598 --- /dev/null +++ b/src/game/modules/modules.py @@ -0,0 +1,179 @@ +import json +import random +from typing import Dict, List, Optional + +class Module: + def __init__(self, id: str, name: str, description: str, max_level: int, cost: int): + self.id = id + self.name = name + self.description = description + self.max_level = max_level + self.cost = cost + self.level = 0 + self.is_unlocked = False + + def get_level_description(self) -> str: + # Retourne la description du niveau actuel du module + if not self.is_unlocked: + return f'Coût: {self.cost} pièces' + return f'Niveau {self.level}/{self.max_level}' + + def get_effect_description(self) -> str: + # Retourne la description de l'effet du module au niveau actuel + if not self.is_unlocked: + return 'Module verrouillé' + + base_effects = { + 'rapid_fire': lambda level: f'Vitesse de tir +{level * 10}%', + 'shield_boost': lambda level: f'Temps de recharge du bouclier -{level * 10}%', + 'damage_boost': lambda level: f'Dégâts +{level * 15}%', + 'speed_boost': lambda level: f'Vitesse de déplacement +{level * 10}%', + 'health_regen': lambda level: f'Régénération de vie +{level} PV/s', + 'multi_shot': lambda level: f'+{level} projectile(s) supplémentaire(s)' + } + + if self.id in base_effects: + return base_effects[self.id](self.level) + return 'Effet inconnu' + +class ModuleManager: + def __init__(self, game): + self.game = game + self.modules: Dict[str, Module] = {} + self.initialize_modules() + self.load_state() + + def initialize_modules(self): + # Initialise les modules disponibles dans le jeu + modules_data = { + 'rapid_fire': { + 'name': 'Module de tir rapide', + 'description': 'Augmente la vitesse de tir', + 'max_level': 5, + 'cost': 100 + }, + 'shield_boost': { + 'name': 'Module de bouclier amélioré', + 'description': 'Réduit le temps de recharge du bouclier', + 'max_level': 3, + 'cost': 150 + }, + 'damage_boost': { + 'name': 'Module de puissance', + 'description': 'Augmente les dégâts infligés', + 'max_level': 4, + 'cost': 200 + }, + 'speed_boost': { + 'name': 'Module de vitesse', + 'description': 'Augmente la vitesse de déplacement', + 'max_level': 3, + 'cost': 120 + }, + 'health_regen': { + 'name': 'Module de régénération', + 'description': 'Régénère lentement la vie', + 'max_level': 3, + 'cost': 250 + }, + 'multi_shot': { + 'name': 'Module multi-tir', + 'description': 'Ajoute des projectiles supplémentaires', + 'max_level': 2, + 'cost': 300 + } + } + + for module_id, data in modules_data.items(): + self.modules[module_id] = Module( + module_id, + data['name'], + data['description'], + data['max_level'], + data['cost'] + ) + + def unlock_module(self, module_id: str) -> bool: + # Déverrouille un module (à appeler après vérification du coût) + if module_id not in self.modules: + return False + + module = self.modules[module_id] + if module.is_unlocked: + return False + + module.is_unlocked = True + module.level = 1 + self.save_state() + return True + + def upgrade_module(self, module_id: str) -> bool: + # Améliore un module déjà déverrouillé + if module_id not in self.modules: + return False + + module = self.modules[module_id] + if not module.is_unlocked or module.level >= module.max_level: + return False + + module.level += 1 + self.save_state() + return True + + def get_random_modules(self, count: int = 3) -> List[Module]: + # Retourne une liste aléatoire de modules déverrouillés + unlocked_modules = [m for m in self.modules.values() if m.is_unlocked] + if not unlocked_modules: + return [] + return random.sample(unlocked_modules, min(count, len(unlocked_modules))) + + def apply_module_effects(self, player) -> None: + """Applique les effets des modules au joueur""" + for module_id, module in self.modules.items(): + if not module.is_unlocked: + continue + + if module_id == 'rapid_fire': + player.shoot_cooldown *= (1 - module.level * 0.1) + elif module_id == 'shield_boost': + player.shield_cooldown *= (1 - module.level * 0.1) + elif module_id == 'damage_boost': + player.bullet_damage *= (1 + module.level * 0.15) + elif module_id == 'speed_boost': + player.speed *= (1 + module.level * 0.1) + elif module_id == 'health_regen': + player.health_regen = module.level + elif module_id == 'multi_shot': + player.extra_bullets = module.level + + def save_state(self) -> None: + # Sauvegarde l'état des modules + state = { + module_id: { + 'is_unlocked': module.is_unlocked, + 'level': module.level + } + for module_id, module in self.modules.items() + } + + try: + with open('src/save/modules.json', 'w') as f: + json.dump(state, f) + except Exception as e: + print(f'Erreur lors de la sauvegarde des modules: {e}') + + def load_state(self) -> None: + # Charge l'état des modules + try: + with open('src/save/modules.json', 'r') as f: + state = json.load(f) + + for module_id, data in state.items(): + if module_id in self.modules: + module = self.modules[module_id] + module.is_unlocked = data['is_unlocked'] + module.level = data['level'] + except FileNotFoundError: + pass # Pas de fichier de sauvegarde, on garde l'état par défaut + except Exception as e: + print(f'Erreur lors du chargement des modules: {e}') \ No newline at end of file diff --git a/src/game/systems/__init__.py b/src/game/systems/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/game/systems/economy.py b/src/game/systems/economy.py new file mode 100644 index 0000000..496a067 --- /dev/null +++ b/src/game/systems/economy.py @@ -0,0 +1,110 @@ +import json +import os +from typing import Dict, List + +class Achievement: + def __init__(self, name: str, description: str): + self.name = name + self.description = description + self.is_unlocked = False + self.progress = 0 + self.max_progress = 100 + + def unlock(self): + """Débloque l'achievement""" + self.is_unlocked = True + self.progress = self.max_progress + + def update_progress(self, progress: int) -> bool: + """Met à jour la progression""" + self.progress = min(self.max_progress, progress) + if self.progress >= self.max_progress: + self.is_unlocked = True + return True + return False + + def is_complete(self) -> bool: + """Vérifie si l'achievement est complété""" + return self.progress >= self.max_progress + +class EconomyManager: + def __init__(self, game): + """Initialise le gestionnaire d'économie""" + self.game = game + self.coins = 0 + self.achievements = { + 'first_kill': Achievement('Premier Sang', 'Tuez votre premier ennemi'), + 'survivor': Achievement('Survivant', 'Survivez pendant 5 minutes'), + 'rich': Achievement('Riche', 'Accumulez 1000 pièces'), + 'collector': Achievement('Collectionneur', 'Débloquez tous les personnages'), + 'master': Achievement('Maître', 'Atteignez le niveau 10') + } + self.save_file = 'save_data.json' + self.load_save() + + def add_coins(self, amount: int): + """Ajoute des pièces au joueur""" + self.coins += amount + self.check_achievement_progress('rich', self.coins) + self.save_game() + + def remove_coins(self, amount: int) -> bool: + """Retire des pièces au joueur""" + if self.coins >= amount: + self.coins -= amount + self.save_game() + return True + return False + + def unlock_achievement(self, achievement_id: str) -> bool: + """Débloque un achievement""" + if achievement_id in self.achievements: + achievement = self.achievements[achievement_id] + if not achievement.is_unlocked: + achievement.unlock() + # Notification et son + if hasattr(self.game, 'achievement_sound') and not self.game.sound_muted: + self.game.achievement_sound.play() + self.game.notification_manager.add_notification( + 'Achievement débloqué !', + achievement.name, + None # Icône à ajouter plus tard + ) + return True + return False + + def check_achievement_progress(self, achievement_id: str, value: int): + if achievement_id in self.achievements: + if self.achievements[achievement_id].update_progress(value): + # Notification à implémenter + pass + + def save_game(self): + save_data = { + 'coins': self.coins, + 'achievements': { + id: { + 'unlocked': achievement.is_unlocked, + 'progress': achievement.progress + } for id, achievement in self.achievements.items() + } + } + + with open(self.save_file, 'w') as f: + json.dump(save_data, f) + + def load_save(self): + if not os.path.exists(self.save_file): + return + + try: + with open(self.save_file, 'r') as f: + save_data = json.load(f) + self.coins = save_data.get('coins', 0) + + for id, data in save_data.get('achievements', {}).items(): + if id in self.achievements: + self.achievements[id].is_unlocked = data.get('unlocked', False) + self.achievements[id].progress = data.get('progress', 0) + except Exception as e: + print(f'Erreur lors du chargement de la sauvegarde: {e}') \ No newline at end of file diff --git a/src/game/ui/__init__.py b/src/game/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/game/ui/button.py b/src/game/ui/button.py new file mode 100644 index 0000000..f832dde --- /dev/null +++ b/src/game/ui/button.py @@ -0,0 +1,66 @@ +import pygame + +class Button: + def __init__(self, text, x, y, width, height): + """Initialise un bouton""" + self.text = text + self.rect = pygame.Rect(x - width // 2, y - height // 2, width, height) + self.font = pygame.font.Font(None, 36) + self.is_hovered = False + + # Couleurs améliorées + self.normal_color = (50, 50, 80) # Bleu foncé + self.hover_color = (70, 70, 120) # Bleu plus clair au survol + self.text_color = (220, 220, 255) # Blanc légèrement bleuté + self.border_color = (100, 100, 200) # Bordure bleue + + # Animation + self.pulse = 0 + self.pulse_direction = 1 + + def draw(self, screen): + """Dessine le bouton""" + # Animation de pulsation pour le bouton survolé + if self.is_hovered: + self.pulse += 0.1 * self.pulse_direction + if self.pulse > 1: + self.pulse = 1 + self.pulse_direction = -1 + elif self.pulse < 0: + self.pulse = 0 + self.pulse_direction = 1 + + # Couleur interpolée pour l'effet de pulsation + r = int(self.normal_color[0] + (self.hover_color[0] - self.normal_color[0]) * self.pulse) + g = int(self.normal_color[1] + (self.hover_color[1] - self.normal_color[1]) * self.pulse) + b = int(self.normal_color[2] + (self.hover_color[2] - self.normal_color[2]) * self.pulse) + color = (r, g, b) + else: + color = self.normal_color + self.pulse = 0 + + # Dessin du rectangle avec coins arrondis + pygame.draw.rect(screen, color, self.rect, border_radius=10) + pygame.draw.rect(screen, self.border_color, self.rect, 2, border_radius=10) # Bordure + + # Effet de lueur pour le bouton survolé + if self.is_hovered: + glow_rect = self.rect.inflate(4, 4) + pygame.draw.rect(screen, self.border_color, glow_rect, 1, border_radius=12) + + # Dessin du texte + text_surface = self.font.render(self.text, True, self.text_color) + text_rect = text_surface.get_rect(center=self.rect.center) + + # Léger décalage du texte quand le bouton est survolé + if self.is_hovered: + text_rect.y -= 2 + + screen.blit(text_surface, text_rect) + + # Mise à jour de l'état de survol + self.is_hovered = self.rect.collidepoint(pygame.mouse.get_pos()) + + def is_clicked(self, pos): + """Vérifie si le bouton est cliqué""" + return self.rect.collidepoint(pos) \ No newline at end of file diff --git a/src/game/ui/character_shop.py b/src/game/ui/character_shop.py new file mode 100644 index 0000000..4e96bbc --- /dev/null +++ b/src/game/ui/character_shop.py @@ -0,0 +1,304 @@ +from typing import Dict, List +import pygame +from utils.constants import * +import os + +class Character: + def __init__(self, id, name, description, cost, stats): + """Initialise un personnage""" + self.id = id + self.name = name + self.description = description + self.cost = cost + self.stats = stats + self.is_unlocked = False + self.is_selected = False + self.rect = None # Initialisation du rectangle à None + + # Définir les chemins des sprites + self.sprite_paths = { + 'default': os.path.join(CHARACTERS_DIR, 'default.png'), + 'speed': os.path.join(CHARACTERS_DIR, 'speeder.png'), + 'tank': os.path.join(CHARACTERS_DIR, 'tank.png'), + 'glass': os.path.join(CHARACTERS_DIR, 'default.png') # Utiliser default pour glass + } + + try: + # Utiliser le chemin correct pour les sprites + if id in self.sprite_paths and os.path.exists(self.sprite_paths[id]): + self.sprite = pygame.image.load(self.sprite_paths[id]).convert_alpha() + else: + # Fallback sur le sprite par défaut + default_path = os.path.join(CHARACTERS_DIR, 'default.png') + if os.path.exists(default_path): + self.sprite = pygame.image.load(default_path).convert_alpha() + else: + # Créer un sprite de base si aucun n'est trouvé + self.sprite = pygame.Surface((PLAYER_SIZE * 3, PLAYER_SIZE * 3), pygame.SRCALPHA) + pygame.draw.circle(self.sprite, PLAYER_COLOR, (PLAYER_SIZE * 1.5, PLAYER_SIZE * 1.5), PLAYER_SIZE) + print(f'Sprite par défaut créé pour {id}') + + # Redimensionner le sprite + self.sprite = pygame.transform.scale(self.sprite, (PLAYER_SIZE * 3, PLAYER_SIZE * 3)) + except Exception as e: + print(f'Erreur lors du chargement du sprite pour {id}: {e}') + self.sprite = pygame.Surface((PLAYER_SIZE * 3, PLAYER_SIZE * 3), pygame.SRCALPHA) + pygame.draw.circle(self.sprite, PLAYER_COLOR, (PLAYER_SIZE * 1.5, PLAYER_SIZE * 1.5), PLAYER_SIZE) + +class CharacterShop: + def __init__(self, game): + """Initialise la boutique de personnages""" + self.game = game + self.title_font = pygame.font.Font(None, 48) # Police plus grande pour le titre + self.font = pygame.font.Font(None, 28) # Police plus petite pour le nom + self.small_font = pygame.font.Font(None, 20) # Police encore plus petite pour les stats + + # Dimensions et positions + self.character_width = 250 # Augmentation de la largeur + self.character_height = 350 # Augmentation de la hauteur + self.padding = 30 + self.characters_per_row = 3 + + # Couleurs + self.bg_color = (30, 30, 50) # Bleu foncé + self.text_color = (220, 220, 255) # Blanc bleuté + self.locked_color = (50, 50, 70) # Gris foncé + self.hover_color = (40, 40, 80) # Bleu un peu plus clair + self.selected_color = (30, 70, 30) # Vert foncé + + # État + self.scroll_offset = 0 + self.selected_character = None + self.hovered_character = None + + # Initialisation des personnages + self.characters = { + 'default': Character( + 'default', + 'Vaisseau Standard', + 'Le vaisseau de base, équilibré et fiable', + 0, # Gratuit + {'health': 100, 'speed': 1.0, 'damage': 1.0} + ), + 'speed': Character( + 'speed', + 'Vaisseau Rapide', + 'Un vaisseau léger et agile', + 150, + {'health': 80, 'speed': 1.3, 'damage': 0.9} + ), + 'tank': Character( + 'tank', + 'Vaisseau Blindé', + 'Un vaisseau lent mais résistant', + 200, + {'health': 150, 'speed': 0.8, 'damage': 1.1} + ), + 'glass': Character( + 'glass', + 'Vaisseau Canon', + 'Fragile mais puissant', + 250, + {'health': 60, 'speed': 1.1, 'damage': 1.5} + ) + } + + # Le vaisseau par défaut est débloqué et sélectionné + self.characters['default'].is_unlocked = True + self.characters['default'].is_selected = True + self.selected_character = self.characters['default'] + + def handle_click(self, pos): + """Gère les clics dans le shop""" + # Vérification des clics sur les personnages + for character_id, character in self.characters.items(): + # Vérifier si le personnage a un rectangle et si le clic est dessus + if hasattr(character, 'rect') and character.rect and character.rect.collidepoint(pos): + print(f"Clic sur le personnage {character_id}") # Débogage + if not character.is_unlocked: + # Tentative d'achat du personnage + if self.game.economy_manager.coins >= character.cost: + self.game.economy_manager.remove_coins(character.cost) + character.is_unlocked = True + # Jouer le son si disponible + if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: + self.game.coin_sound.play() + self.game.notification_manager.add_notification( + 'Nouveau vaisseau !', + f'{character.name} débloqué', + None + ) + else: + # Notification de manque de pièces + self.game.notification_manager.add_notification( + 'Pas assez de pièces !', + f'Il vous manque {character.cost - self.game.economy_manager.coins} pièces', + None + ) + else: + # Sélectionner le personnage + self.select_character(character_id) + return True + return False + + def select_character(self, character_id): + """Sélectionne un personnage""" + if character_id in self.characters and self.characters[character_id].is_unlocked: + # Désélectionne le personnage actuel + if self.selected_character: + self.selected_character.is_selected = False + + # Sélectionne le nouveau personnage + self.characters[character_id].is_selected = True + self.selected_character = self.characters[character_id] + + # Son et notification + if hasattr(self.game, 'coin_sound') and not self.game.sound_muted: + self.game.coin_sound.play() + self.game.notification_manager.add_notification( + 'Personnage sélectionné !', + self.characters[character_id].name, + None # Icône à ajouter plus tard + ) + + return True + return False + + def get_selected_character(self): + """Retourne le personnage sélectionné""" + return self.selected_character + + def draw(self, screen): + """Dessine la boutique de personnages""" + # Titre + title = self.title_font.render("HANGAR SPATIAL", True, (100, 150, 255)) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, 50)) + screen.blit(title, title_rect) + + # Affichage des pièces + coin_text = self.title_font.render(f"{self.game.economy_manager.coins}", True, GOLD_COLOR) + coin_rect = coin_text.get_rect(center=(SCREEN_WIDTH // 2, 100)) + pygame.draw.circle(screen, GOLD_COLOR, (coin_rect.left - 20, coin_rect.centery), 10) + screen.blit(coin_text, coin_rect) + + # Calcul de la disposition des cartes + num_characters = len(self.characters) + total_width = num_characters * (CHARACTER_BUTTON_WIDTH + CHARACTER_SPACING) - CHARACTER_SPACING + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = 150 + + # Affichage des personnages + for i, character in enumerate(self.characters.values()): + x = start_x + i * (CHARACTER_BUTTON_WIDTH + CHARACTER_SPACING) + y = start_y + + # Fond de la carte + card_color = (40, 40, 60) + if character.is_selected: + card_color = (40, 60, 40) + elif not character.is_unlocked: + card_color = (60, 40, 40) + + # Rectangle de la carte avec bordure + card_rect = pygame.Rect(x, y, CHARACTER_BUTTON_WIDTH, CHARACTER_BUTTON_HEIGHT) + pygame.draw.rect(screen, card_color, card_rect) + pygame.draw.rect(screen, (100, 100, 100), card_rect, 2) + character.rect = card_rect + + # Image du personnage + sprite_rect = character.sprite.get_rect(center=(x + CHARACTER_BUTTON_WIDTH // 2, y + CHARACTER_PREVIEW_SIZE // 2 + 20)) + screen.blit(character.sprite, sprite_rect) + + # Nom du personnage avec un fond semi-transparent + name_text = self.font.render(character.name, True, WHITE) + name_rect = name_text.get_rect(center=(x + CHARACTER_BUTTON_WIDTH // 2, y + CHARACTER_PREVIEW_SIZE + 25)) + # Fond semi-transparent pour le nom + name_bg = pygame.Surface((name_rect.width + 20, name_rect.height + 6)) + name_bg.fill((0, 0, 0)) + name_bg.set_alpha(128) + name_bg_rect = name_bg.get_rect(center=name_rect.center) + screen.blit(name_bg, name_bg_rect) + screen.blit(name_text, name_rect) + + # Stats + stats_y = y + CHARACTER_PREVIEW_SIZE + 50 + stats_spacing = 20 # Espacement réduit + stats_center_x = x + CHARACTER_BUTTON_WIDTH // 2 + + # Santé + health_text = self.small_font.render(f'VIE {character.stats["health"]}%', True, (255, 100, 100)) + health_rect = health_text.get_rect(center=(stats_center_x, stats_y)) + screen.blit(health_text, health_rect) + + # Vitesse + speed_text = self.small_font.render(f'VIT {character.stats["speed"]}x', True, (100, 255, 100)) + speed_rect = speed_text.get_rect(center=(stats_center_x, stats_y + stats_spacing)) + screen.blit(speed_text, speed_rect) + + # Dégâts + damage_text = self.small_font.render(f'ATQ {character.stats["damage"]}x', True, (100, 100, 255)) + damage_rect = damage_text.get_rect(center=(stats_center_x, stats_y + stats_spacing * 2)) + screen.blit(damage_text, damage_rect) + + # État/Prix avec fond semi-transparent + if character.is_selected: + status_text = self.small_font.render("SÉLECTIONNÉ", True, (100, 255, 100)) + status_rect = status_text.get_rect(center=(stats_center_x, stats_y + stats_spacing * 3)) + elif not character.is_unlocked: + cost_text = self.small_font.render(f'COÛT: {character.cost}', True, GOLD_COLOR) + cost_rect = cost_text.get_rect(center=(stats_center_x, stats_y + stats_spacing * 3)) + pygame.draw.circle(screen, GOLD_COLOR, (cost_rect.left - 15, cost_rect.centery), 6) + screen.blit(cost_text, cost_rect) + else: + status_text = self.small_font.render("DÉBLOQUÉ", True, WHITE) + status_rect = status_text.get_rect(center=(stats_center_x, stats_y + stats_spacing * 3)) + screen.blit(status_text, status_rect) + + # Instructions + instruction_text = self.small_font.render("Cliquez sur un vaisseau pour le sélectionner ou l'acheter", True, WHITE) + instruction_rect = instruction_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT - 30)) + screen.blit(instruction_text, instruction_rect) + + def wrap_text(self, text, width): + """Découpe le texte en lignes selon la largeur donnée""" + words = text.split() + lines = [] + current_line = [] + current_width = 0 + + for word in words: + word_surface = self.small_font.render(word + ' ', True, self.text_color) + word_width = word_surface.get_width() + + if current_width + word_width > width: + lines.append(' '.join(current_line)) + current_line = [word] + current_width = word_width + else: + current_line.append(word) + current_width += word_width + + if current_line: + lines.append(' '.join(current_line)) + + return lines + + def save_state(self): + """Sauvegarde l'état de la boutique""" + return { + 'characters': { + char_id: { + 'unlocked': char.is_unlocked, + 'selected': char.is_selected + } for char_id, char in self.characters.items() + } + } + + def load_state(self, state): + """Charge l'état de la boutique""" + if 'characters' in state: + for char_id, data in state['characters'].items(): + if char_id in self.characters: + self.characters[char_id].is_unlocked = data.get('unlocked', False) + if data.get('selected', False): + self.select_character(char_id) \ No newline at end of file diff --git a/src/game/ui/game_mode_menu.py b/src/game/ui/game_mode_menu.py new file mode 100644 index 0000000..c454546 --- /dev/null +++ b/src/game/ui/game_mode_menu.py @@ -0,0 +1,152 @@ +import pygame +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT, WHITE, CYAN, GOLD_COLOR + +class GameModeMenu: + def __init__(self, screen, game): + self.screen = screen + self.game = game + self.font = pygame.font.Font(None, 36) + self.title_font = pygame.font.Font(None, 48) + self.small_font = pygame.font.Font(None, 28) # Police plus petite pour les records + self.visible = False + + # Dimensions des boutons de mode + self.mode_width = 200 + self.mode_height = 150 + self.padding = 30 + + # Couleurs + self.bg_color = (40, 40, 60) # Bleu foncé + self.hover_color = (50, 50, 70) # Bleu plus clair + self.locked_color = (60, 40, 40) # Rouge foncé + + # État + self.hovered_mode = None + + def show(self): + """Affiche le menu""" + self.visible = True + self.hovered_mode = None + + def hide(self): + """Cache le menu""" + self.visible = False + self.hovered_mode = None + + def handle_event(self, event): + """Gère les événements du menu""" + if not self.visible: + return None + + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + # Gestion du clic sur le bouton retour + back_rect = pygame.Rect(20, SCREEN_HEIGHT - 70, 120, 50) + if back_rect.collidepoint(event.pos): + return 'back' + + # Gestion du clic sur un mode + if self.hovered_mode: + return f'mode_{self.hovered_mode.id}' + + elif event.type == pygame.MOUSEMOTION: + mouse_pos = event.pos + + # Calcul de la disposition des modes + num_modes = len(self.game.game_modes) + total_width = num_modes * (self.mode_width + self.padding) - self.padding + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = (SCREEN_HEIGHT - self.mode_height) // 2 + + # Vérification de la collision avec chaque mode + self.hovered_mode = None + for i, mode in enumerate(self.game.game_modes.values()): + x = start_x + i * (self.mode_width + self.padding) + mode_rect = pygame.Rect(x, start_y, self.mode_width, self.mode_height) + if mode_rect.collidepoint(mouse_pos): + self.hovered_mode = mode + break + + return None + + def draw(self): + """Dessine le menu""" + if not self.visible: + return + + # Fond semi-transparent + overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + overlay.fill((0, 0, 0)) + overlay.set_alpha(128) + self.screen.blit(overlay, (0, 0)) + + # Titre + title = self.title_font.render("SÉLECTION DU MODE", True, WHITE) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, 100)) + self.screen.blit(title, title_rect) + + # Calcul de la disposition des modes + num_modes = len(self.game.game_modes) + total_width = num_modes * (self.mode_width + self.padding) - self.padding + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = (SCREEN_HEIGHT - self.mode_height) // 2 + + # Affichage des modes + for i, mode in enumerate(self.game.game_modes.values()): + x = start_x + i * (self.mode_width + self.padding) + mode_rect = pygame.Rect(x, start_y, self.mode_width, self.mode_height) + + # Couleur de fond selon l'état + color = self.hover_color if mode == self.hovered_mode else self.bg_color + pygame.draw.rect(self.screen, color, mode_rect) + pygame.draw.rect(self.screen, WHITE, mode_rect, 2) + + # Nom du mode + name = self.font.render(mode.name, True, WHITE) + name_rect = name.get_rect(center=(x + self.mode_width // 2, start_y + 30)) + self.screen.blit(name, name_rect) + + # Description (sur plusieurs lignes si nécessaire) + desc_lines = self.wrap_text(mode.description, self.mode_width - 20) + for j, line in enumerate(desc_lines[:2]): # Limite à 2 lignes + desc = self.font.render(line, True, CYAN) + desc_rect = desc.get_rect(center=(x + self.mode_width // 2, start_y + 70 + j * 25)) + self.screen.blit(desc, desc_rect) + + # Affichage du record (avec police plus petite) + record_text = mode.get_record_text() + if record_text: + record = self.small_font.render(record_text, True, GOLD_COLOR) + record_rect = record.get_rect(center=(x + self.mode_width // 2, start_y + self.mode_height - 30)) + self.screen.blit(record, record_rect) + + # Bouton retour + back_rect = pygame.Rect(20, SCREEN_HEIGHT - 70, 120, 50) + pygame.draw.rect(self.screen, self.bg_color, back_rect) + pygame.draw.rect(self.screen, WHITE, back_rect, 2) + back_text = self.font.render('Retour', True, WHITE) + back_rect = back_text.get_rect(center=(80, SCREEN_HEIGHT - 45)) + self.screen.blit(back_text, back_rect) + + def wrap_text(self, text, max_width): + """Découpe le texte en lignes selon la largeur donnée""" + words = text.split() + lines = [] + current_line = [] + current_width = 0 + + for word in words: + word_surface = self.font.render(word + ' ', True, WHITE) + word_width = word_surface.get_width() + + if current_width + word_width > max_width: + lines.append(' '.join(current_line)) + current_line = [word] + current_width = word_width + else: + current_line.append(word) + current_width += word_width + + if current_line: + lines.append(' '.join(current_line)) + + return lines \ No newline at end of file diff --git a/src/game/ui/menu_manager.py b/src/game/ui/menu_manager.py new file mode 100644 index 0000000..419521e --- /dev/null +++ b/src/game/ui/menu_manager.py @@ -0,0 +1,238 @@ +from typing import Dict +import pygame +from game.ui.menus import MainMenu, ShopMenu, AchievementsMenu, GameModeMenu, PauseMenu, GameOverMenu +from game.ui.button import Button +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT, MENU, PLAYING, PAUSED, GAME_OVER, SOUND_BUTTON_SIZE, SOUND_BUTTON_PADDING + +class MenuManager: + def __init__(self, game): + self.game = game + self.screen = game.screen + self.character_shop = game.character_shop + self.economy_manager = game.economy_manager + self.module_menu = game.module_menu # Nouveau + + # Polices + self.font = pygame.font.Font(None, 36) + self.big_font = pygame.font.Font(None, 72) + + # État du menu + self.current_menu = 'main' # main, pause, game_over, character_shop, modules + self.game_over_score = 0 + + # Position du bouton son + self.sound_button_rect = pygame.Rect( + SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - 10, + SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - 10, + SOUND_BUTTON_SIZE, + SOUND_BUTTON_SIZE + ) + + # Boutons du menu principal + self.main_menu_buttons = [ + Button('Jouer', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 20, 200, 50), + Button('Personnages', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 80, 200, 50), + Button('Modules', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 140, 200, 50), + Button('Quitter', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 200, 200, 50) + ] + + # Boutons du menu pause + self.pause_menu_buttons = [ + Button('Reprendre', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 60, 200, 50), + Button('Recommencer', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2, 200, 50), + Button('Menu Principal', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 60, 200, 50) + ] + + # Boutons du menu game over + self.game_over_buttons = [ + Button('Recommencer', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 60, 200, 50), + Button('Menu Principal', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 120, 200, 50) + ] + + # Boutons du menu character shop + self.character_shop_buttons = [ + Button('Retour', SCREEN_WIDTH // 2, SCREEN_HEIGHT - 100, 200, 50) + ] + + # Boutons du menu modules + self.module_menu_buttons = [ + Button('Retour', SCREEN_WIDTH // 2, SCREEN_HEIGHT - 100, 200, 50) + ] + + def handle_event(self, event): + """Gère les événements du menu""" + if event.type == pygame.MOUSEBUTTONDOWN: + mouse_pos = pygame.mouse.get_pos() + + # Gestion du bouton son + if self.is_sound_button_clicked(mouse_pos): + self.game.toggle_sound() + return None + + # Gestion des menus + if self.current_menu == 'main': + for button in self.main_menu_buttons: + if button.rect.collidepoint(mouse_pos): + if button.text == 'Jouer': + return 'start_game' + elif button.text == 'Quitter': + return 'quit' + elif button.text == 'Personnages': + self.show_menu('character_shop') + return None + elif button.text == 'Modules': + self.show_menu('modules') + return None + + elif self.current_menu == 'pause': + for button in self.pause_menu_buttons: + if button.rect.collidepoint(mouse_pos): + if button.text == 'Reprendre': + return 'resume_game' + elif button.text == 'Recommencer': + return 'restart_game' + elif button.text == 'Menu Principal': + self.show_menu('main') + return 'go_to_main_menu' + + elif self.current_menu == 'game_over': + for button in self.game_over_buttons: + if button.rect.collidepoint(mouse_pos): + if button.text == 'Recommencer': + return 'restart_game' + elif button.text == 'Menu Principal': + self.show_menu('main') + return 'go_to_main_menu' + + elif self.current_menu == 'character_shop': + # Gestion du bouton retour + for button in self.character_shop_buttons: + if button.rect.collidepoint(mouse_pos): + self.show_menu('main') + return None + + # Gestion des clics dans le shop + if hasattr(self.game, 'character_shop'): + self.game.character_shop.handle_click(mouse_pos) + + elif self.current_menu == 'modules': + # Gestion du bouton retour + for button in self.module_menu_buttons: + if button.rect.collidepoint(mouse_pos): + self.show_menu('main') + return None + + # Gestion des clics dans le menu des modules + if hasattr(self.game, 'module_menu'): + self.game.module_menu.handle_click(mouse_pos) + + return None + + def draw(self): + """Dessine le menu actuel""" + # Fond semi-transparent + s = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) + s.set_alpha(128) + s.fill((0, 0, 0)) + self.screen.blit(s, (0, 0)) + + if self.current_menu == 'main': + # Titre avec effet de lueur + title_text = 'ALIEN WAVE' + + # Effet de lueur (ombre portée) + glow_font = pygame.font.Font(None, 74) + glow_surface = glow_font.render(title_text, True, (50, 50, 200)) + glow_rect = glow_surface.get_rect(center=(SCREEN_WIDTH // 2 + 2, SCREEN_HEIGHT // 5 + 2)) + self.screen.blit(glow_surface, glow_rect) + + # Texte principal + title = self.big_font.render(title_text, True, (255, 255, 255)) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 5)) + self.screen.blit(title, title_rect) + + # Affichage des pièces avec icône + coin_icon_size = 24 + coin_icon = pygame.Surface((coin_icon_size, coin_icon_size), pygame.SRCALPHA) + pygame.draw.circle(coin_icon, (255, 215, 0), (coin_icon_size//2, coin_icon_size//2), coin_icon_size//2) + pygame.draw.circle(coin_icon, (255, 255, 150), (coin_icon_size//2, coin_icon_size//2), coin_icon_size//2 - 2) + + coins_text = self.font.render(f' {self.economy_manager.coins}', True, (255, 255, 0)) + coins_rect = coins_text.get_rect(midleft=(SCREEN_WIDTH // 2 - 10, SCREEN_HEIGHT // 5 + 50)) + coin_icon_rect = coin_icon.get_rect(midright=(coins_rect.left, coins_rect.centery)) + + self.screen.blit(coin_icon, coin_icon_rect) + self.screen.blit(coins_text, coins_rect) + + # Boutons + for button in self.main_menu_buttons: + button.draw(self.screen) + + elif self.current_menu == 'pause': + # Titre + title = self.big_font.render('PAUSE', True, (255, 255, 255)) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)) + self.screen.blit(title, title_rect) + + # Boutons + for button in self.pause_menu_buttons: + button.draw(self.screen) + + elif self.current_menu == 'game_over': + # Titre + title = self.big_font.render('GAME OVER', True, (255, 255, 255)) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)) + self.screen.blit(title, title_rect) + + # Score + score_text = self.font.render(f'Score: {self.game_over_score}', True, (255, 255, 255)) + score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 30)) + self.screen.blit(score_text, score_rect) + + # Meilleur score + highscore_text = self.font.render(f'Meilleur score: {self.game.highscore}', True, (255, 255, 255)) + highscore_rect = highscore_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 10)) + self.screen.blit(highscore_text, highscore_rect) + + # Boutons + for button in self.game_over_buttons: + button.draw(self.screen) + + elif self.current_menu == 'character_shop': + # Affichage de la boutique + self.character_shop.draw(self.screen) + + # Bouton retour avec effet spécial + for button in self.character_shop_buttons: + # Ajouter un effet de lueur autour du bouton + glow_rect = button.rect.inflate(8, 8) + pygame.draw.rect(self.screen, (100, 100, 200, 150), glow_rect, 2, border_radius=12) + button.draw(self.screen) + + elif self.current_menu == 'modules': # Nouveau + # Affichage du menu des modules + self.module_menu.draw(self.screen) + + # Bouton retour + for button in self.module_menu_buttons: + button.draw(self.screen) + + def show_menu(self, menu_type): + """Affiche un menu spécifique""" + self.current_menu = menu_type + + def hide(self): + """Cache le menu""" + self.current_menu = None + + def is_menu_active(self): + """Vérifie si un menu est actif""" + return self.current_menu is not None + + def set_game_over_score(self, score): + """Définit le score pour l'écran de game over""" + self.game_over_score = score + + def is_sound_button_clicked(self, mouse_pos): + """Vérifie si le bouton son a été cliqué""" + return self.sound_button_rect.collidepoint(mouse_pos) \ No newline at end of file diff --git a/src/game/ui/menus.py b/src/game/ui/menus.py new file mode 100644 index 0000000..da60efb --- /dev/null +++ b/src/game/ui/menus.py @@ -0,0 +1,366 @@ +import pygame +from typing import List, Dict, Callable +from utils.constants import * + +class Button: + def __init__(self, x: int, y: int, width: int, height: int, text: str, + color: tuple = (100, 100, 100), hover_color: tuple = (150, 150, 150)): + self.rect = pygame.Rect(x, y, width, height) + self.text = text + self.color = color + self.hover_color = hover_color + self.is_hovered = False + + def draw(self, screen: pygame.Surface): + color = self.hover_color if self.is_hovered else self.color + pygame.draw.rect(screen, color, self.rect, border_radius=10) + pygame.draw.rect(screen, (255, 255, 255), self.rect, 2, border_radius=10) + + font = pygame.font.Font(None, 36) + text_surface = font.render(self.text, True, (255, 255, 255)) + text_rect = text_surface.get_rect(center=self.rect.center) + screen.blit(text_surface, text_rect) + + def handle_event(self, event: pygame.event.Event) -> bool: + if event.type == pygame.MOUSEMOTION: + self.is_hovered = self.rect.collidepoint(event.pos) + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + return self.rect.collidepoint(event.pos) + return False + +class Menu: + def __init__(self, screen: pygame.Surface): + self.screen = screen + self.buttons: Dict[str, Button] = {} + self.active = False + + def show(self): + self.active = True + + def hide(self): + self.active = False + + def handle_event(self, event: pygame.event.Event) -> str: + """Retourne l'action à effectuer si un bouton est cliqué""" + if not self.active: + return '' + + for action, button in self.buttons.items(): + if button.handle_event(event): + return action + return '' + + def draw(self): + if not self.active: + return + + # Fond semi-transparent + overlay = pygame.Surface(self.screen.get_size(), pygame.SRCALPHA) + overlay.fill((0, 0, 0, 180)) + self.screen.blit(overlay, (0, 0)) + + for button in self.buttons.values(): + button.draw(self.screen) + +class MainMenu(Menu): + def __init__(self, screen: pygame.Surface): + super().__init__(screen) + width, height = screen.get_size() + button_width = 200 + button_height = 50 + spacing = 20 + start_y = height // 2 - (button_height + spacing) * 2 + + self.buttons = { + 'play': Button(width//2 - button_width//2, start_y, + button_width, button_height, 'Jouer'), + 'shop': Button(width//2 - button_width//2, start_y + button_height + spacing, + button_width, button_height, 'Boutique'), + 'achievements': Button(width//2 - button_width//2, start_y + (button_height + spacing) * 2, + button_width, button_height, 'Succès'), + 'quit': Button(width//2 - button_width//2, start_y + (button_height + spacing) * 3, + button_width, button_height, 'Quitter') + } + + def draw(self): + if not self.active: + return + + super().draw() + + # Titre + font = pygame.font.Font(None, 72) + title = font.render('Survival Shooter', True, (255, 255, 255)) + title_rect = title.get_rect(centerx=self.screen.get_width()//2, y=100) + self.screen.blit(title, title_rect) + +class ShopMenu(Menu): + def __init__(self, screen: pygame.Surface, character_shop): + super().__init__(screen) + self.character_shop = character_shop + self.selected_preview = None + width, height = screen.get_size() + + # Bouton retour + self.buttons['back'] = Button(20, height - 70, 120, 50, 'Retour') + + # Création des boutons pour chaque personnage + char_button_width = 150 + char_button_height = 200 + spacing = 30 + total_width = len(character_shop.characters) * char_button_width + (len(character_shop.characters) - 1) * spacing + start_x = (width - total_width) // 2 + + for i, (char_id, character) in enumerate(character_shop.characters.items()): + x = start_x + i * (char_button_width + spacing) + self.buttons[f'char_{char_id}'] = Button(x, height//2 - char_button_height//2, + char_button_width, char_button_height, character.name) + + def draw(self): + if not self.active: + return + + super().draw() + + # Titre + font = pygame.font.Font(None, 72) + title = font.render('Boutique', True, (255, 255, 255)) + title_rect = title.get_rect(centerx=self.screen.get_width()//2, y=50) + self.screen.blit(title, title_rect) + + # Affichage des pièces + coin_font = pygame.font.Font(None, 36) + coin_text = coin_font.render(f'Pièces: {self.character_shop.economy_manager.coins}', True, (255, 215, 0)) + self.screen.blit(coin_text, (20, 20)) + + # Affichage des personnages + for char_id, character in self.character_shop.characters.items(): + button = self.buttons[f'char_{char_id}'] + + # Sprite du personnage + sprite_rect = character.sprite.get_rect(centerx=button.rect.centerx, + centery=button.rect.centery - 30) + self.screen.blit(character.sprite, sprite_rect) + + # Prix ou statut + if character.is_unlocked: + status_text = 'Débloqué' + if char_id == self.character_shop.selected_character: + status_text = 'Sélectionné' + status_color = (0, 255, 0) + else: + status_text = f'{character.price} pièces' + status_color = (255, 215, 0) + + status_font = pygame.font.Font(None, 24) + status_surface = status_font.render(status_text, True, status_color) + status_rect = status_surface.get_rect(centerx=button.rect.centerx, + bottom=button.rect.bottom - 10) + self.screen.blit(status_surface, status_rect) + +class AchievementsMenu(Menu): + def __init__(self, screen: pygame.Surface, economy_manager): + super().__init__(screen) + self.economy_manager = economy_manager + width, height = screen.get_size() + + # Bouton retour + self.buttons['back'] = Button(20, height - 70, 120, 50, 'Retour') + + def draw(self): + if not self.active: + return + + super().draw() + + # Titre + font = pygame.font.Font(None, 72) + title = font.render('Succès', True, (255, 255, 255)) + title_rect = title.get_rect(centerx=self.screen.get_width()//2, y=50) + self.screen.blit(title, title_rect) + + # Affichage des achievements + achievement_height = 80 + spacing = 20 + start_y = 150 + + for achievement in self.economy_manager.achievements.values(): + # Fond de l'achievement + achievement_rect = pygame.Rect(100, start_y, self.screen.get_width() - 200, achievement_height) + color = (100, 100, 100) if achievement.is_unlocked else (50, 50, 50) + pygame.draw.rect(self.screen, color, achievement_rect, border_radius=10) + + # Icône + if achievement.icon_path: + try: + icon = pygame.image.load(achievement.icon_path) + icon = pygame.transform.scale(icon, (60, 60)) + self.screen.blit(icon, (achievement_rect.x + 10, achievement_rect.y + 10)) + except: + pygame.draw.rect(self.screen, (150, 150, 150), + (achievement_rect.x + 10, achievement_rect.y + 10, 60, 60)) + + # Texte + name_font = pygame.font.Font(None, 32) + desc_font = pygame.font.Font(None, 24) + + name_surface = name_font.render(achievement.name, True, (255, 255, 255)) + desc_surface = desc_font.render(achievement.description, True, (200, 200, 200)) + + self.screen.blit(name_surface, (achievement_rect.x + 80, achievement_rect.y + 10)) + self.screen.blit(desc_surface, (achievement_rect.x + 80, achievement_rect.y + 40)) + + # Barre de progression + if not achievement.is_unlocked: + progress_rect = pygame.Rect(achievement_rect.right - 110, + achievement_rect.centery - 10, 100, 20) + pygame.draw.rect(self.screen, (50, 50, 50), progress_rect, border_radius=5) + + progress_width = int(progress_rect.width * (achievement.progress / achievement.max_progress)) + if progress_width > 0: + pygame.draw.rect(self.screen, (0, 255, 0), + (progress_rect.x, progress_rect.y, progress_width, progress_rect.height), + border_radius=5) + + start_y += achievement_height + spacing + +class GameModeMenu(Menu): + def __init__(self, screen: pygame.Surface, game): + super().__init__(screen) + self.game = game + width, height = screen.get_size() + + # Bouton retour + self.buttons['back'] = Button(20, height - 70, 120, 50, 'Retour') + + # Création des boutons pour chaque mode + mode_button_width = 200 + mode_button_height = 150 + spacing = 30 + total_width = len(game.get_game_modes()) * mode_button_width + (len(game.get_game_modes()) - 1) * spacing + start_x = (width - total_width) // 2 + + for i, (mode_id, mode) in enumerate(game.get_game_modes().items()): + x = start_x + i * (mode_button_width + spacing) + self.buttons[f'mode_{mode_id}'] = Button(x, height//2 - mode_button_height//2, + mode_button_width, mode_button_height, mode.name) + + def draw(self): + if not self.active: + return + + super().draw() + + # Titre + font = pygame.font.Font(None, 72) + title = font.render('Modes de Jeu', True, (255, 255, 255)) + title_rect = title.get_rect(centerx=self.screen.get_width()//2, y=50) + self.screen.blit(title, title_rect) + + # Affichage des modes + for mode_id, mode in self.game.get_game_modes().items(): + button = self.buttons[f'mode_{mode_id}'] + + # Description + desc_font = pygame.font.Font(None, 24) + desc_lines = [mode.description[i:i+20] for i in range(0, len(mode.description), 20)] + for i, line in enumerate(desc_lines): + desc_surface = desc_font.render(line, True, (200, 200, 200)) + desc_rect = desc_surface.get_rect(centerx=button.rect.centerx, + top=button.rect.bottom + 10 + i*25) + self.screen.blit(desc_surface, desc_rect) + + # Statut + if hasattr(self.game.current_game_mode, 'id') and mode_id == self.game.current_game_mode.id: + status_text = 'Sélectionné' + status_color = (0, 255, 0) + else: + status_text = 'Disponible' + status_color = (200, 200, 200) + + status_font = pygame.font.Font(None, 24) + status_surface = status_font.render(status_text, True, status_color) + status_rect = status_surface.get_rect(centerx=button.rect.centerx, + bottom=button.rect.bottom - 10) + self.screen.blit(status_surface, status_rect) + + # High score + if mode.high_score > 0: + score_text = f'Meilleur score: {mode.high_score}' + score_surface = desc_font.render(score_text, True, (255, 215, 0)) + score_rect = score_surface.get_rect(centerx=button.rect.centerx, + top=button.rect.top - 30) + self.screen.blit(score_surface, score_rect) + +class PauseMenu(Menu): + def __init__(self, screen: pygame.Surface): + super().__init__(screen) + width, height = screen.get_size() + button_width = 200 + button_height = 50 + spacing = 20 + start_y = height // 2 - button_height + + self.buttons = { + 'resume': Button(width//2 - button_width//2, start_y, + button_width, button_height, 'Reprendre'), + 'quit': Button(width//2 - button_width//2, start_y + button_height + spacing, + button_width, button_height, 'Quitter') + } + + def draw(self): + if not self.active: + return + + super().draw() + + # Titre + font = pygame.font.Font(None, 72) + title = font.render('PAUSE', True, (255, 255, 255)) + title_rect = title.get_rect(centerx=self.screen.get_width()//2, y=100) + self.screen.blit(title, title_rect) + +class GameOverMenu(Menu): + def __init__(self, screen: pygame.Surface, game): + super().__init__(screen) + self.game = game + width, height = screen.get_size() + button_width = 200 + button_height = 50 + spacing = 20 + start_y = height // 2 + 50 + + self.buttons = { + 'restart': Button(width//2 - button_width//2, start_y, + button_width, button_height, 'Recommencer'), + 'main_menu': Button(width//2 - button_width//2, start_y + button_height + spacing, + button_width, button_height, 'Menu Principal') + } + + self.score = 0 + + def set_score(self, score: int): + self.score = score + + def draw(self): + if not self.active: + return + + super().draw() + + # Titre + font = pygame.font.Font(None, 72) + title = font.render('GAME OVER', True, (255, 0, 0)) + title_rect = title.get_rect(centerx=self.screen.get_width()//2, y=100) + self.screen.blit(title, title_rect) + + # Score + score_font = pygame.font.Font(None, 48) + score_text = score_font.render(f'Score: {self.score}', True, (255, 255, 255)) + score_rect = score_text.get_rect(centerx=self.screen.get_width()//2, y=200) + self.screen.blit(score_text, score_rect) + + # High score + highscore_text = score_font.render(f'Meilleur score: {self.game.highscore}', True, (255, 215, 0)) + highscore_rect = highscore_text.get_rect(centerx=self.screen.get_width()//2, y=250) + self.screen.blit(highscore_text, highscore_rect) \ No newline at end of file diff --git a/src/game/ui/module_menu.py b/src/game/ui/module_menu.py new file mode 100644 index 0000000..fa98b78 --- /dev/null +++ b/src/game/ui/module_menu.py @@ -0,0 +1,227 @@ +import pygame +from typing import Optional, Tuple + +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT, WHITE, BLACK, GREEN, RED, YELLOW, GOLD_COLOR + +class ModuleMenu: + def __init__(self, game): + """Initialise le menu des modules""" + self.game = game + self.title_font = pygame.font.Font(None, 48) # Police plus grande pour le titre + self.font = pygame.font.Font(None, 28) # Police plus petite pour le nom + self.small_font = pygame.font.Font(None, 20) # Police encore plus petite pour les stats + + # Dimensions pour l'affichage des modules + self.module_width = 160 + self.module_height = 200 + self.padding = 20 + self.modules_per_row = 4 + + # Couleurs + self.bg_color = (40, 40, 60) # Bleu foncé + self.selected_color = (40, 60, 40) # Vert foncé + self.locked_color = (60, 40, 40) # Rouge foncé + self.hover_color = (50, 50, 70) # Bleu plus clair + + # État + self.scroll_offset = 0 + self.max_scroll = 0 + self.hovered_module = None + + def draw(self, screen): + """Dessine le menu des modules""" + # Titre + title = self.title_font.render("MODULES", True, (100, 150, 255)) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, 50)) + screen.blit(title, title_rect) + + # Affichage des pièces + coin_text = self.title_font.render(f"{self.game.economy_manager.coins}", True, GOLD_COLOR) + coin_rect = coin_text.get_rect(center=(SCREEN_WIDTH // 2, 100)) + pygame.draw.circle(screen, GOLD_COLOR, (coin_rect.left - 20, coin_rect.centery), 10) + screen.blit(coin_text, coin_rect) + + # Calcul de la disposition des modules + num_modules = len(self.game.module_manager.modules) + total_width = min(self.modules_per_row, num_modules) * (self.module_width + self.padding) - self.padding + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = 150 - self.scroll_offset + + # Affichage des modules + row = 0 + col = 0 + for module in self.game.module_manager.modules.values(): + x = start_x + col * (self.module_width + self.padding) + y = start_y + row * (self.module_height + self.padding) + + # Fond de la carte + card_color = self.bg_color + if module == self.hovered_module: + card_color = self.hover_color + elif not module.is_unlocked: + card_color = self.locked_color + + # Rectangle de la carte avec bordure + card_rect = pygame.Rect(x, y, self.module_width, self.module_height) + pygame.draw.rect(screen, card_color, card_rect) + pygame.draw.rect(screen, (100, 100, 100), card_rect, 2) + + # Nom du module avec fond semi-transparent + name_text = self.font.render(module.name, True, WHITE) + name_rect = name_text.get_rect(center=(x + self.module_width // 2, y + 30)) + # Fond semi-transparent pour le nom + name_bg = pygame.Surface((name_rect.width + 20, name_rect.height + 6)) + name_bg.fill((0, 0, 0)) + name_bg.set_alpha(128) + name_bg_rect = name_bg.get_rect(center=name_rect.center) + screen.blit(name_bg, name_bg_rect) + screen.blit(name_text, name_rect) + + # Description + desc_lines = self.wrap_text(module.description, self.module_width - 20) + for i, line in enumerate(desc_lines[:2]): # Limite à 2 lignes + desc_text = self.small_font.render(line, True, WHITE) + desc_rect = desc_text.get_rect(center=(x + self.module_width // 2, y + 60 + i * 20)) + screen.blit(desc_text, desc_rect) + + # Stats + stats_y = y + 110 + stats_center_x = x + self.module_width // 2 + + # Niveau actuel + if module.is_unlocked: + level_text = self.small_font.render(f'NIV {module.level}/{module.max_level}', True, (100, 255, 100)) + else: + level_text = self.small_font.render('VERROUILLÉ', True, (255, 100, 100)) + level_rect = level_text.get_rect(center=(stats_center_x, stats_y)) + screen.blit(level_text, level_rect) + + # Effet actuel + if module.is_unlocked: + effect_text = self.small_font.render(module.get_effect_description(), True, (100, 100, 255)) + effect_rect = effect_text.get_rect(center=(stats_center_x, stats_y + 25)) + screen.blit(effect_text, effect_rect) + + # Prix/État + if not module.is_unlocked: + cost_text = self.small_font.render(f'COÛT: {module.cost}', True, GOLD_COLOR) + cost_rect = cost_text.get_rect(center=(stats_center_x, y + self.module_height - 30)) + pygame.draw.circle(screen, GOLD_COLOR, (cost_rect.left - 15, cost_rect.centery), 6) + screen.blit(cost_text, cost_rect) + elif module.level < module.max_level: + next_cost = module.cost * (module.level + 1) + upgrade_text = self.small_font.render(f'AMÉL: {next_cost}', True, GOLD_COLOR) + upgrade_rect = upgrade_text.get_rect(center=(stats_center_x, y + self.module_height - 30)) + pygame.draw.circle(screen, GOLD_COLOR, (upgrade_rect.left - 15, upgrade_rect.centery), 6) + screen.blit(upgrade_text, upgrade_rect) + else: + max_text = self.small_font.render('NIVEAU MAX', True, (100, 255, 100)) + max_rect = max_text.get_rect(center=(stats_center_x, y + self.module_height - 30)) + screen.blit(max_text, max_rect) + + # Passage à la colonne/ligne suivante + col += 1 + if col >= self.modules_per_row: + col = 0 + row += 1 + + # Instructions + instruction_text = self.small_font.render("Cliquez sur un module pour le débloquer ou l'améliorer", True, WHITE) + instruction_rect = instruction_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT - 30)) + screen.blit(instruction_text, instruction_rect) + + def wrap_text(self, text, max_width): + """Découpe le texte en lignes selon la largeur donnée""" + words = text.split() + lines = [] + current_line = [] + current_width = 0 + + for word in words: + word_surface = self.small_font.render(word + ' ', True, WHITE) + word_width = word_surface.get_width() + + if current_width + word_width > max_width: + lines.append(' '.join(current_line)) + current_line = [word] + current_width = word_width + else: + current_line.append(word) + current_width += word_width + + if current_line: + lines.append(' '.join(current_line)) + + return lines + + def handle_event(self, event): + """Gère les événements du menu""" + if event.type == pygame.MOUSEBUTTONDOWN: + if event.button in (4, 5): # Molette de la souris + self.scroll_offset = max(0, min(self.max_scroll, + self.scroll_offset + (30 if event.button == 5 else -30))) + elif event.button == 1: # Clic gauche + self.handle_click(event.pos) + elif event.type == pygame.MOUSEMOTION: + self.update_hovered_module(event.pos) + + def update_hovered_module(self, pos): + """Met à jour le module survolé""" + mouse_x, mouse_y = pos + mouse_y += self.scroll_offset # Ajuster pour le scroll + + # Calcul de la disposition des modules + num_modules = len(self.game.module_manager.modules) + total_width = min(self.modules_per_row, num_modules) * (self.module_width + self.padding) - self.padding + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = 150 + + self.hovered_module = None + row = 0 + col = 0 + for module in self.game.module_manager.modules.values(): + x = start_x + col * (self.module_width + self.padding) + y = start_y + row * (self.module_height + self.padding) + + if (x <= mouse_x <= x + self.module_width and + y <= mouse_y <= y + self.module_height): + self.hovered_module = module + break + + col += 1 + if col >= self.modules_per_row: + col = 0 + row += 1 + + def handle_click(self, pos): + """Gère les clics dans le menu""" + if not self.hovered_module: + return + + if not self.hovered_module.is_unlocked: + # Tente de débloquer le module + if self.game.economy_manager.coins >= self.hovered_module.cost: + if self.game.module_manager.unlock_module(self.hovered_module.id): + self.game.economy_manager.remove_coins(self.hovered_module.cost) + if hasattr(self.game, 'coin_sound') and not self.game.sound_muted: + self.game.coin_sound.play() + else: + self.game.notification_manager.add_notification( + 'Pas assez de pièces !', + f'Il vous manque {self.hovered_module.cost - self.game.economy_manager.coins} pièces', + None + ) + elif self.hovered_module.level < self.hovered_module.max_level: + # Tente d'améliorer le module + next_cost = self.hovered_module.cost * (self.hovered_module.level + 1) + if self.game.economy_manager.coins >= next_cost: + if self.game.module_manager.upgrade_module(self.hovered_module.id): + self.game.economy_manager.remove_coins(next_cost) + if hasattr(self.game, 'coin_sound') and not self.game.sound_muted: + self.game.coin_sound.play() + else: + self.game.notification_manager.add_notification( + 'Pas assez de pièces !', + f'Il vous manque {next_cost - self.game.economy_manager.coins} pièces', + None + ) \ No newline at end of file diff --git a/src/game/ui/module_selection.py b/src/game/ui/module_selection.py new file mode 100644 index 0000000..7aa78d0 --- /dev/null +++ b/src/game/ui/module_selection.py @@ -0,0 +1,135 @@ +import pygame +from typing import List, Optional + +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT, WHITE, BLACK, GREEN, YELLOW +from game.modules import Module + +class ModuleSelectionMenu: + def __init__(self, game): + self.game = game + self.font = pygame.font.Font(None, 32) + self.small_font = pygame.font.Font(None, 24) + + # Dimensions pour l'affichage des modules + self.module_width = 200 + self.module_height = 120 + self.padding = 20 + + # Couleurs + self.bg_color = (0, 0, 0, 128) # Noir semi-transparent + self.module_bg = (30, 30, 30) + self.module_hover = (50, 50, 50) + + # État + self.is_visible = False + self.modules: List[Module] = [] + + def show(self, modules: List[Module]) -> None: + self.is_visible = True + self.modules = modules + self.game.state = 'PAUSED' + + def hide(self) -> None: + self.is_visible = False + self.game.state = 'PLAYING' + + def draw(self, surface: pygame.Surface) -> None: + if not self.is_visible: + return + + # Fond semi-transparent + overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA) + overlay.fill(self.bg_color) + surface.blit(overlay, (0, 0)) + + # Titre + title = self.font.render('Choisissez un module à améliorer', True, WHITE) + surface.blit(title, (SCREEN_WIDTH // 2 - title.get_width() // 2, 20)) + + # Calcul des positions des modules + total_width = len(self.modules) * (self.module_width + self.padding) - self.padding + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = (SCREEN_HEIGHT - self.module_height) // 2 + + # Affichage des modules + x = start_x + for module in self.modules: + # Rectangle du module + module_rect = pygame.Rect(x, start_y, self.module_width, self.module_height) + mouse_pos = pygame.mouse.get_pos() + is_hovered = module_rect.collidepoint(mouse_pos) + color = self.module_hover if is_hovered else self.module_bg + pygame.draw.rect(surface, color, module_rect) + pygame.draw.rect(surface, WHITE, module_rect, 2) + + # Nom du module + name = self.font.render(module.name, True, WHITE) + surface.blit(name, (x + 10, start_y + 10)) + + # Description + description_lines = self.wrap_text(module.description, self.module_width - 20, self.small_font) + for i, line in enumerate(description_lines): + text = self.small_font.render(line, True, WHITE) + surface.blit(text, (x + 10, start_y + 45 + i * 20)) + + # Niveau actuel + level_text = self.small_font.render(module.get_level_description(), True, GREEN) + surface.blit(level_text, (x + 10, start_y + self.module_height - 30)) + + # Effet actuel + effect_text = self.small_font.render(module.get_effect_description(), True, YELLOW) + surface.blit(effect_text, (x + 10, start_y + self.module_height - 50)) + + x += self.module_width + self.padding + + def handle_event(self, event: pygame.event.Event) -> None: + if not self.is_visible: + return + + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + # Calcul des positions des modules + total_width = len(self.modules) * (self.module_width + self.padding) - self.padding + start_x = (SCREEN_WIDTH - total_width) // 2 + start_y = (SCREEN_HEIGHT - self.module_height) // 2 + + # Vérifier chaque module + x = start_x + for module in self.modules: + module_rect = pygame.Rect(x, start_y, self.module_width, self.module_height) + + if module_rect.collidepoint(event.pos): + if self.game.module_manager.upgrade_module(module.id): + self.game.notification_manager.add_notification( + f'Module {module.name} amélioré !', + GREEN + ) + self.hide() + break + + x += self.module_width + self.padding + + def wrap_text(self, text: str, max_width: int, font: pygame.font.Font) -> list[str]: + words = text.split() + lines = [] + current_line = [] + current_width = 0 + + for word in words: + word_surface = font.render(word + ' ', True, WHITE) + word_width = word_surface.get_width() + + if current_width + word_width > max_width: + if current_line: + lines.append(' '.join(current_line)) + current_line = [word] + current_width = word_width + else: + lines.append(word) + else: + current_line.append(word) + current_width += word_width + + if current_line: + lines.append(' '.join(current_line)) + + return lines[:2] # Limiter à 2 lignes pour éviter le débordement \ No newline at end of file diff --git a/src/game/ui/notification_manager.py b/src/game/ui/notification_manager.py new file mode 100644 index 0000000..4a72643 --- /dev/null +++ b/src/game/ui/notification_manager.py @@ -0,0 +1,143 @@ +import pygame +from typing import List, Tuple +import time +from utils.constants import SCREEN_WIDTH, SCREEN_HEIGHT + +class Notification: + def __init__(self, title: str, description: str, icon_path: str = None): + self.title = title + self.description = description + self.icon_path = icon_path + self.creation_time = time.time() + self.duration = 3.0 # Durée d'affichage en secondes + self.alpha = 255 # Pour l'effet de fade out + self.height = 80 + self.width = 300 + self.y_offset = 0 # Pour l'animation de slide + + # Chargement de l'icône si présente + self.icon = None + if icon_path: + try: + self.icon = pygame.image.load(icon_path).convert_alpha() + self.icon = pygame.transform.scale(self.icon, (50, 50)) + except Exception as e: + print(f'Erreur lors du chargement de l\'icône {icon_path}: {e}') + + def should_remove(self) -> bool: + return time.time() - self.creation_time > self.duration + + def update(self): + # Calcul de l'alpha pour le fade out + remaining_time = self.duration - (time.time() - self.creation_time) + if remaining_time < 0.5: # Fade out sur la dernière demi-seconde + self.alpha = int(255 * (remaining_time / 0.5)) + + # Animation de slide + target_y = 20 + self.y_offset + current_y = -self.height + (target_y + self.height) * min(1, (time.time() - self.creation_time) * 2) + self.y_offset = current_y + + def draw(self, screen: pygame.Surface): + # Création de la surface de notification avec transparence + notification_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA) + + # Fond semi-transparent + pygame.draw.rect(notification_surface, (0, 0, 0, min(180, self.alpha)), + (0, 0, self.width, self.height), border_radius=10) + + # Titre + font_title = pygame.font.Font(None, 32) + title_surface = font_title.render(self.title, True, (255, 255, 255)) + title_surface.set_alpha(self.alpha) + + # Description + font_desc = pygame.font.Font(None, 24) + desc_surface = font_desc.render(self.description, True, (200, 200, 200)) + desc_surface.set_alpha(self.alpha) + + # Position des éléments + icon_padding = 60 if self.icon else 10 + notification_surface.blit(title_surface, (icon_padding, 10)) + notification_surface.blit(desc_surface, (icon_padding, 40)) + + if self.icon: + icon_copy = self.icon.copy() + icon_copy.set_alpha(self.alpha) + notification_surface.blit(icon_copy, (5, 15)) + + # Affichage de la notification + screen.blit(notification_surface, (screen.get_width() - self.width - 20, self.y_offset)) + +class NotificationManager: + def __init__(self, game): + self.game = game + self.notifications: List[Notification] = [] + self.spacing = 10 # Espacement entre les notifications + self.font = pygame.font.Font(None, 36) + self.small_font = pygame.font.Font(None, 24) + + # Style des notifications + self.notification_width = 300 + self.notification_height = 80 + self.padding = 10 + self.spacing = 10 + + # Couleurs + self.bg_color = (50, 50, 50) + self.title_color = (255, 255, 255) + self.message_color = (200, 200, 200) + + def add_notification(self, title: str, description: str, icon_path: str = None): + notification = Notification(title, description, icon_path) + # Calcul du décalage vertical en fonction des notifications existantes + notification.y_offset = len(self.notifications) * (notification.height + self.spacing) + self.notifications.append(notification) + + def update(self): + # Mise à jour et suppression des notifications expirées + self.notifications = [n for n in self.notifications if not n.should_remove()] + + # Mise à jour des positions + for i, notification in enumerate(self.notifications): + notification.y_offset = i * (notification.height + self.spacing) + notification.update() + + def draw(self, screen: pygame.Surface): + # Position de départ (coin supérieur droit) + x = SCREEN_WIDTH - self.notification_width - self.padding + y = self.padding + + for notification in self.notifications: + # Calcul de l'opacité + alpha = notification.alpha + + # Surface semi-transparente pour le fond + bg = pygame.Surface((self.notification_width, self.notification_height)) + bg.fill(self.bg_color) + bg.set_alpha(int(alpha * 0.8)) # Fond légèrement transparent + screen.blit(bg, (x, y)) + + # Titre + title = self.font.render(notification.title, True, self.title_color) + title.set_alpha(alpha) + title_rect = title.get_rect(topleft=(x + self.padding, y + self.padding)) + screen.blit(title, title_rect) + + # Description + message = self.small_font.render(notification.description, True, self.message_color) + message.set_alpha(alpha) + message_rect = message.get_rect(topleft=(x + self.padding, y + title_rect.height + self.padding)) + screen.blit(message, message_rect) + + # Icône (si présente) + if notification.icon: + try: + icon = notification.icon.copy() + icon.set_alpha(alpha) + screen.blit(icon, (x + self.notification_width - 42, y + self.padding)) + except Exception as e: + print(f"Erreur lors du chargement de l'icône: {e}") + + # Mise à jour de la position pour la prochaine notification + y += self.notification_height + self.spacing \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..7c61329 --- /dev/null +++ b/src/main.py @@ -0,0 +1,42 @@ +from game.core.game import Game +from utils.sound_generator import SoundGenerator +from utils.constants import SOUND_DIR + +def check_assets(): + """Vérifie la présence des assets nécessaires""" + import os + from utils.constants import BASE_DIR + print("Vérification des assets:") + print(f"Dossier de travail actuel: {os.getcwd()}") + print(f"Le fichier existe: {os.path.exists(BASE_DIR)}") + print(f"Chemin complet: {BASE_DIR}") + +def check_sounds(): + """Vérifie la présence des fichiers sons""" + import os + from utils.constants import ( + SHOOT_SOUND, ENEMY_SHOOT_SOUND, HIT_SOUND, + ENEMY_DEATH_SOUND, PLAYER_HURT_SOUND, GAME_MUSIC + ) + print("Vérification des fichiers sons:") + sound_files = [ + SHOOT_SOUND, + ENEMY_SHOOT_SOUND, + HIT_SOUND, + ENEMY_DEATH_SOUND, + PLAYER_HURT_SOUND, + GAME_MUSIC + ] + for sound_file in sound_files: + print(f"Fichier {sound_file} existe: {os.path.exists(sound_file)}") + +if __name__ == '__main__': + # Vérifications initiales + check_assets() + check_sounds() + sound_generator = SoundGenerator() + sound_generator.generate_all_sounds(SOUND_DIR) + + # Lancement du jeu + game = Game() + game.run() \ No newline at end of file diff --git a/src/save/modules_state.json b/src/save/modules_state.json new file mode 100644 index 0000000..2c17615 --- /dev/null +++ b/src/save/modules_state.json @@ -0,0 +1 @@ +{"rapid_fire": {"is_unlocked": false, "level": 0}, "shield_boost": {"is_unlocked": false, "level": 0}, "damage_boost": {"is_unlocked": false, "level": 0}, "speed_boost": {"is_unlocked": false, "level": 0}, "health_regen": {"is_unlocked": false, "level": 0}, "multi_shot": {"is_unlocked": true, "level": 1}} \ No newline at end of file diff --git a/survival-shooter/src/utils/__init__.py b/src/utils/__init__.py similarity index 100% rename from survival-shooter/src/utils/__init__.py rename to src/utils/__init__.py diff --git a/survival-shooter/src/utils/constants.py b/src/utils/constants.py similarity index 70% rename from survival-shooter/src/utils/constants.py rename to src/utils/constants.py index 006680e..aa081fc 100644 --- a/survival-shooter/src/utils/constants.py +++ b/src/utils/constants.py @@ -16,10 +16,11 @@ BULLET_COLOR = (160, 50, 255) # Violet clair pour les projectiles HEALTH_COLOR = (220, 50, 50) # Rouge vif pour la santé XP_COLOR = (80, 220, 255) # Bleu néon pour l'XP +GOLD_COLOR = (255, 215, 0) # Couleur or pour les pièces # Player settings PLAYER_SPEED = 5 -PLAYER_SIZE = 32 +PLAYER_SIZE = 64 # Weapon settings BULLET_SPEED = 10 @@ -52,16 +53,19 @@ # Wave settings ENEMIES_PER_WAVE = 10 -WAVE_TRANSITION_TIME = 1000 -WAVE_SPEEDUP = 100 -WAVE_ENEMY_MULTIPLIER = 1.2 # Coefficient multiplicateur d'ennemis par vague +WAVE_ENEMY_MULTIPLIER = 1.2 +WAVE_SPEEDUP = 50 +WAVE_TRANSITION_TIME = 2000 # 2 secondes en millisecondes SHOOTER_ENEMY_CHANCE = 0.3 # 30% de chance d'avoir un ennemi qui tire ENEMY_BULLET_DAMAGE = 15 # Base paths -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Dossier src +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ASSETS_DIR = os.path.join(BASE_DIR, "assets") EFFECTS_DIR = os.path.join(ASSETS_DIR, "effects") +CHARACTERS_DIR = os.path.join(ASSETS_DIR, "characters") +ACHIEVEMENTS_DIR = os.path.join(ASSETS_DIR, "achievements") +ICONS_DIR = os.path.join(ASSETS_DIR, "icons") # Assets paths PLAYER_SPRITE = os.path.join(ASSETS_DIR,'sprites', "player.png") @@ -71,6 +75,20 @@ ENEMY_BULLET_SPRITE = os.path.join(ASSETS_DIR, "enemy_bullet.png") GAME_LOGO = os.path.join(ASSETS_DIR, "logo.ico") +# Character sprites +DEFAULT_CHARACTER_SPRITE = os.path.join(CHARACTERS_DIR, "default.png") +SPEEDER_CHARACTER_SPRITE = os.path.join(CHARACTERS_DIR, "speeder.png") +TANK_CHARACTER_SPRITE = os.path.join(CHARACTERS_DIR, "tank.png") + +# Achievement icons +FIRST_KILL_ICON = os.path.join(ACHIEVEMENTS_DIR, "first_kill.png") +RICH_ICON = os.path.join(ACHIEVEMENTS_DIR, "rich.png") +SURVIVOR_ICON = os.path.join(ACHIEVEMENTS_DIR, "survivor.png") + +# UI icons +COIN_ICON = os.path.join(ICONS_DIR, "coin.png") +SHIELD_ICON = os.path.join(ICONS_DIR, "shield.png") + # Sound paths SOUND_DIR = os.path.join(ASSETS_DIR, "sounds") SHOOT_SOUND = os.path.join(SOUND_DIR, "shoot.wav") @@ -79,6 +97,9 @@ ENEMY_DEATH_SOUND = os.path.join(SOUND_DIR, "enemy_death.wav") PLAYER_HURT_SOUND = os.path.join(SOUND_DIR, "player_hurt.wav") GAME_MUSIC = os.path.join(SOUND_DIR, "game_music.mp3") +COIN_SOUND = os.path.join(SOUND_DIR, "coin.wav") +ACHIEVEMENT_SOUND = os.path.join(SOUND_DIR, "achievement.wav") +PURCHASE_SOUND = os.path.join(SOUND_DIR, "purchase.wav") # Fireball settings FIREBALL_SPRITE = os.path.join(EFFECTS_DIR, "Part 3 Free.gif") @@ -138,9 +159,44 @@ # Score settings SCORE_FILE = os.path.join(BASE_DIR, "data", "highscore.json") +SAVE_FILE = os.path.join(BASE_DIR, "data", "game_save.json") # Vérification de l'existence du logo if not os.path.exists(GAME_LOGO): print(f"ATTENTION: Le fichier {GAME_LOGO} n'existe pas!") print(f"Chemin complet attendu: {os.path.abspath(GAME_LOGO)}") +# Achievements settings +ACHIEVEMENT_NOTIFICATION_DURATION = 3.0 +ACHIEVEMENT_NOTIFICATION_WIDTH = 300 +ACHIEVEMENT_NOTIFICATION_HEIGHT = 80 + +# Shop settings +CHARACTER_PREVIEW_SIZE = 100 +CHARACTER_BUTTON_WIDTH = 160 +CHARACTER_BUTTON_HEIGHT = 200 +CHARACTER_SPACING = 20 + +# Game modes settings +MODE_BUTTON_WIDTH = 200 +MODE_BUTTON_HEIGHT = 150 +MODE_SPACING = 30 + +# Rewards settings +COINS_PER_WAVE = 100 +COINS_PER_NORMAL_ENEMY = 10 +COINS_PER_SHOOTING_ENEMY = 15 +COINS_PER_BOSS = 500 + +# Création des dossiers s'ils n'existent pas +os.makedirs(CHARACTERS_DIR, exist_ok=True) +os.makedirs(ACHIEVEMENTS_DIR, exist_ok=True) +os.makedirs(ICONS_DIR, exist_ok=True) +os.makedirs(os.path.join(BASE_DIR, "data"), exist_ok=True) + +# Debug +DEBUG_MODE = True # Mettre à False en production + +# Limites de performance +MAX_ACTIVE_ENEMIES = 15 # Nombre maximum d'ennemis actifs à l'écran + diff --git a/survival-shooter/src/utils/sound_generator.py b/src/utils/sound_generator.py similarity index 67% rename from survival-shooter/src/utils/sound_generator.py rename to src/utils/sound_generator.py index 7b6b015..f663ccc 100644 --- a/survival-shooter/src/utils/sound_generator.py +++ b/src/utils/sound_generator.py @@ -7,7 +7,20 @@ class SoundGenerator: def __init__(self, sample_rate=44100): self.sample_rate = sample_rate + self.check_dependencies() + def check_dependencies(self): + """Vérifie si les dépendances nécessaires sont installées""" + try: + import numpy + import wave + print("Dépendances pour la génération de sons vérifiées avec succès.") + except ImportError as e: + print(f"ERREUR: Dépendance manquante pour la génération de sons: {e}") + print("Veuillez installer les dépendances avec: pip install numpy") + return False + return True + def _create_sound_buffer(self, data): # Amplification du son data = data * 1.5 # Augmentation de l'amplitude @@ -81,9 +94,29 @@ def save_sound(self, waveform, filename): except Exception as e: print(f"Erreur lors de la sauvegarde du son {filename}: {e}") + def generate_test_sound_wave(self): + """Génère un son de test simple""" + duration = 0.1 + t = np.linspace(0, duration, int(self.sample_rate * duration)) + frequency = 440 # La4 standard + waveform = np.sin(2 * np.pi * frequency * t) + waveform *= np.exp(-t * 10) # Ajout d'une enveloppe pour adoucir le son + return waveform + def generate_all_sounds(self, sound_dir): """Génère et sauvegarde tous les sons du jeu""" - os.makedirs(sound_dir, exist_ok=True) + print(f"Génération des sons dans le dossier: {sound_dir}") + + # Créer le dossier des sons s'il n'existe pas + try: + os.makedirs(sound_dir, exist_ok=True) + print(f"Dossier des sons créé/vérifié: {sound_dir}") + except Exception as e: + print(f"ERREUR lors de la création du dossier des sons: {e}") + return False + + # Son de test pour tous les sons manquants + test_sound = self.generate_test_sound_wave() # Génération des formes d'onde sounds = { @@ -91,11 +124,21 @@ def generate_all_sounds(self, sound_dir): "enemy_shoot.wav": self.generate_enemy_shoot_sound_wave(), "hit.wav": self.generate_hit_sound_wave(), "enemy_death.wav": self.generate_enemy_death_sound_wave(), - "player_hurt.wav": self.generate_player_hurt_sound_wave() + "player_hurt.wav": self.generate_player_hurt_sound_wave(), + "coin.wav": test_sound, + "achievement.wav": test_sound, + "purchase.wav": test_sound } # Sauvegarde des sons + success_count = 0 for filename, waveform in sounds.items(): - filepath = os.path.join(sound_dir, filename) - if not os.path.exists(filepath): - self.save_sound(waveform, filepath) \ No newline at end of file + full_path = os.path.join(sound_dir, filename) + try: + self.save_sound(waveform, full_path) + success_count += 1 + except Exception as e: + print(f"ERREUR lors de la sauvegarde du son {filename}: {e}") + + print(f"Génération des sons terminée: {success_count}/{len(sounds)} sons générés avec succès.") + return success_count == len(sounds) \ No newline at end of file diff --git a/survival-shooter/src/assets/sounds/enemy_death.wav b/survival-shooter/src/assets/sounds/enemy_death.wav deleted file mode 100644 index 0b6fbeea5d6d3bda2f33a04f55a6d9c573bc244f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17684 zcmW(*by!mm8yy=P8*2s(7~S37jUbrVilW$x2^I>9K`FNWz(5R4N)!<3knU~QL5vQ1@pMIeDsOPcvv&Ek!IZvG z_V8A`4?7EUY=BTs8sv+bjh`!+lvc|REz!;Di_8TXcSz`&&wwi z?8t}bXXSs&ugOm;NGtqNU{)k8x>ua`^KXg!FL0^2bfmoV&!fLNl^3d)YQpN8>M4!t z%?+(j+X6c~y7hal`}+nhMxKr}Ph6ZXAYYr0p;j#1W$JUhc=f_$k%RR0%9GV-xopTR zYzEPd)Wdk;{0PCS{%ZQ9M_NLiAbo$sA!DZLw0WpircJS(vxBJ<%;lMzng`jF>SgYm z>8BQu6F3;u6RfswWF0=tX?@1}cj3Mf+c&gsXx?ygWBtbDjqpvsHc~d`Y`nX%Y{Q)m z1snE9ybkBAcUbQe_8`=8T~Kg+kTAf&zu33IXN%Wvk2$wxmlMwU4oP+_>%W$-%oj~g z7=;=5>w0P_k@|^Tp6ArQa z{d$Hv%v#%<%Igbjj4RduocnG5Gp%6T_nOb{pFX|co0Ivf#)i6UwgsxI7(q?qc=%b7z&$B%Url>2$*HIN=!k@WI0whaMjKa_~#^=IEOT z%Jwt%pWSb>KV|>7{WbeZ`*jX<9>|IgKA3xuaY+4e@8RYn_Q#TsXPta=sxh|rEa`mA z#j*JRE=OHGb^S_W(5>FvY4>!}g6|tW?0tOhx$NcV?1Hxz@Be-(`ojF~Qp73oDO>-y zqFPda_us9yqg}Rr=0mu#(y8fL43)w-#HkgW7gsNjteHTS;r^(1IEado`Ug#Ay$(aB zsiD~+4JB2JqLM*yN(G?l4I-7?YvkWzk0<# zK`*g3X=`%M9fx~nsln;AjEV@&3&;FsqA-TaS~m7e9Ft?{Zkdm%gR z)rD6|uasXUW&38o&q8Ls`OoI%=@-1`&!71}b$n9#_|n7H2RrWAt_SmW1fi9EQyYPxdMG{Ovl`vAeCRW$0g7V|9Z`y;|L|8sqAz%JILu{~A|x zmyegB%lk_2m#+SXl=}R({r%xL^_TVUE5CpL9{7Erbh$LLtg+0YJhxo2!u_vJCBO1_ zRbzE|ZBJcl{msUnCPZ^Y>$ iO_6_XPF*1<;l{;x|SccbJ0B1kPs6-=|%ndoX{o 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 diff --git a/survival-shooter/src/assets/sounds/hit.wav b/survival-shooter/src/assets/sounds/hit.wav deleted file mode 100644 index c10b2ea49d0eb847768290285abb590fb0ab7807..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8864 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
6@j9_te`$<(hSem$Gbpa%bHc2!|* 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 diff --git a/survival-shooter/src/game/__pycache__/enemy.cpython-312.pyc b/survival-shooter/src/game/__pycache__/enemy.cpython-312.pyc deleted file mode 100644 index 2c504446b954c1b5e185d847f7f4f8575f1011e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11314 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 G^8W?!*&|5+ diff --git a/survival-shooter/src/game/__pycache__/player.cpython-312.pyc b/survival-shooter/src/game/__pycache__/player.cpython-312.pyc deleted file mode 100644 index ea23ad8498b097136b9523cc8fe3482c6e0d6347..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10143 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 diff --git a/survival-shooter/src/game/__pycache__/weapon.cpython-312.pyc b/survival-shooter/src/game/__pycache__/weapon.cpython-312.pyc deleted file mode 100644 index fa95123a2cfc897f4395df9717acc38cd6823bf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4698 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 Q#y^IrLaO-{0-`SXFW@+c8~^|S diff --git a/survival-shooter/src/game/player.py b/survival-shooter/src/game/player.py deleted file mode 100644 index 7615e23..0000000 --- a/survival-shooter/src/game/player.py +++ /dev/null @@ -1,153 +0,0 @@ -import pygame -import math -from utils.constants import * -from game.weapon import Bullet - -class Player: - def __init__(self, x, y): - super().__init__() - self.x = x - self.y = y - self.health = 100 - self.score = 0 - - # Chargement et configuration du sprite du joueur - try: - self.original_image = pygame.image.load(PLAYER_SPRITE).convert_alpha() - # Augmentation de la taille à 96x96 (PLAYER_SIZE * 3) - self.original_image = pygame.transform.scale(self.original_image, (PLAYER_SIZE * 3, PLAYER_SIZE * 3)) - # Rotation initiale de 270 degrés + retournement du sprite - self.original_image = pygame.transform.rotate(self.original_image, 270) - # Retournement horizontal du sprite - self.original_image = pygame.transform.flip(self.original_image, True, False) - self.image = self.original_image - except Exception as e: - print(f"Erreur lors du chargement de l'image du joueur: {e}") - # Fallback au dessin par défaut avec la nouvelle taille - self.original_image = pygame.Surface((PLAYER_SIZE * 3, PLAYER_SIZE * 3), pygame.SRCALPHA) - center = PLAYER_SIZE * 1.5 - pygame.draw.circle(self.original_image, (*PLAYER_COLOR, 128), (center, center), PLAYER_SIZE * 1.5) - pygame.draw.circle(self.original_image, PLAYER_COLOR, (center, center), PLAYER_SIZE) - self.image = self.original_image - - self.rect = self.image.get_rect(center=(x, y)) - self.bullets = pygame.sprite.Group() - self.last_shot = 0 - self.trail = [] - self.angle = 0 # Pour suivre l'angle de rotation actuel - self.shield_active = False - self.shield_duration = 5000 # Durée du bouclier en millisecondes - self.shield_cooldown = 10000 # Temps de recharge du bouclier - self.last_shield_activation = 0 - - def update(self): - keys = pygame.key.get_pressed() - if keys[pygame.K_z] or keys[pygame.K_w]: # Haut - self.move(0, -PLAYER_SPEED) - if keys[pygame.K_s]: # Bas - self.move(0, PLAYER_SPEED) - if keys[pygame.K_q] or keys[pygame.K_a]: # Gauche - self.move(-PLAYER_SPEED, 0) - if keys[pygame.K_d]: # Droite - self.move(PLAYER_SPEED, 0) - - # Rotation en fonction de la position de la souris - mouse_x, mouse_y = pygame.mouse.get_pos() - dx = mouse_x - self.rect.centerx - dy = mouse_y - self.rect.centery - self.angle = math.degrees(math.atan2(-dy, dx)) - - # Rotation du sprite - self.image = pygame.transform.rotate(self.original_image, self.angle) - self.rect = self.image.get_rect(center=self.rect.center) - - # Gestion du tir avec le clic gauche - mouse_buttons = pygame.mouse.get_pressed() - if mouse_buttons[0]: # Clic gauche - self.shoot() - - # Mise à jour des balles - self.bullets.update() - - # Supprime les balles qui sortent de l'écran - for bullet in self.bullets: - if (bullet.rect.x < 0 or bullet.rect.x > SCREEN_WIDTH or - bullet.rect.y < 0 or bullet.rect.y > SCREEN_HEIGHT): - bullet.kill() - - if self.shield_active: - current_time = pygame.time.get_ticks() - if current_time - self.last_shield_activation >= self.shield_duration: - self.shield_active = False - - def move(self, dx, dy): - self.x += dx - self.y += dy - # Empêche le joueur de sortir de l'écran - self.x = max(0, min(self.x, SCREEN_WIDTH)) - self.y = max(0, min(self.y, SCREEN_HEIGHT)) - self.rect.center = (self.x, self.y) - - def play_sound(self, sound_type): - if hasattr(self, 'game'): - print(f"Game instance exists, sound_muted: {self.game.sound_muted}") - if not self.game.sound_muted: - if sound_type == 'shoot' and self.game.shoot_sound: - print("Playing shoot sound") - self.game.shoot_sound.play() - elif sound_type == 'hurt' and self.game.player_hurt_sound: - print("Playing hurt sound") - self.game.player_hurt_sound.play() - else: - print("No game instance found") - - def shoot(self): - current_time = pygame.time.get_ticks() - if current_time - self.last_shot >= SHOOT_COOLDOWN: - mouse_x, mouse_y = pygame.mouse.get_pos() - dx = mouse_x - self.x - dy = mouse_y - self.y - distance = math.sqrt(dx**2 + dy**2) - - if distance != 0: - direction = (dx/distance, dy/distance) - bullet = Bullet(self.x, self.y, direction) - self.bullets.add(bullet) - if hasattr(self, 'game') and self.game.shoot_sound and not self.game.sound_muted: - pygame.mixer.find_channel(True).play(self.game.shoot_sound) - self.last_shot = current_time - - def draw(self, screen): - # Dessin des projectiles - for bullet in self.bullets: - bullet.draw(screen) - - # Dessin du joueur (toujours visible) - screen.blit(self.image, self.rect) - - # Dessin du bouclier par-dessus le joueur - if self.shield_active: - # Effet de bouclier plus visible avec double cercle - pygame.draw.circle(screen, (0, 255, 255), self.rect.center, PLAYER_SIZE * 1.5, 3) - pygame.draw.circle(screen, (0, 200, 255), self.rect.center, PLAYER_SIZE * 1.2, 2) - - def take_damage(self, amount): - if not self.shield_active: # Ne prend des dégâts que si le bouclier n'est pas actif - self.health = max(0, self.health - amount) # Empêche la santé de devenir négative - self.play_sound('hurt') - if self.health <= 0: - self.die() - - def die(self): - # Logique pour la mort du joueur - if hasattr(self, 'game'): - self.game.game_over() - - def gain_score(self, points): - self.score += points - - def activate_shield(self): - current_time = pygame.time.get_ticks() - if not self.shield_active and (current_time - self.last_shield_activation >= self.shield_cooldown): - self.shield_active = True - self.last_shield_activation = current_time \ No newline at end of file diff --git a/survival-shooter/src/ui/__pycache__/menu_manager.cpython-312.pyc b/survival-shooter/src/ui/__pycache__/menu_manager.cpython-312.pyc deleted file mode 100644 index d348eee7dde027363d4e1a04c2381b6114210875..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5135 zcmcgwT}&I<6`rxj_89Dt9~>MK5~t0N4S^;h+a`pm*HrIG}1uBb-#7wdxGieanPnk)h zXaLKhyGfJCfi;UJuolq_mKQBxts)P$PPBrpPu6+rwfZ3`mKX}fLNgKx=bnLXf^{SO zy}L`dO0#K3paW>q>}=`WTtCdnKOoM?zkOj9tYd`2I+$TD87lg~U1>=|2m&5X<7Ps0 zl4h2}M3Q0}Hyw{D8hZLLawl>p6m6Gp#p8-ZrsS#)5}ry#reIYAh4Za4IMF`t$=_vGJd8Yp`ERGj~mg|HckyQ#1vGptF*QLHqMc)9gIgr{L^bZe@`9@vA@zIIF3Gda^aqs+m zA}OU@fQZW%9P#$~T=Q|LhvRbqMpzD&CZH??m_ibSIl49ej+kz8G_ua5ofU)R1o z`j1|ZMY9Vc3H?Nf-;qc(l+^0=rV>%2lE(G@*c;I5a$VA0MKfLZ503e09%rNHLK}WFI`Pha>ypha5n2 z5RC(k6HICB8)$szLkU^ZgMYXx_{Bl=D}M*fF42F~^z`Hg@6N~C*|p+Zy|Bsqt0RkV zNe|@bP$G}tR!~Z|x@H{p_3O-m5!TG}K>QNXOq9!YUTJ~>(uk&#Tn^AQN3}uk3_Z_}Rpoj4 zgd^Zd{yCW468-Sg@CM(SBbi6-OWX_VvNh|zsJbs^-94(iCv&AHytuS{Dcjbgw)JFM>68;|T*(yY_g9ovxIKQDU*o*A^1&)x4FlDur$Au8g(qEP zl!Bh9CFF{_K`BVIX@deQ^-?QK^9ti03&IZBsG^AMv)rCkRn$-lvketXpqXYxL*6sX zK>is)5E4{*MYH`1njS;3!@~paNh%sGAoHd}VM((E`$m1ffN*`Vf6TAhb1lDbaKJyN z@xjqP@6{2%7sMsV34(`E<8DSHpf2g{uUT>$A}27Z4xm|=I#ik-I+Nz+o&YcMWiX#G zZ}&SFJ05mCJ@d+*Io|We{KtmhH>~d;S+H#J4G(PhZJ&R%!Jj}B>+jcRc@zeZnx356 zW>OS?{l{Z6~@d>qmB6Ie7#^GLf z{0OF$0owdza+5!+1B$0HU;bZ!a%t|H0h%_ZU~d4_SOC-n=~PSC*8-GFo61fm7Z@Yw z;ibq0FaRTp7J>qRd=E`6_{PPL_TB`y^Ii6hr^l6Vy2~Yy+v98`Oy59-&1nWG=6dT@!_Y^(pa{=S8eajwhyT718au0 zE9>pUnQNmPe6aigv@CI1cbDq!+T<_OV-WDz@P8sPR80bWPz!T$F%iYOO%Og#grfQg zK@K4ZEk3k%C@R1 diff --git a/survival-shooter/src/utils/__pycache__/constants.cpython-312.pyc b/survival-shooter/src/utils/__pycache__/constants.cpython-312.pyc deleted file mode 100644 index dc42864790ce73b8da10c40c181b21e58c5fb2ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4950 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 diff --git a/survival-shooter/src/utils/__pycache__/spritesheet.cpython-312.pyc b/survival-shooter/src/utils/__pycache__/spritesheet.cpython-312.pyc deleted file mode 100644 index 7b99660874858678615ee0e3c53df87c358a152c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2470 zcma)7OH3PA6n*pcA28T1N*ZhuXySYbrHa&$^yBaeY56peAQic_jA#5I#&%}L0lVW0 zMOhFj5=JaAsw&(a5nWKJRq3V*yGY%TLK-w>fz(CCjzXnYRF!()*n=IyqEGgn`|iDS z?tO30eeWlW#fV^R{r+R9lSSw^JkbVSRQAq*GK)CG5m7XRIC6;Ks3DR>*AYiQKpeA3 z79xkJ6yavK@ub&H3D{Z^+c;4G`?Nt6mAyJpW)Y777Y&gd@c<1`90`)0;3&BcHY< zD1}ay=TSHDiB!lx$}3D{)F0v% zIvV$LiX{+_-Qxwx=Z_}Be#KPwy29{+5Em2%5Mz>?QfQHn1{HlU66IqUh~P!M-7c4F z-y3N4-WGX5^m>|`-@4Fz+1nluBuDv}Bzmt$q^{(!cOWU;i`?@^FNooIT;c_<_=-UY zc#~2jDtgPRXiB6Mz0VhkMI@guZ7cisO`25?MTlMy6KIEI4Aq;?#(8;8e&K9>=4@W7 zU30d+aCSX&cC9%-m~4M;e*N#wO8b=jP~Jrp1e>j^w$|mhm(Q+NcdVN`VV>>E-OROR zo7T0WpW{%>dj7D(ILj!Y6rqWQEOv<$S#JT82TpJAFoT;Ee1f?;t z9epy7P=+n@(Ph3V@adq6114(-fWgYT-~ppEdRgy$E6VOsI67tIbyTWN2J^G49GBbeOx?VR~cI4r0-);fnrc2h6wv8#Y-HH7M z0Utby4%#K?5Ace9puf%2{ZW@kp@*XpNx+{?VFZ5+T9^&-(72MqiiIcT9~Rvfp#p~x z;|h^dn6U^ag%vi;M?zs7sGbfAnHYyZxC8MhbT|ROkU|VAgkQuT7dpB$1ibBv5vMA= zopzKrVaZ+vapJ!qCeWt!wb{Dqy1adX%~yWw_}=-AbIt0RWHKTeZ2Jpa!!uh$ zKDcmq>C)rWa{Y4fhqF)kr-Q3ax7PKy|71~3ZH}2azl#ip*2!y|j^nw`nbBROx3p%- zjoP|-%bX=IEVSgqYqb}%%+`sM)5a{3eKYINS+{KV95LOI^Gx5!Tc_X4_y1gTdc$^X zwr9F0-;x*dXBN7j+b(a~Yjf7z%{lwbP`-ZV)32!o&l2&tW6`*DbFpH%BZu~p)~~1_H>bm1ipZeL%hQJ6A3=X z3Flzq))iPn3Gp$-6cpe%_XXm~n50b#yfGI%8hg~4s1OkmbF@g20zYgj6=QIjGi0^=yKpWPYNp{of$bPnFJ++GnLoYGu*)Ug6^*rp!HOya}ySPxl zFtBuPSzbHV{oLHML*bCEO8cZmI1dQDZWdn@_-69?6r;~K8t0NxtebtlyGehv;1Q}J zR^0>P5@?T+`m(wR0q?LX@bW~=`v+*3WSQ#Ssw&2@*FZClLNpG9PF6d$>YAtR!|VNh zw|m=tZGGK+{fgmMUw22pr?;)6fOr|h;u6&?`}(Ve%H9>Of(>5~q7wwHhai4MmK_$^ WPOPG;4RghW;V;@rICc>T72rS0`8+rP From af27914544255f31a854b9ac751f9dd7a693a404 Mon Sep 17 00:00:00 2001 From: Shoyers Date: Mon, 31 Mar 2025 17:45:34 +0200 Subject: [PATCH 2/3] =?UTF-8?q?add=20button=20restart,=20main=20menu,=20le?= =?UTF-8?q?ave=20sur=20l'=C3=A9cran=20Game=20Over?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- save_data.json | 2 +- src/data/game_save.json | 2 +- src/game/core/game.py | 227 +++++++++++++++++++++++++--------------- src/game/ui/button.py | 20 +++- 4 files changed, 163 insertions(+), 88 deletions(-) diff --git a/save_data.json b/save_data.json index 2ccf42f..1026cc2 100644 --- a/save_data.json +++ b/save_data.json @@ -1 +1 @@ -{"coins": 71806, "achievements": {"first_kill": {"unlocked": true, "progress": 0}, "survivor": {"unlocked": false, "progress": 0}, "rich": {"unlocked": true, "progress": 100}, "collector": {"unlocked": false, "progress": 0}, "master": {"unlocked": false, "progress": 0}}} \ No newline at end of file +{"coins": 76226, "achievements": {"first_kill": {"unlocked": true, "progress": 0}, "survivor": {"unlocked": false, "progress": 0}, "rich": {"unlocked": true, "progress": 100}, "collector": {"unlocked": false, "progress": 0}, "master": {"unlocked": false, "progress": 0}}} \ No newline at end of file diff --git a/src/data/game_save.json b/src/data/game_save.json index c2f7831..f35a81a 100644 --- a/src/data/game_save.json +++ b/src/data/game_save.json @@ -1 +1 @@ -{"highscore": 10200, "wave": 1, "score": 0, "records": {"classic": {"best_survival_time": 37}, "survival": {"best_wave": 2}, "boss_rush": {"best_bosses_killed": 0}}} \ No newline at end of file +{"highscore": 10200, "wave": 1, "score": 0, "records": {"classic": {"best_survival_time": 51}, "survival": {"best_wave": 2}, "boss_rush": {"best_bosses_killed": 0}}} \ No newline at end of file diff --git a/src/game/core/game.py b/src/game/core/game.py index d953693..e533a14 100644 --- a/src/game/core/game.py +++ b/src/game/core/game.py @@ -117,6 +117,14 @@ def __init__(self): self.highscore = self.load_highscore() self.show_module_menu = False # Nouvel état pour le menu des modules + # Boutons pour l'écran Game Over + from game.ui.button import Button + self.game_over_buttons = { + 'restart': Button("Recommencer", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 - 40, 200, 50), + 'menu': Button("Menu Principal", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 + 30, 200, 50), + 'quit': Button("Quitter le jeu", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 + 100, 200, 50) + } + # Variables de vague self.wave = 1 self.wave_enemies_left = ENEMIES_PER_WAVE @@ -207,72 +215,80 @@ def draw_stars(self): def run(self): running = True while running: - # Gestion des événements - for event in pygame.event.get(): + # Récupérer les événements une seule fois par frame + events = pygame.event.get() + + # Vérifier si on doit quitter le jeu + for event in events: if event.type == pygame.QUIT: running = False - elif event.type == pygame.KEYDOWN: - if event.key == pygame.K_m: - self.toggle_sound() - elif event.key == pygame.K_ESCAPE: - if self.game_state == PLAYING: - self.game_state = PAUSED - elif self.game_state == PAUSED: - self.game_state = PLAYING - self.show_module_menu = False # Fermer le menu des modules - elif self.game_state == 'mode_selection': - self.game_state = MENU - elif event.key == pygame.K_SPACE: - if self.game_state == MENU: + + # Gestion spécifique à l'état de Game Over + if self.game_state == GAME_OVER: + self.handle_game_over_events(events) + + # Gestion standard des événements pour les autres états + else: + for event in events: + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_m: + self.toggle_sound() + elif event.key == pygame.K_ESCAPE: + if self.game_state == PLAYING: + self.game_state = PAUSED + elif self.game_state == PAUSED: + self.game_state = PLAYING + self.show_module_menu = False # Fermer le menu des modules + elif self.game_state == 'mode_selection': + self.game_state = MENU + elif event.key == pygame.K_SPACE: + if self.game_state == MENU: + self.game_mode_menu.show() + self.game_state = 'mode_selection' + elif event.type == pygame.MOUSEBUTTONDOWN: + if event.button == 1: # Clic gauche + mouse_x, mouse_y = event.pos + button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING + button_y = SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING + if (button_x <= mouse_x <= button_x + SOUND_BUTTON_SIZE and + button_y <= mouse_y <= button_y + SOUND_BUTTON_SIZE): + self.toggle_sound() + elif event.button == 3: # Clic droit + if self.game_state == PLAYING: + self.player.activate_shield() + + # Gestion des événements du menu + if self.game_state == MENU: + action = self.menu_manager.handle_event(event) + if action == 'start_game': self.game_mode_menu.show() self.game_state = 'mode_selection' - elif self.game_state == GAME_OVER: - self.reset_game() - self.game_state = MENU - elif event.type == pygame.MOUSEBUTTONDOWN: - if event.button == 1: # Clic gauche - mouse_x, mouse_y = event.pos - button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - button_y = SCREEN_HEIGHT - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING - if (button_x <= mouse_x <= button_x + SOUND_BUTTON_SIZE and - button_y <= mouse_y <= button_y + SOUND_BUTTON_SIZE): - self.toggle_sound() - elif event.button == 3: # Clic droit - if self.game_state == PLAYING: - self.player.activate_shield() - - # Gestion des événements du menu - if self.game_state == MENU: - action = self.menu_manager.handle_event(event) - if action == 'start_game': - self.game_mode_menu.show() - self.game_state = 'mode_selection' - elif action == 'quit': - running = False - elif action == 'restart_game': - self.current_game_mode.initialize() - self.game_state = PLAYING - elif action == 'resume_game': - self.game_state = PLAYING - elif action == 'go_to_main_menu': - self.game_state = MENU - elif self.game_state == 'mode_selection': - action = self.game_mode_menu.handle_event(event) - if action: - if action == 'back': + elif action == 'quit': + running = False + elif action == 'restart_game': + self.current_game_mode.initialize() + self.game_state = PLAYING + elif action == 'resume_game': + self.game_state = PLAYING + elif action == 'go_to_main_menu': self.game_state = MENU - elif action.startswith('mode_'): - mode_id = action[5:] # Enlève le préfixe 'mode_' - if self.select_game_mode(mode_id): - self.game_state = PLAYING - self.current_game_mode.initialize() - # On n'affiche le menu des modules que s'il y en a de débloqués - unlocked_modules = [module for module in self.module_manager.modules.values() if module.is_unlocked] - if unlocked_modules: - self.module_selection.visible = True - self.module_selection.modules = unlocked_modules - elif self.game_state == PLAYING or self.game_state == PAUSED: - self.module_selection.handle_event(event) + elif self.game_state == 'mode_selection': + action = self.game_mode_menu.handle_event(event) + if action: + if action == 'back': + self.game_state = MENU + elif action.startswith('mode_'): + mode_id = action[5:] # Enlève le préfixe 'mode_' + if self.select_game_mode(mode_id): + self.game_state = PLAYING + self.current_game_mode.initialize() + # On n'affiche le menu des modules que s'il y en a de débloqués + unlocked_modules = [module for module in self.module_manager.modules.values() if module.is_unlocked] + if unlocked_modules: + self.module_selection.visible = True + self.module_selection.modules = unlocked_modules + elif self.game_state == PLAYING or self.game_state == PAUSED: + self.module_selection.handle_event(event) # Gestion des entrées continues (mouvement et tir) if self.game_state == PLAYING: @@ -357,7 +373,11 @@ def run(self): self.screen.blit(resume_text, resume_rect) elif self.game_state == GAME_OVER: self.draw_game_over() - self.handle_game_over_input() + + # Mise à jour des boutons de Game Over (état de survol) + mouse_pos = pygame.mouse.get_pos() + for button in self.game_over_buttons.values(): + button.update_hover_state() # Mise à jour de l'écran pygame.display.flip() @@ -787,6 +807,64 @@ def update_enemies(self): if self.player.health <= 0: self.game_over() + def handle_game_over_events(self, events): + """Gère les événements dans l'écran de game over""" + for event in events: + print(f"Event dans game_over: {event}") + + if event.type == pygame.QUIT: + print("Événement QUIT détecté") + pygame.quit() + sys.exit() + + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + mouse_pos = event.pos + print(f"Clic à la position: {mouse_pos}") + + # Vérifier les clics sur les boutons + if self.game_over_buttons['restart'].is_clicked(mouse_pos): + print("Bouton 'Recommencer' cliqué") + self.reset_game() + self.current_game_mode.initialize() + self.game_state = PLAYING + return + + elif self.game_over_buttons['menu'].is_clicked(mouse_pos): + print("Bouton 'Menu Principal' cliqué") + self.reset_game() + self.game_state = MENU + return + + elif self.game_over_buttons['quit'].is_clicked(mouse_pos): + print("Bouton 'Quitter le jeu' cliqué") + pygame.quit() + sys.exit() + else: + print(f"Clic en dehors des boutons") + + # Conserver aussi la gestion des touches pour les utilisateurs habitués + elif event.type == pygame.KEYDOWN: + print(f"Touche pressée: {pygame.key.name(event.key)}") + if event.key == pygame.K_SPACE: + print("ESPACE pressé - lancement du jeu") + self.reset_game() + self.current_game_mode.initialize() + self.game_state = PLAYING + return + elif event.key == pygame.K_ESCAPE: + print("ÉCHAP pressé - retour au menu") + self.reset_game() + self.game_state = MENU + return + + # Mettre à jour l'état de survol des boutons même sans événement + mouse_pos = pygame.mouse.get_pos() + for button_name, button in self.game_over_buttons.items(): + old_state = button.is_hovered + button.is_hovered = button.rect.collidepoint(mouse_pos) + if old_state != button.is_hovered: + print(f"Bouton '{button_name}' état survol changé: {button.is_hovered}") + def draw_game_over(self): """Affiche l'écran de game over""" # Fond semi-transparent @@ -810,26 +888,11 @@ def draw_game_over(self): highscore_rect = highscore_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 40)) self.screen.blit(highscore_text, highscore_rect) - # Bouton Restart - restart_text = self.font.render("Appuyez sur ESPACE pour recommencer", True, CYAN) - restart_rect = restart_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3)) - self.screen.blit(restart_text, restart_rect) - - # Bouton Quitter - quit_text = self.font.render("Appuyez sur ÉCHAP pour quitter", True, WHITE) - quit_rect = quit_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 3 // 4)) - self.screen.blit(quit_text, quit_rect) - - def handle_game_over_input(self): - """Gère les entrées dans l'écran de game over""" - for event in pygame.event.get(): - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_SPACE: - self.reset_game() - self.game_state = PLAYING - elif event.key == pygame.K_ESCAPE: - pygame.quit() - sys.exit() + # Affichage des boutons et débug de leur position + print("Position des boutons de game over:") + for name, button in self.game_over_buttons.items(): + print(f"Bouton '{name}': {button.rect}") + button.draw(self.screen) def save_game_state(self): """Sauvegarde l'état du jeu""" diff --git a/src/game/ui/button.py b/src/game/ui/button.py index f832dde..419322a 100644 --- a/src/game/ui/button.py +++ b/src/game/ui/button.py @@ -17,9 +17,15 @@ def __init__(self, text, x, y, width, height): # Animation self.pulse = 0 self.pulse_direction = 1 + + # Pour diagnostiquer les problèmes de détection + self.last_clicked = False def draw(self, screen): """Dessine le bouton""" + # Mise à jour de l'état de survol + self.update_hover_state() + # Animation de pulsation pour le bouton survolé if self.is_hovered: self.pulse += 0.1 * self.pulse_direction @@ -57,10 +63,16 @@ def draw(self, screen): text_rect.y -= 2 screen.blit(text_surface, text_rect) - - # Mise à jour de l'état de survol - self.is_hovered = self.rect.collidepoint(pygame.mouse.get_pos()) + + def update_hover_state(self): + """Mise à jour de l'état de survol du bouton""" + mouse_pos = pygame.mouse.get_pos() + self.is_hovered = self.rect.collidepoint(mouse_pos) def is_clicked(self, pos): """Vérifie si le bouton est cliqué""" - return self.rect.collidepoint(pos) \ No newline at end of file + was_clicked = self.rect.collidepoint(pos) + if was_clicked: + print(f"Bouton '{self.text}' cliqué - Rect: {self.rect}, Pos: {pos}") + self.last_clicked = True + return was_clicked \ No newline at end of file From 7fb32366a6a0920cf46d99d81425a5e1a4a0b930 Mon Sep 17 00:00:00 2001 From: Shoyers Date: Tue, 1 Apr 2025 06:50:29 +0200 Subject: [PATCH 3/3] update --- plan.txt | 61 ++++++++++- prompt.txt | 47 ++++++++ requirements.txt | 4 +- save_data.json | 2 +- src/assets/sounds/enemy_death.wav | Bin 17684 -> 17684 bytes src/assets/sounds/hit.wav | Bin 8864 -> 8864 bytes src/data/game_save.json | 2 +- src/game/core/enemy.py | 84 ++++++++++++++- src/game/core/game.py | 116 ++++++++++++++++---- src/game/core/player.py | 168 +++++++++++++++++++++-------- src/game/core/weapon.py | 81 +++++++++++++- src/game/modes/classic_mode.py | 113 +++++++------------ src/game/modules/module_manager.py | 50 ++++++++- src/game/ui/character_shop.py | 6 +- src/game/ui/menu_manager.py | 4 +- src/game/ui/menus.py | 5 +- src/save/modules.json | 1 + src/utils/constants.py | 2 +- 18 files changed, 585 insertions(+), 161 deletions(-) create mode 100644 src/save/modules.json diff --git a/plan.txt b/plan.txt index 79aacf2..eb2cf6d 100644 --- a/plan.txt +++ b/plan.txt @@ -60,4 +60,63 @@ - Sprites du joueur avec rotation - Sprites des ennemis - Logo du jeu -- Interface graphique cohérente \ No newline at end of file +- Interface graphique cohérente + +8. Système de Modules et Progression +- Modules permanents (achetables) : + * Interface d'achat avec pièces + * Niveaux d'amélioration (1 à 3) + * Sauvegarde des modules débloqués +- Modules temporaires (coffres) : + * Menu de sélection en jeu + * Effets cumulables avec les modules permanents +- Types d'effets : + * Tir rapide (cooldown réduit) + * Bouclier amélioré (cooldown réduit) + * Boost de dégâts + * Boost de vitesse + * Régénération de santé + * Multi-tir (projectiles multiples) + * Attraction de pièces + * Tirs critiques + * Tirs explosifs + * Bouclier réfléchissant + * Ralentissement d'ennemis + * Bonus de pièces + * Tirs perçants + +9. Économie et Collectibles +- Système de pièces : + * Drop sur les ennemis tués + * Interface d'affichage + * Sauvegarde entre les sessions +- Coffres : + * Spawn aléatoire + * Sélection de modules temporaires +- Système d'attraction magnétique pour les pièces + +10. Personnages et Statistiques +- Système de personnages débloquables : + * Stats différentes (santé, vitesse, dégâts) + * Sprites personnalisés + * Sauvegarde du personnage sélectionné +- Application des statistiques de base aux personnages + +11. Modes de Jeu +- Mode Classique (survie continue) +- Mode Survie (vagues progressives avec records) +- Mode Boss Rush (combats de boss consécutifs) +- Sélection de mode via menu dédié +- Records spécifiques pour chaque mode : + * Temps de survie (Classique) + * Vague maximale (Survie) + * Boss vaincus (Boss Rush) + +12. Système de Sauvegarde +- Sauvegarde des progrès en JSON : + * High scores pour chaque mode + * Modules débloqués et leur niveau + * Personnages débloqués + * Économie (pièces accumulées) +- Chargement automatique au démarrage +- Sauvegarde automatique à la fermeture \ No newline at end of file diff --git a/prompt.txt b/prompt.txt index e00267a..6bb409b 100644 --- a/prompt.txt +++ b/prompt.txt @@ -84,3 +84,50 @@ ses réflexes et sa précision. - Méthodes réutilisables pour les éléments communs - Meilleure organisation du code UI - Séparation des responsabilités menu/jeu + +## Prompt 14 - Système de Modes de Jeu +- Implémentation de trois modes distincts (Classique, Survie, Boss Rush) +- Menu de sélection de mode avec descriptions visuelles +- Interface adaptée à chaque mode de jeu +- Records spécifiques à chaque mode +- HUD personnalisé selon le mode actif + +## Prompt 15 - Boutique de Personnages +- Système de personnages avec statistiques différentes +- Interface de sélection avec aperçu des personnages +- Animation des personnages dans la boutique +- Système de déblocage de personnages avec des pièces +- Affichage des statistiques comparatives + +## Prompt 16 - Système d'Économie +- Implémentation du système de pièces +- Drop de pièces à la mort des ennemis +- Animation de collecte des pièces +- Interface d'affichage du solde +- Sauvegarde de l'économie entre les sessions + +## Prompt 17 - Modules Permanents +- Boutique de modules améliorables +- Interface d'achat avec description des effets +- Système de niveaux d'amélioration +- Application des effets au joueur + +## Prompt 18 - Modules Temporaires et Coffres +- Système de coffres à la mort des ennemis +- Menu de sélection de modules temporaires en jeu +- Cumul des effets temporaires et permanents +- Diversité des bonus temporaires + +## Prompt 19 - Boss +- Barre de vie dédiée pour les boss + +## Prompt 20 - Interaction avec l'Environnement +- Système de magnétisme pour les pièces + +## Prompt 21 - Système de Notifications +- Interface de notification dans le coin de l'écran +- Animation de fade-in et fade-out pour les notifications +- File d'attente pour gérer plusieurs notifications +- Support des icônes personnalisées +- Adaptable à plusieurs contextes (boutique, combats, etc.) +- Utile pour la gestion des hauts faits (achievements) diff --git a/requirements.txt b/requirements.txt index 03ddd5d..6d7bec6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -numpy==2.2.3 -pygame==2.5.2 +numpy==2.2.4 +pygame==2.6.1 diff --git a/save_data.json b/save_data.json index 1026cc2..5365bb6 100644 --- a/save_data.json +++ b/save_data.json @@ -1 +1 @@ -{"coins": 76226, "achievements": {"first_kill": {"unlocked": true, "progress": 0}, "survivor": {"unlocked": false, "progress": 0}, "rich": {"unlocked": true, "progress": 100}, "collector": {"unlocked": false, "progress": 0}, "master": {"unlocked": false, "progress": 0}}} \ No newline at end of file +{"coins": 68079, "achievements": {"first_kill": {"unlocked": true, "progress": 0}, "survivor": {"unlocked": false, "progress": 0}, "rich": {"unlocked": true, "progress": 100}, "collector": {"unlocked": false, "progress": 0}, "master": {"unlocked": false, "progress": 0}}} \ No newline at end of file diff --git a/src/assets/sounds/enemy_death.wav b/src/assets/sounds/enemy_death.wav index 8c6ea913caedb75008ee1671e98a62d0de60cb1f..cbcab4e3b38a0eab8062d1b1365aebc0f359a860 100644 GIT binary patch literal 17684 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 literal 17684 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 J!~S3Y1px5ke?tHO diff --git a/src/assets/sounds/hit.wav b/src/assets/sounds/hit.wav index 6990ddf6321ac06de3dba23ff4ce7610f24ec312..5e1238230743744e97e8f1410ab283dd8d9a6321 100644 GIT binary patch literal 8864 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 Uo4mqX!=0YVW-_Ax1=| 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 bXFIX0*hG%XWC52p+rA*ZD!bXR^N#0#ETl1| diff --git a/src/data/game_save.json b/src/data/game_save.json index f35a81a..efdc858 100644 --- a/src/data/game_save.json +++ b/src/data/game_save.json @@ -1 +1 @@ -{"highscore": 10200, "wave": 1, "score": 0, "records": {"classic": {"best_survival_time": 51}, "survival": {"best_wave": 2}, "boss_rush": {"best_bosses_killed": 0}}} \ No newline at end of file +{"highscore": 10200, "wave": 1, "score": 0, "records": {"classic": {"best_survival_time": 112}, "survival": {"best_wave": 3}, "boss_rush": {"best_bosses_killed": 0}}, "characters": {"characters": {"default": {"unlocked": true, "selected": false}, "speed": {"unlocked": true, "selected": false}, "tank": {"unlocked": true, "selected": false}, "glass": {"unlocked": true, "selected": true}}}} \ No newline at end of file diff --git a/src/game/core/enemy.py b/src/game/core/enemy.py index f51bd20..cf75d38 100644 --- a/src/game/core/enemy.py +++ b/src/game/core/enemy.py @@ -9,7 +9,13 @@ def __init__(self, health, speed): super().__init__() self.max_health = health # Ajout de la santé maximale self.health = health - self.speed = speed + self.base_speed = speed # Vitesse de base + self.speed = speed # Vitesse actuelle (peut être modifiée par des effets) + + # Attributs pour le ralentissement + self.slow_factor = 0 # Facteur de ralentissement (0 = pas de ralentissement, 0.5 = 50% de ralentissement) + self.slow_duration = 0 # Durée du ralentissement en ms + self.is_slowed = False # État de ralentissement # Configuration de la barre de vie self.health_bar_height = 5 @@ -36,6 +42,7 @@ def __init__(self, health, speed): scaled_points = [(center + (x-center)*0.7, center + (y-center)*0.7) for x, y in points] pygame.draw.polygon(self.image, ENEMY_COLOR, scaled_points) + self.original_image = self.image.copy() # Garder une copie de l'image originale self.rect = self.image.get_rect() self.spawn() @@ -56,6 +63,9 @@ def spawn(self): self.rect.y = random.randint(0, SCREEN_HEIGHT) def move(self, target_position): + # Mise à jour des effets de ralentissement + self.update_slow_effect() + # Calcul du vecteur de direction vers le joueur dx = target_position[0] - self.rect.centerx dy = target_position[1] - self.rect.centery @@ -120,6 +130,31 @@ def draw(self, screen): pygame.draw.rect(screen, (0, 255, 0), (bar_x, bar_y, bar_width, self.health_bar_height)) + def update_slow_effect(self): + """Met à jour l'effet de ralentissement""" + current_time = pygame.time.get_ticks() + + if self.slow_duration > 0 and current_time >= self.slow_duration: + # L'effet de ralentissement est terminé + self.is_slowed = False + self.slow_factor = 0 + self.slow_duration = 0 + self.speed = self.base_speed + # Restaurer l'apparence normale + self.image = self.original_image.copy() + elif self.slow_factor > 0: + # L'effet de ralentissement est actif + if not self.is_slowed: + self.is_slowed = True + # Appliquer le ralentissement + self.speed = self.base_speed * (1 - self.slow_factor) + # Modifier l'apparence pour indiquer le ralentissement + slowed_image = self.original_image.copy() + blue_overlay = pygame.Surface(slowed_image.get_size(), pygame.SRCALPHA) + blue_overlay.fill((0, 0, 255, 100)) # Teinte bleue pour indiquer le ralentissement + slowed_image.blit(blue_overlay, (0, 0), special_flags=pygame.BLEND_RGBA_ADD) + self.image = slowed_image + class ShootingEnemy(Enemy): def __init__(self, health, speed): super().__init__(health, speed) @@ -146,10 +181,19 @@ def __init__(self, health, speed): scaled_points = [(center + (x-center)*0.7, center + (y-center)*0.7) for x, y in points] pygame.draw.polygon(self.image, SHOOTER_ENEMY_COLOR, scaled_points) + self.original_image = self.image.copy() # Garder une copie de l'image originale + def update(self, player_pos): super().move(player_pos) current_time = pygame.time.get_ticks() - if current_time - self.last_shot >= self.shoot_cooldown: + + # Ajuster le cooldown de tir en fonction du ralentissement + effective_cooldown = self.shoot_cooldown + if self.is_slowed: + # Un ennemi ralenti tire moins souvent + effective_cooldown = self.shoot_cooldown * (1 + self.slow_factor) + + if current_time - self.last_shot >= effective_cooldown: self.shoot(player_pos) def shoot(self, player_pos): @@ -161,4 +205,38 @@ def shoot(self, player_pos): self.bullets.add(bullet) if hasattr(self, 'game') and self.game.enemy_shoot_sound and not self.game.sound_muted: self.game.enemy_shoot_sound.play() - self.last_shot = pygame.time.get_ticks() \ No newline at end of file + self.last_shot = pygame.time.get_ticks() + + def check_bullet_shield_collision(self, player): + """Vérifie les collisions entre les balles ennemies et le bouclier du joueur""" + # Si le joueur n'a pas de bouclier actif, pas besoin de vérifier + if not player.shield_active: + return False + + # Vérifier chaque balle + for bullet in self.bullets: + # Calculer la distance entre la balle et le centre du joueur + dx = bullet.rect.centerx - player.rect.centerx + dy = bullet.rect.centery - player.rect.centery + distance = math.sqrt(dx * dx + dy * dy) + + # Si la balle est à l'intérieur du rayon du bouclier + shield_radius = PLAYER_SIZE + 10 # Même valeur que dans player.draw + if distance < shield_radius: + # Déterminer si la balle rebondit ou est détruite + if hasattr(player, 'reflect_chance') and random.random() < player.reflect_chance: + # La balle rebondit vers l'ennemi + bullet.direction_x *= -1 + bullet.direction_y *= -1 + + # Jouer un son si disponible + if hasattr(self, 'game') and hasattr(self.game, 'hit_sound') and self.game.hit_sound and not self.game.sound_muted: + self.game.hit_sound.play() + + return True + else: + # La balle est simplement détruite + bullet.kill() + return True + + return False \ No newline at end of file diff --git a/src/game/core/game.py b/src/game/core/game.py index e533a14..a04ab0d 100644 --- a/src/game/core/game.py +++ b/src/game/core/game.py @@ -120,9 +120,9 @@ def __init__(self): # Boutons pour l'écran Game Over from game.ui.button import Button self.game_over_buttons = { - 'restart': Button("Recommencer", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 - 40, 200, 50), - 'menu': Button("Menu Principal", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 + 30, 200, 50), - 'quit': Button("Quitter le jeu", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 + 100, 200, 50) + 'restart': Button("Recommencer", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3, 200, 50), + 'menu': Button("Menu Principal", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 + 70, 200, 50), + 'quit': Button("Quitter le jeu", SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3 + 140, 200, 50) } # Variables de vague @@ -167,6 +167,13 @@ def __init__(self): # Chargement de l'état du jeu self.load_game_state() + + # Afficher une notification de bienvenue + self.notification_manager.add_notification( + "Bienvenue dans ALIEN WAVE", + "Survivez aussi longtemps que possible!", + None + ) def generate_stars(self): """Génère les étoiles initiales""" @@ -282,11 +289,6 @@ def run(self): if self.select_game_mode(mode_id): self.game_state = PLAYING self.current_game_mode.initialize() - # On n'affiche le menu des modules que s'il y en a de débloqués - unlocked_modules = [module for module in self.module_manager.modules.values() if module.is_unlocked] - if unlocked_modules: - self.module_selection.visible = True - self.module_selection.modules = unlocked_modules elif self.game_state == PLAYING or self.game_state == PAUSED: self.module_selection.handle_event(event) @@ -323,6 +325,9 @@ def run(self): self.update_stars() self.draw_stars() + # Mise à jour des notifications + self.notification_manager.update() + # Gestion des états du jeu if self.game_state == MENU: self.menu_manager.draw() @@ -331,11 +336,14 @@ def run(self): elif self.game_state == PLAYING: # Mise à jour du jeu self.player.update() - if not self.current_game_mode.wave_transition: + if not self.current_game_mode.wave_transition if hasattr(self.current_game_mode, 'wave_transition') else True: self.spawn_enemy() self.update_enemies() self.update_effects() + # Attirer les pièces si l'effet magnet est actif + self.player.attract_coins(self.pickups) + # Mise à jour des pickups self.pickups.update(self.player) @@ -379,10 +387,16 @@ def run(self): for button in self.game_over_buttons.values(): button.update_hover_state() + # Afficher les notifications (indépendamment de l'état du jeu) + self.notification_manager.draw(self.screen) + # Mise à jour de l'écran pygame.display.flip() self.clock.tick(FPS) + # Sauvegarder l'état du jeu avant de quitter + self.save_game_state() + # Nettoyage et fermeture pygame.quit() sys.exit() @@ -430,6 +444,24 @@ def load_game_state(self): if 'boss_rush' in records: self.game_modes['boss_rush'].best_bosses_killed = records['boss_rush'].get('best_bosses_killed', 0) + # Chargement de l'état des personnages + character_state = data.get('characters', {}) + if hasattr(self, 'character_shop') and character_state: + self.character_shop.load_state(character_state) + + # S'assurer que le joueur utilise le bon personnage après le chargement + if hasattr(self, 'player'): + # Réinitialiser le joueur pour prendre en compte le personnage chargé + self.player.apply_character_stats() + # Mettre à jour l'image du joueur si nécessaire + if self.character_shop.selected_character: + try: + self.player.original_image = self.character_shop.selected_character.sprite.copy() + self.player.image = self.player.original_image.copy() + print(f"Image du joueur mise à jour pour le personnage {self.character_shop.selected_character.id}") + except Exception as e: + print(f"Erreur lors de la mise à jour de l'image du joueur: {e}") + print('État du jeu chargé avec succès') except Exception as e: print(f'Erreur lors du chargement de l\'état du jeu: {e}') @@ -468,7 +500,12 @@ def reset_game(self): """Réinitialise le jeu""" self.current_game_mode.initialize() self.game_state = MENU - self.player = Player(self) + self.player = Player(self) # Créer un nouveau joueur (qui utilisera le personnage sélectionné) + + # Appliquer les effets des modules actifs + if hasattr(self, 'module_manager'): + self.module_manager.apply_module_effects(self.player) + self.enemies = pygame.sprite.Group() self.pickups = pygame.sprite.Group() self.wave = 1 @@ -581,7 +618,13 @@ def draw_wave_transition(self): def draw_hud(self): """Affiche l'interface utilisateur""" # Affichage de la santé à gauche - health_text = self.font.render(f"Vie: {self.player.health}", True, WHITE) + health_value = round(self.player.health, 2) # Arrondir à 2 décimales + if health_value == int(health_value): + health_display = str(int(health_value)) # Afficher sans décimales si c'est un entier + else: + health_display = f"{health_value:.2f}" # Afficher avec 2 décimales + + health_text = self.font.render(f"Vie: {health_display}", True, WHITE) health_rect = health_text.get_rect(topleft=(20, 20)) self.screen.blit(health_text, health_rect) @@ -649,10 +692,11 @@ def draw_hud(self): record_rect = record_text.get_rect(midtop=(SCREEN_WIDTH // 2, 50)) self.screen.blit(record_text, record_rect) - # Affichage de la vague - wave_text = self.font.render(f"Vague {self.wave}", True, CYAN) - wave_rect = wave_text.get_rect(midtop=(SCREEN_WIDTH // 2, 80)) - self.screen.blit(wave_text, wave_rect) + # Affichage de la vague (sauf pour le mode classique) + if not isinstance(self.current_game_mode, ClassicMode): + wave_text = self.font.render(f"Vague {self.wave}", True, CYAN) + wave_rect = wave_text.get_rect(midtop=(SCREEN_WIDTH // 2, 80)) + self.screen.blit(wave_text, wave_rect) # Informations sur les ennemis à droite y_offset = 60 @@ -663,11 +707,12 @@ def draw_hud(self): enemies_rect = enemies_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset)) self.screen.blit(enemies_text, enemies_rect) - # Ennemis restants à faire apparaître - remaining = self.current_game_mode.get_remaining_enemies() - remaining_text = self.font.render(f"Restants: {remaining}", True, WHITE) - remaining_rect = remaining_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset + spacing)) - self.screen.blit(remaining_text, remaining_rect) + # Ennemis restants à faire apparaître (sauf en mode classique) + if not isinstance(self.current_game_mode, ClassicMode): + remaining = self.current_game_mode.get_remaining_enemies() + remaining_text = self.font.render(f"Restants: {remaining}", True, WHITE) + remaining_rect = remaining_text.get_rect(topright=(SCREEN_WIDTH - 20, y_offset + spacing)) + self.screen.blit(remaining_text, remaining_rect) # Bouton son en bas à droite button_x = SCREEN_WIDTH - SOUND_BUTTON_SIZE - SOUND_BUTTON_PADDING @@ -778,9 +823,16 @@ def update_enemies(self): enemy.move((self.player.x, self.player.y)) # Vérifie les collisions avec les balles - bullet_hits = pygame.sprite.spritecollide(enemy, self.player.bullets, True) + bullet_hits = pygame.sprite.spritecollide(enemy, self.player.bullets, False) for bullet in bullet_hits: - if enemy.take_damage(BULLET_DAMAGE): + # Utiliser la méthode handle_collision + # Récupérer le gestionnaire de particules si disponible + particle_manager = getattr(self, 'particle_manager', None) + + # Gérer la collision et déterminer si la balle doit être détruite + destroy_bullet = bullet.handle_collision(enemy, particle_manager) + + if enemy.health <= 0: self.player.gain_score(ENEMY_SCORE) self.enemies_killed += 1 @@ -792,11 +844,18 @@ def update_enemies(self): # Chance de faire apparaître une pièce ou un coffre if random.random() < 0.3: # 30% de chance from game.core.pickup import Pickup + + # Calculer le bonus de pièces (à utiliser dans une future version) + coin_bonus_multiplier = self.player.coin_bonus + if random.random() < 0.8: # 80% de chance pour une pièce, 20% pour un coffre pickup = Pickup(enemy.rect.centerx, enemy.rect.centery, 'coin') else: pickup = Pickup(enemy.rect.centerx, enemy.rect.centery, 'chest') self.pickups.add(pickup) + + if destroy_bullet: + bullet.kill() # Vérifie les collisions avec le joueur if enemy.attack(self.player): @@ -913,13 +972,24 @@ def save_game_state(self): } } + # Sauvegarde des modules + if hasattr(self, 'module_manager'): + self.module_manager.save_state() + + # Récupération de l'état des personnages + characters_state = {} + if hasattr(self, 'character_shop'): + characters_state = self.character_shop.save_state() + with open(SAVE_FILE, 'w') as f: json.dump({ 'highscore': self.highscore, 'wave': self.wave, 'score': self.player.score if hasattr(self, 'player') else 0, - 'records': records + 'records': records, + 'characters': characters_state }, f) + print('État du jeu sauvegardé avec succès') except Exception as e: print(f"Erreur lors de la sauvegarde de l'état du jeu: {e}") diff --git a/src/game/core/player.py b/src/game/core/player.py index c73b5cc..7440004 100644 --- a/src/game/core/player.py +++ b/src/game/core/player.py @@ -1,6 +1,7 @@ import pygame import math import os +import random from utils.constants import * from game.core.weapon import Bullet @@ -18,6 +19,13 @@ def __init__(self, game): sprite_path = os.path.join(ASSETS_DIR, 'characters', 'player.png') if os.path.exists(sprite_path): self.original_image = pygame.image.load(sprite_path).convert_alpha() + + # Si un personnage est sélectionné, utiliser son sprite + if hasattr(game, 'character_shop') and game.character_shop.selected_character: + selected_char = game.character_shop.selected_character + # Utiliser le sprite du personnage sélectionné + self.original_image = selected_char.sprite.copy() + print(f'Utilisation du sprite du personnage {selected_char.id}') else: # Fallback sur default.png sprite_path = os.path.join(ASSETS_DIR, 'characters', 'default.png') @@ -27,52 +35,98 @@ def __init__(self, game): # Créer une image par défaut si aucun sprite n'est trouvé self.original_image = pygame.Surface((PLAYER_SIZE, PLAYER_SIZE), pygame.SRCALPHA) # Dessiner un vaisseau triangulaire - points = [(PLAYER_SIZE//2, 0), (0, PLAYER_SIZE), (PLAYER_SIZE, PLAYER_SIZE)] + points = [(PLAYER_SIZE/2, 0), (0, PLAYER_SIZE), (PLAYER_SIZE, PLAYER_SIZE)] pygame.draw.polygon(self.original_image, PLAYER_COLOR, points) # Ajouter des détails - pygame.draw.polygon(self.original_image, (255, 255, 255), - [(PLAYER_SIZE//2, PLAYER_SIZE//4), - (PLAYER_SIZE//3, PLAYER_SIZE//2), - (PLAYER_SIZE//2, PLAYER_SIZE//1.5), - (PLAYER_SIZE//1.5, PLAYER_SIZE//2)]) + pygame.draw.polygon(self.original_image, (255, 255, 255), + [(PLAYER_SIZE/2, PLAYER_SIZE/4), + (PLAYER_SIZE/3, PLAYER_SIZE/2), + (PLAYER_SIZE/2, PLAYER_SIZE/1.5), + (PLAYER_SIZE/1.5, PLAYER_SIZE/2)]) print('Image par défaut créée pour le joueur') - - # Redimensionner l'image - self.original_image = pygame.transform.scale(self.original_image, (PLAYER_SIZE, PLAYER_SIZE)) - self.image = self.original_image.copy() except Exception as e: - print(f'Erreur lors du chargement du sprite du joueur: {e}') + print(f"Erreur lors du chargement du sprite: {e}") self.original_image = pygame.Surface((PLAYER_SIZE, PLAYER_SIZE), pygame.SRCALPHA) - # Dessiner un vaisseau triangulaire - points = [(PLAYER_SIZE//2, 0), (0, PLAYER_SIZE), (PLAYER_SIZE, PLAYER_SIZE)] pygame.draw.polygon(self.original_image, PLAYER_COLOR, points) - self.image = self.original_image.copy() + # Préparation de l'image et du rectangle + self.image = self.original_image.copy() self.rect = self.image.get_rect() - self.angle = 0 # Angle de rotation initial - self.last_shot_effect = 0 # Pour l'effet de tir # Position initiale self.x = SCREEN_WIDTH // 2 self.y = SCREEN_HEIGHT // 2 self.rect.center = (self.x, self.y) - # Attributs de base - self.base_speed = 5 - self.base_shoot_cooldown = 250 - self.base_shield_cooldown = 5000 + # Statistiques de base self.base_health = 100 + self.base_speed = 5 self.base_bullet_damage = BULLET_DAMAGE + self.base_shoot_cooldown = 250 + self.base_shield_cooldown = 5000 # Ajout de l'attribut manquant - # Attributs modifiés par les modules - self.speed = self.base_speed - self.shoot_cooldown = self.base_shoot_cooldown - self.shield_cooldown = self.base_shield_cooldown + # Appliquer les statistiques du personnage + self.apply_character_stats() + + # Initialisation des statistiques + self.reset_stats() + + # Attributs liés au tir + self.last_shot = 0 + self.double_shot_active = False + self.triple_shot_active = False + + # Attributs pour le bouclier + self.shield_cooldown = 5000 # 5 secondes entre les activations + self.last_shield_activation = 0 + self.shield_active = False + self.shield_duration = 2000 + + # Groupe de sprites pour les balles + self.bullets = pygame.sprite.Group() + + def apply_character_stats(self): + """Applique les statistiques du personnage sélectionné""" + if hasattr(self.game, 'character_shop') and self.game.character_shop.selected_character: + selected_char = self.game.character_shop.selected_character + stats = selected_char.stats + + # Appliquer les statistiques du personnage + if 'health' in stats: + self.base_health = stats['health'] + if 'speed' in stats: + self.base_speed = 5 * stats['speed'] # Multiplier la vitesse de base par le facteur + if 'damage' in stats: + self.base_bullet_damage = BULLET_DAMAGE * stats['damage'] # Multiplier les dégâts de base + + print(f"Statistiques du personnage {selected_char.id} appliquées : Santé={self.base_health}, Vitesse={self.base_speed}, Dégâts={self.base_bullet_damage}") + else: + print("Aucun personnage sélectionné, utilisation des statistiques par défaut") + + def reset_stats(self): + """Réinitialise les statistiques du joueur""" + # S'assurer d'abord que les bonnes statistiques de base sont appliquées + self.apply_character_stats() + + # Initialiser les statistiques + self.health = self.base_health self.max_health = self.base_health - self.health = self.max_health + self.speed = self.base_speed self.bullet_damage = self.base_bullet_damage - self.health_regen = 0 + self.score = 0 + self.shoot_cooldown = self.base_shoot_cooldown + self.shield_cooldown = self.base_shield_cooldown # Utilisation de la valeur de base + + # Autres attributs après le reset + self.coin_magnetism = 0 + self.critical_chance = 0 + self.explosion_chance = 0 + self.reflect_chance = 0 + self.slowdown_power = 0 + self.coin_bonus = 1 + self.pierce_count = 0 self.extra_bullets = 0 + self.health_regen = 0 # État du joueur self.score = 0 @@ -81,9 +135,11 @@ def __init__(self, game): self.last_shield_activation = 0 self.shield_active = False self.shield_duration = 2000 + self.extra_bullets = 0 # Groupe de sprites pour les balles self.bullets = pygame.sprite.Group() + # ... autres attributs spécifiques aux modules ... def move(self, dx, dy): """Déplace le joueur""" @@ -123,7 +179,7 @@ def shoot(self): angle = math.atan2(mouse_y - self.y, mouse_x - self.x) # Calcul de la position de départ du projectile (à l'avant du vaisseau) - offset = PLAYER_SIZE // 2 # Distance depuis le centre du vaisseau + offset = PLAYER_SIZE // 3 # Réduit de //2 à //3 pour s'adapter à la nouvelle taille start_x = self.x + math.cos(angle) * offset start_y = self.y + math.sin(angle) * offset @@ -138,7 +194,23 @@ def shoot(self): # Création des balles for shoot_angle in angles: - bullet = Bullet(start_x, start_y, shoot_angle, self.bullet_damage) + # Déterminer si c'est un tir critique + is_critical = random.random() < self.critical_chance + # Déterminer si c'est une balle explosive + is_explosive = random.random() < self.explosion_chance + + # Calculer les dégâts (doublés pour les critiques) + damage = self.bullet_damage * (2 if is_critical else 1) + + # Créer la balle avec les attributs spéciaux + bullet = Bullet( + start_x, start_y, shoot_angle, damage, + is_enemy=False, + is_critical=is_critical, + is_explosive=is_explosive, + pierce_count=self.pierce_count, + slowdown_power=self.slowdown_power + ) self.bullets.add(bullet) # Effet visuel de tir @@ -197,12 +269,12 @@ def draw(self, screen): if current_time - self.last_shot_effect < 100: # Effet pendant 100ms # Calculer la position du flash flash_angle_rad = math.radians(self.angle - 90) # Convertir en radians et ajuster - flash_offset = PLAYER_SIZE // 2 + flash_offset = PLAYER_SIZE // 3 # Réduit de //2 à //3 flash_x = self.rect.centerx + math.cos(flash_angle_rad) * flash_offset flash_y = self.rect.centery + math.sin(flash_angle_rad) * flash_offset # Dessiner le flash - flash_radius = 5 + (current_time - self.last_shot_effect) // 20 + flash_radius = 3 + (current_time - self.last_shot_effect) // 20 # Réduit de 5 à 3 flash_alpha = 255 - (current_time - self.last_shot_effect) * 2.5 # Créer une surface pour le flash avec transparence @@ -218,7 +290,7 @@ def draw(self, screen): # Dessin du bouclier si actif if self.shield_active: - shield_radius = PLAYER_SIZE + 10 # Bouclier légèrement plus grand que le joueur + shield_radius = PLAYER_SIZE + 5 # Réduit de +10 à +5 pygame.draw.circle(screen, (0, 255, 255), self.rect.center, shield_radius, 2) # Effet de lueur pour le bouclier pygame.draw.circle(screen, (0, 200, 200, 50), self.rect.center, shield_radius - 2, 1) @@ -234,17 +306,6 @@ def gain_score(self, points): """Augmente le score du joueur""" self.score += points - def reset_stats(self): - """Réinitialise les statistiques du joueur""" - self.speed = self.base_speed - self.shoot_cooldown = self.base_shoot_cooldown - self.shield_cooldown = self.base_shield_cooldown - self.max_health = self.base_health - self.health = self.max_health - self.bullet_damage = self.base_bullet_damage - self.health_regen = 0 - self.extra_bullets = 0 - def play_sound(self, sound_type): if hasattr(self, 'game'): print(f"Game instance exists, sound_muted: {self.game.sound_muted}") @@ -261,4 +322,25 @@ def play_sound(self, sound_type): def die(self): # Logique pour la mort du joueur if hasattr(self, 'game'): - self.game.game_over() \ No newline at end of file + self.game.game_over() + + def attract_coins(self, pickups): + """Attire les pièces vers le joueur si l'effet coin_magnetism est actif""" + # Si l'effet d'attraction n'est pas disponible, ne rien faire + if not hasattr(self, 'coin_magnetism') or self.coin_magnetism <= 0: + return + + # Définir la portée d'attraction basée sur la puissance de l'effet + attraction_radius = 100 + (self.coin_magnetism * 20) + + # Créer un rectangle d'attraction plus grand autour du joueur + attraction_rect = self.rect.inflate(attraction_radius, attraction_radius) + + # Parcourir tous les pickups et les attirer s'ils sont à portée + for pickup in pickups: + if hasattr(pickup, 'is_attracted') and not pickup.is_attracted: + if pickup.rect.colliderect(attraction_rect): + pickup.is_attracted = True + # Augmenter la vitesse d'attraction en fonction de l'effet + if hasattr(pickup, 'attraction_speed'): + pickup.attraction_speed = 5 + (self.coin_magnetism * 0.5) \ No newline at end of file diff --git a/src/game/core/weapon.py b/src/game/core/weapon.py index ca735d2..7358579 100644 --- a/src/game/core/weapon.py +++ b/src/game/core/weapon.py @@ -3,7 +3,8 @@ from utils.constants import * class Bullet(pygame.sprite.Sprite): - def __init__(self, x, y, direction, damage=BULLET_DAMAGE, is_enemy=False): + def __init__(self, x, y, direction, damage=BULLET_DAMAGE, is_enemy=False, + is_critical=False, is_explosive=False, pierce_count=0, slowdown_power=0): super().__init__() # Convertir l'angle en vecteur de direction self.direction_angle = direction @@ -12,15 +13,33 @@ def __init__(self, x, y, direction, damage=BULLET_DAMAGE, is_enemy=False): self.speed = BULLET_SPEED self.damage = damage self.is_enemy = is_enemy - self.colors = BULLET_COLORS["ENEMY"] if is_enemy else BULLET_COLORS["PLAYER"] + + # Nouvelles propriétés pour les modules + self.is_critical = is_critical + self.is_explosive = is_explosive + self.pierce_count = pierce_count # Nombre d'ennemis que la balle peut traverser + self.pierced_enemies = 0 # Nombre d'ennemis déjà touchés + self.slowdown_power = slowdown_power # Pourcentage de ralentissement + + # Couleurs différentes selon le type de balle + if is_enemy: + self.colors = BULLET_COLORS["ENEMY"] + else: + if is_critical: + self.colors = {"CORE": (255, 50, 50), "GLOW": (255, 100, 100), "TRAIL": (255, 70, 70, 80)} + elif is_explosive: + self.colors = {"CORE": (255, 165, 0), "GLOW": (255, 200, 0), "TRAIL": (255, 180, 0, 80)} + else: + self.colors = BULLET_COLORS["PLAYER"] # Pré-rendu de l'effet de projectile + size_multiplier = 1.5 if is_critical else 1.0 self.image = pygame.Surface((BULLET_SURFACE_SIZE, BULLET_SURFACE_SIZE), pygame.SRCALPHA) center = BULLET_SURFACE_SIZE // 2 # Effet de lueur optimisé - pygame.draw.circle(self.image, self.colors["GLOW"], (center, center), BULLET_SIZE * 2) - pygame.draw.circle(self.image, self.colors["CORE"], (center, center), BULLET_SIZE) + pygame.draw.circle(self.image, self.colors["GLOW"], (center, center), int(BULLET_SIZE * 2 * size_multiplier)) + pygame.draw.circle(self.image, self.colors["CORE"], (center, center), int(BULLET_SIZE * size_multiplier)) self.rect = self.image.get_rect(center=(x, y)) self.trail_positions = [] @@ -59,6 +78,60 @@ def is_on_screen(self): """Vérifie si la balle est toujours à l'écran""" return (0 <= self.rect.x <= SCREEN_WIDTH and 0 <= self.rect.y <= SCREEN_HEIGHT) + + def handle_collision(self, enemy, particle_manager=None): + """Gère la collision avec un ennemi et retourne True si la balle doit être détruite""" + # Infliger des dégâts + enemy.take_damage(self.damage) + + # Appliquer un ralentissement si la balle a cet effet + if self.slowdown_power > 0: + self.apply_slowdown(enemy) + + # Créer une explosion si c'est une balle explosive + if self.is_explosive and enemy.health <= 0 and particle_manager: + self.create_explosion(enemy, particle_manager) + + # Vérifier si la balle peut traverser d'autres ennemis + if self.pierce_count > 0 and self.pierced_enemies < self.pierce_count: + self.pierced_enemies += 1 + return False # Ne pas détruire la balle + + return True # Détruire la balle + + def apply_slowdown(self, enemy): + """Ralentit un ennemi touché""" + if hasattr(enemy, 'slow_factor'): + # Appliquer le ralentissement (par exemple, réduire la vitesse de 20% pour un slowdown_power de 0.2) + enemy.slow_factor = max(enemy.slow_factor, self.slowdown_power) + enemy.slow_duration = pygame.time.get_ticks() + 3000 # Effet durant 3 secondes + + def create_explosion(self, enemy, particle_manager): + """Crée une explosion au point d'impact""" + explosion_radius = BULLET_SIZE * 5 # Rayon de l'explosion + explosion_damage = self.damage * 0.5 # Dégâts de l'explosion (moitié des dégâts directs) + + # Créer un effet visuel d'explosion + if particle_manager: + particle_manager.create_explosion( + enemy.rect.centerx, enemy.rect.centery, + color=(255, 165, 0), # Orange pour les explosions + num_particles=20, + spread=10 + ) + + # Appliquer des dégâts aux ennemis proches + if hasattr(enemy, 'game') and enemy.game: + for nearby_enemy in enemy.game.enemies: + if nearby_enemy != enemy: # Ne pas endommager à nouveau la cible principale + distance = math.sqrt( + (nearby_enemy.rect.centerx - enemy.rect.centerx) ** 2 + + (nearby_enemy.rect.centery - enemy.rect.centery) ** 2 + ) + if distance < explosion_radius: + # Dégâts inversement proportionnels à la distance + damage_factor = 1 - (distance / explosion_radius) + nearby_enemy.take_damage(explosion_damage * damage_factor) class Weapon: def __init__(self): diff --git a/src/game/modes/classic_mode.py b/src/game/modes/classic_mode.py index e1c3813..c0b24ae 100644 --- a/src/game/modes/classic_mode.py +++ b/src/game/modes/classic_mode.py @@ -15,33 +15,22 @@ def __init__(self, game): self.start_time = pygame.time.get_ticks() self.paused_time = 0 self.is_paused = False + self.difficulty_multiplier = 1.0 + self.last_difficulty_increase = 0 def initialize(self): """Initialise le mode de jeu classique""" - self.wave = 1 - self.wave_enemies_left = ENEMIES_PER_WAVE - self.wave_transition = False - self.wave_start_time = 0 self.start_time = pygame.time.get_ticks() self.survival_time = 0 self.paused_time = 0 self.is_paused = False + self.difficulty_multiplier = 1.0 + self.last_difficulty_increase = 0 + # Réinitialiser le spawn cooldown du jeu + self.game.spawn_cooldown = 2000 def update(self): """Met à jour la logique du mode classique""" - # Gestion de la transition de vague en priorité - if self.wave_transition: - current_time = pygame.time.get_ticks() - if current_time - self.wave_start_time > WAVE_TRANSITION_TIME: - self.wave_transition = False - # Calculer le nombre d'ennemis pour la nouvelle vague - self.wave_enemies_left = int(ENEMIES_PER_WAVE * (WAVE_ENEMY_MULTIPLIER ** (self.wave - 1))) - print(f"Fin de la transition de vague {self.wave}, nouveaux ennemis: {self.wave_enemies_left}") - # Pas de spawn/check pendant la transition - elif not self.wave_transition: - self.spawn_enemies() - self.check_wave_completion() - # Mise à jour du temps de survie uniquement si le jeu est en cours if self.game.game_state == PLAYING: if self.is_paused: @@ -52,6 +41,13 @@ def update(self): # Calculer le temps écoulé self.survival_time = (pygame.time.get_ticks() - self.start_time) // 1000 + # Augmenter la difficulté progressivement avec le temps + current_time = pygame.time.get_ticks() + if current_time - self.last_difficulty_increase > 30000: # Toutes les 30 secondes + self.difficulty_multiplier += 0.1 + self.game.spawn_cooldown = max(500, int(self.game.spawn_cooldown * 0.9)) # Spawn plus rapide + self.last_difficulty_increase = current_time + # Mettre à jour le record si nécessaire if self.survival_time > self.best_survival_time: self.best_survival_time = self.survival_time @@ -70,23 +66,25 @@ def format_time(self, seconds): return f"{minutes:02d}:{seconds:02d}" def spawn_enemies(self): - """Logique de spawn des ennemis pour le mode classique""" + """Logique de spawn des ennemis en continu pour le mode classique""" # Limiter le nombre d'ennemis actifs pour des performances optimales if len(self.game.enemies) >= MAX_ACTIVE_ENEMIES: return current_time = pygame.time.get_ticks() - if current_time - self.game.last_spawn >= self.game.spawn_cooldown and self.wave_enemies_left > 0: - # Utiliser la configuration de la vague - wave_config = self.get_wave_config(self.wave) - health = wave_config['enemy_health'] - speed = wave_config['enemy_speed'] + if current_time - self.game.last_spawn >= self.game.spawn_cooldown: + # Configuration de l'ennemi basée sur le temps de survie + difficulty_level = max(1, self.survival_time // 30) # Augmente tous les 30 secondes + + base_health = 50 + (difficulty_level * 10) + base_speed = 1 + (difficulty_level * 0.1) + + # Calcul du ratio d'ennemis tireurs vs normaux (augmente avec le temps) + shooter_ratio = min(0.5, 0.1 + (difficulty_level * 0.02)) # Max 50% de tireurs - # Calcul du ratio d'ennemis tireurs vs normaux - normal_enemies = wave_config['normal_enemies'] - shooting_enemies = wave_config['shooting_enemies'] - total_enemies = normal_enemies + shooting_enemies - shooter_ratio = shooting_enemies / total_enemies if total_enemies > 0 else 0 + # Appliquer le multiplicateur de difficulté + health = int(base_health * self.difficulty_multiplier) + speed = base_speed * self.difficulty_multiplier # Création de l'ennemi if random.random() < shooter_ratio: @@ -96,44 +94,17 @@ def spawn_enemies(self): enemy.game = self.game self.game.enemies.add(enemy) - self.wave_enemies_left -= 1 self.game.last_spawn = current_time - def check_wave_completion(self): - """Vérifie si la vague actuelle est terminée""" - # Vérifier que tous les ennemis prévus sont apparus ET qu'il n'y en a plus sur le terrain - if self.wave_enemies_left <= 0 and len(self.game.enemies) == 0: - if not self.wave_transition: - self.handle_wave_completion() - return True - return False + def get_remaining_enemies(self): + """Retourne le nombre d'ennemis restants (toujours 'infini' en mode classique)""" + return "∞" # Symbole infini def handle_wave_completion(self): - """Gestion de la fin d'une vague""" - self.wave_transition = True - self.wave_start_time = pygame.time.get_ticks() - print(f"Début de la transition de vague {self.wave}") # Log pour debug - self.wave += 1 - self.game.enemies_killed = 0 - - # Le nombre d'ennemis est mis à zéro et sera recalculé à la fin de la transition - self.wave_enemies_left = 0 - self.game.spawn_cooldown = max(500, self.game.spawn_cooldown - WAVE_SPEEDUP) - - # Récompenses - coins_earned = self.wave * COINS_PER_WAVE - self.game.economy_manager.add_coins(coins_earned) - - # Notification - self.game.notification_manager.add_notification( - f'Vague {self.wave-1} terminée !', - f'+{coins_earned} pièces', - COIN_ICON - ) - - # Son - if hasattr(self.game, 'coin_sound') and self.game.coin_sound and not self.game.sound_muted: - self.game.coin_sound.play() + """Méthode requise par l'interface mais non utilisée en mode classique""" + # Cette méthode est requise par l'interface GameModeBase mais n'est pas utilisée + # car le mode classique n'utilise pas de vagues + pass def handle_events(self, events): """Gestion des événements spécifiques au mode classique""" @@ -149,19 +120,11 @@ def draw(self, screen): if isinstance(enemy, ShootingEnemy): enemy.bullets.draw(screen) - # Affichage de la transition de vague - if self.wave_transition: - self.draw_wave_transition(screen) - - def get_wave_config(self, wave_number: int) -> dict: - """Retourne la configuration de la vague pour le mode classique""" - return { - 'normal_enemies': wave_number * 2, - 'shooting_enemies': max(0, wave_number - 2), - 'enemy_speed': min(1 + wave_number * 0.1, 2.0), - 'enemy_health': 100 + wave_number * 10, - 'boss': False # Pas de boss en mode classique - } + # Affichage du temps de survie et du niveau de difficulté + font = pygame.font.Font(None, 30) + difficulty_text = font.render(f"Difficulté: x{self.difficulty_multiplier:.1f}", True, CYAN) + difficulty_rect = difficulty_text.get_rect(topleft=(20, 140)) + screen.blit(difficulty_text, difficulty_rect) def get_record_text(self) -> str: """Retourne le texte du record pour le mode classique""" diff --git a/src/game/modules/module_manager.py b/src/game/modules/module_manager.py index dd823e6..afffb69 100644 --- a/src/game/modules/module_manager.py +++ b/src/game/modules/module_manager.py @@ -1,4 +1,6 @@ import random +import json +import os class Module: def __init__(self, id, name, description, max_level, cost): @@ -44,6 +46,7 @@ def __init__(self, game): self.modules = {} # Modules permanents (achetés) self.temp_modules = {} # Modules temporaires (coffres) self.initialize_modules() + self.load_state() # Charger l'état des modules def initialize_modules(self): """Initialise la liste des modules disponibles""" @@ -182,6 +185,9 @@ def unlock_module(self, module_id): module.is_unlocked = True module.level = 1 + # Sauvegarder l'état après modification + self.save_state() + # Notification et son déjà gérés dans ModuleMenu.handle_click return True @@ -196,5 +202,47 @@ def upgrade_module(self, module_id): module.level += 1 + # Sauvegarder l'état après modification + self.save_state() + # Notification et son déjà gérés dans ModuleMenu.handle_click - return True \ No newline at end of file + return True + + def save_state(self): + """Sauvegarde l'état des modules""" + state = { + module_id: { + 'is_unlocked': module.is_unlocked, + 'level': module.level + } + for module_id, module in self.modules.items() + } + + try: + # Créer le dossier de sauvegarde s'il n'existe pas + save_dir = 'src/save' + if not os.path.exists(save_dir): + os.makedirs(save_dir) + + with open(f'{save_dir}/modules.json', 'w') as f: + json.dump(state, f) + print('Modules sauvegardés avec succès') + except Exception as e: + print(f'Erreur lors de la sauvegarde des modules: {e}') + + def load_state(self): + """Charge l'état des modules""" + try: + with open('src/save/modules.json', 'r') as f: + state = json.load(f) + + for module_id, data in state.items(): + if module_id in self.modules: + module = self.modules[module_id] + module.is_unlocked = data.get('is_unlocked', False) + module.level = data.get('level', 0) + print('Modules chargés avec succès') + except FileNotFoundError: + print('Aucun fichier de sauvegarde de modules trouvé') + except Exception as e: + print(f'Erreur lors du chargement des modules: {e}') \ No newline at end of file diff --git a/src/game/ui/character_shop.py b/src/game/ui/character_shop.py index 4e96bbc..83fd140 100644 --- a/src/game/ui/character_shop.py +++ b/src/game/ui/character_shop.py @@ -128,6 +128,8 @@ def handle_click(self, pos): f'{character.name} débloqué', None ) + # Sauvegarder l'état après l'achat + self.game.save_game_state() else: # Notification de manque de pièces self.game.notification_manager.add_notification( @@ -137,7 +139,9 @@ def handle_click(self, pos): ) else: # Sélectionner le personnage - self.select_character(character_id) + if self.select_character(character_id): + # Sauvegarder l'état après la sélection + self.game.save_game_state() return True return False diff --git a/src/game/ui/menu_manager.py b/src/game/ui/menu_manager.py index 419521e..cec85be 100644 --- a/src/game/ui/menu_manager.py +++ b/src/game/ui/menu_manager.py @@ -45,8 +45,8 @@ def __init__(self, game): # Boutons du menu game over self.game_over_buttons = [ - Button('Recommencer', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 60, 200, 50), - Button('Menu Principal', SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 120, 200, 50) + Button('Recommencer', SCREEN_WIDTH // 2, SCREEN_HEIGHT * 3 // 4, 200, 50), + Button('Menu Principal', SCREEN_WIDTH // 2, SCREEN_HEIGHT * 3 // 4 + 70, 200, 50) ] # Boutons du menu character shop diff --git a/src/game/ui/menus.py b/src/game/ui/menus.py index da60efb..212e672 100644 --- a/src/game/ui/menus.py +++ b/src/game/ui/menus.py @@ -328,12 +328,11 @@ def __init__(self, screen: pygame.Surface, game): button_width = 200 button_height = 50 spacing = 20 - start_y = height // 2 + 50 self.buttons = { - 'restart': Button(width//2 - button_width//2, start_y, + 'restart': Button(width//2 - button_width//2, height * 3 // 4, button_width, button_height, 'Recommencer'), - 'main_menu': Button(width//2 - button_width//2, start_y + button_height + spacing, + 'main_menu': Button(width//2 - button_width//2, height * 3 // 4 + button_height + spacing, button_width, button_height, 'Menu Principal') } diff --git a/src/save/modules.json b/src/save/modules.json new file mode 100644 index 0000000..c885fd4 --- /dev/null +++ b/src/save/modules.json @@ -0,0 +1 @@ +{"rapid_fire": {"is_unlocked": true, "level": 3}, "shield_boost": {"is_unlocked": true, "level": 3}, "damage_boost": {"is_unlocked": true, "level": 3}, "speed_boost": {"is_unlocked": true, "level": 3}, "health_regen": {"is_unlocked": true, "level": 3}, "multi_shot": {"is_unlocked": true, "level": 3}} \ No newline at end of file diff --git a/src/utils/constants.py b/src/utils/constants.py index aa081fc..d07113c 100644 --- a/src/utils/constants.py +++ b/src/utils/constants.py @@ -20,7 +20,7 @@ # Player settings PLAYER_SPEED = 5 -PLAYER_SIZE = 64 +PLAYER_SIZE = 32 # Weapon settings BULLET_SPEED = 10