From c25d7b865e71cdc4222ac3f37c110dd278181437 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 14 Dec 2019 12:15:30 +0100 Subject: [PATCH] Implements creature switching as turn choice. --- src/Battling/Flow/TurnHandler.cpp | 18 +++++++- src/Battling/Flow/TurnHandler.hpp | 3 ++ src/Battling/Models/Creature.cpp | 1 + src/Battling/Models/Creature.hpp | 1 + src/Battling/ScriptHandling/Script.hpp | 3 ++ src/Battling/ScriptHandling/ScriptSet.hpp | 5 ++ src/Battling/TurnChoices/SwitchTurnChoice.hpp | 21 +++++++++ tests/Integration/BattleIntegrations.cpp | 46 +++++++++++++++++++ 8 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/Battling/TurnChoices/SwitchTurnChoice.hpp diff --git a/src/Battling/Flow/TurnHandler.cpp b/src/Battling/Flow/TurnHandler.cpp index dbf3f57..5fd9918 100644 --- a/src/Battling/Flow/TurnHandler.cpp +++ b/src/Battling/Flow/TurnHandler.cpp @@ -43,8 +43,9 @@ void TurnHandler::ExecuteChoice(BaseTurnChoice* choice) { switch (choiceKind) { case TurnChoiceKind::Pass: throw NotReachableException(); case TurnChoiceKind::Attack: return ExecuteAttackChoice(dynamic_cast(choice)); + case TurnChoiceKind::Switch: return ExecuteSwitchChoice(dynamic_cast(choice)); + case TurnChoiceKind::Item: - case TurnChoiceKind::Switch: case TurnChoiceKind::RunAway: throw NotImplementedException(); } } @@ -165,3 +166,18 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, Creature* targe HOOK(OnAfterHits, userSource, attack, target); } } + +void TurnHandler::ExecuteSwitchChoice(CreatureLib::Battling::SwitchTurnChoice* choice) { + bool preventSwitch = false; + HOOK(PreventSelfSwitch, choice, choice, preventSwitch); + if (preventSwitch) { + return; + } + // HOOK: PreventOpponentSwitch for each opponent. + + auto user = choice->GetUser(); + user->ClearVolatileScripts(); + auto userSide = user->GetBattleSide(); + auto userIndex = userSide->GetCreatureIndex(user); + userSide->SetCreature(choice->GetNewCreature(), userIndex); +} \ No newline at end of file diff --git a/src/Battling/Flow/TurnHandler.hpp b/src/Battling/Flow/TurnHandler.hpp index 0115930..4abe1f9 100644 --- a/src/Battling/Flow/TurnHandler.hpp +++ b/src/Battling/Flow/TurnHandler.hpp @@ -3,6 +3,7 @@ #include "../Models/ExecutingAttack.hpp" #include "../TurnChoices/AttackTurnChoice.hpp" +#include "../TurnChoices/SwitchTurnChoice.hpp" #include "ChoiceQueue.hpp" namespace CreatureLib::Battling { @@ -15,6 +16,8 @@ namespace CreatureLib::Battling { static void HandleAttackForTarget(ExecutingAttack* attack, Creature* target, ExecutingAttack::TargetData* targetData); + static void ExecuteSwitchChoice(SwitchTurnChoice* choice); + public: static void RunTurn(Battle* battle, ChoiceQueue* queue); }; diff --git a/src/Battling/Models/Creature.cpp b/src/Battling/Models/Creature.cpp index 95e5289..7c6c67e 100644 --- a/src/Battling/Models/Creature.cpp +++ b/src/Battling/Models/Creature.cpp @@ -129,3 +129,4 @@ void Battling::Creature::GetActiveScripts(std::vector& scripts) { scripts.emplace_back(&_volatile); _side->GetActiveScripts(scripts); } +void Battling::Creature::ClearVolatileScripts() { _volatile.Clear(); } diff --git a/src/Battling/Models/Creature.hpp b/src/Battling/Models/Creature.hpp index 388b9c0..7f0b751 100644 --- a/src/Battling/Models/Creature.hpp +++ b/src/Battling/Models/Creature.hpp @@ -84,6 +84,7 @@ namespace CreatureLib::Battling { void OverrideActiveTalent(const std::string& talent); void GetActiveScripts(std::vector& scripts) override; + void ClearVolatileScripts(); std::vector& GetAttacks() { return _attacks; } diff --git a/src/Battling/ScriptHandling/Script.hpp b/src/Battling/ScriptHandling/Script.hpp index c10384b..ad45b94 100644 --- a/src/Battling/ScriptHandling/Script.hpp +++ b/src/Battling/ScriptHandling/Script.hpp @@ -9,6 +9,7 @@ namespace CreatureLib::Battling { class BaseTurnChoice; class AttackTurnChoice; + class SwitchTurnChoice; class ExecutingAttack; class Creature; @@ -41,6 +42,8 @@ namespace CreatureLib::Battling { virtual void OnSecondaryEffect(const ExecutingAttack* attack, Creature* target, uint8_t hitNumber){}; virtual void OnAfterHits(const ExecutingAttack* attack, Creature* target){}; + + virtual void PreventSelfSwitch(const SwitchTurnChoice* choice, bool& result){}; }; } diff --git a/src/Battling/ScriptHandling/ScriptSet.hpp b/src/Battling/ScriptHandling/ScriptSet.hpp index 959b3df..3a1cb1b 100644 --- a/src/Battling/ScriptHandling/ScriptSet.hpp +++ b/src/Battling/ScriptHandling/ScriptSet.hpp @@ -29,6 +29,11 @@ namespace CreatureLib::Battling { } } + void Clear() { + _scripts.clear(); + _lookup.clear(); + } + size_t Count() const { return _scripts.size(); } const std::vector* GetIterator() const { return &_scripts; } diff --git a/src/Battling/TurnChoices/SwitchTurnChoice.hpp b/src/Battling/TurnChoices/SwitchTurnChoice.hpp new file mode 100644 index 0000000..c154558 --- /dev/null +++ b/src/Battling/TurnChoices/SwitchTurnChoice.hpp @@ -0,0 +1,21 @@ +#ifndef CREATURELIB_SWITCHTURNCHOICE_HPP +#define CREATURELIB_SWITCHTURNCHOICE_HPP + +#include "BaseTurnChoice.hpp" +namespace CreatureLib::Battling { + class SwitchTurnChoice : public BaseTurnChoice { + Creature* _newCreature; + + public: + SwitchTurnChoice(Creature* user, Creature* newCreature) : BaseTurnChoice(user), _newCreature(newCreature) {} + + TurnChoiceKind GetKind() const final { return TurnChoiceKind::Switch; } + + inline Creature* GetNewCreature() const { return _newCreature; } + + protected: + void GetActiveScripts(std::vector& scripts) override { GetUser()->GetActiveScripts(scripts); } + }; +} + +#endif // CREATURELIB_SWITCHTURNCHOICE_HPP diff --git a/tests/Integration/BattleIntegrations.cpp b/tests/Integration/BattleIntegrations.cpp index b1b8743..dc4a508 100644 --- a/tests/Integration/BattleIntegrations.cpp +++ b/tests/Integration/BattleIntegrations.cpp @@ -5,6 +5,7 @@ #include "../../src/Battling/Models/CreateCreature.hpp" #include "../../src/Battling/TurnChoices/AttackTurnChoice.hpp" #include "../../src/Battling/TurnChoices/PassTurnChoice.hpp" +#include "../../src/Battling/TurnChoices/SwitchTurnChoice.hpp" #include "../TestLibrary/TestLibrary.hpp" using namespace CreatureLib; @@ -139,4 +140,49 @@ TEST_CASE("When another creature is available on faint, make sure the battle has REQUIRE(battle.GetResult() == 0); } +TEST_CASE("Switch Creature in", "[Integrations]") { + auto library = TestLibrary::Get(); + auto c1 = CreateCreature(library, "testSpecies1", 100).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + auto c2 = CreateCreature(library, "testSpecies1", 1).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + CreatureParty party1{c1, c2}; + auto battleParty1 = BattleParty(&party1, {CreatureIndex(0, 0)}); + auto c3 = CreateCreature(library, "testSpecies1", 1).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + CreatureParty party2{c3}; + auto battleParty2 = BattleParty(&party2, {CreatureIndex(1, 0)}); + + auto battle = Battle(library, {battleParty1, battleParty2}); + + battle.SwitchCreature(0, 0, c1); + battle.SwitchCreature(1, 0, c3); + + REQUIRE(battle.GetTarget(CreatureIndex(0, 0)) == c1); + + battle.TrySetChoice(new SwitchTurnChoice(c1, c2)); + battle.TrySetChoice(new PassTurnChoice(c3)); + + REQUIRE(battle.GetTarget(CreatureIndex(0, 0)) == c2); +} + +TEST_CASE("Switch Creature in, but have attack aimed at it. Attack should hit new creature", "[Integrations]") { + auto library = TestLibrary::Get(); + auto c1 = CreateCreature(library, "testSpecies1", 50).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + auto c2 = CreateCreature(library, "testSpecies1", 50).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + CreatureParty party1{c1, c2}; + auto battleParty1 = BattleParty(&party1, {CreatureIndex(0, 0)}); + auto c3 = CreateCreature(library, "testSpecies1", 50).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + CreatureParty party2{c3}; + auto battleParty2 = BattleParty(&party2, {CreatureIndex(1, 0)}); + + auto battle = Battle(library, {battleParty1, battleParty2}); + + battle.SwitchCreature(0, 0, c1); + battle.SwitchCreature(1, 0, c3); + + battle.TrySetChoice(new SwitchTurnChoice(c1, c2)); + battle.TrySetChoice(new AttackTurnChoice(c3, c3->GetAttacks()[0], CreatureIndex(0, 0))); + + REQUIRE(c2->GetCurrentHealth() < c2->GetBoostedStat(Core::Statistic::Health)); + REQUIRE(c1->GetCurrentHealth() == c1->GetBoostedStat(Core::Statistic::Health)); +} + #endif \ No newline at end of file