diff --git a/examples/pokemon_stats_example.cpp b/examples/pokemon_stats_example.cpp new file mode 100644 index 0000000..33b998b --- /dev/null +++ b/examples/pokemon_stats_example.cpp @@ -0,0 +1,93 @@ +#include "pokemon_battle_sim.h" +#include +#include + +using namespace PokEng; + +void printPokemonStats(const Pokemon& pokemon, const std::string& name) { + std::cout << "\n=== " << name << " ===\n"; + std::cout << "ID: " << pokemon.getId() << "\n"; + std::cout << "Current HP: " << pokemon.getCurrentHP() << "/" << pokemon.getMaxHP() << "\n"; + std::cout << "Attack: " << pokemon.getAttack() << "\n"; + std::cout << "Defense: " << pokemon.getDefense() << "\n"; + std::cout << "Sp. Attack: " << pokemon.getSpAttack() << "\n"; + std::cout << "Sp. Defense: " << pokemon.getSpDefense() << "\n"; + std::cout << "Speed: " << pokemon.getSpeed() << "\n"; + std::cout << "Friendship: " << static_cast(pokemon.getFriendship()) << "\n"; + std::cout << "Primary Type: " << static_cast(pokemon.getPrimaryType()) << "\n"; + if (pokemon.getSecondaryType() != Type::NONE) { + std::cout << "Secondary Type: " << static_cast(pokemon.getSecondaryType()) << "\n"; + } +} + +void printNatureEffect(Nature nature) { + std::cout << "\nNature: " << static_cast(nature); + StatIndex increased = NatureUtils::getIncreasedStat(nature); + StatIndex decreased = NatureUtils::getDecreasedStat(nature); + + if (increased != StatIndex::HP) { + std::cout << " (+10% to stat " << static_cast(increased) << ")"; + } + if (decreased != StatIndex::HP) { + std::cout << " (-10% to stat " << static_cast(decreased) << ")"; + } + if (NatureUtils::isNeutralNature(nature)) { + std::cout << " (Neutral nature)"; + } + std::cout << "\n"; +} + +int main() { + std::cout << "Pokemon Battle Simulator - Stat System Example\n"; + std::cout << "===============================================\n"; + + // Create base stats for a Charizard-like Pokemon + BaseStats charizardBase(78, 84, 78, 109, 85, 100); + + // Create different Pokemon with different configurations + + // 1. Level 50 Charizard with neutral nature and no EVs/IVs + PokemonInfo basicInfo(charizardBase, 50, Nature::HARDY); + Pokemon basicCharizard(6, basicInfo, PokemonTypes(Type::FIRE, Type::FLYING), 128); + + // 2. Level 100 Charizard with Modest nature (+Sp.Atk, -Atk) and perfect IVs + IndividualValues perfectIVs(31, 31, 31, 31, 31, 31); + PokemonInfo competitiveInfo(charizardBase, 100, Nature::MODEST, perfectIVs); + Pokemon competitiveCharizard(6, competitiveInfo, PokemonTypes(Type::FIRE, Type::FLYING), 255); + + // 3. Level 100 Charizard with Adamant nature (+Atk, -Sp.Atk) and Attack EVs + EffortValues attackEVs(0, 252, 0, 0, 4, 252); // Max Attack and Speed, some HP + PokemonInfo physicalInfo(charizardBase, 100, Nature::ADAMANT, perfectIVs, attackEVs); + Pokemon physicalCharizard(6, physicalInfo, PokemonTypes(Type::FIRE, Type::FLYING), 255); + + // Print all Pokemon + printPokemonStats(basicCharizard, "Basic Charizard (Lv50, Neutral)"); + + printNatureEffect(Nature::MODEST); + printPokemonStats(competitiveCharizard, "Competitive Charizard (Lv100, Modest, Perfect IVs)"); + + printNatureEffect(Nature::ADAMANT); + printPokemonStats(physicalCharizard, "Physical Charizard (Lv100, Adamant, Attack EVs)"); + + // Demonstrate Generation I/II calculation + std::cout << "\n=== Generation I/II Calculation Example ===\n"; + Pokemon gen1Charizard(6, basicInfo.calculateBattleStats(), PokemonTypes(Type::FIRE, Type::FLYING), 128); + printPokemonStats(gen1Charizard, "Gen I Charizard (Same base stats)"); + + // Show EV validation + std::cout << "\n=== EV Validation ===\n"; + EffortValues validEVs(85, 85, 85, 85, 85, 85); // 510 total + EffortValues invalidEVs(100, 100, 100, 100, 100, 100); // 600 total + + std::cout << "Valid EVs (510 total): " << (validEVs.isValid() ? "VALID" : "INVALID") << "\n"; + std::cout << "Invalid EVs (600 total): " << (invalidEVs.isValid() ? "VALID" : "INVALID") << "\n"; + + // Show IV validation + IndividualValues validIVs(31, 31, 31, 31, 31, 31); + IndividualValues invalidIVs(32, 32, 32, 32, 32, 32); + + std::cout << "Valid IVs (all 31): " << (validIVs.isValid() ? "VALID" : "INVALID") << "\n"; + std::cout << "Invalid IVs (all 32): " << (invalidIVs.isValid() ? "VALID" : "INVALID") << "\n"; + + return 0; +} diff --git a/include/core/config.h b/include/core/config.h index 2de4656..bc171cd 100644 --- a/include/core/config.h +++ b/include/core/config.h @@ -18,6 +18,56 @@ VIII = 8, IX = 9 }; +// Pokemon Nature enumeration +enum class Nature : uint8_t { + // Neutral natures (no stat changes) + HARDY = 0, // +Attack, -Attack + DOCILE, // +Defense, -Defense + BASHFUL, // +Sp. Atk, -Sp. Atk + QUIRKY, // +Sp. Def, -Sp. Def + SERIOUS, // +Speed, -Speed + + // Attack boosting natures + LONELY, // +Attack, -Defense + ADAMANT, // +Attack, -Sp. Atk + NAUGHTY, // +Attack, -Sp. Def + BRAVE, // +Attack, -Speed + + // Defense boosting natures + BOLD, // +Defense, -Attack + IMPISH, // +Defense, -Sp. Atk + LAX, // +Defense, -Sp. Def + RELAXED, // +Defense, -Speed + + // Sp. Atk boosting natures + MODEST, // +Sp. Atk, -Attack + MILD, // +Sp. Atk, -Defense + RASH, // +Sp. Atk, -Sp. Def + QUIET, // +Sp. Atk, -Speed + + // Sp. Def boosting natures + CALM, // +Sp. Def, -Attack + GENTLE, // +Sp. Def, -Defense + CAREFUL, // +Sp. Def, -Sp. Atk + SASSY, // +Sp. Def, -Speed + + // Speed boosting natures + TIMID, // +Speed, -Attack + HASTY, // +Speed, -Defense + JOLLY, // +Speed, -Sp. Atk + NAIVE // +Speed, -Sp. Def +}; + +// Stat indices for nature calculations +enum class StatIndex : uint8_t { + HP = 0, + ATTACK = 1, + DEFENSE = 2, + SP_ATTACK = 3, + SP_DEFENSE = 4, + SPEED = 5 +}; + } // namespace PokEng diff --git a/include/core/pokemon.h b/include/core/pokemon.h index 4f85a03..a729aee 100644 --- a/include/core/pokemon.h +++ b/include/core/pokemon.h @@ -3,6 +3,7 @@ #include "config.h" #include "types.h" +#include "pokemon_stats.h" #include namespace PokEng { @@ -13,24 +14,56 @@ public: Pokemon() = default; ~Pokemon() = default; + // Basic constructors Pokemon(uint16_t id) : m_id(id) {} - Pokemon(uint16_t id, uint16_t health) : m_id(id), m_health(health) {} - Pokemon(uint16_t id, uint16_t health, Type primaryType) : m_id(id), m_health(health), m_types(primaryType) {} - Pokemon(uint16_t id, uint16_t health, Type primaryType, Type secondaryType) : m_id(id), m_health(health), m_types(primaryType, secondaryType) {} + Pokemon(uint16_t id, uint16_t currentHP) : m_id(id), m_currentHP(currentHP) {} + Pokemon(uint16_t id, uint16_t currentHP, Type primaryType) : m_id(id), m_currentHP(currentHP), m_types(primaryType) {} + Pokemon(uint16_t id, uint16_t currentHP, Type primaryType, Type secondaryType) : m_id(id), m_currentHP(currentHP), m_types(primaryType, secondaryType) {} + + // Constructor that calculates stats from PokemonInfo + template + Pokemon(uint16_t id, const PokemonInfo& info, const PokemonTypes& types, uint8_t friendship = 0) + : m_id(id), m_types(types), m_friendship(friendship) { + m_battleStats = info.calculateBattleStats(); + m_currentHP = m_battleStats.hp; // Start at full health + } + + // Constructor that takes pre-calculated battle stats + Pokemon(uint16_t id, const BattleStats& stats, const PokemonTypes& types, uint8_t friendship = 0) + : m_id(id), m_battleStats(stats), m_types(types), m_friendship(friendship) { + m_currentHP = m_battleStats.hp; // Start at full health + } public: - // Getters + // Basic getters uint16_t getId() const { return m_id; } - uint16_t getHealth() const { return m_health; } + uint16_t getCurrentHP() const { return m_currentHP; } + uint16_t getMaxHP() const { return m_battleStats.hp; } PokemonTypes getTypes() const { return m_types; } Type getPrimaryType() const { return m_types.getPrimary(); } Type getSecondaryType() const { return m_types.getSecondary(); } + uint8_t getFriendship() const { return m_friendship; } + + // Battle stat getters + const BattleStats& getBattleStats() const { return m_battleStats; } + uint16_t getAttack() const { return m_battleStats.attack; } + uint16_t getDefense() const { return m_battleStats.defense; } + uint16_t getSpAttack() const { return m_battleStats.sp_attack; } + uint16_t getSpDefense() const { return m_battleStats.sp_defense; } + uint16_t getSpeed() const { return m_battleStats.speed; } - // Setters - void setHealth(uint16_t health) { m_health = health; } + // Basic setters + void setCurrentHP(uint16_t hp) { m_currentHP = (hp > m_battleStats.hp) ? m_battleStats.hp : hp; } void setTypes(Type primary) { m_types.setPrimary(primary); m_types.setSecondary(Type::NONE); } void setTypes(Type primary, Type secondary) { m_types.setPrimary(primary); m_types.setSecondary(secondary); } void SetPokemonTypes(PokemonTypes types) { m_types = types; } + void setFriendship(uint8_t friendship) { m_friendship = friendship; } + + // Battle stat setters (for direct stat assignment if needed) + void setBattleStats(const BattleStats& stats) { + m_battleStats = stats; + if (m_currentHP > m_battleStats.hp) m_currentHP = m_battleStats.hp; + } public: // Type-based operations @@ -40,9 +73,11 @@ public: } private: - uint16_t m_id; - uint16_t m_health; + uint16_t m_id = 0; + uint16_t m_currentHP = 0; + BattleStats m_battleStats; PokemonTypes m_types; + uint8_t m_friendship = 0; }; } // namespace PokEng diff --git a/include/core/pokemon_stats.h b/include/core/pokemon_stats.h new file mode 100644 index 0000000..decd4df --- /dev/null +++ b/include/core/pokemon_stats.h @@ -0,0 +1,167 @@ +#ifndef POKEMON_STATS_H +#define POKEMON_STATS_H + +#include "config.h" +#include +#include + +namespace PokEng { + +// Forward declaration +class PokemonInfo; + +// Base stats structure for a Pokemon species +struct BaseStats { + uint16_t hp; + uint16_t attack; + uint16_t defense; + uint16_t sp_attack; + uint16_t sp_defense; + uint16_t speed; + + BaseStats() : hp(0), attack(0), defense(0), sp_attack(0), sp_defense(0), speed(0) {} + BaseStats(uint16_t hp_, uint16_t atk, uint16_t def, uint16_t spa, uint16_t spd, uint16_t spe) + : hp(hp_), attack(atk), defense(def), sp_attack(spa), sp_defense(spd), speed(spe) {} +}; + +// Effort Values (EVs) - max 510 total, max 255 per stat +struct EffortValues { + uint8_t hp; + uint8_t attack; + uint8_t defense; + uint8_t sp_attack; + uint8_t sp_defense; + uint8_t speed; + + EffortValues() : hp(0), attack(0), defense(0), sp_attack(0), sp_defense(0), speed(0) {} + EffortValues(uint8_t hp_, uint8_t atk, uint8_t def, uint8_t spa, uint8_t spd, uint8_t spe) + : hp(hp_), attack(atk), defense(def), sp_attack(spa), sp_defense(spd), speed(spe) {} + + // Get total EVs (should not exceed 510) + uint16_t getTotal() const { + return static_cast(static_cast(hp) + attack + defense + sp_attack + sp_defense + speed); + } + + // Validate EVs (total <= 510, each <= 255) + bool isValid() const { + return getTotal() <= 510; + } +}; + +// Individual Values (IVs) - 0-31 per stat +struct IndividualValues { + uint8_t hp; + uint8_t attack; + uint8_t defense; + uint8_t sp_attack; + uint8_t sp_defense; + uint8_t speed; + + IndividualValues() : hp(0), attack(0), defense(0), sp_attack(0), sp_defense(0), speed(0) {} + IndividualValues(uint8_t hp_, uint8_t atk, uint8_t def, uint8_t spa, uint8_t spd, uint8_t spe) + : hp(hp_), attack(atk), defense(def), sp_attack(spa), sp_defense(spd), speed(spe) {} + + // Validate IVs (each <= 31) + bool isValid() const { + return hp <= 31 && attack <= 31 && defense <= 31 && + sp_attack <= 31 && sp_defense <= 31 && speed <= 31; + } +}; + +// Computed battle stats +struct BattleStats { + uint16_t hp; // Max 20,000 + uint16_t attack; // Max 20,000 + uint16_t defense; // Max 20,000 + uint16_t sp_attack; // Max 20,000 + uint16_t sp_defense; // Max 20,000 + uint16_t speed; // Max 20,000 + + BattleStats() : hp(0), attack(0), defense(0), sp_attack(0), sp_defense(0), speed(0) {} + BattleStats(uint16_t hp_, uint16_t atk, uint16_t def, uint16_t spa, uint16_t spd, uint16_t spe) + : hp(hp_), attack(atk), defense(def), sp_attack(spa), sp_defense(spd), speed(spe) {} +}; + +// Pokemon information class - holds data not directly relevant to battle +class PokemonInfo { +public: + PokemonInfo() = default; + + PokemonInfo(const BaseStats& baseStats, uint8_t level = 1, Nature nature = Nature::HARDY, + const IndividualValues& ivs = IndividualValues(), + const EffortValues& evs = EffortValues()) + : m_baseStats(baseStats), m_level(level), m_nature(nature), m_ivs(ivs), m_evs(evs) {} + + // Getters + const BaseStats& getBaseStats() const { return m_baseStats; } + uint8_t getLevel() const { return m_level; } + Nature getNature() const { return m_nature; } + const IndividualValues& getIVs() const { return m_ivs; } + const EffortValues& getEVs() const { return m_evs; } + + // Setters + void setBaseStats(const BaseStats& baseStats) { m_baseStats = baseStats; } + void setLevel(uint8_t level) { m_level = (level > 100) ? 100 : level; } + void setNature(Nature nature) { m_nature = nature; } + void setIVs(const IndividualValues& ivs) { if (ivs.isValid()) m_ivs = ivs; } + void setEVs(const EffortValues& evs) { if (evs.isValid()) m_evs = evs; } + + // Calculate battle stats for different generations + template + BattleStats calculateBattleStats() const; + +private: + BaseStats m_baseStats; + uint8_t m_level = 1; + Nature m_nature = Nature::HARDY; + IndividualValues m_ivs; + EffortValues m_evs; +}; + +// Nature utility functions +class NatureUtils { +public: + // Get the stat that is increased by this nature (returns StatIndex, or HP if none) + static StatIndex getIncreasedStat(Nature nature); + + // Get the stat that is decreased by this nature (returns StatIndex, or HP if none) + static StatIndex getDecreasedStat(Nature nature); + + // Get the multiplier for a specific stat given a nature (1.1f for increased, 0.9f for decreased, 1.0f for neutral) + static float getStatMultiplier(Nature nature, StatIndex stat); + + // Check if a nature is neutral (increases and decreases the same stat) + static bool isNeutralNature(Nature nature); +}; + +// Stat calculation functions +class StatCalculator { +public: + // Generation I & II stat calculation + template + static typename std::enable_if::type + calculateHP(uint16_t base, uint8_t dv, uint16_t statExp, uint8_t level); + + template + static typename std::enable_if::type + calculateStat(uint16_t base, uint8_t dv, uint16_t statExp, uint8_t level); + + // Generation III+ stat calculation + template + static typename std::enable_if= Generation::III, uint16_t>::type + calculateHP(uint16_t base, uint8_t iv, uint8_t ev, uint8_t level); + + template + static typename std::enable_if= Generation::III, uint16_t>::type + calculateStat(uint16_t base, uint8_t iv, uint8_t ev, uint8_t level, Nature nature, StatIndex statIndex); + +private: + // Helper functions + static uint16_t clampStat(uint32_t value) { + return (value > 20000) ? 20000 : static_cast(value); + } +}; + +} // namespace PokEng + +#endif // POKEMON_STATS_H diff --git a/include/pokemon_battle_sim.h b/include/pokemon_battle_sim.h index 73c152f..ee7ba2c 100644 --- a/include/pokemon_battle_sim.h +++ b/include/pokemon_battle_sim.h @@ -6,6 +6,7 @@ #include "core/types.h" #include "core/pokemon.h" +#include "core/pokemon_stats.h" #include "core/battle.h" #include "core/config.h" diff --git a/src/core/pokemon_stats.cpp b/src/core/pokemon_stats.cpp new file mode 100644 index 0000000..fcb2648 --- /dev/null +++ b/src/core/pokemon_stats.cpp @@ -0,0 +1,224 @@ +#include "pokemon_battle_sim.h" +#include +#include + +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 +typename std::enable_if::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(base) + dv) * 2 + + static_cast(std::ceil(statExp) / 4)) * level / 100 + level + 10; + return clampStat(result); +} + +template +typename std::enable_if::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(base) + dv) * 2 + + static_cast(std::ceil(statExp) / 4)) * level / 100 + 5; + return clampStat(result); +} + +// Generation III+ stat calculation implementations +template +typename std::enable_if= 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(base) + iv + ev / 4) * level / 100 + level + 10; + return clampStat(result); +} + +template +typename std::enable_if= 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(base) + iv + ev / 4) * level / 100 + 5; + float natureMultiplier = NatureUtils::getStatMultiplier(nature, statIndex); + uint32_t result = static_cast(std::floor(static_cast(baseStat) * natureMultiplier)); + return clampStat(result); +} + +// Explicit template instantiations for Generation I & II +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint16_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint16_t, uint8_t); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint16_t, uint8_t); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint16_t, uint8_t); + +// Explicit template instantiations for Generation III+ +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); +template uint16_t StatCalculator::calculateHP(uint16_t, uint8_t, uint8_t, uint8_t); + +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); +template uint16_t StatCalculator::calculateStat(uint16_t, uint8_t, uint8_t, uint8_t, Nature, StatIndex); + +// PokemonInfo stat calculation implementations +template +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((static_cast(m_evs.hp) * m_evs.hp) / 4); + uint16_t atk_statexp = static_cast((static_cast(m_evs.attack) * m_evs.attack) / 4); + uint16_t def_statexp = static_cast((static_cast(m_evs.defense) * m_evs.defense) / 4); + uint16_t spa_statexp = static_cast((static_cast(m_evs.sp_attack) * m_evs.sp_attack) / 4); + uint16_t spd_statexp = static_cast((static_cast(m_evs.sp_defense) * m_evs.sp_defense) / 4); + uint16_t spe_statexp = static_cast((static_cast(m_evs.speed) * m_evs.speed) / 4); + + stats.hp = StatCalculator::calculateHP(m_baseStats.hp, hp_dv, hp_statexp, m_level); + stats.attack = StatCalculator::calculateStat(m_baseStats.attack, atk_dv, atk_statexp, m_level); + stats.defense = StatCalculator::calculateStat(m_baseStats.defense, def_dv, def_statexp, m_level); + stats.sp_attack = StatCalculator::calculateStat(m_baseStats.sp_attack, spa_dv, spa_statexp, m_level); + stats.sp_defense = StatCalculator::calculateStat(m_baseStats.sp_defense, spd_dv, spd_statexp, m_level); + stats.speed = StatCalculator::calculateStat(m_baseStats.speed, spe_dv, spe_statexp, m_level); + } else { + // Generation III+ + stats.hp = StatCalculator::calculateHP(m_baseStats.hp, m_ivs.hp, m_evs.hp, m_level); + stats.attack = StatCalculator::calculateStat(m_baseStats.attack, m_ivs.attack, m_evs.attack, m_level, m_nature, StatIndex::ATTACK); + stats.defense = StatCalculator::calculateStat(m_baseStats.defense, m_ivs.defense, m_evs.defense, m_level, m_nature, StatIndex::DEFENSE); + stats.sp_attack = StatCalculator::calculateStat(m_baseStats.sp_attack, m_ivs.sp_attack, m_evs.sp_attack, m_level, m_nature, StatIndex::SP_ATTACK); + stats.sp_defense = StatCalculator::calculateStat(m_baseStats.sp_defense, m_ivs.sp_defense, m_evs.sp_defense, m_level, m_nature, StatIndex::SP_DEFENSE); + stats.speed = StatCalculator::calculateStat(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() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; +template BattleStats PokemonInfo::calculateBattleStats() const; + +} // namespace PokEng