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