Files
factory-hole-core/include/Components/Chute.hpp
Connor cf20ed827e chute
2026-02-16 16:55:17 +09:00

149 lines
3.8 KiB
C++

#pragma once
#include <deque>
#include <cmath>
#include <algorithm>
#include <vector>
#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<ChuteItem> ItemsInChute{};
uint16_t TicksToReachEnd{};
uint16_t TickCounter{};
};
public:
Chute() = default;
Chute(const std::vector<Vector2>& positions, const ChuteConfig& config = {})
: Data{static_cast<int>(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<float>(std::abs(dy));
velocity = std::max(velocity, config.MinSpeed);
float distance = std::max(1.0f, std::sqrt(static_cast<float>(dx * dx + dy * dy)));
float linkTicks = distance / velocity;
Data[i - 1].RelativeX = static_cast<int8_t>(dx);
Data[i - 1].RelativeY = static_cast<int8_t>(dy);
Data[i - 1].Tick = static_cast<uint16_t>(std::ceil(linkTicks));
totalTicks += linkTicks;
}
Data.GetMetaData()->TicksToReachEnd = static_cast<uint16_t>(std::ceil(totalTicks));
}
void PushItem(Item item)
{
auto* meta = Data.GetMetaData();
meta->ItemsInChute.push_back(ChuteItem{ .ItemInfo = item, .ChuteEntered = meta->TickCounter });
}
public:
SharedBuffer<ChuteLink, ChuteData> Data{};
};
inline void Flecs_Chute(flecs::world& world)
{
world.component<ChuteInventoryInput>().is_a<Inventory>();
world.component<ChuteInventoryOutput>().is_a<Inventory>();
world.component<ChuteConfig>()
.member<float>("Gravity")
.member<float>("MinSpeed");
world.component<Chute>();
// tick the chute counter
world.system<Chute>("Chute Tick")
.kind(flecs::PreUpdate)
.each([](Chute& chute) {
chute.Data.GetMetaData()->TickCounter++;
});
// pull items from input inventory into chute
world.system<Chute, ChuteInventoryInput>("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, ChuteInventoryOutput, WorldInventory>("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<Vector2>& positions,
const Inventory& sourceInventory,
const Inventory& destInventory,
const ChuteConfig& config = {})
{
entity.set<Chute>(Chute{positions, config});
entity.set<ChuteInventoryInput>(ChuteInventoryInput{sourceInventory});
entity.set<ChuteInventoryOutput>(ChuteInventoryOutput{destInventory});
}