from libstormgames import * from src.object import Object class SkullStorm(Object): """Handles falling skulls within a specified range.""" def __init__(self, xRange, y, sounds, damage, maxSkulls=3, minFreq=2, maxFreq=5): super().__init__( xRange, y, "", # No ambient sound for the skull storm isStatic=True, isCollectible=False, isHazard=False ) self.sounds = sounds self.damage = damage self.maxSkulls = maxSkulls self.minFreq = minFreq * 1000 # Convert to milliseconds self.maxFreq = maxFreq * 1000 self.activeSkulls = [] # List of currently falling skulls self.lastSkullTime = 0 self.nextSkullDelay = random.randint(self.minFreq, self.maxFreq) self.playerInRange = False def update(self, currentTime, player): """Update all active skulls and potentially spawn new ones.""" if not self.isActive: return # Check if player has entered range inRange = self.xRange[0] <= player.xPos <= self.xRange[1] if inRange and not self.playerInRange: # Player just entered range - play the warning sound play_sound(self.sounds['skull_storm']) self.playerInRange = True elif not inRange and self.playerInRange: # Only speak when actually leaving range # Player just left range self.playerInRange = False speak("Skull storm ended.") if not inRange: return # Update existing skulls for skull in self.activeSkulls[:]: # Copy list to allow removal if currentTime >= skull['land_time']: # Skull has landed self.handle_landing(skull, player) self.activeSkulls.remove(skull) else: # Update falling sound timeElapsed = currentTime - skull['start_time'] fallProgress = timeElapsed / skull['fall_duration'] currentY = self.yPos * (1 - fallProgress) skull['channel'] = play_random_falling( self.sounds, 'falling_skull', player.xPos, skull['x'], self.yPos, currentY, existing_channel=skull['channel'] ) # Check if we should spawn a new skull if (len(self.activeSkulls) < self.maxSkulls and currentTime - self.lastSkullTime >= self.nextSkullDelay): self.spawn_skull(currentTime) def spawn_skull(self, currentTime): """Spawn a new falling skull at a random position within range.""" # Reset timing self.lastSkullTime = currentTime self.nextSkullDelay = random.randint(self.minFreq, self.maxFreq) # Calculate fall duration based on height (higher = longer fall) fallDuration = self.yPos * 100 # 100ms per unit of height # Create new skull skull = { 'x': random.uniform(self.xRange[0], self.xRange[1]), 'start_time': currentTime, 'fall_duration': fallDuration, 'land_time': currentTime + fallDuration, 'channel': None } self.activeSkulls.append(skull) def handle_landing(self, skull, player): """Handle a skull landing.""" # Stop falling sound if skull['channel']: obj_stop(skull['channel']) # Play landing sound with positional audio once channel = pygame.mixer.find_channel(True) # Find an available channel if channel: soundObj = self.sounds['skull_lands'] obj_play(self.sounds, 'skull_lands', player.xPos, skull['x'], loop=False) # Check if player was hit if abs(player.xPos - skull['x']) < 1: # Within 1 tile if not player.isJumping: # Only hit if not jumping player.set_health(player.get_health() - self.damage) speak("Hit by falling skull!")