Allow for custom footsteps. Some work on footsteps to make the sound less crazy. Various other updates. Added initial documentation and credits files.
This commit is contained in:
4
files/credits.txt
Normal file
4
files/credits.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Billy Wolfe: Designer and coder.
|
||||||
|
https://social.wolfe.casa/storm
|
||||||
|
Source code is available at:
|
||||||
|
https://git.stormux.org/storm/wicked-quest
|
40
files/instructions.txt
Normal file
40
files/instructions.txt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
Welcome to Wicked Quest
|
||||||
|
|
||||||
|
For years, your bones have lain in disarray, scattered about the mausoleum where you were interred.
|
||||||
|
Some foolish mortal wandered in to your burial chamber and put you back together.
|
||||||
|
Now, you can wonder around spreading chaos and destruction where ever you go.
|
||||||
|
First, however, you have to make it past the dead so you can plague the living.
|
||||||
|
You will nead some kind of weapon to make it through.
|
||||||
|
You spot a grave digger's shovel and quickly grab it even though it's covered in rust.
|
||||||
|
You give an evil grin, oh wait, your a skeleton, your skull is always doing that.
|
||||||
|
|
||||||
|
Controls
|
||||||
|
|
||||||
|
a: move left
|
||||||
|
d: move right
|
||||||
|
w: jump
|
||||||
|
Control: attack
|
||||||
|
f: throw jack O'lantern
|
||||||
|
c: check bone dust
|
||||||
|
h check health
|
||||||
|
j: check jack O'lanterns
|
||||||
|
l: check lives remaining
|
||||||
|
|
||||||
|
Notes
|
||||||
|
|
||||||
|
Each 5 bone dust restores 1 health.
|
||||||
|
Each 100 bone dust gains an extra unlife.
|
||||||
|
|
||||||
|
Enemies
|
||||||
|
|
||||||
|
Goblin: Walks back and forth in his area trying to break your bones.
|
||||||
|
Ghoul: Same behavior as goblin, but if you enter his area he will actively chase you, staying close.
|
||||||
|
Pumpkin Catapult: Fires pumpkins at you in hopes of smashing you into bone dust.
|
||||||
|
Skull Storm: Screaming skulls rain down causing damage if they hit you.
|
||||||
|
Zombie: Slow moving creatures that have a chance to climb out of the open grave. They are slow moving but deadly. If they hit you you lose a life.
|
||||||
|
|
||||||
|
Bonuses and items
|
||||||
|
|
||||||
|
Bone dust: Currency of the game. Collect it to gain health and extra lives.
|
||||||
|
Hand of Glory: Grants invincibility for a short time.
|
||||||
|
Jack O'lantern: Throw these at your enemies to hit them from a distance.
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"level_id": 1,
|
"level_id": 1,
|
||||||
"name": "The Mausoleum",
|
"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! Use W to jump, A/D to move, and CTRL to attack with your shovel.",
|
"description": "After years of existing as a pile of bones, someone was crazy enough to assemble your skeleton. Time to wreak some havoc!",
|
||||||
"player_start": {
|
"player_start": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0
|
"y": 0
|
||||||
@@ -28,13 +28,15 @@
|
|||||||
"static": true
|
"static": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 25,
|
"x_range": [25, 30],
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"enemy_type": "goblin",
|
"enemy_type": "goblin",
|
||||||
"health": 3,
|
"health": 3,
|
||||||
"damage": 1,
|
"damage": 1,
|
||||||
"attack_range": 1,
|
"attack_range": 1,
|
||||||
"movement_range": 5
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 35,
|
"x": 35,
|
||||||
@@ -64,13 +66,15 @@
|
|||||||
"type": "coffin"
|
"type": "coffin"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 75,
|
"x_range": [75, 80],
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"enemy_type": "goblin",
|
"enemy_type": "goblin",
|
||||||
"health": 5,
|
"health": 5,
|
||||||
"damage": 2,
|
"damage": 2,
|
||||||
"attack_range": 1,
|
"attack_range": 1,
|
||||||
"movement_range": 5
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 85,
|
"x": 85,
|
||||||
@@ -107,10 +111,18 @@
|
|||||||
"direction": -1,
|
"direction": -1,
|
||||||
"fire_interval": 5000,
|
"fire_interval": 5000,
|
||||||
"range": 15
|
"range": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [154, 159],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"boundaries": {
|
"boundaries": {
|
||||||
"left": 0,
|
"left": 0,
|
||||||
"right": 160
|
"right": 160
|
||||||
}
|
},
|
||||||
|
"footstep_sound": "footstep_stone"
|
||||||
}
|
}
|
||||||
|
@@ -21,13 +21,15 @@
|
|||||||
"static": true
|
"static": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 25,
|
"x_range": [21, 29],
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"enemy_type": "goblin",
|
"enemy_type": "goblin",
|
||||||
"health": 4,
|
"health": 4,
|
||||||
"damage": 2,
|
"damage": 2,
|
||||||
"attack_range": 1,
|
"attack_range": 1,
|
||||||
"movement_range": 5
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 35,
|
"x": 35,
|
||||||
@@ -62,13 +64,15 @@
|
|||||||
"static": true
|
"static": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 75,
|
"x_range": [71, 79],
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"enemy_type": "goblin",
|
"enemy_type": "goblin",
|
||||||
"health": 5,
|
"health": 5,
|
||||||
"damage": 2,
|
"damage": 2,
|
||||||
"attack_range": 1,
|
"attack_range": 1,
|
||||||
"movement_range": 6
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"x": 85,
|
"x": 85,
|
||||||
@@ -109,10 +113,18 @@
|
|||||||
"min": 2,
|
"min": 2,
|
||||||
"max": 5
|
"max": 5
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [145, 150],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"boundaries": {
|
"boundaries": {
|
||||||
"left": 0,
|
"left": 0,
|
||||||
"right": 170
|
"right": 170
|
||||||
}
|
},
|
||||||
|
"footstep_sound": "footstep_tall_grass"
|
||||||
}
|
}
|
||||||
|
238
levels/3.json
Normal file
238
levels/3.json
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
{
|
||||||
|
"level_id": 3,
|
||||||
|
"name": "Endless Graves",
|
||||||
|
"description": "Graves continue in all directions as far as you can see. The dead seem restless.",
|
||||||
|
"player_start": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"x_range": [1, 4],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 5,
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coffin",
|
||||||
|
"type": "coffin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [6, 10],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 15,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 15,
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coffin",
|
||||||
|
"type": "coffin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [25, 27],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [21, 31],
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 5,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [35, 50],
|
||||||
|
"y": 12,
|
||||||
|
"type": "skull_storm",
|
||||||
|
"damage": 3,
|
||||||
|
"maximum_skulls": 2,
|
||||||
|
"frequency": {
|
||||||
|
"min": 2,
|
||||||
|
"max": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [42, 44],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 55,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [60, 70],
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 5,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [75, 77],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [71, 81],
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 5,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 85,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 85,
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coffin",
|
||||||
|
"type": "coffin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [95, 120],
|
||||||
|
"y": 15,
|
||||||
|
"type": "skull_storm",
|
||||||
|
"damage": 3,
|
||||||
|
"maximum_skulls": 2,
|
||||||
|
"frequency": {
|
||||||
|
"min": 2,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [105, 107],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [101, 111],
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 6,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 125,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 135,
|
||||||
|
"y": 0,
|
||||||
|
"hazard": true,
|
||||||
|
"sound": "grave",
|
||||||
|
"static": true,
|
||||||
|
"zombie_spawn_chance": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [140, 150],
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "goblin",
|
||||||
|
"health": 6,
|
||||||
|
"damage": 2,
|
||||||
|
"attack_range": 1,
|
||||||
|
"attack_pattern": {
|
||||||
|
"type": "patrol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [155, 157],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [146, 166],
|
||||||
|
"y": 0,
|
||||||
|
"enemy_type": "ghoul",
|
||||||
|
"health": 10,
|
||||||
|
"damage": 3,
|
||||||
|
"attack_range": 2,
|
||||||
|
"attack_pattern": {
|
||||||
|
"type": "hunter",
|
||||||
|
"turn_threshold": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [165, 190],
|
||||||
|
"y": 15,
|
||||||
|
"type": "skull_storm",
|
||||||
|
"damage": 4,
|
||||||
|
"maximum_skulls": 3,
|
||||||
|
"frequency": {
|
||||||
|
"min": 2,
|
||||||
|
"max": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 175,
|
||||||
|
"y": 0,
|
||||||
|
"type": "catapult",
|
||||||
|
"direction": -1,
|
||||||
|
"fire_interval": 4500,
|
||||||
|
"range": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x_range": [173, 176],
|
||||||
|
"y": 3,
|
||||||
|
"sound": "coin",
|
||||||
|
"collectible": true,
|
||||||
|
"static": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"boundaries": {
|
||||||
|
"left": 0,
|
||||||
|
"right": 200
|
||||||
|
},
|
||||||
|
"footstep_sound": "footstep_tall_grass"
|
||||||
|
}
|
BIN
sounds/footstep_stone.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/footstep_stone.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/footstep_tall_grass.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/footstep_tall_grass.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/ghoul.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/ghoul.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/ghoul_dies.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/ghoul_dies.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
sounds/zombie.ogg
(Stored with Git LFS)
BIN
sounds/zombie.ogg
(Stored with Git LFS)
Binary file not shown.
BIN
sounds/zombie_dies.ogg
(Stored with Git LFS)
Normal file
BIN
sounds/zombie_dies.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -23,7 +23,6 @@ class CoffinObject(Object):
|
|||||||
self.is_broken = True
|
self.is_broken = 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)
|
||||||
self.level.player.stats.update_stat('Coffins remaining', -1)
|
|
||||||
|
|
||||||
# Stop the ongoing coffin sound
|
# Stop the ongoing coffin sound
|
||||||
if self.channel:
|
if self.channel:
|
||||||
|
81
src/enemy.py
81
src/enemy.py
@@ -2,7 +2,6 @@ from libstormgames import *
|
|||||||
from src.object import Object
|
from src.object import Object
|
||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
|
|
||||||
class Enemy(Object):
|
class Enemy(Object):
|
||||||
def __init__(self, xRange, y, enemyType, sounds, level, **kwargs):
|
def __init__(self, xRange, y, enemyType, sounds, level, **kwargs):
|
||||||
# Initialize base object properties
|
# Initialize base object properties
|
||||||
@@ -20,16 +19,20 @@ class Enemy(Object):
|
|||||||
self.health = kwargs.get('health', 5) # Default 5 HP
|
self.health = kwargs.get('health', 5) # Default 5 HP
|
||||||
self.damage = kwargs.get('damage', 1) # Default 1 damage
|
self.damage = kwargs.get('damage', 1) # Default 1 damage
|
||||||
self.attackRange = kwargs.get('attack_range', 1) # Default 1 tile range
|
self.attackRange = kwargs.get('attack_range', 1) # Default 1 tile range
|
||||||
self.movementRange = kwargs.get('movement_range', 5) # Default 5 tile patrol
|
|
||||||
self.sounds = sounds # Store reference to game sounds
|
self.sounds = sounds # Store reference to game sounds
|
||||||
|
|
||||||
# Movement and behavior properties
|
# Movement and behavior properties
|
||||||
self.movingRight = True # Initial direction
|
self.movingRight = True # Initial direction
|
||||||
self.movementSpeed = 0.03 # Base speed
|
self.movementSpeed = 0.03 # Base speed
|
||||||
self.patrolStart = self.xRange[0]
|
self.patrolStart = self.xRange[0] # Use xRange directly for patrol boundaries
|
||||||
self.patrolEnd = self.xRange[0] + self.movementRange
|
self.patrolEnd = self.xRange[1]
|
||||||
self.lastAttackTime = 0
|
self.lastAttackTime = 0
|
||||||
self.attackCooldown = 1000 # 1 second between attacks
|
self.attackCooldown = 1000 # 1 second between attacks
|
||||||
|
self._currentX = self.xRange[0] # Initialize current position
|
||||||
|
|
||||||
|
# Attack pattern configuration
|
||||||
|
self.attackPattern = kwargs.get('attack_pattern', {'type': 'patrol'})
|
||||||
|
self.turnThreshold = self.attackPattern.get('turn_threshold', 5)
|
||||||
|
|
||||||
# Enemy type specific adjustments
|
# Enemy type specific adjustments
|
||||||
if enemyType == "zombie":
|
if enemyType == "zombie":
|
||||||
@@ -41,21 +44,69 @@ class Enemy(Object):
|
|||||||
@property
|
@property
|
||||||
def xPos(self):
|
def xPos(self):
|
||||||
"""Current x position"""
|
"""Current x position"""
|
||||||
return self._currentX if hasattr(self, '_currentX') else self.xRange[0]
|
return self._currentX
|
||||||
|
|
||||||
@xPos.setter
|
@xPos.setter
|
||||||
def xPos(self, value):
|
def xPos(self, value):
|
||||||
"""Set current x position"""
|
"""Set current x position"""
|
||||||
self._currentX = value
|
self._currentX = value
|
||||||
|
|
||||||
|
def update_movement(self, player):
|
||||||
|
"""Update enemy movement based on attack pattern"""
|
||||||
|
if self.attackPattern['type'] == 'hunter':
|
||||||
|
# Calculate distance to player
|
||||||
|
distanceToPlayer = player.xPos - self.xPos
|
||||||
|
|
||||||
|
if abs(distanceToPlayer) <= (self.patrolEnd - self.patrolStart): # Use full range
|
||||||
|
# Player is within movement range
|
||||||
|
if self.movingRight:
|
||||||
|
# Moving right
|
||||||
|
if distanceToPlayer < -self.turnThreshold:
|
||||||
|
# Player is too far behind us, turn around
|
||||||
|
self.movingRight = False
|
||||||
|
else:
|
||||||
|
self.xPos += self.movementSpeed
|
||||||
|
else:
|
||||||
|
# Moving left
|
||||||
|
if distanceToPlayer > self.turnThreshold:
|
||||||
|
# Player is too far ahead of us, turn around
|
||||||
|
self.movingRight = True
|
||||||
|
else:
|
||||||
|
self.xPos -= self.movementSpeed
|
||||||
|
|
||||||
|
# Ensure we stay within our range
|
||||||
|
if self.xPos <= self.patrolStart:
|
||||||
|
self.xPos = self.patrolStart
|
||||||
|
self.movingRight = True
|
||||||
|
elif self.xPos >= self.patrolEnd:
|
||||||
|
self.xPos = self.patrolEnd
|
||||||
|
self.movingRight = False
|
||||||
|
else:
|
||||||
|
# Player out of range, return to normal patrol
|
||||||
|
self.patrol_movement()
|
||||||
|
else:
|
||||||
|
# Default patrol behavior
|
||||||
|
self.patrol_movement()
|
||||||
|
|
||||||
|
def patrol_movement(self):
|
||||||
|
"""Standard back-and-forth patrol movement"""
|
||||||
|
if self.movingRight:
|
||||||
|
self.xPos += self.movementSpeed
|
||||||
|
if self.xPos >= self.patrolEnd:
|
||||||
|
self.movingRight = False
|
||||||
|
else:
|
||||||
|
self.xPos -= self.movementSpeed
|
||||||
|
if self.xPos <= self.patrolStart:
|
||||||
|
self.movingRight = True
|
||||||
|
|
||||||
def update(self, currentTime, player):
|
def update(self, currentTime, player):
|
||||||
"""Update enemy position and handle attacks"""
|
"""Update enemy position and handle attacks"""
|
||||||
if not self.isActive or self.health <= 0:
|
if not self.isActive or self.health <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Zombie behavior - always chase player
|
# Handle movement based on enemy type
|
||||||
if self.enemyType == "zombie":
|
if self.enemyType == "zombie":
|
||||||
# Determine direction to player
|
# Zombies always chase player
|
||||||
if player.xPos > self.xPos:
|
if player.xPos > self.xPos:
|
||||||
self.movingRight = True
|
self.movingRight = True
|
||||||
self.xPos += self.movementSpeed
|
self.xPos += self.movementSpeed
|
||||||
@@ -63,20 +114,13 @@ class Enemy(Object):
|
|||||||
self.movingRight = False
|
self.movingRight = False
|
||||||
self.xPos -= self.movementSpeed
|
self.xPos -= self.movementSpeed
|
||||||
else:
|
else:
|
||||||
# Normal patrol behavior for other enemies
|
# Other enemies use their attack pattern
|
||||||
if self.movingRight:
|
self.update_movement(player)
|
||||||
self.xPos += self.movementSpeed
|
|
||||||
if self.xPos >= self.patrolEnd:
|
|
||||||
self.movingRight = False
|
|
||||||
else:
|
|
||||||
self.xPos -= self.movementSpeed
|
|
||||||
if self.xPos <= self.patrolStart:
|
|
||||||
self.movingRight = True
|
|
||||||
|
|
||||||
# Check for attack opportunity
|
# Check for attack opportunity
|
||||||
if self.can_attack(currentTime, player):
|
if self.can_attack(currentTime, player):
|
||||||
self.attack(currentTime, player)
|
self.attack(currentTime, player)
|
||||||
|
|
||||||
def can_attack(self, currentTime, player):
|
def can_attack(self, currentTime, player):
|
||||||
"""Check if enemy can attack player"""
|
"""Check if enemy can attack player"""
|
||||||
# Must have cooled down from last attack
|
# Must have cooled down from last attack
|
||||||
@@ -128,4 +172,3 @@ class Enemy(Object):
|
|||||||
|
|
||||||
# Update stats
|
# Update stats
|
||||||
self.level.player.stats.update_stat('Enemies killed', 1)
|
self.level.player.stats.update_stat('Enemies killed', 1)
|
||||||
self.level.player.stats.update_stat('Enemies remaining', -1)
|
|
||||||
|
@@ -25,6 +25,12 @@ class Level:
|
|||||||
self.rightBoundary = levelData["boundaries"]["right"]
|
self.rightBoundary = levelData["boundaries"]["right"]
|
||||||
self.levelId = levelData["level_id"]
|
self.levelId = levelData["level_id"]
|
||||||
|
|
||||||
|
# Get footstep sound for this level, default to 'footstep' if not specified
|
||||||
|
self.footstepSound = levelData.get("footstep_sound", "footstep")
|
||||||
|
|
||||||
|
# Pass footstep sound to player
|
||||||
|
self.player.set_footstep_sound(self.footstepSound)
|
||||||
|
|
||||||
# Create end of level object at right boundary
|
# Create end of level object at right boundary
|
||||||
endLevel = Object(
|
endLevel = Object(
|
||||||
self.rightBoundary,
|
self.rightBoundary,
|
||||||
|
@@ -23,6 +23,14 @@ class Player:
|
|||||||
self.distanceSinceLastStep = 0
|
self.distanceSinceLastStep = 0
|
||||||
self.stepDistance = 0.5
|
self.stepDistance = 0.5
|
||||||
self.stats = StatTracker()
|
self.stats = StatTracker()
|
||||||
|
self.sounds = sounds
|
||||||
|
|
||||||
|
# Footstep tracking
|
||||||
|
self.distanceSinceLastStep = 0
|
||||||
|
self.stepDistance = 0.8
|
||||||
|
self.lastStepTime = 0
|
||||||
|
self.minStepInterval = 250 # Minimum milliseconds between steps
|
||||||
|
self.footstepSound = "footstep"
|
||||||
|
|
||||||
# Inventory system
|
# Inventory system
|
||||||
self.inventory = []
|
self.inventory = []
|
||||||
@@ -51,6 +59,11 @@ class Player:
|
|||||||
attackDuration=200 # 200ms attack duration
|
attackDuration=200 # 200ms attack duration
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def should_play_footstep(self, currentTime):
|
||||||
|
"""Check if it's time to play a footstep sound"""
|
||||||
|
return (self.distanceSinceLastStep >= self.stepDistance and
|
||||||
|
currentTime - self.lastStepTime >= self.minStepInterval)
|
||||||
|
|
||||||
def update(self, currentTime):
|
def update(self, currentTime):
|
||||||
"""Update player state"""
|
"""Update player state"""
|
||||||
# Check if invincibility has expired
|
# Check if invincibility has expired
|
||||||
@@ -91,6 +104,10 @@ class Player:
|
|||||||
"""Get current max health"""
|
"""Get current max health"""
|
||||||
return self._maxHealth
|
return self._maxHealth
|
||||||
|
|
||||||
|
def set_footstep_sound(self, soundName):
|
||||||
|
"""Set the current footstep sound"""
|
||||||
|
self.footstepSound = soundName
|
||||||
|
|
||||||
def set_health(self, value):
|
def set_health(self, value):
|
||||||
"""Set health and handle death if needed."""
|
"""Set health and handle death if needed."""
|
||||||
if self.isInvincible:
|
if self.isInvincible:
|
||||||
|
@@ -4,9 +4,7 @@ class StatTracker:
|
|||||||
self.total = {
|
self.total = {
|
||||||
'Bone dust': 0,
|
'Bone dust': 0,
|
||||||
'Enemies killed': 0,
|
'Enemies killed': 0,
|
||||||
'Enemies remaining': 0,
|
|
||||||
'Coffins broken': 0,
|
'Coffins broken': 0,
|
||||||
'Coffins remaining': 0,
|
|
||||||
'Items collected': 0,
|
'Items collected': 0,
|
||||||
'Total time': 0
|
'Total time': 0
|
||||||
}
|
}
|
||||||
|
@@ -65,6 +65,14 @@ class WickedQuest:
|
|||||||
player.xPos += currentSpeed
|
player.xPos += currentSpeed
|
||||||
player.facingRight = True
|
player.facingRight = True
|
||||||
|
|
||||||
|
# Handle footsteps
|
||||||
|
if movementDistance > 0 and not player.isJumping:
|
||||||
|
player.distanceSinceLastStep += movementDistance
|
||||||
|
if player.should_play_footstep(currentTime):
|
||||||
|
play_sound(self.sounds[player.footstepSound])
|
||||||
|
player.distanceSinceLastStep = 0
|
||||||
|
player.lastStepTime = currentTime
|
||||||
|
|
||||||
# Status queries
|
# Status queries
|
||||||
if keys[pygame.K_c]:
|
if keys[pygame.K_c]:
|
||||||
speak(f"{player.get_coins()} gbone dust")
|
speak(f"{player.get_coins()} gbone dust")
|
||||||
@@ -84,13 +92,6 @@ class WickedQuest:
|
|||||||
if (keys[pygame.K_LCTRL] or keys[pygame.K_RCTRL]) and player.start_attack(currentTime):
|
if (keys[pygame.K_LCTRL] or keys[pygame.K_RCTRL]) and player.start_attack(currentTime):
|
||||||
play_sound(self.sounds[player.currentWeapon.attackSound])
|
play_sound(self.sounds[player.currentWeapon.attackSound])
|
||||||
|
|
||||||
# Play footstep sounds if moving and not jumping
|
|
||||||
if movementDistance > 0 and not player.isJumping:
|
|
||||||
player.distanceSinceLastStep += movementDistance
|
|
||||||
if player.distanceSinceLastStep >= player.stepDistance:
|
|
||||||
play_sound(self.sounds['footstep'])
|
|
||||||
player.distanceSinceLastStep = 0
|
|
||||||
|
|
||||||
# Handle jumping
|
# Handle jumping
|
||||||
if keys[pygame.K_w] and not player.isJumping:
|
if keys[pygame.K_w] and not player.isJumping:
|
||||||
player.isJumping = True
|
player.isJumping = True
|
||||||
@@ -100,9 +101,10 @@ class WickedQuest:
|
|||||||
# Check if jump should end
|
# Check if jump should end
|
||||||
if player.isJumping and currentTime - player.jumpStartTime >= player.jumpDuration:
|
if player.isJumping and currentTime - player.jumpStartTime >= player.jumpDuration:
|
||||||
player.isJumping = False
|
player.isJumping = False
|
||||||
play_sound(self.sounds['footstep'])
|
play_sound(self.sounds[player.footstepSound]) # Landing sound
|
||||||
# Reset step distance tracking after landing
|
# Reset step distance tracking after landing
|
||||||
player.distanceSinceLastStep = 0
|
player.distanceSinceLastStep = 0
|
||||||
|
player.lastStepTime = currentTime
|
||||||
|
|
||||||
def display_level_stats(self, timeTaken):
|
def display_level_stats(self, timeTaken):
|
||||||
"""Display level completion statistics."""
|
"""Display level completion statistics."""
|
||||||
@@ -214,6 +216,8 @@ class WickedQuest:
|
|||||||
self.player = None # Reset player for new game
|
self.player = None # Reset player for new game
|
||||||
if self.load_level(1):
|
if self.load_level(1):
|
||||||
self.game_loop()
|
self.game_loop()
|
||||||
|
elif choice == "learn_sounds":
|
||||||
|
choice = learn_sounds(self.sounds)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Reference in New Issue
Block a user