Files
wicked-quest/src/catapult.py
2025-09-26 05:05:42 -04:00

157 lines
6.3 KiB
Python

# -*- coding: utf-8 -*-
from libstormgames import *
from src.object import Object
import random
class Pumpkin:
def __init__(self, x, isHigh, direction, playerMaxHealth, soundOverrides=None):
self.x = x
self.isHigh = isHigh
self.direction = direction
self.speed = 0.15
self.isActive = True
self.damage = playerMaxHealth // 2 # Half of player's max health
self.soundChannel = None
# Apply sound overrides if provided
if soundOverrides:
if isHigh and "pumpkin_high" in soundOverrides:
self.soundName = soundOverrides["pumpkin_low"] # Still inverted for overrides
elif not isHigh and "pumpkin_low" in soundOverrides:
self.soundName = soundOverrides["pumpkin_high"] # Still inverted for overrides
else:
self.soundName = "pumpkin_low" if isHigh else "pumpkin_high" # Inverted mapping
else:
self.soundName = "pumpkin_low" if isHigh else "pumpkin_high" # Inverted mapping
def update(self, sounds, playerX):
"""Update pumpkin position and sound"""
if not self.isActive:
return False
self.x += self.direction * self.speed
# Update or start positional audio
if self.soundChannel is None or not self.soundChannel.get_busy():
self.soundChannel = obj_play(sounds, self.soundName, playerX, self.x)
else:
self.soundChannel = obj_update(self.soundChannel, playerX, self.x)
return True
def stop_sound(self, sounds, playerX):
"""Stop the pumpkin's sound and play splat if needed"""
if self.soundChannel:
obj_stop(self.soundChannel)
self.soundChannel = None
# Calculate volume and pan for splat sound based on final position
volume, left, right = calculate_volume_and_pan(playerX, self.x)
if volume > 0: # Only play if within audible range
obj_play(sounds, "pumpkin_splat", playerX, self.x, loop=False)
def check_collision(self, player):
"""Check if pumpkin hits player"""
if not self.isActive:
return False
distance = abs(player.xPos - self.x)
if distance < 1: # Within 1 tile
if self.isHigh and not player.isJumping:
return True # Hit by high pumpkin while on ground
elif not self.isHigh and player.isJumping:
return True # Hit by low pumpkin while jumping
return False
class Catapult(Object):
def __init__(self, x, y, sounds, direction=1, fireInterval=5000, firingRange=20):
super().__init__(
x,
y,
"catapult",
isStatic=True,
isCollectible=False,
)
self.sounds = sounds
self.fireInterval = fireInterval # Time between shots in milliseconds
self.firingRange = firingRange # How close player needs to be to trigger firing
self.lastFireTime = 0
self.activePumpkins = []
self.isFiring = False # Track if we're currently in firing mode
self.launchDelay = 900 # Time between launch sound and pumpkin firing
self.pendingPumpkin = None # Store pending pumpkin data
self.pumpkinLaunchTime = 0 # When to launch the pending pumpkin
self.launchSound = "catapult_launch" # Configurable launch sound
self.soundOverrides = {} # Store sound overrides for pumpkins
self.activateMessage = "Pumpkin catapult activates!"
self.deactivateMessage = "Out of pumpkin catapult range."
def fire(self, currentTime, player):
"""Start the firing sequence"""
self.lastFireTime = currentTime
# Play launch sound using directional audio
play_directional_sound(self.sounds, self.launchSound, player.xPos, self.xPos)
# Set up pending pumpkin
isHigh = random.choice([True, False])
fireDirection = 1 if player.xPos > self.xPos else -1
# Store pumpkin data for later creation
self.pendingPumpkin = {"isHigh": isHigh, "direction": fireDirection, "playerMaxHealth": player.get_max_health(), "soundOverrides": self.soundOverrides}
# Set when to actually launch the pumpkin
self.pumpkinLaunchTime = currentTime + self.launchDelay
def update(self, currentTime, player):
"""Update catapult and its pumpkins"""
if not self.isActive:
return
# Check if player is in range
distance = abs(player.xPos - self.xPos)
inRange = distance <= self.firingRange
# Handle entering/leaving range
if inRange and not self.isFiring:
self.isFiring = True
self.lastFireTime = currentTime # Reset timer when entering range
speak(self.activateMessage)
elif not inRange and self.isFiring:
self.isFiring = False
speak(self.deactivateMessage)
# Check for pending pumpkin launch
if self.pendingPumpkin and currentTime >= self.pumpkinLaunchTime:
# Create and fire the pending pumpkin
pumpkin = Pumpkin(
self.xPos,
self.pendingPumpkin["isHigh"],
self.pendingPumpkin["direction"],
self.pendingPumpkin["playerMaxHealth"],
self.pendingPumpkin["soundOverrides"],
)
self.activePumpkins.append(pumpkin)
self.pendingPumpkin = None
# Only start new fire sequence if in range and enough time has passed
if self.isFiring and currentTime - self.lastFireTime >= self.fireInterval:
self.fire(currentTime, player)
# Always update existing pumpkins
for pumpkin in self.activePumpkins[:]: # Copy list to allow removal
if not pumpkin.update(self.sounds, player.xPos):
pumpkin.stop_sound(self.sounds, player.xPos)
self.activePumpkins.remove(pumpkin)
continue
if pumpkin.check_collision(player):
player.set_health(player.get_health() - pumpkin.damage)
pumpkin.stop_sound(self.sounds, player.xPos)
pumpkin.isActive = False
self.activePumpkins.remove(pumpkin)
if not player.isInvincible and not getattr(player, '_godMode', False):
self.sounds["player_takes_damage"].play()