Files
pokemon-battle-engine/include/core/stats.h
cdemeyer-teachx aff99a507c code refactor
2025-08-20 13:02:04 +09:00

222 lines
8.0 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.
#ifndef STATS_H
#define STATS_H
#include "config.h"
#include <cstdint>
#include <array>
#include <cmath>
namespace PokEng {
// Forward declaration
class PokemonInfo;
// Base stats structure for a Pokemon species
struct BaseStats {
uint8_t hp;
uint8_t attack;
uint8_t defense;
uint8_t sp_attack;
uint8_t sp_defense;
uint8_t speed;
BaseStats() : hp(0), attack(0), defense(0), sp_attack(0), sp_defense(0), speed(0) {}
BaseStats(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) {}
};
// 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<uint16_t>(static_cast<uint16_t>(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
// Packed into 32 bits: 5 bits per stat + 2 bits unused
struct IndividualValues {
private:
uint32_t m_packed; // 30 bits used: HP(0-4), ATK(5-9), DEF(10-14), SPA(15-19), SPD(20-24), SPE(25-29)
public:
IndividualValues() : m_packed(0) {}
IndividualValues(uint8_t hp_, uint8_t atk, uint8_t def, uint8_t spa, uint8_t spd, uint8_t spe) {
setHP(hp_);
setAttack(atk);
setDefense(def);
setSpAttack(spa);
setSpDefense(spd);
setSpeed(spe);
}
// Getters
uint8_t getHP() const { return (m_packed & 0x1F); }
uint8_t getAttack() const { return (m_packed >> 5) & 0x1F; }
uint8_t getDefense() const { return (m_packed >> 10) & 0x1F; }
uint8_t getSpAttack() const { return (m_packed >> 15) & 0x1F; }
uint8_t getSpDefense() const { return (m_packed >> 20) & 0x1F; }
uint8_t getSpeed() const { return (m_packed >> 25) & 0x1F; }
// Legacy getters for compatibility
uint8_t hp() const { return getHP(); }
uint8_t attack() const { return getAttack(); }
uint8_t defense() const { return getDefense(); }
uint8_t sp_attack() const { return getSpAttack(); }
uint8_t sp_defense() const { return getSpDefense(); }
uint8_t speed() const { return getSpeed(); }
// Setters
void setHP(uint8_t value) {
if (value > 31) value = 31;
m_packed = (m_packed & ~0x1FU) | (value & 0x1FU);
}
void setAttack(uint8_t value) {
if (value > 31) value = 31;
m_packed = (m_packed & ~(0x1FU << 5)) | ((value & 0x1FU) << 5);
}
void setDefense(uint8_t value) {
if (value > 31) value = 31;
m_packed = (m_packed & ~(0x1FU << 10)) | ((value & 0x1FU) << 10);
}
void setSpAttack(uint8_t value) {
if (value > 31) value = 31;
m_packed = (m_packed & ~(0x1FU << 15)) | ((value & 0x1FU) << 15);
}
void setSpDefense(uint8_t value) {
if (value > 31) value = 31;
m_packed = (m_packed & ~(0x1FU << 20)) | ((value & 0x1FU) << 20);
}
void setSpeed(uint8_t value) {
if (value > 31) value = 31;
m_packed = (m_packed & ~(0x1FU << 25)) | ((value & 0x1FU) << 25);
}
// Validate IVs (each <= 31) - always true with our implementation
bool isValid() const {
return true; // Always valid since we enforce limits in setters
}
};
// Computed battle stat
struct BattleStats {
uint16_t hp;
uint16_t attack;
uint16_t defense;
uint16_t sp_attack;
uint16_t sp_defense;
uint16_t speed;
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_ivs(ivs), m_evs(evs), m_level(level), m_nature(nature) {}
// 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<Generation Gen>
BattleStats calculateBattleStats() const;
private:
BaseStats m_baseStats; // size 6, align 1
IndividualValues m_ivs; // size 6, align 1
EffortValues m_evs; // size 6, align 1
uint8_t m_level = 1; // size 1, align 1
Nature m_nature; // size 1, align 1
};
struct StatCalculatorParams
{
StatCalculatorParams() = default;
StatCalculatorParams(uint8_t base, uint8_t iv, uint8_t ev, uint8_t level, Nature nature, StatIndex statIndex, uint16_t statExp)
: m_base(base), m_iv(iv), m_ev(ev), m_level(level), m_nature(nature), m_statIndex(statIndex), m_statExp(statExp) {}
uint8_t m_base;
uint8_t m_iv;
uint8_t m_ev;
uint8_t m_level;
Nature m_nature;
StatIndex m_statIndex;
uint16_t m_statExp;
};
uint16_t CalculateHP_GenI_II(StatCalculatorParams params)
{
// HP=⌊((Base+DV)×2+⌊⌈sqrt(STATEXP)⌉4⌋)×Level100⌋+Level+10
return (((params.m_base + (params.m_iv >> 1u)) << 1u) + (static_cast<uint16_t>(std::ceil(std::sqrt(params.m_statExp))) >> 2u)) * params.m_level / 100u + params.m_level + 10u;
}
uint16_t CalculateStat_GenI_II(StatCalculatorParams params)
{
// OtherStat=⌊((Base+DV)×2+⌊⌈sqrt(STATEXP)⌉4⌋)×Level100⌋+5
return (((params.m_base + (params.m_iv >> 1u)) << 1u) + (static_cast<uint16_t>(std::ceil(std::sqrt(params.m_statExp))) >> 2u)) * params.m_level / 100u + 5u;
}
template <Generation Gen>
uint16_t CalculateHP(StatCalculatorParams params);
template <Generation Gen>
uint16_t CalculateStat(StatCalculatorParams params);
template <> uint16_t CalculateHP<Generation::I>(StatCalculatorParams params) { return CalculateHP_GenI_II(params); }
template <> uint16_t CalculateHP<Generation::II>(StatCalculatorParams params) { return CalculateHP_GenI_II(params); }
template <> uint16_t CalculateStat<Generation::I>(StatCalculatorParams params) { return CalculateStat_GenI_II(params); }
template <> uint16_t CalculateStat<Generation::II>(StatCalculatorParams params) { return CalculateStat_GenI_II(params); }
template <Generation Gen>
uint16_t CalculateHP(StatCalculatorParams params)
{
// HP=⌊(2×Base+IV+⌊EV4⌋)×Level100⌋+Level+10
return ((2 * params.m_base + params.m_iv + (params.m_ev >> 2)) * params.m_level / 100) + params.m_level + 10;
}
template <Generation Gen>
uint16_t CalculateStat(StatCalculatorParams params)
{
// OtherStat=⌊(⌊(2×Base+IV+⌊EV4⌋)×Level100⌋+5)×Nature⌋
return (((2 * params.m_base + params.m_iv + (params.m_ev >> 2)) * params.m_level / 100) + 5) * params.m_nature.getMultiplier10(params.m_statIndex) / 10;
}
} // namespace PokEng
#endif // STATS_H