Skip to content

Commit 0c736b7

Browse files
committed
address feedback
1 parent 440c6fd commit 0c736b7

File tree

10 files changed

+116
-57
lines changed

10 files changed

+116
-57
lines changed

code/ai/aigoals.cpp

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,43 @@ 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+
// NOTE: this function is only evaluated in ai_add_goal_sub_player() and ai_add_goal_sub_scripting(); for
72+
// ai_add_goal_sub_sexp(), the goal clearing is integrated into the logic of the relevant sexp operators
73+
[[nodiscard]] bool causes_goal_clearing(ai_goal_mode ai_mode)
74+
{
75+
return ((ai_mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still])
76+
|| (ai_mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing])
77+
|| (ai_mode == AI_GOAL_PLAY_DEAD));
78+
}
79+
6580
// goals given from the player to other ships in the game are also handled in this
6681
// code
6782

@@ -217,7 +232,8 @@ void ai_maybe_add_form_goal(wing* wingp)
217232
}
218233

219234
// need to add a form on my wing goal here. Ships are always forming on the player's wing.
220-
// it is sufficient enough to check the first goal entry to see if it has a valid goal
235+
// it is sufficient enough to check the first goal entry to see if it has a valid goal.
236+
// Note: Although the player didn't explicitly order this, it is treated as a player-issued order
221237
if (aip->goals[0].ai_mode == AI_GOAL_NONE) {
222238
// Need to have a more specific target in multi, or they may end up trying to target standalone placeholder.
223239
// So form on their team leader. In dogfight, all player-slot ai die, so just exclude.
@@ -605,8 +621,8 @@ void ai_goal_purge_all_invalid_goals(ai_goal *aigp)
605621
int i;
606622
ship_obj *sop;
607623

608-
// only purge goals if a new goal is one of the types in next statement
609-
if (!purge_goals_all_ships(aigp->ai_mode))
624+
// only purge goals if a new goal is one of the types that require purging
625+
if (!causes_invalid_goal_purge_all_ships(aigp->ai_mode))
610626
return;
611627

612628
for (sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop))
@@ -775,10 +791,8 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
775791
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
776792

777793
// set up the clear-goals flag for certain goals
778-
if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still])
779-
|| (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing])
780-
|| (mode == AI_GOAL_PLAY_DEAD))
781-
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
794+
if (causes_goal_clearing(mode))
795+
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
782796

783797
// also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
784798
if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
@@ -813,22 +827,22 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
813827
// my new docking code. :)
814828
int ai_goal_find_empty_slot( ai_goal *goals, int active_goal )
815829
{
816-
int oldest_index = -1, empty_index = -1;
830+
int oldest_index = -1, first_empty_index = -1;
817831

818832
for ( int gindex = 0; gindex < MAX_AI_GOALS; gindex++ )
819833
{
820834
// get the index for the first unused goal
821835
if (goals[gindex].ai_mode == AI_GOAL_NONE)
822836
{
823-
if (empty_index < 0)
824-
empty_index = gindex;
837+
if (first_empty_index < 0)
838+
first_empty_index = gindex;
825839
}
826840
// if any goal needs to be purged when we add a goal, set the flag
827841
else if (goals[gindex].flags[AI::Goal_Flags::Purge_when_new_goal_added])
828842
goals[gindex].flags.set(AI::Goal_Flags::Purge);
829843

830-
// if this is the active goal, don't consider it for pre-emption!!
831-
if (gindex == active_goal)
844+
// if this is the active goal, and a real goal, don't consider it for pre-emption!!
845+
if (gindex == active_goal && goals[gindex].ai_mode != AI_GOAL_NONE)
832846
continue;
833847

834848
// store the index of the oldest goal
@@ -839,8 +853,8 @@ int ai_goal_find_empty_slot( ai_goal *goals, int active_goal )
839853
}
840854

841855
// try to use the first empty slot
842-
if (empty_index >= 0)
843-
return empty_index;
856+
if (first_empty_index >= 0)
857+
return first_empty_index;
844858

845859
// if we didn't find an empty slot, use the oldest goal's slot
846860
return oldest_index;
@@ -875,10 +889,8 @@ void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode
875889
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
876890

877891
// set up the clear-goals flag for certain goals
878-
if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still])
879-
|| (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing])
880-
|| (mode == AI_GOAL_PLAY_DEAD))
881-
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
892+
if (causes_goal_clearing(mode))
893+
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
882894

