#pragma once #include #include #include #include #include "flecs.h" #include "Types/Item.hpp" #include "Components/Misc.hpp" #include "Components/Inventory.hpp" #include "Util/SharedBuffer.h" struct ChuteConfig { float Gravity{ 1.0f }; float MinSpeed{ 0.5f }; }; struct ChuteInventoryInput : public Inventory {}; struct ChuteInventoryOutput : public Inventory {}; struct Chute { struct ChuteLink { int8_t RelativeX{}; int8_t RelativeY{}; uint16_t Tick{}; }; struct ChuteItem { Item ItemInfo{}; uint16_t ChuteEntered{}; // use TickCounter - ChuteEntered to get the time it has been in the chute }; struct ChuteData { std::deque ItemsInChute{}; uint16_t TicksToReachEnd{}; uint16_t TickCounter{}; }; public: Chute() = default; Chute(const std::vector& positions, const ChuteConfig& config = {}) : Data{static_cast(positions.size() - 1), ChuteData{}} { float velocity = 0.0f; float totalTicks = 0.0f; for (size_t i = 1; i < positions.size(); ++i) { int dx = positions[i].X - positions[i - 1].X; int dy = positions[i].Y - positions[i - 1].Y; // accumulate velocity from vertical drop (dy <= 0, so -dy >= 0) velocity += config.Gravity * static_cast(std::abs(dy)); velocity = std::max(velocity, config.MinSpeed); float distance = std::max(1.0f, std::sqrt(static_cast(dx * dx + dy * dy))); float linkTicks = distance / velocity; Data[i - 1].RelativeX = static_cast(dx); Data[i - 1].RelativeY = static_cast(dy); Data[i - 1].Tick = static_cast(std::ceil(linkTicks)); totalTicks += linkTicks; } Data.GetMetaData()->TicksToReachEnd = static_cast(std::ceil(totalTicks)); } void PushItem(Item item) { auto* meta = Data.GetMetaData(); meta->ItemsInChute.push_back(ChuteItem{ .ItemInfo = item, .ChuteEntered = meta->TickCounter }); } public: SharedBuffer Data{}; }; inline void Flecs_Chute(flecs::world& world) { world.component().is_a(); world.component().is_a(); world.component() .member("Gravity") .member("MinSpeed"); world.component(); // tick the chute counter world.system("Chute Tick") .kind(flecs::PreUpdate) .each([](Chute& chute) { chute.Data.GetMetaData()->TickCounter++; }); // pull items from input inventory into chute world.system("Chute Input") .kind(flecs::OnUpdate) .each([](Chute& chute, ChuteInventoryInput& input) { for (uint16_t i = 0; i < input.Slots.GetSize(); ++i) { while (input.Slots[i] > 0) { input.Slots[i]--; chute.PushItem(Item{ i }); } } }); // pop items that have reached the end and deposit into output inventory world.system("Chute Output") .kind(flecs::OnUpdate) .each([](Chute& chute, ChuteInventoryOutput& output, WorldInventory& worldInv) { auto* meta = chute.Data.GetMetaData(); while (!meta->ItemsInChute.empty()) { auto& front = meta->ItemsInChute.front(); uint16_t elapsed = meta->TickCounter - front.ChuteEntered; if (elapsed < meta->TicksToReachEnd) break; uint16_t itemID = front.ItemInfo.ItemID; meta->ItemsInChute.pop_front(); if (!output.IsFull(itemID)) output.AddItems(itemID, 1); else worldInv.AddItems(itemID, 1); } }); } inline void Chute_Helper(const flecs::entity& entity, const std::vector& positions, const Inventory& sourceInventory, const Inventory& destInventory, const ChuteConfig& config = {}) { entity.set(Chute{positions, config}); entity.set(ChuteInventoryInput{sourceInventory}); entity.set(ChuteInventoryOutput{destInventory}); }