diff --git a/feely_pona.cpp b/feely_pona.cpp index 1ed4363..b90b227 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -205,6 +205,7 @@ void TELY_DLL_Init(void *user_data) // NOTE: Hero { FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry"); + entity->type = FP_EntityType_Terry; entity->local_pos = Dqn_V2_InitNx2(1334, 396); entity->action_to_anim_mapping = game->terry_action_mappings; entity->size_scale = Dqn_V2_InitNx1(0.25f); @@ -282,13 +283,31 @@ 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(0, platform->core.window_size.y * .5f); + 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->spawn_cap = 1; + + 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; + + FP_Game_PushParentEntity(game, entity->handle); + { + { + FP_GameEntity *waypoint = FP_Game_MakeEntityPointerF(game, "Waypoint"); + waypoint->local_pos = Dqn_V2_InitNx2(800.f, 100.f); + 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_Game_PopParentEntity(game); } uint16_t font_size = 18; @@ -676,13 +695,17 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *)&entity->action.state; move_entity = *state == FP_EntitySmoochieState_Run || *state == FP_EntitySmoochieState_Idle; } break; + + case FP_EntityType_Merchant: break; } - acceleration = dir_vector * 10000000.f; - if (dir_vector.x) - entity->direction = dir_vector.x > 0.f ? FP_GameDirection_Right : FP_GameDirection_Left; - else if (dir_vector.y) - entity->direction = dir_vector.y > 0.f ? FP_GameDirection_Down : FP_GameDirection_Up; + if (move_entity) { + acceleration = dir_vector * 10000000.f; + if (dir_vector.x) + entity->direction = dir_vector.x > 0.f ? FP_GameDirection_Right : FP_GameDirection_Left; + else if (dir_vector.y) + entity->direction = dir_vector.y > 0.f ? FP_GameDirection_Down : FP_GameDirection_Up; + } } } @@ -718,32 +741,31 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, } } } + } + #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 * game->tile_size + game->tile_size * .5f, waypoint->pos.y * game->tile_size + game->tile_size * .5f); - TELY_Render_CircleColourV4(renderer, circle_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4); - } + // 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); + } - if (entity->waypoints->next != entity->waypoints) { - FP_GameWaypoint *waypoint = entity->waypoints->next; - Dqn_V2I target_tile = entity->waypoints->next->pos; - Dqn_V2 target_pos = Dqn_V2_InitNx2(target_tile.x * game->tile_size + game->tile_size *.5f, target_tile.y * game->tile_size + game->tile_size * .5f); - Dqn_V2 entity_to_target_pos = target_pos - entity_world_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 * 700'000.f; - } + 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; } } } - #endif // NOTE: Core equations of motion ========================================================== bool has_collision = false; @@ -869,12 +891,51 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, // NOTE: Mob spawner ======================================================================= if (entity->flags & FP_GameEntityFlag_MobSpawner) { - if (entity->spawn_count < entity->spawn_cap) { + // 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); + } + + if (entity->spawn_count < 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_Game_EntityAddMob(game, entity_world_pos); + 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); + + // 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; + + 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); + } } } } diff --git a/feely_pona.h b/feely_pona.h index db6fd58..0f96b21 100644 --- a/feely_pona.h +++ b/feely_pona.h @@ -3,6 +3,12 @@ #include "feely_pona_unity.h" #endif +#define FP_SentinelDoublyLinkedList_Insert(list, item) \ + item->next = list; \ + item->prev = list->prev; \ + item->next->prev = item; \ + item->prev->next = item; + enum FP_ProfileZone { FP_ProfileZone_FPUpdate = TELY_ProfileZone_Count, diff --git a/feely_pona_entity.h b/feely_pona_entity.h index 0affe4f..c618462 100644 --- a/feely_pona_entity.h +++ b/feely_pona_entity.h @@ -5,6 +5,7 @@ enum FP_EntityType { + FP_EntityType_Nil, FP_EntityType_Terry, FP_EntityType_Smoochie, FP_EntityType_Merchant, diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index b8bbf6c..99f84ea 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -292,6 +292,9 @@ 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); + if (new_entity_generation > entity_generation) { // NOTE: Update the incremented handle disassociating all prior handles // to this entity which would reference older generation values @@ -672,7 +675,7 @@ static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos) entity->local_pos = pos; entity->size_scale = Dqn_V2_InitNx1(.25f); entity->action_to_anim_mapping = game->smoochie_action_mappings; - entity->local_hit_box_size = Dqn_V2_InitV2I(game->smoochie_sprite_sheet.sprite_size); + 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; diff --git a/feely_pona_game.h b/feely_pona_game.h index 3584bf3..0cb8484 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -13,6 +13,7 @@ enum FP_GameEntityFlag FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox = 1 << 5, FP_GameEntityFlag_NonTraversable = 1 << 6, FP_GameEntityFlag_MobSpawner = 1 << 7, + FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 8, }; enum FP_GameShapeType @@ -52,6 +53,13 @@ struct FP_GameWaypoint FP_GameWaypoint *prev; }; +struct FP_GameEntitySpawnList +{ + FP_GameEntityHandle entity; + FP_GameEntitySpawnList *next; + FP_GameEntitySpawnList *prev; +}; + enum FP_GameEntityActionFlag { FP_GameEntityActionFlag_StateTransition = 1 << 0, @@ -112,6 +120,8 @@ struct FP_GameEntity 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;