Updated libstormgames submodule. Added skull storms. Fixed no jack-o-lanterns reported when there actually were. Fixed player being recreated on each new level thus resetting all stats.

This commit is contained in:
Storm Dragon
2025-02-03 02:17:53 -05:00
parent 0a7052094d
commit 1d033e067a
9 changed files with 186 additions and 50 deletions

112
src/skull_storm.py Normal file
View File

@@ -0,0 +1,112 @@
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
self.sounds['skull_storm'].play()
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)
if skull['channel'] is None or not skull['channel'].get_busy():
skull['channel'] = play_random_falling(
self.sounds,
'falling_skull',
player.xPos,
skull['x'],
self.yPos,
currentY
)
# 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']
channel.play(soundObj, 0) # Play once (0 = no loops)
# Apply positional audio
volume, left, right = calculate_volume_and_pan(player.xPos, skull['x'])
channel.set_volume(volume * left, volume * right)
# 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!")