fp: Rejig the mob spawner to use a fixed-waypoint path

This commit is contained in:
doyle 2023-09-24 19:11:44 +10:00
parent bff3fc759d
commit 30a69e40e9
5 changed files with 111 additions and 30 deletions

View File

@ -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);
}
}
}
}

View File

@ -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,

View File

@ -5,6 +5,7 @@
enum FP_EntityType
{
FP_EntityType_Nil,
FP_EntityType_Terry,
FP_EntityType_Smoochie,
FP_EntityType_Merchant,

View File

@ -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;

View File

@ -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<Dqn_V2, 8> spawner_waypoints;
FP_GameEntitySpawnList *spawn_list;
uint64_t next_spawn_timestamp_s;
uint64_t spawn_count;
uint64_t spawn_cap;