diff --git a/feely_pona.cpp b/feely_pona.cpp index 71fe17f..5b79e2d 100644 --- a/feely_pona.cpp +++ b/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 *link = nullptr; - !has_waypoint_to_terry && FP_SentinelList_Iterate(&entity->waypoints, &link); ) { - has_waypoint_to_terry = link->data.entity == closest_result.entity; + !has_waypoint_to_defender && FP_SentinelList_Iterate(&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 *link = nullptr; FP_SentinelList_Iterate(&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 *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 *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(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 *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; } } } diff --git a/feely_pona_entity_create.cpp b/feely_pona_entity_create.cpp index a0ed33c..9b7cbd0 100644 --- a/feely_pona_entity_create.cpp +++ b/feely_pona_entity_create.cpp @@ -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); diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index f28604a..6e73670 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -667,7 +667,7 @@ static Dqn_Slice 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; diff --git a/feely_pona_game.h b/feely_pona_game.h index 104b063..0b784a2 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -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