eco2d/code/foundation/src/systems/modules/system_logistics.c

131 lines
4.2 KiB
C

#include "models/crafting.h"
static inline
asset_id FetchAssetAtPos(float x, float y) {
world_block_lookup lookup = world_block_from_realpos(x, y);
if (lookup.is_outer) {
return blocks_get_asset(lookup.bid);
}
return ASSET_INVALID;
}
// NOTE(zaklaus): Expects float[4] dx, dy
static inline
uint8_t CheckForNearbyBelts(Position *p, float *dx, float *dy) {
asset_id bid = ASSET_INVALID;
float o = WORLD_BLOCK_SIZE;
uint8_t nodes = 0x0;
// up
bid = FetchAssetAtPos(p->x + 0, p->y - o);
if (bid == ASSET_BELT_UP) {
dx[0] = 0;
dy[0] = -o;
nodes |= ZPL_BIT(0);
}
// down
bid = FetchAssetAtPos(p->x + 0, p->y + o);
if (bid == ASSET_BELT_DOWN) {
dx[1] = 0;
dy[1] = o;
nodes |= ZPL_BIT(1);
}
// left
bid = FetchAssetAtPos(p->x - o, p->y + 0);
if (bid == ASSET_BELT_LEFT) {
dx[2] = -o;
dy[2] = 0;
nodes |= ZPL_BIT(2);
}
// right
bid = FetchAssetAtPos(p->x + o, p->y + 0);
if (bid == ASSET_BELT_RIGHT) {
dx[3] = o;
dy[3] = 0;
nodes |= ZPL_BIT(3);
}
return nodes;
}
void PushItemsOnNodes(ecs_iter_t *it) {
ItemContainer *storage = ecs_field(it, ItemContainer, 1);
Position *p = ecs_field(it, Position, 2);
Device *d = ecs_field(it, Device, 3);
ItemRouter *r = ecs_field(it, ItemRouter, 4);
for (int i = 0; i < it->count; i++) {
// TODO(zaklaus): Cache output nodes so we avoid
// calling world_block_from_realpos each time.
// We need a way to refer to specific blocks in the world so we can do easy block ID checks
// and re-build the cache when a change is detected.
Producer *producer = ecs_get_mut_if(it->world, it->entities[i], Producer);
if (producer) {
if (producer->push_filter == PRODUCER_PUSH_NONE)
continue;
}
float push_dx[4], push_dy[4];
uint8_t nodes = CheckForNearbyBelts(&p[i], push_dx, push_dy);
uint8_t num_nodes = (uint8_t)zpl_count_set_bits(nodes);
if (num_nodes == 0) {
// NOTE(zaklaus): We don't have any output nodes yet.
continue;
}
for (int j = 0; j < ITEMS_CONTAINER_SIZE; j++) {
ecs_entity_t item_slot_ent = storage[i].items[j];
if (item_slot_ent == 0) continue;
Item *item = item_get_data(item_slot_ent);
if (!item) continue;
if (producer) {
if (producer->push_filter == PRODUCER_PUSH_ANY) {
if (craft_is_reagent_used_in_producer(item->kind, d[i].asset)) {
// NOTE(zaklaus): this is an input reagent, keep it
continue;
}
}
else if (producer->push_filter == PRODUCER_PUSH_PRODUCT) {
if (producer->target_item != item->kind) {
// NOTE(zaklaus): this is not a producer's output, keep it
continue;
}
}
}
while (item->quantity > 0 && num_nodes > 0) {
// NOTE(zaklaus): Use a rolling counter to select an output node.
while (!(nodes & (1 << r[i].counter)) && r[i].counter < 4) ++r[i].counter;
if (r[i].counter > 3) {
r[i].counter = 0;
continue;
}
uint64_t e = item_spawn(item->kind, zpl_min(r->push_qty, item->quantity));
entity_set_position(e, p[i].x + push_dx[r[i].counter], p[i].y + push_dy[r[i].counter]);
Velocity *e_vel = ecs_get_mut(it->world, e, Velocity);
e_vel->x = push_dx[r[i].counter];
e_vel->y = push_dy[r[i].counter];
item->quantity -= zpl_min(r->push_qty, item->quantity);
--num_nodes;
++r[i].counter;
if (item->quantity == 0) {
item_despawn(item_slot_ent);
storage[i].items[j] = 0;
}
}
}
}
}