Skip to content

Commit 8bebf66

Browse files
committed
enhance goals with additional parameters
1 parent ebfa123 commit 8bebf66

6 files changed

Lines changed: 137 additions & 22 deletions

File tree

code/ai/ai_flags.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace AI {
4444
Goal_on_hold, // when set, this goal cannot currently be satisfied, although it could be in the future
4545
Subsys_needs_fixup, // when set, the subsystem index (for a destroy subsystem goal) is invalid and must be gotten from the subsys name stored in docker.name field!!
4646
Goal_override, // paired with ai_goal_type::DYNAMIC to mean this goal overrides any other goal
47+
Want_override, // a goal should set this flag if Goal_override should be assigned when the goal is achievable
4748
Purge, // purge this goal next time we process
4849
Goals_purged, // this goal has already caused other goals to get purged (because it is something like ai-disarm that renders other goals invalid)
4950
Depart_sound_played,// Goober5000 - replacement for AL's hack ;)
@@ -177,6 +178,9 @@ namespace AI {
177178
Freespace_1_missile_behavior,
178179
ETS_uses_power_output,
179180
ETS_energy_same_regardless_of_system_presence,
181+
Do_not_clear_goals_when_assigning_form_on_wing,
182+
Do_not_clear_goals_when_assigning_stay_still,
183+
Do_not_set_override_when_assigning_form_on_wing,
180184

181185
NUM_VALUES
182186
};

code/ai/ai_profiles.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,21 @@ void parse_ai_profiles_tbl(const char *filename)
707707

708708
set_flag(profile, "$ETS energy same regardless of system presence:", AI::Profile_Flags::ETS_energy_same_regardless_of_system_presence);
709709

710+
if (optional_string("$default form-on-wing priority:")) {
711+
int priority;
712+
stuff_int(&priority);
713+
if (priority > 0) {
714+
profile->default_form_on_wing_priority = priority;
715+
} else {
716+
mprintf(("Warning: $default form-on-wing priority: should be > 0 (read %d). Value will not be used.\n", priority));
717+
}
718+
}
719+
720+
set_flag(profile, "$do not clear goals when assigning form-on-wing:", AI::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing);
721+
722+
set_flag(profile, "$do not clear goals when assigning stay-still:", AI::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still);
723+
724+
set_flag(profile, "$do not set override when assigning form-on-wing:", AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing);
710725

711726
// end of options ----------------------------------------
712727

@@ -810,6 +825,8 @@ void ai_profile_t::reset()
810825
guard_big_orbit_above_target_radius = 500.0f;
811826
guard_big_orbit_max_speed_percent = 1.0f;
812827

828+
default_form_on_wing_priority = 99; // as originally assigned in ai_add_goal_sub_sexp()
829+
813830
for (int i = 0; i < NUM_SKILL_LEVELS; ++i) {
814831
max_incoming_asteroids[i] = 0;
815832
max_allowed_player_homers[i] = 0;

code/ai/ai_profiles.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class ai_profile_t {
144144
float guard_big_orbit_above_target_radius; // Radius of guardee that triggers ai_big_guard()
145145
float guard_big_orbit_max_speed_percent; // Max percent of forward speed that is used in ai_big_guard()
146146

147+
int default_form_on_wing_priority; // the priority used if not specified in the sexp
148+
147149
void reset();
148150
};
149151

code/ai/aigoals.cpp

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -754,8 +754,14 @@ void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, c
754754
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
755755

756756
// set up the clear-goals flag for certain goals
757-
if (mode == AI_GOAL_STAY_STILL || mode == AI_GOAL_FORM_ON_WING || mode == AI_GOAL_PLAY_DEAD)
758-
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
757+
if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still])
758+
|| (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing])
759+
|| (mode == AI_GOAL_PLAY_DEAD))
760+
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
761+
762+
// also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
763+
if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
764+
aigp->flags.set(AI::Goal_Flags::Want_override);
759765

