Skip to content

Add AI goals for destroying turrets of a certain type - #7700

Open
Kestrellius wants to merge 12 commits into
scp-fs2open:masterfrom
Kestrellius:turret-orders
Open

Kestrellius wants to merge 12 commits into
scp-fs2open:masterfrom
Kestrellius:turret-orders

Conversation

@Kestrellius

Copy link
Copy Markdown
Contributor

Introduces two new AI goals, AI_GOAL_DESTROY_TURRET_TYPE and AI_GOAL_DESTROY_TURRET_TYPE_ON_SHIP, which cause the AI to target turrets carrying a specific class of weapon, either on a targeted ship or on any enemy ship in the mission. From a player-experience perspective, this should help to resolve a certain type of irritating situation that occurs when destroying multiple heavy weapons on an enemy warship is necessary, but wingmen can't effectively help without impractical amounts of micromanagement.

At present, these orders are accessible only through SEXPs and scripting, but future plans involve integrating them into the player commands system.

@wookieejedi wookieejedi added enhancement A new feature or upgrade of an existing feature to add additional functionality. ai A feature or issue related to the AI algorithms labels Aug 12, 2026

@Goober5000 Goober5000 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Partial review for now; still looking through this

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

// Goober5000 - before doing anything else, check if this is a disarm goal for an arrived ship...
if ((status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode == AI_GOAL_DISARM_SHIP || aigp->ai_mode == AI_GOAL_DISARM_SHIP_TACTICAL))
if ((status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode == AI_GOAL_DISARM_SHIP || aigp->ai_mode == AI_GOAL_DISARM_SHIP_TACTICAL || aigp->ai_mode == AI_GOAL_DESTROY_TURRET_TYPE_ON_SHIP))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think AI_GOAL_DESTROY_TURRET_TYPE_ON_SHIP should be lumped in with standard disarm here. I think it needs its own separate handling, just like the other places.

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.

Hmm. What other behavior are you envisioning? I suppose I could check whether any turret aboard the ship has the specified weapon.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That block checked whether the ship had any turrets. A ship with dozens of turrets but none of the desired type would pass the check.

However, on further investigation, this is actually a dead code path for your case. The no-turrets-with-desired-weapon is already checked and returns SATISFIED. So you can just delete the block that you added on line 2115. In fact, you can delete my block too, since it's also dead code: commit 6e9b31c made the goal check the actual status of the subsystem rather than the mission log.

Comment thread code/ai/aigoals.cpp Outdated
Comment thread code/parse/sexp.cpp
Comment thread code/ship/ship.h Outdated
Comment thread code/parse/sexp.cpp Outdated
Comment thread code/parse/sexp.cpp Outdated
Comment thread code/parse/sexp.cpp Outdated
@Kestrellius

Copy link
Copy Markdown
Contributor Author

Good catches, thank you!

@wookieejedi wookieejedi added this to the Release 26.2 milestone Aug 29, 2026

@Goober5000 Goober5000 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Several more review comments. Also, dialog support for initial goals in FRED and QtFRED needs work. You'll need to test adding and modifying both goals in both editors. Alternatively, you could just remove these goals from the initial goals list and designate them as SEXP-only goals

Comment thread code/ai/aigoals.cpp
Comment on lines +1052 to +1062

case OP_AI_DESTROY_TURRET_TYPE:
aigp->ai_mode = AI_GOAL_DESTROY_TURRET_TYPE;
aigp->int_data = weapon_info_lookup(ai_get_goal_target_name( CTEXT(CDR(node)), &dummy ));
break;

case OP_AI_DESTROY_TURRET_TYPE_ON_SHIP:
aigp->ai_mode = AI_GOAL_DESTROY_TURRET_TYPE_ON_SHIP;
aigp->int_data = weapon_info_lookup(ai_get_goal_target_name( CTEXT(CDR(node)), &dummy ));
aigp->target_name = ai_get_goal_target_name( CTEXT(CDDR(node)), &aigp->target_name_index );
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These cases need to assign the goal's priority

Comment thread code/missioneditor/missionsave.cpp Outdated
Comment on lines +678 to +681
case AI_GOAL_DESTROY_TURRET_TYPE:
str = "ai-destroy-turret-type";
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't fit the pattern of goal/target/priority, so it will need to be specially handled in the above else if series

