Tales-of-PKMN/game/battle/damage_calculator.gd

52 lines
2.8 KiB
GDScript

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))