diff --git a/code/game/src/debug_replay.c b/code/game/src/debug_replay.c index 581216c..b206d79 100644 --- a/code/game/src/debug_replay.c +++ b/code/game/src/debug_replay.c @@ -147,6 +147,7 @@ void debug_replay_run(void) { *pos = *p1; ecs_set(world_ecs(), mime, Input, {0}); + ecs_set(world_ecs(), mime, Inventory, {0}); camera_set_follow(mime); } @@ -167,8 +168,16 @@ void debug_replay_update(void) { Input *i = ecs_get_mut(world_ecs(), mime, Input, NULL); i->x = r->pkt.x; i->y = r->pkt.y; + i->mx = r->pkt.mx; + i->my = r->pkt.my; i->use = r->pkt.use; i->sprint = r->pkt.sprint; + i->ctrl = r->pkt.ctrl; + i->selected_item = r->pkt.selected_item; + i->drop = r->pkt.drop; + i->swap = r->pkt.swap; + i->swap_from = r->pkt.swap_from; + i->swap_to = r->pkt.swap_to; }break; case RPKIND_SPAWN_CAR: { ecs_entity_t e = vehicle_spawn(); diff --git a/code/game/src/debug_ui.c b/code/game/src/debug_ui.c index 6f6c135..1069afb 100644 --- a/code/game/src/debug_ui.c +++ b/code/game/src/debug_ui.c @@ -72,18 +72,6 @@ typedef struct debug_item { debug_draw_result (*proc)(struct debug_item*, float, float); } debug_item; -typedef enum { - DAREA_OUTSIDE, - DAREA_HOVER, - DAREA_HELD, - DAREA_PRESS, - - DAREA_FORCE_UINT8 = UINT8_MAX -} debug_area_status; - -debug_area_status check_mouse_area(float xpos, float ypos, float w, float h); -bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color); - static void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color); static int UIMeasureText(const char *text, int fontSize); diff --git a/code/game/src/debug_ui.h b/code/game/src/debug_ui.h index 92b6b70..6a2bd2f 100644 --- a/code/game/src/debug_ui.h +++ b/code/game/src/debug_ui.h @@ -1,4 +1,17 @@ #pragma once #include "system.h" +#include "raylib.h" void debug_draw(void); + +typedef enum { + DAREA_OUTSIDE, + DAREA_HOVER, + DAREA_HELD, + DAREA_PRESS, + + DAREA_FORCE_UINT8 = UINT8_MAX +} debug_area_status; + +debug_area_status check_mouse_area(float xpos, float ypos, float w, float h); +bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color); diff --git a/code/game/src/debug_ui_actions.c b/code/game/src/debug_ui_actions.c index 276b705..848b64f 100644 --- a/code/game/src/debug_ui_actions.c +++ b/code/game/src/debug_ui_actions.c @@ -63,7 +63,7 @@ ActPlaceIceRink(void) { for (int y = 0; y < 100; y++) { for (int x = 0; x < 100; x++) { world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f); - world_chunk_replace_outer_block(world_ecs(), l.chunk_id, l.id, watr_id); + world_chunk_replace_outer_block(l.chunk_id, l.id, watr_id); } } @@ -79,7 +79,7 @@ ActEraseWorldChanges(void) { for (int y = 0; y < 100; y++) { for (int x = 0; x < 100; x++) { world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f); - world_chunk_replace_outer_block(world_ecs(), l.chunk_id, l.id, 0); + world_chunk_replace_outer_block(l.chunk_id, l.id, 0); } } diff --git a/code/game/src/entity_view.c b/code/game/src/entity_view.c index 7b2b4a5..821b091 100644 --- a/code/game/src/entity_view.c +++ b/code/game/src/entity_view.c @@ -26,8 +26,14 @@ pkt_desc pkt_entity_view_desc[] = { { PKT_KEEP_IF(entity_view, kind, EKIND_VEHICLE, 1) }, // NOTE(zaklaus): keep for vehicles { PKT_HALF(entity_view, heading) }, - { PKT_KEEP_IF(entity_view, kind, EKIND_ITEM, 1) }, + { PKT_KEEP_IF(entity_view, kind, EKIND_ITEM, 2) }, { PKT_UINT(entity_view, asset) }, + { PKT_UINT(entity_view, quantity) }, + + { PKT_KEEP_IF(entity_view, has_items, true, 3) }, + { PKT_UINT(entity_view, has_items) }, + { PKT_ARRAY(entity_view, items) }, + { PKT_UINT(entity_view, selected_item) }, { PKT_END }, }; diff --git a/code/game/src/entity_view.h b/code/game/src/entity_view.h index 2f98358..9263bd5 100644 --- a/code/game/src/entity_view.h +++ b/code/game/src/entity_view.h @@ -1,6 +1,9 @@ #pragma once #include "system.h" #include "assets.h" +#include "items.h" + +#include "modules/components.h" #define ZPL_PICO #include "zpl.h" @@ -57,6 +60,12 @@ typedef struct entity_view { // 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): internals uint8_t layer_id; diff --git a/code/game/src/game.c b/code/game/src/game.c index 1d55a07..665b9a0 100644 --- a/code/game/src/game.c +++ b/code/game/src/game.c @@ -92,6 +92,10 @@ void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_vie entity_view_map(&active_viewer->entities, map_proc); } +entity_view *game_world_view_active_get_entity(uint64_t ent_id) { + return entity_view_get(&active_viewer->entities, ent_id); +} + void game_world_view_set_active(world_view *view) { active_viewer = view; camera_set_follow(view->owner_id); @@ -170,8 +174,19 @@ void game_render() { platform_render(); } -void game_action_send_keystate(float x, float y, uint8_t use, uint8_t sprint) { - pkt_send_keystate_send(active_viewer->view_id, x, y, use, sprint); +void game_action_send_keystate(float x, + float y, + float mx, + float my, + uint8_t use, + uint8_t sprint, + uint8_t ctrl, + uint8_t drop, + uint8_t selected_item, + uint8_t swap, + uint8_t swap_from, + uint8_t swap_to) { + pkt_send_keystate_send(active_viewer->view_id, x, y, mx, my, use, sprint, ctrl, drop, selected_item, swap, swap_from, swap_to); } #define GAME_ENT_REMOVAL_TIME 10000 diff --git a/code/game/src/game.h b/code/game/src/game.h index 9aa9b31..06735e8 100644 --- a/code/game/src/game.h +++ b/code/game/src/game.h @@ -28,7 +28,19 @@ void game_world_view_set_active_by_idx(uint16_t idx); void game_world_view_set_active(world_view *view); void game_world_view_cycle_active(int8_t dir); void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)); +entity_view *game_world_view_active_get_entity(uint64_t ent_id); void game_world_cleanup_entities(void); //~ NOTE(zaklaus): viewer -> host actions -void game_action_send_keystate(float x, float y, uint8_t use, uint8_t sprint); \ No newline at end of file +void game_action_send_keystate(float x, + float y, + float mx, + float my, + uint8_t use, + uint8_t sprint, + uint8_t ctrl, + uint8_t drop, + uint8_t selected_item, + uint8_t swap, + uint8_t swap_from, + uint8_t swap_to); \ No newline at end of file diff --git a/code/game/src/gui/inventory.c b/code/game/src/gui/inventory.c new file mode 100644 index 0000000..c93a423 --- /dev/null +++ b/code/game/src/gui/inventory.c @@ -0,0 +1,92 @@ +#include "items.h" + +static uint8_t inv_selected_item = 0; +static bool inv_drop_item = false; +static 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; + +void inventory_draw() { + inv_drop_item = false; + inv_swap = false; + + camera cam = camera_get(); + entity_view *e = game_world_view_active_get_entity(cam.ent_id); + if (!e || !e->has_items) return; + + if (IsKeyPressed(KEY_TAB)) { + inv_is_open = !inv_is_open; + } + + if (!inv_is_open) return; + + float sx = screenWidth/2.0f + 128; + float sy = screenHeight/2.0f - 96; + + float x = sx; + float y = sy; + + 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) { + asset_id asset = item_get_asset(item->kind); + DrawTexturePro(GetSpriteTexture2D(assets_find(asset)), 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; + asset_id asset = item_get_asset(item_find(inv_held_item.kind)); + DrawTexturePro(GetSpriteTexture2D(assets_find(asset)), 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; + } + } +} \ No newline at end of file diff --git a/code/game/src/items.c b/code/game/src/items.c index 17fcaf9..a4178f6 100644 --- a/code/game/src/items.c +++ b/code/game/src/items.c @@ -2,9 +2,15 @@ #include "entity.h" #include "entity_view.h" #include "world/world.h" +#include "world/blocks.h" #include "modules/components.h" +#include "zpl.h" + +#include "items_list.c" +#define ITEMS_COUNT (sizeof(items)/sizeof(item_desc)) + uint64_t item_spawn(item_kind kind, uint32_t qty) { ecs_entity_t e = entity_spawn(EKIND_ITEM); @@ -17,15 +23,42 @@ uint64_t item_spawn(item_kind kind, uint32_t qty) { return (uint64_t)e; } +uint16_t item_find(item_kind kind) { + for (uint32_t i=0; ikind); + item_desc *desc = &items[item_id]; + switch (item_get_usage(item_id)) { + case UKIND_PLACE:{ + world_block_lookup l = world_block_from_realpos(p.x, p.y); + world_chunk_replace_outer_block(l.chunk_id, l.id, blocks_find(desc->place.biome, desc->place.kind)); + it->quantity--; + }break; + } +} + void item_despawn(uint64_t id) { entity_despawn(id); } -asset_id item_get_asset(item_kind kind) { - switch (kind) { - case IKIND_DEMO_ICEMAKER: return ASSET_DEMO_ICEMAKER; - } - - ZPL_PANIC("unreachable code"); - return 0; +uint32_t item_max_quantity(uint16_t id) { + ZPL_ASSERT(id >= 0 && id < ITEMS_COUNT); + return items[id].max_quantity; +} + +asset_id item_get_asset(uint16_t id) { + ZPL_ASSERT(id >= 0 && id < ITEMS_COUNT); + return items[id].asset; +} + +item_usage item_get_usage(uint16_t id) { + ZPL_ASSERT(id >= 0 && id < ITEMS_COUNT); + return items[id].usage; } \ No newline at end of file diff --git a/code/game/src/items.h b/code/game/src/items.h index a751a65..2436b4e 100644 --- a/code/game/src/items.h +++ b/code/game/src/items.h @@ -1,14 +1,46 @@ #pragma once #include "system.h" #include "assets.h" +#include "world/blocks.h" + +#include "modules/components.h" + +#define ITEMS_INVALID 0xFF typedef enum { IKIND_DEMO_ICEMAKER, } item_kind; +typedef enum { + UKIND_PLACE, +} item_usage; + +typedef struct { + item_kind kind; + item_usage usage; + asset_id asset; + uint32_t max_quantity; + + // NOTE(zaklaus): usage data + union { + struct { + block_biome biome; + block_kind kind; + } place; + }; +} item_desc; + +// NOTE(zaklaus): item drops uint64_t item_spawn(item_kind kind, uint32_t qty); void item_despawn(uint64_t id); +// NOTE(zaklaus): items +uint16_t item_find(item_kind kind); +void item_use(ecs_world_t *ecs, ItemDrop *it, Position p); + +uint32_t item_max_quantity(uint16_t id); +item_usage item_get_usage(uint16_t id); + // NOTE(zaklaus): client -asset_id item_get_asset(item_kind kind); \ No newline at end of file +asset_id item_get_asset(uint16_t id); \ No newline at end of file diff --git a/code/game/src/items_list.c b/code/game/src/items_list.c new file mode 100644 index 0000000..ef7817b --- /dev/null +++ b/code/game/src/items_list.c @@ -0,0 +1,15 @@ +#include "items.h" + +static item_desc items[] = { + { + .kind = IKIND_DEMO_ICEMAKER, + .usage = UKIND_PLACE, + .asset = ASSET_DEMO_ICEMAKER, + .max_quantity = 4, + + .place = { + .biome = BLOCK_BIOME_DEV, + .kind = BLOCK_KIND_WATER, + } + } +}; \ No newline at end of file diff --git a/code/game/src/packets/pkt_send_keystate.c b/code/game/src/packets/pkt_send_keystate.c index 9b3ee22..019da1b 100644 --- a/code/game/src/packets/pkt_send_keystate.c +++ b/code/game/src/packets/pkt_send_keystate.c @@ -9,17 +9,33 @@ pkt_desc pkt_send_keystate_desc[] = { { PKT_REAL(pkt_send_keystate, x) }, { PKT_REAL(pkt_send_keystate, y) }, + { PKT_REAL(pkt_send_keystate, mx) }, + { PKT_REAL(pkt_send_keystate, my) }, { PKT_UINT(pkt_send_keystate, use) }, { PKT_UINT(pkt_send_keystate, sprint) }, + { PKT_UINT(pkt_send_keystate, ctrl) }, + { PKT_UINT(pkt_send_keystate, selected_item) }, + { PKT_UINT(pkt_send_keystate, drop) }, + { PKT_UINT(pkt_send_keystate, swap) }, + { PKT_UINT(pkt_send_keystate, swap_from) }, + { PKT_UINT(pkt_send_keystate, swap_to) }, { PKT_END }, }; size_t pkt_send_keystate_send(uint16_t view_id, float x, float y, + float mx, + float my, uint8_t use, - uint8_t sprint) { - pkt_send_keystate table = { .x = x, .y = y, .use = use, .sprint = sprint }; + uint8_t sprint, + uint8_t ctrl, + uint8_t drop, + uint8_t selected_item, + uint8_t swap, + uint8_t swap_from, + uint8_t swap_to) { + pkt_send_keystate table = { .x = x, .y = y, .mx = mx, .my = my, .use = use, .sprint = sprint, .ctrl = ctrl, .drop = drop, .selected_item = selected_item, .swap = swap, .swap_from = swap_from, .swap_to = swap_to }; return pkt_world_write(MSG_ID_SEND_KEYSTATE, pkt_send_keystate_encode(&table), 1, view_id, NULL); } @@ -39,8 +55,16 @@ int32_t pkt_send_keystate_handler(pkt_header *header) { if (i && !i->is_blocked) { i->x = table.x; i->y = table.y; + i->mx = table.mx; + i->my = table.my; 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->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); debug_replay_record_keystate(table); } diff --git a/code/game/src/packets/pkt_send_keystate.h b/code/game/src/packets/pkt_send_keystate.h index 5626b5b..b4055a4 100644 --- a/code/game/src/packets/pkt_send_keystate.h +++ b/code/game/src/packets/pkt_send_keystate.h @@ -5,14 +5,30 @@ typedef struct { float x; float y; + float mx; + float my; uint8_t use; uint8_t sprint; + uint8_t ctrl; + uint8_t selected_item; + uint8_t drop; + uint8_t swap; + uint8_t swap_from; + uint8_t swap_to; } pkt_send_keystate; size_t pkt_send_keystate_send(uint16_t view_id, float x, float y, + float mx, + float my, uint8_t use, - uint8_t sprint); + uint8_t sprint, + uint8_t ctrl, + uint8_t drop, + uint8_t selected_item, + uint8_t swap, + uint8_t swap_from, + uint8_t swap_to); size_t pkt_send_keystate_encode(pkt_send_keystate *table); extern pkt_desc pkt_send_keystate_desc[]; diff --git a/code/game/src/platform_raylib.c b/code/game/src/platform_raylib.c index f971c30..11187e6 100644 --- a/code/game/src/platform_raylib.c +++ b/code/game/src/platform_raylib.c @@ -21,6 +21,9 @@ static bool request_shutdown; #define GFX_KIND 2 #include "renderer_bridge.c" +// NOTE(zaklaus): add-ins +#include "gui/inventory.c" + void platform_init() { InitWindow(screenWidth, screenHeight, "eco2d"); SetWindowState(FLAG_WINDOW_UNDECORATED|FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT); @@ -63,7 +66,7 @@ void platform_input() { // NOTE(zaklaus): keystate handling { float x=0.0f, y=0.0f; - uint8_t use, sprint; + uint8_t use, sprint, drop, ctrl; 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; @@ -71,20 +74,23 @@ 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; // NOTE(zaklaus): NEW! mouse movement + Vector2 mouse_pos = GetMousePosition(); + mouse_pos.x /= screenWidth; + mouse_pos.y /= screenHeight; + mouse_pos.x -= 0.5f; + mouse_pos.y -= 0.5f; + mouse_pos = Vector2Normalize(mouse_pos); + if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) { - Vector2 mouse_pos = GetMousePosition(); - mouse_pos.x /= screenWidth; - mouse_pos.y /= screenHeight; - mouse_pos.x -= 0.5f; - mouse_pos.y -= 0.5f; - mouse_pos = Vector2Normalize(mouse_pos); x = mouse_pos.x; y = -mouse_pos.y; } - game_action_send_keystate(x, y, use, sprint); + game_action_send_keystate(x, y, mouse_pos.x, mouse_pos.y, use, sprint, ctrl, drop, inv_selected_item, inv_swap, inv_swap_from, inv_swap_to); } // NOTE(zaklaus): cycle through viewers @@ -126,6 +132,10 @@ void platform_render() { renderer_draw(); } renderer_debug_draw(); + { + // NOTE(zaklaus): add-ins + inventory_draw(); + } debug_draw(); display_conn_status(); } diff --git a/code/game/src/player.c b/code/game/src/player.c index 1ea81ee..633d40c 100644 --- a/code/game/src/player.c +++ b/code/game/src/player.c @@ -22,6 +22,7 @@ uint64_t player_spawn(char *name) { ecs_set_name(world_ecs(), e, name); ecs_set(world_ecs(), e, ClientInfo, {0}); ecs_set(world_ecs(), e, Input, {0}); + ecs_set(world_ecs(), e, Inventory, {0}); ecs_set(world_ecs(), e, Health, {.hp = PLAYER_MAX_HP, .max_hp = PLAYER_MAX_HP}); ecs_add(world_ecs(), e, Player); diff --git a/code/game/src/renderer_v0.c b/code/game/src/renderer_v0.c index e781bd2..82d4f7f 100644 --- a/code/game/src/renderer_v0.c +++ b/code/game/src/renderer_v0.c @@ -56,6 +56,16 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time)); DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing); DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time)); + + if (data->has_items) { + float ix = data->x; + float iy = data->y; + if (data->items[data->selected_item].quantity > 0) { + item_kind it_kind = data->items[data->selected_item].kind; + uint16_t it_id = item_find(it_kind); + DrawTexturePro(GetSpriteTexture2D(assets_find(item_get_asset(it_id))), ASSET_SRC_RECT(), ((Rectangle){ix, iy, 32, 32}), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE)); + } + } }break; case EKIND_MACRO_BOT: { float x = data->x; @@ -70,6 +80,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { 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)); + DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f); }break; default:break; } diff --git a/code/game/src/world/world.c b/code/game/src/world/world.c index 17592a9..712ec14 100644 --- a/code/game/src/world/world.c +++ b/code/game/src/world/world.c @@ -55,6 +55,21 @@ entity_view world_build_entity_view(int64_t e) { if (ecs_get(world_ecs(), e, ItemDrop)) { ItemDrop const* dr = ecs_get(world_ecs(), e, ItemDrop); view.asset = item_get_asset(dr->kind); + view.quantity = dr->quantity; + + const Input *in = ecs_get(world_ecs(), e, Input); + if (in) + view.selected_item = in->selected_item; + } + + Inventory *inv = 0; + if ((inv = ecs_get_mut_if(world_ecs(), e, Inventory))) { + view.has_items = true; + + for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) { + view.items[i] = inv->items[i]; + } + } Chunk *chpos = 0; @@ -305,9 +320,16 @@ uint32_t world_buf(uint8_t const **ptr, uint32_t *width) { } ecs_world_t * world_ecs() { + if (world.ecs_stage != NULL) { + return world.ecs_stage; + } return world.ecs; } +void world_set_stage(ecs_world_t *ecs) { + world.ecs_stage = ecs; +} + librg_world *world_tracker() { return world.tracker; } @@ -391,32 +413,32 @@ int64_t world_chunk_from_entity(ecs_entity_t id) { return librg_entity_chunk_get(world.tracker, id); } -void world_chunk_replace_block(ecs_world_t *ecs, int64_t id, uint16_t block_idx, uint8_t block_id) { +void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id) { ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); world.block_mapping[id][block_idx] = block_id; - world_chunk_mark_dirty(ecs, world.chunk_mapping[id]); + world_chunk_mark_dirty(world.chunk_mapping[id]); } -void world_chunk_replace_outer_block(ecs_world_t *ecs, int64_t id, uint16_t block_idx, uint8_t block_id) { +void world_chunk_replace_outer_block(int64_t id, uint16_t block_idx, uint8_t block_id) { ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); world.outer_block_mapping[id][block_idx] = block_id; - world_chunk_mark_dirty(ecs, world.chunk_mapping[id]); + world_chunk_mark_dirty(world.chunk_mapping[id]); } uint8_t *world_chunk_get_blocks(int64_t id) { return world.block_mapping[id]; } -void world_chunk_mark_dirty(ecs_world_t *ecs, ecs_entity_t e) { +void world_chunk_mark_dirty(ecs_entity_t e) { bool was_added=false; - Chunk *chunk = ecs_get_mut(ecs, e, Chunk, &was_added); + Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); ZPL_ASSERT(!was_added); if (chunk) chunk->is_dirty = true; } -uint8_t world_chunk_is_dirty(ecs_world_t *ecs, ecs_entity_t e) { +uint8_t world_chunk_is_dirty(ecs_entity_t e) { bool was_added=false; - Chunk *chunk = ecs_get_mut(ecs, e, Chunk, &was_added); + Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); ZPL_ASSERT(!was_added); if (chunk) return chunk->is_dirty; return false; @@ -445,5 +467,5 @@ int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius) uint8_t world_entity_valid(ecs_entity_t e) { if (!e) return false; - return ecs_is_alive(world.ecs, e); + return ecs_is_alive(world_ecs(), e); } diff --git a/code/game/src/world/world.h b/code/game/src/world/world.h index e3990e3..02aa4f8 100644 --- a/code/game/src/world/world.h +++ b/code/game/src/world/world.h @@ -38,6 +38,7 @@ typedef struct { uint64_t tracker_update[3]; uint8_t active_layer_id; ecs_world_t *ecs; + ecs_world_t *ecs_stage; ecs_query_t *ecs_update; ecs_entity_t *chunk_mapping; librg_world *tracker; @@ -54,7 +55,8 @@ int32_t world_read(void* data, uint32_t datalen, void *udata); int32_t world_write(pkt_header *pkt, void *udata); uint32_t world_buf(uint8_t const **ptr, uint32_t *width); -ecs_world_t *world_ecs(void); // TODO(zaklaus): add staging support +ecs_world_t *world_ecs(void); +void world_set_stage(ecs_world_t *ecs); librg_world *world_tracker(void); uint16_t world_chunk_size(void); @@ -74,11 +76,11 @@ world_block_lookup world_block_from_realpos(float x, float y); world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx); int64_t world_chunk_from_realpos(float x, float y); int64_t world_chunk_from_entity(ecs_entity_t id); -void world_chunk_replace_block(ecs_world_t *ecs, int64_t id, uint16_t block_idx, uint8_t block_id); -void world_chunk_replace_outer_block(ecs_world_t *ecs, int64_t id, uint16_t block_idx, uint8_t block_id); +void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id); +void world_chunk_replace_outer_block(int64_t id, uint16_t block_idx, uint8_t block_id); uint8_t *world_chunk_get_blocks(int64_t id); -void world_chunk_mark_dirty(ecs_world_t *ecs, ecs_entity_t e); -uint8_t world_chunk_is_dirty(ecs_world_t *ecs, ecs_entity_t e); +void world_chunk_mark_dirty(ecs_entity_t e); +uint8_t world_chunk_is_dirty(ecs_entity_t e); // NOTE(zaklaus): Uses locally persistent buffer !! int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len); diff --git a/code/modules/modules/components.c b/code/modules/modules/components.c index c17544a..10f8306 100644 --- a/code/modules/modules/components.c +++ b/code/modules/modules/components.c @@ -12,6 +12,7 @@ ECS_COMPONENT_DECLARE(Classify); ECS_COMPONENT_DECLARE(Vehicle); ECS_COMPONENT_DECLARE(IsInVehicle); ECS_COMPONENT_DECLARE(ItemDrop); +ECS_COMPONENT_DECLARE(Inventory); ECS_TAG_DECLARE(EcsActor); ECS_TAG_DECLARE(EcsDemoNPC); ECS_TYPE_DECLARE(Player); @@ -37,6 +38,7 @@ void ComponentsImport(ecs_world_t *ecs) { ECS_COMPONENT_DEFINE(ecs, IsInVehicle); ECS_COMPONENT_DEFINE(ecs, ItemDrop); + ECS_COMPONENT_DEFINE(ecs, Inventory); ECS_TAG_DEFINE(ecs, Walking); ECS_TAG_DEFINE(ecs, Flying); @@ -59,6 +61,7 @@ void ComponentsImport(ecs_world_t *ecs) { ECS_SET_COMPONENT(Vehicle); ECS_SET_COMPONENT(IsInVehicle); ECS_SET_COMPONENT(ItemDrop); + ECS_SET_COMPONENT(Inventory); ECS_SET_ENTITY(Walking); ECS_SET_ENTITY(Flying); ECS_SET_ENTITY(EcsActor); diff --git a/code/modules/modules/components.h b/code/modules/modules/components.h index 5f4b001..d6982c4 100644 --- a/code/modules/modules/components.h +++ b/code/modules/modules/components.h @@ -1,7 +1,6 @@ #pragma once #include "flecs/flecs.h" #include "flecs/flecs_meta.h" -#include "items.h" //NOTE(zaklaus): custom macro to define meta components outside the current scope @@ -16,6 +15,8 @@ ecs_new_meta(world, ecs_entity(T), &__##T##__); (ecs_get(world, entity, component) ? ecs_get_mut(world, entity, component, NULL) : NULL) #endif +#define ITEMS_INVENTORY_SIZE 9 + ECS_STRUCT(Vector2D, { float x; float y; @@ -38,9 +39,19 @@ ECS_ALIAS(Vector2D, Velocity); ECS_STRUCT(Input, { float x; float y; + float mx; + float my; uint8_t use; uint8_t sprint; + uint8_t ctrl; uint8_t is_blocked; + + // NOTE(zaklaus): inventory + uint8_t selected_item; + uint8_t drop; + uint8_t swap; + uint8_t swap_from; + uint8_t swap_to; }); ECS_STRUCT(ClientInfo, { @@ -78,10 +89,15 @@ typedef struct { } IsInVehicle; typedef struct { - item_kind kind; + uint16_t kind; uint32_t quantity; } ItemDrop; +typedef struct { + ItemDrop items[ITEMS_INVENTORY_SIZE]; + float pickup_time; +} Inventory; + ECS_COMPONENT_EXTERN(Chunk); ECS_COMPONENT_EXTERN(Position); ECS_COMPONENT_EXTERN(Vector2D); @@ -94,6 +110,7 @@ ECS_COMPONENT_EXTERN(Classify); ECS_COMPONENT_EXTERN(Vehicle); ECS_COMPONENT_EXTERN(IsInVehicle); ECS_COMPONENT_EXTERN(ItemDrop); +ECS_COMPONENT_EXTERN(Inventory); ECS_TAG_EXTERN(EcsActor); ECS_TAG_EXTERN(EcsDemoNPC); ECS_TYPE_EXTERN(Player); @@ -116,6 +133,7 @@ typedef struct { ECS_DECLARE_COMPONENT(Vehicle); ECS_DECLARE_COMPONENT(IsInVehicle); ECS_DECLARE_COMPONENT(ItemDrop); + ECS_DECLARE_COMPONENT(Inventory); ECS_DECLARE_ENTITY(EcsActor); ECS_DECLARE_ENTITY(EcsDemoNPC); ECS_DECLARE_TYPE(Player); @@ -139,6 +157,7 @@ ECS_IMPORT_COMPONENT(handles, Classify);\ ECS_IMPORT_COMPONENT(handles, Vehicle);\ ECS_IMPORT_COMPONENT(handles, IsInVehicle);\ ECS_IMPORT_COMPONENT(handles, ItemDrop);\ +ECS_IMPORT_COMPONENT(handles, Inventory);\ ECS_IMPORT_TYPE(handles, Player);\ ECS_IMPORT_TYPE(handles, Builder);\ ECS_IMPORT_TYPE(handles, Movement);\ diff --git a/code/modules/modules/systems.c b/code/modules/modules/systems.c index 15596df..70373db 100644 --- a/code/modules/modules/systems.c +++ b/code/modules/modules/systems.c @@ -14,6 +14,7 @@ #include "source/system_onfoot.c" #include "source/system_demo.c" #include "source/system_vehicle.c" +#include "source/system_items.c" inline float physics_correction(float x, float vx, float bounce) { float r = (((zpl_max(0.0f, (WORLD_BLOCK_SIZE/2.0f) - zpl_abs(x))*zpl_sign(x)))*(WORLD_BLOCK_SIZE/2.0f)); @@ -131,9 +132,19 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) { } } +void EnableWorldEdit(ecs_iter_t *it) { + world_set_stage(it->world); +} + +void DisableWorldEdit(ecs_iter_t *it) { + (void)it; + world_set_stage(NULL); +} + 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.EcsDemoNPC); @@ -146,10 +157,16 @@ void SystemsImport(ecs_world_t *ecs) { 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, DemoPlaceIceBlock, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle); + + 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, UseItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle); 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_demo.c b/code/modules/source/system_demo.c index 19855be..92d0196 100644 --- a/code/modules/source/system_demo.c +++ b/code/modules/source/system_demo.c @@ -20,7 +20,7 @@ void DemoPlaceIceBlock(ecs_iter_t *it) { if (in[i].use) { in[i].use = false; world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y); - world_chunk_replace_outer_block(it->world, l.chunk_id, l.id, watr_id); + world_chunk_replace_outer_block(l.chunk_id, l.id, watr_id); } } } diff --git a/code/modules/source/system_items.c b/code/modules/source/system_items.c new file mode 100644 index 0000000..63ad9f3 --- /dev/null +++ b/code/modules/source/system_items.c @@ -0,0 +1,178 @@ +#include "items.h" + +#define ITEM_PICK_RADIUS 25.0f +#define ITEM_ATTRACT_RADIUS 75.0f +#define ITEM_ATTRACT_FORCE 0.63f + +void PickItem(ecs_iter_t *it) { + Position *p = ecs_column(it, Position, 2); + Inventory *inv = ecs_column(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, NULL); + + 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) { + for (size_t k = 0; k < ITEMS_INVENTORY_SIZE; k += 1) { + ItemDrop *item = &inv[i].items[k]; + uint16_t item_id = item_find(item->kind); + if ((item->quantity == 0 || (item->quantity != 0 && item->kind == drop->kind)) && item->quantity < item_max_quantity(item_id)) { + uint32_t picked_count = zpl_max(0, drop->quantity); + picked_count = zpl_clamp(picked_count, 0, item_max_quantity(item_id) - item->quantity); + item->quantity += picked_count; + drop->quantity -= picked_count; + + if (drop->quantity == 0) + item_despawn(ents[j]); + break; + } + } + } else if (range <= ITEM_ATTRACT_RADIUS) { + p2->x = zpl_lerp(p2->x, p[i].x, ITEM_ATTRACT_FORCE*it->delta_time); + p2->y = zpl_lerp(p2->y, p[i].y, ITEM_ATTRACT_FORCE*it->delta_time); + } + } + } + } +} + +#define ITEM_DROP_PICKUP_TIME 2.5f + +void DropItem(ecs_iter_t *it) { + Input *in = ecs_column(it, Input, 1); + Position *p = ecs_column(it, Position, 2); + Inventory *inv = ecs_column(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]; + + if (item->quantity <= 0) + continue; + + bool is_item_nearby = false; + + 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 const* p2 = ecs_get(it->world, ents[j], Position); + + float dx = p2->x - p[i].x; + float dy = p2->y - p[i].y; + float range = zpl_sqrt(dx*dx + dy*dy); + if (range <= ITEM_PICK_RADIUS) { + if (item->kind == drop->kind) { + uint32_t dropped_count = item->quantity; + if (in[i].sprint) { + dropped_count /= 2; + } else if (in[i].ctrl) { + dropped_count = 1; + } + drop->quantity += dropped_count; + item->quantity -= dropped_count; + is_item_nearby = true; + inv[i].pickup_time = game_time() + ITEM_DROP_PICKUP_TIME; + break; + } + } + } + } + + if (!is_item_nearby) { + uint32_t dropped_count = item->quantity; + if (in[i].sprint) { + dropped_count /= 2; + } else if (in[i].ctrl) { + dropped_count = 1; + } + ecs_entity_t te = item_spawn(item->kind, dropped_count); + item->quantity -= dropped_count; + + Position *ipos = ecs_get_mut(it->world, te, Position, NULL); + *ipos = p[i]; + + Velocity *v = ecs_get_mut(it->world, te, Velocity, NULL); + 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; + } +} + +void SwapItems(ecs_iter_t *it) { + Input *in = ecs_column(it, Input, 1); + Inventory *inv = ecs_column(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]; + uint16_t to_id = item_find(to->kind); + + if (to == from) { + // NOTE(zaklaus): do nothing + } else if (to->kind == from->kind && to->quantity > 0) { + uint32_t swapped_count = from->quantity; + if (in[i].sprint) { + swapped_count /= 2; + } else if (in[i].ctrl) { + swapped_count = 1; + } + swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity); + to->quantity += swapped_count; + from->quantity -= swapped_count; + + if (swapped_count == 0) { + ItemDrop tmp = *to; + *to = *from; + *from = tmp; + } + } else if ((in[i].ctrl || in[i].sprint) && to->quantity == 0 && from->quantity > 0) { + // NOTE(zaklaus): item split + uint32_t split_count = from->quantity / 2; + if (in[i].ctrl) { + split_count = 1; + } + to->quantity = split_count; + from->quantity -= split_count; + to->kind = from->kind; + } else { + ItemDrop tmp = *to; + *to = *from; + *from = tmp; + } + + in[i].swap = false; + } +} + +void UseItem(ecs_iter_t *it) { + Input *in = ecs_column(it, Input, 1); + Position *p = ecs_column(it, Position, 2); + Inventory *inv = ecs_column(it, Inventory, 3); + + for (int i = 0; i < it->count; i++) { + if (!in[i].use) continue; + + ItemDrop *item = &inv[i].items[in[i].selected_item]; + if (!item || item->quantity <= 0) continue; + item_use(it->world, item, p[i]); + } +} \ No newline at end of file