inventory tests
This commit is contained in:
@@ -19,6 +19,7 @@ struct InventoryT
|
|||||||
public:
|
public:
|
||||||
InventoryT() = default;
|
InventoryT() = default;
|
||||||
InventoryT(size_t itemAmount) { Slots = { static_cast<int>(itemAmount), InventoryMeta{} }; }
|
InventoryT(size_t itemAmount) { Slots = { static_cast<int>(itemAmount), InventoryMeta{} }; }
|
||||||
|
InventoryT(size_t itemAmount, IntegralType maxAmount) { Slots = { static_cast<int>(itemAmount), InventoryMeta{ maxAmount } }; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IntegralType GetItemsAmount(uint16_t id) const { Assert(id); return Slots[id]; }
|
IntegralType GetItemsAmount(uint16_t id) const { Assert(id); return Slots[id]; }
|
||||||
@@ -41,6 +42,8 @@ public:
|
|||||||
Slots[i] += other.Slots[i];
|
Slots[i] += other.Slots[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsFull(uint16_t id) const { Assert(id); return Slots[id] >= Slots.GetMetaData()->MaxSize; }
|
||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
for (IntegralType i{}; i < Slots.GetSize(); ++i)
|
for (IntegralType i{}; i < Slots.GetSize(); ++i)
|
||||||
@@ -124,6 +127,9 @@ struct InventoryAreaOfEffect
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(InventoryAreaOfEffect) == 1);
|
static_assert(sizeof(InventoryAreaOfEffect) == 1);
|
||||||
|
|
||||||
|
struct InventoryOwner
|
||||||
|
{};
|
||||||
|
|
||||||
struct ItemProcessor
|
struct ItemProcessor
|
||||||
{
|
{
|
||||||
ItemProcessor() = default;
|
ItemProcessor() = default;
|
||||||
@@ -208,4 +214,12 @@ inline void Flecs_Inventory(flecs::world& world)
|
|||||||
|
|
||||||
world.component<ItemProcessor>()
|
world.component<ItemProcessor>()
|
||||||
.member<uint32_t>("ProcessedTicks");
|
.member<uint32_t>("ProcessedTicks");
|
||||||
|
|
||||||
|
world.component<InventoryOwner>();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Inventory_Helper(const flecs::entity& entity, const WorldConfig& config, uint32_t maxPerSlot)
|
||||||
|
{
|
||||||
|
entity.set<Inventory>(Inventory{config.GetItems().size(), maxPerSlot});
|
||||||
|
entity.add<InventoryOwner>();
|
||||||
}
|
}
|
||||||
@@ -48,20 +48,18 @@ inline void Flecs_Resource(flecs::world& world)
|
|||||||
world.component<Renewing>()
|
world.component<Renewing>()
|
||||||
.add<Freezes, ResourceTick>();
|
.add<Freezes, ResourceTick>();
|
||||||
|
|
||||||
// harvesting resource to world inventory
|
// harvesting resource to inventory
|
||||||
world.system<const ResourceInfo, const ResourceTick, WorldInventory>()
|
world.system<const ResourceInfo, const ResourceTick, WorldInventory, Inventory*>()
|
||||||
.kind(flecs::OnUpdate)
|
.kind(flecs::OnUpdate)
|
||||||
.without<Renewing>()
|
.without<Renewing>()
|
||||||
.each([](ResourceInfo info, ResourceTick tick, WorldInventory& worldInventory) {
|
.each([](ResourceInfo info, ResourceTick tick, WorldInventory& worldInventory, Inventory* optionalInventory) {
|
||||||
worldInventory.AddItems(info.ResourceID, tick.Finished());
|
if (tick.Finished())
|
||||||
});
|
{
|
||||||
|
bool pushToLocalInventory = optionalInventory && !optionalInventory->IsFull(info.ResourceID);
|
||||||
// harvesting resource to local inventory
|
|
||||||
world.system<const ResourceInfo, const ResourceTick, Inventory>()
|
if (pushToLocalInventory) optionalInventory->AddItems(info.ResourceID, 1);
|
||||||
.kind(flecs::OnUpdate)
|
else worldInventory.AddItems(info.ResourceID, 1);
|
||||||
.without<Renewing>()
|
}
|
||||||
.each([](ResourceInfo info, ResourceTick tick, Inventory& worldInventory) {
|
|
||||||
worldInventory.AddItems(info.ResourceID, tick.Finished());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// decrease health if ResourceHealth component
|
// decrease health if ResourceHealth component
|
||||||
@@ -73,14 +71,15 @@ inline void Flecs_Resource(flecs::world& world)
|
|||||||
});
|
});
|
||||||
|
|
||||||
// checking if we have to renew the resource
|
// checking if we have to renew the resource
|
||||||
world.system<const ResourceHealth, RenewingTick>()
|
world.system<const ResourceHealth>()
|
||||||
.kind(flecs::OnUpdate)
|
.kind(flecs::OnUpdate)
|
||||||
|
.with<RenewingTick>()
|
||||||
.without<Renewing>()
|
.without<Renewing>()
|
||||||
.each([](flecs::entity entity, ResourceHealth health, RenewingTick& tick) {
|
.each([](flecs::entity entity, ResourceHealth health) {
|
||||||
if (health.Health == 0) {
|
if (health.Health == 0) {
|
||||||
entity.remove<FullyGrown>();
|
entity.remove<FullyGrown>();
|
||||||
entity.add<Renewing>();
|
entity.add<Renewing>();
|
||||||
tick.AccumulatedTick = 0;
|
entity.ensure<RenewingTick>().AccumulatedTick = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -132,3 +132,115 @@ TEST_SUITE("Resource - Health & Renewing") {
|
|||||||
CHECK(inv.GetItemsAmount(woodID) == 2);
|
CHECK(inv.GetItemsAmount(woodID) == 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SUITE("Inventory Entity") {
|
||||||
|
TEST_CASE("resource harvests into local inventory when present") {
|
||||||
|
WorldConfig config{};
|
||||||
|
uint16_t stoneID = config.RegisterItem("Stone");
|
||||||
|
|
||||||
|
WorldInstance world{ config };
|
||||||
|
|
||||||
|
auto entity = world.GetEcsWorld().entity();
|
||||||
|
Resource_Ore_Helper(entity, stoneID, 1);
|
||||||
|
Inventory_Helper(entity, config, 10);
|
||||||
|
|
||||||
|
world.ProcessFrame();
|
||||||
|
|
||||||
|
auto localInv = entity.get<Inventory>();
|
||||||
|
CHECK(localInv.GetItemsAmount(stoneID) == 1);
|
||||||
|
|
||||||
|
auto& worldInv = world.GetEcsWorld().ensure<WorldInventory>();
|
||||||
|
CHECK(worldInv.GetItemsAmount(stoneID) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("overflow goes to world inventory when local is full") {
|
||||||
|
WorldConfig config{};
|
||||||
|
uint16_t stoneID = config.RegisterItem("Stone");
|
||||||
|
|
||||||
|
WorldInstance world{ config };
|
||||||
|
|
||||||
|
auto entity = world.GetEcsWorld().entity();
|
||||||
|
Resource_Ore_Helper(entity, stoneID, 1);
|
||||||
|
Inventory_Helper(entity, config, 2);
|
||||||
|
|
||||||
|
// fill local inventory (max 2)
|
||||||
|
world.ProcessFrame();
|
||||||
|
world.ProcessFrame();
|
||||||
|
|
||||||
|
auto localInv = entity.get<Inventory>();
|
||||||
|
CHECK(localInv.GetItemsAmount(stoneID) == 2);
|
||||||
|
|
||||||
|
auto& worldInv = world.GetEcsWorld().ensure<WorldInventory>();
|
||||||
|
CHECK(worldInv.GetItemsAmount(stoneID) == 0);
|
||||||
|
|
||||||
|
// next item should overflow to world inventory
|
||||||
|
world.ProcessFrame();
|
||||||
|
|
||||||
|
localInv = entity.get<Inventory>();
|
||||||
|
CHECK(localInv.GetItemsAmount(stoneID) == 2);
|
||||||
|
CHECK(worldInv.GetItemsAmount(stoneID) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("inventory tracks multiple item types independently") {
|
||||||
|
WorldConfig config{};
|
||||||
|
uint16_t stoneID = config.RegisterItem("Stone");
|
||||||
|
uint16_t ironID = config.RegisterItem("Iron");
|
||||||
|
|
||||||
|
WorldInstance world{ config };
|
||||||
|
|
||||||
|
auto stoneEntity = world.GetEcsWorld().entity();
|
||||||
|
Resource_Ore_Helper(stoneEntity, stoneID, 1);
|
||||||
|
Inventory_Helper(stoneEntity, config, 5);
|
||||||
|
|
||||||
|
auto ironEntity = world.GetEcsWorld().entity();
|
||||||
|
Resource_Ore_Helper(ironEntity, ironID, 1);
|
||||||
|
Inventory_Helper(ironEntity, config, 5);
|
||||||
|
|
||||||
|
world.ProcessFrame();
|
||||||
|
|
||||||
|
auto stoneInv = stoneEntity.get<Inventory>();
|
||||||
|
CHECK(stoneInv.GetItemsAmount(stoneID) == 1);
|
||||||
|
CHECK(stoneInv.GetItemsAmount(ironID) == 0);
|
||||||
|
|
||||||
|
auto ironInv = ironEntity.get<Inventory>();
|
||||||
|
CHECK(ironInv.GetItemsAmount(ironID) == 1);
|
||||||
|
CHECK(ironInv.GetItemsAmount(stoneID) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("two producers share a separate inventory entity") {
|
||||||
|
WorldConfig config{};
|
||||||
|
uint16_t stoneID = config.RegisterItem("Stone");
|
||||||
|
uint16_t ironID = config.RegisterItem("Iron");
|
||||||
|
|
||||||
|
WorldInstance world{ config };
|
||||||
|
|
||||||
|
// inventory entity (chest/barrel)
|
||||||
|
auto chest = world.GetEcsWorld().entity();
|
||||||
|
Inventory_Helper(chest, config, 10);
|
||||||
|
|
||||||
|
// copy the shared inventory to both producers
|
||||||
|
auto chestInv = chest.get<Inventory>();
|
||||||
|
|
||||||
|
auto stoneProducer = world.GetEcsWorld().entity();
|
||||||
|
Resource_Ore_Helper(stoneProducer, stoneID, 1);
|
||||||
|
stoneProducer.set<Inventory>(chestInv);
|
||||||
|
|
||||||
|
auto ironProducer = world.GetEcsWorld().entity();
|
||||||
|
Resource_Ore_Helper(ironProducer, ironID, 1);
|
||||||
|
ironProducer.set<Inventory>(chestInv);
|
||||||
|
|
||||||
|
world.ProcessFrame();
|
||||||
|
world.ProcessFrame();
|
||||||
|
world.ProcessFrame();
|
||||||
|
|
||||||
|
// all items should be in the shared inventory
|
||||||
|
auto resultInv = chest.get<Inventory>();
|
||||||
|
CHECK(resultInv.GetItemsAmount(stoneID) == 3);
|
||||||
|
CHECK(resultInv.GetItemsAmount(ironID) == 3);
|
||||||
|
|
||||||
|
// world inventory should be empty
|
||||||
|
auto& worldInv = world.GetEcsWorld().ensure<WorldInventory>();
|
||||||
|
CHECK(worldInv.GetItemsAmount(stoneID) == 0);
|
||||||
|
CHECK(worldInv.GetItemsAmount(ironID) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user