diff --git a/code/game/CMakeLists.txt b/code/game/CMakeLists.txt index d1b0ad7..4734aa0 100644 --- a/code/game/CMakeLists.txt +++ b/code/game/CMakeLists.txt @@ -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 diff --git a/code/game/src/assets.h b/code/game/src/assets.h index 5d6e69e..c74b1f2 100644 --- a/code/game/src/assets.h +++ b/code/game/src/assets.h @@ -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, diff --git a/code/game/src/assets_list.c b/code/game/src/assets_list.c index 3732807..b0b2330 100644 --- a/code/game/src/assets_list.c +++ b/code/game/src/assets_list.c @@ -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), diff --git a/code/game/src/debug_replay.c b/code/game/src/debug_replay.c index 60da606..280f3ae 100644 --- a/code/game/src/debug_replay.c +++ b/code/game/src/debug_replay.c @@ -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; diff --git a/code/game/src/debug_ui.c b/code/game/src/debug_ui.c index 1a91e84..d6001ee 100644 --- a/code/game/src/debug_ui.c +++ b/code/game/src/debug_ui.c @@ -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", diff --git a/code/game/src/debug_ui_actions.c b/code/game/src/debug_ui_actions.c index d559cea..88fa8a1 100644 --- a/code/game/src/debug_ui_actions.c +++ b/code/game/src/debug_ui_actions.c @@ -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; diff --git a/code/game/src/entity.c b/code/game/src/entity.c index ad71a26..d35741d 100644 --- a/code/game/src/entity.c +++ b/code/game/src/entity.c @@ -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(); } diff --git a/code/game/src/entity.h b/code/game/src/entity.h index 941825f..d03a915 100644 --- a/code/game/src/entity.h +++ b/code/game/src/entity.h @@ -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); + diff --git a/code/game/src/entity_view.c b/code/game/src/entity_view.c index 3702358..685bc0d 100644 --- a/code/game/src/entity_view.c +++ b/code/game/src/entity_view.c @@ -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 }, }; diff --git a/code/game/src/entity_view.h b/code/game/src/entity_view.h index 85c3620..a2422b4 100644 --- a/code/game/src/entity_view.h +++ b/code/game/src/entity_view.h @@ -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; diff --git a/code/game/src/game.c b/code/game/src/game.c index 547c231..18baef3 100644 --- a/code/game/src/game.c +++ b/code/game/src/game.c @@ -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) { diff --git a/code/game/src/game.h b/code/game/src/game.h index 11949bb..81e9667 100644 --- a/code/game/src/game.h +++ b/code/game/src/game.h @@ -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); diff --git a/code/game/src/gen/texgen.c b/code/game/src/gen/texgen.c index 3246510..81e2994 100644 --- a/code/game/src/gen/texgen.c +++ b/code/game/src/gen/texgen.c @@ -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 diff --git a/code/game/src/gui/build_mode.c b/code/game/src/gui/build_mode.c index cf74937..eb80e03 100644 --- a/code/game/src/gui/build_mode.c +++ b/code/game/src/gui/build_mode.c @@ -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; diff --git a/code/game/src/gui/inventory.c b/code/game/src/gui/inventory.c index 430f338..ca91b00 100644 --- a/code/game/src/gui/inventory.c +++ b/code/game/src/gui/inventory.c @@ -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); } diff --git a/code/game/src/items.c b/code/game/src/items.c index da202fd..d880992 100644 --- a/code/game/src/items.c +++ b/code/game/src/items.c @@ -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; } } diff --git a/code/game/src/items.h b/code/game/src/items.h index 05cf0e1..44081e2 100644 --- a/code/game/src/items.h +++ b/code/game/src/items.h @@ -31,6 +31,10 @@ typedef struct { struct { asset_id id; } proxy; + + struct { + uint64_t (*spawn_proc)(); + } place_item; }; } item_desc; diff --git a/code/game/src/items_list.c b/code/game/src/items_list.c index de1e917..f2efcc4 100644 --- a/code/game/src/items_list.c +++ b/code/game/src/items_list.c @@ -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), }; diff --git a/code/game/src/packet.c b/code/game/src/packet.c index 01d2530..b648621 100644 --- a/code/game/src/packet.c +++ b/code/game/src/packet.c @@ -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}, }; diff --git a/code/game/src/packet.h b/code/game/src/packet.h index a2dd185..2633a01 100644 --- a/code/game/src/packet.h +++ b/code/game/src/packet.h @@ -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; diff --git a/code/game/src/packets/pkt_send_keystate.c b/code/game/src/packets/pkt_send_keystate.c index f65a8ef..5c3a3a1 100644 --- a/code/game/src/packets/pkt_send_keystate.c +++ b/code/game/src/packets/pkt_send_keystate.c @@ -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; +} diff --git a/code/game/src/packets/pkt_send_keystate.h b/code/game/src/packets/pkt_send_keystate.h index 3c2446b..830481a 100644 --- a/code/game/src/packets/pkt_send_keystate.h +++ b/code/game/src/packets/pkt_send_keystate.h @@ -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); diff --git a/code/game/src/platform.h b/code/game/src/platform.h index 99509a6..6e49b10 100644 --- a/code/game/src/platform.h +++ b/code/game/src/platform.h @@ -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); diff --git a/code/game/src/platform_raylib.c b/code/game/src/platform_raylib.c index 0f9450c..c2b5760 100644 --- a/code/game/src/platform_raylib.c +++ b/code/game/src/platform_raylib.c @@ -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(); { diff --git a/code/game/src/renderer_v0.c b/code/game/src/renderer_v0.c index c5403ed..c15d310 100644 --- a/code/game/src/renderer_v0.c +++ b/code/game/src/renderer_v0.c @@ -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; } } diff --git a/code/game/src/storage.c b/code/game/src/storage.c new file mode 100644 index 0000000..a4e265e --- /dev/null +++ b/code/game/src/storage.c @@ -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); +} diff --git a/code/game/src/storage.h b/code/game/src/storage.h new file mode 100644 index 0000000..2a1bfee --- /dev/null +++ b/code/game/src/storage.h @@ -0,0 +1,7 @@ +#pragma once + +#include "system.h" + +uint64_t storage_spawn(void); +void storage_despawn(uint64_t id); + diff --git a/code/game/src/system.h b/code/game/src/system.h index 1374149..63ed0d9 100644 --- a/code/game/src/system.h +++ b/code/game/src/system.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -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) \ No newline at end of file diff --git a/code/game/src/world/world.c b/code/game/src/world/world.c index 3602bef..a879930 100644 --- a/code/game/src/world/world.c +++ b/code/game/src/world/world.c @@ -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; diff --git a/code/game/src/world/worldgen/worldgen_test.c b/code/game/src/world/worldgen/worldgen_test.c index 500e0ca..81cb590 100644 --- a/code/game/src/world/worldgen/worldgen_test.c +++ b/code/game/src/world/worldgen/worldgen_test.c @@ -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; ix = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); + dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); + } + #endif return WORLD_ERROR_NONE; diff --git a/code/modules/modules/components.c b/code/modules/modules/components.c index af56185..0a9ec7c 100644 --- a/code/modules/modules/components.c +++ b/code/modules/modules/components.c @@ -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); } diff --git a/code/modules/modules/components.h b/code/modules/modules/components.h index 825840d..a60e4fa 100644 --- a/code/modules/modules/components.h +++ b/code/modules/modules/components.h @@ -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); diff --git a/code/modules/modules/systems.c b/code/modules/modules/systems.c index 7763227..6df2cad 100644 --- a/code/modules/modules/systems.c +++ b/code/modules/modules/systems.c @@ -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); - + } diff --git a/code/modules/source/system_items.c b/code/modules/source/system_items.c index aa6605b..4744a07 100644 --- a/code/modules/source/system_items.c +++ b/code/modules/source/system_items.c @@ -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; + } + } + } + } + } + } +} + + +