Grasping hands obstacle added.
This commit is contained in:
BIN
sounds/grasping_hands.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/grasping_hands.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/grasping_hands_end.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/grasping_hands_end.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/grasping_hands_start.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/grasping_hands_start.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
127
src/grasping_hands.py
Normal file
127
src/grasping_hands.py
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from libstormgames import *
|
||||||
|
from src.object import Object
|
||||||
|
|
||||||
|
class GraspingHands(Object):
|
||||||
|
"""A hazard where the ground crumbles beneath the player as undead hands reach up."""
|
||||||
|
|
||||||
|
def __init__(self, xRange, y, sounds, delay=1000, crumble_speed=0.065):
|
||||||
|
super().__init__(
|
||||||
|
xRange,
|
||||||
|
y,
|
||||||
|
"", # Empty string so no regular object sound plays
|
||||||
|
isStatic=True,
|
||||||
|
isCollectible=False,
|
||||||
|
isHazard=True
|
||||||
|
)
|
||||||
|
self.sounds = sounds
|
||||||
|
self.delay = delay # Delay in milliseconds before ground starts crumbling
|
||||||
|
self.crumble_speed = crumble_speed # How fast the crumbling catches up (tiles per frame)
|
||||||
|
|
||||||
|
# State tracking
|
||||||
|
self.isTriggered = False # Has the player entered the zone?
|
||||||
|
self.triggerTime = 0 # When did the player enter the zone?
|
||||||
|
self.crumblePosition = 0 # The position of the crumbling ground
|
||||||
|
self.isActive = True # Is this hazard active?
|
||||||
|
self.isReset = True # Has this hazard been reset?
|
||||||
|
self.crumbleChannel = None # Channel for the looping crumble sound
|
||||||
|
self.entryFromRight = False # Which side did player enter from
|
||||||
|
self.crumbleDirection = 1 # Direction the crumbling moves (1=right, -1=left)
|
||||||
|
|
||||||
|
def trigger(self, currentTime, playerX):
|
||||||
|
"""Trigger the grasping hands when player enters range"""
|
||||||
|
if not self.isTriggered:
|
||||||
|
self.isTriggered = True
|
||||||
|
self.triggerTime = currentTime
|
||||||
|
|
||||||
|
# Determine which side player entered from
|
||||||
|
if playerX > (self.xRange[0] + self.xRange[1]) / 2:
|
||||||
|
# Player entered from right side
|
||||||
|
self.entryFromRight = True
|
||||||
|
self.crumblePosition = self.xRange[1] # Start crumbling from right boundary
|
||||||
|
self.crumbleDirection = -1 # Crumble moves left
|
||||||
|
else:
|
||||||
|
# Player entered from left side (or middle)
|
||||||
|
self.entryFromRight = False
|
||||||
|
self.crumblePosition = self.xRange[0] # Start crumbling from left boundary
|
||||||
|
self.crumbleDirection = 1 # Crumble moves right
|
||||||
|
|
||||||
|
self.isReset = False
|
||||||
|
|
||||||
|
# Play initial warning sound
|
||||||
|
play_sound(self.sounds['grasping_hands_start'])
|
||||||
|
speak("The ground crumbles as the dead reach for you.")
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""Reset the trap when player leaves the range"""
|
||||||
|
if not self.isReset:
|
||||||
|
self.isTriggered = False
|
||||||
|
self.crumblePosition = 0
|
||||||
|
self.isReset = True
|
||||||
|
|
||||||
|
# Stop the looping crumble sound if it's playing
|
||||||
|
if self.crumbleChannel:
|
||||||
|
obj_stop(self.crumbleChannel)
|
||||||
|
self.crumbleChannel = None
|
||||||
|
|
||||||
|
# Play the end sound
|
||||||
|
play_sound(self.sounds['grasping_hands_end'])
|
||||||
|
|
||||||
|
def update(self, currentTime, player):
|
||||||
|
"""Update the grasping hands trap state"""
|
||||||
|
if not self.isActive:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if player is in range
|
||||||
|
isInRange = self.xRange[0] <= player.xPos <= self.xRange[1]
|
||||||
|
|
||||||
|
# Handle player entering/exiting range
|
||||||
|
if isInRange and not self.isTriggered:
|
||||||
|
self.trigger(currentTime, player.xPos)
|
||||||
|
elif not isInRange and self.isTriggered:
|
||||||
|
self.reset()
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If triggered and delay has passed, start crumbling
|
||||||
|
if self.isTriggered and currentTime - self.triggerTime >= self.delay:
|
||||||
|
# Update crumble position based on direction
|
||||||
|
self.crumblePosition += self.crumble_speed * self.crumbleDirection
|
||||||
|
|
||||||
|
# Manage the looping positional audio for the crumbling ground
|
||||||
|
if self.crumbleChannel is None or not self.crumbleChannel.get_busy():
|
||||||
|
# Start the sound if it's not playing
|
||||||
|
self.crumbleChannel = obj_play(self.sounds, 'grasping_hands', player.xPos, self.crumblePosition)
|
||||||
|
else:
|
||||||
|
# Update the sound position
|
||||||
|
self.crumbleChannel = obj_update(self.crumbleChannel, player.xPos, self.crumblePosition)
|
||||||
|
|
||||||
|
# Check if player is caught by crumbling
|
||||||
|
playerCaught = False
|
||||||
|
if not player.isJumping:
|
||||||
|
if (self.crumbleDirection > 0 and player.xPos <= self.crumblePosition) or \
|
||||||
|
(self.crumbleDirection < 0 and player.xPos >= self.crumblePosition):
|
||||||
|
playerCaught = True
|
||||||
|
|
||||||
|
if playerCaught:
|
||||||
|
if not player.isInvincible:
|
||||||
|
# Player is caught - instant death
|
||||||
|
# Stop the crumbling sound before death
|
||||||
|
if self.crumbleChannel:
|
||||||
|
obj_stop(self.crumbleChannel)
|
||||||
|
self.crumbleChannel = None
|
||||||
|
|
||||||
|
speak("The hands of the dead drag you down!")
|
||||||
|
player.set_health(0)
|
||||||
|
return True
|
||||||
|
# Player is invincible - no warning needed
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Cleanup when object is destroyed"""
|
||||||
|
# Ensure sound is stopped when object is destroyed
|
||||||
|
if hasattr(self, 'crumbleChannel') and self.crumbleChannel:
|
||||||
|
obj_stop(self.crumbleChannel)
|
||||||
|
self.crumbleChannel = None
|
20
src/level.py
20
src/level.py
@@ -6,6 +6,7 @@ from libstormgames import *
|
|||||||
from src.catapult import Catapult
|
from src.catapult import Catapult
|
||||||
from src.coffin import CoffinObject
|
from src.coffin import CoffinObject
|
||||||
from src.enemy import Enemy
|
from src.enemy import Enemy
|
||||||
|
from src.grasping_hands import GraspingHands
|
||||||
from src.grave import GraveObject
|
from src.grave import GraveObject
|
||||||
from src.object import Object
|
from src.object import Object
|
||||||
from src.player import Player
|
from src.player import Player
|
||||||
@@ -87,6 +88,16 @@ class Level:
|
|||||||
firingRange=obj.get("range", 20)
|
firingRange=obj.get("range", 20)
|
||||||
)
|
)
|
||||||
self.objects.append(catapult)
|
self.objects.append(catapult)
|
||||||
|
# Check if this is grasping hands
|
||||||
|
elif obj.get("type") == "grasping_hands":
|
||||||
|
graspingHands = GraspingHands(
|
||||||
|
xPos,
|
||||||
|
obj["y"],
|
||||||
|
self.sounds,
|
||||||
|
delay=obj.get("delay", 1000),
|
||||||
|
crumble_speed=obj.get("crumble_speed", 0.03)
|
||||||
|
)
|
||||||
|
self.objects.append(graspingHands)
|
||||||
# Check if this is a grave
|
# Check if this is a grave
|
||||||
elif obj.get("type") == "grave":
|
elif obj.get("type") == "grave":
|
||||||
grave = GraveObject(
|
grave = GraveObject(
|
||||||
@@ -247,6 +258,13 @@ class Level:
|
|||||||
if isinstance(obj, SkullStorm):
|
if isinstance(obj, SkullStorm):
|
||||||
obj.update(currentTime, self.player)
|
obj.update(currentTime, self.player)
|
||||||
|
|
||||||
|
# Update grasping hands
|
||||||
|
for obj in self.objects:
|
||||||
|
if isinstance(obj, GraspingHands):
|
||||||
|
caught = obj.update(currentTime, self.player)
|
||||||
|
if caught:
|
||||||
|
return # Stop if player is dead
|
||||||
|
|
||||||
# Update bouncing items
|
# Update bouncing items
|
||||||
for item in self.bouncing_items[:]: # Copy list to allow removal
|
for item in self.bouncing_items[:]: # Copy list to allow removal
|
||||||
if not item.update(currentTime, self.player.xPos):
|
if not item.update(currentTime, self.player.xPos):
|
||||||
@@ -318,7 +336,7 @@ class Level:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Handle grave edge warnings
|
# Handle grave edge warnings
|
||||||
if obj.isHazard and obj.soundName != "spiderweb": # Explicitly exclude spiderwebs
|
if obj.isHazard and obj.soundName != "spiderweb" and not isinstance(obj, GraspingHands): # Exclude spiderwebs and grasping hands
|
||||||
distance = abs(self.player.xPos - obj.xPos)
|
distance = abs(self.player.xPos - obj.xPos)
|
||||||
currentTime = pygame.time.get_ticks()
|
currentTime = pygame.time.get_ticks()
|
||||||
if (distance <= 2 and not self.player.isJumping and not self.player.isInvincible
|
if (distance <= 2 and not self.player.isJumping and not self.player.isInvincible
|
||||||
|
Reference in New Issue
Block a user