Skip to content

form-on-wing and stay-still updates#6630

Merged
Goober5000 merged 6 commits into
scp-fs2open:masterfrom
Goober5000:form_on_wing_and_stay_still_updates
Oct 10, 2025
Merged

form-on-wing and stay-still updates#6630
Goober5000 merged 6 commits into
scp-fs2open:masterfrom
Goober5000:form_on_wing_and_stay_still_updates

Conversation

@Goober5000

@Goober5000 Goober5000 commented Mar 11, 2025

Copy link
Copy Markdown
Contributor

Various goal enhancements requested by FotG. The original ai-form-on-wing and ai-stay-still goals automatically cleared all other goals assigned to the same ship or wing, which is not always desired. This adds several flags to goals and ai_profiles to provide more fine-grained control over these behaviors.

Also removes AI_GOAL_PLACEHOLDER_1 since it is not needed; goals higher than AI_GOAL_NONE do not need to be a specific value.

Supersedes #6520. In draft pending testing.

@Goober5000 Goober5000 added ai A feature or issue related to the AI algorithms Requested by Active Mod A feature request that has been requested by a mod that is actively in development. labels Mar 11, 2025
@Goober5000 Goober5000 added this to the Release 25.0 milestone Mar 11, 2025
@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch from 891e90b to cda582f Compare March 11, 2025 04:47
@Goober5000 Goober5000 added the enhancement A new feature or upgrade of an existing feature to add additional functionality. label Mar 11, 2025
@wookieejedi

Copy link
Copy Markdown
Member

Testing all works as expected with both new behavior and current existing behavior if flags are off, thanks for all the work on this!

@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch 2 times, most recently from 8776d72 to a60b287 Compare May 19, 2025 07:22

@jg18 jg18 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few thoughts from a first pass, but I need to look more closely.

Comment thread code/ai/ai_flags.h
Comment thread code/parse/sexp.cpp Outdated
"\tCauses the specified ship to stay still. The ship will do nothing until attacked at "
"which time the ship will come to life and defend itself.\r\n\r\n"
"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"
"Takes 2 arguments...\r\n"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update this to "2 to 3 arguments".

Comment thread code/scripting/api/objs/order.cpp
Comment thread code/ai/aigoals.h Outdated
enum ai_goal_mode : uint8_t
{
AI_GOAL_NONE = 0,
AI_GOAL_PLACEHOLDER_1,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering why what would normally be a breaking enum change is OK? I take it this enum isn't serialized anywhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't, but as it turned out, I ended up using this placeholder spot for another enum from another PR. I'll rebase this PR and fix the conflicts.

Comment thread code/ai/aigoals.cpp

@jg18 jg18 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still studying, but this is what I have so far.

There was a "must fix" in the previous comments about updating the SEXP help text.

Comment thread code/ai/ai_profiles.cpp
Comment thread code/ai/aigoals.cpp Outdated
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );

