From 56a78aa4ff703826cdd48775cffb0668a39fdff9 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 7 Sep 2025 13:20:49 -0400 Subject: [PATCH] Weapons now do different things. Shovels fill in graves, broom gives a speed and jump bonus and nunchucks just thwack things real hard and have best reach. --- src/grave.py | 32 ++++++++++++++++++++++++++++++-- src/level.py | 13 ++++++++++++- src/player.py | 32 ++++++++++++++++++++++++-------- src/save_manager.py | 17 +++++++++++++++-- src/weapon.py | 8 ++++++-- wicked_quest.py | 2 +- 6 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/grave.py b/src/grave.py index 2f24d16..105bd66 100644 --- a/src/grave.py +++ b/src/grave.py @@ -16,6 +16,7 @@ class GraveObject(Object): ) self.graveItem = item self.isCollected = False # Renamed to match style of isHazard, isStatic etc + self.isFilled = False # Track if grave has been filled with shovel self.sounds = sounds def collect_grave_item(self, player): @@ -28,11 +29,38 @@ class GraveObject(Object): if not self.graveItem or self.isCollected: return False - # Collect the item if player is ducking - if player.isDucking: + # Collect the item if player is ducking, walking (not running), and wielding shovel + if (player.isDucking and not player.isRunning and + player.currentWeapon and player.currentWeapon.name == "rusty_shovel"): self.isCollected = True # Mark as collected when collection succeeds return True return False + def can_fill_grave(self, player): + """Check if grave can be filled with shovel. + + Returns: + bool: True if grave can be filled, False otherwise + """ + # Can only fill empty graves (no item) that haven't been filled yet + if self.graveItem or self.isFilled: + return False + + # Must be ducking, walking (not running), and wielding shovel + return (player.isDucking and not player.isRunning and + player.currentWeapon and player.currentWeapon.name == "rusty_shovel") + + def fill_grave(self, player): + """Fill the grave with dirt using shovel. + + Returns: + bool: True if grave was filled successfully + """ + if self.can_fill_grave(player): + self.isFilled = True + self.isHazard = False # No longer a hazard once filled + return True + return False + diff --git a/src/level.py b/src/level.py index 322a59f..2cbf622 100644 --- a/src/level.py +++ b/src/level.py @@ -417,9 +417,10 @@ class Level: if obj.isHazard and not self.player.isJumping: if isinstance(obj, GraveObject): can_collect = obj.collect_grave_item(self.player) + can_fill = obj.can_fill_grave(self.player) if can_collect: - # Successfully collected item while ducking + # Successfully collected item while ducking with shovel play_sound(self.sounds[f'get_{obj.graveItem}']) self.player.stats.update_stat('Items collected', 1) # Create PowerUp to handle the item effect @@ -434,6 +435,16 @@ class Level: obj.channel = None obj.isActive = False # Mark the grave as inactive after collection continue + elif can_fill and obj.fill_grave(self.player): + # Successfully filled empty grave with shovel + play_sound(self.sounds.get('shovel_dig', obj.soundName)) # Use dig sound if available + self.player.stats.update_stat('Graves filled', 1) + # Stop grave's current audio channel + if obj.channel: + obj_stop(obj.channel) + obj.channel = None + obj.isActive = False # Mark grave as inactive after filling + continue elif not self.player.isInvincible: # Kill player for normal graves or non-ducking collision play_sound(self.sounds[obj.soundName]) diff --git a/src/player.py b/src/player.py index 9ef4da1..d0a2249 100644 --- a/src/player.py +++ b/src/player.py @@ -158,15 +158,23 @@ class Player: def get_step_distance(self): """Get step distance based on current speed""" + weaponBonus = self.currentWeapon.speedBonus if self.currentWeapon else 1.0 + totalMultiplier = weaponBonus + if self.isRunning or self.isJumping: - return self.baseStepDistance / self.runMultiplier - return self.baseStepDistance + totalMultiplier *= self.runMultiplier + + return self.baseStepDistance / totalMultiplier def get_step_interval(self): """Get minimum time between steps based on current speed""" + weaponBonus = self.currentWeapon.speedBonus if self.currentWeapon else 1.0 + totalMultiplier = weaponBonus + if self.isRunning or self.isJumping: - return self.baseStepInterval / self.runMultiplier - return self.baseStepInterval + totalMultiplier *= self.runMultiplier + + return self.baseStepInterval / totalMultiplier def get_health(self): """Get current health""" @@ -181,10 +189,18 @@ class Player: self._health = self._maxHealth def get_current_speed(self): - """Calculate current speed based on state""" + """Calculate current speed based on state and weapon""" baseSpeed = self.moveSpeed - if self.isJumping or self.isRunning: return baseSpeed * self.runMultiplier - return baseSpeed + weaponBonus = self.currentWeapon.speedBonus if self.currentWeapon else 1.0 + + if self.isJumping or self.isRunning: + return baseSpeed * self.runMultiplier * weaponBonus + return baseSpeed * weaponBonus + + def get_current_jump_duration(self): + """Calculate current jump duration based on weapon bonus""" + weaponBonus = self.currentWeapon.jumpDurationBonus if self.currentWeapon else 1.0 + return int(self.jumpDuration * weaponBonus) def set_footstep_sound(self, soundName): """Set the current footstep sound""" @@ -269,7 +285,7 @@ class Player: for weapon in self.weapons: if weapon.name == targetWeaponName: self.equip_weapon(weapon) - speak(f"Switched to {weapon.name.replace('_', ' ')}") + speak(weapon.name.replace('_', ' ')) return True # Weapon not found in inventory diff --git a/src/save_manager.py b/src/save_manager.py index a8fd17c..3110e53 100644 --- a/src/save_manager.py +++ b/src/save_manager.py @@ -100,7 +100,9 @@ class SaveManager: 'range': weapon.range, 'attackSound': weapon.attackSound, 'hitSound': weapon.hitSound, - 'attackDuration': weapon.attackDuration + 'attackDuration': weapon.attackDuration, + 'speedBonus': getattr(weapon, 'speedBonus', 1.0), + 'jumpDurationBonus': getattr(weapon, 'jumpDurationBonus', 1.0) }) return serialized @@ -109,13 +111,24 @@ class SaveManager: from src.weapon import Weapon weapons = [] for data in weapon_data: + # Handle backward compatibility for old saves + speedBonus = data.get('speedBonus', 1.0) + jumpDurationBonus = data.get('jumpDurationBonus', 1.0) + + # For old saves, restore proper bonuses for specific weapons + if data['name'] == 'witch_broom' and speedBonus == 1.0: + speedBonus = 1.17 + jumpDurationBonus = 1.25 + weapon = Weapon( name=data['name'], damage=data['damage'], range=data['range'], attackSound=data['attackSound'], hitSound=data['hitSound'], - attackDuration=data['attackDuration'] + attackDuration=data['attackDuration'], + speedBonus=speedBonus, + jumpDurationBonus=jumpDurationBonus ) weapons.append(weapon) return weapons diff --git a/src/weapon.py b/src/weapon.py index 09464fb..8f347d8 100644 --- a/src/weapon.py +++ b/src/weapon.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- class Weapon: - def __init__(self, name, damage, range, attackSound, hitSound, cooldown=500, attackDuration=200): + def __init__(self, name, damage, range, attackSound, hitSound, cooldown=500, attackDuration=200, speedBonus=1.0, jumpDurationBonus=1.0): self.name = name self.damage = damage self.range = range # Range in tiles @@ -9,6 +9,8 @@ class Weapon: self.hitSound = hitSound self.cooldown = cooldown # Milliseconds between attacks self.attackDuration = attackDuration # Milliseconds the attack is active + self.speedBonus = speedBonus # Speed multiplier when wielding this weapon + self.jumpDurationBonus = jumpDurationBonus # Jump duration multiplier when wielding this weapon self.lastAttackTime = 0 self.hitEnemies = set() @@ -35,7 +37,9 @@ class Weapon: attackSound="player_broom_attack", hitSound="player_broom_hit", cooldown=500, - attackDuration=200 + attackDuration=200, + speedBonus=1.17, # 17% speed bonus when wielding the broom + jumpDurationBonus=1.25 # 25% longer jump duration for better traversal ) def can_attack(self, currentTime): diff --git a/wicked_quest.py b/wicked_quest.py index 1091f0a..c15b710 100755 --- a/wicked_quest.py +++ b/wicked_quest.py @@ -242,7 +242,7 @@ class WickedQuest: play_sound(self.sounds['jump']) # Check if jump should end - if player.isJumping and currentTime - player.jumpStartTime >= player.jumpDuration: + if player.isJumping and currentTime - player.jumpStartTime >= player.get_current_jump_duration(): player.isJumping = False play_sound(self.sounds[player.footstepSound]) # Landing sound # Reset step distance tracking after landing