Merge commit '74b304de1e7d4c84e22b428814d2a61e2697749e' into Prompt/StatSystem
This commit is contained in:
93
examples/pokemon_stats_example.cpp
Normal file
93
examples/pokemon_stats_example.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "pokemon_battle_sim.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
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<int>(pokemon.getFriendship()) << "\n";
|
||||
std::cout << "Primary Type: " << static_cast<int>(pokemon.getPrimaryType()) << "\n";
|
||||
if (pokemon.getSecondaryType() != Type::NONE) {
|
||||
std::cout << "Secondary Type: " << static_cast<int>(pokemon.getSecondaryType()) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void printNatureEffect(Nature nature) {
|
||||
std::cout << "\nNature: " << static_cast<int>(nature);
|
||||
StatIndex increased = NatureUtils::getIncreasedStat(nature);
|
||||
StatIndex decreased = NatureUtils::getDecreasedStat(nature);
|
||||
|
||||
if (increased != StatIndex::HP) {
|
||||
std::cout << " (+10% to stat " << static_cast<int>(increased) << ")";
|
||||
}
|
||||
if (decreased != StatIndex::HP) {
|
||||
std::cout << " (-10% to stat " << static_cast<int>(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<Generation::I>(), 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "pokemon_stats.h"
|
||||
#include <string>
|
||||
|
||||
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<Generation Gen = Generation::III>
|
||||
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<Gen>();
|
||||
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
|
||||
|
||||
167
include/core/pokemon_stats.h
Normal file
167
include/core/pokemon_stats.h
Normal file
@@ -0,0 +1,167 @@
|
||||
#ifndef POKEMON_STATS_H
|
||||
#define POKEMON_STATS_H
|
||||
|
||||
#include "config.h"
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
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<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
|
||||
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<Generation Gen>
|
||||
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<Generation Gen>
|
||||
static typename std::enable_if<Gen == Generation::I || Gen == Generation::II, uint16_t>::type
|
||||
calculateHP(uint16_t base, uint8_t dv, uint16_t statExp, uint8_t level);
|
||||
|
||||
template<Generation Gen>
|
||||
static typename std::enable_if<Gen == Generation::I || Gen == Generation::II, uint16_t>::type
|
||||
calculateStat(uint16_t base, uint8_t dv, uint16_t statExp, uint8_t level);
|
||||
|
||||
// Generation III+ stat calculation
|
||||
template<Generation Gen>
|
||||
static typename std::enable_if<Gen >= Generation::III, uint16_t>::type
|
||||
calculateHP(uint16_t base, uint8_t iv, uint8_t ev, uint8_t level);
|
||||
|
||||
template<Generation Gen>
|
||||
static typename std::enable_if<Gen >= 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<uint16_t>(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace PokEng
|
||||
|
||||
#endif // POKEMON_STATS_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"
|
||||
|
||||
|
||||
224
src/core/pokemon_stats.cpp
Normal file
224
src/core/pokemon_stats.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user