883895
// also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
884896
if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
@@ -887,6 +899,15 @@ void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode
887899
aigp->priority = priority;
888900
aigp->int_data = int_data;
889901
aigp->float_data = float_data;
902+
903+
// range check
904+
if ( aigp->priority > MAX_GOAL_PRIORITY ) {
905+
nprintf (("AI", "bashing scripting priority of goal %d from %d to %d.\n", mode, aigp->priority, MAX_GOAL_PRIORITY));
906+
aigp->priority = MAX_GOAL_PRIORITY;
907+
} else if ( aigp->priority < MIN_GOAL_PRIORITY ) {
908+
nprintf (("AI", "bashing scripting priority of goal %d from %d to %d.\n", mode, aigp->priority, MIN_GOAL_PRIORITY));
909+
aigp->priority = MIN_GOAL_PRIORITY;
910+
}
890911
}
891912

892913
void 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)
@@ -1267,6 +1288,9 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
12671288
} else if ( aigp->priority > MAX_GOAL_PRIORITY ) {
12681289
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));
12691290
aigp->priority = MAX_GOAL_PRIORITY;
1291+
} else if ( aigp->priority < MIN_GOAL_PRIORITY ) {
1292+
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));
1293+
aigp->priority = MIN_GOAL_PRIORITY;
12701294
}
12711295

12721296
// Goober5000 - we now have an extra optional chase argument to allow chasing our own team
@@ -1379,6 +1403,11 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
13791403
nprintf(("AI", "bashing remove-goal sexpression priority of goal %s from %d to %d.\n", Sexp_nodes[CAR(sexp)].text, _priority, MAX_GOAL_PRIORITY));
13801404
_priority = MAX_GOAL_PRIORITY;
13811405
}
1406+
else if (_priority < MIN_GOAL_PRIORITY)
1407+
{
1408+
nprintf(("AI", "bashing remove-goal sexpression priority of goal %s from %d to %d.\n", Sexp_nodes[CAR(sexp)].text, _priority, MIN_GOAL_PRIORITY));
1409+
_priority = MIN_GOAL_PRIORITY;
1410+
}
13821411

