diff --git a/src/entity.h b/src/entity.h index fce590e8..ae5efa09 100644 --- a/src/entity.h +++ b/src/entity.h @@ -104,6 +104,12 @@ struct entity { struct entity_handle first; struct entity_handle last; + /* ====================================================================== */ + /* Layer */ + + i32 layer; + i32 final_layer; /* Calculated each tick from entity tree */ + /* ====================================================================== */ /* Collider */ diff --git a/src/game.c b/src/game.c index 60ee8b1f..8727dc4f 100644 --- a/src/game.c +++ b/src/game.c @@ -202,6 +202,7 @@ INTERNAL void spawn_test_entities(void) //e->sprite_span_name = STR("idle.unarmed"); //e->sprite_span_name = STR("idle.one_handed"); e->sprite_span_name = STR("idle.two_handed"); + e->layer = GAME_LAYER_SHOULDERS; struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size); //xf.bx.y = -1.f; @@ -235,6 +236,7 @@ INTERNAL void spawn_test_entities(void) e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase")); e->sprite_collider_slice = STR("shape"); + e->layer = GAME_LAYER_SHOULDERS; entity_enable_prop(e, ENTITY_PROP_PHYSICAL_DYNAMIC); e->mass_unscaled = 10; @@ -258,6 +260,7 @@ INTERNAL void spawn_test_entities(void) e->sprite = sprite_tag_from_path(STR("res/graphics/box.ase")); e->sprite_collider_slice = STR("shape"); + e->layer = GAME_LAYER_SHOULDERS; entity_enable_prop(e, ENTITY_PROP_PHYSICAL_DYNAMIC); e->mass_unscaled = 100; @@ -280,6 +283,7 @@ INTERNAL void spawn_test_entities(void) e->sprite = sprite_tag_from_path(STR("res/graphics/bullet.ase")); e->sprite_collider_slice = STR("shape"); + e->layer = GAME_LAYER_SHOULDERS; entity_enable_prop(e, ENTITY_PROP_PHYSICAL_DYNAMIC); e->mass_unscaled = 0.5; @@ -295,6 +299,7 @@ INTERNAL void spawn_test_entities(void) entity_enable_prop(e, ENTITY_PROP_ATTACHED); e->attach_slice = STR("attach.wep"); + e->layer = GAME_LAYER_RELATIVE_WEAPON; entity_enable_prop(e, ENTITY_PROP_WEAPON); e->trigger_delay = 1.0f / 10.0f; @@ -418,6 +423,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, array) struct entity *decal = entity_alloc(root); decal->sprite = sprite_tag_from_path(STR("res/graphics/blood.ase")); decal->sprite_tint = RGBA_32_F(1, 1, 1, 0.25f); + decal->layer = GAME_LAYER_FLOOR_DECALS; entity_set_xform(decal, xf); f32 perp_range = 0.5; @@ -860,6 +866,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) bullet->bullet_knockback = 10; bullet->mass_unscaled = 0.04f; bullet->inertia_unscaled = 0.00001f; + bullet->layer = GAME_LAYER_BULLETS; #if 1 /* Point collider */ @@ -878,6 +885,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) { struct entity *tracer = entity_alloc(root); tracer->tracer_fade_duration = 0.025f; + tracer->layer = GAME_LAYER_TRACERS; entity_enable_prop(tracer, ENTITY_PROP_TRACER); bullet->bullet_tracer = tracer->handle; @@ -1228,6 +1236,35 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } + /* ========================== * + * Update relative layers + * ========================== */ + + { + struct temp_arena temp = arena_temp_begin(scratch.arena); + + struct entity **stack = arena_push(temp.arena, struct entity *); + u64 stack_count = 1; + *stack = root; + + while (stack_count > 0) { + struct entity *parent; + arena_pop(temp.arena, struct entity *, &parent); + --stack_count; + + i32 parent_layer = parent->final_layer; + for (struct entity *child = entity_from_handle(store, parent->first); child->valid; child = entity_from_handle(store, child->next)) { + if (entity_is_valid_and_active(child)) { + child->final_layer = parent_layer + child->layer; + *arena_push(temp.arena, struct entity *) = child; + ++stack_count; + } + } + } + + arena_temp_end(temp); + } + /* ========================== * * Update sound emitters * ========================== */ diff --git a/src/game.h b/src/game.h index 905c9dd8..11369b03 100644 --- a/src/game.h +++ b/src/game.h @@ -30,6 +30,16 @@ enum game_cmd_kind { GAME_CMD_KIND_COUNT }; +/* Absolute layers */ +#define GAME_LAYER_FLOOR_DECALS -300 +#define GAME_LAYER_BULLETS -200 +#define GAME_LAYER_TRACERS -100 +#define GAME_LAYER_SHOULDERS 0 + +/* Relative layers */ +#define GAME_LAYER_RELATIVE_DEFAULT 0 +#define GAME_LAYER_RELATIVE_WEAPON 1 + struct game_cmd { enum game_cmd_kind kind; enum game_cmd_state state; diff --git a/src/string.c b/src/string.c index 1e4f6be6..3df278a8 100644 --- a/src/string.c +++ b/src/string.c @@ -312,6 +312,25 @@ b32 string_eq(struct string str1, struct string str2) return eq; } +i32 string_cmp(struct string str1, struct string str2) +{ + i32 res = 0; + for (u64 i = 0; i < min_u64(str1.len, str2.len); ++i) { + res = str1.text[i] - str2.text[i]; + if (res != 0) { + break; + } + } + if (res == 0) { + if (str1.len > str2.len) { + res = str1.text[str2.len]; + } else if (str2.len > str1.len) { + res = str2.text[str1.len]; + } + } + return res; +} + b32 string_contains(struct string str, struct string substring) { if (substring.len > str.len) { diff --git a/src/string.h b/src/string.h index ae77eea5..fa831ff6 100644 --- a/src/string.h +++ b/src/string.h @@ -28,6 +28,7 @@ struct string_array string_split(struct arena *arena, struct string str, struct struct string string_indent(struct arena *arena, struct string str, u32 indent); struct string string_lower(struct arena *arena, struct string str); b32 string_eq(struct string str1, struct string str2); +i32 string_cmp(struct string str1, struct string str2); b32 string_contains(struct string str, struct string substring); b32 string_starts_with(struct string str, struct string substring); b32 string_ends_with(struct string str, struct string substring); diff --git a/src/user.c b/src/user.c index 349b23ff..2f99831a 100644 --- a/src/user.c +++ b/src/user.c @@ -439,10 +439,28 @@ INTERNAL SORT_COMPARE_FUNC_DEF(sort_entities, arg_a, arg_b, udata) struct entity *a = *(struct entity **)arg_a; struct entity *b = *(struct entity **)arg_b; - u64 a_index = a->handle.idx; - u64 b_index = b->handle.idx; + i32 res = 0; - return (a_index < b_index) - (a_index > b_index); + if (res == 0) { + /* Sort by layer */ + i32 a_cmp = a->layer; + i32 b_cmp = b->layer; + res = (a_cmp < b_cmp) - (a_cmp > b_cmp); + } + if (res == 0) { + /* Sort by sprite */ + u64 a_cmp = a->sprite.hash; + u64 b_cmp = b->sprite.hash; + res = (a_cmp < b_cmp) - (a_cmp > b_cmp); + } + if (res == 0) { + /* Sort by activation */ + u64 a_cmp = a->activation_tick; + u64 b_cmp = b->activation_tick; + res = (a_cmp < b_cmp) - (a_cmp > b_cmp); + } + + return res; } /* ========================== *