diff --git a/code/apps/client/source/game.c b/code/apps/client/source/game.c index e367b80..979ce86 100644 --- a/code/apps/client/source/game.c +++ b/code/apps/client/source/game.c @@ -86,7 +86,7 @@ void game_world_view_set_active_by_idx(uint16_t idx) { game_world_view_set_active(&world_viewers[idx]); } -void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view value)) { +void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)) { entity_view_map(&active_viewer->entities, map_proc); } @@ -154,6 +154,8 @@ void game_update() { network_client_tick(); } else world_update(); + + game_world_cleanup_entities(); } void game_render() { @@ -162,4 +164,20 @@ void game_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_world_cleanup_entities(void) { + for (int i = 0; i < zpl_buffer_count(world_viewers); i += 1){ + entity_view_tbl *view = &world_viewers[i].entities; + + for (int j = 0; j < zpl_array_count(view->entries); j += 1){ + entity_view *e = &view->entries[j]; + if (e->tran_effect == ETRAN_REMOVE) { + entity_view_tbl_remove(view, e->ent_id); + j--; + } + } + + } + } \ No newline at end of file diff --git a/code/apps/client/source/main.c b/code/apps/client/source/main.c index 6f16a3c..f87cd53 100644 --- a/code/apps/client/source/main.c +++ b/code/apps/client/source/main.c @@ -16,7 +16,7 @@ #define DEFAULT_WORLD_SEED 302097 #define DEFAULT_BLOCK_SIZE 16 /* amount of units within a block (single axis) */ #define DEFAULT_CHUNK_SIZE 16 /* amount of blocks within a chunk (single axis) */ -#define DEFAULT_WORLD_SIZE 8 /* amount of chunks within a world (single axis) */ +#define DEFAULT_WORLD_SIZE 32 /* amount of chunks within a world (single axis) */ int main(int argc, char** argv) { @@ -50,7 +50,7 @@ int main(int argc, char** argv) uint16_t block_size = zpl_opts_integer(&opts, "block-size", DEFAULT_BLOCK_SIZE); uint16_t chunk_size = zpl_opts_integer(&opts, "chunk-size", DEFAULT_CHUNK_SIZE); uint16_t world_size = zpl_opts_integer(&opts, "world-size", DEFAULT_WORLD_SIZE); - uint32_t npc_count = zpl_opts_integer(&opts, "npc-count", 100); + uint32_t npc_count = zpl_opts_integer(&opts, "npc-count", 1000); if (zpl_opts_has_arg(&opts, "random-seed")) { zpl_random rnd={0}; diff --git a/code/apps/client/source/platform_raylib.c b/code/apps/client/source/platform_raylib.c index 5b91ed2..2c1ba4d 100644 --- a/code/apps/client/source/platform_raylib.c +++ b/code/apps/client/source/platform_raylib.c @@ -129,15 +129,17 @@ void platform_input() { void display_conn_status(); -void DEBUG_draw_entities(uint64_t key, entity_view data); -void DEBUG_draw_ground(uint64_t key, entity_view data); +void DEBUG_draw_entities(uint64_t key, entity_view * data); +void DEBUG_draw_ground(uint64_t key, entity_view * data); -void lerp_entity_positions(uint64_t key, entity_view data); +void lerp_entity_positions(uint64_t key, entity_view * data); +void do_entity_fadeinout(uint64_t key, entity_view * data); float zpl_lerp(float,float,float); void platform_render() { game_world_view_active_entity_map(lerp_entity_positions); + game_world_view_active_entity_map(do_entity_fadeinout); render_camera.zoom = zpl_lerp(render_camera.zoom, target_zoom, 0.18); camera_update(); @@ -163,14 +165,13 @@ void display_conn_status() { } } else { DrawText("Connection: single-player", 5, 5, 12, BLUE); - //DrawText("Connection: ", 5, 5, 52, BLUE); } DrawFPS(0, 20); } -void DEBUG_draw_ground(uint64_t key, entity_view data) { - switch (data.kind) { +void DEBUG_draw_ground(uint64_t key, entity_view * data) { + switch (data->kind) { case EKIND_CHUNK: { world_view *view = game_world_view_get_active(); int32_t size = view->chunk_size * view->block_size; @@ -180,10 +181,10 @@ void DEBUG_draw_ground(uint64_t key, entity_view data) { float block_spacing = (float)block_size * (size/(float)(chunk_size*block_size)); float block_offset = size - block_spacing*chunk_size; - double x = data.x * size + offset; - double y = data.y * size + offset; - DrawRectangleEco((int)x-offset, (int)y-offset, size+offset, size+offset, BLACK); - DrawRectangleEco((int)x, (int)y, size-offset, size-offset, LIME); + float x = data->x * size + offset; + float y = data->y * size + offset; + DrawRectangleEco(x-offset, y-offset, size+offset, size+offset, ColorAlpha(BLACK, data->tran_time)); + DrawRectangleEco(x, y, size-offset, size-offset, ColorAlpha(LIME, data->tran_time)); #if 0 for (uint16_t i = 0; i < chunk_size*chunk_size; i++) { @@ -193,7 +194,7 @@ void DEBUG_draw_ground(uint64_t key, entity_view data) { } #endif - DrawTextEco(TextFormat("%.01f %.01f", data.x, data.y), (int16_t)x+15, (int16_t)y+15, 65 , BLACK, 0.0); + DrawTextEco(TextFormat("%d %d", (int)data->x, (int)data->y), (int16_t)x+15, (int16_t)y+15, 65 , ColorAlpha(BLACK, data->tran_time), 0.0); }break; default:break; @@ -202,32 +203,31 @@ void DEBUG_draw_ground(uint64_t key, entity_view data) { static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; } -void DEBUG_draw_entities(uint64_t key, entity_view data) { - world_view *view = game_world_view_get_active(); +void DEBUG_draw_entities(uint64_t key, entity_view * data) { uint16_t size = 4; uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom); float font_spacing = 1.1f; float title_bg_offset = 4; float fixed_title_offset = 2; - switch (data.kind) { + switch (data->kind) { case EKIND_THING: { - double x = data.x; - double y = data.y; + float x = data->x; + float y = data->y; const char *title = TextFormat("Thing %d", key); int title_w = MeasureTextEco(title, font_size, font_spacing); - DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, BLACK); - DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, RAYWHITE, font_spacing); - DrawCircleEco(x, y, size, BLUE); + DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, 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(BLUE, data->tran_time)); }break; case EKIND_PLAYER: { - double x = data.x; - double y = data.y; + float x = data->x; + float y = data->y; const char *title = TextFormat("Player %d", key); int title_w = MeasureTextEco(title, font_size, font_spacing); - DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, BLACK); - DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, RAYWHITE, font_spacing); - DrawCircleEco(x, y, size, RED); + DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, 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(RED, data->tran_time)); }break; default:break; } @@ -247,4 +247,28 @@ void lerp_entity_positions(uint64_t key, entity_view data) { e->y = e->ty; #endif } +} + +void do_entity_fadeinout(uint64_t key, entity_view * data) { + switch (data->tran_effect) { + case ETRAN_FADEIN: { + data->tran_time += GetFrameTime(); + + if (data->tran_time > 1.0f) { + data->tran_effect = ETRAN_NONE; + data->tran_time = 1.0f; + } + }break; + + case ETRAN_FADEOUT: { + data->tran_time -= GetFrameTime(); + + if (data->tran_time < 0.0f) { + data->tran_effect = ETRAN_REMOVE; + data->tran_time = 0.0f; + } + }break; + + default: break; + } } \ No newline at end of file diff --git a/code/apps/client/source/prediction.c b/code/apps/client/source/prediction.c index 06b150c..8aeab22 100644 --- a/code/apps/client/source/prediction.c +++ b/code/apps/client/source/prediction.c @@ -34,4 +34,8 @@ void predict_receive_update(entity_view *d, entity_view *data) { data->tx = tx; data->ty = ty; } + + data->tran_effect = d->tran_effect; + data->tran_time = d->tran_time; + zpl_printf("? %f\n", d->tran_time); } diff --git a/code/apps/client/source/world_view.c b/code/apps/client/source/world_view.c index 6308aeb..952b979 100644 --- a/code/apps/client/source/world_view.c +++ b/code/apps/client/source/world_view.c @@ -8,7 +8,7 @@ int32_t tracker_read_remove(librg_world *w, librg_event *e) { int64_t entity_id = librg_event_entity_get(w, e); world_view *view = (world_view*)librg_world_userdata_get(w); - entity_view_destroy(&view->entities, entity_id); + entity_view_mark_for_removal(&view->entities, entity_id); return 0; } @@ -42,19 +42,17 @@ int32_t tracker_read_create(librg_world *w, librg_event *e) { size_t actual_length = librg_event_size_get(w, e); char *buffer = librg_event_buffer_get(w, e); world_view *view = (world_view*)librg_world_userdata_get(w); -#ifdef WORLD_LAYERING - if (view->active_layer_id != WORLD_TRACKER_LAYERS-1) { - // NOTE(zaklaus): reject updates from smaller layers - return 0; - } -#endif + entity_view data = entity_view_unpack_struct(buffer, actual_length); + data.ent_id = entity_id; data.layer_id = view->active_layer_id; + data.tran_time = 0.0f; if (data.flag & EFLAG_INTERP) { data.tx = data.x; data.ty = data.y; } entity_view_update_or_create(&view->entities, entity_id, data); + entity_view_mark_for_fadein(&view->entities, entity_id); return 0; } diff --git a/code/common/entity_view.c b/code/common/entity_view.c index 2c34f3e..c710da0 100644 --- a/code/common/entity_view.c +++ b/code/common/entity_view.c @@ -50,6 +50,18 @@ entity_view *entity_view_get(entity_view_tbl *map, uint64_t ent_id) { return entity_view_tbl_get(map, ent_id); } -void entity_view_map(entity_view_tbl *map, void (*map_proc)(uint64_t key, entity_view value)) { - entity_view_tbl_map(map, map_proc); +void entity_view_map(entity_view_tbl *map, void (*map_proc)(uint64_t key, entity_view * value)) { + entity_view_tbl_map_mut(map, map_proc); +} + +void entity_view_mark_for_removal(entity_view_tbl *map, uint64_t ent_id) { + entity_view *view = entity_view_tbl_get(map, ent_id); + view->tran_effect = ETRAN_FADEOUT; + view->tran_time = 1.0f; +} + +void entity_view_mark_for_fadein(entity_view_tbl *map, uint64_t ent_id) { + entity_view *view = entity_view_tbl_get(map, ent_id); + view->tran_effect = ETRAN_FADEIN; + view->tran_time = 0.0f; } diff --git a/code/common/entity_view.h b/code/common/entity_view.h index 092091d..1246e93 100644 --- a/code/common/entity_view.h +++ b/code/common/entity_view.h @@ -18,7 +18,16 @@ typedef enum { FORCE_EFLAG_UINT16 = UINT16_MAX } entity_flag; +typedef enum { + ETRAN_NONE, + ETRAN_FADEOUT, + ETRAN_FADEIN, + ETRAN_REMOVE, + FORCE_ETRAN_UINT8 = UINT8_MAX +} entity_transition_effect; + typedef struct entity_view { + int64_t ent_id; entity_kind kind; entity_flag flag; float x; @@ -31,6 +40,10 @@ typedef struct entity_view { // 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; } entity_view; ZPL_TABLE_DECLARE(, entity_view_tbl, entity_view_tbl_, entity_view); @@ -44,7 +57,10 @@ void entity_view_update_or_create(entity_view_tbl *map, uint64_t ent_id, entity_ void entity_view_destroy(entity_view_tbl *map, uint64_t ent_id); entity_view *entity_view_get(entity_view_tbl *map, uint64_t ent_id); -void entity_view_map(entity_view_tbl *map, void (*map_proc)(uint64_t key, entity_view value)); +void entity_view_map(entity_view_tbl *map, void (*map_proc)(uint64_t key, entity_view * value)); size_t entity_view_pack_struct(void *data, size_t len, entity_view view); entity_view entity_view_unpack_struct(void *data, size_t len); + +void entity_view_mark_for_removal(entity_view_tbl *map, uint64_t ent_id); +void entity_view_mark_for_fadein(entity_view_tbl *map, uint64_t ent_id); \ No newline at end of file diff --git a/code/common/game.h b/code/common/game.h index c2e25f7..6c971b2 100644 --- a/code/common/game.h +++ b/code/common/game.h @@ -18,7 +18,8 @@ world_view *game_world_view_get(uint16_t idx); 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(uint8_t dir); -void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view value)); +void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)); +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 diff --git a/code/vendors/zpl.h b/code/vendors/zpl.h index f29f35e..da9a6e4 100644 --- a/code/vendors/zpl.h +++ b/code/vendors/zpl.h @@ -31,6 +31,8 @@ GitHub: https://github.com/zpl-c/zpl Version History: + 14.1.0 - add hashtable map_mut method + 14.0.1 - fix zpl_array_remove_at boundary bug 14.0.0 - heap memory allocator analysis 13.4.1 - adt optimizations @@ -358,8 +360,8 @@ License: #define ZPL_H #define ZPL_VERSION_MAJOR 14 -#define ZPL_VERSION_MINOR 0 -#define ZPL_VERSION_PATCH 2 +#define ZPL_VERSION_MINOR 1 +#define ZPL_VERSION_PATCH 0 #define ZPL_VERSION_PRE "" // file: zpl_hedley.h @@ -4299,6 +4301,7 @@ License: PREFIX void ZPL_JOIN2(FUNC, grow)(NAME * h); \ PREFIX void ZPL_JOIN2(FUNC, rehash)(NAME * h, zpl_isize new_count); \ PREFIX void ZPL_JOIN2(FUNC, map)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE value)); \ + PREFIX void ZPL_JOIN2(FUNC, map_mut)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE * value)); \ PREFIX void ZPL_JOIN2(FUNC, remove)(NAME * h, zpl_u64 key); #define ZPL_TABLE_DEFINE(NAME, FUNC, VALUE) \ @@ -4397,6 +4400,13 @@ License: map_proc(h->entries[i].key, h->entries[i].value); \ } \ } \ + void ZPL_JOIN2(FUNC, map_mut)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE * value)) { \ + ZPL_ASSERT_NOT_NULL(h); \ + ZPL_ASSERT_NOT_NULL(map_proc); \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); ++i) { \ + map_proc(h->entries[i].key, &h->entries[i].value); \ + } \ + } \ \ void ZPL_JOIN2(FUNC, set)(NAME * h, zpl_u64 key, VALUE value) { \ zpl_isize index; \