mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
apply $fix ai target recovery's pursuit filter only to targets picked for pursuit
The actively-pursues filter added to "$fix ai target recovery:" in #7736 was applied to every target search. But a ship's target is not only used for pursuit: it also drives the ship's turrets, other ships' guard reactions, and attacker counts. In retail SM1-04, the friendly Knossos (which does not pursue fighters) was made to target the Behemoth instead of a Mara, which caused the Mara guards to react to the protected Knossos endlessly instead of rushing the player. The filter now applies only where the target is chosen for maneuvering: * find_enemy() and get_nearest_objnum() take a required for_pursuit parameter; automatic searches pass ai_targets_for_pursuit(), which is true only in AIM_CHASE or AIM_STRAFE * guard reactions, retaliation (dynamic chase), and the retargets in ai_big_chase_attack() and ai_big_maybe_enter_strafe_mode() skip ships the attacker won't pursue, via the new ai_declines_pursuit() * ai_attack_wing() prefers living wing members the ship will pursue; if there are none, it holds a member as an explicit order to attack an unpursued ship does * get_nearest_objnum() holds an unpursued member of the enemy wing rather than leaving the wing Since refused targets are no longer picked for pursuit, ai_chase() no longer needs to drop them. Retail behavior is unchanged when the flag is disabled. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
6e469a2180
commit
1af0eb82bf
4 files changed
+81
-41
No files matched your search
+4
-2
@@ -598,7 +598,9 @@ extern int ai_maybe_fire_afterburner(object *objp, ai_info *aip);
|
||||
extern void set_predicted_enemy_pos(vec3d *predicted_enemy_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, ai_info *aip);
|
||||
|
||||
extern int is_instructor(object *objp);
|
||||
extern int find_enemy(int objnum, float range, int max_attackers, int ship_info_index = -1, int class_type = -1);
|
||||
extern int find_enemy(int objnum, float range, int max_attackers, bool for_pursuit, int ship_info_index = -1, int class_type = -1);
|
||||
extern bool ai_targets_for_pursuit(const ai_info *aip);
|
||||
extern bool ai_declines_pursuit(const object *attacker_objp, const object *target_objp);
|
||||
|
||||
float ai_get_weapon_speed(const ship_weapon *swp);
|
||||
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, const vec3d *gun_pos, const object *pobjp, const vec3d *enemy_pos, const vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range);
|
||||
@@ -619,7 +621,7 @@ extern float dock_orient_and_approach(object *docker_objp, int docker_index, obj
|
||||
void ai_set_mode_warp_out(object *objp, ai_info *aip);
|
||||
|
||||
// prototyped by Goober5000
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index, int class_type = -1);
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, bool for_pursuit, int ship_info_index, int class_type = -1);
|
||||
|
||||
// moved to header file by Goober5000
|
||||
void ai_announce_ship_dying(object *dying_objp);
|
||||
|
||||
+3
-2
@@ -178,8 +178,9 @@ namespace AI {
|
||||
Fix_standard_strafe,
|
||||
Fix_ai_target_recovery, // a) strafing ships that lose their target re-process their orders, like chasing ships already do
|
||||
// b) ships parked in AIM_NONE while holding a standing chase order (attack-any etc.) re-process their orders;
|
||||
// c) target selection skips ships outside the attacker's actively-pursues list, and self-chosen targets
|
||||
// outside that list are dropped rather than held forever
|
||||
// c) target selection for pursuit (chasing, guard reactions, retaliation) skips ships outside the attacker's
|
||||
// actively-pursues list; targets held while not chasing are unaffected, and explicitly ordered targets are
|
||||
// still assigned but not chased or strafed;
|
||||
// d) enemy_wing is reset when a new order takes over (shared with Fix_stale_ai_order_state)
|
||||
Standard_strafe_used_more,
|
||||
Unify_usage_ai_shield_manage_delay,
|
||||
|
||||
+7
-7
@@ -716,7 +716,8 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
if (vm_vec_dot(&in_vec, &objp->orient.vec.fvec) > 0.0f) {
|
||||
dist = vm_vec_normalize(&in_vec);
|
||||
if ((dist < 200.0f) && (vm_vec_dot(&in_vec, &objp->orient.vec.fvec) > 0.95f)) {
|
||||
if ((Objects[objp->parent].signature == objp->parent_sig) && (vm_vec_dist_quick(&objp->pos, &Objects[objp->parent].pos) < 300.0f)) {
|
||||
if ((Objects[objp->parent].signature == objp->parent_sig) && (vm_vec_dist_quick(&objp->pos, &Objects[objp->parent].pos) < 300.0f)
|
||||
&& !ai_declines_pursuit(Pl_objp, &Objects[objp->parent])) {
|
||||
set_target_objnum(aip, objp->parent);
|
||||
aip->submode = SM_ATTACK;
|
||||
aip->submode_start_time = Missiontime;
|
||||
@@ -1118,7 +1119,7 @@ void ai_big_chase()
|
||||
} else if (En_objp->flags[Object::Object_Flags::Protected]) { // If protected and we're not attacking a subsystem, stop attacking!
|
||||
update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
|
||||
aip->target_objnum = -1;
|
||||
if (find_enemy(OBJ_INDEX(Pl_objp), MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]) == -1) {
|
||||
if (find_enemy(OBJ_INDEX(Pl_objp), MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level], ai_targets_for_pursuit(aip)) == -1) {
|
||||
ai_do_default_behavior(Pl_objp);
|
||||
return;
|
||||
}
|
||||
@@ -1978,19 +1979,18 @@ int ai_big_maybe_enter_strafe_mode(const object *pl_objp, int weapon_objnum)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ai_declines_pursuit(pl_objp, parent_objp)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Maybe the ship which fired the weapon isn't the current target
|
||||
if ( OBJ_INDEX(parent_objp) != aip->target_objnum ) {
|
||||
|
||||
//JAS IMPOSSIBLE if (1) { // consider_target_only ) {
|
||||
//JAS IMPOSSIBLE return 0;
|
||||
//JAS IMPOSSIBLE } else {
|
||||
// switch targets
|
||||
sip = &Ship_info[Ships[parent_objp->instance].ship_info_index];
|
||||
if ( !(sip->is_big_or_huge()) || (sip->flags[Ship::Info_Flags::Transport]) ) {
|
||||
return 0;
|
||||
}
|
||||
set_target_objnum(aip, OBJ_INDEX(parent_objp));
|
||||
//JAS IMPOSSIBLE }
|
||||
}
|
||||
|
||||
ai_big_strafe_maybe_attack_turret(pl_objp, weapon_objp);
|
||||
|
||||
+67
-30
@@ -2215,16 +2215,35 @@ static bool ai_class_type_actively_pursues(int attacker_class_type, int target_c
|
||||
return std::any_of(pursues.begin(), pursues.end(), [target_class_type](int pursued_type) { return pursued_type == target_class_type; });
|
||||
}
|
||||
|
||||
// Returns true if a target this ship picks for itself will be used for pursuit, and therefore applicable to the "actively_pursues" check.
|
||||
// In other modes a ship's target is held for its turrets, and is also read by other ships (guard reactions, attacker counts).
|
||||
bool ai_targets_for_pursuit(const ai_info *aip)
|
||||
{
|
||||
return aip->mode == AIM_CHASE || aip->mode == AIM_STRAFE;
|
||||
}
|
||||
|
||||
// Returns true if attacker_objp should not pick target_objp as a target to pursue.
|
||||
bool ai_declines_pursuit(const object *attacker_objp, const object *target_objp)
|
||||
{
|
||||
Assertion(attacker_objp->type == OBJ_SHIP, "ai_declines_pursuit() was called with an attacker of object type %d; it must be a ship!", attacker_objp->type);
|
||||
|
||||
if (!The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery] || target_objp->type != OBJ_SHIP)
|
||||
return false;
|
||||
|
||||
return !ai_class_type_actively_pursues(Ship_info[Ships[attacker_objp->instance].ship_info_index].class_type,
|
||||
Ship_info[Ships[target_objp->instance].ship_info_index].class_type);
|
||||
}
|
||||
|
||||
typedef struct eval_nearest_objnum {
|
||||
int objnum;
|
||||
object *trial_objp;
|
||||
int enemy_team_mask;
|
||||
int enemy_ship_info_index;
|
||||
int enemy_class_type;
|
||||
int attacker_class_type;
|
||||
int enemy_wing;
|
||||
float range;
|
||||
int max_attackers;
|
||||
bool for_pursuit;
|
||||
int nearest_objnum;
|
||||
float nearest_dist;
|
||||
int check_danger_weapon_objnum;
|
||||
@@ -2280,11 +2299,10 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
|
||||
float dist;
|
||||
int num_attacking;
|
||||
|
||||
// Don't pick a target that this ship's type refuses to chase, or it will park in AIM_NONE holding a target it never attacks.
|
||||
// If pursuing, don't pick a target that this ship's type refuses to pursue, or it will park in AIM_NONE holding a target it never attacks.
|
||||
// Only for unconstrained searches: an explicit chase-ship-class/type order should still produce a target even if ai_chase() doesn't chase it.
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery]
|
||||
&& eno->enemy_ship_info_index < 0 && eno->enemy_class_type < 0
|
||||
&& !ai_class_type_actively_pursues(eno->attacker_class_type, Ship_info[shipp->ship_info_index].class_type))
|
||||
if (eno->for_pursuit && eno->enemy_ship_info_index < 0 && eno->enemy_class_type < 0
|
||||
&& ai_declines_pursuit(&Objects[eno->objnum], eno->trial_objp))
|
||||
return;
|
||||
|
||||
// Allow targeting of stealth in nebula by his firing at me
|
||||
@@ -2352,10 +2370,11 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
|
||||
* @param enemy_wing Enemy wing chosen
|
||||
* @param range Ship must be within range "range".
|
||||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
|
||||
* @param for_pursuit True if the ship will chase the enemy found; false if the target is only held (e.g. for turrets)
|
||||
* @param ship_info_index If >=0, the enemy object must be of the specified ship class
|
||||
* @param class_type If >=0, the enemy object must be of the specified ship type
|
||||
*/
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index, int class_type)
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, bool for_pursuit, int ship_info_index, int class_type)
|
||||
{
|
||||
object *danger_weapon_objp;
|
||||
ai_info *aip;
|
||||
@@ -2366,9 +2385,9 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
|
||||
eno.enemy_team_mask = enemy_team_mask;
|
||||
eno.enemy_ship_info_index = ship_info_index;
|
||||
eno.enemy_class_type = class_type;
|
||||
eno.attacker_class_type = Ship_info[Ships[Objects[objnum].instance].ship_info_index].class_type;
|
||||
eno.enemy_wing = enemy_wing;
|
||||
eno.max_attackers = max_attackers;
|
||||
eno.for_pursuit = for_pursuit;
|
||||
eno.objnum = objnum;
|
||||
eno.range = range;
|
||||
eno.nearest_dist = range;
|
||||
@@ -2409,10 +2428,17 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
|
||||
}
|
||||
}
|
||||
|
||||
// If no wing member can be pursued, hold one anyway rather than leave the wing, as an explicit order to attack an unpursued ship does
|
||||
if ((eno.nearest_objnum == -1) && (enemy_wing != -1) && for_pursuit && The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery]) {
|
||||
int held_objnum = get_nearest_objnum(objnum, enemy_team_mask, enemy_wing, range, max_attackers, false, ship_info_index, class_type);
|
||||
if (held_objnum >= 0 && Objects[held_objnum].type == OBJ_SHIP && Ships[Objects[held_objnum].instance].wingnum == enemy_wing)
|
||||
return held_objnum;
|
||||
}
|
||||
|
||||
// If only looking for target in certain wing and couldn't find anything in
|
||||
// that wing, look for any object.
|
||||
if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, ship_info_index, class_type);
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, for_pursuit, ship_info_index, class_type);
|
||||
}
|
||||
|
||||
return eno.nearest_objnum;
|
||||
@@ -2519,10 +2545,11 @@ int get_enemy_timestamp()
|
||||
* @param objnum Object number
|
||||
* @param range Range within which to look
|
||||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
|
||||
* @param for_pursuit True if the ship will chase the enemy found; false if the target is only held (e.g. for turrets)
|
||||
* @param ship_info_index If specified, restrict the search to enemies with this ship class
|
||||
* @param class_type If specified, restrict the search to enemies with this ship type
|
||||
*/
|
||||
int find_enemy(int objnum, float range, int max_attackers, int ship_info_index, int class_type)
|
||||
int find_enemy(int objnum, float range, int max_attackers, bool for_pursuit, int ship_info_index, int class_type)
|
||||
{
|
||||
int enemy_team_mask;
|
||||
|
||||
@@ -2552,10 +2579,7 @@ int find_enemy(int objnum, float range, int max_attackers, int ship_info_index,
|
||||
if (class_type < 0 || class_type == Ship_info[target_shipp->ship_info_index].class_type) {
|
||||
if (!(target_objp->flags[Object::Object_Flags::Protected])) {
|
||||
// same as get_nearest_objnum: only skip an unpursued target for unconstrained searches
|
||||
if (!The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery]
|
||||
|| ship_info_index >= 0 || class_type >= 0
|
||||
|| ai_class_type_actively_pursues(Ship_info[shipp->ship_info_index].class_type,
|
||||
Ship_info[target_shipp->ship_info_index].class_type)) {
|
||||
if (!for_pursuit || ship_info_index >= 0 || class_type >= 0 || !ai_declines_pursuit(objp, target_objp)) {
|
||||
return target_objnum;
|
||||
}
|
||||
}
|
||||
@@ -2568,7 +2592,7 @@ int find_enemy(int objnum, float range, int max_attackers, int ship_info_index,
|
||||
}
|
||||
}
|
||||
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers, ship_info_index, class_type);
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers, for_pursuit, ship_info_index, class_type);
|
||||
} else {
|
||||
aip->target_objnum = -1;
|
||||
aip->target_signature = -1;
|
||||
@@ -2642,7 +2666,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index, i
|
||||
if (attacked == nullptr) {
|
||||
aip->choose_enemy_timestamp = timestamp(0);
|
||||
// nebula safe
|
||||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index, class_type));
|
||||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, true, ship_info_index, class_type));
|
||||
} else {
|
||||
// check if we can see attacked in nebula
|
||||
if (aip->target_objnum != OBJ_INDEX(attacked)) {
|
||||
@@ -2703,10 +2727,24 @@ void ai_attack_wing(object *attacker, int wingnum)
|
||||
if (count > 0) {
|
||||
int index;
|
||||
|
||||
index = (int) (frand() * count);
|
||||
// prefer wing members we will pursue; if there are none, hold any member, as an explicit order to attack an unpursued ship does
|
||||
SCP_vector<int> pursued_indexes;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery]) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto member_shipp = &Ships[Wings[wingnum].ship_index[i]];
|
||||
if (!member_shipp->flags[Ship::Ship_Flags::Dying] && !ai_declines_pursuit(attacker, &Objects[member_shipp->objnum]))
|
||||
pursued_indexes.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= count)
|
||||
index = 0;
|
||||
if (!pursued_indexes.empty()) {
|
||||
index = pursued_indexes[Random::next(sz2i(pursued_indexes.size()))];
|
||||
} else {
|
||||
index = (int) (frand() * count);
|
||||
|
||||
if (index >= count)
|
||||
index = 0;
|
||||
}
|
||||
|
||||
set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
|
||||
|
||||
@@ -9051,15 +9089,6 @@ void ai_chase()
|
||||
|
||||
//WMC - Guess we do need this
|
||||
if (!go_after_it) {
|
||||
// If we picked this target ourselves -- through auto-attack, dynamic chase, or a standing chase order --
|
||||
// drop it, so that the retargeting logic in ai_frame() can find something we are willing to chase.
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery]) {
|
||||
if (aip->active_goal < 0 || aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC
|
||||
|| (aip->active_goal < MAX_AI_GOALS && ai_goal_is_standing_chase(aip->goals[aip->active_goal].ai_mode))) {
|
||||
aip->target_objnum = -1;
|
||||
aip->target_signature = -1;
|
||||
}
|
||||
}
|
||||
aip->mode = AIM_NONE;
|
||||
return;
|
||||
}
|
||||
@@ -10458,6 +10487,10 @@ void guard_object_was_hit(object *guard_objp, object *hitter_objp)
|
||||
if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
|
||||
return;
|
||||
|
||||
// Keep guarding rather than chase a ship we won't pursue
|
||||
if (ai_declines_pursuit(guard_objp, hitter_objp))
|
||||
return;
|
||||
|
||||
// limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
|
||||
if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
|
||||
return;
|
||||
@@ -15499,7 +15532,7 @@ void ai_frame(int objnum)
|
||||
} else if (aip->resume_goal_time == -1) {
|
||||
// AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
|
||||
if ( Ship_info[shipp->ship_info_index].class_type > -1 && (Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_auto_attacks]) ) {
|
||||
target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]); // Attack up to 2.5K units away.
|
||||
target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level], ai_targets_for_pursuit(aip)); // Attack up to 2.5K units away.
|
||||
if (target_objnum != -1) {
|
||||
if (aip->target_objnum != target_objnum)
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
@@ -15583,7 +15616,7 @@ void ai_frame(int objnum)
|
||||
if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
|
||||
aip->active_goal = AI_ACTIVE_GOAL_NONE;
|
||||
aip->resume_goal_time = -1;
|
||||
target_objnum = find_enemy(objnum, 2000.0f, The_mission.ai_profile->max_attackers[Game_skill_level]);
|
||||
target_objnum = find_enemy(objnum, 2000.0f, The_mission.ai_profile->max_attackers[Game_skill_level], ai_targets_for_pursuit(aip));
|
||||
if (target_objnum != -1) {
|
||||
if (aip->target_objnum != target_objnum) {
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
@@ -16106,7 +16139,7 @@ void ai_do_default_behavior(object *obj)
|
||||
// fighters automatically chase things
|
||||
if (!is_instructor(obj) && (sip->is_fighter_bomber()))
|
||||
{
|
||||
int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, The_mission.ai_profile->max_attackers[Game_skill_level]);
|
||||
int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, The_mission.ai_profile->max_attackers[Game_skill_level], true);
|
||||
set_target_objnum(aip, enemy_objnum);
|
||||
aip->mode = AIM_CHASE;
|
||||
aip->submode = SM_ATTACK;
|
||||
@@ -16328,6 +16361,10 @@ void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ai_declines_pursuit(&Objects[Ships[aip->shipnum].objnum], &Objects[hitter_objnum])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aip->target_objnum != hitter_objnum)
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
set_target_objnum(aip, hitter_objnum);
|
||||
|
||||
Reference in new issue
Block a user