760766
if (The_mission.ai_profile->flags[AI::Profile_Flags::Player_orders_afterburn_hard])
761767
aigp->flags.set(AI::Goal_Flags::Afterburn_hard);
@@ -839,8 +845,14 @@ void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode
839845
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
840846

841847
// set up the clear-goals flag for certain goals
842-
if (mode == AI_GOAL_STAY_STILL || mode == AI_GOAL_FORM_ON_WING || mode == AI_GOAL_PLAY_DEAD)
843-
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
848+
if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still])
849+
|| (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing])
850+
|| (mode == AI_GOAL_PLAY_DEAD))
851+
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
852+
853+
// also set up the override, since it's no longer done automatically in ai_mission_goal_achievable
854+
if (mode == AI_GOAL_FORM_ON_WING && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing])
855+
aigp->flags.set(AI::Goal_Flags::Want_override);
844856

845857
aigp->priority = priority;
846858
aigp->int_data = int_data;
@@ -1025,11 +1037,27 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
10251037
}
10261038

10271039
case OP_AI_STAY_STILL:
1040+
{
1041+
int n = CDR(node);
1042+
1043+
aigp->target_name = ai_get_goal_target_name(CTEXT(n), &aigp->target_name_index); // waypoint path name;
1044+
n = CDR(n);
1045+
1046+
aigp->priority = eval_num(n, priority_is_nan, priority_is_nan_forever);
1047+
n = CDR(n);
1048+
1049+
bool clear_goals = !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still];
1050+
if (n >= 0)
1051+
{
1052+
clear_goals = is_sexp_true(n);
1053+
n = CDR(n);
1054+
}
1055+
if (clear_goals)
1056+
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
1057+
10281058
aigp->ai_mode = AI_GOAL_STAY_STILL;
1029-
aigp->target_name = ai_get_goal_target_name(CTEXT(CDR(node)), &aigp->target_name_index); // waypoint path name;
1030-
aigp->priority = eval_num(CDDR(node), priority_is_nan, priority_is_nan_forever);
1031-
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
10321059
break;
1060+
}
10331061

10341062
case OP_AI_DOCK:
10351063
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
@@ -1088,11 +1116,41 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
10881116
}
10891117

10901118
case OP_AI_FORM_ON_WING:
1091-
aigp->priority = 99;
1092-
aigp->target_name = ai_get_goal_target_name(CTEXT(CDR(node)), &aigp->target_name_index);
1119+
{
1120+
int n = CDR(node);
1121+
1122+
aigp->target_name = ai_get_goal_target_name(CTEXT(n), &aigp->target_name_index);
1123+
n = CDR(n);
1124+
1125+
if (n >= 0)
1126+
{
1127+
aigp->priority = eval_num(n, priority_is_nan, priority_is_nan_forever);
1128+
n = CDR(n);
1129+
}
1130+
else
1131+
aigp->priority = The_mission.ai_profile->default_form_on_wing_priority;
1132+
1133+
bool clear_goals = !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_form_on_wing];
1134+
if (n >= 0)
1135+
{
1136+
clear_goals = is_sexp_true(n);
1137+
n = CDR(n);
1138+
}
1139+
if (clear_goals)
1140+
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
1141+
1142+
bool set_override = !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing];
1143+
if (n >= 0)
1144+
{
1145+
set_override = is_sexp_true(n);
1146+
n = CDR(n);
1147+
}
1148+
if (set_override)
1149+
aigp->flags.set(AI::Goal_Flags::Want_override);
1150+
10931151
aigp->ai_mode = AI_GOAL_FORM_ON_WING;
1094-
aigp->flags.set(AI::Goal_Flags::Clear_all_goals_first);
10951152
break;
1153+
}
10961154

10971155
case OP_AI_CHASE:
10981156
case OP_AI_CHASE_WING:
@@ -1657,7 +1715,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
16571715
if (!target_ship_entry || !target_ship_entry->has_shipp())
16581716
return ai_achievability::NOT_ACHIEVABLE;
16591717

