class_name DamageCalculator extends RefCounted const CRIT_CHANCES : Array[float] = [1.0 / 24.0, 1.0 / 8.0, 1.0 / 2.0, 1.0] #selon le bonus de critique de la capacite (0 a 3) const CRIT_MULTIPLIER : float = 1.5 const STAB_MULTIPLIER : float = 1.5 const SPREAD_MULTIPLIER : float = 0.75 #capacite touchant plusieurs cibles static func calculate(attacker : BattleMon, defender : BattleMon, move : MoveData, rng : RandomNumberGenerator = null, spread : bool = false, deterministic : bool = false) -> Dictionary: #renvoie {"damage": int, "effectiveness": float, "critical": bool} var result : Dictionary = {"damage": 0, "effectiveness": 1.0, "critical": false} if move.power <= 0 or move.category == "status": return result var effectiveness : float = TypeChart.get_multiplier(move.type, defender.get_types()) result["effectiveness"] = effectiveness if effectiveness == 0.0: return result var physical : bool = move.category == "physical" var attack : float = attacker.get_stat(PokemonNature.Stat.ATTACK if physical else PokemonNature.Stat.SP_ATTACK) var defense : float = defender.get_stat(PokemonNature.Stat.DEFENSE if physical else PokemonNature.Stat.SP_DEFENSE) var level : int = attacker.pokemon.level #formule officielle var damage : float = floorf(floorf(floorf(2.0 * level / 5.0 + 2.0) * move.power * attack / maxf(defense, 1.0)) / 50.0) + 2.0 var critical : bool = !deterministic and rng != null and rng.randf() < CRIT_CHANCES[clampi(move.crit_rate, 0, 3)] var roll : float = 0.925 if (deterministic or rng == null) else rng.randi_range(85, 100) / 100.0 var stab : float = STAB_MULTIPLIER if attacker.get_types().has(move.type.to_lower()) else 1.0 damage *= roll * stab * effectiveness if attacker.field != null: damage *= attacker.field.damage_multiplier(move.type.to_lower()) for effect in attacker.get_effects(): damage = effect.modify_outgoing_damage(attacker, defender, move, damage) if critical: damage *= CRIT_MULTIPLIER if spread: damage *= SPREAD_MULTIPLIER result["critical"] = critical result["damage"] = maxi(1, floori(damage)) return result static func estimate(attacker : BattleMon, defender : BattleMon, move : MoveData, spread : bool = false) -> int: #degats attendus, sans hasard (utilise par l'IA) return int(calculate(attacker, defender, move, null, spread, true)["damage"]) static func confusion_damage(mon : BattleMon, rng : RandomNumberGenerator) -> int: #le pokemon confus se blesse : capacite physique de puissance 40, sans type ni critique var attack : float = mon.get_stat(PokemonNature.Stat.ATTACK) var defense : float = mon.get_stat(PokemonNature.Stat.DEFENSE) var damage : float = floorf(floorf(floorf(2.0 * mon.pokemon.level / 5.0 + 2.0) * 40.0 * attack / maxf(defense, 1.0)) / 50.0) + 2.0 damage *= rng.randi_range(85, 100) / 100.0 return maxi(1, floori(damage))