Combat Backend & Data Models

- Implement Combat Service
- Implement Damage Calculator
- Implement Effect Processor
- Implement Combat Actions
- Created Combat API Endpoints
This commit is contained in:
2025-11-26 15:43:20 -06:00
parent 30c3b800e6
commit 03ab783eeb
22 changed files with 9091 additions and 5 deletions

View File

@@ -65,6 +65,12 @@ class Item:
crit_chance: float = 0.05 # 5% default critical hit chance
crit_multiplier: float = 2.0 # 2x damage on critical hit
# Elemental weapon properties (for split damage like Fire Sword)
# These enable weapons to deal both physical AND elemental damage
elemental_damage_type: Optional[DamageType] = None # Secondary damage type (fire, ice, etc.)
physical_ratio: float = 1.0 # Portion of damage that is physical (0.0-1.0)
elemental_ratio: float = 0.0 # Portion of damage that is elemental (0.0-1.0)
# Armor-specific
defense: int = 0
resistance: int = 0
@@ -89,6 +95,27 @@ class Item:
"""Check if this item is a quest item."""
return self.item_type == ItemType.QUEST_ITEM
def is_elemental_weapon(self) -> bool:
"""
Check if this weapon deals elemental damage (split damage).
Elemental weapons deal both physical AND elemental damage,
calculated separately against DEF and RES.
Examples:
Fire Sword: 70% physical / 30% fire
Frost Blade: 60% physical / 40% ice
Lightning Spear: 50% physical / 50% lightning
Returns:
True if weapon has elemental damage component
"""
return (
self.is_weapon() and
self.elemental_ratio > 0.0 and
self.elemental_damage_type is not None
)
def can_equip(self, character_level: int, character_class: Optional[str] = None) -> bool:
"""
Check if a character can equip this item.
@@ -133,6 +160,8 @@ class Item:
data["item_type"] = self.item_type.value
if self.damage_type:
data["damage_type"] = self.damage_type.value
if self.elemental_damage_type:
data["elemental_damage_type"] = self.elemental_damage_type.value
data["effects_on_use"] = [effect.to_dict() for effect in self.effects_on_use]
return data
@@ -150,6 +179,11 @@ class Item:
# Convert string values back to enums
item_type = ItemType(data["item_type"])
damage_type = DamageType(data["damage_type"]) if data.get("damage_type") else None
elemental_damage_type = (
DamageType(data["elemental_damage_type"])
if data.get("elemental_damage_type")
else None
)
# Deserialize effects
effects = []
@@ -169,6 +203,9 @@ class Item:
damage_type=damage_type,
crit_chance=data.get("crit_chance", 0.05),
crit_multiplier=data.get("crit_multiplier", 2.0),
elemental_damage_type=elemental_damage_type,
physical_ratio=data.get("physical_ratio", 1.0),
elemental_ratio=data.get("elemental_ratio", 0.0),
defense=data.get("defense", 0),
resistance=data.get("resistance", 0),
required_level=data.get("required_level", 1),
@@ -178,6 +215,12 @@ class Item:
def __repr__(self) -> str:
"""String representation of the item."""
if self.is_weapon():
if self.is_elemental_weapon():
return (
f"Item({self.name}, elemental_weapon, dmg={self.damage}, "
f"phys={self.physical_ratio:.0%}/{self.elemental_damage_type.value}={self.elemental_ratio:.0%}, "
f"crit={self.crit_chance*100:.0f}%, value={self.value}g)"
)
return (
f"Item({self.name}, weapon, dmg={self.damage}, "
f"crit={self.crit_chance*100:.0f}%, value={self.value}g)"