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:
@@ -23,7 +23,6 @@ class CoffinObject(Object):
|
||||
self.is_broken = True
|
||||
play_sound(self.sounds['coffin_shatter'])
|
||||
self.level.player.stats.update_stat('Coffins broken', 1)
|
||||
self.level.player.stats.update_stat('Coffins remaining', -1)
|
||||
|
||||
# Stop the ongoing coffin sound
|
||||
if self.channel:
|
||||
|
||||
81
src/enemy.py
81
src/enemy.py
@@ -2,7 +2,6 @@ from libstormgames import *
|
||||
from src.object import Object
|
||||
import pygame
|
||||
|
||||
|
||||
class Enemy(Object):
|
||||
def __init__(self, xRange, y, enemyType, sounds, level, **kwargs):
|
||||
# Initialize base object properties
|
||||
@@ -20,16 +19,20 @@ class Enemy(Object):
|
||||
self.health = kwargs.get('health', 5) # Default 5 HP
|
||||
self.damage = kwargs.get('damage', 1) # Default 1 damage
|
||||
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
|
||||
|
||||
# Movement and behavior properties
|
||||
self.movingRight = True # Initial direction
|
||||
self.movementSpeed = 0.03 # Base speed
|
||||
self.patrolStart = self.xRange[0]
|
||||
self.patrolEnd = self.xRange[0] + self.movementRange
|
||||
self.patrolStart = self.xRange[0] # Use xRange directly for patrol boundaries
|
||||
self.patrolEnd = self.xRange[1]
|
||||
self.lastAttackTime = 0
|
||||
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
|
||||
if enemyType == "zombie":
|
||||
@@ -41,21 +44,69 @@ class Enemy(Object):
|
||||
@property
|
||||
def xPos(self):
|
||||
"""Current x position"""
|
||||
return self._currentX if hasattr(self, '_currentX') else self.xRange[0]
|
||||
return self._currentX
|
||||
|
||||
@xPos.setter
|
||||
def xPos(self, value):
|
||||
"""Set current x position"""
|
||||
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):
|
||||
"""Update enemy position and handle attacks"""
|
||||
if not self.isActive or self.health <= 0:
|
||||
return
|
||||
|
||||
# Zombie behavior - always chase player
|
||||
# Handle movement based on enemy type
|
||||
if self.enemyType == "zombie":
|
||||
# Determine direction to player
|
||||
# Zombies always chase player
|
||||
if player.xPos > self.xPos:
|
||||
self.movingRight = True
|
||||
self.xPos += self.movementSpeed
|
||||
@@ -63,20 +114,13 @@ class Enemy(Object):
|
||||
self.movingRight = False
|
||||
self.xPos -= self.movementSpeed
|
||||
else:
|
||||
# Normal patrol behavior for other enemies
|
||||
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
|
||||
# Other enemies use their attack pattern
|
||||
self.update_movement(player)
|
||||
|
||||
# Check for attack opportunity
|
||||
if self.can_attack(currentTime, player):
|
||||
self.attack(currentTime, player)
|
||||
|
||||
|
||||
def can_attack(self, currentTime, player):
|
||||
"""Check if enemy can attack player"""
|
||||
# Must have cooled down from last attack
|
||||
@@ -128,4 +172,3 @@ class Enemy(Object):
|
||||
|
||||
# Update stats
|
||||
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.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
|
||||
endLevel = Object(
|
||||
self.rightBoundary,
|
||||
|
||||
@@ -23,6 +23,14 @@ class Player:
|
||||
self.distanceSinceLastStep = 0
|
||||
self.stepDistance = 0.5
|
||||
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
|
||||
self.inventory = []
|
||||
@@ -51,6 +59,11 @@ class Player:
|
||||
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):
|
||||
"""Update player state"""
|
||||
# Check if invincibility has expired
|
||||
@@ -91,6 +104,10 @@ class Player:
|
||||
"""Get current max health"""
|
||||
return self._maxHealth
|
||||
|
||||
def set_footstep_sound(self, soundName):
|
||||
"""Set the current footstep sound"""
|
||||
self.footstepSound = soundName
|
||||
|
||||
def set_health(self, value):
|
||||
"""Set health and handle death if needed."""
|
||||
if self.isInvincible:
|
||||
|
||||
@@ -4,9 +4,7 @@ class StatTracker:
|
||||
self.total = {
|
||||
'Bone dust': 0,
|
||||
'Enemies killed': 0,
|
||||
'Enemies remaining': 0,
|
||||
'Coffins broken': 0,
|
||||
'Coffins remaining': 0,
|
||||
'Items collected': 0,
|
||||
'Total time': 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user