Got end of level working. Added floating coffins, shatter them and collect what comes out.

This commit is contained in:
Storm Dragon
2025-02-01 19:50:13 -05:00
parent 6fda41f57b
commit 0a7052094d
8 changed files with 124 additions and 74 deletions

View File

@@ -1,7 +1,7 @@
{
"level_id": 1,
"name": "The Mausoleum",
"description": "After years of existing as a pile of bones, someone was crazy enough to assemble your skeleton. Time to wreak some havoc!",
"description": "After years of existing as a pile of bones, someone was crazy enough to assemble your skeleton. Time to wreak some havoc! Use W to jump, A/D to move, and CTRL to attack with your shovel.",
"player_start": {
"x": 0,
"y": 0
@@ -14,6 +14,12 @@
"collectible": true,
"static": true
},
{
"x": 10,
"y": 3,
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [15, 17],
"y": 3,
@@ -25,20 +31,19 @@
"x": 25,
"y": 0,
"enemy_type": "goblin",
"health": 5,
"damage": 2,
"health": 3,
"damage": 1,
"attack_range": 1,
"movement_range": 5
},
{
"x": 30,
"x": 35,
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
"sound": "coffin",
"type": "coffin"
},
{
"x": 40,
"x": 45,
"y": 0,
"hazard": true,
"sound": "grave",
@@ -46,21 +51,20 @@
"zombie_spawn_chance": 10
},
{
"x_range": [45, 47],
"x_range": [55, 57],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 55,
"x": 65,
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
"sound": "coffin",
"type": "coffin"
},
{
"x": 60,
"x": 75,
"y": 0,
"enemy_type": "goblin",
"health": 5,
@@ -69,7 +73,7 @@
"movement_range": 5
},
{
"x": 70,
"x": 85,
"y": 0,
"hazard": true,
"sound": "grave",
@@ -77,22 +81,7 @@
"zombie_spawn_chance": 15
},
{
"x_range": [80, 82],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 90,
"y": 0,
"hazard": true,
"sound": "grave",
"static": true,
"zombie_spawn_chance": 20
},
{
"x": 100,
"x_range": [95, 97],
"y": 3,
"sound": "coin",
"collectible": true,
@@ -100,27 +89,17 @@
},
{
"x": 110,
"y": 0,
"hazard": true,
"sound": "grave",
"static": true,
"zombie_spawn_chance": 25
"y": 3,
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [120, 123],
"x_range": [120, 122],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 130,
"y": 0,
"hazard": true,
"sound": "grave",
"static": true,
"zombie_spawn_chance": 30
},
{
"x": 145,
"y": 0,
@@ -132,6 +111,6 @@
],
"boundaries": {
"left": 0,
"right": 150
"right": 160
}
}

BIN
sounds/end_of_level.ogg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
sounds/lose_a_life.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -21,6 +21,14 @@ class CoffinObject(Object):
self.is_broken = True
self.sounds['coffin_shatter'].play()
# Stop the ongoing coffin sound
if self.channel:
obj_stop(self.channel)
self.channel = None
# Mark coffin as inactive since it's broken
self.isActive = False
# Randomly choose item type
item_type = random.choice(['hand_of_glory', 'jack_o_lantern'])

View File

@@ -7,7 +7,6 @@ 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:
@@ -17,9 +16,23 @@ class Level:
self.enemies = []
self.bouncing_items = []
self.projectiles = [] # Track active projectiles
self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"])
self.player = Player(levelData["player_start"]["x"], levelData["player_start"]["y"], sounds)
self.edge_warning_channel = None
self.weapon_hit_channel = None
self.leftBoundary = levelData["boundaries"]["left"]
self.rightBoundary = levelData["boundaries"]["right"]
self.levelId = levelData["level_id"]
# Create end of level object at right boundary
endLevel = Object(
self.rightBoundary,
0, # Same y-level as player start
"end_of_level",
isStatic=True,
isCollectible=False,
isHazard=False
)
self.objects.append(endLevel)
# Load objects and enemies from level data
for obj in levelData["objects"]:
@@ -40,6 +53,14 @@ class Level:
firingRange=obj.get("range", 20)
)
self.objects.append(catapult)
# Check if this is a coffin
elif obj.get("type") == "coffin":
coffin = CoffinObject(
xPos[0],
obj["y"],
self.sounds
)
self.objects.append(coffin)
# Check if this is an enemy
elif "enemy_type" in obj:
enemy = Enemy(
@@ -66,6 +87,7 @@ class Level:
self.objects.append(gameObject)
def update_audio(self):
"""Update all audio and entity state."""
currentTime = pygame.time.get_ticks()
# Update regular objects and check for zombie spawning
@@ -156,7 +178,7 @@ class Level:
if self.weapon_hit_channel is not None and not self.weapon_hit_channel.get_busy():
self.weapon_hit_channel = None
# Check for coffin hits - only if we have any coffins
# Check for coffin hits
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
@@ -164,11 +186,17 @@ class Level:
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!")
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):
"""Handle all collision checks and return True if level is complete."""
# First check if player is dead
if self.player.get_health() <= 0:
return False
# Process object collisions for hazards and collectibles
for obj in self.objects:
if not obj.isActive:
continue
@@ -176,13 +204,9 @@ class Level:
# Handle grave edge warnings
if obj.isHazard:
distance = abs(self.player.xPos - obj.xPos)
# Check if within 1 tile and moving
if distance <= 2 and not self.player.isJumping:
# Only play edge warning if not already playing
if self.edge_warning_channel is None or not self.edge_warning_channel.get_busy():
self.edge_warning_channel = self.sounds['edge'].play()
else:
if self.edge_warning_channel is not None and not self.edge_warning_channel.get_busy():
self.edge_warning_channel = None
@@ -200,6 +224,25 @@ class Level:
self.sounds[obj.soundName].play()
speak("You fell in an open grave!")
self.player.set_health(0)
return False
# Handle boundaries
if self.player.xPos < self.leftBoundary:
self.player.xPos = self.leftBoundary
speak("Start of level!")
# Check for level completion - takes precedence over everything except death
if self.player.get_health() > 0:
for obj in self.objects:
if obj.soundName == "end_of_level":
# Check if player has reached or passed the end marker
if self.player.xPos >= obj.xPos:
# Stop all current sounds and play end level sound
pygame.mixer.stop()
self.sounds["end_of_level"].play()
return True
return False
def handle_projectiles(self, currentTime):
"""Update projectiles and check for collisions"""

View File

@@ -1,6 +1,11 @@
import pygame
from libstormgames import *
from src.weapon import Weapon
class Player:
def __init__(self, xPos, yPos):
def __init__(self, xPos, yPos, sounds):
self.sounds = sounds
# Movement attributes
self.xPos = xPos
self.yPos = yPos
@@ -98,15 +103,21 @@ class Player:
return self._maxHealth
def set_health(self, value):
"""Set health and handle death if needed"""
"""Set health and handle death if needed."""
if self.isInvincible:
return # No damage while invincible
old_health = self._health
self._health = max(0, value) # Health can't go below 0
if self._health == 0:
if self._health == 0 and old_health > 0:
self._lives -= 1
# Stop all current sounds before playing death sound
pygame.mixer.stop()
cut_scene(self.sounds, 'lose_a_life')
if self._lives > 0:
self._health = 10 # Reset health if we still have lives
self._health = self._maxHealth # Reset health if we still have lives
speak(f"{self._lives} lives remaining")
def set_max_health(self, value):
"""Set max health"""

View File

@@ -14,6 +14,7 @@ class PowerUp(Object):
self.speed = 0.05 # Base movement speed
self.item_type = item_type
self.channel = None
self._currentX = x # Initialize the current x position
def update(self, current_time):
"""Update item position"""
@@ -23,9 +24,11 @@ class PowerUp(Object):
# Update position
self._currentX += self.direction * self.speed
# Keep bounce sound playing while moving
# Update positional audio
if self.channel is None or not self.channel.get_busy():
self.channel = self.sounds['item_bounce'].play(-1)
self.channel = obj_play(self.sounds, "item_bounce", self.xPos, self._currentX)
else:
self.channel = obj_update(self.channel, self.xPos, self._currentX)
# Check if item has gone too far (20 tiles)
if abs(self._currentX - self.xRange[0]) > 20: