fp: Improve swarming of buildings
This commit is contained in:
parent
11cab9a726
commit
d564ad225d
192
feely_pona.cpp
192
feely_pona.cpp
@ -177,11 +177,15 @@ static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle,
|
||||
if (collider->type == FP_EntityType_Smoochie || collider->type == FP_EntityType_Clinger || collider->type == FP_EntityType_Catfish) {
|
||||
entity_collides_with_collider = false;
|
||||
} else if (FP_Entity_IsBuildingForMobs(collider)) {
|
||||
#if 0
|
||||
// NOTE: We disable collision on buildings we have visited to avoid some
|
||||
// problems ...
|
||||
if (FP_SentinelList_Find(&entity->buildings_visited, collider->handle)) {
|
||||
entity_collides_with_collider = false;
|
||||
}
|
||||
#else
|
||||
entity_collides_with_collider = false;
|
||||
#endif
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -439,7 +443,10 @@ void TELY_DLL_Init(void *user_data)
|
||||
game->merchant_phone_company = FP_Entity_CreateMerchantPhoneCompany(game, base_top_right, "PhoneCompany");
|
||||
}
|
||||
|
||||
FP_Entity_CreateClubTerry(game, Dqn_V2_InitNx2(567, -191), "Club Terry");
|
||||
FP_Entity_CreateClubTerry(game, Dqn_V2_InitNx2(+500, -191), "Club Terry");
|
||||
FP_Entity_CreateAirportTerry(game, Dqn_V2_InitNx2(+200, -191), "Airport Terry");
|
||||
FP_Entity_CreateKennelTerry(game, Dqn_V2_InitNx2(-300, -191), "Kennel Terry");
|
||||
FP_Entity_CreateChurchTerry(game, Dqn_V2_InitNx2(-800, -191), "Church Terry");
|
||||
|
||||
game->tile_size = 37;
|
||||
Dqn_V2I max_tile = platform->core.window_size / game->tile_size;
|
||||
@ -1092,8 +1099,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
|
||||
|
||||
if (action_has_finished) {
|
||||
if (!FP_Game_IsNilEntityHandle(game, entity->building_patron)) {
|
||||
FP_GameEntity *patron = FP_Game_GetEntity(game, entity->building_patron);
|
||||
patron->flags &= ~FP_GameEntityFlag_OccupiedInBuilding;
|
||||
FP_GameEntity *patron = FP_Game_GetEntity(game, entity->building_patron);
|
||||
patron->flags &= ~(FP_GameEntityFlag_OccupiedInBuilding | FP_GameEntityFlag_RespondsToBuildings | FP_GameEntityFlag_PointOfInterestHeart);
|
||||
patron->faction = FP_GameEntityFaction_Friendly;
|
||||
patron->converted_faction = true;
|
||||
}
|
||||
entity->building_patron = {};
|
||||
FP_Game_EntityTransitionState(game, entity, FP_EntityChurchTerryState_Idle);
|
||||
@ -1361,22 +1370,44 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
if (acceleration_meters_per_s.x == 0 && acceleration_meters_per_s.y == 0) {
|
||||
Dqn_V2 entity_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
|
||||
|
||||
if (entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) {
|
||||
FP_GameFindClosestEntityResult closest_result = FP_Game_FindClosestEntityWithType(game, entity->handle, FP_EntityType_Terry);
|
||||
Dqn_f32 aggro_dist_threshold = FP_Game_MetersToPixelsNx1(game, 4.f);
|
||||
if (entity->flags & FP_GameEntityFlag_Aggros && entity->faction != FP_GameEntityFaction_Nil) {
|
||||
FP_GameFindClosestEntityResult closest_defender = {};
|
||||
closest_defender.dist_squared = DQN_F32_MAX;
|
||||
closest_defender.pos = Dqn_V2_InitNx1(DQN_F32_MAX);
|
||||
|
||||
if (closest_result.dist_squared < DQN_SQUARED(aggro_dist_threshold)) {
|
||||
bool has_waypoint_to_terry = false;
|
||||
FP_GameEntityFaction enemy_faction =
|
||||
entity->faction == FP_GameEntityFaction_Friendly
|
||||
? FP_GameEntityFaction_Foe
|
||||
: FP_GameEntityFaction_Friendly;
|
||||
|
||||
for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->root_entity); ) {
|
||||
FP_GameEntity *it_entity = defender_it.entity;
|
||||
Dqn_V2 pos = FP_Game_CalcEntityWorldPos(game, it_entity->handle);
|
||||
Dqn_f32 dist = Dqn_V2_LengthSq_V2x2(pos, entity_pos);
|
||||
|
||||
if (it_entity->faction != enemy_faction)
|
||||
continue;
|
||||
|
||||
if (dist < closest_defender.dist_squared) {
|
||||
closest_defender.pos = pos;
|
||||
closest_defender.dist_squared = dist;
|
||||
closest_defender.entity = it_entity->handle;
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_f32 aggro_dist_threshold = FP_Game_MetersToPixelsNx1(game, 4.f);
|
||||
if (closest_defender.dist_squared < DQN_SQUARED(aggro_dist_threshold)) {
|
||||
bool has_waypoint_to_defender = false;
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr;
|
||||
!has_waypoint_to_terry && FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
has_waypoint_to_terry = link->data.entity == closest_result.entity;
|
||||
!has_waypoint_to_defender && FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
has_waypoint_to_defender = link->data.entity == closest_defender.entity;
|
||||
}
|
||||
|
||||
if (!has_waypoint_to_terry) {
|
||||
FP_GameEntity *terry = FP_Game_GetEntity(game, closest_result.entity);
|
||||
if (!has_waypoint_to_defender) {
|
||||
FP_GameEntity *defender = FP_Game_GetEntity(game, closest_defender.entity);
|
||||
FP_GameDirection aggro_direction = FP_GameDirection_Count;
|
||||
DQN_FOR_UINDEX(dir_index, FP_GameDirection_Count) {
|
||||
FP_GameEntityHandle slot_entity_handle = terry->aggro_slot[dir_index];
|
||||
FP_GameEntityHandle slot_entity_handle = defender->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;
|
||||
@ -1388,21 +1419,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
FP_SentinelList_Front(&entity->waypoints),
|
||||
game->chunk_pool);
|
||||
FP_GameWaypoint *waypoint = &link->data;
|
||||
waypoint->entity = terry->handle;
|
||||
|
||||
waypoint->entity = defender->handle;
|
||||
if (aggro_direction != FP_GameDirection_Count) {
|
||||
waypoint->type = FP_GameWaypointType_Side;
|
||||
waypoint->type_direction = aggro_direction;
|
||||
terry->aggro_slot[aggro_direction] = entity->handle;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (closest_result.dist_squared > DQN_SQUARED(aggro_dist_threshold * 2.f)) {
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate<FP_GameWaypoint>(&entity->waypoints, &link); ) {
|
||||
FP_GameEntity *maybe_terry = FP_Game_GetEntity(game, link->data.entity);
|
||||
if (maybe_terry->type == FP_EntityType_Terry) {
|
||||
link = FP_SentinelList_Erase(&entity->waypoints, link, game->chunk_pool);
|
||||
}
|
||||
waypoint->type = FP_GameWaypointType_Side;
|
||||
waypoint->type_direction = aggro_direction;
|
||||
defender->aggro_slot[aggro_direction] = entity->handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1420,6 +1441,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
it_entity->type != FP_EntityType_ChurchTerry)
|
||||
continue;
|
||||
|
||||
// NOTE: Already converted, we cannot attend church again
|
||||
if (entity->converted_faction && it_entity->type == FP_EntityType_ChurchTerry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool already_visited_building = false;
|
||||
for (FP_SentinelListLink<FP_GameEntityHandle> *link_it = {};
|
||||
!already_visited_building && FP_SentinelList_Iterate(&entity->buildings_visited, &link_it);
|
||||
@ -1459,15 +1485,26 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
waypoint->entity = closest_building.entity;
|
||||
waypoint->type = FP_GameWaypointType_Side;
|
||||
|
||||
if (entity_pos.x <= top_left.x) {
|
||||
waypoint->type_direction = FP_GameDirection_Left;
|
||||
} else if (entity_pos.x >= top_right.x) {
|
||||
waypoint->type_direction = FP_GameDirection_Right;
|
||||
} else if (entity_pos.y <= top_left.y) {
|
||||
waypoint->type_direction = FP_GameDirection_Up;
|
||||
} else {
|
||||
waypoint->type_direction = FP_GameDirection_Down;
|
||||
|
||||
uint32_t *direction_hit_count = nullptr;
|
||||
FP_GameDirection least_encountered_direction = FP_GameDirection_Down;
|
||||
|
||||
DQN_FOR_UINDEX(dir_index, FP_GameDirection_Count) {
|
||||
FP_GameEntity *waypoint_entity = FP_Game_GetEntity(game, waypoint->entity);
|
||||
uint32_t *hit_count = waypoint_entity->count_of_entities_targetting_sides + dir_index;
|
||||
if (!direction_hit_count || *hit_count < *direction_hit_count) {
|
||||
direction_hit_count = hit_count;
|
||||
least_encountered_direction = DQN_CAST(FP_GameDirection)dir_index;
|
||||
} else if (hit_count == direction_hit_count) {
|
||||
if (Dqn_PCG32_NextF32(&game->rng) >= 0.5f) {
|
||||
direction_hit_count = hit_count;
|
||||
least_encountered_direction = DQN_CAST(FP_GameDirection)dir_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
waypoint->type_direction = least_encountered_direction;
|
||||
(*direction_hit_count)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1490,17 +1527,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
FP_SentinelListLink<FP_GameWaypoint> *link = FP_SentinelList_MakeBefore(&entity->waypoints, FP_SentinelList_Front(&entity->waypoints), game->chunk_pool);
|
||||
FP_GameWaypoint *waypoint = &link->data;
|
||||
waypoint->entity = closest_heart.entity;
|
||||
waypoint->type = FP_GameWaypointType_Side;
|
||||
|
||||
if (entity_pos.x <= club_top_left.x) {
|
||||
waypoint->type_direction = FP_GameDirection_Left;
|
||||
} else if (entity_pos.x >= club_top_right.x) {
|
||||
waypoint->type_direction = FP_GameDirection_Right;
|
||||
} else if (entity_pos.y <= club_top_left.y) {
|
||||
waypoint->type_direction = FP_GameDirection_Up;
|
||||
} else {
|
||||
waypoint->type_direction = FP_GameDirection_Down;
|
||||
}
|
||||
waypoint->type = FP_GameWaypointType_ClosestSide;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1515,7 +1542,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
// NOTE: We found a waypoint that is valid to move towards
|
||||
Dqn_V2 target_pos = FP_Game_CalcWaypointWorldPos(game, waypoint);
|
||||
Dqn_V2 target_pos = FP_Game_CalcWaypointWorldPos(game, entity->handle, waypoint);
|
||||
Dqn_V2 entity_to_waypoint = target_pos - entity_pos;
|
||||
|
||||
// NOTE: Check if we've arrived at the waypoint
|
||||
@ -1540,16 +1567,20 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
|
||||
// NOTE: We have arrived at the waypoint
|
||||
bool aggro_on_terry = (entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) && waypoint_entity->type == FP_EntityType_Terry;
|
||||
bool aggro = false;
|
||||
if (entity->flags & FP_GameEntityFlag_Aggros) {
|
||||
aggro |= entity->faction == FP_GameEntityFaction_Friendly && waypoint_entity->faction & FP_GameEntityFaction_Foe;
|
||||
aggro |= entity->faction == FP_GameEntityFaction_Foe && waypoint_entity->faction & FP_GameEntityFaction_Friendly;
|
||||
}
|
||||
|
||||
bool building_response = (entity->flags & FP_GameEntityFlag_RespondsToBuildings) &&
|
||||
waypoint_entity->type == FP_EntityType_ClubTerry ||
|
||||
waypoint_entity->type == FP_EntityType_AirportTerry ||
|
||||
waypoint_entity->type == FP_EntityType_ChurchTerry;
|
||||
bool heart_response = (entity->flags & FP_GameEntityFlag_PointOfInterestHeart) && waypoint_entity->type == FP_EntityType_Heart;
|
||||
|
||||
if (((waypoint->flags & FP_GameWaypointFlag_NonInterruptible) == 0) && (aggro_on_terry || building_response || heart_response)) {
|
||||
if (((waypoint->flags & FP_GameWaypointFlag_NonInterruptible) == 0) && (aggro || building_response || heart_response)) {
|
||||
bool can_attack = !entity->is_dying; // TODO(doyle): State transition needs to check if it's valid to move to making this not necessary
|
||||
|
||||
if (building_response) {
|
||||
FP_GameEntity *building = waypoint_entity;
|
||||
if (FP_Game_IsNilEntityHandle(game, building->building_patron)) {
|
||||
@ -1632,8 +1663,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Aggro makes the entity attack Terry, we will
|
||||
// exit here preserving the waypoint in the entity.
|
||||
// NOTE: Aggro makes the entity attack, we will exit here preserving the waypoint in the entity.
|
||||
break;
|
||||
} else {
|
||||
FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool);
|
||||
@ -1812,7 +1842,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
// NOTE: Setup the mob with waypoints
|
||||
FP_GameEntity *mob = FP_Game_GetEntity(game, link->data);
|
||||
mob->waypoints = FP_SentinelList_Init<FP_GameWaypoint>(game->chunk_pool);
|
||||
mob->flags |= FP_GameEntityFlag_AggrosWhenNearTerry;
|
||||
mob->flags |= FP_GameEntityFlag_Aggros;
|
||||
mob->flags |= FP_GameEntityFlag_RespondsToBuildings;
|
||||
mob->hp_cap += hp_adjustment;
|
||||
mob->hp = entity->hp_cap;
|
||||
@ -1850,6 +1880,11 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
Dqn_Rect attacker_box = FP_Game_CalcEntityAttackWorldHitBox(game, attacker->handle);
|
||||
Dqn_V2 attacker_world_pos = FP_Game_CalcEntityWorldPos(game, attacker->handle);
|
||||
|
||||
FP_GameEntityFaction enemy_faction =
|
||||
entity->faction == FP_GameEntityFaction_Friendly
|
||||
? FP_GameEntityFaction_Foe
|
||||
: FP_GameEntityFaction_Friendly;
|
||||
|
||||
for (FP_GameEntityIterator defender_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &defender_it, game->root_entity); ) {
|
||||
FP_GameEntity *defender = defender_it.entity;
|
||||
if (defender->handle == attacker->handle)
|
||||
@ -1862,40 +1897,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
|
||||
if (attacker->projectile_owner == defender->handle)
|
||||
continue;
|
||||
|
||||
bool permit_attack = true;
|
||||
switch (attacker->type) {
|
||||
case FP_EntityType_Smoochie: {
|
||||
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger || defender->type == FP_EntityType_Catfish)
|
||||
permit_attack = false;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Catfish: {
|
||||
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger || defender->type == FP_EntityType_Catfish)
|
||||
permit_attack = false;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Clinger: {
|
||||
if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger || defender->type == FP_EntityType_Catfish)
|
||||
permit_attack = false;
|
||||
} break;
|
||||
|
||||
case FP_EntityType_Nil: break;
|
||||
case FP_EntityType_Terry: break;
|
||||
case FP_EntityType_Count: break;
|
||||
case FP_EntityType_ClubTerry: break;
|
||||
case FP_EntityType_Map: break;
|
||||
case FP_EntityType_MerchantTerry: break;
|
||||
case FP_EntityType_MerchantGraveyard: break;
|
||||
case FP_EntityType_MerchantGym: break;
|
||||
case FP_EntityType_MerchantPhoneCompany: break;
|
||||
case FP_EntityType_Heart: break;
|
||||
case FP_EntityType_AirportTerry:
|
||||
case FP_EntityType_ChurchTerry:
|
||||
case FP_EntityType_KennelTerry: break;
|
||||
case FP_EntityType_PhoneMessageProjectile: break;
|
||||
}
|
||||
|
||||
if (!permit_attack)
|
||||
if (defender->faction != enemy_faction)
|
||||
continue;
|
||||
|
||||
Dqn_Rect defender_box = FP_Game_CalcEntityWorldHitBox(game, defender->handle);
|
||||
@ -2050,6 +2052,12 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
Dqn_V2_Zero /*rotate origin*/,
|
||||
0.f /*rotate radians*/,
|
||||
TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, entity->action.sprite_alpha));
|
||||
|
||||
if (entity->converted_faction) {
|
||||
Dqn_V2 label_p = Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0, -0.25f));
|
||||
TELY_Render_TextF(renderer, label_p, Dqn_V2_InitNx2(0.f, 0.1f), "CONVERTED");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) {
|
||||
@ -2175,7 +2183,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
// NOTE: Draw the waypoints that the entity is moving along
|
||||
Dqn_V2 start = world_pos;
|
||||
for (FP_SentinelListLink<FP_GameWaypoint> *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) {
|
||||
Dqn_V2 end = FP_Game_CalcWaypointWorldPos(game, &link->data);
|
||||
Dqn_V2 end = FP_Game_CalcWaypointWorldPos(game, entity->handle, &link->data);
|
||||
TELY_Render_LineColourV4(renderer, start, end, TELY_COLOUR_WHITE_PALE_GOLDENROD_V4, 2.f);
|
||||
start = end;
|
||||
}
|
||||
@ -2194,6 +2202,14 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx2(0.f, 1), "Hit Box Size: %.1fx%.1f", world_hit_box.size.x, world_hit_box.size.y); draw_p.y += line_height;
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx2(0.f, 1), "Tile: %I32dx%I32d", player_tile.x, player_tile.y); draw_p.y += line_height;
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx2(0.f, 1), "World Mouse Pos: (%.1f, %.1f)", world_mouse_p.x, world_mouse_p.y); draw_p.y += line_height;
|
||||
|
||||
Dqn_String8 faction = {};
|
||||
switch (entity->faction) {
|
||||
case FP_GameEntityFaction_Nil: faction = DQN_STRING8("Nil"); break;
|
||||
case FP_GameEntityFaction_Friendly: faction = DQN_STRING8("Friendly"); break;
|
||||
case FP_GameEntityFaction_Foe: faction = DQN_STRING8("Foe"); break;
|
||||
}
|
||||
TELY_Render_TextF(renderer, draw_p, Dqn_V2_InitNx2(0.f, 1), "Faction: %.*s", DQN_STRING_FMT(faction)); draw_p.y += line_height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,8 +305,9 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||
entity->attack_cooldown_ms = 1000;
|
||||
entity->faction = FP_GameEntityFaction_Foe;
|
||||
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .5f);
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.7f, entity->sprite_height.meters * .5f);
|
||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
@ -327,10 +328,11 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||
entity->attack_cooldown_ms = 1000;
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .6f);
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.7f, entity->sprite_height.meters * .6f);
|
||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
entity->faction = FP_GameEntityFaction_Foe;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -348,10 +350,11 @@ static FP_GameEntityHandle FP_Entity_CreateCatfish(FP_Game *game, Dqn_V2 pos, DQ
|
||||
entity->local_pos = pos;
|
||||
entity->sprite_height = FP_Entity_GetRenderData(game, entity->type, 0 /*state*/, FP_GameDirection_Down).height;
|
||||
entity->attack_cooldown_ms = 1000;
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .6f);
|
||||
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.7f, entity->sprite_height.meters * .6f);
|
||||
FP_Entity_AddDebugEditorFlags(game, entity->handle);
|
||||
entity->flags |= FP_GameEntityFlag_NonTraversable;
|
||||
entity->flags |= FP_GameEntityFlag_Attackable;
|
||||
entity->faction = FP_GameEntityFaction_Foe;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -433,6 +436,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_
|
||||
entity->flags |= FP_GameEntityFlag_CameraTracking;
|
||||
entity->terry_mobile_data_plan_cap = DQN_KILOBYTES(6);
|
||||
entity->terry_mobile_data_plan = entity->terry_mobile_data_plan_cap;
|
||||
entity->faction = FP_GameEntityFaction_Friendly;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -676,6 +680,7 @@ static FP_GameEntityHandle FP_Entity_CreatePhoneMessageProjectile(FP_Game *game,
|
||||
entity->attack_box_size = entity->local_hit_box_size;
|
||||
entity->ttl_end_timestamp = game->clock_ms + 1000;
|
||||
entity->projectile_owner = owner;
|
||||
entity->faction = FP_GameEntityFaction_Friendly;
|
||||
|
||||
uint64_t duration_ms = FP_GAME_ENTITY_ACTION_INFINITE_TIMER;
|
||||
FP_Game_EntityActionReset(game, result, duration_ms, render_data.sprite);
|
||||
|
@ -667,7 +667,7 @@ static Dqn_Slice<Dqn_V2I> FP_Game_AStarPathFind(FP_Game *game,
|
||||
return result;
|
||||
}
|
||||
|
||||
static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameWaypoint const *waypoint)
|
||||
static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameEntityHandle src_entity, FP_GameWaypoint const *waypoint)
|
||||
{
|
||||
Dqn_V2 result = {};
|
||||
if (!game || !waypoint)
|
||||
@ -683,26 +683,38 @@ static Dqn_V2 FP_Game_CalcWaypointWorldPos(FP_Game *game, FP_GameWaypoint const
|
||||
result = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle);
|
||||
} break;
|
||||
|
||||
case FP_GameWaypointType_ClosestSide: /*FALLTHRU*/
|
||||
case FP_GameWaypointType_Side: {
|
||||
// NOTE: Sweep entity with half the radius of the source entity
|
||||
Dqn_Rect src_rect = FP_Game_CalcEntityWorldHitBox(game, src_entity);
|
||||
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 * .1f);
|
||||
} break;
|
||||
entity_rect.pos -= (src_rect.size * .5f);
|
||||
entity_rect.size += src_rect.size;
|
||||
|
||||
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 * .1f);
|
||||
} break;
|
||||
Dqn_V2 side_pos_list[FP_GameDirection_Count] = {};
|
||||
side_pos_list[FP_GameDirection_Up] = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w * .5f, entity_rect.pos.y - entity_rect.size.h * .1f);
|
||||
side_pos_list[FP_GameDirection_Down] = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w * .5f, entity_rect.pos.y + entity_rect.size.h + entity_rect.size.h * .1f);
|
||||
side_pos_list[FP_GameDirection_Left] = Dqn_V2_InitNx2(entity_rect.pos.x - entity_rect.size.w * .1f, entity_rect.pos.y + entity_rect.size.h * .5f);
|
||||
side_pos_list[FP_GameDirection_Right] = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w + entity_rect.size.w * .1f, entity_rect.pos.y + entity_rect.size.h * .5f);
|
||||
|
||||
case FP_GameDirection_Left: {
|
||||
result = Dqn_V2_InitNx2(entity_rect.pos.x - entity_rect.size.w * .1f, entity_rect.pos.y + entity_rect.size.h * .5f);
|
||||
} break;
|
||||
if (waypoint->type == FP_GameWaypointType_Side) {
|
||||
result = side_pos_list[waypoint->type_direction];
|
||||
} else {
|
||||
Dqn_f32 best_dist = DQN_F32_MAX;
|
||||
for (Dqn_V2 target_pos : side_pos_list) {
|
||||
Dqn_f32 dist_squared = Dqn_V2_LengthSq_V2x2(src_rect.pos, target_pos);
|
||||
if (dist_squared < best_dist) {
|
||||
best_dist = dist_squared;
|
||||
result = target_pos;
|
||||
}
|
||||
}
|
||||
|
||||
case FP_GameDirection_Right: {
|
||||
result = Dqn_V2_InitNx2(entity_rect.pos.x + entity_rect.size.w + entity_rect.size.w * .1f, entity_rect.pos.y + entity_rect.size.h * .5f);
|
||||
} break;
|
||||
|
||||
case FP_GameDirection_Count: DQN_INVALID_CODE_PATH; break;
|
||||
Dqn_f32 curr_dist_to_entity = Dqn_V2_LengthSq_V2x2(entity_rect.pos, src_rect.pos);
|
||||
if (curr_dist_to_entity < best_dist) {
|
||||
// NOTE: We are already closer to the entity than the closest calculated side,
|
||||
// we assume we're at the entity already.
|
||||
result = FP_Game_CalcEntityWorldPos(game, src_entity);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -14,7 +14,7 @@ enum FP_GameEntityFlag
|
||||
FP_GameEntityFlag_NonTraversable = 1 << 6,
|
||||
FP_GameEntityFlag_MobSpawner = 1 << 7,
|
||||
FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 8,
|
||||
FP_GameEntityFlag_AggrosWhenNearTerry = 1 << 9,
|
||||
FP_GameEntityFlag_Aggros = 1 << 9,
|
||||
FP_GameEntityFlag_Attackable = 1 << 10,
|
||||
FP_GameEntityFlag_RespondsToBuildings = 1 << 11,
|
||||
FP_GameEntityFlag_OccupiedInBuilding = 1 << 12,
|
||||
@ -22,6 +22,8 @@ enum FP_GameEntityFlag
|
||||
FP_GameEntityFlag_CameraTracking = 1 << 14,
|
||||
FP_GameEntityFlag_BuildZone = 1 << 15,
|
||||
FP_GameEntityFlag_TTL = 1 << 16,
|
||||
FP_GameEntityFlag_Friendly = 1 << 17,
|
||||
FP_GameEntityFlag_Foe = 1 << 18,
|
||||
};
|
||||
|
||||
enum FP_GameShapeType
|
||||
@ -71,9 +73,10 @@ 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_Offset, // Move to the designed offset from the entity
|
||||
FP_GameWaypointType_At, // Move to the specified entity
|
||||
FP_GameWaypointType_Side, // Move to the side of the entity specified by the direction
|
||||
FP_GameWaypointType_ClosestSide, // Move to the side of the entity closest to us
|
||||
FP_GameWaypointType_Offset, // Move to the designed offset from the entity
|
||||
};
|
||||
|
||||
enum FP_GameDirection
|
||||
@ -152,6 +155,13 @@ struct FP_GameInventory
|
||||
uint8_t churchs;
|
||||
};
|
||||
|
||||
enum FP_GameEntityFaction
|
||||
{
|
||||
FP_GameEntityFaction_Nil,
|
||||
FP_GameEntityFaction_Friendly,
|
||||
FP_GameEntityFaction_Foe,
|
||||
};
|
||||
|
||||
struct FP_GameEntity
|
||||
{
|
||||
FP_GameEntity *next;
|
||||
@ -221,6 +231,11 @@ struct FP_GameEntity
|
||||
uint16_t hp_cap;
|
||||
uint16_t stamina;
|
||||
uint16_t stamina_cap;
|
||||
|
||||
bool converted_faction;
|
||||
FP_GameEntityFaction faction;
|
||||
|
||||
uint32_t count_of_entities_targetting_sides[FP_GameDirection_Count];
|
||||
};
|
||||
|
||||
struct FP_GameEntityIterator
|
||||
|
Loading…
Reference in New Issue
Block a user