# -*- 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