From 3233daf9ab70834f2468d365d09af88477aa1ad2 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 30 Aug 2020 13:14:33 +0200 Subject: [PATCH] More defensive programming. Signed-off-by: Deukhoofd --- src/Battling/Flow/TurnOrdering.cpp | 28 +++++++++++++++--------- src/Battling/Flow/TurnOrdering.hpp | 2 +- src/Battling/Models/Battle.cpp | 8 +++---- tests/BattleTests/TurnOrderTests.cpp | 20 ++++++++--------- tests/Integration/BattleIntegrations.cpp | 27 +++++++++++++++++++++++ 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/Battling/Flow/TurnOrdering.cpp b/src/Battling/Flow/TurnOrdering.cpp index 0f361e4..7751bfa 100644 --- a/src/Battling/Flow/TurnOrdering.cpp +++ b/src/Battling/Flow/TurnOrdering.cpp @@ -8,20 +8,27 @@ using namespace Battling; class ChoiceCompare { public: - explicit ChoiceCompare() {} bool operator()(const std::shared_ptr& a, const std::shared_ptr& b) { auto aKind = a->GetKind(); auto bKind = b->GetKind(); if (aKind != bKind) return aKind > bKind; if (aKind == TurnChoiceKind::Attack) { - auto aPriority = dynamic_cast(a.get())->GetPriority(); - auto bPriority = dynamic_cast(b.get())->GetPriority(); + auto aAttack = std::dynamic_pointer_cast(a); + auto bAttack = std::dynamic_pointer_cast(b); + AssertNotNull(aAttack); + AssertNotNull(bAttack); + auto aPriority = aAttack->GetPriority(); + auto bPriority = bAttack->GetPriority(); if (aPriority != bPriority) return aPriority > bPriority; } - auto aSpeed = a->GetUser()->GetBoostedStat(Library::Statistic::Speed); - auto bSpeed = b->GetUser()->GetBoostedStat(Library::Statistic::Speed); + auto aUser = a->GetUser(); + auto bUser = b->GetUser(); + AssertNotNull(aUser); + AssertNotNull(bUser); + auto aSpeed = aUser->GetBoostedStat(Library::Statistic::Speed); + auto bSpeed = bUser->GetBoostedStat(Library::Statistic::Speed); if (aSpeed != bSpeed) return aSpeed > bSpeed; @@ -29,13 +36,14 @@ public: } }; -void TurnOrdering::OrderChoices(std::vector>& vec, - [[maybe_unused]] ArbUt::Random& rand) { - for (auto item : vec) { +void TurnOrdering::OrderChoices(std::vector>& vec) { + for (const auto& item : vec) { + AssertNotNull(item); if (item->GetKind() == TurnChoiceKind::Attack) { - auto attackChoice = static_cast(item.get()); + auto attackChoice = std::dynamic_pointer_cast(item); + AssertNotNull(attackChoice); auto priority = attackChoice->GetPriority(); - HOOK(ChangePriority, attackChoice, attackChoice, &priority); + HOOK(ChangePriority, attackChoice, attackChoice.get(), &priority); attackChoice->SetPriority(priority); } } diff --git a/src/Battling/Flow/TurnOrdering.hpp b/src/Battling/Flow/TurnOrdering.hpp index 61dc8c5..37776b5 100644 --- a/src/Battling/Flow/TurnOrdering.hpp +++ b/src/Battling/Flow/TurnOrdering.hpp @@ -9,7 +9,7 @@ namespace CreatureLib::Battling { class TurnOrdering { public: - static void OrderChoices(std::vector>& vec, ArbUt::Random& rand); + static void OrderChoices(std::vector>& vec); }; } diff --git a/src/Battling/Models/Battle.cpp b/src/Battling/Models/Battle.cpp index 0ea3041..40cba22 100644 --- a/src/Battling/Models/Battle.cpp +++ b/src/Battling/Models/Battle.cpp @@ -54,12 +54,12 @@ void Battle::CheckChoicesSetAndRun() { for (auto choice : side->GetChoices()) { AssertNotNull(choice) if (choice->GetKind() == TurnChoiceKind::Attack) { - auto attack = ((AttackTurnChoice*)choice.get())->GetAttack(); + auto attack = std::static_pointer_cast(choice); uint8_t uses = 1; // HOOK: change number of uses needed. - if (attack->GetRemainingUses() < uses) { + if (attack->GetAttack()->GetRemainingUses() < uses) { choice = std::shared_ptr(_library->GetMiscLibrary()->ReplacementAttack( - choice->GetUser().GetRaw(), ((AttackTurnChoice*)choice.get())->GetTarget())); + choice->GetUser().GetRaw(), attack->GetTarget())); } // HOOK: Check if we need to change the move } @@ -74,7 +74,7 @@ void Battle::CheckChoicesSetAndRun() { } _currentTurn++; try { - TurnOrdering::OrderChoices(choices, _random.GetRNG()); + TurnOrdering::OrderChoices(choices); } catch (const std::exception& e) { THROW("Exception during turn ordering: '" << e.what() << "'.") } diff --git a/tests/BattleTests/TurnOrderTests.cpp b/tests/BattleTests/TurnOrderTests.cpp index b8b956a..031342f 100644 --- a/tests/BattleTests/TurnOrderTests.cpp +++ b/tests/BattleTests/TurnOrderTests.cpp @@ -17,11 +17,11 @@ TEST_CASE("Turn ordering: Attack before pass", "[Battling]") { auto choice2 = std::make_shared(nullptr, &learnedAttack, CreatureIndex(0, 0)); auto vec = std::vector>{choice1, choice2}; auto rand = ArbUt::Random(); - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); vec = std::vector>{choice2, choice1}; - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); } @@ -34,11 +34,11 @@ TEST_CASE("Turn ordering: High priority goes before no priority", "[Battling]") auto choice2 = std::make_shared(nullptr, a2, CreatureIndex(0, 0)); auto vec = std::vector>{choice1, choice2}; auto rand = ArbUt::Random(); - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); vec = std::vector>{choice2, choice1}; - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); @@ -54,11 +54,11 @@ TEST_CASE("Turn ordering: Higher priority goes before high priority", "[Battling auto choice2 = std::make_shared(nullptr, a2, CreatureIndex(0, 0)); auto vec = std::vector>{choice1, choice2}; auto rand = ArbUt::Random(); - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); vec = std::vector>{choice2, choice1}; - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); delete a1; @@ -73,11 +73,11 @@ TEST_CASE("Turn ordering: High priority goes before low priority", "[Battling]") auto choice2 = std::make_shared(nullptr, a2, CreatureIndex(0, 0)); auto vec = std::vector>{choice1, choice2}; auto rand = ArbUt::Random(); - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); vec = std::vector>{choice2, choice1}; - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); @@ -93,11 +93,11 @@ TEST_CASE("Turn ordering: No priority goes before low priority", "[Battling]") { auto choice2 = std::make_shared(nullptr, a2, CreatureIndex(0, 0)); auto vec = std::vector>{choice1, choice2}; auto rand = ArbUt::Random(); - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); vec = std::vector>{choice2, choice1}; - TurnOrdering::OrderChoices(vec, rand); + TurnOrdering::OrderChoices(vec); CHECK(vec[0] == choice2); CHECK(vec[1] == choice1); diff --git a/tests/Integration/BattleIntegrations.cpp b/tests/Integration/BattleIntegrations.cpp index 20391a6..e3cb529 100644 --- a/tests/Integration/BattleIntegrations.cpp +++ b/tests/Integration/BattleIntegrations.cpp @@ -56,6 +56,33 @@ TEST_CASE("Use damaging move", "[Integrations]") { REQUIRE(c2->GetCurrentHealth() < c2->GetBoostedStat(Statistic::Health)); } +TEST_CASE("Run more turns", "[Integrations]") { + auto library = TestLibrary::Get(); + auto c1 = + CreateCreature(library, "testSpecies1"_cnc, 50).WithAttack("standard"_cnc, AttackLearnMethod::Unknown).Create(); + CreatureParty party1{c1}; + auto battleParty1 = new BattleParty(&party1, {CreatureIndex(0, 0)}); + auto c2 = + CreateCreature(library, "testSpecies1"_cnc, 1).WithAttack("standard"_cnc, AttackLearnMethod::Unknown).Create(); + auto c3 = + CreateCreature(library, "testSpecies1"_cnc, 1).WithAttack("standard"_cnc, AttackLearnMethod::Unknown).Create(); + CreatureParty party2{c2, c3}; + auto battleParty2 = new BattleParty(&party2, {CreatureIndex(1, 0)}); + + auto battle = Battle(library, {battleParty1, battleParty2}); + + battle.SwitchCreature(0, 0, c1); + battle.SwitchCreature(1, 0, c2); + + battle.TrySetChoice(new AttackTurnChoice(c1, c1->GetAttacks()[0], CreatureIndex(1, 0))); + battle.TrySetChoice(new PassTurnChoice(c2)); + + REQUIRE(c2->IsFainted()); + battle.SwitchCreature(1, 0, c3); + battle.TrySetChoice(new AttackTurnChoice(c1, c1->GetAttacks()[0], CreatureIndex(1, 0))); + battle.TrySetChoice(new PassTurnChoice(c3)); +} + TEST_CASE("Finish battle when all battle of one side have fainted", "[Integrations]") { auto library = TestLibrary::Get(); auto c1 =