From 57f16bc4209a96032026df91cba4f782c6797f57 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 7 Dec 2019 12:13:12 +0100 Subject: [PATCH] Better handling of filling empty slots. --- src/Battling/Flow/TurnHandler.cpp | 3 --- src/Battling/Models/Battle.cpp | 32 +++++++++++++----------- src/Battling/Models/Battle.hpp | 8 +++--- src/Battling/Models/BattleParty.hpp | 32 +++++++++++++++++++++++- src/Battling/Models/BattleSide.cpp | 11 ++++++++ src/Battling/Models/BattleSide.hpp | 7 ++++-- src/Battling/Models/Creature.hpp | 2 ++ src/Battling/Models/CreatureIndex.hpp | 4 +++ tests/BattleTests/ChoiceSetTests.cpp | 8 +++--- tests/Integration/BattleIntegrations.cpp | 4 +-- 10 files changed, 80 insertions(+), 31 deletions(-) diff --git a/src/Battling/Flow/TurnHandler.cpp b/src/Battling/Flow/TurnHandler.cpp index 030ebd0..d8a0535 100644 --- a/src/Battling/Flow/TurnHandler.cpp +++ b/src/Battling/Flow/TurnHandler.cpp @@ -10,9 +10,6 @@ void TurnHandler::RunTurn(Battle* battle, ChoiceQueue* queue) { HOOK(OnBeforeTurn, choice, choice); } while (queue->HasNext()) { - if (battle->HasRecalledSlots()) { - return; - } auto item = queue->Dequeue(); ExecuteChoice(item); delete item; diff --git a/src/Battling/Models/Battle.cpp b/src/Battling/Models/Battle.cpp index a61df4d..57f6e21 100644 --- a/src/Battling/Models/Battle.cpp +++ b/src/Battling/Models/Battle.cpp @@ -26,6 +26,9 @@ bool Battle::TrySetChoice(BaseTurnChoice* choice) { void Battle::CheckChoicesSetAndRun() { for (auto side : _sides) { + if (!side->AllPossibleSlotsFilled()) { + return; + } if (!side->AllChoicesSet()) { return; } @@ -34,8 +37,11 @@ void Battle::CheckChoicesSetAndRun() { auto i = 0; for (auto side : _sides) { for (BaseTurnChoice* choice : side->GetChoices()) { + if (choice == nullptr) { + throw CreatureException("Choice was null"); + } if (choice->GetKind() == TurnChoiceKind::Attack) { - auto attack = dynamic_cast(choice)->GetAttack(); + auto attack = (dynamic_cast((choice)))->GetAttack(); uint8_t uses = 1; // HOOK: change number of uses needed. if (attack->GetRemainingUses() < uses) { @@ -70,23 +76,21 @@ bool Battle::CreatureInField(const Creature* creature) const { return false; } -bool Battle::HasRecalledSlots() const { return _numberOfRecalledSlots > 0; } +void Battle::ForceRecall(uint8_t side, uint8_t index) { _sides[side]->SetCreature(nullptr, index); } -void Battle::ForceRecall(uint8_t side, uint8_t index) { - _numberOfRecalledSlots++; - // TODO: Switch out. -} - -void Battle::FillRecall(uint8_t side, uint8_t, Creature* c) { - _numberOfRecalledSlots--; - // TODO: switch in. - if (_numberOfRecalledSlots == 0 && _currentTurnQueue != nullptr) { - TurnHandler::RunTurn(this, _currentTurnQueue); - } -} +void Battle::FillEmptySlot(uint8_t side, uint8_t index, Creature* c) { _sides[side]->SetCreature(c, index); } void Battle::GetActiveScripts(std::vector& scripts) { scripts.emplace_back(&_volatile); } void Battle::SwitchCreature(uint8_t sideIndex, uint8_t index, Creature* c) { auto side = this->_sides[sideIndex]; side->SetCreature(c, index); } +bool Battle::CanSlotBeFilled(uint8_t side, uint8_t index) const { + for (const auto& party : _parties) { + if (party.IsResponsibleForIndex(side, index)) { + if (party.HasCreaturesNotInField()) + return true; + } + } + return false; +} diff --git a/src/Battling/Models/Battle.hpp b/src/Battling/Models/Battle.hpp index aabfda0..9325b4a 100644 --- a/src/Battling/Models/Battle.hpp +++ b/src/Battling/Models/Battle.hpp @@ -19,7 +19,6 @@ namespace CreatureLib::Battling { Core::Random _random; ChoiceQueue* _currentTurnQueue = nullptr; - uint8_t _numberOfRecalledSlots = 0; ScriptSet _volatile; public: @@ -28,7 +27,7 @@ namespace CreatureLib::Battling { : _library(library), _parties(parties), _numberOfSides(numberOfSides), _creaturesPerSide(creaturesPerSide) { _sides = std::vector(numberOfSides); for (size_t i = 0; i < numberOfSides; i++) { - _sides[i] = new BattleSide(this, creaturesPerSide); + _sides[i] = new BattleSide(i, this, creaturesPerSide); } } @@ -54,11 +53,10 @@ namespace CreatureLib::Battling { return _sides[target.GetSideIndex()]->GetCreature(target.GetCreatureIndex()); } - [[nodiscard]] bool HasRecalledSlots() const; - void ForceRecall(uint8_t side, uint8_t index); - void FillRecall(uint8_t side, uint8_t, Creature* c); + void FillEmptySlot(uint8_t side, uint8_t index, Creature* c); void SwitchCreature(uint8_t side, uint8_t index, Creature* c); + bool CanSlotBeFilled(uint8_t side, uint8_t index) const; void GetActiveScripts(std::vector& scripts) override; }; diff --git a/src/Battling/Models/BattleParty.hpp b/src/Battling/Models/BattleParty.hpp index 3b21bb3..cf31a85 100644 --- a/src/Battling/Models/BattleParty.hpp +++ b/src/Battling/Models/BattleParty.hpp @@ -14,7 +14,37 @@ namespace CreatureLib::Battling { : _party(party), _responsibleIndices(responsibleIndices) {} CreatureParty* GetParty() { return _party; } - std::vector& GetResponsibleIndices() { return _responsibleIndices; } + const std::vector& GetResponsibleIndices() const { return _responsibleIndices; } + + bool IsResponsibleForIndex(const CreatureIndex& index) const { + for (const auto& i : _responsibleIndices) { + if (i == index) + return true; + } + return false; + } + + bool IsResponsibleForIndex(uint8_t side, uint8_t index) const { + for (const auto& i : _responsibleIndices) { + if (i.GetSideIndex() == side && i.GetCreatureIndex() == index) + return true; + } + return false; + } + + bool HasCreaturesNotInField() const { + auto p = _party->GetParty(); + for (const auto& creature : p) { + if (creature == nullptr) + continue; + if (creature->IsFainted()) + continue; + if (creature->IsOnBattleField()) + continue; + return true; + } + return false; + } }; } diff --git a/src/Battling/Models/BattleSide.cpp b/src/Battling/Models/BattleSide.cpp index 1353701..1a6881a 100644 --- a/src/Battling/Models/BattleSide.cpp +++ b/src/Battling/Models/BattleSide.cpp @@ -7,6 +7,17 @@ using namespace CreatureLib::Battling; bool BattleSide::AllChoicesSet() const { return _choicesSet == _creaturesPerSide; } +bool BattleSide::AllPossibleSlotsFilled() const { + for (size_t i = 0; i < _creatures.size(); i++) { + auto c = _creatures[i]; + if (c == nullptr || c->IsFainted()) { + if (_battle->CanSlotBeFilled(_index, i)) + return false; + } + } + return true; +} + void BattleSide::ResetChoices() { _choicesSet = 0; for (uint8_t i = 0; i < _creaturesPerSide; i++) { diff --git a/src/Battling/Models/BattleSide.hpp b/src/Battling/Models/BattleSide.hpp index 43c1a56..9cca52f 100644 --- a/src/Battling/Models/BattleSide.hpp +++ b/src/Battling/Models/BattleSide.hpp @@ -7,6 +7,7 @@ namespace CreatureLib::Battling { class BattleSide : public ScriptSource { + uint8_t _index; uint8_t _creaturesPerSide; std::vector _creatures; std::vector _choices; @@ -15,8 +16,8 @@ namespace CreatureLib::Battling { Battle* _battle; public: - explicit BattleSide(Battle* battle, uint8_t creaturesPerSide) - : _creaturesPerSide(creaturesPerSide), _battle(battle) { + explicit BattleSide(uint8_t index, Battle* battle, uint8_t creaturesPerSide) + : _index(index), _creaturesPerSide(creaturesPerSide), _battle(battle) { _creatures = std::vector(creaturesPerSide); _choices = std::vector(creaturesPerSide); for (size_t i = 0; i < creaturesPerSide; i++) { @@ -31,6 +32,8 @@ namespace CreatureLib::Battling { [[nodiscard]] bool AllChoicesSet() const; [[nodiscard]] const std::vector& GetChoices() const; + [[nodiscard]] bool AllPossibleSlotsFilled() const; + void SetChoice(BaseTurnChoice* choice); void ResetChoices(); diff --git a/src/Battling/Models/Creature.hpp b/src/Battling/Models/Creature.hpp index a0ce9c7..6fa0615 100644 --- a/src/Battling/Models/Creature.hpp +++ b/src/Battling/Models/Creature.hpp @@ -39,6 +39,7 @@ namespace CreatureLib::Battling { Battle* _battle; BattleSide* _side; + bool _onBattleField; std::string _nickname = ""; int8_t _talentIndex; @@ -68,6 +69,7 @@ namespace CreatureLib::Battling { void SetBattleData(Battle* battle, BattleSide* side); Battle* GetBattle() const; BattleSide* GetBattleSide() const; + bool IsOnBattleField() const { return _onBattleField; } const std::string& GetNickname() const; const std::string& GetActiveTalent() const; diff --git a/src/Battling/Models/CreatureIndex.hpp b/src/Battling/Models/CreatureIndex.hpp index 4a7d219..f10e2cb 100644 --- a/src/Battling/Models/CreatureIndex.hpp +++ b/src/Battling/Models/CreatureIndex.hpp @@ -15,6 +15,10 @@ namespace CreatureLib::Battling { uint8_t GetSideIndex() const { return _side; } uint8_t GetCreatureIndex() const { return _creature; } + + bool operator==(const CreatureIndex& rhs) const { return (_side == rhs._side) && (_creature == rhs._creature); } + + bool operator!=(const CreatureIndex& rhs) const { return !operator==(rhs); } }; } diff --git a/tests/BattleTests/ChoiceSetTests.cpp b/tests/BattleTests/ChoiceSetTests.cpp index 5b45405..1f42bfe 100644 --- a/tests/BattleTests/ChoiceSetTests.cpp +++ b/tests/BattleTests/ChoiceSetTests.cpp @@ -8,7 +8,7 @@ using namespace CreatureLib::Battling; TEST_CASE("Set Choice one-sized side", "[Battling]") { - auto side = BattleSide(nullptr, 1); + auto side = BattleSide(0, nullptr, 1); auto c = CreateCreature(GetLibrary(), "testSpecies1", 5).Create(); side.SetCreature(c, 0); auto choice = new PassTurnChoice(c); @@ -18,7 +18,7 @@ TEST_CASE("Set Choice one-sized side", "[Battling]") { } TEST_CASE("Set Choice one-sized side, validate all choices set", "[Battling]") { - auto side = BattleSide(nullptr, 1); + auto side = BattleSide(0, nullptr, 1); auto c = CreateCreature(GetLibrary(), "testSpecies1", 5).Create(); side.SetCreature(c, 0); auto choice = new PassTurnChoice(c); @@ -30,7 +30,7 @@ TEST_CASE("Set Choice one-sized side, validate all choices set", "[Battling]") { } TEST_CASE("Set Choice two-sized side", "[Battling]") { - auto side = BattleSide(nullptr, 2); + auto side = BattleSide(0, nullptr, 2); auto c1 = CreateCreature(GetLibrary(), "testSpecies1", 5).Create(); auto c2 = CreateCreature(GetLibrary(), "testSpecies1", 5).Create(); side.SetCreature(c1, 0); @@ -46,7 +46,7 @@ TEST_CASE("Set Choice two-sized side", "[Battling]") { } TEST_CASE("Set Choice two-sized side, validate all choices set", "[Battling]") { - auto side = BattleSide(nullptr, 2); + auto side = BattleSide(0, nullptr, 2); auto c1 = CreateCreature(GetLibrary(), "testSpecies1", 5).Create(); auto c2 = CreateCreature(GetLibrary(), "testSpecies1", 5).Create(); side.SetCreature(c1, 0); diff --git a/tests/Integration/BattleIntegrations.cpp b/tests/Integration/BattleIntegrations.cpp index e0cadff..23f15c9 100644 --- a/tests/Integration/BattleIntegrations.cpp +++ b/tests/Integration/BattleIntegrations.cpp @@ -41,8 +41,8 @@ TEST_CASE("Use damaging move", "[Integrations]") { auto battle = Battle(library, {battleParty1, battleParty2}); - battle.SwitchCreature(0, 0, c1); - battle.SwitchCreature(1, 0, c2); + battle.FillEmptySlot(0, 0, c1); + battle.FillEmptySlot(1, 0, c2); battle.TrySetChoice(new AttackTurnChoice(c1, c1->GetAttacks()[0], CreatureIndex(1, 0))); battle.TrySetChoice(new PassTurnChoice(c2));