190 lines
7.9 KiB
Python
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()
|