mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
reset stale AI order state and track previous_mode through dynamic chases
Several ai_info fields remember which order set them up (enemy_wing, guard_objnum/signature/wingnum) or which mode a dynamic goal interrupted (previous_mode, resume_goal_time), but nothing cleared them when a new order took over. A ship that once guarded a wing would be yanked back into AIM_GUARD whenever a wingmate died; a stale previous_mode of AIM_GUARD could make a former guard abandon a counterattack; a stale enemy_wing biased every find_enemy() toward a long-ago target; and a resume_goal_time left over from a dynamic chase suppressed target acquisition for up to 20 seconds into the next order. Add ai_reset_order_state(), called when ai_process_mission_orders() takes up a goal and when ai_do_default_behavior() runs. Each order's own setup repopulates the fields it needs. enemy_wing is reset under either $fix stale ai order state or $fix ai target recovery, since it affects target selection; the rest only under the new flag. maybe_set_dynamic_chase() wrote aip->mode into previous_submode, a retail typo that left previous_mode untracked for dynamic chases. Under the flag, save both fields properly, but not when a dynamic goal is already in progress, so a nested interruption does not clobber the mode to return to. Apply the same guard to the strafe/chase transitions in aibig.cpp. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
a1250d7168
commit
2052dcaeb0
6 files changed
+78
-7
No files matched your search
@@ -535,6 +535,7 @@ extern void ai_ignore_wing(object *ignorer, int wingnum);
|
||||
extern void ai_dock_with_object(object *docker, int docker_index, object *dockee, int dockee_index, int dock_type);
|
||||
extern void ai_stay_still(object *still_objp, vec3d *view_pos);
|
||||
extern void ai_do_default_behavior(object *obj);
|
||||
extern void ai_reset_order_state(ai_info *aip);
|
||||
extern void ai_start_waypoints(object *objp, int wl_index, int wp_flags, int start_index, bool force = false);
|
||||
extern void ai_ship_hit(object *objp_ship, object *hit_objp, const vec3d *hit_normal);
|
||||
extern void ai_ship_destroy(int shipnum);
|
||||
|
||||
@@ -180,6 +180,7 @@ namespace AI {
|
||||
// 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
|
||||
// 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,
|
||||
Fix_AI_shield_management_bug,
|
||||
@@ -202,6 +203,10 @@ namespace AI {
|
||||
Fix_small_ai_recover_after_engines_repaired, // ensure small ship AI can switch back to useful AI modes if engines get repaired
|
||||
Fix_model_path_refresh_randomization, // a subsystem path keeps its randomized point when the path is refreshed because the target moved
|
||||
Fix_bay_speed_ramp, // the fighterbay speed ramp covers only the bay path itself, not points that route a departing ship around the carrier, and never exceeds max speed
|
||||
Fix_stale_ai_order_state, // a) enemy_wing, guard_*, previous_mode, resume_goal_time, and the targeted subsystem (with its path) are reset when a new order takes over (enemy_wing is also reset under Fix_ai_target_recovery);
|
||||
// b) a dynamic chase saves previous_mode/previous_submode; it and strafe entry leave them alone during an ongoing dynamic goal (evading a weapon still overwrites them, which is harmless since the evade resume re-processes orders);
|
||||
// c) resuming guard duty discards any subsystem target and path left over from the interruption;
|
||||
// d) a ship ordered to guard itself, or to guard a wing it leads (including one that becomes leader mid-mission), treats that goal as unachievable instead of holding it forever
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
@@ -771,6 +771,8 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$fix fighterbay speed ramp:", AI::Profile_Flags::Fix_bay_speed_ramp);
|
||||
|
||||
set_flag(profile, "$fix stale ai order state:", AI::Profile_Flags::Fix_stale_ai_order_state);
|
||||
|
||||
// end of options ----------------------------------------
|
||||
|
||||
// if we've been through once already and are at the same place, force a move
|
||||
@@ -1007,5 +1009,6 @@ void ai_profile_t::reset()
|
||||
flags.set(AI::Profile_Flags::Fix_ai_target_recovery);
|
||||
flags.set(AI::Profile_Flags::Fix_model_path_refresh_randomization);
|
||||
flags.set(AI::Profile_Flags::Fix_bay_speed_ramp);
|
||||
flags.set(AI::Profile_Flags::Fix_stale_ai_order_state);
|
||||
}
|
||||
}
|
||||
+15
-5
@@ -66,6 +66,16 @@ static bool ai_big_strafe_maybe_retreat(const vec3d *target_pos);
|
||||
extern void compute_desired_rvec(vec3d *rvec, const vec3d *goal_pos, const vec3d *cur_pos);
|
||||
extern void big_ship_collide_recover_start(const object *objp, const object *big_objp, const vec3d *collision_normal);
|
||||
|
||||
// Record the mode being left as the one to return to, unless a dynamic goal is already in progress
|
||||
// (in which case previous_mode still refers to the mode that goal interrupted).
|
||||
static void ai_big_save_previous_mode(ai_info *aip)
|
||||
{
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_stale_ai_order_state] && aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)
|
||||
return;
|
||||
|
||||
aip->previous_mode = aip->mode;
|
||||
}
|
||||
|
||||
|
||||
// Called by ai_big_pick_attack_point.
|
||||
// Generates a random attack point.
|
||||
@@ -499,7 +509,7 @@ bool ai_big_maybe_follow_subsys_path(bool do_dot_check)
|
||||
// for now, only follow the path to the first point
|
||||
if ( aip->path_cur < (aip->path_start+aip->path_length-1) ) {
|
||||
if ( aip->goal_objnum != aip->target_objnum ) {
|
||||
aip->previous_mode = aip->mode;
|
||||
ai_big_save_previous_mode(aip);
|
||||
aip->mode = AIM_NONE;
|
||||
aip->submode = -1;
|
||||
aip->submode_start_time = Missiontime;
|
||||
@@ -727,7 +737,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
// including ai_profile flag and if enemy fighters are near
|
||||
if (Pl_objp->phys_info.speed < The_mission.ai_profile->standard_strafe_when_below_speed &&
|
||||
(The_mission.ai_profile->flags[AI::Profile_Flags::Standard_strafe_used_more] || ai_big_maybe_start_strafe(aip, sip))) {
|
||||
aip->previous_mode = aip->mode;
|
||||
ai_big_save_previous_mode(aip);
|
||||
aip->mode = AIM_STRAFE;
|
||||
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered (i.e. MODE start time)
|
||||
ai_big_strafe_position();
|
||||
@@ -736,7 +746,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
|
||||
//Maybe enter glide strafe (check every 8 seconds, on a different schedule for each ship)
|
||||
if ((sip->can_glide == true) && !(aip->ai_flags[AI::AI_Flags::Kamikaze]) && static_randf((Missiontime + static_rand(aip->shipnum)) >> 19) < aip->ai_glide_strafe_percent) {
|
||||
aip->previous_mode = aip->mode;
|
||||
ai_big_save_previous_mode(aip);
|
||||
aip->mode = AIM_STRAFE;
|
||||
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered (i.e. MODE start time)
|
||||
aip->submode = AIS_STRAFE_GLIDE_ATTACK;
|
||||
@@ -1008,7 +1018,7 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
// switch ai ship into chase mode
|
||||
void ai_big_switch_to_chase_mode(ai_info *aip)
|
||||
{
|
||||
aip->previous_mode = aip->mode;
|
||||
ai_big_save_previous_mode(aip);
|
||||
aip->mode = AIM_CHASE;
|
||||
aip->submode = SM_ATTACK;
|
||||
aip->submode_start_time = Missiontime;
|
||||
@@ -1987,7 +1997,7 @@ int ai_big_maybe_enter_strafe_mode(const object *pl_objp, int weapon_objnum)
|
||||
|
||||
// if we've got this far, the weapon must have come from the player's target, and it is a
|
||||
// big/capital ship... so enter strafe mode
|
||||
aip->previous_mode = aip->mode;
|
||||
ai_big_save_previous_mode(aip);
|
||||
aip->mode = AIM_STRAFE;
|
||||
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered (i.e. MODE start time)
|
||||
aip->submode = AIS_STRAFE_AVOID;
|
||||
|
||||
+38
-1
@@ -5185,6 +5185,8 @@ int maybe_resume_previous_mode(object *objp, ai_info *aip)
|
||||
aip->submode = AIS_GUARD_PATROL;
|
||||
aip->submode_start_time = Missiontime;
|
||||
aip->active_goal = AI_ACTIVE_GOAL_NONE;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_stale_ai_order_state])
|
||||
set_targeted_subsys(aip, nullptr, -1); // also discards any subsystem path from attacking a big ship
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -8350,6 +8352,7 @@ void ai_set_guard_wing(object *objp, int wingnum)
|
||||
// prevents a ship from trying to guard a non-existent wing.
|
||||
if (Wings[wingnum].current_count < 1) {
|
||||
aip->guard_objnum = -1;
|
||||
aip->guard_signature = -1;
|
||||
aip->guard_wingnum = -1;
|
||||
aip->mode = AIM_NONE;
|
||||
} else {
|
||||
@@ -16059,6 +16062,29 @@ void init_aip_from_class_and_profile(ai_info *aip, ai_class *aicp, ai_profile_t
|
||||
aip->ai_profile_flags = profile->flags | (aicp->ai_profile_flags & aicp->ai_profile_flags_set);
|
||||
}
|
||||
|
||||
// Clear the fields that remember which order set them up or which order a dynamic goal interrupted.
|
||||
// Call when a new order takes over; the order's own setup repopulates whatever it needs.
|
||||
void ai_reset_order_state(ai_info *aip)
|
||||
{
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_stale_ai_order_state]
|
||||
|| The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_target_recovery])
|
||||
aip->enemy_wing = -1;
|
||||
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_stale_ai_order_state])
|
||||
{
|
||||
aip->guard_objnum = -1;
|
||||
aip->guard_signature = -1;
|
||||
aip->guard_wingnum = -1;
|
||||
aip->previous_mode = AIM_NONE;
|
||||
aip->previous_submode = 0;
|
||||
aip->resume_goal_time = -1;
|
||||
|
||||
// the player's targeted subsystem is HUD state, not order state
|
||||
if (aip != Player_ai || Player_use_ai)
|
||||
set_targeted_subsys(aip, nullptr, -1); // also discards any subsystem path
|
||||
}
|
||||
}
|
||||
|
||||
void ai_do_default_behavior(object *obj)
|
||||
{
|
||||
Assertion(obj != nullptr, "Ai_do_default_behavior() was given a bad information. Specifically obj is a nullptr. This is a coder error, please report!");
|
||||
@@ -16072,6 +16098,7 @@ void ai_do_default_behavior(object *obj)
|
||||
aip->mode = AIM_NONE;
|
||||
aip->submode_start_time = Missiontime;
|
||||
aip->active_goal = AI_ACTIVE_GOAL_NONE;
|
||||
ai_reset_order_state(aip);
|
||||
|
||||
// if we're not docked, we may modify the behavior a bit
|
||||
if (!object_is_docked(obj))
|
||||
@@ -16304,12 +16331,22 @@ void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
|
||||
if (aip->target_objnum != hitter_objnum)
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
set_target_objnum(aip, hitter_objnum);
|
||||
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_stale_ai_order_state]) {
|
||||
// don't bash the mode we're returning to if this is a dynamic goal interrupting another one
|
||||
if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) {
|
||||
aip->previous_mode = aip->mode;
|
||||
aip->previous_submode = aip->submode;
|
||||
}
|
||||
} else {
|
||||
aip->previous_submode = aip->mode; // retail typo, almost certainly meant to be previous_mode
|
||||
}
|
||||
|
||||
aip->resume_goal_time = Missiontime + i2f(20); // Only chase up to 20 seconds.
|
||||
aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
|
||||
|
||||
set_targeted_subsys(aip, NULL, -1); // Say not attacking any particular subsystem.
|
||||
|
||||
aip->previous_submode = aip->mode;
|
||||
aip->mode = AIM_CHASE;
|
||||
aip->submode = SM_ATTACK;
|
||||
aip->submode_start_time = Missiontime;
|
||||
|
||||
+16
-1
@@ -1803,6 +1803,20 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
auto target_ship_entry = aigp->target_name == nullptr ? nullptr : ship_registry_get(aigp->target_name);
|
||||
|
||||
// a ship can't guard itself, and the leader of a wing is the ship the rest of the wing guards
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_stale_ai_order_state])
|
||||
{
|
||||
if (aigp->ai_mode == AI_GOAL_GUARD && target_ship_entry && target_ship_entry->has_shipp() && target_ship_entry->shipp() == shipp)
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
if (aigp->ai_mode == AI_GOAL_GUARD_WING)
|
||||
{
|
||||
int wingnum = wing_name_lookup(aigp->target_name);
|
||||
if (wingnum >= 0 && Wings[wingnum].ship_index[0] == objp->instance)
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
}
|
||||
|
||||
// warp (depart) only achievable if there's somewhere to depart to
|
||||
if (aigp->ai_mode == AI_GOAL_WARP)
|
||||
{
|
||||
@@ -2535,8 +2549,9 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
if ( !(Player_use_ai) && (objp->flags[Object::Object_Flags::Player_ship]) && (current_goal->ai_mode != AI_GOAL_FORM_ON_WING) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ai_reset_order_state(aip);
|
||||
|
||||
std::unique_ptr<ai_goal> current_goal_backup;
|
||||
auto current_goal_target_ship = current_goal->target_name ? ship_registry_get(current_goal->target_name) : nullptr;
|
||||
|
||||
Reference in new issue
Block a user