@@ -40,28 +40,41 @@ struct ship_registry_entry;
4040// dynamic goals are not handled here.
4141
4242// defines for player issued goal priorities
43- # define PLAYER_PRIORITY_MIN 90
44- # define PLAYER_PRIORITY_SHIP 100
45- # define PLAYER_PRIORITY_WING 95
46- # define PLAYER_PRIORITY_SUPPORT_LOW 10
43+ constexpr int PLAYER_PRIORITY_MIN = 90 ;
44+ constexpr int PLAYER_PRIORITY_SHIP = 100 ;
45+ constexpr int PLAYER_PRIORITY_WING = 95 ;
46+ constexpr int PLAYER_PRIORITY_SUPPORT_LOW = 10 ;
4747
48- #define MAX_GOAL_PRIORITY 200
48+ constexpr int MAX_GOAL_PRIORITY = 200 ;
49+ constexpr int MIN_GOAL_PRIORITY = 1 ;
50+
51+
52+ // note: "purging" is not the same as "clearing" here
53+ // purging: any goal that no longer makes sense (i.e. is "invalid") in light of a new goal is removed
54+ // clearing: every goal is removed, including, ironically, the goal that caused the clearing
4955
50- // define for which goals cause other goals to get purged
5156// Goober5000 - okay, this seems really stupid. If any ship in the mission is assigned a goal
5257// in PURGE_GOALS_ALL_SHIPS, *every* other ship will have certain goals purged. So I added
5358// PURGE_GOALS_ONE_SHIP for goals which should only purge other goals in the one ship.
5459// Goober5000 - note that the new disable and disarm goals (AI_GOAL_DISABLE_SHIP_TACTICAL and
5560// AI_GOAL_DISARM_SHIP_TACTICAL) do not purge ANY goals, not even the ones in the one ship
56- [[nodiscard]] bool purge_goals_all_ships (ai_goal_mode ai_mode)
61+ [[nodiscard]] bool causes_invalid_goal_purge_all_ships (ai_goal_mode ai_mode)
5762{
5863 return ai_mode == AI_GOAL_IGNORE || ai_mode == AI_GOAL_DISABLE_SHIP || ai_mode == AI_GOAL_DISARM_SHIP ;
5964}
60- [[nodiscard]] bool purge_goals_one_ship (ai_goal_mode ai_mode)
65+ [[nodiscard]] bool causes_invalid_goal_purge_one_ship (ai_goal_mode ai_mode)
6166{
6267 return ai_mode == AI_GOAL_IGNORE_NEW ;
6368}
6469
70+ // function for which goals cause other goals to be cleared -- see comments above on purging vs clearing
71+ [[nodiscard]] bool causes_goal_clearing (ai_goal_mode ai_mode)
72+ {
73+ return ((ai_mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still])
74+ || (ai_mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing])
75+ || (ai_mode == AI_GOAL_PLAY_DEAD ));
76+ }
77+
6578// goals given from the player to other ships in the game are also handled in this
6679// code
6780
@@ -212,7 +225,8 @@ void ai_maybe_add_form_goal(wing* wingp)
212225 }
213226
214227 // need to add a form on my wing goal here. Ships are always forming on the player's wing.
215- // it is sufficient enough to check the first goal entry to see if it has a valid goal
228+ // it is sufficient enough to check the first goal entry to see if it has a valid goal.
229+ // Note: Although the player didn't explicitly order this, it is treated as a player-issued order
216230 if (aip->goals [0 ].ai_mode == AI_GOAL_NONE ) {
217231 // Need to have a more specific target in multi, or they may end up trying to target standalone placeholder.
218232 // So form on their team leader. In dogfight, all player-slot ai die, so just exclude.
@@ -601,7 +615,7 @@ void ai_goal_purge_all_invalid_goals(ai_goal *aigp)
601615 ship_obj *sop;
602616
603617 // only purge goals if a new goal is one of the types in next statement
604- if (!purge_goals_all_ships (aigp->ai_mode ))
618+ if (!causes_invalid_goal_purge_all_ships (aigp->ai_mode ))
605619 return ;
606620
607621 for (sop = GET_FIRST (&Ship_obj_list); sop != END_OF_LIST (&Ship_obj_list); sop = GET_NEXT (sop))
@@ -770,10 +784,8 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
770784 aigp->target_name = ai_get_goal_target_name ( target_name, &aigp->target_name_index );
771785
772786 // set up the clear-goals flag for certain goals
773- if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still])
774- || (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing])
775- || (mode == AI_GOAL_PLAY_DEAD ))
776- aigp->flags .set (AI ::Goal_Flags::Clear_all_goals_first);
787+ if (causes_goal_clearing (mode))
788+ aigp->flags .set (AI ::Goal_Flags::Clear_all_goals_first);
777789
778790 // also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
779791 if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
@@ -794,10 +806,6 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
794806 if ( (mode == AI_GOAL_STAY_NEAR_SHIP ) || (mode == AI_GOAL_KEEP_SAFE_DISTANCE ) )
795807 aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW ;
796808
797- // Goober5000 - same with form-on-wing, since it's a type of staying near
798- else if ( mode == AI_GOAL_FORM_ON_WING )
799- aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW ;
800-
801809 else if ( aigp->type == ai_goal_type::PLAYER_WING ) // NOLINT(readability-braces-around-statements)
802810 aigp->priority = PLAYER_PRIORITY_WING ; // player wing goals not as high as ship goals
803811 else
@@ -812,22 +820,22 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
812820// my new docking code. :)
813821int ai_goal_find_empty_slot ( ai_goal *goals, int active_goal )
814822{
815- int oldest_index = -1 , empty_index = -1 ;
823+ int oldest_index = -1 , first_empty_index = -1 ;
816824
817825 for ( int gindex = 0 ; gindex < MAX_AI_GOALS ; gindex++ )
818826 {
819827 // get the index for the first unused goal
820828 if (goals[gindex].ai_mode == AI_GOAL_NONE )
821829 {
822- if (empty_index < 0 )
823- empty_index = gindex;
830+ if (first_empty_index < 0 )
831+ first_empty_index = gindex;
824832 }
825833 // if any goal needs to be purged when we add a goal, set the flag
826834 else if (goals[gindex].flags [AI ::Goal_Flags::Purge_when_new_goal_added])
827835 goals[gindex].flags .set (AI ::Goal_Flags::Purge);
828836
829- // if this is the active goal, don't consider it for pre-emption!!
830- if (gindex == active_goal)
837+ // if this is the active goal, and a real goal, don't consider it for pre-emption!!
838+ if (gindex == active_goal && goals[gindex]. ai_mode != AI_GOAL_NONE )
831839 continue ;
832840
833841 // store the index of the oldest goal
@@ -838,8 +846,8 @@ int ai_goal_find_empty_slot( ai_goal *goals, int active_goal )
838846 }
839847
840848 // try to use the first empty slot
841- if (empty_index >= 0 )
842- return empty_index ;
849+ if (first_empty_index >= 0 )
850+ return first_empty_index ;
843851
844852 // if we didn't find an empty slot, use the oldest goal's slot
845853 return oldest_index;
@@ -874,10 +882,8 @@ void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode
874882 aigp->target_name = ai_get_goal_target_name ( target_name, &aigp->target_name_index );
875883
876884 // set up the clear-goals flag for certain goals
877- if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still])
878- || (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing])
879- || (mode == AI_GOAL_PLAY_DEAD ))
880- aigp->flags .set (AI ::Goal_Flags::Clear_all_goals_first);
885+ if (causes_goal_clearing (mode))
886+ aigp->flags .set (AI ::Goal_Flags::Clear_all_goals_first);
881887
882888 // also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
883889 if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile ->flags [AI ::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
@@ -886,6 +892,15 @@ void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode
886892 aigp->priority = priority;
887893 aigp->int_data = int_data;
888894 aigp->float_data = float_data;
895+
896+ // range check
897+ if ( aigp->priority > MAX_GOAL_PRIORITY ) {
898+ nprintf ((" AI" , " bashing scripting priority of goal %d from %d to %d.\n " , mode, aigp->priority , MAX_GOAL_PRIORITY ));
899+ aigp->priority = MAX_GOAL_PRIORITY ;
900+ } else if ( aigp->priority < MIN_GOAL_PRIORITY ) {
901+ nprintf ((" AI" , " bashing scripting priority of goal %d from %d to %d.\n " , mode, aigp->priority , MIN_GOAL_PRIORITY ));
902+ aigp->priority = MIN_GOAL_PRIORITY ;
903+ }
889904}
890905
891906void ai_add_ship_goal_scripting (ai_goal_mode mode, int submode, int priority, const char *shipname, ai_info *aip, int int_data, float float_data)
@@ -1266,6 +1281,9 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
12661281 } else if ( aigp->priority > MAX_GOAL_PRIORITY ) {
12671282 nprintf ((" AI" , " bashing add-goal sexpression priority of goal %s from %d to %d.\n " , Sexp_nodes[CAR (sexp)].text , aigp->priority , MAX_GOAL_PRIORITY ));
12681283 aigp->priority = MAX_GOAL_PRIORITY ;
1284+ } else if ( aigp->priority < MIN_GOAL_PRIORITY ) {
1285+ nprintf ((" AI" , " bashing add-goal sexpression priority of goal %s from %d to %d.\n " , Sexp_nodes[CAR (sexp)].text , aigp->priority , MIN_GOAL_PRIORITY ));
1286+ aigp->priority = MIN_GOAL_PRIORITY ;
12691287 }
12701288
12711289 // Goober5000 - we now have an extra optional chase argument to allow chasing our own team
@@ -1378,6 +1396,11 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
13781396 nprintf ((" AI" , " bashing remove-goal sexpression priority of goal %s from %d to %d.\n " , Sexp_nodes[CAR (sexp)].text , _priority, MAX_GOAL_PRIORITY ));
13791397 _priority = MAX_GOAL_PRIORITY ;
13801398 }
1399+ else if (_priority < MIN_GOAL_PRIORITY )
1400+ {
1401+ nprintf ((" AI" , " bashing remove-goal sexpression priority of goal %s from %d to %d.\n " , Sexp_nodes[CAR (sexp)].text , _priority, MIN_GOAL_PRIORITY ));
1402+ _priority = MIN_GOAL_PRIORITY ;
1403+ }
13811404
13821405 if (n >= 0 )
13831406 {
@@ -1496,7 +1519,7 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
14961519 goalmode = (op == OP_AI_IGNORE ) ? AI_GOAL_IGNORE : AI_GOAL_IGNORE_NEW ;
14971520 break ;
14981521 case OP_AI_FORM_ON_WING :
1499- priority = eval_priority_et_seq (- 1 , 99 );
1522+ priority = eval_priority_et_seq (CDDR (node), The_mission. ai_profile -> default_form_on_wing_priority );
15001523 goalmode = AI_GOAL_FORM_ON_WING ;
15011524 break ;
15021525 case OP_AI_FLY_TO_SHIP :
@@ -2034,11 +2057,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
20342057 // Goober5000 - see note at PURGE_GOALS_ALL_SHIPS... this is bizarre
20352058 if ((status == SHIP_STATUS_ARRIVED ) && !(aigp->flags [AI ::Goal_Flags::Goals_purged]))
20362059 {
2037- if (purge_goals_all_ships (aigp->ai_mode )) {
2060+ if (causes_invalid_goal_purge_all_ships (aigp->ai_mode )) {
20382061 ai_goal_purge_all_invalid_goals (aigp);
20392062 aigp->flags .set (AI ::Goal_Flags::Goals_purged);
20402063 }
2041- else if (purge_goals_one_ship (aigp->ai_mode )) {
2064+ else if (causes_invalid_goal_purge_one_ship (aigp->ai_mode )) {
20422065 ai_goal_purge_invalid_goals (aigp, aip->goals , aip, -1 );
20432066 aigp->flags .set (AI ::Goal_Flags::Goals_purged);
20442067 }
@@ -2395,7 +2418,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
23952418 object *objp = &Objects[objnum];
23962419 object *other_obj;
23972420 ai_goal *current_goal;
2398- int wingnum, shipnum ;
2421+ int wingnum;
23992422 int original_signature;
24002423
24012424/* if (!stricmp(Ships[objp->instance].ship_name, "gtt comet")) {
@@ -2470,12 +2493,14 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
24702493 }
24712494
24722495
2473- // save the current goal (if any) first, in case it's wiped out by the next action
2474- int current_goal_ai_mode = current_goal->ai_mode ;
2475- auto current_goal_target_name = current_goal->target_name ;
2476- auto current_goal_target_ship = current_goal_target_name ? ship_registry_get (current_goal_target_name) : nullptr ;
2496+ std::unique_ptr<ai_goal> current_goal_backup;
2497+ auto current_goal_target_ship = current_goal->target_name ? ship_registry_get (current_goal->target_name ) : nullptr ;
24772498
24782499 if (current_goal->flags [AI ::Goal_Flags::Clear_all_goals_first]) {
2500+ // save the current goal before we wipe it out by clearing everything
2501+ current_goal_backup.reset (new ai_goal (*current_goal));
2502+ current_goal = current_goal_backup.get ();
2503+
24792504 // stay-still, form-on-wing, and play-dead all clear their goals here...
24802505 //
24812506 // clear out the object's goals. Seems to me that if a ship is staying still for a purpose
@@ -2490,10 +2515,10 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
24902515 ai_clear_ship_goals (aip);
24912516 }
24922517
2493- switch ( current_goal_ai_mode ) {
2518+ switch ( current_goal-> ai_mode ) {
24942519
24952520 case AI_GOAL_CHASE :
2496- if (current_goal_target_name ) {
2521+ if (current_goal-> target_name ) {
24972522 Assert (current_goal_target_ship && current_goal_target_ship->has_objp ()); // shouldn't get here if this is false!!!!
24982523 other_obj = current_goal_target_ship->objp ();
24992524 } else
@@ -2526,7 +2551,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
25262551 break ;
25272552
25282553 case AI_GOAL_GUARD_WING :
2529- wingnum = wing_name_lookup ( current_goal_target_name );
2554+ wingnum = wing_name_lookup ( current_goal-> target_name );
25302555 Assert (wingnum != -1 ); // shouldn't get here if this is false!!!!
25312556 ai_set_guard_wing (objp, wingnum);
25322557 aip->submode_start_time = Missiontime;
@@ -2535,7 +2560,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
25352560 case AI_GOAL_WAYPOINTS : // do nothing for waypoints
25362561 case AI_GOAL_WAYPOINTS_ONCE : {
25372562 int flags = 0 ;
2538- if (current_goal_ai_mode == AI_GOAL_WAYPOINTS )
2563+ if (current_goal-> ai_mode == AI_GOAL_WAYPOINTS )
25392564 flags |= WPF_REPEAT ;
25402565 if (current_goal->flags [AI ::Goal_Flags::Waypoints_in_reverse])
25412566 flags |= WPF_BACKTRACK ;
@@ -2560,7 +2585,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
25602585 // goal cannot continue. Spit out a warning and remove the goal.
25612586
25622587 // Goober5000 - do we have a specific ship to undock from?
2563- if (current_goal_target_name )
2588+ if (current_goal-> target_name )
25642589 {
25652590 // hmm, perhaps he was destroyed
25662591 if (!current_goal_target_ship || !current_goal_target_ship->has_objp ())
@@ -2629,7 +2654,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
26292654 ai_set_attack_subsystem ( objp, current_goal->ai_submode ); // submode stored the subsystem type
26302655
26312656 // don't protect-ship for tactical goals
2632- if (current_goal_ai_mode != AI_GOAL_DESTROY_SUBSYSTEM && current_goal_ai_mode != AI_GOAL_DISABLE_SHIP_TACTICAL && current_goal_ai_mode != AI_GOAL_DISARM_SHIP_TACTICAL ) {
2657+ if (current_goal-> ai_mode != AI_GOAL_DESTROY_SUBSYSTEM && current_goal-> ai_mode != AI_GOAL_DISABLE_SHIP_TACTICAL && current_goal-> ai_mode != AI_GOAL_DISARM_SHIP_TACTICAL ) {
26332658 if (aip->target_objnum != -1 ) {
26342659 int class_type = Ship_info[current_goal_target_ship->shipp ()->ship_info_index ].class_type ;
26352660 // Only protect if _not_ a capital ship. We don't want the Lucifer accidentally getting protected.
@@ -2644,7 +2669,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
26442669 }
26452670
26462671 case AI_GOAL_CHASE_WING :
2647- wingnum = wing_name_lookup ( current_goal_target_name );
2672+ wingnum = wing_name_lookup ( current_goal-> target_name );
26482673 Assertion ( wingnum >= 0 , " The target of AI_GOAL_CHASE_WING must refer to a valid wing!" );
26492674 ai_attack_wing (objp, wingnum);
26502675 break ;
@@ -2656,7 +2681,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
26562681 // chase-ship-class is chase-any but restricted to a subset of ships
26572682 case AI_GOAL_CHASE_SHIP_CLASS :
26582683 {
2659- int ship_info_index = ship_info_lookup (current_goal_target_name );
2684+ int ship_info_index = ship_info_lookup (current_goal-> target_name );
26602685 Assertion (ship_info_index >= 0 , " The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!" );
26612686 ai_attack_object (objp, nullptr , ship_info_index);
26622687 break ;
@@ -2733,7 +2758,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
27332758 break ;
27342759
27352760 default :
2736- UNREACHABLE (" unsupported goal of %d found in ai_process_mission_orders. Please report to the SCP" , current_goal_ai_mode );
2761+ UNREACHABLE (" unsupported goal of %d found in ai_process_mission_orders. Please report to the SCP" , current_goal-> ai_mode );
27372762 break ;
27382763 }
27392764
0 commit comments