from typing import Any from dataclasses import dataclass from items import Weapon from game import d, Roll from character import Character @dataclass class Attack: attacker: Character target: Any weapon: Weapon strike_type="melee" previous_attacks: int = 0 distance: int = 0 def bonus(self): return self.attacker.attack_bonus(self.weapon, self.strike_type) + self.map() def roll(self): ac = self.target.ac() return d(20).apply(Roll.check, ac, self.bonus()) def strike(self): def strike_dmg(check): return self.damage(check) return self.roll().apply(strike_dmg) def damage(self, check=Roll.Success): persistant_damage_factor = 3 + 1/3 wp = self.weapon dmg_bonus = self.attacker.attack_damage_bonus(wp, self.strike_type) if wp.backstabber: dmg_bonus += 1 if wp.potency >= 3: dmg_bonus += 1 if wp.twin and self.previous_attacks > 0: dmg_bonus += wp.dice_count() if check == Roll.Success: return wp.dice_count() * d(wp.main_die) + dmg_bonus if check == Roll.Crit: dmg = None if wp.fatal is not None and wp.fatal > 0: dmg = (wp.dice_count() + 1) * d(wp.fatal) else: dmg = wp.dice_count() * d(wp.main_die) dmg = 2 * (dmg+dmg_bonus) if wp.deadly is not None and wp.deadly > 0: dmg += max(1, wp.dice_count()-1) * d(wp.deadly) if wp.group == "crossbow": dmg += persistant_damage_factor * d(8).expected() elif wp.group == "dart" or wp.group == "knife": dmg += persistant_damage_factor * d(6).expected() elif wp.group == "dagger": dmg += persistant_damage_factor * d(6).expected() elif wp.group == "pick": dmg += 2 * wp.dice_count() return dmg return 0 def range_penalty(self): return 0 def map(self): factor = max(0, min(2, self.previous_attacks)) if self.weapon.agile: return factor * -4 return factor * -5