Spiderweb obstacles added. Latest libstormgames module update. Level 4 created, needs adjustment, currently way too hard.
This commit is contained in:
@@ -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"
|
||||
}
|
||||
+1
-1
Submodule libstormgames updated: b479811a98...7cbbc64d27
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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):
|
||||
|
||||
+110
-51
@@ -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,56 +289,80 @@ class Level:
|
||||
play_sound(self.sounds['edge'])
|
||||
self.lastWarningTime = currentTime
|
||||
|
||||
if obj.is_in_range(self.player.xPos):
|
||||
if obj.isCollectible and self.player.isJumping:
|
||||
currentPos = round(self.player.xPos)
|
||||
if currentPos not in obj.collectedPositions:
|
||||
play_sound(self.sounds[f'get_{obj.soundName}'])
|
||||
obj.collect_at_position(currentPos)
|
||||
self.player.collectedItems.append(obj.soundName)
|
||||
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:
|
||||
play_sound(self.sounds[f'get_{obj.soundName}'])
|
||||
obj.collect_at_position(currentPos)
|
||||
self.player.collectedItems.append(obj.soundName)
|
||||
self.player.stats.update_stat('Items collected', 1)
|
||||
if obj.soundName == "coin":
|
||||
self.player._coins += 1
|
||||
self.player.stats.update_stat('Bone dust', 1)
|
||||
if self.player._coins % 5 == 0:
|
||||
# Only heal if below max health
|
||||
if self.player.get_health() < self.player.get_max_health():
|
||||
self.player.set_health(min(
|
||||
self.player.get_health() + 1,
|
||||
self.player.get_max_health()
|
||||
))
|
||||
|
||||
if self.player._coins % 100 == 0:
|
||||
# Extra life
|
||||
self.player._coins = 0
|
||||
self.player._lives += 1
|
||||
play_sound(self.sounds['get_extra_life'])
|
||||
continue
|
||||
|
||||
# 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)
|
||||
|
||||
if can_collect:
|
||||
# Successfully collected item while ducking
|
||||
play_sound(self.sounds[f'get_{obj.graveItem}'])
|
||||
self.player.stats.update_stat('Items collected', 1)
|
||||
if obj.soundName == "coin":
|
||||
self.player._coins += 1
|
||||
self.player.stats.update_stat('Bone dust', 1)
|
||||
if self.player._coins % 5 == 0:
|
||||
# Only heal if below max health
|
||||
if self.player.get_health() < self.player.get_max_health():
|
||||
self.player.set_health(min(
|
||||
self.player.get_health() + 1,
|
||||
self.player.get_max_health()
|
||||
))
|
||||
|
||||
if self.player._coins % 100 == 0:
|
||||
# Extra life
|
||||
self.player._coins = 0
|
||||
self.player._lives += 1
|
||||
play_sound(self.sounds['get_extra_life'])
|
||||
|
||||
elif obj.isHazard and not self.player.isJumping:
|
||||
if isinstance(obj, GraveObject):
|
||||
can_collect = obj.collect_grave_item(self.player)
|
||||
|
||||
if can_collect:
|
||||
# Successfully collected item while ducking
|
||||
play_sound(self.sounds[f'get_{obj.graveItem}'])
|
||||
self.player.stats.update_stat('Items collected', 1)
|
||||
# Create PowerUp to handle the item effect
|
||||
item = PowerUp(obj.xPos, obj.yPos, obj.graveItem, self.sounds, 1)
|
||||
item.apply_effect(self.player)
|
||||
# Stop grave's current audio channel
|
||||
if obj.channel:
|
||||
obj_stop(obj.channel)
|
||||
# Remove the grave
|
||||
obj.graveItem = None
|
||||
obj.channel = None
|
||||
obj.isActive = False # Mark the grave as inactive after collection
|
||||
continue
|
||||
elif not self.player.isInvincible:
|
||||
# Kill player for normal graves or non-ducking collision
|
||||
play_sound(self.sounds[obj.soundName])
|
||||
speak("You fell in an open grave! Now, it's yours!")
|
||||
self.player.set_health(0)
|
||||
return False
|
||||
# Create PowerUp to handle the item effect
|
||||
item = PowerUp(obj.xPos, obj.yPos, obj.graveItem, self.sounds, 1)
|
||||
item.apply_effect(self.player)
|
||||
# Stop grave's current audio channel
|
||||
if obj.channel:
|
||||
obj_stop(obj.channel)
|
||||
# Remove the grave
|
||||
obj.graveItem = None
|
||||
obj.channel = None
|
||||
obj.isActive = False # Mark the grave as inactive after collection
|
||||
continue
|
||||
elif not self.player.isInvincible:
|
||||
# Kill player for normal graves or non-ducking collision
|
||||
play_sound(self.sounds[obj.soundName])
|
||||
speak("You fell in an open grave! Now, it's yours!")
|
||||
self.player.set_health(0)
|
||||
return False
|
||||
|
||||
# Handle boundaries
|
||||
if self.player.xPos < self.leftBoundary:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user