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:
@@ -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])
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
16
src/level.py
16
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user