diff --git a/feely_pona.cpp b/feely_pona.cpp index 171beff..adf99e4 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -3,6 +3,7 @@ #include "feely_pona_unity.h" #endif +Dqn_f32 const PHYSICS_STEP = 1 / 60.f; struct FP_GlobalAnimations { Dqn_String8 terry_walk_idle = DQN_STRING8("terry_walk_idle"); @@ -257,28 +258,60 @@ void TELY_DLL_Init(void *user_data) // NOTE: Mob spawner =========================================================================== { FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob spawner"); - entity->local_pos = Dqn_V2_InitNx2(50.f, platform->core.window_size.y * .5f); - entity->flags |= FP_GameEntityFlag_Clickable; - entity->flags |= FP_GameEntityFlag_MoveByKeyboard; - entity->flags |= FP_GameEntityFlag_MoveByMouse; - entity->flags |= FP_GameEntityFlag_MoveByGamepad; - entity->flags |= FP_GameEntityFlag_MobSpawner; + { + entity->local_pos = Dqn_V2_InitNx2(50.f, platform->core.window_size.y * .5f); + entity->local_hit_box_size = Dqn_V2_InitNx1(32); + entity->flags |= FP_GameEntityFlag_Clickable; + entity->flags |= FP_GameEntityFlag_MoveByKeyboard; + entity->flags |= FP_GameEntityFlag_MoveByMouse; + entity->flags |= FP_GameEntityFlag_MoveByGamepad; + entity->flags |= FP_GameEntityFlag_MobSpawner; - entity->spawn_cap = 16; - entity->spawn_list = TELY_ChunkPool_New(game->chunk_pool, FP_GameEntitySpawnList); - entity->spawn_list->next = entity->spawn_list; - entity->spawn_list->prev = entity->spawn_list; + entity->spawn_cap = 16; + entity->spawn_list = FP_SentinelList_Init(game->chunk_pool); + + FP_GameShape *shape = Dqn_FArray_Make(&entity->shapes, Dqn_ZeroMem_Yes); + shape->type = FP_GameShapeType_Rect; + shape->p1 = {}; + shape->p2 = entity->local_hit_box_size; + shape->render_mode = TELY_RenderShapeMode_Line; + shape->colour = TELY_COLOUR_BLUE_CADET_V4; + } FP_Game_PushParentEntity(game, entity->handle); { { - FP_GameEntity *waypoint = FP_Game_MakeEntityPointerF(game, "Waypoint"); - waypoint->local_pos = Dqn_V2_InitNx2(800.f, 100.f); + FP_GameEntity *waypoint = FP_Game_MakeEntityPointerF(game, "Waypoint"); + waypoint->local_pos = Dqn_V2_InitNx2(800.f, -200.f); + waypoint->local_hit_box_size = Dqn_V2_InitNx1(32); waypoint->flags |= FP_GameEntityFlag_Clickable; waypoint->flags |= FP_GameEntityFlag_MoveByKeyboard; waypoint->flags |= FP_GameEntityFlag_MoveByMouse; waypoint->flags |= FP_GameEntityFlag_MoveByGamepad; waypoint->flags |= FP_GameEntityFlag_MobSpawnerWaypoint; + + FP_GameShape *shape = Dqn_FArray_Make(&waypoint->shapes, Dqn_ZeroMem_Yes); + shape->type = FP_GameShapeType_Circle; + shape->circle_radius = 16.f; + shape->render_mode = TELY_RenderShapeMode_Line; + shape->colour = TELY_COLOUR_BLUE_CADET_V4; + } + + { + FP_GameEntity *waypoint = FP_Game_MakeEntityPointerF(game, "Waypoint"); + waypoint->local_pos = Dqn_V2_InitNx2(932.f, 200.f); + waypoint->local_hit_box_size = Dqn_V2_InitNx1(32); + waypoint->flags |= FP_GameEntityFlag_Clickable; + waypoint->flags |= FP_GameEntityFlag_MoveByKeyboard; + waypoint->flags |= FP_GameEntityFlag_MoveByMouse; + waypoint->flags |= FP_GameEntityFlag_MoveByGamepad; + waypoint->flags |= FP_GameEntityFlag_MobSpawnerWaypoint; + + FP_GameShape *shape = Dqn_FArray_Make(&waypoint->shapes, Dqn_ZeroMem_Yes); + shape->type = FP_GameShapeType_Circle; + shape->circle_radius = 16.f; + shape->render_mode = TELY_RenderShapeMode_Line; + shape->colour = TELY_COLOUR_BLUE_CADET_V4; } } FP_Game_PopParentEntity(game); @@ -444,7 +477,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } Dqn_V2 dash_acceleration = dash_dir * 400'000'000.f; - Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s); + Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP); entity->velocity = (dash_acceleration * t) + entity->velocity * 2.0f; } @@ -618,7 +651,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } } -void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input) +void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input) { Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate); @@ -662,7 +695,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, Dqn_ProfilerZone update_zone = Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8("FP_Update: Entity loop"), FP_ProfileZone_FPUpdate_EntityLoop); for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) { FP_GameEntity *entity = it.entity; - entity->alive_time_s += input->delta_s; + entity->alive_time_s += PHYSICS_STEP; // NOTE: Move entity by keyboard and gamepad =============================================== Dqn_V2 acceleration = {}; @@ -671,16 +704,17 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, bool move_entity = false; switch (entity->type) { case FP_EntityType_Terry: { - FP_EntityTerryState *state = DQN_CAST(FP_EntityTerryState *)&entity->action.state; - move_entity = *state == FP_EntityTerryState_Run || *state == FP_EntityTerryState_Idle; + auto *state = DQN_CAST(FP_EntityTerryState *)&entity->action.state; + move_entity = *state == FP_EntityTerryState_Run || *state == FP_EntityTerryState_Idle; } break; case FP_EntityType_Smoochie: { - FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *)&entity->action.state; - move_entity = *state == FP_EntitySmoochieState_Run || *state == FP_EntitySmoochieState_Idle; + auto *state = DQN_CAST(FP_EntitySmoochieState *)&entity->action.state; + move_entity = *state == FP_EntitySmoochieState_Run || *state == FP_EntitySmoochieState_Idle; } break; - case FP_EntityType_Merchant: break; + case FP_EntityType_Merchant: move_entity = true; break; + case FP_EntityType_Nil: move_entity = true; break; } if (move_entity) { @@ -693,61 +727,33 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, } } - // NOTE: Stalk entity ====================================================================== - Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); - #if 0 - { - FP_GameEntity *stalk_entity = FP_Game_GetEntity(game, entity->stalk_entity); - if (stalk_entity->handle.id) { - Dqn_Profiler_ZoneScopeWithIndex("FP_Update: Path finding", FP_ProfileZone_FPUpdate_PathFinding); - Dqn_V2 stalk_world_pos = FP_Game_CalcEntityWorldPos(game, stalk_entity->handle); - Dqn_V2I stalk_tile = Dqn_V2I_InitNx2(stalk_world_pos.x / game->tile_size, stalk_world_pos.y / game->tile_size); - if (entity->stalk_entity_last_known_tile != stalk_tile) { - entity->stalk_entity_last_known_tile = stalk_tile; - - // NOTE: Dealloc all waypoints - for (FP_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; ) { - FP_GameWaypoint *next = waypoint->next; - TELY_ChunkPool_Dealloc(game->chunk_pool, waypoint); - waypoint = next; - } - entity->waypoints->next = entity->waypoints; - entity->waypoints->prev = entity->waypoints; - - Dqn_Slice path_find = FP_Game_AStarPathFind(game, &platform->frame_arena, platform, entity->handle, stalk_tile); - for (Dqn_usize index = path_find.size - 1; index < path_find.size; index--) { - FP_GameWaypoint *waypoint = TELY_ChunkPool_New(game->chunk_pool, FP_GameWaypoint); - waypoint->pos = path_find.data[index]; - waypoint->next = entity->waypoints; - waypoint->prev = entity->waypoints->prev; - waypoint->next->prev = waypoint; - waypoint->prev->next = waypoint; - } + // NOTE: Determine acceleration to move towards next waypoint if we have one + if (acceleration.x == 0 && acceleration.y == 0) { + while (entity->waypoints.size) { + FP_SentinelListLink *waypoint_link = entity->waypoints.sentinel->next; + FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data); + if (FP_Game_IsNilEntity(waypoint)) { + FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); + continue; } - } - } - #endif - // NOTE: Render the waypoints - for (FP_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; waypoint = waypoint->next) { - Dqn_V2 circle_pos = Dqn_V2_InitNx2(waypoint->pos.x, waypoint->pos.y); - TELY_Render_CircleColourV4(renderer, circle_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4); - } + // NOTE: We found a waypoint that is valid to move towards + Dqn_V2 waypoint_pos = FP_Game_CalcEntityWorldPos(game, waypoint->handle); + Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); + Dqn_V2 entity_to_waypoint = waypoint_pos - entity_pos; - if (entity->waypoints->next != entity->waypoints) { - FP_GameWaypoint *waypoint = entity->waypoints->next; - Dqn_V2 target_pos = Dqn_V2_InitV2I(entity->waypoints->next->pos); - Dqn_V2 entity_to_target_pos = target_pos - entity_world_pos; - - if (Dqn_V2_LengthSq(entity_to_target_pos) < DQN_SQUARED(entity->local_hit_box_size.x * .5f)) { - waypoint->next->prev = waypoint->prev; - waypoint->prev->next = waypoint->next; - TELY_ChunkPool_Dealloc(game->chunk_pool, waypoint); - } else { - Dqn_V2 entity_to_target_pos_norm = Dqn_V2_Normalise(entity_to_target_pos); - if (acceleration.x == 0 && acceleration.y == 0) { - acceleration = entity_to_target_pos_norm * 1'000'000.f; + // NOTE: Check if we've arrived at the waypoint + Dqn_f32 dist_to_waypoint_sq = Dqn_V2_LengthSq(entity_to_waypoint); + Dqn_f32 arrival_threshold = DQN_MIN(DQN_SQUARED(entity->local_hit_box_size.x * .5f), 10.f); + if (dist_to_waypoint_sq < arrival_threshold) { + FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); + continue; } + + // NOTE: We haven't arrived yet, calculate an acceleration vector to the waypoint + Dqn_V2 entity_to_waypoint_norm = Dqn_V2_Normalise(entity_to_waypoint); + acceleration = entity_to_waypoint_norm * 1'000'000.f; + break; } } @@ -757,7 +763,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, // f"(t) = a // f'(t) = at + v // f (t) = 0.5f*a(t^2) + vt + p - Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s); + Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP); Dqn_f32 t_squared = DQN_SQUARED(t); Dqn_f32 velocity_falloff_coefficient = 0.25f; @@ -785,6 +791,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, if (collider->handle == entity->handle) continue; + // TODO(doyle): Calculate the list of collidables at the start of the frame + if ((collider->flags & FP_GameEntityFlag_NonTraversable) == 0) + continue; + // NOTE: Sweep collider with half the radius of the source entity Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle); Dqn_Rect swept_collider_world_hit_box = collider_world_hit_box; @@ -875,50 +885,33 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, // NOTE: Mob spawner ======================================================================= if (entity->flags & FP_GameEntityFlag_MobSpawner) { - // NOTE: Check if any spawned entities dies to remove it from the spawn cap tracker - for (FP_GameEntitySpawnList *link = entity->spawn_list->next; link != entity->spawn_list;) { - FP_GameEntity *spawned_entity = FP_Game_GetEntity(game, link->entity); - if (spawned_entity) { - // NOTE: Spawned entity is still alive - link = link->next; - continue; - } - DQN_ASSERT(entity->spawn_count); - entity->spawn_count--; - - // NOTE: Entity is dead remove it from the linked list - FP_GameEntitySpawnList *link_to_delete = link; - link->next->prev = link->prev; - link->prev->next = link->next; - link = link->next; - TELY_ChunkPool_Dealloc(game->chunk_pool, link_to_delete); + // NOTE: Flush any spawn entities that are dead + for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(&entity->spawn_list, &link); ) { + FP_GameEntity *spawned_entity = FP_Game_GetEntity(game, link->data); + if (FP_Game_IsNilEntity(spawned_entity)) // NOTE: Entity is dead remove it from the linked list + link = FP_SentinelList_Erase(&entity->spawn_list, link, game->chunk_pool); } - if (entity->spawn_count < entity->spawn_cap) { // NOTE: Spawn new entities + if (entity->spawn_list.size < entity->spawn_cap) { // NOTE: Spawn new entities if (input->timer_s >= entity->next_spawn_timestamp_s) { entity->next_spawn_timestamp_s = DQN_CAST(uint64_t)(input->timer_s + 5.f); - entity->spawn_count++; - FP_GameEntitySpawnList *item = TELY_ChunkPool_New(game->chunk_pool, FP_GameEntitySpawnList); - item->entity = FP_Game_EntityAddMob(game, entity_world_pos); - FP_SentinelDoublyLinkedList_Insert(entity->spawn_list, item); + Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); + FP_SentinelListLink *link = FP_SentinelList_Make(&entity->spawn_list, game->chunk_pool); + link->data = FP_Game_EntityAddMob(game, entity_world_pos); - // NOTE: Setup the mob with a sentinel waypoint - FP_GameEntity *mob = FP_Game_GetEntity(game, item->entity); - mob->waypoints = TELY_ChunkPool_New(game->chunk_pool, FP_GameWaypoint); - mob->waypoints->next = mob->waypoints; - mob->waypoints->prev = mob->waypoints; + // NOTE: Setup the mob with waypoints + FP_GameEntity *mob = FP_Game_GetEntity(game, link->data); + mob->waypoints = FP_SentinelList_Init(game->chunk_pool); for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) { if ((waypoint_entity->flags & FP_GameEntityFlag_MobSpawnerWaypoint) == 0) continue; // NOTE: Add the waypoint - FP_GameWaypoint *waypoint = TELY_ChunkPool_New(game->chunk_pool, FP_GameWaypoint); - Dqn_V2 waypoint_pos = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle); - waypoint->pos = Dqn_V2I_InitV2(waypoint_pos); - FP_SentinelDoublyLinkedList_Insert(mob->waypoints, waypoint); + FP_SentinelListLink *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool); + waypoint->data = waypoint_entity->handle; } } } @@ -955,7 +948,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, attack_dir_vector.x = -1.f; Dqn_V2 acceleration = attack_dir_vector * 500'000.f; - Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s); + Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP); Dqn_f32 t_squared = DQN_SQUARED(t); Dqn_V2 delta_p = (acceleration * 0.5f * t_squared) + (defender->velocity * t); @@ -991,7 +984,6 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) // NOTE: Draw entities ========================================================================= for (FP_GameEntityIterator it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &it, game->root_entity); ) { FP_GameEntity *entity = it.entity; - entity->alive_time_s += input->delta_s; // NOTE: Render shapes in entity =========================================================== Dqn_V2 world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); @@ -1058,6 +1050,18 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_Render_TextureColourV4(renderer, sprite.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4); } + if (entity->flags & FP_GameEntityFlag_MobSpawner) { + Dqn_V2 start = world_pos; + for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) { + if ((waypoint_entity->flags & FP_GameEntityFlag_MobSpawnerWaypoint) == 0) + continue; + + Dqn_V2 end = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle); + TELY_Render_LineColourV4(renderer, start, end, TELY_COLOUR_BLUE_CADET_V4, 2.f); + start = end; + } + } + // NOTE: Render attack box ================================================================= { Dqn_Rect attack_box = FP_Game_CalcEntityAttackWorldHitBox(game, entity->handle); @@ -1071,6 +1075,21 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) Dqn_Rect world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle); if (game->clicked_entity == entity->handle) { TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, TELY_COLOUR_WHITE_PALE_GOLDENROD_V4); + + // NOTE: Draw the waypoints that the entity is moving along + if (entity->waypoints.size) { + Dqn_V2 start = world_pos; + for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) { + FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data); + if (FP_Game_IsNilEntity(waypoint)) + continue; + + Dqn_V2 end = FP_Game_CalcEntityWorldPos(game, waypoint->handle); + TELY_Render_LineColourV4(renderer, start, end, TELY_COLOUR_WHITE_PALE_GOLDENROD_V4, 2.f); + start = end; + } + } + } else if (game->hot_entity == entity->handle || (entity->flags & FP_GameEntityFlag_DrawHitBox)) { Dqn_V4 hot_colour = game->hot_entity == entity->handle ? TELY_COLOUR_RED_TOMATO_V4 : TELY_Colour_V4Alpha(TELY_COLOUR_YELLOW_SANDY_V4, .5f); TELY_Render_RectColourV4(renderer, world_hit_box, TELY_RenderShapeMode_Line, hot_colour); @@ -1158,11 +1177,10 @@ void TELY_DLL_FrameUpdate(void *user_data) } } - Dqn_f32 const PHYSICS_STEP = 1 / 60.f; for (game->delta_s_accumulator += DQN_CAST(Dqn_f32)input->delta_s; game->delta_s_accumulator > PHYSICS_STEP; game->delta_s_accumulator -= PHYSICS_STEP) { - FP_Update(platform, game, renderer, input); + FP_Update(platform, game, input); } FP_Render(game, platform, renderer); diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 385a114..9b8e0ad 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -203,12 +203,9 @@ static FP_GameEntity *FP_Game_MakeEntityPointerFV(FP_Game *game, DQN_FMT_STRING_ result->handle.id = (game->entities.size - 1) & FP_GAME_ENTITY_HANDLE_INDEX_MASK; } - result->size_scale = Dqn_V2_InitNx1(1); - result->parent = FP_Game_ActiveParentEntityPointer(game); - result->name = TELY_ChunkPool_AllocFmtFV(game->chunk_pool, fmt, args); - result->waypoints = TELY_ChunkPool_New(game->chunk_pool, FP_GameWaypoint); - result->waypoints->next = result->waypoints; - result->waypoints->prev = result->waypoints; + result->size_scale = Dqn_V2_InitNx1(1); + result->parent = FP_Game_ActiveParentEntityPointer(game); + result->name = TELY_ChunkPool_AllocFmtFV(game->chunk_pool, fmt, args); // NOTE: Attach entity as a child to the parent FP_GameEntity *parent = result->parent; @@ -292,8 +289,8 @@ static void FP_Game_DetachEntityIntoFreeList(FP_Game *game, FP_GameEntityHandle if (entity->name.size) TELY_ChunkPool_Dealloc(game->chunk_pool, entity->name.data); - if (entity->spawn_list) - TELY_ChunkPool_Dealloc(game->chunk_pool, entity->spawn_list); + FP_SentinelList_Deinit(&entity->spawn_list, game->chunk_pool); + FP_SentinelList_Deinit(&entity->waypoints, game->chunk_pool); if (new_entity_generation > entity_generation) { // NOTE: Update the incremented handle disassociating all prior handles @@ -654,17 +651,15 @@ static FP_GameEntityHandle FP_Game_EntityAddWallAtTile(FP_Game *game, Dqn_String static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos) { - FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob"); - entity->type = FP_EntityType_Smoochie; - entity->local_pos = pos; - entity->size_scale = Dqn_V2_InitNx1(.25f); - entity->local_hit_box_size = Dqn_V2_InitNx2(428, 471) * entity->size_scale; - entity->flags |= FP_GameEntityFlag_Clickable; - entity->flags |= FP_GameEntityFlag_MoveByKeyboard; - entity->flags |= FP_GameEntityFlag_MoveByMouse; - entity->flags |= FP_GameEntityFlag_NonTraversable; - entity->stalk_entity = game->player; - - FP_GameEntityHandle result = entity->handle; + FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob"); + entity->type = FP_EntityType_Smoochie; + entity->local_pos = pos; + entity->size_scale = Dqn_V2_InitNx1(.25f); + entity->local_hit_box_size = Dqn_V2_InitNx2(428, 471) * entity->size_scale; + entity->flags |= FP_GameEntityFlag_Clickable; + entity->flags |= FP_GameEntityFlag_MoveByKeyboard; + entity->flags |= FP_GameEntityFlag_MoveByMouse; + entity->flags |= FP_GameEntityFlag_NonTraversable; + FP_GameEntityHandle result = entity->handle; return result; } diff --git a/feely_pona_game.h b/feely_pona_game.h index ac372f6..6c60f80 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -92,36 +92,33 @@ struct FP_GameEntity FP_GameEntity *last_child; FP_GameEntity *parent; - FP_EntityType type; - Dqn_String8 name; - FP_GameEntityHandle handle; + FP_EntityType type; + Dqn_String8 name; + FP_GameEntityHandle handle; - Dqn_V2 size_scale; - FP_GameEntityAction action; - Dqn_V2 velocity; + Dqn_V2 size_scale; + FP_GameEntityAction action; + Dqn_V2 velocity; - FP_GameEntityHandle stalk_entity; - Dqn_V2I stalk_entity_last_known_tile; - FP_GameWaypoint *waypoints; + FP_SentinelList waypoints; // NOTE: The entity hit box is positioned at the center of the entity. - Dqn_V2 local_hit_box_size; - Dqn_V2 local_hit_box_offset; + Dqn_V2 local_hit_box_size; + Dqn_V2 local_hit_box_offset; - Dqn_V2 attack_box_size; - Dqn_V2 attack_box_offset; + Dqn_V2 attack_box_size; + Dqn_V2 attack_box_offset; - Dqn_FArray spawner_waypoints; - FP_GameEntitySpawnList *spawn_list; - uint64_t next_spawn_timestamp_s; - uint64_t spawn_count; - uint64_t spawn_cap; + Dqn_FArray spawner_waypoints; + FP_SentinelList spawn_list; + uint64_t next_spawn_timestamp_s; + uint64_t spawn_cap; - uint64_t flags; - FP_GameDirection direction; - Dqn_V2 local_pos; - Dqn_f64 alive_time_s; - Dqn_FArray shapes; + uint64_t flags; + FP_GameDirection direction; + Dqn_V2 local_pos; + Dqn_f64 alive_time_s; + Dqn_FArray shapes; }; struct FP_GameEntityIterator diff --git a/feely_pona_stdlib.h b/feely_pona_stdlib.h new file mode 100644 index 0000000..8e9e1cb --- /dev/null +++ b/feely_pona_stdlib.h @@ -0,0 +1,88 @@ +#if defined(__clang__) +#pragma once +#include "feely_pona_unity.h" +#endif + +template +struct FP_SentinelListLink +{ + T data; + FP_SentinelListLink *next; + FP_SentinelListLink *prev; +}; + +template +struct FP_SentinelList +{ + Dqn_usize size; + FP_SentinelListLink *sentinel; +}; + +template +bool FP_SentinelList_Iterate(FP_SentinelList *list, FP_SentinelListLink **it) +{ + if (!list || !list->sentinel || !it) + return false; + + if (!DQN_CHECK(list->sentinel->next && list->sentinel->prev)) + return false; + + if (!(*it)) + *it = list->sentinel; + + bool result = list && (*it)->next != list->sentinel; + if (result) + (*it) = (*it)->next; + return result; +} + +template +FP_SentinelList FP_SentinelList_Init(TELY_ChunkPool *pool) +{ + FP_SentinelList result = {}; + result.sentinel = TELY_ChunkPool_New(pool, FP_SentinelListLink); + result.sentinel->next = result.sentinel->prev = result.sentinel; + return result; +} + +template +FP_SentinelListLink *FP_SentinelList_Make(FP_SentinelList *list, TELY_ChunkPool *pool) +{ + DQN_ASSERT(list->sentinel->next); + DQN_ASSERT(list->sentinel->prev); + DQN_ASSERT(list->sentinel->prev->next == list->sentinel); + DQN_ASSERT(list->sentinel->next->prev == list->sentinel); + + FP_SentinelListLink *result = TELY_ChunkPool_New(pool, FP_SentinelListLink); + result->next = list->sentinel; + result->prev = list->sentinel->prev; + result->next->prev = result; + result->prev->next = result; + list->size++; + return result; +} + +template +FP_SentinelListLink *FP_SentinelList_Erase(FP_SentinelList *list, FP_SentinelListLink *link, TELY_ChunkPool *pool) +{ + FP_SentinelListLink *result = link->prev; + link->next->prev = link->prev; + link->prev->next = link->next; + TELY_ChunkPool_Dealloc(pool, link); + list->size--; + return result; +} + +template +void FP_SentinelList_Deinit(FP_SentinelList *list, TELY_ChunkPool *pool) +{ + if (!list || !pool) + return; + + for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(list, &link); ) + link = FP_SentinelList_Erase(list, link, pool); + + TELY_ChunkPool_Dealloc(pool, list->sentinel); + *list = {}; +} + diff --git a/feely_pona_unity.h b/feely_pona_unity.h index 2f80954..0a5ef93 100644 --- a/feely_pona_unity.h +++ b/feely_pona_unity.h @@ -61,6 +61,7 @@ DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: unreferenced function with inte #include "External/tely/tely_rfui.cpp" // NOTE: feely_pona ================================================================================ +#include "feely_pona_stdlib.h" #include "feely_pona_entity.h" #include "feely_pona_game.h" #include "feely_pona.h"