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.

This commit is contained in:
Storm Dragon
2025-09-21 01:38:49 -04:00
parent ed764cdfdf
commit 4b21552ca9
4 changed files with 87 additions and 12 deletions

View File

@@ -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])

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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