diff --git a/dungeonsheets/character.py b/dungeonsheets/character.py index 5a541c6..acc18fa 100644 --- a/dungeonsheets/character.py +++ b/dungeonsheets/character.py @@ -617,30 +617,17 @@ class Character(): """ # Retrieve the weapon class from the weapons module if isinstance(weapon, weapons.Weapon): - weapon_ = type(weapon)() + weapon_ = type(weapon)(wielder=self) elif isinstance(weapon, str): try: NewWeapon = findattr(weapons, weapon) except AttributeError: raise AttributeError(f'Weapon "{weapon}" is not defined') - weapon_ = NewWeapon() + weapon_ = NewWeapon(wielder=self) elif issubclass(weapon, weapons.Weapon): - weapon_ = weapon() + weapon_ = weapon(wielder=self) else: raise AttributeError(f'Weapon "{weapon}" is not defined') - # check if features add any bonuses - for f in self.features: - weapon_ = f.weapon_func(weapon_) - # Set weapon attributes based on character - if weapon_.is_finesse: - ability_mod = max(self.strength.modifier, self.dexterity.modifier) - else: - ability_mod = getattr(self, weapon_.ability).modifier - weapon_.attack_bonus += ability_mod - weapon_.bonus_damage += ability_mod - # Check for prifiency - if self.is_proficient(weapon_): - weapon_.attack_bonus += self.proficiency_bonus # Save it to the array self.weapons.append(weapon_) diff --git a/dungeonsheets/features/features.py b/dungeonsheets/features/features.py index cfe2448..5d42072 100644 --- a/dungeonsheets/features/features.py +++ b/dungeonsheets/features/features.py @@ -59,14 +59,8 @@ class Feature(): The weapon to be tested for special bonuses kwargs Any other key-word arguments the function may require - - Returns - ------- - weapon - Updated weapon (perhaps changed damage bonus, etc.) - """ - return weapon + pass class FeatureSelector(Feature): diff --git a/dungeonsheets/features/monk.py b/dungeonsheets/features/monk.py index 0caa526..9be1736 100644 --- a/dungeonsheets/features/monk.py +++ b/dungeonsheets/features/monk.py @@ -64,7 +64,6 @@ class MartialArts(Feature): if int(self.die[1:]) > int(weapon.base_damage.split('d')[-1]): weapon.base_damage = '1' + str(self.die) weapon.is_finesse = True - return weapon class Ki(Feature): diff --git a/dungeonsheets/features/ranger.py b/dungeonsheets/features/ranger.py index ab4fcd8..7e2d024 100644 --- a/dungeonsheets/features/ranger.py +++ b/dungeonsheets/features/ranger.py @@ -72,7 +72,6 @@ class Archery(Feature): """ if isinstance(weapon, weapons.RangedWeapon): weapon.attack_bonus += 2 - return weapon class Defense(Feature): @@ -98,8 +97,7 @@ class Dueling(Feature): """ if (isinstance(weapon, weapons.MeleeWeapon) and "two-handed" not in weapon.properties.lower()): - weapon.bonus_damage += 2 - return weapon + weapon.damage_bonus += 2 class TwoWeaponFighting(Feature): diff --git a/dungeonsheets/features/warlock.py b/dungeonsheets/features/warlock.py index f4d755e..46b2f65 100644 --- a/dungeonsheets/features/warlock.py +++ b/dungeonsheets/features/warlock.py @@ -468,14 +468,13 @@ class HexWarrior(Feature): it is higher than STR/DEX bonus """ if weapon.is_finesse: - existing_mod = max(self.owner.strength.modifier, - self.owner.dexterity.modifier) + abils = {'strength': self.owner.strength.modifier, + 'dexterity': self.owner.dexterity.modifier, + 'charisma': self.owner.charisma.modifier} else: - existing_mod = self.owner.strength.modifier - cha_mod = self.owner.charisma.modifier - if cha_mod > existing_mod: - weapon.attack_bonus += (cha_mod - existing_mod) - return weapon + abils = {weapon.ability: getattr(self.owner, weapon.ability).modifier, + 'charisma': self.owner.charisma.modifier} + weapon.ability = max(abils, key=abils.get) class AccursedSpecter(Feature): @@ -958,11 +957,9 @@ class ImprovedPactWeapon(Invocation): """ Add +1 to attack and damage if magic is not already magic """ - if weapon.magic_bonus <= 1: - weapon.magic_bonus = 1 + if (weapon.attack_bonus == 0) or (weapon.bonus_damage == 0): weapon.attack_bonus += 1 weapon.bonus_damage += 1 - return weapon class LanceOfLethargy(Invocation): diff --git a/dungeonsheets/make_sheets.py b/dungeonsheets/make_sheets.py index ab8cec7..d722a50 100644 --- a/dungeonsheets/make_sheets.py +++ b/dungeonsheets/make_sheets.py @@ -308,7 +308,7 @@ def create_character_pdf(character, basename, flatten=False): for _fields, weapon in zip(weapon_fields, character.weapons): name_field, atk_field, dmg_field = _fields fields[name_field] = weapon.name - fields[atk_field] = '{:+d}'.format(weapon.attack_bonus) + fields[atk_field] = '{:+d}'.format(weapon.attack_modifier) fields[dmg_field] = f'{weapon.damage}/{weapon.damage_type}' # Other attack information attack_str = f'Armor: {character.armor}' diff --git a/dungeonsheets/weapons.py b/dungeonsheets/weapons.py index 3c7de29..e546d02 100644 --- a/dungeonsheets/weapons.py +++ b/dungeonsheets/weapons.py @@ -2,24 +2,54 @@ class Weapon(): name = "" cost = "0 gp" base_damage = "1d4" - bonus_damage = 0 - damage_type = "p" + damage_bonus = 0 attack_bonus = 0 - magic_bonus = 0 + damage_type = "p" weight = 1 # In lbs - properties = "Light" + properties = "" ability = 'strength' is_finesse = False + features_applied = False - def __init__(self): - self.attack_bonus += self.magic_bonus - self.bonus_damage += self.magic_bonus + def __init__(self, wielder=None): + self.wielder = wielder + + def apply_features(self): + if (not self.features_applied) and (self.wielder is not None): + self.features_applied = True + for f in self.wielder.features: + f.weapon_func(self) + + @property + def ability_mod(self): + if self.wielder is None: + return 0 + else: + if self.is_finesse: + return max(self.wielder.strength.modifier, + self.wielder.dexterity.modifier) + else: + return getattr(self.wielder, self.ability).modifier + + @property + def attack_modifier(self): + self.apply_features() + mod = self.attack_bonus + if self.wielder is not None: + mod += self.ability_mod + if self.wielder.is_proficient(self): + mod += self.wielder.proficiency_bonus + return mod @property def damage(self): + self.apply_features() dam_str = str(self.base_damage) - if self.bonus_damage != 0: - dam_str += '{:+d}'.format(self.bonus_damage) + bonus = self.damage_bonus + if self.wielder is not None: + bonus += self.ability_mod + if bonus != 0: + dam_str += '{:+d}'.format(bonus) return dam_str def __str__(self): @@ -31,10 +61,12 @@ class Weapon(): class MeleeWeapon(Weapon): name = "Melee Weapons" + ability = 'strength' class RangedWeapon(Weapon): name = "Ranged Weapons" + ability = 'dexterity' class SimpleWeapon(Weapon): @@ -523,7 +555,8 @@ class Musket(Firearm): # Magic Items class FlameTongue(Greatsword): name = "Flame Tongue +1" - magic_bonus = 1 + damage_bonus = 1 + attack_bonus = 1 base_damage = "4d6" damage_type = 'f' diff --git a/tests/test_character.py b/tests/test_character.py index e43389e..efe7433 100644 --- a/tests/test_character.py +++ b/tests/test_character.py @@ -48,21 +48,21 @@ class TestCharacter(TestCase): sword = char.weapons[0] self.assertTrue(isinstance(sword, Weapon)) self.assertTrue(isinstance(sword, Shortsword)) - self.assertEqual(sword.attack_bonus, 4) # str + prof - self.assertEqual(sword.bonus_damage, 2) # str + self.assertEqual(sword.attack_modifier, 4) # str + prof + self.assertEqual(sword.damage, '1d6+2') # str # Check if dexterity is used if it's higher (Finesse weapon) char.weapons = [] char.dexterity = 16 char.wield_weapon('shortsword') sword = char.weapons[0] - self.assertEqual(sword.attack_bonus, 5) # dex + prof + self.assertEqual(sword.attack_modifier, 5) # dex + prof # Check if race weapon proficiencies are considered char.weapons = [] char.weapon_proficiencies = [] char.race = race.HighElf() char.wield_weapon('shortsword') sword = char.weapons[0] - self.assertEqual(sword.attack_bonus, 5) + self.assertEqual(sword.attack_modifier, 5) def test_str(self): char = Wizard(name="Inara") diff --git a/tests/test_weapon.py b/tests/test_weapon.py index 881f7cb..4aad746 100644 --- a/tests/test_weapon.py +++ b/tests/test_weapon.py @@ -9,5 +9,5 @@ class WeaponTestCase(unittest.TestCase): weapon.base_damage = '1d6' self.assertEqual(weapon.damage, '1d6') # Now add some bonus damage - weapon.bonus_damage = 2 + weapon.damage_bonus = 2 self.assertEqual(weapon.damage, '1d6+2')