""" Character statistics data model. This module defines the Stats dataclass which represents a character's core attributes and provides computed properties for derived values like HP and MP. """ from dataclasses import dataclass, field, asdict from typing import Dict, Any @dataclass class Stats: """ Character statistics representing core attributes. Attributes: strength: Physical power, affects melee damage dexterity: Agility and precision, affects initiative and evasion constitution: Endurance and health, affects HP and defense intelligence: Magical power, affects spell damage and MP wisdom: Perception and insight, affects magical resistance charisma: Social influence, affects NPC interactions luck: Fortune and fate, affects critical hits, loot, and random outcomes Computed Properties: hit_points: Maximum HP = 10 + (constitution × 2) mana_points: Maximum MP = 10 + (intelligence × 2) defense: Physical defense = constitution // 2 resistance: Magical resistance = wisdom // 2 """ strength: int = 10 dexterity: int = 10 constitution: int = 10 intelligence: int = 10 wisdom: int = 10 charisma: int = 10 luck: int = 8 @property def hit_points(self) -> int: """ Calculate maximum hit points based on constitution. Formula: 10 + (constitution × 2) Returns: Maximum HP value """ return 10 + (self.constitution * 2) @property def mana_points(self) -> int: """ Calculate maximum mana points based on intelligence. Formula: 10 + (intelligence × 2) Returns: Maximum MP value """ return 10 + (self.intelligence * 2) @property def defense(self) -> int: """ Calculate physical defense from constitution. Formula: constitution // 2 Returns: Physical defense value (damage reduction) """ return self.constitution // 2 @property def resistance(self) -> int: """ Calculate magical resistance from wisdom. Formula: wisdom // 2 Returns: Magical resistance value (spell damage reduction) """ return self.wisdom // 2 @property def crit_bonus(self) -> float: """ Calculate critical hit chance bonus from luck. Formula: luck * 0.5% (0.005) This bonus is added to the weapon's base crit chance. The total crit chance is capped at 25% in the DamageCalculator. Returns: Crit chance bonus as a decimal (e.g., 0.04 for LUK 8) Examples: LUK 8: 0.04 (4% bonus) LUK 12: 0.06 (6% bonus) """ return self.luck * 0.005 @property def hit_bonus(self) -> float: """ Calculate hit chance bonus (miss reduction) from luck. Formula: luck * 0.5% (0.005) This reduces the base 10% miss chance. The minimum miss chance is hard capped at 5% to prevent frustration. Returns: Miss reduction as a decimal (e.g., 0.04 for LUK 8) Examples: LUK 8: 0.04 (reduces miss from 10% to 6%) LUK 12: 0.06 (reduces miss from 10% to 4%, capped at 5%) """ return self.luck * 0.005 @property def lucky_roll_chance(self) -> float: """ Calculate chance for a "lucky" high damage variance roll. Formula: 5% + (luck * 0.25%) When triggered, damage variance uses 100%-110% instead of 95%-105%. This gives LUK characters more frequent high damage rolls. Returns: Lucky roll chance as a decimal Examples: LUK 8: 0.07 (7% chance for lucky roll) LUK 12: 0.08 (8% chance for lucky roll) """ return 0.05 + (self.luck * 0.0025) def to_dict(self) -> Dict[str, Any]: """ Serialize stats to a dictionary. Returns: Dictionary containing all stat values """ return asdict(self) @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'Stats': """ Deserialize stats from a dictionary. Args: data: Dictionary containing stat values Returns: Stats instance """ return cls( strength=data.get("strength", 10), dexterity=data.get("dexterity", 10), constitution=data.get("constitution", 10), intelligence=data.get("intelligence", 10), wisdom=data.get("wisdom", 10), charisma=data.get("charisma", 10), luck=data.get("luck", 8), ) def copy(self) -> 'Stats': """ Create a deep copy of this Stats instance. Returns: New Stats instance with same values """ return Stats( strength=self.strength, dexterity=self.dexterity, constitution=self.constitution, intelligence=self.intelligence, wisdom=self.wisdom, charisma=self.charisma, luck=self.luck, ) def __repr__(self) -> str: """String representation showing all stats and computed properties.""" return ( f"Stats(STR={self.strength}, DEX={self.dexterity}, " f"CON={self.constitution}, INT={self.intelligence}, " f"WIS={self.wisdom}, CHA={self.charisma}, LUK={self.luck}, " f"HP={self.hit_points}, MP={self.mana_points}, " f"DEF={self.defense}, RES={self.resistance}, " f"CRIT_BONUS={self.crit_bonus:.1%}, HIT_BONUS={self.hit_bonus:.1%})" )