Files
factory-hole-core/tests/Components/test_Resource.cpp
2026-02-16 11:27:01 +09:00

247 lines
6.8 KiB
C++

#include <doctest/doctest.h>
#include "Components/Configs/WorldConfig.hpp"
#include "Core/WorldInstance.h"
#include "Components/Resource.hpp"
#include "Components/Inventory.hpp"
TEST_SUITE("Resource") {
TEST_CASE("basic resource gathering produces item after one tick") {
WorldConfig config{};
uint16_t stoneID = config.RegisterItem("Stone");
WorldInstance world{ config };
// Create a resource entity with gatherTicks = 1
auto entity = world.GetEcsWorld().entity();
Resource_Ore_Helper(entity, stoneID, 1);
world.ProcessFrame();
auto& inv = world.GetEcsWorld().ensure<WorldInventory>();
CHECK(inv.GetItemsAmount(stoneID) >= 1);
}
TEST_CASE("resource with 20 tick gather time produces item after 20 ticks") {
WorldConfig config{};
uint16_t stoneID = config.RegisterItem("Stone");
WorldInstance world{ config };
auto entity = world.GetEcsWorld().entity();
Resource_Ore_Helper(entity, stoneID, 20);
for (int i = 0; i < 19; ++i)
world.ProcessFrame();
auto& inv = world.GetEcsWorld().ensure<WorldInventory>();
CHECK(inv.GetItemsAmount(stoneID) == 0);
world.ProcessFrame();
CHECK(inv.GetItemsAmount(stoneID) >= 1);
}
}
TEST_SUITE("Resource - Health & Renewing") {
TEST_CASE("tree resource loses health each gather cycle") {
WorldConfig config{};
uint16_t woodID = config.RegisterItem("Wood");
WorldInstance world{ config };
auto entity = world.GetEcsWorld().entity();
Resource_Tree_Helper(entity, woodID, 1, 3, 5);
world.ProcessFrame();
auto& inv = world.GetEcsWorld().ensure<WorldInventory>();
CHECK(inv.GetItemsAmount(woodID) == 1);
auto health = entity.get<ResourceHealth>();
CHECK(health.Health == 2);
}
TEST_CASE("tree enters renewing state when health reaches 0") {
WorldConfig config{};
uint16_t woodID = config.RegisterItem("Wood");
WorldInstance world{ config };
auto entity = world.GetEcsWorld().entity();
Resource_Tree_Helper(entity, woodID, 1, 2, 5);
// 2 gather cycles to deplete health
world.ProcessFrame();
world.ProcessFrame();
auto& inv = world.GetEcsWorld().ensure<WorldInventory>();
CHECK(inv.GetItemsAmount(woodID) == 2);
CHECK(entity.has<Renewing>());
// one more tick should not produce items while renewing
world.ProcessFrame();
CHECK(inv.GetItemsAmount(woodID) == 2);
}
TEST_CASE("tree restores health after renewal period") {
WorldConfig config{};
uint16_t woodID = config.RegisterItem("Wood");
WorldInstance world{ config };
auto entity = world.GetEcsWorld().entity();
Resource_Tree_Helper(entity, woodID, 1, 1, 5);
// 1 gather cycle depletes health
world.ProcessFrame();
CHECK(entity.has<Renewing>());
// 5 ticks for renewal
for (int i = 0; i < 5; ++i)
world.ProcessFrame();
CHECK_FALSE(entity.has<Renewing>());
CHECK(entity.has<FullyGrown>());
auto health = entity.get<ResourceHealth>();
CHECK(health.Health == health.MaxHealth);
}
TEST_CASE("tree resumes gathering after renewal") {
WorldConfig config{};
uint16_t woodID = config.RegisterItem("Wood");
WorldInstance world{ config };
auto entity = world.GetEcsWorld().entity();
Resource_Tree_Helper(entity, woodID, 1, 1, 5);
// deplete
world.ProcessFrame();
auto& inv = world.GetEcsWorld().ensure<WorldInventory>();
CHECK(inv.GetItemsAmount(woodID) == 1);
// renew (5 ticks)
for (int i = 0; i < 5; ++i)
world.ProcessFrame();
CHECK_FALSE(entity.has<Renewing>());
// should gather again
world.ProcessFrame();
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);
}
}