Files
EVEngine/test/tactics_reaction.cpp

89 lines
4.4 KiB
C++

#include "common/ECS.h"
#include "tactics/Tactics.h"
#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
namespace {
eve::SubjectRef subject(const char* text) {
const auto id = eve::PersistentId::parse(text);
REQUIRE(id.has_value());
return eve::SubjectRef::fromPersistentId(*id);
}
} // namespace
TEST_CASE("tactics.reactionConsumesResourceAndPreventsSameTriggerCycle") {
ecs::Table world;
ecs::ScopedTable guard(world);
eve::tactics::Tactics tactics;
auto battleResult = tactics.newBattle(subject("00000000-0000-0000-0000-000000000060"));
REQUIRE(battleResult.ok());
const auto battle = std::move(battleResult).takeValue();
REQUIRE(tactics.addCell(battle, {0, 0, 0}).ok());
REQUIRE(tactics.addCell(battle, {1, 0, 0}).ok());
auto sideResult = tactics.newSide(battle, subject("00000000-0000-0000-0000-000000000061"));
REQUIRE(sideResult.ok());
const auto side = std::move(sideResult).takeValue();
const auto actor = subject("00000000-0000-0000-0000-000000000062");
const auto defender = subject("00000000-0000-0000-0000-000000000063");
REQUIRE(tactics.newUnit(battle, side, actor, {}, {0, 0, 0}, {1, 0, 0, 20}).ok());
REQUIRE(tactics.newUnit(battle, side, defender, {}, {1, 0, 0}, {1, 0, 1, 10}).ok());
REQUIRE(tactics.start(battle, eve::tactics::TurnPolicyKind::Initiative).ok());
REQUIRE(tactics.advance(battle, {eve::SimulationTick(1), eve::Duration::zero()}).ok());
REQUIRE(tactics.advance(battle, {eve::SimulationTick(2), eve::Duration::zero()}).ok());
REQUIRE(tactics.advance(battle, {eve::SimulationTick(3), eve::Duration::zero()}).ok());
const auto reaction = *eve::LogicalId::parse("tactics:counter");
auto opened = tactics.openReaction(battle, 4, {{defender, reaction, 5, 10}});
REQUIRE(opened.ok());
CHECK_EQ(std::move(opened).takeValue(), 1u);
auto accepted = tactics.acceptReaction(battle, defender, reaction);
REQUIRE(accepted.ok());
CHECK_EQ(std::move(accepted).takeValue().remainingReactionPoints, 0);
auto* battleValue = dynamic_cast<eve::tactics::Battle*>(ecs::try_get(battle));
REQUIRE(battleValue != nullptr);
const auto& acceptedEvent = battleValue->events()->values.back();
CHECK(acceptedEvent.causationCommand != 0);
CHECK(acceptedEvent.correlationCommand != 0);
CHECK(acceptedEvent.correlationCommand != acceptedEvent.causationCommand);
REQUIRE(tactics.openReaction(battle, 4, {{defender, reaction, 5, 10}}).ok());
auto duplicate = tactics.acceptReaction(battle, defender, reaction);
CHECK(!duplicate.ok());
CHECK_EQ(duplicate.code(), eve::StatusCode::Conflict);
REQUIRE(tactics.declineReaction(battle).ok());
}
TEST_CASE("tactics.reactionDepthIsBounded") {
ecs::Table world;
ecs::ScopedTable guard(world);
eve::tactics::Tactics tactics;
auto battleResult = tactics.newBattle(subject("00000000-0000-0000-0000-000000000070"));
REQUIRE(battleResult.ok());
const auto battle = std::move(battleResult).takeValue();
REQUIRE(tactics.addCell(battle, {0, 0, 0}).ok());
auto sideResult = tactics.newSide(battle, subject("00000000-0000-0000-0000-000000000071"));
REQUIRE(sideResult.ok());
const auto side = std::move(sideResult).takeValue();
const auto actor = subject("00000000-0000-0000-0000-000000000072");
REQUIRE(tactics.newUnit(battle, side, actor, {}, {0, 0, 0}, {1, 0, 8, 1}).ok());
REQUIRE(tactics.start(battle, eve::tactics::TurnPolicyKind::Initiative).ok());
REQUIRE(tactics.advance(battle, {eve::SimulationTick(1), eve::Duration::zero()}).ok());
REQUIRE(tactics.advance(battle, {eve::SimulationTick(2), eve::Duration::zero()}).ok());
REQUIRE(tactics.advance(battle, {eve::SimulationTick(3), eve::Duration::zero()}).ok());
const auto reaction = *eve::LogicalId::parse("tactics:counter");
auto* battleValue = dynamic_cast<eve::tactics::Battle*>(ecs::try_get(battle));
REQUIRE(battleValue != nullptr);
for (std::uint64_t depth = 1; depth <= 8; ++depth) {
const auto trigger = battleValue->events()->nextSequence - 1;
REQUIRE(tactics.openReaction(battle, trigger, {{actor, reaction, 0, 1}}).ok());
}
const auto overflowTrigger = battleValue->events()->nextSequence - 1;
auto overflow = tactics.openReaction(battle, overflowTrigger, {{actor, reaction, 0, 1}});
CHECK(!overflow.ok());
CHECK_EQ(overflow.code(), eve::StatusCode::Rejected);
}