fp: Start drafting up swarming of buildings

This commit is contained in:
doyle 2023-09-29 22:44:27 +10:00
parent 158fcb12fe
commit a35cb8d2a6
3 changed files with 117 additions and 7 deletions

View File

@ -91,6 +91,83 @@ TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_A
return result;
}
struct FP_GameSwarmSlot
{
Dqn_usize count;
Dqn_V2 world_pos;
};
Dqn_FArray<FP_GameSwarmSlot, 32> FP_Entity_GetSwarmingPositions(FP_Game *game, FP_GameEntityHandle entity_handle)
{
Dqn_FArray<FP_GameSwarmSlot, 32> result = {};
FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle);
if (FP_Game_IsNilEntity(entity))
return result;
DQN_FOR_UINDEX (dir_index, FP_GameDirection_Count) {
FP_GameSwarm *swarm = entity->swarm + dir_index;
Dqn_f32 const min_dist_between_slots = FP_Game_MetersToPixelsNx1(game, 1.f);
// NOTE: Calculate the number of swarming slots we need for this entity
if (swarm->slots_active == 0) {
switch (DQN_CAST(FP_GameDirection)dir_index) {
case FP_GameDirection_Up: /*FALLTHRU*/
case FP_GameDirection_Down: {
swarm->slots_active = DQN_CAST(Dqn_usize)(entity->local_hit_box_size.w / min_dist_between_slots);
} break;
case FP_GameDirection_Left: /*FALLTHRU*/
case FP_GameDirection_Right: {
swarm->slots_active = DQN_CAST(Dqn_usize)(entity->local_hit_box_size.h / min_dist_between_slots);
} break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
DQN_ASSERT(swarm->slots_active < Dqn_FArray_Max(&swarm->slots));
}
// NOTE: Calculate potential swarming positions ========================
Dqn_Rect entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle);
DQN_FOR_UINDEX (slot_index, swarm->slots_active) {
FP_SentinelList<FP_GameEntityHandle> *list = swarm->slots.data + slot_index;
FP_GameSwarmSlot *slot = Dqn_FArray_Make(&result, Dqn_ZeroMem_No);
slot->count = list->size;
switch (DQN_CAST(FP_GameDirection)dir_index) {
case FP_GameDirection_Up: {
Dqn_V2 start = Dqn_V2_InitNx2(entity_world_hit_box.pos.x,
entity_world_hit_box.pos.y - entity_world_hit_box.size.h * .1f);
slot->world_pos = Dqn_V2_InitNx2(start.x + slot_index * min_dist_between_slots, start.y);
} break;
case FP_GameDirection_Down: {
Dqn_V2 start = Dqn_V2_InitNx2(entity_world_hit_box.pos.x,
entity_world_hit_box.pos.y + entity_world_hit_box.size.h + entity_world_hit_box.size.h * .1f);
slot->world_pos = Dqn_V2_InitNx2(start.x + slot_index * min_dist_between_slots, start.y);
} break;
case FP_GameDirection_Left: {
Dqn_V2 start = Dqn_V2_InitNx2(entity_world_hit_box.pos.x - entity_world_hit_box.size.w * .1f,
entity_world_hit_box.pos.y);
slot->world_pos = Dqn_V2_InitNx2(start.x, start.y + slot_index * min_dist_between_slots);
} break;
case FP_GameDirection_Right: {
Dqn_V2 start = Dqn_V2_InitNx2(entity_world_hit_box.pos.x + entity_world_hit_box.size.w + entity_world_hit_box.size.w * .1f,
entity_world_hit_box.pos.y);
slot->world_pos = Dqn_V2_InitNx2(start.x, start.y + slot_index * min_dist_between_slots);
} break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
}
}
return result;
}
static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_V2 acceleration_meters_per_s)
{
// f"(t) = a
@ -986,6 +1063,13 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
}
}
if (entity->type == FP_EntityType_ClubTerry) {
Dqn_FArray<FP_GameSwarmSlot, 32> candidate_swarm_slots = FP_Entity_GetSwarmingPositions(game, entity->handle);
DQN_ASSERT(candidate_swarm_slots.size);
for (FP_GameSwarmSlot& slot : candidate_swarm_slots)
TELY_Render_CircleColourV4(&platform->renderer, slot.world_pos, 8.f, TELY_RenderShapeMode_Line, TELY_COLOUR_RED_TOMATO_V4);
}
if (entity->flags & FP_GameEntityFlag_RespondsToClubTerry) {
FP_GameFindClosestEntityResult closest_result = FP_Game_FindClosestEntityWithType(game, entity->handle, FP_EntityType_ClubTerry);
if (closest_result.dist_squared < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4.f))) {
@ -993,11 +1077,24 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (first_waypoint->data.entity != closest_result.entity) {
FP_GameEntity *club_terry = FP_Game_GetEntity(game, closest_result.entity);
Dqn_FArray<FP_GameSwarmSlot, 32> candidate_swarm_slots = FP_Entity_GetSwarmingPositions(game, closest_result.entity);
DQN_ASSERT(candidate_swarm_slots.size);
Dqn_f32 closest_swarm_dist = DQN_F32_MAX;
Dqn_V2 closest_swarm_pos = {};
for (FP_GameSwarmSlot& slot : candidate_swarm_slots) {
Dqn_f32 dist = Dqn_V2_Length_V2x2(slot.world_pos, entity_pos);
if (dist < closest_swarm_dist) {
closest_swarm_dist = dist;
closest_swarm_pos = slot.world_pos;
}
}
FP_SentinelListLink<FP_GameWaypoint> *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool);
FP_GameWaypoint *waypoint = &link->data;
waypoint->entity = club_terry->handle;
waypoint->type = FP_GameWaypointType_Side;
waypoint->type_direction = FP_GameDirection_Down;
waypoint->type = FP_GameWaypointType_Offset;
waypoint->offset = closest_swarm_pos - entity_pos;
}
}
}

