From 4b21552ca9e5332db4533ebf6fa92eb8514a5761 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 21 Sep 2025 01:38:49 -0400 Subject: [PATCH] Actually found a bug with weapon progression. Made survival mode more random. Graves can now have any item from all levels or be empty. Coffins will always have a random item. --- src/enemy.py | 5 ++- src/item_types.py | 8 +++-- src/level.py | 16 ++++++++- src/survival_generator.py | 70 +++++++++++++++++++++++++++++++++++---- 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/src/enemy.py b/src/enemy.py index 239cd5d..a43a2fd 100644 --- a/src/enemy.py +++ b/src/enemy.py @@ -262,9 +262,8 @@ class Enemy(Object): if self.enemyType == "witch": # Determine which item to drop hasBroom = any(weapon.originalName == "witch_broom" for weapon in self.level.player.weapons) - hasNunchucks = any(weapon.originalName == "nunchucks" for weapon in self.level.player.weapons) - # Drop witch_broom only if player has neither broom nor nunchucks - itemType = "witch_broom" if not (hasBroom or hasNunchucks) else "cauldron" + # Drop witch_broom if player doesn't have it, otherwise drop cauldron + itemType = "witch_broom" if not hasBroom else "cauldron" # Create drop 1-2 tiles away in random direction direction = random.choice([-1, 1]) diff --git a/src/item_types.py b/src/item_types.py index 1e3bc8a..e43b5d9 100644 --- a/src/item_types.py +++ b/src/item_types.py @@ -18,8 +18,12 @@ class ItemType(Enum): class ItemProperties: """Manages item properties and availability""" - # Items that can appear in random drops - RANDOM_ELIGIBLE = {ItemType.HAND_OF_GLORY: "hand_of_glory", ItemType.JACK_O_LANTERN: "jack_o_lantern"} + # Items that naturally appear in graves and coffins (excluding special items like extra_life, cauldron, witch_broom) + RANDOM_ELIGIBLE = { + ItemType.HAND_OF_GLORY: "hand_of_glory", + ItemType.JACK_O_LANTERN: "jack_o_lantern", + ItemType.GUTS: "guts" + } # All possible items (including special ones) ALL_ITEMS = { diff --git a/src/level.py b/src/level.py index a0846ba..57e7ee0 100644 --- a/src/level.py +++ b/src/level.py @@ -13,6 +13,7 @@ from src.player import Player from src.projectile import Projectile from src.powerup import PowerUp from src.skull_storm import SkullStorm +from src.item_types import ItemProperties class Level: @@ -139,11 +140,24 @@ class Level: self.objects.append(graspingHands) # Check if this is a grave elif obj.get("type") == "grave": + # Handle item spawning with chance (for survival mode) + grave_item = 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") + else: + # Story mode: use item as specified + grave_item = obj.get("item", None) + if grave_item == "random": + grave_item = ItemProperties.get_random_item() + grave = GraveObject( xPos[0], obj["y"], self.sounds, - item=obj.get("item", None), + item=grave_item, zombieSpawnChance=obj.get("zombie_spawn_chance", 0), ) # Apply sound overrides if specified diff --git a/src/survival_generator.py b/src/survival_generator.py index 86c550e..3b67673 100644 --- a/src/survival_generator.py +++ b/src/survival_generator.py @@ -20,8 +20,11 @@ class SurvivalGenerator: self.enemyTemplates = [] self.collectibleTemplates = [] self.hazardTemplates = [] + self.graveTemplates = [] self.ambientSounds = [] self.footstepSounds = [] + self.weaponOverrides = {} + self.availableItems = set() # Dynamically discovered items from containers self.loadLevelData() self.parseTemplates() @@ -54,12 +57,27 @@ class SurvivalGenerator: if "footstep_sound" in data and data["footstep_sound"] not in self.footstepSounds: self.footstepSounds.append(data["footstep_sound"]) + # Collect weapon overrides (merge from all levels, later levels take precedence) + if "weapon_sound_overrides" in data: + self.weaponOverrides.update(data["weapon_sound_overrides"]) + # Parse objects for obj in data.get("objects", []): objCopy = copy.deepcopy(obj) # Add source level information to track difficulty progression objCopy["source_level"] = levelNum + # Discover items from containers (graves and coffins) + if obj.get("type") in ["grave", "coffin"] and "item" in obj: + item_name = 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"] + + # 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) + # Categorize objects if "enemy_type" in obj: self.enemyTemplates.append(objCopy) @@ -67,9 +85,18 @@ class SurvivalGenerator: self.collectibleTemplates.append(objCopy) elif obj.get("type") in ["skull_storm", "catapult", "grasping_hands"]: self.hazardTemplates.append(objCopy) + elif obj.get("type") == "grave": + self.graveTemplates.append(objCopy) else: self.objectTemplates.append(objCopy) + def get_random_container_item(self): + """Get a random item from items discovered in containers.""" + if not self.availableItems: + # Fallback to original hardcoded items if no items discovered + return random.choice(["hand_of_glory", "jack_o_lantern", "guts"]) + return random.choice(list(self.availableItems)) + def generate_survival_level(self, difficultyLevel=1, segmentLength=100): """Generate an endless survival level segment. @@ -99,10 +126,15 @@ class SurvivalGenerator: if self.footstepSounds: levelData["footstep_sound"] = random.choice(self.footstepSounds) + # Include weapon overrides if any were found + if self.weaponOverrides: + levelData["weapon_sound_overrides"] = self.weaponOverrides + # 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 + graveDensity = max(0.15, 0.25 - (difficultyLevel * 0.01)) # Moderate grave density, slight decrease over time objectDensity = max(0.1, 0.2 - (difficultyLevel * 0.01)) # Fewer misc objects over time # Generate objects across the segment with buffer zones @@ -123,7 +155,10 @@ class SurvivalGenerator: 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: + elif rand < collectibleDensity + enemyDensity + hazardDensity + graveDensity and self.graveTemplates: + obj = self.place_grave(currentX, difficultyLevel) + currentX += random.randint(12, 20) + elif rand < collectibleDensity + enemyDensity + hazardDensity + graveDensity + objectDensity and self.objectTemplates: obj = self.place_object(currentX, difficultyLevel) currentX += random.randint(12, 20) else: @@ -141,6 +176,11 @@ class SurvivalGenerator: } levelData["objects"].append(endMarker) + # Post-process all objects to resolve "random" items to specific items + for obj in levelData["objects"]: + if obj.get("item") == "random": + obj["item"] = self.get_random_container_item() + return levelData def place_collectible(self, xPos, difficultyLevel): @@ -221,16 +261,34 @@ class SurvivalGenerator: return obj + def place_grave(self, xPos, difficultyLevel): + """Place a grave at the given position with scaled difficulty.""" + template = random.choice(self.graveTemplates) + obj = copy.deepcopy(template) + + # Increase zombie spawn chance with difficulty for graves + baseChance = obj.get("zombie_spawn_chance", 0) + obj["zombie_spawn_chance"] = min(50, baseChance + (difficultyLevel * 2)) + + # Add item spawn chance that scales with difficulty (more items later when you need them) + baseItemChance = 30 # 30% base chance + obj["item_spawn_chance"] = min(70, baseItemChance + (difficultyLevel * 3)) + obj["item"] = "random" # Will be resolved to specific item in level generation + + # 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 coffins - make all items random in survival mode if obj.get("type") == "coffin": obj["item"] = "random" # Override any specified item