diff --git a/CMakeLists.txt b/CMakeLists.txt index bddf207..5d8180d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,7 +188,6 @@ setup_target( src/Battle/Pokemon/ManagePokemonState.cpp src/Battle/Setup/BattleStateSetup.cpp src/Battle/Setup/EmplaceTagFromEnum.cpp - src/Battle/Setup/MoveStateSetup.cpp src/Battle/Setup/PokemonStateSetup.cpp src/Battle/Setup/SideStateSetup.cpp src/Battle/Side/ManageSideState.cpp @@ -205,7 +204,6 @@ setup_target( src/Pokedex/Setup/GetMoveBuild.cpp src/Pokedex/Setup/GetSpeciesBuild.cpp src/Pokedex/Setup/ItemDexDataSetup.cpp - src/Pokedex/Setup/MoveDexDataSetup.cpp src/Pokedex/Setup/SpeciesDexDataSetup.cpp src/SimulateTurn/CalcDamageSpecifics.cpp src/SimulateTurn/ManageActionQueue.cpp diff --git a/benchmarks/Benchmarks.hpp b/benchmarks/Benchmarks.hpp index 2b04592..7fd7bf6 100644 --- a/benchmarks/Benchmarks.hpp +++ b/benchmarks/Benchmarks.hpp @@ -89,7 +89,6 @@ struct CreateSingleBattleSimulation : BenchmarkInputHolder { struct CreateDoubleBattleSimulation : BenchmarkInputHolder { inline static const std::vector TAGS = {"DoubleBattle"}; - static constexpr types::entityIndex MAX_INPUTS = 1U << 13U; static Simulation run(types::rngState&, Pokedex& pokedex) { return Simulation{pokedex, BattleFormat::DOUBLES}; } }; diff --git a/benchmarks/RandomInputs.cpp b/benchmarks/RandomInputs.cpp index cb01ad5..5f12831 100644 --- a/benchmarks/RandomInputs.cpp +++ b/benchmarks/RandomInputs.cpp @@ -49,7 +49,7 @@ struct Random { }; template - static auto pickFromList(const T& list, types::rngState rngState) { + static auto pickFromList(const T& list, types::rngState& rngState) { return list[internal::nextBoundedRandomValue(rngState, (types::rngResult)list.size())]; } @@ -149,34 +149,34 @@ struct Random { SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; if (simulation.isBattleFormat(BattleFormat::SINGLES)) { - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P1A}; - p1SlotDecision.moveChoice = pickFromList(battle.sides.p1().team[0].moves, rngState).name; - p2SlotDecision.moveChoice = pickFromList(battle.sides.p2().team[0].moves, rngState).name; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P1A}; + p1MoveDecision.move = pickFromList(battle.sides.p1().team[0].moves, rngState).name; + p2MoveDecision.move = pickFromList(battle.sides.p2().team[0].moves, rngState).name; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; } else { - SlotDecision p1ASlotDecision{Slot::P1A, pickFromList(std::vector{Slot::P1B, Slot::P2A, Slot::P2B}, rngState)}; - SlotDecision p1BSlotDecision{Slot::P1B, pickFromList(std::vector{Slot::P1A, Slot::P2A, Slot::P2B}, rngState)}; - SlotDecision p2ASlotDecision{Slot::P2A, pickFromList(std::vector{Slot::P2B, Slot::P1A, Slot::P1B}, rngState)}; - SlotDecision p2BSlotDecision{Slot::P2B, pickFromList(std::vector{Slot::P2A, Slot::P1A, Slot::P1B}, rngState)}; - p1ASlotDecision.moveChoice = pickFromList(battle.sides.p1().team[0].moves, rngState).name; - p1BSlotDecision.moveChoice = pickFromList(battle.sides.p1().team[1].moves, rngState).name; - p2ASlotDecision.moveChoice = pickFromList(battle.sides.p2().team[0].moves, rngState).name; - p2BSlotDecision.moveChoice = pickFromList(battle.sides.p2().team[1].moves, rngState).name; - - p1Decision.decisions = types::slotDecisions{p1ASlotDecision, p1BSlotDecision}; - p2Decision.decisions = types::slotDecisions{p2ASlotDecision, p2BSlotDecision}; + MoveDecision p1AMoveDecision{Slot::P1A, pickFromList(std::vector{Slot::P1B, Slot::P2A, Slot::P2B}, rngState)}; + MoveDecision p1BMoveDecision{Slot::P1B, pickFromList(std::vector{Slot::P1A, Slot::P2A, Slot::P2B}, rngState)}; + MoveDecision p2AMoveDecision{Slot::P2A, pickFromList(std::vector{Slot::P2B, Slot::P1A, Slot::P1B}, rngState)}; + MoveDecision p2BMoveDecision{Slot::P2B, pickFromList(std::vector{Slot::P2A, Slot::P1A, Slot::P1B}, rngState)}; + p1AMoveDecision.move = pickFromList(battle.sides.p1().team[0].moves, rngState).name; + p1BMoveDecision.move = pickFromList(battle.sides.p1().team[1].moves, rngState).name; + p2AMoveDecision.move = pickFromList(battle.sides.p2().team[0].moves, rngState).name; + p2BMoveDecision.move = pickFromList(battle.sides.p2().team[1].moves, rngState).name; + + p1Decision.decisions = types::slotDecisions{p1AMoveDecision, p1BMoveDecision}; + p2Decision.decisions = types::slotDecisions{p2AMoveDecision, p2BMoveDecision}; } TurnDecisionInfo turnDecision{p1Decision, p2Decision}; for (auto& sideDecision : turnDecision) { for (auto& slotDecision : sideDecision.decisions.get()) { - if (simulation.pokedex().moveHas(slotDecision.moveChoice.value())) { - slotDecision.targetSlot = slotDecision.sourceSlot; + if (simulation.pokedex().moveHas(slotDecision.get().move)) { + slotDecision.get().targetSlot = slotDecision.sourceSlot(); } } } @@ -307,6 +307,7 @@ struct Random { } } + pokedex.loadForBattleInfo(battleInfoList); simulation.createInitialStates(battleInfoList); updateAllStats(simulation); } @@ -314,33 +315,33 @@ struct Random { public: struct AssignSimulateTurnOneBattleOneInput : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 16U; }; struct AssignSimulateTurnOneBattleManyInputs : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 16U; }; struct AssignSimulateTurnManyBattlesManyInputs : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 16U; }; struct AssignCalcDamageOneBattleOneInput : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 16U; }; struct AssignCalcDamageOneBattleManyInputs : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 16U; }; struct AssignCalcDamageManyBattlesManyInputs : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 16U; }; struct AssignAnalyzeEffectOneBattleOneInput : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 13U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 15U; }; struct AssignAnalyzeEffectOneBattleManyInputs : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 13U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 15U; }; struct AssignAnalyzeEffectManyBattlesManyInputs : Assign { - static constexpr types::entityIndex MAX_INPUTS = 1U << 13U; + static constexpr types::entityIndex MAX_INPUTS = 1U << 15U; }; }; } // namespace diff --git a/benchmarks/VerticalSlices.cpp b/benchmarks/VerticalSlices.cpp index 4ad27dc..a81ef7e 100644 --- a/benchmarks/VerticalSlices.cpp +++ b/benchmarks/VerticalSlices.cpp @@ -101,13 +101,11 @@ struct VerticalSlice { battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P1A}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::KNOCK_OFF}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P1A, dex::Move::THUNDERBOLT}; - p1SlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::THUNDERBOLT; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({inputCount, battleCreationInfo}); @@ -124,17 +122,13 @@ struct VerticalSlice { battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1ASlotDecision{Slot::P1A, Slot::P2B}; - SlotDecision p1BSlotDecision{Slot::P1B, Slot::P2A}; - SlotDecision p2ASlotDecision{Slot::P2A, Slot::P1B}; - SlotDecision p2BSlotDecision{Slot::P2B, Slot::P2B}; - - p1ASlotDecision.moveChoice = dex::Move::MOONBLAST; - p1BSlotDecision.moveChoice = dex::Move::WILL_O_WISP; - p1Decision.decisions = types::sideSlots{p1ASlotDecision, p1BSlotDecision}; - p2ASlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p2BSlotDecision.moveChoice = dex::Move::QUIVER_DANCE; - p2Decision.decisions = types::sideSlots{p2ASlotDecision, p2BSlotDecision}; + MoveDecision p1AMoveDecision{Slot::P1A, Slot::P2B, dex::Move::MOONBLAST}; + MoveDecision p1BMoveDecision{Slot::P1B, Slot::P2A, dex::Move::WILL_O_WISP}; + MoveDecision p2AMoveDecision{Slot::P2A, Slot::P1B, dex::Move::KNOCK_OFF}; + MoveDecision p2BMoveDecision{Slot::P2B, Slot::P2B, dex::Move::QUIVER_DANCE}; + + p1Decision.decisions = types::slotDecisions{p1AMoveDecision, p1BMoveDecision}; + p2Decision.decisions = types::slotDecisions{p2AMoveDecision, p2BMoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({inputCount, battleCreationInfo}); @@ -160,7 +154,6 @@ struct VerticalSlice { struct AssignAnalyzeEffectSingleBattleInputs : BenchmarkInputHolder { inline static const std::vector TAGS = {"AnalyzeEffect", "VerticalSlice1"}; - static constexpr types::entityIndex MAX_INPUTS = 1U << 14U; static void run(types::rngState&, types::entityIndex inputCount, Simulation& simulation, Pokedex& pokedex) { static BattleCreationInfo battleCreationInfo = createSingleBattleTeam(pokedex); pokedex.loadForBattleInfo({battleCreationInfo}); @@ -187,8 +180,11 @@ BENCHMARK_CASE( BENCHMARK_CASE( CreatePokedex, CreateDoubleBattleSimulation, ChooseMonteCarloOptions, VerticalSlice::AssignSimulateTurnDoubleBattleInputs) +struct ChooseRandomDoublesBranchingOptions : ChooseRandomBranchingOptions { + static constexpr types::entityIndex MAX_INPUTS = 1U << 13U; +}; BENCHMARK_CASE( - CreatePokedex, CreateDoubleBattleSimulation, ChooseRandomBranchingOptions, + CreatePokedex, CreateDoubleBattleSimulation, ChooseRandomDoublesBranchingOptions, VerticalSlice::AssignSimulateTurnDoubleBattleInputs) BENCHMARK_CASE( diff --git a/extras/PokeSim.cpp b/extras/PokeSim.cpp index b970eda..36e15da 100644 --- a/extras/PokeSim.cpp +++ b/extras/PokeSim.cpp @@ -16,7 +16,6 @@ * src/SimulateTurn/ManageActionQueue.cpp * src/SimulateTurn/CalcDamageSpecifics.cpp * src/Pokedex/Setup/SpeciesDexDataSetup.cpp - * src/Pokedex/Setup/MoveDexDataSetup.cpp * src/Pokedex/Setup/ItemDexDataSetup.cpp * src/Pokedex/Setup/GetSpeciesBuild.cpp * src/Pokedex/Setup/GetMoveBuild.cpp @@ -33,7 +32,6 @@ * src/Battle/Side/ManageSideState.cpp * src/Battle/Setup/SideStateSetup.cpp * src/Battle/Setup/PokemonStateSetup.cpp - * src/Battle/Setup/MoveStateSetup.cpp * src/Battle/Setup/EmplaceTagFromEnum.cpp * src/Battle/Setup/BattleStateSetup.cpp * src/Battle/Pokemon/ManagePokemonState.cpp @@ -265,23 +263,18 @@ void checkAction(types::entity actionEntity, const types::registry& registry) { POKESIM_REQUIRE_NM(!has(actionEntity, registry)); POKESIM_REQUIRE_NM(!has(actionEntity, registry)); POKESIM_REQUIRE_NM(!has(actionEntity, registry)); - POKESIM_REQUIRE_NM(!has(actionEntity, registry)); checkTeamOrder(registry.get(actionEntity).val); } - else { - POKESIM_REQUIRE_NM(has(actionEntity, registry)); - } if (registry.any_of(actionEntity)) { POKESIM_REQUIRE_NM(has(actionEntity, registry)); POKESIM_REQUIRE_NM(has(actionEntity, registry)); POKESIM_REQUIRE_NM(!has(actionEntity, registry)); - const auto& [source, target, speedSort] = registry.get(actionEntity); + const auto& [source, target] = registry.get(actionEntity); check(source); check(target); - check(speedSort); if (has(actionEntity, registry)) { check(registry.get(actionEntity)); @@ -405,20 +398,7 @@ void checkPokemon(types::entity pokemonEntity, const types::registry& registry) if (effectiveSpd) check(*effectiveSpd); if (effectiveSpe) check(*effectiveSpe); - checkBounds(moveSlots.val.size()); -} - -void checkMoveSlot(types::entity moveSlotEntity, const types::registry& registry) { - types::registry::checkEntity(moveSlotEntity, registry); - POKESIM_REQUIRE_NM(has(moveSlotEntity, registry)); - POKESIM_REQUIRE_NM(has(moveSlotEntity, registry)); - POKESIM_REQUIRE_NM(has(moveSlotEntity, registry)); - - const auto& [move, pp, maxPp] = registry.get(moveSlotEntity); - - check(move); - check(pp); - check(maxPp); + check(moveSlots); } void checkActionMove(types::entity moveEntity, const types::registry& registry) { @@ -485,6 +465,25 @@ void check(const Accuracy& accuracy) { checkBounds(accuracy.val); } +template <> +void check(const ActionQueueItem& actionQueueItem) { + POKESIM_REQUIRE_NM(listContains(VALID_ACTION_ORDERS, actionQueueItem.order)); + checkBounds(actionQueueItem.priority); + checkStat(actionQueueItem.speed); + + if (actionQueueItem.order == ActionOrder::MOVE || actionQueueItem.order == ActionOrder::SWITCH) { + check(actionQueueItem.decision); + } +} + +template <> +void check(const ActionQueue& actionQueue) { + checkBounds(actionQueue.val.size()); + for (const ActionQueueItem& actionQueueItem : actionQueue.val) { + check(actionQueueItem); + } +} + template <> void check(const AddedTargets& addedTargets) { POKESIM_REQUIRE_NM(listContains(VALID_ADDED_TARGET_OPTIONS, addedTargets.val)); @@ -670,6 +669,16 @@ void check(const calc_damage::Power& power) { checkBounds(power.val); } +template <> +void check(const ChoiceLock& choiceLock) { + POKESIM_REQUIRE_NM(choiceLock.val < Constants::MoveSlots::MAX); +} + +template <> +void check(const CurrentActionMoveSlot& currentActionMoveSlot) { + POKESIM_REQUIRE_NM(currentActionMoveSlot.val < Constants::MoveSlots::MAX); +} + template <> void check(const Damage& damage) { POKESIM_REQUIRE_NM(damage.val <= Constants::Damage::MAX); @@ -690,25 +699,11 @@ void check(const DamageRolls& damageRolls) { } template <> -void check(const SlotDecision& slotDecision) { - checkSlot(slotDecision.sourceSlot); - checkSlot(slotDecision.targetSlot); - POKESIM_REQUIRE_NM(!(slotDecision.moveChoice.has_value() && slotDecision.itemChoice.has_value())); - POKESIM_REQUIRE_NM(!(slotDecision.megaEvolve && slotDecision.primalRevert)); -} - -template <> -void check(const SideDecision& sideDecision) { - checkPlayerSideId(sideDecision.sideId); - if (sideDecision.decisions.holds()) { - const types::slotDecisions& decisions = sideDecision.decisions.get(); - for (const SlotDecision& decision : decisions) { - check(decision); - } - } - else { - checkTeamOrder(sideDecision.decisions.get()); - } +void check(const DisabledMoveSlots& disabledMoveSlots) { + checkBounds(disabledMoveSlots.val.size()); + POKESIM_REQUIRE( + listContains(disabledMoveSlots.val, true), + "The component should be removed if no moves are disabled."); } template <> @@ -731,14 +726,6 @@ void check(const Ivs& ivs) { checkIv(ivs.spe); } -template <> -void check(const ActionQueue& actionQueue, const types::registry& registry) { - checkBounds(actionQueue.val.size()); - for (types::entity entity : actionQueue.val) { - checkAction(entity, registry); - } -} - template <> void check(const Battle& battle, const types::registry& registry) { checkBattle(battle.val, registry); @@ -759,21 +746,11 @@ void check(const ParentEntity& parentEntity, const types::registry& registry) { types::registry::checkEntity(parentEntity.val, registry); } -template <> -void check(const ChoiceLock& choiceLock, const types::registry& registry) { - checkMoveSlot(choiceLock.val, registry); -} - template <> void check(const CurrentAction& currentAction, const types::registry& registry) { checkAction(currentAction.val, registry); } -template <> -void check(const NextAction& nextAction, const types::registry& registry) { - checkAction(nextAction.val, registry); -} - template <> void check(const CurrentActionTargets& targets, const types::registry& registry) { checkBounds(targets.val.size()); @@ -818,11 +795,6 @@ void check(const CurrentActionMovesAsTarget& moves, const types::registry& regis } } -template <> -void check(const CurrentActionMoveSlot& move, const types::registry& registry) { - checkMoveSlot(move.val, registry); -} - template <> void check(const CurrentEffectSource& source, const types::registry& registry) { checkPokemon(source.val, registry); @@ -862,21 +834,27 @@ void check(const FoeSide& foeSide, const types::registry& registry) { } template <> -void check(const LastUsedMove& lastUsedMove, const types::registry& registry) { - checkMoveSlot(lastUsedMove.val, registry); +void check(const RecycledAction& recycledAction, const types::registry& registry) { + types::registry::checkEntity(recycledAction.val, registry); + POKESIM_REQUIRE_NM(has(recycledAction.val, registry)); } template <> -void check(const MoveSlots& moveSlots, const types::registry& registry) { - checkBounds(moveSlots.val.size()); - for (types::entity moveEntity : moveSlots.val) { - checkMoveSlot(moveEntity, registry); - } +void check(const RecycledActionMove& recycledActionMove, const types::registry& registry) { + types::registry::checkEntity(recycledActionMove.val, registry); + POKESIM_REQUIRE_NM(has(recycledActionMove.val, registry)); +} + +template <> +void check(const AddedRecycledActionMove1& addedRecycledActionMove1, const types::registry& registry) { + types::registry::checkEntity(addedRecycledActionMove1.val, registry); + POKESIM_REQUIRE_NM(has(addedRecycledActionMove1.val, registry)); } template <> -void check(const Pokemon& pokemon, const types::registry& registry) { - checkPokemon(pokemon.val, registry); +void check(const AddedRecycledActionMove2& addedRecycledActionMove2, const types::registry& registry) { + types::registry::checkEntity(addedRecycledActionMove2.val, registry); + POKESIM_REQUIRE_NM(has(addedRecycledActionMove2.val, registry)); } template <> @@ -905,11 +883,31 @@ void check(const HitCount& hitCount) { checkBounds(hitCount.val); } +template <> +void check(const LastUsedMove& lastUsedMove) { + POKESIM_REQUIRE_NM(lastUsedMove.val < Constants::MoveSlots::MAX); +} + template <> void check(const Level& level) { checkBounds(level.val); } +template <> +void check(const MoveSlot& moveSlot) { + check(MoveName{moveSlot.move}); + check(Pp{moveSlot.pp}); + checkBounds(moveSlot.maxPp); +} + +template <> +void check(const MoveSlots& moveSlots) { + checkBounds(moveSlots.val.size()); + for (MoveSlot moveSlot : moveSlots.val) { + check(moveSlot); + } +} + template <> void check(const AbilityName& abilityName) { POKESIM_REQUIRE_NM(abilityName.val != dex::Ability::NO_ABILITY); @@ -1015,16 +1013,6 @@ void check(const WeatherName& weatherName) { POKESIM_REQUIRE_NM((std::underlying_type_t)weatherName.val <= dex::TOTAL_WEATHER_COUNT); } -template <> -void check(const Pp& pp) { - checkBounds(pp.val); -} - -template <> -void check(const MaxPp& maxPp) { - checkBounds(maxPp.val); -} - template <> void check(const PlayerSide& playerSide) { checkPlayerSideId(playerSide.val); @@ -1055,6 +1043,11 @@ void check(const BaseStats& baseStats) { checkBaseStat(baseStats.spe); } +template <> +void check(const Pp& pp) { + checkBounds(pp.val); +} + template <> void check(const Position& position) { checkBounds(position.val); @@ -1159,6 +1152,20 @@ void check(const internal::RandomEqualChanceStack& randomEqualChanceStack, const } } +template <> +void check(const SideDecision& sideDecision) { + checkPlayerSideId(sideDecision.sideId); + if (sideDecision.decisions.holds()) { + const types::slotDecisions& decisions = sideDecision.decisions.get(); + for (const auto& decision : decisions) { + check(decision); + } + } + else { + checkTeamOrder(sideDecision.decisions.get()); + } +} + template <> void check(const SpeedTieIndexes& speedTieIndexes) { checkBounds(speedTieIndexes.val.size()); @@ -1228,13 +1235,6 @@ void check(const SpeciesTypes& speciesTypes) { } } -template <> -void check(const SpeedSort& speedSort) { - POKESIM_REQUIRE_NM(listContains(VALID_ACTION_ORDERS, speedSort.order)); - checkBounds(speedSort.priority); - checkStat(speedSort.speed); -} - template <> void check(const stat::Hp& hp) { checkStat(hp.val, true); @@ -1307,6 +1307,26 @@ void check(const Winner& winner) { winner.val == PlayerSideId::P1 || winner.val == PlayerSideId::P2 || winner.val == PlayerSideId::NONE); } +template <> +void check(const types::slotDecision& slotDecision) { + checkSlot(slotDecision.sourceSlot()); + checkSlot(slotDecision.targetSlot()); + auto [moveDecision, megaDecision, zMoveDecision, dynamaxDecision, teraDecision, itemDecision] = slotDecision.get_if< + MoveDecision, + MegaEvolveAndMoveDecision, + ZMoveDecision, + DynamaxAndMoveDecision, + TerastallizeAndMoveDecision, + ItemDecision>(); + + if (moveDecision) check(MoveName{moveDecision->move}); + if (megaDecision) check(MoveName{megaDecision->move}); + if (zMoveDecision) check(MoveName{zMoveDecision->move}); + if (dynamaxDecision) check(MoveName{dynamaxDecision->move}); + if (teraDecision) check(MoveName{teraDecision->move}); + if (itemDecision) check(ItemName{itemDecision->item}); +} + template <> void check(const DamageRollKind& damageRollKind) { if (listContains(VALID_DAMAGE_ROLL_KINDS, damageRollKind)) { @@ -1342,11 +1362,89 @@ void check(const DamageRollOptions& damageRollOptions) { ///////////////// START OF src/Simulation/SimulationSetup.cpp ////////////////// +#include #include #include namespace pokesim { namespace { +std::size_t getBattleCreationCount(const BattleCreationInfo& battleInfo) { + return std::max((std::size_t)1UL, battleInfo.decisionsToSimulate.size()); +} + +struct EntityLists { + struct EntityList { + private: + types::entityVector list{}; + types::entityIndex index = 0U; + + public: + EntityList() {} + EntityList(types::registry& registry, types::entityIndex size) : list(size) { + registry.create(list.begin(), list.end()); + } + + types::entity getNext() { + POKESIM_REQUIRE(index < list.size(), "More entities are being asked for than were created."); + types::entity nextEntity = list[index]; + index++; + return nextEntity; + } + }; + + EntityList battles; + EntityList sides; + EntityList pokemon; + EntityList recycledActions; + EntityList recycledActionMoves; + EntityList addedRecycledActionMoves; + EntityList calcDamageInputs; + EntityList analyzeEffectInputs; + + EntityLists(Simulation* simulation, const std::vector& battleInfoList) { + types::entityIndex battleCount = 0U; + types::entityIndex pokemonCount = 0U; + types::entityIndex calcDamageInputCount = 0U; + types::entityIndex analyzeEffectInputCount = 0U; + for (const BattleCreationInfo& battleInfo : battleInfoList) { + types::entityIndex battleCountIncrease = (types::entityIndex)getBattleCreationCount(battleInfo); + battleCount += battleCountIncrease; + + for (const SideCreationInfo& side : battleInfo.sides) { + pokemonCount += (types::teamPositionIndex)side.team.size() * battleCountIncrease; + } + + for (const CalcDamageInputInfo& calcDamageInputInfo : battleInfo.damageCalculations) { + calcDamageInputCount += (types::entityIndex)calcDamageInputInfo.moves.size(); + } + for (const AnalyzeEffectInputInfo& analyzeEffectInputInfo : battleInfo.effectsToAnalyze) { + analyzeEffectInputCount += (types::entityIndex)analyzeEffectInputInfo.moves.size(); + } + } + + types::entityIndex sideCount = battleCount * 2U; + types::entityIndex recycledActionCount = battleCount; + types::entityIndex recycledActionMoveCount = battleCount; + types::entityIndex addedRecycledActionMoveCount = + simulation->isBattleFormat(BattleFormat::DOUBLES) ? battleCount * 2U : 0U; + + POKESIM_REQUIRE( + battleCount + sideCount + recycledActionCount + pokemonCount + calcDamageInputCount + analyzeEffectInputCount < + std::numeric_limits::max(), + "More entities than can be created would be made for this input."); + + types::registry& registry = simulation->registry; + battles = {registry, battleCount}; + sides = {registry, sideCount}; + pokemon = {registry, pokemonCount}; + recycledActions = {registry, recycledActionCount}; + recycledActionMoves = {registry, recycledActionMoveCount}; + addedRecycledActionMoves = {registry, addedRecycledActionMoveCount}; + calcDamageInputs = {registry, calcDamageInputCount}; + analyzeEffectInputs = {registry, analyzeEffectInputCount}; + } +}; + void setPokemonAbility( const PokemonCreationInfo& pokemonInfo, PokemonStateSetup& pokemonSetup, const Pokedex& pokedex) { if (pokemonInfo.ability != dex::Ability::NO_ABILITY) { @@ -1459,31 +1557,9 @@ void setPokemonCurrentBoosts(const PokemonCreationInfo& pokemonInfo, PokemonStat if (pokemonInfoBoosts.spd.has_value()) pokemonSetup.setBoost(pokemonInfoBoosts.spd.value()); if (pokemonInfoBoosts.spe.has_value()) pokemonSetup.setBoost(pokemonInfoBoosts.spe.value()); } -} // namespace - -types::entityVector Simulation::createInitialMoves(const std::vector& moveInfoList) { - types::entityVector moveEntities{}; - moveEntities.reserve((types::entityVector::size_type)moveInfoList.size()); - for (const MoveCreationInfo& moveInfo : moveInfoList) { - MoveStateSetup moveSetup(registry); - moveSetup.setName(moveInfo.name); - types::pp maxPp = Constants::MoveMaxPp::DEFAULT; - if (!moveInfo.pp.has_value() || !moveInfo.maxPp.has_value()) { - maxPp = pokedex().getMoveData(moveInfo.name).val; - } - maxPp = moveInfo.maxPp.value_or(maxPp); - - moveSetup.setPP(moveInfo.pp.value_or(maxPp)); - moveSetup.setMaxPP(maxPp); - moveEntities.push_back(moveSetup.entity()); - } - - return moveEntities; -} - -PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& pokemonInfo) { - PokemonStateSetup pokemonSetup(registry); +void createInitialPokemon( + const PokemonCreationInfo& pokemonInfo, PokemonStateSetup& pokemonSetup, const Pokedex& pokedex) { if (pokemonInfo.id.has_value()) { pokemonSetup.setID(pokemonInfo.id.value()); } @@ -1492,13 +1568,13 @@ PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& po } pokemonSetup.setSpecies(pokemonInfo.species); - setPokemonAbility(pokemonInfo, pokemonSetup, pokedex()); + setPokemonAbility(pokemonInfo, pokemonSetup, pokedex); types::level level = pokemonInfo.level.value_or(Constants::PokemonLevel::DEFAULT); dex::Nature nature = setPokemonNature(pokemonInfo, pokemonSetup); Evs evs = setPokemonEvs(pokemonInfo, pokemonSetup); Ivs ivs = setPokemonIvs(pokemonInfo, pokemonSetup); - types::stat hp = setPokemonStats(pokemonInfo, pokemonSetup, pokedex(), level, nature, evs, ivs); + types::stat hp = setPokemonStats(pokemonInfo, pokemonSetup, pokedex, level, nature, evs, ivs); pokemonSetup.setLevel(level); @@ -1506,7 +1582,7 @@ PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& po pokemonSetup.setTypes(pokemonInfo.currentTypes.value()); } else { - pokemonSetup.setTypes(pokedex().getSpeciesData(pokemonInfo.species)); + pokemonSetup.setTypes(pokedex.getSpeciesData(pokemonInfo.species)); } if (pokemonInfo.gender.has_value() && pokemonInfo.gender != dex::Gender::NO_GENDER) { @@ -1534,41 +1610,102 @@ PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& po pokemonSetup.setProperty(); pokemonSetup.setProperty(); pokemonSetup.setProperty(); +} + +void createCalcDamageInput( + const CalcDamageInputInfo& inputInfo, BattleStateSetup& battleSetup, types::registry& registry, + EntityLists& entityLists, debug::SimulationSetupChecks& debugChecks) { + POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "A damage calculation must have a attacker."); + POKESIM_REQUIRE(inputInfo.defenderSlot != Slot::NONE, "A damage calculation must have a defender."); + POKESIM_REQUIRE(!inputInfo.moves.empty(), "A damage calculation must have a move."); + + const Sides& sides = registry.get(battleSetup.entity()); + types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); + types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); + + for (dex::Move move : inputInfo.moves) { + calc_damage::InputSetup inputSetup{registry, entityLists.calcDamageInputs.getNext()}; + POKESIM_REQUIRE(move != dex::Move::NO_MOVE, "A damage calculation must have a move."); - return pokemonSetup; + inputSetup.setup(battleSetup.entity(), attackerEntity, defenderEntity, move); + debugChecks.addToCalcDamageChecklist(battleSetup, inputSetup, inputInfo); + } } -void Simulation::createInitialSide( - SideStateSetup sideSetup, const SideCreationInfo& sideInfo, const BattleCreationInfo& battleInfo) { - internal::maxSizedVector pokemonSetupList; - pokemonSetupList.reserve(sideInfo.team.size()); +void createAnalyzeEffectInput( + const AnalyzeEffectInputInfo& inputInfo, BattleStateSetup& battleSetup, types::registry& registry, + EntityLists& entityLists, debug::SimulationSetupChecks& debugChecks) { + POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "An effect analysis must have a attacker."); + 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( + boostEffect.has_value() || (effect.has_value() && !effect.value().empty()), + "An effect analysis must have an effect."); + + const Sides& sides = registry.get(battleSetup.entity()); + types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); + types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); + types::entity effectTargetEntity = slotToPokemonEntity(registry, sides, inputInfo.effectTarget); + + for (dex::Move move : inputInfo.moves) { + analyze_effect::InputSetup inputSetup{registry, entityLists.analyzeEffectInputs.getNext()}; + + inputSetup.setAttacker(attackerEntity); + inputSetup.setDefender(defenderEntity); + inputSetup.setEffectTarget(effectTargetEntity); + inputSetup.setEffectMove(move); + inputSetup.setBattle(battleSetup.entity()); + + if (effect.has_value()) { + inputSetup.setEffect(effect.value()); + } + if (boostEffect.has_value()) { + inputSetup.setBoostEffect(boostEffect.value().stat, boostEffect.value().boost); + } + + debugChecks.addToAnalyzeEffectChecklist(battleSetup, inputSetup, inputInfo); + } +} + +void createInitialTurnDecision(const TurnDecisionInfo& turnDecisionInfo, types::sides& sideSetupList) { + for (types::sideIndex i = 0U; i < sideSetupList.size(); i++) { + sideSetupList[i].setSideDecision(turnDecisionInfo[i]); + } +} + +void createInitialSide( + const SideCreationInfo& sideInfo, SideStateSetup& sideSetup, const BattleCreationInfo& battleInfo, + Simulation* simulation, internal::maxSizedVector& pokemonSetupList) { for (std::size_t i = 0U; i < sideInfo.team.size(); i++) { + PokemonStateSetup& pokemonSetup = pokemonSetupList[i]; const PokemonCreationInfo& pokemonInfo = sideInfo.team[i]; - PokemonStateSetup pokemonSetup = createInitialPokemon(pokemonInfo); + bool battleStarted = battleInfo.turn > Constants::TurnCount::MIN; - bool inActiveSlot = (isBattleFormat(BattleFormat::SINGLES) ? 1U : 2U) > i; + bool inActiveSlot = (simulation->isBattleFormat(BattleFormat::SINGLES) ? 1U : 2U) > i; bool isFainted = pokemonInfo.currentHp.has_value() && pokemonInfo.currentHp == Constants::PokemonCurrentHpStat::MIN; if (battleStarted && inActiveSlot && !isFainted) { pokemonSetup.setProperty(); } - types::entityVector moveEntities = createInitialMoves(pokemonInfo.moves); + std::vector moveSlots; + for (const MoveCreationInfo& moveInfo : pokemonInfo.moves) { + types::pp maxPp = Constants::MoveMaxPp::DEFAULT; + if (!moveInfo.pp.has_value() || !moveInfo.maxPp.has_value()) { + maxPp = simulation->pokedex().getMoveData(moveInfo.name).val; + } + maxPp = moveInfo.maxPp.value_or(maxPp); - if (battleInfo.runWithSimulateTurn) { - registry.insert(moveEntities.begin(), moveEntities.end()); - } - if (battleInfo.runWithCalculateDamage) { - registry.insert(moveEntities.begin(), moveEntities.end()); - } - if (battleInfo.runWithAnalyzeEffect) { - registry.insert(moveEntities.begin(), moveEntities.end()); + moveSlots.push_back({moveInfo.name, moveInfo.pp.value_or(maxPp), maxPp}); } - pokemonSetup.setMoves(moveEntities); - pokemonSetupList.push_back(pokemonSetup); + pokemonSetup.setMoves(moveSlots); } + types::registry& registry = simulation->registry; if (battleInfo.runWithSimulateTurn) { sideSetup.setProperty(); registry.insert(pokemonSetupList.begin(), pokemonSetupList.end()); @@ -1585,166 +1722,131 @@ void Simulation::createInitialSide( sideSetup.setTeam(pokemonSetupList); } -types::sides Simulation::createInitialBattle( - BattleStateSetup battleStateSetup, const BattleCreationInfo& battleInfo) { - battleStateSetup.setAutoID(); - battleStateSetup.setTurn(battleInfo.turn.value_or(Constants::TurnCount::DEFAULT)); - battleStateSetup.setRNGSeed(battleInfo.rngSeed); - battleStateSetup.setProbability(battleInfo.probability.value_or(Constants::Probability::DEFAULT)); +void createInitialBattle( + const BattleCreationInfo& battleInfo, BattleStateSetup& battleSetup, types::sides& sideSetupList) { + battleSetup.setTurn(battleInfo.turn.value_or(Constants::TurnCount::DEFAULT)); + battleSetup.setRNGSeed(battleInfo.rngSeed); + battleSetup.setProbability(battleInfo.probability.value_or(Constants::Probability::DEFAULT)); if (battleInfo.runWithSimulateTurn) { - battleStateSetup.setProperty(); + battleSetup.setProperty(); } if (battleInfo.runWithCalculateDamage) { - battleStateSetup.setProperty(); + battleSetup.setProperty(); } if (battleInfo.runWithAnalyzeEffect) { - battleStateSetup.setProperty(); + battleSetup.setProperty(); } - SideStateSetup p1SideSetup(registry, PlayerSideId::P1); - SideStateSetup p2SideSetup(registry, PlayerSideId::P2); + SideStateSetup& p1SideSetup = sideSetupList.p1(); + SideStateSetup& p2SideSetup = sideSetupList.p2(); - types::entity battleEntity = battleStateSetup.entity(); + p1SideSetup.setPlayerSide(PlayerSideId::P1); + p2SideSetup.setPlayerSide(PlayerSideId::P2); + + types::entity battleEntity = battleSetup.entity(); types::entity p1Entity = p1SideSetup.entity(); types::entity p2Entity = p2SideSetup.entity(); - battleStateSetup.setSide(p1Entity); - battleStateSetup.setSide(p2Entity); + battleSetup.setSide(p1Entity); + battleSetup.setSide(p2Entity); p1SideSetup.setOpponent(p2Entity); p2SideSetup.setOpponent(p1Entity); p1SideSetup.setBattle(battleEntity); p2SideSetup.setBattle(battleEntity); - - return {p1SideSetup, p2SideSetup}; } -void Simulation::createInitialTurnDecision( - BattleStateSetup battleStateSetup, const TurnDecisionInfo& turnDecisionInfo) { - types::handle battleHandle{registry, battleStateSetup.entity()}; - const Sides& sides = battleHandle.get(); - - for (types::sideIndex i = 0U; i < sides.val.size(); i++) { - registry.emplace(sides.val[i], turnDecisionInfo[i]); - } -} +void createInitialState( + const BattleCreationInfo& battleInfo, Simulation* simulation, EntityLists& entityLists, + debug::SimulationSetupChecks& debugChecks) { + internal::maxSizedVector battleSetupList; + internal::maxSizedVector> sideSetupLists; + internal::maxSizedVector>> + pokemonSetupLists; + battleSetupList.resize(getBattleCreationCount(battleInfo)); + sideSetupLists.resize(battleSetupList.size()); + pokemonSetupLists.resize(battleSetupList.size()); -void Simulation::createCalcDamageInput( - BattleStateSetup battleStateSetup, const CalcDamageInputInfo& inputInfo, debug::SimulationSetupChecks& debugChecks) { - POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "A damage calculation must have a attacker."); - POKESIM_REQUIRE(inputInfo.defenderSlot != Slot::NONE, "A damage calculation must have a defender."); - POKESIM_REQUIRE(!inputInfo.moves.empty(), "A damage calculation must have a move."); + const Pokedex& pokedex = simulation->pokedex(); + types::registry& registry = simulation->registry; - const Sides& sides = registry.get(battleStateSetup.entity()); - types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); - types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); - - for (dex::Move move : inputInfo.moves) { - calc_damage::InputSetup inputSetup(registry); - POKESIM_REQUIRE(move != dex::Move::NO_MOVE, "A damage calculation must have a move."); + for (BattleStateSetup& battleSetup : battleSetupList) { + battleSetup = {registry, entityLists.battles.getNext()}; + } - inputSetup.setup(battleStateSetup.entity(), attackerEntity, defenderEntity, move, pokedex()); - debugChecks.addToCalcDamageChecklist(battleStateSetup, inputSetup, inputInfo); + for (types::sideIndex sideIndex = 0; sideIndex < battleInfo.sides.size(); sideIndex++) { + for (auto& sideSetupList : sideSetupLists) { + sideSetupList[sideIndex] = {registry, entityLists.sides.getNext()}; + } } -} -void Simulation::createAnalyzeEffectInput( - BattleStateSetup battleStateSetup, const AnalyzeEffectInputInfo& inputInfo, - debug::SimulationSetupChecks& debugChecks) { - POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "An effect analysis must have a attacker."); - 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."); + for (types::entityIndex battleIndex = 0; battleIndex < battleSetupList.size(); battleIndex++) { + BattleStateSetup& battleSetup = battleSetupList[battleIndex]; + createInitialBattle(battleInfo, battleSetup, sideSetupLists[battleIndex]); + battleSetup.setRecycledAction(entityLists.recycledActions.getNext(), entityLists.recycledActionMoves.getNext()); + if (simulation->isBattleFormat(BattleFormat::DOUBLES)) { + battleSetup.setAddedRecycledActionMoves( + entityLists.addedRecycledActionMoves.getNext(), + entityLists.addedRecycledActionMoves.getNext()); + } + } - const auto& effect = inputInfo.effect; - const auto& boostEffect = inputInfo.boostEffect; - POKESIM_REQUIRE( - boostEffect.has_value() || (effect.has_value() && !effect.value().empty()), - "An effect analysis must have an effect."); + for (types::sideIndex sideIndex = 0; sideIndex < battleInfo.sides.size(); sideIndex++) { + for (const PokemonCreationInfo& pokemonInfo : battleInfo.sides[sideIndex].team) { + for (auto& pokemonSetupList : pokemonSetupLists) { + PokemonStateSetup& pokemonSetup = + pokemonSetupList[sideIndex].emplace_back(registry, entityLists.pokemon.getNext()); + createInitialPokemon(pokemonInfo, pokemonSetup, pokedex); + } + } + } - const Sides& sides = registry.get(battleStateSetup.entity()); - types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); - types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); - types::entity effectTargetEntity = slotToPokemonEntity(registry, sides, inputInfo.effectTarget); + for (types::entityIndex battleIndex = 0; battleIndex < battleSetupList.size(); battleIndex++) { + BattleStateSetup& battleSetup = battleSetupList[battleIndex]; + auto& sideSetupList = sideSetupLists[battleIndex]; + for (types::sideIndex sideIndex = 0; sideIndex < battleInfo.sides.size(); sideIndex++) { + createInitialSide( + battleInfo.sides[sideIndex], + sideSetupList[sideIndex], + battleInfo, + simulation, + pokemonSetupLists[battleIndex][sideIndex]); + } - for (dex::Move move : inputInfo.moves) { - analyze_effect::InputSetup inputSetup(registry); + if (battleIndex < battleInfo.decisionsToSimulate.size()) { + const TurnDecisionInfo& turnDecisionInfo = battleInfo.decisionsToSimulate[battleIndex]; + createInitialTurnDecision(turnDecisionInfo, sideSetupList); + debugChecks.addToTurnDecisionChecklist(battleSetup, turnDecisionInfo); + } + battleSetup.setID(battleIndex); + debugChecks.addToBattleChecklist(battleSetup, battleInfo); + } - inputSetup.setAttacker(attackerEntity); - inputSetup.setDefender(defenderEntity); - inputSetup.setEffectTarget(effectTargetEntity); - inputSetup.setEffectMove(move); - inputSetup.setBattle(battleStateSetup.entity()); + BattleStateSetup& battleSetup = battleSetupList[0]; + for (const CalcDamageInputInfo& calcDamageInputInfo : battleInfo.damageCalculations) { + createCalcDamageInput(calcDamageInputInfo, battleSetup, registry, entityLists, debugChecks); + } - if (effect.has_value()) { - inputSetup.setEffect(effect.value()); - } - if (boostEffect.has_value()) { - inputSetup.setBoostEffect(boostEffect.value().stat, boostEffect.value().boost); - } - debugChecks.addToAnalyzeEffectChecklist(battleStateSetup, inputSetup, inputInfo); + for (const AnalyzeEffectInputInfo& analyzeEffectInputInfo : battleInfo.effectsToAnalyze) { + createAnalyzeEffectInput(analyzeEffectInputInfo, battleSetup, registry, entityLists, debugChecks); } } +} // namespace void Simulation::createInitialStates(const std::vector& battleInfoList) { debug::SimulationSetupChecks debugChecks(this, battleInfoList); - for (const BattleCreationInfo& battleInfo : battleInfoList) { - BattleStateSetup battleStateSetup(registry); - types::sides sideSetup = createInitialBattle(battleStateSetup, battleInfo); - - for (types::sideIndex i = 0U; i < sideSetup.size(); i++) { - createInitialSide(sideSetup[i], battleInfo.sides[i], battleInfo); - } - - debugChecks.addToBattleChecklist(battleStateSetup, battleInfo); - - if (!battleInfo.decisionsToSimulate.empty()) { - POKESIM_REQUIRE( - battleInfo.decisionsToSimulate.size() < std::numeric_limits::max(), - "Cannot make more clones than there are entities available."); - - types::entityIndex cloneCount = (types::entityIndex)(battleInfo.decisionsToSimulate.size() - 1U); - if (cloneCount) { - battleStateSetup.setProperty(); - const types::ClonedEntityMap entityMap = pokesim::clone(registry, cloneCount); - - const auto& clonedBattles = entityMap.at(battleStateSetup.entity()); - internal::maxSizedVector clones; - clones.reserve(clonedBattles.size()); - - for (types::entity entity : clonedBattles) { - clones.emplace_back(registry, entity); - } - - for (types::entityIndex i = 0U; i < cloneCount; i++) { - BattleStateSetup& setupClone = clones[i]; - const TurnDecisionInfo& turnDecisionInfo = battleInfo.decisionsToSimulate[i]; - debugChecks.addToBattleChecklist(setupClone, battleInfo); - - createInitialTurnDecision(setupClone, turnDecisionInfo); - setupClone.setID(i); - - debugChecks.addToTurnDecisionChecklist(setupClone, turnDecisionInfo); - } - } - - createInitialTurnDecision(battleStateSetup, battleInfo.decisionsToSimulate.back()); - debugChecks.addToTurnDecisionChecklist(battleStateSetup, battleInfo.decisionsToSimulate.back()); - battleStateSetup.setID(cloneCount); - } + EntityLists entityLists{this, battleInfoList}; - for (const CalcDamageInputInfo& calcDamageInputInfo : battleInfo.damageCalculations) { - createCalcDamageInput(battleStateSetup, calcDamageInputInfo, debugChecks); - } - - for (const AnalyzeEffectInputInfo& analyzeEffectInputInfo : battleInfo.effectsToAnalyze) { - createAnalyzeEffectInput(battleStateSetup, analyzeEffectInputInfo, debugChecks); - } + for (const BattleCreationInfo& battleInfo : battleInfoList) { + createInitialState(battleInfo, this, entityLists, debugChecks); } + pokedex().buildMoves(registry); + registry.clear(); + debugChecks.checkOutputs(); } } // namespace pokesim @@ -2030,6 +2132,8 @@ template void runAfterEachBoostEvent(Simulation&); void runAfterBoostEvent(Simulation&) {} +void runModifyTarget(Simulation&) {} + void runModifyMove(Simulation& simulation) { dex::ChoiceScarf::onSourceModifyMove(simulation); dex::ChoiceSpecs::onSourceModifyMove(simulation); @@ -2315,63 +2419,116 @@ auto getBattleFilter(Simulation& simulation) { return pokesim::internal::EntityFilter{simulation}; } -void addTargetAllyToTargets(types::registry& registry, const Battle& battle) { +void addAddedTarget(types::registry& registry, Battle battle, Slot allySlot) { const Sides& sides = registry.get(battle.val); - const TargetSlotName& targetSlotName = registry.get(registry.get(battle.val).val); - - types::entity allyEntity = slotToAllyPokemonEntity(registry, sides, targetSlotName.val); + types::entity allyEntity = slotToAllyPokemonEntity(registry, sides, allySlot); if (allyEntity == entt::null) { return; } CurrentActionTargets& targets = registry.get(battle.val); + + POKESIM_REQUIRE(!targets.val.empty(), "Added targets should not be the first in the list of targets."); + + if (targets.val.size() == 1U) { + registry.emplace(battle.val); + } + else { + registry.emplace(battle.val); + } + targets.val.push_back(allyEntity); } +void addTargetAllyToTargets(types::registry& registry, Battle battle) { + const TargetSlotName& targetSlotName = registry.get(registry.get(battle.val).val); + addAddedTarget(registry, battle, targetSlotName.val); +} + void addUserAllyToTargets(types::registry& registry, const Battle& battle) { - const Sides& sides = registry.get(battle.val); const SourceSlotName& sourceSlotName = registry.get(registry.get(battle.val).val); + addAddedTarget(registry, battle, sourceSlotName.val); +} - types::entity allyEntity = slotToAllyPokemonEntity(registry, sides, sourceSlotName.val); - if (allyEntity == entt::null) { - return; +void setTargetReferenceComponents( + types::registry& registry, const CurrentActionTargets& targets, CurrentActionSource source) { + for (types::entity target : targets.val) { + registry.emplace(target); + registry.emplace(target, source.val); } - - CurrentActionTargets& targets = registry.get(battle.val); - targets.val.push_back(allyEntity); } -void resolveMoveTargets(types::registry& registry, CurrentActionTargets& targets) { - for (types::entity target : targets.val) { - registry.emplace_or_replace(target); +template +void setActionMoveReferenceComponents( + types::handle battleHandle, CurrentActionSource source, CurrentActionTargets targets, CurrentAction action, + RecycledActionMoveType actionMove) { + types::registry& registry = *battleHandle.registry(); + types::handle actionMoveHandle{registry, actionMove.val}; + types::entity target; + + if constexpr (std::is_same_v) { + target = targets.val[0]; + } + else if constexpr (std::is_same_v) { + target = targets.val[1]; + } + else if constexpr (std::is_same_v) { + target = targets.val[2]; + } + else { + POKESIM_REQUIRE_FAIL("Using a RecycledActionMoveType that isn't associated with a target."); } - // More to do... + MoveName move = registry.get(action.val); + setupActionMoveBuild(registry, battleHandle.entity(), source.val, target, actionMove.val, move.val); } -void createActionMoveForTargets( - types::handle targetHandle, Battle battle, CurrentActionSource source, const Pokedex& pokedex) { - types::registry& registry = *targetHandle.registry(); +void setActionMoveData(Simulation& simulation) { + simulation.addToEntities(); + simulation.pokedex().buildMoves(simulation.registry); +} - dex::Move move = registry.get(registry.get(battle.val).val).val; - types::entity moveEntity = createActionMoveForTarget(targetHandle, battle.val, source.val, move, pokedex); +void setCurrentActionMoveSlot(types::handle handle, CurrentActionSource source, CurrentAction action) { + types::registry& registry = *handle.registry(); + const MoveName& move = registry.get(action.val); + const MoveSlots& moveSlots = registry.get(source.val); - registry.emplace(moveEntity); + types::moveSlotIndex moveSlotIndex = moveToMoveSlot(moveSlots, move.val); + handle.emplace(moveSlotIndex); } -void getMoveTargets(Simulation& simulation) { +void setMoveTargets(Simulation& simulation) { + pokesim::internal::EntityFilter battleFilter{simulation}; + if (battleFilter.hasNoneSelected()) { + return; + } + + battleFilter.view(simulation); + + battleFilter.view>(); + setActionMoveData(simulation); + simulation.removeFromEntities(); + + runModifyTarget(simulation); if (simulation.isBattleFormat(BattleFormat::DOUBLES)) { simulation .view>(); simulation .view>(); + + battleFilter.view< + setActionMoveReferenceComponents, + Tags>(); + battleFilter.view< + setActionMoveReferenceComponents, + Tags>(); + setActionMoveData(simulation); + simulation.removeFromEntities(); + simulation.removeFromEntities(); + simulation.removeFromEntities(); } - simulation.view, entt::exclude_t>(); - simulation.view< - createActionMoveForTargets, - Tags, - entt::exclude_t>(simulation.pokedex()); + battleFilter.view(); } void useMove(Simulation& simulation) { @@ -2379,7 +2536,6 @@ void useMove(Simulation& simulation) { // ModifyType runModifyMove(simulation); - getMoveTargets(simulation); runMoveHitChecks(simulation); runAfterMoveUsedEvent(simulation); } @@ -2408,13 +2564,13 @@ void runMoveAction(Simulation& simulation) { return; } - battleFilter.view(simulation); - battleFilter.view(simulation.pokedex()); + setMoveTargets(simulation); + battleFilter.view(); runBeforeMove(simulation); - simulation.view>(); simulation.view(); + simulation.view>(); useMove(simulation); } @@ -2454,7 +2610,7 @@ void checkWin(types::handle battleHandle, const Sides& sides) { types::teamPositionIndex pokemonLeft = foeSidePokemonLeft(registry, sideEntity); if (!pokemonLeft) { battleHandle.emplace(registry.get(sideEntity).val); - clearActionQueue(battleHandle, battleHandle.get()); + clearActionQueue(battleHandle.get()); return; } } @@ -2521,16 +2677,12 @@ void incrementTurn(Turn& turn) { turn.val++; } -void updateActivePokemonPostTurn(types::registry& registry, const pokesim::MoveSlots& moveSlots) { - registry.remove(moveSlots.val.begin(), moveSlots.val.end()); -} - void nextTurn(Simulation& simulation) { getBattleFilter(simulation).view(); pokesim::internal::EntityFilter pokemonFilter{simulation}; if (!pokemonFilter.hasNoneSelected()) { - pokemonFilter.view(); + simulation.removeFromEntities(); pokemonFilter.addToSelected(); runDisableMove(simulation); @@ -2558,8 +2710,11 @@ void simulateTurn(Simulation& simulation) { simulation.addToEntities(); const auto entityMap = clone(simulation.registry, 1U); for (const auto& inputBattleMapping : entityMap) { - simulation.registry.emplace(inputBattleMapping.first); - simulation.registry.remove(inputBattleMapping.first); + types::entity original = inputBattleMapping.first; + if (simulation.registry.all_of(original)) { + simulation.registry.emplace(original); + simulation.registry.remove(original); + } } } @@ -2573,7 +2728,7 @@ void simulateTurn(Simulation& simulation) { battleFilter.view(); battleFilter.view, entt::exclude_t>(); - simulation.addToEntities(); + simulation.addToEntities(); battleFilter.view(); using ActionsLimit = Constants::ActionQueueLength; @@ -2590,8 +2745,7 @@ void simulateTurn(Simulation& simulation) { nextTurn(simulation); - simulation - .removeFromEntities(); + simulation.removeFromEntities(); battleFilter.view(); simulation.addToEntities(); @@ -3177,62 +3331,59 @@ template void setRandomEventChances<5U>(types::handle, const Simulation&, const namespace pokesim::simulate_turn { namespace { -void resolveSlotDecisions( - types::handle sideHandle, const types::slotDecisions& decisions, ActionQueue& battleActionQueue) { +template +void resolveSlotDecision(types::handle sideHandle, const types::slotDecision& slotDecision, ActionQueue& actionQueue) { + if (!slotDecision.holds()) { + return; + } + types::registry& registry = *sideHandle.registry(); - for (const SlotDecision& decision : decisions) { - POKESIM_REQUIRE(decision.sourceSlot != Slot::NONE, "Source slot must be assigned."); - POKESIM_REQUIRE(decision.targetSlot != Slot::NONE, "Target slot must be assigned."); - POKESIM_REQUIRE( - !(decision.moveChoice.has_value() && decision.itemChoice.has_value()), - "Decisions can't have a move and an item choice."); + const auto& decision = slotDecision.get(); - types::handle actionHandle = {registry, registry.create()}; - actionHandle.emplace(decision.sourceSlot); - actionHandle.emplace(decision.targetSlot); + POKESIM_REQUIRE(decision.sourceSlot != Slot::NONE, "Source slot must be assigned."); + POKESIM_REQUIRE(decision.targetSlot != Slot::NONE, "Target slot must be assigned."); - SpeedSort speedSort; - types::entity sourceEntity = slotToPokemonEntity(registry, sideHandle.entity(), decision.sourceSlot); + ActionQueueItem actionQueueItem; + actionQueueItem.decision = decision; - stat::EffectiveSpe* effectiveSpe = registry.try_get(sourceEntity); - if (effectiveSpe != nullptr) { - speedSort.speed = effectiveSpe->val; - } - else { - speedSort.speed = registry.get(sourceEntity).val; - } + types::entity sourceEntity = slotToPokemonEntity(registry, sideHandle.entity(), decision.sourceSlot); + actionQueueItem.speed = registry.get(sourceEntity).val; - if (decision.moveChoice.has_value()) { - actionHandle.emplace(); - actionHandle.emplace(decision.moveChoice.value()); + if constexpr (std::is_base_of_v) { + actionQueueItem.order = ActionOrder::MOVE; + actionQueueItem.priority = Constants::MovePriority::DEFAULT; // TODO (aed3): Move priority + modify priority + actionQueueItem.fractionalPriority = false; // TODO (aed3): get fractionalPriority - speedSort.order = ActionOrder::MOVE; - speedSort.priority = Constants::MovePriority::DEFAULT; // TODO (aed3): Move priority + modify priority - speedSort.fractionalPriority = false; // TODO (aed3): get fractionalPriority - } - else if (decision.itemChoice.has_value()) { - actionHandle.emplace(); - actionHandle.emplace(decision.itemChoice.value()); - speedSort.order = ActionOrder::ITEM; - } - else { - actionHandle.emplace(); - speedSort.order = ActionOrder::SWITCH; + if constexpr (!std::is_same_v) { + POKESIM_REQUIRE_FAIL(std::string(entt::type_name().value()) + " is not yet supported."); } + } + else if constexpr (std::is_same_v) { + actionQueueItem.order = ActionOrder::SWITCH; + } + else if constexpr (std::is_same_v) { + actionQueueItem.order = ActionOrder::ITEM; + } + else { + POKESIM_REQUIRE_FAIL(std::string(entt::type_name().value()) + " is not yet supported."); + } - actionHandle.emplace(speedSort); + actionQueue.val.push_back(actionQueueItem); +} - battleActionQueue.val.push_back(actionHandle.entity()); +void resolveSlotDecisions(types::handle sideHandle, const types::slotDecisions& decisions, ActionQueue& actionQueue) { + for (const types::slotDecision& decision : decisions) { + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); } } -void resolveTeamDecision(types::registry& registry, const types::teamOrder& teamOrder, ActionQueue& battleActionQueue) { - types::handle actionHandle = {registry, registry.create()}; - - actionHandle.emplace(teamOrder); - - battleActionQueue.val.push_back(actionHandle.entity()); -} +void resolveTeamDecision(types::registry&, const types::teamOrder&, ActionQueue&) {} } // namespace void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) { @@ -3241,7 +3392,7 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) const Battle& battle = sideHandle.get(); types::registry& registry = *sideHandle.registry(); - ActionQueue& battleActionQueue = registry.get(battle.val); + ActionQueue& actionQueue = registry.get(battle.val); if (sideDecision.decisions.holds()) { POKESIM_REQUIRE( @@ -3251,21 +3402,21 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) const auto& decisions = sideDecision.decisions.get(); #ifdef POKESIM_DEBUG_CHECK_UTILITIES - for (const SlotDecision& decision : decisions) { + for (const auto& decision : decisions) { if (sideDecision.sideId == PlayerSideId::P1) { POKESIM_REQUIRE( - (decision.sourceSlot == Slot::P1A || decision.sourceSlot == Slot::P1B), + (decision.sourceSlot() == Slot::P1A || decision.sourceSlot() == Slot::P1B), "Source must be from a player 1 in battle slot."); } else { POKESIM_REQUIRE( - (decision.sourceSlot == Slot::P2A || decision.sourceSlot == Slot::P2B), + (decision.sourceSlot() == Slot::P2A || decision.sourceSlot() == Slot::P2B), "Source must be from a player 2 in battle slot."); } } #endif - resolveSlotDecisions(sideHandle, decisions, battleActionQueue); + resolveSlotDecisions(sideHandle, decisions, actionQueue); } else if (sideDecision.decisions.holds()) { POKESIM_REQUIRE( @@ -3277,7 +3428,7 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) POKESIM_REQUIRE( sideHandle.get().val.size() == teamOrder.size(), "Must pick a placement for each Pokemon on the team."); - resolveTeamDecision(*sideHandle.registry(), teamOrder, battleActionQueue); + resolveTeamDecision(*sideHandle.registry(), teamOrder, actionQueue); } else { POKESIM_REQUIRE_FAIL( @@ -3286,51 +3437,23 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) } void speedSort(types::handle handle, ActionQueue& actionQueue) { - auto& entityList = actionQueue.val; + auto& actionQueueItems = actionQueue.val; - if (entityList.size() == 1U) return; - const types::registry* registry = handle.registry(); - - internal::maxSizedVector, Constants::ActionQueueLength::MAX> speedSortList; - speedSortList.reserve(entityList.size()); - - for (types::entity entity : entityList) { - speedSortList.push_back({registry->get(entity), entity}); + if (actionQueueItems.size() == 1U) { + return; } // TODO(aed3): Test how different sorting algorithms affect speed - std::sort(speedSortList.begin(), speedSortList.end(), [](const auto& pairA, const auto& pairB) { - if (pairA.first.order != pairB.first.order) { - return pairA.first.order < pairB.first.order; - } - - if (pairA.first.priority != pairB.first.priority) { - return pairB.first.priority < pairA.first.priority; - } - - if (pairA.first.fractionalPriority != pairB.first.fractionalPriority) { - return pairB.first.fractionalPriority; - } - - if (pairA.first.speed != pairB.first.speed) { - return pairB.first.speed < pairA.first.speed; - } - - return false; - }); + std::sort( + actionQueueItems.begin(), + actionQueueItems.end(), + [](const ActionQueueItem& itemA, const ActionQueueItem& itemB) { return itemA.isFasterThan(itemB); }); SpeedTieIndexes speedTies; types::activePokemonIndex lastEqual = 0U, tieCount = 1U; - auto speedSortEqual = [](const SpeedSort& speedSortA, const SpeedSort& speedSortB) { - return speedSortA.order == speedSortB.order && speedSortA.priority == speedSortB.priority && - speedSortA.speed == speedSortB.speed && speedSortA.fractionalPriority == speedSortB.fractionalPriority; - }; - - for (types::activePokemonIndex i = 0U; i < speedSortList.size(); i++) { - entityList[i] = speedSortList[i].second; - - if (i > 0U && speedSortEqual(speedSortList[i].first, speedSortList[i - 1].first)) { + for (types::activePokemonIndex i = 1U; i < actionQueueItems.size(); i++) { + if (actionQueueItems[i].isSameSpeed(actionQueueItems[i - 1U])) { tieCount++; } else { @@ -3351,55 +3474,51 @@ void speedSort(types::handle handle, ActionQueue& actionQueue) { } } -void addBeforeTurnAction(types::registry& registry, ActionQueue& actionQueue) { - types::handle actionHandle{registry, registry.create()}; - SpeedSort speedSort{ActionOrder::BEFORE_TURN}; - - actionHandle.emplace(); - actionHandle.emplace(speedSort); - actionQueue.val.push_back(actionHandle.entity()); +void addBeforeTurnAction(ActionQueue& actionQueue) { + actionQueue.val.push_back({ActionOrder::BEFORE_TURN}); } -void addResidualAction(types::registry& registry, ActionQueue& actionQueue) { - types::handle actionHandle{registry, registry.create()}; - SpeedSort speedSort{ActionOrder::RESIDUAL}; - - actionHandle.emplace(); - actionHandle.emplace(speedSort); - actionQueue.val.push_back(actionHandle.entity()); +void addResidualAction(ActionQueue& actionQueue) { + actionQueue.val.push_back({ActionOrder::RESIDUAL}); } -void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue) { +void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue, RecycledAction& action) { types::registry& registry = *battleHandle.registry(); if (actionQueue.val.empty()) return; - types::entity newCurrentAction = actionQueue.val.front(); - registry.emplace(newCurrentAction); + ActionQueueItem nextActon = actionQueue.val.front(); + registry.emplace(action.val); - if (registry.all_of(newCurrentAction)) { - battleHandle.emplace(); - battleHandle.emplace(registry.get(newCurrentAction)); - } - else if (registry.all_of(newCurrentAction)) { - battleHandle.emplace(); - } - else { - POKESIM_REQUIRE_FAIL("Action kind not implemented yet."); + switch (nextActon.order) { + case ActionOrder::MOVE: { + battleHandle.emplace(); + const MoveDecision& decision = nextActon.decision.get(); + registry.emplace(action.val, decision.sourceSlot); + registry.emplace(action.val, decision.targetSlot); + registry.emplace(action.val, decision.move); + break; + } + case ActionOrder::RESIDUAL: { + battleHandle.emplace(); + break; + } + case ActionOrder::BEFORE_TURN: { + battleHandle.emplace(); + break; + } + default: { + POKESIM_REQUIRE_FAIL("Action kind not implemented yet."); + break; + } } actionQueue.val.erase(actionQueue.val.begin()); - battleHandle.remove(); - battleHandle.emplace(newCurrentAction); - if (!actionQueue.val.empty()) { - battleHandle.emplace(actionQueue.val[0]); - } + battleHandle.emplace(action.val); } -void clearActionQueue(types::handle battleHandle, ActionQueue& actionQueue) { - battleHandle.remove(); - battleHandle.registry()->destroy(actionQueue.val.begin(), actionQueue.val.end()); +void clearActionQueue(ActionQueue& actionQueue) { actionQueue.val.clear(); } } // namespace pokesim::simulate_turn @@ -3472,161 +3591,65 @@ void updatePartialProbabilities(Simulation& simulation) { void cloneFromDamageRolls(Simulation& simulation, DamageRollKind damageRollKind) { pokesim::internal::EntityFilter moveFilter{simulation}; - moveFilter.applySelectionTags(); - if (moveFilter.hasNoneSelected()) return; - - bool forAllDamageRolls = damageRollKind & DamageRollKind::ALL_DAMAGE_ROLLS; - bool forRequiredDamageRolls = simulation.simulateTurnOptions.getMakeBranchesOnRandomEvents() || forAllDamageRolls; - auto applyChoices = [](Simulation& sim) { sim.view(); }; - auto updateProbabilities = forAllDamageRolls ? updateAllDamageRollProbabilities : updatePartialProbabilities; - - runRandomEventCount( - simulation, - countUniqueDamageRolls, - applyChoices, - updateProbabilities, - forRequiredDamageRolls); - - simulation.removeFromEntities(); - moveFilter.clearSelectionTags(); -} - -void setIfMoveCrits(Simulation& simulation) { - runReciprocalRandomBinaryChance(simulation, [](Simulation& sim) { - sim.addToEntities(); - }); -} -} // namespace pokesim::simulate_turn - -/////////////// END OF src/SimulateTurn/CalcDamageSpecifics.cpp //////////////// - -////////////// START OF src/Pokedex/Setup/SpeciesDexDataSetup.cpp ////////////// - -namespace pokesim::dex::internal { -void SpeciesDexDataSetup::setName(Species species) { - handle.emplace(species); -} - -void SpeciesDexDataSetup::setType(Type type1, Type type2) { - handle.emplace(type1, type2); -} - -void SpeciesDexDataSetup::setBaseStats( - types::baseStat hp, types::baseStat atk, types::baseStat def, types::baseStat spa, types::baseStat spd, - types::baseStat spe) { - handle.emplace(hp, atk, def, spa, spd, spe); -} - -void SpeciesDexDataSetup::setPrimaryAbility(Ability ability) { - handle.emplace(ability); -} - -void SpeciesDexDataSetup::setSecondaryAbility(Ability ability) { - handle.emplace(ability); -} - -void SpeciesDexDataSetup::setHiddenAbility(Ability ability) { - handle.emplace(ability); -} -} // namespace pokesim::dex::internal - -/////////////// END OF src/Pokedex/Setup/SpeciesDexDataSetup.cpp /////////////// - -/////////////// START OF src/Pokedex/Setup/MoveDexDataSetup.cpp //////////////// - -namespace pokesim::dex::internal { -void MoveDexDataSetup::setName(Move move) { - handle.emplace(move); -} - -void MoveDexDataSetup::setNameTag(Move move) { - move::tags::emplaceTagFromEnum(move, handle); -} - -void MoveDexDataSetup::setType(Type type) { - handle.emplace(type); -} - -void MoveDexDataSetup::addAddedTargets(AddedTargetOptions addedTargets) { - AddedTargets& existingTargets = handle.get_or_emplace(); - existingTargets.val = existingTargets.val | addedTargets; - - switch (addedTargets) { - case AddedTargetOptions::TARGET_ALLY: { - setProperty(); - break; - } - case AddedTargetOptions::USER_ALLY: { - setProperty(); - break; - } - case AddedTargetOptions::TARGET_SIDE: { - setProperty(); - break; - } - case AddedTargetOptions::USER_SIDE: { - setProperty(); - break; - } - case AddedTargetOptions::FIELD: { - setProperty(); - break; - } - default: break; - } -} + moveFilter.applySelectionTags(); + if (moveFilter.hasNoneSelected()) return; -void MoveDexDataSetup::setAccuracy(types::baseAccuracy accuracy) { - handle.emplace(accuracy); -} + bool forAllDamageRolls = damageRollKind & DamageRollKind::ALL_DAMAGE_ROLLS; + bool forRequiredDamageRolls = simulation.simulateTurnOptions.getMakeBranchesOnRandomEvents() || forAllDamageRolls; + auto applyChoices = [](Simulation& sim) { sim.view(); }; + auto updateProbabilities = forAllDamageRolls ? updateAllDamageRollProbabilities : updatePartialProbabilities; -void MoveDexDataSetup::setBasePower(types::basePower basePower) { - handle.emplace(basePower); -} + runRandomEventCount( + simulation, + countUniqueDamageRolls, + applyChoices, + updateProbabilities, + forRequiredDamageRolls); -void MoveDexDataSetup::setCategoryPhysical() { - POKESIM_REQUIRE(!(handle.any_of()), "A move can only have one category."); - setProperty(); + simulation.removeFromEntities(); + moveFilter.clearSelectionTags(); } -void MoveDexDataSetup::setCategorySpecial() { - POKESIM_REQUIRE(!(handle.any_of()), "A move can only have one category."); - setProperty(); +void setIfMoveCrits(Simulation& simulation) { + runReciprocalRandomBinaryChance(simulation, [](Simulation& sim) { + sim.addToEntities(); + }); } +} // namespace pokesim::simulate_turn + +/////////////// END OF src/SimulateTurn/CalcDamageSpecifics.cpp //////////////// + +////////////// START OF src/Pokedex/Setup/SpeciesDexDataSetup.cpp ////////////// -void MoveDexDataSetup::setCategoryStatus() { - POKESIM_REQUIRE(!(handle.any_of()), "A move can only have one category."); - setProperty(); +namespace pokesim::dex::internal { +void SpeciesDexDataSetup::setName(Species species) { + handle.emplace(species); } -void MoveDexDataSetup::setBasePp(types::pp pp) { - handle.emplace(pp); +void SpeciesDexDataSetup::setType(Type type1, Type type2) { + handle.emplace(type1, type2); } -void MoveDexDataSetup::setPriority(types::priority priority) { - handle.emplace(priority); +void SpeciesDexDataSetup::setBaseStats( + types::baseStat hp, types::baseStat atk, types::baseStat def, types::baseStat spa, types::baseStat spd, + types::baseStat spe) { + handle.emplace(hp, atk, def, spa, spd, spe); } -void MoveDexDataSetup::setHitCount(types::moveHits hitCount) { - handle.emplace(hitCount); +void SpeciesDexDataSetup::setPrimaryAbility(Ability ability) { + handle.emplace(ability); } -void MoveDexDataSetup::setEffectTargetsMoveSource() { - POKESIM_REQUIRE( - !handle.all_of(), - "Moves effects can only affect the target or source, not both."); - handle.emplace(); +void SpeciesDexDataSetup::setSecondaryAbility(Ability ability) { + handle.emplace(ability); } -void MoveDexDataSetup::setEffectTargetsMoveTarget() { - POKESIM_REQUIRE( - !handle.all_of(), - "Moves effects can only affect the source or target, not both."); - handle.emplace(); +void SpeciesDexDataSetup::setHiddenAbility(Ability ability) { + handle.emplace(ability); } } // namespace pokesim::dex::internal -//////////////// END OF src/Pokedex/Setup/MoveDexDataSetup.cpp ///////////////// +/////////////// END OF src/Pokedex/Setup/SpeciesDexDataSetup.cpp /////////////// /////////////// START OF src/Pokedex/Setup/ItemDexDataSetup.cpp //////////////// @@ -3732,7 +3755,7 @@ types::entity Pokedex::buildSpecies(dex::Species species, types::registry& regis namespace pokesim { namespace { -template +template struct BuildMove { private: enum class Optional : std::uint8_t { @@ -3792,226 +3815,283 @@ struct BuildMove { template struct has> : std::true_type {}; - template - static void setEffect( - dex::internal::MoveDexDataSetup& move, GameMechanics gameMechanic, 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...); + struct EntitySetup { + using EntityList = entt::view>; + types::registry* registry; + const EntityList* list; + std::size_t size; + + EntitySetup(types::registry& registry_, const EntityList& list_) + : registry(®istry_), list(&list_), size(list->size_hint()) {} + + template + void add(const Component& component) { + registry->storage().reserve(size); + registry->insert(list->begin(), list->end(), component); } - else { - types::percentChance chance = Constants::MoveBaseEffectChance::DEFAULT; - if constexpr (has::value) { - chance = EffectData::chance(gameMechanic); - } - move.setSecondaryEffect(chance, effectValues...); + + template + void setProperties(Tags) { + (add(Types{}), ...); } - } - template - static void setEffectTags(dex::internal::MoveDexDataSetup& move, GameMechanics gameMechanic, Tags) { + template + bool any_of() { + return std::any_of(list->begin(), list->end(), [&](types::entity entity) { + return registry->template any_of(entity); + }); + } + }; + + template + static void buildEffect(EntitySetup& setup, GameMechanics gameMechanic) { 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(), ...); + + POKESIM_REQUIRE( + !setup.template any_of(), + "Moves can only have primary or secondary effects, not both."); + + setup.add(move::effect::tags::Primary{}); } else { + POKESIM_REQUIRE( + !setup.template any_of(), + "Moves can only have secondary or primary effects, not both."); + types::percentChance chance = Constants::MoveBaseEffectChance::DEFAULT; if constexpr (has::value) { chance = EffectData::chance(gameMechanic); } - (move.setSecondaryEffect(chance), ...); + setup.add(move::effect::tags::Secondary{}); + setup.add(BaseEffectChance{chance}); } - } - template - static void buildEffect(dex::internal::MoveDexDataSetup& move, GameMechanics gameMechanic) { if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::atkBoost(gameMechanic)); + setup.add(AtkBoost{EffectData::atkBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::defBoost(gameMechanic)); + setup.add(DefBoost{EffectData::defBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::spaBoost(gameMechanic)); + setup.add(SpaBoost{EffectData::spaBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::spdBoost(gameMechanic)); + setup.add(SpdBoost{EffectData::spdBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::speBoost(gameMechanic)); + setup.add(SpeBoost{EffectData::speBoost(gameMechanic)}); } - setEffectTags(move, gameMechanic, EffectData::effectTags); + setup.setProperties(EffectData::effectTags); } public: - static types::entity build(types::registry& registry, GameMechanics gameMechanic, bool forActiveMove) { - dex::internal::MoveDexDataSetup move(registry); - - if (forActiveMove) { - move.setNameTag(T::name(gameMechanic)); + static void build(types::registry& registry, GameMechanics gameMechanic) { + auto list = registry.view(); + if (list.begin() == list.end()) { + return; } - else { - move.setName(T::name(gameMechanic)); - move.setBasePp(T::basePp(gameMechanic)); + + EntitySetup setup{registry, list}; + + if (std::is_same_v) { + setup.add(MoveName{Move::name(gameMechanic)}); + setup.add(Pp{Move::basePp(gameMechanic)}); } - move.setType(T::type(gameMechanic)); - switch (T::category(gameMechanic)) { + setup.add(TypeName{Move::type(gameMechanic)}); + switch (Move::category(gameMechanic)) { case dex::MoveCategory::PHYSICAL: { - move.setCategoryPhysical(); + POKESIM_REQUIRE( + !(setup.template any_of()), + "A move can only have one category."); + + setup.add(move::tags::Physical{}); break; } case dex::MoveCategory::SPECIAL: { - move.setCategorySpecial(); + POKESIM_REQUIRE( + !(setup.template any_of()), + "A move can only have one category."); + + setup.add(move::tags::Special{}); break; } case dex::MoveCategory::STATUS: { - move.setCategoryStatus(); + POKESIM_REQUIRE( + !(setup.template any_of()), + "A move can only have one category."); + setup.add(move::tags::Status{}); break; } } - if constexpr (has::value) { - move.setAccuracy(T::accuracy(gameMechanic)); + if constexpr (has::value) { + setup.add(Accuracy{Move::accuracy(gameMechanic)}); } - if constexpr (has::value) { - move.setBasePower(T::basePower(gameMechanic)); + if constexpr (has::value) { + setup.add(BasePower{Move::basePower(gameMechanic)}); } - if constexpr (has::value) { - move.setHitCount(T::hitCount(gameMechanic)); + if constexpr (has::value) { + setup.add(HitCount{Move::hitCount(gameMechanic)}); } - if constexpr (has::value) { - buildEffect(move); - move.setEffectTargetsMoveSource(); + if constexpr (has::value) { + buildEffect(setup); + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + setup.add(move::effect::tags::MoveSource{}); } - if constexpr (has::value) { - buildEffect(move, gameMechanic); - move.setEffectTargetsMoveTarget(); + if constexpr (has::value) { + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + buildEffect(setup, gameMechanic); + setup.add(move::effect::tags::MoveTarget{}); } - if constexpr (has::value) { - buildEffect(move, gameMechanic); - move.setEffectTargetsMoveSource(); + if constexpr (has::value) { + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + buildEffect(setup, gameMechanic); + setup.add(move::effect::tags::MoveSource{}); } - if constexpr (has::value) { - buildEffect(move, gameMechanic); - move.setEffectTargetsMoveTarget(); + if constexpr (has::value) { + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + buildEffect(setup, gameMechanic); + setup.add(move::effect::tags::MoveTarget{}); } - move.setProperties(T::moveTags); + setup.setProperties(Move::moveTags); - switch (T::target(gameMechanic)) { + switch (Move::target(gameMechanic)) { case MoveTarget::ANY_SINGLE_TARGET: { - move.setProperty(); + setup.add(move::tags::AnySingleTarget{}); break; } case MoveTarget::ANY_SINGLE_FOE: { - move.setProperty(); + setup.add(move::tags::AnySingleFoe{}); break; } case MoveTarget::ANY_SINGLE_ALLY: { - move.setProperty(); + setup.add(move::tags::AnySingleAlly{}); break; } case MoveTarget::ALLY_OR_SELF: { - move.setProperty(); + setup.add(move::tags::AllyOrSelf{}); break; } case MoveTarget::SELF: { - move.setProperty(); + setup.add(move::tags::Self{}); break; } case MoveTarget::ALL_FOES: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::TARGET_ALLY); + setup.add(move::tags::AllFoes{}); + setup.add(move::added_targets::tags::TargetAlly{}); break; } case MoveTarget::ALLIES_AND_FOES: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::TARGET_ALLY); - move.addAddedTargets(AddedTargetOptions::USER_ALLY); + setup.add(move::tags::AlliesAndFoes{}); + setup.add(move::added_targets::tags::TargetAlly{}); + setup.add(move::added_targets::tags::UserAlly{}); break; } case MoveTarget::ALLIES_AND_SELF: { - move.setProperty(); + setup.add(move::tags::AlliesAndSelf{}); // Deliberately not USER_ALLY as the target of AlliesAndSelf moves is the user - move.addAddedTargets(AddedTargetOptions::TARGET_ALLY); + setup.add(move::added_targets::tags::TargetAlly{}); break; } case MoveTarget::FOE_SIDE: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::TARGET_SIDE); + setup.add(move::tags::FoeSide{}); + setup.add(move::added_targets::tags::TargetSide{}); break; } case MoveTarget::ALLY_SIDE: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::USER_SIDE); + setup.add(move::tags::AllySide{}); + setup.add(move::added_targets::tags::UserSide{}); break; } case MoveTarget::FIELD: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::FIELD); + setup.add(move::tags::Field{}); + setup.add(move::added_targets::tags::Field{}); break; } case MoveTarget::ALLY_TEAM: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::USER_SIDE); + setup.add(move::tags::AllyTeam{}); + setup.add(move::added_targets::tags::UserSide{}); break; } case MoveTarget::RETALIATION: { - move.setProperty(); + setup.add(move::tags::Retaliation{}); break; } case MoveTarget::RANDOM_FOE: { - move.setProperty(); + setup.add(move::tags::RandomFoe{}); break; } default: break; } - return move.entity(); + if (std::is_same_v) { + registry.remove(list.begin(), list.end()); + } } }; -types::entity buildByGameMechanic( - dex::Move move, types::registry& registry, bool forActiveMove, GameMechanics gameMechanic) { - // Tidy check ignored because "using namespace" is in function - using namespace pokesim::dex; // NOLINT(google-build-using-namespace) - switch (move) { - case Move::FURY_ATTACK: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::KNOCK_OFF: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::MOONBLAST: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::QUIVER_DANCE: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::SPLASH: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::THUNDERBOLT: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::WILL_O_WISP: return BuildMove::build(registry, gameMechanic, forActiveMove); - default: break; +template +struct BuildMoves { + private: + template + static void buildActionMoveFromView(types::registry& registry, GameMechanics gameMechanics) { + BuildMove::build(registry, gameMechanics); } - POKESIM_REQUIRE_FAIL("Building a move that is not yet supported."); - return types::entity{}; -} + static void buildActionMoveByGameMechanic(types::registry& registry, GameMechanics gameMechanic) { + using namespace pokesim::dex; // NOLINT(google-build-using-namespace) + + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + } + + public: + static void build(const Pokedex& pokedex, types::registry& registry) { + if (pokedex.isGameMechanic(GameMechanics::SCARLET_VIOLET)) { + buildActionMoveByGameMechanic(registry, GameMechanics::SCARLET_VIOLET); + return; + } + + POKESIM_REQUIRE_FAIL("Building for a game that is not yet supported."); + } +}; } // namespace -types::entity Pokedex::buildMove(dex::Move move, types::registry& registry, bool forActiveMove) const { - if (isGameMechanic(GameMechanics::SCARLET_VIOLET)) { - return buildByGameMechanic(move, registry, forActiveMove, GameMechanics::SCARLET_VIOLET); +void Pokedex::buildMoves(types::registry& registry) const { + if (!registry.view().empty()) { + BuildMoves::build(*this, registry); } - POKESIM_REQUIRE_FAIL("Building for a game that is not yet supported."); - return types::entity{}; + if (!registry.view().empty()) { + BuildMoves::build(*this, registry); + } } } // namespace pokesim @@ -4155,16 +4235,24 @@ void Pokedex::loadItems(const entt::dense_set& itemSet) { load(itemsMap, itemSet, [this](dex::Item item) { return buildItem(item, dexRegistry); }); } -void Pokedex::loadMoves(const entt::dense_set& moveSet) { - load(movesMap, moveSet, [this](dex::Move move) { return buildMove(move, dexRegistry, false); }); -} - void Pokedex::loadAbilities(const entt::dense_set& abilitySet) { load(abilitiesMap, abilitySet, [this](dex::Ability ability) { return buildAbility(ability, dexRegistry); }); } -types::entity Pokedex::buildActionMove(dex::Move move, types::registry& registry) const { - return buildMove(move, registry, true); +void Pokedex::loadMoves(const entt::dense_set& moveSet) { + for (dex::Move move : moveSet) { + if (movesMap.contains(move)) { + continue; + } + + types::entity moveEntity = dexRegistry.create(); + movesMap[move] = moveEntity; + move::tags::emplaceTagFromEnum(move, {dexRegistry, moveEntity}); + dexRegistry.emplace(moveEntity); + } + + buildMoves(dexRegistry); + dexRegistry.clear(); } void Pokedex::loadForBattleInfo(const std::vector& battleInfoList) { @@ -4188,6 +4276,14 @@ void Pokedex::loadForBattleInfo(const std::vector& battleInf } } } + + for (const auto& damageCalculation : battleCreationInfo.damageCalculations) { + moveSet.insert(damageCalculation.moves.begin(), damageCalculation.moves.end()); + } + + for (const auto& effectToAnalyze : battleCreationInfo.effectsToAnalyze) { + moveSet.insert(effectToAnalyze.moves.begin(), effectToAnalyze.moves.end()); + } } loadSpecies(speciesSet); @@ -4246,8 +4342,6 @@ void KnockOff::onAfterHit(Simulation& simulation) { ////////////////// START OF src/Pokedex/Events/ItemEvents.cpp ////////////////// -#include - namespace pokesim::dex { namespace { void setChoiceLock(types::handle pokemonHandle, Battle battle) { @@ -4455,16 +4549,12 @@ void choiceLockRemoveWithItem( } void choiceLockOnDisableMove( - types::registry& registry, const pokesim::ChoiceLock& choiceLocked, const MoveSlots& moveSlots) { - POKESIM_REQUIRE( - std::find(moveSlots.val.begin(), moveSlots.val.end(), choiceLocked.val) != moveSlots.val.end(), - "Should skip if the move is no longer present, but when does that happen?"); - - for (types::entity entity : moveSlots.val) { - if (entity != choiceLocked.val) { - registry.emplace(entity); - } + types::handle handle, const pokesim::ChoiceLock& choiceLocked, const MoveSlots& moveSlots) { + if (!handle.all_of()) { + handle.emplace(types::moveSlots(moveSlots.val.size(), false)); } + + handle.get().val[choiceLocked.val] = true; } } // namespace @@ -4593,23 +4683,19 @@ void Static::onDamagingHit(Simulation& simulation) { //////////// START OF src/CalcDamage/Setup/CalcDamageInputSetup.cpp //////////// namespace pokesim::calc_damage { -InputSetup::InputSetup(types::registry& _registry) : registry(&_registry) {} +InputSetup::InputSetup(types::registry& registry, types::entity moveEntity) : handle(registry, moveEntity) {} void InputSetup::setup( - types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move, - const Pokedex& pokedex) { - moveEntity = createActionMoveForTarget({*registry, targetEntity}, battleEntity, sourceEntity, move, pokedex); - types::handle handle{*registry, moveEntity}; + types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move) { + types::registry& registry = *handle.registry(); + + setupActionMoveBuild(registry, battleEntity, sourceEntity, targetEntity, entity(), move); handle.emplace(move); handle.emplace(); - registry->emplace_or_replace(sourceEntity); - registry->emplace_or_replace(targetEntity); -} - -types::entity InputSetup::entity() const { - POKESIM_REQUIRE(moveEntity != entt::null, "Getting move entity before proper setup."); - return moveEntity; + handle.emplace(); + registry.emplace_or_replace(sourceEntity); + registry.emplace_or_replace(targetEntity); } } // namespace pokesim::calc_damage @@ -5025,7 +5111,7 @@ void setUnboostedStat(Simulation& simulation) { updateSpd(simulation, true); } else { - static_assert("No other stat is used as the attacking or defending stat."); + POKESIM_REQUIRE_FAIL("No other stat is used as the attacking or defending stat."); } if constexpr (forAttacker) { @@ -5140,10 +5226,8 @@ types::teamPositionIndex foeSidePokemonLeft(const types::registry& registry, typ ///////////////// START OF src/Battle/Setup/SideStateSetup.cpp ///////////////// namespace pokesim { -SideStateSetup::SideStateSetup(types::registry& registry, types::entity entity, PlayerSideId playerSideId) - : StateSetupBase(registry, entity) { +SideStateSetup::SideStateSetup(types::registry& registry, types::entity entity) : StateSetupBase(registry, entity) { handle.emplace(); - handle.emplace(playerSideId); } void SideStateSetup::initBlank() { @@ -5176,6 +5260,10 @@ void SideStateSetup::setBattle(types::entity entity) { void SideStateSetup::setPlayerSide(PlayerSideId playerSideId) { handle.emplace(playerSideId); } + +void SideStateSetup::setSideDecision(const SideDecision& sideDecision) { + handle.emplace(sideDecision); +} } // namespace pokesim ////////////////// END OF src/Battle/Setup/SideStateSetup.cpp ////////////////// @@ -5243,13 +5331,14 @@ void PokemonStateSetup::setItem(dex::Item item) { item::tags::emplaceTagFromEnum(item, handle); } -void PokemonStateSetup::setMoves(const std::vector& moveSlots) { - MoveSlots& moveEntities = handle.emplace(); +void PokemonStateSetup::setMoves(const std::vector& moveSlots) { + MoveSlots& newMoveSlots = handle.emplace(); POKESIM_REQUIRE( - moveSlots.size() <= moveEntities.val.max_size(), + moveSlots.size() <= newMoveSlots.val.max_size(), "Cannot add more moves to a Pokemon than MAX_MOVE_SLOTS."); - for (types::entity moveSlot : moveSlots) { - moveEntities.val.push_back(moveSlot); + + for (MoveSlot moveSlot : moveSlots) { + newMoveSlots.val.push_back(moveSlot); } } @@ -5288,30 +5377,6 @@ void PokemonStateSetup::setIVs(const Ivs& ivs) { //////////////// END OF src/Battle/Setup/PokemonStateSetup.cpp ///////////////// -///////////////// START OF src/Battle/Setup/MoveStateSetup.cpp ///////////////// - -namespace pokesim { -void MoveStateSetup::initBlank() { - handle.emplace(); - handle.emplace(); - handle.emplace(); -} - -void MoveStateSetup::setName(dex::Move moveName) { - handle.emplace(moveName); -} - -void MoveStateSetup::setPP(types::pp pp) { - handle.emplace(pp); -} - -void MoveStateSetup::setMaxPP(types::pp maxPp) { - handle.emplace(maxPp); -} -} // namespace pokesim - -////////////////// END OF src/Battle/Setup/MoveStateSetup.cpp ////////////////// - /////////////// START OF src/Battle/Setup/EmplaceTagFromEnum.cpp /////////////// namespace pokesim { @@ -5369,12 +5434,33 @@ void BattleStateSetup::initBlank() { setProbability(Constants::Probability::DEFAULT); } +void BattleStateSetup::setRecycledAction(types::entity recycledAction, types::entity recycledActionMove) { + types::registry& registry = *handle.registry(); + + handle.emplace(recycledAction); + registry.emplace(recycledAction); + + handle.emplace(recycledActionMove); + registry.emplace(recycledActionMove); +} + +void BattleStateSetup::setAddedRecycledActionMoves( + types::entity addedRecycledActionMove1, types::entity addedRecycledActionMove2) { + types::registry& registry = *handle.registry(); + + handle.emplace(addedRecycledActionMove1); + registry.emplace(addedRecycledActionMove1); + + handle.emplace(addedRecycledActionMove2); + registry.emplace(addedRecycledActionMove2); +} + void BattleStateSetup::setAutoID() { setID((types::stateId)handle.registry()->view().size()); } void BattleStateSetup::setID(types::stateId id) { - handle.emplace_or_replace(id); + handle.emplace(id); } void BattleStateSetup::setSide(types::entity sideEntity) { @@ -5400,7 +5486,7 @@ void BattleStateSetup::setRNGSeed(std::optional seed) { handle.emplace(seed.value()); } -void BattleStateSetup::setActionQueue(const std::vector& queue) { +void BattleStateSetup::setActionQueue(const std::vector& queue) { handle.emplace(queue); } @@ -5408,32 +5494,6 @@ void BattleStateSetup::setTurn(types::battleTurn turn) { handle.emplace(turn); } -void BattleStateSetup::setCurrentActionTarget(types::targets actionTargets) { - handle.emplace(actionTargets); - for (types::entity entity : actionTargets) { - handle.registry()->emplace(entity); - } -} - -void BattleStateSetup::setCurrentActionSource(types::entity actionSource) { - handle.emplace(actionSource); - handle.registry()->emplace(actionSource); -} - -void BattleStateSetup::setCurrentActionMove(types::entity actionMove) { - handle.registry()->emplace(actionMove); - const auto* source = handle.try_get(); - const auto* targets = handle.try_get(); - if (source) { - handle.registry()->emplace(source->val).val.push_back(actionMove); - } - if (targets) { - for (types::entity target : targets->val) { - handle.registry()->emplace(target).val.push_back(actionMove); - } - } -} - void BattleStateSetup::setProbability(types::probability probability) { handle.emplace(probability); } @@ -5640,9 +5700,13 @@ void clearVolatiles(types::handle pokemonHandle) { pokemonHandle.remove(); } -void deductPp(Pp& pp) { - if (pp.val) { - pp.val -= 1U; // TODO(aed3): Make this into a mechanic constant +void deductPp(MoveSlots& moveSlots, LastUsedMove lastUsedMove) { + MoveSlot& moveSlot = moveSlots.val[lastUsedMove.val]; + if (moveSlot.pp >= Constants::PP_USE_DEDUCTION + Constants::MovePp::MIN) { + moveSlot.pp -= Constants::PP_USE_DEDUCTION; + } + else { + moveSlot.pp = Constants::MovePp::MIN; } } @@ -5834,6 +5898,23 @@ void updateCurrentActionTargets(types::registry& registry, CurrentActionTargets& targets.val.pop_count(deleteCount); } + +template +void clearActionMoveComponents(types::registry& registry, const View& view) { + registry.remove< + tags::SimulateTurn, + tags::CalculateDamage, + tags::AnalyzeEffect, + Battle, + TypeName, + AtkBoost, + DefBoost, + SpaBoost, + SpdBoost, + SpeBoost, + status::tags::Paralysis, + status::tags::Burn>(view.begin(), view.end()); +} } // namespace void assignRootBattle(types::handle battleHandle) { @@ -5857,8 +5938,7 @@ void setCurrentActionSource(types::handle battleHandle, const Sides& sides, Curr } void setCurrentActionTarget( - types::handle battleHandle, const Sides& sides, CurrentAction action, CurrentActionSource source, - const Simulation& simulation) { + types::handle battleHandle, const Sides& sides, CurrentAction action, const Simulation& simulation) { types::registry& registry = *battleHandle.registry(); const TargetSlotName& targetSlotName = registry.get(action.val); types::entity targetEntity = slotToPokemonEntity(registry, sides, targetSlotName.val); @@ -5873,8 +5953,6 @@ void setCurrentActionTarget( if (pickedTarget) { battleHandle.emplace(types::targets{targetEntity}); - registry.emplace(targetEntity); - registry.emplace(targetEntity, source); } else { types::entity sourceEntity = battleHandle.get().val; @@ -5883,22 +5961,6 @@ void setCurrentActionTarget( } } -void setCurrentActionMove( - types::handle battleHandle, CurrentActionSource source, const CurrentActionTargets& targets, CurrentAction action, - const Pokedex& pokedex) { - types::registry& registry = *battleHandle.registry(); - const MoveName& move = registry.get(action.val); - const MoveSlots& moveSlots = registry.get(source.val); - - for (types::entity target : targets.val) { - createActionMoveForTarget({registry, target}, battleHandle.entity(), source.val, move.val, pokedex); - } - - types::entity moveSlotEntity = moveToEntity(registry, moveSlots, move.val); - battleHandle.emplace(moveSlotEntity); - registry.emplace(moveSlotEntity); -} - void setFailedActionMove( types::handle moveHandle, Battle battle, CurrentActionSource source, CurrentActionTarget target) { types::registry& registry = *moveHandle.registry(); @@ -5920,44 +5982,62 @@ void setFailedActionMove( registry.emplace(battle.val, target.val); } - types::entity moveSlotEntity = registry.get(battle.val).val; registry.erase(battle.val); - registry.erase(moveSlotEntity); updateCurrentActionTargets(registry, registry.get(battle.val)); } void clearCurrentAction(Simulation& simulation) { types::registry& registry = simulation.registry; - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); + registry.clear< + CurrentAction, + CurrentActionTargets, + CurrentActionSource, + CurrentActionTarget, + FailedCurrentActionSource, + FailedCurrentActionTarget, + CurrentActionMovesAsSource, + CurrentActionMovesAsTarget, + CurrentActionMoveSlot, + tags::CurrentActionMoveSource, + tags::CurrentActionMoveTarget, + tags::CurrentActionMoveSlot, + tags::CurrentMoveHit, + tags::FailedCurrentMoveHit>(); - auto actionMoves = registry.view(); - auto failedActionMoves = registry.view(); - auto currentActions = registry.view(); - registry.destroy(actionMoves.begin(), actionMoves.end()); - registry.destroy(failedActionMoves.begin(), failedActionMoves.end()); - registry.destroy(currentActions.begin(), currentActions.end()); + registry.clear< + move::effect::tags::Primary, + move::effect::tags::Secondary, + move::effect::tags::MoveSource, + move::effect::tags::MoveTarget, + move::tags::FuryAttack, + move::tags::KnockOff, + move::tags::Moonblast, + move::tags::QuiverDance, + move::tags::Splash, + move::tags::Thunderbolt, + move::tags::WillOWisp, + move::tags::Physical, + move::tags::Special, + move::tags::Status, + move::tags::Contact, + move::tags::BypassSubstitute, + move::tags::Punch, + move::tags::VariableHitCount, + BaseEffectChance, + Accuracy, + BasePower, + HitCount, + move::tags::AccuracyDependentHitCount, + move::tags::Self, + move::tags::AnySingleTarget, + move::tags::AnySingleAlly>(); + + registry.clear(); - auto battles = simulation.battleEntities(); - registry.remove< + registry.clear< action::tags::Item, - ItemName, action::tags::Move, - MoveName, action::tags::BeforeTurn, action::tags::Dynamax, action::tags::MegaEvolve, @@ -5970,7 +6050,19 @@ void clearCurrentAction(Simulation& simulation) { action::tags::RevivalBlessing, action::tags::Switch, action::tags::SwitchOut, - action::tags::Terastallize>(battles.begin(), battles.end()); + action::tags::Terastallize>(); + + auto actionMoves = registry.view(); + auto failedActionMoves = registry.view(); + clearActionMoveComponents(registry, actionMoves); + clearActionMoveComponents(registry, failedActionMoves); + registry.clear(); + + simulation.removeFromEntities(); + registry.clear(); + + auto battles = registry.view(); + registry.remove(battles.begin(), battles.end()); } } // namespace pokesim @@ -6048,42 +6140,31 @@ types::entity slotToAllyPokemonEntity(const types::registry& registry, const Sid return allyEntity; } -types::entity moveToEntity(const types::registry& registry, const MoveSlots& moveSlots, dex::Move move) { - for (types::entity moveSlot : moveSlots.val) { - if (registry.get(moveSlot).val == move) { - return moveSlot; +types::moveSlotIndex moveToMoveSlot(const MoveSlots& moveSlots, dex::Move move) { + for (types::moveSlotIndex i = 0; i < moveSlots.val.size(); i++) { + if (moveSlots.val[i].move == move) { + return i; } } - POKESIM_REQUIRE_FAIL("No move of entity found."); - return entt::null; + POKESIM_REQUIRE_FAIL("No move found."); + return 0U; } -types::entity createActionMoveForTarget( - types::handle targetHandle, types::entity battleEntity, types::entity sourceEntity, dex::Move move, - const Pokedex& pokedex) { - types::registry& registry = *targetHandle.registry(); - types::entity moveEntity = pokedex.buildActionMove(move, registry); - - registry.emplace(moveEntity); - registry.emplace(moveEntity, battleEntity); - registry.emplace(moveEntity, sourceEntity); - registry.emplace(moveEntity, targetHandle.entity()); +void setupActionMoveBuild( + types::registry& registry, types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, + types::entity actionMoveEntity, dex::Move move) { + types::handle actionMoveHandle{registry, actionMoveEntity}; - targetHandle.get_or_emplace().val.push_back(moveEntity); - registry.get_or_emplace(sourceEntity).val.push_back(moveEntity); - - if (registry.all_of(battleEntity)) { - registry.emplace(moveEntity); - } - if (registry.all_of(battleEntity)) { - registry.emplace(moveEntity); - } - if (registry.all_of(battleEntity)) { - registry.emplace(moveEntity); - } + move::tags::emplaceTagFromEnum(move, actionMoveHandle); + actionMoveHandle.emplace(battleEntity); + actionMoveHandle.emplace(sourceEntity); + actionMoveHandle.emplace(targetEntity); + actionMoveHandle.emplace(); + actionMoveHandle.emplace(); - return moveEntity; + registry.get_or_emplace(targetEntity).val.push_back(actionMoveEntity); + registry.get_or_emplace(sourceEntity).val.push_back(actionMoveEntity); } } // namespace pokesim @@ -6114,20 +6195,26 @@ void traverseBattle(types::registry& registry, VisitEntity visitEntity = nullptr const static bool ForCloning = !std::is_same_v; using Tag = std::conditional_t; - for (const auto [entity, sides, actionQueue] : registry.view().each()) { + for (const auto [entity, sides] : registry.view().each()) { for (auto side : sides.val) { registry.emplace(side); } - for (auto queueItem : actionQueue.val) { - registry.emplace(queueItem); - } if constexpr (ForCloning) { visitEntity(entity); } } - for (const auto [entity, currentAction] : registry.view().each()) { - registry.emplace(currentAction.val); + + for (const auto [entity, recycledAction, recycledActionMove] : + registry.view().each()) { + registry.emplace(recycledAction.val); + registry.emplace(recycledActionMove.val); + } + + for (const auto [entity, addedRecycledActionMove1, addedRecycledActionMove2] : + registry.view().each()) { + registry.emplace(addedRecycledActionMove1.val); + registry.emplace(addedRecycledActionMove2.val); } } @@ -6153,19 +6240,19 @@ void traverseAction(types::registry& registry, VisitEntity visitEntity = nullptr using Tag = std::conditional_t; if constexpr (ForCloning) { - for (types::entity entity : registry.view()) { + for (types::entity entity : registry.view()) { visitEntity(entity); } - } -} -template -void traverseCurrentActionMove(types::registry& registry, VisitEntity visitEntity = nullptr) { - const static bool ForCloning = !std::is_same_v; - using Tag = std::conditional_t; + for (types::entity entity : registry.view()) { + visitEntity(entity); + } - if constexpr (ForCloning) { - for (types::entity entity : registry.view()) { + for (types::entity entity : registry.view()) { + visitEntity(entity); + } + + for (types::entity entity : registry.view()) { visitEntity(entity); } } @@ -6176,12 +6263,8 @@ void traversePokemon(types::registry& registry, VisitEntity visitEntity = nullpt const static bool ForCloning = !std::is_same_v; using Tag = std::conditional_t; - for (const auto [entity, moveSlots] : registry.view().each()) { - for (auto move : moveSlots.val) { - registry.emplace(move); - } - - if constexpr (ForCloning) { + if constexpr (ForCloning) { + for (types::entity entity : registry.view()) { visitEntity(entity); } } @@ -6191,6 +6274,7 @@ void traversePokemon(types::registry& registry, VisitEntity visitEntity = nullpt registry.emplace_or_replace(move); } } + for (const auto [entity, moves] : registry.view().each()) { for (types::entity move : moves.val) { registry.emplace_or_replace(move); @@ -6198,18 +6282,6 @@ void traversePokemon(types::registry& registry, VisitEntity visitEntity = nullpt } } -template -void traverseMove(types::registry& registry, VisitEntity visitEntity = nullptr) { - const static bool ForCloning = !std::is_same_v; - using Tag = std::conditional_t; - - if constexpr (ForCloning) { - for (types::entity entity : registry.view()) { - visitEntity(entity); - } - } -} - void cloneBattle( types::registry& registry, types::ClonedEntityMap& entityMap, entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { @@ -6234,14 +6306,6 @@ void cloneAction( }); } -void cloneCurrentActionMove( - types::registry& registry, types::ClonedEntityMap& entityMap, - entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { - traverseCurrentActionMove(registry, [&](types::entity entity) { - cloneEntity(entity, registry, entityMap, srcEntityStorages, cloneCount); - }); -} - void clonePokemon( types::registry& registry, types::ClonedEntityMap& entityMap, entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { @@ -6250,14 +6314,6 @@ void clonePokemon( }); } -void cloneMove( - types::registry& registry, types::ClonedEntityMap& entityMap, - entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { - traverseMove(registry, [&](types::entity entity) { - cloneEntity(entity, registry, entityMap, srcEntityStorages, cloneCount); - }); -} - void deleteBattle(types::registry& registry) { traverseBattle(registry); } @@ -6270,18 +6326,10 @@ void deleteAction(types::registry& registry) { traverseAction(registry); } -void deleteCurrentActionMove(types::registry& registry) { - traverseCurrentActionMove(registry); -} - void deletePokemon(types::registry& registry) { traversePokemon(registry); } -void deleteMove(types::registry& registry) { - traverseMove(registry); -} - void remapEntity(types::entity& entity, const CloneTo& cloneTo, const types::ClonedEntityMap& entityMap) { POKESIM_REQUIRE(entityMap.contains(entity), "Source node was not loaded into the map."); POKESIM_REQUIRE( @@ -6331,8 +6379,6 @@ types::ClonedEntityMap clone(types::registry& registry, std::optional(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); @@ -6409,8 +6452,6 @@ void deleteClones(types::registry& registry) { deleteSide(registry); deleteAction(registry); deletePokemon(registry); - deleteCurrentActionMove(registry); - deleteMove(registry); auto remove = registry.view(); registry.destroy(remove.begin(), remove.end()); } @@ -6733,11 +6774,14 @@ void ignoreBattlesWithEffectActive(Simulation& simulation) { types::entity createAnalyzeEffectMove( types::registry& registry, dex::Move move, types::entity battleEntity, types::entity attackerEntity, - types::entity defenderEntity, const Pokedex& pokedex) { - types::entity moveEntity = - createActionMoveForTarget({registry, defenderEntity}, battleEntity, attackerEntity, move, pokedex); + types::entity defenderEntity) { + types::entity moveEntity = registry.create(); + + setupActionMoveBuild(registry, battleEntity, attackerEntity, defenderEntity, moveEntity, move); + registry.emplace(moveEntity, move); registry.emplace(moveEntity); + registry.emplace(moveEntity); registry.emplace_or_replace(attackerEntity); registry.emplace_or_replace(defenderEntity); @@ -6745,22 +6789,20 @@ types::entity createAnalyzeEffectMove( } void createOneCalculationMovePair( - types::handle inputHandle, Battle battle, EffectMove move, Attacker attacker, Defender defender, - const Pokedex& pokedex) { + types::handle inputHandle, Battle battle, EffectMove move, Attacker attacker, Defender defender) { entt::entity moveEntity = - createAnalyzeEffectMove(*inputHandle.registry(), move.val, battle.val, attacker.val, defender.val, pokedex); + createAnalyzeEffectMove(*inputHandle.registry(), move.val, battle.val, attacker.val, defender.val); inputHandle.emplace(moveEntity, moveEntity); } void createTwoCalculationsMovePair( types::handle inputHandle, Battle battle, EffectMove move, Attacker attacker, Defender defender, - const OriginalInputEntities& originals, const Pokedex& pokedex) { + const OriginalInputEntities& originals) { types::registry& registry = *inputHandle.registry(); entt::entity originalEntity = - createAnalyzeEffectMove(registry, move.val, originals.battle, originals.attacker, originals.defender, pokedex); - entt::entity copyEntity = - createAnalyzeEffectMove(registry, move.val, battle.val, attacker.val, defender.val, pokedex); + createAnalyzeEffectMove(registry, move.val, originals.battle, originals.attacker, originals.defender); + entt::entity copyEntity = createAnalyzeEffectMove(registry, move.val, battle.val, attacker.val, defender.val); // All active pokemon in should have their stats refreshed in doubles for moves like Beat Up which rely on the stats // of Pokemon outside of the attacker and defender @@ -6874,9 +6916,10 @@ void createAppliedEffectBattles(Simulation& simulation) { } } - simulation.view, entt::exclude_t>( - simulation.pokedex()); - simulation.view(simulation.pokedex()); + simulation.view, entt::exclude_t>(); + simulation.view(); + simulation.pokedex().buildMoves(simulation.registry); + simulation.registry.clear(); } void applyPseudoWeatherEffect(types::handle, Battle, PseudoWeatherName) {} diff --git a/extras/PokeSim.hpp b/extras/PokeSim.hpp index de47dd9..a453ca7 100644 --- a/extras/PokeSim.hpp +++ b/extras/PokeSim.hpp @@ -83,18 +83,23 @@ * src/CalcDamage/Helpers.hpp * src/Types/Random.hpp * src/Components/Accuracy.hpp + * src/Types/Enums/Item.hpp + * src/Types/Enums/Move.hpp + * src/Types/Enums/Slot.hpp + * src/Utilities/Variant.hpp + * src/Types/Decisions.hpp + * src/Types/Enums/ActionOrder.hpp + * src/Types/Move.hpp + * src/Components/ActionQueue.hpp * src/Types/Enums/AddedTargets.hpp * src/Components/AddedTargets.hpp * src/Components/EntityHolders/Current.hpp * src/Components/Tags/Current.hpp * src/Components/AnalyzeEffect/Aliases.hpp - * src/Types/Enums/Move.hpp * src/Components/AnalyzeEffect/AnalyzeEffectInputs.hpp - * src/Utilities/Variant.hpp * src/Types/Effect.hpp * src/Components/AnalyzeEffect/RemovedEffect.hpp * src/Components/BaseEffectChance.hpp - * src/Types/Move.hpp * src/Components/BasePower.hpp * src/Components/CalcDamage/Aliases.hpp * src/Types/Enums/GameMechanics.hpp @@ -104,26 +109,23 @@ * src/Components/CalcDamage/DamageRollSides.hpp * src/Components/CalcDamage/ModifyingEventRanTags.hpp * src/Components/CalcDamage/TemporaryMoveProperties.hpp + * src/Components/ChoiceLock.hpp * src/Components/CloneFromCloneTo.hpp - * src/Types/Enums/Item.hpp - * src/Types/Enums/PlayerSideId.hpp - * src/Types/Enums/Slot.hpp - * src/Components/Decisions.hpp + * src/Components/Current.hpp + * src/Components/DisabledMoveSlots.hpp * src/Components/EVsIVs.hpp - * src/Components/EntityHolders/ActionQueue.hpp - * src/Components/EntityHolders/ChoiceLock.hpp * src/Components/EntityHolders/FaintQueue.hpp * src/Components/EntityHolders/FoeSide.hpp - * src/Components/EntityHolders/LastUsedMove.hpp - * src/Components/EntityHolders/MoveSlots.hpp - * src/Components/EntityHolders/Pokemon.hpp + * src/Components/EntityHolders/RecycledEntities.hpp * src/Components/EntityHolders/Side.hpp * src/Components/EntityHolders/Sides.hpp * src/Components/EntityHolders/Team.hpp * src/Components/EventModifier.hpp * src/Components/HitCount.hpp * src/Components/ID.hpp + * src/Components/LastUsedMove.hpp * src/Components/Level.hpp + * src/Components/MoveSlots.hpp * src/Types/Enums/Ability.hpp * src/Components/Names/AbilityNames.hpp * src/Types/Enums/Gender.hpp @@ -138,16 +140,18 @@ * src/Components/Names/TargetSlotName.hpp * src/Types/Enums/Type.hpp * src/Components/Names/TypeNames.hpp - * src/Components/PP.hpp + * src/Types/Enums/PlayerSideId.hpp * src/Components/PlayerSide.hpp * src/Components/Pokedex/Abilities.hpp * src/Components/Pokedex/BaseStats.hpp + * src/Components/Pokedex/PP.hpp * src/Components/Position.hpp * src/Components/Priority.hpp * src/Components/Probability.hpp * src/Components/RNGSeed.hpp * src/Components/RandomEventInputs.hpp * src/Components/RandomEventOutputs.hpp + * src/Components/SideDecisions.hpp * src/Components/SimulateTurn/ActionTags.hpp * src/Components/SimulateTurn/MoveHitStepTags.hpp * src/Components/SimulateTurn/SimulateTurnTags.hpp @@ -155,8 +159,6 @@ * src/Components/SimulateTurn/TeamAction.hpp * src/Components/SimulationResults.hpp * src/Components/SpeciesTypes.hpp - * src/Types/Enums/ActionOrder.hpp - * src/Components/SpeedSort.hpp * src/Components/Stats.hpp * src/Components/Tags/AbilityTags.hpp * src/Components/Tags/BattleTags.hpp @@ -166,6 +168,7 @@ * src/Components/Tags/MoveTags.hpp * src/Components/Tags/NatureTags.hpp * src/Components/Tags/PokemonTags.hpp + * src/Components/Tags/RecycledEntities.hpp * src/Components/Tags/RunEventTags.hpp * src/Components/Tags/Selection.hpp * src/Components/Tags/SimulationTags.hpp @@ -189,7 +192,6 @@ * src/Battle/Setup/PokemonStateSetup.hpp * src/Battle/Setup/BattleStateSetup.hpp * src/Battle/Setup/EmplaceTagFromEnum.hpp - * src/Battle/Setup/MoveStateSetup.hpp * src/Battle/Setup/SideStateSetup.hpp * src/CalcDamage/Setup/CalcDamageInputSetup.hpp * src/Pokedex/TypeChart.hpp @@ -247,7 +249,6 @@ * src/SimulateTurn/CalcDamageSpecifics.hpp * src/Pokedex/Setup/DexDataSetup.hpp * src/Pokedex/Setup/SpeciesDexDataSetup.hpp - * src/Pokedex/Setup/MoveDexDataSetup.hpp * src/Pokedex/Setup/ItemDexDataSetup.hpp * src/Pokedex/Species/Ampharos.hpp * src/Pokedex/Species/Dragapult.hpp @@ -17182,6 +17183,8 @@ struct Constants { static constexpr std::uint8_t SIDE_COUNT = 2U; + static constexpr std::uint8_t PP_USE_DEDUCTION = 1U; + struct PokemonLevel { static constexpr std::uint8_t MAX = 100U; static constexpr std::uint8_t MIN = 1U; @@ -17405,6 +17408,12 @@ class fixedMemoryVector : private std::array { "A std::vector for this type and size would be smaller."); } + fixedMemoryVector(std::uint8_t size, const T& value) : fixedMemoryVector() { + for (std::uint8_t i = 0; i < size; i++) { + push_back(value); + } + } + fixedMemoryVector(std::initializer_list list) : fixedMemoryVector() { for (const T& item : list) { push_back(item); @@ -17490,6 +17499,7 @@ using stateId = std::underlying_type_t; using battleTurn = pokesim::internal::unsignedIntType; using sideIndex = pokesim::internal::unsignedIntType; + template struct sides : public std::array { constexpr T& p1() { return this->at(0); }; @@ -17499,6 +17509,7 @@ struct sides : public std::array { template decltype(auto) get() const { + static_assert(N < Constants::SIDE_COUNT); return this->at(N); } @@ -18254,6 +18265,275 @@ struct Accuracy { ////////////////////// END OF src/Components/Accuracy.hpp ////////////////////// +////////////////////// START OF src/Types/Enums/Item.hpp /////////////////////// + +namespace pokesim::dex { +// Name of items in Pokemon games +enum class Item : std::uint16_t { + // clang-format off + NO_ITEM = 0U, ABILITY_CAPSULE, ABILITY_PATCH, ABILITY_SHIELD, ABOMASITE, ABRA_CANDY, ABSOLITE, ABSORB_BULB, ACRO_BIKE, ADAMANT_CRYSTAL, ADAMANT_MINT, ADAMANT_ORB, ADRENALINE_ORB, ADVENTURE_GUIDE, AERODACTYLITE, AERODACTYL_CANDY, AGGRONITE, AGUAV_BERRY, AIR_BALLOON, AIR_MAIL, ALAKAZITE, ALORAICHIUM_Z, ALTARIANITE, AMAZE_MULCH, AMPHAROSITE, AMULET_COIN, ANTIDOTE, APICOT_BERRY, APRICORN, APRICORN_BOX, AQUA_SUIT, ARMOR_FOSSIL, ARMOR_PASS, ARMORITE_ORE, ARTICUNO_CANDY, ASPEAR_BERRY, ASSAULT_VEST, AUDINITE, AURORATICKET, AUSPICIOUS_ARMOR, AUTOGRAPH, AUX_EVASION, AUX_GUARD, AUX_POWER, AUX_POWERGUARD, AWAKENING, AZELFS_FANG, AZURE_FLUTE, BABIRI_BERRY, BALL_OF_MUD, BALM_MUSHROOM, BAND_AUTOGRAPH, BANETTITE, BASCULEGION_FOOD, BASEMENT_KEY, BEACH_GLASS, BEAD_MAIL, BEAN_CAKE, BEAST_BALL, BEEDRILLITE, BELLSPROUT_CANDY, BELUE_BERRY, BERRY, BERRY_JUICE, BERRY_POTS, BERRY_POUCH, BERRY_SWEET, BERSERK_GENE, BICYCLE, BIG_BAMBOO_SHOOT, BIG_MALASADA, BIG_MUSHROOM, BIG_NUGGET, BIG_PEARL, BIG_ROOT, BIKE_VOUCHER, BINDING_BAND, BITTER_BERRY, BLACK_APRICORN, BLACK_AUGURITE, BLACK_BELT, BLACK_FLUTE, BLACK_GLASSES, BLACK_MANE_HAIR, BLACK_SLUDGE, BLACK_TUMBLESTONE, BLANK_PLATE, BLASTOISINITE, BLAZIKENITE, BLOOM_MAIL, BLUE_APRICORN, BLUE_CARD, BLUE_FLUTE, BLUE_ORB, BLUE_PETAL, BLUE_SCARF, BLUE_SHARD, BLUESKY_MAIL, BLU_ID_BADGE, BLUK_BERRY, BLUNDER_POLICY, BOLD_MINT, BONSLY_CARD, BONSLY_PHOTO, BOOST_MULCH, BOOSTER_ENERGY, BOTTLE_CAP, BRAVE_MINT, BRICK_MAIL, BRICK_PIECE, BRIDGE_MAIL_D, BRIDGE_MAIL_M, BRIDGE_MAIL_S, BRIDGE_MAIL_T, BRIDGE_MAIL_V, BRIGHT_POWDER, BUBBLE_MAIL, BUG_GEM, BUG_MEMORY, BUG_TERA_SHARD, BUGINIUM_Z, BUGWORT, BULBASAUR_CANDY, BURN_DRIVE, BURN_HEAL, BURNT_BERRY, CAKE_LURE_BASE, CALCIUM, CALM_MINT, CAMERUPTITE, CAMPING_GEAR, CANDY_TRUFFLE, CARBOS, CARD_KEY, CAREFUL_MINT, CARROT_SEEDS, CASTELIACONE, CASTER_FERN, CATCHING_CHARM, CATERPIE_CANDY, CELESTICA_FLUTE, CELL_BATTERY, CHALKY_STONE, CHANSEY_CANDY, CHARCOAL, CHARIZARDITE_X, CHARIZARDITE_Y, CHARMANDER_CANDY, CHARTI_BERRY, CHERI_BERRY, CHERISH_BALL, CHESTO_BERRY, CHILAN_BERRY, CHILL_DRIVE, CHIPPED_POT, CHOICE_BAND, CHOICE_DUMPLING, CHOICE_SCARF, CHOICE_SPECS, CHOPLE_BERRY, CLAW_FOSSIL, CLEANSE_TAG, CLEAR_AMULET, CLEAR_BELL, CLEFAIRY_CANDY, CLEVER_FEATHER, CLOVER_SWEET, COBA_BERRY, COIN_CASE, COLBUR_BERRY, COLOGNE_CASE, COLRESS_MACHINE, COMET_SHARD, COMMON_STONE, CONTEST_COSTUME, CONTEST_PASS, CORNN_BERRY, COUPON_1, COUPON_2, COUPON_3, COURAGE_CANDY, COURAGE_CANDY_L, COURAGE_CANDY_XL, COVER_FOSSIL, COVERT_CLOAK, CRACKED_POT, CRAFTING_KIT, CROWN_PASS, CRUNCHY_SALT, CUBONE_CANDY, CRY_ANALYZER, CUSTAP_BERRY, DAMP_MULCH, DAMP_ROCK, DARK_GEM, DARK_MEMORY, DARK_STONE, DARK_TERA_SHARD, DARKINIUM_Z, DATA_CARDS, DATA_ROM, DAWN_STONE, DAZZLING_HONEY, D_DISK, DECIDIUM_Z, DEEP_SEA_SCALE, DEEP_SEA_TOOTH, DESTINY_KNOT, DEVON_PARTS, DEVON_SCOPE, DEVON_SCUBA_GEAR, DIANCITE, DIGGER_DRILL, DIGLETT_CANDY, DIRE_HIT, DIRESHROOM, DISC_CASE, DISCOUNT_COUPON, DISCOVERY_SLATE, DISTORTION_SLATE, DITTO_CANDY, DIVE_BALL, DNA_SAMPLE, DNA_SPLICERS, DODUO_CANDY, DOME_FOSSIL, DOPPEL_BONNETS, DOUSE_DRIVE, DOWN_ST_KEY, DOWSING_MACHINE, DRACO_PLATE, DRAGON_FANG, DRAGON_GEM, DRAGON_MEMORY, DRAGON_SCALE, DRAGON_SKULL, DRAGON_TERA_SHARD, DRAGONIUM_Z, DRASH_BERRY, DRATINI_CANDY, DREAD_PLATE, DREAM_BALL, DREAM_MAIL, DROPPED_ITEM, DROWZEE_CANDY, DS_SOUNDS, DUBIOUS_DISC, DURIN_BERRY, DUSK_BALL, DUSK_STONE, DYNAMAX_BAND, DYNAMAX_CANDY, DYNAMAX_CRYSTALS, DYNITE_ORE, EARTH_PLATE, EEVEE_CANDY, EEVIUM_Z, EGG_TICKET, EGGANT_BERRY, EIN_FILE_C, EIN_FILE_F, EIN_FILE_H, EIN_FILE_P, EIN_FILE_S, EJECT_BUTTON, EJECT_PACK, EKANS_CANDY, ELECTABUZZ_CANDY, ELECTIRIZER, ELECTRIC_GEM, ELECTRIC_MEMORY, ELECTRIC_SEED, ELECTRIC_TERA_SHARD, ELECTRIUM_Z, ELEVATOR_KEY, ELIXIR, ENDORSEMENT, ENERGY_POWDER, ENERGY_ROOT, ENIGMA_BERRY, ENIGMA_STONE, ENIGMATIC_CARD, EON_FLUTE, EON_MAIL, EON_TICKET, ESCAPE_ROPE, ETERNAL_ICE, ETHER, EVERSTONE, EVIOLITE, EXCITE_SCENT, EXEGGCUTE_CANDY, EXP_CANDY_L, EXP_CANDY_M, EXP_CANDY_S, EXP_CANDY_XL, EXP_CANDY_XS, EXP_CHARM, EXP_SHARE, EXPERT_BELT, EXPLORER_KIT, FAB_MAIL, FAIRIUM_Z, FAIRY_GEM, FAIRY_MEMORY, FAIRY_TERA_SHARD, FAME_CHECKER, FARFETCHD_CANDY, FASHION_CASE, FAST_BALL, FAVORED_MAIL, F_DISK, FEATHER_BALL, FESTIVAL_TICKET, FIGHTING_GEM, FIGHTING_MEMORY, FIGHTING_TERA_SHARD, FIGHTINIUM_Z, FIGY_BERRY, FINE_REMEDY, FIRE_GEM, FIRE_MEMORY, FIRE_STONE, FIRE_TERA_SHARD, FIRIUM_Z, FISHING_ROD, FIST_PLATE, FLAME_MAIL, FLAME_ORB, FLAME_PLATE, FLOAT_STONE, FLOWER_MAIL, FLOWER_SWEET, FLUFFY_TAIL, FLYING_GEM, FLYING_MEMORY, FLYING_TERA_SHARD, FLYINIUM_Z, FOCUS_BAND, FOCUS_SASH, FORAGE_BAG, FOREST_BALM, FOSSILIZED_BIRD, FOSSILIZED_DINO, FOSSILIZED_DRAKE, FOSSILIZED_FISH, FRESH_WATER, FRIEND_BALL, FULL_HEAL, FULL_INCENSE, FULL_RESTORE, GALACTIC_KEY, GALARICA_CUFF, GALARICA_TWIG, GALARICA_WREATH, GALLADITE, GANLON_BERRY, GARCHOMPITE, GARDEVOIRITE, GASTLY_CANDY, GB_SOUNDS, GEAR, GENGARITE, GENIUS_FEATHER, GENOME_SLATE, GENTLE_MINT, GEODUDE_CANDY, GHOST_GEM, GHOST_MEMORY, GHOST_TERA_SHARD, GHOSTIUM_Z, GIGATON_BALL, GINEMA_BERRY, GLALITITE, GLITTER_MAIL, GO_GOGGLES, GOD_STONE, GOLD_BERRY, GOLD_BOTTLE_CAP, GOLD_LEAF, GOLD_TEETH, GOLDEEN_CANDY, GONZAPS_KEY, GOLDEN_NANAB_BERRY, GOLDEN_PINAP_BERRY, GOLDEN_RAZZ_BERRY, GOOD_ROD, GOOEY_MULCH, GORGEOUS_BOX, GRACIDEA, GRAIN_CAKE, GRAM_1, GRAM_2, GRAM_3, GRASS_GEM, GRASS_MEMORY, GRASS_TERA_SHARD, GRASSIUM_Z, GRASS_MAIL, GRASSY_SEED, GREAT_BALL, GREEN_APRICORN, GREEN_PETAL, GREEN_SCARF, GREEN_SHARD, GREET_MAIL, GREPA_BERRY, GRIMER_CANDY, GRIP_CLAW, GRIT_DUST, GRIT_GRAVEL, GRIT_PEBBLE, GRIT_ROCK, GRISEOUS_CORE, GRISEOUS_ORB, GRN_ID_BADGE, GROUND_GEM, GROUND_MEMORY, GROUND_TERA_SHARD, GROUNDIUM_Z, GROWLITHE_CANDY, GROWTH_MULCH, GRUBBY_HANKY, GS_BALL, GUARD_SPEC, GUIDEBOOK, GYARADOSITE, HABAN_BERRY, HARBOR_MAIL, HARD_STONE, HASTY_MINT, HEAL_BALL, HEAL_POWDER, HEALTH_CANDY, HEALTH_CANDY_L, HEALTH_CANDY_XL, HEALTH_FEATHER, HEART_MAIL, HEART_SCALE, HEARTY_GRAINS, HEAT_ROCK, HEAVY_BALL, HEAVY_DUTY_BOOTS, HELIX_FOSSIL, HERACRONITE, HI_TECH_EARBUDS, HITMONCHAN_CANDY, HITMONLEE_CANDY, HM01, HM02, HM03, HM04, HM05, HM06, HM07, HM08, HOLO_CASTER, HOMETOWN_MUFFIN, HONDEW_BERRY, HONEY, HONEY_CAKE, HONOR_OF_KALOS, HOPO_BERRY, HORSEA_CANDY, HOUNDOOMINITE, HP_UP, HYPER_POTION, IAPAPA_BERRY, ICE_BERRY, ICE_GEM, ICE_HEAL, ICE_MEMORY, ICE_STONE, ICE_TERA_SHARD, ICEROOT_CARROT, ICICLE_PLATE, ICIUM_Z, ICY_ROCK, ID_CARD, ILIMAS_NORMALIUM_Z, IMPISH_MINT, INCINIUM_Z, INQUIRY_MAIL, INSECT_PLATE, INTRIGUING_STONE, IRON, IRON_BALL, IRON_BARKTONGUE, IRON_CHUNK, IRON_PLATE, JABOCA_BERRY, JADE_ORB, JAIL_KEY, JAW_FOSSIL, JET_BALL, JIGGLYPUFF_CANDY, JOHTO_SLATE, JOLLY_MINT, JOY_SCENT, JUBILIFE_MUFFIN, JYNX_CANDY, KABUTO_CANDY, KANGASKHAN_CANDY, KANGASKHANITE, KANTO_SLATE, KASIB_BERRY, KEBIA_BERRY, KEE_BERRY, KELPSY_BERRY, KEY_STONE, KEY_TO_ROOM_1, KEY_TO_ROOM_2, KEY_TO_ROOM_4, KEY_TO_ROOM_6, KINGS_LEAF, KINGS_ROCK, KOFFING_CANDY, KOFUS_BOOK, KOMMONIUM_Z, KORAIDONS_POKE_BALL, KRABBY_CANDY, KRANE_MEMO_1, KRANE_MEMO_2, KRANE_MEMO_3, KRANE_MEMO_4, KRANE_MEMO_5, KUO_BERRY, LAGGING_TAIL, LANSAT_BERRY, LAPRAS_CANDY, LATIASITE, LATIOSITE, LAVA_COOKIE, LAX_INCENSE, LAX_MINT, L_DISK, LEADEN_BALL, LEADERS_CREST, LEAF_LETTER, LEAF_STONE, LEEK, LEFT_POKE_BALL, LEFTOVERS, LEGEND_PLATE, LEGENDARY_CLUE_1, LEGENDARY_CLUE_2, LEGENDARY_CLUE_3, LEGENDARY_CLUE, LEMONADE, LENS_CASE, LEPPA_BERRY, LETTER, LEVEL_BALL, LIBERTY_PASS, LICKITUNG_CANDY, LIECHI_BERRY, LIFE_ORB, LIFT_KEY, LIGHT_BALL, LIGHT_CLAY, LIGHT_STONE, LIKE_MAIL, LINKING_CORD, LITEBLUEMAIL, LOADED_DICE, LOCK_CAPSULE, LONE_EARRING, LONELY_MINT, LOOKER_TICKET, LOOT_SACK, LOPUNNITE, LOST_ITEM, LOST_SATCHEL, LOVE_BALL, LOVE_SWEET, LOVELY_MAIL, LUCARIONITE, LUCK_INCENSE, LUCKY_EGG, LUCKY_PUNCH, LUM_BERRY, LUMINOUS_MOSS, LUMIOSE_GALETTE, LUNALIUM_Z, LUNAR_FEATHER, LURE, LURE_BALL, LUSTROUS_GLOBE, LUSTROUS_ORB, LUXURY_BALL, LYCANIUM_Z, MACH_BIKE, MACHINE_PART, MACHO_BRACE, MACHOP_CANDY, MAGIKARP_CANDY, MAGMA_EMBLEM, MAGMA_STONE, MAGMA_SUIT, MAGMAR_CANDY, MAGMARIZER, MAGNEMITE_CANDY, MAGNET, MAGO_BERRY, MAGOST_BERRY, MAINGATE_KEY, MAKEUP_BAG, MALICIOUS_ARMOR, MANECTITE, MANKEY_CANDY, MARANGA_BERRY, MARBLE, MARK_CHARM, MARSHADIUM_Z, MARSH_BALM, MASTER_BALL, MAWILITE, MAX_ELIXIR, MAX_ETHER, MAX_LURE, MAX_HONEY, MAX_MUSHROOMS, MAX_POTION, MAX_REPEL, MAX_REVIVE, MAYORS_NOTE, MEADOW_PLATE, MECH_MAIL, MECHANICAL_BOX, MECHANICAL_CABINET, MECHANICAL_CIRCULAR_SAW, MECHANICAL_PINWHEEL, MECHANICAL_TUB, MEDAL_BOX, MEDICHAMITE, MEDICINAL_LEEK, MEGA_BRACELET, MEGA_RING, MELTAN_CANDY, MEMBER_CARD, MENTAL_HERB, MEOWTH_CANDY, MESPRITS_PLUME, METAGROSSITE, METAL_COAT, METAL_POWDER, METEORITE, METEORITE_SHARD, METRONOME, MEW_CANDY, MEWNIUM_Z, MEWTWO_CANDY, MEWTWONITE_X, MEWTWONITE_Y, MICLE_BERRY, MIGHTY_CANDY, MIGHTY_CANDY_L, MIGHTY_CANDY_XL, MILD_MINT, MIMIKIUM_Z, MIND_PLATE, MINT_BERRY, MIRACLEBERRY, MIRACLE_SEED, MIRAGE_MAIL, MIRAIDONS_POKE_BALL, MIROR_RADAR, MIRROR_HERB, MISTY_SEED, MODEST_MINT, MOLTRES_CANDY, MOOMOO_MILK, MOON_BALL, MOON_FLUTE, MOON_SHARD, MOON_STONE, MORPH_MAIL, MOSAIC_MAIL, MOUNTAIN_BALM, MR_MIME_CANDY, MUSCLE_BAND, MUSCLE_FEATHER, MUSHROOM_CAKE, MUSIC_DISC, MUSIC_MAIL, MYSTERIOUS_BALM, MYSTERIOUS_SHARD_S, MYSTERIOUS_SHARD_L, MYSTERYBERRY, MYSTERY_EGG, MYSTIC_WATER, MYSTICTICKET, NAIVE_MINT, NANAB_BERRY, NAUGHTY_MINT, NEST_BALL, NET_BALL, NEVER_MELT_ICE, NIDORAN_MALE_CANDY, NIDORAN_FEMALE_CANDY, NINIKU_BERRY, N_LUNARIZER, NOMEL_BERRY, NORMAL_BOX, NORMAL_GEM, NORMAL_TERA_SHARD, NORMALIUM_Z, N_SOLARIZER, NUGGET, NUTPEA_BERRY, OAKS_LETTER, OAKS_PARCEL, OCCA_BERRY, OCEANIC_SLATE, ODD_INCENSE, ODD_KEYSTONE, ODDISH_CANDY, OLD_AMBER, OLD_CHARM, OLD_GATEAU, OLD_JOURNAL, OLD_LETTER, OLD_ROD, OLD_SEA_MAP, OLD_VERSES, OMANYTE_CANDY, ONIX_CANDY, ORAN_BERRY, ORANGE_MAIL, ORANGE_PETAL, ORIGIN_BALL, ORIGIN_ORE, OVAL_CHARM, OVAL_STONE, PAIR_OF_TICKETS, PAL_PAD, PAMTRE_BERRY, PARALYZE_HEAL, PARAS_CANDY, PARCEL, PARK_BALL, PASS, PASS_ORB, PASSHO_BERRY, PAYAPA_BERRY, PEARL, PEARL_STRING, PEAT_BLOCK, PECHA_BERRY, PEP_UP_PLANT, PERMIT, PERSIM_BERRY, PETAYA_BERRY, PEWTER_CRUNCHIES, PICNIC_SET, PIDGEOTITE, PIDGEY_CANDY, PIKACHU_CANDY, PIKANIUM_Z, PIKASHUNIUM_Z, PINAP_BERRY, PINK_APRICORN, PINK_BOW, PINK_NECTAR, PINK_PETAL, PINK_SCARF, PINSIR_CANDY, PINSIRITE, PIXIE_PLATE, PLASMA_CARD, PLUME_FOSSIL, PLUMP_BEANS, POFFIN_CASE, POINT_CARD, POISON_BARB, POISON_GEM, POISON_MEMORY, POISON_TERA_SHARD, POISONIUM_Z, POKE_BALL, POKE_DOLL, POKE_FLUTE, POKE_RADAR, POKE_SNACK, POKE_TOY, POKEBLOCK_CASE, POKEBLOCK_KIT, POKEDEX, POKEMON_BOX_LINK, POKESHI_DOLL, POLKADOT_BOW, POLISHED_MUD_BALL, POLIWAG_CANDY, POMEG_BERRY, PONYTA_CANDY, POP_POD, PORTRAITMAIL, PORYGON_CANDY, POTION, POWER_ANKLET, POWER_BAND, POWER_BELT, POWER_BRACER, POWER_HERB, POWER_LENS, POWER_PLANT_PASS, POWER_WEIGHT, POWERUP_PART, POWDER_JAR, PP_MAX, PP_UP, PREMIER_BALL, PRETTY_FEATHER, PRIMARIUM_Z, PRISM_SCALE, PRISON_BOTTLE, PROFS_LETTER, PROFESSORS_MASK, PROP_CASE, PROTECTIVE_PADS, PROTECTOR, PROTEIN, PRZCUREBERRY, PSNCUREBERRY, PSYCHIC_GEM, PSYCHIC_MEMORY, PSYCHIC_SEED, PSYCHIC_TERA_SHARD, PSYCHIUM_Z, PSYDUCK_CANDY, PUMKIN_BERRY, PUNCHING_GLOVE, PURE_INCENSE, PURPLE_NECTAR, PURPLE_PETAL, QUALOT_BERRY, QUICK_BALL, QUICK_CANDY, QUICK_CANDY_L, QUICK_CANDY_XL, QUICK_CLAW, QUICK_POWDER, QUIET_MINT, RABUTA_BERRY, RADIANT_PETAL, RAGECANDYBAR, RAINBOW_FLOWER, RAINBOW_PASS, RAINBOW_SLATE, RAINBOW_WING, RARE_BONE, RARE_CANDY, RASH_MINT, RATTATA_CANDY, RAWST_BERRY, RAZOR_CLAW, RAZOR_FANG, RAZZ_BERRY, R_DISK, REAPER_CLOTH, RECIPES, RED_APRICORN, RED_CARD, RED_CHAIN, RED_FLUTE, RED_ID_BADGE, RED_NECTAR, RED_ORB, RED_PETAL, RED_SCALE, RED_SCARF, RED_SHARD, REINS_OF_UNITY, RELAXED_MINT, RELIC_BAND, RELIC_COPPER, RELIC_CROWN, RELIC_GOLD, RELIC_SILVER, RELIC_STATUE, RELIC_VASE, REMEDY, REPEAT_BALL, REPEL, REPLY_MAIL, RESIST_FEATHER, RETRO_MAIL, REVEAL_GLASS, REVIVAL_HERB, REVIVE, RHYHORN_CANDY, RIBBON_SWEET, RICH_MULCH, RIDE_PAGER, RINDO_BERRY, RING_TARGET, ROCK_GEM, ROCK_INCENSE, ROCK_MEMORY, ROCK_TERA_SHARD, ROCKIUM_Z, ROCKY_HELMET, ROLLER_SKATES, ROOM_SERVICE, ROOT_FOSSIL, ROSE_INCENSE, ROSELI_BERRY, ROTO_BARGAIN, ROTO_BOOST, ROTO_CATCH, ROTO_ENCOUNTER, ROTO_EXP_POINTS, ROTO_FRIENDSHIP, ROTO_HATCH, ROTO_HP_RESTORE, ROTO_PP_RESTORE, ROTO_PRIZE_MONEY, ROTO_STEALTH, ROTOM_BIKE, ROTOM_CATALOG, ROTOM_PHONE, ROWAP_BERRY, RSVP_MAIL, RUBY, RUNNING_SHOES, RUSTED_SHIELD, RUSTED_SWORD, S_S_TICKET, SABLENITE, SACHET, SACRED_ASH, SAFARI_BALL, SAFETY_GOGGLES, SAIL_FOSSIL, SALAC_BERRY, SALAMENCITE, SALT_CAKE, SAND_RADISH, SANDSHREW_CANDY, SANDWICH, SAPPHIRE, SASSY_MINT, SCANNER, SCARLET_BOOK, SCATTER_BANG, SCEPTILITE, SCIZORITE, SCOPE_LENS, SCROLL_OF_DARKNESS, SCROLL_OF_WATERS, SCYTHER_CANDY, SEA_INCENSE, SEAL_CASE, SECRET_KEY, SECRET_MEDICINE, SEED_OF_MASTERY, SEEL_CANDY, SERIOUS_MINT, SHADOW_MAIL, SHADEROOT_CARROT, SHALOUR_SABLE, SHARP_BEAK, SHARPEDONITE, SHED_SHELL, SHELL_BELL, SHELLDER_CANDY, SHINY_CHARM, SHINY_LEAF, SHINY_STONE, SHOAL_SALT, SHOAL_SHELL, SHOCK_DRIVE, SHUCA_BERRY, SILK_SCARF, SILPH_SCOPE, SILVER_LEAF, SILVER_NANAB_BERRY, SILVER_PINAP_BERRY, SILVER_POWDER, SILVER_RAZZ_BERRY, SILVER_WING, SITRUS_BERRY, SKULL_FOSSIL, SKY_PLATE, SKY_TUMBLESTONE, SLOWBRONITE, SLOWPOKE_CANDY, SLOWPOKE_TAIL, SMALL_BOUQUET, SMALL_TABLET, SMART_CANDY, SMART_CANDY_L, SMART_CANDY_XL, SMOKE_BALL, SMOKE_BOMB, SMOOTH_ROCK, SNORLAX_CANDY, SNORLIUM_Z, SNOWBALL, SNOW_BALM, SNOW_MAIL, SODA_POP, SOFT_SAND, SOLGANIUM_Z, SONIAS_BOOK, SOOT_SACK, SOOTFOOT_ROOT, SOOTHE_BELL, SOUL_DEW, SOUL_SLATE, SPACE_BALM, SPACE_MAIL, SPARKLING_STONE, SPEAROW_CANDY, SPELL_TAG, SPELON_BERRY, SPLASH_PLATE, SPOILED_APRICORN, SPOOKY_PLATE, SPORT_BALL, SPRAYDUCK, SPRINGY_MUSHROOM, SPRINKLOTAD, SQUALL_SLATE, SQUIRT_BOTTLE, SQUIRTLE_CANDY, STABLE_MULCH, STAR_PIECE, STAR_SWEET, STARDUST, STARF_BERRY, STARYU_CANDY, STEALTH_SPRAY, STEEL_GEM, STEEL_MAIL, STEEL_MEMORY, STEEL_TEETH, STEEL_TERA_SHARD, STEELIUM_Z, STEELIXITE, STICKY_BARB, STICKY_GLOB, STONE_PLATE, STORAGE_KEY, STRANGE_BALL, STRANGE_SOUVENIR, STRATOSPHERIC_SLATE, STRAWBERRY_SWEET, STRETCHY_SPRING, STRIB_BERRY, STYLE_CARD, SUBWAY_KEY, SUITE_KEY, SUN_FLUTE, SUN_SHARD, SUN_STONE, SUPER_LURE, SUPER_POTION, SUPER_REPEL, SUPER_ROD, SUPERB_REMEDY, SURFBOARD, SURF_MAIL, SURGE_BADGE, SURPRISE_MULCH, SURVIVAL_CHARM_B, SURVIVAL_CHARM_P, SURVIVAL_CHARM_R, SURVIVAL_CHARM_T, SURVIVAL_CHARM_Y, SWAMPERTITE, SWAP_SNACK, SWEET_APPLE, SWEET_HEART, SWIFT_FEATHER, SWORDCAP, SYSTEM_LEVER, TAMATO_BERRY, TANGA_BERRY, TANGELA_CANDY, TAPUNIUM_Z, TART_APPLE, TAUROS_CANDY, TEA, TEACHY_TV, TECTONIC_SLATE, TEMPTING_CHARM_B, TEMPTING_CHARM_P, TEMPTING_CHARM_R, TEMPTING_CHARM_T, TEMPTING_CHARM_Y, TENTACOOL_CANDY, TERA_ORB, TERRAIN_EXTENDER, TERU_SAMA, THANKS_MAIL, THICK_CLUB, THROAT_SPRAY, THUNDER_STONE, TIDAL_BELL, TIME_BALM, TIME_FLUTE, TIMER_BALL, TIMID_MINT, TINY_BAMBOO_SHOOT, TINY_MUSHROOM, TM01, TM02, TM03, TM04, TM05, TM06, TM07, TM08, TM09, TM10, TM11, TM12, TM13, TM14, TM15, TM16, TM17, TM18, TM19, TM20, TM21, TM22, TM23, TM24, TM25, TM26, TM27, TM28, TM29, TM30, TM31, TM32, TM33, TM34, TM35, TM36, TM37, TM38, TM39, TM40, TM41, TM42, TM43, TM44, TM45, TM46, TM47, TM48, TM49, TM50, TM51, TM52, TM53, TM54, TM55, TM56, TM57, TM58, TM59, TM60, TM61, TM62, TM63, TM64, TM65, TM66, TM67, TM68, TM69, TM70, TM71, TM72, TM73, TM74, TM75, TM76, TM77, TM78, TM79, TM80, TM81, TM82, TM83, TM84, TM85, TM86, TM87, TM88, TM89, TM90, TM91, TM92, TM93, TM94, TM95, TM96, TM97, TM98, TM99, TM_CASE, TM_MATERIALS, TR01, TR02, TR03, TR04, TR05, TR06, TR07, TR08, TR09, TR10, TR11, TR12, TR13, TR14, TR15, TR16, TR17, TR18, TR19, TR20, TR21, TR22, TR23, TR24, TR25, TR26, TR27, TR28, TR29, TR30, TR31, TR32, TR33, TR34, TR35, TR36, TR37, TR38, TR39, TR40, TR41, TR42, TR43, TR44, TR45, TR46, TR47, TR48, TR49, TR50, TR51, TR52, TR53, TR54, TR55, TR56, TR57, TR58, TR59, TR60, TR61, TR62, TR63, TR64, TR65, TR66, TR67, TR68, TR69, TR70, TR71, TR72, TR73, TR74, TR75, TR76, TR77, TR78, TR79, TR80, TR81, TR82, TR83, TR84, TR85, TR86, TR87, TR88, TR89, TR90, TR91, TR92, TR93, TR94, TR95, TR96, TR97, TR98, TR99, TMV_PASS, TOPO_BERRY, TORN_JOURNAL, TOUGA_BERRY, TOUGH_CANDY, TOUGH_CANDY_L, TOUGH_CANDY_XL, TOWN_MAP, TOXIC_ORB, TOXIC_PLATE, TRAVEL_TRUNK, TRI_PASS, TROPIC_MAIL, TROPICAL_SHELL, TUMBLESTONE, TUNNEL_MAIL, TWICE_SPICED_RADISH, TWISTED_SPOON, TYRANITARITE, U_DISK, ULTRA_BALL, ULTRANECROZIUM_Z, UNOWN_REPORT, UNUSUAL_SHOES, UPGRADE, UTILITY_UMBRELLA, UXIES_CLAW, VENONAT_CANDY, VENUSAURITE, VIOLET_BOOK, VIVICHOKE, VIVID_SCENT, VOICE_CASE_1, VOICE_CASE_2, VOICE_CASE_3, VOICE_CASE_4, VOICE_CASE_5, VOLCANO_BALM, VOLTORB_CANDY, VS_RECORDER, VS_SEEKER, VULPIX_CANDY, WACAN_BERRY, WAILMER_PAIL, WALL_FRAGMENT, WARDING_CHARM_B, WARDING_CHARM_P, WARDING_CHARM_R, WARDING_CHARM_T, WARDING_CHARM_Y, WATER_GEM, WATER_MEMORY, WATER_STONE, WATER_TERA_SHARD, WATERIUM_Z, WATMEL_BERRY, WAVE_INCENSE, WAVE_MAIL, WEAKNESS_POLICY, WEEDLE_CANDY, WEPEAR_BERRY, WHIPPED_DREAM, WHITE_APRICORN, WHITE_FLUTE, WHITE_HERB, WHITE_MANE_HAIR, WIDE_LENS, WIKI_BERRY, WING_BALL, WISE_GLASSES, WISHING_CHIP, WISHING_PIECE, WISHING_STAR, WOOD, WOOD_MAIL, WOODEN_CROWN, WORKS_KEY, X_ACCURACY, X_ATTACK, X_DEFENSE, X_SP_ATK, X_SP_DEF, X_SPEED, XTRANSCEIVER, YACHE_BERRY, YAGO_BERRY, YELLOW_APRICORN, YELLOW_FLUTE, YELLOW_NECTAR, YELLOW_PETAL, YELLOW_SCARF, YELLOW_SHARD, YLW_ID_BADGE, ZAP_PLATE, ZAPDOS_CANDY, ZINC, ZOOM_LENS, Z_POWER_RING, Z_RING, ZUBAT_CANDY, ZYGARDE_CUBE, ITEM_TOTAL + // clang-format on +}; + +static constexpr std::uint16_t TOTAL_ITEM_COUNT = (std::uint16_t)Item::ITEM_TOTAL - 1U; +} // namespace pokesim::dex + +/////////////////////// END OF src/Types/Enums/Item.hpp //////////////////////// + +////////////////////// START OF src/Types/Enums/Move.hpp /////////////////////// + +namespace pokesim::dex { +// Pokemon move name +enum class Move : std::uint16_t { + // clang-format off + NO_MOVE = 0U, ONE_MILLION_VOLT_THUNDERBOLT, ABSORB, ACCELEROCK, ACID, ACID_ARMOR, ACID_DOWNPOUR, ACID_SPRAY, ACROBATICS, ACUPRESSURE, AERIAL_ACE, AEROBLAST, AFTER_YOU, AGILITY, AIR_CUTTER, AIR_SLASH, ALL_OUT_PUMMELING, ALLY_SWITCH, AMNESIA, ANCHOR_SHOT, ANCIENT_POWER, APPLE_ACID, AQUA_CUTTER, AQUA_JET, AQUA_RING, AQUA_STEP, AQUA_TAIL, ARMOR_CANNON, ARM_THRUST, AROMATHERAPY, AROMATIC_MIST, ASSIST, ASSURANCE, ASTONISH, ASTRAL_BARRAGE, ATTACK_ORDER, ATTRACT, AURA_SPHERE, AURA_WHEEL, AURORA_BEAM, AURORA_VEIL, AUTOTOMIZE, AVALANCHE, AXE_KICK, BABY_DOLL_EYES, BADDY_BAD, BANEFUL_BUNKER, BARB_BARRAGE, BARRAGE, BARRIER, BATON_PASS, BEAK_BLAST, BEAT_UP, BEHEMOTH_BASH, BEHEMOTH_BLADE, BELCH, BELLY_DRUM, BESTOW, BIDE, BIND, BITE, BITTER_BLADE, BITTER_MALICE, BLACK_HOLE_ECLIPSE, BLAST_BURN, BLAZE_KICK, BLAZING_TORQUE, BLEAKWIND_STORM, BLIZZARD, BLOCK, BLOOM_DOOM, BLUE_FLARE, BODY_PRESS, BODY_SLAM, BOLT_BEAK, BOLT_STRIKE, BONE_CLUB, BONEMERANG, BONE_RUSH, BOOMBURST, BOUNCE, BOUNCY_BUBBLE, BRANCH_POKE, BRAVE_BIRD, BREAKING_SWIPE, BREAKNECK_BLITZ, BRICK_BREAK, BRINE, BRUTAL_SWING, BUBBLE, BUBBLE_BEAM, BUG_BITE, BUG_BUZZ, BULK_UP, BULLDOZE, BULLET_PUNCH, BULLET_SEED, BURNING_JEALOUSY, BURN_UP, BUZZY_BUZZ, CALM_MIND, CAMOUFLAGE, CAPTIVATE, CATASTROPIKA, CEASELESS_EDGE, CELEBRATE, CHARGE, CHARGE_BEAM, CHARM, CHATTER, CHILLING_WATER, CHILLY_RECEPTION, CHIP_AWAY, CHLOROBLAST, CIRCLE_THROW, CLAMP, CLANGING_SCALES, CLANGOROUS_SOUL, CLANGOROUS_SOULBLAZE, CLEAR_SMOG, CLOSE_COMBAT, COACHING, COIL, COLLISION_COURSE, COMBAT_TORQUE, COMET_PUNCH, COMEUPPANCE, CONFIDE, CONFUSE_RAY, CONFUSION, CONSTRICT, CONTINENTAL_CRUSH, CONVERSION, CONVERSION_2, COPYCAT, CORE_ENFORCER, CORKSCREW_CRASH, CORROSIVE_GAS, COSMIC_POWER, COTTON_GUARD, COTTON_SPORE, COUNTER, COURT_CHANGE, COVET, CRABHAMMER, CRAFTY_SHIELD, CROSS_CHOP, CROSS_POISON, CRUNCH, CRUSH_CLAW, CRUSH_GRIP, CURSE, CUT, DARKEST_LARIAT, DARK_PULSE, DARK_VOID, DAZZLING_GLEAM, DECORATE, DEFEND_ORDER, DEFENSE_CURL, DEFOG, DESTINY_BOND, DETECT, DEVASTATING_DRAKE, DIAMOND_STORM, DIG, DISABLE, DISARMING_VOICE, DISCHARGE, DIRE_CLAW, DIVE, DIZZY_PUNCH, DOODLE, DOOM_DESIRE, DOUBLE_EDGE, DOUBLE_HIT, DOUBLE_IRON_BASH, DOUBLE_KICK, DOUBLE_SHOCK, DOUBLE_SLAP, DOUBLE_TEAM, DRACO_METEOR, DRAGON_ASCENT, DRAGON_BREATH, DRAGON_CLAW, DRAGON_DANCE, DRAGON_DARTS, DRAGON_ENERGY, DRAGON_HAMMER, DRAGON_PULSE, DRAGON_RAGE, DRAGON_RUSH, DRAGON_TAIL, DRAINING_KISS, DRAIN_PUNCH, DREAM_EATER, DRILL_PECK, DRILL_RUN, DRUM_BEATING, DUAL_CHOP, DUAL_WINGBEAT, DYNAMAX_CANNON, DYNAMIC_PUNCH, EARTH_POWER, EARTHQUAKE, ECHOED_VOICE, EERIE_IMPULSE, EERIE_SPELL, EGG_BOMB, ELECTRIC_TERRAIN, ELECTRIFY, ELECTRO_BALL, ELECTRO_DRIFT, ELECTROWEB, EMBARGO, EMBER, ENCORE, ENDEAVOR, ENDURE, ENERGY_BALL, ENTRAINMENT, ERUPTION, ESPER_WING, ETERNABEAM, EXPANDING_FORCE, EXPLOSION, EXTRASENSORY, EXTREME_EVOBOOST, EXTREME_SPEED, FACADE, FAIRY_LOCK, FAIRY_WIND, FAKE_OUT, FAKE_TEARS, FALSE_SURRENDER, FALSE_SWIPE, FEATHER_DANCE, FEINT, FEINT_ATTACK, FELL_STINGER, FIERY_DANCE, FIERY_WRATH, FILLET_AWAY, FINAL_GAMBIT, FIRE_BLAST, FIRE_FANG, FIRE_LASH, FIRE_PLEDGE, FIRE_PUNCH, FIRE_SPIN, FIRST_IMPRESSION, FISHIOUS_REND, FISSURE, FLAIL, FLAME_BURST, FLAME_CHARGE, FLAME_WHEEL, FLAMETHROWER, FLARE_BLITZ, FLASH, FLASH_CANNON, FLATTER, FLEUR_CANNON, FLING, FLIP_TURN, FLOATY_FALL, FLORAL_HEALING, FLOWER_SHIELD, FLOWER_TRICK, FLY, FLYING_PRESS, FOCUS_BLAST, FOCUS_ENERGY, FOCUS_PUNCH, FOLLOW_ME, FORCE_PALM, FORESIGHT, FORESTS_CURSE, FOUL_PLAY, FREEZE_DRY, FREEZE_SHOCK, FREEZING_GLARE, FREEZY_FROST, FRENZY_PLANT, FROST_BREATH, FRUSTRATION, FURY_ATTACK, FURY_CUTTER, FURY_SWIPES, FUSION_BOLT, FUSION_FLARE, FUTURE_SIGHT, GASTRO_ACID, GEAR_GRIND, GEAR_UP, GENESIS_SUPERNOVA, GEOMANCY, GIGA_DRAIN, GIGA_IMPACT, GIGATON_HAMMER, GIGAVOLT_HAVOC, GLACIAL_LANCE, GLACIATE, GLAIVE_RUSH, GLARE, GLITZY_GLOW, G_MAX_BEFUDDLE, G_MAX_CANNONADE, G_MAX_CENTIFERNO, G_MAX_CHI_STRIKE, G_MAX_CUDDLE, G_MAX_DEPLETION, G_MAX_DRUM_SOLO, G_MAX_FINALE, G_MAX_FIREBALL, G_MAX_FOAM_BURST, G_MAX_GOLD_RUSH, G_MAX_GRAVITAS, G_MAX_HYDROSNIPE, G_MAX_MALODOR, G_MAX_MELTDOWN, G_MAX_ONE_BLOW, G_MAX_RAPID_FLOW, G_MAX_REPLENISH, G_MAX_RESONANCE, G_MAX_SANDBLAST, G_MAX_SMITE, G_MAX_SNOOZE, G_MAX_STEELSURGE, G_MAX_STONESURGE, G_MAX_STUN_SHOCK, G_MAX_SWEETNESS, G_MAX_TARTNESS, G_MAX_TERROR, G_MAX_VINE_LASH, G_MAX_VOLCALITH, G_MAX_VOLT_CRASH, G_MAX_WILDFIRE, G_MAX_WIND_RAGE, GRASS_KNOT, GRASS_PLEDGE, GRASS_WHISTLE, GRASSY_GLIDE, GRASSY_TERRAIN, GRAV_APPLE, GRAVITY, GROWL, GROWTH, GRUDGE, GUARDIAN_OF_ALOLA, GUARD_SPLIT, GUARD_SWAP, GUILLOTINE, GUNK_SHOT, GUST, GYRO_BALL, HAIL, HAMMER_ARM, HAPPY_HOUR, HARDEN, HAZE, HEADBUTT, HEAD_CHARGE, HEADLONG_RUSH, HEAD_SMASH, HEAL_BELL, HEAL_BLOCK, HEALING_WISH, HEAL_ORDER, HEAL_PULSE, HEART_STAMP, HEART_SWAP, HEAT_CRASH, HEAT_WAVE, HEAVY_SLAM, HELPING_HAND, HEX, HIDDEN_POWER, HIDDEN_POWER_BUG, HIDDEN_POWER_DARK, HIDDEN_POWER_DRAGON, HIDDEN_POWER_ELECTRIC, HIDDEN_POWER_FIGHTING, HIDDEN_POWER_FIRE, HIDDEN_POWER_FLYING, HIDDEN_POWER_GHOST, HIDDEN_POWER_GRASS, HIDDEN_POWER_GROUND, HIDDEN_POWER_ICE, HIDDEN_POWER_POISON, HIDDEN_POWER_PSYCHIC, HIDDEN_POWER_ROCK, HIDDEN_POWER_STEEL, HIDDEN_POWER_WATER, HIGH_HORSEPOWER, HIGH_JUMP_KICK, HOLD_BACK, HOLD_HANDS, HONE_CLAWS, HORN_ATTACK, HORN_DRILL, HORN_LEECH, HOWL, HURRICANE, HYDRO_CANNON, HYDRO_PUMP, HYDRO_STEAM, HYDRO_VORTEX, HYPER_BEAM, HYPER_DRILL, HYPER_FANG, HYPERSPACE_FURY, HYPERSPACE_HOLE, HYPER_VOICE, HYPNOSIS, ICE_BALL, ICE_BEAM, ICE_BURN, ICE_FANG, ICE_HAMMER, ICE_PUNCH, ICE_SHARD, ICE_SPINNER, ICICLE_CRASH, ICICLE_SPEAR, ICY_WIND, IMPRISON, INCINERATE, INFERNAL_PARADE, INFERNO, INFERNO_OVERDRIVE, INFESTATION, INGRAIN, INSTRUCT, ION_DELUGE, IRON_DEFENSE, IRON_HEAD, IRON_TAIL, JAW_LOCK, JET_PUNCH, JUDGMENT, JUMP_KICK, JUNGLE_HEALING, KARATE_CHOP, KINESIS, KINGS_SHIELD, KNOCK_OFF, KOWTOW_CLEAVE, LANDS_WRATH, LASER_FOCUS, LASH_OUT, LAST_RESORT, LAST_RESPECTS, LAVA_PLUME, LEAFAGE, LEAF_BLADE, LEAF_STORM, LEAF_TORNADO, LEECH_LIFE, LEECH_SEED, LEER, LETS_SNUGGLE_FOREVER, LICK, LIFE_DEW, LIGHT_OF_RUIN, LIGHT_SCREEN, LIGHT_THAT_BURNS_THE_SKY, LIQUIDATION, LOCK_ON, LOVELY_KISS, LOW_KICK, LOW_SWEEP, LUCKY_CHANT, LUMINA_CRASH, LUNAR_BLESSING, LUNAR_DANCE, LUNGE, LUSTER_PURGE, MACH_PUNCH, MAGICAL_LEAF, MAGICAL_TORQUE, MAGIC_COAT, MAGIC_POWDER, MAGIC_ROOM, MAGMA_STORM, MAGNET_BOMB, MAGNETIC_FLUX, MAGNET_RISE, MAGNITUDE, MAKE_IT_RAIN, MALICIOUS_MOONSAULT, MAT_BLOCK, MAX_AIRSTREAM, MAX_DARKNESS, MAX_FLARE, MAX_FLUTTERBY, MAX_GEYSER, MAX_GUARD, MAX_HAILSTORM, MAX_KNUCKLE, MAX_LIGHTNING, MAX_MINDSTORM, MAX_OOZE, MAX_OVERGROWTH, MAX_PHANTASM, MAX_QUAKE, MAX_ROCKFALL, MAX_STARFALL, MAX_STEELSPIKE, MAX_STRIKE, MAX_WYRMWIND, MEAN_LOOK, MEDITATE, ME_FIRST, MEGA_DRAIN, MEGAHORN, MEGA_KICK, MEGA_PUNCH, MEMENTO, MENACING_MOONRAZE_MAELSTROM, METAL_BURST, METAL_CLAW, METAL_SOUND, METEOR_ASSAULT, METEOR_BEAM, METEOR_MASH, METRONOME, MILK_DRINK, MIMIC, MIND_BLOWN, MIND_READER, MINIMIZE, MIRACLE_EYE, MIRROR_COAT, MIRROR_MOVE, MIRROR_SHOT, MIST, MIST_BALL, MISTY_EXPLOSION, MISTY_TERRAIN, MOONBLAST, MOONGEIST_BEAM, MOONLIGHT, MORNING_SUN, MORTAL_SPIN, MOUNTAIN_GALE, MUD_BOMB, MUD_SHOT, MUD_SLAP, MUD_SPORT, MUDDY_WATER, MULTI_ATTACK, MYSTICAL_FIRE, MYSTICAL_POWER, NASTY_PLOT, NATURAL_GIFT, NATURE_POWER, NATURES_MADNESS, NEEDLE_ARM, NEVER_ENDING_NIGHTMARE, NIGHT_DAZE, NIGHTMARE, NIGHT_SHADE, NIGHT_SLASH, NOBLE_ROAR, NO_RETREAT, NOXIOUS_TORQUE, NUZZLE, OBLIVION_WING, OBSTRUCT, OCEANIC_OPERETTA, OCTAZOOKA, OCTOLOCK, ODOR_SLEUTH, OMINOUS_WIND, ORDER_UP, ORIGIN_PULSE, OUTRAGE, OVERDRIVE, OVERHEAT, PAIN_SPLIT, PARABOLIC_CHARGE, PARTING_SHOT, PAYBACK, PAY_DAY, PECK, PERISH_SONG, PETAL_BLIZZARD, PETAL_DANCE, PHANTOM_FORCE, PHOTON_GEYSER, PIKA_PAPOW, PIN_MISSILE, PLASMA_FISTS, PLAY_NICE, PLAY_ROUGH, PLUCK, POISON_FANG, POISON_GAS, POISON_JAB, POISON_POWDER, POISON_STING, POISON_TAIL, POLLEN_PUFF, POLTERGEIST, POPULATION_BOMB, POUNCE, POUND, POWDER, POWDER_SNOW, POWER_GEM, POWER_SHIFT, POWER_SPLIT, POWER_SWAP, POWER_TRICK, POWER_TRIP, POWER_UP_PUNCH, POWER_WHIP, PRECIPICE_BLADES, PRESENT, PRISMATIC_LASER, PROTECT, PSYBEAM, PSYBLADE, PSYCH_UP, PSYCHIC, PSYCHIC_FANGS, PSYCHIC_TERRAIN, PSYCHO_BOOST, PSYCHO_CUT, PSYCHO_SHIFT, PSYSHIELD_BASH, PSYSHOCK, PSYSTRIKE, PSYWAVE, PULVERIZING_PANCAKE, PUNISHMENT, PURIFY, PURSUIT, PYRO_BALL, QUASH, QUICK_ATTACK, QUICK_GUARD, QUIVER_DANCE, RAGE, RAGE_FIST, RAGE_POWDER, RAGING_BULL, RAGING_FURY, RAIN_DANCE, RAPID_SPIN, RAZOR_LEAF, RAZOR_SHELL, RAZOR_WIND, RECOVER, RECYCLE, REFLECT, REFLECT_TYPE, REFRESH, RELIC_SONG, REST, RETALIATE, RETURN, REVELATION_DANCE, REVENGE, REVERSAL, REVIVAL_BLESSING, RISING_VOLTAGE, ROAR, ROAR_OF_TIME, ROCK_BLAST, ROCK_CLIMB, ROCK_POLISH, ROCK_SLIDE, ROCK_SMASH, ROCK_THROW, ROCK_TOMB, ROCK_WRECKER, ROLE_PLAY, ROLLING_KICK, ROLLOUT, ROOST, ROTOTILLER, ROUND, RUINATION, SACRED_FIRE, SACRED_SWORD, SAFEGUARD, SALT_CURE, SAND_ATTACK, SANDSEAR_STORM, SANDSTORM, SAND_TOMB, SAPPY_SEED, SAVAGE_SPIN_OUT, SCALD, SCALE_SHOT, SCARY_FACE, SCORCHING_SANDS, SCRATCH, SCREECH, SEARING_SHOT, SEARING_SUNRAZE_SMASH, SECRET_POWER, SECRET_SWORD, SEED_BOMB, SEED_FLARE, SEISMIC_TOSS, SELF_DESTRUCT, SHADOW_BALL, SHADOW_BONE, SHADOW_CLAW, SHADOW_FORCE, SHADOW_PUNCH, SHADOW_SNEAK, SHARPEN, SHATTERED_PSYCHE, SHED_TAIL, SHEER_COLD, SHELL_SIDE_ARM, SHELL_SMASH, SHELL_TRAP, SHELTER, SHIFT_GEAR, SHOCK_WAVE, SHORE_UP, SIGNAL_BEAM, SILK_TRAP, SILVER_WIND, SIMPLE_BEAM, SING_MOVE /*Many math libraries define SING as a macro*/, SINISTER_ARROW_RAID, SIZZLY_SLIDE, SKETCH, SKILL_SWAP, SKITTER_SMACK, SKULL_BASH, SKY_ATTACK, SKY_DROP, SKY_UPPERCUT, SLACK_OFF, SLAM, SLASH, SLEEP_POWDER, SLEEP_TALK, SLUDGE, SLUDGE_BOMB, SLUDGE_WAVE, SMACK_DOWN, SMART_STRIKE, SMELLING_SALTS, SMOG, SMOKESCREEN, SNAP_TRAP, SNARL, SNATCH, SNIPE_SHOT, SNORE, SNOWSCAPE, SOAK, SOFT_BOILED, SOLAR_BEAM, SOLAR_BLADE, SONIC_BOOM, SOUL_STEALING_7_STAR_STRIKE, SPACIAL_REND, SPARK, SPARKLING_ARIA, SPARKLY_SWIRL, SPECTRAL_THIEF, SPEED_SWAP, SPICY_EXTRACT, SPIDER_WEB, SPIKE_CANNON, SPIKES, SPIKY_SHIELD, SPIN_OUT, SPIRIT_BREAK, SPIRIT_SHACKLE, SPIT_UP, SPITE, SPLASH, SPLINTERED_STORMSHARDS, SPLISHY_SPLASH, SPORE, SPOTLIGHT, SPRINGTIDE_STORM, STEALTH_ROCK, STEAM_ERUPTION, STEAMROLLER, STEEL_BEAM, STEEL_ROLLER, STEEL_WING, STICKY_WEB, STOCKPILE, STOKED_SPARKSURFER, STOMP, STOMPING_TANTRUM, STONE_AXE, STONE_EDGE, STORED_POWER, STORM_THROW, STRANGE_STEAM, STRENGTH, STRENGTH_SAP, STRING_SHOT, STRUGGLE, STRUGGLE_BUG, STUFF_CHEEKS, STUN_SPORE, SUBMISSION, SUBSTITUTE, SUBZERO_SLAMMER, SUCKER_PUNCH, SUNNY_DAY, SUNSTEEL_STRIKE, SUPER_FANG, SUPERPOWER, SUPERSONIC, SUPERSONIC_SKYSTRIKE, SURF, SURGING_STRIKES, SWAGGER, SWALLOW, SWEET_KISS, SWEET_SCENT, SWIFT, SWITCHEROO, SWORDS_DANCE, SYNCHRONOISE, SYNTHESIS, TACKLE, TAIL_GLOW, TAIL_SLAP, TAIL_WHIP, TAILWIND, TAKE_DOWN, TAKE_HEART, TAR_SHOT, TAUNT, TEARFUL_LOOK, TEATIME, TECHNO_BLAST, TECTONIC_RAGE, TEETER_DANCE, TELEKINESIS, TELEPORT, TERA_BLAST, TERRAIN_PULSE, THIEF, THOUSAND_ARROWS, THOUSAND_WAVES, THRASH, THROAT_CHOP, THUNDER, THUNDERBOLT, THUNDER_CAGE, THUNDER_FANG, THUNDEROUS_KICK, THUNDER_PUNCH, THUNDER_SHOCK, THUNDER_WAVE, TICKLE, TIDY_UP, TOPSY_TURVY, TORCH_SONG, TORMENT, TOXIC, TOXIC_SPIKES, TOXIC_THREAD, TRAILBLAZE, TRANSFORM, TRI_ATTACK, TRICK, TRICK_OR_TREAT, TRICK_ROOM, TRIPLE_ARROWS, TRIPLE_AXEL, TRIPLE_DIVE, TRIPLE_KICK, TROP_KICK, TRUMP_CARD, TWIN_BEAM, TWINEEDLE, TWINKLE_TACKLE, TWISTER, U_TURN, UPROAR, VACUUM_WAVE, V_CREATE, VEEVEE_VOLLEY, VENOM_DRENCH, VENOSHOCK, VICTORY_DANCE, VINE_WHIP, VISE_GRIP, VITAL_THROW, VOLT_SWITCH, VOLT_TACKLE, WAKE_UP_SLAP, WATERFALL, WATER_GUN, WATER_PLEDGE, WATER_PULSE, WATER_SHURIKEN, WATER_SPORT, WATER_SPOUT, WAVE_CRASH, WEATHER_BALL, WHIRLPOOL, WHIRLWIND, WICKED_BLOW, WICKED_TORQUE, WIDE_GUARD, WILDBOLT_STORM, WILD_CHARGE, WILL_O_WISP, WING_ATTACK, WISH, WITHDRAW, WONDER_ROOM, WOOD_HAMMER, WORK_UP, WORRY_SEED, WRAP, WRING_OUT, X_SCISSOR, YAWN, ZAP_CANNON, ZEN_HEADBUTT, ZING_ZAP, ZIPPY_ZAP, MOVE_TOTAL, + // clang-format on +}; + +static constexpr std::uint16_t TOTAL_MOVE_COUNT = (std::uint16_t)Move::MOVE_TOTAL - 1U; +} // namespace pokesim::dex + +/////////////////////// END OF src/Types/Enums/Move.hpp //////////////////////// + +////////////////////// START OF src/Types/Enums/Slot.hpp /////////////////////// + +namespace pokesim { +enum class Slot : std::uint8_t { + NONE = 0U, + P1A, + P2A, + P1B, + P2B, + + P1C, + P2C, + P1D, + P2D, + P1E, + P2E, + P1F, + P2F, + + TOTAL_SLOT, +}; + +static constexpr std::uint8_t TOTAL_SLOT_COUNT = (std::uint8_t)Slot::TOTAL_SLOT - 1U; +} // namespace pokesim + +/////////////////////// END OF src/Types/Enums/Slot.hpp //////////////////////// + +////////////////////// START OF src/Utilities/Variant.hpp ////////////////////// + +#include + +namespace pokesim::internal { +template +class variant : public std::variant { + protected: + using base = std::variant; + + public: + using base::variant; + + template + variant& operator=(const T& rhs) { + std::variant::operator=(rhs); + return *this; + } + + constexpr bool empty() const { return holds(); } + + template + constexpr bool holds() const { + return std::holds_alternative(*this); + } + + template + constexpr auto& get() const { + return std::get(*this); + } + + template + constexpr auto& get() { + return std::get(*this); + } + + template + constexpr auto get_if() const { + return std::make_tuple(std::get_if(this)...); + } + + template + constexpr auto get_if() { + return std::make_tuple(std::get_if(this)...); + } +}; +} // namespace pokesim::internal + +/////////////////////// END OF src/Utilities/Variant.hpp /////////////////////// + +/////////////////////// START OF src/Types/Decisions.hpp /////////////////////// + +namespace pokesim { +struct MoveDecision { + Slot sourceSlot = Slot::NONE; + Slot targetSlot = Slot::NONE; + + dex::Move move = dex::Move::NO_MOVE; + + constexpr bool operator==(const MoveDecision& other) const { + return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot && move == other.move; + } +}; + +struct MegaEvolveAndMoveDecision : MoveDecision {}; +struct ZMoveDecision : MoveDecision {}; +struct DynamaxAndMoveDecision : MoveDecision {}; +struct TerastallizeAndMoveDecision : MoveDecision {}; + +struct SwitchDecision { + Slot sourceSlot = Slot::NONE; + Slot targetSlot = Slot::NONE; + + constexpr bool operator==(const SwitchDecision& other) const { + return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot; + } +}; + +struct ItemDecision { + Slot sourceSlot = Slot::NONE; + Slot targetSlot = Slot::NONE; + + dex::Item item = dex::Item::NO_ITEM; + + constexpr bool operator==(const ItemDecision& other) const { + return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot && item == other.item; + } +}; + +namespace types { +struct slotDecision : pokesim::internal::variant< + MoveDecision, MegaEvolveAndMoveDecision, TerastallizeAndMoveDecision, DynamaxAndMoveDecision, + ZMoveDecision, SwitchDecision, ItemDecision> { + using variant = pokesim::internal::variant< + MoveDecision, MegaEvolveAndMoveDecision, TerastallizeAndMoveDecision, DynamaxAndMoveDecision, ZMoveDecision, + SwitchDecision, ItemDecision>; + using variant::variant; + + constexpr Slot sourceSlot() const { + return std::visit([](auto&& decision) { return decision.sourceSlot; }, (variant::base) * this); + } + + constexpr Slot targetSlot() const { + return std::visit([](auto&& decision) { return decision.targetSlot; }, (variant::base) * this); + } +}; + +using slotDecisions = types::sideSlots; +} // namespace types +} // namespace pokesim + +//////////////////////// END OF src/Types/Decisions.hpp //////////////////////// + +/////////////////// START OF src/Types/Enums/ActionOrder.hpp /////////////////// + +namespace pokesim { +enum class ActionOrder : std::uint8_t { + NONE = std::numeric_limits>::max(), + TEAM = 1U, + START = 2U, + BEFORE_TURN = 4U, + ITEM = BEFORE_TURN, + + SWITCH = 103U, + + MOVE = 200U, + + RESIDUAL = 254U, +}; + +static constexpr inline std::array VALID_ACTION_ORDERS = { + ActionOrder::NONE, + ActionOrder::TEAM, + ActionOrder::START, + ActionOrder::BEFORE_TURN, + ActionOrder::ITEM, + ActionOrder::SWITCH, + ActionOrder::MOVE, + ActionOrder::RESIDUAL, +}; +} // namespace pokesim + +//////////////////// END OF src/Types/Enums/ActionOrder.hpp //////////////////// + +///////////////////////// START OF src/Types/Move.hpp ////////////////////////// + +namespace pokesim::types { +using pp = pokesim::internal::unsignedIntType; +using basePower = pokesim::internal::unsignedIntType; +using power = pokesim::internal::unsignedIntType; +using baseAccuracy = pokesim::internal::unsignedIntType; +using moveHits = pokesim::internal::unsignedIntType; + +using priority = pokesim::internal::signedIntType; +using fractionalPriority = bool; +} // namespace pokesim::types + +////////////////////////// END OF src/Types/Move.hpp /////////////////////////// + +/////////////////// START OF src/Components/ActionQueue.hpp //////////////////// + +namespace pokesim { +struct ActionQueueItem { + // Order of the types of actions (lower first). + ActionOrder order = ActionOrder::NONE; + // Priority of the action (higher first). + types::priority priority = Constants::MovePriority::DEFAULT; + // Whether negative fractional priority is active for the action (false first). + types::fractionalPriority fractionalPriority = false; + // Speed of Pokemon using move (higher first if priority tie). + types::stat speed = Constants::PokemonEffectiveStat::DEFAULT; + + // The decision that created this action queue item. + types::slotDecision decision{}; + + constexpr bool operator==(const ActionQueueItem& other) const { + return isSameSpeed(other) && decision == other.decision; + } + + constexpr bool isSameSpeed(const ActionQueueItem& other) const { + return order == other.order && priority == other.priority && fractionalPriority == other.fractionalPriority && + speed == other.speed; + } + + constexpr bool isFasterThan(const ActionQueueItem& other) const { + if (order != other.order) { + return order < other.order; + } + + if (priority != other.priority) { + return priority > other.priority; + } + + if (fractionalPriority != other.fractionalPriority) { + return !fractionalPriority; + } + + if (speed != other.speed) { + return speed > other.speed; + } + + return false; + } +}; + +struct ActionQueue { + internal::maxSizedVector val{}; +}; +} // namespace pokesim + +//////////////////// END OF src/Components/ActionQueue.hpp ///////////////////// + ////////////////// START OF src/Types/Enums/AddedTargets.hpp /////////////////// namespace pokesim { @@ -18303,10 +18583,6 @@ struct CurrentAction { types::entity val{}; }; -struct NextAction { - types::entity val{}; -}; - struct CurrentActionTargets { types::targets val{}; }; @@ -18335,10 +18611,6 @@ struct CurrentActionMovesAsTarget { types::entityVector val{}; }; -struct CurrentActionMoveSlot { - types::entity val{}; -}; - struct CurrentEffectSource { types::entity val{}; }; @@ -18396,21 +18668,6 @@ using Move = pokesim::tags::CurrentActionMove; /////////////// END OF src/Components/AnalyzeEffect/Aliases.hpp //////////////// -////////////////////// START OF src/Types/Enums/Move.hpp /////////////////////// - -namespace pokesim::dex { -// Pokemon move name -enum class Move : std::uint16_t { - // clang-format off - NO_MOVE = 0U, ONE_MILLION_VOLT_THUNDERBOLT, ABSORB, ACCELEROCK, ACID, ACID_ARMOR, ACID_DOWNPOUR, ACID_SPRAY, ACROBATICS, ACUPRESSURE, AERIAL_ACE, AEROBLAST, AFTER_YOU, AGILITY, AIR_CUTTER, AIR_SLASH, ALL_OUT_PUMMELING, ALLY_SWITCH, AMNESIA, ANCHOR_SHOT, ANCIENT_POWER, APPLE_ACID, AQUA_CUTTER, AQUA_JET, AQUA_RING, AQUA_STEP, AQUA_TAIL, ARMOR_CANNON, ARM_THRUST, AROMATHERAPY, AROMATIC_MIST, ASSIST, ASSURANCE, ASTONISH, ASTRAL_BARRAGE, ATTACK_ORDER, ATTRACT, AURA_SPHERE, AURA_WHEEL, AURORA_BEAM, AURORA_VEIL, AUTOTOMIZE, AVALANCHE, AXE_KICK, BABY_DOLL_EYES, BADDY_BAD, BANEFUL_BUNKER, BARB_BARRAGE, BARRAGE, BARRIER, BATON_PASS, BEAK_BLAST, BEAT_UP, BEHEMOTH_BASH, BEHEMOTH_BLADE, BELCH, BELLY_DRUM, BESTOW, BIDE, BIND, BITE, BITTER_BLADE, BITTER_MALICE, BLACK_HOLE_ECLIPSE, BLAST_BURN, BLAZE_KICK, BLAZING_TORQUE, BLEAKWIND_STORM, BLIZZARD, BLOCK, BLOOM_DOOM, BLUE_FLARE, BODY_PRESS, BODY_SLAM, BOLT_BEAK, BOLT_STRIKE, BONE_CLUB, BONEMERANG, BONE_RUSH, BOOMBURST, BOUNCE, BOUNCY_BUBBLE, BRANCH_POKE, BRAVE_BIRD, BREAKING_SWIPE, BREAKNECK_BLITZ, BRICK_BREAK, BRINE, BRUTAL_SWING, BUBBLE, BUBBLE_BEAM, BUG_BITE, BUG_BUZZ, BULK_UP, BULLDOZE, BULLET_PUNCH, BULLET_SEED, BURNING_JEALOUSY, BURN_UP, BUZZY_BUZZ, CALM_MIND, CAMOUFLAGE, CAPTIVATE, CATASTROPIKA, CEASELESS_EDGE, CELEBRATE, CHARGE, CHARGE_BEAM, CHARM, CHATTER, CHILLING_WATER, CHILLY_RECEPTION, CHIP_AWAY, CHLOROBLAST, CIRCLE_THROW, CLAMP, CLANGING_SCALES, CLANGOROUS_SOUL, CLANGOROUS_SOULBLAZE, CLEAR_SMOG, CLOSE_COMBAT, COACHING, COIL, COLLISION_COURSE, COMBAT_TORQUE, COMET_PUNCH, COMEUPPANCE, CONFIDE, CONFUSE_RAY, CONFUSION, CONSTRICT, CONTINENTAL_CRUSH, CONVERSION, CONVERSION_2, COPYCAT, CORE_ENFORCER, CORKSCREW_CRASH, CORROSIVE_GAS, COSMIC_POWER, COTTON_GUARD, COTTON_SPORE, COUNTER, COURT_CHANGE, COVET, CRABHAMMER, CRAFTY_SHIELD, CROSS_CHOP, CROSS_POISON, CRUNCH, CRUSH_CLAW, CRUSH_GRIP, CURSE, CUT, DARKEST_LARIAT, DARK_PULSE, DARK_VOID, DAZZLING_GLEAM, DECORATE, DEFEND_ORDER, DEFENSE_CURL, DEFOG, DESTINY_BOND, DETECT, DEVASTATING_DRAKE, DIAMOND_STORM, DIG, DISABLE, DISARMING_VOICE, DISCHARGE, DIRE_CLAW, DIVE, DIZZY_PUNCH, DOODLE, DOOM_DESIRE, DOUBLE_EDGE, DOUBLE_HIT, DOUBLE_IRON_BASH, DOUBLE_KICK, DOUBLE_SHOCK, DOUBLE_SLAP, DOUBLE_TEAM, DRACO_METEOR, DRAGON_ASCENT, DRAGON_BREATH, DRAGON_CLAW, DRAGON_DANCE, DRAGON_DARTS, DRAGON_ENERGY, DRAGON_HAMMER, DRAGON_PULSE, DRAGON_RAGE, DRAGON_RUSH, DRAGON_TAIL, DRAINING_KISS, DRAIN_PUNCH, DREAM_EATER, DRILL_PECK, DRILL_RUN, DRUM_BEATING, DUAL_CHOP, DUAL_WINGBEAT, DYNAMAX_CANNON, DYNAMIC_PUNCH, EARTH_POWER, EARTHQUAKE, ECHOED_VOICE, EERIE_IMPULSE, EERIE_SPELL, EGG_BOMB, ELECTRIC_TERRAIN, ELECTRIFY, ELECTRO_BALL, ELECTRO_DRIFT, ELECTROWEB, EMBARGO, EMBER, ENCORE, ENDEAVOR, ENDURE, ENERGY_BALL, ENTRAINMENT, ERUPTION, ESPER_WING, ETERNABEAM, EXPANDING_FORCE, EXPLOSION, EXTRASENSORY, EXTREME_EVOBOOST, EXTREME_SPEED, FACADE, FAIRY_LOCK, FAIRY_WIND, FAKE_OUT, FAKE_TEARS, FALSE_SURRENDER, FALSE_SWIPE, FEATHER_DANCE, FEINT, FEINT_ATTACK, FELL_STINGER, FIERY_DANCE, FIERY_WRATH, FILLET_AWAY, FINAL_GAMBIT, FIRE_BLAST, FIRE_FANG, FIRE_LASH, FIRE_PLEDGE, FIRE_PUNCH, FIRE_SPIN, FIRST_IMPRESSION, FISHIOUS_REND, FISSURE, FLAIL, FLAME_BURST, FLAME_CHARGE, FLAME_WHEEL, FLAMETHROWER, FLARE_BLITZ, FLASH, FLASH_CANNON, FLATTER, FLEUR_CANNON, FLING, FLIP_TURN, FLOATY_FALL, FLORAL_HEALING, FLOWER_SHIELD, FLOWER_TRICK, FLY, FLYING_PRESS, FOCUS_BLAST, FOCUS_ENERGY, FOCUS_PUNCH, FOLLOW_ME, FORCE_PALM, FORESIGHT, FORESTS_CURSE, FOUL_PLAY, FREEZE_DRY, FREEZE_SHOCK, FREEZING_GLARE, FREEZY_FROST, FRENZY_PLANT, FROST_BREATH, FRUSTRATION, FURY_ATTACK, FURY_CUTTER, FURY_SWIPES, FUSION_BOLT, FUSION_FLARE, FUTURE_SIGHT, GASTRO_ACID, GEAR_GRIND, GEAR_UP, GENESIS_SUPERNOVA, GEOMANCY, GIGA_DRAIN, GIGA_IMPACT, GIGATON_HAMMER, GIGAVOLT_HAVOC, GLACIAL_LANCE, GLACIATE, GLAIVE_RUSH, GLARE, GLITZY_GLOW, G_MAX_BEFUDDLE, G_MAX_CANNONADE, G_MAX_CENTIFERNO, G_MAX_CHI_STRIKE, G_MAX_CUDDLE, G_MAX_DEPLETION, G_MAX_DRUM_SOLO, G_MAX_FINALE, G_MAX_FIREBALL, G_MAX_FOAM_BURST, G_MAX_GOLD_RUSH, G_MAX_GRAVITAS, G_MAX_HYDROSNIPE, G_MAX_MALODOR, G_MAX_MELTDOWN, G_MAX_ONE_BLOW, G_MAX_RAPID_FLOW, G_MAX_REPLENISH, G_MAX_RESONANCE, G_MAX_SANDBLAST, G_MAX_SMITE, G_MAX_SNOOZE, G_MAX_STEELSURGE, G_MAX_STONESURGE, G_MAX_STUN_SHOCK, G_MAX_SWEETNESS, G_MAX_TARTNESS, G_MAX_TERROR, G_MAX_VINE_LASH, G_MAX_VOLCALITH, G_MAX_VOLT_CRASH, G_MAX_WILDFIRE, G_MAX_WIND_RAGE, GRASS_KNOT, GRASS_PLEDGE, GRASS_WHISTLE, GRASSY_GLIDE, GRASSY_TERRAIN, GRAV_APPLE, GRAVITY, GROWL, GROWTH, GRUDGE, GUARDIAN_OF_ALOLA, GUARD_SPLIT, GUARD_SWAP, GUILLOTINE, GUNK_SHOT, GUST, GYRO_BALL, HAIL, HAMMER_ARM, HAPPY_HOUR, HARDEN, HAZE, HEADBUTT, HEAD_CHARGE, HEADLONG_RUSH, HEAD_SMASH, HEAL_BELL, HEAL_BLOCK, HEALING_WISH, HEAL_ORDER, HEAL_PULSE, HEART_STAMP, HEART_SWAP, HEAT_CRASH, HEAT_WAVE, HEAVY_SLAM, HELPING_HAND, HEX, HIDDEN_POWER, HIDDEN_POWER_BUG, HIDDEN_POWER_DARK, HIDDEN_POWER_DRAGON, HIDDEN_POWER_ELECTRIC, HIDDEN_POWER_FIGHTING, HIDDEN_POWER_FIRE, HIDDEN_POWER_FLYING, HIDDEN_POWER_GHOST, HIDDEN_POWER_GRASS, HIDDEN_POWER_GROUND, HIDDEN_POWER_ICE, HIDDEN_POWER_POISON, HIDDEN_POWER_PSYCHIC, HIDDEN_POWER_ROCK, HIDDEN_POWER_STEEL, HIDDEN_POWER_WATER, HIGH_HORSEPOWER, HIGH_JUMP_KICK, HOLD_BACK, HOLD_HANDS, HONE_CLAWS, HORN_ATTACK, HORN_DRILL, HORN_LEECH, HOWL, HURRICANE, HYDRO_CANNON, HYDRO_PUMP, HYDRO_STEAM, HYDRO_VORTEX, HYPER_BEAM, HYPER_DRILL, HYPER_FANG, HYPERSPACE_FURY, HYPERSPACE_HOLE, HYPER_VOICE, HYPNOSIS, ICE_BALL, ICE_BEAM, ICE_BURN, ICE_FANG, ICE_HAMMER, ICE_PUNCH, ICE_SHARD, ICE_SPINNER, ICICLE_CRASH, ICICLE_SPEAR, ICY_WIND, IMPRISON, INCINERATE, INFERNAL_PARADE, INFERNO, INFERNO_OVERDRIVE, INFESTATION, INGRAIN, INSTRUCT, ION_DELUGE, IRON_DEFENSE, IRON_HEAD, IRON_TAIL, JAW_LOCK, JET_PUNCH, JUDGMENT, JUMP_KICK, JUNGLE_HEALING, KARATE_CHOP, KINESIS, KINGS_SHIELD, KNOCK_OFF, KOWTOW_CLEAVE, LANDS_WRATH, LASER_FOCUS, LASH_OUT, LAST_RESORT, LAST_RESPECTS, LAVA_PLUME, LEAFAGE, LEAF_BLADE, LEAF_STORM, LEAF_TORNADO, LEECH_LIFE, LEECH_SEED, LEER, LETS_SNUGGLE_FOREVER, LICK, LIFE_DEW, LIGHT_OF_RUIN, LIGHT_SCREEN, LIGHT_THAT_BURNS_THE_SKY, LIQUIDATION, LOCK_ON, LOVELY_KISS, LOW_KICK, LOW_SWEEP, LUCKY_CHANT, LUMINA_CRASH, LUNAR_BLESSING, LUNAR_DANCE, LUNGE, LUSTER_PURGE, MACH_PUNCH, MAGICAL_LEAF, MAGICAL_TORQUE, MAGIC_COAT, MAGIC_POWDER, MAGIC_ROOM, MAGMA_STORM, MAGNET_BOMB, MAGNETIC_FLUX, MAGNET_RISE, MAGNITUDE, MAKE_IT_RAIN, MALICIOUS_MOONSAULT, MAT_BLOCK, MAX_AIRSTREAM, MAX_DARKNESS, MAX_FLARE, MAX_FLUTTERBY, MAX_GEYSER, MAX_GUARD, MAX_HAILSTORM, MAX_KNUCKLE, MAX_LIGHTNING, MAX_MINDSTORM, MAX_OOZE, MAX_OVERGROWTH, MAX_PHANTASM, MAX_QUAKE, MAX_ROCKFALL, MAX_STARFALL, MAX_STEELSPIKE, MAX_STRIKE, MAX_WYRMWIND, MEAN_LOOK, MEDITATE, ME_FIRST, MEGA_DRAIN, MEGAHORN, MEGA_KICK, MEGA_PUNCH, MEMENTO, MENACING_MOONRAZE_MAELSTROM, METAL_BURST, METAL_CLAW, METAL_SOUND, METEOR_ASSAULT, METEOR_BEAM, METEOR_MASH, METRONOME, MILK_DRINK, MIMIC, MIND_BLOWN, MIND_READER, MINIMIZE, MIRACLE_EYE, MIRROR_COAT, MIRROR_MOVE, MIRROR_SHOT, MIST, MIST_BALL, MISTY_EXPLOSION, MISTY_TERRAIN, MOONBLAST, MOONGEIST_BEAM, MOONLIGHT, MORNING_SUN, MORTAL_SPIN, MOUNTAIN_GALE, MUD_BOMB, MUD_SHOT, MUD_SLAP, MUD_SPORT, MUDDY_WATER, MULTI_ATTACK, MYSTICAL_FIRE, MYSTICAL_POWER, NASTY_PLOT, NATURAL_GIFT, NATURE_POWER, NATURES_MADNESS, NEEDLE_ARM, NEVER_ENDING_NIGHTMARE, NIGHT_DAZE, NIGHTMARE, NIGHT_SHADE, NIGHT_SLASH, NOBLE_ROAR, NO_RETREAT, NOXIOUS_TORQUE, NUZZLE, OBLIVION_WING, OBSTRUCT, OCEANIC_OPERETTA, OCTAZOOKA, OCTOLOCK, ODOR_SLEUTH, OMINOUS_WIND, ORDER_UP, ORIGIN_PULSE, OUTRAGE, OVERDRIVE, OVERHEAT, PAIN_SPLIT, PARABOLIC_CHARGE, PARTING_SHOT, PAYBACK, PAY_DAY, PECK, PERISH_SONG, PETAL_BLIZZARD, PETAL_DANCE, PHANTOM_FORCE, PHOTON_GEYSER, PIKA_PAPOW, PIN_MISSILE, PLASMA_FISTS, PLAY_NICE, PLAY_ROUGH, PLUCK, POISON_FANG, POISON_GAS, POISON_JAB, POISON_POWDER, POISON_STING, POISON_TAIL, POLLEN_PUFF, POLTERGEIST, POPULATION_BOMB, POUNCE, POUND, POWDER, POWDER_SNOW, POWER_GEM, POWER_SHIFT, POWER_SPLIT, POWER_SWAP, POWER_TRICK, POWER_TRIP, POWER_UP_PUNCH, POWER_WHIP, PRECIPICE_BLADES, PRESENT, PRISMATIC_LASER, PROTECT, PSYBEAM, PSYBLADE, PSYCH_UP, PSYCHIC, PSYCHIC_FANGS, PSYCHIC_TERRAIN, PSYCHO_BOOST, PSYCHO_CUT, PSYCHO_SHIFT, PSYSHIELD_BASH, PSYSHOCK, PSYSTRIKE, PSYWAVE, PULVERIZING_PANCAKE, PUNISHMENT, PURIFY, PURSUIT, PYRO_BALL, QUASH, QUICK_ATTACK, QUICK_GUARD, QUIVER_DANCE, RAGE, RAGE_FIST, RAGE_POWDER, RAGING_BULL, RAGING_FURY, RAIN_DANCE, RAPID_SPIN, RAZOR_LEAF, RAZOR_SHELL, RAZOR_WIND, RECOVER, RECYCLE, REFLECT, REFLECT_TYPE, REFRESH, RELIC_SONG, REST, RETALIATE, RETURN, REVELATION_DANCE, REVENGE, REVERSAL, REVIVAL_BLESSING, RISING_VOLTAGE, ROAR, ROAR_OF_TIME, ROCK_BLAST, ROCK_CLIMB, ROCK_POLISH, ROCK_SLIDE, ROCK_SMASH, ROCK_THROW, ROCK_TOMB, ROCK_WRECKER, ROLE_PLAY, ROLLING_KICK, ROLLOUT, ROOST, ROTOTILLER, ROUND, RUINATION, SACRED_FIRE, SACRED_SWORD, SAFEGUARD, SALT_CURE, SAND_ATTACK, SANDSEAR_STORM, SANDSTORM, SAND_TOMB, SAPPY_SEED, SAVAGE_SPIN_OUT, SCALD, SCALE_SHOT, SCARY_FACE, SCORCHING_SANDS, SCRATCH, SCREECH, SEARING_SHOT, SEARING_SUNRAZE_SMASH, SECRET_POWER, SECRET_SWORD, SEED_BOMB, SEED_FLARE, SEISMIC_TOSS, SELF_DESTRUCT, SHADOW_BALL, SHADOW_BONE, SHADOW_CLAW, SHADOW_FORCE, SHADOW_PUNCH, SHADOW_SNEAK, SHARPEN, SHATTERED_PSYCHE, SHED_TAIL, SHEER_COLD, SHELL_SIDE_ARM, SHELL_SMASH, SHELL_TRAP, SHELTER, SHIFT_GEAR, SHOCK_WAVE, SHORE_UP, SIGNAL_BEAM, SILK_TRAP, SILVER_WIND, SIMPLE_BEAM, SING_MOVE /*Many math libraries define SING as a macro*/, SINISTER_ARROW_RAID, SIZZLY_SLIDE, SKETCH, SKILL_SWAP, SKITTER_SMACK, SKULL_BASH, SKY_ATTACK, SKY_DROP, SKY_UPPERCUT, SLACK_OFF, SLAM, SLASH, SLEEP_POWDER, SLEEP_TALK, SLUDGE, SLUDGE_BOMB, SLUDGE_WAVE, SMACK_DOWN, SMART_STRIKE, SMELLING_SALTS, SMOG, SMOKESCREEN, SNAP_TRAP, SNARL, SNATCH, SNIPE_SHOT, SNORE, SNOWSCAPE, SOAK, SOFT_BOILED, SOLAR_BEAM, SOLAR_BLADE, SONIC_BOOM, SOUL_STEALING_7_STAR_STRIKE, SPACIAL_REND, SPARK, SPARKLING_ARIA, SPARKLY_SWIRL, SPECTRAL_THIEF, SPEED_SWAP, SPICY_EXTRACT, SPIDER_WEB, SPIKE_CANNON, SPIKES, SPIKY_SHIELD, SPIN_OUT, SPIRIT_BREAK, SPIRIT_SHACKLE, SPIT_UP, SPITE, SPLASH, SPLINTERED_STORMSHARDS, SPLISHY_SPLASH, SPORE, SPOTLIGHT, SPRINGTIDE_STORM, STEALTH_ROCK, STEAM_ERUPTION, STEAMROLLER, STEEL_BEAM, STEEL_ROLLER, STEEL_WING, STICKY_WEB, STOCKPILE, STOKED_SPARKSURFER, STOMP, STOMPING_TANTRUM, STONE_AXE, STONE_EDGE, STORED_POWER, STORM_THROW, STRANGE_STEAM, STRENGTH, STRENGTH_SAP, STRING_SHOT, STRUGGLE, STRUGGLE_BUG, STUFF_CHEEKS, STUN_SPORE, SUBMISSION, SUBSTITUTE, SUBZERO_SLAMMER, SUCKER_PUNCH, SUNNY_DAY, SUNSTEEL_STRIKE, SUPER_FANG, SUPERPOWER, SUPERSONIC, SUPERSONIC_SKYSTRIKE, SURF, SURGING_STRIKES, SWAGGER, SWALLOW, SWEET_KISS, SWEET_SCENT, SWIFT, SWITCHEROO, SWORDS_DANCE, SYNCHRONOISE, SYNTHESIS, TACKLE, TAIL_GLOW, TAIL_SLAP, TAIL_WHIP, TAILWIND, TAKE_DOWN, TAKE_HEART, TAR_SHOT, TAUNT, TEARFUL_LOOK, TEATIME, TECHNO_BLAST, TECTONIC_RAGE, TEETER_DANCE, TELEKINESIS, TELEPORT, TERA_BLAST, TERRAIN_PULSE, THIEF, THOUSAND_ARROWS, THOUSAND_WAVES, THRASH, THROAT_CHOP, THUNDER, THUNDERBOLT, THUNDER_CAGE, THUNDER_FANG, THUNDEROUS_KICK, THUNDER_PUNCH, THUNDER_SHOCK, THUNDER_WAVE, TICKLE, TIDY_UP, TOPSY_TURVY, TORCH_SONG, TORMENT, TOXIC, TOXIC_SPIKES, TOXIC_THREAD, TRAILBLAZE, TRANSFORM, TRI_ATTACK, TRICK, TRICK_OR_TREAT, TRICK_ROOM, TRIPLE_ARROWS, TRIPLE_AXEL, TRIPLE_DIVE, TRIPLE_KICK, TROP_KICK, TRUMP_CARD, TWIN_BEAM, TWINEEDLE, TWINKLE_TACKLE, TWISTER, U_TURN, UPROAR, VACUUM_WAVE, V_CREATE, VEEVEE_VOLLEY, VENOM_DRENCH, VENOSHOCK, VICTORY_DANCE, VINE_WHIP, VISE_GRIP, VITAL_THROW, VOLT_SWITCH, VOLT_TACKLE, WAKE_UP_SLAP, WATERFALL, WATER_GUN, WATER_PLEDGE, WATER_PULSE, WATER_SHURIKEN, WATER_SPORT, WATER_SPOUT, WAVE_CRASH, WEATHER_BALL, WHIRLPOOL, WHIRLWIND, WICKED_BLOW, WICKED_TORQUE, WIDE_GUARD, WILDBOLT_STORM, WILD_CHARGE, WILL_O_WISP, WING_ATTACK, WISH, WITHDRAW, WONDER_ROOM, WOOD_HAMMER, WORK_UP, WORRY_SEED, WRAP, WRING_OUT, X_SCISSOR, YAWN, ZAP_CANNON, ZEN_HEADBUTT, ZING_ZAP, ZIPPY_ZAP, MOVE_TOTAL, - // clang-format on -}; - -static constexpr std::uint16_t TOTAL_MOVE_COUNT = (std::uint16_t)Move::MOVE_TOTAL - 1U; -} // namespace pokesim::dex - -/////////////////////// END OF src/Types/Enums/Move.hpp //////////////////////// - //////// START OF src/Components/AnalyzeEffect/AnalyzeEffectInputs.hpp ///////// namespace pokesim::analyze_effect { @@ -18464,49 +18721,12 @@ struct InvertFinalAnswer {}; ///////// END OF src/Components/AnalyzeEffect/AnalyzeEffectInputs.hpp ////////// -////////////////////// START OF src/Utilities/Variant.hpp ////////////////////// +//////////////////////// START OF src/Types/Effect.hpp ///////////////////////// -#include - -namespace pokesim::internal { -template -class variant : public std::variant { - public: - using std::variant::variant; - - template - variant& operator=(const T& rhs) { - std::variant::operator=(rhs); - return *this; - } - - bool empty() const { return holds(); } - - template - bool holds() const { - return std::holds_alternative(*this); - } - - template - auto& get() const { - return std::get(*this); - } - - template - auto& get() { - return std::get(*this); - } -}; -} // namespace pokesim::internal - -/////////////////////// END OF src/Utilities/Variant.hpp /////////////////////// - -//////////////////////// START OF src/Types/Effect.hpp ///////////////////////// - -namespace pokesim::types { -using effectEnum = pokesim::internal::variant< - std::monostate, dex::PseudoWeather, dex::SideCondition, dex::Status, dex::Terrain, dex::Volatile, dex::Weather>; -} // namespace pokesim::types +namespace pokesim::types { +using effectEnum = pokesim::internal::variant< + std::monostate, dex::PseudoWeather, dex::SideCondition, dex::Status, dex::Terrain, dex::Volatile, dex::Weather>; +} // namespace pokesim::types ///////////////////////// END OF src/Types/Effect.hpp ////////////////////////// @@ -18535,21 +18755,6 @@ struct BaseEffectChance { ////////////////// END OF src/Components/BaseEffectChance.hpp ////////////////// -///////////////////////// START OF src/Types/Move.hpp ////////////////////////// - -namespace pokesim::types { -using pp = pokesim::internal::unsignedIntType; -using basePower = pokesim::internal::unsignedIntType; -using power = pokesim::internal::unsignedIntType; -using baseAccuracy = pokesim::internal::unsignedIntType; -using moveHits = pokesim::internal::unsignedIntType; - -using priority = pokesim::internal::signedIntType; -using fractionalPriority = bool; -} // namespace pokesim::types - -////////////////////////// END OF src/Types/Move.hpp /////////////////////////// - //////////////////// START OF src/Components/BasePower.hpp ///////////////////// namespace pokesim { @@ -18690,6 +18895,16 @@ struct IgnoresDefendingBoost {}; ///////// END OF src/Components/CalcDamage/TemporaryMoveProperties.hpp ///////// +//////////////////// START OF src/Components/ChoiceLock.hpp //////////////////// + +namespace pokesim { +struct ChoiceLock { + types::moveSlotIndex val{}; +}; +} // namespace pokesim + +///////////////////// END OF src/Components/ChoiceLock.hpp ///////////////////// + ///////////////// START OF src/Components/CloneFromCloneTo.hpp ///////////////// namespace pokesim { @@ -18705,97 +18920,26 @@ struct CloneTo { ////////////////// END OF src/Components/CloneFromCloneTo.hpp ////////////////// -////////////////////// START OF src/Types/Enums/Item.hpp /////////////////////// - -namespace pokesim::dex { -// Name of items in Pokemon games -enum class Item : std::uint16_t { - // clang-format off - NO_ITEM = 0U, ABILITY_CAPSULE, ABILITY_PATCH, ABILITY_SHIELD, ABOMASITE, ABRA_CANDY, ABSOLITE, ABSORB_BULB, ACRO_BIKE, ADAMANT_CRYSTAL, ADAMANT_MINT, ADAMANT_ORB, ADRENALINE_ORB, ADVENTURE_GUIDE, AERODACTYLITE, AERODACTYL_CANDY, AGGRONITE, AGUAV_BERRY, AIR_BALLOON, AIR_MAIL, ALAKAZITE, ALORAICHIUM_Z, ALTARIANITE, AMAZE_MULCH, AMPHAROSITE, AMULET_COIN, ANTIDOTE, APICOT_BERRY, APRICORN, APRICORN_BOX, AQUA_SUIT, ARMOR_FOSSIL, ARMOR_PASS, ARMORITE_ORE, ARTICUNO_CANDY, ASPEAR_BERRY, ASSAULT_VEST, AUDINITE, AURORATICKET, AUSPICIOUS_ARMOR, AUTOGRAPH, AUX_EVASION, AUX_GUARD, AUX_POWER, AUX_POWERGUARD, AWAKENING, AZELFS_FANG, AZURE_FLUTE, BABIRI_BERRY, BALL_OF_MUD, BALM_MUSHROOM, BAND_AUTOGRAPH, BANETTITE, BASCULEGION_FOOD, BASEMENT_KEY, BEACH_GLASS, BEAD_MAIL, BEAN_CAKE, BEAST_BALL, BEEDRILLITE, BELLSPROUT_CANDY, BELUE_BERRY, BERRY, BERRY_JUICE, BERRY_POTS, BERRY_POUCH, BERRY_SWEET, BERSERK_GENE, BICYCLE, BIG_BAMBOO_SHOOT, BIG_MALASADA, BIG_MUSHROOM, BIG_NUGGET, BIG_PEARL, BIG_ROOT, BIKE_VOUCHER, BINDING_BAND, BITTER_BERRY, BLACK_APRICORN, BLACK_AUGURITE, BLACK_BELT, BLACK_FLUTE, BLACK_GLASSES, BLACK_MANE_HAIR, BLACK_SLUDGE, BLACK_TUMBLESTONE, BLANK_PLATE, BLASTOISINITE, BLAZIKENITE, BLOOM_MAIL, BLUE_APRICORN, BLUE_CARD, BLUE_FLUTE, BLUE_ORB, BLUE_PETAL, BLUE_SCARF, BLUE_SHARD, BLUESKY_MAIL, BLU_ID_BADGE, BLUK_BERRY, BLUNDER_POLICY, BOLD_MINT, BONSLY_CARD, BONSLY_PHOTO, BOOST_MULCH, BOOSTER_ENERGY, BOTTLE_CAP, BRAVE_MINT, BRICK_MAIL, BRICK_PIECE, BRIDGE_MAIL_D, BRIDGE_MAIL_M, BRIDGE_MAIL_S, BRIDGE_MAIL_T, BRIDGE_MAIL_V, BRIGHT_POWDER, BUBBLE_MAIL, BUG_GEM, BUG_MEMORY, BUG_TERA_SHARD, BUGINIUM_Z, BUGWORT, BULBASAUR_CANDY, BURN_DRIVE, BURN_HEAL, BURNT_BERRY, CAKE_LURE_BASE, CALCIUM, CALM_MINT, CAMERUPTITE, CAMPING_GEAR, CANDY_TRUFFLE, CARBOS, CARD_KEY, CAREFUL_MINT, CARROT_SEEDS, CASTELIACONE, CASTER_FERN, CATCHING_CHARM, CATERPIE_CANDY, CELESTICA_FLUTE, CELL_BATTERY, CHALKY_STONE, CHANSEY_CANDY, CHARCOAL, CHARIZARDITE_X, CHARIZARDITE_Y, CHARMANDER_CANDY, CHARTI_BERRY, CHERI_BERRY, CHERISH_BALL, CHESTO_BERRY, CHILAN_BERRY, CHILL_DRIVE, CHIPPED_POT, CHOICE_BAND, CHOICE_DUMPLING, CHOICE_SCARF, CHOICE_SPECS, CHOPLE_BERRY, CLAW_FOSSIL, CLEANSE_TAG, CLEAR_AMULET, CLEAR_BELL, CLEFAIRY_CANDY, CLEVER_FEATHER, CLOVER_SWEET, COBA_BERRY, COIN_CASE, COLBUR_BERRY, COLOGNE_CASE, COLRESS_MACHINE, COMET_SHARD, COMMON_STONE, CONTEST_COSTUME, CONTEST_PASS, CORNN_BERRY, COUPON_1, COUPON_2, COUPON_3, COURAGE_CANDY, COURAGE_CANDY_L, COURAGE_CANDY_XL, COVER_FOSSIL, COVERT_CLOAK, CRACKED_POT, CRAFTING_KIT, CROWN_PASS, CRUNCHY_SALT, CUBONE_CANDY, CRY_ANALYZER, CUSTAP_BERRY, DAMP_MULCH, DAMP_ROCK, DARK_GEM, DARK_MEMORY, DARK_STONE, DARK_TERA_SHARD, DARKINIUM_Z, DATA_CARDS, DATA_ROM, DAWN_STONE, DAZZLING_HONEY, D_DISK, DECIDIUM_Z, DEEP_SEA_SCALE, DEEP_SEA_TOOTH, DESTINY_KNOT, DEVON_PARTS, DEVON_SCOPE, DEVON_SCUBA_GEAR, DIANCITE, DIGGER_DRILL, DIGLETT_CANDY, DIRE_HIT, DIRESHROOM, DISC_CASE, DISCOUNT_COUPON, DISCOVERY_SLATE, DISTORTION_SLATE, DITTO_CANDY, DIVE_BALL, DNA_SAMPLE, DNA_SPLICERS, DODUO_CANDY, DOME_FOSSIL, DOPPEL_BONNETS, DOUSE_DRIVE, DOWN_ST_KEY, DOWSING_MACHINE, DRACO_PLATE, DRAGON_FANG, DRAGON_GEM, DRAGON_MEMORY, DRAGON_SCALE, DRAGON_SKULL, DRAGON_TERA_SHARD, DRAGONIUM_Z, DRASH_BERRY, DRATINI_CANDY, DREAD_PLATE, DREAM_BALL, DREAM_MAIL, DROPPED_ITEM, DROWZEE_CANDY, DS_SOUNDS, DUBIOUS_DISC, DURIN_BERRY, DUSK_BALL, DUSK_STONE, DYNAMAX_BAND, DYNAMAX_CANDY, DYNAMAX_CRYSTALS, DYNITE_ORE, EARTH_PLATE, EEVEE_CANDY, EEVIUM_Z, EGG_TICKET, EGGANT_BERRY, EIN_FILE_C, EIN_FILE_F, EIN_FILE_H, EIN_FILE_P, EIN_FILE_S, EJECT_BUTTON, EJECT_PACK, EKANS_CANDY, ELECTABUZZ_CANDY, ELECTIRIZER, ELECTRIC_GEM, ELECTRIC_MEMORY, ELECTRIC_SEED, ELECTRIC_TERA_SHARD, ELECTRIUM_Z, ELEVATOR_KEY, ELIXIR, ENDORSEMENT, ENERGY_POWDER, ENERGY_ROOT, ENIGMA_BERRY, ENIGMA_STONE, ENIGMATIC_CARD, EON_FLUTE, EON_MAIL, EON_TICKET, ESCAPE_ROPE, ETERNAL_ICE, ETHER, EVERSTONE, EVIOLITE, EXCITE_SCENT, EXEGGCUTE_CANDY, EXP_CANDY_L, EXP_CANDY_M, EXP_CANDY_S, EXP_CANDY_XL, EXP_CANDY_XS, EXP_CHARM, EXP_SHARE, EXPERT_BELT, EXPLORER_KIT, FAB_MAIL, FAIRIUM_Z, FAIRY_GEM, FAIRY_MEMORY, FAIRY_TERA_SHARD, FAME_CHECKER, FARFETCHD_CANDY, FASHION_CASE, FAST_BALL, FAVORED_MAIL, F_DISK, FEATHER_BALL, FESTIVAL_TICKET, FIGHTING_GEM, FIGHTING_MEMORY, FIGHTING_TERA_SHARD, FIGHTINIUM_Z, FIGY_BERRY, FINE_REMEDY, FIRE_GEM, FIRE_MEMORY, FIRE_STONE, FIRE_TERA_SHARD, FIRIUM_Z, FISHING_ROD, FIST_PLATE, FLAME_MAIL, FLAME_ORB, FLAME_PLATE, FLOAT_STONE, FLOWER_MAIL, FLOWER_SWEET, FLUFFY_TAIL, FLYING_GEM, FLYING_MEMORY, FLYING_TERA_SHARD, FLYINIUM_Z, FOCUS_BAND, FOCUS_SASH, FORAGE_BAG, FOREST_BALM, FOSSILIZED_BIRD, FOSSILIZED_DINO, FOSSILIZED_DRAKE, FOSSILIZED_FISH, FRESH_WATER, FRIEND_BALL, FULL_HEAL, FULL_INCENSE, FULL_RESTORE, GALACTIC_KEY, GALARICA_CUFF, GALARICA_TWIG, GALARICA_WREATH, GALLADITE, GANLON_BERRY, GARCHOMPITE, GARDEVOIRITE, GASTLY_CANDY, GB_SOUNDS, GEAR, GENGARITE, GENIUS_FEATHER, GENOME_SLATE, GENTLE_MINT, GEODUDE_CANDY, GHOST_GEM, GHOST_MEMORY, GHOST_TERA_SHARD, GHOSTIUM_Z, GIGATON_BALL, GINEMA_BERRY, GLALITITE, GLITTER_MAIL, GO_GOGGLES, GOD_STONE, GOLD_BERRY, GOLD_BOTTLE_CAP, GOLD_LEAF, GOLD_TEETH, GOLDEEN_CANDY, GONZAPS_KEY, GOLDEN_NANAB_BERRY, GOLDEN_PINAP_BERRY, GOLDEN_RAZZ_BERRY, GOOD_ROD, GOOEY_MULCH, GORGEOUS_BOX, GRACIDEA, GRAIN_CAKE, GRAM_1, GRAM_2, GRAM_3, GRASS_GEM, GRASS_MEMORY, GRASS_TERA_SHARD, GRASSIUM_Z, GRASS_MAIL, GRASSY_SEED, GREAT_BALL, GREEN_APRICORN, GREEN_PETAL, GREEN_SCARF, GREEN_SHARD, GREET_MAIL, GREPA_BERRY, GRIMER_CANDY, GRIP_CLAW, GRIT_DUST, GRIT_GRAVEL, GRIT_PEBBLE, GRIT_ROCK, GRISEOUS_CORE, GRISEOUS_ORB, GRN_ID_BADGE, GROUND_GEM, GROUND_MEMORY, GROUND_TERA_SHARD, GROUNDIUM_Z, GROWLITHE_CANDY, GROWTH_MULCH, GRUBBY_HANKY, GS_BALL, GUARD_SPEC, GUIDEBOOK, GYARADOSITE, HABAN_BERRY, HARBOR_MAIL, HARD_STONE, HASTY_MINT, HEAL_BALL, HEAL_POWDER, HEALTH_CANDY, HEALTH_CANDY_L, HEALTH_CANDY_XL, HEALTH_FEATHER, HEART_MAIL, HEART_SCALE, HEARTY_GRAINS, HEAT_ROCK, HEAVY_BALL, HEAVY_DUTY_BOOTS, HELIX_FOSSIL, HERACRONITE, HI_TECH_EARBUDS, HITMONCHAN_CANDY, HITMONLEE_CANDY, HM01, HM02, HM03, HM04, HM05, HM06, HM07, HM08, HOLO_CASTER, HOMETOWN_MUFFIN, HONDEW_BERRY, HONEY, HONEY_CAKE, HONOR_OF_KALOS, HOPO_BERRY, HORSEA_CANDY, HOUNDOOMINITE, HP_UP, HYPER_POTION, IAPAPA_BERRY, ICE_BERRY, ICE_GEM, ICE_HEAL, ICE_MEMORY, ICE_STONE, ICE_TERA_SHARD, ICEROOT_CARROT, ICICLE_PLATE, ICIUM_Z, ICY_ROCK, ID_CARD, ILIMAS_NORMALIUM_Z, IMPISH_MINT, INCINIUM_Z, INQUIRY_MAIL, INSECT_PLATE, INTRIGUING_STONE, IRON, IRON_BALL, IRON_BARKTONGUE, IRON_CHUNK, IRON_PLATE, JABOCA_BERRY, JADE_ORB, JAIL_KEY, JAW_FOSSIL, JET_BALL, JIGGLYPUFF_CANDY, JOHTO_SLATE, JOLLY_MINT, JOY_SCENT, JUBILIFE_MUFFIN, JYNX_CANDY, KABUTO_CANDY, KANGASKHAN_CANDY, KANGASKHANITE, KANTO_SLATE, KASIB_BERRY, KEBIA_BERRY, KEE_BERRY, KELPSY_BERRY, KEY_STONE, KEY_TO_ROOM_1, KEY_TO_ROOM_2, KEY_TO_ROOM_4, KEY_TO_ROOM_6, KINGS_LEAF, KINGS_ROCK, KOFFING_CANDY, KOFUS_BOOK, KOMMONIUM_Z, KORAIDONS_POKE_BALL, KRABBY_CANDY, KRANE_MEMO_1, KRANE_MEMO_2, KRANE_MEMO_3, KRANE_MEMO_4, KRANE_MEMO_5, KUO_BERRY, LAGGING_TAIL, LANSAT_BERRY, LAPRAS_CANDY, LATIASITE, LATIOSITE, LAVA_COOKIE, LAX_INCENSE, LAX_MINT, L_DISK, LEADEN_BALL, LEADERS_CREST, LEAF_LETTER, LEAF_STONE, LEEK, LEFT_POKE_BALL, LEFTOVERS, LEGEND_PLATE, LEGENDARY_CLUE_1, LEGENDARY_CLUE_2, LEGENDARY_CLUE_3, LEGENDARY_CLUE, LEMONADE, LENS_CASE, LEPPA_BERRY, LETTER, LEVEL_BALL, LIBERTY_PASS, LICKITUNG_CANDY, LIECHI_BERRY, LIFE_ORB, LIFT_KEY, LIGHT_BALL, LIGHT_CLAY, LIGHT_STONE, LIKE_MAIL, LINKING_CORD, LITEBLUEMAIL, LOADED_DICE, LOCK_CAPSULE, LONE_EARRING, LONELY_MINT, LOOKER_TICKET, LOOT_SACK, LOPUNNITE, LOST_ITEM, LOST_SATCHEL, LOVE_BALL, LOVE_SWEET, LOVELY_MAIL, LUCARIONITE, LUCK_INCENSE, LUCKY_EGG, LUCKY_PUNCH, LUM_BERRY, LUMINOUS_MOSS, LUMIOSE_GALETTE, LUNALIUM_Z, LUNAR_FEATHER, LURE, LURE_BALL, LUSTROUS_GLOBE, LUSTROUS_ORB, LUXURY_BALL, LYCANIUM_Z, MACH_BIKE, MACHINE_PART, MACHO_BRACE, MACHOP_CANDY, MAGIKARP_CANDY, MAGMA_EMBLEM, MAGMA_STONE, MAGMA_SUIT, MAGMAR_CANDY, MAGMARIZER, MAGNEMITE_CANDY, MAGNET, MAGO_BERRY, MAGOST_BERRY, MAINGATE_KEY, MAKEUP_BAG, MALICIOUS_ARMOR, MANECTITE, MANKEY_CANDY, MARANGA_BERRY, MARBLE, MARK_CHARM, MARSHADIUM_Z, MARSH_BALM, MASTER_BALL, MAWILITE, MAX_ELIXIR, MAX_ETHER, MAX_LURE, MAX_HONEY, MAX_MUSHROOMS, MAX_POTION, MAX_REPEL, MAX_REVIVE, MAYORS_NOTE, MEADOW_PLATE, MECH_MAIL, MECHANICAL_BOX, MECHANICAL_CABINET, MECHANICAL_CIRCULAR_SAW, MECHANICAL_PINWHEEL, MECHANICAL_TUB, MEDAL_BOX, MEDICHAMITE, MEDICINAL_LEEK, MEGA_BRACELET, MEGA_RING, MELTAN_CANDY, MEMBER_CARD, MENTAL_HERB, MEOWTH_CANDY, MESPRITS_PLUME, METAGROSSITE, METAL_COAT, METAL_POWDER, METEORITE, METEORITE_SHARD, METRONOME, MEW_CANDY, MEWNIUM_Z, MEWTWO_CANDY, MEWTWONITE_X, MEWTWONITE_Y, MICLE_BERRY, MIGHTY_CANDY, MIGHTY_CANDY_L, MIGHTY_CANDY_XL, MILD_MINT, MIMIKIUM_Z, MIND_PLATE, MINT_BERRY, MIRACLEBERRY, MIRACLE_SEED, MIRAGE_MAIL, MIRAIDONS_POKE_BALL, MIROR_RADAR, MIRROR_HERB, MISTY_SEED, MODEST_MINT, MOLTRES_CANDY, MOOMOO_MILK, MOON_BALL, MOON_FLUTE, MOON_SHARD, MOON_STONE, MORPH_MAIL, MOSAIC_MAIL, MOUNTAIN_BALM, MR_MIME_CANDY, MUSCLE_BAND, MUSCLE_FEATHER, MUSHROOM_CAKE, MUSIC_DISC, MUSIC_MAIL, MYSTERIOUS_BALM, MYSTERIOUS_SHARD_S, MYSTERIOUS_SHARD_L, MYSTERYBERRY, MYSTERY_EGG, MYSTIC_WATER, MYSTICTICKET, NAIVE_MINT, NANAB_BERRY, NAUGHTY_MINT, NEST_BALL, NET_BALL, NEVER_MELT_ICE, NIDORAN_MALE_CANDY, NIDORAN_FEMALE_CANDY, NINIKU_BERRY, N_LUNARIZER, NOMEL_BERRY, NORMAL_BOX, NORMAL_GEM, NORMAL_TERA_SHARD, NORMALIUM_Z, N_SOLARIZER, NUGGET, NUTPEA_BERRY, OAKS_LETTER, OAKS_PARCEL, OCCA_BERRY, OCEANIC_SLATE, ODD_INCENSE, ODD_KEYSTONE, ODDISH_CANDY, OLD_AMBER, OLD_CHARM, OLD_GATEAU, OLD_JOURNAL, OLD_LETTER, OLD_ROD, OLD_SEA_MAP, OLD_VERSES, OMANYTE_CANDY, ONIX_CANDY, ORAN_BERRY, ORANGE_MAIL, ORANGE_PETAL, ORIGIN_BALL, ORIGIN_ORE, OVAL_CHARM, OVAL_STONE, PAIR_OF_TICKETS, PAL_PAD, PAMTRE_BERRY, PARALYZE_HEAL, PARAS_CANDY, PARCEL, PARK_BALL, PASS, PASS_ORB, PASSHO_BERRY, PAYAPA_BERRY, PEARL, PEARL_STRING, PEAT_BLOCK, PECHA_BERRY, PEP_UP_PLANT, PERMIT, PERSIM_BERRY, PETAYA_BERRY, PEWTER_CRUNCHIES, PICNIC_SET, PIDGEOTITE, PIDGEY_CANDY, PIKACHU_CANDY, PIKANIUM_Z, PIKASHUNIUM_Z, PINAP_BERRY, PINK_APRICORN, PINK_BOW, PINK_NECTAR, PINK_PETAL, PINK_SCARF, PINSIR_CANDY, PINSIRITE, PIXIE_PLATE, PLASMA_CARD, PLUME_FOSSIL, PLUMP_BEANS, POFFIN_CASE, POINT_CARD, POISON_BARB, POISON_GEM, POISON_MEMORY, POISON_TERA_SHARD, POISONIUM_Z, POKE_BALL, POKE_DOLL, POKE_FLUTE, POKE_RADAR, POKE_SNACK, POKE_TOY, POKEBLOCK_CASE, POKEBLOCK_KIT, POKEDEX, POKEMON_BOX_LINK, POKESHI_DOLL, POLKADOT_BOW, POLISHED_MUD_BALL, POLIWAG_CANDY, POMEG_BERRY, PONYTA_CANDY, POP_POD, PORTRAITMAIL, PORYGON_CANDY, POTION, POWER_ANKLET, POWER_BAND, POWER_BELT, POWER_BRACER, POWER_HERB, POWER_LENS, POWER_PLANT_PASS, POWER_WEIGHT, POWERUP_PART, POWDER_JAR, PP_MAX, PP_UP, PREMIER_BALL, PRETTY_FEATHER, PRIMARIUM_Z, PRISM_SCALE, PRISON_BOTTLE, PROFS_LETTER, PROFESSORS_MASK, PROP_CASE, PROTECTIVE_PADS, PROTECTOR, PROTEIN, PRZCUREBERRY, PSNCUREBERRY, PSYCHIC_GEM, PSYCHIC_MEMORY, PSYCHIC_SEED, PSYCHIC_TERA_SHARD, PSYCHIUM_Z, PSYDUCK_CANDY, PUMKIN_BERRY, PUNCHING_GLOVE, PURE_INCENSE, PURPLE_NECTAR, PURPLE_PETAL, QUALOT_BERRY, QUICK_BALL, QUICK_CANDY, QUICK_CANDY_L, QUICK_CANDY_XL, QUICK_CLAW, QUICK_POWDER, QUIET_MINT, RABUTA_BERRY, RADIANT_PETAL, RAGECANDYBAR, RAINBOW_FLOWER, RAINBOW_PASS, RAINBOW_SLATE, RAINBOW_WING, RARE_BONE, RARE_CANDY, RASH_MINT, RATTATA_CANDY, RAWST_BERRY, RAZOR_CLAW, RAZOR_FANG, RAZZ_BERRY, R_DISK, REAPER_CLOTH, RECIPES, RED_APRICORN, RED_CARD, RED_CHAIN, RED_FLUTE, RED_ID_BADGE, RED_NECTAR, RED_ORB, RED_PETAL, RED_SCALE, RED_SCARF, RED_SHARD, REINS_OF_UNITY, RELAXED_MINT, RELIC_BAND, RELIC_COPPER, RELIC_CROWN, RELIC_GOLD, RELIC_SILVER, RELIC_STATUE, RELIC_VASE, REMEDY, REPEAT_BALL, REPEL, REPLY_MAIL, RESIST_FEATHER, RETRO_MAIL, REVEAL_GLASS, REVIVAL_HERB, REVIVE, RHYHORN_CANDY, RIBBON_SWEET, RICH_MULCH, RIDE_PAGER, RINDO_BERRY, RING_TARGET, ROCK_GEM, ROCK_INCENSE, ROCK_MEMORY, ROCK_TERA_SHARD, ROCKIUM_Z, ROCKY_HELMET, ROLLER_SKATES, ROOM_SERVICE, ROOT_FOSSIL, ROSE_INCENSE, ROSELI_BERRY, ROTO_BARGAIN, ROTO_BOOST, ROTO_CATCH, ROTO_ENCOUNTER, ROTO_EXP_POINTS, ROTO_FRIENDSHIP, ROTO_HATCH, ROTO_HP_RESTORE, ROTO_PP_RESTORE, ROTO_PRIZE_MONEY, ROTO_STEALTH, ROTOM_BIKE, ROTOM_CATALOG, ROTOM_PHONE, ROWAP_BERRY, RSVP_MAIL, RUBY, RUNNING_SHOES, RUSTED_SHIELD, RUSTED_SWORD, S_S_TICKET, SABLENITE, SACHET, SACRED_ASH, SAFARI_BALL, SAFETY_GOGGLES, SAIL_FOSSIL, SALAC_BERRY, SALAMENCITE, SALT_CAKE, SAND_RADISH, SANDSHREW_CANDY, SANDWICH, SAPPHIRE, SASSY_MINT, SCANNER, SCARLET_BOOK, SCATTER_BANG, SCEPTILITE, SCIZORITE, SCOPE_LENS, SCROLL_OF_DARKNESS, SCROLL_OF_WATERS, SCYTHER_CANDY, SEA_INCENSE, SEAL_CASE, SECRET_KEY, SECRET_MEDICINE, SEED_OF_MASTERY, SEEL_CANDY, SERIOUS_MINT, SHADOW_MAIL, SHADEROOT_CARROT, SHALOUR_SABLE, SHARP_BEAK, SHARPEDONITE, SHED_SHELL, SHELL_BELL, SHELLDER_CANDY, SHINY_CHARM, SHINY_LEAF, SHINY_STONE, SHOAL_SALT, SHOAL_SHELL, SHOCK_DRIVE, SHUCA_BERRY, SILK_SCARF, SILPH_SCOPE, SILVER_LEAF, SILVER_NANAB_BERRY, SILVER_PINAP_BERRY, SILVER_POWDER, SILVER_RAZZ_BERRY, SILVER_WING, SITRUS_BERRY, SKULL_FOSSIL, SKY_PLATE, SKY_TUMBLESTONE, SLOWBRONITE, SLOWPOKE_CANDY, SLOWPOKE_TAIL, SMALL_BOUQUET, SMALL_TABLET, SMART_CANDY, SMART_CANDY_L, SMART_CANDY_XL, SMOKE_BALL, SMOKE_BOMB, SMOOTH_ROCK, SNORLAX_CANDY, SNORLIUM_Z, SNOWBALL, SNOW_BALM, SNOW_MAIL, SODA_POP, SOFT_SAND, SOLGANIUM_Z, SONIAS_BOOK, SOOT_SACK, SOOTFOOT_ROOT, SOOTHE_BELL, SOUL_DEW, SOUL_SLATE, SPACE_BALM, SPACE_MAIL, SPARKLING_STONE, SPEAROW_CANDY, SPELL_TAG, SPELON_BERRY, SPLASH_PLATE, SPOILED_APRICORN, SPOOKY_PLATE, SPORT_BALL, SPRAYDUCK, SPRINGY_MUSHROOM, SPRINKLOTAD, SQUALL_SLATE, SQUIRT_BOTTLE, SQUIRTLE_CANDY, STABLE_MULCH, STAR_PIECE, STAR_SWEET, STARDUST, STARF_BERRY, STARYU_CANDY, STEALTH_SPRAY, STEEL_GEM, STEEL_MAIL, STEEL_MEMORY, STEEL_TEETH, STEEL_TERA_SHARD, STEELIUM_Z, STEELIXITE, STICKY_BARB, STICKY_GLOB, STONE_PLATE, STORAGE_KEY, STRANGE_BALL, STRANGE_SOUVENIR, STRATOSPHERIC_SLATE, STRAWBERRY_SWEET, STRETCHY_SPRING, STRIB_BERRY, STYLE_CARD, SUBWAY_KEY, SUITE_KEY, SUN_FLUTE, SUN_SHARD, SUN_STONE, SUPER_LURE, SUPER_POTION, SUPER_REPEL, SUPER_ROD, SUPERB_REMEDY, SURFBOARD, SURF_MAIL, SURGE_BADGE, SURPRISE_MULCH, SURVIVAL_CHARM_B, SURVIVAL_CHARM_P, SURVIVAL_CHARM_R, SURVIVAL_CHARM_T, SURVIVAL_CHARM_Y, SWAMPERTITE, SWAP_SNACK, SWEET_APPLE, SWEET_HEART, SWIFT_FEATHER, SWORDCAP, SYSTEM_LEVER, TAMATO_BERRY, TANGA_BERRY, TANGELA_CANDY, TAPUNIUM_Z, TART_APPLE, TAUROS_CANDY, TEA, TEACHY_TV, TECTONIC_SLATE, TEMPTING_CHARM_B, TEMPTING_CHARM_P, TEMPTING_CHARM_R, TEMPTING_CHARM_T, TEMPTING_CHARM_Y, TENTACOOL_CANDY, TERA_ORB, TERRAIN_EXTENDER, TERU_SAMA, THANKS_MAIL, THICK_CLUB, THROAT_SPRAY, THUNDER_STONE, TIDAL_BELL, TIME_BALM, TIME_FLUTE, TIMER_BALL, TIMID_MINT, TINY_BAMBOO_SHOOT, TINY_MUSHROOM, TM01, TM02, TM03, TM04, TM05, TM06, TM07, TM08, TM09, TM10, TM11, TM12, TM13, TM14, TM15, TM16, TM17, TM18, TM19, TM20, TM21, TM22, TM23, TM24, TM25, TM26, TM27, TM28, TM29, TM30, TM31, TM32, TM33, TM34, TM35, TM36, TM37, TM38, TM39, TM40, TM41, TM42, TM43, TM44, TM45, TM46, TM47, TM48, TM49, TM50, TM51, TM52, TM53, TM54, TM55, TM56, TM57, TM58, TM59, TM60, TM61, TM62, TM63, TM64, TM65, TM66, TM67, TM68, TM69, TM70, TM71, TM72, TM73, TM74, TM75, TM76, TM77, TM78, TM79, TM80, TM81, TM82, TM83, TM84, TM85, TM86, TM87, TM88, TM89, TM90, TM91, TM92, TM93, TM94, TM95, TM96, TM97, TM98, TM99, TM_CASE, TM_MATERIALS, TR01, TR02, TR03, TR04, TR05, TR06, TR07, TR08, TR09, TR10, TR11, TR12, TR13, TR14, TR15, TR16, TR17, TR18, TR19, TR20, TR21, TR22, TR23, TR24, TR25, TR26, TR27, TR28, TR29, TR30, TR31, TR32, TR33, TR34, TR35, TR36, TR37, TR38, TR39, TR40, TR41, TR42, TR43, TR44, TR45, TR46, TR47, TR48, TR49, TR50, TR51, TR52, TR53, TR54, TR55, TR56, TR57, TR58, TR59, TR60, TR61, TR62, TR63, TR64, TR65, TR66, TR67, TR68, TR69, TR70, TR71, TR72, TR73, TR74, TR75, TR76, TR77, TR78, TR79, TR80, TR81, TR82, TR83, TR84, TR85, TR86, TR87, TR88, TR89, TR90, TR91, TR92, TR93, TR94, TR95, TR96, TR97, TR98, TR99, TMV_PASS, TOPO_BERRY, TORN_JOURNAL, TOUGA_BERRY, TOUGH_CANDY, TOUGH_CANDY_L, TOUGH_CANDY_XL, TOWN_MAP, TOXIC_ORB, TOXIC_PLATE, TRAVEL_TRUNK, TRI_PASS, TROPIC_MAIL, TROPICAL_SHELL, TUMBLESTONE, TUNNEL_MAIL, TWICE_SPICED_RADISH, TWISTED_SPOON, TYRANITARITE, U_DISK, ULTRA_BALL, ULTRANECROZIUM_Z, UNOWN_REPORT, UNUSUAL_SHOES, UPGRADE, UTILITY_UMBRELLA, UXIES_CLAW, VENONAT_CANDY, VENUSAURITE, VIOLET_BOOK, VIVICHOKE, VIVID_SCENT, VOICE_CASE_1, VOICE_CASE_2, VOICE_CASE_3, VOICE_CASE_4, VOICE_CASE_5, VOLCANO_BALM, VOLTORB_CANDY, VS_RECORDER, VS_SEEKER, VULPIX_CANDY, WACAN_BERRY, WAILMER_PAIL, WALL_FRAGMENT, WARDING_CHARM_B, WARDING_CHARM_P, WARDING_CHARM_R, WARDING_CHARM_T, WARDING_CHARM_Y, WATER_GEM, WATER_MEMORY, WATER_STONE, WATER_TERA_SHARD, WATERIUM_Z, WATMEL_BERRY, WAVE_INCENSE, WAVE_MAIL, WEAKNESS_POLICY, WEEDLE_CANDY, WEPEAR_BERRY, WHIPPED_DREAM, WHITE_APRICORN, WHITE_FLUTE, WHITE_HERB, WHITE_MANE_HAIR, WIDE_LENS, WIKI_BERRY, WING_BALL, WISE_GLASSES, WISHING_CHIP, WISHING_PIECE, WISHING_STAR, WOOD, WOOD_MAIL, WOODEN_CROWN, WORKS_KEY, X_ACCURACY, X_ATTACK, X_DEFENSE, X_SP_ATK, X_SP_DEF, X_SPEED, XTRANSCEIVER, YACHE_BERRY, YAGO_BERRY, YELLOW_APRICORN, YELLOW_FLUTE, YELLOW_NECTAR, YELLOW_PETAL, YELLOW_SCARF, YELLOW_SHARD, YLW_ID_BADGE, ZAP_PLATE, ZAPDOS_CANDY, ZINC, ZOOM_LENS, Z_POWER_RING, Z_RING, ZUBAT_CANDY, ZYGARDE_CUBE, ITEM_TOTAL - // clang-format on -}; - -static constexpr std::uint16_t TOTAL_ITEM_COUNT = (std::uint16_t)Item::ITEM_TOTAL - 1U; -} // namespace pokesim::dex - -/////////////////////// END OF src/Types/Enums/Item.hpp //////////////////////// - -////////////////// START OF src/Types/Enums/PlayerSideId.hpp /////////////////// - -namespace pokesim { -enum class PlayerSideId : std::uint8_t { - NONE = 0U, - P1 = 1U, - P2 = 2U, -}; - -static constexpr std::uint8_t TOTAL_PLAYER_SIDE_ID_COUNT = 2U; -} // namespace pokesim - -/////////////////// END OF src/Types/Enums/PlayerSideId.hpp //////////////////// - -////////////////////// START OF src/Types/Enums/Slot.hpp /////////////////////// +///////////////////// START OF src/Components/Current.hpp ////////////////////// namespace pokesim { -enum class Slot : std::uint8_t { - NONE = 0U, - P1A, - P2A, - P1B, - P2B, - - P1C, - P2C, - P1D, - P2D, - P1E, - P2E, - P1F, - P2F, - - TOTAL_SLOT, +struct CurrentActionMoveSlot { + types::moveSlotIndex val{}; }; -static constexpr std::uint8_t TOTAL_SLOT_COUNT = (std::uint8_t)Slot::TOTAL_SLOT - 1U; } // namespace pokesim -/////////////////////// END OF src/Types/Enums/Slot.hpp //////////////////////// +////////////////////// END OF src/Components/Current.hpp /////////////////////// -//////////////////// START OF src/Components/Decisions.hpp ///////////////////// +//////////////// START OF src/Components/DisabledMoveSlots.hpp ///////////////// namespace pokesim { -struct SlotDecision { - Slot sourceSlot = Slot::NONE; - Slot targetSlot = Slot::NONE; - - bool megaEvolve = false; - bool primalRevert = false; - bool dynamax = false; - bool terastallize = false; - - std::optional moveChoice = std::nullopt; - std::optional itemChoice = std::nullopt; - - bool operator==(const SlotDecision& other) const { - return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot && megaEvolve == other.megaEvolve && - primalRevert == other.primalRevert && dynamax == other.dynamax && terastallize == other.terastallize && - moveChoice == other.moveChoice && itemChoice == other.itemChoice; - } -}; - -namespace types { -using slotDecisions = types::sideSlots; -} - -struct SideDecision { - PlayerSideId sideId = PlayerSideId::NONE; - internal::variant decisions{}; - - bool operator==(const SideDecision other) const { return sideId == other.sideId && decisions == other.decisions; } +struct DisabledMoveSlots { + types::moveSlots val{}; }; } // namespace pokesim -///////////////////// END OF src/Components/Decisions.hpp ////////////////////// +///////////////// END OF src/Components/DisabledMoveSlots.hpp ////////////////// ////////////////////// START OF src/Components/EVsIVs.hpp ////////////////////// @@ -18831,27 +18975,6 @@ struct Ivs { /////////////////////// END OF src/Components/EVsIVs.hpp /////////////////////// -//////////// START OF src/Components/EntityHolders/ActionQueue.hpp ///////////// - -namespace pokesim { -// Contains the list of action entities queued up to be simulated for a battle's current turn. -struct ActionQueue { - internal::maxSizedVector val{}; -}; -} // namespace pokesim - -///////////// END OF src/Components/EntityHolders/ActionQueue.hpp ////////////// - -///////////// START OF src/Components/EntityHolders/ChoiceLock.hpp ///////////// - -namespace pokesim { -struct ChoiceLock { - types::entity val{}; -}; -} // namespace pokesim - -////////////// END OF src/Components/EntityHolders/ChoiceLock.hpp ////////////// - ///////////// START OF src/Components/EntityHolders/FaintQueue.hpp ///////////// namespace pokesim { @@ -18874,37 +18997,27 @@ struct FoeSide { /////////////// END OF src/Components/EntityHolders/FoeSide.hpp //////////////// -//////////// START OF src/Components/EntityHolders/LastUsedMove.hpp //////////// +////////// START OF src/Components/EntityHolders/RecycledEntities.hpp ////////// namespace pokesim { -struct LastUsedMove { +struct RecycledAction { types::entity val{}; }; -} // namespace pokesim -///////////// END OF src/Components/EntityHolders/LastUsedMove.hpp ///////////// - -///////////// START OF src/Components/EntityHolders/MoveSlots.hpp ////////////// - -namespace pokesim { -// Contains a list of entities of the moves a Pokemon known. -struct MoveSlots { - types::moveSlots val{}; +struct RecycledActionMove { + types::entity val{}; }; -} // namespace pokesim -////////////// END OF src/Components/EntityHolders/MoveSlots.hpp /////////////// - -////////////// START OF src/Components/EntityHolders/Pokemon.hpp /////////////// +struct AddedRecycledActionMove1 { + types::entity val{}; +}; -namespace pokesim { -// Contains the entity pointing to a Pokemon. -struct Pokemon { +struct AddedRecycledActionMove2 { types::entity val{}; }; } // namespace pokesim -/////////////// END OF src/Components/EntityHolders/Pokemon.hpp //////////////// +/////////// END OF src/Components/EntityHolders/RecycledEntities.hpp /////////// //////////////// START OF src/Components/EntityHolders/Side.hpp //////////////// @@ -18969,6 +19082,16 @@ struct Id { ///////////////////////// END OF src/Components/ID.hpp ///////////////////////// +/////////////////// START OF src/Components/LastUsedMove.hpp /////////////////// + +namespace pokesim { +struct LastUsedMove { + types::moveSlotIndex val{}; +}; +} // namespace pokesim + +//////////////////// END OF src/Components/LastUsedMove.hpp //////////////////// + ////////////////////// START OF src/Components/Level.hpp /////////////////////// namespace pokesim { @@ -18980,6 +19103,26 @@ struct Level { /////////////////////// END OF src/Components/Level.hpp //////////////////////// +//////////////////// START OF src/Components/MoveSlots.hpp ///////////////////// + +namespace pokesim { +struct MoveSlot { + dex::Move move = dex::Move::NO_MOVE; + types::pp pp = Constants::MovePp::DEFAULT; + types::pp maxPp = Constants::MovePp::DEFAULT; + + constexpr bool operator==(const MoveSlot& other) const { + return move == other.move && pp == other.pp && maxPp == other.maxPp; + } +}; + +struct MoveSlots { + types::moveSlots val{}; +}; +} // namespace pokesim + +///////////////////// END OF src/Components/MoveSlots.hpp ////////////////////// + ///////////////////// START OF src/Types/Enums/Ability.hpp ///////////////////// namespace pokesim::dex { @@ -19180,19 +19323,19 @@ struct TypeName { ////////////////// END OF src/Components/Names/TypeNames.hpp /////////////////// -//////////////////////// START OF src/Components/PP.hpp //////////////////////// +////////////////// START OF src/Types/Enums/PlayerSideId.hpp /////////////////// namespace pokesim { -struct Pp { - types::pp val = Constants::MovePp::DEFAULT; +enum class PlayerSideId : std::uint8_t { + NONE = 0U, + P1 = 1U, + P2 = 2U, }; -struct MaxPp { - types::pp val = Constants::MoveMaxPp::DEFAULT; -}; +static constexpr std::uint8_t TOTAL_PLAYER_SIDE_ID_COUNT = 2U; } // namespace pokesim -///////////////////////// END OF src/Components/PP.hpp ///////////////////////// +/////////////////// END OF src/Types/Enums/PlayerSideId.hpp //////////////////// //////////////////// START OF src/Components/PlayerSide.hpp //////////////////// @@ -19241,6 +19384,16 @@ struct BaseStats { ///////////////// END OF src/Components/Pokedex/BaseStats.hpp ////////////////// +//////////////////// START OF src/Components/Pokedex/PP.hpp //////////////////// + +namespace pokesim { +struct Pp { + types::pp val = Constants::MovePp::DEFAULT; +}; +} // namespace pokesim + +///////////////////// END OF src/Components/Pokedex/PP.hpp ///////////////////// + ///////////////////// START OF src/Components/Position.hpp ///////////////////// namespace pokesim { @@ -19387,6 +19540,19 @@ struct RandomEventIndex { ///////////////// END OF src/Components/RandomEventOutputs.hpp ///////////////// +////////////////// START OF src/Components/SideDecisions.hpp /////////////////// + +namespace pokesim { +struct SideDecision { + PlayerSideId sideId = PlayerSideId::NONE; + internal::variant decisions{}; + + bool operator==(const SideDecision other) const { return sideId == other.sideId && decisions == other.decisions; } +}; +} // namespace pokesim + +/////////////////// END OF src/Components/SideDecisions.hpp //////////////////// + ///////////// START OF src/Components/SimulateTurn/ActionTags.hpp ////////////// namespace pokesim::action::tags { @@ -19563,60 +19729,6 @@ struct SpeciesTypes { //////////////////// END OF src/Components/SpeciesTypes.hpp //////////////////// -/////////////////// START OF src/Types/Enums/ActionOrder.hpp /////////////////// - -namespace pokesim { -enum class ActionOrder : std::uint8_t { - NONE = std::numeric_limits>::max(), - TEAM = 1U, - START = 2U, - BEFORE_TURN = 4U, - ITEM = BEFORE_TURN, - - SWITCH = 103U, - - MOVE = 200U, - - RESIDUAL = 254U, -}; - -static constexpr inline std::array VALID_ACTION_ORDERS = { - ActionOrder::NONE, - ActionOrder::TEAM, - ActionOrder::START, - ActionOrder::BEFORE_TURN, - ActionOrder::ITEM, - ActionOrder::SWITCH, - ActionOrder::MOVE, - ActionOrder::RESIDUAL, -}; -} // namespace pokesim - -//////////////////// END OF src/Types/Enums/ActionOrder.hpp //////////////////// - -//////////////////// START OF src/Components/SpeedSort.hpp ///////////////////// - -namespace pokesim { -// Data that determines the order actions take place -struct SpeedSort { - // Order of the types of actions (lower first) - ActionOrder order = ActionOrder::NONE; - // Priority of the action (higher first) - types::priority priority = Constants::MovePriority::DEFAULT; - // Whether negative fractional priority is active for the action (false first) - types::fractionalPriority fractionalPriority = false; - // Speed of Pokemon using move (higher first if priority tie) - types::stat speed = Constants::PokemonEffectiveStat::DEFAULT; - - bool operator==(const SpeedSort& other) const { - return order == other.order && priority == other.priority && fractionalPriority == other.fractionalPriority && - speed == other.speed; - } -}; -} // namespace pokesim - -///////////////////// END OF src/Components/SpeedSort.hpp ////////////////////// - ////////////////////// START OF src/Components/Stats.hpp /////////////////////// namespace pokesim::stat { @@ -19754,8 +19866,6 @@ struct Punch {}; struct VariableHitCount {}; // Move Property Tag: A multi-hit move where each hit checks accuracy (i.e. Triple Kick) struct AccuracyDependentHitCount {}; - -struct Disabled {}; } // namespace tags namespace effect::tags { @@ -19847,6 +19957,17 @@ struct CanSetStatus {}; ////////////////// END OF src/Components/Tags/PokemonTags.hpp ////////////////// +////////////// START OF src/Components/Tags/RecycledEntities.hpp /////////////// + +namespace pokesim::tags { +struct RecycledAction {}; +struct RecycledActionMove {}; +struct AddedRecycledActionMove1 {}; +struct AddedRecycledActionMove2 {}; +} // namespace pokesim::tags + +/////////////// END OF src/Components/Tags/RecycledEntities.hpp //////////////// + //////////////// START OF src/Components/Tags/RunEventTags.hpp ///////////////// namespace pokesim::tags { @@ -19859,6 +19980,8 @@ struct EndItem {}; ////////////////// START OF src/Components/Tags/Selection.hpp ////////////////// namespace pokesim::internal::tags { +struct BuildPokedexMove {}; +struct BuildActionMove {}; struct CloneFromDamageRolls {}; struct ApplySideDamageRollOptions {}; } // namespace pokesim::internal::tags @@ -20303,6 +20426,8 @@ struct Options { namespace pokesim { struct Accuracy; +struct ActionQueueItem; +struct ActionQueue; struct AddedTargets; struct BaseEffectChance; struct BasePower; @@ -20311,22 +20436,20 @@ struct DefBoost; struct SpaBoost; struct SpdBoost; struct SpeBoost; +struct ChoiceLock; struct CloneTo; +struct CurrentActionMoveSlot; struct Damage; struct DamageRollModifiers; struct DamageRolls; -struct SlotDecision; -struct SideDecision; +struct DisabledMoveSlots; struct Evs; struct Ivs; -struct ActionQueue; struct Battle; struct ParentBattle; struct RootBattle; struct ParentEntity; -struct ChoiceLock; struct CurrentAction; -struct NextAction; struct CurrentActionTargets; struct CurrentActionSource; struct CurrentActionTarget; @@ -20334,23 +20457,26 @@ struct FailedCurrentActionSource; struct FailedCurrentActionTarget; struct CurrentActionMovesAsSource; struct CurrentActionMovesAsTarget; -struct CurrentActionMoveSlot; struct CurrentEffectSource; struct CurrentEffectTarget; struct CurrentEffectsAsSource; struct CurrentEffectsAsTarget; struct FaintQueue; struct FoeSide; -struct LastUsedMove; -struct MoveSlots; -struct Pokemon; +struct RecycledAction; +struct RecycledActionMove; +struct AddedRecycledActionMove1; +struct AddedRecycledActionMove2; struct Side; struct Sides; struct Team; struct EventModifier; struct HitCount; struct Id; +struct LastUsedMove; struct Level; +struct MoveSlot; +struct MoveSlots; struct AbilityName; struct GenderName; struct ItemName; @@ -20367,20 +20493,19 @@ struct TerrainName; struct TypeName; struct VolatileName; struct WeatherName; -struct Pp; -struct MaxPp; struct PlayerSide; struct PrimaryAbility; struct SecondaryAbility; struct HiddenAbility; struct BaseStats; +struct Pp; struct Position; struct MovePriority; struct Probability; struct RngSeed; +struct SideDecision; struct SpeedTieIndexes; struct SpeciesTypes; -struct SpeedSort; struct Turn; struct Winner; namespace analyze_effect { @@ -20449,13 +20574,18 @@ void check(const T&) {} void checkBattle(types::entity, const types::registry&); void checkSide(types::entity, const types::registry&); void checkPokemon(types::entity, const types::registry&); -void checkMoveSlot(types::entity, const types::registry&); void checkActionMove(types::entity, const types::registry&); void checkPercentChance(types::percentChance); template <> void check(const Accuracy&); +template <> +void check(const ActionQueueItem&); + +template <> +void check(const ActionQueue&); + template <> void check(const AddedTargets&); @@ -20522,8 +20652,14 @@ void check(const calc_damage::RealEffectiveStat&); template <> void check(const calc_damage::Power&); +template <> +void check(const ChoiceLock&); + // template <> void check(const CloneTo&); +template <> +void check(const CurrentActionMoveSlot&); + template <> void check(const Damage&); @@ -20534,10 +20670,7 @@ template <> void check(const DamageRolls&); template <> -void check(const SlotDecision&); - -template <> -void check(const SideDecision&); +void check(const DisabledMoveSlots&); template <> void check(const Evs&); @@ -20545,9 +20678,6 @@ void check(const Evs&); template <> void check(const Ivs&); -template <> -void check(const ActionQueue&, const types::registry&); - template <> void check(const Battle&, const types::registry&); @@ -20560,15 +20690,9 @@ void check(const RootBattle&, const types::registry&); template <> void check(const ParentEntity&, const types::registry&); -template <> -void check(const ChoiceLock&, const types::registry&); - template <> void check(const CurrentAction&, const types::registry&); -template <> -void check(const NextAction&, const types::registry&); - template <> void check(const CurrentActionTargets&, const types::registry&); @@ -20590,9 +20714,6 @@ void check(const CurrentActionMovesAsSource&, const types::registry&); template <> void check(const CurrentActionMovesAsTarget&, const types::registry&); -template <> -void check(const CurrentActionMoveSlot&, const types::registry&); - template <> void check(const CurrentEffectSource&, const types::registry&); @@ -20612,13 +20733,16 @@ template <> void check(const FoeSide&, const types::registry&); template <> -void check(const LastUsedMove&, const types::registry&); +void check(const RecycledAction&, const types::registry&); + +template <> +void check(const RecycledActionMove&, const types::registry&); template <> -void check(const MoveSlots&, const types::registry&); +void check(const AddedRecycledActionMove1&, const types::registry&); template <> -void check(const Pokemon&, const types::registry&); +void check(const AddedRecycledActionMove2&, const types::registry&); template <> void check(const Side&, const types::registry&); @@ -20636,9 +20760,18 @@ void check(const HitCount&); // template <> void check(const Id&); +template <> +void check(const LastUsedMove&); + template <> void check(const Level&); +template <> +void check(const MoveSlot&); + +template <> +void check(const MoveSlots&); + template <> void check(const AbilityName&); @@ -20687,12 +20820,6 @@ void check(const VolatileName&); template <> void check(const WeatherName&); -template <> -void check(const Pp&); - -template <> -void check(const MaxPp&); - template <> void check(const PlayerSide&); @@ -20708,6 +20835,9 @@ void check(const HiddenAbility&); template <> void check(const BaseStats&); +template <> +void check(const Pp&); + template <> void check(const Position&); @@ -20761,6 +20891,9 @@ void check(const internal::RandomEqualChanceStack&, const types::registry&); // template <> void check(const internal::RandomEventIndex&); +template <> +void check(const SideDecision&); + template <> void check(const SpeedTieIndexes&); @@ -20785,9 +20918,6 @@ void check(const analyze_effect::EffectMultiplier&); template <> void check(const SpeciesTypes&); -template <> -void check(const SpeedSort&); - template <> void check(const stat::Hp&); @@ -20830,6 +20960,9 @@ void check(const Turn&); template <> void check(const Winner&); +template <> +void check(const types::slotDecision&); + template <> void check(const DamageRollKind&); @@ -21230,7 +21363,6 @@ struct InputSetup { public: InputSetup(types::registry& registry, types::entity entity); - InputSetup(types::registry& registry) : InputSetup(registry, registry.create()) {} void setAttacker(types::entity entity); void setEffectTarget(types::entity entity); @@ -21268,11 +21400,11 @@ types::entity slotToSideEntity(const Sides& sides, Slot targetSlot); types::entity slotToPokemonEntity(const types::registry& registry, types::entity sideEntity, Slot targetSlot); types::entity slotToPokemonEntity(const types::registry& registry, const Sides& sides, Slot targetSlot); types::entity slotToAllyPokemonEntity(const types::registry& registry, const Sides& sides, Slot targetSlot); -types::entity moveToEntity(const types::registry& registry, const MoveSlots& moveSlots, dex::Move move); +types::moveSlotIndex moveToMoveSlot(const MoveSlots& moveSlots, dex::Move move); -types::entity createActionMoveForTarget( - types::handle targetHandle, types::entity battleEntity, types::entity sourceEntity, dex::Move move, - const Pokedex& pokedex); +void setupActionMoveBuild( + types::registry& registry, types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, + types::entity actionMoveEntity, dex::Move move); } // namespace pokesim //////////////////// END OF src/Battle/Helpers/Helpers.hpp ///////////////////// @@ -21312,10 +21444,14 @@ struct StateSetupBase { /////////////// START OF src/Battle/Setup/PokemonStateSetup.hpp //////////////// namespace pokesim { +struct Evs; +struct Ivs; +struct SpeciesTypes; +struct MoveSlot; + // Tool to set properties of a Pokemon's state to an entity. struct PokemonStateSetup : internal::StateSetupBase { PokemonStateSetup() : internal::StateSetupBase() {} - PokemonStateSetup(types::registry& registry) : PokemonStateSetup(registry, registry.create()) {} PokemonStateSetup(types::registry& registry, types::entity entity); operator types::entity() const { return entity(); } @@ -21341,7 +21477,7 @@ struct PokemonStateSetup : internal::StateSetupBase { void setGender(dex::Gender gender); void setAbility(dex::Ability ability); void setItem(dex::Item item); - void setMoves(const std::vector& moveSlots); + void setMoves(const std::vector& moveSlots); void setPostion(types::teamPositionIndex position); void setStatus(dex::Status status); @@ -21382,11 +21518,11 @@ namespace pokesim { struct SimulateTurnOptions; struct CalculateDamageOptions; struct AnalyzeEffectOptions; +struct ActionQueueItem; // Tool to set properties of a battle's state to an entity. struct BattleStateSetup : internal::StateSetupBase { BattleStateSetup() : internal::StateSetupBase() {} - BattleStateSetup(types::registry& registry) : BattleStateSetup(registry, registry.create()) {} BattleStateSetup(types::registry& registry, types::entity entity); /** @@ -21401,17 +21537,16 @@ struct BattleStateSetup : internal::StateSetupBase { */ void initBlank(); + void setRecycledAction(types::entity recycledAction, types::entity recycledActionMove); + void setAddedRecycledActionMoves(types::entity addedRecycledActionMove1, types::entity addedRecycledActionMove2); void setAutoID(); void setID(types::stateId id); void setSide(types::entity sideEntity); // If a seed is not provided, the seed is set to a random number based on the current time in nanoseconds. void setRNGSeed(std::optional seed = std::nullopt); - void setActionQueue(const std::vector& queue); + void setActionQueue(const std::vector& queue); void setTurn(types::battleTurn turn); - void setCurrentActionTarget(types::targets actionTargets); - void setCurrentActionSource(types::entity actionSource); - void setCurrentActionMove(types::entity actionMove); void setProbability(types::probability probability); }; } // namespace pokesim @@ -21454,41 +21589,16 @@ void emplaceTagFromEnum(dex::Move move, types::handle handle); //////////////// END OF src/Battle/Setup/EmplaceTagFromEnum.hpp //////////////// -///////////////// START OF src/Battle/Setup/MoveStateSetup.hpp ///////////////// - -namespace pokesim { -// Tool to set properties of a move's state to an entity. -struct MoveStateSetup : internal::StateSetupBase { - MoveStateSetup() : internal::StateSetupBase() {} - MoveStateSetup(types::registry& registry) : MoveStateSetup(registry, registry.create()) {} - MoveStateSetup(types::registry& registry, types::entity entity) : StateSetupBase(registry, entity) {} - - /** - * @brief Applies the defaults to the required properties for a move state. - * - * Some of the required properties are a blank `MoveName`, `Pp`, and `MaxPp` component. - */ - void initBlank(); - - void setName(dex::Move moveName); - void setPP(types::pp pp); - void setMaxPP(types::pp maxPp); -}; -} // namespace pokesim - -////////////////// END OF src/Battle/Setup/MoveStateSetup.hpp ////////////////// - ///////////////// START OF src/Battle/Setup/SideStateSetup.hpp ///////////////// namespace pokesim { struct PokemonStateSetup; +struct SideDecision; // Tool to set properties of a player's side state to an entity. struct SideStateSetup : internal::StateSetupBase { SideStateSetup() : internal::StateSetupBase() {} - SideStateSetup(types::registry& registry, PlayerSideId playerSideId) - : SideStateSetup(registry, registry.create(), playerSideId) {} - SideStateSetup(types::registry& registry, types::entity entity, PlayerSideId playerSideId); + SideStateSetup(types::registry& registry, types::entity entity); /** * @brief Applies the defaults to the required properties for a player side's state. * @@ -21500,6 +21610,7 @@ struct SideStateSetup : internal::StateSetupBase { void setOpponent(types::entity entity); void setBattle(types::entity entity); void setPlayerSide(PlayerSideId playerSideId); + void setSideDecision(const SideDecision& sideDecision); }; } // namespace pokesim @@ -21513,17 +21624,14 @@ class Pokedex; namespace calc_damage { struct InputSetup { protected: - types::registry* registry; - types::entity moveEntity = entt::null; + types::handle handle; public: - InputSetup(types::registry& registry); + InputSetup(types::registry& registry, types::entity moveEntity); - void setup( - types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move, - const Pokedex& pokedex); + void setup(types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move); - types::entity entity() const; + types::entity entity() const { return handle.entity(); } }; } // namespace calc_damage } // namespace pokesim @@ -21810,7 +21918,6 @@ class Pokedex { void load(entt::dense_map& map, const entt::dense_set& list, Build build); types::entity buildSpecies(dex::Species species, types::registry& registry) const; - types::entity buildMove(dex::Move move, types::registry& registry, bool forActiveMove) const; types::entity buildItem(dex::Item item, types::registry& registry) const; types::entity buildAbility(dex::Ability ability, types::registry& registry) const; @@ -21996,7 +22103,7 @@ class Pokedex { return dexRegistry.all_of(movesMap.at(move)); } - types::entity buildActionMove(dex::Move move, types::registry& registry) const; + void buildMoves(types::registry& registry) const; void loadForBattleInfo(const std::vector& battleInfoList); }; } // namespace pokesim @@ -22428,9 +22535,6 @@ struct RegistryLoop< //////////////////// START OF src/Simulation/Simulation.hpp //////////////////// namespace pokesim { -struct SideStateSetup; -struct PokemonStateSetup; -struct BattleStateSetup; class Pokedex; namespace simulate_turn { @@ -22442,9 +22546,6 @@ struct Results; namespace analyze_effect { struct Results; } // namespace analyze_effect -namespace debug { -struct SimulationSetupChecks; -} /** * @brief The entry point for creating and running simulations. @@ -22453,22 +22554,6 @@ struct SimulationSetupChecks; * for running multiple simulations of the same battle, where each battle state has completed the same number of turns. */ class Simulation { - private: - types::entityVector createInitialMoves(const std::vector& moveInfoList); - PokemonStateSetup createInitialPokemon(const PokemonCreationInfo& pokemonInfo); - void createInitialSide( - SideStateSetup sideSetup, const SideCreationInfo& sideInfo, const BattleCreationInfo& battleInfo); - - void createInitialTurnDecision(BattleStateSetup battleStateSetup, const TurnDecisionInfo& turnDecisionInfo); - void createCalcDamageInput( - BattleStateSetup battleStateSetup, const CalcDamageInputInfo& inputInfo, debug::SimulationSetupChecks& debugChecks); - void createAnalyzeEffectInput( - BattleStateSetup battleStateSetup, const AnalyzeEffectInputInfo& inputInfo, - debug::SimulationSetupChecks& debugChecks); - - types::sides createInitialBattle( - BattleStateSetup battleStateSetup, const BattleCreationInfo& battleInfo); - private: struct ConstantValues { ConstantValues(const Pokedex& pokedex_, BattleFormat battleFormat_) @@ -22556,14 +22641,22 @@ class Simulation { } template void addToEntities(const Args&... args) { + static_assert( + sizeof...(ViewComponents) != 0, + "Using this function without view components will cause Type to be added to every entity."); auto view = registry.view(); registry.insert(view.begin(), view.end(), args...); } template void removeFromEntities(entt::exclude_t exclude = entt::exclude_t{}) { - auto view = registry.view(exclude); - registry.remove(view.begin(), view.end()); + if constexpr (sizeof...(ViewComponents) == 0 && sizeof...(ExcludeComponents) == 0) { + registry.clear(); + } + else { + auto view = registry.view(exclude); + registry.remove(view.begin(), view.end()); + } } }; } // namespace pokesim @@ -22576,12 +22669,21 @@ class Simulation { namespace pokesim::debug { struct Checks { - Checks(const Simulation& _simulation) : simulation(&_simulation), registry(&_simulation.registry) {} + Checks(const Simulation& _simulation) + : simulation(&_simulation), + registry(&_simulation.registry), + simulateTurnOptionsOnInput(simulation->simulateTurnOptions), + calcDamageOptionsOnInput(simulation->calculateDamageOptions), + analyzeEffectOptionsOnInput(simulation->analyzeEffectOptions) {} protected: const Simulation* simulation; const types::registry* registry; types::registry registryOnInput; + simulate_turn::Options simulateTurnOptionsOnInput; + calc_damage::Options calcDamageOptionsOnInput; + analyze_effect::Options analyzeEffectOptionsOnInput; + entt::dense_map currentEntitiesToInitial; entt::dense_set specificallyChecked; types::entityIndex initialEntityCount = 0U; @@ -22614,6 +22716,12 @@ struct Checks { } } + void checkOptions() const { + POKESIM_REQUIRE_NM(simulateTurnOptionsOnInput == simulation->simulateTurnOptions); + POKESIM_REQUIRE_NM(calcDamageOptionsOnInput == simulation->calculateDamageOptions); + POKESIM_REQUIRE_NM(analyzeEffectOptionsOnInput == simulation->analyzeEffectOptions); + } + void checkRemainingOutputs() const { for (auto [original, copy] : currentEntitiesToInitial) { if (!specificallyChecked.contains(original)) { @@ -22632,7 +22740,6 @@ struct Checks { return finalEntityCount; } - void checkMoveSlot(types::entity moveEntity) const { pokesim::debug::checkMoveSlot(moveEntity, *registry); } void checkPokemon(types::entity pokemonEntity) const { pokesim::debug::checkPokemon(pokemonEntity, *registry); } void checkSide(types::entity sideEntity) const { pokesim::debug::checkSide(sideEntity, *registry); } void checkBattle(types::entity battleEntity) const { pokesim::debug::checkBattle(battleEntity, *registry); } @@ -22864,18 +22971,15 @@ struct SimulationSetupChecks { POKESIM_REQUIRE_NM(moveSlots.val.size() == creationInfo.moves.size()); for (std::size_t i = 0U; i < creationInfo.moves.size(); i++) { - const MoveCreationInfo& move = creationInfo.moves[i]; - types::entity moveEntity = moveSlots.val[(types::moveSlotIndex)i]; - POKESIM_REQUIRE_NM(registry->all_of(moveEntity)); - POKESIM_REQUIRE_NM(registry->all_of(moveEntity)); - POKESIM_REQUIRE_NM(registry->all_of(moveEntity)); - POKESIM_REQUIRE_NM(registry->get(moveEntity).val == move.name); + const MoveCreationInfo& moveInfo = creationInfo.moves[i]; + MoveSlot moveSlot = moveSlots.val[(types::moveSlotIndex)i]; + types::pp idealMaxPp = moveInfo.maxPp.value_or(pokedex->getMoveData(moveInfo.name).val); + types::pp idealPp = moveInfo.pp.value_or(idealMaxPp); - types::pp idealMaxPp = move.maxPp.value_or(pokedex->getMoveData(move.name).val); - types::pp idealPp = move.pp.value_or(idealMaxPp); - POKESIM_REQUIRE_NM(registry->get(moveEntity).val == idealPp); - POKESIM_REQUIRE_NM(registry->get(moveEntity).val == idealMaxPp); - pokesim::debug::checkMoveSlot(moveEntity, *registry); + POKESIM_REQUIRE_NM(moveSlot.move == moveInfo.name); + POKESIM_REQUIRE_NM(moveSlot.pp == idealPp); + POKESIM_REQUIRE_NM(moveSlot.maxPp == idealMaxPp); + pokesim::debug::check(moveSlot); } if (creationInfo.currentHp.has_value()) { @@ -22972,14 +23076,7 @@ struct SimulationSetupChecks { const auto& slotDecision = slotDecisions[slot]; const auto& slotDecisionInfo = slotDecisionsInfo[slot]; - POKESIM_REQUIRE_NM(slotDecision.sourceSlot == slotDecisionInfo.sourceSlot); - POKESIM_REQUIRE_NM(slotDecision.targetSlot == slotDecisionInfo.targetSlot); - POKESIM_REQUIRE_NM(slotDecision.megaEvolve == slotDecisionInfo.megaEvolve); - POKESIM_REQUIRE_NM(slotDecision.primalRevert == slotDecisionInfo.primalRevert); - POKESIM_REQUIRE_NM(slotDecision.dynamax == slotDecisionInfo.dynamax); - POKESIM_REQUIRE_NM(slotDecision.terastallize == slotDecisionInfo.terastallize); - POKESIM_REQUIRE_NM(slotDecision.moveChoice == slotDecisionInfo.moveChoice); - POKESIM_REQUIRE_NM(slotDecision.itemChoice == slotDecisionInfo.itemChoice); + POKESIM_REQUIRE_NM(slotDecision == slotDecisionInfo); } } else if (sideDecisionInfo.decisions.holds()) { @@ -23204,6 +23301,8 @@ struct SimulationSetupChecks { #else namespace pokesim { +struct BattleStateSetup; + namespace calc_damage { struct InputSetup; } @@ -23308,7 +23407,8 @@ struct CurrentActionSource; struct CurrentActionTarget; struct CurrentActionMoveSlot; struct Damage; -struct Pp; +struct LastUsedMove; +struct MoveSlots; namespace stat { struct Atk; @@ -23333,7 +23433,7 @@ void clearStatus(types::handle pokemonHandle); void clearVolatiles(types::handle pokemonHandle); -void deductPp(Pp& pp); +void deductPp(MoveSlots& moveSlots, LastUsedMove lastUsedMove); void setLastMoveUsed(types::registry& registry, CurrentActionSource source, const CurrentActionMoveSlot& move); void resetEffectiveAtk(types::handle handle, stat::Atk atk); void resetEffectiveDef(types::handle handle, stat::Def def); @@ -24122,6 +24222,7 @@ void runTryBoostEvent(Simulation& simulation); template void runAfterEachBoostEvent(Simulation& simulation); void runAfterBoostEvent(Simulation& simulation); +void runModifyTarget(Simulation& simulation); // onModifyMove for Curse and Expanding force should go here void runModifyMove(Simulation& simulation); void runDisableMove(Simulation& simulation); @@ -24164,11 +24265,7 @@ void collectTurnOutcomeBattles(types::handle leafBattleHandle, const RootBattle& void setCurrentActionSource(types::handle battleHandle, const Sides& sides, CurrentAction action); void setCurrentActionTarget( - types::handle battleHandle, const Sides& sides, CurrentAction action, CurrentActionSource source, - const Simulation& simulation); -void setCurrentActionMove( - types::handle battleHandle, CurrentActionSource source, const CurrentActionTargets& targets, CurrentAction action, - const Pokedex& pokedex); + types::handle battleHandle, const Sides& sides, CurrentAction action, const Simulation& simulation); void setFailedActionMove( types::handle moveHandle, Battle battle, CurrentActionSource source, CurrentActionTarget target); void clearCurrentAction(Simulation& simulation); @@ -24380,15 +24477,16 @@ types::teamPositionIndex foeSidePokemonLeft(const types::registry& registry, typ namespace pokesim { struct SideDecision; struct ActionQueue; +struct RecycledAction; namespace simulate_turn { void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision); void speedSort(types::handle handle, ActionQueue& actionQueue); -void addBeforeTurnAction(types::registry& registry, ActionQueue& actionQueue); -void addResidualAction(types::registry& registry, ActionQueue& actionQueue); -void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue); -void clearActionQueue(types::handle battleHandle, ActionQueue& actionQueue); +void addBeforeTurnAction(ActionQueue& actionQueue); +void addResidualAction(ActionQueue& actionQueue); +void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue, RecycledAction& action); +void clearActionQueue(ActionQueue& actionQueue); } // namespace simulate_turn } // namespace pokesim @@ -24400,11 +24498,10 @@ void clearActionQueue(types::handle battleHandle, ActionQueue& actionQueue); namespace pokesim::simulate_turn::debug { struct Checks : pokesim::debug::Checks { - Options options; - Checks(const Simulation& _simulation) - : pokesim::debug::Checks(_simulation), options(_simulation.simulateTurnOptions) {} + Checks(const Simulation& _simulation) : pokesim::debug::Checks(_simulation) {} - void checkInputs() const { + void checkInputs() { + const auto& options = simulateTurnOptionsOnInput; pokesim::debug::check(options.getDamageRollsConsidered()); pokesim::debug::checkPercentChance(options.getRandomChanceLowerLimit()); pokesim::debug::checkPercentChance(options.getRandomChanceUpperLimit()); @@ -24412,26 +24509,75 @@ struct Checks : pokesim::debug::Checks { pokesim::debug::check(Probability{options.getBranchProbabilityLowerLimit()}); } + copyBattles(); check(); } void checkOutputs() const { - POKESIM_REQUIRE_NM(options == simulation->simulateTurnOptions); - + checkOptions(); + checkBattleOutputs(); check(); } private: + auto getBattleView() const { return registry->view(); } + + void copyBattles() { + for (types::entity entity : getBattleView()) { + copyEntity(entity); + copyEntity(registry->get(entity).val); + } + } + + void checkBattleOutputs() const { + pokesim::debug::TypesToIgnore typesToIgnore; + typesToIgnore.add(); + + pokesim::debug::TypesToIgnore typesIgnoredOnConstants = typesToIgnore; + typesToIgnore.add(); + + if (!simulateTurnOptionsOnInput.getMakeBranchesOnRandomEvents()) { + typesToIgnore.add(); + } + + for (types::entity currentEntity : getBattleView()) { + types::entity original = pokesim::debug::findCopyParent(currentEntitiesToInitial, *registry, currentEntity); + bool shouldNotChange = !simulateTurnOptionsOnInput.getApplyChangesToInputBattle() && original == currentEntity; + if (!registryOnInput.all_of(original)) { + typesToIgnore.add(); + } + + types::entity initialEntity = getInitialEntity(currentEntity); + pokesim::debug::areEntitiesEqual( + *registry, + currentEntity, + registryOnInput, + initialEntity, + shouldNotChange ? typesIgnoredOnConstants : typesToIgnore); + + bool initialIsMidTurn = registryOnInput.all_of(initialEntity); + bool currentIsMidTurn = registry->all_of(currentEntity); + types::entity currAction = registry->get(currentEntity).val; + if (!initialIsMidTurn && !currentIsMidTurn) { + pokesim::debug::areEntitiesEqual(*registry, currAction, registryOnInput, getInitialEntity(currAction)); + } + + if (!currentIsMidTurn) { + types::registry blankRegistry; + types::entity idealRecycledAction = blankRegistry.create(); + blankRegistry.emplace(idealRecycledAction); + pokesim::debug::hasSameComponents(*registry, currAction, blankRegistry, idealRecycledAction); + } + } + } + void check() const { - for (types::entity battleEntity : registry->view()) { + for (types::entity battleEntity : getBattleView()) { checkBattle(battleEntity); for (types::entity sideEntity : registry->get(battleEntity).val) { checkSide(sideEntity); for (types::entity pokemonEntity : registry->get(sideEntity).val) { checkPokemon(pokemonEntity); - for (types::entity moveEntity : registry->get(pokemonEntity).val) { - checkMoveSlot(moveEntity); - } } } } @@ -24552,6 +24698,7 @@ struct DexDataSetup { public: DexDataSetup(types::registry& registry) : handle(registry, registry.create()) {} + DexDataSetup(types::registry& registry, types::entity entity) : handle(registry, entity) {} template void setProperty() { @@ -24592,54 +24739,6 @@ struct SpeciesDexDataSetup : DexDataSetup { /////////////// END OF src/Pokedex/Setup/SpeciesDexDataSetup.hpp /////////////// -/////////////// START OF src/Pokedex/Setup/MoveDexDataSetup.hpp //////////////// - -namespace pokesim::dex::internal { -struct MoveDexDataSetup : DexDataSetup { - MoveDexDataSetup(types::registry& registry) : DexDataSetup(registry) {} - - void setName(Move move); - void setNameTag(Move move); - void setType(Type type); - void setAccuracy(types::baseAccuracy accuracy); - void setBasePower(types::basePower basePower); - - void setCategoryPhysical(); - void setCategorySpecial(); - void setCategoryStatus(); - - void setBasePp(types::pp pp); - void setPriority(types::priority priority); - void setHitCount(types::moveHits hitCount); - - void addAddedTargets(AddedTargetOptions addedTargets); - - void setEffectTargetsMoveSource(); - void setEffectTargetsMoveTarget(); - - 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 setSecondaryEffect(types::percentChance 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 - -//////////////// END OF src/Pokedex/Setup/MoveDexDataSetup.hpp ///////////////// - /////////////// START OF src/Pokedex/Setup/ItemDexDataSetup.hpp //////////////// namespace pokesim::dex::internal { @@ -24888,12 +24987,10 @@ constexpr types::typeEffectiveness getAttackEffectiveness( namespace pokesim::calc_damage::debug { struct Checks : pokesim::debug::Checks { - Options options; - Checks(const Simulation& _simulation) - : pokesim::debug::Checks(_simulation), options(_simulation.calculateDamageOptions) {} + Checks(const Simulation& _simulation) : pokesim::debug::Checks(_simulation) {} void checkInputs() { - pokesim::debug::check(options.getDamageRollOptions()); + pokesim::debug::check(calcDamageOptionsOnInput.getDamageRollOptions()); checkMoveInputs(); checkPokemonInputs(true); @@ -24907,8 +25004,7 @@ struct Checks : pokesim::debug::Checks { } void checkOutputs() const { - POKESIM_REQUIRE_NM(options == simulation->calculateDamageOptions); - + checkOptions(); checkMoveOutputs(); checkPokemonOutputs(true); checkPokemonOutputs(false); @@ -25503,12 +25599,10 @@ void enumToTag(dex::Type type, RunFunctionArgs&&... args) { namespace pokesim::analyze_effect::debug { struct Checks : pokesim::debug::Checks { - Options options; - Checks(const Simulation& _simulation) - : pokesim::debug::Checks(_simulation), options(_simulation.analyzeEffectOptions) {} + Checks(const Simulation& _simulation) : pokesim::debug::Checks(_simulation) {} void checkInputs() { - pokesim::debug::check(options.getDamageRollOptions()); + pokesim::debug::check(analyzeEffectOptionsOnInput.getDamageRollOptions()); auto view = registry->view(); types::entityVector inputs{view.begin(), view.end()}; @@ -25532,8 +25626,7 @@ struct Checks : pokesim::debug::Checks { } void checkOutputs() const { - POKESIM_REQUIRE_NM(options == simulation->analyzeEffectOptions); - + checkOptions(); types::entityIndex finalEntityCount = getFinalEntityCount(); POKESIM_REQUIRE_NM(initialEntityCount == finalEntityCount); checkInputOutputs(); diff --git a/extras/RecentBenchmarkResults.md b/extras/RecentBenchmarkResults.md index 7621167..4adce63 100644 --- a/extras/RecentBenchmarkResults.md +++ b/extras/RecentBenchmarkResults.md @@ -7,286 +7,311 @@ ## SV-SingleBattle-MonteCarlo-SimulateTurn-VerticalSlice1 | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 42.686600ms | 71.677420us | 12.397813us | 71.677420us | 69.971260us | 75.555430us | -| 2 | 100 | 1 | 43.637700ms | 81.768690us | 14.135944us | 40.884345us | 39.940000us | 43.185170us | -| 4 | 100 | 1 | 47.456800ms | 94.351630us | 15.499108us | 23.587908us | 23.051030us | 24.776653us | -| 8 | 100 | 1 | 51.027000ms | 114.655950us | 12.443183us | 14.331994us | 14.091234us | 14.737929us | -| 16 | 100 | 1 | 55.206500ms | 155.018130us | 12.876058us | 9.688633us | 9.569308us | 9.909428us | -| 32 | 100 | 1 | 65.644000ms | 219.742430us | 11.755610us | 6.866951us | 6.811495us | 6.965010us | -| 64 | 100 | 1 | 78.471600ms | 347.097550us | 14.043183us | 5.423399us | 5.387873us | 5.477365us | -| 128 | 100 | 1 | 105.135400ms | 568.687200us | 10.294603us | 4.442869us | 4.431304us | 4.466204us | -| 256 | 100 | 1 | 148.946300ms | 1.023886ms | 11.716033us | 3.999556us | 3.991984us | 4.010497us | -| 512 | 100 | 1 | 239.104800ms | 1.873348ms | 55.333180us | 3.658883us | 3.643921us | 3.691891us | -| 1024 | 100 | 1 | 436.389900ms | 3.622736ms | 42.548706us | 3.537828us | 3.531145us | 3.548062us | -| 2048 | 100 | 1 | 849.179100ms | 7.121922ms | 75.966519us | 3.477501us | 3.470775us | 3.485435us | -| 4096 | 100 | 1 | 1661.602700ms | 14.142980ms | 158.758946us | 3.452876us | 3.445977us | 3.461268us | -| 8192 | 100 | 1 | 2939.969000ms | 28.937484ms | 376.175091us | 3.532408us | 3.524270us | 3.542405us | -| 16384 | 100 | 1 | 5991.517100ms | 59.653404ms | 545.711054us | 3.640955us | 3.634894us | 3.647996us | -| 32768 | 100 | 1 | 12380.215000ms | 123.621082ms | 982.954473us | 3.772616us | 3.767070us | 3.778850us | -| 65536 | 100 | 1 | 28598.221500ms | 255.883671ms | 6.538011ms | 3.904475us | 3.887244us | 3.926714us | +| 1 | 100 | 1 | 41.812800ms | 64.410770us | 10.613091us | 64.410770us | 62.923830us | 67.598850us | +| 2 | 100 | 1 | 44.811200ms | 69.359510us | 12.225382us | 34.679755us | 33.837560us | 36.581325us | +| 4 | 100 | 1 | 45.432100ms | 79.643050us | 13.034458us | 19.910763us | 19.410393us | 20.769002us | +| 8 | 100 | 1 | 47.340000ms | 89.290100us | 12.679521us | 11.161263us | 10.922881us | 11.587753us | +| 16 | 100 | 1 | 50.377200ms | 116.391260us | 14.009545us | 7.274454us | 7.145342us | 7.516420us | +| 32 | 100 | 1 | 55.668500ms | 159.736090us | 11.495001us | 4.991753us | 4.936947us | 5.086350us | +| 64 | 100 | 1 | 65.361300ms | 242.715470us | 13.385733us | 3.792429us | 3.762530us | 3.853901us | +| 128 | 100 | 1 | 82.613400ms | 404.300350us | 11.075102us | 3.158596us | 3.145546us | 3.181765us | +| 256 | 100 | 1 | 91.589800ms | 722.717890us | 12.542475us | 2.823117us | 2.815423us | 2.835470us | +| 512 | 100 | 1 | 161.780200ms | 1.367900ms | 18.836156us | 2.671679us | 2.664916us | 2.679382us | +| 1024 | 100 | 1 | 270.591700ms | 2.627893ms | 19.273673us | 2.566302us | 2.562771us | 2.570133us | +| 2048 | 100 | 1 | 529.576500ms | 5.214860ms | 53.712859us | 2.546319us | 2.541447us | 2.551790us | +| 4096 | 100 | 1 | 1028.742500ms | 10.355275ms | 105.030953us | 2.528143us | 2.523365us | 2.533476us | +| 8192 | 100 | 1 | 2070.164400ms | 20.586104ms | 189.235971us | 2.512952us | 2.508843us | 2.517972us | +| 16384 | 100 | 1 | 4141.424700ms | 41.496992ms | 386.738538us | 2.532775us | 2.528571us | 2.537910us | +| 32768 | 100 | 1 | 8299.488800ms | 83.789194ms | 775.171153us | 2.557043us | 2.552890us | 2.562288us | +| 65536 | 100 | 1 | 16878.553400ms | 169.356254ms | 932.327033us | 2.584171us | 2.581514us | 2.587102us | ## SV-SingleBattle-Branching-SimulateTurn-VerticalSlice1 | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 60.685900ms | 214.916300us | 13.059062us | 214.916300us | 213.182120us | 219.265460us | -| 2 | 100 | 1 | 41.047000ms | 299.930550us | 16.395717us | 149.965275us | 148.758430us | 152.232750us | -| 4 | 100 | 1 | 54.396800ms | 438.564760us | 14.642541us | 109.641190us | 109.152398us | 110.826992us | -| 8 | 100 | 1 | 84.344400ms | 726.544370us | 18.529752us | 90.818046us | 90.493310us | 91.508447us | -| 16 | 100 | 1 | 140.344700ms | 1.218861ms | 14.648183us | 76.178809us | 76.038477us | 76.420934us | -| 32 | 100 | 1 | 247.554800ms | 2.216142ms | 17.651782us | 69.254430us | 69.153987us | 69.370142us | -| 64 | 100 | 1 | 461.344200ms | 4.183938ms | 40.049227us | 65.374038us | 65.267205us | 65.516310us | -| 128 | 100 | 1 | 879.518900ms | 8.043609ms | 87.995843us | 62.840699us | 62.719146us | 62.990218us | -| 256 | 100 | 1 | 1670.127000ms | 15.655372ms | 150.340349us | 61.153797us | 61.048648us | 61.278528us | -| 512 | 100 | 1 | 3110.317300ms | 30.370697ms | 389.345804us | 59.317767us | 59.178821us | 59.476225us | -| 1024 | 100 | 1 | 6128.363900ms | 60.103176ms | 2.193512ms | 58.694508us | 58.385339us | 59.318821us | -| 2048 | 100 | 1 | 12111.434600ms | 121.666626ms | 1.407967ms | 59.407532us | 59.285462us | 59.557427us | -| 4096 | 100 | 1 | 26226.142800ms | 268.925574ms | 7.613842ms | 65.655658us | 65.365416us | 66.126693us | -| 8192 | 100 | 1 | 59750.197600ms | 586.434134ms | 2.541032ms | 71.586198us | 71.527706us | 71.648953us | +| 1 | 100 | 1 | 58.163000ms | 176.119500us | 14.512645us | 176.119500us | 174.076850us | 180.498130us | +| 2 | 100 | 1 | 65.307900ms | 234.839760us | 15.332718us | 117.419880us | 116.348895us | 119.747675us | +| 4 | 100 | 1 | 45.145400ms | 336.142090us | 17.076938us | 84.035523us | 83.452685us | 85.377718us | +| 8 | 100 | 1 | 62.287900ms | 522.963890us | 11.457068us | 65.370486us | 65.161474us | 65.773331us | +| 16 | 100 | 1 | 101.154700ms | 888.516450us | 20.869015us | 55.532278us | 55.317294us | 55.840456us | +| 32 | 100 | 1 | 176.625400ms | 1.556070ms | 19.464035us | 48.627184us | 48.535648us | 48.793738us | +| 64 | 100 | 1 | 319.875400ms | 2.896670ms | 33.479973us | 45.260475us | 45.163830us | 45.369003us | +| 128 | 100 | 1 | 618.644100ms | 5.546284ms | 59.750287us | 43.330343us | 43.252121us | 43.438863us | +| 256 | 100 | 1 | 1151.506400ms | 10.722321ms | 84.227375us | 41.884064us | 41.822439us | 41.951441us | +| 512 | 100 | 1 | 2196.152500ms | 20.810947ms | 219.889225us | 40.646381us | 40.571680us | 40.741695us | +| 1024 | 100 | 1 | 4219.736700ms | 40.871901ms | 538.619921us | 39.913965us | 39.822107us | 40.031604us | +| 2048 | 100 | 1 | 8269.893600ms | 81.293653ms | 816.960237us | 39.694166us | 39.622747us | 39.781102us | +| 4096 | 100 | 1 | 16490.486400ms | 166.452831ms | 1.743960ms | 40.637898us | 40.570346us | 40.744618us | +| 8192 | 100 | 1 | 34419.343400ms | 346.039368ms | 1.470653ms | 42.241134us | 42.206612us | 42.277211us | ## SV-DoubleBattle-MonteCarlo-SimulateTurn-VerticalSlice1 | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 55.964400ms | 115.877490us | 16.720004us | 115.877490us | 113.554910us | 120.909400us | -| 2 | 100 | 1 | 59.530400ms | 135.605040us | 19.475652us | 67.802520us | 66.362530us | 70.517680us | -| 4 | 100 | 1 | 62.759800ms | 162.073490us | 18.959391us | 40.518372us | 39.811813us | 41.817232us | -| 8 | 100 | 1 | 66.891000ms | 201.089840us | 18.976933us | 25.136230us | 24.801624us | 25.833714us | -| 16 | 100 | 1 | 48.290200ms | 278.583320us | 14.929359us | 17.411458us | 17.268964us | 17.657396us | -| 32 | 100 | 1 | 62.537900ms | 405.417060us | 16.080762us | 12.669283us | 12.593608us | 12.804263us | -| 64 | 100 | 1 | 89.560900ms | 635.639520us | 13.649220us | 9.931868us | 9.902686us | 9.996860us | -| 128 | 100 | 1 | 121.084400ms | 1.079786ms | 24.897752us | 8.435828us | 8.412213us | 8.511360us | -| 256 | 100 | 1 | 210.715100ms | 1.951523ms | 27.945863us | 7.623136us | 7.603233us | 7.646388us | -| 512 | 100 | 1 | 387.840800ms | 3.671426ms | 44.252494us | 7.170753us | 7.156522us | 7.191479us | -| 1024 | 100 | 1 | 724.431500ms | 6.931923ms | 58.466017us | 6.769456us | 6.759651us | 6.782334us | -| 2048 | 100 | 1 | 1419.421800ms | 13.719796ms | 165.763638us | 6.699119us | 6.685875us | 6.718696us | -| 4096 | 100 | 1 | 2779.198700ms | 27.414956ms | 326.448162us | 6.693105us | 6.679361us | 6.710962us | +| 1 | 100 | 1 | 54.424500ms | 96.700350us | 16.468150us | 96.700350us | 94.469360us | 101.891580us | +| 2 | 100 | 1 | 55.863600ms | 109.740020us | 17.444388us | 54.870010us | 53.589350us | 57.350430us | +| 4 | 100 | 1 | 59.855000ms | 123.350980us | 19.947269us | 30.837745us | 30.195015us | 32.628638us | +| 8 | 100 | 1 | 61.344800ms | 145.996070us | 16.544502us | 18.249509us | 17.970055us | 18.936669us | +| 16 | 100 | 1 | 40.349500ms | 192.570550us | 16.320874us | 12.035659us | 11.893081us | 12.346166us | +| 32 | 100 | 1 | 50.671400ms | 275.636480us | 14.517291us | 8.613640us | 8.547742us | 8.742956us | +| 64 | 100 | 1 | 68.248000ms | 429.723110us | 12.393512us | 6.714424us | 6.683335us | 6.762779us | +| 128 | 100 | 1 | 82.665400ms | 730.178540us | 12.349704us | 5.704520us | 5.689514us | 5.729443us | +| 256 | 100 | 1 | 141.352400ms | 1.327241ms | 13.103564us | 5.184534us | 5.174971us | 5.195082us | +| 512 | 100 | 1 | 260.796700ms | 2.517451ms | 29.220961us | 4.916897us | 4.906757us | 4.929288us | +| 1024 | 100 | 1 | 506.724300ms | 4.887645ms | 42.071362us | 4.773091us | 4.766053us | 4.782430us | +| 2048 | 100 | 1 | 976.053700ms | 9.673801ms | 94.835464us | 4.723536us | 4.715200us | 4.733430us | +| 4096 | 100 | 1 | 1939.586300ms | 19.169027ms | 179.625633us | 4.679938us | 4.672027us | 4.689362us | +| 8192 | 100 | 1 | 3880.831800ms | 38.621122ms | 364.510444us | 4.714492us | 4.706193us | 4.723713us | +| 16384 | 100 | 1 | 7760.602200ms | 77.884083ms | 722.467931us | 4.753667us | 4.745750us | 4.763304us | +| 32768 | 100 | 1 | 15970.031900ms | 160.705123ms | 1.162949ms | 4.904331us | 4.897900us | 4.911857us | +| 65536 | 100 | 1 | 33302.099700ms | 333.177880ms | 1.406518ms | 5.083891us | 5.079829us | 5.088285us | ## SV-DoubleBattle-Branching-SimulateTurn-VerticalSlice1 | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 75.270800ms | 508.602400us | 17.345902us | 508.602400us | 506.139640us | 513.742420us | -| 2 | 100 | 1 | 91.073600ms | 766.268280us | 20.822925us | 383.134140us | 381.601245us | 386.056480us | -| 4 | 100 | 1 | 140.884600ms | 1.234372ms | 25.630393us | 308.593075us | 307.718875us | 310.627845us | -| 8 | 100 | 1 | 237.525100ms | 2.126358ms | 29.208138us | 265.794692us | 265.265781us | 266.844206us | -| 16 | 100 | 1 | 412.565900ms | 3.843884ms | 45.760242us | 240.242774us | 239.807671us | 240.998281us | -| 32 | 100 | 1 | 779.825300ms | 7.227782ms | 91.403858us | 225.868190us | 225.368616us | 226.491117us | -| 64 | 100 | 1 | 1466.020600ms | 13.838717ms | 148.317962us | 216.229960us | 215.824493us | 216.743195us | -| 128 | 100 | 1 | 2793.256300ms | 27.019668ms | 296.437375us | 211.091153us | 210.685697us | 211.600460us | -| 256 | 100 | 1 | 5608.744500ms | 54.398256ms | 878.712537us | 212.493188us | 211.899317us | 213.258705us | -| 512 | 100 | 1 | 10780.813800ms | 108.080436ms | 1.030936ms | 211.094601us | 210.750078us | 211.552514us | -| 1024 | 100 | 1 | 22096.036300ms | 224.556038ms | 1.926193ms | 219.293006us | 218.949630us | 219.691322us | -| 2048 | 100 | 1 | 46172.211500ms | 462.778756ms | 2.329673ms | 225.966190us | 225.765048us | 226.215252us | -| 4096 | 100 | 1 | 98078.403600ms | 974.312828ms | 3.124922ms | 237.869343us | 237.719071us | 238.017367us | +| 1 | 100 | 1 | 68.781600ms | 418.995070us | 18.291826us | 418.995070us | 416.385850us | 424.331330us | +| 2 | 100 | 1 | 74.290300ms | 613.443300us | 18.385417us | 306.721650us | 305.413160us | 309.418715us | +| 4 | 100 | 1 | 111.756300ms | 975.307620us | 18.308259us | 243.826905us | 243.171562us | 245.149225us | +| 8 | 100 | 1 | 184.274200ms | 1.660305ms | 26.456051us | 207.538166us | 206.925606us | 208.224419us | +| 16 | 100 | 1 | 324.812500ms | 2.951015ms | 35.147060us | 184.438419us | 184.070315us | 184.954260us | +| 32 | 100 | 1 | 593.472100ms | 5.529738ms | 52.863369us | 172.804314us | 172.505745us | 173.154561us | +| 64 | 100 | 1 | 1143.484900ms | 10.519655ms | 98.933867us | 164.369613us | 164.087314us | 164.693767us | +| 128 | 100 | 1 | 2204.598000ms | 20.468510ms | 238.941670us | 159.910237us | 159.589040us | 160.329441us | +| 256 | 100 | 1 | 4261.828000ms | 41.830724ms | 1.723036ms | 163.401267us | 162.234796us | 164.907955us | +| 512 | 100 | 1 | 8289.530600ms | 80.515633ms | 1.292235ms | 157.257096us | 156.811789us | 157.806180us | +| 1024 | 100 | 1 | 16396.756800ms | 161.586219ms | 1.370532ms | 157.799042us | 157.547054us | 158.070821us | +| 2048 | 100 | 1 | 31942.599700ms | 323.483822ms | 4.232925ms | 157.951085us | 157.605704us | 158.435030us | +| 4096 | 100 | 1 | 65320.875900ms | 650.907380ms | 2.389461ms | 158.912935us | 158.807548us | 159.037943us | ## SV-SingleBattle-CalcDamage-VerticalSlice1 | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 7.642500ms | 10.398820us | 3.063497us | 10.398820us | 9.960560us | 11.298590us | -| 2 | 100 | 1 | 7.745100ms | 11.103900us | 3.392469us | 5.551950us | 5.325755us | 6.119065us | -| 4 | 100 | 1 | 8.026200ms | 12.309900us | 3.474284us | 3.077475us | 2.952238us | 3.325508us | -| 8 | 100 | 1 | 8.227500ms | 13.864170us | 6.102111us | 1.733021us | 1.641125us | 2.042768us | -| 16 | 100 | 1 | 8.736000ms | 16.692990us | 2.016397us | 1.043312us | 1.024764us | 1.078439us | -| 32 | 100 | 1 | 9.535100ms | 23.858510us | 2.491570us | 745.578437ns | 733.193750ns | 765.090312ns | -| 64 | 100 | 1 | 10.809900ms | 35.511910us | 2.702795us | 554.873594ns | 548.341563ns | 565.851094ns | -| 128 | 100 | 1 | 13.653200ms | 59.022540us | 3.580700us | 461.113594ns | 456.618906ns | 468.037812ns | -| 256 | 100 | 1 | 19.013100ms | 103.848520us | 3.291804us | 405.658281ns | 403.589570ns | 408.820000ns | -| 512 | 100 | 1 | 35.826100ms | 198.899650us | 10.628629us | 388.475879ns | 385.709844ns | 395.332734ns | -| 1024 | 100 | 1 | 61.840800ms | 377.479570us | 6.132247us | 368.632393ns | 367.592744ns | 369.981914ns | -| 2048 | 100 | 1 | 116.708700ms | 738.333260us | 9.389160us | 360.514287ns | 359.715317ns | 361.544409ns | -| 4096 | 100 | 1 | 227.340000ms | 1.466441ms | 25.817565us | 358.017773ns | 357.047986ns | 359.668740ns | -| 8192 | 100 | 1 | 449.032500ms | 3.033444ms | 38.899051us | 370.293453ns | 369.446519ns | 371.316660ns | -| 16384 | 100 | 1 | 898.235700ms | 6.182320ms | 72.195765us | 377.338885ns | 376.564456ns | 378.326620ns | -| 32768 | 100 | 1 | 1824.083300ms | 12.557321ms | 152.288220us | 383.219027ns | 382.413417ns | 384.258423ns | -| 65536 | 100 | 1 | 3600.715800ms | 26.273869ms | 259.742839us | 400.907430ns | 400.211568ns | 401.774471ns | +| 1 | 100 | 1 | 7.744100ms | 10.695850us | 3.416505us | 10.695850us | 10.225820us | 11.760020us | +| 2 | 100 | 1 | 7.999900ms | 12.316620us | 4.003514us | 6.158310us | 5.873765us | 6.742695us | +| 4 | 100 | 1 | 7.990500ms | 12.923390us | 3.909913us | 3.230847us | 3.092548us | 3.525740us | +| 8 | 100 | 1 | 8.248800ms | 14.491220us | 3.936035us | 1.811403us | 1.738154us | 1.946499us | +| 16 | 100 | 1 | 8.703000ms | 18.785980us | 5.926089us | 1.174124us | 1.126027us | 1.301746us | +| 32 | 100 | 1 | 9.490200ms | 24.697390us | 6.589186us | 771.793437ns | 746.378750ns | 848.976562ns | +| 64 | 100 | 1 | 11.030100ms | 36.141160us | 3.659896us | 564.705625ns | 556.369375ns | 580.727188ns | +| 128 | 100 | 1 | 13.696300ms | 58.077550us | 3.697501us | 453.730859ns | 449.304531ns | 461.260000ns | +| 256 | 100 | 1 | 18.714700ms | 103.424030us | 7.166658us | 404.000117ns | 400.001484ns | 412.038906ns | +| 512 | 100 | 1 | 29.134400ms | 188.731770us | 3.412212us | 368.616738ns | 367.495566ns | 370.155566ns | +| 1024 | 100 | 1 | 41.800400ms | 362.664140us | 4.132482us | 354.164199ns | 353.408486ns | 354.997178ns | +| 2048 | 100 | 1 | 91.092400ms | 710.905980us | 7.654724us | 347.122061ns | 346.427822ns | 347.898501ns | +| 4096 | 100 | 1 | 144.453400ms | 1.410041ms | 16.486543us | 344.248381ns | 343.515916ns | 345.099919ns | +| 8192 | 100 | 1 | 328.181500ms | 2.808827ms | 32.681289us | 342.874351ns | 342.181533ns | 343.766742ns | +| 16384 | 100 | 1 | 650.308000ms | 5.584978ms | 59.026460us | 340.879984ns | 340.235309ns | 341.657454ns | +| 32768 | 100 | 1 | 1302.452000ms | 11.229741ms | 108.848624us | 342.704485ns | 342.119248ns | 343.432393ns | +| 65536 | 100 | 1 | 2652.809300ms | 22.544264ms | 179.816747us | 343.998169ns | 343.522599ns | 344.615771ns | ## SV-SingleBattle-AnalyzeEffect-VerticalSlice1 | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 24.481600ms | 54.964360us | 7.044274us | 54.964360us | 53.911850us | 56.880880us | -| 2 | 100 | 1 | 25.104900ms | 65.947320us | 7.352050us | 32.973660us | 32.435185us | 34.000945us | -| 4 | 100 | 1 | 30.405900ms | 88.159770us | 7.485760us | 22.039943us | 21.764252us | 22.565152us | -| 8 | 100 | 1 | 20.583600ms | 123.633890us | 8.698520us | 15.454236us | 15.308336us | 15.811117us | -| 16 | 100 | 1 | 26.535300ms | 193.958910us | 10.594951us | 12.122432us | 12.035806us | 12.347179us | -| 32 | 100 | 1 | 43.723100ms | 324.832140us | 7.827796us | 10.151004us | 10.113281us | 10.214358us | -| 64 | 100 | 1 | 73.957000ms | 576.465210us | 11.948879us | 9.007269us | 8.983196us | 9.071728us | -| 128 | 100 | 1 | 130.907300ms | 1.085209ms | 9.193191us | 8.478199us | 8.464917us | 8.493125us | -| 256 | 100 | 1 | 247.298500ms | 2.050264ms | 21.500260us | 8.008845us | 7.993296us | 8.026267us | -| 512 | 100 | 1 | 462.560900ms | 4.014678ms | 47.820052us | 7.841167us | 7.825207us | 7.862244us | -| 1024 | 100 | 1 | 900.050500ms | 7.961406ms | 96.532561us | 7.774810us | 7.758503us | 7.796074us | -| 2048 | 100 | 1 | 1755.826000ms | 15.702850ms | 163.978679us | 7.667407us | 7.653746us | 7.685741us | -| 4096 | 100 | 1 | 3541.823000ms | 31.940608ms | 565.817260us | 7.798000us | 7.774238us | 7.829396us | -| 8192 | 100 | 1 | 7715.873300ms | 72.500488ms | 962.395195us | 8.850157us | 8.829439us | 8.876009us | +| 1 | 100 | 1 | 28.694800ms | 57.617330us | 9.198119us | 57.617330us | 56.350270us | 60.472490us | +| 2 | 100 | 1 | 30.301300ms | 69.118740us | 11.595012us | 34.559370us | 33.699535us | 36.144545us | +| 4 | 100 | 1 | 32.548200ms | 88.074370us | 12.473999us | 22.018593us | 21.579225us | 22.947085us | +| 8 | 100 | 1 | 37.372200ms | 127.977970us | 9.835300us | 15.997246us | 15.804711us | 16.307325us | +| 16 | 100 | 1 | 46.329700ms | 187.443720us | 10.319932us | 11.715233us | 11.619277us | 11.891499us | +| 32 | 100 | 1 | 42.392400ms | 313.347180us | 10.416110us | 9.792099us | 9.743124us | 9.881070us | +| 64 | 100 | 1 | 72.380800ms | 548.792260us | 10.908489us | 8.574879us | 8.550356us | 8.623546us | +| 128 | 100 | 1 | 129.958800ms | 1.034135ms | 14.708402us | 8.079183us | 8.058559us | 8.103757us | +| 256 | 100 | 1 | 241.450000ms | 1.947662ms | 22.481428us | 7.608053us | 7.591882us | 7.626514us | +| 512 | 100 | 1 | 454.467400ms | 3.809853ms | 51.576459us | 7.441118us | 7.424772us | 7.465450us | +| 1024 | 100 | 1 | 862.451000ms | 7.419605ms | 63.198825us | 7.245708us | 7.234562us | 7.258873us | +| 2048 | 100 | 1 | 1613.272500ms | 14.545996ms | 116.832606us | 7.102537us | 7.092377us | 7.114790us | +| 4096 | 100 | 1 | 3056.781800ms | 28.652978ms | 275.603694us | 6.995356us | 6.983105us | 7.009625us | +| 8192 | 100 | 1 | 6062.615100ms | 59.389962ms | 773.001591us | 7.249751us | 7.232492us | 7.269536us | +| 16384 | 100 | 1 | 12404.663600ms | 124.122005ms | 1.207120ms | 7.575806us | 7.561884us | 7.590622us | +| 32768 | 100 | 1 | 26375.589700ms | 263.007432ms | 1.736749ms | 8.026350us | 8.016360us | 8.037224us | +| 65536 | 100 | 1 | 58744.488500ms | 583.838130ms | 1.947928ms | 8.908663us | 8.903017us | 8.914710us | ## SV-SingleBattle-MonteCarlo-SimulateTurn-RandomInputs-OneRandomBattle-OneRandomInput | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 31.405100ms | 79.627850us | 16.798804us | 79.627850us | 77.065170us | 83.995270us | -| 2 | 100 | 1 | 32.859500ms | 88.241610us | 15.014696us | 44.120805us | 42.811545us | 45.790660us | -| 4 | 100 | 1 | 35.227800ms | 103.868940us | 18.084822us | 25.967235us | 25.181938us | 26.963115us | -| 8 | 100 | 1 | 38.356900ms | 122.526940us | 20.410842us | 15.315867us | 14.861446us | 15.872014us | -| 16 | 100 | 1 | 44.109700ms | 164.399640us | 26.805288us | 10.274978us | 9.967774us | 10.627374us | -| 32 | 100 | 1 | 53.756800ms | 227.502310us | 36.652149us | 7.109447us | 6.900819us | 7.349929us | -| 64 | 100 | 1 | 52.178500ms | 356.978230us | 62.386717us | 5.577785us | 5.399720us | 5.782223us | -| 128 | 100 | 1 | 95.395700ms | 602.104090us | 109.546264us | 4.703938us | 4.549463us | 4.885052us | -| 256 | 100 | 1 | 227.800100ms | 1.082499ms | 199.611478us | 4.228510us | 4.087883us | 4.396125us | -| 512 | 100 | 1 | 282.668800ms | 2.028465ms | 392.856219us | 3.961846us | 3.823944us | 4.125946us | -| 1024 | 100 | 1 | 525.508800ms | 4.363781ms | 782.736717us | 4.261505us | 4.124087us | 4.425017us | -| 2048 | 100 | 1 | 1306.134100ms | 9.276676ms | 1.583899ms | 4.529627us | 4.388070us | 4.692534us | -| 4096 | 100 | 1 | 2807.882000ms | 19.656655ms | 3.300202ms | 4.798988us | 4.652843us | 4.970605us | -| 8192 | 100 | 1 | 4096.550900ms | 43.108654ms | 7.323969ms | 5.262287us | 5.097864us | 5.448632us | +| 1 | 100 | 1 | 39.296800ms | 65.967040us | 15.651345us | 65.967040us | 63.477230us | 69.905500us | +| 2 | 100 | 1 | 39.786700ms | 74.364430us | 18.692057us | 37.182215us | 35.645205us | 39.405250us | +| 4 | 100 | 1 | 40.330800ms | 81.503910us | 16.633579us | 20.375978us | 19.680528us | 21.341272us | +| 8 | 100 | 1 | 42.143900ms | 90.237610us | 16.599146us | 11.279701us | 10.919396us | 11.740909us | +| 16 | 100 | 1 | 43.412100ms | 119.949260us | 37.148564us | 7.496829us | 7.170080us | 8.195664us | +| 32 | 100 | 1 | 47.338500ms | 157.963110us | 31.916415us | 4.936347us | 4.758865us | 5.155409us | +| 64 | 100 | 1 | 53.622700ms | 233.826040us | 46.734239us | 3.653532us | 3.523565us | 3.813181us | +| 128 | 100 | 1 | 57.840400ms | 385.494130us | 79.282268us | 3.011673us | 2.904076us | 3.150131us | +| 256 | 100 | 1 | 93.011100ms | 689.251710us | 147.508840us | 2.692389us | 2.593992us | 2.822483us | +| 512 | 100 | 1 | 215.428900ms | 1.230267ms | 287.296993us | 2.402865us | 2.306641us | 2.531161us | +| 1024 | 100 | 1 | 436.221200ms | 2.396178ms | 573.736951us | 2.340018us | 2.243888us | 2.466864us | +| 2048 | 100 | 1 | 747.289200ms | 4.742278ms | 1.186398ms | 2.315565us | 2.215083us | 2.444445us | +| 4096 | 100 | 1 | 1486.914900ms | 9.670002ms | 2.398906ms | 2.360840us | 2.259849us | 2.491898us | +| 8192 | 100 | 1 | 1567.550800ms | 19.481458ms | 4.897783ms | 2.378108us | 2.274543us | 2.512788us | +| 16384 | 100 | 1 | 3294.990700ms | 39.614178ms | 9.978755ms | 2.417858us | 2.310997us | 2.551948us | +| 32768 | 100 | 1 | 7206.425300ms | 80.945634ms | 20.213887ms | 2.470265us | 2.362542us | 2.607539us | ## SV-SingleBattle-MonteCarlo-SimulateTurn-RandomInputs-OneRandomBattle-ManyRandomInputs | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 30.121100ms | 80.781340us | 17.367615us | 80.781340us | 78.041950us | 85.083140us | -| 2 | 100 | 1 | 44.880900ms | 89.919410us | 17.300855us | 44.959705us | 43.482080us | 46.933715us | -| 4 | 100 | 1 | 47.803300ms | 101.938520us | 17.789255us | 25.484630us | 24.714028us | 26.474287us | -| 8 | 100 | 1 | 48.867600ms | 118.753320us | 20.122918us | 14.844165us | 14.396960us | 15.390319us | -| 16 | 100 | 1 | 55.006100ms | 158.468110us | 25.199222us | 9.904257us | 9.612741us | 10.235304us | -| 32 | 100 | 1 | 40.038700ms | 221.400650us | 34.847082us | 6.918770us | 6.719167us | 7.146684us | -| 64 | 100 | 1 | 71.996500ms | 338.960440us | 62.560404us | 5.296257us | 5.118236us | 5.501753us | -| 128 | 100 | 1 | 83.615600ms | 554.975250us | 105.748969us | 4.335744us | 4.185573us | 4.510324us | -| 256 | 100 | 1 | 143.709600ms | 980.446730us | 195.124015us | 3.829870us | 3.693816us | 3.993576us | -| 512 | 100 | 1 | 187.946600ms | 1.833044ms | 368.229964us | 3.580164us | 3.450057us | 3.732975us | -| 1024 | 100 | 1 | 355.572500ms | 3.523513ms | 733.367483us | 3.440930us | 3.313001us | 3.596060us | -| 2048 | 100 | 1 | 738.497200ms | 6.929981ms | 1.441611ms | 3.383780us | 3.258147us | 3.533939us | -| 4096 | 100 | 1 | 1951.701100ms | 14.028916ms | 2.828331ms | 3.425028us | 3.301326us | 3.574195us | -| 8192 | 100 | 1 | 2650.411700ms | 28.507829ms | 5.423487ms | 3.479960us | 3.361519us | 3.622731us | +| 1 | 100 | 1 | 38.895300ms | 65.179230us | 15.947083us | 65.179230us | 62.647510us | 69.191710us | +| 2 | 100 | 1 | 39.771500ms | 86.643040us | 17.384812us | 43.321520us | 41.808725us | 45.253165us | +| 4 | 100 | 1 | 57.081500ms | 98.455710us | 17.096963us | 24.613928us | 23.912005us | 25.637207us | +| 8 | 100 | 1 | 46.726100ms | 119.458700us | 18.736255us | 14.932337us | 14.532960us | 15.471093us | +| 16 | 100 | 1 | 50.652000ms | 149.415710us | 21.057817us | 9.338482us | 9.099269us | 9.619647us | +| 32 | 100 | 1 | 60.559000ms | 193.016630us | 25.203515us | 6.031770us | 5.887825us | 6.198360us | +| 64 | 100 | 1 | 68.742900ms | 275.413990us | 35.996543us | 4.303344us | 4.198623us | 4.421129us | +| 128 | 100 | 1 | 61.222500ms | 447.448670us | 50.539713us | 3.495693us | 3.422321us | 3.577479us | +| 256 | 100 | 1 | 94.411800ms | 751.251990us | 94.376256us | 2.934578us | 2.867349us | 3.012100us | +| 512 | 100 | 1 | 226.852600ms | 1.397829ms | 181.452892us | 2.730134us | 2.664364us | 2.803475us | +| 1024 | 100 | 1 | 377.991800ms | 2.585393ms | 370.758004us | 2.524798us | 2.458083us | 2.600545us | +| 2048 | 100 | 1 | 530.871900ms | 5.135049ms | 645.747600us | 2.507348us | 2.447650us | 2.570923us | +| 4096 | 100 | 1 | 1297.011500ms | 10.580533ms | 1.554946ms | 2.583138us | 2.512838us | 2.662451us | +| 8192 | 100 | 1 | 1953.164000ms | 21.136448ms | 3.252297ms | 2.580133us | 2.509519us | 2.666563us | +| 16384 | 100 | 1 | 3390.706500ms | 44.123448ms | 5.172564ms | 2.693082us | 2.633290us | 2.757579us | +| 32768 | 100 | 1 | 9742.000200ms | 90.811982ms | 11.504846ms | 2.771362us | 2.704248us | 2.842261us | ## SV-SingleBattle-MonteCarlo-SimulateTurn-RandomInputs-ManyRandomBattles-ManyRandomInputs | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 42.553000ms | 81.108080us | 17.442912us | 81.108080us | 78.372810us | 85.496630us | -| 2 | 100 | 1 | 56.405400ms | 102.116280us | 19.137545us | 51.058140us | 49.529410us | 53.432670us | -| 4 | 100 | 1 | 40.246900ms | 132.205090us | 16.053161us | 33.051272us | 32.334255us | 33.923702us | -| 8 | 100 | 1 | 54.914100ms | 175.955640us | 18.210563us | 21.994455us | 21.617350us | 22.540533us | -| 16 | 100 | 1 | 65.440000ms | 230.539240us | 19.357769us | 14.408702us | 14.221073us | 14.716801us | -| 32 | 100 | 1 | 81.031300ms | 332.951890us | 24.534449us | 10.404747us | 10.285364us | 10.602595us | -| 64 | 100 | 1 | 79.207200ms | 516.057330us | 34.829066us | 8.063396us | 7.986856us | 8.226971us | -| 128 | 100 | 1 | 189.169300ms | 836.859850us | 24.604275us | 6.537968us | 6.510081us | 6.591978us | -| 256 | 100 | 1 | 266.100100ms | 1.472600ms | 36.437895us | 5.752344us | 5.730479us | 5.789780us | -| 512 | 100 | 1 | 447.265800ms | 2.819624ms | 79.202500us | 5.507079us | 5.481025us | 5.543039us | -| 1024 | 100 | 1 | 774.171300ms | 6.699105ms | 235.055795us | 6.542095us | 6.501089us | 6.591319us | -| 2048 | 100 | 1 | 1582.866400ms | 14.667931ms | 276.069397us | 7.162076us | 7.139669us | 7.193820us | -| 4096 | 100 | 1 | 3213.526200ms | 31.844605ms | 425.335216us | 7.774562us | 7.756775us | 7.798163us | -| 8192 | 100 | 1 | 7243.663300ms | 72.859805ms | 912.702451us | 8.894019us | 8.873635us | 8.917347us | +| 1 | 100 | 1 | 39.136400ms | 66.576990us | 17.309905us | 66.576990us | 63.875020us | 71.031900us | +| 2 | 100 | 1 | 46.481400ms | 84.827920us | 18.982256us | 42.413960us | 40.998280us | 45.014000us | +| 4 | 100 | 1 | 48.575900ms | 104.577050us | 18.194988us | 26.144262us | 25.464862us | 27.395430us | +| 8 | 100 | 1 | 59.066000ms | 129.160640us | 18.754053us | 16.145080us | 15.799235us | 16.784999us | +| 16 | 100 | 1 | 67.905100ms | 169.310210us | 20.041990us | 10.581888us | 10.382197us | 10.893959us | +| 32 | 100 | 1 | 73.221600ms | 234.828800us | 22.610659us | 7.338400us | 7.226386us | 7.515850us | +| 64 | 100 | 1 | 93.302100ms | 348.880560us | 23.250427us | 5.451259us | 5.392658us | 5.539776us | +| 128 | 100 | 1 | 114.429100ms | 556.978350us | 20.545081us | 4.351393us | 4.324243us | 4.388245us | +| 256 | 100 | 1 | 128.150700ms | 952.892230us | 21.429536us | 3.722235us | 3.706526us | 3.739384us | +| 512 | 100 | 1 | 204.682400ms | 1.702825ms | 30.587237us | 3.325830us | 3.314766us | 3.338207us | +| 1024 | 100 | 1 | 387.349500ms | 3.282676ms | 68.190076us | 3.205738us | 3.195898us | 3.224204us | +| 2048 | 100 | 1 | 764.120800ms | 6.563048ms | 140.324327us | 3.204613us | 3.192260us | 3.219359us | +| 4096 | 100 | 1 | 1568.697200ms | 14.457058ms | 420.090960us | 3.529555us | 3.510256us | 3.550583us | +| 8192 | 100 | 1 | 3282.954800ms | 31.616605ms | 752.427811us | 3.859449us | 3.843104us | 3.879339us | +| 16384 | 100 | 1 | 7811.748100ms | 77.396062ms | 901.243971us | 4.723881us | 4.714107us | 4.735845us | +| 32768 | 100 | 1 | 17717.420700ms | 175.728594ms | 1.438841ms | 5.362811us | 5.354887us | 5.372185us | ## SV-SingleBattle-CalcDamage-RandomInputs-OneRandomBattle-OneRandomInput | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 10.154500ms | 12.770960us | 4.757985us | 12.770960us | 12.039660us | 14.012200us | -| 2 | 100 | 1 | 10.565400ms | 14.725520us | 5.558946us | 7.362760us | 6.899630us | 8.024930us | -| 4 | 100 | 1 | 11.151500ms | 17.530480us | 8.261466us | 4.382620us | 4.081207us | 4.972752us | -| 8 | 100 | 1 | 12.139000ms | 21.268460us | 7.591241us | 2.658557us | 2.477965us | 2.851915us | -| 16 | 100 | 1 | 13.902100ms | 29.751230us | 11.831791us | 1.859452us | 1.717234us | 2.006126us | -| 32 | 100 | 1 | 10.695700ms | 45.104220us | 18.879927us | 1.409507us | 1.292444us | 1.523793us | -| 64 | 100 | 1 | 18.719600ms | 77.782920us | 37.174636us | 1.215358us | 1.103153us | 1.330819us | -| 128 | 100 | 1 | 50.457000ms | 139.299200us | 69.197438us | 1.088275us | 983.970312ns | 1.194662us | -| 256 | 100 | 1 | 87.918000ms | 267.140260us | 135.958140us | 1.043517us | 939.459375ns | 1.147468us | -| 512 | 100 | 1 | 166.691000ms | 477.188810us | 270.771916us | 932.009395ns | 827.687539ns | 1.034706us | -| 1024 | 100 | 1 | 329.279600ms | 1.148998ms | 644.517873us | 1.122068us | 997.091611ns | 1.242825us | -| 2048 | 100 | 1 | 652.648300ms | 2.493000ms | 1.377171ms | 1.217285us | 1.084202us | 1.346281us | -| 4096 | 100 | 1 | 1268.678400ms | 5.106965ms | 2.778428ms | 1.246818us | 1.111135us | 1.376692us | -| 8192 | 100 | 1 | 2610.247200ms | 11.288210ms | 6.208669ms | 1.377955us | 1.226488us | 1.523966us | +| 1 | 100 | 1 | 9.281300ms | 12.882730us | 4.503493us | 12.882730us | 12.181030us | 14.034820us | +| 2 | 100 | 1 | 9.493800ms | 14.967660us | 5.621356us | 7.483830us | 7.018605us | 8.155395us | +| 4 | 100 | 1 | 9.830400ms | 18.185240us | 7.597458us | 4.546310us | 4.252262us | 5.037750us | +| 8 | 100 | 1 | 10.493300ms | 21.487820us | 7.231599us | 2.685977us | 2.516505us | 2.869960us | +| 16 | 100 | 1 | 11.714100ms | 29.953710us | 11.058561us | 1.872107us | 1.737398us | 2.006979us | +| 32 | 100 | 1 | 10.349800ms | 45.853260us | 21.100808us | 1.432914us | 1.321325us | 1.585775us | +| 64 | 100 | 1 | 12.246200ms | 75.088690us | 31.266282us | 1.173261us | 1.075051us | 1.267450us | +| 128 | 100 | 1 | 20.548400ms | 132.740740us | 58.557500us | 1.037037us | 947.394609ns | 1.125965us | +| 256 | 100 | 1 | 31.726600ms | 248.382120us | 114.311158us | 970.242656ns | 882.405234ns | 1.057647us | +| 512 | 100 | 1 | 61.065800ms | 419.251070us | 224.621054us | 818.849746ns | 732.365781ns | 904.899023ns | +| 1024 | 100 | 1 | 108.493200ms | 828.829220us | 449.072441us | 809.403535ns | 723.406074ns | 896.395889ns | +| 2048 | 100 | 1 | 214.125200ms | 1.668830ms | 912.108377us | 814.858462ns | 727.528975ns | 901.593911ns | +| 4096 | 100 | 1 | 608.766300ms | 3.339028ms | 1.809850ms | 815.192283ns | 728.678948ns | 902.426763ns | +| 8192 | 100 | 1 | 1212.611100ms | 6.753387ms | 3.620123ms | 824.388018ns | 736.994628ns | 910.304702ns | +| 16384 | 100 | 1 | 2436.718600ms | 13.787446ms | 7.485719ms | 841.518928ns | 752.065709ns | 931.515229ns | +| 32768 | 100 | 1 | 1263.239900ms | 27.800361ms | 15.609926ms | 848.399691ns | 755.240095ns | 942.076887ns | ## SV-SingleBattle-CalcDamage-RandomInputs-OneRandomBattle-ManyRandomInputs | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 10.165300ms | 12.867020us | 5.372350us | 12.867020us | 12.042290us | 14.271810us | -| 2 | 100 | 1 | 10.584700ms | 14.700520us | 4.379222us | 7.350260us | 7.037065us | 7.986950us | -| 4 | 100 | 1 | 10.471400ms | 17.385130us | 3.858959us | 4.346283us | 4.208570us | 4.629733us | -| 8 | 100 | 1 | 11.471500ms | 22.025650us | 4.468590us | 2.753206us | 2.668673us | 2.901986us | -| 16 | 100 | 1 | 12.061500ms | 32.931470us | 27.521200us | 2.058217us | 1.869211us | 2.842650us | -| 32 | 100 | 1 | 14.285100ms | 45.614030us | 17.271659us | 1.425438us | 1.362060us | 1.652059us | -| 64 | 100 | 1 | 16.516300ms | 70.599020us | 6.197957us | 1.103110us | 1.086095us | 1.124418us | -| 128 | 100 | 1 | 23.737300ms | 121.761450us | 6.389777us | 951.261328ns | 941.613125ns | 961.178437ns | -| 256 | 100 | 1 | 36.062100ms | 229.622680us | 9.427472us | 896.963594ns | 889.899727ns | 904.331094ns | -| 512 | 100 | 1 | 41.619800ms | 441.194690us | 10.889678us | 861.708379ns | 857.538359ns | 865.855410ns | -| 1024 | 100 | 1 | 87.522900ms | 857.297990us | 17.392077us | 837.205068ns | 833.891895ns | 840.563203ns | -| 2048 | 100 | 1 | 176.454500ms | 1.684521ms | 30.232978us | 822.519824ns | 819.682109ns | 825.468745ns | -| 4096 | 100 | 1 | 347.414500ms | 3.375787ms | 66.124234us | 824.166746ns | 821.349736ns | 827.720208ns | -| 8192 | 100 | 1 | 689.636000ms | 6.763567ms | 123.313306us | 825.630715ns | 822.868110ns | 828.783402ns | +| 1 | 100 | 1 | 9.242000ms | 13.146540us | 4.147769us | 13.146540us | 12.422940us | 14.070120us | +| 2 | 100 | 1 | 9.254200ms | 15.793350us | 3.749088us | 7.896675us | 7.603480us | 8.373470us | +| 4 | 100 | 1 | 10.247200ms | 17.389930us | 3.509557us | 4.347482us | 4.219698us | 4.594655us | +| 8 | 100 | 1 | 10.610800ms | 22.772540us | 5.863189us | 2.846567us | 2.735711us | 3.044040us | +| 16 | 100 | 1 | 12.307000ms | 30.029510us | 9.243447us | 1.876844us | 1.798936us | 2.063049us | +| 32 | 100 | 1 | 13.232300ms | 43.718160us | 9.099352us | 1.366193us | 1.328665us | 1.461762us | +| 64 | 100 | 1 | 17.139300ms | 70.400470us | 6.037977us | 1.100007us | 1.083706us | 1.121252us | +| 128 | 100 | 1 | 13.113100ms | 123.717720us | 7.394618us | 966.544688ns | 956.258281ns | 979.130937ns | +| 256 | 100 | 1 | 23.374300ms | 226.781330us | 9.301527us | 885.864570ns | 879.212500ns | 893.545469ns | +| 512 | 100 | 1 | 42.946300ms | 437.958200us | 12.213190us | 855.387109ns | 850.744961ns | 860.124453ns | +| 1024 | 100 | 1 | 89.047600ms | 857.226530us | 16.951742us | 837.135283ns | 833.826396ns | 840.334795ns | +| 2048 | 100 | 1 | 174.729600ms | 1.702241ms | 36.595307us | 831.172412ns | 827.694912ns | 834.690112ns | +| 4096 | 100 | 1 | 348.893700ms | 3.429840ms | 53.283505us | 837.363330ns | 834.858679ns | 839.959651ns | +| 8192 | 100 | 1 | 712.074200ms | 6.871512ms | 95.368851us | 838.807644ns | 836.629833ns | 841.219465ns | +| 16384 | 100 | 1 | 1406.063900ms | 13.859635ms | 214.366783us | 845.924990ns | 843.457043ns | 848.561189ns | +| 32768 | 100 | 1 | 2826.122700ms | 28.015054ms | 318.756751us | 854.951609ns | 853.149748ns | 856.972537ns | ## SV-SingleBattle-CalcDamage-RandomInputs-ManyRandomBattles-ManyRandomInputs | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 10.118900ms | 13.196670us | 5.623242us | 13.196670us | 12.315860us | 14.632450us | -| 2 | 100 | 1 | 9.975600ms | 15.488890us | 5.499116us | 7.744445us | 7.380100us | 8.653995us | -| 4 | 100 | 1 | 10.843200ms | 18.572920us | 5.870794us | 4.643230us | 4.446508us | 5.119195us | -| 8 | 100 | 1 | 11.280200ms | 23.194170us | 4.497054us | 2.899271us | 2.816280us | 3.057430us | -| 16 | 100 | 1 | 13.639600ms | 33.864060us | 7.773025us | 2.116504us | 2.050127us | 2.269154us | -| 32 | 100 | 1 | 16.975300ms | 51.248510us | 4.918828us | 1.601516us | 1.576967us | 1.639746us | -| 64 | 100 | 1 | 30.069900ms | 91.563110us | 6.391016us | 1.430674us | 1.413829us | 1.454109us | -| 128 | 100 | 1 | 51.995000ms | 169.878430us | 22.184326us | 1.327175us | 1.302509us | 1.378400us | -| 256 | 100 | 1 | 89.286100ms | 319.688410us | 16.598105us | 1.248783us | 1.237859us | 1.263646us | -| 512 | 100 | 1 | 171.896500ms | 610.812870us | 32.852947us | 1.192994us | 1.183824us | 1.211806us | -| 1024 | 100 | 1 | 336.640100ms | 1.578508ms | 67.384991us | 1.541512us | 1.529491us | 1.555314us | -| 2048 | 100 | 1 | 670.177100ms | 3.641640ms | 327.045601us | 1.778144us | 1.748979us | 1.812021us | -| 4096 | 100 | 1 | 1331.147100ms | 8.074754ms | 261.307612us | 1.971375us | 1.960577us | 1.986096us | -| 8192 | 100 | 1 | 2690.146400ms | 17.813892ms | 242.845050us | 2.174547us | 2.169275us | 2.180981us | +| 1 | 100 | 1 | 6.272300ms | 13.112660us | 4.499970us | 13.112660us | 12.386770us | 14.215710us | +| 2 | 100 | 1 | 9.939900ms | 14.920500us | 3.902991us | 7.460250us | 7.144165us | 7.937335us | +| 4 | 100 | 1 | 11.007700ms | 18.320580us | 4.447133us | 4.580145us | 4.414435us | 4.882195us | +| 8 | 100 | 1 | 11.669500ms | 23.451450us | 4.845250us | 2.931431us | 2.837827us | 3.089150us | +| 16 | 100 | 1 | 12.712800ms | 32.194210us | 4.411407us | 2.012138us | 1.968504us | 2.082025us | +| 32 | 100 | 1 | 14.026700ms | 49.918150us | 6.977955us | 1.559942us | 1.528417us | 1.621345us | +| 64 | 100 | 1 | 17.886900ms | 81.493460us | 4.993903us | 1.273335us | 1.259162us | 1.290016us | +| 128 | 100 | 1 | 15.010600ms | 145.415530us | 7.119982us | 1.136059us | 1.125540us | 1.147279us | +| 256 | 100 | 1 | 27.180300ms | 271.272000us | 10.441639us | 1.059656us | 1.051775us | 1.067861us | +| 512 | 100 | 1 | 48.908700ms | 459.334060us | 14.951726us | 897.136836ns | 891.545957ns | 902.946211ns | +| 1024 | 100 | 1 | 100.008500ms | 907.734450us | 20.632221us | 886.459424ns | 882.509971ns | 890.433340ns | +| 2048 | 100 | 1 | 203.609400ms | 1.837764ms | 43.725756us | 897.345801ns | 893.424351ns | 901.847134ns | +| 4096 | 100 | 1 | 391.410300ms | 3.870556ms | 83.233563us | 944.959976ns | 941.130142ns | 949.114746ns | +| 8192 | 100 | 1 | 795.626000ms | 7.780079ms | 97.857467us | 949.716643ns | 947.494556ns | 952.193033ns | +| 16384 | 100 | 1 | 1623.647000ms | 16.095192ms | 176.318422us | 982.372531ns | 980.370444ns | 984.608819ns | +| 32768 | 100 | 1 | 3301.038600ms | 33.148607ms | 384.229519us | 1.011615us | 1.009492us | 1.014106us | ## SV-SingleBattle-AnalyzeEffect-RandomInputs-OneRandomBattle-OneRandomInput | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 42.531000ms | 109.316270us | 38.460601us | 109.316270us | 100.665190us | 115.998430us | -| 2 | 100 | 1 | 24.734500ms | 159.139560us | 53.088410us | 79.569780us | 73.441425us | 84.020155us | -| 4 | 100 | 1 | 36.997300ms | 249.772020us | 83.730606us | 62.443005us | 57.521828us | 65.933352us | -| 8 | 100 | 1 | 60.052200ms | 436.180360us | 146.308428us | 54.522545us | 50.184251us | 57.547200us | -| 16 | 100 | 1 | 117.921000ms | 761.197030us | 271.738756us | 47.574814us | 43.601292us | 50.407463us | -| 32 | 100 | 1 | 183.921600ms | 1.408681ms | 500.568681us | 44.021271us | 40.370078us | 46.649134us | -| 64 | 100 | 1 | 371.654900ms | 2.710436ms | 964.963685us | 42.350570us | 38.844759us | 44.865624us | -| 128 | 100 | 1 | 766.951700ms | 5.243822ms | 1.870609ms | 40.967362us | 37.561996us | 43.412360us | -| 256 | 100 | 1 | 1469.585900ms | 10.420130ms | 3.731715ms | 40.703634us | 37.301496us | 43.143086us | -| 512 | 100 | 1 | 3099.200000ms | 21.713813ms | 7.821248ms | 42.409791us | 38.857384us | 44.979531us | -| 1024 | 100 | 1 | 7386.275000ms | 54.349521ms | 19.476992ms | 53.075705us | 48.617242us | 56.244592us | -| 2048 | 100 | 1 | 13506.667400ms | 125.996615ms | 44.999922ms | 61.521785us | 56.316566us | 65.176185us | -| 4096 | 100 | 1 | 28557.440000ms | 268.259960ms | 95.773962ms | 65.493154us | 60.013779us | 69.396059us | +| 1 | 100 | 1 | 14.015100ms | 89.379360us | 29.402899us | 89.379360us | 82.966480us | 94.617710us | +| 2 | 100 | 1 | 17.163900ms | 118.764530us | 38.436526us | 59.382265us | 55.090790us | 62.691225us | +| 4 | 100 | 1 | 23.072100ms | 167.545470us | 54.105146us | 41.886367us | 38.774960us | 44.182360us | +| 8 | 100 | 1 | 35.796000ms | 258.874210us | 84.213013us | 32.359276us | 29.924467us | 34.130726us | +| 16 | 100 | 1 | 51.182100ms | 437.330450us | 146.665676us | 27.333153us | 25.249321us | 28.901304us | +| 32 | 100 | 1 | 102.245700ms | 773.093330us | 248.564255us | 24.159167us | 22.377807us | 25.474037us | +| 64 | 100 | 1 | 232.327400ms | 1.423977ms | 464.632705us | 22.249636us | 20.587487us | 23.477125us | +| 128 | 100 | 1 | 443.885000ms | 2.747839ms | 897.953542us | 21.467495us | 19.848680us | 22.648245us | +| 256 | 100 | 1 | 576.809700ms | 5.317923ms | 1.760734ms | 20.773135us | 19.221506us | 21.949039us | +| 512 | 100 | 1 | 1114.246400ms | 10.240397ms | 3.373785ms | 20.000776us | 18.484790us | 21.124387us | +| 1024 | 100 | 1 | 2247.263900ms | 20.564683ms | 6.860419ms | 20.082698us | 18.557170us | 21.228746us | +| 2048 | 100 | 1 | 4227.531700ms | 42.715353ms | 14.364216ms | 20.857106us | 19.272759us | 22.057492us | +| 4096 | 100 | 1 | 10849.898800ms | 88.098303ms | 29.610923ms | 21.508375us | 19.874705us | 22.754319us | +| 8192 | 100 | 1 | 23739.510900ms | 187.444712ms | 63.531659ms | 22.881435us | 21.149769us | 24.230355us | +| 16384 | 100 | 1 | 48676.663900ms | 385.924891ms | 131.034838ms | 23.554986us | 21.747504us | 24.930574us | ## SV-SingleBattle-AnalyzeEffect-RandomInputs-OneRandomBattle-ManyRandomInputs | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 41.637000ms | 109.643980us | 41.596182us | 109.643980us | 100.861190us | 117.269310us | -| 2 | 100 | 1 | 23.129500ms | 148.812360us | 27.161198us | 74.406180us | 71.138555us | 76.643275us | -| 4 | 100 | 1 | 23.123600ms | 197.375650us | 20.619914us | 49.343913us | 48.258955us | 50.280367us | -| 8 | 100 | 1 | 41.765200ms | 296.526250us | 26.812168us | 37.065781us | 36.350027us | 37.670425us | -| 16 | 100 | 1 | 62.517200ms | 492.483370us | 39.648213us | 30.780211us | 30.307544us | 31.277875us | -| 32 | 100 | 1 | 99.103600ms | 874.124690us | 49.750100us | 27.316397us | 27.019154us | 27.628301us | -| 64 | 100 | 1 | 182.087300ms | 1.669714ms | 87.652872us | 26.089282us | 25.818790us | 26.357352us | -| 128 | 100 | 1 | 384.885300ms | 3.322508ms | 139.562149us | 25.957095us | 25.748588us | 26.175857us | -| 256 | 100 | 1 | 746.072400ms | 7.409611ms | 291.348554us | 28.943793us | 28.721938us | 29.167358us | -| 512 | 100 | 1 | 1831.934600ms | 18.025901ms | 599.538927us | 35.206838us | 34.982078us | 35.442248us | -| 1024 | 100 | 1 | 4495.163900ms | 44.641554ms | 1.175760ms | 43.595268us | 43.378296us | 43.830676us | -| 2048 | 100 | 1 | 10321.920600ms | 105.533562ms | 2.592880ms | 51.530060us | 51.279321us | 51.774961us | -| 4096 | 100 | 1 | 23177.237100ms | 233.712622ms | 4.984853ms | 57.058746us | 56.822787us | 57.301496us | +| 1 | 100 | 1 | 14.264500ms | 90.206140us | 29.048567us | 90.206140us | 83.858240us | 95.359690us | +| 2 | 100 | 1 | 17.314200ms | 115.941590us | 13.545431us | 57.970795us | 56.934520us | 59.760240us | +| 4 | 100 | 1 | 19.335100ms | 148.389520us | 17.085416us | 37.097380us | 36.340885us | 38.030793us | +| 8 | 100 | 1 | 31.087200ms | 219.757700us | 25.138538us | 27.469712us | 26.917089us | 28.165299us | +| 16 | 100 | 1 | 46.248800ms | 336.541010us | 31.904953us | 21.033813us | 20.674353us | 21.461407us | +| 32 | 100 | 1 | 69.011700ms | 607.908410us | 37.441701us | 18.997138us | 18.761706us | 19.221494us | +| 64 | 100 | 1 | 136.789700ms | 1.168939ms | 58.701380us | 18.264676us | 18.088168us | 18.446317us | +| 128 | 100 | 1 | 265.082200ms | 2.545773ms | 135.818576us | 19.888855us | 19.683653us | 20.099171us | +| 256 | 100 | 1 | 658.302600ms | 6.079851ms | 271.970482us | 23.749418us | 23.551003us | 23.967573us | +| 512 | 100 | 1 | 1551.688000ms | 15.637741ms | 536.187438us | 30.542462us | 30.336983us | 30.748214us | +| 1024 | 100 | 1 | 4158.878100ms | 40.751015ms | 1.285745ms | 39.795913us | 39.560420us | 40.052851us | +| 2048 | 100 | 1 | 10009.695000ms | 100.369033ms | 2.604113ms | 49.008317us | 48.760195us | 49.258035us | +| 4096 | 100 | 1 | 22328.018000ms | 228.021804ms | 4.051899ms | 55.669386us | 55.477999us | 55.866209us | +| 8192 | 100 | 1 | 48203.900400ms | 489.236563ms | 7.836421ms | 59.721260us | 59.539786us | 59.915870us | +| 16384 | 100 | 1 | 100309.384500ms | 1021.017615ms | 12.709936ms | 62.317970us | 62.167280us | 62.472558us | ## SV-SingleBattle-AnalyzeEffect-RandomInputs-ManyRandomBattles-ManyRandomInputs | Inputs | Samples | Iterations / Sample | Estimated Completion Time | Mean of Samples | Standard Deviation | Mean / Input Count | Mean Lower Bound / Input Count | Mean Upper Bound / Input Count | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | 100 | 1 | 42.558300ms | 107.964890us | 38.505708us | 107.964890us | 99.406580us | 114.627270us | -| 2 | 100 | 1 | 28.954100ms | 173.803840us | 30.404899us | 86.901920us | 83.405830us | 89.482370us | -| 4 | 100 | 1 | 45.509200ms | 291.029400us | 34.254968us | 72.757350us | 71.033250us | 74.395245us | -| 8 | 100 | 1 | 67.321700ms | 504.829130us | 44.757951us | 63.103641us | 61.929281us | 64.125856us | -| 16 | 100 | 1 | 124.245500ms | 921.545720us | 65.010962us | 57.596607us | 56.780442us | 58.371183us | -| 32 | 100 | 1 | 221.935600ms | 1.700471ms | 81.624776us | 53.139726us | 52.624607us | 53.627066us | -| 64 | 100 | 1 | 435.242300ms | 3.309236ms | 136.302712us | 51.706820us | 51.273590us | 52.106788us | -| 128 | 100 | 1 | 819.701900ms | 6.258337ms | 180.803823us | 48.893258us | 48.614581us | 49.166315us | -| 256 | 100 | 1 | 1696.559600ms | 12.474110ms | 434.037386us | 48.726991us | 48.410046us | 49.076310us | -| 512 | 100 | 1 | 3393.983800ms | 27.957184ms | 1.081162ms | 54.603874us | 54.210460us | 55.037221us | -| 1024 | 100 | 1 | 7789.308400ms | 72.546752ms | 2.116763ms | 70.846437us | 70.455347us | 71.267056us | -| 2048 | 100 | 1 | 16209.502400ms | 161.259813ms | 2.107425ms | 78.740143us | 78.547132us | 78.947634us | -| 4096 | 100 | 1 | 33531.482700ms | 340.105325ms | 2.852476ms | 83.033527us | 82.898180us | 83.170424us | +| 1 | 100 | 1 | 14.443300ms | 89.508500us | 29.350274us | 89.508500us | 83.127520us | 94.730250us | +| 2 | 100 | 1 | 18.431500ms | 129.523140us | 22.955727us | 64.761570us | 62.520840us | 67.035790us | +| 4 | 100 | 1 | 26.085700ms | 192.032470us | 18.546889us | 48.008117us | 47.043235us | 48.863107us | +| 8 | 100 | 1 | 43.938800ms | 312.720100us | 32.801661us | 39.090013us | 38.259749us | 39.866901us | +| 16 | 100 | 1 | 63.950000ms | 513.112620us | 37.897235us | 32.069539us | 31.596078us | 32.528343us | +| 32 | 100 | 1 | 121.276600ms | 906.939940us | 45.832578us | 28.341873us | 28.061428us | 28.621278us | +| 64 | 100 | 1 | 214.976400ms | 1.688651ms | 78.004462us | 26.385173us | 26.139625us | 26.617205us | +| 128 | 100 | 1 | 394.588500ms | 3.219096ms | 101.783063us | 25.149186us | 24.993880us | 25.305009us | +| 256 | 100 | 1 | 736.303900ms | 6.227213ms | 159.458198us | 24.325049us | 24.202862us | 24.448462us | +| 512 | 100 | 1 | 1454.820300ms | 12.157858ms | 251.844087us | 23.745817us | 23.651304us | 23.844751us | +| 1024 | 100 | 1 | 2765.638200ms | 24.305804ms | 677.094068us | 23.736137us | 23.617806us | 23.879778us | +| 2048 | 100 | 1 | 5408.036800ms | 51.303629ms | 1.184174ms | 25.050600us | 24.945373us | 25.173154us | +| 4096 | 100 | 1 | 10411.095500ms | 105.463330ms | 1.254550ms | 25.747883us | 25.690347us | 25.810688us | +| 8192 | 100 | 1 | 22601.287600ms | 224.739582ms | 1.663130ms | 27.434031us | 27.396160us | 27.475692us | +| 16384 | 100 | 1 | 46901.729800ms | 468.131335ms | 2.517891ms | 28.572469us | 28.543054us | 28.603217us | diff --git a/src/AnalyzeEffect/AnalyzeEffect.cpp b/src/AnalyzeEffect/AnalyzeEffect.cpp index aef9ce6..d5ab68e 100644 --- a/src/AnalyzeEffect/AnalyzeEffect.cpp +++ b/src/AnalyzeEffect/AnalyzeEffect.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -274,11 +275,14 @@ void ignoreBattlesWithEffectActive(Simulation& simulation) { types::entity createAnalyzeEffectMove( types::registry& registry, dex::Move move, types::entity battleEntity, types::entity attackerEntity, - types::entity defenderEntity, const Pokedex& pokedex) { - types::entity moveEntity = - createActionMoveForTarget({registry, defenderEntity}, battleEntity, attackerEntity, move, pokedex); + types::entity defenderEntity) { + types::entity moveEntity = registry.create(); + + setupActionMoveBuild(registry, battleEntity, attackerEntity, defenderEntity, moveEntity, move); + registry.emplace(moveEntity, move); registry.emplace(moveEntity); + registry.emplace(moveEntity); registry.emplace_or_replace(attackerEntity); registry.emplace_or_replace(defenderEntity); @@ -286,22 +290,20 @@ types::entity createAnalyzeEffectMove( } void createOneCalculationMovePair( - types::handle inputHandle, Battle battle, EffectMove move, Attacker attacker, Defender defender, - const Pokedex& pokedex) { + types::handle inputHandle, Battle battle, EffectMove move, Attacker attacker, Defender defender) { entt::entity moveEntity = - createAnalyzeEffectMove(*inputHandle.registry(), move.val, battle.val, attacker.val, defender.val, pokedex); + createAnalyzeEffectMove(*inputHandle.registry(), move.val, battle.val, attacker.val, defender.val); inputHandle.emplace(moveEntity, moveEntity); } void createTwoCalculationsMovePair( types::handle inputHandle, Battle battle, EffectMove move, Attacker attacker, Defender defender, - const OriginalInputEntities& originals, const Pokedex& pokedex) { + const OriginalInputEntities& originals) { types::registry& registry = *inputHandle.registry(); entt::entity originalEntity = - createAnalyzeEffectMove(registry, move.val, originals.battle, originals.attacker, originals.defender, pokedex); - entt::entity copyEntity = - createAnalyzeEffectMove(registry, move.val, battle.val, attacker.val, defender.val, pokedex); + createAnalyzeEffectMove(registry, move.val, originals.battle, originals.attacker, originals.defender); + entt::entity copyEntity = createAnalyzeEffectMove(registry, move.val, battle.val, attacker.val, defender.val); // All active pokemon in should have their stats refreshed in doubles for moves like Beat Up which rely on the stats // of Pokemon outside of the attacker and defender @@ -415,9 +417,10 @@ void createAppliedEffectBattles(Simulation& simulation) { } } - simulation.view, entt::exclude_t>( - simulation.pokedex()); - simulation.view(simulation.pokedex()); + simulation.view, entt::exclude_t>(); + simulation.view(); + simulation.pokedex().buildMoves(simulation.registry); + simulation.registry.clear(); } void applyPseudoWeatherEffect(types::handle, Battle, PseudoWeatherName) {} diff --git a/src/AnalyzeEffect/AnalyzeEffectDebugChecks.hpp b/src/AnalyzeEffect/AnalyzeEffectDebugChecks.hpp index 3931478..0d5ed79 100644 --- a/src/AnalyzeEffect/AnalyzeEffectDebugChecks.hpp +++ b/src/AnalyzeEffect/AnalyzeEffectDebugChecks.hpp @@ -33,12 +33,10 @@ namespace pokesim::analyze_effect::debug { struct Checks : pokesim::debug::Checks { - Options options; - Checks(const Simulation& _simulation) - : pokesim::debug::Checks(_simulation), options(_simulation.analyzeEffectOptions) {} + Checks(const Simulation& _simulation) : pokesim::debug::Checks(_simulation) {} void checkInputs() { - pokesim::debug::check(options.getDamageRollOptions()); + pokesim::debug::check(analyzeEffectOptionsOnInput.getDamageRollOptions()); auto view = registry->view(); types::entityVector inputs{view.begin(), view.end()}; @@ -62,8 +60,7 @@ struct Checks : pokesim::debug::Checks { } void checkOutputs() const { - POKESIM_REQUIRE_NM(options == simulation->analyzeEffectOptions); - + checkOptions(); types::entityIndex finalEntityCount = getFinalEntityCount(); POKESIM_REQUIRE_NM(initialEntityCount == finalEntityCount); checkInputOutputs(); diff --git a/src/AnalyzeEffect/Setup/AnalyzeEffectInputSetup.hpp b/src/AnalyzeEffect/Setup/AnalyzeEffectInputSetup.hpp index c3cf0bd..710ad41 100644 --- a/src/AnalyzeEffect/Setup/AnalyzeEffectInputSetup.hpp +++ b/src/AnalyzeEffect/Setup/AnalyzeEffectInputSetup.hpp @@ -16,7 +16,6 @@ struct InputSetup { public: InputSetup(types::registry& registry, types::entity entity); - InputSetup(types::registry& registry) : InputSetup(registry, registry.create()) {} void setAttacker(types::entity entity); void setEffectTarget(types::entity entity); diff --git a/src/Battle/Clone/Clone.cpp b/src/Battle/Clone/Clone.cpp index 451fae8..12831dd 100644 --- a/src/Battle/Clone/Clone.cpp +++ b/src/Battle/Clone/Clone.cpp @@ -3,12 +3,12 @@ #include #include #include -#include +#include #include -#include #include #include #include +#include #include #include #include @@ -42,20 +42,26 @@ void traverseBattle(types::registry& registry, VisitEntity visitEntity = nullptr const static bool ForCloning = !std::is_same_v; using Tag = std::conditional_t; - for (const auto [entity, sides, actionQueue] : registry.view().each()) { + for (const auto [entity, sides] : registry.view().each()) { for (auto side : sides.val) { registry.emplace(side); } - for (auto queueItem : actionQueue.val) { - registry.emplace(queueItem); - } if constexpr (ForCloning) { visitEntity(entity); } } - for (const auto [entity, currentAction] : registry.view().each()) { - registry.emplace(currentAction.val); + + for (const auto [entity, recycledAction, recycledActionMove] : + registry.view().each()) { + registry.emplace(recycledAction.val); + registry.emplace(recycledActionMove.val); + } + + for (const auto [entity, addedRecycledActionMove1, addedRecycledActionMove2] : + registry.view().each()) { + registry.emplace(addedRecycledActionMove1.val); + registry.emplace(addedRecycledActionMove2.val); } } @@ -81,19 +87,19 @@ void traverseAction(types::registry& registry, VisitEntity visitEntity = nullptr using Tag = std::conditional_t; if constexpr (ForCloning) { - for (types::entity entity : registry.view()) { + for (types::entity entity : registry.view()) { visitEntity(entity); } - } -} -template -void traverseCurrentActionMove(types::registry& registry, VisitEntity visitEntity = nullptr) { - const static bool ForCloning = !std::is_same_v; - using Tag = std::conditional_t; + for (types::entity entity : registry.view()) { + visitEntity(entity); + } - if constexpr (ForCloning) { - for (types::entity entity : registry.view()) { + for (types::entity entity : registry.view()) { + visitEntity(entity); + } + + for (types::entity entity : registry.view()) { visitEntity(entity); } } @@ -104,12 +110,8 @@ void traversePokemon(types::registry& registry, VisitEntity visitEntity = nullpt const static bool ForCloning = !std::is_same_v; using Tag = std::conditional_t; - for (const auto [entity, moveSlots] : registry.view().each()) { - for (auto move : moveSlots.val) { - registry.emplace(move); - } - - if constexpr (ForCloning) { + if constexpr (ForCloning) { + for (types::entity entity : registry.view()) { visitEntity(entity); } } @@ -119,6 +121,7 @@ void traversePokemon(types::registry& registry, VisitEntity visitEntity = nullpt registry.emplace_or_replace(move); } } + for (const auto [entity, moves] : registry.view().each()) { for (types::entity move : moves.val) { registry.emplace_or_replace(move); @@ -126,18 +129,6 @@ void traversePokemon(types::registry& registry, VisitEntity visitEntity = nullpt } } -template -void traverseMove(types::registry& registry, VisitEntity visitEntity = nullptr) { - const static bool ForCloning = !std::is_same_v; - using Tag = std::conditional_t; - - if constexpr (ForCloning) { - for (types::entity entity : registry.view()) { - visitEntity(entity); - } - } -} - void cloneBattle( types::registry& registry, types::ClonedEntityMap& entityMap, entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { @@ -162,14 +153,6 @@ void cloneAction( }); } -void cloneCurrentActionMove( - types::registry& registry, types::ClonedEntityMap& entityMap, - entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { - traverseCurrentActionMove(registry, [&](types::entity entity) { - cloneEntity(entity, registry, entityMap, srcEntityStorages, cloneCount); - }); -} - void clonePokemon( types::registry& registry, types::ClonedEntityMap& entityMap, entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { @@ -178,14 +161,6 @@ void clonePokemon( }); } -void cloneMove( - types::registry& registry, types::ClonedEntityMap& entityMap, - entt::dense_map& srcEntityStorages, types::entityIndex cloneCount) { - traverseMove(registry, [&](types::entity entity) { - cloneEntity(entity, registry, entityMap, srcEntityStorages, cloneCount); - }); -} - void deleteBattle(types::registry& registry) { traverseBattle(registry); } @@ -198,18 +173,10 @@ void deleteAction(types::registry& registry) { traverseAction(registry); } -void deleteCurrentActionMove(types::registry& registry) { - traverseCurrentActionMove(registry); -} - void deletePokemon(types::registry& registry) { traversePokemon(registry); } -void deleteMove(types::registry& registry) { - traverseMove(registry); -} - void remapEntity(types::entity& entity, const CloneTo& cloneTo, const types::ClonedEntityMap& entityMap) { POKESIM_REQUIRE(entityMap.contains(entity), "Source node was not loaded into the map."); POKESIM_REQUIRE( @@ -259,8 +226,6 @@ types::ClonedEntityMap clone(types::registry& registry, std::optional(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); - remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); + remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); remapComponentEntities(registry, entityMap); @@ -337,8 +299,6 @@ void deleteClones(types::registry& registry) { deleteSide(registry); deleteAction(registry); deletePokemon(registry); - deleteCurrentActionMove(registry); - deleteMove(registry); auto remove = registry.view(); registry.destroy(remove.begin(), remove.end()); } diff --git a/src/Battle/Helpers/Helpers.cpp b/src/Battle/Helpers/Helpers.cpp index 5f03663..5aa15ed 100644 --- a/src/Battle/Helpers/Helpers.cpp +++ b/src/Battle/Helpers/Helpers.cpp @@ -1,13 +1,15 @@ #include "Helpers.hpp" +#include #include #include -#include #include #include +#include #include #include #include +#include #include #include #include @@ -89,41 +91,30 @@ types::entity slotToAllyPokemonEntity(const types::registry& registry, const Sid return allyEntity; } -types::entity moveToEntity(const types::registry& registry, const MoveSlots& moveSlots, dex::Move move) { - for (types::entity moveSlot : moveSlots.val) { - if (registry.get(moveSlot).val == move) { - return moveSlot; +types::moveSlotIndex moveToMoveSlot(const MoveSlots& moveSlots, dex::Move move) { + for (types::moveSlotIndex i = 0; i < moveSlots.val.size(); i++) { + if (moveSlots.val[i].move == move) { + return i; } } - POKESIM_REQUIRE_FAIL("No move of entity found."); - return entt::null; + POKESIM_REQUIRE_FAIL("No move found."); + return 0U; } -types::entity createActionMoveForTarget( - types::handle targetHandle, types::entity battleEntity, types::entity sourceEntity, dex::Move move, - const Pokedex& pokedex) { - types::registry& registry = *targetHandle.registry(); - types::entity moveEntity = pokedex.buildActionMove(move, registry); +void setupActionMoveBuild( + types::registry& registry, types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, + types::entity actionMoveEntity, dex::Move move) { + types::handle actionMoveHandle{registry, actionMoveEntity}; - registry.emplace(moveEntity); - registry.emplace(moveEntity, battleEntity); - registry.emplace(moveEntity, sourceEntity); - registry.emplace(moveEntity, targetHandle.entity()); + move::tags::emplaceTagFromEnum(move, actionMoveHandle); + actionMoveHandle.emplace(battleEntity); + actionMoveHandle.emplace(sourceEntity); + actionMoveHandle.emplace(targetEntity); + actionMoveHandle.emplace(); + actionMoveHandle.emplace(); - targetHandle.get_or_emplace().val.push_back(moveEntity); - registry.get_or_emplace(sourceEntity).val.push_back(moveEntity); - - if (registry.all_of(battleEntity)) { - registry.emplace(moveEntity); - } - if (registry.all_of(battleEntity)) { - registry.emplace(moveEntity); - } - if (registry.all_of(battleEntity)) { - registry.emplace(moveEntity); - } - - return moveEntity; + registry.get_or_emplace(targetEntity).val.push_back(actionMoveEntity); + registry.get_or_emplace(sourceEntity).val.push_back(actionMoveEntity); } } // namespace pokesim diff --git a/src/Battle/Helpers/Helpers.hpp b/src/Battle/Helpers/Helpers.hpp index 6a678f1..20a78d9 100644 --- a/src/Battle/Helpers/Helpers.hpp +++ b/src/Battle/Helpers/Helpers.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace pokesim { struct Sides; @@ -14,9 +15,9 @@ types::entity slotToSideEntity(const Sides& sides, Slot targetSlot); types::entity slotToPokemonEntity(const types::registry& registry, types::entity sideEntity, Slot targetSlot); types::entity slotToPokemonEntity(const types::registry& registry, const Sides& sides, Slot targetSlot); types::entity slotToAllyPokemonEntity(const types::registry& registry, const Sides& sides, Slot targetSlot); -types::entity moveToEntity(const types::registry& registry, const MoveSlots& moveSlots, dex::Move move); +types::moveSlotIndex moveToMoveSlot(const MoveSlots& moveSlots, dex::Move move); -types::entity createActionMoveForTarget( - types::handle targetHandle, types::entity battleEntity, types::entity sourceEntity, dex::Move move, - const Pokedex& pokedex); +void setupActionMoveBuild( + types::registry& registry, types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, + types::entity actionMoveEntity, dex::Move move); } // namespace pokesim diff --git a/src/Battle/ManageBattleState.cpp b/src/Battle/ManageBattleState.cpp index 35f2cd1..35c0e4b 100644 --- a/src/Battle/ManageBattleState.cpp +++ b/src/Battle/ManageBattleState.cpp @@ -1,19 +1,37 @@ #include "ManageBattleState.hpp" #include +#include +#include +#include +#include +#include +#include #include #include #include -#include +#include +#include +#include #include #include #include #include +#include +#include +#include #include #include +#include #include +#include +#include #include +#include +#include #include +#include +#include #include #include #include @@ -59,6 +77,23 @@ void updateCurrentActionTargets(types::registry& registry, CurrentActionTargets& targets.val.pop_count(deleteCount); } + +template +void clearActionMoveComponents(types::registry& registry, const View& view) { + registry.remove< + tags::SimulateTurn, + tags::CalculateDamage, + tags::AnalyzeEffect, + Battle, + TypeName, + AtkBoost, + DefBoost, + SpaBoost, + SpdBoost, + SpeBoost, + status::tags::Paralysis, + status::tags::Burn>(view.begin(), view.end()); +} } // namespace void assignRootBattle(types::handle battleHandle) { @@ -82,8 +117,7 @@ void setCurrentActionSource(types::handle battleHandle, const Sides& sides, Curr } void setCurrentActionTarget( - types::handle battleHandle, const Sides& sides, CurrentAction action, CurrentActionSource source, - const Simulation& simulation) { + types::handle battleHandle, const Sides& sides, CurrentAction action, const Simulation& simulation) { types::registry& registry = *battleHandle.registry(); const TargetSlotName& targetSlotName = registry.get(action.val); types::entity targetEntity = slotToPokemonEntity(registry, sides, targetSlotName.val); @@ -98,8 +132,6 @@ void setCurrentActionTarget( if (pickedTarget) { battleHandle.emplace(types::targets{targetEntity}); - registry.emplace(targetEntity); - registry.emplace(targetEntity, source); } else { types::entity sourceEntity = battleHandle.get().val; @@ -108,22 +140,6 @@ void setCurrentActionTarget( } } -void setCurrentActionMove( - types::handle battleHandle, CurrentActionSource source, const CurrentActionTargets& targets, CurrentAction action, - const Pokedex& pokedex) { - types::registry& registry = *battleHandle.registry(); - const MoveName& move = registry.get(action.val); - const MoveSlots& moveSlots = registry.get(source.val); - - for (types::entity target : targets.val) { - createActionMoveForTarget({registry, target}, battleHandle.entity(), source.val, move.val, pokedex); - } - - types::entity moveSlotEntity = moveToEntity(registry, moveSlots, move.val); - battleHandle.emplace(moveSlotEntity); - registry.emplace(moveSlotEntity); -} - void setFailedActionMove( types::handle moveHandle, Battle battle, CurrentActionSource source, CurrentActionTarget target) { types::registry& registry = *moveHandle.registry(); @@ -145,44 +161,62 @@ void setFailedActionMove( registry.emplace(battle.val, target.val); } - types::entity moveSlotEntity = registry.get(battle.val).val; registry.erase(battle.val); - registry.erase(moveSlotEntity); updateCurrentActionTargets(registry, registry.get(battle.val)); } void clearCurrentAction(Simulation& simulation) { types::registry& registry = simulation.registry; - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - registry.clear(); - - auto actionMoves = registry.view(); - auto failedActionMoves = registry.view(); - auto currentActions = registry.view(); - registry.destroy(actionMoves.begin(), actionMoves.end()); - registry.destroy(failedActionMoves.begin(), failedActionMoves.end()); - registry.destroy(currentActions.begin(), currentActions.end()); - - auto battles = simulation.battleEntities(); - registry.remove< + registry.clear< + CurrentAction, + CurrentActionTargets, + CurrentActionSource, + CurrentActionTarget, + FailedCurrentActionSource, + FailedCurrentActionTarget, + CurrentActionMovesAsSource, + CurrentActionMovesAsTarget, + CurrentActionMoveSlot, + tags::CurrentActionMoveSource, + tags::CurrentActionMoveTarget, + tags::CurrentActionMoveSlot, + tags::CurrentMoveHit, + tags::FailedCurrentMoveHit>(); + + registry.clear< + move::effect::tags::Primary, + move::effect::tags::Secondary, + move::effect::tags::MoveSource, + move::effect::tags::MoveTarget, + move::tags::FuryAttack, + move::tags::KnockOff, + move::tags::Moonblast, + move::tags::QuiverDance, + move::tags::Splash, + move::tags::Thunderbolt, + move::tags::WillOWisp, + move::tags::Physical, + move::tags::Special, + move::tags::Status, + move::tags::Contact, + move::tags::BypassSubstitute, + move::tags::Punch, + move::tags::VariableHitCount, + BaseEffectChance, + Accuracy, + BasePower, + HitCount, + move::tags::AccuracyDependentHitCount, + move::tags::Self, + move::tags::AnySingleTarget, + move::tags::AnySingleAlly>(); + + registry.clear(); + + registry.clear< action::tags::Item, - ItemName, action::tags::Move, - MoveName, action::tags::BeforeTurn, action::tags::Dynamax, action::tags::MegaEvolve, @@ -195,6 +229,18 @@ void clearCurrentAction(Simulation& simulation) { action::tags::RevivalBlessing, action::tags::Switch, action::tags::SwitchOut, - action::tags::Terastallize>(battles.begin(), battles.end()); + action::tags::Terastallize>(); + + auto actionMoves = registry.view(); + auto failedActionMoves = registry.view(); + clearActionMoveComponents(registry, actionMoves); + clearActionMoveComponents(registry, failedActionMoves); + registry.clear(); + + simulation.removeFromEntities(); + registry.clear(); + + auto battles = registry.view(); + registry.remove(battles.begin(), battles.end()); } } // namespace pokesim diff --git a/src/Battle/ManageBattleState.hpp b/src/Battle/ManageBattleState.hpp index a3afb94..3998324 100644 --- a/src/Battle/ManageBattleState.hpp +++ b/src/Battle/ManageBattleState.hpp @@ -18,11 +18,7 @@ void collectTurnOutcomeBattles(types::handle leafBattleHandle, const RootBattle& void setCurrentActionSource(types::handle battleHandle, const Sides& sides, CurrentAction action); void setCurrentActionTarget( - types::handle battleHandle, const Sides& sides, CurrentAction action, CurrentActionSource source, - const Simulation& simulation); -void setCurrentActionMove( - types::handle battleHandle, CurrentActionSource source, const CurrentActionTargets& targets, CurrentAction action, - const Pokedex& pokedex); + types::handle battleHandle, const Sides& sides, CurrentAction action, const Simulation& simulation); void setFailedActionMove( types::handle moveHandle, Battle battle, CurrentActionSource source, CurrentActionTarget target); void clearCurrentAction(Simulation& simulation); diff --git a/src/Battle/Pokemon/ManagePokemonState.cpp b/src/Battle/Pokemon/ManagePokemonState.cpp index cc48432..073ef40 100644 --- a/src/Battle/Pokemon/ManagePokemonState.cpp +++ b/src/Battle/Pokemon/ManagePokemonState.cpp @@ -2,16 +2,18 @@ #include #include +#include +#include #include #include -#include #include #include -#include +#include +#include #include #include #include -#include +#include #include #include #include @@ -227,9 +229,13 @@ void clearVolatiles(types::handle pokemonHandle) { pokemonHandle.remove(); } -void deductPp(Pp& pp) { - if (pp.val) { - pp.val -= 1U; // TODO(aed3): Make this into a mechanic constant +void deductPp(MoveSlots& moveSlots, LastUsedMove lastUsedMove) { + MoveSlot& moveSlot = moveSlots.val[lastUsedMove.val]; + if (moveSlot.pp >= Constants::PP_USE_DEDUCTION + Constants::MovePp::MIN) { + moveSlot.pp -= Constants::PP_USE_DEDUCTION; + } + else { + moveSlot.pp = Constants::MovePp::MIN; } } diff --git a/src/Battle/Pokemon/ManagePokemonState.hpp b/src/Battle/Pokemon/ManagePokemonState.hpp index 6c3b4b5..68a3e19 100644 --- a/src/Battle/Pokemon/ManagePokemonState.hpp +++ b/src/Battle/Pokemon/ManagePokemonState.hpp @@ -12,7 +12,8 @@ struct CurrentActionSource; struct CurrentActionTarget; struct CurrentActionMoveSlot; struct Damage; -struct Pp; +struct LastUsedMove; +struct MoveSlots; namespace stat { struct Atk; @@ -37,7 +38,7 @@ void clearStatus(types::handle pokemonHandle); void clearVolatiles(types::handle pokemonHandle); -void deductPp(Pp& pp); +void deductPp(MoveSlots& moveSlots, LastUsedMove lastUsedMove); void setLastMoveUsed(types::registry& registry, CurrentActionSource source, const CurrentActionMoveSlot& move); void resetEffectiveAtk(types::handle handle, stat::Atk atk); void resetEffectiveDef(types::handle handle, stat::Def def); diff --git a/src/Battle/Setup/BattleStateSetup.cpp b/src/Battle/Setup/BattleStateSetup.cpp index 1344021..3a393d7 100644 --- a/src/Battle/Setup/BattleStateSetup.cpp +++ b/src/Battle/Setup/BattleStateSetup.cpp @@ -1,7 +1,8 @@ #include "BattleStateSetup.hpp" -#include +#include #include +#include #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -39,12 +41,33 @@ void BattleStateSetup::initBlank() { setProbability(Constants::Probability::DEFAULT); } +void BattleStateSetup::setRecycledAction(types::entity recycledAction, types::entity recycledActionMove) { + types::registry& registry = *handle.registry(); + + handle.emplace(recycledAction); + registry.emplace(recycledAction); + + handle.emplace(recycledActionMove); + registry.emplace(recycledActionMove); +} + +void BattleStateSetup::setAddedRecycledActionMoves( + types::entity addedRecycledActionMove1, types::entity addedRecycledActionMove2) { + types::registry& registry = *handle.registry(); + + handle.emplace(addedRecycledActionMove1); + registry.emplace(addedRecycledActionMove1); + + handle.emplace(addedRecycledActionMove2); + registry.emplace(addedRecycledActionMove2); +} + void BattleStateSetup::setAutoID() { setID((types::stateId)handle.registry()->view().size()); } void BattleStateSetup::setID(types::stateId id) { - handle.emplace_or_replace(id); + handle.emplace(id); } void BattleStateSetup::setSide(types::entity sideEntity) { @@ -70,7 +93,7 @@ void BattleStateSetup::setRNGSeed(std::optional seed) { handle.emplace(seed.value()); } -void BattleStateSetup::setActionQueue(const std::vector& queue) { +void BattleStateSetup::setActionQueue(const std::vector& queue) { handle.emplace(queue); } @@ -78,32 +101,6 @@ void BattleStateSetup::setTurn(types::battleTurn turn) { handle.emplace(turn); } -void BattleStateSetup::setCurrentActionTarget(types::targets actionTargets) { - handle.emplace(actionTargets); - for (types::entity entity : actionTargets) { - handle.registry()->emplace(entity); - } -} - -void BattleStateSetup::setCurrentActionSource(types::entity actionSource) { - handle.emplace(actionSource); - handle.registry()->emplace(actionSource); -} - -void BattleStateSetup::setCurrentActionMove(types::entity actionMove) { - handle.registry()->emplace(actionMove); - const auto* source = handle.try_get(); - const auto* targets = handle.try_get(); - if (source) { - handle.registry()->emplace(source->val).val.push_back(actionMove); - } - if (targets) { - for (types::entity target : targets->val) { - handle.registry()->emplace(target).val.push_back(actionMove); - } - } -} - void BattleStateSetup::setProbability(types::probability probability) { handle.emplace(probability); } diff --git a/src/Battle/Setup/BattleStateSetup.hpp b/src/Battle/Setup/BattleStateSetup.hpp index 9ecf303..f709dcc 100644 --- a/src/Battle/Setup/BattleStateSetup.hpp +++ b/src/Battle/Setup/BattleStateSetup.hpp @@ -14,11 +14,11 @@ namespace pokesim { struct SimulateTurnOptions; struct CalculateDamageOptions; struct AnalyzeEffectOptions; +struct ActionQueueItem; // Tool to set properties of a battle's state to an entity. struct BattleStateSetup : internal::StateSetupBase { BattleStateSetup() : internal::StateSetupBase() {} - BattleStateSetup(types::registry& registry) : BattleStateSetup(registry, registry.create()) {} BattleStateSetup(types::registry& registry, types::entity entity); /** @@ -33,17 +33,16 @@ struct BattleStateSetup : internal::StateSetupBase { */ void initBlank(); + void setRecycledAction(types::entity recycledAction, types::entity recycledActionMove); + void setAddedRecycledActionMoves(types::entity addedRecycledActionMove1, types::entity addedRecycledActionMove2); void setAutoID(); void setID(types::stateId id); void setSide(types::entity sideEntity); // If a seed is not provided, the seed is set to a random number based on the current time in nanoseconds. void setRNGSeed(std::optional seed = std::nullopt); - void setActionQueue(const std::vector& queue); + void setActionQueue(const std::vector& queue); void setTurn(types::battleTurn turn); - void setCurrentActionTarget(types::targets actionTargets); - void setCurrentActionSource(types::entity actionSource); - void setCurrentActionMove(types::entity actionMove); void setProbability(types::probability probability); }; } // namespace pokesim diff --git a/src/Battle/Setup/MoveStateSetup.cpp b/src/Battle/Setup/MoveStateSetup.cpp deleted file mode 100644 index f51ae14..0000000 --- a/src/Battle/Setup/MoveStateSetup.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "MoveStateSetup.hpp" - -#include -#include -#include - -namespace pokesim { -void MoveStateSetup::initBlank() { - handle.emplace(); - handle.emplace(); - handle.emplace(); -} - -void MoveStateSetup::setName(dex::Move moveName) { - handle.emplace(moveName); -} - -void MoveStateSetup::setPP(types::pp pp) { - handle.emplace(pp); -} - -void MoveStateSetup::setMaxPP(types::pp maxPp) { - handle.emplace(maxPp); -} -} // namespace pokesim diff --git a/src/Battle/Setup/MoveStateSetup.hpp b/src/Battle/Setup/MoveStateSetup.hpp deleted file mode 100644 index 00c9d1c..0000000 --- a/src/Battle/Setup/MoveStateSetup.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "StateSetupBase.hpp" - -namespace pokesim { -// Tool to set properties of a move's state to an entity. -struct MoveStateSetup : internal::StateSetupBase { - MoveStateSetup() : internal::StateSetupBase() {} - MoveStateSetup(types::registry& registry) : MoveStateSetup(registry, registry.create()) {} - MoveStateSetup(types::registry& registry, types::entity entity) : StateSetupBase(registry, entity) {} - - /** - * @brief Applies the defaults to the required properties for a move state. - * - * Some of the required properties are a blank `MoveName`, `Pp`, and `MaxPp` component. - */ - void initBlank(); - - void setName(dex::Move moveName); - void setPP(types::pp pp); - void setMaxPP(types::pp maxPp); -}; -} // namespace pokesim diff --git a/src/Battle/Setup/PokemonStateSetup.cpp b/src/Battle/Setup/PokemonStateSetup.cpp index 282892e..77b5219 100644 --- a/src/Battle/Setup/PokemonStateSetup.cpp +++ b/src/Battle/Setup/PokemonStateSetup.cpp @@ -3,10 +3,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -90,13 +91,14 @@ void PokemonStateSetup::setItem(dex::Item item) { item::tags::emplaceTagFromEnum(item, handle); } -void PokemonStateSetup::setMoves(const std::vector& moveSlots) { - MoveSlots& moveEntities = handle.emplace(); +void PokemonStateSetup::setMoves(const std::vector& moveSlots) { + MoveSlots& newMoveSlots = handle.emplace(); POKESIM_REQUIRE( - moveSlots.size() <= moveEntities.val.max_size(), + moveSlots.size() <= newMoveSlots.val.max_size(), "Cannot add more moves to a Pokemon than MAX_MOVE_SLOTS."); - for (types::entity moveSlot : moveSlots) { - moveEntities.val.push_back(moveSlot); + + for (MoveSlot moveSlot : moveSlots) { + newMoveSlots.val.push_back(moveSlot); } } diff --git a/src/Battle/Setup/PokemonStateSetup.hpp b/src/Battle/Setup/PokemonStateSetup.hpp index 6d82aaa..4d1b42c 100644 --- a/src/Battle/Setup/PokemonStateSetup.hpp +++ b/src/Battle/Setup/PokemonStateSetup.hpp @@ -1,8 +1,6 @@ #pragma once #include -#include -#include #include #include #include @@ -21,10 +19,14 @@ #include "StateSetupBase.hpp" namespace pokesim { +struct Evs; +struct Ivs; +struct SpeciesTypes; +struct MoveSlot; + // Tool to set properties of a Pokemon's state to an entity. struct PokemonStateSetup : internal::StateSetupBase { PokemonStateSetup() : internal::StateSetupBase() {} - PokemonStateSetup(types::registry& registry) : PokemonStateSetup(registry, registry.create()) {} PokemonStateSetup(types::registry& registry, types::entity entity); operator types::entity() const { return entity(); } @@ -50,7 +52,7 @@ struct PokemonStateSetup : internal::StateSetupBase { void setGender(dex::Gender gender); void setAbility(dex::Ability ability); void setItem(dex::Item item); - void setMoves(const std::vector& moveSlots); + void setMoves(const std::vector& moveSlots); void setPostion(types::teamPositionIndex position); void setStatus(dex::Status status); diff --git a/src/Battle/Setup/SideStateSetup.cpp b/src/Battle/Setup/SideStateSetup.cpp index 17e88af..3898e0f 100644 --- a/src/Battle/Setup/SideStateSetup.cpp +++ b/src/Battle/Setup/SideStateSetup.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,10 +14,8 @@ #include "PokemonStateSetup.hpp" namespace pokesim { -SideStateSetup::SideStateSetup(types::registry& registry, types::entity entity, PlayerSideId playerSideId) - : StateSetupBase(registry, entity) { +SideStateSetup::SideStateSetup(types::registry& registry, types::entity entity) : StateSetupBase(registry, entity) { handle.emplace(); - handle.emplace(playerSideId); } void SideStateSetup::initBlank() { @@ -49,4 +48,8 @@ void SideStateSetup::setBattle(types::entity entity) { void SideStateSetup::setPlayerSide(PlayerSideId playerSideId) { handle.emplace(playerSideId); } + +void SideStateSetup::setSideDecision(const SideDecision& sideDecision) { + handle.emplace(sideDecision); +} } // namespace pokesim diff --git a/src/Battle/Setup/SideStateSetup.hpp b/src/Battle/Setup/SideStateSetup.hpp index 1557b02..a16ff6d 100644 --- a/src/Battle/Setup/SideStateSetup.hpp +++ b/src/Battle/Setup/SideStateSetup.hpp @@ -11,13 +11,12 @@ namespace pokesim { struct PokemonStateSetup; +struct SideDecision; // Tool to set properties of a player's side state to an entity. struct SideStateSetup : internal::StateSetupBase { SideStateSetup() : internal::StateSetupBase() {} - SideStateSetup(types::registry& registry, PlayerSideId playerSideId) - : SideStateSetup(registry, registry.create(), playerSideId) {} - SideStateSetup(types::registry& registry, types::entity entity, PlayerSideId playerSideId); + SideStateSetup(types::registry& registry, types::entity entity); /** * @brief Applies the defaults to the required properties for a player side's state. * @@ -29,5 +28,6 @@ struct SideStateSetup : internal::StateSetupBase { void setOpponent(types::entity entity); void setBattle(types::entity entity); void setPlayerSide(PlayerSideId playerSideId); + void setSideDecision(const SideDecision& sideDecision); }; } // namespace pokesim diff --git a/src/Battle/Setup/headers.hpp b/src/Battle/Setup/headers.hpp index e0c976b..af8de34 100644 --- a/src/Battle/Setup/headers.hpp +++ b/src/Battle/Setup/headers.hpp @@ -4,7 +4,6 @@ #include "BattleStateSetup.hpp" #include "EmplaceTagFromEnum.hpp" -#include "MoveStateSetup.hpp" #include "PokemonStateSetup.hpp" #include "SideStateSetup.hpp" #include "StateSetupBase.hpp" diff --git a/src/Battle/headers.hpp b/src/Battle/headers.hpp index 7d62041..a16b66a 100644 --- a/src/Battle/headers.hpp +++ b/src/Battle/headers.hpp @@ -10,7 +10,6 @@ #include "Pokemon/PokemonDataChecks.hpp" #include "Setup/BattleStateSetup.hpp" #include "Setup/EmplaceTagFromEnum.hpp" -#include "Setup/MoveStateSetup.hpp" #include "Setup/PokemonStateSetup.hpp" #include "Setup/SideStateSetup.hpp" #include "Setup/StateSetupBase.hpp" diff --git a/src/CalcDamage/CalcDamage.cpp b/src/CalcDamage/CalcDamage.cpp index 6ff2cf1..b9ab6be 100644 --- a/src/CalcDamage/CalcDamage.cpp +++ b/src/CalcDamage/CalcDamage.cpp @@ -457,7 +457,7 @@ void setUnboostedStat(Simulation& simulation) { updateSpd(simulation, true); } else { - static_assert("No other stat is used as the attacking or defending stat."); + POKESIM_REQUIRE_FAIL("No other stat is used as the attacking or defending stat."); } if constexpr (forAttacker) { diff --git a/src/CalcDamage/CalcDamageDebugChecks.hpp b/src/CalcDamage/CalcDamageDebugChecks.hpp index 9066349..62e8cde 100644 --- a/src/CalcDamage/CalcDamageDebugChecks.hpp +++ b/src/CalcDamage/CalcDamageDebugChecks.hpp @@ -32,16 +32,12 @@ #include #include -#include "Helpers.hpp" - namespace pokesim::calc_damage::debug { struct Checks : pokesim::debug::Checks { - Options options; - Checks(const Simulation& _simulation) - : pokesim::debug::Checks(_simulation), options(_simulation.calculateDamageOptions) {} + Checks(const Simulation& _simulation) : pokesim::debug::Checks(_simulation) {} void checkInputs() { - pokesim::debug::check(options.getDamageRollOptions()); + pokesim::debug::check(calcDamageOptionsOnInput.getDamageRollOptions()); checkMoveInputs(); checkPokemonInputs(true); @@ -55,8 +51,7 @@ struct Checks : pokesim::debug::Checks { } void checkOutputs() const { - POKESIM_REQUIRE_NM(options == simulation->calculateDamageOptions); - + checkOptions(); checkMoveOutputs(); checkPokemonOutputs(true); checkPokemonOutputs(false); diff --git a/src/CalcDamage/Setup/CalcDamageInputSetup.cpp b/src/CalcDamage/Setup/CalcDamageInputSetup.cpp index 19811d8..4c63ad0 100644 --- a/src/CalcDamage/Setup/CalcDamageInputSetup.cpp +++ b/src/CalcDamage/Setup/CalcDamageInputSetup.cpp @@ -13,22 +13,18 @@ #include namespace pokesim::calc_damage { -InputSetup::InputSetup(types::registry& _registry) : registry(&_registry) {} +InputSetup::InputSetup(types::registry& registry, types::entity moveEntity) : handle(registry, moveEntity) {} void InputSetup::setup( - types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move, - const Pokedex& pokedex) { - moveEntity = createActionMoveForTarget({*registry, targetEntity}, battleEntity, sourceEntity, move, pokedex); - types::handle handle{*registry, moveEntity}; + types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move) { + types::registry& registry = *handle.registry(); + + setupActionMoveBuild(registry, battleEntity, sourceEntity, targetEntity, entity(), move); handle.emplace(move); handle.emplace(); - registry->emplace_or_replace(sourceEntity); - registry->emplace_or_replace(targetEntity); -} - -types::entity InputSetup::entity() const { - POKESIM_REQUIRE(moveEntity != entt::null, "Getting move entity before proper setup."); - return moveEntity; + handle.emplace(); + registry.emplace_or_replace(sourceEntity); + registry.emplace_or_replace(targetEntity); } } // namespace pokesim::calc_damage diff --git a/src/CalcDamage/Setup/CalcDamageInputSetup.hpp b/src/CalcDamage/Setup/CalcDamageInputSetup.hpp index 1ee1926..a71e454 100644 --- a/src/CalcDamage/Setup/CalcDamageInputSetup.hpp +++ b/src/CalcDamage/Setup/CalcDamageInputSetup.hpp @@ -12,17 +12,14 @@ class Pokedex; namespace calc_damage { struct InputSetup { protected: - types::registry* registry; - types::entity moveEntity = entt::null; + types::handle handle; public: - InputSetup(types::registry& registry); + InputSetup(types::registry& registry, types::entity moveEntity); - void setup( - types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move, - const Pokedex& pokedex); + void setup(types::entity battleEntity, types::entity sourceEntity, types::entity targetEntity, dex::Move move); - types::entity entity() const; + types::entity entity() const { return handle.entity(); } }; } // namespace calc_damage } // namespace pokesim diff --git a/src/Components/ActionQueue.hpp b/src/Components/ActionQueue.hpp new file mode 100644 index 0000000..45f778d --- /dev/null +++ b/src/Components/ActionQueue.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace pokesim { +struct ActionQueueItem { + // Order of the types of actions (lower first). + ActionOrder order = ActionOrder::NONE; + // Priority of the action (higher first). + types::priority priority = Constants::MovePriority::DEFAULT; + // Whether negative fractional priority is active for the action (false first). + types::fractionalPriority fractionalPriority = false; + // Speed of Pokemon using move (higher first if priority tie). + types::stat speed = Constants::PokemonEffectiveStat::DEFAULT; + + // The decision that created this action queue item. + types::slotDecision decision{}; + + constexpr bool operator==(const ActionQueueItem& other) const { + return isSameSpeed(other) && decision == other.decision; + } + + constexpr bool isSameSpeed(const ActionQueueItem& other) const { + return order == other.order && priority == other.priority && fractionalPriority == other.fractionalPriority && + speed == other.speed; + } + + constexpr bool isFasterThan(const ActionQueueItem& other) const { + if (order != other.order) { + return order < other.order; + } + + if (priority != other.priority) { + return priority > other.priority; + } + + if (fractionalPriority != other.fractionalPriority) { + return !fractionalPriority; + } + + if (speed != other.speed) { + return speed > other.speed; + } + + return false; + } +}; + +struct ActionQueue { + internal::maxSizedVector val{}; +}; +} // namespace pokesim diff --git a/src/Components/EntityHolders/ChoiceLock.hpp b/src/Components/ChoiceLock.hpp similarity index 58% rename from src/Components/EntityHolders/ChoiceLock.hpp rename to src/Components/ChoiceLock.hpp index 06865c0..8289061 100644 --- a/src/Components/EntityHolders/ChoiceLock.hpp +++ b/src/Components/ChoiceLock.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include namespace pokesim { struct ChoiceLock { - types::entity val{}; + types::moveSlotIndex val{}; }; } // namespace pokesim diff --git a/src/Components/Current.hpp b/src/Components/Current.hpp new file mode 100644 index 0000000..87e7a2d --- /dev/null +++ b/src/Components/Current.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace pokesim { +struct CurrentActionMoveSlot { + types::moveSlotIndex val{}; +}; + +} // namespace pokesim diff --git a/src/Components/Decisions.hpp b/src/Components/Decisions.hpp deleted file mode 100644 index 4963d19..0000000 --- a/src/Components/Decisions.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace pokesim { -struct SlotDecision { - Slot sourceSlot = Slot::NONE; - Slot targetSlot = Slot::NONE; - - bool megaEvolve = false; - bool primalRevert = false; - bool dynamax = false; - bool terastallize = false; - - std::optional moveChoice = std::nullopt; - std::optional itemChoice = std::nullopt; - - bool operator==(const SlotDecision& other) const { - return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot && megaEvolve == other.megaEvolve && - primalRevert == other.primalRevert && dynamax == other.dynamax && terastallize == other.terastallize && - moveChoice == other.moveChoice && itemChoice == other.itemChoice; - } -}; - -namespace types { -using slotDecisions = types::sideSlots; -} - -struct SideDecision { - PlayerSideId sideId = PlayerSideId::NONE; - internal::variant decisions{}; - - bool operator==(const SideDecision other) const { return sideId == other.sideId && decisions == other.decisions; } -}; -} // namespace pokesim diff --git a/src/Components/DisabledMoveSlots.hpp b/src/Components/DisabledMoveSlots.hpp new file mode 100644 index 0000000..3f90336 --- /dev/null +++ b/src/Components/DisabledMoveSlots.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace pokesim { +struct DisabledMoveSlots { + types::moveSlots val{}; +}; +} // namespace pokesim diff --git a/src/Components/EntityHolders/ActionQueue.hpp b/src/Components/EntityHolders/ActionQueue.hpp deleted file mode 100644 index 3e3fed8..0000000 --- a/src/Components/EntityHolders/ActionQueue.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace pokesim { -// Contains the list of action entities queued up to be simulated for a battle's current turn. -struct ActionQueue { - internal::maxSizedVector val{}; -}; -} // namespace pokesim diff --git a/src/Components/EntityHolders/Current.hpp b/src/Components/EntityHolders/Current.hpp index 2f68fbc..f07933e 100644 --- a/src/Components/EntityHolders/Current.hpp +++ b/src/Components/EntityHolders/Current.hpp @@ -9,10 +9,6 @@ struct CurrentAction { types::entity val{}; }; -struct NextAction { - types::entity val{}; -}; - struct CurrentActionTargets { types::targets val{}; }; @@ -41,10 +37,6 @@ struct CurrentActionMovesAsTarget { types::entityVector val{}; }; -struct CurrentActionMoveSlot { - types::entity val{}; -}; - struct CurrentEffectSource { types::entity val{}; }; diff --git a/src/Components/EntityHolders/MoveSlots.hpp b/src/Components/EntityHolders/MoveSlots.hpp deleted file mode 100644 index a47b6c7..0000000 --- a/src/Components/EntityHolders/MoveSlots.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -namespace pokesim { -// Contains a list of entities of the moves a Pokemon known. -struct MoveSlots { - types::moveSlots val{}; -}; -} // namespace pokesim diff --git a/src/Components/EntityHolders/Pokemon.hpp b/src/Components/EntityHolders/Pokemon.hpp deleted file mode 100644 index 1678082..0000000 --- a/src/Components/EntityHolders/Pokemon.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -namespace pokesim { -// Contains the entity pointing to a Pokemon. -struct Pokemon { - types::entity val{}; -}; -} // namespace pokesim diff --git a/src/Components/EntityHolders/RecycledEntities.hpp b/src/Components/EntityHolders/RecycledEntities.hpp new file mode 100644 index 0000000..b6b7c93 --- /dev/null +++ b/src/Components/EntityHolders/RecycledEntities.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace pokesim { +struct RecycledAction { + types::entity val{}; +}; + +struct RecycledActionMove { + types::entity val{}; +}; + +struct AddedRecycledActionMove1 { + types::entity val{}; +}; + +struct AddedRecycledActionMove2 { + types::entity val{}; +}; +} // namespace pokesim diff --git a/src/Components/EntityHolders/headers.hpp b/src/Components/EntityHolders/headers.hpp index bc13f21..a99a2bd 100644 --- a/src/Components/EntityHolders/headers.hpp +++ b/src/Components/EntityHolders/headers.hpp @@ -2,16 +2,12 @@ #pragma once -#include "ActionQueue.hpp" #include "Battle.hpp" #include "BattleTree.hpp" -#include "ChoiceLock.hpp" #include "Current.hpp" #include "FaintQueue.hpp" #include "FoeSide.hpp" -#include "LastUsedMove.hpp" -#include "MoveSlots.hpp" -#include "Pokemon.hpp" +#include "RecycledEntities.hpp" #include "Side.hpp" #include "Sides.hpp" #include "Team.hpp" diff --git a/src/Components/EntityHolders/LastUsedMove.hpp b/src/Components/LastUsedMove.hpp similarity index 59% rename from src/Components/EntityHolders/LastUsedMove.hpp rename to src/Components/LastUsedMove.hpp index c0bda2a..5a6215c 100644 --- a/src/Components/EntityHolders/LastUsedMove.hpp +++ b/src/Components/LastUsedMove.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include namespace pokesim { struct LastUsedMove { - types::entity val{}; + types::moveSlotIndex val{}; }; } // namespace pokesim diff --git a/src/Components/MoveSlots.hpp b/src/Components/MoveSlots.hpp new file mode 100644 index 0000000..c0a8573 --- /dev/null +++ b/src/Components/MoveSlots.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include + +namespace pokesim { +struct MoveSlot { + dex::Move move = dex::Move::NO_MOVE; + types::pp pp = Constants::MovePp::DEFAULT; + types::pp maxPp = Constants::MovePp::DEFAULT; + + constexpr bool operator==(const MoveSlot& other) const { + return move == other.move && pp == other.pp && maxPp == other.maxPp; + } +}; + +struct MoveSlots { + types::moveSlots val{}; +}; +} // namespace pokesim diff --git a/src/Components/PP.hpp b/src/Components/Pokedex/PP.hpp similarity index 72% rename from src/Components/PP.hpp rename to src/Components/Pokedex/PP.hpp index 0528393..affb0a2 100644 --- a/src/Components/PP.hpp +++ b/src/Components/Pokedex/PP.hpp @@ -7,8 +7,4 @@ namespace pokesim { struct Pp { types::pp val = Constants::MovePp::DEFAULT; }; - -struct MaxPp { - types::pp val = Constants::MoveMaxPp::DEFAULT; -}; } // namespace pokesim diff --git a/src/Components/Pokedex/headers.hpp b/src/Components/Pokedex/headers.hpp index 808bc9d..35b5121 100644 --- a/src/Components/Pokedex/headers.hpp +++ b/src/Components/Pokedex/headers.hpp @@ -4,3 +4,4 @@ #include "Abilities.hpp" #include "BaseStats.hpp" +#include "PP.hpp" diff --git a/src/Components/SideDecisions.hpp b/src/Components/SideDecisions.hpp new file mode 100644 index 0000000..1ec8c78 --- /dev/null +++ b/src/Components/SideDecisions.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace pokesim { +struct SideDecision { + PlayerSideId sideId = PlayerSideId::NONE; + internal::variant decisions{}; + + bool operator==(const SideDecision other) const { return sideId == other.sideId && decisions == other.decisions; } +}; +} // namespace pokesim diff --git a/src/Components/SpeedSort.hpp b/src/Components/SpeedSort.hpp deleted file mode 100644 index 775ef26..0000000 --- a/src/Components/SpeedSort.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace pokesim { -// Data that determines the order actions take place -struct SpeedSort { - // Order of the types of actions (lower first) - ActionOrder order = ActionOrder::NONE; - // Priority of the action (higher first) - types::priority priority = Constants::MovePriority::DEFAULT; - // Whether negative fractional priority is active for the action (false first) - types::fractionalPriority fractionalPriority = false; - // Speed of Pokemon using move (higher first if priority tie) - types::stat speed = Constants::PokemonEffectiveStat::DEFAULT; - - bool operator==(const SpeedSort& other) const { - return order == other.order && priority == other.priority && fractionalPriority == other.fractionalPriority && - speed == other.speed; - } -}; -} // namespace pokesim diff --git a/src/Components/Tags/MovePropertyTags.hpp b/src/Components/Tags/MovePropertyTags.hpp index 9212508..702ad6d 100644 --- a/src/Components/Tags/MovePropertyTags.hpp +++ b/src/Components/Tags/MovePropertyTags.hpp @@ -20,8 +20,6 @@ struct Punch {}; struct VariableHitCount {}; // Move Property Tag: A multi-hit move where each hit checks accuracy (i.e. Triple Kick) struct AccuracyDependentHitCount {}; - -struct Disabled {}; } // namespace tags namespace effect::tags { diff --git a/src/Components/Tags/RecycledEntities.hpp b/src/Components/Tags/RecycledEntities.hpp new file mode 100644 index 0000000..fae9eea --- /dev/null +++ b/src/Components/Tags/RecycledEntities.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace pokesim::tags { +struct RecycledAction {}; +struct RecycledActionMove {}; +struct AddedRecycledActionMove1 {}; +struct AddedRecycledActionMove2 {}; +} // namespace pokesim::tags diff --git a/src/Components/Tags/Selection.hpp b/src/Components/Tags/Selection.hpp index 15cf44d..3a9f55c 100644 --- a/src/Components/Tags/Selection.hpp +++ b/src/Components/Tags/Selection.hpp @@ -1,6 +1,8 @@ #pragma once namespace pokesim::internal::tags { +struct BuildPokedexMove {}; +struct BuildActionMove {}; struct CloneFromDamageRolls {}; struct ApplySideDamageRollOptions {}; } // namespace pokesim::internal::tags diff --git a/src/Components/Tags/headers.hpp b/src/Components/Tags/headers.hpp index bca4e2e..ee5c241 100644 --- a/src/Components/Tags/headers.hpp +++ b/src/Components/Tags/headers.hpp @@ -11,6 +11,7 @@ #include "MoveTags.hpp" #include "NatureTags.hpp" #include "PokemonTags.hpp" +#include "RecycledEntities.hpp" #include "RunEventTags.hpp" #include "Selection.hpp" #include "SimulationTags.hpp" diff --git a/src/Components/headers.hpp b/src/Components/headers.hpp index ff533d3..833e9c7 100644 --- a/src/Components/headers.hpp +++ b/src/Components/headers.hpp @@ -3,6 +3,7 @@ #pragma once #include "Accuracy.hpp" +#include "ActionQueue.hpp" #include "AddedTargets.hpp" #include "AnalyzeEffect/Aliases.hpp" #include "AnalyzeEffect/AnalyzeEffectInputs.hpp" @@ -16,27 +17,27 @@ #include "CalcDamage/DamageRollSides.hpp" #include "CalcDamage/ModifyingEventRanTags.hpp" #include "CalcDamage/TemporaryMoveProperties.hpp" +#include "ChoiceLock.hpp" #include "CloneFromCloneTo.hpp" +#include "Current.hpp" #include "Damage.hpp" -#include "Decisions.hpp" +#include "DisabledMoveSlots.hpp" #include "EVsIVs.hpp" -#include "EntityHolders/ActionQueue.hpp" #include "EntityHolders/Battle.hpp" #include "EntityHolders/BattleTree.hpp" -#include "EntityHolders/ChoiceLock.hpp" #include "EntityHolders/Current.hpp" #include "EntityHolders/FaintQueue.hpp" #include "EntityHolders/FoeSide.hpp" -#include "EntityHolders/LastUsedMove.hpp" -#include "EntityHolders/MoveSlots.hpp" -#include "EntityHolders/Pokemon.hpp" +#include "EntityHolders/RecycledEntities.hpp" #include "EntityHolders/Side.hpp" #include "EntityHolders/Sides.hpp" #include "EntityHolders/Team.hpp" #include "EventModifier.hpp" #include "HitCount.hpp" #include "ID.hpp" +#include "LastUsedMove.hpp" #include "Level.hpp" +#include "MoveSlots.hpp" #include "Names/AbilityNames.hpp" #include "Names/GenderNames.hpp" #include "Names/ItemNames.hpp" @@ -53,16 +54,17 @@ #include "Names/TypeNames.hpp" #include "Names/VolatileNames.hpp" #include "Names/WeatherNames.hpp" -#include "PP.hpp" #include "PlayerSide.hpp" #include "Pokedex/Abilities.hpp" #include "Pokedex/BaseStats.hpp" +#include "Pokedex/PP.hpp" #include "Position.hpp" #include "Priority.hpp" #include "Probability.hpp" #include "RNGSeed.hpp" #include "RandomEventInputs.hpp" #include "RandomEventOutputs.hpp" +#include "SideDecisions.hpp" #include "SimulateTurn/ActionTags.hpp" #include "SimulateTurn/MoveHitStepTags.hpp" #include "SimulateTurn/SimulateTurnTags.hpp" @@ -70,7 +72,6 @@ #include "SimulateTurn/TeamAction.hpp" #include "SimulationResults.hpp" #include "SpeciesTypes.hpp" -#include "SpeedSort.hpp" #include "Stats.hpp" #include "Tags/AbilityTags.hpp" #include "Tags/BattleTags.hpp" @@ -81,6 +82,7 @@ #include "Tags/MoveTags.hpp" #include "Tags/NatureTags.hpp" #include "Tags/PokemonTags.hpp" +#include "Tags/RecycledEntities.hpp" #include "Tags/RunEventTags.hpp" #include "Tags/Selection.hpp" #include "Tags/SimulationTags.hpp" diff --git a/src/Pokedex/Events/EffectEvents.cpp b/src/Pokedex/Events/EffectEvents.cpp index de412c1..619ed8e 100644 --- a/src/Pokedex/Events/EffectEvents.cpp +++ b/src/Pokedex/Events/EffectEvents.cpp @@ -1,11 +1,12 @@ #include #include #include +#include #include +#include #include -#include #include -#include +#include #include #include #include @@ -60,16 +61,12 @@ void choiceLockRemoveWithItem( } void choiceLockOnDisableMove( - types::registry& registry, const pokesim::ChoiceLock& choiceLocked, const MoveSlots& moveSlots) { - POKESIM_REQUIRE( - std::find(moveSlots.val.begin(), moveSlots.val.end(), choiceLocked.val) != moveSlots.val.end(), - "Should skip if the move is no longer present, but when does that happen?"); - - for (types::entity entity : moveSlots.val) { - if (entity != choiceLocked.val) { - registry.emplace(entity); - } + types::handle handle, const pokesim::ChoiceLock& choiceLocked, const MoveSlots& moveSlots) { + if (!handle.all_of()) { + handle.emplace(types::moveSlots(moveSlots.val.size(), false)); } + + handle.get().val[choiceLocked.val] = true; } } // namespace diff --git a/src/Pokedex/Events/ItemEvents.cpp b/src/Pokedex/Events/ItemEvents.cpp index 54b6bdd..72c7db1 100644 --- a/src/Pokedex/Events/ItemEvents.cpp +++ b/src/Pokedex/Events/ItemEvents.cpp @@ -1,9 +1,10 @@ #include #include #include +#include +#include #include #include -#include #include #include #include diff --git a/src/Pokedex/Pokedex.cpp b/src/Pokedex/Pokedex.cpp index 086b505..4267049 100644 --- a/src/Pokedex/Pokedex.cpp +++ b/src/Pokedex/Pokedex.cpp @@ -1,5 +1,7 @@ #include "Pokedex.hpp" +#include +#include #include #include #include @@ -29,16 +31,24 @@ void Pokedex::loadItems(const entt::dense_set& itemSet) { load(itemsMap, itemSet, [this](dex::Item item) { return buildItem(item, dexRegistry); }); } -void Pokedex::loadMoves(const entt::dense_set& moveSet) { - load(movesMap, moveSet, [this](dex::Move move) { return buildMove(move, dexRegistry, false); }); -} - void Pokedex::loadAbilities(const entt::dense_set& abilitySet) { load(abilitiesMap, abilitySet, [this](dex::Ability ability) { return buildAbility(ability, dexRegistry); }); } -types::entity Pokedex::buildActionMove(dex::Move move, types::registry& registry) const { - return buildMove(move, registry, true); +void Pokedex::loadMoves(const entt::dense_set& moveSet) { + for (dex::Move move : moveSet) { + if (movesMap.contains(move)) { + continue; + } + + types::entity moveEntity = dexRegistry.create(); + movesMap[move] = moveEntity; + move::tags::emplaceTagFromEnum(move, {dexRegistry, moveEntity}); + dexRegistry.emplace(moveEntity); + } + + buildMoves(dexRegistry); + dexRegistry.clear(); } void Pokedex::loadForBattleInfo(const std::vector& battleInfoList) { @@ -62,6 +72,14 @@ void Pokedex::loadForBattleInfo(const std::vector& battleInf } } } + + for (const auto& damageCalculation : battleCreationInfo.damageCalculations) { + moveSet.insert(damageCalculation.moves.begin(), damageCalculation.moves.end()); + } + + for (const auto& effectToAnalyze : battleCreationInfo.effectsToAnalyze) { + moveSet.insert(effectToAnalyze.moves.begin(), effectToAnalyze.moves.end()); + } } loadSpecies(speciesSet); diff --git a/src/Pokedex/Pokedex.hpp b/src/Pokedex/Pokedex.hpp index 49efafc..166872c 100644 --- a/src/Pokedex/Pokedex.hpp +++ b/src/Pokedex/Pokedex.hpp @@ -39,7 +39,6 @@ class Pokedex { void load(entt::dense_map& map, const entt::dense_set& list, Build build); types::entity buildSpecies(dex::Species species, types::registry& registry) const; - types::entity buildMove(dex::Move move, types::registry& registry, bool forActiveMove) const; types::entity buildItem(dex::Item item, types::registry& registry) const; types::entity buildAbility(dex::Ability ability, types::registry& registry) const; @@ -225,7 +224,7 @@ class Pokedex { return dexRegistry.all_of(movesMap.at(move)); } - types::entity buildActionMove(dex::Move move, types::registry& registry) const; + void buildMoves(types::registry& registry) const; void loadForBattleInfo(const std::vector& battleInfoList); }; } // namespace pokesim diff --git a/src/Pokedex/Setup/DexDataSetup.hpp b/src/Pokedex/Setup/DexDataSetup.hpp index aa5af72..078f57d 100644 --- a/src/Pokedex/Setup/DexDataSetup.hpp +++ b/src/Pokedex/Setup/DexDataSetup.hpp @@ -13,6 +13,7 @@ struct DexDataSetup { public: DexDataSetup(types::registry& registry) : handle(registry, registry.create()) {} + DexDataSetup(types::registry& registry, types::entity entity) : handle(registry, entity) {} template void setProperty() { diff --git a/src/Pokedex/Setup/GetMoveBuild.cpp b/src/Pokedex/Setup/GetMoveBuild.cpp index 7b1f94d..f07fcba 100644 --- a/src/Pokedex/Setup/GetMoveBuild.cpp +++ b/src/Pokedex/Setup/GetMoveBuild.cpp @@ -1,7 +1,20 @@ +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include #include #include #include @@ -10,17 +23,17 @@ #include #include #include +#include #include #include #include "../Moves/headers.hpp" -#include "MoveDexDataSetup.hpp" // TODO(aed3): Make this and the individual move files autogenerated namespace pokesim { namespace { -template +template struct BuildMove { private: enum class Optional : std::uint8_t { @@ -80,225 +93,282 @@ struct BuildMove { template struct has> : std::true_type {}; - template - static void setEffect( - dex::internal::MoveDexDataSetup& move, GameMechanics gameMechanic, 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...); + struct EntitySetup { + using EntityList = entt::view>; + types::registry* registry; + const EntityList* list; + std::size_t size; + + EntitySetup(types::registry& registry_, const EntityList& list_) + : registry(®istry_), list(&list_), size(list->size_hint()) {} + + template + void add(const Component& component) { + registry->storage().reserve(size); + registry->insert(list->begin(), list->end(), component); } - else { - types::percentChance chance = Constants::MoveBaseEffectChance::DEFAULT; - if constexpr (has::value) { - chance = EffectData::chance(gameMechanic); - } - move.setSecondaryEffect(chance, effectValues...); + + template + void setProperties(Tags) { + (add(Types{}), ...); } - } - template - static void setEffectTags(dex::internal::MoveDexDataSetup& move, GameMechanics gameMechanic, Tags) { + template + bool any_of() { + return std::any_of(list->begin(), list->end(), [&](types::entity entity) { + return registry->template any_of(entity); + }); + } + }; + + template + static void buildEffect(EntitySetup& setup, GameMechanics gameMechanic) { 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(), ...); + + POKESIM_REQUIRE( + !setup.template any_of(), + "Moves can only have primary or secondary effects, not both."); + + setup.add(move::effect::tags::Primary{}); } else { + POKESIM_REQUIRE( + !setup.template any_of(), + "Moves can only have secondary or primary effects, not both."); + types::percentChance chance = Constants::MoveBaseEffectChance::DEFAULT; if constexpr (has::value) { chance = EffectData::chance(gameMechanic); } - (move.setSecondaryEffect(chance), ...); + setup.add(move::effect::tags::Secondary{}); + setup.add(BaseEffectChance{chance}); } - } - template - static void buildEffect(dex::internal::MoveDexDataSetup& move, GameMechanics gameMechanic) { if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::atkBoost(gameMechanic)); + setup.add(AtkBoost{EffectData::atkBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::defBoost(gameMechanic)); + setup.add(DefBoost{EffectData::defBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::spaBoost(gameMechanic)); + setup.add(SpaBoost{EffectData::spaBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::spdBoost(gameMechanic)); + setup.add(SpdBoost{EffectData::spdBoost(gameMechanic)}); } if constexpr (has::value) { - setEffect(move, gameMechanic, EffectData::speBoost(gameMechanic)); + setup.add(SpeBoost{EffectData::speBoost(gameMechanic)}); } - setEffectTags(move, gameMechanic, EffectData::effectTags); + setup.setProperties(EffectData::effectTags); } public: - static types::entity build(types::registry& registry, GameMechanics gameMechanic, bool forActiveMove) { - dex::internal::MoveDexDataSetup move(registry); - - if (forActiveMove) { - move.setNameTag(T::name(gameMechanic)); + static void build(types::registry& registry, GameMechanics gameMechanic) { + auto list = registry.view(); + if (list.begin() == list.end()) { + return; } - else { - move.setName(T::name(gameMechanic)); - move.setBasePp(T::basePp(gameMechanic)); + + EntitySetup setup{registry, list}; + + if (std::is_same_v) { + setup.add(MoveName{Move::name(gameMechanic)}); + setup.add(Pp{Move::basePp(gameMechanic)}); } - move.setType(T::type(gameMechanic)); - switch (T::category(gameMechanic)) { + setup.add(TypeName{Move::type(gameMechanic)}); + switch (Move::category(gameMechanic)) { case dex::MoveCategory::PHYSICAL: { - move.setCategoryPhysical(); + POKESIM_REQUIRE( + !(setup.template any_of()), + "A move can only have one category."); + + setup.add(move::tags::Physical{}); break; } case dex::MoveCategory::SPECIAL: { - move.setCategorySpecial(); + POKESIM_REQUIRE( + !(setup.template any_of()), + "A move can only have one category."); + + setup.add(move::tags::Special{}); break; } case dex::MoveCategory::STATUS: { - move.setCategoryStatus(); + POKESIM_REQUIRE( + !(setup.template any_of()), + "A move can only have one category."); + setup.add(move::tags::Status{}); break; } } - if constexpr (has::value) { - move.setAccuracy(T::accuracy(gameMechanic)); + if constexpr (has::value) { + setup.add(Accuracy{Move::accuracy(gameMechanic)}); } - if constexpr (has::value) { - move.setBasePower(T::basePower(gameMechanic)); + if constexpr (has::value) { + setup.add(BasePower{Move::basePower(gameMechanic)}); } - if constexpr (has::value) { - move.setHitCount(T::hitCount(gameMechanic)); + if constexpr (has::value) { + setup.add(HitCount{Move::hitCount(gameMechanic)}); } - if constexpr (has::value) { - buildEffect(move); - move.setEffectTargetsMoveSource(); + if constexpr (has::value) { + buildEffect(setup); + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + setup.add(move::effect::tags::MoveSource{}); } - if constexpr (has::value) { - buildEffect(move, gameMechanic); - move.setEffectTargetsMoveTarget(); + if constexpr (has::value) { + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + buildEffect(setup, gameMechanic); + setup.add(move::effect::tags::MoveTarget{}); } - if constexpr (has::value) { - buildEffect(move, gameMechanic); - move.setEffectTargetsMoveSource(); + if constexpr (has::value) { + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + buildEffect(setup, gameMechanic); + setup.add(move::effect::tags::MoveSource{}); } - if constexpr (has::value) { - buildEffect(move, gameMechanic); - move.setEffectTargetsMoveTarget(); + if constexpr (has::value) { + POKESIM_REQUIRE( + !(setup.template any_of()), + "Moves effects can only affect the target or source, not both."); + buildEffect(setup, gameMechanic); + setup.add(move::effect::tags::MoveTarget{}); } - move.setProperties(T::moveTags); + setup.setProperties(Move::moveTags); - switch (T::target(gameMechanic)) { + switch (Move::target(gameMechanic)) { case MoveTarget::ANY_SINGLE_TARGET: { - move.setProperty(); + setup.add(move::tags::AnySingleTarget{}); break; } case MoveTarget::ANY_SINGLE_FOE: { - move.setProperty(); + setup.add(move::tags::AnySingleFoe{}); break; } case MoveTarget::ANY_SINGLE_ALLY: { - move.setProperty(); + setup.add(move::tags::AnySingleAlly{}); break; } case MoveTarget::ALLY_OR_SELF: { - move.setProperty(); + setup.add(move::tags::AllyOrSelf{}); break; } case MoveTarget::SELF: { - move.setProperty(); + setup.add(move::tags::Self{}); break; } case MoveTarget::ALL_FOES: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::TARGET_ALLY); + setup.add(move::tags::AllFoes{}); + setup.add(move::added_targets::tags::TargetAlly{}); break; } case MoveTarget::ALLIES_AND_FOES: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::TARGET_ALLY); - move.addAddedTargets(AddedTargetOptions::USER_ALLY); + setup.add(move::tags::AlliesAndFoes{}); + setup.add(move::added_targets::tags::TargetAlly{}); + setup.add(move::added_targets::tags::UserAlly{}); break; } case MoveTarget::ALLIES_AND_SELF: { - move.setProperty(); + setup.add(move::tags::AlliesAndSelf{}); // Deliberately not USER_ALLY as the target of AlliesAndSelf moves is the user - move.addAddedTargets(AddedTargetOptions::TARGET_ALLY); + setup.add(move::added_targets::tags::TargetAlly{}); break; } case MoveTarget::FOE_SIDE: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::TARGET_SIDE); + setup.add(move::tags::FoeSide{}); + setup.add(move::added_targets::tags::TargetSide{}); break; } case MoveTarget::ALLY_SIDE: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::USER_SIDE); + setup.add(move::tags::AllySide{}); + setup.add(move::added_targets::tags::UserSide{}); break; } case MoveTarget::FIELD: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::FIELD); + setup.add(move::tags::Field{}); + setup.add(move::added_targets::tags::Field{}); break; } case MoveTarget::ALLY_TEAM: { - move.setProperty(); - move.addAddedTargets(AddedTargetOptions::USER_SIDE); + setup.add(move::tags::AllyTeam{}); + setup.add(move::added_targets::tags::UserSide{}); break; } case MoveTarget::RETALIATION: { - move.setProperty(); + setup.add(move::tags::Retaliation{}); break; } case MoveTarget::RANDOM_FOE: { - move.setProperty(); + setup.add(move::tags::RandomFoe{}); break; } default: break; } - return move.entity(); + if (std::is_same_v) { + registry.remove(list.begin(), list.end()); + } } }; -types::entity buildByGameMechanic( - dex::Move move, types::registry& registry, bool forActiveMove, GameMechanics gameMechanic) { - // Tidy check ignored because "using namespace" is in function - using namespace pokesim::dex; // NOLINT(google-build-using-namespace) - switch (move) { - case Move::FURY_ATTACK: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::KNOCK_OFF: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::MOONBLAST: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::QUIVER_DANCE: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::SPLASH: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::THUNDERBOLT: return BuildMove::build(registry, gameMechanic, forActiveMove); - case Move::WILL_O_WISP: return BuildMove::build(registry, gameMechanic, forActiveMove); - - default: break; + +template +struct BuildMoves { + private: + template + static void buildActionMoveFromView(types::registry& registry, GameMechanics gameMechanics) { + BuildMove::build(registry, gameMechanics); } - POKESIM_REQUIRE_FAIL("Building a move that is not yet supported."); - return types::entity{}; -} + static void buildActionMoveByGameMechanic(types::registry& registry, GameMechanics gameMechanic) { + using namespace pokesim::dex; // NOLINT(google-build-using-namespace) + + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + buildActionMoveFromView(registry, gameMechanic); + } + + public: + static void build(const Pokedex& pokedex, types::registry& registry) { + if (pokedex.isGameMechanic(GameMechanics::SCARLET_VIOLET)) { + buildActionMoveByGameMechanic(registry, GameMechanics::SCARLET_VIOLET); + return; + } + + POKESIM_REQUIRE_FAIL("Building for a game that is not yet supported."); + } +}; } // namespace -types::entity Pokedex::buildMove(dex::Move move, types::registry& registry, bool forActiveMove) const { - if (isGameMechanic(GameMechanics::SCARLET_VIOLET)) { - return buildByGameMechanic(move, registry, forActiveMove, GameMechanics::SCARLET_VIOLET); +void Pokedex::buildMoves(types::registry& registry) const { + if (!registry.view().empty()) { + BuildMoves::build(*this, registry); } - POKESIM_REQUIRE_FAIL("Building for a game that is not yet supported."); - return types::entity{}; + if (!registry.view().empty()) { + BuildMoves::build(*this, registry); + } } } // namespace pokesim diff --git a/src/Pokedex/Setup/MoveDexDataSetup.cpp b/src/Pokedex/Setup/MoveDexDataSetup.cpp deleted file mode 100644 index 79ba64f..0000000 --- a/src/Pokedex/Setup/MoveDexDataSetup.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "MoveDexDataSetup.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace pokesim::dex::internal { -void MoveDexDataSetup::setName(Move move) { - handle.emplace(move); -} - -void MoveDexDataSetup::setNameTag(Move move) { - move::tags::emplaceTagFromEnum(move, handle); -} - -void MoveDexDataSetup::setType(Type type) { - handle.emplace(type); -} - -void MoveDexDataSetup::addAddedTargets(AddedTargetOptions addedTargets) { - AddedTargets& existingTargets = handle.get_or_emplace(); - existingTargets.val = existingTargets.val | addedTargets; - - switch (addedTargets) { - case AddedTargetOptions::TARGET_ALLY: { - setProperty(); - break; - } - case AddedTargetOptions::USER_ALLY: { - setProperty(); - break; - } - case AddedTargetOptions::TARGET_SIDE: { - setProperty(); - break; - } - case AddedTargetOptions::USER_SIDE: { - setProperty(); - break; - } - case AddedTargetOptions::FIELD: { - setProperty(); - break; - } - default: break; - } -} - -void MoveDexDataSetup::setAccuracy(types::baseAccuracy accuracy) { - handle.emplace(accuracy); -} - -void MoveDexDataSetup::setBasePower(types::basePower basePower) { - handle.emplace(basePower); -} - -void MoveDexDataSetup::setCategoryPhysical() { - POKESIM_REQUIRE(!(handle.any_of()), "A move can only have one category."); - setProperty(); -} - -void MoveDexDataSetup::setCategorySpecial() { - POKESIM_REQUIRE(!(handle.any_of()), "A move can only have one category."); - setProperty(); -} - -void MoveDexDataSetup::setCategoryStatus() { - POKESIM_REQUIRE(!(handle.any_of()), "A move can only have one category."); - setProperty(); -} - -void MoveDexDataSetup::setBasePp(types::pp pp) { - handle.emplace(pp); -} - -void MoveDexDataSetup::setPriority(types::priority priority) { - handle.emplace(priority); -} - -void MoveDexDataSetup::setHitCount(types::moveHits hitCount) { - handle.emplace(hitCount); -} - -void MoveDexDataSetup::setEffectTargetsMoveSource() { - POKESIM_REQUIRE( - !handle.all_of(), - "Moves effects can only affect the target or source, not both."); - handle.emplace(); -} - -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 diff --git a/src/Pokedex/Setup/MoveDexDataSetup.hpp b/src/Pokedex/Setup/MoveDexDataSetup.hpp deleted file mode 100644 index 8778753..0000000 --- a/src/Pokedex/Setup/MoveDexDataSetup.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DexDataSetup.hpp" - -namespace pokesim::dex::internal { -struct MoveDexDataSetup : DexDataSetup { - MoveDexDataSetup(types::registry& registry) : DexDataSetup(registry) {} - - void setName(Move move); - void setNameTag(Move move); - void setType(Type type); - void setAccuracy(types::baseAccuracy accuracy); - void setBasePower(types::basePower basePower); - - void setCategoryPhysical(); - void setCategorySpecial(); - void setCategoryStatus(); - - void setBasePp(types::pp pp); - void setPriority(types::priority priority); - void setHitCount(types::moveHits hitCount); - - void addAddedTargets(AddedTargetOptions addedTargets); - - void setEffectTargetsMoveSource(); - void setEffectTargetsMoveTarget(); - - 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 setSecondaryEffect(types::percentChance 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 diff --git a/src/Pokedex/Setup/headers.hpp b/src/Pokedex/Setup/headers.hpp index 233c3a2..c1050cd 100644 --- a/src/Pokedex/Setup/headers.hpp +++ b/src/Pokedex/Setup/headers.hpp @@ -5,5 +5,4 @@ #include "AbilityDexDataSetup.hpp" #include "DexDataSetup.hpp" #include "ItemDexDataSetup.hpp" -#include "MoveDexDataSetup.hpp" #include "SpeciesDexDataSetup.hpp" diff --git a/src/Pokedex/headers.hpp b/src/Pokedex/headers.hpp index 23f94ec..ba40563 100644 --- a/src/Pokedex/headers.hpp +++ b/src/Pokedex/headers.hpp @@ -40,7 +40,6 @@ #include "Setup/AbilityDexDataSetup.hpp" #include "Setup/DexDataSetup.hpp" #include "Setup/ItemDexDataSetup.hpp" -#include "Setup/MoveDexDataSetup.hpp" #include "Setup/SpeciesDexDataSetup.hpp" #include "Species/Ampharos.hpp" #include "Species/Dragapult.hpp" diff --git a/src/SimulateTurn/ManageActionQueue.cpp b/src/SimulateTurn/ManageActionQueue.cpp index ce747c4..34e2e5f 100644 --- a/src/SimulateTurn/ManageActionQueue.cpp +++ b/src/SimulateTurn/ManageActionQueue.cpp @@ -1,23 +1,24 @@ #include "ManageActionQueue.hpp" #include -#include -#include +#include #include #include +#include #include #include #include #include #include #include +#include #include #include #include -#include #include #include #include +#include #include #include #include @@ -31,62 +32,59 @@ namespace pokesim::simulate_turn { namespace { -void resolveSlotDecisions( - types::handle sideHandle, const types::slotDecisions& decisions, ActionQueue& battleActionQueue) { +template +void resolveSlotDecision(types::handle sideHandle, const types::slotDecision& slotDecision, ActionQueue& actionQueue) { + if (!slotDecision.holds()) { + return; + } + types::registry& registry = *sideHandle.registry(); - for (const SlotDecision& decision : decisions) { - POKESIM_REQUIRE(decision.sourceSlot != Slot::NONE, "Source slot must be assigned."); - POKESIM_REQUIRE(decision.targetSlot != Slot::NONE, "Target slot must be assigned."); - POKESIM_REQUIRE( - !(decision.moveChoice.has_value() && decision.itemChoice.has_value()), - "Decisions can't have a move and an item choice."); + const auto& decision = slotDecision.get(); - types::handle actionHandle = {registry, registry.create()}; - actionHandle.emplace(decision.sourceSlot); - actionHandle.emplace(decision.targetSlot); + POKESIM_REQUIRE(decision.sourceSlot != Slot::NONE, "Source slot must be assigned."); + POKESIM_REQUIRE(decision.targetSlot != Slot::NONE, "Target slot must be assigned."); - SpeedSort speedSort; - types::entity sourceEntity = slotToPokemonEntity(registry, sideHandle.entity(), decision.sourceSlot); + ActionQueueItem actionQueueItem; + actionQueueItem.decision = decision; - stat::EffectiveSpe* effectiveSpe = registry.try_get(sourceEntity); - if (effectiveSpe != nullptr) { - speedSort.speed = effectiveSpe->val; - } - else { - speedSort.speed = registry.get(sourceEntity).val; - } + types::entity sourceEntity = slotToPokemonEntity(registry, sideHandle.entity(), decision.sourceSlot); + actionQueueItem.speed = registry.get(sourceEntity).val; - if (decision.moveChoice.has_value()) { - actionHandle.emplace(); - actionHandle.emplace(decision.moveChoice.value()); + if constexpr (std::is_base_of_v) { + actionQueueItem.order = ActionOrder::MOVE; + actionQueueItem.priority = Constants::MovePriority::DEFAULT; // TODO (aed3): Move priority + modify priority + actionQueueItem.fractionalPriority = false; // TODO (aed3): get fractionalPriority - speedSort.order = ActionOrder::MOVE; - speedSort.priority = Constants::MovePriority::DEFAULT; // TODO (aed3): Move priority + modify priority - speedSort.fractionalPriority = false; // TODO (aed3): get fractionalPriority - } - else if (decision.itemChoice.has_value()) { - actionHandle.emplace(); - actionHandle.emplace(decision.itemChoice.value()); - speedSort.order = ActionOrder::ITEM; - } - else { - actionHandle.emplace(); - speedSort.order = ActionOrder::SWITCH; + if constexpr (!std::is_same_v) { + POKESIM_REQUIRE_FAIL(std::string(entt::type_name().value()) + " is not yet supported."); } + } + else if constexpr (std::is_same_v) { + actionQueueItem.order = ActionOrder::SWITCH; + } + else if constexpr (std::is_same_v) { + actionQueueItem.order = ActionOrder::ITEM; + } + else { + POKESIM_REQUIRE_FAIL(std::string(entt::type_name().value()) + " is not yet supported."); + } - actionHandle.emplace(speedSort); + actionQueue.val.push_back(actionQueueItem); +} - battleActionQueue.val.push_back(actionHandle.entity()); +void resolveSlotDecisions(types::handle sideHandle, const types::slotDecisions& decisions, ActionQueue& actionQueue) { + for (const types::slotDecision& decision : decisions) { + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); + resolveSlotDecision(sideHandle, decision, actionQueue); } } -void resolveTeamDecision(types::registry& registry, const types::teamOrder& teamOrder, ActionQueue& battleActionQueue) { - types::handle actionHandle = {registry, registry.create()}; - - actionHandle.emplace(teamOrder); - - battleActionQueue.val.push_back(actionHandle.entity()); -} +void resolveTeamDecision(types::registry&, const types::teamOrder&, ActionQueue&) {} } // namespace void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) { @@ -95,7 +93,7 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) const Battle& battle = sideHandle.get(); types::registry& registry = *sideHandle.registry(); - ActionQueue& battleActionQueue = registry.get(battle.val); + ActionQueue& actionQueue = registry.get(battle.val); if (sideDecision.decisions.holds()) { POKESIM_REQUIRE( @@ -105,21 +103,21 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) const auto& decisions = sideDecision.decisions.get(); #ifdef POKESIM_DEBUG_CHECK_UTILITIES - for (const SlotDecision& decision : decisions) { + for (const auto& decision : decisions) { if (sideDecision.sideId == PlayerSideId::P1) { POKESIM_REQUIRE( - (decision.sourceSlot == Slot::P1A || decision.sourceSlot == Slot::P1B), + (decision.sourceSlot() == Slot::P1A || decision.sourceSlot() == Slot::P1B), "Source must be from a player 1 in battle slot."); } else { POKESIM_REQUIRE( - (decision.sourceSlot == Slot::P2A || decision.sourceSlot == Slot::P2B), + (decision.sourceSlot() == Slot::P2A || decision.sourceSlot() == Slot::P2B), "Source must be from a player 2 in battle slot."); } } #endif - resolveSlotDecisions(sideHandle, decisions, battleActionQueue); + resolveSlotDecisions(sideHandle, decisions, actionQueue); } else if (sideDecision.decisions.holds()) { POKESIM_REQUIRE( @@ -131,7 +129,7 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) POKESIM_REQUIRE( sideHandle.get().val.size() == teamOrder.size(), "Must pick a placement for each Pokemon on the team."); - resolveTeamDecision(*sideHandle.registry(), teamOrder, battleActionQueue); + resolveTeamDecision(*sideHandle.registry(), teamOrder, actionQueue); } else { POKESIM_REQUIRE_FAIL( @@ -140,51 +138,23 @@ void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision) } void speedSort(types::handle handle, ActionQueue& actionQueue) { - auto& entityList = actionQueue.val; - - if (entityList.size() == 1U) return; - const types::registry* registry = handle.registry(); + auto& actionQueueItems = actionQueue.val; - internal::maxSizedVector, Constants::ActionQueueLength::MAX> speedSortList; - speedSortList.reserve(entityList.size()); - - for (types::entity entity : entityList) { - speedSortList.push_back({registry->get(entity), entity}); + if (actionQueueItems.size() == 1U) { + return; } // TODO(aed3): Test how different sorting algorithms affect speed - std::sort(speedSortList.begin(), speedSortList.end(), [](const auto& pairA, const auto& pairB) { - if (pairA.first.order != pairB.first.order) { - return pairA.first.order < pairB.first.order; - } - - if (pairA.first.priority != pairB.first.priority) { - return pairB.first.priority < pairA.first.priority; - } - - if (pairA.first.fractionalPriority != pairB.first.fractionalPriority) { - return pairB.first.fractionalPriority; - } - - if (pairA.first.speed != pairB.first.speed) { - return pairB.first.speed < pairA.first.speed; - } - - return false; - }); + std::sort( + actionQueueItems.begin(), + actionQueueItems.end(), + [](const ActionQueueItem& itemA, const ActionQueueItem& itemB) { return itemA.isFasterThan(itemB); }); SpeedTieIndexes speedTies; types::activePokemonIndex lastEqual = 0U, tieCount = 1U; - auto speedSortEqual = [](const SpeedSort& speedSortA, const SpeedSort& speedSortB) { - return speedSortA.order == speedSortB.order && speedSortA.priority == speedSortB.priority && - speedSortA.speed == speedSortB.speed && speedSortA.fractionalPriority == speedSortB.fractionalPriority; - }; - - for (types::activePokemonIndex i = 0U; i < speedSortList.size(); i++) { - entityList[i] = speedSortList[i].second; - - if (i > 0U && speedSortEqual(speedSortList[i].first, speedSortList[i - 1].first)) { + for (types::activePokemonIndex i = 1U; i < actionQueueItems.size(); i++) { + if (actionQueueItems[i].isSameSpeed(actionQueueItems[i - 1U])) { tieCount++; } else { @@ -205,55 +175,51 @@ void speedSort(types::handle handle, ActionQueue& actionQueue) { } } -void addBeforeTurnAction(types::registry& registry, ActionQueue& actionQueue) { - types::handle actionHandle{registry, registry.create()}; - SpeedSort speedSort{ActionOrder::BEFORE_TURN}; - - actionHandle.emplace(); - actionHandle.emplace(speedSort); - actionQueue.val.push_back(actionHandle.entity()); +void addBeforeTurnAction(ActionQueue& actionQueue) { + actionQueue.val.push_back({ActionOrder::BEFORE_TURN}); } -void addResidualAction(types::registry& registry, ActionQueue& actionQueue) { - types::handle actionHandle{registry, registry.create()}; - SpeedSort speedSort{ActionOrder::RESIDUAL}; - - actionHandle.emplace(); - actionHandle.emplace(speedSort); - actionQueue.val.push_back(actionHandle.entity()); +void addResidualAction(ActionQueue& actionQueue) { + actionQueue.val.push_back({ActionOrder::RESIDUAL}); } -void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue) { +void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue, RecycledAction& action) { types::registry& registry = *battleHandle.registry(); if (actionQueue.val.empty()) return; - types::entity newCurrentAction = actionQueue.val.front(); - registry.emplace(newCurrentAction); - - if (registry.all_of(newCurrentAction)) { - battleHandle.emplace(); - battleHandle.emplace(registry.get(newCurrentAction)); - } - else if (registry.all_of(newCurrentAction)) { - battleHandle.emplace(); - } - else { - POKESIM_REQUIRE_FAIL("Action kind not implemented yet."); + ActionQueueItem nextActon = actionQueue.val.front(); + registry.emplace(action.val); + + switch (nextActon.order) { + case ActionOrder::MOVE: { + battleHandle.emplace(); + const MoveDecision& decision = nextActon.decision.get(); + registry.emplace(action.val, decision.sourceSlot); + registry.emplace(action.val, decision.targetSlot); + registry.emplace(action.val, decision.move); + break; + } + case ActionOrder::RESIDUAL: { + battleHandle.emplace(); + break; + } + case ActionOrder::BEFORE_TURN: { + battleHandle.emplace(); + break; + } + default: { + POKESIM_REQUIRE_FAIL("Action kind not implemented yet."); + break; + } } actionQueue.val.erase(actionQueue.val.begin()); - battleHandle.remove(); - battleHandle.emplace(newCurrentAction); - if (!actionQueue.val.empty()) { - battleHandle.emplace(actionQueue.val[0]); - } + battleHandle.emplace(action.val); } -void clearActionQueue(types::handle battleHandle, ActionQueue& actionQueue) { - battleHandle.remove(); - battleHandle.registry()->destroy(actionQueue.val.begin(), actionQueue.val.end()); +void clearActionQueue(ActionQueue& actionQueue) { actionQueue.val.clear(); } } // namespace pokesim::simulate_turn diff --git a/src/SimulateTurn/ManageActionQueue.hpp b/src/SimulateTurn/ManageActionQueue.hpp index b875f4c..dcaf7a4 100644 --- a/src/SimulateTurn/ManageActionQueue.hpp +++ b/src/SimulateTurn/ManageActionQueue.hpp @@ -6,14 +6,15 @@ namespace pokesim { struct SideDecision; struct ActionQueue; +struct RecycledAction; namespace simulate_turn { void resolveDecision(types::handle sideHandle, const SideDecision& sideDecision); void speedSort(types::handle handle, ActionQueue& actionQueue); -void addBeforeTurnAction(types::registry& registry, ActionQueue& actionQueue); -void addResidualAction(types::registry& registry, ActionQueue& actionQueue); -void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue); -void clearActionQueue(types::handle battleHandle, ActionQueue& actionQueue); +void addBeforeTurnAction(ActionQueue& actionQueue); +void addResidualAction(ActionQueue& actionQueue); +void setCurrentAction(types::handle battleHandle, ActionQueue& actionQueue, RecycledAction& action); +void clearActionQueue(ActionQueue& actionQueue); } // namespace simulate_turn } // namespace pokesim diff --git a/src/SimulateTurn/SimulateTurn.cpp b/src/SimulateTurn/SimulateTurn.cpp index 96c844e..2448b82 100644 --- a/src/SimulateTurn/SimulateTurn.cpp +++ b/src/SimulateTurn/SimulateTurn.cpp @@ -6,28 +6,33 @@ #include #include #include +#include #include #include -#include +#include +#include #include #include #include #include -#include +#include #include +#include +#include #include #include #include -#include #include +#include #include #include -#include #include #include #include #include +#include #include +#include #include #include #include @@ -50,63 +55,116 @@ auto getBattleFilter(Simulation& simulation) { return pokesim::internal::EntityFilter{simulation}; } -void addTargetAllyToTargets(types::registry& registry, const Battle& battle) { +void addAddedTarget(types::registry& registry, Battle battle, Slot allySlot) { const Sides& sides = registry.get(battle.val); - const TargetSlotName& targetSlotName = registry.get(registry.get(battle.val).val); - - types::entity allyEntity = slotToAllyPokemonEntity(registry, sides, targetSlotName.val); + types::entity allyEntity = slotToAllyPokemonEntity(registry, sides, allySlot); if (allyEntity == entt::null) { return; } CurrentActionTargets& targets = registry.get(battle.val); + + POKESIM_REQUIRE(!targets.val.empty(), "Added targets should not be the first in the list of targets."); + + if (targets.val.size() == 1U) { + registry.emplace(battle.val); + } + else { + registry.emplace(battle.val); + } + targets.val.push_back(allyEntity); } +void addTargetAllyToTargets(types::registry& registry, Battle battle) { + const TargetSlotName& targetSlotName = registry.get(registry.get(battle.val).val); + addAddedTarget(registry, battle, targetSlotName.val); +} + void addUserAllyToTargets(types::registry& registry, const Battle& battle) { - const Sides& sides = registry.get(battle.val); const SourceSlotName& sourceSlotName = registry.get(registry.get(battle.val).val); + addAddedTarget(registry, battle, sourceSlotName.val); +} - types::entity allyEntity = slotToAllyPokemonEntity(registry, sides, sourceSlotName.val); - if (allyEntity == entt::null) { - return; +void setTargetReferenceComponents( + types::registry& registry, const CurrentActionTargets& targets, CurrentActionSource source) { + for (types::entity target : targets.val) { + registry.emplace(target); + registry.emplace(target, source.val); } - - CurrentActionTargets& targets = registry.get(battle.val); - targets.val.push_back(allyEntity); } -void resolveMoveTargets(types::registry& registry, CurrentActionTargets& targets) { - for (types::entity target : targets.val) { - registry.emplace_or_replace(target); +template +void setActionMoveReferenceComponents( + types::handle battleHandle, CurrentActionSource source, CurrentActionTargets targets, CurrentAction action, + RecycledActionMoveType actionMove) { + types::registry& registry = *battleHandle.registry(); + types::handle actionMoveHandle{registry, actionMove.val}; + types::entity target; + + if constexpr (std::is_same_v) { + target = targets.val[0]; + } + else if constexpr (std::is_same_v) { + target = targets.val[1]; + } + else if constexpr (std::is_same_v) { + target = targets.val[2]; + } + else { + POKESIM_REQUIRE_FAIL("Using a RecycledActionMoveType that isn't associated with a target."); } - // More to do... + MoveName move = registry.get(action.val); + setupActionMoveBuild(registry, battleHandle.entity(), source.val, target, actionMove.val, move.val); } -void createActionMoveForTargets( - types::handle targetHandle, Battle battle, CurrentActionSource source, const Pokedex& pokedex) { - types::registry& registry = *targetHandle.registry(); +void setActionMoveData(Simulation& simulation) { + simulation.addToEntities(); + simulation.pokedex().buildMoves(simulation.registry); +} - dex::Move move = registry.get(registry.get(battle.val).val).val; - types::entity moveEntity = createActionMoveForTarget(targetHandle, battle.val, source.val, move, pokedex); +void setCurrentActionMoveSlot(types::handle handle, CurrentActionSource source, CurrentAction action) { + types::registry& registry = *handle.registry(); + const MoveName& move = registry.get(action.val); + const MoveSlots& moveSlots = registry.get(source.val); - registry.emplace(moveEntity); + types::moveSlotIndex moveSlotIndex = moveToMoveSlot(moveSlots, move.val); + handle.emplace(moveSlotIndex); } -void getMoveTargets(Simulation& simulation) { +void setMoveTargets(Simulation& simulation) { + pokesim::internal::EntityFilter battleFilter{simulation}; + if (battleFilter.hasNoneSelected()) { + return; + } + + battleFilter.view(simulation); + + battleFilter.view>(); + setActionMoveData(simulation); + simulation.removeFromEntities(); + + runModifyTarget(simulation); if (simulation.isBattleFormat(BattleFormat::DOUBLES)) { simulation .view>(); simulation .view>(); + + battleFilter.view< + setActionMoveReferenceComponents, + Tags>(); + battleFilter.view< + setActionMoveReferenceComponents, + Tags>(); + setActionMoveData(simulation); + simulation.removeFromEntities(); + simulation.removeFromEntities(); + simulation.removeFromEntities(); } - simulation.view, entt::exclude_t>(); - simulation.view< - createActionMoveForTargets, - Tags, - entt::exclude_t>(simulation.pokedex()); + battleFilter.view(); } void useMove(Simulation& simulation) { @@ -114,7 +172,6 @@ void useMove(Simulation& simulation) { // ModifyType runModifyMove(simulation); - getMoveTargets(simulation); runMoveHitChecks(simulation); runAfterMoveUsedEvent(simulation); } @@ -143,13 +200,13 @@ void runMoveAction(Simulation& simulation) { return; } - battleFilter.view(simulation); - battleFilter.view(simulation.pokedex()); + setMoveTargets(simulation); + battleFilter.view(); runBeforeMove(simulation); - simulation.view>(); simulation.view(); + simulation.view>(); useMove(simulation); } @@ -189,7 +246,7 @@ void checkWin(types::handle battleHandle, const Sides& sides) { types::teamPositionIndex pokemonLeft = foeSidePokemonLeft(registry, sideEntity); if (!pokemonLeft) { battleHandle.emplace(registry.get(sideEntity).val); - clearActionQueue(battleHandle, battleHandle.get()); + clearActionQueue(battleHandle.get()); return; } } @@ -256,16 +313,12 @@ void incrementTurn(Turn& turn) { turn.val++; } -void updateActivePokemonPostTurn(types::registry& registry, const pokesim::MoveSlots& moveSlots) { - registry.remove(moveSlots.val.begin(), moveSlots.val.end()); -} - void nextTurn(Simulation& simulation) { getBattleFilter(simulation).view(); pokesim::internal::EntityFilter pokemonFilter{simulation}; if (!pokemonFilter.hasNoneSelected()) { - pokemonFilter.view(); + simulation.removeFromEntities(); pokemonFilter.addToSelected(); runDisableMove(simulation); @@ -293,8 +346,11 @@ void simulateTurn(Simulation& simulation) { simulation.addToEntities(); const auto entityMap = clone(simulation.registry, 1U); for (const auto& inputBattleMapping : entityMap) { - simulation.registry.emplace(inputBattleMapping.first); - simulation.registry.remove(inputBattleMapping.first); + types::entity original = inputBattleMapping.first; + if (simulation.registry.all_of(original)) { + simulation.registry.emplace(original); + simulation.registry.remove(original); + } } } @@ -308,7 +364,7 @@ void simulateTurn(Simulation& simulation) { battleFilter.view(); battleFilter.view, entt::exclude_t>(); - simulation.addToEntities(); + simulation.addToEntities(); battleFilter.view(); using ActionsLimit = Constants::ActionQueueLength; @@ -325,8 +381,7 @@ void simulateTurn(Simulation& simulation) { nextTurn(simulation); - simulation - .removeFromEntities(); + simulation.removeFromEntities(); battleFilter.view(); simulation.addToEntities(); diff --git a/src/SimulateTurn/SimulateTurnDebugChecks.hpp b/src/SimulateTurn/SimulateTurnDebugChecks.hpp index f9bcdfd..f0165fe 100644 --- a/src/SimulateTurn/SimulateTurnDebugChecks.hpp +++ b/src/SimulateTurn/SimulateTurnDebugChecks.hpp @@ -5,23 +5,26 @@ #ifdef POKESIM_DEBUG_CHECK_UTILITIES #include -#include +#include #include #include +#include +#include #include #include +#include #include +#include #include #include #include namespace pokesim::simulate_turn::debug { struct Checks : pokesim::debug::Checks { - Options options; - Checks(const Simulation& _simulation) - : pokesim::debug::Checks(_simulation), options(_simulation.simulateTurnOptions) {} + Checks(const Simulation& _simulation) : pokesim::debug::Checks(_simulation) {} - void checkInputs() const { + void checkInputs() { + const auto& options = simulateTurnOptionsOnInput; pokesim::debug::check(options.getDamageRollsConsidered()); pokesim::debug::checkPercentChance(options.getRandomChanceLowerLimit()); pokesim::debug::checkPercentChance(options.getRandomChanceUpperLimit()); @@ -29,26 +32,75 @@ struct Checks : pokesim::debug::Checks { pokesim::debug::check(Probability{options.getBranchProbabilityLowerLimit()}); } + copyBattles(); check(); } void checkOutputs() const { - POKESIM_REQUIRE_NM(options == simulation->simulateTurnOptions); - + checkOptions(); + checkBattleOutputs(); check(); } private: + auto getBattleView() const { return registry->view(); } + + void copyBattles() { + for (types::entity entity : getBattleView()) { + copyEntity(entity); + copyEntity(registry->get(entity).val); + } + } + + void checkBattleOutputs() const { + pokesim::debug::TypesToIgnore typesToIgnore; + typesToIgnore.add(); + + pokesim::debug::TypesToIgnore typesIgnoredOnConstants = typesToIgnore; + typesToIgnore.add(); + + if (!simulateTurnOptionsOnInput.getMakeBranchesOnRandomEvents()) { + typesToIgnore.add(); + } + + for (types::entity currentEntity : getBattleView()) { + types::entity original = pokesim::debug::findCopyParent(currentEntitiesToInitial, *registry, currentEntity); + bool shouldNotChange = !simulateTurnOptionsOnInput.getApplyChangesToInputBattle() && original == currentEntity; + if (!registryOnInput.all_of(original)) { + typesToIgnore.add(); + } + + types::entity initialEntity = getInitialEntity(currentEntity); + pokesim::debug::areEntitiesEqual( + *registry, + currentEntity, + registryOnInput, + initialEntity, + shouldNotChange ? typesIgnoredOnConstants : typesToIgnore); + + bool initialIsMidTurn = registryOnInput.all_of(initialEntity); + bool currentIsMidTurn = registry->all_of(currentEntity); + types::entity currAction = registry->get(currentEntity).val; + if (!initialIsMidTurn && !currentIsMidTurn) { + pokesim::debug::areEntitiesEqual(*registry, currAction, registryOnInput, getInitialEntity(currAction)); + } + + if (!currentIsMidTurn) { + types::registry blankRegistry; + types::entity idealRecycledAction = blankRegistry.create(); + blankRegistry.emplace(idealRecycledAction); + pokesim::debug::hasSameComponents(*registry, currAction, blankRegistry, idealRecycledAction); + } + } + } + void check() const { - for (types::entity battleEntity : registry->view()) { + for (types::entity battleEntity : getBattleView()) { checkBattle(battleEntity); for (types::entity sideEntity : registry->get(battleEntity).val) { checkSide(sideEntity); for (types::entity pokemonEntity : registry->get(sideEntity).val) { checkPokemon(pokemonEntity); - for (types::entity moveEntity : registry->get(pokemonEntity).val) { - checkMoveSlot(moveEntity); - } } } } diff --git a/src/Simulation/BattleCreationInfo.hpp b/src/Simulation/BattleCreationInfo.hpp index 2af33ab..7ba18dc 100644 --- a/src/Simulation/BattleCreationInfo.hpp +++ b/src/Simulation/BattleCreationInfo.hpp @@ -1,9 +1,10 @@ #pragma once -#include +#include #include #include #include +#include #include #include #include diff --git a/src/Simulation/RunEvent.cpp b/src/Simulation/RunEvent.cpp index eb45d1a..9286ddd 100644 --- a/src/Simulation/RunEvent.cpp +++ b/src/Simulation/RunEvent.cpp @@ -6,11 +6,11 @@ #include #include #include +#include #include -#include #include -#include #include +#include #include #include #include @@ -130,6 +130,8 @@ template void runAfterEachBoostEvent(Simulation&); void runAfterBoostEvent(Simulation&) {} +void runModifyTarget(Simulation&) {} + void runModifyMove(Simulation& simulation) { dex::ChoiceScarf::onSourceModifyMove(simulation); dex::ChoiceSpecs::onSourceModifyMove(simulation); diff --git a/src/Simulation/RunEvent.hpp b/src/Simulation/RunEvent.hpp index 560a0e5..5d453c3 100644 --- a/src/Simulation/RunEvent.hpp +++ b/src/Simulation/RunEvent.hpp @@ -31,6 +31,7 @@ void runTryBoostEvent(Simulation& simulation); template void runAfterEachBoostEvent(Simulation& simulation); void runAfterBoostEvent(Simulation& simulation); +void runModifyTarget(Simulation& simulation); // onModifyMove for Curse and Expanding force should go here void runModifyMove(Simulation& simulation); void runDisableMove(Simulation& simulation); diff --git a/src/Simulation/Simulation.hpp b/src/Simulation/Simulation.hpp index d2bcce1..5f632ff 100644 --- a/src/Simulation/Simulation.hpp +++ b/src/Simulation/Simulation.hpp @@ -13,9 +13,6 @@ #include "SimulationOptions.hpp" namespace pokesim { -struct SideStateSetup; -struct PokemonStateSetup; -struct BattleStateSetup; class Pokedex; namespace simulate_turn { @@ -27,9 +24,6 @@ struct Results; namespace analyze_effect { struct Results; } // namespace analyze_effect -namespace debug { -struct SimulationSetupChecks; -} /** * @brief The entry point for creating and running simulations. @@ -38,22 +32,6 @@ struct SimulationSetupChecks; * for running multiple simulations of the same battle, where each battle state has completed the same number of turns. */ class Simulation { - private: - types::entityVector createInitialMoves(const std::vector& moveInfoList); - PokemonStateSetup createInitialPokemon(const PokemonCreationInfo& pokemonInfo); - void createInitialSide( - SideStateSetup sideSetup, const SideCreationInfo& sideInfo, const BattleCreationInfo& battleInfo); - - void createInitialTurnDecision(BattleStateSetup battleStateSetup, const TurnDecisionInfo& turnDecisionInfo); - void createCalcDamageInput( - BattleStateSetup battleStateSetup, const CalcDamageInputInfo& inputInfo, debug::SimulationSetupChecks& debugChecks); - void createAnalyzeEffectInput( - BattleStateSetup battleStateSetup, const AnalyzeEffectInputInfo& inputInfo, - debug::SimulationSetupChecks& debugChecks); - - types::sides createInitialBattle( - BattleStateSetup battleStateSetup, const BattleCreationInfo& battleInfo); - private: struct ConstantValues { ConstantValues(const Pokedex& pokedex_, BattleFormat battleFormat_) @@ -141,14 +119,22 @@ class Simulation { } template void addToEntities(const Args&... args) { + static_assert( + sizeof...(ViewComponents) != 0, + "Using this function without view components will cause Type to be added to every entity."); auto view = registry.view(); registry.insert(view.begin(), view.end(), args...); } template void removeFromEntities(entt::exclude_t exclude = entt::exclude_t{}) { - auto view = registry.view(exclude); - registry.remove(view.begin(), view.end()); + if constexpr (sizeof...(ViewComponents) == 0 && sizeof...(ExcludeComponents) == 0) { + registry.clear(); + } + else { + auto view = registry.view(exclude); + registry.remove(view.begin(), view.end()); + } } }; } // namespace pokesim diff --git a/src/Simulation/SimulationSetup.cpp b/src/Simulation/SimulationSetup.cpp index 1952020..f7a455f 100644 --- a/src/Simulation/SimulationSetup.cpp +++ b/src/Simulation/SimulationSetup.cpp @@ -6,23 +6,26 @@ #include #include #include -#include -#include #include #include #include -#include +#include #include #include +#include +#include #include #include +#include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -34,6 +37,83 @@ namespace pokesim { namespace { +std::size_t getBattleCreationCount(const BattleCreationInfo& battleInfo) { + return std::max((std::size_t)1UL, battleInfo.decisionsToSimulate.size()); +} + +struct EntityLists { + struct EntityList { + private: + types::entityVector list{}; + types::entityIndex index = 0U; + + public: + EntityList() {} + EntityList(types::registry& registry, types::entityIndex size) : list(size) { + registry.create(list.begin(), list.end()); + } + + types::entity getNext() { + POKESIM_REQUIRE(index < list.size(), "More entities are being asked for than were created."); + types::entity nextEntity = list[index]; + index++; + return nextEntity; + } + }; + + EntityList battles; + EntityList sides; + EntityList pokemon; + EntityList recycledActions; + EntityList recycledActionMoves; + EntityList addedRecycledActionMoves; + EntityList calcDamageInputs; + EntityList analyzeEffectInputs; + + EntityLists(Simulation* simulation, const std::vector& battleInfoList) { + types::entityIndex battleCount = 0U; + types::entityIndex pokemonCount = 0U; + types::entityIndex calcDamageInputCount = 0U; + types::entityIndex analyzeEffectInputCount = 0U; + for (const BattleCreationInfo& battleInfo : battleInfoList) { + types::entityIndex battleCountIncrease = (types::entityIndex)getBattleCreationCount(battleInfo); + battleCount += battleCountIncrease; + + for (const SideCreationInfo& side : battleInfo.sides) { + pokemonCount += (types::teamPositionIndex)side.team.size() * battleCountIncrease; + } + + for (const CalcDamageInputInfo& calcDamageInputInfo : battleInfo.damageCalculations) { + calcDamageInputCount += (types::entityIndex)calcDamageInputInfo.moves.size(); + } + for (const AnalyzeEffectInputInfo& analyzeEffectInputInfo : battleInfo.effectsToAnalyze) { + analyzeEffectInputCount += (types::entityIndex)analyzeEffectInputInfo.moves.size(); + } + } + + types::entityIndex sideCount = battleCount * 2U; + types::entityIndex recycledActionCount = battleCount; + types::entityIndex recycledActionMoveCount = battleCount; + types::entityIndex addedRecycledActionMoveCount = + simulation->isBattleFormat(BattleFormat::DOUBLES) ? battleCount * 2U : 0U; + + POKESIM_REQUIRE( + battleCount + sideCount + recycledActionCount + pokemonCount + calcDamageInputCount + analyzeEffectInputCount < + std::numeric_limits::max(), + "More entities than can be created would be made for this input."); + + types::registry& registry = simulation->registry; + battles = {registry, battleCount}; + sides = {registry, sideCount}; + pokemon = {registry, pokemonCount}; + recycledActions = {registry, recycledActionCount}; + recycledActionMoves = {registry, recycledActionMoveCount}; + addedRecycledActionMoves = {registry, addedRecycledActionMoveCount}; + calcDamageInputs = {registry, calcDamageInputCount}; + analyzeEffectInputs = {registry, analyzeEffectInputCount}; + } +}; + void setPokemonAbility( const PokemonCreationInfo& pokemonInfo, PokemonStateSetup& pokemonSetup, const Pokedex& pokedex) { if (pokemonInfo.ability != dex::Ability::NO_ABILITY) { @@ -146,31 +226,9 @@ void setPokemonCurrentBoosts(const PokemonCreationInfo& pokemonInfo, PokemonStat if (pokemonInfoBoosts.spd.has_value()) pokemonSetup.setBoost(pokemonInfoBoosts.spd.value()); if (pokemonInfoBoosts.spe.has_value()) pokemonSetup.setBoost(pokemonInfoBoosts.spe.value()); } -} // namespace -types::entityVector Simulation::createInitialMoves(const std::vector& moveInfoList) { - types::entityVector moveEntities{}; - moveEntities.reserve((types::entityVector::size_type)moveInfoList.size()); - - for (const MoveCreationInfo& moveInfo : moveInfoList) { - MoveStateSetup moveSetup(registry); - moveSetup.setName(moveInfo.name); - types::pp maxPp = Constants::MoveMaxPp::DEFAULT; - if (!moveInfo.pp.has_value() || !moveInfo.maxPp.has_value()) { - maxPp = pokedex().getMoveData(moveInfo.name).val; - } - maxPp = moveInfo.maxPp.value_or(maxPp); - - moveSetup.setPP(moveInfo.pp.value_or(maxPp)); - moveSetup.setMaxPP(maxPp); - moveEntities.push_back(moveSetup.entity()); - } - - return moveEntities; -} - -PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& pokemonInfo) { - PokemonStateSetup pokemonSetup(registry); +void createInitialPokemon( + const PokemonCreationInfo& pokemonInfo, PokemonStateSetup& pokemonSetup, const Pokedex& pokedex) { if (pokemonInfo.id.has_value()) { pokemonSetup.setID(pokemonInfo.id.value()); } @@ -179,13 +237,13 @@ PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& po } pokemonSetup.setSpecies(pokemonInfo.species); - setPokemonAbility(pokemonInfo, pokemonSetup, pokedex()); + setPokemonAbility(pokemonInfo, pokemonSetup, pokedex); types::level level = pokemonInfo.level.value_or(Constants::PokemonLevel::DEFAULT); dex::Nature nature = setPokemonNature(pokemonInfo, pokemonSetup); Evs evs = setPokemonEvs(pokemonInfo, pokemonSetup); Ivs ivs = setPokemonIvs(pokemonInfo, pokemonSetup); - types::stat hp = setPokemonStats(pokemonInfo, pokemonSetup, pokedex(), level, nature, evs, ivs); + types::stat hp = setPokemonStats(pokemonInfo, pokemonSetup, pokedex, level, nature, evs, ivs); pokemonSetup.setLevel(level); @@ -193,7 +251,7 @@ PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& po pokemonSetup.setTypes(pokemonInfo.currentTypes.value()); } else { - pokemonSetup.setTypes(pokedex().getSpeciesData(pokemonInfo.species)); + pokemonSetup.setTypes(pokedex.getSpeciesData(pokemonInfo.species)); } if (pokemonInfo.gender.has_value() && pokemonInfo.gender != dex::Gender::NO_GENDER) { @@ -221,41 +279,102 @@ PokemonStateSetup Simulation::createInitialPokemon(const PokemonCreationInfo& po pokemonSetup.setProperty(); pokemonSetup.setProperty(); pokemonSetup.setProperty(); +} + +void createCalcDamageInput( + const CalcDamageInputInfo& inputInfo, BattleStateSetup& battleSetup, types::registry& registry, + EntityLists& entityLists, debug::SimulationSetupChecks& debugChecks) { + POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "A damage calculation must have a attacker."); + POKESIM_REQUIRE(inputInfo.defenderSlot != Slot::NONE, "A damage calculation must have a defender."); + POKESIM_REQUIRE(!inputInfo.moves.empty(), "A damage calculation must have a move."); + + const Sides& sides = registry.get(battleSetup.entity()); + types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); + types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); - return pokemonSetup; + for (dex::Move move : inputInfo.moves) { + calc_damage::InputSetup inputSetup{registry, entityLists.calcDamageInputs.getNext()}; + POKESIM_REQUIRE(move != dex::Move::NO_MOVE, "A damage calculation must have a move."); + + inputSetup.setup(battleSetup.entity(), attackerEntity, defenderEntity, move); + debugChecks.addToCalcDamageChecklist(battleSetup, inputSetup, inputInfo); + } } -void Simulation::createInitialSide( - SideStateSetup sideSetup, const SideCreationInfo& sideInfo, const BattleCreationInfo& battleInfo) { - internal::maxSizedVector pokemonSetupList; - pokemonSetupList.reserve(sideInfo.team.size()); +void createAnalyzeEffectInput( + const AnalyzeEffectInputInfo& inputInfo, BattleStateSetup& battleSetup, types::registry& registry, + EntityLists& entityLists, debug::SimulationSetupChecks& debugChecks) { + POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "An effect analysis must have a attacker."); + 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( + boostEffect.has_value() || (effect.has_value() && !effect.value().empty()), + "An effect analysis must have an effect."); + + const Sides& sides = registry.get(battleSetup.entity()); + types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); + types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); + types::entity effectTargetEntity = slotToPokemonEntity(registry, sides, inputInfo.effectTarget); + + for (dex::Move move : inputInfo.moves) { + analyze_effect::InputSetup inputSetup{registry, entityLists.analyzeEffectInputs.getNext()}; + + inputSetup.setAttacker(attackerEntity); + inputSetup.setDefender(defenderEntity); + inputSetup.setEffectTarget(effectTargetEntity); + inputSetup.setEffectMove(move); + inputSetup.setBattle(battleSetup.entity()); + + if (effect.has_value()) { + inputSetup.setEffect(effect.value()); + } + if (boostEffect.has_value()) { + inputSetup.setBoostEffect(boostEffect.value().stat, boostEffect.value().boost); + } + debugChecks.addToAnalyzeEffectChecklist(battleSetup, inputSetup, inputInfo); + } +} + +void createInitialTurnDecision(const TurnDecisionInfo& turnDecisionInfo, types::sides& sideSetupList) { + for (types::sideIndex i = 0U; i < sideSetupList.size(); i++) { + sideSetupList[i].setSideDecision(turnDecisionInfo[i]); + } +} + +void createInitialSide( + const SideCreationInfo& sideInfo, SideStateSetup& sideSetup, const BattleCreationInfo& battleInfo, + Simulation* simulation, internal::maxSizedVector& pokemonSetupList) { for (std::size_t i = 0U; i < sideInfo.team.size(); i++) { + PokemonStateSetup& pokemonSetup = pokemonSetupList[i]; const PokemonCreationInfo& pokemonInfo = sideInfo.team[i]; - PokemonStateSetup pokemonSetup = createInitialPokemon(pokemonInfo); + bool battleStarted = battleInfo.turn > Constants::TurnCount::MIN; - bool inActiveSlot = (isBattleFormat(BattleFormat::SINGLES) ? 1U : 2U) > i; + bool inActiveSlot = (simulation->isBattleFormat(BattleFormat::SINGLES) ? 1U : 2U) > i; bool isFainted = pokemonInfo.currentHp.has_value() && pokemonInfo.currentHp == Constants::PokemonCurrentHpStat::MIN; if (battleStarted && inActiveSlot && !isFainted) { pokemonSetup.setProperty(); } - types::entityVector moveEntities = createInitialMoves(pokemonInfo.moves); + std::vector moveSlots; + for (const MoveCreationInfo& moveInfo : pokemonInfo.moves) { + types::pp maxPp = Constants::MoveMaxPp::DEFAULT; + if (!moveInfo.pp.has_value() || !moveInfo.maxPp.has_value()) { + maxPp = simulation->pokedex().getMoveData(moveInfo.name).val; + } + maxPp = moveInfo.maxPp.value_or(maxPp); - if (battleInfo.runWithSimulateTurn) { - registry.insert(moveEntities.begin(), moveEntities.end()); - } - if (battleInfo.runWithCalculateDamage) { - registry.insert(moveEntities.begin(), moveEntities.end()); - } - if (battleInfo.runWithAnalyzeEffect) { - registry.insert(moveEntities.begin(), moveEntities.end()); + moveSlots.push_back({moveInfo.name, moveInfo.pp.value_or(maxPp), maxPp}); } - pokemonSetup.setMoves(moveEntities); - pokemonSetupList.push_back(pokemonSetup); + pokemonSetup.setMoves(moveSlots); } + types::registry& registry = simulation->registry; if (battleInfo.runWithSimulateTurn) { sideSetup.setProperty(); registry.insert(pokemonSetupList.begin(), pokemonSetupList.end()); @@ -272,166 +391,131 @@ void Simulation::createInitialSide( sideSetup.setTeam(pokemonSetupList); } -types::sides Simulation::createInitialBattle( - BattleStateSetup battleStateSetup, const BattleCreationInfo& battleInfo) { - battleStateSetup.setAutoID(); - battleStateSetup.setTurn(battleInfo.turn.value_or(Constants::TurnCount::DEFAULT)); - battleStateSetup.setRNGSeed(battleInfo.rngSeed); - battleStateSetup.setProbability(battleInfo.probability.value_or(Constants::Probability::DEFAULT)); +void createInitialBattle( + const BattleCreationInfo& battleInfo, BattleStateSetup& battleSetup, types::sides& sideSetupList) { + battleSetup.setTurn(battleInfo.turn.value_or(Constants::TurnCount::DEFAULT)); + battleSetup.setRNGSeed(battleInfo.rngSeed); + battleSetup.setProbability(battleInfo.probability.value_or(Constants::Probability::DEFAULT)); if (battleInfo.runWithSimulateTurn) { - battleStateSetup.setProperty(); + battleSetup.setProperty(); } if (battleInfo.runWithCalculateDamage) { - battleStateSetup.setProperty(); + battleSetup.setProperty(); } if (battleInfo.runWithAnalyzeEffect) { - battleStateSetup.setProperty(); + battleSetup.setProperty(); } - SideStateSetup p1SideSetup(registry, PlayerSideId::P1); - SideStateSetup p2SideSetup(registry, PlayerSideId::P2); + SideStateSetup& p1SideSetup = sideSetupList.p1(); + SideStateSetup& p2SideSetup = sideSetupList.p2(); + + p1SideSetup.setPlayerSide(PlayerSideId::P1); + p2SideSetup.setPlayerSide(PlayerSideId::P2); - types::entity battleEntity = battleStateSetup.entity(); + types::entity battleEntity = battleSetup.entity(); types::entity p1Entity = p1SideSetup.entity(); types::entity p2Entity = p2SideSetup.entity(); - battleStateSetup.setSide(p1Entity); - battleStateSetup.setSide(p2Entity); + battleSetup.setSide(p1Entity); + battleSetup.setSide(p2Entity); p1SideSetup.setOpponent(p2Entity); p2SideSetup.setOpponent(p1Entity); p1SideSetup.setBattle(battleEntity); p2SideSetup.setBattle(battleEntity); - - return {p1SideSetup, p2SideSetup}; } -void Simulation::createInitialTurnDecision( - BattleStateSetup battleStateSetup, const TurnDecisionInfo& turnDecisionInfo) { - types::handle battleHandle{registry, battleStateSetup.entity()}; - const Sides& sides = battleHandle.get(); - - for (types::sideIndex i = 0U; i < sides.val.size(); i++) { - registry.emplace(sides.val[i], turnDecisionInfo[i]); +void createInitialState( + const BattleCreationInfo& battleInfo, Simulation* simulation, EntityLists& entityLists, + debug::SimulationSetupChecks& debugChecks) { + internal::maxSizedVector battleSetupList; + internal::maxSizedVector> sideSetupLists; + internal::maxSizedVector>> + pokemonSetupLists; + battleSetupList.resize(getBattleCreationCount(battleInfo)); + sideSetupLists.resize(battleSetupList.size()); + pokemonSetupLists.resize(battleSetupList.size()); + + const Pokedex& pokedex = simulation->pokedex(); + types::registry& registry = simulation->registry; + + for (BattleStateSetup& battleSetup : battleSetupList) { + battleSetup = {registry, entityLists.battles.getNext()}; } -} - -void Simulation::createCalcDamageInput( - BattleStateSetup battleStateSetup, const CalcDamageInputInfo& inputInfo, debug::SimulationSetupChecks& debugChecks) { - POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "A damage calculation must have a attacker."); - POKESIM_REQUIRE(inputInfo.defenderSlot != Slot::NONE, "A damage calculation must have a defender."); - POKESIM_REQUIRE(!inputInfo.moves.empty(), "A damage calculation must have a move."); - - const Sides& sides = registry.get(battleStateSetup.entity()); - types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); - types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); - for (dex::Move move : inputInfo.moves) { - calc_damage::InputSetup inputSetup(registry); - POKESIM_REQUIRE(move != dex::Move::NO_MOVE, "A damage calculation must have a move."); - - inputSetup.setup(battleStateSetup.entity(), attackerEntity, defenderEntity, move, pokedex()); - debugChecks.addToCalcDamageChecklist(battleStateSetup, inputSetup, inputInfo); + for (types::sideIndex sideIndex = 0; sideIndex < battleInfo.sides.size(); sideIndex++) { + for (auto& sideSetupList : sideSetupLists) { + sideSetupList[sideIndex] = {registry, entityLists.sides.getNext()}; + } } -} -void Simulation::createAnalyzeEffectInput( - BattleStateSetup battleStateSetup, const AnalyzeEffectInputInfo& inputInfo, - debug::SimulationSetupChecks& debugChecks) { - POKESIM_REQUIRE(inputInfo.attackerSlot != Slot::NONE, "An effect analysis must have a attacker."); - 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."); + for (types::entityIndex battleIndex = 0; battleIndex < battleSetupList.size(); battleIndex++) { + BattleStateSetup& battleSetup = battleSetupList[battleIndex]; + createInitialBattle(battleInfo, battleSetup, sideSetupLists[battleIndex]); + battleSetup.setRecycledAction(entityLists.recycledActions.getNext(), entityLists.recycledActionMoves.getNext()); + if (simulation->isBattleFormat(BattleFormat::DOUBLES)) { + battleSetup.setAddedRecycledActionMoves( + entityLists.addedRecycledActionMoves.getNext(), + entityLists.addedRecycledActionMoves.getNext()); + } + } - const auto& effect = inputInfo.effect; - const auto& boostEffect = inputInfo.boostEffect; - POKESIM_REQUIRE( - boostEffect.has_value() || (effect.has_value() && !effect.value().empty()), - "An effect analysis must have an effect."); + for (types::sideIndex sideIndex = 0; sideIndex < battleInfo.sides.size(); sideIndex++) { + for (const PokemonCreationInfo& pokemonInfo : battleInfo.sides[sideIndex].team) { + for (auto& pokemonSetupList : pokemonSetupLists) { + PokemonStateSetup& pokemonSetup = + pokemonSetupList[sideIndex].emplace_back(registry, entityLists.pokemon.getNext()); + createInitialPokemon(pokemonInfo, pokemonSetup, pokedex); + } + } + } - const Sides& sides = registry.get(battleStateSetup.entity()); - types::entity attackerEntity = slotToPokemonEntity(registry, sides, inputInfo.attackerSlot); - types::entity defenderEntity = slotToPokemonEntity(registry, sides, inputInfo.defenderSlot); - types::entity effectTargetEntity = slotToPokemonEntity(registry, sides, inputInfo.effectTarget); + for (types::entityIndex battleIndex = 0; battleIndex < battleSetupList.size(); battleIndex++) { + BattleStateSetup& battleSetup = battleSetupList[battleIndex]; + auto& sideSetupList = sideSetupLists[battleIndex]; + for (types::sideIndex sideIndex = 0; sideIndex < battleInfo.sides.size(); sideIndex++) { + createInitialSide( + battleInfo.sides[sideIndex], + sideSetupList[sideIndex], + battleInfo, + simulation, + pokemonSetupLists[battleIndex][sideIndex]); + } - for (dex::Move move : inputInfo.moves) { - analyze_effect::InputSetup inputSetup(registry); + if (battleIndex < battleInfo.decisionsToSimulate.size()) { + const TurnDecisionInfo& turnDecisionInfo = battleInfo.decisionsToSimulate[battleIndex]; + createInitialTurnDecision(turnDecisionInfo, sideSetupList); + debugChecks.addToTurnDecisionChecklist(battleSetup, turnDecisionInfo); + } + battleSetup.setID(battleIndex); + debugChecks.addToBattleChecklist(battleSetup, battleInfo); + } - inputSetup.setAttacker(attackerEntity); - inputSetup.setDefender(defenderEntity); - inputSetup.setEffectTarget(effectTargetEntity); - inputSetup.setEffectMove(move); - inputSetup.setBattle(battleStateSetup.entity()); + BattleStateSetup& battleSetup = battleSetupList[0]; + for (const CalcDamageInputInfo& calcDamageInputInfo : battleInfo.damageCalculations) { + createCalcDamageInput(calcDamageInputInfo, battleSetup, registry, entityLists, debugChecks); + } - if (effect.has_value()) { - inputSetup.setEffect(effect.value()); - } - if (boostEffect.has_value()) { - inputSetup.setBoostEffect(boostEffect.value().stat, boostEffect.value().boost); - } - debugChecks.addToAnalyzeEffectChecklist(battleStateSetup, inputSetup, inputInfo); + for (const AnalyzeEffectInputInfo& analyzeEffectInputInfo : battleInfo.effectsToAnalyze) { + createAnalyzeEffectInput(analyzeEffectInputInfo, battleSetup, registry, entityLists, debugChecks); } } +} // namespace void Simulation::createInitialStates(const std::vector& battleInfoList) { debug::SimulationSetupChecks debugChecks(this, battleInfoList); - for (const BattleCreationInfo& battleInfo : battleInfoList) { - BattleStateSetup battleStateSetup(registry); - types::sides sideSetup = createInitialBattle(battleStateSetup, battleInfo); - - for (types::sideIndex i = 0U; i < sideSetup.size(); i++) { - createInitialSide(sideSetup[i], battleInfo.sides[i], battleInfo); - } - - debugChecks.addToBattleChecklist(battleStateSetup, battleInfo); - - if (!battleInfo.decisionsToSimulate.empty()) { - POKESIM_REQUIRE( - battleInfo.decisionsToSimulate.size() < std::numeric_limits::max(), - "Cannot make more clones than there are entities available."); - - types::entityIndex cloneCount = (types::entityIndex)(battleInfo.decisionsToSimulate.size() - 1U); - if (cloneCount) { - battleStateSetup.setProperty(); - const types::ClonedEntityMap entityMap = pokesim::clone(registry, cloneCount); - - const auto& clonedBattles = entityMap.at(battleStateSetup.entity()); - internal::maxSizedVector clones; - clones.reserve(clonedBattles.size()); - - for (types::entity entity : clonedBattles) { - clones.emplace_back(registry, entity); - } - - for (types::entityIndex i = 0U; i < cloneCount; i++) { - BattleStateSetup& setupClone = clones[i]; - const TurnDecisionInfo& turnDecisionInfo = battleInfo.decisionsToSimulate[i]; - debugChecks.addToBattleChecklist(setupClone, battleInfo); - - createInitialTurnDecision(setupClone, turnDecisionInfo); - setupClone.setID(i); + EntityLists entityLists{this, battleInfoList}; - debugChecks.addToTurnDecisionChecklist(setupClone, turnDecisionInfo); - } - } - - createInitialTurnDecision(battleStateSetup, battleInfo.decisionsToSimulate.back()); - debugChecks.addToTurnDecisionChecklist(battleStateSetup, battleInfo.decisionsToSimulate.back()); - battleStateSetup.setID(cloneCount); - } - - for (const CalcDamageInputInfo& calcDamageInputInfo : battleInfo.damageCalculations) { - createCalcDamageInput(battleStateSetup, calcDamageInputInfo, debugChecks); - } - - for (const AnalyzeEffectInputInfo& analyzeEffectInputInfo : battleInfo.effectsToAnalyze) { - createAnalyzeEffectInput(battleStateSetup, analyzeEffectInputInfo, debugChecks); - } + for (const BattleCreationInfo& battleInfo : battleInfoList) { + createInitialState(battleInfo, this, entityLists, debugChecks); } + pokedex().buildMoves(registry); + registry.clear(); + debugChecks.checkOutputs(); } } // namespace pokesim diff --git a/src/Simulation/SimulationSetupDebugChecks.hpp b/src/Simulation/SimulationSetupDebugChecks.hpp index 9979682..71ba976 100644 --- a/src/Simulation/SimulationSetupDebugChecks.hpp +++ b/src/Simulation/SimulationSetupDebugChecks.hpp @@ -7,19 +7,19 @@ #include #include #include +#include #include #include #include #include -#include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -32,8 +32,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -274,18 +274,15 @@ struct SimulationSetupChecks { POKESIM_REQUIRE_NM(moveSlots.val.size() == creationInfo.moves.size()); for (std::size_t i = 0U; i < creationInfo.moves.size(); i++) { - const MoveCreationInfo& move = creationInfo.moves[i]; - types::entity moveEntity = moveSlots.val[(types::moveSlotIndex)i]; - POKESIM_REQUIRE_NM(registry->all_of(moveEntity)); - POKESIM_REQUIRE_NM(registry->all_of(moveEntity)); - POKESIM_REQUIRE_NM(registry->all_of(moveEntity)); - POKESIM_REQUIRE_NM(registry->get(moveEntity).val == move.name); + const MoveCreationInfo& moveInfo = creationInfo.moves[i]; + MoveSlot moveSlot = moveSlots.val[(types::moveSlotIndex)i]; + types::pp idealMaxPp = moveInfo.maxPp.value_or(pokedex->getMoveData(moveInfo.name).val); + types::pp idealPp = moveInfo.pp.value_or(idealMaxPp); - types::pp idealMaxPp = move.maxPp.value_or(pokedex->getMoveData(move.name).val); - types::pp idealPp = move.pp.value_or(idealMaxPp); - POKESIM_REQUIRE_NM(registry->get(moveEntity).val == idealPp); - POKESIM_REQUIRE_NM(registry->get(moveEntity).val == idealMaxPp); - pokesim::debug::checkMoveSlot(moveEntity, *registry); + POKESIM_REQUIRE_NM(moveSlot.move == moveInfo.name); + POKESIM_REQUIRE_NM(moveSlot.pp == idealPp); + POKESIM_REQUIRE_NM(moveSlot.maxPp == idealMaxPp); + pokesim::debug::check(moveSlot); } if (creationInfo.currentHp.has_value()) { @@ -382,14 +379,7 @@ struct SimulationSetupChecks { const auto& slotDecision = slotDecisions[slot]; const auto& slotDecisionInfo = slotDecisionsInfo[slot]; - POKESIM_REQUIRE_NM(slotDecision.sourceSlot == slotDecisionInfo.sourceSlot); - POKESIM_REQUIRE_NM(slotDecision.targetSlot == slotDecisionInfo.targetSlot); - POKESIM_REQUIRE_NM(slotDecision.megaEvolve == slotDecisionInfo.megaEvolve); - POKESIM_REQUIRE_NM(slotDecision.primalRevert == slotDecisionInfo.primalRevert); - POKESIM_REQUIRE_NM(slotDecision.dynamax == slotDecisionInfo.dynamax); - POKESIM_REQUIRE_NM(slotDecision.terastallize == slotDecisionInfo.terastallize); - POKESIM_REQUIRE_NM(slotDecision.moveChoice == slotDecisionInfo.moveChoice); - POKESIM_REQUIRE_NM(slotDecision.itemChoice == slotDecisionInfo.itemChoice); + POKESIM_REQUIRE_NM(slotDecision == slotDecisionInfo); } } else if (sideDecisionInfo.decisions.holds()) { @@ -616,6 +606,8 @@ struct SimulationSetupChecks { #include "Simulation.hpp" namespace pokesim { +struct BattleStateSetup; + namespace calc_damage { struct InputSetup; } diff --git a/src/Types/Constants.hpp b/src/Types/Constants.hpp index f481a08..40f95fe 100644 --- a/src/Types/Constants.hpp +++ b/src/Types/Constants.hpp @@ -19,6 +19,8 @@ struct Constants { static constexpr std::uint8_t SIDE_COUNT = 2U; + static constexpr std::uint8_t PP_USE_DEDUCTION = 1U; + struct PokemonLevel { static constexpr std::uint8_t MAX = 100U; static constexpr std::uint8_t MIN = 1U; diff --git a/src/Types/Decisions.hpp b/src/Types/Decisions.hpp new file mode 100644 index 0000000..e7fe3c1 --- /dev/null +++ b/src/Types/Decisions.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace pokesim { +struct MoveDecision { + Slot sourceSlot = Slot::NONE; + Slot targetSlot = Slot::NONE; + + dex::Move move = dex::Move::NO_MOVE; + + constexpr bool operator==(const MoveDecision& other) const { + return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot && move == other.move; + } +}; + +struct MegaEvolveAndMoveDecision : MoveDecision {}; +struct ZMoveDecision : MoveDecision {}; +struct DynamaxAndMoveDecision : MoveDecision {}; +struct TerastallizeAndMoveDecision : MoveDecision {}; + +struct SwitchDecision { + Slot sourceSlot = Slot::NONE; + Slot targetSlot = Slot::NONE; + + constexpr bool operator==(const SwitchDecision& other) const { + return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot; + } +}; + +struct ItemDecision { + Slot sourceSlot = Slot::NONE; + Slot targetSlot = Slot::NONE; + + dex::Item item = dex::Item::NO_ITEM; + + constexpr bool operator==(const ItemDecision& other) const { + return sourceSlot == other.sourceSlot && targetSlot == other.targetSlot && item == other.item; + } +}; + +namespace types { +struct slotDecision : pokesim::internal::variant< + MoveDecision, MegaEvolveAndMoveDecision, TerastallizeAndMoveDecision, DynamaxAndMoveDecision, + ZMoveDecision, SwitchDecision, ItemDecision> { + using variant = pokesim::internal::variant< + MoveDecision, MegaEvolveAndMoveDecision, TerastallizeAndMoveDecision, DynamaxAndMoveDecision, ZMoveDecision, + SwitchDecision, ItemDecision>; + using variant::variant; + + constexpr Slot sourceSlot() const { + return std::visit([](auto&& decision) { return decision.sourceSlot; }, (variant::base) * this); + } + + constexpr Slot targetSlot() const { + return std::visit([](auto&& decision) { return decision.targetSlot; }, (variant::base) * this); + } +}; + +using slotDecisions = types::sideSlots; +} // namespace types +} // namespace pokesim diff --git a/src/Types/State.hpp b/src/Types/State.hpp index 81b79d9..840800b 100644 --- a/src/Types/State.hpp +++ b/src/Types/State.hpp @@ -18,6 +18,7 @@ using stateId = std::underlying_type_t; using battleTurn = pokesim::internal::unsignedIntType; using sideIndex = pokesim::internal::unsignedIntType; + template struct sides : public std::array { constexpr T& p1() { return this->at(0); }; @@ -27,6 +28,7 @@ struct sides : public std::array { template decltype(auto) get() const { + static_assert(N < Constants::SIDE_COUNT); return this->at(N); } diff --git a/src/Types/headers.hpp b/src/Types/headers.hpp index 71dcb64..f3a66d3 100644 --- a/src/Types/headers.hpp +++ b/src/Types/headers.hpp @@ -4,6 +4,7 @@ #include "Constants.hpp" #include "Damage.hpp" +#include "Decisions.hpp" #include "Effect.hpp" #include "Entity.hpp" #include "Enums/Ability.hpp" diff --git a/src/Utilities/ArgumentChecks.cpp b/src/Utilities/ArgumentChecks.cpp index 3bdeb30..b9f720f 100644 --- a/src/Utilities/ArgumentChecks.cpp +++ b/src/Utilities/ArgumentChecks.cpp @@ -107,23 +107,18 @@ void checkAction(types::entity actionEntity, const types::registry& registry) { POKESIM_REQUIRE_NM(!has(actionEntity, registry)); POKESIM_REQUIRE_NM(!has(actionEntity, registry)); POKESIM_REQUIRE_NM(!has(actionEntity, registry)); - POKESIM_REQUIRE_NM(!has(actionEntity, registry)); checkTeamOrder(registry.get(actionEntity).val); } - else { - POKESIM_REQUIRE_NM(has(actionEntity, registry)); - } if (registry.any_of(actionEntity)) { POKESIM_REQUIRE_NM(has(actionEntity, registry)); POKESIM_REQUIRE_NM(has(actionEntity, registry)); POKESIM_REQUIRE_NM(!has(actionEntity, registry)); - const auto& [source, target, speedSort] = registry.get(actionEntity); + const auto& [source, target] = registry.get(actionEntity); check(source); check(target); - check(speedSort); if (has(actionEntity, registry)) { check(registry.get(actionEntity)); @@ -247,20 +242,7 @@ void checkPokemon(types::entity pokemonEntity, const types::registry& registry) if (effectiveSpd) check(*effectiveSpd); if (effectiveSpe) check(*effectiveSpe); - checkBounds(moveSlots.val.size()); -} - -void checkMoveSlot(types::entity moveSlotEntity, const types::registry& registry) { - types::registry::checkEntity(moveSlotEntity, registry); - POKESIM_REQUIRE_NM(has(moveSlotEntity, registry)); - POKESIM_REQUIRE_NM(has(moveSlotEntity, registry)); - POKESIM_REQUIRE_NM(has(moveSlotEntity, registry)); - - const auto& [move, pp, maxPp] = registry.get(moveSlotEntity); - - check(move); - check(pp); - check(maxPp); + check(moveSlots); } void checkActionMove(types::entity moveEntity, const types::registry& registry) { @@ -327,6 +309,25 @@ void check(const Accuracy& accuracy) { checkBounds(accuracy.val); } +template <> +void check(const ActionQueueItem& actionQueueItem) { + POKESIM_REQUIRE_NM(listContains(VALID_ACTION_ORDERS, actionQueueItem.order)); + checkBounds(actionQueueItem.priority); + checkStat(actionQueueItem.speed); + + if (actionQueueItem.order == ActionOrder::MOVE || actionQueueItem.order == ActionOrder::SWITCH) { + check(actionQueueItem.decision); + } +} + +template <> +void check(const ActionQueue& actionQueue) { + checkBounds(actionQueue.val.size()); + for (const ActionQueueItem& actionQueueItem : actionQueue.val) { + check(actionQueueItem); + } +} + template <> void check(const AddedTargets& addedTargets) { POKESIM_REQUIRE_NM(listContains(VALID_ADDED_TARGET_OPTIONS, addedTargets.val)); @@ -512,6 +513,16 @@ void check(const calc_damage::Power& power) { checkBounds(power.val); } +template <> +void check(const ChoiceLock& choiceLock) { + POKESIM_REQUIRE_NM(choiceLock.val < Constants::MoveSlots::MAX); +} + +template <> +void check(const CurrentActionMoveSlot& currentActionMoveSlot) { + POKESIM_REQUIRE_NM(currentActionMoveSlot.val < Constants::MoveSlots::MAX); +} + template <> void check(const Damage& damage) { POKESIM_REQUIRE_NM(damage.val <= Constants::Damage::MAX); @@ -532,25 +543,11 @@ void check(const DamageRolls& damageRolls) { } template <> -void check(const SlotDecision& slotDecision) { - checkSlot(slotDecision.sourceSlot); - checkSlot(slotDecision.targetSlot); - POKESIM_REQUIRE_NM(!(slotDecision.moveChoice.has_value() && slotDecision.itemChoice.has_value())); - POKESIM_REQUIRE_NM(!(slotDecision.megaEvolve && slotDecision.primalRevert)); -} - -template <> -void check(const SideDecision& sideDecision) { - checkPlayerSideId(sideDecision.sideId); - if (sideDecision.decisions.holds()) { - const types::slotDecisions& decisions = sideDecision.decisions.get(); - for (const SlotDecision& decision : decisions) { - check(decision); - } - } - else { - checkTeamOrder(sideDecision.decisions.get()); - } +void check(const DisabledMoveSlots& disabledMoveSlots) { + checkBounds(disabledMoveSlots.val.size()); + POKESIM_REQUIRE( + listContains(disabledMoveSlots.val, true), + "The component should be removed if no moves are disabled."); } template <> @@ -573,14 +570,6 @@ void check(const Ivs& ivs) { checkIv(ivs.spe); } -template <> -void check(const ActionQueue& actionQueue, const types::registry& registry) { - checkBounds(actionQueue.val.size()); - for (types::entity entity : actionQueue.val) { - checkAction(entity, registry); - } -} - template <> void check(const Battle& battle, const types::registry& registry) { checkBattle(battle.val, registry); @@ -601,21 +590,11 @@ void check(const ParentEntity& parentEntity, const types::registry& registry) { types::registry::checkEntity(parentEntity.val, registry); } -template <> -void check(const ChoiceLock& choiceLock, const types::registry& registry) { - checkMoveSlot(choiceLock.val, registry); -} - template <> void check(const CurrentAction& currentAction, const types::registry& registry) { checkAction(currentAction.val, registry); } -template <> -void check(const NextAction& nextAction, const types::registry& registry) { - checkAction(nextAction.val, registry); -} - template <> void check(const CurrentActionTargets& targets, const types::registry& registry) { checkBounds(targets.val.size()); @@ -660,11 +639,6 @@ void check(const CurrentActionMovesAsTarget& moves, const types::registry& regis } } -template <> -void check(const CurrentActionMoveSlot& move, const types::registry& registry) { - checkMoveSlot(move.val, registry); -} - template <> void check(const CurrentEffectSource& source, const types::registry& registry) { checkPokemon(source.val, registry); @@ -704,21 +678,27 @@ void check(const FoeSide& foeSide, const types::registry& registry) { } template <> -void check(const LastUsedMove& lastUsedMove, const types::registry& registry) { - checkMoveSlot(lastUsedMove.val, registry); +void check(const RecycledAction& recycledAction, const types::registry& registry) { + types::registry::checkEntity(recycledAction.val, registry); + POKESIM_REQUIRE_NM(has(recycledAction.val, registry)); } template <> -void check(const MoveSlots& moveSlots, const types::registry& registry) { - checkBounds(moveSlots.val.size()); - for (types::entity moveEntity : moveSlots.val) { - checkMoveSlot(moveEntity, registry); - } +void check(const RecycledActionMove& recycledActionMove, const types::registry& registry) { + types::registry::checkEntity(recycledActionMove.val, registry); + POKESIM_REQUIRE_NM(has(recycledActionMove.val, registry)); } template <> -void check(const Pokemon& pokemon, const types::registry& registry) { - checkPokemon(pokemon.val, registry); +void check(const AddedRecycledActionMove1& addedRecycledActionMove1, const types::registry& registry) { + types::registry::checkEntity(addedRecycledActionMove1.val, registry); + POKESIM_REQUIRE_NM(has(addedRecycledActionMove1.val, registry)); +} + +template <> +void check(const AddedRecycledActionMove2& addedRecycledActionMove2, const types::registry& registry) { + types::registry::checkEntity(addedRecycledActionMove2.val, registry); + POKESIM_REQUIRE_NM(has(addedRecycledActionMove2.val, registry)); } template <> @@ -747,11 +727,31 @@ void check(const HitCount& hitCount) { checkBounds(hitCount.val); } +template <> +void check(const LastUsedMove& lastUsedMove) { + POKESIM_REQUIRE_NM(lastUsedMove.val < Constants::MoveSlots::MAX); +} + template <> void check(const Level& level) { checkBounds(level.val); } +template <> +void check(const MoveSlot& moveSlot) { + check(MoveName{moveSlot.move}); + check(Pp{moveSlot.pp}); + checkBounds(moveSlot.maxPp); +} + +template <> +void check(const MoveSlots& moveSlots) { + checkBounds(moveSlots.val.size()); + for (MoveSlot moveSlot : moveSlots.val) { + check(moveSlot); + } +} + template <> void check(const AbilityName& abilityName) { POKESIM_REQUIRE_NM(abilityName.val != dex::Ability::NO_ABILITY); @@ -857,16 +857,6 @@ void check(const WeatherName& weatherName) { POKESIM_REQUIRE_NM((std::underlying_type_t)weatherName.val <= dex::TOTAL_WEATHER_COUNT); } -template <> -void check(const Pp& pp) { - checkBounds(pp.val); -} - -template <> -void check(const MaxPp& maxPp) { - checkBounds(maxPp.val); -} - template <> void check(const PlayerSide& playerSide) { checkPlayerSideId(playerSide.val); @@ -897,6 +887,11 @@ void check(const BaseStats& baseStats) { checkBaseStat(baseStats.spe); } +template <> +void check(const Pp& pp) { + checkBounds(pp.val); +} + template <> void check(const Position& position) { checkBounds(position.val); @@ -1001,6 +996,20 @@ void check(const internal::RandomEqualChanceStack& randomEqualChanceStack, const } } +template <> +void check(const SideDecision& sideDecision) { + checkPlayerSideId(sideDecision.sideId); + if (sideDecision.decisions.holds()) { + const types::slotDecisions& decisions = sideDecision.decisions.get(); + for (const auto& decision : decisions) { + check(decision); + } + } + else { + checkTeamOrder(sideDecision.decisions.get()); + } +} + template <> void check(const SpeedTieIndexes& speedTieIndexes) { checkBounds(speedTieIndexes.val.size()); @@ -1070,13 +1079,6 @@ void check(const SpeciesTypes& speciesTypes) { } } -template <> -void check(const SpeedSort& speedSort) { - POKESIM_REQUIRE_NM(listContains(VALID_ACTION_ORDERS, speedSort.order)); - checkBounds(speedSort.priority); - checkStat(speedSort.speed); -} - template <> void check(const stat::Hp& hp) { checkStat(hp.val, true); @@ -1149,6 +1151,26 @@ void check(const Winner& winner) { winner.val == PlayerSideId::P1 || winner.val == PlayerSideId::P2 || winner.val == PlayerSideId::NONE); } +template <> +void check(const types::slotDecision& slotDecision) { + checkSlot(slotDecision.sourceSlot()); + checkSlot(slotDecision.targetSlot()); + auto [moveDecision, megaDecision, zMoveDecision, dynamaxDecision, teraDecision, itemDecision] = slotDecision.get_if< + MoveDecision, + MegaEvolveAndMoveDecision, + ZMoveDecision, + DynamaxAndMoveDecision, + TerastallizeAndMoveDecision, + ItemDecision>(); + + if (moveDecision) check(MoveName{moveDecision->move}); + if (megaDecision) check(MoveName{megaDecision->move}); + if (zMoveDecision) check(MoveName{zMoveDecision->move}); + if (dynamaxDecision) check(MoveName{dynamaxDecision->move}); + if (teraDecision) check(MoveName{teraDecision->move}); + if (itemDecision) check(ItemName{itemDecision->item}); +} + template <> void check(const DamageRollKind& damageRollKind) { if (listContains(VALID_DAMAGE_ROLL_KINDS, damageRollKind)) { diff --git a/src/Utilities/ArgumentChecks.hpp b/src/Utilities/ArgumentChecks.hpp index 7715f8b..5b24784 100644 --- a/src/Utilities/ArgumentChecks.hpp +++ b/src/Utilities/ArgumentChecks.hpp @@ -5,12 +5,15 @@ #ifdef POKESIM_DEBUG_CHECK_UTILITIES #include +#include #include #include #include namespace pokesim { struct Accuracy; +struct ActionQueueItem; +struct ActionQueue; struct AddedTargets; struct BaseEffectChance; struct BasePower; @@ -19,22 +22,20 @@ struct DefBoost; struct SpaBoost; struct SpdBoost; struct SpeBoost; +struct ChoiceLock; struct CloneTo; +struct CurrentActionMoveSlot; struct Damage; struct DamageRollModifiers; struct DamageRolls; -struct SlotDecision; -struct SideDecision; +struct DisabledMoveSlots; struct Evs; struct Ivs; -struct ActionQueue; struct Battle; struct ParentBattle; struct RootBattle; struct ParentEntity; -struct ChoiceLock; struct CurrentAction; -struct NextAction; struct CurrentActionTargets; struct CurrentActionSource; struct CurrentActionTarget; @@ -42,23 +43,26 @@ struct FailedCurrentActionSource; struct FailedCurrentActionTarget; struct CurrentActionMovesAsSource; struct CurrentActionMovesAsTarget; -struct CurrentActionMoveSlot; struct CurrentEffectSource; struct CurrentEffectTarget; struct CurrentEffectsAsSource; struct CurrentEffectsAsTarget; struct FaintQueue; struct FoeSide; -struct LastUsedMove; -struct MoveSlots; -struct Pokemon; +struct RecycledAction; +struct RecycledActionMove; +struct AddedRecycledActionMove1; +struct AddedRecycledActionMove2; struct Side; struct Sides; struct Team; struct EventModifier; struct HitCount; struct Id; +struct LastUsedMove; struct Level; +struct MoveSlot; +struct MoveSlots; struct AbilityName; struct GenderName; struct ItemName; @@ -75,20 +79,19 @@ struct TerrainName; struct TypeName; struct VolatileName; struct WeatherName; -struct Pp; -struct MaxPp; struct PlayerSide; struct PrimaryAbility; struct SecondaryAbility; struct HiddenAbility; struct BaseStats; +struct Pp; struct Position; struct MovePriority; struct Probability; struct RngSeed; +struct SideDecision; struct SpeedTieIndexes; struct SpeciesTypes; -struct SpeedSort; struct Turn; struct Winner; namespace analyze_effect { @@ -157,13 +160,18 @@ void check(const T&) {} void checkBattle(types::entity, const types::registry&); void checkSide(types::entity, const types::registry&); void checkPokemon(types::entity, const types::registry&); -void checkMoveSlot(types::entity, const types::registry&); void checkActionMove(types::entity, const types::registry&); void checkPercentChance(types::percentChance); template <> void check(const Accuracy&); +template <> +void check(const ActionQueueItem&); + +template <> +void check(const ActionQueue&); + template <> void check(const AddedTargets&); @@ -230,8 +238,14 @@ void check(const calc_damage::RealEffectiveStat&); template <> void check(const calc_damage::Power&); +template <> +void check(const ChoiceLock&); + // template <> void check(const CloneTo&); +template <> +void check(const CurrentActionMoveSlot&); + template <> void check(const Damage&); @@ -242,10 +256,7 @@ template <> void check(const DamageRolls&); template <> -void check(const SlotDecision&); - -template <> -void check(const SideDecision&); +void check(const DisabledMoveSlots&); template <> void check(const Evs&); @@ -253,9 +264,6 @@ void check(const Evs&); template <> void check(const Ivs&); -template <> -void check(const ActionQueue&, const types::registry&); - template <> void check(const Battle&, const types::registry&); @@ -268,15 +276,9 @@ void check(const RootBattle&, const types::registry&); template <> void check(const ParentEntity&, const types::registry&); -template <> -void check(const ChoiceLock&, const types::registry&); - template <> void check(const CurrentAction&, const types::registry&); -template <> -void check(const NextAction&, const types::registry&); - template <> void check(const CurrentActionTargets&, const types::registry&); @@ -298,9 +300,6 @@ void check(const CurrentActionMovesAsSource&, const types::registry&); template <> void check(const CurrentActionMovesAsTarget&, const types::registry&); -template <> -void check(const CurrentActionMoveSlot&, const types::registry&); - template <> void check(const CurrentEffectSource&, const types::registry&); @@ -320,13 +319,16 @@ template <> void check(const FoeSide&, const types::registry&); template <> -void check(const LastUsedMove&, const types::registry&); +void check(const RecycledAction&, const types::registry&); + +template <> +void check(const RecycledActionMove&, const types::registry&); template <> -void check(const MoveSlots&, const types::registry&); +void check(const AddedRecycledActionMove1&, const types::registry&); template <> -void check(const Pokemon&, const types::registry&); +void check(const AddedRecycledActionMove2&, const types::registry&); template <> void check(const Side&, const types::registry&); @@ -344,9 +346,18 @@ void check(const HitCount&); // template <> void check(const Id&); +template <> +void check(const LastUsedMove&); + template <> void check(const Level&); +template <> +void check(const MoveSlot&); + +template <> +void check(const MoveSlots&); + template <> void check(const AbilityName&); @@ -395,12 +406,6 @@ void check(const VolatileName&); template <> void check(const WeatherName&); -template <> -void check(const Pp&); - -template <> -void check(const MaxPp&); - template <> void check(const PlayerSide&); @@ -416,6 +421,9 @@ void check(const HiddenAbility&); template <> void check(const BaseStats&); +template <> +void check(const Pp&); + template <> void check(const Position&); @@ -469,6 +477,9 @@ void check(const internal::RandomEqualChanceStack&, const types::registry&); // template <> void check(const internal::RandomEventIndex&); +template <> +void check(const SideDecision&); + template <> void check(const SpeedTieIndexes&); @@ -493,9 +504,6 @@ void check(const analyze_effect::EffectMultiplier&); template <> void check(const SpeciesTypes&); -template <> -void check(const SpeedSort&); - template <> void check(const stat::Hp&); @@ -538,6 +546,9 @@ void check(const Turn&); template <> void check(const Winner&); +template <> +void check(const types::slotDecision&); + template <> void check(const DamageRollKind&); diff --git a/src/Utilities/DebugChecks.hpp b/src/Utilities/DebugChecks.hpp index 4305190..30e5db5 100644 --- a/src/Utilities/DebugChecks.hpp +++ b/src/Utilities/DebugChecks.hpp @@ -8,18 +8,17 @@ #include #include #include -#include -#include #include #include #include #include #include +#include #include #include #include -#include #include +#include #include #include #include @@ -38,12 +37,21 @@ namespace pokesim::debug { struct Checks { - Checks(const Simulation& _simulation) : simulation(&_simulation), registry(&_simulation.registry) {} + Checks(const Simulation& _simulation) + : simulation(&_simulation), + registry(&_simulation.registry), + simulateTurnOptionsOnInput(simulation->simulateTurnOptions), + calcDamageOptionsOnInput(simulation->calculateDamageOptions), + analyzeEffectOptionsOnInput(simulation->analyzeEffectOptions) {} protected: const Simulation* simulation; const types::registry* registry; types::registry registryOnInput; + simulate_turn::Options simulateTurnOptionsOnInput; + calc_damage::Options calcDamageOptionsOnInput; + analyze_effect::Options analyzeEffectOptionsOnInput; + entt::dense_map currentEntitiesToInitial; entt::dense_set specificallyChecked; types::entityIndex initialEntityCount = 0U; @@ -76,6 +84,12 @@ struct Checks { } } + void checkOptions() const { + POKESIM_REQUIRE_NM(simulateTurnOptionsOnInput == simulation->simulateTurnOptions); + POKESIM_REQUIRE_NM(calcDamageOptionsOnInput == simulation->calculateDamageOptions); + POKESIM_REQUIRE_NM(analyzeEffectOptionsOnInput == simulation->analyzeEffectOptions); + } + void checkRemainingOutputs() const { for (auto [original, copy] : currentEntitiesToInitial) { if (!specificallyChecked.contains(original)) { @@ -94,7 +108,6 @@ struct Checks { return finalEntityCount; } - void checkMoveSlot(types::entity moveEntity) const { pokesim::debug::checkMoveSlot(moveEntity, *registry); } void checkPokemon(types::entity pokemonEntity) const { pokesim::debug::checkPokemon(pokemonEntity, *registry); } void checkSide(types::entity sideEntity) const { pokesim::debug::checkSide(sideEntity, *registry); } void checkBattle(types::entity battleEntity) const { pokesim::debug::checkBattle(battleEntity, *registry); } diff --git a/src/Utilities/FixedMemoryVector.hpp b/src/Utilities/FixedMemoryVector.hpp index 0ca4fc3..1689b34 100644 --- a/src/Utilities/FixedMemoryVector.hpp +++ b/src/Utilities/FixedMemoryVector.hpp @@ -24,6 +24,12 @@ class fixedMemoryVector : private std::array { "A std::vector for this type and size would be smaller."); } + fixedMemoryVector(std::uint8_t size, const T& value) : fixedMemoryVector() { + for (std::uint8_t i = 0; i < size; i++) { + push_back(value); + } + } + fixedMemoryVector(std::initializer_list list) : fixedMemoryVector() { for (const T& item : list) { push_back(item); diff --git a/src/Utilities/Variant.hpp b/src/Utilities/Variant.hpp index 005c440..e2adc93 100644 --- a/src/Utilities/Variant.hpp +++ b/src/Utilities/Variant.hpp @@ -1,12 +1,16 @@ #pragma once +#include #include namespace pokesim::internal { template class variant : public std::variant { + protected: + using base = std::variant; + public: - using std::variant::variant; + using base::variant; template variant& operator=(const T& rhs) { @@ -14,21 +18,31 @@ class variant : public std::variant { return *this; } - bool empty() const { return holds(); } + constexpr bool empty() const { return holds(); } template - bool holds() const { + constexpr bool holds() const { return std::holds_alternative(*this); } template - auto& get() const { + constexpr auto& get() const { return std::get(*this); } template - auto& get() { + constexpr auto& get() { return std::get(*this); } + + template + constexpr auto get_if() const { + return std::make_tuple(std::get_if(this)...); + } + + template + constexpr auto get_if() { + return std::make_tuple(std::get_if(this)...); + } }; } // namespace pokesim::internal diff --git a/tests/BattleStateTest.cpp b/tests/BattleStateTest.cpp index b6def94..ede27a3 100644 --- a/tests/BattleStateTest.cpp +++ b/tests/BattleStateTest.cpp @@ -152,26 +152,14 @@ TEST_CASE("Clone Battles", "[Simulation][Setup]") { REQUIRE_FALSE(existingEntities.contains(clonePokemon)); existingEntities.insert(clonePokemon); - const auto& [baseMoveSlots, basePokemonSide, basePokemonBattle] = - registry.get(basePokemon); - const auto& [cloneMoveSlots, clonePokemonSide, clonePokemonBattle] = - registry.get(clonePokemon); + const auto& [basePokemonSide, basePokemonBattle] = registry.get(basePokemon); + const auto& [clonePokemonSide, clonePokemonBattle] = registry.get(clonePokemon); REQUIRE(basePokemonSide.val != clonePokemonSide.val); REQUIRE(cloneSide == clonePokemonSide.val); REQUIRE(basePokemonBattle.val != clonePokemonBattle.val); REQUIRE(battle == clonePokemonBattle.val); - - REQUIRE(baseMoveSlots.val.size() == cloneMoveSlots.val.size()); - - for (types::moveSlotIndex j = 0U; j < baseMoveSlots.val.size(); j++) { - types::entity baseMoveSlot = baseMoveSlots.val[j]; - types::entity cloneMoveSlot = cloneMoveSlots.val[j]; - existingEntities.insert(baseMoveSlot); - REQUIRE_FALSE(existingEntities.contains(cloneMoveSlot)); - existingEntities.insert(cloneMoveSlot); - } } }; diff --git a/tests/Effects/ChoiceLock.cpp b/tests/Effects/ChoiceLock.cpp index 21eaa0e..e1f3b98 100644 --- a/tests/Effects/ChoiceLock.cpp +++ b/tests/Effects/ChoiceLock.cpp @@ -19,12 +19,10 @@ TEST_CASE("Choice Lock: Choice lock starts", "[Simulation][SimulateTurn][Effect] battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P2A}; - p1SlotDecision.moveChoice = dex::Move::SPLASH; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::SPLASH; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::SPLASH}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P2A, dex::Move::SPLASH}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({battleCreationInfo}); @@ -75,20 +73,26 @@ TEST_CASE("Choice Lock: Choice lock starts", "[Simulation][SimulateTurn][Effect] types::entity p2Side = sides.val.p2(); types::entity p1Pokemon = registry.get(p1Side).val[0]; types::entity p2Pokemon = registry.get(p2Side).val[0]; - types::entity p1Move = registry.get(p1Pokemon).val[0]; - types::entity p2Move = registry.get(p2Pokemon).val[0]; + types::moveSlotIndex p1MoveIndex = 0U; + types::moveSlotIndex p2MoveIndex = 0U; - checks.checkEntityForChanges(p1Pokemon); - checks.checkEntityForChanges(p2Pokemon); + checks.checkEntityForChanges(p1Pokemon); + checks.checkEntityForChanges(p2Pokemon); auto p1PokemonLastUsedMove = registry.get(p1Pokemon); - REQUIRE(p1PokemonLastUsedMove.val == p1Move); + REQUIRE(p1PokemonLastUsedMove.val == p1MoveIndex); auto p2PokemonLastUsedMove = registry.get(p2Pokemon); - REQUIRE(p2PokemonLastUsedMove.val == p2Move); + REQUIRE(p2PokemonLastUsedMove.val == p2MoveIndex); auto choiceLock = registry.get(p2Pokemon); - REQUIRE(choiceLock.val == p2Move); + REQUIRE(choiceLock.val == p2MoveIndex); + + auto disabledMoveSlots = registry.get(p2Pokemon); + REQUIRE(disabledMoveSlots.val[p2MoveIndex] == true); + + checks.checkMovePpUsage(p1Pokemon, p1MoveIndex); + checks.checkMovePpUsage(p2Pokemon, p2MoveIndex); } TEST_CASE( @@ -110,12 +114,10 @@ TEST_CASE( battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P2A}; - p1SlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::SPLASH; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::KNOCK_OFF}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P2A, dex::Move::SPLASH}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({battleCreationInfo}); @@ -169,20 +171,28 @@ TEST_CASE( types::entity p2Side = sides.val.p2(); types::entity p1Pokemon = registry.get(p1Side).val[0]; types::entity p2Pokemon = registry.get(p2Side).val[0]; - types::entity p1Move = registry.get(p1Pokemon).val[1]; - types::entity p2Move = registry.get(p2Pokemon).val[0]; - - checks.checkEntityForChanges(p1Pokemon); - checks.checkEntityForChanges( - p2Pokemon); + types::moveSlotIndex p1MoveIndex = 1U; + types::moveSlotIndex p2MoveIndex = 0U; + + checks.checkEntityForChanges(p1Pokemon); + checks.checkEntityForChanges< + LastUsedMove, + ItemName, + item::tags::ChoiceScarf, + stat::EffectiveSpe, + stat::CurrentHp, + MoveSlots>(p2Pokemon); auto p1PokemonLastUsedMove = registry.get(p1Pokemon); - REQUIRE(p1PokemonLastUsedMove.val == p1Move); + REQUIRE(p1PokemonLastUsedMove.val == p1MoveIndex); auto p2PokemonLastUsedMove = registry.get(p2Pokemon); - REQUIRE(p2PokemonLastUsedMove.val == p2Move); + REQUIRE(p2PokemonLastUsedMove.val == p2MoveIndex); REQUIRE_FALSE(registry.all_of(p2Pokemon)); REQUIRE_FALSE(registry.all_of(p2Pokemon)); + + checks.checkMovePpUsage(p1Pokemon, p1MoveIndex); + checks.checkMovePpUsage(p2Pokemon, p2MoveIndex); } } // namespace pokesim diff --git a/tests/Effects/Paralysis.cpp b/tests/Effects/Paralysis.cpp index e5cf183..30734bc 100644 --- a/tests/Effects/Paralysis.cpp +++ b/tests/Effects/Paralysis.cpp @@ -18,12 +18,10 @@ TEST_CASE("Paralysis: Can cause move failure", "[Simulation][SimulateTurn][Effec battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P2A}; - p1SlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::SPLASH; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::KNOCK_OFF}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P2A, dex::Move::SPLASH}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({battleCreationInfo}); @@ -82,8 +80,8 @@ TEST_CASE("Paralysis: Can cause move failure", "[Simulation][SimulateTurn][Effec types::entity p2Side = sides.val.p2(); types::entity p1Pokemon = registry.get(p1Side).val[0]; types::entity p2Pokemon = registry.get(p2Side).val[0]; - types::entity p1Move = registry.get(p1Pokemon).val[1]; - types::entity p2Move = registry.get(p2Pokemon).val[0]; + types::moveSlotIndex p1MoveIndex = 1U; + types::moveSlotIndex p2MoveIndex = 0U; REQUIRE(turn.val == 2U); auto initialRngSeed = checks.getInitialComponents(battle); @@ -97,19 +95,18 @@ TEST_CASE("Paralysis: Can cause move failure", "[Simulation][SimulateTurn][Effec if (paralysisStoppedP1Move) { checks.checkEntityForChanges<>(p1Pokemon); - checks.checkEntityForChanges<>(p1Move); - checks.checkEntityForChanges(p2Pokemon); + checks.checkEntityForChanges(p2Pokemon); } if (p1Moved) { - checks.checkEntityForChanges(p1Pokemon); - checks.checkMovePpUsage(p1Move); + checks.checkEntityForChanges(p1Pokemon); + checks.checkMovePpUsage(p1Pokemon, p1MoveIndex); - checks.checkEntityForChanges(p2Pokemon); + checks.checkEntityForChanges(p2Pokemon); auto p1PokemonLastUsedMove = registry.get(p1Pokemon); - REQUIRE(p1PokemonLastUsedMove.val == p1Move); + REQUIRE(p1PokemonLastUsedMove.val == p1MoveIndex); auto p2PokemonHp = registry.get(p2Pokemon); auto initialP2PokemonHp = checks.getInitialComponents(p2Pokemon); @@ -117,9 +114,9 @@ TEST_CASE("Paralysis: Can cause move failure", "[Simulation][SimulateTurn][Effec } auto p2PokemonLastUsedMove = registry.get(p2Pokemon); - REQUIRE(p2PokemonLastUsedMove.val == p2Move); + REQUIRE(p2PokemonLastUsedMove.val == p2MoveIndex); - checks.checkMovePpUsage(p2Move); + checks.checkMovePpUsage(p2Pokemon, p2MoveIndex); foundProbabilities.insert(probability.val); } diff --git a/tests/Moves/FuryAttack.cpp b/tests/Moves/FuryAttack.cpp index 5c8186b..d2ee40d 100644 --- a/tests/Moves/FuryAttack.cpp +++ b/tests/Moves/FuryAttack.cpp @@ -18,12 +18,10 @@ TEST_CASE("Fury Attack: Multi-hit Branches", "[Simulation][SimulateTurn][Move][F battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P1A}; - p1SlotDecision.moveChoice = dex::Move::FURY_ATTACK; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::SPLASH; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::FURY_ATTACK}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P1A, dex::Move::SPLASH}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({battleCreationInfo}); diff --git a/tests/Moves/KnockOff.cpp b/tests/Moves/KnockOff.cpp index 21e23a9..53093f6 100644 --- a/tests/Moves/KnockOff.cpp +++ b/tests/Moves/KnockOff.cpp @@ -20,12 +20,10 @@ TEST_CASE("Knock Off: Remove Most Items", "[Simulation][SimulateTurn][Move][Knoc battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P2A}; - p1SlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::SPLASH; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::KNOCK_OFF}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P2A, dex::Move::SPLASH}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({battleCreationInfo}); @@ -81,25 +79,30 @@ TEST_CASE("Knock Off: Remove Most Items", "[Simulation][SimulateTurn][Move][Knoc types::entity p2Side = sides.val.p2(); types::entity p1Pokemon = registry.get(p1Side).val[0]; types::entity p2Pokemon = registry.get(p2Side).val[0]; - types::entity p1Move = registry.get(p1Pokemon).val[1]; - types::entity p2Move = registry.get(p2Pokemon).val[0]; - - checks.checkEntityForChanges(p1Pokemon); - checks.checkEntityForChanges( - p2Pokemon); + types::moveSlotIndex p1MoveIndex = 1U; + types::moveSlotIndex p2MoveIndex = 0U; + + checks.checkEntityForChanges(p1Pokemon); + checks.checkEntityForChanges< + stat::CurrentHp, + LastUsedMove, + ItemName, + item::tags::AssaultVest, + stat::EffectiveSpd, + MoveSlots>(p2Pokemon); auto p1PokemonLastUsedMove = registry.get(p1Pokemon); - REQUIRE(p1PokemonLastUsedMove.val == p1Move); + REQUIRE(p1PokemonLastUsedMove.val == p1MoveIndex); auto p2PokemonLastUsedMove = registry.get(p2Pokemon); - REQUIRE(p2PokemonLastUsedMove.val == p2Move); + REQUIRE(p2PokemonLastUsedMove.val == p2MoveIndex); auto p2PokemonHp = registry.get(p2Pokemon); auto initialP2PokemonHp = checks.getInitialComponents(p2Pokemon); REQUIRE(p2PokemonHp.val < initialP2PokemonHp.val); - checks.checkMovePpUsage(p1Move); - checks.checkMovePpUsage(p2Move); + checks.checkMovePpUsage(p1Pokemon, p1MoveIndex); + checks.checkMovePpUsage(p2Pokemon, p2MoveIndex); REQUIRE_FALSE(registry.all_of(p2Pokemon)); REQUIRE_FALSE(registry.all_of(p2Pokemon)); diff --git a/tests/SimulateTurnsTest.cpp b/tests/SimulateTurnsTest.cpp index 12f2fdd..23730cb 100644 --- a/tests/SimulateTurnsTest.cpp +++ b/tests/SimulateTurnsTest.cpp @@ -2,47 +2,41 @@ namespace pokesim { namespace { -using SpeedSortList = std::vector; +using ActionQueueList = std::vector; -void runSpeedSortTest( - const SpeedSortList& speedSortList, const SpeedSortList& idealSortedList, +void runQueueOrderTest( + const ActionQueueList& actionQueueList, const ActionQueueList& idealActionQueueList, const SpeedTieIndexes& idealSpeedTies = {}) { - types::registry registry; - ActionQueue initialQueue; - - for (const SpeedSort& speedSort : speedSortList) { - types::entity entity = registry.create(); - registry.emplace(entity, speedSort); - initialQueue.val.push_back(entity); - } + ActionQueue initialQueue{actionQueueList}; + types::registry registry; types::handle handle{registry, registry.create()}; ActionQueue sortedQueue = initialQueue; simulate_turn::speedSort(handle, sortedQueue); REQUIRE(initialQueue.val.size() == sortedQueue.val.size()); - for (types::entity entity : initialQueue.val) { - bool entityFound = false; - for (types::entity sortedEntity : sortedQueue.val) { - if (sortedEntity == entity) { - entityFound = true; + for (const ActionQueueItem& initialItem : initialQueue.val) { + bool itemFound = false; + for (const ActionQueueItem& sortedItem : sortedQueue.val) { + if (sortedItem == initialItem) { + itemFound = true; break; } } - REQUIRE(entityFound); + REQUIRE(itemFound); } - for (std::size_t i = 0U; i < idealSortedList.size(); i++) { + for (std::size_t i = 0U; i < idealActionQueueList.size(); i++) { INFO(std::to_string(i)); - const SpeedSort& idealSpeedSort = idealSortedList[i]; - const SpeedSort& trueSpeedSort = registry.get(sortedQueue.val[i]); + const ActionQueueItem& idealQueueItem = idealActionQueueList[i]; + const ActionQueueItem& trueQueueItem = sortedQueue.val[i]; - REQUIRE(trueSpeedSort.order == idealSpeedSort.order); - REQUIRE(trueSpeedSort.priority == idealSpeedSort.priority); - REQUIRE(trueSpeedSort.fractionalPriority == idealSpeedSort.fractionalPriority); - REQUIRE(trueSpeedSort.speed == idealSpeedSort.speed); + REQUIRE(trueQueueItem.order == idealQueueItem.order); + REQUIRE(trueQueueItem.priority == idealQueueItem.priority); + REQUIRE(trueQueueItem.fractionalPriority == idealQueueItem.fractionalPriority); + REQUIRE(trueQueueItem.speed == idealQueueItem.speed); } if (idealSpeedTies.val.empty()) { @@ -65,33 +59,33 @@ void runSpeedSortTest( }; } // namespace -TEST_CASE("Simulate Turn: SpeedSort", "[Simulation][SimulateTurn]") { +TEST_CASE("Simulate Turn: Action Queue Order", "[Simulation][SimulateTurn]") { SECTION("One Queue Item") { - SpeedSort emptySpeedSort{}; - runSpeedSortTest({emptySpeedSort}, {emptySpeedSort}); + ActionQueueItem emptyQueueItem{}; + runQueueOrderTest({emptyQueueItem}, {emptyQueueItem}); } SECTION("Two Identical Items") { - SpeedSort emptySpeedSort{}; - runSpeedSortTest( - {emptySpeedSort, emptySpeedSort}, - {emptySpeedSort, emptySpeedSort}, + ActionQueueItem emptyQueueItem{}; + runQueueOrderTest( + {emptyQueueItem, emptyQueueItem}, + {emptyQueueItem, emptyQueueItem}, SpeedTieIndexes{ {SpeedTieIndexes::Span{0U, 2U}}, }); } SECTION("Sort By Order") { - SpeedSortList idealList = { - SpeedSort{ActionOrder::TEAM}, - SpeedSort{ActionOrder::START}, - SpeedSort{ActionOrder::BEFORE_TURN}, - SpeedSort{ActionOrder::SWITCH}, - SpeedSort{ActionOrder::MOVE}, - SpeedSort{ActionOrder::NONE}, + ActionQueueList idealList = { + ActionQueueItem{ActionOrder::TEAM}, + ActionQueueItem{ActionOrder::START}, + ActionQueueItem{ActionOrder::BEFORE_TURN}, + ActionQueueItem{ActionOrder::SWITCH}, + ActionQueueItem{ActionOrder::MOVE}, + ActionQueueItem{ActionOrder::NONE}, }; - runSpeedSortTest( + runQueueOrderTest( { idealList[2], idealList[0], @@ -104,17 +98,17 @@ TEST_CASE("Simulate Turn: SpeedSort", "[Simulation][SimulateTurn]") { } SECTION("Sort By Priority") { - SpeedSortList idealList = { - SpeedSort{ActionOrder::MOVE, 5}, - SpeedSort{ActionOrder::MOVE, 3}, - SpeedSort{ActionOrder::MOVE, 1}, - SpeedSort{ActionOrder::MOVE, 0}, - SpeedSort{ActionOrder::MOVE, -2}, - SpeedSort{ActionOrder::MOVE, -3}, - SpeedSort{ActionOrder::MOVE, -7}, + ActionQueueList idealList = { + ActionQueueItem{ActionOrder::MOVE, 5}, + ActionQueueItem{ActionOrder::MOVE, 3}, + ActionQueueItem{ActionOrder::MOVE, 1}, + ActionQueueItem{ActionOrder::MOVE, 0}, + ActionQueueItem{ActionOrder::MOVE, -2}, + ActionQueueItem{ActionOrder::MOVE, -3}, + ActionQueueItem{ActionOrder::MOVE, -7}, }; - runSpeedSortTest( + runQueueOrderTest( { idealList[1], idealList[0], @@ -128,16 +122,16 @@ TEST_CASE("Simulate Turn: SpeedSort", "[Simulation][SimulateTurn]") { } SECTION("Sort By Priority and Fractional Priority") { - SpeedSortList idealList = { - SpeedSort{ActionOrder::MOVE, 5, false}, - SpeedSort{ActionOrder::MOVE, 3, true}, - SpeedSort{ActionOrder::MOVE, 0, false}, - SpeedSort{ActionOrder::MOVE, 0, true}, - SpeedSort{ActionOrder::MOVE, -3, false}, - SpeedSort{ActionOrder::MOVE, -7, true}, + ActionQueueList idealList = { + ActionQueueItem{ActionOrder::MOVE, 5, false}, + ActionQueueItem{ActionOrder::MOVE, 3, true}, + ActionQueueItem{ActionOrder::MOVE, 0, false}, + ActionQueueItem{ActionOrder::MOVE, 0, true}, + ActionQueueItem{ActionOrder::MOVE, -3, false}, + ActionQueueItem{ActionOrder::MOVE, -7, true}, }; - runSpeedSortTest( + runQueueOrderTest( { idealList[5], idealList[4], @@ -150,27 +144,27 @@ TEST_CASE("Simulate Turn: SpeedSort", "[Simulation][SimulateTurn]") { } SECTION("Sort By Fractional Priority") { - SpeedSortList idealList = { - SpeedSort{ActionOrder::MOVE, 0, false}, - SpeedSort{ActionOrder::MOVE, 0, true}, + ActionQueueList idealList = { + ActionQueueItem{ActionOrder::MOVE, 0, false}, + ActionQueueItem{ActionOrder::MOVE, 0, true}, }; - runSpeedSortTest({idealList[1], idealList[0]}, idealList); + runQueueOrderTest({idealList[1], idealList[0]}, idealList); } SECTION("Sort By Speed") { - SpeedSortList idealList = { - SpeedSort{ActionOrder::MOVE, 0, false, 772U}, - SpeedSort{ActionOrder::MOVE, 0, false, 621U}, - SpeedSort{ActionOrder::MOVE, 0, false, 584U}, - SpeedSort{ActionOrder::MOVE, 0, false, 444U}, - SpeedSort{ActionOrder::MOVE, 0, false, 305U}, - SpeedSort{ActionOrder::MOVE, 0, false, 152U}, - SpeedSort{ActionOrder::MOVE, 0, false, 90U}, - SpeedSort{ActionOrder::MOVE, 0, false, 11U}, + ActionQueueList idealList = { + ActionQueueItem{ActionOrder::MOVE, 0, false, 772U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 621U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 584U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 444U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 305U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 152U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 90U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 11U}, }; - runSpeedSortTest( + runQueueOrderTest( { idealList[1], idealList[0], @@ -185,19 +179,19 @@ TEST_CASE("Simulate Turn: SpeedSort", "[Simulation][SimulateTurn]") { } SECTION("Sort Combination") { - SpeedSortList idealList = { - SpeedSort{ActionOrder::TEAM}, - SpeedSort{ActionOrder::START}, - SpeedSort{ActionOrder::BEFORE_TURN, 0, false, 584U}, - SpeedSort{ActionOrder::BEFORE_TURN, 0, false, 444U}, - SpeedSort{ActionOrder::SWITCH, 0, false, 52U}, - SpeedSort{ActionOrder::SWITCH, 0, false, 40U}, - SpeedSort{ActionOrder::MOVE, 1, false, 152U}, - SpeedSort{ActionOrder::MOVE, 0, false, 315U}, - SpeedSort{ActionOrder::MOVE, -3, true, 700U}, + ActionQueueList idealList = { + ActionQueueItem{ActionOrder::TEAM}, + ActionQueueItem{ActionOrder::START}, + ActionQueueItem{ActionOrder::BEFORE_TURN, 0, false, 584U}, + ActionQueueItem{ActionOrder::BEFORE_TURN, 0, false, 444U}, + ActionQueueItem{ActionOrder::SWITCH, 0, false, 52U}, + ActionQueueItem{ActionOrder::SWITCH, 0, false, 40U}, + ActionQueueItem{ActionOrder::MOVE, 1, false, 152U}, + ActionQueueItem{ActionOrder::MOVE, 0, false, 315U}, + ActionQueueItem{ActionOrder::MOVE, -3, true, 700U}, }; - runSpeedSortTest( + runQueueOrderTest( { idealList[8], idealList[7], @@ -230,12 +224,10 @@ TEST_CASE("Simulate Turn: Battle ends on faint", "[Simulation][SimulateTurn]") { battleCreationInfo.runWithSimulateTurn = true; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P1A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P1A}; - p1SlotDecision.moveChoice = dex::Move::SPLASH; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::THUNDERBOLT; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P1A, dex::Move::SPLASH}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P1A, dex::Move::THUNDERBOLT}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; simulation.createInitialStates({battleCreationInfo}); @@ -289,22 +281,22 @@ TEST_CASE("Simulate Turn: Battle ends on faint", "[Simulation][SimulateTurn]") { types::entity p2Side = sides.val.p2(); types::entity p1Pokemon = registry.get(p1Side).val[0]; types::entity p2Pokemon = registry.get(p2Side).val[0]; - types::entity p1Move = registry.get(p1Pokemon).val[0]; - types::entity p2Move = registry.get(p2Pokemon).val[0]; + types::moveSlotIndex p1MoveIndex = 0U; + types::moveSlotIndex p2MoveIndex = 0U; - checks.checkEntityForChanges(p1Pokemon); - checks.checkEntityForChanges(p2Pokemon); + checks.checkEntityForChanges(p1Pokemon); + checks.checkEntityForChanges(p2Pokemon); auto p2PokemonLastUsedMove = registry.get(p2Pokemon); - REQUIRE(p2PokemonLastUsedMove.val == p2Move); + REQUIRE(p2PokemonLastUsedMove.val == p2MoveIndex); auto p1PokemonHp = registry.get(p1Pokemon); REQUIRE(p1PokemonHp.val == Constants::PokemonCurrentHpStat::MIN); REQUIRE(registry.all_of(p1Pokemon)); REQUIRE_FALSE(registry.all_of(p1Pokemon)); - checks.checkMovePpUsage(p1Move); - checks.checkMovePpUsage(p2Move); + checks.checkMovePpUsage(p1Pokemon, p1MoveIndex); + checks.checkMovePpUsage(p1Pokemon, p2MoveIndex); REQUIRE(winner.val == PlayerSideId::P2); } diff --git a/tests/SimulationSetupTest.cpp b/tests/SimulationSetupTest.cpp index 7c20d58..e15f513 100644 --- a/tests/SimulationSetupTest.cpp +++ b/tests/SimulationSetupTest.cpp @@ -79,17 +79,16 @@ TEST_CASE("Simulation Setup: Simulate Turn", "[Simulation][SimulateTurn][Setup]" SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P1A}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::FURY_ATTACK}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P1A, dex::Move::THUNDERBOLT}; - p1SlotDecision.moveChoice = dex::Move::FURY_ATTACK; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::THUNDERBOLT; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; auto check = [&]() { Simulation simulation(pokedex, BattleFormat::SINGLES); simulation.createInitialStates({battleInfo}); + updateAllStats(simulation); types::registry& registry = simulation.registry; auto group = registry.group(); @@ -107,48 +106,49 @@ TEST_CASE("Simulation Setup: Simulate Turn", "[Simulation][SimulateTurn][Setup]" const auto id = registry.get(group[i]).val; REQUIRE(queue.size() == 2U); - auto p1DecisionEntity = *std::find_if(queue.begin(), queue.end(), [&](types::entity entity) { - return registry.get(entity).val == Slot::P1A; + auto p1DecisionItem = *std::find_if(queue.begin(), queue.end(), [&](const ActionQueueItem& item) { + return item.decision.sourceSlot() == Slot::P1A; }); - auto p2DecisionEntity = *std::find_if(queue.begin(), queue.end(), [&](types::entity entity) { - return registry.get(entity).val == Slot::P2A; + auto p2DecisionItem = *std::find_if(queue.begin(), queue.end(), [&](const ActionQueueItem& item) { + return item.decision.sourceSlot() == Slot::P2A; }); auto checkDecision = - [&](types::entity decisionEntity, const pokesim::SlotDecision& decision, const PokemonCreationInfo& pokemon) { - if (decision.moveChoice) { - const auto [target, move, speedSort] = registry.get(decisionEntity); - - REQUIRE(target.val == decision.targetSlot); - REQUIRE(move.val == decision.moveChoice); - REQUIRE(speedSort.speed == pokemon.stats.spe); - REQUIRE(speedSort.order == ActionOrder::MOVE); - REQUIRE(speedSort.priority == 0); - REQUIRE(speedSort.fractionalPriority == false); + [&](ActionQueueItem actionQueueItem, const types::slotDecision& decision, const PokemonCreationInfo& pokemon) { + if (decision.holds()) { + const auto& trueMoveDecision = actionQueueItem.decision.get(); + const auto& idealMoveDecision = decision.get(); + + REQUIRE(trueMoveDecision == idealMoveDecision); + + REQUIRE(actionQueueItem.speed == pokemon.stats.spe); + REQUIRE(actionQueueItem.order == ActionOrder::MOVE); + REQUIRE(actionQueueItem.priority == 0); + REQUIRE(actionQueueItem.fractionalPriority == false); } - else { - REQUIRE(registry.all_of(decisionEntity)); - const auto [target, speedSort] = registry.get(decisionEntity); - - REQUIRE(target.val == decision.targetSlot); - REQUIRE(speedSort.speed == pokemon.stats.spe); - REQUIRE(speedSort.order == ActionOrder::SWITCH); - REQUIRE(speedSort.priority == 0); - REQUIRE(speedSort.fractionalPriority == false); + else if (decision.holds()) { + const auto& trueSwitchDecision = actionQueueItem.decision.get(); + const auto& idealSwitchDecision = decision.get(); + + REQUIRE(trueSwitchDecision == idealSwitchDecision); + REQUIRE(actionQueueItem.speed == pokemon.stats.spe); + REQUIRE(actionQueueItem.order == ActionOrder::SWITCH); + REQUIRE(actionQueueItem.priority == 0); + REQUIRE(actionQueueItem.fractionalPriority == false); } }; { INFO("P1 Action"); const auto& decision = battleInfo.decisionsToSimulate[id].p1().decisions.get()[0]; - checkDecision(p1DecisionEntity, decision, battleInfo.sides.p1().team[0]); + checkDecision(p1DecisionItem, decision, battleInfo.sides.p1().team[0]); } { INFO("P2 Action"); const auto& decision = battleInfo.decisionsToSimulate[id].p2().decisions.get()[0]; - checkDecision(p2DecisionEntity, decision, battleInfo.sides.p2().team[0]); + checkDecision(p2DecisionItem, decision, battleInfo.sides.p2().team[0]); } } }; @@ -175,18 +175,15 @@ TEST_CASE("Simulation Setup: Simulate Turn", "[Simulation][SimulateTurn][Setup]" switch (i) { case 0U: { - newP1SlotDecision.moveChoice = p1SlotDecision.moveChoice; - newP1SlotDecision.targetSlot = Slot::P2A; + newP1SlotDecision = MoveDecision{p1MoveDecision.sourceSlot, Slot::P2A, p1MoveDecision.move}; break; } case 1U: { - newP1SlotDecision.moveChoice = std::nullopt; - newP1SlotDecision.targetSlot = Slot::P1B; + newP1SlotDecision = SwitchDecision{p1MoveDecision.sourceSlot, Slot::P1B}; break; } case 2U: { - newP1SlotDecision.moveChoice = std::nullopt; - newP1SlotDecision.targetSlot = Slot::P1C; + newP1SlotDecision = SwitchDecision{p1MoveDecision.sourceSlot, Slot::P1C}; break; } default: break; @@ -194,18 +191,15 @@ TEST_CASE("Simulation Setup: Simulate Turn", "[Simulation][SimulateTurn][Setup]" switch (j) { case 0U: { - newP2SlotDecision.moveChoice = p2SlotDecision.moveChoice; - newP2SlotDecision.targetSlot = Slot::P1A; + newP2SlotDecision = MoveDecision{p2MoveDecision.sourceSlot, Slot::P2A, p2MoveDecision.move}; break; } case 1U: { - newP2SlotDecision.moveChoice = std::nullopt; - newP2SlotDecision.targetSlot = Slot::P2B; + newP2SlotDecision = SwitchDecision{p2MoveDecision.sourceSlot, Slot::P2B}; break; } case 2U: { - newP2SlotDecision.moveChoice = std::nullopt; - newP2SlotDecision.targetSlot = Slot::P2C; + newP2SlotDecision = SwitchDecision{p2MoveDecision.sourceSlot, Slot::P2C}; break; } default: break; @@ -263,7 +257,6 @@ TEST_CASE("Simulation Setup: Calc Damage", "[Simulation][CalculateDamage][Setup] std::size_t idealCalcDamageTagCount = 1U + // Battle 2U + // Sides 6U + // Pokemon - 6U + // Moves 6U; // Damage calculation move inputs REQUIRE(calcDamageTagCount == idealCalcDamageTagCount); @@ -302,8 +295,7 @@ TEST_CASE("Simulation Setup: Analyze Effect", "[Simulation][AnalyzeEffect][Setup std::size_t analyzeEffectTagCount = registry.view().size(); std::size_t idealAnalyzeEffectTagCount = 1U + // Battle 2U + // Sides - 6U + // Pokemon - 6U; // Moves + 6U; // Pokemon REQUIRE(analyzeEffectTagCount == idealAnalyzeEffectTagCount); } diff --git a/tests/Tests.hpp b/tests/Tests.hpp index 96c9e60..8cd55fb 100644 --- a/tests/Tests.hpp +++ b/tests/Tests.hpp @@ -207,11 +207,10 @@ struct TestChecks : debug::Checks { return registryOnInput.get(getInitialEntity(entity)); } - void checkMovePpUsage(types::entity moveSlot) { - checkEntityForChanges(moveSlot); - auto movePp = registry->get(moveSlot); - auto initialMovePp = getInitialComponents(moveSlot); - REQUIRE(movePp.val == initialMovePp.val - 1U); + void checkMovePpUsage(types::entity entity, types::moveSlotIndex moveSlotIndex) { + auto movePp = registry->get(entity).val[moveSlotIndex].pp; + auto initialMovePp = getInitialComponents(entity).val[moveSlotIndex].pp; + REQUIRE(movePp == initialMovePp - Constants::PP_USE_DEDUCTION); } TestChecks(const Simulation& _simulation, const types::entityVector& specificallyCheckedEntities = {}) diff --git a/tests/VerticalSlices.cpp b/tests/VerticalSlices.cpp index 3829f3f..eb94978 100644 --- a/tests/VerticalSlices.cpp +++ b/tests/VerticalSlices.cpp @@ -314,6 +314,7 @@ TEST_CASE( tags::Pokemon, stat::CurrentHp, LastUsedMove, + MoveSlots, StatusName, status::tags::Paralysis, stat::EffectiveSpe>(); @@ -369,13 +370,11 @@ TEST_CASE( BattleCreationInfo battleCreationInfo; SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1SlotDecision{Slot::P1A, Slot::P2A}; - SlotDecision p2SlotDecision{Slot::P2A, Slot::P1A}; + MoveDecision p1MoveDecision{Slot::P1A, Slot::P2A, dex::Move::KNOCK_OFF}; + MoveDecision p2MoveDecision{Slot::P2A, Slot::P1A, dex::Move::THUNDERBOLT}; - p1SlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p1Decision.decisions = types::sideSlots{p1SlotDecision}; - p2SlotDecision.moveChoice = dex::Move::THUNDERBOLT; - p2Decision.decisions = types::sideSlots{p2SlotDecision}; + p1Decision.decisions = types::slotDecisions{p1MoveDecision}; + p2Decision.decisions = types::slotDecisions{p2MoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; Simulation simulation = createSingleBattleSimulation(pokedex, battleCreationInfo); @@ -515,8 +514,8 @@ TEST_CASE( types::entity p2Side = sides.val.p2(); types::entity p1Pokemon = registry.get(p1Side).val[0]; types::entity p2Pokemon = registry.get(p2Side).val[0]; - types::entity p1Move = registry.get(p1Pokemon).val[1]; - types::entity p2Move = registry.get(p2Pokemon).val[0]; + types::moveSlotIndex p1MoveIndex = 1U; + types::moveSlotIndex p2MoveIndex = 0U; bool p1Paralyzed = registry.all_of(p1Pokemon); const auto& [p1Hp, p1LastUsedMove, p1Speed] = @@ -546,10 +545,10 @@ TEST_CASE( REQUIRE_FALSE(registry.all_of(p1Side)); REQUIRE_FALSE(registry.all_of(p2Side)); - REQUIRE(p1LastUsedMove.val == p1Move); - REQUIRE(p2LastUsedMove.val == p2Move); - checks.checkMovePpUsage(p1Move); - checks.checkMovePpUsage(p2Move); + REQUIRE(p1LastUsedMove.val == p1MoveIndex); + REQUIRE(p2LastUsedMove.val == p2MoveIndex); + checks.checkMovePpUsage(p1Pokemon, p1MoveIndex); + checks.checkMovePpUsage(p2Pokemon, p2MoveIndex); if (!p2DamageInfo.mightCauseParalysis() || !p1Paralyzed) { REQUIRE_FALSE(registry.all_of(p1Pokemon)); @@ -682,17 +681,13 @@ TEST_CASE( SideDecision p1Decision{PlayerSideId::P1}; SideDecision p2Decision{PlayerSideId::P2}; - SlotDecision p1ASlotDecision{Slot::P1A, Slot::P2B}; - SlotDecision p1BSlotDecision{Slot::P1B, Slot::P2A}; - SlotDecision p2ASlotDecision{Slot::P2A, Slot::P1B}; - SlotDecision p2BSlotDecision{Slot::P2B, Slot::P2B}; - - p1ASlotDecision.moveChoice = dex::Move::MOONBLAST; - p1BSlotDecision.moveChoice = dex::Move::WILL_O_WISP; - p1Decision.decisions = types::sideSlots{p1ASlotDecision, p1BSlotDecision}; - p2ASlotDecision.moveChoice = dex::Move::KNOCK_OFF; - p2BSlotDecision.moveChoice = dex::Move::QUIVER_DANCE; - p2Decision.decisions = types::sideSlots{p2ASlotDecision, p2BSlotDecision}; + MoveDecision p1AMoveDecision{Slot::P1A, Slot::P2B, dex::Move::MOONBLAST}; + MoveDecision p1BMoveDecision{Slot::P1B, Slot::P2A, dex::Move::WILL_O_WISP}; + MoveDecision p2AMoveDecision{Slot::P2A, Slot::P1B, dex::Move::KNOCK_OFF}; + MoveDecision p2BMoveDecision{Slot::P2B, Slot::P2B, dex::Move::QUIVER_DANCE}; + + p1Decision.decisions = types::slotDecisions{p1AMoveDecision, p1BMoveDecision}; + p2Decision.decisions = types::slotDecisions{p2AMoveDecision, p2BMoveDecision}; battleCreationInfo.decisionsToSimulate = {{p1Decision, p2Decision}}; auto simulation = createDoubleBattleSimulation(pokedex, battleCreationInfo); @@ -891,17 +886,17 @@ TEST_CASE( types::entity p1BPokemon = registry.get(p1Side).val[1]; types::entity p2APokemon = registry.get(p2Side).val[0]; types::entity p2BPokemon = registry.get(p2Side).val[1]; - types::entity p1AMove = registry.get(p1APokemon).val[0]; - types::entity p1BMove = registry.get(p1BPokemon).val[0]; - types::entity p2AMove = registry.get(p2APokemon).val[0]; - types::entity p2BMove = registry.get(p2BPokemon).val[0]; + types::moveSlotIndex p1AMoveIndex = 0U; + types::moveSlotIndex p1BMoveIndex = 0U; + types::moveSlotIndex p2AMoveIndex = 0U; + types::moveSlotIndex p2BMoveIndex = 0U; bool p2ABurned = registry.all_of(p2APokemon); bool p2BFainted = registry.all_of(p2BPokemon); bool p2BSpaBoosted = registry.all_of(p2BPokemon); - const auto& [p1AHp, p1AChoiceLock, p1ALastUsedMove] = - registry.get(p1APokemon); + const auto& [p1AHp, p1AChoiceLock, p1ALastUsedMove, p1ADisabledMoveSlots] = + registry.get(p1APokemon); const auto& [p1BHp, p1BLastUsedMove] = registry.get(p1BPokemon); const auto& [p2AHp, p2ALastUsedMove, effectiveAtk] = registry.get(p2APokemon); @@ -910,22 +905,23 @@ TEST_CASE( CAPTURE(p1AHp.val, p1BHp.val, p2AHp.val, p2BHp.val, p2ABurned, p2BFainted, p2BSpaBoosted, probability.val); - checks.checkEntityForChanges(p1APokemon); - checks.checkEntityForChanges(p1BPokemon); - checks.checkEntityForChanges(p2APokemon); + checks.checkEntityForChanges(p1APokemon); + checks.checkEntityForChanges(p1BPokemon); + checks.checkEntityForChanges(p2APokemon); - checks.checkMovePpUsage(p1AMove); - checks.checkMovePpUsage(p1BMove); - checks.checkMovePpUsage(p2AMove); + checks.checkMovePpUsage(p1APokemon, p1AMoveIndex); + checks.checkMovePpUsage(p1BPokemon, p1BMoveIndex); + checks.checkMovePpUsage(p2APokemon, p2AMoveIndex); - REQUIRE(p1ALastUsedMove.val == p1AMove); - REQUIRE(p1BLastUsedMove.val == p1BMove); - REQUIRE(p2ALastUsedMove.val == p2AMove); + REQUIRE(p1ALastUsedMove.val == p1AMoveIndex); + REQUIRE(p1BLastUsedMove.val == p1BMoveIndex); + REQUIRE(p2ALastUsedMove.val == p2AMoveIndex); // P1A (Gardevoir) Specific Checks types::stat p1ABurnHpDecrease = p1AInfo.stats.hp.value() / dex::Burn::onResidualHpDecreaseDivisor(TestMechanic); REQUIRE(p1AHp.val == p1AInfo.stats.hp.value() - p1ABurnHpDecrease); - REQUIRE(p1AChoiceLock.val == p1AMove); + REQUIRE(p1AChoiceLock.val == p1AMoveIndex); + REQUIRE(p1ADisabledMoveSlots.val[p1AMoveIndex] == true); // P1B (Dragapult) Specific Checks REQUIRE_FALSE(registry.all_of(p1BPokemon)); @@ -964,7 +960,6 @@ TEST_CASE( REQUIRE(expectedP2BHp.contains(p2BHp.val)); if (p2BFainted) { checks.checkEntityForChanges(p2BPokemon); - checks.checkEntityForChanges<>(p2BMove); REQUIRE_FALSE(registry.all_of(p2BPokemon)); REQUIRE_FALSE(p2BSpaBoosted); REQUIRE(p2BHp.val == MIN_HP); @@ -979,7 +974,8 @@ TEST_CASE( stat::EffectiveSpa, stat::EffectiveSpd, stat::EffectiveSpe, - LastUsedMove>(p2BPokemon); + LastUsedMove, + MoveSlots>(p2BPokemon); const auto& [p2BLastUsedMove, p2BSpdBoost, p2BSpeBoost] = registry.get(p2BPokemon); const auto& [p2BInitialSpa, p2BInitialSpd, p2BInitialSpe] = @@ -987,8 +983,8 @@ TEST_CASE( REQUIRE_FALSE(p2BHp.val == MIN_HP); - checks.checkMovePpUsage(p2BMove); - REQUIRE(p2BLastUsedMove.val == p2BMove); + checks.checkMovePpUsage(p2BPokemon, p2BMoveIndex); + REQUIRE(p2BLastUsedMove.val == p2BMoveIndex); using Effects = dex::QuiverDance::targetPrimaryEffect; REQUIRE(p2BSpdBoost.val == Effects::spdBoost(TestMechanic)); diff --git a/tools/updateBenchmarkResultsFile.js b/tools/updateBenchmarkResultsFile.js index 56d4413..8440ceb 100644 --- a/tools/updateBenchmarkResultsFile.js +++ b/tools/updateBenchmarkResultsFile.js @@ -151,17 +151,14 @@ benchmarkProcess.stdout.on('data', (chunk) => { } valueDiff = valueDiff.toFixed(3); - let timesDiff = ''; let color = ''; let suffix = ''; if (valueDiff > 0) { valueDiff = '+' + valueDiff; - timesDiff = (1 / percentDiff).toFixed(3); suffix = '% slower'; color = 'red'; } else { - timesDiff = (1 / (1 - percentDiff)).toFixed(3); suffix = '% faster'; color = 'green'; }