1660-
aigp->flags.set(AI::Goal_Flags::Goal_override);
1718+
// the override flag is now set in the calling function, ai_mission_goal_achievable
16611719
return ai_achievability::ACHIEVABLE;
16621720
}
16631721

@@ -2227,6 +2285,10 @@ void validate_mission_goals(int objnum, ai_info *aip)
22272285
continue;
22282286
}
22292287

2288+
// if the status is achievable, and we want to set the override, set it
2289+
if ( (state == ai_achievability::ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Want_override]))
2290+
aigp->flags.set(AI::Goal_Flags::Goal_override);
2291+
22302292
// if the status is achievable, and the on_hold flag is set, clear the flagb
22312293
if ( (state == ai_achievability::ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Goal_on_hold]) )
22322294
aigp->flags.remove(AI::Goal_Flags::Goal_on_hold);

code/parse/sexp.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -866,12 +866,12 @@ SCP_vector<sexp_oper> Operators = {
866866
{ "ai-waypoints-once", OP_AI_WAYPOINTS_ONCE, 2, 5, SEXP_GOAL_OPERATOR, },
867867
{ "ai-ignore", OP_AI_IGNORE, 2, 2, SEXP_GOAL_OPERATOR, },
868868
{ "ai-ignore-new", OP_AI_IGNORE_NEW, 2, 2, SEXP_GOAL_OPERATOR, },
869-
{ "ai-form-on-wing", OP_AI_FORM_ON_WING, 1, 1, SEXP_GOAL_OPERATOR, },
869+
{ "ai-form-on-wing", OP_AI_FORM_ON_WING, 1, 4, SEXP_GOAL_OPERATOR, },
870870
{ "ai-fly-to-ship", OP_AI_FLY_TO_SHIP, 2, 5, SEXP_GOAL_OPERATOR, },
871871
{ "ai-stay-near-ship", OP_AI_STAY_NEAR_SHIP, 2, 5, SEXP_GOAL_OPERATOR, },
872872
{ "ai-evade-ship", OP_AI_EVADE_SHIP, 2, 2, SEXP_GOAL_OPERATOR, },
873873
{ "ai-keep-safe-distance", OP_AI_KEEP_SAFE_DISTANCE, 1, 1, SEXP_GOAL_OPERATOR, },
874-
{ "ai-stay-still", OP_AI_STAY_STILL, 2, 2, SEXP_GOAL_OPERATOR, },
874+
{ "ai-stay-still", OP_AI_STAY_STILL, 2, 3, SEXP_GOAL_OPERATOR, },
875875
{ "ai-play-dead", OP_AI_PLAY_DEAD, 1, 1, SEXP_GOAL_OPERATOR, },
876876
{ "ai-play-dead-persistent", OP_AI_PLAY_DEAD_PERSISTENT, 1, 1, SEXP_GOAL_OPERATOR, },
877877

@@ -32981,13 +32981,20 @@ int query_operator_argument_type(int op, int argnum)
3298132981
return OPF_POSITIVE;
3298232982

3298332983
case OP_AI_STAY_STILL:
32984-
if (!argnum)
32984+
if (argnum == 0)
3298532985
return OPF_SHIP_POINT;
32986-
else
32986+
else if (argnum == 1)
3298732987
return OPF_POSITIVE;
32988+
else
32989+
return OPF_BOOL;
3298832990

3298932991
case OP_AI_FORM_ON_WING:
32990-
return OPF_SHIP;
32992+
if (argnum == 0)
32993+
return OPF_SHIP;
32994+
else if (argnum == 1)
32995+
return OPF_POSITIVE;
32996+
else
32997+
return OPF_BOOL;
3299132998

3299232999
case OP_GOOD_REARM_TIME:
3299333000
case OP_BAD_REARM_TIME:
@@ -39813,16 +39820,18 @@ SCP_vector<sexp_help_struct> Sexp_help = {
3981339820

3981439821
{ OP_AI_STAY_STILL, "Ai-stay-still (Ship goal)\r\n"
3981539822
"\tCauses the specified ship to stay still. The ship will do nothing until attacked at "
39816-
"which time the ship will come to life and defend itself. All other goals specified for the ship will be cleared.\r\n\r\n"
39823+
"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"
3981739824
"Takes 2 arguments...\r\n"
3981839825
"\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
39819-
"\t2:\tGoal priority (number between 0 and 89)." },
39826+
"\t2:\tGoal priority (number between 0 and 89).\r\n"
39827+
"\t3:\tWhether to clear all goals (optional). If not specified this will be true, or the value defined in ai_profiles.\r\n"
39828+
},
3982039829

3982139830
{ OP_AI_PLAY_DEAD, "Ai-play-dead (Ship goal)\r\n"
3982239831
"\tCauses the specified ship to pretend that it is dead and not do anything. This "
3982339832
"expression should be used to indicate that a ship has no pilot and cannot respond "
3982439833
"to any enemy threats. A ship playing dead will not respond to any attack.\r\n\r\n"
39825-
"Do note that the ship's goal list is cleared, which means both that it forgets "
39834+
"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 "
3982639835
"this goal and all previous goals, and that if it receives any other goal in any way, "
3982739836
"it will immediately come back to life. Use ai-play-dead-persistent to prevent this "
3982839837
"from happening.\r\n\r\n"
@@ -39841,9 +39850,14 @@ SCP_vector<sexp_help_struct> Sexp_help = {
3984139850

3984239851
{ OP_AI_FORM_ON_WING, "Ai-form-on-wing (Ship Goal)\r\n"
3984339852
"\tCauses the ship to form on the specified ship's wing. This works analogous to the "
39844-
"player order, and will cause all other goals specified for the ship to be cleared.\r\n\r\n"
39845-
"Takes 1 argument...\r\n"
39846-
"\t1:\tShip to form on." },
39853+
"player order, and by default will cause all goals on the ship's goal list to be cleared when this goal runs. "
39854+
"By default it will also take priority over all other goals.\r\n\r\n"
39855+
"Takes 1 to 4 arguments...\r\n"
39856+
"\t1:\tShip to form on.\r\n"
39857+
"\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"
39858+
"\t3:\tWhether to clear all goals (optional). If not specified this will be true, or the value defined in ai_profiles.\r\n"
39859+
"\t4:\tWhether this goal should override all other goals (optional). If not specified this will be true, or the value defined in ai_profiles.\r\n"
39860+
},
3984739861

3984839862
{ OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n"
3984939863
"\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n"

code/scripting/api/objs/order.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,22 @@ ADE_VIRTVAR(WaypointsInReverse, l_Order, "boolean", "Waypoint-reverse flag of th
489489
return ade_set_args(L, "b", ohp->aigp->flags[AI::Goal_Flags::Waypoints_in_reverse]);
490490
}
491491

492+
ADE_VIRTVAR(OverridesWhenAchievable, l_Order, "boolean", "Whether this goal pre-empts all other goals when it is achievable", "boolean", "OverridesWhenAchievable flag, or invalid false if order handle is invalid")
493+
{
494+
order_h* ohp = nullptr;
495+
bool want_override = false;
496+
if (!ade_get_args(L, "o|b", l_Order.GetPtr(&ohp), &want_override))
497+
return ade_set_error(L, "b", false);
498+
499+
if (!ohp->isValid())
500+
return ade_set_error(L, "b", false);
501+
502+
if (ADE_SETTING_VAR)
503+
ohp->aigp->flags.set(AI::Goal_Flags::Want_override, want_override);
504+
505+
return ade_set_args(L, "b", ohp->aigp->flags[AI::Goal_Flags::Want_override]);
506+
}
507+
492508
ADE_FUNC(isValid, l_Order, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs")
493509
{
494510
order_h *ohp = NULL;

0 commit comments

Comments
 (0)