Updates to libstormgames submodule. Updated the game to use the menu improvements. Started work on survival mode.
This commit is contained in:
		 Submodule libstormgames updated: ca2d0d34bd...dcd204e476
									
								
							| @@ -1,10 +1,11 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| import pygame | ||||
| from os.path import isdir, join | ||||
| from libstormgames import speak | ||||
| from libstormgames import speak, instruction_menu | ||||
|  | ||||
| def get_available_games(): | ||||
|     """Get list of available game directories in levels folder. | ||||
| @@ -13,12 +14,21 @@ def get_available_games(): | ||||
|         list: List of game directory names | ||||
|     """ | ||||
|     try: | ||||
|         return [d for d in os.listdir("levels") if isdir(join("levels", d))] | ||||
|         # Handle PyInstaller path issues | ||||
|         if hasattr(sys, '_MEIPASS'): | ||||
|             # Running as PyInstaller executable | ||||
|             base_path = sys._MEIPASS | ||||
|         else: | ||||
|             # Running as script | ||||
|             base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||||
|          | ||||
|         levels_path = os.path.join(base_path, "levels") | ||||
|         return [d for d in os.listdir(levels_path) if isdir(join(levels_path, d))] | ||||
|     except FileNotFoundError: | ||||
|         return [] | ||||
|  | ||||
| def selection_menu(sounds, *options): | ||||
|     """Display level selection menu. | ||||
|     """Display level selection menu using instruction_menu. | ||||
|  | ||||
|     Args: | ||||
|         sounds (dict): Dictionary of loaded sound effects | ||||
| @@ -27,70 +37,7 @@ def selection_menu(sounds, *options): | ||||
|     Returns: | ||||
|         str: Selected option or None if cancelled | ||||
|     """ | ||||
|     loop = True | ||||
|     pygame.mixer.stop() | ||||
|     i = 0 | ||||
|     j = -1 | ||||
|  | ||||
|     # Clear any pending events | ||||
|     pygame.event.clear() | ||||
|  | ||||
|     speak("Select an adventure") | ||||
|     time.sleep(1.0) | ||||
|  | ||||
|     while loop: | ||||
|         if i != j: | ||||
|             speak(options[i]) | ||||
|             j = i | ||||
|  | ||||
|         pygame.event.pump() | ||||
|         event = pygame.event.wait() | ||||
|  | ||||
|         if event.type == pygame.KEYDOWN: | ||||
|             if event.key == pygame.K_ESCAPE: | ||||
|                 return None | ||||
|  | ||||
|             if event.key == pygame.K_DOWN and i < len(options) - 1: | ||||
|                 i = i + 1 | ||||
|                 try: | ||||
|                     sounds['menu-move'].play() | ||||
|                 except: | ||||
|                     pass | ||||
|  | ||||
|             if event.key == pygame.K_UP and i > 0: | ||||
|                 i = i - 1 | ||||
|                 try: | ||||
|                     sounds['menu-move'].play() | ||||
|                 except: | ||||
|                     pass | ||||
|  | ||||
|             if event.key == pygame.K_HOME and i != 0: | ||||
|                 i = 0 | ||||
|                 try: | ||||
|                     sounds['menu-move'].play() | ||||
|                 except: | ||||
|                     pass | ||||
|  | ||||
|             if event.key == pygame.K_END and i != len(options) - 1: | ||||
|                 i = len(options) - 1 | ||||
|                 try: | ||||
|                     sounds['menu-move'].play() | ||||
|                 except: | ||||
|                     pass | ||||
|  | ||||
|             if event.key == pygame.K_RETURN: | ||||
|                 try: | ||||
|                     sounds['menu-select'].play() | ||||
|                     time.sleep(sounds['menu-select'].get_length()) | ||||
|                 except: | ||||
|                     pass | ||||
|                 return options[i] | ||||
|         elif event.type == pygame.QUIT: | ||||
|             return None | ||||
|  | ||||
|         pygame.event.pump() | ||||
|         event = pygame.event.clear() | ||||
|         time.sleep(0.001) | ||||
|     return instruction_menu(sounds, "Select an adventure", *options) | ||||
|  | ||||
| def select_game(sounds): | ||||
|     """Display game selection menu and return chosen game. | ||||
| @@ -134,4 +81,14 @@ def get_level_path(gameDir, levelNum): | ||||
|     """ | ||||
|     if gameDir is None: | ||||
|         raise ValueError("gameDir cannot be None") | ||||
|     return os.path.join("levels", gameDir, f"{levelNum}.json") | ||||
|      | ||||
|     # Handle PyInstaller path issues | ||||
|     if hasattr(sys, '_MEIPASS'): | ||||
|         # Running as PyInstaller executable | ||||
|         base_path = sys._MEIPASS | ||||
|     else: | ||||
|         # Running as script | ||||
|         base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||||
|      | ||||
|     level_path = os.path.join(base_path, "levels", gameDir, f"{levelNum}.json") | ||||
|     return level_path | ||||
|   | ||||
| @@ -40,7 +40,8 @@ class Level: | ||||
|         # Pass footstep sound to player | ||||
|         self.player.set_footstep_sound(self.footstepSound) | ||||
|  | ||||
|         # Level intro message | ||||
|         # Level intro message (skip for survival mode) | ||||
|         if levelData['level_id'] != 999:  # 999 is survival mode | ||||
|             levelIntro = f"Level {levelData['level_id']}, {levelData['name']}. " | ||||
|             if self.isLocked: | ||||
|                 levelIntro += "This is a boss level. You must defeat all enemies before you can advance. " | ||||
|   | ||||
							
								
								
									
										224
									
								
								src/survival_generator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								src/survival_generator.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| import json | ||||
| import os | ||||
| import random | ||||
| import copy | ||||
| from src.game_selection import get_level_path | ||||
|  | ||||
|  | ||||
| class SurvivalGenerator: | ||||
|     def __init__(self, gamePack): | ||||
|         """Initialize the survival generator for a specific game pack. | ||||
|          | ||||
|         Args: | ||||
|             gamePack (str): Name of the game pack directory | ||||
|         """ | ||||
|         self.gamePack = gamePack | ||||
|         self.levelData = {} | ||||
|         self.objectTemplates = [] | ||||
|         self.enemyTemplates = [] | ||||
|         self.collectibleTemplates = [] | ||||
|         self.hazardTemplates = [] | ||||
|         self.ambientSounds = [] | ||||
|         self.footstepSounds = [] | ||||
|         self.loadLevelData() | ||||
|         self.parseTemplates() | ||||
|      | ||||
|     def loadLevelData(self): | ||||
|         """Load all level JSON files from the game pack.""" | ||||
|         levelFiles = [] | ||||
|         packPath = os.path.join("levels", self.gamePack) | ||||
|          | ||||
|         if not os.path.exists(packPath): | ||||
|             raise FileNotFoundError(f"Game pack '{self.gamePack}' not found") | ||||
|          | ||||
|         # Get all JSON files in the pack directory | ||||
|         for file in os.listdir(packPath): | ||||
|             if file.endswith('.json') and file[0].isdigit(): | ||||
|                 levelFiles.append(file) | ||||
|          | ||||
|         # Load each level file | ||||
|         for levelFile in levelFiles: | ||||
|             levelPath = os.path.join(packPath, levelFile) | ||||
|             with open(levelPath, 'r') as f: | ||||
|                 levelNum = int(levelFile.split('.')[0]) | ||||
|                 self.levelData[levelNum] = json.load(f) | ||||
|      | ||||
|     def parseTemplates(self): | ||||
|         """Parse all level data to extract object templates by type.""" | ||||
|         for levelNum, data in self.levelData.items(): | ||||
|             # Store ambience and footstep sounds | ||||
|             if 'ambience' in data: | ||||
|                 self.ambientSounds.append(data['ambience']) | ||||
|             if 'footstep_sound' in data: | ||||
|                 self.footstepSounds.append(data['footstep_sound']) | ||||
|              | ||||
|             # Parse objects | ||||
|             for obj in data.get('objects', []): | ||||
|                 objCopy = copy.deepcopy(obj) | ||||
|                  | ||||
|                 # Categorize objects | ||||
|                 if 'enemy_type' in obj: | ||||
|                     self.enemyTemplates.append(objCopy) | ||||
|                 elif obj.get('collectible', False) or obj.get('sound') == 'bone_dust': | ||||
|                     self.collectibleTemplates.append(objCopy) | ||||
|                 elif obj.get('type') in ['skull_storm', 'catapult', 'grasping_hands']: | ||||
|                     self.hazardTemplates.append(objCopy) | ||||
|                 else: | ||||
|                     self.objectTemplates.append(objCopy) | ||||
|      | ||||
|     def generate_survival_level(self, difficultyLevel=1, segmentLength=100): | ||||
|         """Generate an endless survival level segment. | ||||
|          | ||||
|         Args: | ||||
|             difficultyLevel (int): Current difficulty level (increases over time) | ||||
|             segmentLength (int): Length of this level segment | ||||
|              | ||||
|         Returns: | ||||
|             dict: Generated level data | ||||
|         """ | ||||
|         # Base level structure | ||||
|         levelData = { | ||||
|             "level_id": 999,  # Special ID for survival mode | ||||
|             "name": f"Wave {difficultyLevel}", | ||||
|             "description": "",  # Empty description to avoid automatic messagebox | ||||
|             "player_start": {"x": 0, "y": 0}, | ||||
|             "objects": [], | ||||
|             "boundaries": {"left": 0, "right": segmentLength}, | ||||
|             "ambience": "Escaping the Grave.ogg",  # Will be overridden below | ||||
|             "footstep_sound": "footstep_stone"  # Will be overridden below | ||||
|         } | ||||
|          | ||||
|         # Choose random music and footstep from any level | ||||
|         randomLevel = random.choice(list(self.levelData.values())) | ||||
|         if 'ambience' in randomLevel and randomLevel['ambience']: | ||||
|             levelData["ambience"] = randomLevel['ambience'] | ||||
|         if 'footstep_sound' in randomLevel and randomLevel['footstep_sound']: | ||||
|             levelData["footstep_sound"] = randomLevel['footstep_sound'] | ||||
|          | ||||
|         # Calculate spawn rates based on difficulty | ||||
|         collectibleDensity = max(0.1, 0.3 - (difficultyLevel * 0.02))  # Fewer collectibles over time | ||||
|         enemyDensity = min(0.8, 0.2 + (difficultyLevel * 0.05))  # More enemies over time   | ||||
|         hazardDensity = min(0.4, 0.1 + (difficultyLevel * 0.03))  # More hazards over time | ||||
|         objectDensity = max(0.1, 0.2 - (difficultyLevel * 0.01))  # Fewer misc objects over time | ||||
|          | ||||
|         # Generate objects across the segment | ||||
|         currentX = 10  # Start placing objects at x=10 | ||||
|          | ||||
|         while currentX < segmentLength - 10: | ||||
|             # Determine what to place based on probability | ||||
|             rand = random.random() | ||||
|              | ||||
|             if rand < collectibleDensity and self.collectibleTemplates: | ||||
|                 obj = self.place_collectible(currentX, difficultyLevel) | ||||
|                 currentX += random.randint(8, 15) | ||||
|             elif rand < collectibleDensity + enemyDensity and self.enemyTemplates: | ||||
|                 obj = self.place_enemy(currentX, difficultyLevel) | ||||
|                 currentX += random.randint(15, 25) | ||||
|             elif rand < collectibleDensity + enemyDensity + hazardDensity and self.hazardTemplates: | ||||
|                 obj = self.place_hazard(currentX, difficultyLevel) | ||||
|                 currentX += random.randint(20, 35) | ||||
|             elif rand < collectibleDensity + enemyDensity + hazardDensity + objectDensity and self.objectTemplates: | ||||
|                 obj = self.place_object(currentX, difficultyLevel) | ||||
|                 currentX += random.randint(12, 20) | ||||
|             else: | ||||
|                 currentX += random.randint(5, 15) | ||||
|                 continue | ||||
|              | ||||
|             if obj: | ||||
|                 levelData["objects"].append(obj) | ||||
|          | ||||
|         return levelData | ||||
|      | ||||
|     def place_collectible(self, xPos, difficultyLevel): | ||||
|         """Place a collectible at the given position.""" | ||||
|         template = random.choice(self.collectibleTemplates) | ||||
|         obj = copy.deepcopy(template) | ||||
|          | ||||
|         # Handle x_range vs single x | ||||
|         if 'x_range' in obj: | ||||
|             rangeSize = obj['x_range'][1] - obj['x_range'][0] | ||||
|             obj['x_range'] = [xPos, xPos + rangeSize] | ||||
|         else: | ||||
|             obj['x'] = xPos | ||||
|          | ||||
|         return obj | ||||
|      | ||||
|     def place_enemy(self, xPos, difficultyLevel): | ||||
|         """Place an enemy at the given position with scaled difficulty.""" | ||||
|         # Filter out boss enemies for early waves | ||||
|         bossEnemies = ['witch', 'boogie_man', 'revenant', 'ghost', 'headless_horseman'] | ||||
|          | ||||
|         if difficultyLevel < 3:  # Waves 1-2: no bosses | ||||
|             availableEnemies = [e for e in self.enemyTemplates  | ||||
|                               if e.get('enemy_type') not in bossEnemies] | ||||
|         elif difficultyLevel < 5:  # Waves 3-4: exclude the hardest bosses | ||||
|             hardestBosses = ['revenant', 'ghost', 'headless_horseman'] | ||||
|             availableEnemies = [e for e in self.enemyTemplates  | ||||
|                               if e.get('enemy_type') not in hardestBosses] | ||||
|         else:  # Wave 5+: all enemies allowed | ||||
|             availableEnemies = self.enemyTemplates | ||||
|          | ||||
|         # Fallback to all enemies if filtering removed everything | ||||
|         if not availableEnemies: | ||||
|             availableEnemies = self.enemyTemplates | ||||
|          | ||||
|         template = random.choice(availableEnemies) | ||||
|         obj = copy.deepcopy(template) | ||||
|          | ||||
|         # Scale enemy stats based on difficulty | ||||
|         healthMultiplier = 1 + (difficultyLevel * 0.15) | ||||
|         damageMultiplier = 1 + (difficultyLevel * 0.1) | ||||
|          | ||||
|         obj['health'] = int(obj.get('health', 1) * healthMultiplier) | ||||
|         obj['damage'] = max(1, int(obj.get('damage', 1) * damageMultiplier)) | ||||
|          | ||||
|         # Handle x_range vs single x | ||||
|         if 'x_range' in obj: | ||||
|             rangeSize = obj['x_range'][1] - obj['x_range'][0] | ||||
|             obj['x_range'] = [xPos, xPos + rangeSize] | ||||
|         else: | ||||
|             obj['x'] = xPos | ||||
|          | ||||
|         return obj | ||||
|      | ||||
|     def place_hazard(self, xPos, difficultyLevel): | ||||
|         """Place a hazard at the given position with scaled difficulty.""" | ||||
|         template = random.choice(self.hazardTemplates) | ||||
|         obj = copy.deepcopy(template) | ||||
|          | ||||
|         # Scale hazard difficulty | ||||
|         if obj.get('type') == 'skull_storm': | ||||
|             obj['damage'] = max(1, int(obj.get('damage', 1) * (1 + difficultyLevel * 0.1))) | ||||
|             obj['maximum_skulls'] = min(6, obj.get('maximum_skulls', 2) + (difficultyLevel // 3)) | ||||
|         elif obj.get('type') == 'catapult': | ||||
|             obj['fire_interval'] = max(1000, obj.get('fire_interval', 4000) - (difficultyLevel * 100)) | ||||
|          | ||||
|         # Handle x_range vs single x   | ||||
|         if 'x_range' in obj: | ||||
|             rangeSize = obj['x_range'][1] - obj['x_range'][0] | ||||
|             obj['x_range'] = [xPos, xPos + rangeSize] | ||||
|         else: | ||||
|             obj['x'] = xPos | ||||
|          | ||||
|         return obj | ||||
|      | ||||
|     def place_object(self, xPos, difficultyLevel): | ||||
|         """Place a misc object at the given position.""" | ||||
|         template = random.choice(self.objectTemplates) | ||||
|         obj = copy.deepcopy(template) | ||||
|          | ||||
|         # Handle graves - increase zombie spawn chance with difficulty | ||||
|         if obj.get('type') == 'grave': | ||||
|             baseChance = obj.get('zombie_spawn_chance', 0) | ||||
|             obj['zombie_spawn_chance'] = min(50, baseChance + (difficultyLevel * 2)) | ||||
|          | ||||
|         # Handle x_range vs single x | ||||
|         if 'x_range' in obj: | ||||
|             rangeSize = obj['x_range'][1] - obj['x_range'][0] | ||||
|             obj['x_range'] = [xPos, xPos + rangeSize] | ||||
|         else: | ||||
|             obj['x'] = xPos | ||||
|          | ||||
|         return obj | ||||
							
								
								
									
										168
									
								
								wicked_quest.py
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								wicked_quest.py
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ from src.object import Object | ||||
| from src.player import Player | ||||
| from src.game_selection import select_game, get_level_path | ||||
| from src.save_manager import SaveManager | ||||
| from src.survival_generator import SurvivalGenerator | ||||
|  | ||||
|  | ||||
| class WickedQuest: | ||||
| @@ -24,6 +25,9 @@ class WickedQuest: | ||||
|         self.currentGame = None | ||||
|         self.runLock = False  # Toggle behavior of the run keys | ||||
|         self.saveManager = SaveManager() | ||||
|         self.survivalGenerator = None | ||||
|         self.survivalWave = 1 | ||||
|         self.survivalScore = 0 | ||||
|  | ||||
|     def load_level(self, levelNumber): | ||||
|         """Load a level from its JSON file.""" | ||||
| @@ -87,7 +91,7 @@ class WickedQuest: | ||||
|         return errors | ||||
|  | ||||
|     def load_game_menu(self): | ||||
|         """Display load game menu with available saves""" | ||||
|         """Display load game menu with available saves using instruction_menu""" | ||||
|         save_files = self.saveManager.get_save_files() | ||||
|          | ||||
|         if not save_files: | ||||
| @@ -101,45 +105,17 @@ class WickedQuest: | ||||
|          | ||||
|         options.append("Cancel") | ||||
|          | ||||
|         # Show menu | ||||
|         currentIndex = 0 | ||||
|         lastSpoken = -1 | ||||
|         # Use instruction_menu for consistent behavior | ||||
|         choice = instruction_menu(self.sounds, "Select a save file to load:", *options) | ||||
|          | ||||
|         messagebox("Select a save file to load:") | ||||
|          | ||||
|         while True: | ||||
|             if currentIndex != lastSpoken: | ||||
|                 speak(options[currentIndex]) | ||||
|                 lastSpoken = currentIndex | ||||
|              | ||||
|             event = pygame.event.wait() | ||||
|             if event.type == pygame.KEYDOWN: | ||||
|                 if event.key == pygame.K_ESCAPE: | ||||
|                     return None | ||||
|                 elif event.key in [pygame.K_DOWN, pygame.K_s] and currentIndex < len(options) - 1: | ||||
|                     currentIndex += 1 | ||||
|                     try: | ||||
|                         self.sounds['menu-move'].play() | ||||
|                     except: | ||||
|                         pass | ||||
|                 elif event.key in [pygame.K_UP, pygame.K_w] and currentIndex > 0: | ||||
|                     currentIndex -= 1 | ||||
|                     try: | ||||
|                         self.sounds['menu-move'].play() | ||||
|                     except: | ||||
|                         pass | ||||
|                 elif event.key == pygame.K_RETURN: | ||||
|                     try: | ||||
|                         self.sounds['menu-select'].play() | ||||
|                     except: | ||||
|                         pass | ||||
|                      | ||||
|                     if currentIndex == len(options) - 1:  # Cancel | ||||
|         if choice == "Cancel" or choice is None: | ||||
|             return None | ||||
|         else: | ||||
|                         return save_files[currentIndex] | ||||
|              | ||||
|             pygame.event.clear() | ||||
|             # Find the corresponding save file | ||||
|             for save_file in save_files: | ||||
|                 if save_file['display_name'] == choice: | ||||
|                     return save_file | ||||
|             return None | ||||
|  | ||||
|     def auto_save(self): | ||||
|         """Automatically save the game if player has enough bone dust""" | ||||
| @@ -448,10 +424,15 @@ class WickedQuest: | ||||
|                     display_text(errorLines) | ||||
|                     continue | ||||
|                 if self.currentGame: | ||||
|                     # Ask player to choose game mode | ||||
|                     mode_choice = game_mode_menu(self.sounds) | ||||
|                     if mode_choice == "campaign": | ||||
|                         self.player = None  # Reset player for new game | ||||
|                         self.gameStartTime = pygame.time.get_ticks() | ||||
|                         if self.load_level(1): | ||||
|                             self.game_loop() | ||||
|                     elif mode_choice == "survival": | ||||
|                         self.start_survival_mode() | ||||
|             elif choice == "high_scores": | ||||
|                 board = Scoreboard() | ||||
|                 scores = board.get_high_scores() | ||||
| @@ -465,6 +446,119 @@ class WickedQuest: | ||||
|             elif choice == "learn_sounds": | ||||
|                 choice = learn_sounds(self.sounds) | ||||
|  | ||||
|     def start_survival_mode(self): | ||||
|         """Initialize and start survival mode.""" | ||||
|         self.survivalGenerator = SurvivalGenerator(self.currentGame) | ||||
|         self.survivalWave = 1 | ||||
|         self.survivalScore = 0 | ||||
|         self.player = Player(0, 0, self.sounds) | ||||
|         self.gameStartTime = pygame.time.get_ticks() | ||||
|          | ||||
|         # Generate first survival segment | ||||
|         levelData = self.survivalGenerator.generate_survival_level(self.survivalWave, 300) | ||||
|         self.currentLevel = Level(levelData, self.sounds, self.player) | ||||
|          | ||||
|         messagebox(f"Survival Mode - Wave {self.survivalWave}! Survive as long as you can!") | ||||
|         self.survival_loop() | ||||
|      | ||||
|     def survival_loop(self): | ||||
|         """Main survival mode game loop with endless level generation.""" | ||||
|         clock = pygame.time.Clock() | ||||
|          | ||||
|         while True: | ||||
|             currentTime = pygame.time.get_ticks() | ||||
|             pygame.event.pump() | ||||
|              | ||||
|             # Handle events | ||||
|             for event in pygame.event.get(): | ||||
|                 if event.type == pygame.KEYDOWN: | ||||
|                     if event.key == pygame.K_ESCAPE: | ||||
|                         messagebox(f"Survival ended! Final score: {self.survivalScore}") | ||||
|                         return | ||||
|                     elif event.key in [pygame.K_CAPSLOCK, pygame.K_TAB]: | ||||
|                         self.runLock = not self.runLock | ||||
|                         speak("Run lock " + ("enabled." if self.runLock else "disabled.")) | ||||
|                     elif event.key == pygame.K_BACKSPACE: | ||||
|                         pause_game() | ||||
|                 elif event.type == pygame.QUIT: | ||||
|                     exit_game() | ||||
|              | ||||
|             # Update game state (following main game_loop pattern) | ||||
|             self.currentLevel.player.update(currentTime) | ||||
|             self.handle_input() | ||||
|             self.currentLevel.update_audio() | ||||
|              | ||||
|             # Handle combat and projectiles | ||||
|             self.currentLevel.handle_combat(currentTime) | ||||
|             self.currentLevel.handle_projectiles(currentTime) | ||||
|              | ||||
|             # Handle collisions (including collecting items) | ||||
|             self.currentLevel.handle_collisions() | ||||
|              | ||||
|             # Check if player reached end of segment - generate new one | ||||
|             if self.player.xPos >= self.currentLevel.rightBoundary - 20: | ||||
|                 self.advance_survival_wave() | ||||
|              | ||||
|             # Check for death first (following main game loop pattern) | ||||
|             if self.currentLevel.player.get_health() <= 0: | ||||
|                 if self.currentLevel.player.get_lives() <= 0: | ||||
|                     # Game over - stop all sounds | ||||
|                     pygame.mixer.stop() | ||||
|                     messagebox(f"Game Over! Final wave: {self.survivalWave}, Final score: {self.survivalScore}") | ||||
|                     return | ||||
|                 else: | ||||
|                     # Player died but has lives left - respawn | ||||
|                     pygame.mixer.stop() | ||||
|                     self.currentLevel.player._health = self.currentLevel.player._maxHealth | ||||
|                     # Reset player position to beginning of current segment | ||||
|                     self.player.xPos = 10 | ||||
|              | ||||
|             # Update score based on survival time | ||||
|             self.survivalScore += 1 | ||||
|              | ||||
|             clock.tick(60)  # 60 FPS | ||||
|      | ||||
|     def advance_survival_wave(self): | ||||
|         """Generate next wave/segment for survival mode.""" | ||||
|         self.survivalWave += 1 | ||||
|          | ||||
|         # Clear any lingering projectiles/sounds from previous wave | ||||
|         if hasattr(self, 'currentLevel') and self.currentLevel: | ||||
|             self.currentLevel.projectiles.clear() | ||||
|         pygame.mixer.stop()  # Stop any ongoing catapult/enemy sounds | ||||
|          | ||||
|         # Generate new segment | ||||
|         segmentLength = min(500, 300 + (self.survivalWave * 20))  # Longer segments over time | ||||
|         levelData = self.survivalGenerator.generate_survival_level(self.survivalWave, segmentLength) | ||||
|          | ||||
|         # Preserve player position but shift to start of new segment | ||||
|         playerX = 10 | ||||
|         self.player.xPos = playerX | ||||
|          | ||||
|         # Create new level | ||||
|         self.currentLevel = Level(levelData, self.sounds, self.player) | ||||
|          | ||||
|         speak(f"Wave {self.survivalWave}! Difficulty increased!") | ||||
|  | ||||
|  | ||||
| def game_mode_menu(sounds): | ||||
|     """Display game mode selection menu using instruction_menu. | ||||
|      | ||||
|     Args: | ||||
|         sounds (dict): Dictionary of loaded sound effects | ||||
|          | ||||
|     Returns: | ||||
|         str: Selected game mode or None if cancelled | ||||
|     """ | ||||
|     choice = instruction_menu(sounds, "Select game mode:", "Campaign", "Survival Mode") | ||||
|      | ||||
|     if choice == "Campaign": | ||||
|         return "campaign" | ||||
|     elif choice == "Survival Mode": | ||||
|         return "survival" | ||||
|     else: | ||||
|         return None | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     game = WickedQuest() | ||||
|   | ||||
| @@ -5,7 +5,11 @@ a = Analysis( | ||||
|     ['wicked_quest.py'], | ||||
|     pathex=[], | ||||
|     binaries=[], | ||||
|     datas=[], | ||||
|     datas=[ | ||||
|         ('levels', 'levels'), | ||||
|         ('sounds', 'sounds'), | ||||
|         ('libstormgames', 'libstormgames'), | ||||
|     ], | ||||
|     hiddenimports=[], | ||||
|     hookspath=[], | ||||
|     hooksconfig={}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user