triggering beginnings.
This commit is contained in:
parent
f5400b0205
commit
911208a926
11
src/entity.h
11
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);
|
||||
|
||||
293
src/game.c
293
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;
|
||||
|
||||
/* ========================== *
|
||||
|
||||
10
src/game.h
10
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;
|
||||
|
||||
48
src/user.c
48
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,28 +983,21 @@ 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
|
||||
.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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user