Hard Light Productions Forums

Modding, Mission Design, and Coding => FS2 Open Coding - The Source Code Project (SCP) => Topic started by: The E on July 19, 2010, 03:38:28 pm

Title: Turret AI settings
Post by: The E on July 19, 2010, 03:38:28 pm
Instead of using my usual approach (also known as "commit first, ask for forgiveness later"), I decided to post this here for some feedback.

Okay, as you may or may not know, while turrets do have an AI setting, it's basically unused. This patch changes that by introducing a new AI_profiles.tbl flag.
Now, I'm not really sure if ai_profiles is really the right place for it. It would make just as much sense as a missions flag, to be honest. What are your opinions on the subject?

Here's the code:
Code: [Select]
Index: code/ai/ai_profiles.cpp
===================================================================
--- code/ai/ai_profiles.cpp (revision 6314)
+++ code/ai/ai_profiles.cpp (working copy)
@@ -395,6 +395,8 @@
 
  set_flag(profile, "$big ships manage shields:", AIPF2_BIG_SHIELD_MANAGE, AIP_FLAG2);
 
+ set_flag(profile, "$turrets use separate ai class:", AIPF2_TURRETS_USE_SEPARATE_AI, AIP_FLAG2);
+
  // if we've been through once already and are at the same place, force a move
  if ( saved_Mp && (saved_Mp == Mp) )
  Mp++;
Index: code/ai/ai_profiles.h
===================================================================
--- code/ai/ai_profiles.h (revision 6314)
+++ code/ai/ai_profiles.h (working copy)
@@ -55,6 +55,7 @@
 #define AIPF2_NO_SPECIAL_PLAYER_AVOID (1 << 1)
 #define AIPF2_PERFORM_LESS_SCREAM_CHECKS (1 << 2)
 #define AIPF2_BIG_SHIELD_MANAGE (1 << 3)
+#define AIPF2_TURRETS_USE_SEPARATE_AI (1 << 4)
 
 #define MAX_AI_PROFILES 5
 
Index: code/ai/aiturret.cpp
===================================================================
--- code/ai/aiturret.cpp (revision 6314)
+++ code/ai/aiturret.cpp (working copy)
@@ -1414,6 +1414,7 @@
  matrix turret_orient;
  int weapon_objnum;
  ai_info *parent_aip;
+ ai_info *turret_aip;
  ship *parent_ship;
  float flak_range = 0.0f;
  weapon_info *wip;
@@ -1438,6 +1439,15 @@
  return false;
 
  parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+ turret_aip = parent_aip;
+
+ if (The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI) {
+ init_aip_from_class_and_profile(turret_aip, &Ai_classes[turret->weapons.ai_class], The_mission.ai_profile);
+ //Assertion((turret_aip != NULL), "No AI class set for turret %s", turret->system_info->name);
+ //if (turret_aip == NULL)
+ // turret_aip = parent_aip;
+ }
+
  parent_ship = &Ships[Objects[parent_objnum].instance];
  wip = get_turret_weapon_wip(&turret->weapons, weapon_num);
  int turret_weapon_class = WEAPON_INFO_INDEX(wip);
@@ -1447,8 +1457,10 @@
  turret->turret_last_fire_direction = *turret_fvec;
 
  // set next fire timestamp for the turret
- if (last_shot_in_salvo)
+ if (last_shot_in_salvo && !(The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI))
  turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
