code: storage system and mouse picking

isolation_bkp/dynres
Dominik Madarász 2022-08-09 16:46:23 +02:00
parent 4164e62a7b
commit d0d69e7039
34 changed files with 693 additions and 192 deletions

View File

@ -18,6 +18,7 @@ add_executable(eco2d
src/packet.c
src/player.c
src/vehicle.c
src/storage.c
src/signal_handling.c
src/profiler.c
src/debug_ui.c

View File

@ -6,11 +6,13 @@
typedef enum {
// NOTE(zaklaus): Debug
ASSET_EMPTY,
ASSET_BLANK,
ASSET_BUILDMODE_HIGHLIGHT,
// NOTE(zaklaus): entities
ASSET_PLAYER,
ASSET_THING,
ASSET_CHEST,
// NOTE(zaklaus): items
ASSET_DEMO_ICEMAKER,

View File

@ -12,8 +12,10 @@
static asset assets[] = {
ASSET_TEX(ASSET_EMPTY),
ASSET_TEX(ASSET_BLANK),
ASSET_TEX(ASSET_BUILDMODE_HIGHLIGHT),
ASSET_TEX(ASSET_DEMO_ICEMAKER),
ASSET_TEX(ASSET_CHEST),
// NOTE(zaklaus): blocks
ASSET_TEX(ASSET_FENCE),

View File

@ -13,6 +13,8 @@ typedef enum {
RPKIND_PLACE_ERASE_CHANGES,
RPKIND_SPAWN_CIRCLING_DRIVER,
RPKIND_SPAWN_ICEMAKER_ITEM,
RPKIND_SPAWN_CHEST,
RPKIND_SPAWN_BELT,
} replay_kind;
typedef struct {
@ -174,6 +176,8 @@ void ActPlaceIceRink(void);
void ActSpawnCirclingDriver(void);
void ActEraseWorldChanges(void);
void ActSpawnIcemaker(void);
void ActSpawnChest(void);
void ActSpawnBelt(void);
void debug_replay_update(void) {
if (!is_playing) return;
@ -224,6 +228,12 @@ void debug_replay_update(void) {
case RPKIND_SPAWN_ICEMAKER_ITEM:{
ActSpawnIcemaker();
}break;
case RPKIND_SPAWN_CHEST:{
ActSpawnChest();
}break;
case RPKIND_SPAWN_BELT:{
ActSpawnBelt();
}break;
default: {
ZPL_PANIC("unreachable");
}break;

View File

@ -133,6 +133,8 @@ static debug_item items[] = {
{ .kind = DITEM_BUTTON, .name = "erase world changes", .on_click = ActEraseWorldChanges },
{ .kind = DITEM_BUTTON, .name = "spawn circling driver", .on_click = ActSpawnCirclingDriver },
{ .kind = DITEM_BUTTON, .name = "spawn icemaker item", .on_click = ActSpawnIcemaker },
{ .kind = DITEM_BUTTON, .name = "spawn chest", .on_click = ActSpawnChest },
{ .kind = DITEM_BUTTON, .name = "spawn belt", .on_click = ActSpawnBelt },
{
.kind = DITEM_LIST,
.name = "demo npcs",

View File

@ -32,6 +32,30 @@ ActSpawnIcemaker(void) {
debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM);
}
void
ActSpawnChest(void) {
ecs_entity_t e = item_spawn(ASSET_CHEST, 32);
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
debug_replay_special_action(RPKIND_SPAWN_CHEST);
}
void
ActSpawnBelt(void) {
ecs_entity_t e = item_spawn(ASSET_BELT, 999);
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
debug_replay_special_action(RPKIND_SPAWN_BELT);
}
void
ActSpawnCirclingDriver(void) {
ecs_entity_t plr = camera_get().ent_id;

View File

@ -10,10 +10,10 @@
uint64_t entity_spawn(uint16_t class_id) {
ecs_entity_t e = ecs_new(world_ecs(), 0);
ecs_set(world_ecs(), e, Classify, { .id = class_id });
entity_wake(e);
if (class_id != EKIND_SERVER) {
ecs_set(world_ecs(), e, Velocity, {0});
Position *pos = ecs_get_mut(world_ecs(), e, Position);
@ -24,12 +24,12 @@ uint64_t entity_spawn(uint16_t class_id) {
pos->x=350.0f;
pos->y=88.0f;
#endif
librg_entity_track(world_tracker(), e);
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0));
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
}
return (uint64_t)e;
}
@ -49,7 +49,7 @@ void entity_set_position(uint64_t ent_id, float x, float y) {
Position *p = ecs_get_mut(world_ecs(), ent_id, Position);
p->x = x;
p->y = y;
entity_wake(ent_id);
}
@ -67,12 +67,12 @@ void entity_update_action_timers() {
ecs_streaminfo = ecs_query_new(world_ecs(), "components.StreamInfo");
last_update_time = zpl_time_rel();
}
ecs_iter_t it = ecs_query_iter(world_ecs(), ecs_streaminfo);
while (ecs_query_next(&it)) {
StreamInfo *si = ecs_field(&it, StreamInfo, 1);
for (size_t i = 0; i < it.count; i++) {
if (si[i].last_update < zpl_time_rel()) {
si[i].last_update = zpl_time_rel() + si[i].tick_delay;
@ -80,7 +80,7 @@ void entity_update_action_timers() {
}
}
}
last_update_time = zpl_time_rel();
}

View File

@ -12,3 +12,4 @@ void entity_set_position(uint64_t ent_id, float x, float y);
void entity_wake(uint64_t ent_id);
void entity_update_action_timers();
bool entity_can_stream(uint64_t ent_id);

View File

@ -36,6 +36,14 @@ pkt_desc pkt_entity_view_desc[] = {
{ PKT_UINT(entity_view, selected_item) },
{ PKT_ARRAY(entity_view, items) },
{ PKT_UINT(entity_view, pick_ent) },
{ PKT_UINT(entity_view, sel_ent) },
{ PKT_KEEP_IF(entity_view, has_storage_items, true, 3) },
{ PKT_UINT(entity_view, has_storage_items) },
{ PKT_UINT(entity_view, storage_selected_item) },
{ PKT_ARRAY(entity_view, storage_items) },
{ PKT_END },
};

View File

@ -10,6 +10,7 @@ typedef enum {
EKIND_SERVER = 0,
EKIND_PLAYER,
EKIND_ITEM,
EKIND_DEVICE,
EKIND_VEHICLE,
EKIND_DEMO_NPC,
EKIND_MONSTER,
@ -41,10 +42,10 @@ typedef struct entity_view {
float vy;
float tx;
float ty;
float hp;
float max_hp;
// TODO(zaklaus): Find a way to stream dynamic arrays
uint8_t blocks_used;
block_id blocks[256];
@ -52,24 +53,33 @@ typedef struct entity_view {
uint32_t color;
uint8_t is_dirty;
int64_t tex;
// NOTE(zaklaus): vehicle
float heading, theading;
bool inside_vehicle;
// NOTE(zaklaus): items, ...
asset_id asset;
uint32_t quantity;
// NOTE(zaklaus): inventory
uint8_t has_items;
ItemDrop items[ITEMS_INVENTORY_SIZE];
uint8_t selected_item;
// NOTE(zaklaus): storage interface
uint8_t has_storage_items;
ItemDrop storage_items[ITEMS_CONTAINER_SIZE];
uint8_t storage_selected_item;
// NOTE(zaklaus): entity picking
uint64_t pick_ent;
uint64_t sel_ent;
// NOTE(zaklaus): internals
uint8_t layer_id;
uint64_t last_update;
// NOTE(zaklaus): fade in-out effect
entity_transition_effect tran_effect;
float tran_time;

View File

@ -30,7 +30,7 @@ static WORLD_PKT_READER(pkt_reader) {
pkt_header header = {0};
uint32_t ok = pkt_header_decode(&header, data, datalen);
header.udata = udata;
if (ok && header.ok) {
return pkt_handlers[header.id].handler(&header) >= 0;
} else {
@ -65,7 +65,7 @@ static WORLD_PKT_WRITER(mp_cli_pkt_writer) {
void world_viewers_init(uint32_t num_viewers) {
zpl_buffer_init(world_viewers, zpl_heap(), num_viewers);
for (uint32_t i = 0; i < num_viewers; i++) {
zpl_buffer_append(world_viewers, world_view_create(i));
}
@ -125,31 +125,31 @@ float game_time() {
void game_init(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) {
game_mode = play_mode;
game_should_close = false;
#ifndef _DEBUG
const char *host_ip = "lab.zakto.pw";
#else
const char *host_ip = "127.0.0.1";
#endif
uint16_t host_port = (port > 0) ? port : 27000;
if (ip != NULL) {
host_ip = ip;
}
if (game_mode != GAMEKIND_HEADLESS) {
platform_init();
world_viewers_init(num_viewers);
active_viewer = &world_viewers[0];
camera_reset();
}
if (game_mode != GAMEKIND_SINGLE) {
network_init();
}
if (game_mode == GAMEKIND_CLIENT) {
world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer);
network_client_connect(host_ip, host_port);
@ -158,13 +158,13 @@ void game_init(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_
world_setup_pkt_handlers(pkt_reader, game_mode == GAMEKIND_SINGLE ? sp_pkt_writer : mp_pkt_writer);
world_init(seed, chunk_size, chunk_amount);
if (is_dash_enabled) flecs_dash_init();
if (game_mode == GAMEKIND_HEADLESS) {
network_server_start(0, 27000);
//ecs_set_target_fps(world_ecs(), 60);
}
}
if (game_mode == GAMEKIND_SINGLE) {
for (uint32_t i = 0; i < num_viewers; i++) {
pkt_00_init_send(i);
@ -177,24 +177,24 @@ int8_t game_is_networked() {
}
void game_shutdown() {
if (game_mode == GAMEKIND_CLIENT) {
network_client_disconnect();
} else {
world_destroy();
if (game_mode == GAMEKIND_HEADLESS) {
network_server_stop();
}
}
if (game_mode != GAMEKIND_SINGLE) {
network_destroy();
}
if (game_mode != GAMEKIND_HEADLESS) {
world_viewers_destroy();
// TODO(zaklaus): crashes on exit
//platform_shutdown();
}
@ -225,10 +225,10 @@ void game_update() {
}
else {
world_update();
if (game_mode == GAMEKIND_HEADLESS) {
network_server_tick();
static uint64_t ms_report = 2500;
if (ms_report < zpl_time_rel_ms()) {
ms_report = zpl_time_rel_ms() + 5000;
@ -236,7 +236,7 @@ void game_update() {
}
}
}
last_update = zpl_time_rel();
}
@ -250,6 +250,14 @@ void game_action_send_keystate(game_keystate_data *data) {
pkt_send_keystate_send(active_viewer->view_id, data);
}
void game_action_send_blockpos(float mx, float my) {
pkt_send_blockpos data = {
.mx = mx,
.my = my
};
pkt_send_blockpos_send(active_viewer->view_id, &data);
}
void game_request_close() {
game_should_close = true;
if (game_mode != GAMEKIND_HEADLESS) {

View File

@ -35,3 +35,4 @@ entity_view *game_world_view_active_get_entity(uint64_t ent_id);
//~ NOTE(zaklaus): viewer -> host actions
void game_action_send_keystate(game_keystate_data *data);
void game_action_send_blockpos(float mx, float my);

View File

@ -45,6 +45,7 @@ Texture2D texgen_build_anim(asset_id id, int64_t counter) {
Texture2D texgen_build_sprite(asset_id id) {
switch (id) {
case ASSET_BLANK: return GenColorEco(WHITE); break;
case ASSET_BUILDMODE_HIGHLIGHT: return GenColorEco(WHITE); break;
// NOTE(zaklaus): items

View File

@ -19,18 +19,13 @@ void buildmode_draw(void) {
if (inv_is_inside) return;
camera cam = camera_get();
camera old_cam = cam;
Vector2 mpos = GetMousePosition();
entity_view *e = game_world_view_active_get_entity(cam.ent_id);
if (!e) return;
float zoom = renderer_zoom_get();
mpos.x -= screenWidth/2.0f;
mpos.y -= screenHeight/2.0f;
cam.x += mpos.x*(1.0f/zoom);
cam.y += mpos.y*(1.0f/zoom);
cam.x = ((int32_t)cam.x / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.y = ((int32_t)cam.y / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.x += WORLD_BLOCK_SIZE/2.0f;
cam.y += WORLD_BLOCK_SIZE/2.0f;
float mx = 0, my = 0;
platform_get_block_realpos(&mx, &my);
cam.x = (double)mx;
cam.y = (double)my;
// NOTE(zaklaus): Check distance
double dx = old_cam.x - cam.x;

View File

@ -1,19 +1,130 @@
static uint8_t inv_selected_item = 0;
static bool inv_drop_item = false;
typedef struct {
uint8_t selected_item;
bool drop_item;
bool item_is_held;
uint8_t held_item_idx;
ItemDrop held_item;
bool is_inside;
bool storage_action;
bool swap;
uint8_t swap_from;
uint8_t swap_to;
} inv_keystate;
static inv_keystate player_inv = {0};
static inv_keystate storage_inv = {0};
bool inv_is_open = false;
static bool inv_item_is_held = false;
static uint8_t inv_held_item_idx = 0;
static ItemDrop inv_held_item = {0};
static bool inv_swap = false;
static uint8_t inv_swap_from = 0;
static uint8_t inv_swap_to = 0;
bool inv_is_inside = false;
bool inv_is_storage_action = false;
bool inv_swap_storage = false;
void inventory_draw_panel(entity_view *e, bool is_player, float sx, float sy){
if (!e->has_items && is_player)
return;
if (!e->has_storage_items && !is_player)
return;
float x = sx;
float y = sy;
const int32_t grid_size = (is_player) ? (64*3) : (64*4);
const int32_t inv_size = (is_player) ? ITEMS_INVENTORY_SIZE : ITEMS_CONTAINER_SIZE;
const int32_t inv_cols = (is_player) ? 3 : 4;
inv_keystate *inv = (!is_player) ? &storage_inv : &player_inv;
inv_keystate *inv2 = (is_player) ? &storage_inv : &player_inv;
inv->is_inside = check_mouse_area(sx, sy, (float)grid_size, (float)grid_size) != DAREA_OUTSIDE;
inv_is_inside |= inv->is_inside;
for (int32_t i = 0; i < inv_size; i += 1) {
{
debug_area_status area = check_mouse_area(x, y, 64, 64);
Color color = RAYWHITE;
ItemDrop *item = (is_player) ? &e->items[i] : &e->storage_items[i];
if (area == DAREA_HOVER) {
color = YELLOW;
} else if (area == DAREA_PRESS && inv2->item_is_held){
color = VIOLET;
inv_swap_storage = true;
inv_is_storage_action = true;
inv->item_is_held = false;
inv2->item_is_held = false;
inv->selected_item = i;
inv->swap = true;
inv->swap_from = inv2->held_item_idx;
inv->swap_to = i;
} else if (area == DAREA_PRESS && !inv->item_is_held) {
color = VIOLET;
inv->selected_item = i;
inv_is_storage_action = true;
} else if (area == DAREA_PRESS && inv->item_is_held) {
color = VIOLET;
inv->selected_item = i;
inv->item_is_held = false;
inv->swap = true;
inv->swap_from = inv->held_item_idx;
inv->swap_to = i;
inv_is_storage_action = true;
} else if (area == DAREA_HELD && item->quantity > 0 && !inv->item_is_held) {
inv->selected_item = i;
inv->item_is_held = true;
inv->held_item = *item;
inv->held_item_idx = i;
inv_is_storage_action = true;
} else if (i == inv->selected_item) {
color = RED;
}
DrawRectangleLinesEco(x, y, 64, 64, color);
if (item->quantity > 0) {
DrawTexturePro(GetSpriteTexture2D(assets_find(item->kind)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, WHITE);
DrawTextEco(zpl_bprintf("%d", item->quantity), x+5, y+5, 16, RAYWHITE, 0.0f);
}
}
x += 64;
if ((i+1) % inv_cols == 0) {
x = sx;
y += 64;
}
}
if (inv->item_is_held) {
Vector2 mpos = GetMousePosition();
mpos.x -= 32;
mpos.y -= 32;
DrawTexturePro(GetSpriteTexture2D(assets_find(inv->held_item.kind)), ASSET_SRC_RECT(), ASSET_DST_RECT(mpos.x, mpos.y), (Vector2){0.5f,0.5f}, 0.0f, ColorAlpha(WHITE, 0.8f));
DrawTextEco(zpl_bprintf("%d", inv->held_item.quantity), mpos.x, mpos.y, 16, RAYWHITE, 0.0f);
if (!inv->is_inside && IsMouseButtonReleased(MOUSE_LEFT_BUTTON) && !inv2->is_inside) {
inv->drop_item = true;
inv->item_is_held = false;
inv_is_storage_action = true;
}
}
// NOTE(zaklaus): switch it off if is_player
if (is_player)
inv_is_storage_action = false;
}
void inventory_reset_states(inv_keystate *ik) {
ik->drop_item = false;
ik->swap = false;
}
void inventory_draw() {
inv_drop_item = false;
inv_swap = false;
inv_is_storage_action = false;
inv_is_inside = false;
inv_swap_storage = false;
inventory_reset_states(&player_inv);
inventory_reset_states(&storage_inv);
camera cam = camera_get();
entity_view *e = game_world_view_active_get_entity(cam.ent_id);
@ -27,67 +138,6 @@ void inventory_draw() {
return;
}
float sx = screenWidth/2.0f + 128;
float sy = screenHeight/2.0f - 96;
float x = sx;
float y = sy;
inv_is_inside = check_mouse_area(sx, sy, 64*3, 64*3) != DAREA_OUTSIDE;
for (int32_t i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) {
{
debug_area_status area = check_mouse_area(x, y, 64, 64);
Color color = RAYWHITE;
ItemDrop *item = &e->items[i];
if (area == DAREA_HOVER) {
color = YELLOW;
} else if (area == DAREA_PRESS && !inv_item_is_held) {
color = VIOLET;
inv_selected_item = i;
} else if (area == DAREA_PRESS && inv_item_is_held) {
color = VIOLET;
inv_selected_item = i;
inv_item_is_held = false;
inv_swap = true;
inv_swap_from = inv_held_item_idx;
inv_swap_to = i;
} else if (area == DAREA_HELD && item->quantity > 0 && !inv_item_is_held) {
inv_selected_item = i;
inv_held_item = *item;
inv_item_is_held = true;
inv_held_item_idx = i;
} else if (i == inv_selected_item) {
color = RED;
}
DrawRectangleLinesEco(x, y, 64, 64, color);
if (item->quantity > 0) {
DrawTexturePro(GetSpriteTexture2D(assets_find(item->kind)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, WHITE);
DrawTextEco(zpl_bprintf("%d", item->quantity), x+5, y+5, 16, RAYWHITE, 0.0f);
}
}
x += 64;
if ((i+1) % 3 == 0) {
x = sx;
y += 64;
}
}
if (inv_item_is_held) {
Vector2 mpos = GetMousePosition();
mpos.x -= 32;
mpos.y -= 32;
DrawTexturePro(GetSpriteTexture2D(assets_find(inv_held_item.kind)), ASSET_SRC_RECT(), ASSET_DST_RECT(mpos.x, mpos.y), (Vector2){0.5f,0.5f}, 0.0f, ColorAlpha(WHITE, 0.8f));
DrawTextEco(zpl_bprintf("%d", inv_held_item.quantity), mpos.x, mpos.y, 16, RAYWHITE, 0.0f);
debug_area_status area = check_mouse_area(sx, sy, 64*3, 64*3);
if (area == DAREA_OUTSIDE && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
inv_drop_item = true;
inv_item_is_held = false;
}
}
inventory_draw_panel(e, true, screenWidth/2.0f + 128, screenHeight/2.0f - 96);
inventory_draw_panel(e, false, screenWidth/2.0f - 384, screenHeight/2.0f - 128);
}

View File

@ -26,14 +26,14 @@ static inline asset_id item_fix_kind(asset_id id) {
uint64_t item_spawn(asset_id kind, uint32_t qty) {
ecs_entity_t e = entity_spawn(EKIND_ITEM);
ItemDrop *d = ecs_get_mut(world_ecs(), e, ItemDrop);
*d = (ItemDrop){
.kind = item_fix_kind(kind),
.quantity = qty,
.merger_time = 0,
};
return (uint64_t)e;
}
@ -58,7 +58,7 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
asset_id item_asset = blocks_get_asset(l.bid);
item_id item_asset_id = item_find(item_asset);
if (item_asset_id == ASSET_INVALID) return;
// NOTE(zaklaus): If we replace the same item, refund 1 qty and let it replace it
if (item_asset_id == it_id) {
it->quantity++;
@ -73,6 +73,25 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
world_chunk_replace_block(l.chunk_id, l.id, blocks_find(desc->place.kind + (asset_id)udata));
it->quantity--;
}break;
case UKIND_PLACE_ITEM:{
world_block_lookup l = world_block_from_realpos(p.x, p.y);
if (l.is_outer && l.bid > 0) {
return;
}
// NOTE(zaklaus): This is an inner layer block, we can't build over it if it has a collision!
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
ecs_entity_t e = desc->place_item.spawn_proc();
ZPL_ASSERT(world_entity_valid(e));
Position *pos = ecs_get_mut(ecs, e, Position);
pos->x = p.x;
pos->y = p.y;
it->quantity--;
}break;
}
}

View File

@ -31,6 +31,10 @@ typedef struct {
struct {
asset_id id;
} proxy;
struct {
uint64_t (*spawn_proc)();
} place_item;
};
} item_desc;

View File

@ -1,4 +1,5 @@
#include "items.h"
#include "entity_view.h"
#define ITEM_HOLD(asset, qty)\
{\
@ -37,9 +38,22 @@
}\
}
#define ITEM_ENT(asset, qty, proc)\
{\
.kind = asset,\
.usage = UKIND_PLACE_ITEM,\
.max_quantity = qty,\
.place_item = {\
.spawn_proc = proc\
}\
}
#define ITEM_SELF(asset, qty) ITEM_BLOCK(asset, qty, asset)
#define ITEM_SELF_DIR(asset, qty) ITEM_BLOCK_DIR(asset, qty, asset)
// NOTE(zaklaus): access to spawners
#include "storage.h"
static item_desc items[] = {
{
.kind = 0,
@ -55,4 +69,6 @@ static item_desc items[] = {
ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_UP, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_DOWN, ASSET_BELT),
ITEM_ENT(ASSET_CHEST, 32, storage_spawn),
};

View File

@ -2,7 +2,7 @@
#include "compress.h"
#include "cwpack/cwpack.h"
// NOTE(zaklaus): packets
//NOTE(zaklaus): packets
#include "packets/pkt_00_init.h"
#include "packets/pkt_01_welcome.h"
@ -17,6 +17,7 @@ pkt_handler pkt_handlers[] = {
{.id = MSG_ID_01_WELCOME, .handler = pkt_01_welcome_handler},
{.id = MSG_ID_LIBRG_UPDATE, .handler = pkt_send_librg_update_handler},
{.id = MSG_ID_SEND_KEYSTATE, .handler = pkt_send_keystate_handler},
{.id = MSG_ID_SEND_BLOCKPOS, .handler = pkt_send_blockpos_handler},
{.id = MSG_ID_SWITCH_VIEWER, .handler = pkt_switch_viewer_handler},
};

View File

@ -8,6 +8,7 @@ typedef enum {
MSG_ID_01_WELCOME,
MSG_ID_LIBRG_UPDATE,
MSG_ID_SEND_KEYSTATE,
MSG_ID_SEND_BLOCKPOS,
MSG_ID_SWITCH_VIEWER,
MSG_ID_FORCE_UINT16 = UINT16_MAX,
} pkt_messages;

View File

@ -16,9 +16,13 @@ pkt_desc pkt_send_keystate_desc[] = {
{ PKT_UINT(pkt_send_keystate, use) },
{ PKT_UINT(pkt_send_keystate, sprint) },
{ PKT_UINT(pkt_send_keystate, ctrl) },
{ PKT_UINT(pkt_send_keystate, pick) },
{ PKT_UINT(pkt_send_keystate, storage_action) },
{ PKT_UINT(pkt_send_keystate, selected_item) },
{ PKT_UINT(pkt_send_keystate, storage_selected_item) },
{ PKT_UINT(pkt_send_keystate, drop) },
{ PKT_UINT(pkt_send_keystate, swap) },
{ PKT_UINT(pkt_send_keystate, swap_storage) },
{ PKT_UINT(pkt_send_keystate, swap_from) },
{ PKT_UINT(pkt_send_keystate, swap_to) },
{ PKT_UINT(pkt_send_keystate, placement_num) },
@ -26,11 +30,23 @@ pkt_desc pkt_send_keystate_desc[] = {
{ PKT_END },
};
pkt_desc pkt_send_blockpos_desc[] = {
{ PKT_REAL(pkt_send_blockpos, mx) },
{ PKT_REAL(pkt_send_blockpos, my) },
{ PKT_END },
};
size_t pkt_send_keystate_send(uint16_t view_id,
game_keystate_data *data) {
return pkt_world_write(MSG_ID_SEND_KEYSTATE, pkt_table_encode(pkt_send_keystate_desc, PKT_STRUCT_PTR(data)), 1, view_id, NULL, 1);
}
size_t pkt_send_blockpos_send(uint16_t view_id,
pkt_send_blockpos *data){
return pkt_world_write(MSG_ID_SEND_BLOCKPOS, pkt_table_encode(pkt_send_blockpos_desc, PKT_STRUCT_PTR(data)), 1, view_id, NULL, 1);
}
int32_t pkt_send_keystate_handler(pkt_header *header) {
pkt_send_keystate table;
PKT_IF(pkt_msg_decode(header, pkt_send_keystate_desc, pkt_pack_desc_args(pkt_send_keystate_desc), PKT_STRUCT_PTR(&table)));
@ -48,11 +64,15 @@ int32_t pkt_send_keystate_handler(pkt_header *header) {
i->use |= table.use;
i->sprint = table.sprint;
i->ctrl = table.ctrl;
i->selected_item = zpl_clamp(table.selected_item, 0, ITEMS_INVENTORY_SIZE-1);
i->pick |= table.pick;
i->selected_item = zpl_clamp(table.selected_item, 0, ITEMS_CONTAINER_SIZE-1);
i->storage_selected_item = zpl_clamp(table.storage_selected_item, 0, ITEMS_CONTAINER_SIZE-1);
i->drop |= table.drop;
i->swap |= table.swap;
i->swap_from = zpl_clamp(table.swap_from, 0, ITEMS_INVENTORY_SIZE-1);
i->swap_to = zpl_clamp(table.swap_to, 0, ITEMS_INVENTORY_SIZE-1);
i->swap_storage |= table.swap_storage;
i->swap_from = zpl_clamp(table.swap_from, 0, ITEMS_CONTAINER_SIZE-1);
i->swap_to = zpl_clamp(table.swap_to, 0, ITEMS_CONTAINER_SIZE-1);
i->storage_action = table.storage_action;
if (table.placement_num > 0) {
i->num_placements = zpl_clamp(table.placement_num, 0, BUILD_MAX_PLACEMENTS);
@ -67,3 +87,20 @@ int32_t pkt_send_keystate_handler(pkt_header *header) {
return 0;
}
int32_t pkt_send_blockpos_handler(pkt_header *header) {
pkt_send_blockpos table;
PKT_IF(pkt_msg_decode(header, pkt_send_blockpos_desc, pkt_pack_desc_args(pkt_send_blockpos_desc), PKT_STRUCT_PTR(&table)));
ecs_entity_t e = network_server_get_entity(header->udata, header->view_id);
if (!world_entity_valid(e))
return 1;
Input *i = ecs_get_mut(world_ecs(), e, Input);
if (i && !i->is_blocked) {
i->bx = table.mx;
i->by = table.my;
}
return 0;
}

View File

@ -11,9 +11,15 @@ typedef struct {
uint8_t use;
uint8_t sprint;
uint8_t ctrl;
uint8_t pick;
// NOTE(zaklaus): inventory
uint8_t storage_action;
uint8_t selected_item;
uint8_t storage_selected_item;
uint8_t drop;
uint8_t swap;
uint8_t swap_storage;
uint8_t swap_from;
uint8_t swap_to;
@ -22,11 +28,22 @@ typedef struct {
item_placement placements[BUILD_MAX_PLACEMENTS];
} pkt_send_keystate;
typedef struct {
float mx;
float my;
} pkt_send_blockpos;
typedef pkt_send_keystate game_keystate_data;
size_t pkt_send_keystate_send(uint16_t view_id,
game_keystate_data *data);
size_t pkt_send_blockpos_send(uint16_t view_id,
pkt_send_blockpos *data);
extern pkt_desc pkt_send_keystate_desc[];
extern pkt_desc pkt_send_blockpos_desc[];
PKT_HANDLER_PROC(pkt_send_keystate_handler);
PKT_HANDLER_PROC(pkt_send_blockpos_handler);

View File

@ -6,6 +6,7 @@ void platform_shutdown(void);
void platform_request_close(void);
float platform_frametime(void);
uint8_t platform_is_running(void);
void platform_get_block_realpos(float *x, float *y);
float platform_zoom_get(void);

View File

@ -60,16 +60,46 @@ uint8_t platform_is_running() {
return !WindowShouldClose();
}
void platform_get_block_realpos(float *x, float *y){
camera cam = camera_get();
Vector2 mpos = GetMousePosition();
entity_view *e = game_world_view_active_get_entity(cam.ent_id);
if (!e) return;
float zoom = renderer_zoom_get();
mpos.x -= screenWidth/2.0f;
mpos.y -= screenHeight/2.0f;
cam.x += mpos.x*(1.0f/zoom);
cam.y += mpos.y*(1.0f/zoom);
cam.x = ((int32_t)cam.x / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.y = ((int32_t)cam.y / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.x += WORLD_BLOCK_SIZE/2.0f;
cam.y += WORLD_BLOCK_SIZE/2.0f;
if (x) *x = (float)cam.x;
if (y) *y = (float)cam.y;
}
static game_keystate_data last_input_data = {0};
static pkt_send_blockpos last_blockpos_data = {0};
inline static
void platform_input_update_input_frame(game_keystate_data data) {
float mx = 0, my = 0;
platform_get_block_realpos(&mx, &my);
if (mx != last_blockpos_data.mx || my != last_blockpos_data.my){
last_blockpos_data.mx = mx;
last_blockpos_data.my = my;
game_action_send_blockpos(mx, my);
}
// NOTE(zaklaus): Test if there are any changes
if (data.x != last_input_data.x) goto send_data;
if (data.y != last_input_data.y) goto send_data;
if (data.use != last_input_data.use) goto send_data;
if (data.sprint != last_input_data.sprint) goto send_data;
if (data.ctrl != last_input_data.ctrl) goto send_data;
if (data.pick != last_input_data.pick) goto send_data;
if (data.storage_action != last_input_data.storage_action) goto send_data;
if (data.selected_item != last_input_data.selected_item) goto send_data;
if (data.drop != last_input_data.drop) goto send_data;
if (data.swap != last_input_data.swap) goto send_data;
@ -94,7 +124,7 @@ void platform_input() {
// NOTE(zaklaus): keystate handling
{
float x=0.0f, y=0.0f;
uint8_t use, sprint, drop, ctrl;
uint8_t use, sprint, drop, ctrl, pick;
if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f;
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f;
if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f;
@ -103,7 +133,7 @@ void platform_input() {
use = IsKeyPressed(KEY_SPACE);
sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
drop = IsKeyPressed(KEY_G) || inv_drop_item;
drop = IsKeyPressed(KEY_G) || player_inv.drop_item || storage_inv.drop_item;
// NOTE(zaklaus): NEW! mouse movement
Vector2 mouse_pos = GetMousePosition();
@ -118,6 +148,11 @@ void platform_input() {
y = -mouse_pos.y;
}
inv_keystate *inv = (inv_is_storage_action) ? &storage_inv : &player_inv;
// NOTE(zaklaus): don't perform picking if we manipulate our inventories
pick = (inv_is_inside||inv->item_is_held) ? false : IsMouseButtonDown(MOUSE_LEFT_BUTTON);
game_keystate_data in_data = {
.x = x,
.y = y,
@ -126,12 +161,16 @@ void platform_input() {
.use = use,
.sprint = sprint,
.ctrl = ctrl,
.pick = pick,
.drop = drop,
.selected_item = inv_selected_item,
.swap = inv_swap,
.swap_from = inv_swap_from,
.swap_to = inv_swap_to,
.storage_action = inv_is_storage_action,
.selected_item = player_inv.selected_item,
.storage_selected_item = storage_inv.selected_item,
.swap = inv->swap,
.swap_storage = inv_swap_storage,
.swap_from = inv->swap_from,
.swap_to = inv->swap_to,
};
if (build_submit_placements) {
@ -169,6 +208,26 @@ void platform_input() {
#endif
}
void draw_selected_item() {
camera cam = camera_get();
entity_view *oe = game_world_view_active_get_entity(cam.ent_id);
if (oe) {
// NOTE(zaklaus): sel item
entity_view *e = game_world_view_active_get_entity(oe->sel_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.4f));
}else{
// NOTE(zaklaus): hover item
entity_view *e = game_world_view_active_get_entity(oe->pick_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.1f));
}
}
}
}
void platform_render() {
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
@ -184,6 +243,7 @@ void platform_render() {
{
profile (PROF_RENDER) {
renderer_draw();
draw_selected_item();
}
renderer_debug_draw();
{

View File

@ -113,6 +113,11 @@ void DEBUG_draw_entities_low(uint64_t key, entity_view * data) {
float const h = 50;
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(RED, data->tran_time));
}break;
case EKIND_DEVICE:{
float x = data->x - 32.f;
float y = data->y - 32.f;
DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
}break;
default:break;
}
}

View File

@ -0,0 +1,18 @@
#include "storage.h"
#include "entity.h"
#include "entity_view.h"
#include "world/world.h"
#include "modules/components.h"
uint64_t storage_spawn(void) {
ecs_entity_t e = entity_spawn(EKIND_DEVICE);
ItemContainer *storage = ecs_get_mut(world_ecs(), e, ItemContainer);
*storage = (ItemContainer){0};
return (uint64_t)e;
}
void storage_despawn(uint64_t ent_id) {
entity_despawn(ent_id);
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "system.h"
uint64_t storage_spawn(void);
void storage_despawn(uint64_t id);

View File

@ -2,6 +2,7 @@
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <inttypes.h>
@ -13,4 +14,4 @@
#define defer(s,e) for ( \
uint32_t defer_var = (s, 0); \
!defer_var; \
(defer_var += 1), e)
(defer_var += 1), e)

View File

@ -71,8 +71,24 @@ entity_view world_build_entity_view(int64_t e) {
}
const Input *in = ecs_get(world_ecs(), e, Input);
if (in)
if (in){
view.selected_item = in->selected_item;
view.pick_ent = (uint64_t)in->pick_ent;
view.sel_ent = (uint64_t)in->sel_ent;
if (world_entity_valid(in->storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(world_ecs(), in->storage_ent, ItemContainer))){
view.has_storage_items = true;
for (int i = 0; i < ITEMS_CONTAINER_SIZE; i += 1) {
view.storage_items[i] = ic->items[i];
}
view.storage_selected_item = in->storage_selected_item;
}
}
}
}
Chunk *chunk = 0;

View File

@ -252,6 +252,14 @@ int32_t worldgen_test(world_data *wld) {
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
}
for (int i=0; i<RAND_RANGE(128, 964); i++) {
uint64_t e = item_spawn(ASSET_CHEST, 4);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
}
#endif
return WORLD_ERROR_NONE;

View File

@ -13,12 +13,13 @@ ECS_COMPONENT_DECLARE(Vehicle);
ECS_COMPONENT_DECLARE(IsInVehicle);
ECS_COMPONENT_DECLARE(ItemDrop);
ECS_COMPONENT_DECLARE(Inventory);
ECS_COMPONENT_DECLARE(ItemContainer);
ECS_COMPONENT_DECLARE(DemoNPC);
ECS_COMPONENT_DECLARE(StreamInfo);
void ComponentsImport(ecs_world_t *ecs) {
ECS_MODULE(ecs, Components);
ECS_COMPONENT_DEFINE(ecs, Vector2D);
ECS_COMPONENT_DEFINE(ecs, Position);
ECS_COMPONENT_DEFINE(ecs, Velocity);
@ -32,6 +33,7 @@ void ComponentsImport(ecs_world_t *ecs) {
ECS_COMPONENT_DEFINE(ecs, IsInVehicle);
ECS_COMPONENT_DEFINE(ecs, ItemDrop);
ECS_COMPONENT_DEFINE(ecs, Inventory);
ECS_COMPONENT_DEFINE(ecs, ItemContainer);
ECS_COMPONENT_DEFINE(ecs, DemoNPC);
ECS_COMPONENT_DEFINE(ecs, StreamInfo);
}

View File

@ -7,6 +7,7 @@
#endif
#define ITEMS_INVENTORY_SIZE 9
#define ITEMS_CONTAINER_SIZE 16
typedef struct {
float x;
@ -32,15 +33,24 @@ typedef struct {
float y;
float mx;
float my;
float bx;
float by;
uint8_t use;
uint8_t sprint;
uint8_t ctrl;
uint8_t pick;
uint8_t is_blocked;
ecs_entity_t pick_ent;
ecs_entity_t sel_ent;
// NOTE(zaklaus): inventory
ecs_entity_t storage_ent;
uint8_t storage_action;
uint8_t selected_item;
uint8_t storage_selected_item;
uint8_t drop;
uint8_t swap;
uint8_t swap_storage;
uint8_t swap_from;
uint8_t swap_to;
@ -92,10 +102,14 @@ typedef struct {
} ItemDrop;
typedef struct {
ItemDrop items[ITEMS_INVENTORY_SIZE];
ItemDrop items[ITEMS_CONTAINER_SIZE];
float pickup_time;
} Inventory;
typedef struct {
ItemDrop items[ITEMS_CONTAINER_SIZE];
} ItemContainer;
typedef struct {
double last_update;
double tick_delay;
@ -116,6 +130,7 @@ extern ECS_COMPONENT_DECLARE(Vehicle);
extern ECS_COMPONENT_DECLARE(IsInVehicle);
extern ECS_COMPONENT_DECLARE(ItemDrop);
extern ECS_COMPONENT_DECLARE(Inventory);
extern ECS_COMPONENT_DECLARE(ItemContainer);
extern ECS_COMPONENT_DECLARE(DemoNPC);
extern ECS_COMPONENT_DECLARE(StreamInfo);

View File

@ -25,7 +25,7 @@ void IntegratePositions(ecs_iter_t *it) {
profile(PROF_INTEGRATE_POS) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
// NOTE(zaklaus): world bounds
{
@ -33,7 +33,7 @@ void IntegratePositions(ecs_iter_t *it) {
p[i].x = zpl_clamp(p[i].x, 0, w-1);
p[i].y = zpl_clamp(p[i].y, 0, w-1);
}
#if PHY_BLOCK_COLLISION==1
// NOTE(zaklaus): X axis
{
@ -44,7 +44,7 @@ void IntegratePositions(ecs_iter_t *it) {
v[i].x = physics_correction(lookup.ox, v[i].x, bounce);
}
}
// NOTE(zaklaus): Y axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y));
@ -55,10 +55,10 @@ void IntegratePositions(ecs_iter_t *it) {
}
}
#endif
p[i].x += v[i].x * safe_dt(it);
p[i].y += v[i].y * safe_dt(it);
{
debug_v2 a = {p[i].x, p[i].y};
debug_v2 b = {p[i].x+v[i].x, p[i].y+v[i].y};
@ -70,10 +70,10 @@ void IntegratePositions(ecs_iter_t *it) {
void UpdateTrackerPos(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
for (int i = 0; i < it->count; i++){
librg_entity_chunk_set(world_tracker(), it->entities[i], librg_chunk_from_realpos(world_tracker(), p[i].x, p[i].y, 0));
{
debug_v2 a = {p[i].x-2.5f, p[i].y-2.5f};
debug_v2 b = {p[i].x+2.5f, p[i].y+2.5f};
@ -88,7 +88,7 @@ void UpdateTrackerPos(ecs_iter_t *it) {
void HurtOnHazardBlock(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Health *h = ecs_field(it, Health, 2);
for (int i = 0; i < it->count; i++) {
world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y);
if (blocks_get_flags(l.bid) & BLOCK_FLAG_HAZARD) {
@ -107,7 +107,7 @@ void HurtOnHazardBlock(ecs_iter_t *it) {
void RegenerateHP(ecs_iter_t *it) {
Health *h = ecs_field(it, Health, 1);
for (int i = 0; i < it->count; i++) {
if (h[i].pain_time < 0.0f) {
if (h[i].heal_time < 0.0f && h[i].hp < h[i].max_hp) {
@ -126,11 +126,12 @@ void RegenerateHP(ecs_iter_t *it) {
void ResetActivators(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; i++) {
in[i].use = false;
in[i].swap = false;
in[i].drop = false;
in[i].pick = false;
in[i].num_placements = 0;
}
}
@ -138,7 +139,7 @@ void ResetActivators(ecs_iter_t *it) {
void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
@ -147,6 +148,43 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
float vely = blocks_get_vely(lookup.bid);
v[i].x = zpl_lerp(v[i].x, zpl_max(0.0f, zpl_abs(velx))*zpl_sign(velx), PHY_WALK_DRAG*drag*friction*safe_dt(it));
v[i].y = zpl_lerp(v[i].y, zpl_max(0.0f, zpl_abs(vely))*zpl_sign(vely), PHY_WALK_DRAG*drag*friction*safe_dt(it));
if ( zpl_abs(v[i].x) > ENTITY_ACTION_VELOCITY_THRESHOLD
|| zpl_abs(v[i].y) > ENTITY_ACTION_VELOCITY_THRESHOLD) {
entity_wake(it->entities[i]);
}
}
}
#define PLAYER_MAX_INTERACT_RANGE 35.0f
void PlayerClosestInteractable(ecs_iter_t *it){
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; ++i) {
size_t ents_count;
int64_t *ents = world_chunk_fetch_entities_realpos(in[i].bx, in[i].by, &ents_count);
ecs_entity_t closest_pick = 0;
float min_pick = ZPL_F32_MAX;
for (size_t j = 0; j < ents_count; j++) {
const Position *p2 = ecs_get(it->world, ents[j], Position);
if (!p2) continue;
float dx = p2->x - in[i].bx;
float dy = p2->y - in[i].by;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= PLAYER_MAX_INTERACT_RANGE && range < min_pick) {
min_pick = range;
closest_pick = ents[j];
}
}
in[i].pick_ent = closest_pick;
if (in[i].pick)
in[i].sel_ent = closest_pick;
}
}
@ -161,33 +199,36 @@ void DisableWorldEdit(ecs_iter_t *it) {
void SystemsImport(ecs_world_t *ecs) {
ECS_MODULE(ecs, Systems);
ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad);
ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.DemoNPC);
ECS_SYSTEM(ecs, ApplyWorldDragOnVelocity, EcsOnUpdate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, HurtOnHazardBlock, EcsOnUpdate, components.Position, components.Health);
ECS_SYSTEM(ecs, RegenerateHP, EcsOnUpdate, components.Health);
ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity);
ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity);
ECS_SYSTEM(ecs, PlayerClosestInteractable, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, PickItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, DropItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, SwapItems, EcsPostUpdate, components.Input, components.Inventory);
//ECS_SYSTEM(ecs, MergeItems, EcsPostUpdate, components.Position, components.ItemDrop);
ECS_SYSTEM(ecs, UseItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle);
//ECS_SYSTEM(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position);
ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, UpdateTrackerPos, EcsPostUpdate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate);
}

View File

@ -5,20 +5,22 @@
#define ITEM_ATTRACT_RADIUS 75.0f
#define ITEM_ATTRACT_FORCE 0.63f
#define ITEM_CONTAINER_REACH_RADIUS 105.0f
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);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
Position *p2 = ecs_get_mut(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);
@ -34,7 +36,7 @@ void PickItem(ecs_iter_t *it) {
drop->quantity -= picked_count;
item->kind = drop->kind;
entity_wake(ents[j]);
if (drop->quantity == 0)
item_despawn(ents[j]);
break;
@ -57,45 +59,60 @@ 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;
ItemDrop *item = &inv[i].items[in[i].selected_item];
ItemDrop *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;
}
}
ItemDrop *item = &items[in[i].storage_action ? in[i].storage_selected_item : in[i].selected_item];
if (item->quantity <= 0)
continue;
uint32_t dropped_count = item->quantity;
if (in[i].sprint) {
dropped_count /= 2;
} else if (in[i].ctrl) {
dropped_count = dropped_count > 0 ? 1 : 0;
}
if (dropped_count == 0)
continue;
ecs_entity_t te = item_spawn(item->kind, dropped_count);
item->quantity -= dropped_count;
ItemDrop *d = ecs_get_mut(world_ecs(), te, ItemDrop);
*d = (ItemDrop){
.kind = item->kind,
.quantity = dropped_count,
.merger_time = game_time() + ITEM_DROP_MERGER_TIME,
};
Position *ipos = ecs_get_mut(it->world, te, Position);
*ipos = p[i];
Velocity *v = ecs_get_mut(it->world, te, Velocity);
v->x = in[i].mx * 800.0f;
v->y = in[i].my * 800.0f;
inv[i].pickup_time = game_time() + ITEM_DROP_PICKUP_TIME;
in[i].drop = false;
if (item->quantity == 0) {
item->kind = 0;
}
@ -105,24 +122,24 @@ void DropItem(ecs_iter_t *it) {
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);
@ -139,14 +156,56 @@ void MergeItems(ecs_iter_t *it) {
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;
ItemDrop *to = &inv[i].items[in[i].swap_to];
ItemDrop *from = &inv[i].items[in[i].swap_from];
ItemDrop *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;
}
}
ItemDrop *to = 0;
ItemDrop *from = 0;
if (in[i].swap_storage){
in[i].swap_storage = false;
if (in[i].storage_action){
from = &inv[i].items[in[i].swap_from];
to = &items[in[i].swap_to];
}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 = &ic->items[in[i].swap_from];
}else{
continue;
}
}else{
continue;
}
to = &items[in[i].swap_to];
}
}else{
from = &items[in[i].swap_from];
to = &items[in[i].swap_to];
}
ZPL_ASSERT(from && to);
uint16_t to_id = item_find(to->kind);
if (to == from) {
// NOTE(zaklaus): do nothing
} else if (to->kind == from->kind && to->quantity > 0) {
@ -159,7 +218,7 @@ void SwapItems(ecs_iter_t *it) {
swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity);
to->quantity += swapped_count;
from->quantity -= swapped_count;
if (swapped_count == 0) {
ItemDrop tmp = *to;
*to = *from;
@ -179,7 +238,7 @@ void SwapItems(ecs_iter_t *it) {
*to = *from;
*from = tmp;
}
in[i].swap = false;
}
}
@ -188,15 +247,19 @@ 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;
}
ItemDrop *item = &inv[i].items[in[i].selected_item];
if (!item || item->quantity <= 0) continue;
uint16_t item_id = item_find(item->kind);
item_usage usage = item_get_usage(item_id);
if (in[i].use && usage > UKIND_END_PLACE)
item_use(it->world, item, p[i], 0);
else if (in[i].num_placements > 0 && usage < UKIND_END_PLACE) {
@ -216,15 +279,69 @@ void UseItem(ecs_iter_t *it) {
// 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, pos, ofs);
}
in[i].num_placements = 0;
}
entity_wake(it->entities[i]);
}
}
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;
in[i].storage_ent = in[i].pick_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, 2);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
const Position *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 <= ITEM_PICK_RADIUS) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_CONTAINER_SIZE; k += 1) {
ItemDrop *item = &in->items[k];
uint16_t item_id = item_find(item->kind);
if (item_id != ASSET_INVALID && (item->quantity == 0 || (item->quantity != 0 && item->kind == drop->kind)) && item->quantity < item_max_quantity(drop_id)) {
uint32_t picked_count = zpl_max(0, 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;
entity_wake(ents[j]);
entity_wake(it->entities[i]);
if (drop->quantity == 0)
item_despawn(ents[j]);
break;
}
}
}
}
}
}
}