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

421 lines
16 KiB
C

#include "models/items.h"
void PickItem(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (inv[i].pickup_time > game_time()) continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
bool picked = false;
for (size_t j = 0; j < ents_count; j++) {
Item *drop = 0;
uint64_t ent_id = ents[j];
if ((drop = ecs_get_mut_if(it->world, ent_id, Item))) {
Position *p2 = ecs_get_mut(it->world, ent_id, Position);
Velocity *v2 = ecs_get_mut(it->world, ent_id, Velocity);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= game_rules.item_pick_radius) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_INVENTORY_SIZE; k += 1) {
ecs_entity_t item_slot_ent = inv[i].items[k];
Item *item = item_get_data(item_slot_ent);
uint16_t item_id = item ? item_find(item->kind) : 0;
if (!item || (item_id != ASSET_INVALID && (item->kind == drop->kind && item->durability == drop->durability) && item->quantity < item_max_quantity(drop_id))) {
if (item) {
uint32_t picked_count = zpl_max(0, (int32_t)drop->quantity);
picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
item->quantity += picked_count;
drop->quantity -= picked_count;
item->kind = drop->kind;
if (drop->quantity == 0)
item_despawn(ent_id);
} else if (!world_entity_valid(item_slot_ent)) {
entity_wake(ent_id);
item_show(ent_id, false);
inv[i].items[k] = ent_id;
}
picked = true;
break;
}
}
} else if (range <= game_rules.item_attract_radius) {
v2->x = (p[i].x - p2->x) * game_rules.item_attract_force;
v2->y = (p[i].y - p2->y) * game_rules.item_attract_force;
}
}
if (picked) break;
}
}
}
void CraftItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; i++) {
if (in[i].craft_item == 0) continue;
if (world_entity_valid(in[i].storage_ent)){
Producer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, Producer))){
ic->target_item = in[i].craft_item;
if (ic->pending_task == PRODUCER_CRAFT_WAITING) {
ic->pending_task = PRODUCER_CRAFT_ENQUEUED;
}
in[i].craft_item = 0;
}
}
}
}
void DropItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].drop) continue;
ecs_entity_t *items = inv[i].items;
if (in[i].storage_action){
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
items = ic->items;
}else{
continue;
}
}else{
continue;
}
}
uint8_t slot_id = in[i].storage_action ? in[i].storage_selected_item : in[i].selected_item;
ecs_entity_t item_slot_ent = items[slot_id];
Item *item = item_get_data(item_slot_ent);
if (!item || item->quantity <= 0)
continue;
uint32_t dropped_count = item->quantity;
if (in[i].sprint) {
dropped_count /= 2;
} else if (in[i].ctrl) {
dropped_count = item->quantity-1;
}
if (dropped_count == 0)
continue;
item_show(item_slot_ent, true);
Position *ipos = ecs_get_mut(it->world, item_slot_ent, Position);
entity_set_position(item_slot_ent, p[i].x, p[i].y);
Velocity *v = ecs_get_mut(it->world, item_slot_ent, Velocity);
v->x = in[i].mx * 800.0f;
v->y = in[i].my * 800.0f;
inv[i].pickup_time = game_time() + game_rules.item_drop_pickup_time;
in[i].drop = false;
items[slot_id] = 0;
if (item->quantity - dropped_count > 0) {
item->quantity -= dropped_count;
ecs_entity_t te = item_spawn(item->kind, dropped_count);
item_show(te, false);
items[slot_id] = te;
}
}
}
// void MergeItems(ecs_iter_t *it) {
// Position *p = ecs_field(it, Position, 1);
// ItemDrop *id = ecs_field(it, ItemDrop, 2);
// for (int i = 0; i < it->count; i += 1) {
// ItemDrop *item = &id[i];
// if (item->merger_time < game_time())
// continue;
// size_t ents_count;
// int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 1);
// for (size_t j = 0; j < ents_count; j++) {
// ItemDrop *drop = 0;
// if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
// if (drop->kind != item->kind || (ecs_entity_t)ents[j] == it->entities[i] || drop->quantity == 0 || item->quantity == 0)
// continue;
// Position const* p2 = ecs_get(it->world, ents[j], Position);
// float dx = p2->x - (p[i].x);
// float dy = p2->y - (p[i].y);
// float range = zpl_sqrt(dx*dx + dy*dy);
// if (range <= game_rules.item_merger_radius) {
// drop->quantity += item->quantity;
// item_despawn(it->entities[i]);
// break;
// }
// }
// }
// }
// }
void SwapItems(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Inventory *inv = ecs_field(it, Inventory, 2);
for (int i = 0; i < it->count; i++) {
if (!in[i].swap) continue;
ecs_entity_t *items = inv[i].items;
if (in[i].storage_action){
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
items = ic->items;
}else{
continue;
}
}else{
continue;
}
}
ecs_entity_t *from_ent = 0;
ecs_entity_t *to_ent = 0;
Item *to = 0;
Item *from = 0;
if (in[i].swap_storage){
in[i].swap_storage = false;
if (in[i].storage_action){
from_ent = &inv[i].items[in[i].swap_from];
to_ent = &items[in[i].swap_to];
from = item_get_data(*from_ent);
to = item_get_data(*to_ent);
}else{
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
from_ent = &ic->items[in[i].swap_from];
from = item_get_data(*from_ent);
}else{
continue;
}
}else{
continue;
}
to_ent = &items[in[i].swap_to];
to = item_get_data(*to_ent);
}
}else{
from_ent = &items[in[i].swap_from];
to_ent = &items[in[i].swap_to];
from = item_get_data(*from_ent);
to = item_get_data(*to_ent);
}
if (!from) continue;
uint16_t to_id = to ? item_find(to->kind) : ASSET_EMPTY;
asset_id to_kind = to ? to->kind : ASSET_EMPTY;
if (to_ent == from_ent) {
// NOTE(zaklaus): do nothing
} else if (to_kind == from->kind && to->quantity > 0) {
uint32_t swapped_count = from->quantity;
if (in[i].sprint) {
swapped_count /= 2;
} else if (in[i].ctrl) {
swapped_count = 1;
}
swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity);
to->quantity += swapped_count;
from->quantity -= swapped_count;
if (from->quantity == 0) {
item_despawn(*from_ent);
*from_ent = 0;
}
} else if ((in[i].ctrl || in[i].sprint) && to == 0 && from->quantity > 0) {
// NOTE(zaklaus): item split
uint32_t split_count = from->quantity / 2;
if (in[i].ctrl) {
split_count = 1;
}
if (from->quantity - split_count == 0) {
continue;
}
ecs_entity_t te = item_spawn(from->kind, split_count);
item_show(te, false);
*to_ent = te;
from->quantity -= split_count;
} else {
ecs_entity_t tmp = *to_ent;
*to_ent = *from_ent;
*from_ent = tmp;
}
in[i].swap = false;
}
}
void UseItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].use && !in[i].num_placements) continue;
if (in[i].storage_action){
continue;
}
ecs_entity_t item_ent = 0;
Item *item = NULL;
uint16_t item_id = 0;
item_usage usage = UKIND_DELETE;
if (!in[i].deletion_mode){
item_ent = inv[i].items[in[i].selected_item];
item = item_get_data(item_ent);
item_id = item ? item_find(item->kind) : ASSET_EMPTY;
usage = item_get_usage(item_id);
if (!item || item->quantity <= 0) continue;
}
if (!in[i].use && usage == UKIND_DELETE){
for (size_t j = 0; j < in[i].num_placements; j++) {
world_chunk_destroy_block(in[i].placements_x[j], in[i].placements_y[j], true);
}
}
else if (in[i].use && usage > UKIND_END_PLACE)
item_use(it->world, item_ent, item, p[i], 0);
else if (in[i].num_placements > 0 && usage < UKIND_END_PLACE) {
asset_id ofs = 0;
if (item_get_place_directional(item_id) && in[i].num_placements >= 2) {
float p1x = in[i].placements_x[0];
float p1y = in[i].placements_y[0];
float p2x = in[i].placements_x[1];
float p2y = in[i].placements_y[1];
float sx = zpl_sign0(p2x-p1x);
float sy = zpl_sign0(p2y-p1y);
ofs = (sx < 0.0f) ? 1 : 2;
if (sx == 0.0f) {
ofs = (sy < 0.0f) ? 3 : 4;
}
} else if(item_get_place_directional(item_id)) {
// NOTE(zaklaus): ensure we pick the first variant
ofs = 1;
}
for (size_t j = 0; j < in[i].num_placements; j++) {
Position pos = {.x = in[i].placements_x[j], .y = in[i].placements_y[j]};
item_use(it->world, item_ent, item, pos, ofs);
}
in[i].num_placements = 0;
}
entity_wake(it->entities[i]);
if (usage != UKIND_DELETE && item->quantity == 0) {
item_despawn(item_ent);
inv[i].items[in[i].selected_item] = 0;
}
}
}
void InspectContainers(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; ++i) {
if (!in[i].pick) continue;
if ((in[i].sel_ent && ecs_get(it->world, in[i].sel_ent, ItemContainer)) || !in[i].sel_ent)
in[i].storage_ent = in[i].sel_ent;
}
}
void HarvestIntoContainers(ecs_iter_t *it) {
ItemContainer *in = ecs_field(it, ItemContainer, 1);
Position *p = ecs_field(it, Position, 2);
for (int i = 0; i < it->count; ++i) {
// NOTE(zaklaus): find any item
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 0);
bool picked = false;
for (size_t j = 0; j < ents_count; j++) {
Item *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], Item))) {
Position *p2 = ecs_get_mut(it->world, ents[j], Position);
uint64_t ent_id = ents[j];
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= game_rules.item_pick_radius) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_CONTAINER_SIZE; k += 1) {
uint64_t item_slot_ent = in[i].items[k];
Item *item = item_get_data(item_slot_ent);
uint16_t item_id = item ? item_find(item->kind) : 0;
if (!item || (item_id != ASSET_INVALID && (item->kind == drop->kind && item->durability == drop->durability) && item->quantity < item_max_quantity(drop_id))) {
if (item) {
uint32_t picked_count = zpl_max(0, (int32_t)drop->quantity);
picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
item->quantity += picked_count;
drop->quantity -= picked_count;
if (drop->quantity == 0) {
item_despawn(ent_id);
}
} else if (!world_entity_valid(item_slot_ent)) {
entity_wake(ent_id);
item_show(ent_id, false);
in[i].items[k] = ent_id;
}
entity_wake(it->entities[i]);
picked = true;
break;
}
}
}
}
if (picked) break;
}
}
}
void ThrowItemsOut(ecs_iter_t *it) {
ItemContainer *storage = ecs_field(it, ItemContainer, 1);
Position *p = ecs_field(it, Position, 2);
for (int i = 0; i < it->count; i++) {
for (int j = 0; j < ITEMS_CONTAINER_SIZE; j++) {
ecs_entity_t item_slot_ent = storage[i].items[j];
Item *item = item_get_data(item_slot_ent);
if (!item) continue;
item_show(item_slot_ent, true);
entity_set_position(item_slot_ent, p[i].x, p[i].y);
}
}
}