diff --git a/move_dirs_hook.py b/move_dirs_hook.py index b81a76d..1fe5739 100644 --- a/move_dirs_hook.py +++ b/move_dirs_hook.py @@ -5,53 +5,55 @@ import shutil # Runtime hook to move directories and files from _internal to parent directory if hasattr(sys, '_MEIPASS'): # We're running from a PyInstaller bundle - bundle_dir = os.path.dirname(sys.executable) - internal_dir = sys._MEIPASS + bundleDir = os.path.dirname(sys.executable) + internalDir = sys._MEIPASS # Directories to move from _internal to parent - dirs_to_move = ['sounds', 'libstormgames'] + # All these use the parent directory for easier user customization + dirsToMove = ['sounds', 'libstormgames', 'levels'] # Directories to copy (keep in both locations) - dirs_to_copy = ['levels'] + # None needed - everything now uses parent directory + dirsToCopy = [] # Files to move from _internal to parent - files_to_move = ['files', 'logo.png'] + filesToMove = ['files', 'logo.png'] # Move directories - for dir_name in dirs_to_move: - internal_path = os.path.join(internal_dir, dir_name) - target_path = os.path.join(bundle_dir, dir_name) + for dir_name in dirsToMove: + internalPath = os.path.join(internalDir, dir_name) + targetPath = os.path.join(bundleDir, dir_name) # Only move if source exists and target doesn't exist - if os.path.exists(internal_path) and not os.path.exists(target_path): + if os.path.exists(internalPath) and not os.path.exists(targetPath): try: - shutil.move(internal_path, target_path) + shutil.move(internalPath, targetPath) except Exception as e: # Silently fail if we can't move - game will still work from _internal pass # Copy directories (keep in both locations) - for dir_name in dirs_to_copy: - internal_path = os.path.join(internal_dir, dir_name) - target_path = os.path.join(bundle_dir, dir_name) + for dir_name in dirsToCopy: + internalPath = os.path.join(internalDir, dir_name) + targetPath = os.path.join(bundleDir, dir_name) # Only copy if source exists and target doesn't exist - if os.path.exists(internal_path) and not os.path.exists(target_path): + if os.path.exists(internalPath) and not os.path.exists(targetPath): try: - shutil.copytree(internal_path, target_path) + shutil.copytree(internalPath, targetPath) except Exception as e: # Silently fail if we can't copy - game will still work from _internal pass # Move files - for file_name in files_to_move: - internal_path = os.path.join(internal_dir, file_name) - target_path = os.path.join(bundle_dir, file_name) + for file_name in filesToMove: + internalPath = os.path.join(internalDir, file_name) + targetPath = os.path.join(bundleDir, file_name) # Only move if source exists and target doesn't exist - if os.path.exists(internal_path) and not os.path.exists(target_path): + if os.path.exists(internalPath) and not os.path.exists(targetPath): try: - shutil.move(internal_path, target_path) + shutil.move(internalPath, targetPath) except Exception as e: # Silently fail if we can't move - game will still work from _internal at least enough to exit. pass diff --git a/sounds/grave.ogg b/sounds/grave.ogg index cbaf9f9..62dea5d 100644 --- a/sounds/grave.ogg +++ b/sounds/grave.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f63cf7cbb16dfb2782f2b0e4f57f67cc70b22be98820bbdf02aeae938aa57b04 -size 160157 +oid sha256:3f95061b4fb7a6b6acc4bd514123e4949b9cbccafdc069b874976f0b0fb1ec83 +size 183297 diff --git a/src/coffin.py b/src/coffin.py index de42ee8..1f58896 100644 --- a/src/coffin.py +++ b/src/coffin.py @@ -18,8 +18,8 @@ class CoffinObject(Object): self.sounds = sounds self.level = level self.isBroken = False - self.dropped_item = None - self.specified_item = item + self.droppedItem = None + self.specifiedItem = item def hit(self, player_pos): """Handle being hit by the player's weapon""" @@ -38,32 +38,32 @@ class CoffinObject(Object): self.isActive = False # Determine item to drop - if self.specified_item == "random": + if self.specifiedItem == "random": # Check if we're in survival mode (level_id 999) - is_survival_mode = hasattr(self.level, 'levelData') and self.level.levelData.get('level_id') == 999 - item_type = ItemProperties.get_random_item(survival_mode=is_survival_mode) + isSurvivalMode = hasattr(self.level, 'levelData') and self.level.levelData.get('level_id') == 999 + itemType = ItemProperties.get_random_item(survivalMode=isSurvivalMode) else: # Validate specified item - if ItemProperties.is_valid_item(self.specified_item): - item_type = self.specified_item + if ItemProperties.is_valid_item(self.specifiedItem): + itemType = self.specifiedItem else: # Fall back to random if invalid item specified # Check if we're in survival mode (level_id 999) - is_survival_mode = hasattr(self.level, 'levelData') and self.level.levelData.get('level_id') == 999 - item_type = ItemProperties.get_random_item(survival_mode=is_survival_mode) + isSurvivalMode = hasattr(self.level, 'levelData') and self.level.levelData.get('level_id') == 999 + itemType = ItemProperties.get_random_item(survivalMode=isSurvivalMode) # Create item 1-2 tiles away in random direction direction = random.choice([-1, 1]) - drop_distance = random.randint(1, 2) - drop_x = self.xPos + (direction * drop_distance) + dropDistance = random.randint(1, 2) + dropX = self.xPos + (direction * dropDistance) - self.dropped_item = PowerUp( - drop_x, self.yPos, item_type, self.sounds, direction, self.level.leftBoundary, self.level.rightBoundary + self.droppedItem = PowerUp( + dropX, self.yPos, itemType, self.sounds, direction, self.level.leftBoundary, self.level.rightBoundary ) # Apply sound override after creation (similar to how graves handle it) if hasattr(self, 'itemSoundOverride'): - self.dropped_item.soundName = self.itemSoundOverride + self.droppedItem.soundName = self.itemSoundOverride return True return False diff --git a/src/enemy.py b/src/enemy.py index a43a2fd..3ff77df 100644 --- a/src/enemy.py +++ b/src/enemy.py @@ -19,7 +19,7 @@ class Enemy(Object): self.level = level self.health = kwargs.get("health", 5) # Default 5 HP self.damage = kwargs.get("damage", 1) # Default 1 damage - self.attackRange = kwargs.get("attack_range", 1) # Default 1 tile range + self.attackRange = kwargs.get("attackRange", 1) # Default 1 tile range self.sounds = sounds # Store reference to game sounds # Movement and behavior properties @@ -32,25 +32,25 @@ class Enemy(Object): self._currentX = self.xRange[0] # Initialize current position # Add spawn configuration - self.canSpawn = kwargs.get("can_spawn", False) + self.canSpawn = kwargs.get("canSpawn", False) if self.canSpawn: - self.spawnCooldown = kwargs.get("spawn_cooldown", 2000) - self.spawnChance = kwargs.get("spawn_chance", 25) - self.spawnType = kwargs.get("spawn_type", "zombie") # Default to zombie for backward compatibility - self.spawnDistance = kwargs.get("spawn_distance", 5) + self.spawnCooldown = kwargs.get("spawnCooldown", 2000) + self.spawnChance = kwargs.get("spawnChance", 25) + self.spawnType = kwargs.get("spawnType", "zombie") # Default to zombie for backward compatibility + self.spawnDistance = kwargs.get("spawnDistance", 5) self.lastSpawnTime = 0 # Attack pattern configuration - self.attackPattern = kwargs.get("attack_pattern", {"type": "patrol"}) + self.attackPattern = kwargs.get("attackPattern", {"type": "patrol"}) self.turnThreshold = self.attackPattern.get("turn_threshold", 5) # Initialize vulnerability system - self.hasVulnerabilitySystem = kwargs.get("has_vulnerability", False) + self.hasVulnerabilitySystem = kwargs.get("hasVulnerability", False) if self.hasVulnerabilitySystem: self.isVulnerable = False # Start invulnerable self.vulnerabilityTimer = pygame.time.get_ticks() - self.vulnerabilityDuration = kwargs.get("vulnerability_duration", 2000) - self.invulnerabilityDuration = kwargs.get("invulnerability_duration", 5000) + self.vulnerabilityDuration = kwargs.get("vulnerabilityDuration", 2000) + self.invulnerabilityDuration = kwargs.get("invulnerabilityDuration", 5000) soundName = f"{self.enemyType}_is_vulnerable" if self.isVulnerable else self.enemyType self.channel = obj_play(self.sounds, soundName, self.level.player.xPos, self.xPos) else: @@ -63,7 +63,7 @@ class Enemy(Object): self.health = 1 # Easy to kill self.attackCooldown = 1500 # Slower attack rate elif enemyType == "spider": - speedMultiplier = kwargs.get("speed_multiplier", 2.0) + speedMultiplier = kwargs.get("speedMultiplier", 2.0) self.movementSpeed *= speedMultiplier # Spiders are faster self.attackPattern = {"type": "hunter"} # Spiders actively hunt the player self.turnThreshold = 3 # Spiders turn around quickly to chase player @@ -162,7 +162,7 @@ class Enemy(Object): # Set behavior based on game mode behavior = "hunter" if self.level.levelId == 999 else "patrol" - turn_rate = 2 if self.level.levelId == 999 else 8 # Faster turn rate for survival + turnRate = 2 if self.level.levelId == 999 else 8 # Faster turn rate for survival # Create new enemy of specified type spawned = Enemy( @@ -173,9 +173,9 @@ class Enemy(Object): self.level, health=4, # Default health for spawned enemies damage=2, # Default damage for spawned enemies - attack_range=1, # Default range for spawned enemies - attack_pattern={"type": behavior}, - turn_rate=turn_rate, + attackRange=1, # Default range for spawned enemies + attackPattern={"type": behavior}, + turnRate=turnRate, ) # Add to level's enemies @@ -273,7 +273,7 @@ class Enemy(Object): droppedItem = PowerUp( dropX, self.yPos, itemType, self.sounds, direction, self.level.leftBoundary, self.level.rightBoundary ) - self.level.bouncing_items.append(droppedItem) + self.level.bouncingItems.append(droppedItem) # Update stats self.level.player.stats.update_stat("Enemies killed", 1) diff --git a/src/game_selection.py b/src/game_selection.py index 89661ae..b0c79c5 100644 --- a/src/game_selection.py +++ b/src/game_selection.py @@ -15,7 +15,9 @@ def get_levels_base_path(): """ if hasattr(sys, "_MEIPASS"): # Running as PyInstaller executable - return sys._MEIPASS + # Use parent directory (where executable is) instead of _internal + # This allows users to add new level packs without recompiling + return os.path.dirname(sys.executable) else: # Running as script return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -28,9 +30,9 @@ def get_available_games(): list: List of game directory names """ try: - base_path = get_levels_base_path() - levels_path = os.path.join(base_path, "levels") - return [d for d in os.listdir(levels_path) if isdir(join(levels_path, d)) and not d.endswith(".md")] + basePath = get_levels_base_path() + levelsPath = os.path.join(basePath, "levels") + return [d for d in os.listdir(levelsPath) if isdir(join(levelsPath, d)) and not d.endswith(".md")] except FileNotFoundError: return [] @@ -57,57 +59,57 @@ def select_game(sounds): Returns: str: Selected game directory name or None if cancelled """ - available_games = get_available_games() + availableGames = get_available_games() - if not available_games: + if not availableGames: speak("No games found in levels directory!") return None # Convert directory names to display names (replace underscores with spaces) - menu_options = [game.replace("_", " ") for game in available_games] + menuOptions = [game.replace("_", " ") for game in availableGames] - choice = selection_menu(sounds, *menu_options) + choice = selection_menu(sounds, *menuOptions) if choice is None: return None # Convert display name back to directory name if needed - game_dir = choice.replace(" ", "_") - if game_dir not in available_games: - game_dir = choice # Use original if conversion doesn't match + gameDir = choice.replace(" ", "_") + if gameDir not in availableGames: + gameDir = choice # Use original if conversion doesn't match - return game_dir + return gameDir -def get_level_path(game_dir, level_num): +def get_level_path(gameDir, levelNum): """Get full path to level JSON file. Args: - game_dir (str): Game directory name - level_num (int): Level number + gameDir (str): Game directory name + levelNum (int): Level number Returns: str: Full path to level JSON file """ - if game_dir is None: - raise ValueError("game_dir cannot be None") + if gameDir is None: + raise ValueError("gameDir cannot be None") - base_path = get_levels_base_path() - level_path = os.path.join(base_path, "levels", game_dir, f"{level_num}.json") - return level_path + basePath = get_levels_base_path() + levelPath = os.path.join(basePath, "levels", gameDir, f"{levelNum}.json") + return levelPath -def get_game_dir_path(game_dir): +def get_game_dir_path(gameDir): """Get full path to game directory for end.ogg and other game files. Args: - game_dir (str): Game directory name + gameDir (str): Game directory name Returns: str: Full path to game directory """ - if game_dir is None: - raise ValueError("game_dir cannot be None") + if gameDir is None: + raise ValueError("gameDir cannot be None") - base_path = get_levels_base_path() - return os.path.join(base_path, "levels", game_dir) + basePath = get_levels_base_path() + return os.path.join(basePath, "levels", gameDir) diff --git a/src/grasping_hands.py b/src/grasping_hands.py index 3eb72a0..16ca986 100644 --- a/src/grasping_hands.py +++ b/src/grasping_hands.py @@ -8,7 +8,7 @@ from src.object import Object class GraspingHands(Object): """A hazard where the ground crumbles beneath the player as undead hands reach up.""" - def __init__(self, xRange, y, sounds, delay=1000, crumble_speed=0.065): + def __init__(self, xRange, y, sounds, delay=1000, crumbleSpeed=0.065): super().__init__( xRange, y, @@ -19,7 +19,7 @@ class GraspingHands(Object): ) self.sounds = sounds self.delay = delay # Delay in milliseconds before ground starts crumbling - self.crumble_speed = crumble_speed # How fast the crumbling catches up (tiles per frame) + self.crumbleSpeed = crumbleSpeed # How fast the crumbling catches up (tiles per frame) # Sound prefix for different sound effects (can be overridden) self.soundPrefix = "grasping_hands" @@ -95,7 +95,7 @@ class GraspingHands(Object): # If triggered and delay has passed, start crumbling if self.isTriggered and currentTime - self.triggerTime >= self.delay: # Update crumble position based on direction - self.crumblePosition += self.crumble_speed * self.crumbleDirection + self.crumblePosition += self.crumbleSpeed * self.crumbleDirection # Manage the looping positional audio for the crumbling ground if self.crumbleChannel is None or not self.crumbleChannel.get_busy(): diff --git a/src/item_types.py b/src/item_types.py index 9af0fd4..b032d5c 100644 --- a/src/item_types.py +++ b/src/item_types.py @@ -45,28 +45,28 @@ class ItemProperties: } @staticmethod - def get_sound_name(item_type): + def get_sound_name(itemType): """Convert enum to sound/asset name""" - return ItemProperties.ALL_ITEMS.get(item_type) + return ItemProperties.ALL_ITEMS.get(itemType) @staticmethod - def get_random_item(survival_mode=False): + def get_random_item(survivalMode=False): """Get a random item from eligible items""" - if survival_mode: - item_type = random.choice(list(ItemProperties.SURVIVAL_MODE_ELIGIBLE.keys())) + if survivalMode: + itemType = random.choice(list(ItemProperties.SURVIVAL_MODE_ELIGIBLE.keys())) else: - item_type = random.choice(list(ItemProperties.STORY_MODE_ELIGIBLE.keys())) - return ItemProperties.get_sound_name(item_type) + itemType = random.choice(list(ItemProperties.STORY_MODE_ELIGIBLE.keys())) + return ItemProperties.get_sound_name(itemType) @staticmethod - def is_valid_item(item_name): + def is_valid_item(itemName): """Check if an item name is valid""" - return item_name in [v for v in ItemProperties.ALL_ITEMS.values()] + return itemName in [v for v in ItemProperties.ALL_ITEMS.values()] @staticmethod - def get_item_type(item_name): + def get_item_type(itemName): """Get ItemType enum from string name""" - for item_type, name in ItemProperties.ALL_ITEMS.items(): - if name == item_name: - return item_type + for itemType, name in ItemProperties.ALL_ITEMS.items(): + if name == itemName: + return itemType return None diff --git a/src/level.py b/src/level.py index 5c9f208..f85f7e2 100644 --- a/src/level.py +++ b/src/level.py @@ -22,12 +22,12 @@ class Level: self.levelPackName = levelPackName self.objects = [] self.enemies = [] - self.bouncing_items = [] + self.bouncingItems = [] self.projectiles = [] # Track active projectiles self.player = player self.lastWarningTime = 0 self.warningInterval = int(self.sounds["edge"].get_length() * 1000) # Convert seconds to milliseconds - self.weapon_hit_channel = None + self.weaponHitChannel = None self.leftBoundary = levelData["boundaries"]["left"] self.rightBoundary = levelData["boundaries"]["right"] @@ -137,7 +137,7 @@ class Level: obj["y"], self.sounds, delay=obj.get("delay", 1000), - crumble_speed=obj.get("crumble_speed", 0.03), + crumbleSpeed=obj.get("crumble_speed", 0.03), ) # Apply sound overrides if specified if "sound_overrides" in obj: @@ -146,25 +146,25 @@ class Level: # Check if this is a grave elif obj.get("type") == "grave": # Handle item spawning with chance (for survival mode) - grave_item = None + graveItem = None if "item_spawn_chance" in obj: # Survival mode: spawn item based on chance (random items already resolved) - spawn_chance = obj.get("item_spawn_chance", 0) - if random.randint(1, 100) <= spawn_chance: - grave_item = obj.get("item") + spawnChance = obj.get("item_spawn_chance", 0) + if random.randint(1, 100) <= spawnChance: + graveItem = obj.get("item") else: # Story mode: use item as specified - grave_item = obj.get("item", None) - if grave_item == "random": + graveItem = obj.get("item", None) + if graveItem == "random": # Check if we're in survival mode (level_id 999) - is_survival_mode = self.levelData.get('level_id') == 999 - grave_item = ItemProperties.get_random_item(survival_mode=is_survival_mode) + isSurvivalMode = self.levelData.get('level_id') == 999 + graveItem = ItemProperties.get_random_item(survivalMode=isSurvivalMode) grave = GraveObject( xPos[0], obj["y"], self.sounds, - item=grave_item, + item=graveItem, zombieSpawnChance=obj.get("zombie_spawn_chance", 0), ) # Apply sound overrides if specified @@ -232,18 +232,18 @@ class Level: self, # Pass level reference health=obj.get("health", 5), damage=obj.get("damage", 1), - attack_range=obj.get("attack_range", 1), - movement_range=obj.get("movement_range", 5), - attack_pattern=obj.get("attack_pattern", {"type": "patrol"}), - can_spawn=obj.get("can_spawn", False), - spawn_type=obj.get("spawn_type", "zombie"), - spawn_cooldown=obj.get("spawn_cooldown", 2000), - spawn_chance=obj.get("spawn_chance", 25), - spawn_distance=obj.get("spawn_distance", 5), - has_vulnerability=obj.get("has_vulnerability", False), - is_vulnerable=obj.get("is_vulnerable", False), - vulnerability_duration=obj.get("vulnerability_duration", 1000), - invulnerability_duration=obj.get("invulnerability_duration", 5000), + attackRange=obj.get("attack_range", 1), + movementRange=obj.get("movement_range", 5), + attackPattern=obj.get("attack_pattern", {"type": "patrol"}), + canSpawn=obj.get("can_spawn", False), + spawnType=obj.get("spawn_type", "zombie"), + spawnCooldown=obj.get("spawn_cooldown", 2000), + spawnChance=obj.get("spawn_chance", 25), + spawnDistance=obj.get("spawn_distance", 5), + hasVulnerability=obj.get("has_vulnerability", False), + isVulnerable=obj.get("is_vulnerable", False), + vulnerabilityDuration=obj.get("vulnerability_duration", 1000), + invulnerabilityDuration=obj.get("invulnerability_duration", 5000), ) self.enemies.append(enemy) else: @@ -286,7 +286,7 @@ class Level: if roll <= obj.zombieSpawnChance: # Set behavior based on game mode behavior = "hunter" if self.levelId == 999 else "patrol" - turn_rate = 2 if self.levelId == 999 else 8 # Faster turn rate for survival + turnRate = 2 if self.levelId == 999 else 8 # Faster turn rate for survival zombie = Enemy( [obj.xPos, obj.xPos], @@ -296,9 +296,9 @@ class Level: self, # Pass the level reference health=3, damage=10, - attack_range=1, - attack_pattern={"type": behavior}, - turn_rate=turn_rate, + attackRange=1, + attackPattern={"type": behavior}, + turnRate=turnRate, ) self.enemies.append(zombie) speak("A zombie emerges from the grave!") @@ -354,9 +354,9 @@ class Level: self.objects = [obj for obj in self.objects if obj.isActive] # Update bouncing items - for item in self.bouncing_items[:]: # Copy list to allow removal + for item in self.bouncingItems[:]: # Copy list to allow removal if not item.update(currentTime, self.player.xPos): - self.bouncing_items.remove(item) + self.bouncingItems.remove(item) if not item.isActive: speak(f"{item.soundName} got away!") continue @@ -367,7 +367,7 @@ class Level: item.apply_effect(self.player, self) self.levelScore += 1000 # All items collected points awarded item.isActive = False - self.bouncing_items.remove(item) + self.bouncingItems.remove(item) def handle_combat(self, currentTime): """Handle combat interactions between player and enemies""" @@ -394,7 +394,7 @@ class Level: ): # Must be jumping to hit floating coffins if obj.hit(self.player.xPos): - self.bouncing_items.append(obj.dropped_item) + self.bouncingItems.append(obj.droppedItem) def spawn_spider(self, xPos, yPos): """Spawn a spider at the given position""" @@ -406,8 +406,8 @@ class Level: self, health=8, damage=8, - attack_range=1, - speed_multiplier=2.0, + attackRange=1, + speedMultiplier=2.0, ) self.enemies.append(spider) @@ -417,20 +417,20 @@ class Level: if enemyStats: health = enemyStats.get("health", 8) damage = enemyStats.get("damage", 2) - speed_multiplier = enemyStats.get("speed_multiplier", 1.0) - attack_range = enemyStats.get("attack_range", 1) + speedMultiplier = enemyStats.get("speed_multiplier", 1.0) + attackRange = enemyStats.get("attack_range", 1) else: # Default stats for common enemy types (backward compatibility) if enemyType == "spider": - health, damage, speed_multiplier = 8, 8, 2.0 + health, damage, speedMultiplier = 8, 8, 2.0 elif enemyType == "elf": - health, damage, speed_multiplier = 2, 1, 1.0 + health, damage, speedMultiplier = 2, 1, 1.0 elif enemyType == "witch": - health, damage, speed_multiplier = 6, 2, 1.0 + health, damage, speedMultiplier = 6, 2, 1.0 else: # For any other enemy type, use reasonable defaults - health, damage, speed_multiplier = 6, 2, 1.0 - attack_range = 1 + health, damage, speedMultiplier = 6, 2, 1.0 + attackRange = 1 enemy = Enemy( [xPos - 5, xPos + 5], # Give enemy a patrol range @@ -440,9 +440,9 @@ class Level: self, health=health, damage=damage, - attack_range=attack_range, - speed_multiplier=speed_multiplier, - attack_pattern={"type": "hunter"}, # Hunt like original spiders + attackRange=attackRange, + speedMultiplier=speedMultiplier, + attackPattern={"type": "hunter"}, # Hunt like original spiders ) self.enemies.append(enemy) @@ -550,16 +550,16 @@ class Level: # Handle graves and other hazards if obj.isHazard and not self.player.isJumping: if isinstance(obj, GraveObject): - can_collect = obj.collect_grave_item(self.player) and not self.player.diedThisFrame - can_fill = obj.can_fill_grave(self.player) + canCollect = obj.collect_grave_item(self.player) and not self.player.diedThisFrame + canFill = obj.can_fill_grave(self.player) - if can_collect: + if canCollect: # Successfully collected item while ducking with shovel # Check for item sound override - item_sound = obj.graveItem + itemSound = obj.graveItem if hasattr(obj, 'itemSoundOverride'): - item_sound = obj.itemSoundOverride - play_sound(self.sounds[f"get_{item_sound}"]) + itemSound = obj.itemSoundOverride + play_sound(self.sounds[f"get_{itemSound}"]) play_sound(self.sounds.get("fill_in_grave", "shovel_dig")) # Also play fill sound self.player.stats.update_stat("Items collected", 1) # Create PowerUp to handle the item effect @@ -575,7 +575,7 @@ class Level: obj.channel = None obj.isActive = False # Mark the grave as inactive after collection continue - elif can_fill and obj.fill_grave(self.player): + elif canFill and obj.fill_grave(self.player): # Successfully filled empty grave with shovel play_sound( self.sounds.get("fill_in_grave", "shovel_dig") @@ -643,12 +643,12 @@ class Level: def throw_projectile(self): """Have player throw a projectile""" - proj_info = self.player.throw_projectile() - if proj_info is None: + projInfo = self.player.throw_projectile() + if projInfo is None: speak("No jack o'lanterns to throw!") return - self.projectiles.append(Projectile(proj_info["type"], proj_info["start_x"], proj_info["direction"])) + self.projectiles.append(Projectile(projInfo["type"], projInfo["start_x"], projInfo["direction"])) # Play throw sound play_sound(self.sounds["throw_jack_o_lantern"]) @@ -663,11 +663,11 @@ class Level: # Create projectile with weapon-specific properties # Use a generic projectile type for weapon projectiles - projectile_type = f"weapon_{weapon.name}" - start_x = player.xPos + (direction * 1) # Start 1 tile away from player + projectileType = f"weapon_{weapon.name}" + startX = player.xPos + (direction * 1) # Start 1 tile away from player # Create projectile with custom speed and damage from weapon - projectile = Projectile(projectile_type, start_x, direction) + projectile = Projectile(projectileType, startX, direction) # Override projectile properties with weapon stats projectile.damage = weapon.damage @@ -822,7 +822,7 @@ class Level: obj.itemSoundOverride = soundOverrides["item"] # Handle item sound overrides for coffins - if hasattr(obj, 'specified_item') and obj.specified_item and obj.specified_item != "random" and "item" in soundOverrides: + if hasattr(obj, 'specifiedItem') and obj.specifiedItem and obj.specifiedItem != "random" and "item" in soundOverrides: # Store the override for later use when coffin is broken if not hasattr(obj, 'itemSoundOverride'): obj.itemSoundOverride = soundOverrides["item"] diff --git a/src/player.py b/src/player.py index 328f144..f446c54 100644 --- a/src/player.py +++ b/src/player.py @@ -45,7 +45,7 @@ class Player: self.collectedItems = [] self._coins = 0 # Regular bone dust for extra lives self._saveBoneDust = 0 # Separate bone dust counter for saves - self._jack_o_lantern_count = 0 + self._jackOLanternCount = 0 self.shinBoneCount = 0 # Combat related attributes @@ -115,7 +115,7 @@ class Player: # Check invincibility status if self.isInvincible: - remaining_time = ( + remainingTime = ( self.invincibilityStartTime + self.invincibilityDuration - currentTime ) / 1000 # Convert to seconds @@ -123,10 +123,10 @@ class Player: if not hasattr(self, "_last_countdown"): self._last_countdown = 4 # Start counting from 4 to catch 3,2,1 - current_second = int(remaining_time) - if current_second < self._last_countdown and current_second <= 3 and current_second > 0: + currentSecond = int(remainingTime) + if currentSecond < self._last_countdown and currentSecond <= 3 and currentSecond > 0: play_sound(self.sounds["end_of_invincibility_warning"]) - self._last_countdown = current_second + self._last_countdown = currentSecond # Check if invincibility has expired if currentTime - self.invincibilityStartTime >= self.invincibilityDuration: @@ -147,11 +147,11 @@ class Player: def get_jack_o_lanterns(self): """Get number of jack o'lanterns""" - return self._jack_o_lantern_count + return self._jackOLanternCount def add_jack_o_lantern(self): """Add a jack o'lantern""" - self._jack_o_lantern_count += 1 + self._jackOLanternCount += 1 def add_guts(self): """Apply guts, increase max_health by 2 if less than 20 else restore health""" @@ -165,7 +165,7 @@ class Player: if self.get_jack_o_lanterns() <= 0: return None - self._jack_o_lantern_count -= 1 + self._jackOLanternCount -= 1 return {"type": "jack_o_lantern", "start_x": self.xPos, "direction": 1 if self.facingRight else -1} def get_step_distance(self): @@ -220,19 +220,19 @@ class Player: def set_health(self, value): """Set health and handle death if needed.""" - old_health = self._health + oldHealth = self._health # Oops, allow healing while invincible. - if self.isInvincible and value < old_health: + if self.isInvincible and value < oldHealth: return # Check for god mode (prevents all damage) - if hasattr(self, '_godMode') and self._godMode and value < old_health: + if hasattr(self, '_godMode') and self._godMode and value < oldHealth: return self._health = max(0, value) # Health can't go below 0 - if self._health == 0 and old_health > 0: + if self._health == 0 and oldHealth > 0: self._lives -= 1 # Mark that player died this frame to prevent revival self.diedThisFrame = True @@ -379,7 +379,7 @@ class Player: elif ammoType == "hand_of_glory": return self.collectedItems.count("hand_of_glory") >= cost elif ammoType == "jack_o_lantern": - return self._jack_o_lantern_count >= cost + return self._jackOLanternCount >= cost else: # Check for any other item type in collectedItems return self.collectedItems.count(ammoType) >= cost @@ -398,7 +398,7 @@ class Player: for _ in range(min(cost, self.collectedItems.count("hand_of_glory"))): self.collectedItems.remove("hand_of_glory") elif ammoType == "jack_o_lantern": - self._jack_o_lantern_count = max(0, self._jack_o_lantern_count - cost) + self._jackOLanternCount = max(0, self._jackOLanternCount - cost) else: # Handle any other item type for _ in range(min(cost, self.collectedItems.count(ammoType))): @@ -409,18 +409,18 @@ class Player: # Check for themed mappings in weapon overrides if hasattr(self, 'weaponOverrides') and self.weaponOverrides: # Define themed mappings (this could be extended or made configurable) - themed_mappings = { + themedMappings = { "shin_bone": "candy_cane", # Christmas theme example "guts": "reindeer_guts" } # Check if there's a themed equivalent and if the override exists - if ammoType in themed_mappings: - themed_name = themed_mappings[ammoType] + if ammoType in themedMappings: + themedName = themedMappings[ammoType] # If the themed item exists in overrides, use the themed name - for override_key, override_data in self.weaponOverrides.items(): - if isinstance(override_data, dict) and themed_name in str(override_data): - return themed_name.replace("_", " ") + for overrideKey, overrideData in self.weaponOverrides.items(): + if isinstance(overrideData, dict) and themedName in str(overrideData): + return themedName.replace("_", " ") # Return default name return ammoType.replace("_", " ") diff --git a/src/powerup.py b/src/powerup.py index 9067a2d..ad9b0b0 100644 --- a/src/powerup.py +++ b/src/powerup.py @@ -7,43 +7,43 @@ from src.weapon import Weapon class PowerUp(Object): - def __init__(self, x, y, item_type, sounds, direction, left_boundary=1, right_boundary=100): - super().__init__(x, y, item_type, isStatic=False, isCollectible=True, isHazard=False) + def __init__(self, x, y, itemType, sounds, direction, leftBoundary=1, rightBoundary=100): + super().__init__(x, y, itemType, isStatic=False, isCollectible=True, isHazard=False) self.sounds = sounds self.direction = direction self.speed = 0.049 # Base movement speed - self.item_type = item_type + self.itemType = itemType self.channel = None self._currentX = x # Initialize the current x position - self.left_boundary = left_boundary - self.right_boundary = right_boundary + self.leftBoundary = leftBoundary + self.rightBoundary = rightBoundary - def update(self, current_time, player_pos): + def update(self, currentTime, playerPos): """Update item position""" if not self.isActive: return False # Update position - new_x = self._currentX + self.direction * self.speed + newX = self._currentX + self.direction * self.speed # Check boundaries and bounce if needed - if new_x < self.left_boundary: - self._currentX = self.left_boundary + if newX < self.leftBoundary: + self._currentX = self.leftBoundary self.direction = 1 # Start moving right - elif new_x > self.right_boundary: - self._currentX = self.right_boundary + elif newX > self.rightBoundary: + self._currentX = self.rightBoundary self.direction = -1 # Start moving left else: - self._currentX = new_x + self._currentX = newX # Update positional audio if self.channel is None or not self.channel.get_busy(): - self.channel = obj_play(self.sounds, "item_bounce", player_pos, self._currentX) + self.channel = obj_play(self.sounds, "item_bounce", playerPos, self._currentX) else: - self.channel = obj_update(self.channel, player_pos, self._currentX) + self.channel = obj_update(self.channel, playerPos, self._currentX) # Check if item is too far from player (12 tiles) - if abs(self._currentX - player_pos) > 12: + if abs(self._currentX - playerPos) > 12: self.isActive = False if self.channel: self.channel.stop() @@ -55,22 +55,22 @@ class PowerUp(Object): def apply_effect(self, player, level=None): """Apply the item's effect when collected""" # Map themed items to their original equivalents for game logic - original_item_type = self._get_original_item_type(self.item_type) + originalItemType = self._get_original_item_type(self.itemType) - if original_item_type == "hand_of_glory": + if originalItemType == "hand_of_glory": player.start_invincibility() self.check_for_custom_weapons(player) - elif original_item_type == "cauldron": + elif originalItemType == "cauldron": player.restore_health() - elif original_item_type == "guts": + elif originalItemType == "guts": player.add_guts() - player.collectedItems.append(original_item_type) + player.collectedItems.append(originalItemType) self.check_for_nunchucks(player) self.check_for_custom_weapons(player) - elif original_item_type == "jack_o_lantern": + elif originalItemType == "jack_o_lantern": player.add_jack_o_lantern() self.check_for_custom_weapons(player) - elif original_item_type == "extra_life": + elif originalItemType == "extra_life": # Don't give extra lives in survival mode if level and level.levelId == 999: # In survival mode, give bonus score instead @@ -79,7 +79,7 @@ class PowerUp(Object): play_sound(self.sounds.get("survivor_bonus", "get_extra_life")) # Use survivor_bonus sound if available else: player.extra_life() - elif original_item_type == "shin_bone": # Add shin bone handling + elif originalItemType == "shin_bone": # Add shin bone handling player.shinBoneCount += 1 player._coins += 5 player.add_save_bone_dust(5) # Add to save bone dust counter too @@ -106,11 +106,11 @@ class PowerUp(Object): self.check_for_nunchucks(player) self.check_for_custom_weapons(player) - elif original_item_type == "witch_broom": + elif originalItemType == "witch_broom": broomWeapon = Weapon.create_witch_broom() player.add_weapon(broomWeapon) player.equip_weapon(broomWeapon) - elif original_item_type == "spiderweb": + elif originalItemType == "spiderweb": # Bounce player back (happens even if invincible) player.xPos -= 3 if player.xPos > self.xPos else -3 @@ -193,7 +193,7 @@ class PowerUp(Object): canCraft = False break elif itemType == "jack_o_lantern": - if player._jack_o_lantern_count < neededCount: + if player._jackOLanternCount < neededCount: canCraft = False break else: @@ -225,10 +225,10 @@ class PowerUp(Object): player.stats.update_stat("Items collected", 1) - def _get_original_item_type(self, item_type): + def _get_original_item_type(self, itemType): """Map themed item names to their original equivalents for game logic.""" # Define themed equivalents that should behave like original items - themed_mappings = { + themedMappings = { # Christmas theme "candy_cane": "shin_bone", "reindeer_guts": "guts", @@ -237,4 +237,4 @@ class PowerUp(Object): # "frozen_heart": "guts", } - return themed_mappings.get(item_type, item_type) + return themedMappings.get(itemType, itemType) diff --git a/src/projectile.py b/src/projectile.py index 7907a2a..b0a04cf 100644 --- a/src/projectile.py +++ b/src/projectile.py @@ -2,15 +2,15 @@ class Projectile: - def __init__(self, projectile_type, start_x, direction): - self.type = projectile_type - self.x = start_x + def __init__(self, projectileType, startX, direction): + self.type = projectileType + self.x = startX self.direction = direction self.speed = 0.2 # Projectiles move faster than player self.isActive = True self.damage = 5 # All projectiles do same damage for now self.range = 12 # Maximum travel distance in tiles - self.start_x = start_x + self.startX = startX def update(self): """Update projectile position and check if it should still exist""" @@ -20,7 +20,7 @@ class Projectile: self.x += self.direction * self.speed # Check if projectile has gone too far - if abs(self.x - self.start_x) > self.range: + if abs(self.x - self.startX) > self.range: self.isActive = False return False diff --git a/src/save_manager.py b/src/save_manager.py index 4028f7b..15ad286 100644 --- a/src/save_manager.py +++ b/src/save_manager.py @@ -11,32 +11,32 @@ class SaveManager: def __init__(self): """Initialize save manager with XDG-compliant save directory""" # Use XDG_CONFIG_HOME or default to ~/.config - config_home = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) - self.save_dir = Path(config_home) / "storm-games" / "wicked-quest" - self.save_dir.mkdir(parents=True, exist_ok=True) - self.max_saves = 10 + configHome = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) + self.saveDir = Path(configHome) / "storm-games" / "wicked-quest" + self.saveDir.mkdir(parents=True, exist_ok=True) + self.maxSaves = 10 - def create_save(self, player, current_level, game_start_time, current_game, bypass_cost=False): + def create_save(self, player, currentLevel, gameStartTime, currentGame, bypassCost=False): """Create a save file with current game state""" - if not bypass_cost: + if not bypassCost: if not player.can_save(): return False, "Not enough bone dust to save (need 200)" # Validate required parameters - if current_game is None: + if currentGame is None: return False, "No game selected to save" - if current_level is None: + if currentLevel is None: return False, "No current level to save" # Spend the bone dust (only if not bypassing cost) - if not bypass_cost: + if not bypassCost: if not player.spend_save_bone_dust(200): return False, "Failed to spend bone dust" # Create save data - save_data = { - "player_state": { + saveData = { + "playerState": { "xPos": player.xPos, "yPos": player.yPos, "health": player._health, @@ -44,7 +44,7 @@ class SaveManager: "lives": player._lives, "coins": player._coins, "saveBoneDust": player._saveBoneDust, - "jackOLanternCount": player._jack_o_lantern_count, + "jackOLanternCount": player._jackOLanternCount, "shinBoneCount": player.shinBoneCount, "inventory": player.inventory, "collectedItems": player.collectedItems, @@ -56,9 +56,9 @@ class SaveManager: "scoreboard": self._serialize_scoreboard(player.scoreboard), }, "game_state": { - "currentLevel": current_level, - "currentGame": current_game, - "gameStartTime": game_start_time, + "currentLevel": currentLevel, + "currentGame": currentGame, + "gameStartTime": gameStartTime, "saveTime": datetime.now(), }, "version": "1.0", @@ -67,21 +67,21 @@ class SaveManager: # Generate filename with timestamp timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") filename = f"save_{timestamp}.pickle" - filepath = self.save_dir / filename + filepath = self.saveDir / filename try: # Write to temporary file first, then rename for atomic operation - temp_filepath = filepath.with_suffix(".tmp") + tempFilepath = filepath.with_suffix(".tmp") - with open(temp_filepath, "wb") as f: - pickle.dump(save_data, f) + with open(tempFilepath, "wb") as f: + pickle.dump(saveData, f) f.flush() # Ensure data is written to disk os.fsync(f.fileno()) # Force write to disk # Atomic rename (replaces old file if it exists) - temp_filepath.rename(filepath) + tempFilepath.rename(filepath) - # Clean up old saves if we exceed max_saves + # Clean up old saves if we exceed maxSaves self._cleanup_old_saves() return True, f"Game saved to {filename}" @@ -205,38 +205,38 @@ class SaveManager: scoreboard.highScores = scoreboard_data["highScores"] return scoreboard - def get_save_files(self): + def get_saveFiles(self): """Get list of save files with metadata""" - save_files = [] - pattern = str(self.save_dir / "save_*.pickle") + saveFiles = [] + pattern = str(self.saveDir / "save_*.pickle") for filepath in glob.glob(pattern): try: with open(filepath, "rb") as f: - save_data = pickle.load(f) + saveData = pickle.load(f) # Validate save data structure - if not self._validate_save_data(save_data): + if not self._validate_saveData(saveData): print(f"Invalid save file structure: {filepath}") continue # Extract save info - save_time = save_data["game_state"]["saveTime"] - level = save_data["game_state"]["currentLevel"] - game_name = save_data["game_state"]["currentGame"] + saveTime = saveData["game_state"]["saveTime"] + level = saveData["game_state"]["currentLevel"] + gameName = saveData["game_state"]["currentGame"] # Format display name - formatted_time = save_time.strftime("%B %d %I:%M%p") - display_name = f"{formatted_time} {game_name} Level {level}" + formattedTime = saveTime.strftime("%B %d %I:%M%p") + displayName = f"{formattedTime} {gameName} Level {level}" - save_files.append( + saveFiles.append( { "filepath": filepath, - "display_name": display_name, - "save_time": save_time, + "displayName": displayName, + "saveTime": saveTime, "level": level, - "game_name": game_name, - "save_data": save_data, + "gameName": gameName, + "saveData": saveData, } ) except (pickle.PickleError, EOFError, OSError) as e: @@ -253,94 +253,94 @@ class SaveManager: continue # Sort by save time (newest first) - save_files.sort(key=lambda x: x["save_time"], reverse=True) - return save_files + saveFiles.sort(key=lambda x: x["saveTime"], reverse=True) + return saveFiles def load_save(self, filepath): """Load game state from save file""" try: with open(filepath, "rb") as f: - save_data = pickle.load(f) - return True, save_data + saveData = pickle.load(f) + return True, saveData except Exception as e: return False, f"Failed to load save: {str(e)}" - def restore_player_state(self, player, save_data): + def restore_playerState(self, player, saveData): """Restore player state from save data""" - player_state = save_data["player_state"] + playerState = saveData["playerState"] # Restore basic attributes - player.xPos = player_state["xPos"] - player.yPos = player_state["yPos"] - player._health = player_state["health"] - player._maxHealth = player_state["maxHealth"] - player._lives = player_state["lives"] - player._coins = player_state["coins"] - player._saveBoneDust = player_state["saveBoneDust"] - player._jack_o_lantern_count = player_state["jackOLanternCount"] - player.shinBoneCount = player_state["shinBoneCount"] - player.inventory = player_state["inventory"] - player.collectedItems = player_state["collectedItems"] + player.xPos = playerState["xPos"] + player.yPos = playerState["yPos"] + player._health = playerState["health"] + player._maxHealth = playerState["maxHealth"] + player._lives = playerState["lives"] + player._coins = playerState["coins"] + player._saveBoneDust = playerState["saveBoneDust"] + player._jackOLanternCount = playerState["jackOLanternCount"] + player.shinBoneCount = playerState["shinBoneCount"] + player.inventory = playerState["inventory"] + player.collectedItems = playerState["collectedItems"] # Restore weapons - player.weapons = self._deserialize_weapons(player_state["weapons"]) + player.weapons = self._deserialize_weapons(playerState["weapons"]) # Restore custom weapon tracking data (for backward compatibility, use get with defaults) - player.craftedCustomWeapons = set(player_state.get("craftedCustomWeapons", [])) - player.customWeapons = player_state.get("customWeapons", []) + player.craftedCustomWeapons = set(playerState.get("craftedCustomWeapons", [])) + player.customWeapons = playerState.get("customWeapons", []) # Restore current weapon - current_weapon_name = player_state.get("currentWeaponName") - if current_weapon_name: + currentWeaponName = playerState.get("currentWeaponName") + if currentWeaponName: for weapon in player.weapons: - if weapon.name == current_weapon_name: + if weapon.name == currentWeaponName: player.currentWeapon = weapon break # Restore stats - if "stats" in player_state: - player.stats = self._deserialize_stats(player_state["stats"]) + if "stats" in playerState: + player.stats = self._deserialize_stats(playerState["stats"]) else: from src.stat_tracker import StatTracker player.stats = StatTracker() # Restore scoreboard - if "scoreboard" in player_state: - player.scoreboard = self._deserialize_scoreboard(player_state["scoreboard"]) + if "scoreboard" in playerState: + player.scoreboard = self._deserialize_scoreboard(playerState["scoreboard"]) else: from libstormgames import Scoreboard player.scoreboard = Scoreboard() def _cleanup_old_saves(self): - """Remove old save files if we exceed max_saves""" - save_files = self.get_save_files() + """Remove old save files if we exceed maxSaves""" + saveFiles = self.get_saveFiles() - if len(save_files) > self.max_saves: + if len(saveFiles) > self.maxSaves: # Remove oldest saves - for save_file in save_files[self.max_saves:]: + for save_file in saveFiles[self.maxSaves:]: try: os.remove(save_file["filepath"]) except Exception as e: print(f"Error removing old save {save_file['filepath']}: {e}") - def _validate_save_data(self, save_data): + def _validate_saveData(self, saveData): """Validate that save data has required structure""" try: # Check for required top-level keys - required_keys = ["player_state", "game_state", "version"] - if not all(key in save_data for key in required_keys): + requiredKeys = ["playerState", "game_state", "version"] + if not all(key in saveData for key in requiredKeys): return False - # Check player_state structure - player_required = ["xPos", "yPos", "health", "maxHealth", "lives", "coins", "saveBoneDust"] - if not all(key in save_data["player_state"] for key in player_required): + # Check playerState structure + playerRequired = ["xPos", "yPos", "health", "maxHealth", "lives", "coins", "saveBoneDust"] + if not all(key in saveData["playerState"] for key in playerRequired): return False # Check game_state structure - game_required = ["currentLevel", "currentGame", "gameStartTime", "saveTime"] - if not all(key in save_data["game_state"] for key in game_required): + gameRequired = ["currentLevel", "currentGame", "gameStartTime", "saveTime"] + if not all(key in saveData["game_state"] for key in gameRequired): return False return True @@ -349,4 +349,4 @@ class SaveManager: def has_saves(self): """Check if any save files exist""" - return len(self.get_save_files()) > 0 + return len(self.get_saveFiles()) > 0 diff --git a/src/survival_generator.py b/src/survival_generator.py index 46d787b..87c4f39 100644 --- a/src/survival_generator.py +++ b/src/survival_generator.py @@ -26,10 +26,10 @@ class SurvivalGenerator: self.weaponOverrides = {} self.customWeapons = [] # Custom weapons from level pack self.availableItems = set() # Dynamically discovered items from containers - self.loadLevelData() - self.parseTemplates() + self.load_level_data() + self.parse_templates() - def loadLevelData(self): + def load_level_data(self): """Load all level JSON files from the game pack.""" levelFiles = [] packPath = os.path.join(get_levels_base_path(), "levels", self.gamePack) @@ -49,7 +49,7 @@ class SurvivalGenerator: levelNum = int(levelFile.split(".")[0]) self.levelData[levelNum] = json.load(f) - def parseTemplates(self): + def parse_templates(self): """Parse all level data to extract object templates by type.""" for levelNum, data in self.levelData.items(): # Store ambience and footstep sounds (remove duplicates) @@ -74,14 +74,14 @@ class SurvivalGenerator: # Discover items from containers (graves and coffins) if obj.get("type") in ["grave", "coffin"] and "item" in obj: - item_name = obj["item"] + itemName = obj["item"] # Check for sound override (themed equivalent) if "sound_overrides" in obj and "item" in obj["sound_overrides"]: - item_name = obj["sound_overrides"]["item"] + itemName = obj["sound_overrides"]["item"] # Exclude special items that should remain rare/exclusive - if item_name and item_name != "random" and item_name not in ["extra_life"]: - self.availableItems.add(item_name) + if itemName and itemName != "random" and itemName not in ["extra_life"]: + self.availableItems.add(itemName) # Categorize objects if "enemy_type" in obj: @@ -154,16 +154,16 @@ class SurvivalGenerator: # Guarantee at least one coffin per wave if coffins exist in templates coffinTemplates = [obj for obj in self.objectTemplates if obj.get("type") == "coffin"] - coffin_placed = False + coffinPlaced = False if coffinTemplates: # Place guaranteed coffin in the first quarter of the level - coffin_x = random.randint(startBufferZone + 10, (segmentLength - endBufferZone) // 4) - coffin_template = random.choice(coffinTemplates) - coffin_obj = copy.deepcopy(coffin_template) - coffin_obj["item"] = "random" # Override any specified item - coffin_obj["x"] = coffin_x - levelData["objects"].append(coffin_obj) - coffin_placed = True + coffinX = random.randint(startBufferZone + 10, (segmentLength - endBufferZone) // 4) + coffinTemplate = random.choice(coffinTemplates) + coffinObj = copy.deepcopy(coffinTemplate) + coffinObj["item"] = "random" # Override any specified item + coffinObj["x"] = coffinX + levelData["objects"].append(coffinObj) + coffinPlaced = True while currentX < segmentLength - endBufferZone: # Determine what to place based on probability diff --git a/wicked_quest.py b/wicked_quest.py index b7a0cfc..e56af8c 100755 --- a/wicked_quest.py +++ b/wicked_quest.py @@ -64,35 +64,35 @@ class WickedQuest: return None # Find active enemies - active_enemies = [] + activeEnemies = [] for enemy in self.currentLevel.enemies: if enemy.isActive: distance = abs(enemy.xPos - self.player.xPos) direction = "right" if enemy.xPos > self.player.xPos else "left" - active_enemies.append((enemy, distance, direction)) + activeEnemies.append((enemy, distance, direction)) - if not active_enemies: + if not activeEnemies: return None # Sort by distance and get closest - active_enemies.sort(key=lambda x: x[1]) - enemy, distance, direction = active_enemies[0] + activeEnemies.sort(key=lambda x: x[1]) + enemy, distance, direction = activeEnemies[0] # Convert distance to natural language if distance == 0: return f"{enemy.enemyType} right on top of you" elif distance <= 10: - distance_desc = "very close" + distanceDesc = "very close" elif distance <= 30: - distance_desc = "close" + distanceDesc = "close" elif distance <= 60: - distance_desc = "far" + distanceDesc = "far" elif distance <= 100: - distance_desc = "very far" + distanceDesc = "very far" else: - distance_desc = "extremely far" + distanceDesc = "extremely far" - return f"{enemy.enemyType} {distance_desc} to the {direction}" + return f"{enemy.enemyType} {distanceDesc} to the {direction}" def process_console_command(self, command): """Process console commands and execute their effects.""" @@ -132,9 +132,9 @@ class WickedQuest: enemyCount = len([enemy for enemy in self.currentLevel.enemies if enemy.isActive]) if enemyCount > 0: speak(f"{enemyCount} enemies remaining on this level") - closest_enemy_info = self.get_closest_enemy_info() - if closest_enemy_info: - speak(f"Closest enemy: {closest_enemy_info}") + closestEnemyInfo = self.get_closest_enemy_info() + if closestEnemyInfo: + speak(f"Closest enemy: {closestEnemyInfo}") else: speak("No active enemies on this level") else: @@ -190,14 +190,14 @@ class WickedQuest: elif command == "diemonsterdie": # Give 100 jack o'lanterns if self.player: - self.player._jack_o_lantern_count += 100 + self.player._jackOLanternCount += 100 speak(f"100 jack o'lanterns granted. You now have {self.player.get_jack_o_lanterns()} jack o'lanterns") if 'get_jack_o_lantern' in self.get_sounds(): play_sound(self.get_sounds()['get_jack_o_lantern']) elif command == "murderland": # Set jack o'lanterns to exactly 13 (the special number) if self.player: - self.player._jack_o_lantern_count = 13 + self.player._jackOLanternCount = 13 speak("13 jack o'lanterns, this power courses through my soul.") if 'get_jack_o_lantern' in self.get_sounds(): play_sound(self.get_sounds()['get_jack_o_lantern']) @@ -238,7 +238,7 @@ class WickedQuest: self.currentLevel.levelId, self.gameStartTime, self.currentGame, - bypass_cost=True # Skip bone dust requirement + bypassCost=True # Skip bone dust requirement ) if success: @@ -273,7 +273,7 @@ class WickedQuest: self.player._saveBoneDust = 999 # Max jack o'lanterns (set to 99 for practicality) - self.player._jack_o_lantern_count = 99 + self.player._jackOLanternCount = 99 # Grant all weapons with level pack override support weaponOverrides = getattr(self.currentLevel, 'weaponSoundOverrides', {}) if self.currentLevel else {} @@ -438,29 +438,29 @@ class WickedQuest: def load_game_menu(self): """Display load game menu with available saves using instruction_menu""" - save_files = self.saveManager.get_save_files() - - if not save_files: + saveFiles = self.saveManager.get_save_files() + + if not saveFiles: messagebox("No save files found.") return None - + # Create menu options options = [] - for save_file in save_files: - options.append(save_file['display_name']) - + for saveFile in saveFiles: + options.append(saveFile['display_name']) + options.append("Cancel") - + # Use instruction_menu for consistent behavior choice = instruction_menu(self.get_sounds(), "Select a save file to load:", *options) - + if choice == "Cancel" or choice is None: return None else: # Find the corresponding save file - for save_file in save_files: - if save_file['display_name'] == choice: - return save_file + for saveFile in saveFiles: + if saveFile['display_name'] == choice: + return saveFile return None def auto_save(self): @@ -559,56 +559,56 @@ class WickedQuest: elif keys[pygame.K_e]: # Weapon and ammo status if player.currentWeapon: - weapon_name = getattr(player.currentWeapon, 'displayName', player.currentWeapon.name.replace("_", " ")) - status_message = f"Wielding {weapon_name}" + weaponName = getattr(player.currentWeapon, 'displayName', player.currentWeapon.name.replace("_", " ")) + statusMessage = f"Wielding {weaponName}" # Check if it's a projectile weapon - always show ammo for projectile weapons - weapon_type = getattr(player.currentWeapon, 'weaponType', 'melee') - if weapon_type == "projectile": - ammo_type = getattr(player.currentWeapon, 'ammoType', None) - if ammo_type: - ammo_count = 0 - ammo_display_name = ammo_type.replace("_", " ") # Default fallback + weaponType = getattr(player.currentWeapon, 'weaponType', 'melee') + if weaponType == "projectile": + ammoType = getattr(player.currentWeapon, 'ammoType', None) + if ammoType: + ammoCount = 0 + ammoDisplayName = ammoType.replace("_", " ") # Default fallback # Get current ammo count based on ammo type - if ammo_type == "bone_dust": - ammo_count = player.get_coins() - ammo_display_name = "bone dust" - elif ammo_type == "shin_bone": - ammo_count = player.shinBoneCount - ammo_display_name = "shin bones" - elif ammo_type == "jack_o_lantern": - ammo_count = player._jack_o_lantern_count - ammo_display_name = "jack o'lanterns" - elif ammo_type == "guts": - ammo_count = player.collectedItems.count("guts") - ammo_display_name = "guts" - elif ammo_type == "hand_of_glory": - ammo_count = player.collectedItems.count("hand_of_glory") - ammo_display_name = "hands of glory" + if ammoType == "bone_dust": + ammoCount = player.get_coins() + ammoDisplayName = "bone dust" + elif ammoType == "shin_bone": + ammoCount = player.shinBoneCount + ammoDisplayName = "shin bones" + elif ammoType == "jack_o_lantern": + ammoCount = player._jackOLanternCount + ammoDisplayName = "jack o'lanterns" + elif ammoType == "guts": + ammoCount = player.collectedItems.count("guts") + ammoDisplayName = "guts" + elif ammoType == "hand_of_glory": + ammoCount = player.collectedItems.count("hand_of_glory") + ammoDisplayName = "hands of glory" else: # Check for any other item type in collectedItems - ammo_count = player.collectedItems.count(ammo_type) + ammoCount = player.collectedItems.count(ammoType) - status_message += f". {ammo_count} {ammo_display_name}" + statusMessage += f". {ammoCount} {ammoDisplayName}" - speak(status_message) + speak(statusMessage) else: speak("No weapon equipped") elif keys[pygame.K_h]: speak(f"{player.get_health()} health of {player.get_max_health()}") elif keys[pygame.K_i]: if self.currentLevel.levelId == 999: - base_info = f"Wave {self.survivalWave}. {player.get_health()} health of {player.get_max_health()}. {int(self.currentLevel.levelScore)} points on this wave so far. {player.get_lives()} lives remaining." + baseInfo = f"Wave {self.survivalWave}. {player.get_health()} health of {player.get_max_health()}. {int(self.currentLevel.levelScore)} points on this wave so far. {player.get_lives()} lives remaining." else: - base_info = f"Level {self.currentLevel.levelId}, {self.currentLevel.levelName}. {player.get_health()} health of {player.get_max_health()}. {int(self.currentLevel.levelScore)} points on this level so far. {player.get_lives()} lives remaining." + baseInfo = f"Level {self.currentLevel.levelId}, {self.currentLevel.levelName}. {player.get_health()} health of {player.get_max_health()}. {int(self.currentLevel.levelScore)} points on this level so far. {player.get_lives()} lives remaining." # Add closest enemy info - closest_enemy_info = self.get_closest_enemy_info() - if closest_enemy_info: - speak(f"{base_info} {closest_enemy_info}") + closestEnemyInfo = self.get_closest_enemy_info() + if closestEnemyInfo: + speak(f"{baseInfo} {closestEnemyInfo}") else: - speak(base_info) + speak(baseInfo) if keys[pygame.K_l]: speak(f"{player.get_lives()} lives") if keys[pygame.K_j]: # Check jack o'lanterns @@ -898,39 +898,39 @@ class WickedQuest: while True: # Add load game option if saves exist - custom_options = [] + customOptions = [] if self.saveManager.has_saves(): - custom_options.append("load_game") - - choice = game_menu(self.get_sounds(), None, *custom_options) + customOptions.append("load_game") + + choice = game_menu(self.get_sounds(), None, *customOptions) if choice == "exit": exit_game() elif choice == "load_game": - selected_save = self.load_game_menu() - if selected_save: - success, save_data = self.saveManager.load_save(selected_save['filepath']) + selectedSave = self.load_game_menu() + if selectedSave: + success, saveData = self.saveManager.load_save(selectedSave['filepath']) if success: # Load the saved game - self.currentGame = save_data['game_state']['currentGame'] - self.gameStartTime = save_data['game_state']['gameStartTime'] - current_level = save_data['game_state']['currentLevel'] + self.currentGame = saveData['game_state']['currentGame'] + self.gameStartTime = saveData['game_state']['gameStartTime'] + currentLevel = saveData['game_state']['currentLevel'] # Initialize pack-specific sound system self.initialize_pack_sounds() - + # Load the level - if self.load_level(current_level): + if self.load_level(currentLevel): # Restore player state - self.saveManager.restore_player_state(self.player, save_data) + self.saveManager.restore_player_state(self.player, saveData) # Re-apply weapon overrides after restoring player state to ensure # sound/name overrides work with restored weapon properties if hasattr(self.currentLevel, 'weaponOverrides') and self.currentLevel.weaponOverrides: self.currentLevel._apply_weapon_overrides(self.currentLevel.weaponOverrides) - self.game_loop(current_level) + self.game_loop(currentLevel) else: messagebox("Failed to load saved level.") else: - messagebox(f"Failed to load save: {save_data}") + messagebox(f"Failed to load save: {saveData}") elif choice == "play": self.currentGame = select_game(self.get_sounds()) if self.currentGame is None: @@ -947,13 +947,13 @@ class WickedQuest: continue if self.currentGame: # Ask player to choose game mode - mode_choice = game_mode_menu(self.get_sounds(), self.currentGame) - if mode_choice == "story": + modeChoice = game_mode_menu(self.get_sounds(), self.currentGame) + if modeChoice == "story": 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": + elif modeChoice == "survival": self.start_survival_mode() elif choice == "high_scores": board = Scoreboard() @@ -1116,12 +1116,12 @@ class WickedQuest: self.currentLevel = Level(levelData, self.get_sounds(), self.player, self.currentGame) -def game_mode_menu(sounds, game_dir=None): +def game_mode_menu(sounds, gameDir=None): """Display game mode selection menu using instruction_menu. Args: sounds (dict): Dictionary of loaded sound effects - game_dir (str): Current game directory to check for instructions/credits + gameDir (str): Current game directory to check for instructions/credits Returns: str: Selected game mode or None if cancelled @@ -1130,79 +1130,63 @@ def game_mode_menu(sounds, game_dir=None): import os # Build base menu options - menu_options = ["Story", "Survival Mode"] + menuOptions = ["Story", "Survival Mode"] # Check for level pack specific files if game directory is provided - if game_dir: + if gameDir: try: - game_path = get_game_dir_path(game_dir) + gamePath = get_game_dir_path(gameDir) # Check for instructions.txt - instructions_path = os.path.join(game_path, "instructions.txt") - if os.path.exists(instructions_path): - menu_options.append("Instructions") + instructionsPath = os.path.join(gamePath, "instructions.txt") + if os.path.exists(instructionsPath): + menuOptions.append("Instructions") # Check for credits.txt - credits_path = os.path.join(game_path, "credits.txt") - if os.path.exists(credits_path): - menu_options.append("Credits") + creditsPath = os.path.join(gamePath, "credits.txt") + if os.path.exists(creditsPath): + menuOptions.append("Credits") except Exception: # If there's any error checking files, just continue with basic menu pass while True: - choice = instruction_menu(sounds, "Select game mode:", *menu_options) + choice = instruction_menu(sounds, "Select game mode:", *menuOptions) if choice == "Story": return "story" elif choice == "Survival Mode": return "survival" - elif choice == "Instructions" and game_dir: + elif choice == "Instructions" and gameDir: # Display instructions file try: - game_path = get_game_dir_path(game_dir) - instructions_path = os.path.join(game_path, "instructions.txt") - print(f"DEBUG: Looking for instructions at: {instructions_path}") - if os.path.exists(instructions_path): - print("DEBUG: Instructions file found, loading content...") - with open(instructions_path, 'r', encoding='utf-8') as f: - instructions_content = f.read() - print(f"DEBUG: Content length: {len(instructions_content)} characters") - print("DEBUG: Calling display_text...") + gamePath = get_game_dir_path(gameDir) + instructionsPath = os.path.join(gamePath, "instructions.txt") + if os.path.exists(instructionsPath): + with open(instructionsPath, 'r', encoding='utf-8') as f: + instructionsContent = f.read() # Convert string to list of lines for display_text - content_lines = instructions_content.split('\n') - print(f"DEBUG: Split into {len(content_lines)} lines") - display_text(content_lines) - print("DEBUG: display_text returned") + contentLines = instructionsContent.split('\n') + display_text(contentLines) else: - print("DEBUG: Instructions file not found at expected path") speak("Instructions file not found") except Exception as e: - print(f"DEBUG: Error loading instructions: {str(e)}") speak(f"Error loading instructions: {str(e)}") - elif choice == "Credits" and game_dir: + elif choice == "Credits" and gameDir: # Display credits file try: - game_path = get_game_dir_path(game_dir) - credits_path = os.path.join(game_path, "credits.txt") - print(f"DEBUG: Looking for credits at: {credits_path}") - if os.path.exists(credits_path): - print("DEBUG: Credits file found, loading content...") - with open(credits_path, 'r', encoding='utf-8') as f: - credits_content = f.read() - print(f"DEBUG: Content length: {len(credits_content)} characters") - print("DEBUG: Calling display_text...") + gamePath = get_game_dir_path(gameDir) + creditsPath = os.path.join(gamePath, "credits.txt") + if os.path.exists(creditsPath): + with open(creditsPath, 'r', encoding='utf-8') as f: + creditsContent = f.read() # Convert string to list of lines for display_text - content_lines = credits_content.split('\n') - print(f"DEBUG: Split into {len(content_lines)} lines") - display_text(content_lines) - print("DEBUG: display_text returned") + contentLines = creditsContent.split('\n') + display_text(contentLines) else: - print("DEBUG: Credits file not found at expected path") speak("Credits file not found") except Exception as e: - print(f"DEBUG: Error loading credits: {str(e)}") speak(f"Error loading credits: {str(e)}") else: return None