+ else
+ turret_set_next_fire_timestamp(weapon_num, wip, turret, turret_aip);
 
  // if this weapon is a beam weapon, handle it specially
  if (wip->wi_flags & WIF_BEAM) {

NOTE: No matter what the outcome of this is, this won't be in 3.6.12
Title: Re: Turret AI settings
Post by: General Battuta on July 19, 2010, 03:42:59 pm
It would be useful as both. A global force would be good for mods using this from the ground up, but a per-mission flag would allow us to use it only where needed and prevent the utter destruction of backwards compatibility if a mod added it without wanting to go through all its existing missions with a fine-toothed comb.
Title: Re: Turret AI settings
Post by: The E on July 19, 2010, 03:46:15 pm
Come to think of it, as an ai_profiles flag, it is already somewhat of a per-mission flag (since you have to specifically set the AI profile in the mission specs dialogue).
Title: Re: Turret AI settings
Post by: FUBAR-BDHR on July 19, 2010, 04:04:26 pm
Should not $fix AI class bug: YES already cover this?
Title: Re: Turret AI settings
Post by: The E on July 19, 2010, 04:07:31 pm
Whether it should or not, fact is that it doesn't.

In addition, I am a bit weary of rolling this flag into that one, as it might inadvertently alter mission behaviour.
Title: Re: Turret AI settings
Post by: chief1983 on July 19, 2010, 05:17:13 pm
I like it, but it looks overly complicated near the end.  Based on the previous code, it looks like the last bit should just be:

Code: [Select]
// set next fire timestamp for the turret
if (last_shot_in_salvo)
- turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
+ turret_set_next_fire_timestamp(weapon_num, wip, turret, turret_aip);

Since you've already set turret_aip based on the profile flag, default initializing it to parent_aip.
Title: Re: Turret AI settings
Post by: The E on July 19, 2010, 05:21:17 pm
True, that would work perfectly fine.

I didn't do much optimizing, apparently........  :nervous:
Title: Re: Turret AI settings
Post by: FUBAR-BDHR on July 19, 2010, 05:32:41 pm
Ideally an optional ai_class entry in the subsystem would be the way to go defaulting back to the parent ship if none provided.  That way you could have certain turrets set to higher accuracy by default.  Shadow main beams for instance as they never miss could be set to perfect while the ship still defaults to standard Shadow cap AI.. 
Title: Re: Turret AI settings
Post by: chief1983 on July 19, 2010, 05:37:10 pm
It is defaulting to the ship's AI class via the initialization of turret_aip.  I'm guessing that init_aip_from_class_and_profile doesn't set it to NULL, if it was already initialized to the parent_aip?  If not that whole section might need to be reordered actually, to ensure turret_aip is always non-NULL.  And parent_aip shouldn't be NULL, and if it is there's nothing we can do about it at that point I don't think.

FUBAR, it sounds like what you're asking is what already happens, unless I'm really confused by what you're asking.

The_E, if my previous assumption about init_aip_from_class_and_profile was correct, I'd uncomment that assertion and delete the two commented out lines below it.
Title: Re: Turret AI settings
Post by: FUBAR-BDHR on July 19, 2010, 05:43:34 pm
Yep your confused :p

What I'm asking for is an additional optional field in the subsystem section that is used so you could have the ship default to captain but some turrets default to other ai classes.  So it would be like:

$subsystem turret03a,5,0
$subsystem_ai_class:  general

where $subsystem_ai_class is an optional field.  if not supplied then the ships ai_class is used.
Title: Re: Turret AI settings
Post by: chief1983 on July 19, 2010, 05:58:23 pm
Ah, so, instead of having to define every turret's AI every mission, it would have a tabled default different from the ship's AI level.  Roger.  Not sure that's a great idea though, we don't even set a default ai level for ships in the ships.tbl.  But I suppose there's not a much better place to put it.  It does seem a bit like a lot of potentially extra code to just save setting turrets on a few capital ships, but I guess since ships can have 80 or more turrets that would save some time, if the turrets will always have the same setup.
Title: Re: Turret AI settings
Post by: Sushi on July 19, 2010, 06:21:47 pm
Should not $fix AI class bug: YES already cover this?

No, it shouldn't. The problems are different enough that they deserve to be handled separately.


Ah, so, instead of having to define every turret's AI every mission, it would have a tabled default different from the ship's AI level.  Roger.  Not sure that's a great idea though,

I am also dubious about the cost/benefit ratio of having subsystem-specific default AIs...

we don't even set a default ai level for ships in the ships.tbl. 

Actually, we do.

But I suppose there's not a much better place to put it.  It does seem a bit like a lot of potentially extra code to just save setting turrets on a few capital ships, but I guess since ships can have 80 or more turrets that would save some time, if the turrets will always have the same setup.

Either way, modders we be able to get their desired turret behavior. Adding defaults to ships.tbl makes it rougher on the coders, but I suppose it does make life (potentially) a bit easier on the modders.


Have you tested the patch to make sure it works? I could have sworn there was code elsewhere that looks at a turret's parent AI to decide stuff, and if so that would need to be adjusted too.
Title: Re: Turret AI settings
Post by: The E on July 19, 2010, 06:21:59 pm
It is defaulting to the ship's AI class via the initialization of turret_aip.  I'm guessing that init_aip_from_class_and_profile doesn't set it to NULL, if it was already initialized to the parent_aip?  If not that whole section might need to be reordered actually, to ensure turret_aip is always non-NULL.  And parent_aip shouldn't be NULL, and if it is there's nothing we can do about it at that point I don't think.

init_aip_from_class_and_profile() actually rewrites the whole entry based on the class and profile supplied, so there really shouldn't be a situation where it is not initialized correctly.

Quote
The_E, if my previous assumption about init_aip_from_class_and_profile was correct, I'd uncomment that assertion and delete the two commented out lines below it.

Code: [Select]
Index: ai/ai_profiles.cpp
===================================================================
--- ai/ai_profiles.cpp (revision 6316)
+++ ai/ai_profiles.cpp (working copy)
@@ -395,6 +395,8 @@
 
  set_flag(profile, "$big ships manage shields:", AIPF2_BIG_SHIELD_MANAGE, AIP_FLAG2);
 
+ set_flag(profile, "$turrets use separate ai class:", AIPF2_TURRETS_USE_SEPARATE_AI, AIP_FLAG2);
+
  // if we've been through once already and are at the same place, force a move
  if ( saved_Mp && (saved_Mp == Mp) )
  Mp++;
Index: ai/ai_profiles.h
===================================================================
--- ai/ai_profiles.h (revision 6316)
+++ ai/ai_profiles.h (working copy)
@@ -55,6 +55,7 @@
 #define AIPF2_NO_SPECIAL_PLAYER_AVOID (1 << 1)
 #define AIPF2_PERFORM_LESS_SCREAM_CHECKS (1 << 2)
 #define AIPF2_BIG_SHIELD_MANAGE (1 << 3)
+#define AIPF2_TURRETS_USE_SEPARATE_AI (1 << 4)
 
 #define MAX_AI_PROFILES 5
 
Index: ai/aiturret.cpp
===================================================================
--- ai/aiturret.cpp (revision 6316)
+++ ai/aiturret.cpp (working copy)
@@ -1413,7 +1413,7 @@
 {
  matrix turret_orient;
  int weapon_objnum;
- ai_info *parent_aip;
+ ai_info *turret_aip;
  ship *parent_ship;
  float flak_range = 0.0f;
  weapon_info *wip;
@@ -1437,7 +1437,13 @@
  if((!timestamp_elapsed(get_turret_weapon_next_fire_stamp(&turret->weapons, weapon_num))) && last_shot_in_salvo)
  return false;
 
- parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+ turret_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+
+ if (The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI)
+ init_aip_from_class_and_profile(turret_aip, &Ai_classes[turret->weapons.ai_class], The_mission.ai_profile);
+
+ Assertion((turret_aip != NULL), "No AI class set for turret %s", turret->system_info->subobj_name);
+
  parent_ship = &Ships[Objects[parent_objnum].instance];
  wip = get_turret_weapon_wip(&turret->weapons, weapon_num);
  int turret_weapon_class = WEAPON_INFO_INDEX(wip);
@@ -1448,7 +1454,7 @@
 
  // set next fire timestamp for the turret
  if (last_shot_in_salvo)
- turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
+ turret_set_next_fire_timestamp(weapon_num, wip, turret, turret_aip);
 
  // if this weapon is a beam weapon, handle it specially
  if (wip->wi_flags & WIF_BEAM) {
@@ -1551,7 +1557,7 @@
  }
  }
  //Not useful -WMC
- else if (!(parent_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
+ else if (!(turret_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
  {
  float wait = 1000.0f * frand_range(0.9f, 1.1f);
  turret->turret_next_fire_stamp = timestamp((int) wait);

Like that?

Ah, so, instead of having to define every turret's AI every mission, it would have a tabled default different from the ship's AI level.  Roger.  Not sure that's a great idea though, we don't even set a default ai level for ships in the ships.tbl.  But I suppose there's not a much better place to put it.  It does seem a bit like a lot of potentially extra code to just save setting turrets on a few capital ships, but I guess since ships can have 80 or more turrets that would save some time, if the turrets will always have the same setup.

We so do set default AI classes for ships (http://www.hard-light.net/wiki/index.php/Ships.tbl#.24AI_Class:). That was sort of the problem with the is-ai-class sexp (See also: Commit 6135).

I am also dubious about the cost/benefit ratio of having subsystem-specific default AIs...

FUBAR's point is right on the money. You might want to set default values like that to simulate something like "supercharged" weapons, without having to do a whole new weapon entry for Weapons.tbl.


Quote
Have you tested the patch to make sure it works? I could have sworn there was code elsewhere that looks at a turret's parent AI to decide stuff, and if so that would need to be adjusted too.

Couldn't find any on my search (But then, I was coding this on a train ride, so I might not have looked hard enough).
However, I did test it, and it does, in fact, work.
Title: Re: Turret AI settings
Post by: General Battuta on July 19, 2010, 06:37:11 pm
I just want to emphasize that none of this should work unless a specific AI profile is selected. It's very important that default turret AI behavior not change, and that means relying on the ship's pilot AI UNLESS an AI profile that enables this option is turned on.

I know The_E is aware of this but I just want to make sure it's vocalized.
Title: Re: Turret AI settings
Post by: chief1983 on July 19, 2010, 07:57:45 pm
Ok, my bad, we do set default ai :)

Just did not remember ever setting that in ships.tbl.
Title: Re: Turret AI settings
Post by: chief1983 on July 19, 2010, 07:59:46 pm
turret_aip gets initialized by default the same way it always has, it only does the extra init step if the ai_profile flag is enabled, so you shouldn't have to worry about that Battuta.
Title: Re: Turret AI settings
Post by: AugustusVarius on July 20, 2010, 12:27:13 am
turret_aip gets initialized by default the same way it always has, it only does the extra init step if the ai_profile flag is enabled, so you shouldn't have to worry about that Battuta.
I think that's what he was saying.  He tends to be a bit of a stickler when it comes to changing default behavior.

Also, haven't default AI settings been in ships.tbl since retail?
Title: Re: Turret AI settings
Post by: Fury on July 20, 2010, 01:40:12 am
Oh boy....

If this flag gets in and is enabled, every turret in every mission would have to be checked what AI class it has not to alter mission balance. I hope you're prepared for that. From the looks of it, you'd have to set AI class for each and every turret manually as opposed to just the ship pilot. Would be a lot of work.
Title: Re: Turret AI settings
Post by: FUBAR-BDHR on July 20, 2010, 01:48:38 am
No you wouldn't.  If it you don't specify it would default to the ships just as it always did even if the flag is on.  The only way it could break anything is if someone turns it on it their mod and doesn't bother to check the missions in the mod to see if they set ai for turrets. 

On the other hand I'm actually looking forward to adding this to multiplayer as most of the missions were written with the assumption that setting turret ai actually did something.  So we will get to see those missions as they were intended.
Title: Re: Turret AI settings
Post by: Sushi on July 20, 2010, 09:28:24 am
No you wouldn't.  If it you don't specify it would default to the ships just as it always did even if the flag is on.  The only way it could break anything is if someone turns it on it their mod and doesn't bother to check the missions in the mod to see if they set ai for turrets. 

On the other hand I'm actually looking forward to adding this to multiplayer as most of the missions were written with the assumption that setting turret ai actually did something.  So we will get to see those missions as they were intended.

...but not as they were tested or ultimately balanced. :) That's hardly a problem, though, because this is set up purely as an "opt-in" feature.

Title: Re: Turret AI settings
Post by: Sushi on July 20, 2010, 09:49:24 am
Code review time! :)

Looks like you missed a case around line 2145:

 
Code: [Select]
// make sure salvo fire mode does not turn into autofire
if ((tp->flags & MSS_FLAG_TURRET_SALVO) && ((i + 1) == number_of_firings)) {
ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, parent_aip);
}

also around 338
Code: [Select]
if ( (!(wip->wi_flags & WIF_BOMB) && !(Ai_info[Ships[turret_parent->instance].ai_index].ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
    return 0;
}

and 472:
Code: [Select]
if ((Ai_info[Ships[turret_parent_obj->instance].ai_index].ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
check_weapon = false;
}

You also missed a case in the for loop starting around 793:
Code: [Select]
for(int i = 0; i < NUM_TURRET_ORDER_TYPES; i++)
{
    ai_info *aip = &Ai_info[Ships[Objects[eeo.turret_parent_objnum].instance].ai_index];
    switch(turret_subsys->turret_targeting_order[i])
...
    //Return if a bomb is found
    //don't fire anti capital ship turrets at bombs.
    if ( !((aip->ai_profile_flags & AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS) && big_only_flag) )


Line 1270:
Code: [Select]
if (Ai_info[shipp->ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {

I think that's all of them, although you may also want to look for and consider cases like:
Code: [Select]
if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
where a turret-specific behavior is applied at the ship level, but it might make sense to be able to apply it at the per-turret level too.
Title: Re: Turret AI settings
Post by: The E on July 20, 2010, 10:15:45 am
Thanks for the feedback, Sushi.

One question, however: How do I get the current turret subsystem if all I have is a pointer to the turret parent?

NM, figured it out.

EDIT: New patch:
Code: [Select]
Index: ai/ai_profiles.cpp
===================================================================
--- ai/ai_profiles.cpp (revision 6316)
+++ ai/ai_profiles.cpp (working copy)
@@ -395,6 +395,8 @@
 
  set_flag(profile, "$big ships manage shields:", AIPF2_BIG_SHIELD_MANAGE, AIP_FLAG2);
 
+ set_flag(profile, "$turrets use separate ai class:", AIPF2_TURRETS_USE_SEPARATE_AI, AIP_FLAG2);
+
  // if we've been through once already and are at the same place, force a move
  if ( saved_Mp && (saved_Mp == Mp) )
  Mp++;
Index: ai/ai_profiles.h
===================================================================
--- ai/ai_profiles.h (revision 6316)
+++ ai/ai_profiles.h (working copy)
@@ -55,6 +55,7 @@
 #define AIPF2_NO_SPECIAL_PLAYER_AVOID (1 << 1)
 #define AIPF2_PERFORM_LESS_SCREAM_CHECKS (1 << 2)
 #define AIPF2_BIG_SHIELD_MANAGE (1 << 3)
+#define AIPF2_TURRETS_USE_SEPARATE_AI (1 << 4)
 
 #define MAX_AI_PROFILES 5
 
Index: ai/aiturret.cpp
===================================================================
--- ai/aiturret.cpp (revision 6316)
+++ ai/aiturret.cpp (working copy)
@@ -286,11 +286,23 @@
  return longest_range_so_far;
 }
 
+//Sets up an ai profile for a given turret, using the turret parent, turret subsystem and current AI profile from the mission
+ai_info* init_turret_aip(ship* parent, ship_subsys* turret)
+{
+ ai_info* turret_aip = &Ai_info[parent->ai_index];
+
+ if (The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI)
+ init_aip_from_class_and_profile(turret_aip, &Ai_classes[turret->weapons.ai_class], The_mission.ai_profile);
+
+ Assertion((turret_aip != NULL), "No AI class set for turret %s", turret->system_info->subobj_name);
+ return turret_aip;
+}
+
 // return !0 if objp can be considered for a turret target, 0 otherwise
 // input: objp => object that turret is considering as an enemy
 // turret_parent => object of ship that turret sits on
 // turret => turret pointer
-int valid_turret_enemy(object *objp, object *turret_parent)
+int valid_turret_enemy(object *objp, object *turret_parent, ship_subsys* turret)
 {
  if ( objp == turret_parent ) {
  return 0;
@@ -335,7 +347,9 @@
  weapon *wp = &Weapons[objp->instance];
  weapon_info *wip = &Weapon_info[wp->weapon_info_index];
 
- if ( (!(wip->wi_flags & WIF_BOMB) && !(Ai_info[Ships[turret_parent->instance].ai_index].ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
+ ai_info* turret_aip = init_turret_aip(&Ships[turret_parent->instance], turret);
+
+ if ( (!(wip->wi_flags & WIF_BOMB) && !(turret_aip->ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
  return 0;
  }
 
@@ -366,7 +380,7 @@
  return;
  }
 
- if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
+ if ( !valid_turret_enemy(objp, turret_parent_obj, eeo->turret_subsys) ) {
  return;
  }
 
@@ -469,7 +483,9 @@
  // check if bomb is homing on the turret parent ship
  bool check_weapon = true;
 
- if ((Ai_info[Ships[turret_parent_obj->instance].ai_index].ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
+ ai_info* turret_aip = init_turret_aip(&Ships[turret_parent_obj->instance], ss);
+
+ if ((turret_aip->ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
  check_weapon = false;
  }
 
@@ -792,7 +808,8 @@
 
  for(int i = 0; i < NUM_TURRET_ORDER_TYPES; i++)
  {
- ai_info *aip = &Ai_info[Ships[Objects[eeo.turret_parent_objnum].instance].ai_index];
+ ai_info *aip = init_turret_aip(&Ships[Objects[eeo.turret_parent_objnum].instance], eeo.turret_subsys);
+
  switch(turret_subsys->turret_targeting_order[i])
  {
  case -1:
@@ -1147,7 +1164,9 @@
  vm_vec_unrotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
  float dot_return = vm_vec_dot(&turret_norm, &vector_out);
 
- if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
+ ai_info* turret_aip = init_turret_aip(&Ships[objp->instance], turret_subsysp);
+
+ if (turret_aip->ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
  if (dot_return > turret_subsysp->system_info->turret_fov) {
  // target is in sight and in fov
  return dot_return;
@@ -1267,7 +1286,9 @@
  int idx;
  float dot_fov_modifier = 0.0f;
 
- if (Ai_info[shipp->ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
+ ai_info* turret_aip = init_turret_aip(shipp, ssp);
+
+ if (turret_aip->ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
  if (ssp->system_info->turret_fov < 0)
  dot_fov_modifier = ssp->system_info->turret_fov;
  }
@@ -1413,7 +1434,7 @@
 {
  matrix turret_orient;
  int weapon_objnum;
- ai_info *parent_aip;
+ ai_info *turret_aip = init_turret_aip(&Ships[Objects[parent_objnum].instance], turret);
  ship *parent_ship;
  float flak_range = 0.0f;
  weapon_info *wip;
@@ -1437,7 +1458,6 @@
  if((!timestamp_elapsed(get_turret_weapon_next_fire_stamp(&turret->weapons, weapon_num))) && last_shot_in_salvo)
  return false;
 
- parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
  parent_ship = &Ships[Objects[parent_objnum].instance];
  wip = get_turret_weapon_wip(&turret->weapons, weapon_num);
  int turret_weapon_class = WEAPON_INFO_INDEX(wip);
@@ -1448,7 +1468,7 @@
 
  // set next fire timestamp for the turret
  if (last_shot_in_salvo)
- turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
+ turret_set_next_fire_timestamp(weapon_num, wip, turret, turret_aip);
 
  // if this weapon is a beam weapon, handle it specially
  if (wip->wi_flags & WIF_BEAM) {
@@ -1551,7 +1571,7 @@
  }
  }
  //Not useful -WMC
- else if (!(parent_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
+ else if (!(turret_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
  {
  float wait = 1000.0f * frand_range(0.9f, 1.1f);
  turret->turret_next_fire_stamp = timestamp((int) wait);
@@ -2144,8 +2164,9 @@
  } else {
  // make sure salvo fire mode does not turn into autofire
  if ((tp->flags & MSS_FLAG_TURRET_SALVO) && ((i + 1) == number_of_firings)) {
- ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
- turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, parent_aip);
+ ai_info *turret_aip = init_turret_aip(&Ships[Objects[parent_objnum].instance], ss);
+
+ turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, turret_aip);
  }
  }
  // moved this here so we increment the fire pos only after we have fired and not during it

There are still a few instances left where flags that deal with turret behaviour are only checked using the missions' AI profile. I thought it was best to leave those as they are.
Title: Re: Turret AI settings
Post by: General Battuta on July 20, 2010, 11:26:03 am
Oh boy....

If this flag gets in and is enabled, every turret in every mission would have to be checked what AI class it has not to alter mission balance. I hope you're prepared for that. From the looks of it, you'd have to set AI class for each and every turret manually as opposed to just the ship pilot. Would be a lot of work.

It would only be a problem if you enabled an AI profile that used turret AI in a given mission.

So while it would destroy (for example) War in Heaven if enabled in all missions, we could create a special AI profile with this flag and only enable it in some.
Title: Re: Turret AI settings
Post by: Sushi on July 20, 2010, 01:40:28 pm

There are still a few instances left where flags that deal with turret behaviour are only checked using the missions' AI profile. I thought it was best to leave those as they are.

Yeah, I noticed those too. When I was first adding the ability to do stuff in AI classes as well as AI profiles, I tried to distinguish between things that were AI behaviors and things that were essentially gameplay/engine tweaks. Since then, a few new flags have been added by others, and I don't know which (if any) were added with AI.tbl in mind.

I don't have time to check the whole patch right now, but I do see one major problem up front: it looks like init_turret_aip actually overrides the parent aip struct with what the turret had...watch your pointers. The fact that init_turret_aip is also being called constantly (several times per frame?) is also less than ideal.

What needs to happen is that each turret object has its own aip struct, initialized once when the ship is being loaded. I don't know if turret objects already have their own aip which just remains unused, but if we want AI class to matter for turrets, each turret will definitely need its own, and you should only have to init them once.
Title: Re: Turret AI settings
Post by: chief1983 on July 20, 2010, 01:52:37 pm
It sounds like this feature isn't as simple as originally planned, so take your time The E.
Title: Re: Turret AI settings
Post by: The E on July 20, 2010, 01:58:02 pm
I don't have time to check the whole patch right now, but I do see one major problem up front: it looks like init_turret_aip actually overrides the parent aip struct with what the turret had...watch your pointers. The fact that init_turret_aip is also being called constantly (several times per frame?) is also less than ideal.

Problem is, it needs to be called several times per frame, as the turret ai class may change during gameplay if a FREDer calls change-ai-class on it.
Title: Re: Turret AI settings
Post by: Sushi on July 20, 2010, 04:11:45 pm
I don't have time to check the whole patch right now, but I do see one major problem up front: it looks like init_turret_aip actually overrides the parent aip struct with what the turret had...watch your pointers. The fact that init_turret_aip is also being called constantly (several times per frame?) is also less than ideal.

Problem is, it needs to be called several times per frame, as the turret ai class may change during gameplay if a FREDer calls change-ai-class on it.

In that case, change-ai-class should trigger a "rebuild" of aip, but only once. That's what it does now, it would just need to apply to the turrets too.

Does the change-ai-class SEXP even work on subsystems? If not, it wouldn't make a difference. :)

Title: Re: Turret AI settings
Post by: The E on July 20, 2010, 04:23:47 pm
Yes, it does. It's set up to set subsystem AI classes already. And yes, the init thing should be done only once, if you can give me a pointer as to how this is done for ships, I'd be grateful. This would also make adding the subsystem tbl option infinitely easier.
Title: Re: Turret AI settings
Post by: Sushi on July 20, 2010, 07:07:15 pm
if you can give me a pointer as to how this is done for ships, I'd be grateful.

Don't remember it all off the op of my head, but if you look at calls to "init_aip_from_class_and_profile" you should be able to find all of the cases.
Title: Re: Turret AI settings
Post by: The E on July 20, 2010, 07:44:51 pm
Hmm.

Doing it properly seems to necessitate bumping the number of AI slots. Right now, the number of AI slots is set to the same number as the number of ships in-mission (that is, 400).
Title: Re: Turret AI settings
Post by: Sushi on July 20, 2010, 08:52:46 pm
Hmm.

Doing it properly seems to necessitate bumping the number of AI slots. Right now, the number of AI slots is set to the same number as the number of ships in-mission (that is, 400).

Yeah, I was afraid of that. This means we're going to invoke quite a bit of additional data storage to get this working. :/
Title: Re: Turret AI settings
Post by: The E on July 20, 2010, 09:11:54 pm
Or, I follow a sensible suggestion by FUBAR and just add an ai_info field to the ship_weapons struct.
Title: Re: Turret AI settings
Post by: FUBAR-BDHR on July 20, 2010, 10:28:58 pm
Yea there's a nice new head shaped dent in his desk.
Title: Re: Turret AI settings
Post by: The E on July 20, 2010, 11:05:05 pm
Okay, here's a completely new approach. This time, I added an optional field ("$Turret AI class:") to the subsystem entry, placed after "$Armor Type:", which works in the same way as the ship default AI entry.

Note that, as of right now, I can only confirm that it builds, not that it actually works (but I will, once I had a few hours' sleep).

EDIT:

Yeah, that so didn't work. Anyway, here's a new patch, and this time it's even tested!

Code: [Select]
Index: ai/ai_profiles.cpp
===================================================================
--- ai/ai_profiles.cpp (revision 6320)
+++ ai/ai_profiles.cpp (working copy)
@@ -395,6 +395,8 @@
 
  set_flag(profile, "$big ships manage shields:", AIPF2_BIG_SHIELD_MANAGE, AIP_FLAG2);
 
+ set_flag(profile, "$turrets use separate ai class:", AIPF2_TURRETS_USE_SEPARATE_AI, AIP_FLAG2);
+
  // if we've been through once already and are at the same place, force a move
  if ( saved_Mp && (saved_Mp == Mp) )
  Mp++;
Index: ai/ai_profiles.h
===================================================================
--- ai/ai_profiles.h (revision 6320)
+++ ai/ai_profiles.h (working copy)
@@ -55,6 +55,7 @@
 #define AIPF2_NO_SPECIAL_PLAYER_AVOID (1 << 1)
 #define AIPF2_PERFORM_LESS_SCREAM_CHECKS (1 << 2)
 #define AIPF2_BIG_SHIELD_MANAGE (1 << 3)
+#define AIPF2_TURRETS_USE_SEPARATE_AI (1 << 4)
 
 #define MAX_AI_PROFILES 5
 
Index: ai/aiturret.cpp
===================================================================
--- ai/aiturret.cpp (revision 6320)
+++ ai/aiturret.cpp (working copy)
@@ -286,11 +286,21 @@
  return longest_range_so_far;
 }
 
+//Returns a pointer to the AI profile of a given turret, or a pointer to the ships' AI profile
+//if no separate AI class is defined or the TURRETS_USE_SEPARATE_AI flag isn't set
+ai_info* get_current_turret_aip(ship_subsys* turret){
+ if ((The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI) && turret->weapons.ai_index == -1){
+ return &turret->weapons.turret_aip;
+ } else {
+ return &Ai_info[turret->weapons.ai_index];
+ }
+}
+
 // return !0 if objp can be considered for a turret target, 0 otherwise
 // input: objp => object that turret is considering as an enemy
 // turret_parent => object of ship that turret sits on
 // turret => turret pointer
-int valid_turret_enemy(object *objp, object *turret_parent)
+int valid_turret_enemy(object *objp, object *turret_parent, ship_subsys* turret)
 {
  if ( objp == turret_parent ) {
  return 0;
@@ -335,7 +345,7 @@
  weapon *wp = &Weapons[objp->instance];
  weapon_info *wip = &Weapon_info[wp->weapon_info_index];
 
- if ( (!(wip->wi_flags & WIF_BOMB) && !(Ai_info[Ships[turret_parent->instance].ai_index].ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
+ if ( (!(wip->wi_flags & WIF_BOMB) && !(get_current_turret_aip(turret)->ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
  return 0;
  }
 
@@ -366,7 +376,7 @@
  return;
  }
 
- if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
+ if ( !valid_turret_enemy(objp, turret_parent_obj, eeo->turret_subsys) ) {
  return;
  }
 
@@ -469,7 +479,7 @@
  // check if bomb is homing on the turret parent ship
  bool check_weapon = true;
 
- if ((Ai_info[Ships[turret_parent_obj->instance].ai_index].ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
+ if ((get_current_turret_aip(eeo->turret_subsys)->ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
  check_weapon = false;
  }
 
@@ -792,7 +802,7 @@
 
  for(int i = 0; i < NUM_TURRET_ORDER_TYPES; i++)
  {
- ai_info *aip = &Ai_info[Ships[Objects[eeo.turret_parent_objnum].instance].ai_index];
+ ai_info *aip = get_current_turret_aip(eeo.turret_subsys);
  switch(turret_subsys->turret_targeting_order[i])
  {
  case -1:
@@ -1147,7 +1157,7 @@
  vm_vec_unrotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
  float dot_return = vm_vec_dot(&turret_norm, &vector_out);
 
- if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
+ if (get_current_turret_aip(turret_subsysp)->ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
  if (dot_return > turret_subsysp->system_info->turret_fov) {
  // target is in sight and in fov
  return dot_return;
@@ -1267,7 +1277,7 @@
  int idx;
  float dot_fov_modifier = 0.0f;
 
- if (Ai_info[shipp->ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
+ if (get_current_turret_aip(ssp)->ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
  if (ssp->system_info->turret_fov < 0)
  dot_fov_modifier = ssp->system_info->turret_fov;
  }
@@ -1437,7 +1447,7 @@
  if((!timestamp_elapsed(get_turret_weapon_next_fire_stamp(&turret->weapons, weapon_num))) && last_shot_in_salvo)
  return false;
 
- parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+ parent_aip = get_current_turret_aip(turret);
  parent_ship = &Ships[Objects[parent_objnum].instance];
  wip = get_turret_weapon_wip(&turret->weapons, weapon_num);
  int turret_weapon_class = WEAPON_INFO_INDEX(wip);
@@ -1551,7 +1561,7 @@
  }
  }
  //Not useful -WMC
- else if (!(parent_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
+ else if (!(turret->weapons.turret_aip.ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
  {
  float wait = 1000.0f * frand_range(0.9f, 1.1f);
  turret->turret_next_fire_stamp = timestamp((int) wait);
@@ -2144,8 +2154,8 @@
  } else {
  // make sure salvo fire mode does not turn into autofire
  if ((tp->flags & MSS_FLAG_TURRET_SALVO) && ((i + 1) == number_of_firings)) {
- ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
- turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, parent_aip);
+ //ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+ turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, get_current_turret_aip(ss));
  }
  }
  // moved this here so we increment the fire pos only after we have fired and not during it
Index: model/model.h
===================================================================
--- model/model.h (revision 6320)
+++ model/model.h (working copy)
@@ -144,6 +144,9 @@
  float max_subsys_strength; // maximum hits of this subsystem
  int armor_type_idx; //Armor type on teh subsystem -C
 
+ int ai_class; // Stuff for per-subsystem AI
+ int ai_index;
+
  // The following items are specific to turrets and will probably be moved to
  // a separate struct so they don't take up space for all subsystem types.
  char crewspot[MAX_NAME_LEN]; // unique identifying name for this turret -- used to assign AI class and multiplayer people
Index: ship/ship.cpp
===================================================================
--- ship/ship.cpp (revision 6320)
+++ ship/ship.cpp (working copy)
@@ -2711,6 +2711,7 @@
  sp->max_subsys_strength = 0.0f;
  sp->turret_turning_rate = 0.0f;
  sp->weapon_rotation_pbank = -1;
+ sp->ai_class = sip->ai_class; //Default to ship AI
 
  memset(sp->alt_sub_name, 0, sizeof(sp->alt_sub_name) );
  memset(sp->alt_dmg_sub_name, 0, sizeof(sp->alt_dmg_sub_name) );
@@ -2796,6 +2797,19 @@
  WarningEx(LOCATION, "Ship %s, subsystem %s\nInvalid armor type %s!", sip->name, sp->subobj_name, buf);
  }
 
+ //Get the optional subsystem AI class. If it is != the ship default AI class, set AI index to -1, else leave at ship default.
+ //find_and_stuff_optional("$Turret AI Class:", &sp->ai_class, F_NAME, Ai_class_names, Num_ai_classes, "AI class names");
+ if (optional_string("$Turret AI Class:")){
+ stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
+ for (i=0; i<Num_ai_classes; i++){
+ if (!stricmp(Ai_class_names[i], buf))
+ sp->ai_class = i;
+ }
+ sp->ai_index = -1;
+ } else {
+ sp->ai_class = sip->ai_class;
+ }
+
  // Get default primary bank weapons
  if (optional_string("$Default PBanks:")){
 strcat_s(parse_error_text,"'s default primary banks");
@@ -4774,7 +4788,54 @@
 
 }
 
+void init_turret_aip(ai_info* aip)
+{
+ aip->ai_flags = 0;
+ aip->previous_mode = AIM_NONE;
+ aip->mode_time = -1;
+ aip->target_objnum = -1;
+ aip->target_signature = -1;
+ aip->previous_target_objnum = -1;
+ aip->target_time = 0.0f;
+ aip->enemy_wing = -1;
+ aip->attacker_objnum = -1;
+ aip->goal_objnum = -1;
+ aip->goal_signature = -1;
+ aip->guard_objnum = -1;
+ aip->guard_signature = -1;
+ aip->guard_wingnum = -1;
+ aip->submode = 0;
+ aip->previous_submode = 0;
+ aip->best_dot_to_enemy = -1.0f;
+ aip->best_dot_from_enemy = -1.0f;
+ aip->best_dot_to_time = 0;
+ aip->best_dot_from_time = 0;
+ aip->submode_start_time = 0;
+ aip->submode_parm0 = 0;
+ aip->active_goal = -1;
+ aip->goal_check_time = timestamp(0);
+ aip->time_enemy_in_range = 0.0f;
+ aip->time_enemy_near = 0.0f;
+ aip->last_attack_time = 0;
+ aip->last_hit_time = 0;
+ aip->last_hit_quadrant = 0;
+ aip->hitter_objnum = -1;
+ aip->hitter_signature = -1;
+ aip->resume_goal_time = -1;
+ aip->prev_accel = 0.0f;
+ aip->prev_dot_to_goal = 0.0f;
 
+ aip->ignore_objnum = UNUSED_OBJNUM;
+ aip->ignore_signature = -1;
+
+ // Goober5000
+ for (int i = 0; i < MAX_IGNORE_NEW_OBJECTS; i++)
+ {
+ aip->ignore_new_objnums[i] = UNUSED_OBJNUM;
+ aip->ignore_new_signatures[i] = -1;
+ }
+}
+
 // ignore_subsys_info => default parameter with value of 0.  This is
 // only set to 1 by the save/restore code
 int subsys_set(int objnum, int ignore_subsys_info)
@@ -4868,7 +4929,23 @@
  ship_system->favor_current_facing = model_system->favor_current_facing;
  ship_system->subsys_cargo_name = -1;
  ship_system->time_subsys_cargo_revealed = 0;
-
+
+ //Init subsystem AI
+ //We only need to set up a whole new AI profile if the subsystem in question
+ //actually has a custom AI setting and the TURRETS_USE_SEPARATE_AI flag is active.
+ //If that isn't the case, we'll just default to the parent ships' ai profile.
+ ship_system->weapons.ai_class = model_system->ai_class;
+ ship_system->weapons.ai_index = model_system->ai_index;
+ if (ship_system->weapons.ai_index == -1 && (The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI))
+ {
+ init_turret_aip(&ship_system->weapons.turret_aip);
+ init_aip_from_class_and_profile(&ship_system->weapons.turret_aip, &Ai_classes[ship_system->weapons.ai_class], The_mission.ai_profile);
+ }
+ else
+ {
+ ship_system->weapons.ai_index = shipp->ai_index;
+ }
+
  j = 0;
  int number_of_weapons = 0;
 
@@ -15324,22 +15401,31 @@
  Assert(new_ai_class >= 0);
 
  ship_subsys *ss;
+ //Only do this if we really need to
+ if (The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI) {
+ // find the ship subsystem by searching ship's subsys_list
+ ss = GET_FIRST( &Ships[ship_num].subsys_list );
+ while ( ss != END_OF_LIST( &Ships[ship_num].subsys_list ) )
+ {
+ // if we found the subsystem
+ if ( !subsystem_stricmp(ss->system_info->subobj_name, subsystem))
+ {
+ // set ai class and ai profile
+ ss->weapons.ai_class = new_ai_class;
+ if (new_ai_class != Ai_info[Ships[ship_num].ai_index].ai_class) {
+ init_aip_from_class_and_profile(&ss->weapons.turret_aip, &Ai_classes[new_ai_class], The_mission.ai_profile);
+ if ( ss->weapons.ai_index != -1 )
+ ss->weapons.ai_index = -1;
+ return;
+ } else {
+ ss->weapons.ai_index = Ships[ship_num].ai_index;
+ }
+ }
 
- // find the ship subsystem by searching ship's subsys_list
- ss = GET_FIRST( &Ships[ship_num].subsys_list );
- while ( ss != END_OF_LIST( &Ships[ship_num].subsys_list ) )
- {
- // if we found the subsystem
- if ( !subsystem_stricmp(ss->system_info->subobj_name, subsystem))
- {
- // set ai class
- ss->weapons.ai_class = new_ai_class;
- return;
+ ss = GET_NEXT( ss );
  }
-
- ss = GET_NEXT( ss );
+ // Int3(); // subsystem not found
  }
- // Int3(); // subsystem not found
 }
 
 // Goober5000 - will attempt to load an insignia bitmap and set it as active for the wing
Index: ship/ship.h
===================================================================
--- ship/ship.h (revision 6320)
+++ ship/ship.h (working copy)
@@ -133,6 +133,8 @@
  int last_fired_weapon_signature; // Signature of last fired weapon.
  int detonate_weapon_time; // time at which last fired weapon can be detonated
  int ai_class;
+ int ai_index;
+ ai_info turret_aip;
 
  int flags; // see SW_FLAG_* defines above
  ubyte primary_animation_position[MAX_SHIP_PRIMARY_BANKS];
Title: Re: Turret AI settings
Post by: The E on July 22, 2010, 11:48:00 am
Hrm.

I keep running into issues.

I have it running so that changing the ai class via sexp works, but I seem to run into brick walls when setting it up to use AI settings done via tbl or using the FRED ships editor.

Basically, it seems that the ai profile associated with the turret does not get initialized correctly. While it gets set up correctly in missionparse, by the time the mission is entered, those settings are gone again (having been reset to the "Captain" values somewhere along the line).

Can someone tell me what I'm doing wrong?

Code: [Select]
Index: ai/ai.h
===================================================================
--- ai/ai.h (revision 6320)
+++ ai/ai.h (working copy)
@@ -609,6 +609,7 @@
 extern void ai_ship_destroy(int shipnum, int method);
 extern void ai_turn_towards_vector(vec3d *dest, object *objp, float frametime, float turn_time, vec3d *slide_vec, vec3d *rel_pos, float bank_override, int flags, vec3d *rvec = NULL, int sexp_flags = 0);
 extern void init_ai_object(int objnum);
+extern void init_turret_aip(ai_info* aip);
 extern void ai_init(void); // Call this one to parse ai.tbl.
 extern void ai_level_init(void); // Call before each level to reset AI
 
Index: ai/ai_profiles.cpp
===================================================================
--- ai/ai_profiles.cpp (revision 6320)
+++ ai/ai_profiles.cpp (working copy)
@@ -395,6 +395,8 @@
 
  set_flag(profile, "$big ships manage shields:", AIPF2_BIG_SHIELD_MANAGE, AIP_FLAG2);
 
+ set_flag(profile, "$turrets use separate ai class:", AIPF2_TURRETS_USE_SEPARATE_AI, AIP_FLAG2);
+
  // if we've been through once already and are at the same place, force a move
  if ( saved_Mp && (saved_Mp == Mp) )
  Mp++;
Index: ai/ai_profiles.h
===================================================================
--- ai/ai_profiles.h (revision 6320)
+++ ai/ai_profiles.h (working copy)
@@ -55,6 +55,7 @@
 #define AIPF2_NO_SPECIAL_PLAYER_AVOID (1 << 1)
 #define AIPF2_PERFORM_LESS_SCREAM_CHECKS (1 << 2)
 #define AIPF2_BIG_SHIELD_MANAGE (1 << 3)
+#define AIPF2_TURRETS_USE_SEPARATE_AI (1 << 4)
 
 #define MAX_AI_PROFILES 5
 
Index: ai/aicode.cpp
===================================================================
--- ai/aicode.cpp (revision 6320)
+++ ai/aicode.cpp (working copy)
@@ -14531,6 +14531,54 @@
  memset(&aip->ai_override_ci,0,sizeof(control_info));
 }
 
+void init_turret_aip(ai_info* aip)
+{
+ aip->ai_flags = 0;
+ aip->previous_mode = AIM_NONE;
+ aip->mode_time = -1;
+ aip->target_objnum = -1;
+ aip->target_signature = -1;
+ aip->previous_target_objnum = -1;
+ aip->target_time = 0.0f;
+ aip->enemy_wing = -1;
+ aip->attacker_objnum = -1;
+ aip->goal_objnum = -1;
+ aip->goal_signature = -1;
+ aip->guard_objnum = -1;
+ aip->guard_signature = -1;
+ aip->guard_wingnum = -1;
+ aip->submode = 0;
+ aip->previous_submode = 0;
+ aip->best_dot_to_enemy = -1.0f;
+ aip->best_dot_from_enemy = -1.0f;
+ aip->best_dot_to_time = 0;
+ aip->best_dot_from_time = 0;
+ aip->submode_start_time = 0;
+ aip->submode_parm0 = 0;
+ aip->active_goal = -1;
+ aip->goal_check_time = timestamp(0);
+ aip->time_enemy_in_range = 0.0f;
+ aip->time_enemy_near = 0.0f;
+ aip->last_attack_time = 0;
+ aip->last_hit_time = 0;
+ aip->last_hit_quadrant = 0;
+ aip->hitter_objnum = -1;
+ aip->hitter_signature = -1;
+ aip->resume_goal_time = -1;
+ aip->prev_accel = 0.0f;
+ aip->prev_dot_to_goal = 0.0f;
+
+ aip->ignore_objnum = UNUSED_OBJNUM;
+ aip->ignore_signature = -1;
+
+ // Goober5000
+ for (int i = 0; i < MAX_IGNORE_NEW_OBJECTS; i++)
+ {
+ aip->ignore_new_objnums[i] = UNUSED_OBJNUM;
+ aip->ignore_new_signatures[i] = -1;
+ }
+}
+
 void init_ai_objects()
 {
  int i;
Index: ai/aiturret.cpp
===================================================================
--- ai/aiturret.cpp (revision 6320)
+++ ai/aiturret.cpp (working copy)
@@ -286,11 +286,24 @@
  return longest_range_so_far;
 }
 
+//Returns a pointer to the AI profile of a given turret, or a pointer to the ships' AI profile
+//if no separate AI class is defined or the TURRETS_USE_SEPARATE_AI flag isn't set
+ai_info* get_current_turret_aip(ship_subsys* turret){
+ ai_info* aip = NULL;
+ if ((The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI) && turret->weapons.ai_index == -1){
+ aip = &turret->weapons.turret_aip;
+ } else {
+ aip = &Ai_info[turret->weapons.ai_index];
+ }
+ Assert(aip != NULL);
+ return aip;
+}
+
 // return !0 if objp can be considered for a turret target, 0 otherwise
 // input: objp => object that turret is considering as an enemy
 // turret_parent => object of ship that turret sits on
 // turret => turret pointer
-int valid_turret_enemy(object *objp, object *turret_parent)
+int valid_turret_enemy(object *objp, object *turret_parent, ship_subsys* turret)
 {
  if ( objp == turret_parent ) {
  return 0;
@@ -335,7 +348,7 @@
  weapon *wp = &Weapons[objp->instance];
  weapon_info *wip = &Weapon_info[wp->weapon_info_index];
 
- if ( (!(wip->wi_flags & WIF_BOMB) && !(Ai_info[Ships[turret_parent->instance].ai_index].ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
+ if ( (!(wip->wi_flags & WIF_BOMB) && !(get_current_turret_aip(turret)->ai_profile_flags & AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY) ) ) {
  return 0;
  }
 
@@ -366,7 +379,7 @@
  return;
  }
 
- if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
+ if ( !valid_turret_enemy(objp, turret_parent_obj, eeo->turret_subsys) ) {
  return;
  }
 
@@ -469,7 +482,7 @@
  // check if bomb is homing on the turret parent ship
  bool check_weapon = true;
 
- if ((Ai_info[Ships[turret_parent_obj->instance].ai_index].ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
+ if ((get_current_turret_aip(eeo->turret_subsys)->ai_profile_flags & AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE) && (dist > eeo->weapon_travel_dist)) {
  check_weapon = false;
  }
 
@@ -792,7 +805,7 @@
 
  for(int i = 0; i < NUM_TURRET_ORDER_TYPES; i++)
  {
- ai_info *aip = &Ai_info[Ships[Objects[eeo.turret_parent_objnum].instance].ai_index];
+ ai_info *aip = get_current_turret_aip(eeo.turret_subsys);
  switch(turret_subsys->turret_targeting_order[i])
  {
  case -1:
@@ -1147,7 +1160,7 @@
  vm_vec_unrotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
  float dot_return = vm_vec_dot(&turret_norm, &vector_out);
 
- if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
+ if (get_current_turret_aip(turret_subsysp)->ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
  if (dot_return > turret_subsysp->system_info->turret_fov) {
  // target is in sight and in fov
  return dot_return;
@@ -1267,7 +1280,7 @@
  int idx;
  float dot_fov_modifier = 0.0f;
 
- if (Ai_info[shipp->ai_index].ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
+ if (get_current_turret_aip(ssp)->ai_profile_flags & AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS) {
  if (ssp->system_info->turret_fov < 0)
  dot_fov_modifier = ssp->system_info->turret_fov;
  }
@@ -1437,7 +1450,7 @@
  if((!timestamp_elapsed(get_turret_weapon_next_fire_stamp(&turret->weapons, weapon_num))) && last_shot_in_salvo)
  return false;
 
- parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+ parent_aip = get_current_turret_aip(turret);
  parent_ship = &Ships[Objects[parent_objnum].instance];
  wip = get_turret_weapon_wip(&turret->weapons, weapon_num);
  int turret_weapon_class = WEAPON_INFO_INDEX(wip);
@@ -1551,7 +1564,7 @@
  }
  }
  //Not useful -WMC
- else if (!(parent_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
+ else if (!(get_current_turret_aip(turret)->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
  {
  float wait = 1000.0f * frand_range(0.9f, 1.1f);
  turret->turret_next_fire_stamp = timestamp((int) wait);
@@ -2144,8 +2157,8 @@
  } else {
  // make sure salvo fire mode does not turn into autofire
  if ((tp->flags & MSS_FLAG_TURRET_SALVO) && ((i + 1) == number_of_firings)) {
- ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
- turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, parent_aip);
+ //ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
+ turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, get_current_turret_aip(ss));
  }
  }
  // moved this here so we increment the fire pos only after we have fired and not during it
Index: mission/missionparse.cpp
===================================================================
--- mission/missionparse.cpp (revision 6320)
+++ mission/missionparse.cpp (working copy)
@@ -2161,8 +2161,14 @@
 
  ptr->subsys_cargo_name = sssp->subsys_cargo_name;
 
- if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
+ if ((The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI) && sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
+ {
+ init_turret_aip(&ptr->weapons.turret_aip);
  ptr->weapons.ai_class = sssp->ai_class;
+ ptr->weapons.ai_index = -1;
+ init_aip_from_class_and_profile(&ptr->weapons.turret_aip, &Ai_classes[ptr->weapons.ai_class], The_mission.ai_profile);
+ ship_subsystem_set_new_ai_class(shipnum, ptr->system_info->subobj_name, sssp->ai_class);
+ }
 
  ptr->turret_best_weapon = -1;
  ptr->turret_animation_position = 0; // MA_POS_NOT_SET -> model animation position is not set
Index: model/model.h
===================================================================
--- model/model.h (revision 6320)
+++ model/model.h (working copy)
@@ -144,6 +144,9 @@
  float max_subsys_strength; // maximum hits of this subsystem
  int armor_type_idx; //Armor type on teh subsystem -C
 
+ int ai_class; // Stuff for per-subsystem AI
+ int ai_index;
+
  // The following items are specific to turrets and will probably be moved to
  // a separate struct so they don't take up space for all subsystem types.
  char crewspot[MAX_NAME_LEN]; // unique identifying name for this turret -- used to assign AI class and multiplayer people
Index: ship/ship.cpp
===================================================================
--- ship/ship.cpp (revision 6320)
+++ ship/ship.cpp (working copy)
@@ -2711,6 +2711,7 @@
  sp->max_subsys_strength = 0.0f;
  sp->turret_turning_rate = 0.0f;
  sp->weapon_rotation_pbank = -1;
+ sp->ai_class = sip->ai_class; //Default to ship AI
 
  memset(sp->alt_sub_name, 0, sizeof(sp->alt_sub_name) );
  memset(sp->alt_dmg_sub_name, 0, sizeof(sp->alt_dmg_sub_name) );
@@ -2796,6 +2797,20 @@
  WarningEx(LOCATION, "Ship %s, subsystem %s\nInvalid armor type %s!", sip->name, sp->subobj_name, buf);
  }
 
+ //Get the optional subsystem AI class. If it is != the ship default AI class, set AI index to -1, else leave at ship default.
+ //find_and_stuff_optional("$Turret AI Class:", &sp->ai_class, F_NAME, Ai_class_names, Num_ai_classes, "AI class names");
+ if (optional_string("$Turret AI Class:")){
+ stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
+ for (i=0; i<Num_ai_classes; i++){
+ if (!stricmp(Ai_class_names[i], buf))
+ sp->ai_class = i;
+ }
+ sp->ai_index = -1;
+ } else {
+ sp->ai_class = sip->ai_class;
+ sp->ai_index = -2;
+ }
+
  // Get default primary bank weapons
  if (optional_string("$Default PBanks:")){
 strcat_s(parse_error_text,"'s default primary banks");
@@ -4771,10 +4786,8 @@
  break;
  }
  // }
-
 }
 
-
 // ignore_subsys_info => default parameter with value of 0.  This is
 // only set to 1 by the save/restore code
 int subsys_set(int objnum, int ignore_subsys_info)
@@ -4868,7 +4881,28 @@
  ship_system->favor_current_facing = model_system->favor_current_facing;
  ship_system->subsys_cargo_name = -1;
  ship_system->time_subsys_cargo_revealed = 0;
-
+
+ //Init subsystem AI
+ //We only need to set up a whole new AI profile if the subsystem in question
+ //actually has a custom AI setting and the TURRETS_USE_SEPARATE_AI flag is active.
+ //If that isn't the case, we'll just default to the parent ships' ai profile.
+ ship_system->weapons.ai_class = model_system->ai_class;
+ ship_system->weapons.ai_index = model_system->ai_index;
+ if ((The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI))
+ {
+ if (ship_system->weapons.ai_index == -2)
+ {
+ ship_system->weapons.ai_index = shipp->ai_index;
+ ship_system->weapons.ai_class = Ai_info[shipp->ai_index].ai_class;
+ }
+ init_turret_aip(&ship_system->weapons.turret_aip);
+ init_aip_from_class_and_profile(&ship_system->weapons.turret_aip, &Ai_classes[ship_system->weapons.ai_class], The_mission.ai_profile);
+ }
+ else
+ {
+ ship_system->weapons.ai_index = shipp->ai_index;
+ }
+
  j = 0;
  int number_of_weapons = 0;
 
@@ -15324,22 +15358,31 @@
  Assert(new_ai_class >= 0);
 
  ship_subsys *ss;
+ //Only do this if we really need to
+ if (The_mission.ai_profile->flags2 & AIPF2_TURRETS_USE_SEPARATE_AI) {
+ // find the ship subsystem by searching ship's subsys_list
+ ss = GET_FIRST( &Ships[ship_num].subsys_list );
+ while ( ss != END_OF_LIST( &Ships[ship_num].subsys_list ) )
+ {
+ // if we found the subsystem
+ if ( !subsystem_stricmp(ss->system_info->subobj_name, subsystem))
+ {
+ // set ai class and ai profile
+ ss->weapons.ai_class = new_ai_class;
+ if (new_ai_class != Ai_info[Ships[ship_num].ai_index].ai_class ) {
+ init_aip_from_class_and_profile(&ss->weapons.turret_aip, &Ai_classes[new_ai_class], The_mission.ai_profile);
+ if ( ss->weapons.ai_index != -1 )
+ ss->weapons.ai_index = -1;
+ return;
+ } else {
+ ss->weapons.ai_index = Ships[ship_num].ai_index;
+ }
+ }
 
- // find the ship subsystem by searching ship's subsys_list
- ss = GET_FIRST( &Ships[ship_num].subsys_list );
- while ( ss != END_OF_LIST( &Ships[ship_num].subsys_list ) )
- {
- // if we found the subsystem
- if ( !subsystem_stricmp(ss->system_info->subobj_name, subsystem))
- {
- // set ai class
- ss->weapons.ai_class = new_ai_class;
- return;
+ ss = GET_NEXT( ss );
  }
-
- ss = GET_NEXT( ss );
+ // Int3(); // subsystem not found
  }
- // Int3(); // subsystem not found
 }
 
 // Goober5000 - will attempt to load an insignia bitmap and set it as active for the wing
Index: ship/ship.h
===================================================================
--- ship/ship.h (revision 6320)
+++ ship/ship.h (working copy)
@@ -133,6 +133,8 @@
  int last_fired_weapon_signature; // Signature of last fired weapon.
  int detonate_weapon_time; // time at which last fired weapon can be detonated
  int ai_class;
+ int ai_index;
+ ai_info turret_aip;
 
  int flags; // see SW_FLAG_* defines above
  ubyte primary_animation_position[MAX_SHIP_PRIMARY_BANKS];
Title: Re: Turret AI settings
Post by: Sushi on July 22, 2010, 02:50:17 pm
Or, I follow a sensible suggestion by FUBAR and just add an ai_info field to the ship_weapons struct.

Um, that's still additional data storage. Just in a different place. :)

IMO it still makes more sense to just increase the number of AI slots, so they're all lumped together. Much more transparent and easy to understand what's going on and exactly how much extra storage is being invoked.
(Alternately, we could get rid of the Ai_info array and just bundle the data structure in with the ship...hmmm...)

As to why things aren't  working: couldn't tell based on a quick scan of the diff, but you're juggling a lot of pointers and indexes and those are almost certainly the cause of the problem.