Graves basically rewritten.

This commit is contained in:
Storm Dragon
2025-02-08 03:30:45 -05:00
parent e0e097a397
commit ada16cb40f
8 changed files with 172 additions and 52 deletions

View File

@@ -7,13 +7,6 @@
"y": 0
},
"objects": [
{
"x": 2,
"y": 3,
"item": "guts",
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [5, 8],
"y": 3,
@@ -38,7 +31,7 @@
"x_range": [25, 30],
"y": 0,
"enemy_type": "goblin",
"health": 3,
"health": 1,
"damage": 1,
"attack_range": 1,
"attack_pattern": {
@@ -54,10 +47,10 @@
{
"x": 45,
"y": 0,
"hazard": true,
"type": "grave",
"sound": "grave",
"static": true,
"zombie_spawn_chance": 10
"zombie_spawn_chance": 0
},
{
"x_range": [55, 57],
@@ -76,8 +69,8 @@
"x_range": [75, 80],
"y": 0,
"enemy_type": "goblin",
"health": 5,
"damage": 2,
"health": 1,
"damage": 1,
"attack_range": 1,
"attack_pattern": {
"type": "patrol"
@@ -86,10 +79,10 @@
{
"x": 85,
"y": 0,
"hazard": true,
"type": "grave",
"sound": "grave",
"static": true,
"zombie_spawn_chance": 15
"zombie_spawn_chance": 0
},
{
"x_range": [95, 100],
@@ -125,11 +118,52 @@
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 165,
"y": 0,
"type": "grave",
"sound": "grave",
"static": true,
"zombie_spawn_chance": 0
},
{
"x_range": [170, 172],
"y": 3,
"sound": "coin",
"collectible": true,
"static": true
},
{
"x": 180,
"y": 3,
"sound": "coffin",
"type": "coffin"
},
{
"x_range": [185, 190],
"y": 0,
"enemy_type": "goblin",
"health": 1,
"damage": 1,
"attack_range": 1,
"attack_pattern": {
"type": "patrol"
}
},
{
"x": 195,
"y": 0,
"type": "grave",
"sound": "grave",
"static": true,
"zombie_spawn_chance": 0,
"item": "guts"
}
],
"boundaries": {
"left": 0,
"right": 165
"right": 200
},
"footstep_sound": "footstep_stone"
}

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

Binary file not shown.

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

Binary file not shown.

View File

@@ -15,14 +15,14 @@ class CoffinObject(Object):
)
self.sounds = sounds
self.level = level
self.is_broken = False
self.isBroken = False
self.dropped_item = None
self.specified_item = item
def hit(self, player_pos):
"""Handle being hit by the player's weapon"""
if not self.is_broken:
self.is_broken = True
if not self.isBroken:
self.isBroken = True
play_sound(self.sounds['coffin_shatter'])
self.level.player.stats.update_stat('Coffins broken', 1)

34
src/grave.py Normal file
View File

@@ -0,0 +1,34 @@
from libstormgames import *
from src.object import Object
from src.powerup import PowerUp
class GraveObject(Object):
def __init__(self, x, y, sounds, item=None, zombieSpawnChance=0):
super().__init__(
x, y, "grave",
isStatic=True,
isCollectible=False,
isHazard=True,
zombieSpawnChance=zombieSpawnChance
)
self.graveItem = item
self.isCollected = False # Renamed to match style of isHazard, isStatic etc
self.sounds = sounds
def collect_grave_item(self, player):
"""Handle collection of items from graves via ducking.
Returns:
bool: True if item was collected, False if player should die
"""
# If grave has no item or item was already collected, player dies
if not self.graveItem or self.isCollected:
return False
# Collect the item if player is ducking
if player.isDucking:
self.isCollected = True # Mark as collected when collection succeeds
return True
return False

View File

@@ -4,6 +4,7 @@ from libstormgames import *
from src.catapult import Catapult
from src.coffin import CoffinObject
from src.enemy import Enemy
from src.grave import GraveObject
from src.object import Object
from src.player import Player
from src.projectile import Projectile
@@ -19,7 +20,9 @@ class Level:
self.bouncing_items = []
self.projectiles = [] # Track active projectiles
self.player = player
self.edge_warning_channel = None
self.lastWarningTime = 0
self.warningInterval = int(self.sounds['edge'].get_length() * 1000) # Convert seconds to milliseconds
self.weapon_hit_channel = None
self.leftBoundary = levelData["boundaries"]["left"]
self.rightBoundary = levelData["boundaries"]["right"]
@@ -61,6 +64,16 @@ class Level:
firingRange=obj.get("range", 20)
)
self.objects.append(catapult)
# Check if this is a grave
elif obj.get("type") == "grave":
grave = GraveObject(
xPos[0],
obj["y"],
self.sounds,
item=obj.get("item", None),
zombieSpawnChance=obj.get("zombie_spawn_chance", 0)
)
self.objects.append(grave)
# Check if this is a skull storm
elif obj.get("type") == "skull_storm":
skullStorm = SkullStorm(
@@ -105,11 +118,11 @@ class Level:
isStatic=obj.get("static", True),
isCollectible=obj.get("collectible", False),
isHazard=obj.get("hazard", False),
zombie_spawn_chance=obj.get("zombie_spawn_chance", 0)
zombieSpawnChance=obj.get("zombieSpawnChance", 0)
)
self.objects.append(gameObject)
enemyCount = len(self.enemies)
coffinCount = sum(1 for obj in self.objects if hasattr(obj, 'is_broken'))
coffinCount = sum(1 for obj in self.objects if hasattr(obj, 'isBroken'))
player.stats.update_stat('Enemies remaining', enemyCount)
player.stats.update_stat('Coffins remaining', coffinCount)
@@ -124,16 +137,16 @@ class Level:
# Check for potential zombie spawn from graves
if (obj.soundName == "grave" and
obj.zombie_spawn_chance > 0 and
not obj.has_spawned):
obj.zombieSpawnChance > 0 and
not obj.hasSpawned):
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
obj.hasSpawned = True
roll = random.randint(1, 100)
if roll <= obj.zombie_spawn_chance:
if roll <= obj.zombieSpawnChance:
zombie = Enemy(
[obj.xPos, obj.xPos],
obj.yPos,
@@ -209,8 +222,8 @@ class Level:
# 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
if hasattr(obj, 'isBroken'): # Check if it's a coffin without using isinstance
if (not obj.isBroken and
obj.xPos >= attackRange[0] and
obj.xPos <= attackRange[1] and
self.player.isJumping): # Must be jumping to hit floating coffins
@@ -229,16 +242,18 @@ class Level:
for obj in self.objects:
if not obj.isActive:
continue
# Handle grave edge warnings
if obj.isHazard:
distance = abs(self.player.xPos - obj.xPos)
if distance <= 2 and not self.player.isJumping and not self.player.isInvincible:
if self.edge_warning_channel is None or not self.edge_warning_channel.get_busy():
self.edge_warning_channel = play_sound(self.sounds['edge'])
else:
if self.edge_warning_channel is not None and not self.edge_warning_channel.get_busy():
self.edge_warning_channel = None
currentTime = pygame.time.get_ticks()
if (distance <= 2 and not self.player.isJumping and not self.player.isInvincible
and currentTime - self.lastWarningTime >= self.warningInterval):
if isinstance(obj, GraveObject) and obj.graveItem and not obj.isCollected:
play_sound(self.sounds['_edge'])
else:
play_sound(self.sounds['edge'])
self.lastWarningTime = currentTime
if obj.is_in_range(self.player.xPos):
if obj.isCollectible and self.player.isJumping:
@@ -255,25 +270,41 @@ class Level:
# Only heal if below max health
if self.player.get_health() < self.player.get_max_health():
self.player.set_health(min(
self.player.get_health() + 1,
self.player.get_max_health()
self.player.get_health() + 1,
self.player.get_max_health()
))
if self.player._coins % 100 == 0:
# Extra life
self.player._coins = 0
self.player._lives += 1
play_sound(self.sounds['get_extra_life'])
if self.player._coins % 100 == 0:
# Extra life
self.player._coins = 0
self.player._lives += 1
play_sound(self.sounds['get_extra_life'])
elif obj.isHazard and not self.player.isJumping:
if not self.player.isInvincible:
play_sound(self.sounds[obj.soundName])
speak("You fell in an open grave! Now, it's yours!")
self.player.set_health(0)
return False
else:
# When invincible, treat it like a successful jump over the grave
pass
if isinstance(obj, GraveObject):
can_collect = obj.collect_grave_item(self.player)
if can_collect:
# Successfully collected item while ducking
play_sound(self.sounds[f'get_{obj.graveItem}'])
self.player.stats.update_stat('Items collected', 1)
# Create PowerUp to handle the item effect
item = PowerUp(obj.xPos, obj.yPos, obj.graveItem, self.sounds, 1)
item.apply_effect(self.player)
# Stop grave's current audio channel
if obj.channel:
obj_stop(obj.channel)
# Remove the grave
obj.graveItem = None
obj.channel = None
obj.isActive = False # Mark the grave as inactive after collection
continue
elif not self.player.isInvincible:
# Kill player for normal graves or non-ducking collision
play_sound(self.sounds[obj.soundName])
speak("You fell in an open grave! Now, it's yours!")
self.player.set_health(0)
return False
# Handle boundaries
if self.player.xPos < self.leftBoundary:

View File

@@ -1,7 +1,7 @@
from libstormgames import *
class Object:
def __init__(self, x, yPos, soundName, isStatic=True, isCollectible=False, isHazard=False, zombie_spawn_chance=0):
def __init__(self, x, yPos, soundName, isStatic=True, isCollectible=False, isHazard=False, zombieSpawnChance=0):
# x can be either a single position or a range [start, end]
self.xRange = [x, x] if isinstance(x, (int, float)) else x
self.yPos = yPos
@@ -9,8 +9,8 @@ class Object:
self.isStatic = isStatic
self.isCollectible = isCollectible
self.isHazard = isHazard
self.zombie_spawn_chance = zombie_spawn_chance
self.has_spawned = False # Track if this object has spawned a zombie
self.zombieSpawnChance = zombieSpawnChance
self.hasSpawned = False
self.channel = None # For tracking the sound channel
self.isActive = True
# For collectibles in a range, track which positions have been collected

View File

@@ -13,6 +13,7 @@ class Player:
self.moveSpeed = 0.05
self.jumpDuration = 1000 # Jump duration in milliseconds
self.jumpStartTime = 0
self.isDucking = False
self.isJumping = False
self.isRunning = False
self.runMultiplier = 1.5 # Same multiplier as jumping
@@ -69,6 +70,20 @@ class Player:
return (self.distanceSinceLastStep >= self.get_step_distance() and
currentTime - self.lastStepTime >= self.get_step_interval())
def duck(self):
"""Start ducking"""
if not self.isDucking and not self.isJumping: # Can't duck while jumping
self.isDucking = True
play_sound(self.sounds['duck'])
return True
return False
def stand(self):
"""Stop ducking state and play sound"""
if self.isDucking:
self.isDucking = False
play_sound(self.sounds['stand'])
def update(self, currentTime):
"""Update player state"""
# Check if invincibility has expired