Comment thread code/missioneditor/missionsave.cpp Outdated
Comment on lines +706 to +709
case AI_GOAL_DESTROY_TURRET_TYPE_ON_SHIP:
str = "ai-destroy-turret-type-on-ship";
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here

Comment thread code/ai/aigoals.cpp Outdated
if (aigp->ai_mode == AI_GOAL_DESTROY_TURRET_TYPE) {
for (auto so : list_range(&Ship_obj_list)) {
auto type_objp = &Objects[so->objnum];
if (type_objp->type != OBJ_SHIP || type_objp->flags[Object::Object_Flags::Should_be_dead] || !Weapon_info.in_bounds(aigp->int_data))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The Weapon_info.in_bounds() check should be extracted and placed above the loop, and it should return NOT_ACHIEVABLE for an invalid weapon. See the waypoints check a few lines up for an example.

Comment thread code/ai/aigoals.cpp Outdated
Assertion(current_goal->int_data >= 0, "The target of AI_GOAL_DESTROY_TURRET_TYPE_ON_SHIP must refer to a valid weapon class!");
other_obj = current_goal_target_ship->objp();
ai_attack_object( objp, other_obj);
ai_set_attack_subsystem( objp, SUBSYSTEM_TURRET, current_goal->int_data );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sorry, this should be -SUBSYSTEM_TURRET (note the minus sign in front)

Comment thread code/ship/ship.cpp Outdated
}
}

ssp = GET_NEXT( ssp );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is this?

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.

Copy-paste error, I think.

Comment thread code/ship/ship.cpp Outdated
return false;
}

float ship_get_turret_type_aggregate_hits(ship *shipp, int wi_index)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this could be const ship *shipp

Comment thread code/ship/ship.cpp Outdated
Comment on lines +15604 to +15628
ssp = GET_FIRST(&shipp->subsys_list);
while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
ship_weapon *swp = &ssp->weapons;
bool weapon_found = false;
for ( auto& i : swp->primary_bank_weapons ) {
if (weapon_found) {
break;
}
if (i == wi_index) {
weapon_found = true;
strength += ssp->current_hits;
}
}
for ( auto& i : swp->secondary_bank_weapons ) {
if (weapon_found) {
break;
}
if (i == wi_index) {
weapon_found = true;
strength += ssp->current_hits;
}
}

ssp = GET_NEXT( ssp );
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This whole thing can be reduced to something like:

for (auto ssp: list_range(&shipp->subsys_list)) {
    if (ssp->system_info->type == SUBSYSTEM_TURRET && turret_has_weapon(ssp, wi_index))
        strength += ssp->current_hits;
}

Comment thread code/parse/sexp.cpp Outdated
{ OP_AI_DESTROY_TURRET_TYPE, "Ai-destroy-turret-type (Ship/wing goal)\r\n"
"\tThis AI goal causes a ship/wing to destroy all enemy turrets in the mission that "
"are carrying a specified weapon."
"Takes 2 arguments...\r\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be 2 or 3 arguments. Also the previous line needs \r\n\r\n

Comment thread code/parse/sexp.cpp Outdated
{ OP_AI_DESTROY_TURRET_TYPE_ON_SHIP, "Ai-destroy-turret-type-on-ship (Ship/wing goal)\r\n"
"\tThis AI goal causes a ship/wing to destroy all turrets on a specified ship that "
"are carrying a specified weapon."
"Takes 2 arguments...\r\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be 3 or 4 arguments. Also the previous line needs \r\n\r\n

@Kestrellius

Copy link
Copy Markdown
Contributor Author

Also, dialog support for initial goals in FRED and QtFRED needs work. You'll need to test adding and modifying both goals in both editors. Alternatively, you could just remove these goals from the initial goals list and designate them as SEXP-only goals

Yeah, they'll have to be left out for now. In the long term they ought to have initial-goal support, but I am not up to trying to figure out UI stuff right at the moment.

@BMagnu
BMagnu requested a review from Goober5000 September 12, 2026 01:09
@BMagnu BMagnu modified the milestones: Release 26.2, Release 27.0 Sep 19, 2026
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants