Graves basically rewritten.
This commit is contained in:
@@ -7,13 +7,6 @@
|
|||||||
"y": 0
|
"y": 0
|
||||||
},
|
},
|
||||||
"objects": [
|
"objects": [
|
||||||
{
|
|
||||||
"x": 2,
|
|
||||||
"y": 3,
|
|
||||||
"item": "guts",
|
|
||||||
"sound": "coffin",
|
|
||||||
"type": "coffin"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"x_range": [5, 8],
|
"x_range": [5, 8],
|
||||||
"y": 3,
|
"y": 3,
|
||||||
@@ -38,7 +31,7 @@
|
|||||||
"x_range": [25, 30],
|
"x_range": [25, 30],
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"enemy_type": "goblin",
|
"enemy_type": "goblin",
|
||||||
"health": 3,
|
"health": 1,
|
||||||
"damage": 1,
|
"damage": 1,
|
||||||
"attack_range": 1,
|
"attack_range": 1,
|
||||||
"attack_pattern": {
|
"attack_pattern": {
|
||||||
@@ -54,10 +47,10 @@
|
|||||||
{
|
{
|
||||||
"x": 45,
|
"x": 45,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"hazard": true,
|
"type": "grave",
|
||||||
"sound": "grave",
|
"sound": "grave",
|
||||||
"static": true,
|
"static": true,
|
||||||
"zombie_spawn_chance": 10
|
"zombie_spawn_chance": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x_range": [55, 57],
|
"x_range": [55, 57],
|
||||||
@@ -76,8 +69,8 @@
|
|||||||
"x_range": [75, 80],
|
"x_range": [75, 80],
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"enemy_type": "goblin",
|
"enemy_type": "goblin",
|
||||||
"health": 5,
|
"health": 1,
|
||||||
"damage": 2,
|
"damage": 1,
|
||||||
"attack_range": 1,
|
"attack_range": 1,
|
||||||
"attack_pattern": {
|
"attack_pattern": {
|
||||||
"type": "patrol"
|
"type": "patrol"
|
||||||
@@ -86,10 +79,10 @@
|
|||||||
{
|
{
|
||||||
"x": 85,
|
"x": 85,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"hazard": true,
|
"type": "grave",
|
||||||
"sound": "grave",
|
"sound": "grave",
|
||||||
"static": true,
|
"static": true,
|
||||||
"zombie_spawn_chance": 15
|
"zombie_spawn_chance": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x_range": [95, 100],
|
"x_range": [95, 100],
|
||||||
@@ -125,11 +118,52 @@
|
|||||||
"sound": "coin",
|
"sound": "coin",
|
||||||
"collectible": true,
|
"collectible": true,
|
||||||
"static": 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": {
|
"boundaries": {
|
||||||
"left": 0,
|
"left": 0,
|
||||||
"right": 165
|
"right": 200
|
||||||
},
|
},
|
||||||
"footstep_sound": "footstep_stone"
|
"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.sounds = sounds
|
||||||
self.level = level
|
self.level = level
|
||||||
self.is_broken = False
|
self.isBroken = False
|
||||||
self.dropped_item = None
|
self.dropped_item = None
|
||||||
self.specified_item = item
|
self.specified_item = item
|
||||||
|
|
||||||
def hit(self, player_pos):
|
def hit(self, player_pos):
|
||||||
"""Handle being hit by the player's weapon"""
|
"""Handle being hit by the player's weapon"""
|
||||||
if not self.is_broken:
|
if not self.isBroken:
|
||||||
self.is_broken = True
|
self.isBroken = True
|
||||||
play_sound(self.sounds['coffin_shatter'])
|
play_sound(self.sounds['coffin_shatter'])
|
||||||
self.level.player.stats.update_stat('Coffins broken', 1)
|
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.catapult import Catapult
|
||||||
from src.coffin import CoffinObject
|
from src.coffin import CoffinObject
|
||||||
from src.enemy import Enemy
|
from src.enemy import Enemy
|
||||||
|
from src.grave import GraveObject
|
||||||
from src.object import Object
|
from src.object import Object
|
||||||
from src.player import Player
|
from src.player import Player
|
||||||
from src.projectile import Projectile
|
from src.projectile import Projectile
|
||||||
@@ -19,7 +20,9 @@ class Level:
|
|||||||
self.bouncing_items = []
|
self.bouncing_items = []
|
||||||
self.projectiles = [] # Track active projectiles
|
self.projectiles = [] # Track active projectiles
|
||||||
self.player = player
|
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.weapon_hit_channel = None
|
||||||
self.leftBoundary = levelData["boundaries"]["left"]
|
self.leftBoundary = levelData["boundaries"]["left"]
|
||||||
self.rightBoundary = levelData["boundaries"]["right"]
|
self.rightBoundary = levelData["boundaries"]["right"]
|
||||||
@@ -61,6 +64,16 @@ class Level:
|
|||||||
firingRange=obj.get("range", 20)
|
firingRange=obj.get("range", 20)
|
||||||
)
|
)
|
||||||
self.objects.append(catapult)
|
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
|
# Check if this is a skull storm
|
||||||
elif obj.get("type") == "skull_storm":
|
elif obj.get("type") == "skull_storm":
|
||||||
skullStorm = SkullStorm(
|
skullStorm = SkullStorm(
|
||||||
@@ -105,11 +118,11 @@ class Level:
|
|||||||
isStatic=obj.get("static", True),
|
isStatic=obj.get("static", True),
|
||||||
isCollectible=obj.get("collectible", False),
|
isCollectible=obj.get("collectible", False),
|
||||||
isHazard=obj.get("hazard", False),
|
isHazard=obj.get("hazard", False),
|
||||||
zombie_spawn_chance=obj.get("zombie_spawn_chance", 0)
|
zombieSpawnChance=obj.get("zombieSpawnChance", 0)
|
||||||
)
|
)
|
||||||
self.objects.append(gameObject)
|
self.objects.append(gameObject)
|
||||||
enemyCount = len(self.enemies)
|
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('Enemies remaining', enemyCount)
|
||||||
player.stats.update_stat('Coffins remaining', coffinCount)
|
player.stats.update_stat('Coffins remaining', coffinCount)
|
||||||
|
|
||||||
@@ -124,16 +137,16 @@ class Level:
|
|||||||
|
|
||||||
# Check for potential zombie spawn from graves
|
# Check for potential zombie spawn from graves
|
||||||
if (obj.soundName == "grave" and
|
if (obj.soundName == "grave" and
|
||||||
obj.zombie_spawn_chance > 0 and
|
obj.zombieSpawnChance > 0 and
|
||||||
not obj.has_spawned):
|
not obj.hasSpawned):
|
||||||
|
|
||||||
distance = abs(self.player.xPos - obj.xPos)
|
distance = abs(self.player.xPos - obj.xPos)
|
||||||
if distance < 6: # Within 6 tiles
|
if distance < 6: # Within 6 tiles
|
||||||
# Mark as checked before doing anything else to prevent multiple checks
|
# Mark as checked before doing anything else to prevent multiple checks
|
||||||
obj.has_spawned = True
|
obj.hasSpawned = True
|
||||||
|
|
||||||
roll = random.randint(1, 100)
|
roll = random.randint(1, 100)
|
||||||
if roll <= obj.zombie_spawn_chance:
|
if roll <= obj.zombieSpawnChance:
|
||||||
zombie = Enemy(
|
zombie = Enemy(
|
||||||
[obj.xPos, obj.xPos],
|
[obj.xPos, obj.xPos],
|
||||||
obj.yPos,
|
obj.yPos,
|
||||||
@@ -209,8 +222,8 @@ class Level:
|
|||||||
|
|
||||||
# Check for coffin hits
|
# Check for coffin hits
|
||||||
for obj in self.objects:
|
for obj in self.objects:
|
||||||
if hasattr(obj, 'is_broken'): # Check if it's a coffin without using isinstance
|
if hasattr(obj, 'isBroken'): # Check if it's a coffin without using isinstance
|
||||||
if (not obj.is_broken and
|
if (not obj.isBroken and
|
||||||
obj.xPos >= attackRange[0] and
|
obj.xPos >= attackRange[0] and
|
||||||
obj.xPos <= attackRange[1] and
|
obj.xPos <= attackRange[1] and
|
||||||
self.player.isJumping): # Must be jumping to hit floating coffins
|
self.player.isJumping): # Must be jumping to hit floating coffins
|
||||||
@@ -233,12 +246,14 @@ class Level:
|
|||||||
# Handle grave edge warnings
|
# Handle grave edge warnings
|
||||||
if obj.isHazard:
|
if obj.isHazard:
|
||||||
distance = abs(self.player.xPos - obj.xPos)
|
distance = abs(self.player.xPos - obj.xPos)
|
||||||
if distance <= 2 and not self.player.isJumping and not self.player.isInvincible:
|
currentTime = pygame.time.get_ticks()
|
||||||
if self.edge_warning_channel is None or not self.edge_warning_channel.get_busy():
|
if (distance <= 2 and not self.player.isJumping and not self.player.isInvincible
|
||||||
self.edge_warning_channel = play_sound(self.sounds['edge'])
|
and currentTime - self.lastWarningTime >= self.warningInterval):
|
||||||
|
if isinstance(obj, GraveObject) and obj.graveItem and not obj.isCollected:
|
||||||
|
play_sound(self.sounds['_edge'])
|
||||||
else:
|
else:
|
||||||
if self.edge_warning_channel is not None and not self.edge_warning_channel.get_busy():
|
play_sound(self.sounds['edge'])
|
||||||
self.edge_warning_channel = None
|
self.lastWarningTime = currentTime
|
||||||
|
|
||||||
if obj.is_in_range(self.player.xPos):
|
if obj.is_in_range(self.player.xPos):
|
||||||
if obj.isCollectible and self.player.isJumping:
|
if obj.isCollectible and self.player.isJumping:
|
||||||
@@ -266,14 +281,30 @@ class Level:
|
|||||||
play_sound(self.sounds['get_extra_life'])
|
play_sound(self.sounds['get_extra_life'])
|
||||||
|
|
||||||
elif obj.isHazard and not self.player.isJumping:
|
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])
|
play_sound(self.sounds[obj.soundName])
|
||||||
speak("You fell in an open grave! Now, it's yours!")
|
speak("You fell in an open grave! Now, it's yours!")
|
||||||
self.player.set_health(0)
|
self.player.set_health(0)
|
||||||
return False
|
return False
|
||||||
else:
|
|
||||||
# When invincible, treat it like a successful jump over the grave
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Handle boundaries
|
# Handle boundaries
|
||||||
if self.player.xPos < self.leftBoundary:
|
if self.player.xPos < self.leftBoundary:
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
from libstormgames import *
|
from libstormgames import *
|
||||||
|
|
||||||
class Object:
|
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]
|
# x can be either a single position or a range [start, end]
|
||||||
self.xRange = [x, x] if isinstance(x, (int, float)) else x
|
self.xRange = [x, x] if isinstance(x, (int, float)) else x
|
||||||
self.yPos = yPos
|
self.yPos = yPos
|
||||||
@@ -9,8 +9,8 @@ class Object:
|
|||||||
self.isStatic = isStatic
|
self.isStatic = isStatic
|
||||||
self.isCollectible = isCollectible
|
self.isCollectible = isCollectible
|
||||||
self.isHazard = isHazard
|
self.isHazard = isHazard
|
||||||
self.zombie_spawn_chance = zombie_spawn_chance
|
self.zombieSpawnChance = zombieSpawnChance
|
||||||
self.has_spawned = False # Track if this object has spawned a zombie
|
self.hasSpawned = False
|
||||||
self.channel = None # For tracking the sound channel
|
self.channel = None # For tracking the sound channel
|
||||||
self.isActive = True
|
self.isActive = True
|
||||||
# For collectibles in a range, track which positions have been collected
|
# For collectibles in a range, track which positions have been collected
|
||||||
|
@@ -13,6 +13,7 @@ class Player:
|
|||||||
self.moveSpeed = 0.05
|
self.moveSpeed = 0.05
|
||||||
self.jumpDuration = 1000 # Jump duration in milliseconds
|
self.jumpDuration = 1000 # Jump duration in milliseconds
|
||||||
self.jumpStartTime = 0
|
self.jumpStartTime = 0
|
||||||
|
self.isDucking = False
|
||||||
self.isJumping = False
|
self.isJumping = False
|
||||||
self.isRunning = False
|
self.isRunning = False
|
||||||
self.runMultiplier = 1.5 # Same multiplier as jumping
|
self.runMultiplier = 1.5 # Same multiplier as jumping
|
||||||
@@ -69,6 +70,20 @@ class Player:
|
|||||||
return (self.distanceSinceLastStep >= self.get_step_distance() and
|
return (self.distanceSinceLastStep >= self.get_step_distance() and
|
||||||
currentTime - self.lastStepTime >= self.get_step_interval())
|
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):
|
def update(self, currentTime):
|
||||||
"""Update player state"""
|
"""Update player state"""
|
||||||
# Check if invincibility has expired
|
# Check if invincibility has expired
|
||||||
|
Reference in New Issue
Block a user