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?
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];