Spiderweb obstacles added. Latest libstormgames module update. Level 4 created, needs adjustment, currently way too hard.

This commit is contained in:
Storm Dragon
2025-02-09 13:38:11 -05:00
parent 0f0d719578
commit 6303cf93e7
12 changed files with 435 additions and 56 deletions

280
levels/Wicked Quest/4.json Normal file
View File

@@ -0,0 +1,280 @@
{
"level_id": 4,
"name": "Spider's Domain",
"description": "The spiders have claimed this part of the graveyard. Be careful to not disturb them. You know how spiders think, if you can't eat it, kill it!",
"player_start": {
"x": 0,
"y": 0
},
"objects": [
{
"x_range": [5, 8],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"type": "spider_web",
"x": 10,
"y": 0
},
{
"x": 15,
"y": 3,
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [20, 23],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x_range": [25, 35],
"y": 0,
"enemy_type": "goblin",
"health": 4,
"damage": 2,
"attack_range": 1,
"attack_pattern": {
"type": "patrol"
}
},
{
"x_range": [30, 45],
"y": 12,
"type": "skull_storm",
"damage": 3,
"maximum_skulls": 2,
"frequency": {
"min": 2,
"max": 4
}
},
{
"type": "spider_web",
"x": 40,
"y": 0
},
{
"x": 45,
"y": 0,
"type": "grave",
"sound": "grave",
"static": true,
"zombie_spawn_chance": 0
},
{
"x_range": [50, 54],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 60,
"y": 3,
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [65, 85],
"y": 0,
"enemy_type": "witch",
"health": 8,
"damage": 2,
"attack_range": 1,
"attack_pattern": {
"type": "patrol"
}
},
{
"type": "spider_web",
"x": 75,
"y": 0
},
{
"x_range": [80, 95],
"y": 15,
"type": "skull_storm",
"damage": 4,
"maximum_skulls": 3,
"frequency": {
"min": 1,
"max": 3
}
},
{
"x_range": [90, 93],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 100,
"y": 0,
"type": "catapult",
"fire_interval": 4000,
"range": 20
},
{
"x": 110,
"y": 0,
"type": "grave",
"sound": "grave",
"static": true,
"zombie_spawn_chance": 50
},
{
"x_range": [115, 118],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"type": "spider_web",
"x": 120,
"y": 0
},
{
"x_range": [125, 145],
"y": 0,
"enemy_type": "ghoul",
"health": 12,
"damage": 3,
"attack_range": 2,
"attack_pattern": {
"type": "hunter",
"turn_threshold": 4
}
},
{
"x": 135,
"y": 3,
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [140, 160],
"y": 15,
"type": "skull_storm",
"damage": 4,
"maximum_skulls": 3,
"frequency": {
"min": 1,
"max": 3
}
},
{
"x_range": [150, 153],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 165,
"y": 0,
"type": "grave",
"sound": "grave",
"static": true,
"item": "guts",
"zombie_spawn_chance": 0
},
{
"type": "spider_web",
"x": 175,
"y": 0
},
{
"x_range": [180, 200],
"y": 0,
"enemy_type": "witch",
"health": 8,
"damage": 2,
"attack_range": 1,
"attack_pattern": {
"type": "patrol"
}
},
{
"x_range": [185, 188],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 195,
"y": 3,
"item": "hand_of_glory",
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [205, 225],
"y": 12,
"type": "skull_storm",
"damage": 4,
"maximum_skulls": 3,
"frequency": {
"min": 1,
"max": 3
}
},
{
"x_range": [210, 213],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"type": "spider_web",
"x": 220,
"y": 0
},
{
"x": 225,
"y": 0,
"type": "catapult",
"fire_interval": 4000,
"range": 20
},
{
"x_range": [230, 245],
"y": 0,
"enemy_type": "ghoul",
"health": 12,
"damage": 3,
"attack_range": 2,
"attack_pattern": {
"type": "hunter",
"turn_threshold": 4
}
},
{
"x_range": [235, 238],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 245,
"y": 3,
"sound": "coffin",
"type": "coffin"
}
],
"boundaries": {
"left": 0,
"right": 250
},
"footstep_sound": "footstep_tall_grass"
}

BIN
sounds/get_cauldron.ogg (Stored with Git LFS)

Binary file not shown.

BIN
sounds/get_guts.ogg (Stored with Git LFS)

Binary file not shown.

BIN
sounds/hit_spiderweb.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/spider.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/spider_dies.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/spiderweb.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -41,6 +41,11 @@ class Enemy(Object):
self.damage = level.player.get_max_health() # Instant death
self.health = 1 # Easy to kill
self.attackCooldown = 1500 # Slower attack rate
elif enemyType == "spider":
speedMultiplier = kwargs.get('speed_multiplier', 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
@property
def xPos(self):

View File

@@ -95,6 +95,27 @@ class Level:
item=obj.get("item", "random") # Get item type or default to random
)
self.objects.append(coffin)
# Check if this is a spider web
elif obj.get("type") == "spider_web":
# Check distance from graves
isValidPosition = True
for existingObj in self.objects:
if (existingObj.soundName == "grave" and
not hasattr(existingObj, 'graveItem')):
distance = abs(obj["x"] - existingObj.xPos)
if distance < 3:
isValidPosition = False
break
if isValidPosition:
web = Object(
obj["x"], # Just pass the single x value
obj["y"],
"spiderweb",
isStatic=True,
isCollectible=False,
)
self.objects.append(web)
# Check if this is an enemy
elif "enemy_type" in obj:
enemy = Enemy(
@@ -229,7 +250,21 @@ class Level:
if obj.hit(self.player.xPos):
self.bouncing_items.append(obj.dropped_item)
#speak(f"{obj.dropped_item.soundName} falls out!")
def spawn_spider(self, xPos, yPos):
"""Spawn a spider at the given position"""
spider = Enemy(
[xPos - 5, xPos + 5], # Give spider a patrol range
yPos,
"spider",
self.sounds,
self,
health=8,
damage=8,
attack_range=1,
speed_multiplier=2.0
)
self.enemies.append(spider)
def handle_collisions(self):
"""Handle all collision checks and return True if level is complete."""
@@ -243,7 +278,7 @@ class Level:
continue
# Handle grave edge warnings
if obj.isHazard:
if obj.isHazard and obj.soundName != "spiderweb": # Explicitly exclude spiderwebs
distance = abs(self.player.xPos - obj.xPos)
currentTime = pygame.time.get_ticks()
if (distance <= 2 and not self.player.isJumping and not self.player.isInvincible
@@ -254,7 +289,10 @@ class Level:
play_sound(self.sounds['edge'])
self.lastWarningTime = currentTime
if obj.is_in_range(self.player.xPos):
if not obj.is_in_range(self.player.xPos):
continue
# Handle collectibles
if obj.isCollectible and self.player.isJumping:
currentPos = round(self.player.xPos)
if currentPos not in obj.collectedPositions:
@@ -278,8 +316,29 @@ class Level:
self.player._coins = 0
self.player._lives += 1
play_sound(self.sounds['get_extra_life'])
continue
elif obj.isHazard and not self.player.isJumping:
# Handle spiderweb - this should trigger for both walking and jumping if not ducking
if obj.soundName == "spiderweb" and not self.player.isDucking:
# Create and apply web effect
webEffect = PowerUp(
obj.xPos,
obj.yPos,
'spiderweb',
self.sounds,
0 # No direction needed since it's just for effect
)
webEffect.level = self # Pass level reference for spider spawning
play_sound(self.sounds['hit_spiderweb'])
webEffect.apply_effect(self.player)
# Deactivate web
obj.isActive = False
obj.channel = obj_stop(obj.channel)
continue
# 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)

View File

@@ -86,6 +86,13 @@ class Player:
def update(self, currentTime):
"""Update player state"""
if hasattr(self, 'webPenaltyEndTime'):
if currentTime >= self.webPenaltyEndTime:
self.moveSpeed *= 2 # Restore speed
if self.currentWeapon:
self.currentWeapon.attackDuration *= 0.5 # Restore attack speed
del self.webPenaltyEndTime
# Check if invincibility has expired
if self.isInvincible and currentTime - self.invincibilityStartTime >= self.invincibilityDuration:
self.isInvincible = False

View File

@@ -57,6 +57,22 @@ class PowerUp(Object):
broomWeapon = Weapon.create_witch_broom()
player.add_weapon(broomWeapon)
player.equip_weapon(broomWeapon)
elif self.item_type == 'spiderweb':
# Bounce player back (happens even if invincible)
player.xPos -= 3 if player.xPos > self.xPos else -3
# Only apply debuffs if not invincible
if not player.isInvincible:
# Half speed and double attack time for 15 seconds
player.moveSpeed *= 0.5
if player.currentWeapon:
player.currentWeapon.attackDuration *= 2
# Set timer for penalty removal
player.webPenaltyEndTime = pygame.time.get_ticks() + 15000
# Tell level to spawn a spider
if hasattr(self, 'level'):
self.level.spawn_spider(self.xPos, self.yPos)
# Stop movement sound when collected
if self.channel: