Graves basically rewritten.
This commit is contained in:
@@ -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
BIN
sounds/duck.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/stand.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/stand.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -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
34
src/grave.py
Normal 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
|
67
src/level.py
67
src/level.py
@@ -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
|
||||
@@ -233,12 +246,14 @@ class Level:
|
||||
# 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'])
|
||||
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:
|
||||
if self.edge_warning_channel is not None and not self.edge_warning_channel.get_busy():
|
||||
self.edge_warning_channel = None
|
||||
play_sound(self.sounds['edge'])
|
||||
self.lastWarningTime = currentTime
|
||||
|
||||
if obj.is_in_range(self.player.xPos):
|
||||
if obj.isCollectible and self.player.isJumping:
|
||||
@@ -266,14 +281,30 @@ class Level:
|
||||
play_sound(self.sounds['get_extra_life'])
|
||||
|
||||
elif obj.isHazard and not self.player.isJumping:
|
||||
if not self.player.isInvincible:
|
||||
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
|
||||
else:
|
||||
# When invincible, treat it like a successful jump over the grave
|
||||
pass
|
||||
|
||||
# Handle boundaries
|
||||
if self.player.xPos < self.leftBoundary:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user