From 6ba708ad129d70d8cf9dce154022607cfcf77749 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 15 Dec 2019 11:52:10 +0100 Subject: [PATCH] Implements running from battle. --- src/Battling/Flow/TurnHandler.cpp | 22 ++++++++++--- src/Battling/Flow/TurnHandler.hpp | 2 ++ src/Battling/Library/BattleLibrary.cpp | 10 +++--- src/Battling/Library/BattleLibrary.hpp | 10 +++--- src/Battling/Library/CriticalLibrary.cpp | 9 ------ src/Battling/Library/CriticalLibrary.hpp | 14 --------- src/Battling/Library/MiscLibrary.cpp | 10 ++++++ src/Battling/Library/MiscLibrary.hpp | 16 ++++++++++ src/Battling/Models/Battle.cpp | 17 +++++++---- src/Battling/Models/Battle.hpp | 15 ++++++--- src/Battling/Models/BattleResult.hpp | 25 +++++++++++++++ src/Battling/Models/BattleSide.hpp | 5 +++ src/Battling/TurnChoices/FleeTurnChoice.hpp | 19 ++++++++++++ src/Battling/TurnChoices/TurnChoiceKind.hpp | 2 +- tests/Integration/BattleIntegrations.cpp | 34 +++++++++++++++++++-- tests/TestLibrary/TestLibrary.cpp | 4 +-- 16 files changed, 160 insertions(+), 54 deletions(-) delete mode 100644 src/Battling/Library/CriticalLibrary.cpp delete mode 100644 src/Battling/Library/CriticalLibrary.hpp create mode 100644 src/Battling/Library/MiscLibrary.cpp create mode 100644 src/Battling/Library/MiscLibrary.hpp create mode 100644 src/Battling/Models/BattleResult.hpp create mode 100644 src/Battling/TurnChoices/FleeTurnChoice.hpp diff --git a/src/Battling/Flow/TurnHandler.cpp b/src/Battling/Flow/TurnHandler.cpp index 5fd9918..e65cfcd 100644 --- a/src/Battling/Flow/TurnHandler.cpp +++ b/src/Battling/Flow/TurnHandler.cpp @@ -44,9 +44,9 @@ void TurnHandler::ExecuteChoice(BaseTurnChoice* choice) { case TurnChoiceKind::Pass: throw NotReachableException(); case TurnChoiceKind::Attack: return ExecuteAttackChoice(dynamic_cast(choice)); case TurnChoiceKind::Switch: return ExecuteSwitchChoice(dynamic_cast(choice)); + case TurnChoiceKind::Flee: return ExecuteFleeChoice(dynamic_cast(choice)); - case TurnChoiceKind::Item: - case TurnChoiceKind::RunAway: throw NotImplementedException(); + case TurnChoiceKind::Item: throw NotImplementedException(); } } @@ -138,7 +138,7 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, Creature* targe auto hitType = hit->GetType(); HOOK(ChangeAttackType, targetSource, attack, target, hitIndex, hitType); hit->SetEffectiveness(library->GetTypeLibrary()->GetEffectiveness(hitType, target->GetTypes())); - hit->SetCritical(library->GetCriticalLibrary()->IsCritical(attack, target, hitIndex)); + hit->SetCritical(library->GetMiscLibrary()->IsCritical(attack, target, hitIndex)); hit->SetBasePower(dmgLibrary->GetBasePower(attack, target, hitIndex)); hit->SetDamage(dmgLibrary->GetDamage(attack, target, hitIndex)); @@ -180,4 +180,18 @@ void TurnHandler::ExecuteSwitchChoice(CreatureLib::Battling::SwitchTurnChoice* c auto userSide = user->GetBattleSide(); auto userIndex = userSide->GetCreatureIndex(user); userSide->SetCreature(choice->GetNewCreature(), userIndex); -} \ No newline at end of file +} +void TurnHandler::ExecuteFleeChoice(FleeTurnChoice* choice) { + auto user = choice->GetUser(); + auto battle = user->GetBattle(); + if (!battle->CanFlee()) { + return; + } + // TODO: If any of the creatures on the users side has a script that prevents it from running, block. + // TODO: If any of the creatures on any other side has a script that prevents this side from running, block. + + if (battle->GetLibrary()->GetMiscLibrary()->CanFlee(choice)) { + user->GetBattleSide()->MarkAsFled(); + battle->ValidateBattleState(); + } +} diff --git a/src/Battling/Flow/TurnHandler.hpp b/src/Battling/Flow/TurnHandler.hpp index 4abe1f9..868ad8e 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/FleeTurnChoice.hpp" #include "../TurnChoices/SwitchTurnChoice.hpp" #include "ChoiceQueue.hpp" @@ -17,6 +18,7 @@ namespace CreatureLib::Battling { ExecutingAttack::TargetData* targetData); static void ExecuteSwitchChoice(SwitchTurnChoice* choice); + static void ExecuteFleeChoice(FleeTurnChoice* choice); public: static void RunTurn(Battle* battle, ChoiceQueue* queue); diff --git a/src/Battling/Library/BattleLibrary.cpp b/src/Battling/Library/BattleLibrary.cpp index 48a819a..74e0064 100644 --- a/src/Battling/Library/BattleLibrary.cpp +++ b/src/Battling/Library/BattleLibrary.cpp @@ -4,18 +4,18 @@ using namespace CreatureLib::Battling; BattleLibrary::BattleLibrary(CreatureLib::Library::DataLibrary* staticLib, BattleStatCalculator* statCalculator, - DamageLibrary* damageLibrary, CriticalLibrary* criticalLibrary, - ExperienceLibrary* experienceLibrary, ScriptResolver* scriptResolver) + DamageLibrary* damageLibrary, ExperienceLibrary* experienceLibrary, + ScriptResolver* scriptResolver, MiscLibrary* miscLibrary) : _staticLib(staticLib), _statCalculator(statCalculator), _damageLibrary(damageLibrary), - _criticalLibrary(criticalLibrary), _experienceLibrary(experienceLibrary), _scriptResolver(scriptResolver) {} + _experienceLibrary(experienceLibrary), _scriptResolver(scriptResolver), _miscLibrary(miscLibrary) {} BattleLibrary::~BattleLibrary() { delete _staticLib; delete _statCalculator; delete _damageLibrary; - delete _criticalLibrary; delete _experienceLibrary; delete _scriptResolver; + delete _miscLibrary; } const CreatureLib::Library::LibrarySettings& BattleLibrary::GetSettings() const { return _staticLib->GetSettings(); } @@ -36,7 +36,7 @@ const CreatureLib::Library::TypeLibrary* BattleLibrary::GetTypeLibrary() const { const DamageLibrary* BattleLibrary::GetDamageLibrary() const { return _damageLibrary; } -const CriticalLibrary* BattleLibrary::GetCriticalLibrary() const { return _criticalLibrary; } +const MiscLibrary* BattleLibrary::GetMiscLibrary() const { return _miscLibrary; } Script* BattleLibrary::LoadScript(ScriptResolver::ScriptCategory category, const std::string& scriptName) const { return _scriptResolver->LoadScript(category, scriptName); diff --git a/src/Battling/Library/BattleLibrary.hpp b/src/Battling/Library/BattleLibrary.hpp index 238eb29..86aaab5 100644 --- a/src/Battling/Library/BattleLibrary.hpp +++ b/src/Battling/Library/BattleLibrary.hpp @@ -4,23 +4,23 @@ #include "../../Library/DataLibrary.hpp" #include "../ScriptHandling/ScriptResolver.hpp" #include "BattleStatCalculator.hpp" -#include "CriticalLibrary.hpp" #include "DamageLibrary.hpp" #include "ExperienceLibrary.hpp" +#include "MiscLibrary.hpp" namespace CreatureLib::Battling { class BattleLibrary { const Library::DataLibrary* _staticLib = nullptr; BattleStatCalculator* _statCalculator = nullptr; DamageLibrary* _damageLibrary = nullptr; - CriticalLibrary* _criticalLibrary = nullptr; ExperienceLibrary* _experienceLibrary = nullptr; ScriptResolver* _scriptResolver = nullptr; + MiscLibrary* _miscLibrary = nullptr; public: BattleLibrary(Library::DataLibrary* staticLib, BattleStatCalculator* statCalculator, - DamageLibrary* damageLibrary, CriticalLibrary* criticalLibrary, - ExperienceLibrary* experienceLibrary, ScriptResolver* scriptResolver); + DamageLibrary* damageLibrary, ExperienceLibrary* experienceLibrary, + ScriptResolver* scriptResolver, MiscLibrary* miscLibrary); ~BattleLibrary(); [[nodiscard]] const Library::LibrarySettings& GetSettings() const; @@ -34,7 +34,7 @@ namespace CreatureLib::Battling { [[nodiscard]] const BattleStatCalculator* GetStatCalculator() const; [[nodiscard]] const DamageLibrary* GetDamageLibrary() const; - [[nodiscard]] const CriticalLibrary* GetCriticalLibrary() const; + [[nodiscard]] const MiscLibrary* GetMiscLibrary() const; [[nodiscard]] const ExperienceLibrary* GetExperienceLibrary() const { return _experienceLibrary; } [[nodiscard]] Script* LoadScript(ScriptResolver::ScriptCategory category, const std::string& scriptName) const; diff --git a/src/Battling/Library/CriticalLibrary.cpp b/src/Battling/Library/CriticalLibrary.cpp deleted file mode 100644 index 706bed5..0000000 --- a/src/Battling/Library/CriticalLibrary.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "CriticalLibrary.hpp" -#include "../Models/Battle.hpp" - -bool CreatureLib::Battling::CriticalLibrary::IsCritical(CreatureLib::Battling::ExecutingAttack* attack, - CreatureLib::Battling::Creature* target, uint8_t hit) const { - auto rand = target->GetBattle()->GetRandom(); - // HOOK: Increase chance for critical hits. - return rand.Get(10) <= 0; -} diff --git a/src/Battling/Library/CriticalLibrary.hpp b/src/Battling/Library/CriticalLibrary.hpp deleted file mode 100644 index ac91559..0000000 --- a/src/Battling/Library/CriticalLibrary.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CREATURELIB_CRITICALLIBRARY_HPP -#define CREATURELIB_CRITICALLIBRARY_HPP - -#include "../Models/ExecutingAttack.hpp" - -namespace CreatureLib::Battling { - class CriticalLibrary { - public: - virtual ~CriticalLibrary() = default; - virtual bool IsCritical(ExecutingAttack* attack, Creature* target, uint8_t hit) const; - }; -} - -#endif // CREATURELIB_CRITICALLIBRARY_HPP diff --git a/src/Battling/Library/MiscLibrary.cpp b/src/Battling/Library/MiscLibrary.cpp new file mode 100644 index 0000000..8d2f80d --- /dev/null +++ b/src/Battling/Library/MiscLibrary.cpp @@ -0,0 +1,10 @@ +#include "MiscLibrary.hpp" +#include "../Models/Battle.hpp" + +bool CreatureLib::Battling::MiscLibrary::IsCritical(CreatureLib::Battling::ExecutingAttack* attack, + CreatureLib::Battling::Creature* target, uint8_t hit) const { + auto rand = target->GetBattle()->GetRandom(); + // HOOK: Increase chance for critical hits. + return rand.Get(10) <= 0; +} +bool CreatureLib::Battling::MiscLibrary::CanFlee(FleeTurnChoice* switchChoice) const { return true; } diff --git a/src/Battling/Library/MiscLibrary.hpp b/src/Battling/Library/MiscLibrary.hpp new file mode 100644 index 0000000..cd83bb0 --- /dev/null +++ b/src/Battling/Library/MiscLibrary.hpp @@ -0,0 +1,16 @@ +#ifndef CREATURELIB_MISCLIBRARY_HPP +#define CREATURELIB_MISCLIBRARY_HPP + +#include "../Models/ExecutingAttack.hpp" +#include "../TurnChoices/FleeTurnChoice.hpp" + +namespace CreatureLib::Battling { + class MiscLibrary { + public: + virtual ~MiscLibrary() = default; + virtual bool IsCritical(ExecutingAttack* attack, Creature* target, uint8_t hit) const; + virtual bool CanFlee(FleeTurnChoice* switchChoice) const; + }; +} + +#endif // CREATURELIB_MISCLIBRARY_HPP diff --git a/src/Battling/Models/Battle.cpp b/src/Battling/Models/Battle.cpp index 0120c36..0aa4c7e 100644 --- a/src/Battling/Models/Battle.cpp +++ b/src/Battling/Models/Battle.cpp @@ -100,17 +100,22 @@ bool Battle::CanSlotBeFilled(uint8_t side, uint8_t index) const { void Battle::ValidateBattleState() { bool survivingSideExists = false; - uint8_t result = 0; - for (uint8_t i = 0; i < _sides.size(); i++){ + uint8_t winningSide = 0; + for (uint8_t i = 0; i < _sides.size(); i++) { auto side = _sides[i]; - if (!side->IsDefeated()){ - if (survivingSideExists){ + if (side->HasFled()) { + this->_battleResult = BattleResult::Inconclusive(); + this->_hasEnded = true; + return; + } + if (!side->IsDefeated()) { + if (survivingSideExists) { return; } survivingSideExists = true; - result = i; + winningSide = i; } } - this->_battleResult = result; + this->_battleResult = BattleResult::Conclusive(winningSide); this->_hasEnded = true; } diff --git a/src/Battling/Models/Battle.hpp b/src/Battling/Models/Battle.hpp index 707e397..60ea4dd 100644 --- a/src/Battling/Models/Battle.hpp +++ b/src/Battling/Models/Battle.hpp @@ -6,6 +6,7 @@ #include "../Library/BattleLibrary.hpp" #include "../TurnChoices/BaseTurnChoice.hpp" #include "BattleParty.hpp" +#include "BattleResult.hpp" #include "BattleSide.hpp" #include "CreatureIndex.hpp" @@ -13,20 +14,22 @@ namespace CreatureLib::Battling { class Battle : public ScriptSource { const BattleLibrary* _library; std::vector _parties; + bool _canFlee; uint8_t _numberOfSides; uint8_t _creaturesPerSide; std::vector _sides; Core::Random _random; ChoiceQueue* _currentTurnQueue = nullptr; bool _hasEnded = false; - uint8_t _battleResult = 0; + BattleResult _battleResult = BattleResult::Empty(); ScriptSet _volatile; public: - Battle(const BattleLibrary* library, std::vector parties, uint8_t numberOfSides = 2, - uint8_t creaturesPerSide = 1) - : _library(library), _parties(parties), _numberOfSides(numberOfSides), _creaturesPerSide(creaturesPerSide) { + Battle(const BattleLibrary* library, std::vector parties, bool canFlee = true, + uint8_t numberOfSides = 2, uint8_t creaturesPerSide = 1) + : _library(library), _parties(parties), _canFlee(canFlee), _numberOfSides(numberOfSides), + _creaturesPerSide(creaturesPerSide) { _sides = std::vector(numberOfSides); for (size_t i = 0; i < numberOfSides; i++) { _sides[i] = new BattleSide(i, this, creaturesPerSide); @@ -44,6 +47,8 @@ namespace CreatureLib::Battling { virtual bool CanUse(const BaseTurnChoice* choice); virtual bool TrySetChoice(BaseTurnChoice* choice); + bool CanFlee() const { return _canFlee; } + void CheckChoicesSetAndRun(); [[nodiscard]] ChoiceQueue* GetCurrentTurnQueue() const; @@ -63,7 +68,7 @@ namespace CreatureLib::Battling { void ValidateBattleState(); inline bool HasEnded() const { return _hasEnded; } - inline uint8_t GetResult() const { return _battleResult; } + inline const BattleResult& GetResult() const { return _battleResult; } const std::vector& GetSides() const { return _sides; } }; diff --git a/src/Battling/Models/BattleResult.hpp b/src/Battling/Models/BattleResult.hpp new file mode 100644 index 0000000..ef72405 --- /dev/null +++ b/src/Battling/Models/BattleResult.hpp @@ -0,0 +1,25 @@ +#ifndef CREATURELIB_BATTLERESULT_HPP +#define CREATURELIB_BATTLERESULT_HPP + +#include +namespace CreatureLib::Battling { + class BattleResult { + bool _conclusiveResult; + uint8_t _winningSide; + + BattleResult(bool conclusiveResult, uint8_t winningSide) + : _conclusiveResult(conclusiveResult), _winningSide(winningSide) {} + + public: + static BattleResult Inconclusive() { return BattleResult(false, 0); } + static BattleResult Conclusive(uint8_t winner) { return BattleResult(true, winner); } + static BattleResult Empty() { return BattleResult(false, 0); } + + /// Whether or not the battle has ended with a conclusive result. + bool IsConclusiveResult() const { return _conclusiveResult; } + /// Get the index of the side that has won the battle. Only valid if the battle has a conclusive result. + uint8_t GetWinningSide() const { return _winningSide; } + }; +} + +#endif // CREATURELIB_BATTLERESULT_HPP diff --git a/src/Battling/Models/BattleSide.hpp b/src/Battling/Models/BattleSide.hpp index 7b58803..4f6e67e 100644 --- a/src/Battling/Models/BattleSide.hpp +++ b/src/Battling/Models/BattleSide.hpp @@ -15,6 +15,7 @@ namespace CreatureLib::Battling { uint8_t _choicesSet = 0; ScriptSet _volatile; Battle* _battle; + bool _hasFled = false; public: explicit BattleSide(uint8_t index, Battle* battle, uint8_t creaturesPerSide) @@ -74,6 +75,10 @@ namespace CreatureLib::Battling { } return true; } + + bool HasFled() { return _hasFled; } + + void MarkAsFled() { _hasFled = true; } }; } diff --git a/src/Battling/TurnChoices/FleeTurnChoice.hpp b/src/Battling/TurnChoices/FleeTurnChoice.hpp new file mode 100644 index 0000000..6d7823e --- /dev/null +++ b/src/Battling/TurnChoices/FleeTurnChoice.hpp @@ -0,0 +1,19 @@ +#ifndef CREATURELIB_FLEETURNCHOICE_HPP +#define CREATURELIB_FLEETURNCHOICE_HPP + +#include "../Models/Creature.hpp" +#include "BaseTurnChoice.hpp" + +namespace CreatureLib::Battling { + class FleeTurnChoice : public BaseTurnChoice { + public: + FleeTurnChoice(Creature* user) : BaseTurnChoice(user) {} + + TurnChoiceKind GetKind() const override { return TurnChoiceKind ::Flee; } + + protected: + void GetActiveScripts(std::vector& scripts) override { GetUser()->GetActiveScripts(scripts); } + }; +} + +#endif // CREATURELIB_FLEETURNCHOICE_HPP diff --git a/src/Battling/TurnChoices/TurnChoiceKind.hpp b/src/Battling/TurnChoices/TurnChoiceKind.hpp index 818e9ed..1577c09 100644 --- a/src/Battling/TurnChoices/TurnChoiceKind.hpp +++ b/src/Battling/TurnChoices/TurnChoiceKind.hpp @@ -4,6 +4,6 @@ #include namespace CreatureLib::Battling { - enum class TurnChoiceKind : uint8_t { Pass, Attack, Item, Switch, RunAway }; + enum class TurnChoiceKind : uint8_t { Pass, Attack, Item, Switch, Flee }; } #endif // CREATURELIB_TURNCHOICEKIND_HPP diff --git a/tests/Integration/BattleIntegrations.cpp b/tests/Integration/BattleIntegrations.cpp index b66cc29..4c62864 100644 --- a/tests/Integration/BattleIntegrations.cpp +++ b/tests/Integration/BattleIntegrations.cpp @@ -79,7 +79,9 @@ TEST_CASE("Finish battle when all battle of one side have fainted", "[Integratio c2->Damage(c2->GetCurrentHealth(), DamageSource::AttackDamage); REQUIRE(battle.HasEnded()); - REQUIRE(battle.GetResult() == 0); + auto result = battle.GetResult(); + REQUIRE(result.IsConclusiveResult()); + REQUIRE(result.GetWinningSide() == 0); } TEST_CASE("When creature is dealt enough damage, faint it and mark battle as ended", "[Integrations]") { @@ -104,7 +106,9 @@ TEST_CASE("When creature is dealt enough damage, faint it and mark battle as end battle.TrySetChoice(new PassTurnChoice(c2)); REQUIRE(battle.HasEnded()); - REQUIRE(battle.GetResult() == 0); + auto result = battle.GetResult(); + REQUIRE(result.IsConclusiveResult()); + REQUIRE(result.GetWinningSide() == 0); } TEST_CASE("When another creature is available on faint, make sure the battle hasn't ended", "[Integrations]") { @@ -137,7 +141,9 @@ TEST_CASE("When another creature is available on faint, make sure the battle has battle.TrySetChoice(new PassTurnChoice(c3)); REQUIRE(battle.HasEnded()); - REQUIRE(battle.GetResult() == 0); + auto result = battle.GetResult(); + REQUIRE(result.IsConclusiveResult()); + REQUIRE(result.GetWinningSide() == 0); } TEST_CASE("Switch Creature in", "[Integrations]") { @@ -221,4 +227,26 @@ TEST_CASE("Switch Creature in, mark as seen opponent for opponent", "[Integratio REQUIRE(seen.find(c2) != seen.end()); } +TEST_CASE("Flee Battle", "[Integrations]") { + auto library = TestLibrary::Get(); + auto c1 = CreateCreature(library, "testSpecies1", 100).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + CreatureParty party1{c1}; + auto battleParty1 = BattleParty(&party1, {CreatureIndex(0, 0)}); + auto c2 = CreateCreature(library, "testSpecies1", 1).WithAttack("standard", AttackLearnMethod::Unknown)->Create(); + CreatureParty party2{c2}; + auto battleParty2 = BattleParty(&party2, {CreatureIndex(1, 0)}); + + auto battle = Battle(library, {battleParty1, battleParty2}); + + battle.SwitchCreature(0, 0, c1); + battle.SwitchCreature(1, 0, c2); + + battle.TrySetChoice(new FleeTurnChoice(c1)); + battle.TrySetChoice(new PassTurnChoice(c2)); + + REQUIRE(battle.HasEnded()); + auto result = battle.GetResult(); + REQUIRE_FALSE(result.IsConclusiveResult()); +} + #endif \ No newline at end of file diff --git a/tests/TestLibrary/TestLibrary.cpp b/tests/TestLibrary/TestLibrary.cpp index f371db1..6000eba 100644 --- a/tests/TestLibrary/TestLibrary.cpp +++ b/tests/TestLibrary/TestLibrary.cpp @@ -13,8 +13,8 @@ BattleLibrary* TestLibrary::Get() { auto l = new DataLibrary(LibrarySettings(100, 4), BuildSpeciesLibrary(), BuildAttackLibrary(), BuildItemLibrary(), BuildGrowthRateLibrary(), BuildTypeLibrary()); auto statCalc = new BattleStatCalculator(); - auto battleLib = new BattleLibrary(l, statCalc, new DamageLibrary(), new CriticalLibrary(), - new ExperienceLibrary(), new ScriptResolver()); + auto battleLib = new BattleLibrary(l, statCalc, new DamageLibrary(), new ExperienceLibrary(), + new ScriptResolver(), new MiscLibrary()); TestLibrary::_library = battleLib; } return TestLibrary::_library;