From 911208a926d380cb93f2ec1e64529ad2fa370b50 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 9 Aug 2024 22:27:02 -0500 Subject: [PATCH] triggering beginnings. --- src/entity.h | 11 +- src/game.c | 293 +++++++++++++++++++++++++++++---------------------- src/game.h | 10 +- src/user.c | 56 ++++------ 4 files changed, 203 insertions(+), 167 deletions(-) diff --git a/src/entity.h b/src/entity.h index 1f6d7a3f..2c2f7d3f 100644 --- a/src/entity.h +++ b/src/entity.h @@ -15,7 +15,8 @@ enum entity_prop { ENTITY_PROP_EQUIPPER, ENTITY_PROP_EQUIPABLE, - ENTITY_PROP_FIRING_EQUIPPED, + ENTITY_PROP_TRIGGERED, + ENTITY_PROP_TRIGGERING_EQUIPPED, /* Test props */ @@ -62,6 +63,12 @@ struct entity { struct entity_handle first; struct entity_handle last; + /* ====================================================================== */ + /* Allocation/Release timing */ + + u64 valid_tick; /* `valid` will be set to true when world tick >= `valid_tick` & `valid_tick` != 0 */ + u64 release_tick; /* Entity will be released when world tick >= `release_tick` & `release_tick` != 0 */ + /* ====================================================================== */ /* Position */ @@ -203,8 +210,6 @@ INLINE b32 entity_has_prop(struct entity *entity, enum entity_prop prop) * Entity functions * ========================== */ -INLINE struct entity_array entity_store_as_array(struct entity_store *store) { return (struct entity_array) { .count = store->count, .entities = store->entities }; } - /* Entity store */ struct entity_store *entity_store_alloc(void); void entity_store_release(struct entity_store *store); diff --git a/src/game.c b/src/game.c index 348d68e6..548e9bdd 100644 --- a/src/game.c +++ b/src/game.c @@ -188,8 +188,21 @@ INTERNAL void game_update(void) * Begin frame * ========================== */ + ++G.world.tick_id; + G.world.tick_ts = sys_timestamp(); + G.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * G.world.timescale); + G.world.time += G.world.dt; + + u64 world_tick = G.world.tick_id; + struct game_cmd_array game_cmds = pop_cmds(scratch.arena); + struct entity_store *store = G.world.entity_store; + struct entity *root = entity_from_handle(store, store->root); struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); + /* ========================== * + * Spawn test entities + * ========================== */ + /* TODO: remove this (testing) */ /* Initialize entities */ static b32 run = 0; @@ -198,20 +211,8 @@ INTERNAL void game_update(void) spawn_test_entities(); } - ++G.world.tick_id; - G.world.tick_ts = sys_timestamp(); - G.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * G.world.timescale); - G.world.time += G.world.dt; - /* ========================== * - * Pull cmds - * ========================== */ - - struct entity_store *store = G.world.entity_store; - struct game_cmd_array game_cmds = pop_cmds(scratch.arena); - - /* ========================== * - * Pre-sim game cmds + * Process pre-sim game cmds * ========================== */ for (u64 cmd_index = 0; cmd_index < game_cmds.count; ++cmd_index) { @@ -239,18 +240,143 @@ INTERNAL void game_update(void) }; } - /* ---------------------------------------------------------------------- */ - /* ---------------------------------------------------------------------- */ - - struct entity *root = entity_from_handle(store, store->root); - struct entity_array entities_array = entity_store_as_array(store); - /* ========================== * - * Update sprite + * Make valid or release entities * ========================== */ - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; + { + u64 ents_to_release_count = 0; + struct entity **ents_to_release = arena_dry_push(scratch.arena, struct entity *); + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + + u64 valid_tick = ent->valid_tick; + u64 release_tick = ent->release_tick; + + b32 valid = ent->valid; + if (valid_tick != 0 && valid_tick >= world_tick && !valid) { + ent->valid = true; + valid = true; + } + + if (valid && release_tick != 0 && release_tick >= world_tick) { + *arena_push(scratch.arena, struct entity *) = ent; + ++ents_to_release_count; + } + } + + for (u64 i = 0; i < ents_to_release_count; ++i) { + struct entity *ent = ents_to_release[i]; + entity_release(store, ent); + } + } + + /* ========================== * + * Update entities from cmds + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!ent->valid) continue; + + /* ========================== * + * Initialize test + * ========================== */ + + /* ENTITY_PROP_TEST */ + if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) { + ent->test_initialized = true; + ent->test_start_local_xform = entity_get_local_xform(ent); + ent->test_start_sprite_xform = ent->sprite_local_xform; + } + + /* ========================== * + * Update control from player cmds + * ========================== */ + + if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { + /* Process cmds */ + struct v2 move = ent->control.move; + struct v2 focus = ent->control.focus; + b32 firing = entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED); + + for (u64 i = 0; i < game_cmds.count; ++i) { + struct game_cmd cmd = game_cmds.cmds[i]; + b32 start = cmd.state == GAME_CMD_STATE_START; + b32 stop = cmd.state == GAME_CMD_STATE_STOP; + (UNUSED)start; + (UNUSED)stop; + + /* TODO: Combine movement from multiple inputs? E.G. a sudden + * start and immediate stop cmd should still move the player a + * tad. */ + switch (cmd.kind) { + case GAME_CMD_KIND_PLAYER_MOVE: { + move = cmd.move_dir; + focus = v2_sub(cmd.aim_pos, entity_get_xform(ent).og); + } break; + + case GAME_CMD_KIND_PLAYER_FIRE: { + if (start) { + firing = true; + } else if (stop) { + firing = false; + } + } break; + + default: break; + } + } + + /* Movement */ + if (v2_len_squared(move) > 1) { + /* Cap movement vector magnitude at 1 */ + move = v2_norm(move); + } + ent->control.move = move; + ent->control.focus = focus; + + /* Firing */ + if (firing) { + entity_enable_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED); + } else { + entity_disable_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED); + } + } + + /* ========================== * + * Test + * ========================== */ + + /* ENTITY_PROP_TEST */ + if (entity_has_prop(ent, ENTITY_PROP_TEST)) { + f32 t = ((f32)G.world.time); + struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); + f32 r = t * 3; + struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + (UNUSED)og; + (UNUSED)r; + (UNUSED)s; + + og = v2_add(og, ent->test_start_local_xform.og); + r += xform_get_rotation(ent->test_start_local_xform); + s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); + + + struct xform xf = entity_get_local_xform(ent); + xf.og = og; + xf= xform_with_rotation(xf, r); + xf= xform_with_scale(xf, s); + entity_set_local_xform(ent, xf); + } + } + + /* ========================== * + * Update sprites + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; if (!ent->valid) continue; if (sprite_tag_is_nil(ent->sprite)) continue; @@ -301,66 +427,28 @@ INTERNAL void game_update(void) ent->sprite_local_xform = xf; } - /* ========================== * - * Update entities pre-simulation + * Update triggering * ========================== */ - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!ent->valid) continue; + + if (entity_has_prop(ent, ENTITY_PROP_TRIGGERED)) { + } + } + + /* ========================== * + * Simulate entities + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; if (!ent->valid) continue; /* ========================== * - * Initialize test - * ========================== */ - - /* ENTITY_PROP_TEST */ - if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) { - ent->test_initialized = true; - ent->test_start_local_xform = entity_get_local_xform(ent); - ent->test_start_sprite_xform = ent->sprite_local_xform; - } - - /* ========================== * - * Update control from player cmds - * ========================== */ - - if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - for (u64 i = 0; i < game_cmds.count; ++i) { - struct game_cmd cmd = game_cmds.cmds[i]; - b32 start = cmd.state == 1; - b32 stop = cmd.state == -1; - - /* TODO: Combine movement from multiple inputs? E.G. a sudden - * start and immediate stop cmd should still move the player a - * tad. */ - - switch (cmd.kind) { - case GAME_CMD_KIND_PLAYER_MOVE: { - struct v2 move = cmd.move_dir; - if (v2_len_squared(move) > 1) { - /* Cap movement vector magnitude at 1 */ - move = v2_norm(move); - } - ent->control.move = move; - ent->control.focus = v2_sub(cmd.aim_pos, entity_get_xform(ent).og); - } break; - - case GAME_CMD_KIND_PLAYER_FIRE: { - if (start) { - entity_enable_prop(ent, ENTITY_PROP_FIRING_EQUIPPED); - } else if (stop) { - entity_disable_prop(ent, ENTITY_PROP_FIRING_EQUIPPED); - } - } break; - - default: break; - } - } - } - - /* ========================== * - * Update player angle + * Player angle * ========================== */ if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { @@ -417,54 +505,7 @@ INTERNAL void game_update(void) } /* ========================== * - * Test - * ========================== */ - - /* ENTITY_PROP_TEST */ - if (entity_has_prop(ent, ENTITY_PROP_TEST)) { - f32 t = ((f32)G.world.time); - struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); - f32 r = t * 3; - struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); - (UNUSED)og; - (UNUSED)r; - (UNUSED)s; - - og = v2_add(og, ent->test_start_local_xform.og); - r += xform_get_rotation(ent->test_start_local_xform); - s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); - - - struct xform xf = entity_get_local_xform(ent); - xf.og = og; - xf= xform_with_rotation(xf, r); - xf= xform_with_scale(xf, s); - entity_set_local_xform(ent, xf); - } - - /* ENTITY_PROP_FIRING_EQUIPPED */ - { - struct entity *eq = entity_from_handle(store, ent->equipped); - if (eq->valid) { - if (entity_has_prop(ent, ENTITY_PROP_FIRING_EQUIPPED)) { - eq->sprite_tint = RGBA_32_F(1, 0, 0, 1); - } else { - eq->sprite_tint = RGBA_32_F(1, 1, 1, 1); - } - } - } - } - - /* ========================== * - * Simulate entities - * ========================== */ - - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; - if (!ent->valid) continue; - - /* ========================== * - * Player control + * Player movement * ========================== */ if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { @@ -498,8 +539,8 @@ INTERNAL void game_update(void) * Update entities post-simulation * ========================== */ - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { - struct entity *ent = &entities_array.entities[entity_index]; + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; if (!ent->valid) continue; /* ========================== * diff --git a/src/game.h b/src/game.h index 2a6a1067..32a8e374 100644 --- a/src/game.h +++ b/src/game.h @@ -6,6 +6,12 @@ struct mixer_startup_receipt; struct sprite_startup_receipt; struct sound_startup_receipt; +enum game_cmd_state { + GAME_CMD_STATE_STOP = -1, + GAME_CMD_STATE_NO_CHANGE = 0, + GAME_CMD_STATE_START = 1 +}; + enum game_cmd_kind { GAME_CMD_KIND_NONE, @@ -21,9 +27,7 @@ enum game_cmd_kind { struct game_cmd { enum game_cmd_kind kind; - - /* 1 = start, -1 = stop */ - i32 state; + enum game_cmd_state state; /* GAME_CMD_KIND_PLAYER_MOVE */ struct v2 move_dir; diff --git a/src/user.c b/src/user.c index cdb604ea..21b5c78b 100644 --- a/src/user.c +++ b/src/user.c @@ -394,23 +394,21 @@ INTERNAL void debug_draw_movement(struct entity *ent) INTERNAL void user_update(void) { struct temp_arena scratch = scratch_begin_no_conflict(); - struct game_cmd_list cmd_list = { - .arena = scratch.arena - }; - struct entity_store *store = G.world.entity_store; + /* ========================== * + * Begin frame + * ========================== */ - /* Get time */ f64 cur_time = sys_timestamp_seconds(sys_timestamp()); G.dt = max_f64(0.0, cur_time - G.time); G.time += G.dt; G.screen_size = sys_window_get_size(G.window); - /* ========================== * - * Begin frame cache scopes - * ========================== */ - + struct entity_store *store = G.world.entity_store; struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); + struct game_cmd_list cmd_list = { + .arena = scratch.arena + }; /* ========================== * * Produce interpolated tick @@ -450,17 +448,13 @@ INTERNAL void user_update(void) G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend); /* Blend entities */ - struct entity_array t0_entities = entity_store_as_array(t0->entity_store); - struct entity_array t1_entities = entity_store_as_array(t1->entity_store); - struct entity_array world_entities = entity_store_as_array(store); - - u64 num_entities = min_u64(t0_entities.count, t1_entities.count); + u64 num_entities = min_u64(t0->entity_store->count, t1->entity_store->count); { __profscope(tick_blending); for (u64 i = 0; i < num_entities; ++i) { - struct entity *e0 = &t0_entities.entities[i]; - struct entity *e1 = &t1_entities.entities[i]; - struct entity *e = &world_entities.entities[i]; + struct entity *e0 = &t0->entity_store->entities[i]; + struct entity *e1 = &t1->entity_store->entities[i]; + struct entity *e = &store->entities[i]; ASSERT(e->cached_global_xform_dirty == false); /* Game thread should have cached all global xforms before publishing */ if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) { e->local_xform = xform_lerp(e0->local_xform, e1->local_xform, tick_blend); @@ -489,8 +483,6 @@ INTERNAL void user_update(void) #endif } - struct entity_array entities_array = entity_store_as_array(store); - /* ========================== * * Find important entities * ========================== */ @@ -768,10 +760,10 @@ INTERNAL void user_update(void) * ========================== */ /* Iterate entities */ - for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { + for (u64 entity_index = 0; entity_index < store->count; ++entity_index) { __profscope(user_entity_iter); - struct entity *ent = &entities_array.entities[entity_index]; + struct entity *ent = &store->entities[entity_index]; if (!ent->valid) continue; if (ent->is_root) continue; @@ -954,8 +946,9 @@ INTERNAL void user_update(void) * Construct player control cmd * ========================== */ - /* Movement */ { + + /* Queue player move cmd */ struct v2 input_move_dir = { 0 }; { for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(G.bind_states); ++bind) { @@ -990,27 +983,20 @@ INTERNAL void user_update(void) input_move_dir = xform_basis_invert_mul_v2(G.world_view, input_move_dir); /* Make move dir relative to world view */ input_move_dir = v2_norm(input_move_dir); } - struct v2 input_aim_pos = G.world_cursor; - - i32 cmd_fire = (G.bind_states[USER_BIND_KIND_FIRE].num_presses > 0 || G.bind_states[USER_BIND_KIND_FIRE].is_held) ? 1 : -1; - if (!G.debug_camera) { - /* Queue player move cmd */ queue_game_cmd(&cmd_list, (struct game_cmd) { .kind = GAME_CMD_KIND_PLAYER_MOVE, .move_dir = input_move_dir, .aim_pos = input_aim_pos }); - - /* Queue player fire cmd */ - if (cmd_fire) { - queue_game_cmd(&cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_PLAYER_FIRE, - .state = cmd_fire - }); - } } + + /* Queue player fire cmd */ + queue_game_cmd(&cmd_list, (struct game_cmd) { + .kind = GAME_CMD_KIND_PLAYER_FIRE, + .state = (G.bind_states[USER_BIND_KIND_FIRE].num_presses > 0 || G.bind_states[USER_BIND_KIND_FIRE].is_held) ? GAME_CMD_STATE_START : GAME_CMD_STATE_STOP + }); } /* ---------------------------------------------------------------------- */