fp: First draft swarming on entity

This commit is contained in:
doyle 2023-09-29 14:50:40 +10:00
parent 9fde0594e1
commit 3829a2bdc9
3 changed files with 96 additions and 28 deletions

View File

@ -880,35 +880,54 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) {
FP_SentinelListLink<FP_GameWaypoint> *first_waypoint = FP_SentinelList_Front(&entity->waypoints);
if (first_waypoint->data.entity != closest_terry->handle) {
FP_GameDirection aggro_direction = FP_GameDirection_Count;
DQN_FOR_UINDEX(dir_index, FP_GameDirection_Count) {
FP_GameEntityHandle slot_entity_handle = closest_terry->aggro_slot[dir_index];
FP_GameEntity *slot_entity = FP_Game_GetEntity(game, slot_entity_handle);
if (FP_Game_IsNilEntity(slot_entity)) {
aggro_direction = DQN_CAST(FP_GameDirection)dir_index;
break;
}
}
FP_SentinelListLink<FP_GameWaypoint> *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool);
link->data.entity = closest_terry->handle;
FP_GameWaypoint *waypoint = &link->data;
waypoint->entity = closest_terry->handle;
if (aggro_direction != FP_GameDirection_Count) {
waypoint->type = FP_GameWaypointType_Side;
waypoint->type_direction = aggro_direction;
closest_terry->aggro_slot[aggro_direction] = entity->handle;
}
}
}
}
while (entity->waypoints.size) {
FP_SentinelListLink<FP_GameWaypoint> *waypoint_link = entity->waypoints.sentinel->next;
FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data.entity);
if (FP_Game_IsNilEntity(waypoint)) {
FP_GameWaypoint const *waypoint = &waypoint_link->data;
FP_GameEntity *waypoint_entity = FP_Game_GetEntity(game, waypoint_link->data.entity);
if (FP_Game_IsNilEntity(waypoint_entity)) {
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
continue;
}
// NOTE: We found a waypoint that is valid to move towards
Dqn_V2 waypoint_pos = FP_Game_CalcEntityWorldPos(game, waypoint->handle);
Dqn_V2 entity_to_waypoint = waypoint_pos - entity_pos;
Dqn_V2 target_pos = FP_Game_CalcWaypointWorldPos(game, waypoint);
Dqn_V2 entity_to_waypoint = target_pos - entity_pos;
// 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 = {};
switch (waypoint_link->data.arrive) {
switch (waypoint->arrive) {
case FP_GameWaypointArrive_Default: {
arrival_threshold = FP_Game_MetersToPixelsNx1(game, 1.f);
} break;
case FP_GameWaypointArrive_WhenWithinEntitySize: {
arrival_threshold = DQN_MAX(waypoint->local_hit_box_size.w, waypoint->local_hit_box_size.h) * waypoint_link->data.value;
arrival_threshold = DQN_MAX(waypoint_entity->local_hit_box_size.w, waypoint_entity->local_hit_box_size.h) * waypoint_link->data.value;
} break;
}
@ -920,7 +939,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
}
// NOTE: We have arrived at the waypoint
if ((entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) && waypoint->type == FP_EntityType_Terry) {
if ((entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) && waypoint_entity->type == FP_EntityType_Terry) {
// NOTE: We had a waypoint to move to Terry because he has
// drawn our aggro and we've arrived at Terry
@ -1330,6 +1349,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
TELY_Render_TextureColourV4(renderer, sprite->asset.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4);
}
// NOTE: Render waypoint entities ==========================================================
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) {
@ -1357,19 +1377,12 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
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<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) {
FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data.entity);
if (FP_Game_IsNilEntity(waypoint))
continue;
Dqn_V2 end = FP_Game_CalcEntityWorldPos(game, waypoint->handle);
Dqn_V2 end = FP_Game_CalcWaypointWorldPos(game, &link->data);
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);

View File

@ -630,3 +630,48 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
DQN_ASSERT(result.size == slice_size);
return result;
}
static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameWaypoint const *waypoint)
{
Dqn_V2 result = {};
if (!game || !waypoint)
return result;
FP_GameEntity *waypoint_entity = FP_Game_GetEntity(game, waypoint->entity);
if (FP_Game_IsNilEntity(waypoint_entity))
return result;
// NOTE: We found a waypoint that is valid to move towards
switch (waypoint->type) {
case FP_GameWaypointType_At: {
result = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle);
} break;
case FP_GameWaypointType_Side: {
Dqn_Rect entity_rect = FP_Game_CalcEntityWorldHitBox(game, waypoint_entity->handle);
switch (waypoint->type_direction) {
case FP_GameDirection_Up: {
result = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w * .5f, entity_rect.pos.y - entity_rect.size.h * .5f);
} break;
case FP_GameDirection_Down: {
result = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w * .5f, entity_rect.pos.y + entity_rect.size.h + (entity_rect.size.h * .5f));
} break;
case FP_GameDirection_Left: {
result = Dqn_V2_InitNx2(entity_rect.pos.x - entity_rect.size.w * .5f, entity_rect.pos.y + entity_rect.size.h * .5f);
} break;
case FP_GameDirection_Right: {
result = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w + entity_rect.size.w * .5f, entity_rect.pos.y + entity_rect.size.h * .5f);
} break;
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
}
} break;
}
return result;
}

View File

@ -63,8 +63,25 @@ enum FP_GameWaypointArrive
FP_GameWaypointArrive_WhenWithinEntitySize,
};
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
};
enum FP_GameDirection
{
FP_GameDirection_Up,
FP_GameDirection_Down,
FP_GameDirection_Left,
FP_GameDirection_Right,
FP_GameDirection_Count,
};
struct FP_GameWaypoint
{
FP_GameWaypointType type;
FP_GameDirection type_direction;
FP_GameEntityHandle entity;
FP_GameWaypointArrive arrive;
Dqn_f32 value;
@ -93,14 +110,6 @@ struct FP_GameEntityAction
uint64_t end_at_clock_ms;
};
enum FP_GameDirection
{
FP_GameDirection_Up,
FP_GameDirection_Down,
FP_GameDirection_Left,
FP_GameDirection_Right,
};
struct FP_GameRenderSprite
{
Dqn_V2 offset;
@ -134,6 +143,7 @@ struct FP_GameEntity
Dqn_FArray<FP_GameRenderSprite, 2> extra_cosmetic_anims;
FP_SentinelList<FP_GameWaypoint> waypoints;
FP_GameEntityHandle aggro_slot[FP_GameDirection_Count];
// NOTE: The entity hit box is positioned at the center of the entity.
Dqn_V2 local_hit_box_size;