Files
pokemon-battle-engine/src/core/pokemon_stats.cpp
2025-08-15 14:30:00 +00:00

225 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pokemon_battle_sim.h"
#include <cmath>
#include <algorithm>
namespace PokEng {
// Nature utility function implementations
StatIndex NatureUtils::getIncreasedStat(Nature nature) {
switch (nature) {
// Attack boosting
case Nature::LONELY:
case Nature::ADAMANT:
case Nature::NAUGHTY:
case Nature::BRAVE:
return StatIndex::ATTACK;
// Defense boosting
case Nature::BOLD:
case Nature::IMPISH:
case Nature::LAX:
case Nature::RELAXED:
return StatIndex::DEFENSE;
// Sp. Attack boosting
case Nature::MODEST:
case Nature::MILD:
case Nature::RASH:
case Nature::QUIET:
return StatIndex::SP_ATTACK;
// Sp. Defense boosting
case Nature::CALM:
case Nature::GENTLE:
case Nature::CAREFUL:
case Nature::SASSY:
return StatIndex::SP_DEFENSE;
// Speed boosting
case Nature::TIMID:
case Nature::HASTY:
case Nature::JOLLY:
case Nature::NAIVE:
return StatIndex::SPEED;
// Neutral natures
default:
return StatIndex::HP; // Use HP as "no stat" indicator
}
}
StatIndex NatureUtils::getDecreasedStat(Nature nature) {
switch (nature) {
// Attack decreasing
case Nature::BOLD:
case Nature::MODEST:
case Nature::CALM:
case Nature::TIMID:
return StatIndex::ATTACK;
// Defense decreasing
case Nature::LONELY:
case Nature::MILD:
case Nature::GENTLE:
case Nature::HASTY:
return StatIndex::DEFENSE;
// Sp. Attack decreasing
case Nature::ADAMANT:
case Nature::IMPISH:
case Nature::CAREFUL:
case Nature::JOLLY:
return StatIndex::SP_ATTACK;
// Sp. Defense decreasing
case Nature::NAUGHTY:
case Nature::LAX:
case Nature::RASH:
return StatIndex::SP_DEFENSE;
// Speed decreasing
case Nature::BRAVE:
case Nature::RELAXED:
case Nature::QUIET:
case Nature::SASSY:
return StatIndex::SPEED;
// Neutral natures
default:
return StatIndex::HP; // Use HP as "no stat" indicator
}
}
float NatureUtils::getStatMultiplier(Nature nature, StatIndex stat) {
StatIndex increased = getIncreasedStat(nature);
StatIndex decreased = getDecreasedStat(nature);
if (stat == increased && stat != StatIndex::HP) {
return 1.1f; // +10%
} else if (stat == decreased && stat != StatIndex::HP) {
return 0.9f; // -10%
} else {
return 1.0f; // Neutral
}
}
bool NatureUtils::isNeutralNature(Nature nature) {
return getIncreasedStat(nature) == getDecreasedStat(nature) ||
getIncreasedStat(nature) == StatIndex::HP;
}
// Generation I & II stat calculation implementations
template<Generation Gen>
typename std::enable_if<Gen == Generation::I || Gen == Generation::II, uint16_t>::type
StatCalculator::calculateHP(uint16_t base, uint8_t dv, uint16_t statExp, uint8_t level) {
// HP=⌊((Base+DV)×2+⌊⌈STATEXP⌉/4⌋)×Level/100⌋+Level+10
uint32_t result = ((static_cast<uint32_t>(base) + dv) * 2 +
static_cast<uint32_t>(std::ceil(statExp) / 4)) * level / 100 + level + 10;
return clampStat(result);
}
template<Generation Gen>
typename std::enable_if<Gen == Generation::I || Gen == Generation::II, uint16_t>::type
StatCalculator::calculateStat(uint16_t base, uint8_t dv, uint16_t statExp, uint8_t level) {
// OtherStat=⌊((Base+DV)×2+⌊⌈STATEXP⌉/4⌋)×Level/100⌋+5
uint32_t result = ((static_cast<uint32_t>(base) + dv) * 2 +
static_cast<uint32_t>(std::ceil(statExp) / 4)) * level / 100 + 5;
return clampStat(result);
}
// Generation III+ stat calculation implementations
template<Generation Gen>
typename std::enable_if<Gen >= Generation::III, uint16_t>::type
StatCalculator::calculateHP(uint16_t base, uint8_t iv, uint8_t ev, uint8_t level) {
// HP=⌊(2×Base+IV+⌊EV/4⌋)×Level/100⌋+Level+10
uint32_t result = (2 * static_cast<uint32_t>(base) + iv + ev / 4) * level / 100 + level + 10;
return clampStat(result);
}
template<Generation Gen>
typename std::enable_if<Gen >= Generation::III, uint16_t>::type
StatCalculator::calculateStat(uint16_t base, uint8_t iv, uint8_t ev, uint8_t level, Nature nature, StatIndex statIndex) {
// OtherStat=⌊(⌊(2×Base+IV+⌊EV/4⌋)×Level/100⌋+5)×Nature⌋
uint32_t baseStat = (2 * static_cast<uint32_t>(base) + iv + ev / 4) * level / 100 + 5;
float natureMultiplier = NatureUtils::getStatMultiplier(nature, statIndex);
uint32_t result = static_cast<uint32_t>(std::floor(static_cast<float>(baseStat) * natureMultiplier));
return clampStat(result);
}
// Explicit template instantiations for Generation I & II
template uint16_t StatCalculator::calculateHP<Generation::I>(uint16_t, uint8_t, uint16_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::II>(uint16_t, uint8_t, uint16_t, uint8_t);
template uint16_t StatCalculator::calculateStat<Generation::I>(uint16_t, uint8_t, uint16_t, uint8_t);
template uint16_t StatCalculator::calculateStat<Generation::II>(uint16_t, uint8_t, uint16_t, uint8_t);
// Explicit template instantiations for Generation III+
template uint16_t StatCalculator::calculateHP<Generation::III>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::IV>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::V>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::VI>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::VII>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::VIII>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateHP<Generation::IX>(uint16_t, uint8_t, uint8_t, uint8_t);
template uint16_t StatCalculator::calculateStat<Generation::III>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
template uint16_t StatCalculator::calculateStat<Generation::IV>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
template uint16_t StatCalculator::calculateStat<Generation::V>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
template uint16_t StatCalculator::calculateStat<Generation::VI>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
template uint16_t StatCalculator::calculateStat<Generation::VII>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
template uint16_t StatCalculator::calculateStat<Generation::VIII>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
template uint16_t StatCalculator::calculateStat<Generation::IX>(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex);
// PokemonInfo stat calculation implementations
template<Generation Gen>
BattleStats PokemonInfo::calculateBattleStats() const {
BattleStats stats;
if constexpr (Gen == Generation::I || Gen == Generation::II) {
// For Gen I/II, we need DV and STATEXP values
// For simplicity, we'll convert IVs to DVs (IV/2) and EVs to STATEXP (EV*EV/4)
uint8_t hp_dv = m_ivs.hp / 2;
uint8_t atk_dv = m_ivs.attack / 2;
uint8_t def_dv = m_ivs.defense / 2;
uint8_t spa_dv = m_ivs.sp_attack / 2;
uint8_t spd_dv = m_ivs.sp_defense / 2;
uint8_t spe_dv = m_ivs.speed / 2;
uint16_t hp_statexp = static_cast<uint16_t>((static_cast<uint32_t>(m_evs.hp) * m_evs.hp) / 4);
uint16_t atk_statexp = static_cast<uint16_t>((static_cast<uint32_t>(m_evs.attack) * m_evs.attack) / 4);
uint16_t def_statexp = static_cast<uint16_t>((static_cast<uint32_t>(m_evs.defense) * m_evs.defense) / 4);
uint16_t spa_statexp = static_cast<uint16_t>((static_cast<uint32_t>(m_evs.sp_attack) * m_evs.sp_attack) / 4);
uint16_t spd_statexp = static_cast<uint16_t>((static_cast<uint32_t>(m_evs.sp_defense) * m_evs.sp_defense) / 4);
uint16_t spe_statexp = static_cast<uint16_t>((static_cast<uint32_t>(m_evs.speed) * m_evs.speed) / 4);
stats.hp = StatCalculator::calculateHP<Gen>(m_baseStats.hp, hp_dv, hp_statexp, m_level);
stats.attack = StatCalculator::calculateStat<Gen>(m_baseStats.attack, atk_dv, atk_statexp, m_level);
stats.defense = StatCalculator::calculateStat<Gen>(m_baseStats.defense, def_dv, def_statexp, m_level);
stats.sp_attack = StatCalculator::calculateStat<Gen>(m_baseStats.sp_attack, spa_dv, spa_statexp, m_level);
stats.sp_defense = StatCalculator::calculateStat<Gen>(m_baseStats.sp_defense, spd_dv, spd_statexp, m_level);
stats.speed = StatCalculator::calculateStat<Gen>(m_baseStats.speed, spe_dv, spe_statexp, m_level);
} else {
// Generation III+
stats.hp = StatCalculator::calculateHP<Gen>(m_baseStats.hp, m_ivs.hp, m_evs.hp, m_level);
stats.attack = StatCalculator::calculateStat<Gen>(m_baseStats.attack, m_ivs.attack, m_evs.attack, m_level, m_nature, StatIndex::ATTACK);
stats.defense = StatCalculator::calculateStat<Gen>(m_baseStats.defense, m_ivs.defense, m_evs.defense, m_level, m_nature, StatIndex::DEFENSE);
stats.sp_attack = StatCalculator::calculateStat<Gen>(m_baseStats.sp_attack, m_ivs.sp_attack, m_evs.sp_attack, m_level, m_nature, StatIndex::SP_ATTACK);
stats.sp_defense = StatCalculator::calculateStat<Gen>(m_baseStats.sp_defense, m_ivs.sp_defense, m_evs.sp_defense, m_level, m_nature, StatIndex::SP_DEFENSE);
stats.speed = StatCalculator::calculateStat<Gen>(m_baseStats.speed, m_ivs.speed, m_evs.speed, m_level, m_nature, StatIndex::SPEED);
}
return stats;
}
// Explicit template instantiations for PokemonInfo::calculateBattleStats
template BattleStats PokemonInfo::calculateBattleStats<Generation::I>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::II>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::III>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::IV>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::V>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::VI>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::VII>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::VIII>() const;
template BattleStats PokemonInfo::calculateBattleStats<Generation::IX>() const;
} // namespace PokEng