Source code for simulation_framework.src.systems.combat_resolver

from __future__ import annotations

import random
from typing import TYPE_CHECKING, Dict, Tuple

if TYPE_CHECKING:
    from ..entities.base import Entity


[docs] class DamageType: PHYSICAL = "physical" MAGICAL = "magical" FIRE = "fire" ICE = "ice" POISON = "poison" HOLY = "holy" DARK = "dark"
[docs] class CombatResolver: def __init__(self): self.damage_type_effectiveness = { DamageType.FIRE: {DamageType.ICE: 1.5, DamageType.FIRE: 0.5}, DamageType.ICE: {DamageType.FIRE: 1.5, DamageType.ICE: 0.5}, DamageType.HOLY: {DamageType.DARK: 1.5, DamageType.HOLY: 0.5}, DamageType.DARK: {DamageType.HOLY: 1.5, DamageType.DARK: 0.5}, }
[docs] def calculate_damage( self, attacker: Entity, defender: Entity, base_damage: int, damage_type: str = DamageType.PHYSICAL, critical_chance: float = 0.0, critical_multiplier: float = 2.0, ) -> Tuple[int, bool, Dict]: attack_power = attacker.stats.attack_power defense = defender.stats.defense raw_damage = base_damage + attack_power effectiveness = self._get_damage_effectiveness(damage_type, defender) raw_damage = int(raw_damage * effectiveness) mitigated_damage = max(1, raw_damage - defense) is_critical = random.random() < critical_chance if is_critical: mitigated_damage = int(mitigated_damage * critical_multiplier) final_damage = self._apply_random_variance(mitigated_damage) damage_info = { "base_damage": base_damage, "attack_power": attack_power, "raw_damage": raw_damage, "defense": defense, "effectiveness": effectiveness, "damage_type": damage_type, "final_damage": final_damage, } return final_damage, is_critical, damage_info
def _get_damage_effectiveness(self, damage_type: str, defender: Entity) -> float: resistances = getattr(defender, "resistances", {}) weaknesses = getattr(defender, "weaknesses", {}) effectiveness = 1.0 if damage_type in resistances: effectiveness *= 1.0 - resistances[damage_type] if damage_type in weaknesses: effectiveness *= 1.0 + weaknesses[damage_type] base_effectiveness = self.damage_type_effectiveness.get(damage_type, {}) for defender_type in getattr(defender, "damage_types", []): if defender_type in base_effectiveness: effectiveness *= base_effectiveness[defender_type] return max(0.1, min(3.0, effectiveness)) def _apply_random_variance(self, damage: int, variance: float = 0.1) -> int: min_damage = int(damage * (1.0 - variance)) max_damage = int(damage * (1.0 + variance)) return random.randint(min_damage, max_damage)
[docs] def calculate_hit_chance( self, attacker: Entity, defender: Entity, base_accuracy: float = 0.9, range_penalty: float = 0.0, ) -> float: attacker_skill = getattr(attacker, "combat_skill", 0) defender_evasion = getattr(defender, "evasion_skill", 0) skill_modifier = (attacker_skill - defender_evasion) * 0.01 hit_chance = base_accuracy + skill_modifier - range_penalty hit_chance = max(0.05, min(0.95, hit_chance)) return hit_chance
[docs] def resolve_attack( self, attacker: Entity, defender: Entity, weapon_damage: int, damage_type: str = DamageType.PHYSICAL, critical_chance: float = 0.1, critical_multiplier: float = 2.0, base_accuracy: float = 0.9, attack_range: float = 1.0, ) -> Dict: distance = attacker.distance_to(defender) range_penalty = max(0, (distance - attack_range) * 0.1) hit_chance = self.calculate_hit_chance( attacker, defender, base_accuracy, range_penalty ) hit = random.random() < hit_chance result = { "hit": hit, "distance": distance, "hit_chance": hit_chance, "damage": 0, "is_critical": False, "damage_info": {}, "target_died": False, } if hit: damage, is_critical, damage_info = self.calculate_damage( attacker, defender, weapon_damage, damage_type, critical_chance, critical_multiplier, ) actual_damage = defender.take_damage(damage, attacker) target_died = not defender.stats.is_alive() result.update( { "damage": actual_damage, "is_critical": is_critical, "damage_info": damage_info, "target_died": target_died, } ) return result
[docs] def get_combat_modifiers(self, entity: Entity) -> Dict: modifiers = { "attack_bonus": 0, "defense_bonus": 0, "critical_chance_bonus": 0.0, "accuracy_bonus": 0.0, "damage_multiplier": 1.0, } for effect in entity.status_effects: if effect.effect_type == "strength": modifiers["attack_bonus"] += int(effect.power) elif effect.effect_type == "defense": modifiers["defense_bonus"] += int(effect.power) elif effect.effect_type == "precision": modifiers["accuracy_bonus"] += effect.power * 0.01 elif effect.effect_type == "weakness": modifiers["damage_multiplier"] *= 0.5 elif effect.effect_type == "rage": modifiers["attack_bonus"] += int(effect.power) modifiers["critical_chance_bonus"] += 0.1 return modifiers