Files
wicked-quest/src/level.py

190 lines
7.9 KiB
Python

import pygame
import random
from libstormgames import *
from src.enemy import Enemy
from src.object import Object
from src.player import Player
from src.projectile import Projectile
from src.coffin import CoffinObject
from src.powerup import PowerUp
class Level:
def __init__(self, levelData, sounds):
self.sounds = sounds
self.objects = []
self.enemies = []
self.bouncing_items = []
self.projectiles = [] # Track active projectiles
self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"])
# Load objects and enemies from level data
for obj in levelData["objects"]:
# Handle x position or range
if "x_range" in obj:
xPos = obj["x_range"]
else:
xPos = [obj["x"], obj["x"]] # Single position as range
# Check if this is an enemy
if "enemy_type" in obj:
enemy = Enemy(
xPos,
obj["y"],
obj["enemy_type"],
self.sounds,
health=obj.get("health", 5),
damage=obj.get("damage", 1),
attack_range=obj.get("attack_range", 1),
movement_range=obj.get("movement_range", 5)
)
self.enemies.append(enemy)
else:
gameObject = Object(
xPos,
obj["y"],
obj["sound"],
isStatic=obj.get("static", True),
isCollectible=obj.get("collectible", False),
isHazard=obj.get("hazard", False),
zombie_spawn_chance=obj.get("zombie_spawn_chance", 0)
)
self.objects.append(gameObject)
def update_audio(self):
currentTime = pygame.time.get_ticks()
# Update regular objects and check for zombie spawning
for obj in self.objects:
if not obj.isActive:
continue
# Check for potential zombie spawn from graves
if (obj.soundName == "grave" and
obj.zombie_spawn_chance > 0 and
not obj.has_spawned):
distance = abs(self.player.xPos - obj.xPos)
if distance < 6: # Within 6 tiles
# Mark as checked before doing anything else to prevent multiple checks
obj.has_spawned = True
roll = random.randint(1, 100)
speak(f"Near grave, chance to spawn zombie")
if roll <= obj.zombie_spawn_chance:
zombie = Enemy(
[obj.xPos, obj.xPos],
obj.yPos,
"zombie",
self.sounds,
health=3,
damage=10,
attack_range=1
)
self.enemies.append(zombie)
speak("A zombie emerges from the grave!")
# Handle object audio
if not obj.isStatic:
if obj.channel is None or not obj.channel.get_busy():
obj.channel = obj_play(self.sounds, obj.soundName, self.player.xPos, obj.xPos)
else:
if obj.channel is None:
obj.channel = obj_play(self.sounds, obj.soundName, self.player.xPos, obj.xPos)
if obj.channel is not None:
obj.channel = obj_update(obj.channel, self.player.xPos, obj.xPos)
# Update enemies
for enemy in self.enemies:
if not enemy.isActive:
continue
enemy.update(currentTime, self.player)
if enemy.channel is None or not enemy.channel.get_busy():
enemy.channel = obj_play(self.sounds, enemy.enemyType, self.player.xPos, enemy.xPos)
if enemy.channel is not None:
enemy.channel = obj_update(enemy.channel, self.player.xPos, enemy.xPos)
# Update bouncing items
for item in self.bouncing_items[:]: # Copy list to allow removal
if not item.update(currentTime):
self.bouncing_items.remove(item)
if not item.isActive:
speak(f"{item.soundName} got away!")
continue
# Check for item collection
if abs(item.xPos - self.player.xPos) < 1 and self.player.isJumping:
self.sounds[f'get_{item.soundName}'].play()
item.apply_effect(self.player)
item.isActive = False
self.bouncing_items.remove(item)
def handle_combat(self, currentTime):
"""Handle combat interactions between player and enemies"""
attackRange = self.player.get_attack_range(currentTime)
if attackRange:
# Check for enemy hits
for enemy in self.enemies:
if enemy.isActive and enemy.xPos >= attackRange[0] and enemy.xPos <= attackRange[1]:
self.sounds[self.player.currentWeapon.hitSound].play()
enemy.take_damage(self.player.currentWeapon.damage)
# Check for coffin hits - only if we have any coffins
for obj in self.objects:
if hasattr(obj, 'is_broken'): # Check if it's a coffin without using isinstance
if (not obj.is_broken and
obj.xPos >= attackRange[0] and
obj.xPos <= attackRange[1] and
self.player.isJumping): # Must be jumping to hit floating coffins
if obj.hit(self.player.xPos):
self.bouncing_items.append(obj.dropped_item)
speak(f"{obj.dropped_item.item_type} falls out!")
def handle_collisions(self):
for obj in self.objects:
if not obj.isActive:
continue
if obj.is_in_range(self.player.xPos):
if obj.isCollectible and self.player.isJumping:
currentPos = round(self.player.xPos)
if currentPos not in obj.collectedPositions:
self.sounds[f'get_{obj.soundName}'].play()
speak(f"Collected {obj.soundName}")
obj.collect_at_position(currentPos)
self.player.collectedItems.append(obj.soundName)
elif obj.isHazard and not self.player.isJumping:
self.sounds[obj.soundName].play()
speak("You fell in an open grave!")
self.player.set_health(0)
def handle_projectiles(self, currentTime):
"""Update projectiles and check for collisions"""
for proj in self.projectiles[:]: # Copy list to allow removal
if not proj.update():
self.projectiles.remove(proj)
continue
# Check for enemy hits
for enemy in self.enemies:
if enemy.isActive and abs(proj.x - enemy.xPos) < 1:
proj.hit_enemy(enemy)
self.projectiles.remove(proj)
break
def throw_projectile(self):
"""Have player throw a projectile"""
proj_info = self.player.throw_projectile()
if proj_info:
self.projectiles.append(Projectile(
proj_info['type'],
proj_info['start_x'],
proj_info['direction']
))
# Play throw sound
if f"{proj_info['type']}_throw" in self.sounds:
self.sounds[f"{proj_info['type']}_throw"].play()