Animate items entering building input ports
This commit is contained in:
@@ -580,10 +580,93 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing
|
||||
|
||||
const Building* b = bs.findBuilding(sid);
|
||||
REQUIRE(b != nullptr);
|
||||
const std::map<ItemType, int>::const_iterator it =
|
||||
// The item was accepted; it may still be travelling inward on the input belt,
|
||||
// so count buffered + in-transit (REQ-MAT-INPUT-INTAKE).
|
||||
REQUIRE(b->pendingInputCount(ItemType{"iron_ore"}) >= 1);
|
||||
}
|
||||
|
||||
// An accepted input item travels inward on its input belt before it becomes usable
|
||||
// stock: it is reserved (counts against the cap) on entry and only enters the
|
||||
// buffer on reaching the tile centre (REQ-MAT-INPUT-INTAKE).
|
||||
TEST_CASE("BuildingSystem: accepted input travels inward before entering the buffer",
|
||||
"[building]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
BeltSystem belts(static_cast<double>(kTickRateHz)); // fast belt: 1 tile/tick
|
||||
int stock = 0;
|
||||
std::mt19937 rng(0);
|
||||
BuildingId nextBuildingId = 1;
|
||||
BuildingSystem bs(cfg, belts,
|
||||
[&nextBuildingId]() { return nextBuildingId++; },
|
||||
[&stock](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
[](const std::string&) -> bool { return true; },
|
||||
rng);
|
||||
|
||||
const BuildingId sid = bs.place(BuildingType::Smelter, QPoint(0, 0), Rotation::East, 0);
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)) + 1, tick);
|
||||
|
||||
belts.placeBelt(QPoint(2, 0), Rotation::West);
|
||||
belts.tryPutItem(QPoint(2, 0), makeItem("iron_ore"));
|
||||
belts.tick();
|
||||
bs.tickBeltPull(); // accepts the item onto the input belt at progress 0.0
|
||||
|
||||
const Building* b = bs.findBuilding(sid);
|
||||
REQUIRE(b != nullptr);
|
||||
// Reserved but not yet consumable: nothing in the buffer, but it counts against
|
||||
// the cap via pendingInputCount.
|
||||
const std::map<ItemType, int>::const_iterator it0 =
|
||||
b->inputBuffer.counts.find(ItemType{"iron_ore"});
|
||||
REQUIRE(it != b->inputBuffer.counts.end());
|
||||
REQUIRE(it->second >= 1);
|
||||
REQUIRE((it0 == b->inputBuffer.counts.end() || it0->second == 0));
|
||||
REQUIRE(b->pendingInputCount(ItemType{"iron_ore"}) == 1);
|
||||
|
||||
// One more pull tick advances the input belt to the centre; the item arrives.
|
||||
bs.tickBeltPull();
|
||||
REQUIRE(b->inputBuffer.counts.at(ItemType{"iron_ore"}) == 1);
|
||||
REQUIRE(b->pendingInputCount(ItemType{"iron_ore"}) == 1);
|
||||
}
|
||||
|
||||
// The acceptance test counts in-transit items, so buffered + reserved never exceeds
|
||||
// the per-material cap; excess items stay on the belt (REQ-MAT-INPUT-INTAKE).
|
||||
TEST_CASE("BuildingSystem: input reservation caps buffered plus in-transit at the cap",
|
||||
"[building]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
BeltSystem belts(static_cast<double>(kTickRateHz)); // fast belt
|
||||
int stock = 0;
|
||||
std::mt19937 rng(0);
|
||||
BuildingId nextBuildingId = 1;
|
||||
BuildingSystem bs(cfg, belts,
|
||||
[&nextBuildingId]() { return nextBuildingId++; },
|
||||
[&stock](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
[](const std::string&) -> bool { return true; },
|
||||
rng);
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::ReprocessingPlant,
|
||||
QPoint(0, 0), Rotation::East, 0);
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)) + 1, tick);
|
||||
|
||||
// Feed scrap via an input belt without ever running production (only pull), so
|
||||
// the buffer fills and stays full. Try to over-fill it well past the cap.
|
||||
belts.placeBelt(QPoint(-1, 0), Rotation::East);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
belts.tryPutItem(QPoint(-1, 0), makeItem("scrap"), Rotation::East);
|
||||
belts.tick();
|
||||
bs.tickBeltPull();
|
||||
}
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
REQUIRE(b != nullptr);
|
||||
const int cap = b->inputBuffer.caps.at(ItemType{"scrap"});
|
||||
REQUIRE(cap > 0);
|
||||
// buffered + in-transit is capped; the plant never over-pulls.
|
||||
REQUIRE(b->pendingInputCount(ItemType{"scrap"}) == cap);
|
||||
// Excess scrap is left stuck on the feeding belt rather than silently dropped.
|
||||
REQUIRE(belts.peekItem(eastPort(QPoint(-1, 0))).has_value());
|
||||
}
|
||||
|
||||
// A smelter auto-selects the matching recipe for whatever it is fed, with no
|
||||
@@ -838,14 +921,12 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
|
||||
bs.tickBeltPull();
|
||||
}
|
||||
|
||||
// Verify scrap is in input buffer.
|
||||
// Verify all five scrap were accepted; some may still be travelling inward on
|
||||
// the input belt (REQ-MAT-INPUT-INTAKE), so count buffered + in-transit.
|
||||
{
|
||||
const Building* b = bs.findBuilding(id);
|
||||
REQUIRE(b != nullptr);
|
||||
const std::map<ItemType, int>::const_iterator it =
|
||||
b->inputBuffer.counts.find(ItemType{"scrap"});
|
||||
REQUIRE(it != b->inputBuffer.counts.end());
|
||||
REQUIRE(it->second == 5);
|
||||
REQUIRE(b->pendingInputCount(ItemType{"scrap"}) == 5);
|
||||
}
|
||||
|
||||
// Run production cycle (3s = 90 ticks + 1 for the completion tick).
|
||||
|
||||
Reference in New Issue
Block a user