13831412
if (n >= 0)
13841413
{
@@ -1497,7 +1526,7 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more )
14971526
goalmode = (op == OP_AI_IGNORE) ? AI_GOAL_IGNORE : AI_GOAL_IGNORE_NEW;
14981527
break;
14991528
case OP_AI_FORM_ON_WING:
1500-
priority = eval_priority_et_seq(-1, 99);
1529+
priority = eval_priority_et_seq(CDDR(node), The_mission.ai_profile->default_form_on_wing_priority);
15011530
goalmode = AI_GOAL_FORM_ON_WING;
15021531
break;
15031532
case OP_AI_FLY_TO_SHIP:
@@ -2035,11 +2064,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
20352064
// Goober5000 - see note at PURGE_GOALS_ALL_SHIPS... this is bizarre
20362065
if ((status == SHIP_STATUS_ARRIVED) && !(aigp->flags[AI::Goal_Flags::Goals_purged]))
20372066
{
2038-
if (purge_goals_all_ships(aigp->ai_mode)) {
2067+
if (causes_invalid_goal_purge_all_ships(aigp->ai_mode)) {
20392068
ai_goal_purge_all_invalid_goals(aigp);
20402069
aigp->flags.set(AI::Goal_Flags::Goals_purged);
20412070
}
2042-
else if (purge_goals_one_ship(aigp->ai_mode)) {
2071+
else if (causes_invalid_goal_purge_one_ship(aigp->ai_mode)) {
20432072
ai_goal_purge_invalid_goals(aigp, aip->goals, aip, -1);
20442073
aigp->flags.set(AI::Goal_Flags::Goals_purged);
20452074
}
@@ -2396,7 +2425,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
23962425
object *objp = &Objects[objnum];
23972426
object *other_obj;
23982427
ai_goal *current_goal;
2399-
int wingnum, shipnum;
2428+
int wingnum;
24002429
int original_signature;
24012430

24022431
/* if (!stricmp(Ships[objp->instance].ship_name, "gtt comet")) {
@@ -2471,12 +2500,14 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
24712500
}
24722501

24732502

2474-
// save the current goal (if any) first, in case it's wiped out by the next action
2475-
int current_goal_ai_mode = current_goal->ai_mode;
2476-
auto current_goal_target_name = current_goal->target_name;
2477-
auto current_goal_target_ship = current_goal_target_name ? ship_registry_get(current_goal_target_name) : nullptr;
2503+
std::unique_ptr<ai_goal> current_goal_backup;
2504+
auto current_goal_target_ship = current_goal->target_name ? ship_registry_get(current_goal->target_name) : nullptr;
24782505

24792506
if (current_goal->flags[AI::Goal_Flags::Clear_all_goals_first]) {
2507+
// save the current goal before we wipe it out by clearing everything
2508+
current_goal_backup.reset(new ai_goal(*current_goal));
2509+
current_goal = current_goal_backup.get();
2510+
24802511
// stay-still, form-on-wing, and play-dead all clear their goals here...
24812512
//
24822513
// clear out the object's goals. Seems to me that if a ship is staying still for a purpose
@@ -2491,10 +2522,10 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
24912522
ai_clear_ship_goals(aip);
24922523
}
24932524

2494-
switch ( current_goal_ai_mode ) {
2525+
switch ( current_goal->ai_mode ) {
24952526

24962527
case AI_GOAL_CHASE:
2497-
if (current_goal_target_name) {
2528+
if (current_goal->target_name) {
24982529
Assert(current_goal_target_ship && current_goal_target_ship->has_objp()); // shouldn't get here if this is false!!!!
24992530
other_obj = current_goal_target_ship->objp();
25002531
} else
@@ -2527,7 +2558,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
25272558
break;
25282559

25292560
case AI_GOAL_GUARD_WING:
2530-
wingnum = wing_name_lookup( current_goal_target_name );
2561+
wingnum = wing_name_lookup( current_goal->target_name );
25312562
Assert (wingnum != -1 ); // shouldn't get here if this is false!!!!
25322563
ai_set_guard_wing(objp, wingnum);
25332564
aip->submode_start_time = Missiontime;
@@ -2536,7 +2567,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
25362567
case AI_GOAL_WAYPOINTS: // do nothing for waypoints
25372568
case AI_GOAL_WAYPOINTS_ONCE: {
25382569
int flags = 0;
2539-
if (current_goal_ai_mode == AI_GOAL_WAYPOINTS)
2570+
if (current_goal->ai_mode == AI_GOAL_WAYPOINTS)
25402571
flags |= WPF_REPEAT;
25412572
if (current_goal->flags[AI::Goal_Flags::Waypoints_in_reverse])
25422573
flags |= WPF_BACKTRACK;
@@ -2561,7 +2592,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
25612592
// goal cannot continue. Spit out a warning and remove the goal.
25622593

25632594
// Goober5000 - do we have a specific ship to undock from?
2564-
if (current_goal_target_name)
2595+
if (current_goal->target_name)
25652596
{
25662597
// hmm, perhaps he was destroyed
25672598
if (!current_goal_target_ship || !current_goal_target_ship->has_objp())
@@ -2630,7 +2661,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
26302661
ai_set_attack_subsystem( objp, current_goal->ai_submode ); // submode stored the subsystem type
26312662

26322663
// don't protect-ship for tactical goals
2633-
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) {
2664+
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) {
26342665
if (aip->target_objnum != -1) {
26352666
int class_type = Ship_info[current_goal_target_ship->shipp()->ship_info_index].class_type;
26362667
// Only protect if _not_ a capital ship. We don't want the Lucifer accidentally getting protected.
@@ -2645,7 +2676,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
26452676
}
26462677

26472678
case AI_GOAL_CHASE_WING:
2648-
wingnum = wing_name_lookup( current_goal_target_name );
2679+
wingnum = wing_name_lookup( current_goal->target_name );
26492680
Assertion( wingnum >= 0, "The target of AI_GOAL_CHASE_WING must refer to a valid wing!" );
26502681
ai_attack_wing(objp, wingnum);
26512682
break;
@@ -2657,7 +2688,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
26572688
// chase-ship-class is chase-any but restricted to a subset of ships
26582689
case AI_GOAL_CHASE_SHIP_CLASS:
26592690
{
2660-
int ship_info_index = ship_info_lookup(current_goal_target_name);
2691+
int ship_info_index = ship_info_lookup(current_goal->target_name);
26612692
Assertion(ship_info_index >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!");
26622693
ai_attack_object(objp, nullptr, ship_info_index);
26632694
break;
@@ -2734,7 +2765,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
27342765
break;
27352766

27362767
default:
2737-
UNREACHABLE("unsupported goal of %d found in ai_process_mission_orders. Please report to the SCP", current_goal_ai_mode);
2768+
UNREACHABLE("unsupported goal of %d found in ai_process_mission_orders. Please report to the SCP", current_goal->ai_mode);
27382769
break;
27392770
}
27402771

code/parse/sexp.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39975,8 +39975,11 @@ SCP_vector<sexp_help_struct> Sexp_help = {
3997539975

3997639976
{ OP_AI_STAY_STILL, "Ai-stay-still (Ship goal)\r\n"
3997739977
"\tCauses the specified ship to stay still. The ship will do nothing until attacked at "
39978-
"which time the ship will come to life and defend itself. By default all goals on the ship's goal list will be cleared when this goal runs.\r\n\r\n"
39979-
"Takes 2 arguments...\r\n"
39978+
"which time the ship will come to life and defend itself.\r\n\r\n"
39979+
"By default all goals on the ship's goal list will be cleared when this goal runs, which means that the ship forgets both "
39980+
"this goal and all previous goals, and if it receives any other goal in any way, "
39981+
"it will immediately begin a new behavior. Use the optional sexp flag (argument 3) to prevent this from happening.\r\n\r\n"
39982+
"Takes 2 to 3 arguments...\r\n"
3998039983
"\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
3998139984
"\t2:\tGoal priority (number between 0 and 89).\r\n"
3998239985
"\t3:\tWhether to clear all goals (optional). If not specified this will be true, or the value defined in ai_profiles.\r\n"
@@ -39986,10 +39989,9 @@ SCP_vector<sexp_help_struct> Sexp_help = {
3998639989
"\tCauses the specified ship to pretend that it is dead and not do anything. This "
3998739990
"expression should be used to indicate that a ship has no pilot and cannot respond "
3998839991
"to any enemy threats. A ship playing dead will not respond to any attack.\r\n\r\n"
39989-
"Do note that all goals on the ship's goal list will be cleared when this goal runs, which means that the ship forgets both "
39990-
"this goal and all previous goals, and that if it receives any other goal in any way, "
39991-
"it will immediately come back to life. Use ai-play-dead-persistent to prevent this "
39992-
"from happening.\r\n\r\n"
39992+
"By default all goals on the ship's goal list will be cleared when this goal runs, which means that the ship forgets both "
39993+
"this goal and all previous goals, and if it receives any other goal in any way, "
39994+
"it will immediately come back to life. Use ai-play-dead-persistent to prevent this from happening.\r\n\r\n"
3999339995
"Takes 1 argument...\r\n"
3999439996
"\t1:\tGoal priority (number between 0 and 89)." },
3999539997

@@ -40005,8 +40007,10 @@ SCP_vector<sexp_help_struct> Sexp_help = {
4000540007

4000640008
{ OP_AI_FORM_ON_WING, "Ai-form-on-wing (Ship Goal)\r\n"
4000740009
"\tCauses the ship to form on the specified ship's wing. This works analogous to the "
40008-
"player order, and by default will cause all goals on the ship's goal list to be cleared when this goal runs. "
40009-
"By default it will also take priority over all other goals.\r\n\r\n"
40010+
"player order, and by default will cause all goals on the ship's goal list to be cleared when this goal runs, which means that the ship forgets both "
40011+
"this goal and all previous goals, and if it receives any other goal in any way, "
40012+
"it will immediately begin a new behavior. By default it will also be given an internal override flag that will cause it to "
40013+
"take priority over all other goals.\r\n\r\n"
4001040014
"Takes 1 to 5 arguments...\r\n"
4001140015
"\t1:\tShip to form on.\r\n"
4001240016
"\t2:\tGoal priority (number between 0 and 89, optional). If not specified this will be 99, or the number defined in ai_profiles.\r\n"

0 commit comments

Comments
 (0)