From 6fe97c56262df9e9f19343ec2546269b04276d26 Mon Sep 17 00:00:00 2001 From: aed3 <32887801+aed3@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:29:05 -0500 Subject: [PATCH 1/2] - Implemented primary and secondary boost and status effects. - Effect information for a move is now stored directly on the move entity instead in its own entity. I did this since no move has both a primary and secondary effect and only one move has multiple secondary effects. This change will make filtering and applying effects much simpler. Triple Arrows will be handled a different way. - Changed test Ampharos' default ability to Plus so there's only one place paralysis can come from in the test result. Otherwise, calculating the ideal probability is impossible. - Added effect target components since effect targets and sources are not the same as the current move's source and target. - Changed multiple Targets components to single target components. The old way was assuming moves with multiple targets would share move data. Since multi-target moves aren't the norm, that caused more data to be moved around than. - Status moves are explicitly ignored in calc damage now. - Updated Simulate Turn single battle test to factor in Thunderbolt's paralysis chance. --- include/PokeSim.hpp | 1098 +++++++++++------ src/AnalyzeEffect/AnalyzeEffect.cpp | 36 +- .../AnalyzeEffectDebugChecks.hpp | 4 +- .../Setup/AnalyzeEffectInputSetup.cpp | 5 +- src/Battle/Clone/Clone.cpp | 6 +- src/Battle/Helpers/Helpers.cpp | 13 +- src/Battle/ManageBattleState.cpp | 19 +- src/Battle/ManageBattleState.hpp | 8 +- src/Battle/Pokemon/ManagePokemonState.cpp | 131 +- src/Battle/Pokemon/ManagePokemonState.hpp | 11 +- src/Battle/Setup/EmplaceTagFromEnum.cpp | 5 + src/Battle/Setup/EmplaceTagFromEnum.hpp | 6 + src/Battle/Setup/PokemonStateSetup.cpp | 10 +- src/CalcDamage/CalcDamage.cpp | 51 +- src/CalcDamage/CalcDamageDebugChecks.hpp | 12 +- src/CalcDamage/Setup/CalcDamageInputSetup.cpp | 1 - src/Components/AnalyzeEffect/Aliases.hpp | 2 +- src/Components/CalcDamage/Aliases.hpp | 2 +- src/Components/EntityHolders/Current.hpp | 27 +- src/Components/EntityHolders/MoveEffect.hpp | 11 - src/Components/EntityHolders/headers.hpp | 1 - .../SimulateTurn/MoveHitStepTags.hpp | 1 + src/Components/Tags/AbilityTags.hpp | 1 + src/Components/Tags/MoveTags.hpp | 13 +- src/Components/Tags/PokemonTags.hpp | 2 + src/Components/headers.hpp | 1 - src/Pokedex/Abilities/AbilityEvents.cpp | 10 +- src/Pokedex/Abilities/Plus.hpp | 34 + src/Pokedex/Abilities/headers.hpp | 1 + src/Pokedex/EnumToTag/AbililtyEnumToTag.hpp | 3 +- src/Pokedex/EnumToTag/ItemEnumToTag.hpp | 2 +- src/Pokedex/EnumToTag/NatureEnumToTag.hpp | 3 +- src/Pokedex/EnumToTag/StatusEnumToTag.hpp | 13 +- src/Pokedex/EnumToTag/TypeEnumToTag.hpp | 52 + src/Pokedex/EnumToTag/headers.hpp | 3 +- src/Pokedex/Items/ItemEvents.cpp | 1 - src/Pokedex/Moves/QuiverDance.hpp | 2 +- src/Pokedex/Pokedex.hpp | 11 - src/Pokedex/Setup/GetMoveBuild.cpp | 86 +- src/Pokedex/Setup/MoveDexDataSetup.cpp | 28 +- src/Pokedex/Setup/MoveDexDataSetup.hpp | 41 +- src/Pokedex/Species/Pangoro.hpp | 2 +- src/Pokedex/TypeChart.hpp | 2 +- src/Pokedex/headers.hpp | 2 + src/SimulateTurn/RandomChance.hpp | 36 +- src/SimulateTurn/SimulateTurn.cpp | 13 +- src/SimulateTurn/SimulateTurn.hpp | 4 - src/Simulation/MoveHitSteps.cpp | 210 ++-- src/Simulation/MoveHitSteps.hpp | 18 +- src/Simulation/RunEvent.cpp | 62 +- src/Simulation/RunEvent.hpp | 14 + src/Simulation/SimulationSetup.cpp | 5 +- src/Simulation/SimulationSetupDebugChecks.hpp | 12 +- src/Utilities/ArgumentChecks.cpp | 51 +- src/Utilities/ArgumentChecks.hpp | 23 +- tests/AnalyzeEffectTest.cpp | 6 +- tests/BattleStateTest.cpp | 2 +- tests/CalcDamageTest.cpp | 2 +- tests/PokedexTest.cpp | 59 +- tests/SimulateTurnsTest.cpp | 75 +- tests/SimulationSetupTest.cpp | 2 +- tests/Tests.hpp | 2 +- 62 files changed, 1611 insertions(+), 758 deletions(-) delete mode 100644 src/Components/EntityHolders/MoveEffect.hpp create mode 100644 src/Pokedex/Abilities/Plus.hpp create mode 100644 src/Pokedex/EnumToTag/TypeEnumToTag.hpp diff --git a/include/PokeSim.hpp b/include/PokeSim.hpp index 4c2d8c4..c80b14c 100644 --- a/include/PokeSim.hpp +++ b/include/PokeSim.hpp @@ -112,7 +112,6 @@ * src/Components/EntityHolders/ChoiceLock.hpp * src/Components/EntityHolders/FoeSide.hpp * src/Components/EntityHolders/LastUsedMove.hpp - * src/Components/EntityHolders/MoveEffect.hpp * src/Components/EntityHolders/MoveSlots.hpp * src/Components/EntityHolders/Pokemon.hpp * src/Components/EntityHolders/Side.hpp @@ -205,6 +204,7 @@ * src/SimulateTurn/SimulateTurn.hpp * src/Simulation/Simulation.cpp * src/Battle/Helpers/IntegerModify.hpp + * src/Pokedex/Abilities/Plus.hpp * src/Pokedex/Abilities/Static.hpp * src/Pokedex/Effects/ChoiceLock.hpp * src/Pokedex/Items/AssaultVest.hpp @@ -268,6 +268,7 @@ * src/Pokedex/EnumToTag/ItemEnumToTag.hpp * src/Pokedex/EnumToTag/NatureEnumToTag.hpp * src/Pokedex/EnumToTag/StatusEnumToTag.hpp + * src/Pokedex/EnumToTag/TypeEnumToTag.hpp * src/Battle/Setup/EmplaceTagFromEnum.cpp * src/Battle/Setup/BattleStateSetup.cpp * src/Battle/Pokemon/ManagePokemonState.cpp @@ -18596,12 +18597,10 @@ struct NextAction { struct CurrentActionTargets { types::targets val{}; - const types::entity& only() const { - POKESIM_REQUIRE( - val.size() == 1U, - "This method is supposed to get the first target when there's only one target stored."); - return val[0]; - }; +}; + +struct CurrentActionTarget { + types::entity val{}; }; struct CurrentActionSource { @@ -18619,6 +18618,23 @@ struct CurrentActionMovesAsSource { struct CurrentActionMoveSlot { types::entity val{}; }; + +struct CurrentEffectTarget { + types::entity val{}; +}; + +struct CurrentEffectSource { + types::entity val{}; +}; + +struct CurrentEffectsAsTarget { + types::entityVector val{}; +}; + +struct CurrentEffectsAsSource { + types::entityVector val{}; +}; + } // namespace pokesim /////////////// END OF src/Components/EntityHolders/Current.hpp //////////////// @@ -18642,7 +18658,7 @@ struct CurrentActionMoveSource {}; namespace pokesim::analyze_effect { using Attacker = CurrentActionSource; -using Defenders = CurrentActionTargets; +using Defender = CurrentActionTarget; using UsedMovesAsAttacker = CurrentActionMovesAsSource; using UsedMovesAsDefender = CurrentActionMovesAsTarget; @@ -18828,7 +18844,7 @@ struct BasePower { namespace pokesim::calc_damage { using Attacker = CurrentActionSource; -using Defenders = CurrentActionTargets; +using Defender = CurrentActionTarget; using UsedMovesAsAttacker = CurrentActionMovesAsSource; using UsedMovesAsDefender = CurrentActionMovesAsTarget; @@ -19102,18 +19118,6 @@ struct LastUsedMove { ///////////// END OF src/Components/EntityHolders/LastUsedMove.hpp ///////////// -///////////// START OF src/Components/EntityHolders/MoveEffect.hpp ///////////// - -namespace pokesim { -// Contains the entity of what the primary or secondary effect of a move does. -struct MoveEffect { - bool primary = true; - types::entity val{}; -}; -} // namespace pokesim - -////////////// END OF src/Components/EntityHolders/MoveEffect.hpp ////////////// - ///////////// START OF src/Components/EntityHolders/MoveSlots.hpp ////////////// namespace pokesim { @@ -19696,6 +19700,7 @@ struct Terastallize {}; namespace pokesim::tags::internal { struct MoveHits {}; +struct RunEffect {}; } // namespace pokesim::tags::internal //////////// END OF src/Components/SimulateTurn/MoveHitStepTags.hpp //////////// @@ -19944,6 +19949,7 @@ namespace pokesim::ability::tags { struct Defiant {}; struct Infiltrator {}; struct IronFist {}; +struct Plus {}; struct Static {}; struct SweetVeil {}; struct Trace {}; @@ -19982,7 +19988,8 @@ struct LifeOrb {}; ////////////////// START OF src/Components/Tags/MoveTags.hpp /////////////////// -namespace pokesim::move::tags { +namespace pokesim::move { +namespace tags { // Move Category Tag struct Physical {}; // Move Category Tag @@ -20003,14 +20010,18 @@ struct VariableHitCount {}; struct AccuracyDependentHitCount {}; struct Disabled {}; +} // namespace tags -namespace effect { +namespace effect::tags { // Move Effect Participant Tag: Who the effect will affect struct MoveTarget {}; // Move Effect Participant Tag: Who created the effect struct MoveSource {}; -} // namespace effect -} // namespace pokesim::move::tags + +struct Primary {}; +struct Secondary {}; +} // namespace effect::tags +} // namespace pokesim::move /////////////////// END OF src/Components/Tags/MoveTags.hpp //////////////////// @@ -20063,6 +20074,8 @@ struct SpaStatUpdateRequired {}; struct SpeStatUpdateRequired {}; struct Fainted {}; + +struct CanSetStatus {}; } // namespace pokesim::tags ////////////////// END OF src/Components/Tags/PokemonTags.hpp ////////////////// @@ -20401,13 +20414,17 @@ struct ChoiceLock; struct CurrentAction; struct NextAction; struct CurrentActionTargets; +struct CurrentActionTarget; struct CurrentActionSource; struct CurrentActionMovesAsTarget; struct CurrentActionMovesAsSource; struct CurrentActionMoveSlot; +struct CurrentEffectTarget; +struct CurrentEffectSource; +struct CurrentEffectsAsTarget; +struct CurrentEffectsAsSource; struct FoeSide; struct LastUsedMove; -struct MoveEffect; struct MoveSlots; struct Pokemon; struct Side; @@ -20632,6 +20649,9 @@ inline void check(const NextAction&, const types::registry&); template <> inline void check(const CurrentActionTargets&, const types::registry&); +template <> +inline void check(const CurrentActionTarget&, const types::registry&); + template <> inline void check(const CurrentActionSource&, const types::registry&); @@ -20644,14 +20664,24 @@ inline void check(const CurrentActionMovesAsSource&, const types::registry&); template <> inline void check(const CurrentActionMoveSlot&, const types::registry&); +template <> +inline void check(const CurrentEffectTarget&, const types::registry&); + +template <> +inline void check(const CurrentEffectSource&, const types::registry&); + +template <> +inline void check(const CurrentEffectsAsTarget&, const types::registry&); + +template <> +inline void check(const CurrentEffectsAsSource&, const types::registry&); + template <> inline void check(const FoeSide&, const types::registry&); template <> inline void check(const LastUsedMove&, const types::registry&); -// template <> void check(const MoveEffect&, const types::registry&); - template <> inline void check(const MoveSlots&, const types::registry&); @@ -21137,16 +21167,16 @@ inline void checkMoveSlot(types::entity moveSlotEntity, const types::registry& r inline void checkActionMove(types::entity moveEntity, const types::registry& registry) { types::registry::checkEntity(moveEntity, registry); POKESIM_REQUIRE_NM(has(moveEntity, registry)); - POKESIM_REQUIRE_NM(has(moveEntity, registry)); + POKESIM_REQUIRE_NM(has(moveEntity, registry)); POKESIM_REQUIRE_NM(has(moveEntity, registry)); POKESIM_REQUIRE_NM(has(moveEntity, registry)); POKESIM_REQUIRE_NM(has(moveEntity, registry) != has(moveEntity, registry)); const auto& [attacker, defender, battle, typeName] = - registry.get(moveEntity); + registry.get(moveEntity); POKESIM_REQUIRE_NM(has(attacker.val, registry)); - POKESIM_REQUIRE_NM(has(defender.only(), registry)); + POKESIM_REQUIRE_NM(has(defender.val, registry)); POKESIM_REQUIRE_NM(has(battle.val, registry)); check(typeName); @@ -21222,7 +21252,7 @@ inline void check(const analyze_effect::Inputs& inputs, const types::registry& r types::registry::checkEntity(input, registry); POKESIM_REQUIRE_NM(has(input, registry)); POKESIM_REQUIRE_NM(has(input, registry)); - POKESIM_REQUIRE_NM(has(input, registry)); + POKESIM_REQUIRE_NM(has(input, registry)); POKESIM_REQUIRE_NM(has(input, registry)); POKESIM_REQUIRE_NM(has(input, registry)); POKESIM_REQUIRE_NM(has(input, registry)); @@ -21242,14 +21272,14 @@ inline void check(const analyze_effect::Inputs& inputs, const types::registry& r pseudoWeather || sideCondition || status || terrain || volatileCondition || weather || atkBoost || defBoost || spaBoost || spdBoost || speBoost); - auto const& [attacker, defenders, target, move] = registry.get< + auto const& [attacker, defender, target, move] = registry.get< analyze_effect::Attacker, - analyze_effect::Defenders, + analyze_effect::Defender, analyze_effect::EffectTarget, analyze_effect::EffectMove>(input); checkPokemon(attacker.val, registry); - checkPokemon(defenders.only(), registry); + checkPokemon(defender.val, registry); checkPokemon(target.val, registry); check(move); @@ -21283,17 +21313,17 @@ inline void check(const analyze_effect::MovePair& movePair, const types::registr } const auto& [parentAttacker, parentDefenders, parentBattle, parentTypeName, parentMoveName] = - registry.get(parentBattleMove); + registry.get(parentBattleMove); const auto& [childAttacker, childDefenders, childBattle, childTypeName, childMoveName] = - registry.get(childBattleMove); + registry.get(childBattleMove); if (has(childBattle.val, registry)) { POKESIM_REQUIRE_NM(parentAttacker.val != childAttacker.val); - POKESIM_REQUIRE_NM(parentDefenders.only() != childDefenders.only()); + POKESIM_REQUIRE_NM(parentDefenders.val != childDefenders.val); POKESIM_REQUIRE_NM(parentBattle.val != childBattle.val); } else { POKESIM_REQUIRE_NM(parentAttacker.val == childAttacker.val); - POKESIM_REQUIRE_NM(parentDefenders.only() == childDefenders.only()); + POKESIM_REQUIRE_NM(parentDefenders.val == childDefenders.val); POKESIM_REQUIRE_NM(parentBattle.val == childBattle.val); } @@ -21484,6 +21514,11 @@ inline void check(const CurrentActionTargets& targets, const types::registry& re } } +template <> +inline void check(const CurrentActionTarget& target, const types::registry& registry) { + checkPokemon(target.val, registry); +} + template <> inline void check(const CurrentActionSource& source, const types::registry& registry) { checkPokemon(source.val, registry); @@ -21508,6 +21543,30 @@ inline void check(const CurrentActionMoveSlot& move, const types::registry& regi checkMoveSlot(move.val, registry); } +template <> +inline void check(const CurrentEffectTarget& target, const types::registry& registry) { + checkPokemon(target.val, registry); +} + +template <> +inline void check(const CurrentEffectSource& source, const types::registry& registry) { + checkPokemon(source.val, registry); +} + +template <> +inline void check(const CurrentEffectsAsTarget& effects, const types::registry& registry) { + for (types::entity effect : effects.val) { + types::registry::checkEntity(effect, registry); + } +} + +template <> +inline void check(const CurrentEffectsAsSource& effects, const types::registry& registry) { + for (types::entity effect : effects.val) { + types::registry::checkEntity(effect, registry); + } +} + template <> inline void check(const FoeSide& foeSide, const types::registry& registry) { checkSide(foeSide.val, registry); @@ -22600,6 +22659,11 @@ namespace status::tags { // Assigns a status' tag to a handle inline void emplaceTagFromEnum(dex::Status status, types::handle handle); } // namespace status::tags + +namespace type::tags { +// Assigns a type' tag to a handle +inline void emplaceTagFromEnum(dex::Type type, types::handle handle); +} // namespace type::tags } // namespace pokesim //////////////// END OF src/Battle/Setup/EmplaceTagFromEnum.hpp //////////////// @@ -22699,7 +22763,6 @@ struct TypeChart : private TypeChartBase { using enumType = std::underlying_type_t; - public: constexpr TypeChart(const constructorType partialChart) : TypeChartBase() { for (auto& ratios : *this) { for (auto& effectiveness : ratios) { @@ -22714,6 +22777,7 @@ struct TypeChart : private TypeChartBase { } } + public: constexpr TypeChart(GameMechanics gameMechanics) : TypeChart(TypeChart::pickForMechanics(gameMechanics)) {} constexpr TypeEffectiveness effectiveness(dex::Type attacking, dex::Type defending) const { @@ -23110,11 +23174,6 @@ class Pokedex { return dexRegistry.get(movesMap.at(move)); } - template - auto getEffectData(MoveEffect effect) const { - return dexRegistry.get(effect.val); - } - template bool speciesHas(dex::Species species) const { return dexRegistry.all_of(speciesMap.at(species)); @@ -23130,11 +23189,6 @@ class Pokedex { return dexRegistry.all_of(movesMap.at(move)); } - template - bool effectHas(MoveEffect effect) const { - return dexRegistry.all_of(effect.val); - } - constexpr const TypeChart& typeChart() const { return constantTypeChart; } inline types::entity buildActionMove(dex::Move move, types::registry& registry) const; @@ -24238,14 +24292,14 @@ struct SimulationSetupChecks { const auto& p1Team = registry->get(registry->get(battleEntity).p1()).val; const auto& p2Team = registry->get(registry->get(battleEntity).p2()).val; - const auto& [battle, moveName, attacker, defenders] = - registry->get(calcDamageEntity); + const auto& [battle, moveName, attacker, defender] = + registry->get(calcDamageEntity); types::entity setupInfoAttacker = targetSlotToEntity(calcDamageInputInfo.attackerSlot, p1Team, p2Team); types::entity setupInfoDefender = targetSlotToEntity(calcDamageInputInfo.defenderSlot, p1Team, p2Team); POKESIM_REQUIRE_NM(battle.val == battleEntity); POKESIM_REQUIRE_NM(attacker.val == setupInfoAttacker); - POKESIM_REQUIRE_NM(defenders.only() == setupInfoDefender); + POKESIM_REQUIRE_NM(defender.val == setupInfoDefender); POKESIM_REQUIRE_NM( std::find(calcDamageInputInfo.moves.begin(), calcDamageInputInfo.moves.end(), moveName.name) != calcDamageInputInfo.moves.end()); @@ -24272,11 +24326,11 @@ struct SimulationSetupChecks { const auto& p1Team = registry->get(registry->get(battleEntity).p1()).val; const auto& p2Team = registry->get(registry->get(battleEntity).p2()).val; - const auto& [battle, effectMove, attacker, defenders, effectTarget] = registry->get< + const auto& [battle, effectMove, attacker, defender, effectTarget] = registry->get< Battle, analyze_effect::EffectMove, analyze_effect::Attacker, - analyze_effect::Defenders, + analyze_effect::Defender, analyze_effect::EffectTarget>(analyzeEffectEntity); types::entity setupInfoAttacker = targetSlotToEntity(analyzeEffectInputInfo.attackerSlot, p1Team, p2Team); types::entity setupInfoDefender = targetSlotToEntity(analyzeEffectInputInfo.defenderSlot, p1Team, p2Team); @@ -24284,7 +24338,7 @@ struct SimulationSetupChecks { POKESIM_REQUIRE_NM(battle.val == battleEntity); POKESIM_REQUIRE_NM(attacker.val == setupInfoAttacker); - POKESIM_REQUIRE_NM(defenders.only() == setupInfoDefender); + POKESIM_REQUIRE_NM(defender.val == setupInfoDefender); POKESIM_REQUIRE_NM(effectTarget.val == setupInfoEffectTarget); POKESIM_REQUIRE_NM( @@ -24590,10 +24644,10 @@ inline std::tuple Simulation::createInitialBattl if (battleInfo.runWithSimulateTurn) { battleStateSetup.setProperty(); } - if (battleInfo.runWithCalculateDamage) { + if (battleInfo.runWithCalculateDamage || !battleInfo.damageCalculations.empty()) { battleStateSetup.setProperty(); } - if (battleInfo.runWithAnalyzeEffect) { + if (battleInfo.runWithAnalyzeEffect || !battleInfo.effectsToAnalyze.empty()) { battleStateSetup.setProperty(); } @@ -24651,6 +24705,7 @@ inline void Simulation::createAnalyzeEffectInput( POKESIM_REQUIRE(inputInfo.defenderSlot != Slot::NONE, "An effect analysis must have a defender."); POKESIM_REQUIRE(inputInfo.effectTarget != Slot::NONE, "An effect analysis must have a effect target."); POKESIM_REQUIRE(!inputInfo.moves.empty(), "An effect analysis must include a move."); + const auto& effect = inputInfo.effect; const auto& boostEffect = inputInfo.boostEffect; POKESIM_REQUIRE( @@ -24867,7 +24922,7 @@ inline void run(Simulation& simulation); namespace pokesim { class Simulation; struct CurrentActionSource; -struct CurrentActionTargets; +struct CurrentActionTarget; struct CurrentActionMoveSlot; struct Damage; struct Pp; @@ -24881,20 +24936,23 @@ struct Spe; struct CurrentHp; } // namespace stat -inline void setStatus(types::handle pokemonHandle, dex::Status status); +inline void checkIfStatusIsSettable(Simulation& simulation); +inline void trySetStatus(Simulation& simulation); inline void clearStatus(types::handle pokemonHandle); inline void deductPp(Pp& pp); -inline void setLastMoveUsed(types::registry& registry, const CurrentActionSource& source, const CurrentActionMoveSlot& move); +inline void setLastMoveUsed(types::registry& registry, CurrentActionSource source, const CurrentActionMoveSlot& move); inline void resetEffectiveAtk(types::handle handle, stat::Atk atk); inline void resetEffectiveDef(types::handle handle, stat::Def def); inline void resetEffectiveSpa(types::handle handle, stat::Spa spa); inline void resetEffectiveSpd(types::handle handle, stat::Spd spd); inline void resetEffectiveSpe(types::handle handle, stat::Spe spe); -inline void applyDamageToHp(types::registry& registry, const Damage& damage, CurrentActionTargets& targets); +inline void applyDamageToHp(types::registry& registry, const Damage& damage, CurrentActionTarget target); inline void applyStatBoost(types::stat& stat, types::boost boost); +inline void tryBoost(Simulation& simulation); + inline void updateAllStats(Simulation& simulation); inline void updateAtk(Simulation& simulation); inline void updateDef(Simulation& simulation); @@ -24926,10 +24984,6 @@ inline void setDamageRollModifiers(Simulation& simulation); namespace pokesim { class Simulation; -class Pokedex; -struct Battle; -struct CurrentActionTargets; -struct CurrentActionSource; namespace simulate_turn { inline void run(Simulation& simulation); @@ -25103,6 +25157,39 @@ void chainToModifier(types::eventModifier& eventModifier, Multiplier multiplier) ///////////////// END OF src/Battle/Helpers/IntegerModify.hpp ////////////////// +/////////////////// START OF src/Pokedex/Abilities/Plus.hpp //////////////////// + +#include + +namespace pokesim { +struct EventModifier; +} // namespace pokesim + +namespace pokesim::dex { +namespace internal { +struct PlusEvents { + inline static void onModifySpA(types::handle pokemonHandle, EventModifier& eventModifier); +}; +} // namespace internal + +template +struct Plus : internal::PlusEvents { + static constexpr dex::Ability name = dex::Ability::PLUS; + + static constexpr types::effectMultiplier onModifySpaModifier = 1.5F; + struct Strings { + static constexpr std::string_view name = "Plus"; + static constexpr std::string_view smogonId = "plus"; + }; +}; + +namespace latest { +using Plus = dex::Plus; +} +} // namespace pokesim::dex + +//////////////////// END OF src/Pokedex/Abilities/Plus.hpp ///////////////////// + ////////////////// START OF src/Pokedex/Abilities/Static.hpp /////////////////// #include @@ -25341,6 +25428,17 @@ inline void runModifyAccuracyEvent(Simulation& simulation); inline void runModifyCritBoostEvent(Simulation& simulation); inline void runBasePowerEvent(Simulation& simulation); inline void runDamagingHitEvent(Simulation& simulation); +inline void runModifySecondariesEvent(Simulation& simulation); + +template +inline void runStatusImmunityEvent(Simulation& simulation); +inline void runAfterSetStatusEvent(Simulation& simulation); + +inline void runChangeBoostEvent(Simulation& simulation); +inline void runTryBoostEvent(Simulation& simulation); +template +inline void runAfterEachBoostEvent(Simulation& simulation); +inline void runAfterBoostEvent(Simulation& simulation); inline void runModifyMove(Simulation& simulation); inline void runDisableMove(Simulation& simulation); @@ -25349,6 +25447,9 @@ inline void runModifyDef(Simulation& simulation); inline void runModifySpa(Simulation& simulation); inline void runModifySpd(Simulation& simulation); inline void runModifySpe(Simulation& simulation); + +inline void runStartSleep(Simulation& simulation); +inline void runStartFreeze(Simulation& simulation); } // namespace pokesim ////////////////////// END OF src/Simulation/RunEvent.hpp ////////////////////// @@ -25449,7 +25550,7 @@ internal::RegistryContainer::SelectionFunction getMoveEventPokemonSelector() { if constexpr ( SelectAnyPokemon || std::disjunction_v...>) { - entities.insert(registry.get(entity).only()); + entities.insert(registry.get(entity).val); } }); @@ -25458,29 +25559,58 @@ internal::RegistryContainer::SelectionFunction getMoveEventPokemonSelector() { } } // namespace -inline void runAccuracyEvent(Simulation& /*simulation*/) {} +inline void runAccuracyEvent(Simulation&) {} -inline void runModifyAccuracyEvent(Simulation& /*simulation*/) {} +inline void runModifyAccuracyEvent(Simulation&) {} -inline void runModifyCritBoostEvent(Simulation& /*simulation*/) {} +inline void runModifyCritBoostEvent(Simulation&) {} -inline void runBasePowerEvent(Simulation& /*simulation*/) {} +inline void runBasePowerEvent(Simulation&) {} inline void runDamagingHitEvent(Simulation& simulation) { dex::latest::Static::onDamagingHit(simulation); } +inline void runModifySecondariesEvent(Simulation&) {} +template +void runStatusImmunityEvent(Simulation&) { + // This is where Corrosion (will add CanSetStatus back), the `onSetStatus` events that only block status (i.e. Misty + // Terrain), and all the `onImmunity` events that relate to non-volatile status conditions will go +} + +template void runStatusImmunityEvent(Simulation&); +template void runStatusImmunityEvent(Simulation&); +template void runStatusImmunityEvent(Simulation&); +template void runStatusImmunityEvent(Simulation&); +template void runStatusImmunityEvent(Simulation&); +template void runStatusImmunityEvent(Simulation&); + +inline void runAfterSetStatusEvent(Simulation&) {} + +inline void runChangeBoostEvent(Simulation&) {} +inline void runTryBoostEvent(Simulation&) {} + +template +void runAfterEachBoostEvent(Simulation&) {} +template void runAfterEachBoostEvent(Simulation&); +template void runAfterEachBoostEvent(Simulation&); +template void runAfterEachBoostEvent(Simulation&); +template void runAfterEachBoostEvent(Simulation&); +template void runAfterEachBoostEvent(Simulation&); + +inline void runAfterBoostEvent(Simulation&) {} + inline void runModifyMove(Simulation& simulation) { - internal::SelectForPokemonView<> selectedPokemon{ - simulation, - getMoveEventPokemonSelector()}; + // internal::SelectForPokemonView<> selectedPokemon{ + // simulation, + // getMoveEventPokemonSelector()}; - simulation.viewForSelectedPokemon< + simulation.view< dex::latest::ChoiceScarf::onSourceModifyMove, Tags, entt::exclude_t>(); - simulation.viewForSelectedPokemon< + simulation.view< dex::latest::ChoiceSpecs::onSourceModifyMove, Tags, entt::exclude_t>(); @@ -25497,8 +25627,14 @@ inline void runModifyDef(Simulation&) {} inline void runModifySpa(Simulation& simulation) { simulation.addToEntities(); + // Priority 1 simulation.viewForSelectedPokemon>(); + // Priority 5 + if (simulation.battleFormat() == BattleFormat::DOUBLES_BATTLE_FORMAT) { + simulation.viewForSelectedPokemon>(); + } + simulation.viewForSelectedPokemon>(); simulation.registry.clear(); } @@ -25524,6 +25660,11 @@ inline void runModifySpe(Simulation& simulation) { dex::latest::Static::onModifySpe, Tags /*, entt::exclude_t*/>(); } + +inline void runStartSleep(Simulation&) {} +inline void runStartFreeze(Simulation&) { + // Just Shaymin +} } // namespace pokesim ////////////////////// END OF src/Simulation/RunEvent.cpp ////////////////////// @@ -25613,17 +25754,19 @@ void setBinaryChanceFromChanceLimit( } } -template +template void setRandomBinaryChoice( - types::handle handle, const Component& percentChance, const Battle& battle, const simulate_turn::Options& options) { + types::handle handle, const PercentChanceComponent& percentChance, const Battle& battle, + const simulate_turn::Options& options) { types::probability probability = handle.registry()->get(battle.val).val; setBinaryChanceFromChanceLimit(handle, percentChance.val, percentChance.val, probability, options); } -template +template void setReciprocalRandomBinaryChoice( - types::handle handle, const Component& percentChance, const Battle& battle, const simulate_turn::Options& options) { + types::handle handle, const PercentChanceComponent& percentChance, const Battle& battle, + const simulate_turn::Options& options) { types::percentChance passChance = MechanicConstants::PercentChance::MAX / percentChance.val; types::probability probability = handle.registry()->get(battle.val).val; @@ -25709,33 +25852,35 @@ void setRandomChoice( } } -template +template void setRandomBinaryChoice(Simulation& simulation) { const auto& options = simulation.simulateTurnOptions; if (simulation.battleFormat() == BattleFormat::SINGLES_BATTLE_FORMAT) { - simulation.view, Tags>( - options); + simulation + .view, Tags>( + options); } else { - simulation.view, Tags>( - options); + simulation + .view, Tags>( + options); } } -template +template void setReciprocalRandomBinaryChoice(Simulation& simulation) { const auto& options = simulation.simulateTurnOptions; if (simulation.battleFormat() == BattleFormat::SINGLES_BATTLE_FORMAT) { - simulation - .view, Tags>( - options); + simulation.view< + internal::setReciprocalRandomBinaryChoice, + Tags>(options); } else { - simulation - .view, Tags>( - options); + simulation.view< + internal::setReciprocalRandomBinaryChoice, + Tags>(options); } } @@ -25789,27 +25934,11 @@ inline void clearRandomChanceResult(Simulation& simulation); namespace pokesim { class Simulation; -struct CurrentActionSource; -struct CurrentActionTargets; -struct Accuracy; -struct Battle; -struct HitCount; - -namespace internal { -inline void deductMoveHitCount(types::handle moveHandle, HitCount& hitCount); - -template -inline void runMoveHitCheck(Simulation& simulation); -inline void postMoveHitCheck(Simulation& simulation); -inline void updateCurrentActionTargets(types::registry& registry, CurrentActionTargets& targets); -inline void removeFailedHitTargets( - types::handle moveTarget, const CurrentActionTargets& targets, const CurrentActionSource& source); -} // namespace internal inline void setMoveHitCount(Simulation& simulation); -inline void trySetStatusFromEffect(Simulation& simulation); inline void applyDamage(Simulation& simulation); +inline void runPrimaryMoveEffects(Simulation& simulation); inline void runSecondaryMoveEffects(Simulation& simulation); inline void accuracyCheck(Simulation& simulation); inline void moveHitLoop(Simulation& simulation); @@ -25825,6 +25954,110 @@ inline void runMoveHitChecks(Simulation& simulation); namespace pokesim { +namespace { +inline void deductMoveHitCount(types::handle moveHandle, HitCount& hitCount) { + POKESIM_REQUIRE(hitCount.val > 0U, "A hit count shouldn't be decremented if it's already 0."); + hitCount.val--; + if (!hitCount.val) { + moveHandle.remove(); + } +} + +inline void updateCurrentActionTargets(types::registry& registry, CurrentActionTargets& targets) { + types::activePokemonIndex deleteCount = 0U; + for (types::entity& target : targets.val) { + if (!registry.all_of(target)) { + types::activePokemonIndex swapIndex = targets.val.size() - 1 - deleteCount; + POKESIM_REQUIRE(swapIndex < targets.val.size(), "Swap index out of bounds."); + std::swap(target, targets.val[swapIndex]); + deleteCount++; + } + } + + targets.val.pop_count(deleteCount); +} + +inline void removeFailedHitTargets(types::handle moveHandle, CurrentActionTarget target, CurrentActionSource source) { + types::registry& registry = *moveHandle.registry(); + + registry.remove(target.val); + + CurrentActionMovesAsTarget& targetedMoves = registry.get(target.val); + auto newTargetedMoveEnd = std::remove(targetedMoves.val.begin(), targetedMoves.val.end(), moveHandle.entity()); + targetedMoves.val.erase(newTargetedMoveEnd, targetedMoves.val.end()); + + CurrentActionMovesAsSource& sourcedMoves = registry.get(source.val); + auto newSourcedMoveEnd = std::remove(sourcedMoves.val.begin(), sourcedMoves.val.end(), moveHandle.entity()); + sourcedMoves.val.erase(newSourcedMoveEnd, sourcedMoves.val.end()); +} + +inline void postMoveHitCheck(Simulation& simulation) { + auto removedMoves = + simulation.view, entt::exclude_t>(); + simulation.registry.destroy(removedMoves.begin(), removedMoves.end()); + simulation.registry.clear(); + simulation.view(); +} + +template +void runMoveHitCheck(Simulation& simulation) { + simulation.addToEntities(); + + internal::SelectForCurrentActionMoveView selectedMoves{simulation}; + if (selectedMoves.hasNoneSelected()) { + return; + } + + Function(simulation); + selectedMoves.deselect(); + + postMoveHitCheck(simulation); + simulation.registry.clear(); +} + +inline void trySetStatusFromEffect(Simulation&) {} +inline void trySetVolatileFromEffect(Simulation&) {} +inline void trySetSideConditionFromEffect(Simulation&) {} +inline void trySetSlotConditionFromEffect(Simulation&) {} +inline void trySetWeatherFromEffect(Simulation&) {} +inline void trySetTerrainFromEffect(Simulation&) {} +inline void trySetPseudoWeatherFromEffect(Simulation&) {} + +inline void setEffectSource(types::handle handle, CurrentActionSource source) { + handle.emplace(source.val); + handle.registry()->get_or_emplace(source.val).val.push_back(handle.entity()); +} + +template +void setEffectTarget(types::handle handle, TargetEntityHolder target) { + handle.emplace(target.val); + handle.registry()->get_or_emplace(target.val).val.push_back(handle.entity()); +} + +inline void runMoveEffects(Simulation& simulation) { + internal::SelectForCurrentActionMoveView selectedMoves{simulation}; + if (selectedMoves.hasNoneSelected()) { + return; + } + + simulation.viewForSelectedMoves(); + simulation.viewForSelectedMoves, Tags>(); + simulation.viewForSelectedMoves, Tags>(); + + tryBoost(simulation); + trySetStatus(simulation); + trySetStatusFromEffect(simulation); + trySetVolatileFromEffect(simulation); + trySetSideConditionFromEffect(simulation); + trySetSlotConditionFromEffect(simulation); + trySetWeatherFromEffect(simulation); + trySetTerrainFromEffect(simulation); + trySetPseudoWeatherFromEffect(simulation); + + simulation.registry.clear(); +} +} // namespace + inline void setMoveHitCount(Simulation& simulation) { auto noAssignedHitCount = simulation.registry.view(entt::exclude); @@ -25848,16 +26081,26 @@ inline void setMoveHitCount(Simulation& simulation) { inline void applyDamage(Simulation& simulation) { simulation.viewForSelectedMoves(); - simulation.removeFromEntities(entt::exclude); + simulation.removeFromEntities( + entt::exclude); simulation.removeFromEntities(entt::exclude); } -inline void trySetStatusFromEffect(Simulation& /*simulation*/) {} +inline void runPrimaryMoveEffects(Simulation& simulation) { + simulation.addToEntities(); + runMoveEffects(simulation); + simulation.registry.clear(); +} inline void runSecondaryMoveEffects(Simulation& simulation) { - // Set secondary effect of active move + runModifySecondariesEvent(simulation); - trySetStatusFromEffect(simulation); + setRandomBinaryChoice(simulation); + randomBinaryChance(simulation, [](Simulation& sim) { + sim.addToEntities(); + }); + runMoveEffects(simulation); + simulation.registry.clear(); } inline void accuracyCheck(Simulation& simulation) { @@ -25870,96 +26113,39 @@ inline void accuracyCheck(Simulation& simulation) { }); } -inline void internal::deductMoveHitCount(types::handle moveHandle, HitCount& hitCount) { - POKESIM_REQUIRE(hitCount.val > 0U, "A hit count shouldn't be decremented if it's already 0."); - hitCount.val--; - if (!hitCount.val) { - moveHandle.remove(); - } -} - inline void moveHitLoop(Simulation& simulation) { setMoveHitCount(simulation); while (!simulation.registry.view().empty()) { internal::SelectForCurrentActionMoveView selectedMoves{simulation}; - calc_damage::run(simulation); + POKESIM_REQUIRE( + !selectedMoves.hasNoneSelected(), + "HitCount should only be present on active moves, meaning this loop should only be entered if there are moves to " + "select."); + calc_damage::run(simulation); // 1. call to this.battle.getDamage + + applyDamage(simulation); // 2. call to this.battle.spreadDamage + runPrimaryMoveEffects(simulation); // 3. primary effects + // 4. self drops + runSecondaryMoveEffects(simulation); // 5. secondary effects + // 6. force switch - // for simulate turn only - applyDamage(simulation); - runSecondaryMoveEffects(simulation); runDamagingHitEvent(simulation); updateAllStats(simulation); - internal::postMoveHitCheck(simulation); - simulation.view(); + postMoveHitCheck(simulation); + simulation.view(); } } -inline void internal::removeFailedHitTargets( - types::handle moveTarget, const CurrentActionTargets& targets, const CurrentActionSource& source) { - types::registry& registry = *moveTarget.registry(); - for (types::entity target : targets.val) { - registry.remove(target); - - CurrentActionMovesAsTarget& moves = registry.get(target); - auto newEnd = std::remove(moves.val.begin(), moves.val.end(), moveTarget.entity()); - moves.val.erase(newEnd, moves.val.end()); - } - - CurrentActionMovesAsSource& moves = registry.get(source.val); - auto newEnd = std::remove(moves.val.begin(), moves.val.end(), moveTarget.entity()); - moves.val.erase(newEnd, moves.val.end()); -} - -inline void internal::updateCurrentActionTargets(types::registry& registry, CurrentActionTargets& targets) { - types::activePokemonIndex deleteCount = 0U; - for (types::entity& target : targets.val) { - if (!registry.all_of(target)) { - types::activePokemonIndex swapIndex = targets.val.size() - 1 - deleteCount; - POKESIM_REQUIRE(swapIndex < targets.val.size(), "Swap index out of bounds."); - std::swap(target, targets.val[swapIndex]); - deleteCount++; - } - } - - targets.val.pop_count(deleteCount); -} - -inline void internal::postMoveHitCheck(Simulation& simulation) { - auto removedMoves = simulation.view< - internal::removeFailedHitTargets, - Tags, - entt::exclude_t>(); - simulation.registry.destroy(removedMoves.begin(), removedMoves.end()); - simulation.registry.clear(); - simulation.view(); -} - -template -void internal::runMoveHitCheck(Simulation& simulation) { - simulation.addToEntities(); - - internal::SelectForCurrentActionMoveView selectedMoves{simulation}; - if (selectedMoves.hasNoneSelected()) { - return; - } - - Function(simulation); - selectedMoves.deselect(); - - postMoveHitCheck(simulation); - simulation.registry.clear(); -} - inline void runMoveHitChecks(Simulation& simulation) { // invulnerabilityCheck // hitCheck // immunityCheck - internal::runMoveHitCheck(simulation); + runMoveHitCheck(simulation); // breakProtectCheck // stealBoostCheck - internal::runMoveHitCheck(simulation); + runMoveHitCheck(simulation); } } // namespace pokesim @@ -25979,12 +26165,12 @@ struct RootBattle; inline void assignRootBattle(types::handle battleHandle); inline void collectTurnOutcomeBattles(types::handle leafBattleHandle, const RootBattle& root); -inline void setCurrentActionSource(types::handle battleHandle, const Sides& sides, const CurrentAction& action); +inline void setCurrentActionSource(types::handle battleHandle, const Sides& sides, CurrentAction action); inline void setCurrentActionTarget( - types::handle battleHandle, const Sides& sides, const CurrentAction& action, const CurrentActionSource& source); + types::handle battleHandle, const Sides& sides, CurrentAction action, CurrentActionSource source); inline void setCurrentActionMove( - types::handle battleHandle, const CurrentActionSource& source, const CurrentActionTargets& targets, - const CurrentAction& action, const Pokedex& pokedex); + types::handle battleHandle, CurrentActionSource source, const CurrentActionTargets& targets, CurrentAction action, + const Pokedex& pokedex); inline void clearCurrentAction(Simulation& simulation); } // namespace pokesim @@ -26077,7 +26263,6 @@ struct Checks { namespace pokesim::simulate_turn { namespace { - inline void addTargetAllyToTargets(types::registry& registry, const Battle& battle) { const Sides& sides = registry.get(battle.val); const TargetSlotName& targetSlotName = registry.get(registry.get(battle.val).val); @@ -26104,10 +26289,16 @@ inline void addUserAllyToTargets(types::registry& registry, const Battle& battle targets.val.push_back(allyEntity); } -inline void resolveMoveTargets(CurrentActionTargets&) {} +inline void resolveMoveTargets(types::registry& registry, CurrentActionTargets& targets) { + for (types::entity target : targets.val) { + registry.emplace_or_replace(target); + } + + // More to do... +} inline void createActionMoveForTargets( - types::handle targetHandle, const Battle& battle, const CurrentActionSource& source, const Pokedex& pokedex) { + types::handle targetHandle, Battle battle, CurrentActionSource source, const Pokedex& pokedex) { types::registry& registry = *targetHandle.registry(); dex::Move move = registry.get(registry.get(battle.val).val).name; @@ -26158,7 +26349,7 @@ inline void runResidualAction(Simulation& simulation) { if (selectedBattle.hasNoneSelected()) return; } -inline void runBeforeTurnAction(Simulation& /*simulation*/) { +inline void runBeforeTurnAction(Simulation&) { // Barely used, will find different way of handling it } @@ -27218,28 +27409,28 @@ struct MoveDexDataSetup : DexDataSetup { inline void setPriority(types::priority priority); inline void setHitCount(types::moveHits hitCount); - inline void setPrimaryEffect(types::entity entity); - inline void setSecondaryEffect(types::entity entity); - inline void addAddedTargets(AddedTargetOptions addedTargets); -}; -struct MoveEffectSetup : DexDataSetup { - MoveEffectSetup(types::registry& registry) : DexDataSetup(registry) {} - types::entity entity() const { return handle; } + inline void setEffectTargetsMoveSource(); + inline void setEffectTargetsMoveTarget(); - inline void setChance(types::baseEffectChance chance); - inline void setEffectsSelf(); - inline void setEffectsTarget(); + template + void setPrimaryEffect(const EffectValues&... effectValues) { + POKESIM_REQUIRE( + !handle.all_of(), + "Moves can only have primary or secondary effects, not both."); + handle.emplace_or_replace(); + handle.emplace(effectValues...); + } - template - void setBoost(types::boost boost) { - static_assert( - std::is_same() || std::is_same() || - std::is_same() || std::is_same() || - std::is_same(), - "Boosts can only be applied to a Pokemon boost stat struct (excluding HP)."); - handle.emplace(boost); + template + void setSecondaryEffect(types::baseEffectChance chance, const EffectValues&... effectValues) { + POKESIM_REQUIRE( + !handle.all_of(), + "Moves can only have secondary or primary effects, not both."); + handle.emplace_or_replace(); + handle.emplace(effectValues...); + handle.emplace(chance); } }; } // namespace pokesim::dex::internal @@ -27324,24 +27515,18 @@ inline void MoveDexDataSetup::setHitCount(types::moveHits hitCount) { handle.emplace(hitCount); } -inline void MoveDexDataSetup::setPrimaryEffect(types::entity entity) { - handle.emplace(true, entity); -} - -inline void MoveDexDataSetup::setSecondaryEffect(types::entity entity) { - handle.emplace(false, entity); -} - -inline void MoveEffectSetup::setChance(types::baseEffectChance chance) { - handle.emplace(chance); -} - -inline void MoveEffectSetup::setEffectsSelf() { - handle.emplace(); +inline void MoveDexDataSetup::setEffectTargetsMoveSource() { + POKESIM_REQUIRE( + !handle.all_of(), + "Moves effects can only affect the target or source, not both."); + handle.emplace(); } -inline void MoveEffectSetup::setEffectsTarget() { - handle.emplace(); +inline void MoveDexDataSetup::setEffectTargetsMoveTarget() { + POKESIM_REQUIRE( + !handle.all_of(), + "Moves effects can only affect the source or target, not both."); + handle.emplace(); } } // namespace pokesim::dex::internal @@ -27516,7 +27701,7 @@ struct Pangoro { }; namespace latest { -using Pangoro = dex::Pangoro; +using Pangoro = dex::Pangoro; } } // namespace pokesim::dex @@ -27753,7 +27938,7 @@ struct QuiverDance { static constexpr types::pp basePp = 20U; - struct sourcePrimaryEffect { + struct targetPrimaryEffect { static constexpr types::boost spaBoost = 1, spdBoost = 1, speBoost = 1; static constexpr Tags<> effectTags{}; @@ -27877,6 +28062,11 @@ struct BuildMove { // moveTags and effectTags are not optional because setting them as optional does not work with clang }; + enum class MoveEffectKind : std::uint8_t { + primary, + secondary, + }; + template using void_t = std::void_t; @@ -27911,44 +28101,63 @@ struct BuildMove { template struct has> : std::true_type {}; - template - static types::entity buildEffect(types::registry& registry, bool effectsTarget) { - dex::internal::MoveEffectSetup effect(registry); - - if constexpr (has::value) { - effect.setChance(EffectData::chance); + template + static void setEffect(dex::internal::MoveDexDataSetup& move, const EffectValues&... effectValues) { + if constexpr (moveEffectKind == MoveEffectKind::primary) { + static_assert( + !has::value, + "Primary effects cannot have a chance to happen because they always happen if the move is successful."); + move.setPrimaryEffect(effectValues...); + } + else { + types::baseEffectChance chance = MechanicConstants::MoveBaseEffectChance::MAX; + if constexpr (has::value) { + chance = EffectData::chance; + } + move.setSecondaryEffect(chance, effectValues...); } + } - if (effectsTarget) { - effect.setEffectsTarget(); + template + static void setEffectTags(dex::internal::MoveDexDataSetup& move, Tags) { + if constexpr (moveEffectKind == MoveEffectKind::primary) { + static_assert( + !has::value, + "Primary effects cannot have a chance to happen because they always happen if the move is successful."); + (move.setPrimaryEffect(), ...); } else { - effect.setEffectsSelf(); + types::baseEffectChance chance = MechanicConstants::MoveBaseEffectChance::MAX; + if constexpr (has::value) { + chance = EffectData::chance; + } + (move.setSecondaryEffect(chance), ...); } + } + template + static void buildEffect(dex::internal::MoveDexDataSetup& move) { if constexpr (has::value) { - effect.setBoost(EffectData::atkBoost); + setEffect(move, EffectData::atkBoost); } if constexpr (has::value) { - effect.setBoost(EffectData::defBoost); + setEffect(move, EffectData::defBoost); } if constexpr (has::value) { - effect.setBoost(EffectData::spaBoost); + setEffect(move, EffectData::spaBoost); } if constexpr (has::value) { - effect.setBoost(EffectData::spdBoost); + setEffect(move, EffectData::spdBoost); } if constexpr (has::value) { - effect.setBoost(EffectData::speBoost); + setEffect(move, EffectData::speBoost); } - effect.setProperties(EffectData::effectTags); - - return effect.entity(); + setEffectTags(move, EffectData::effectTags); } public: @@ -27986,22 +28195,24 @@ struct BuildMove { move.setHitCount(T::hitCount); } - if (!forActiveMove) { - if constexpr (has::value) { - move.setPrimaryEffect(buildEffect(registry, false)); - } + if constexpr (has::value) { + buildEffect(move); + move.setEffectTargetsMoveSource(); + } - if constexpr (has::value) { - move.setPrimaryEffect(buildEffect(registry, true)); - } + if constexpr (has::value) { + buildEffect(move); + move.setEffectTargetsMoveTarget(); + } - if constexpr (has::value) { - move.setSecondaryEffect(buildEffect(registry, false)); - } + if constexpr (has::value) { + buildEffect(move); + move.setEffectTargetsMoveSource(); + } - if constexpr (has::value) { - move.setSecondaryEffect(buildEffect(registry, true)); - } + if constexpr (has::value) { + buildEffect(move); + move.setEffectTargetsMoveTarget(); } move.setProperties(T::moveTags); @@ -28284,7 +28495,6 @@ inline types::entity Pokedex::buildActionMove(dex::Move move, types::registry& r namespace pokesim::dex { namespace { - inline void setChoiceLock(types::handle pokemonHandle, const Battle& battle) { types::entity moveSlot = pokemonHandle.registry()->get(battle.val).val; pokemonHandle.emplace(moveSlot); @@ -28337,12 +28547,14 @@ inline void internal::ChoiceLockEvents::onDisableMove( /////////////// START OF src/Pokedex/Abilities/AbilityEvents.cpp /////////////// -namespace pokesim::dex { -inline void internal::StaticEvents::onDamagingHit(Simulation& /*simulation*/) {} -inline void internal::StaticEvents::onModifySpe(stat::EffectiveSpe& effectiveSpe) { +namespace pokesim::dex::internal { +inline void PlusEvents::onModifySpA(types::handle, EventModifier&) {} + +inline void StaticEvents::onDamagingHit(Simulation&) {} +inline void StaticEvents::onModifySpe(stat::EffectiveSpe& effectiveSpe) { effectiveSpe.val /= 2U; } -} // namespace pokesim::dex +} // namespace pokesim::dex::internal //////////////// END OF src/Pokedex/Abilities/AbilityEvents.cpp //////////////// @@ -28358,7 +28570,6 @@ inline void InputSetup::setup( types::handle handle{*registry, moveEntity}; handle.emplace(move); - handle.emplace(); registry->emplace_or_replace(sourceEntity); registry->emplace_or_replace(targetEntity); } @@ -28467,6 +28678,8 @@ struct Checks : pokesim::debug::Checks { void checkMoveInputs() { CurrentActionMovesAsSource moves{simulation->selectedMoveEntities()}; for (types::entity move : moves.val) { + if (has(move)) continue; + originalToCopy[move] = pokesim::debug::createEntityCopy(move, *registry, registryOnInput); bool hasSimulateTurn = has(move); @@ -28550,8 +28763,8 @@ struct Checks : pokesim::debug::Checks { } DamageRollKind getDamageRollKind(types::entity move, DamageRollOptions damageRollOptions) const { - const Defenders& defenders = registry->get(move); - const Side& side = registry->get(defenders.only()); + const Defender& defender = registry->get(move); + const Side& side = registry->get(defender.val); PlayerSideId playerSide = registry->get(side.val).val; switch (playerSide) { case PlayerSideId::P1: { @@ -28591,8 +28804,8 @@ struct Checks : pokesim::debug::Checks { } types::damage lastDamage = std::numeric_limits::max(); - const auto& [damageRolls, defenders] = registry->get(move); - const stat::CurrentHp& defenderHp = registry->get(defenders.only()); + const auto& [damageRolls, defender] = registry->get(move); + const stat::CurrentHp& defenderHp = registry->get(defender.val); POKESIM_REQUIRE_NM(!damageRolls.val.empty()); @@ -28663,6 +28876,8 @@ struct Checks : pokesim::debug::Checks { void checkMoveOutputs() const { for (types::entity move : simulation->selectedMoveEntities()) { + if (has(move)) continue; + pokesim::debug::TypesToIgnore typesToIgnore{}; if (has(move)) { typesToIgnore.add(); @@ -28764,7 +28979,7 @@ inline void clearRunVariables(Simulation& simulation) { } inline void checkForAndApplyStab( - types::handle moveHandle, const Attacker& attacker, const TypeName& type, DamageRollModifiers& modifier) { + types::handle moveHandle, const Attacker& attacker, TypeName type, DamageRollModifiers& modifier) { const SpeciesTypes& attackerTypes = moveHandle.registry()->get(attacker.val); if (attackerTypes.has(type.name)) { @@ -28773,9 +28988,8 @@ inline void checkForAndApplyStab( } inline void checkForAndApplyTypeEffectiveness( - types::handle moveHandle, const Defenders& defenders, const TypeName& type, DamageRollModifiers& modifier, - const Pokedex& pokedex) { - const SpeciesTypes& defenderTypes = moveHandle.registry()->get(defenders.only()); + types::handle moveHandle, Defender defender, TypeName type, DamageRollModifiers& modifier, const Pokedex& pokedex) { + const SpeciesTypes& defenderTypes = moveHandle.registry()->get(defender.val); modifier.typeEffectiveness = getAttackEffectiveness(defenderTypes, type.name, pokedex.typeChart()); } @@ -28796,9 +29010,9 @@ inline void setDamageToMinimumPossible(Damage& damage) { damage.val = std::max(damage.val, MechanicConstants::Damage::MIN); } -inline void setDefendingSide(types::handle moveHandle, const Defenders& defenders) { +inline void setDefendingSide(types::handle moveHandle, Defender defender) { types::registry& registry = *moveHandle.registry(); - PlayerSideId playerSide = registry.get(registry.get(defenders.only()).val).val; + PlayerSideId playerSide = registry.get(registry.get(defender.val).val).val; switch (playerSide) { case PlayerSideId::NONE: { POKESIM_REQUIRE_FAIL("Player side wasn't set properly."); @@ -28837,7 +29051,7 @@ inline void modifyDamage(Damage& damage, const DamageRollModifiers& modifiers) { setDamageToMinimumPossible(damage); } -inline void calculateAllDamageRolls(DamageRolls& damageRolls, const Damage& damage, const DamageRollModifiers& modifier) { +inline void calculateAllDamageRolls(DamageRolls& damageRolls, Damage damage, const DamageRollModifiers& modifier) { damageRolls.val.reserve(MechanicConstants::DamageRollCount::MAX); for (types::damageRollIndex i = 0U; i < MechanicConstants::DamageRollCount::MAX; i++) { Damage& damageRoll = damageRolls.val.emplace_back(damage); @@ -28864,26 +29078,26 @@ inline void applyDamageRollModifier(DamageRolls& damageRolls, Damage damage, con } inline void reduceDamageRollsToDefenderHp( - types::handle moveHandle, DamageRolls& damageRolls, Damage& damage, const Defenders& defenders) { - const stat::CurrentHp& defenderHp = moveHandle.registry()->get(defenders.only()); + types::handle moveHandle, DamageRolls& damageRolls, Damage& damage, Defender defender) { + const stat::CurrentHp& defenderHp = moveHandle.registry()->get(defender.val); for (auto& damageRoll : damageRolls.val) { damageRoll.val = std::min(defenderHp.val, damageRoll.val); } damage.val = std::min(defenderHp.val, damage.val); } -inline void assignCritChanceDivisor(types::handle moveHandle, const CritBoost& critBoost) { +inline void assignCritChanceDivisor(types::handle moveHandle, CritBoost critBoost) { std::size_t index = std::min((std::size_t)critBoost.val, pokesim::MechanicConstants::CRIT_CHANCE_DIVISORS.size() - 1U); moveHandle.emplace(pokesim::MechanicConstants::CRIT_CHANCE_DIVISORS[index]); } -inline void setSourceLevel(types::handle moveHandle, const Attacker& attacker) { +inline void setSourceLevel(types::handle moveHandle, Attacker attacker) { moveHandle.emplace(moveHandle.registry()->get(attacker.val).val); } template -void setUsedAttackStat(types::handle moveHandle, const Attacker& attacker) { +void setUsedAttackStat(types::handle moveHandle, Attacker attacker) { types::stat attackingStat = MechanicConstants::PokemonEffectiveStat::MIN; if constexpr (std::is_same_v) { attackingStat = moveHandle.registry()->get(attacker.val).val; @@ -28897,14 +29111,14 @@ void setUsedAttackStat(types::handle moveHandle, const Attacker& attacker) { } template -void setUsedDefenseStat(types::handle moveHandle, const Defenders& defenders) { +void setUsedDefenseStat(types::handle moveHandle, Defender defender) { types::stat defendingStat = MechanicConstants::PokemonEffectiveStat::MIN; if constexpr (std::is_same_v) { - defendingStat = moveHandle.registry()->get(defenders.only()).val; + defendingStat = moveHandle.registry()->get(defender.val).val; moveHandle.emplace(); } else { - defendingStat = moveHandle.registry()->get(defenders.only()).val; + defendingStat = moveHandle.registry()->get(defender.val).val; moveHandle.emplace(); } moveHandle.emplace(defendingStat); @@ -28919,23 +29133,22 @@ void setIgnoreAttackingBoostIfNegative(types::handle moveHandle, Attacker attack } template -void setIgnoreDefendingBoostIfPositive(types::handle moveHandle, const Defenders& defenders) { - BoostType* boost = moveHandle.registry()->try_get(defenders.only()); +void setIgnoreDefendingBoostIfPositive(types::handle moveHandle, Defender defender) { + BoostType* boost = moveHandle.registry()->try_get(defender.val); if (boost && boost->val > MechanicConstants::PokemonStatBoost::BASE) { moveHandle.emplace(); } } inline void calculateBaseDamage( - types::handle moveHandle, const BasePower& basePower, const AttackingLevel& level, const AttackingStat& attack, - const DefendingStat& defense) { + types::handle moveHandle, BasePower basePower, AttackingLevel level, AttackingStat attack, DefendingStat defense) { // NOLINTNEXTLINE(readability-magic-numbers) types::damage damage = ((((2U * level.val / 5U + 2U) * basePower.val * attack.val) / defense.val) / 50U) + 2U; moveHandle.emplace(damage); } -inline void applyUsesUntilKo(types::handle moveHandle, const DamageRolls& damageRolls, const Defenders& defender) { - const stat::CurrentHp& defenderHp = moveHandle.registry()->get(defender.only()); +inline void applyUsesUntilKo(types::handle moveHandle, const DamageRolls& damageRolls, Defender defender) { + const stat::CurrentHp& defenderHp = moveHandle.registry()->get(defender.val); UsesUntilKo usesUntilKo; POKESIM_REQUIRE( damageRolls.val.size() == MechanicConstants::DamageRollCount::MAX, @@ -29092,9 +29305,9 @@ void saveRealEffectiveAttackerStat(types::registry& registry, Attacker attacker) } template -void saveRealEffectiveDefenderStat(types::registry& registry, const Defenders& defenders) { - EffectiveStat effectiveStat = registry.get(defenders.only()); - registry.emplace(defenders.only(), effectiveStat.val); +void saveRealEffectiveDefenderStat(types::registry& registry, Defender defender) { + EffectiveStat effectiveStat = registry.get(defender.val); + registry.emplace(defender.val, effectiveStat.val); } template @@ -29105,9 +29318,8 @@ void resetEffectiveAndAttackingStat(types::registry& registry, Attacker attacker } template -void resetEffectiveAndDefendingStat( - types::registry& registry, const Defenders& defenders, DefendingStat& defendingStat) { - auto [effectiveStat, realEffectiveStat] = registry.get(defenders.only()); +void resetEffectiveAndDefendingStat(types::registry& registry, Defender defender, DefendingStat& defendingStat) { + auto [effectiveStat, realEffectiveStat] = registry.get(defender.val); defendingStat.val = effectiveStat.val; effectiveStat.val = realEffectiveStat.val; } @@ -29334,6 +29546,8 @@ inline void PokemonStateSetup::setCurrentHp(types::stat hp) { inline void PokemonStateSetup::setTypes(SpeciesTypes types) { handle.emplace(types); + type::tags::emplaceTagFromEnum(types.type1(), handle); + type::tags::emplaceTagFromEnum(types.type2(), handle); } inline void PokemonStateSetup::setLevel(types::level level) { @@ -29369,7 +29583,8 @@ inline void PokemonStateSetup::setPostion(types::teamPositionIndex position) { } inline void PokemonStateSetup::setStatus(dex::Status status) { - pokesim::setStatus(handle, status); + handle.emplace(status); + status::tags::emplaceTagFromEnum(status, handle); } inline void PokemonStateSetup::setNature(dex::Nature nature) { @@ -29447,12 +29662,13 @@ auto enumToTag(dex::Ability ability, RunFunctionArgs&&... args) { case dex::Ability::DEFIANT: return RunStruct::run(std::forward(args)...); case dex::Ability::INFILTRATOR: return RunStruct::run(std::forward(args)...); case dex::Ability::IRON_FIST: return RunStruct::run(std::forward(args)...); + case dex::Ability::PLUS: return RunStruct::run(std::forward(args)...); case dex::Ability::STATIC: return RunStruct::run(std::forward(args)...); case dex::Ability::SWEET_VEIL: return RunStruct::run(std::forward(args)...); case dex::Ability::TRACE: return RunStruct::run(std::forward(args)...); default: { - POKESIM_REQUIRE(false, "Adding tag for ability that does not exist."); + POKESIM_REQUIRE_FAIL("Using a tag for ability that does not exist."); } } } @@ -29490,7 +29706,7 @@ auto enumToTag(dex::Item item, RunFunctionArgs&&... args) { case dex::Item::LIFE_ORB: return RunStruct::run(std::forward(args)...); default: { - POKESIM_REQUIRE(false, "Adding tag for item that does not exist."); + POKESIM_REQUIRE_FAIL("Using a tag for item that does not exist."); } } } @@ -29500,8 +29716,6 @@ auto enumToTag(dex::Item item, RunFunctionArgs&&... args) { ////////////// START OF src/Pokedex/EnumToTag/NatureEnumToTag.hpp ////////////// -// TODO(aed3): Make this auto generated - namespace pokesim::nature::tags { /* * Runs a function with a certain nature tag based on the passed in enum. @@ -29547,7 +29761,7 @@ auto enumToTag(dex::Nature nature, RunFunctionArgs&&... args) { case dex::Nature::TIMID: return RunStruct::run(std::forward(args)...); default: { - POKESIM_REQUIRE(false, "Adding tag for nature that does not exist."); + POKESIM_REQUIRE_FAIL("Using a tag for nature that does not exist."); } } } @@ -29557,8 +29771,6 @@ auto enumToTag(dex::Nature nature, RunFunctionArgs&&... args) { ////////////// START OF src/Pokedex/EnumToTag/StatusEnumToTag.hpp ////////////// -// TODO(aed3): Make this auto generated - namespace pokesim::status::tags { /* * Runs a function with a certain status tag based on the passed in enum. @@ -29585,14 +29797,75 @@ auto enumToTag(dex::Status status, RunFunctionArgs&&... args) { case dex::Status::TOX: return RunStruct::run(std::forward(args)...); default: { - POKESIM_REQUIRE(false, "Adding tag for status that does not exist."); + POKESIM_REQUIRE_FAIL("Using a tag for status that does not exist."); } } } + +template