class_name DamageCalculator extends RefCounted const CRIT_CHANCE : float = 1.0 / 24.0 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_CHANCE 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 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"])