View File

@ -669,6 +669,10 @@ static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameWaypoint const
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
case FP_GameWaypointType_Offset: {
result = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle) + waypoint->offset;
} break;
}
return result;

View File

@ -66,8 +66,9 @@ enum FP_GameWaypointArrive
enum FP_GameWaypointType
{
FP_GameWaypointType_At, // Move to the specified entity
FP_GameWaypointType_Side, // Move to the side of the entity specified by the direction
FP_GameWaypointType_At, // Move to the specified entity
FP_GameWaypointType_Side, // Move to the side of the entity specified by the direction
FP_GameWaypointType_Offset, // Move to the designed offset from the entity
};
enum FP_GameDirection
@ -82,10 +83,11 @@ enum FP_GameDirection
struct FP_GameWaypoint
{
FP_GameWaypointType type;
FP_GameDirection type_direction;
FP_GameEntityHandle entity;
FP_GameDirection type_direction; // Used if type is `FP_GameWaypointType_Side`
FP_GameEntityHandle entity; // The entity to move to
FP_GameWaypointArrive arrive;
Dqn_f32 value;
Dqn_f32 value; // Used for `arrive` threshold
Dqn_V2 offset; // Used if type is `FP_GameWaypointType_Offset`
};
struct FP_GameEntitySpawnList
@ -120,6 +122,12 @@ struct FP_GameRenderSprite
uint64_t started_at_clock_ms;
};
struct FP_GameSwarm
{
Dqn_FArray<FP_SentinelList<FP_GameEntityHandle>, 8> slots;
Dqn_usize slots_active;
};
struct FP_GameEntity
{
FP_GameEntity *next;
@ -145,6 +153,7 @@ struct FP_GameEntity
FP_SentinelList<FP_GameWaypoint> waypoints;
FP_GameEntityHandle aggro_slot[FP_GameDirection_Count];
FP_GameSwarm swarm[FP_GameDirection_Count];
// NOTE: The entity hit box is positioned at the center of the entity.
Dqn_V2 local_hit_box_size;