Files
EVEngine/test/gameplay_control_weapon.cpp

140 lines
5.8 KiB
C++

#include "common/Capability.h"
#include "inventory/Bag.h"
#include "inventory/InventoryResourceAccount.h"
#include "inventory/Item.h"
#include "transaction/Transaction.h"
#include "weapon/WeaponControl.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);
}
eve::LogicalId action(const char* text) {
const auto id = eve::LogicalId::parse(text);
REQUIRE(id.has_value());
return *id;
}
class Effect final : public eve::transaction::ITransactionParticipant {
public:
[[nodiscard]] eve::Result<void> prepare(const eve::transaction::TransactionContext&) override {
prepared = true;
return eve::Result<void>::success();
}
[[nodiscard]] eve::Result<void> commit(const eve::transaction::TransactionContext&) override {
committed = true;
return eve::Result<void>::success();
}
[[nodiscard]] eve::Result<void> rollback(const eve::transaction::TransactionContext&) override {
prepared = false;
return eve::Result<void>::success();
}
[[nodiscard]] eve::Result<void> compensate(const eve::transaction::TransactionContext&) override {
committed = false;
prepared = false;
return eve::Result<void>::success();
}
bool prepared = false;
bool committed = false;
};
eve::GameplayCommand fireCommand(const char* commandId, eve::SubjectRef wielder,
const eve::GameplayObservation& observed) {
eve::GameplayCommand command;
command.id = commandId;
command.action = action("weapon:fire");
command.subject = wielder;
command.observedTick = observed.tick;
command.expectedRevision = observed.revision;
command.parameters = eve::Value(eve::Value::Object{{"shooterId", eve::Value(7)},
{"targetX", eve::Value(4.0)},
{"targetY", eve::Value(5.0)},
{"targetZ", eve::Value(6.0)}});
return command;
}
} // namespace
TEST_CASE("gameplay.control.weaponUsesCanonicalActionAndAtomicAmmoPayment") {
eve::inventory::ItemRegistry::clear();
eve::inventory::ItemDefinition ammo;
ammo.id = "ammo";
ammo.maxStack = 20;
eve::inventory::ItemRegistry::registerItem(ammo);
eve::inventory::Bag bag(1);
REQUIRE_EQ(bag.addItem("ammo", 2), 2);
eve::inventory::InventoryResourceAccount account(bag);
Effect effect;
eve::weapon::WeaponDefinition definition;
definition.id = "player-rifle";
definition.resource.kind = eve::weapon::ResourceKind::Ammo;
definition.resource.cost = 1.0f;
const auto instance = subject("00000000-0000-7000-8000-000000000801");
const auto wielder = subject("00000000-0000-7000-8000-000000000802");
eve::weapon::WeaponControl control(instance, wielder, definition, account, effect);
eve::GameplaySession player{"player", eve::GameplayAccess::PlayerEquivalent, {wielder}};
eve::GameplaySession automation{"automation", eve::GameplayAccess::TestDriver, {wielder}};
bool discovered = false;
eve::cap::forEach<eve::IGameplayControlProvider>([&](auto* provider) {
if (provider == &control && provider->gameplayDomain() == "weapon") discovered = true;
});
CHECK(discovered);
auto playerActions = control.availableGameplayActions(player, instance, wielder);
auto automationActions = control.availableGameplayActions(automation, instance, wielder);
REQUIRE(playerActions.ok());
REQUIRE(automationActions.ok());
CHECK_EQ(playerActions.value().front().id, automationActions.value().front().id);
auto observed = control.observeGameplay(player, instance);
REQUIRE(observed.ok());
const auto before = std::move(observed).takeValue();
auto fired = control.submitGameplay(player, instance, fireCommand("weapon-fire-1", wielder, before));
REQUIRE(fired.ok());
CHECK_EQ(bag.countItem("ammo"), 1);
CHECK(effect.committed);
auto stale = control.submitGameplay(player, instance, fireCommand("weapon-fire-stale", wielder, before));
CHECK(!stale.ok());
CHECK_EQ(stale.code(), eve::StatusCode::Conflict);
CHECK_EQ(bag.countItem("ammo"), 1);
auto events = control.gameplayEvents(player, instance, 0);
REQUIRE(events.ok());
REQUIRE_EQ(events.value().size(), std::size_t{1});
CHECK_EQ(events.value().front().causationCommandId, std::string("weapon-fire-1"));
eve::inventory::ItemRegistry::clear();
}
TEST_CASE("gameplay.control.weaponRejectsUnauthorizedWielderWithoutCharging") {
eve::inventory::ItemRegistry::clear();
eve::inventory::ItemDefinition ammo;
ammo.id = "ammo";
ammo.maxStack = 20;
eve::inventory::ItemRegistry::registerItem(ammo);
eve::inventory::Bag bag(1);
REQUIRE_EQ(bag.addItem("ammo", 1), 1);
eve::inventory::InventoryResourceAccount account(bag);
Effect effect;
eve::weapon::WeaponDefinition definition;
definition.id = "guard-rifle";
definition.resource.kind = eve::weapon::ResourceKind::Ammo;
definition.resource.cost = 1.0f;
const auto instance = subject("00000000-0000-7000-8000-000000000811");
const auto wielder = subject("00000000-0000-7000-8000-000000000812");
const auto stranger = subject("00000000-0000-7000-8000-000000000813");
eve::weapon::WeaponControl control(instance, wielder, definition, account, effect);
eve::GameplaySession unauthorized{"other", eve::GameplayAccess::PlayerEquivalent, {stranger}};
auto observed = control.observeGameplay(unauthorized, instance);
CHECK(!observed.ok());
CHECK_EQ(bag.countItem("ammo"), 1);
CHECK(!effect.committed);
eve::inventory::ItemRegistry::clear();
}