// set up the clear-goals flag for certain goals
if ((mode == AI_GOAL_STAY_STILL && !The_mission.ai_profile->flags[AI::Profile_Flags::Do_not_clear_goals_when_assigning_stay_still])

@jg18 jg18 May 26, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this code fragment appears again below, I'd highly encourage creating an internal/static helper function for readability/maintainability.

Suggested function signature:

void ai_goal_set_clear_goals_flag_if_needed(ai_goal *aigp, ai_goal_mode mode)

You might want to create a helper function for the check for Do_not_set_override_when_assigning_form_on_wing as well, since both places check it, but up to you.

Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp Outdated
goals[gindex].flags.set(AI::Goal_Flags::Purge);

// if this is the active goal, don't consider it for pre-emption!!
if (gindex == active_goal)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's now a bug below when setting/updating oldest_index. We first need to check that goals[gindex].ai_mode != AI_GOAL_NONE.

Creating a helper function might be handy to use both here and above, but up to you:

bool ai_goal_is_goal_slot_empty(const ai_goal *goals, int gIndex)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably rare in practice, and shouldn't matter since if the active goal is NONE, all the other goals would also be NONE and it would just pick the next index. But fair point.

Comment thread code/ai/aigoals.cpp Outdated
int ai_goal_find_empty_slot( ai_goal *goals, int active_goal )
{
int gindex, oldest_index;
int oldest_index = -1, empty_index = -1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name first_empty_index is clearer IMO.

Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp Outdated
case AI_GOAL_WAYPOINTS_ONCE: {
int flags = 0;
if (current_goal->ai_mode == AI_GOAL_WAYPOINTS)
if (current_goal_ai_mode == AI_GOAL_WAYPOINTS)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we still dereferencing current_goal below?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(addressed elsewhere)

Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp Outdated

// save the current goal (if any) first, in case it's wiped out by the next action
int current_goal_ai_mode = current_goal->ai_mode;
auto current_goal_target_name = current_goal->target_name;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be const auto, or even better, just spell out the type: const char *.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(obsolete)

Comment thread code/ai/aigoals.cpp Outdated
// save the current goal (if any) first, in case it's wiped out by the next action
int current_goal_ai_mode = current_goal->ai_mode;
auto current_goal_target_name = current_goal->target_name;
auto current_goal_target_ship = current_goal_target_name ? ship_registry_get(current_goal_target_name) : nullptr;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be const auto or (better IMHO) const auto *.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already const by virtue of the return value of ship_registry_get

Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp Outdated
}


// save the current goal (if any) first, in case it's wiped out by the next action

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After we check for Clear_all_goals_first below, we shouldn't refer to current_goal again, right?

So there's more data from current_goal we need to save at this point, right?

Although I'm a bit confused. Since we were clearing goals before, why do we need to backup data from current_goal? Why wasn't this necessary before?

@jg18 jg18 May 30, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On 2nd thought, instead of saving individual fields from *current_goal, here's what I suggest:

  1. Keep an extra ai_goal instance in memory called, say, current_goal_backup.
  2. Right before calling ai_clear_ship_goals() below, first copy the contents of *current_goal to current_goal_backup, and then set current_goal to be &current_goal_backup.

That way, current_goal will always be a valid pointer.

Will need to confirm that there aren't unintended consequences from this change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't necessary before because the implementations of the relevant goals handled the clearing themselves. (In the case of form-on-wing it had a bug analogous to use-after-free, but it worked anyhow). Since the clearing is now generalized, saving the data is now necessary.

Your idea of backing up the current goal is a really good one. I'll implement it with a slight tweak.

Comment thread code/ai/aigoals.cpp
@jg18

jg18 commented May 30, 2025

Copy link
Copy Markdown
Member

@Goober5000 @wookieejedi I looked into whether multi is OK, and I think it is.

When the player sends orders like "Form on my wing" in multi, the result is a call to send_player_order_packet() to notify the server.

The server handles the order in process_player_order_packet(), in which the server calls either hud_squadmsg_send_ship_command() for an order the player sent to a ship or hud_squadmsg_send_wing_command() for an order the player sent to a wing. These are the same functions used to handle the order in single player. So AFAICT the server should handle the new AI flags correctly.

@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch from a60b287 to 58318a7 Compare June 7, 2025 05:30

@jg18 jg18 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Goober5000 Found two bugs in ai_remove_goal_sexp_sub() for case OP_AI_FORM_ON_WING:

  1. The 1st argument to eval_priority_et_seq() should be the node corresponding to the priority argument and not hardcoded to -1.
  2. The default priority should be The_mission.ai_profile->default_form_on_wing_priority and not hardcoded to 99.

@jg18

jg18 commented Jun 9, 2025

Copy link
Copy Markdown
Member

@Goober5000 @wookieejedi I'm confused on how this PR meshes with existing priority code for form-on-wing in ai_add_goal_sub_player() (aigoals.cpp:781-783)

	// Goober5000 - same with form-on-wing, since it's a type of staying near
	else if ( mode == AI_GOAL_FORM_ON_WING )
		aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW;

Shouldn't we be setting aigp->priority to The_mission.ai_profile->default_form_on_wing_priority instead?

This would be quite a behavior change WRT priority boost, though, since PLAYER_PRIORITY_SUPPORT_LOW is just 10.

One idea: set the priority to The_mission.ai_profile->default_form_on_wing_priority if the goal was added via player-issued order (see this comment), otherwise fall back to PLAYER_PRIORITY_SUPPORT_LOW.

As an aside, I wonder if the above code snippet explains why it always felt like issuing a "Form on my wing" order had no effect back when I used to play.

@jg18

jg18 commented Jun 9, 2025

Copy link
Copy Markdown
Member

Semi-related: strange that the switch block in error_check_initial_orders() in fredview.cpp doesn't include a case for AI_GOAL_FORM_ON_WING. I wonder why. Ditto for CFred_mission_save::save_ai_goals() in missionsave.cpp and ShipGoalsDlg::initialize() (and other functions) in shipgoalsdlg.cpp. I guess it's because FSO automatically sets "form on wing" as the default AI goal if a ship has none.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The giveOrder Lua function doesn't support using The_mission.ai_profile->default_form_on_wing_priority with form-on-wing orders. Should it?

@jg18 jg18 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some requests/suggestions around setting priority for these AI goals via SEXP.

Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp
Comment thread code/parse/sexp.cpp
"By default it will also take priority over all other goals.\r\n\r\n"
"Takes 1 to 5 arguments...\r\n"
"\t1:\tShip to form on.\r\n"
"\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"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we make priority the 2nd argument for ai-form-on-wing, then there's no way for the FREDder to customize the boolean flags (remaining optional arguments) and simultaneously leverage the default form-on-wing priority.

Therefore, consider making priority the 5th argument instead.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wookieejedi said it's more important not to break existing missions, so scratch this idea.

Presumably, if being able to use the default priority while specifying other flags were important, the SEXP would have been designed with the priority as the last arg.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, most of the optional sexp flags were added after the priority arguments already existed.

@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch from 58318a7 to 6697ce1 Compare June 30, 2025 03:55
@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch 4 times, most recently from 69d48f8 to a9a70e6 Compare August 24, 2025 21:19
@Goober5000

Goober5000 commented Aug 25, 2025

Copy link
Copy Markdown
Contributor Author

error_check_initial_orders

Added a case for this in FRED and qtFRED.

save_ai_goals

Added save code

(functions in shipgoalsdlg.cpp)

Done, plus the equivalent in qtFRED

The giveOrder Lua function doesn't support using The_mission.ai_profile->default_form_on_wing_priority with form-on-wing orders. Should it?

Hmm, it probably should.

@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch 2 times, most recently from 3240ce3 to 719dc6d Compare August 25, 2025 04:43
@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch from 719dc6d to a14729d Compare August 26, 2025 18:57
@Goober5000

Copy link
Copy Markdown
Contributor Author

@Goober5000 @wookieejedi I'm confused on how this PR meshes with existing priority code for form-on-wing in ai_add_goal_sub_player() (aigoals.cpp:781-783)

	// Goober5000 - same with form-on-wing, since it's a type of staying near
	else if ( mode == AI_GOAL_FORM_ON_WING )
		aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW;

Shouldn't we be setting aigp->priority to The_mission.ai_profile->default_form_on_wing_priority instead?

Not necessarily, because that is intended for orders given specifically via sexp. In the messaging system, orders always had different priorities:

This would be quite a behavior change WRT priority boost, though, since PLAYER_PRIORITY_SUPPORT_LOW is just 10.

That itself is a behavior change from retail, as those lines were added in 09d116f. I think the best thing to do is remove those lines to restore the original squad-commanded behavior. Presumably with the fixes to the codebase over the years and in this PR, it will cause form-on-wing to work properly, but we'll test it.

As an aside, I wonder if the above code snippet explains why it always felt like issuing a "Form on my wing" order had no effect back when I used to play.

Those lines were intended to fix that exact bug, but if the order had no effect for you, they probably didn't do their job.

@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch from a14729d to f281827 Compare September 1, 2025 20:13

@jg18 jg18 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good improvements. Still studying, but a few questions so far.

Comment thread code/ai/aigoals.cpp Outdated
@@ -603,7 +615,7 @@ void ai_goal_purge_all_invalid_goals(ai_goal *aigp)
ship_obj *sop;

// only purge goals if a new goal is one of the types in next statement

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this comment still make sense? Did it make sense even before this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, yes. For example, issuing an attack order causes any previously issued ignore orders to be removed.

@jg18 jg18 Sep 4, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, I was referring to the "next statement" part of the comment.

Presumably, the list of relevant goal types was in the if condition at one point, and then the list was moved to a helper function, so the relevant goal types no longer appear here.

Anyway, it's not important, as long as coders know what it means.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see what you mean. I'll tweak the comment.

Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp
Comment thread code/ai/aigoals.cpp
@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch 4 times, most recently from 0f80684 to f4cb41c Compare September 7, 2025 03:37
@Goober5000 Goober5000 marked this pull request as ready for review September 7, 2025 04:55
@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch 2 times, most recently from 97e551a to 66d31b7 Compare September 18, 2025 02:56
@Goober5000 Goober5000 force-pushed the form_on_wing_and_stay_still_updates branch from 66d31b7 to 0c736b7 Compare September 19, 2025 01:30
@wookieejedi

wookieejedi commented Sep 21, 2025

Copy link
Copy Markdown
Member

Alrighty, at long last the PR is ready for review :D

@jg18 jg18 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've agreed that I'll pre-approve the updated version now and then re-review in depth during 25.0 RC phase. The RC cycle shouldn't be blocked by a re-review of this PR.

@Goober5000 Goober5000 merged commit 897429e into scp-fs2open:master Oct 10, 2025
20 checks passed
@Goober5000 Goober5000 deleted the form_on_wing_and_stay_still_updates branch October 10, 2025 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai A feature or issue related to the AI algorithms enhancement A new feature or upgrade of an existing feature to add additional functionality. Requested by Active Mod A feature request that has been requested by a mod that is actively in development.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants