FS2_Open
Open source remastering of the Freespace 2 engine
aiturret.cpp
Go to the documentation of this file.
1 
2 
3 #include "ai/aibig.h"
4 #include "ai/aiinternal.h"
5 #include "asteroid/asteroid.h"
6 #include "debugconsole/console.h"
7 #include "freespace2/freespace.h"
8 #include "gamesnd/gamesnd.h"
9 #include "globalincs/linklist.h"
10 #include "globalincs/systemvars.h"
11 #include "iff_defs/iff_defs.h"
12 #include "io/timer.h"
13 #include "math/staticrand.h"
14 #include "network/multi.h"
15 #include "network/multimsgs.h"
16 #include "object/objectdock.h"
17 #include "parse/scripting.h"
18 #include "render/3d.h"
19 #include "ship/ship.h"
20 #include "ship/shipfx.h"
21 #include "weapon/beam.h"
22 #include "weapon/flak.h"
23 #include "weapon/muzzleflash.h"
24 #include "weapon/swarm.h"
25 #include "weapon/weapon.h"
26 
27 #include <limits.h>
28 
29 
30 // How close a turret has to be point at its target before it
31 // can fire. If the dot of the gun normal and the vector from gun
32 // to target is greater than this, the turret fires. The smaller
33 // the sloppier the shooting.
34 #define AICODE_TURRET_DUMBFIRE_ANGLE (0.8f)
35 #define AICODE_TURRET_HEATSEEK_ANGLE (0.7f)
36 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
37 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR (0.8f)
38 
39 float Lethality_range_const = 2.0f;
40 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
41 {
43 }
44 
46  // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
47  0.0f, 0.0f, 0.0f, 0.0f, 0.0f
48 };
49 
51  "Bombs",
52  "Ships",
53  "Asteroids",
54 };
55 
56 #define EEOF_BIG_ONLY (1<<0) // turret fires only at big and huge ships
57 #define EEOF_SMALL_ONLY (1<<1) // turret fires only at small ships
58 #define EEOF_TAGGED_ONLY (1<<2) // turret fires only at tagged ships
59 #define EEOF_BEAM (1<<3) // turret is a beam
60 #define EEOF_FLAK (1<<4) // turret is flak
61 #define EEOF_LASER (1<<5) // turret is a laser
62 #define EEOF_MISSILE (1<<6) // turret is a missile
63 
64 typedef struct eval_enemy_obj_struct {
65  int turret_parent_objnum; // parent of turret
66  float weapon_travel_dist; // max targeting range of turret weapon
68  int weapon_system_ok; // is the weapon subsystem of turret ship ok
69  int eeo_flags;
70 
75 
76 
77  float nearest_attacker_dist; // nearest ship
79 
80  float nearest_homing_bomb_dist; // nearest homing bomb
82 
83  float nearest_bomb_dist; // nearest non-homing bomb
85 
86  float nearest_dist; // nearest ship attacking this turret
89 
101 int object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
102 {
103  vec3d v2e;
104  float size_mod;
105  bool in_fov;
106 
107  vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
108  size_mod = objp->radius / (dist + objp->radius);
109 
110  in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
111 
112  if ( in_fov ) {
113  return 1;
114  }
115 
116  return 0;
117 }
118 
119 bool is_object_radius_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, vec3d *v2e, vec3d *predicted_pos, float distance)
120 {
121  float target_dist = distance;
122  if (distance == 0.0f)
123  target_dist = vm_vec_dist(predicted_pos,tpos);
124 
125  if (object_in_turret_fov(objp, ss, tvec, tpos, target_dist + objp->radius)) {
126  // so the targeted spot in not in fov but the enemy + radius is
127  // lets align the darn gun and try shooting there
128  vec3d temp_vec;
129  float multiplier = 0;
130  model_subsystem *tp = ss->system_info;
131 
132  // project v2e_from_turret to turret normal
133  // substract resultant vector from the temp_vec (v2e_from_turret)
134  // adjust z component as necessary
135  // calculate multiplier for the resultant vector
136  // use multiplier and the z component and compose a new vector
137  float dot = vm_vec_dot(v2e, tvec);
138 
139  vm_vec_scale_add(&temp_vec, v2e, tvec, -dot);
140 
141  if (IS_VEC_NULL_SQ_SAFE(&temp_vec)) {
142  // return false, target is perfectly aligned over or below the turret
143  // safe bet is to allow turret to reacquire better target
144  return false;
145  }
146 
147  // normalize the vec, it needs to be done regardless
148  vm_vec_normalize(&temp_vec);
149  bool fix_elevation = false;
150  bool fix_base_rot = false;
151 
152  if (dot < tp->turret_fov) {
153  dot = tp->turret_fov;
154  fix_elevation = true;
155  }
156  if (dot > tp->turret_max_fov) {
157  dot = tp->turret_max_fov;
158  fix_elevation = true;
159  }
160 
161  if (tp->flags & MSS_FLAG_TURRET_ALT_MATH) {
162  vec3d temp_vec2;
163  vm_vec_rotate(&temp_vec2, &temp_vec, &ss->world_to_turret_matrix);
164 
165  // now in turrets frame of reference
166  // check if math is actually possible
167  if (!((temp_vec2.xyz.x == 0) && (temp_vec2.xyz.y == 0))) {
168  float temp_z = temp_vec2.xyz.z;
169  temp_vec2.xyz.z = 0.0f;
170  // make sure null vecs wont happen
171  if (!IS_VEC_NULL_SQ_SAFE(&temp_vec2)) {
172  vm_vec_normalize(&temp_vec2);
173  // only do this if it actually is required
174  if (-temp_vec2.xyz.y < tp->turret_y_fov) {
175  float check_pos = 1;
176 
177  fix_base_rot = true;
178  temp_vec2.xyz.y = -tp->turret_y_fov;
179  if (temp_vec2.xyz.x < 0)
180  check_pos = -1;
181  temp_vec2.xyz.x = check_pos * sqrtf(1 - (temp_vec2.xyz.y*temp_vec2.xyz.y));
182 
183  // restore z component
184  float scaler = sqrtf(1 - (temp_z*temp_z));
185  vm_vec_scale(&temp_vec2,scaler);
186  temp_vec2.xyz.z = temp_z;
187  // back to world frame
188  vm_vec_unrotate(&temp_vec, &temp_vec2, &ss->world_to_turret_matrix);
189  }
190  }
191  }
192  }
193 
194  if (fix_elevation || fix_base_rot) {
195  if (fix_elevation) {
196  multiplier = sqrtf(1 - (dot*dot));
197  // keep the temp_vec scaled with the tweaked vector
198  vm_vec_scale(&temp_vec, multiplier);
199  }
200  vm_vec_scale_add(v2e, &temp_vec, tvec, dot);
201  // and we are done with v2e...
202  vm_vec_scale_add(predicted_pos, tpos, v2e, target_dist);
203  // and we are done with predicted position
204  return true;
205  } else {
206  mprintf(("Warning: Function 'is_object_radius_in_turret_fov' was called\nwithout need to fix turret alignments\n"));
207  return false;
208  }
209  }
210  // outside of the expanded radii, unable to align, return false
211  return false;
212 }
213 
222 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
223 {
224  float dot;
225  vec3d bomb_to_ship_vector;
226 
227  vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
228  dot = vm_vec_dot(&bomb_objp->orient.vec.fvec, &bomb_to_ship_vector);
229 
230  if ( dot > 0 ) {
231  return 1;
232  }
233 
234  return 0;
235 }
236 
237 // Set active weapon for turret
238 //This really isn't needed...but whatever -WMC
239 
247 {
248  //TODO: Fill this out with extraodinary gun-picking algorithms
249  if(turret->weapons.num_primary_banks > 0)
250  return 0;
251  else if(turret->weapons.num_secondary_banks > 0)
252  return MAX_SHIP_PRIMARY_BANKS;
253  else
254  return -1;
255 }
256 
263 {
264  int i;
265  for(i = 0; i < swp->num_primary_banks; i++)
266  {
267  if(!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & flags))
268  return false;
269  }
270  for(i = 0; i < swp->num_secondary_banks; i++)
271  {
272  if(!(Weapon_info[swp->secondary_bank_weapons[i]].wi_flags & flags))
273  return false;
274  }
275 
276  return true;
277 }
278 
285 {
286  int i;
287  for(i = 0; i < swp->num_primary_banks; i++)
288  {
289  if(!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags2 & flags))
290  return false;
291  }
292  for(i = 0; i < swp->num_secondary_banks; i++)
293  {
294  if(!(Weapon_info[swp->secondary_bank_weapons[i]].wi_flags2 & flags))
295  return false;
296  }
297 
298  return true;
299 }
300 
307 {
308  Assert(swp != NULL);
309 
310  int i = 0;
311  for(i = 0; i < swp->num_primary_banks; i++)
312  {
313  if(swp->primary_bank_weapons[i] >=0) {
314  if(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & flags)
315  return true;
316  }
317  }
318  for(i = 0; i < swp->num_secondary_banks; i++)
319  {
320  if(swp->secondary_bank_weapons[i] >=0) {
321  if(Weapon_info[swp->secondary_bank_weapons[i]].wi_flags & flags)
322  return true;
323  }
324  }
325 
326  return false;
327 }
328 
335 {
336  Assert(swp != NULL);
337 
338  int i = 0;
339  for(i = 0; i < swp->num_primary_banks; i++)
340  {
341  if(swp->primary_bank_weapons[i] >=0) {
342  if(Weapon_info[swp->primary_bank_weapons[i]].wi_flags2 & flags)
343  return true;
344  }
345  }
346  for(i = 0; i < swp->num_secondary_banks; i++)
347  {
348  if(swp->secondary_bank_weapons[i] >=0) {
349  if(Weapon_info[swp->secondary_bank_weapons[i]].wi_flags2 & flags)
350  return true;
351  }
352  }
353 
354  return false;
355 }
356 
361 {
362  Assert(swp != NULL);
363 
364  int i = 0, flags = 0;
365  for (i = 0; i < swp->num_primary_banks; i++)
366  {
367  if (swp->primary_bank_weapons[i] >= 0) {
369  }
370  }
371  for (i = 0; i < swp->num_secondary_banks; i++)
372  {
373  if (swp->secondary_bank_weapons[i] >= 0) {
375  }
376  }
377 
378  return flags;
379 }
380 
385 {
386  Assert(swp != NULL);
387 
388  int i = 0, flags2 = 0;
389  for (i = 0; i < swp->num_primary_banks; i++)
390  {
391  if (swp->primary_bank_weapons[i] >= 0) {
392  flags2 |= Weapon_info[swp->primary_bank_weapons[i]].wi_flags2;
393  }
394  }
395  for (i = 0; i < swp->num_secondary_banks; i++)
396  {
397  if (swp->secondary_bank_weapons[i] >= 0) {
399  }
400  }
401 
402  return flags2;
403 }
404 
412 {
413  Assert(swp != NULL);
414 
415  int i = 0;
416  for(i = 0; i < swp->num_primary_banks; i++)
417  {
418  if(swp->primary_bank_weapons[i] >=0) {
419  if(Weapon_info[swp->primary_bank_weapons[i]].subtype == subtype)
420  return true;
421  }
422  }
423  for(i = 0; i < swp->num_secondary_banks; i++)
424  {
425  if(swp->secondary_bank_weapons[i] >=0) {
426  if(Weapon_info[swp->secondary_bank_weapons[i]].subtype == subtype)
427  return true;
428  }
429  }
430 
431  return false;
432 }
433 
440 {
441  Assert(weapon_num < MAX_SHIP_WEAPONS);
442  Assert(weapon_num >= 0);
443 
444  if(weapon_num >= MAX_SHIP_PRIMARY_BANKS)
445  return &Weapon_info[swp->secondary_bank_weapons[weapon_num - MAX_SHIP_PRIMARY_BANKS]];
446  else
447  return &Weapon_info[swp->primary_bank_weapons[weapon_num]];
448 }
449 
451 {
452  Assert(weapon_num < MAX_SHIP_WEAPONS);
453  Assert(weapon_num >= 0);
454 
455  if(weapon_num >= MAX_SHIP_PRIMARY_BANKS)
456  return swp->next_secondary_fire_stamp[weapon_num - MAX_SHIP_PRIMARY_BANKS];
457  else
458  return swp->next_primary_fire_stamp[weapon_num];
459 }
460 
467 {
468  float longest_range_so_far = 0.0f;
469  float weapon_range;
470  weapon_info *wip;
471 
472  int i = 0;
473  for(i = 0; i < swp->num_primary_banks; i++)
474  {
475  wip = &Weapon_info[swp->primary_bank_weapons[i]];
476  if (wip->wi_flags2 & WIF2_LOCAL_SSM)
477  weapon_range = wip->lssm_lock_range;
478  else
479  weapon_range = MIN(wip->lifetime * wip->max_speed, wip->weapon_range);
480 
481  if(weapon_range > longest_range_so_far)
482  longest_range_so_far = weapon_range;
483  }
484  for(i = 0; i < swp->num_secondary_banks; i++)
485  {
486  wip = &Weapon_info[swp->secondary_bank_weapons[i]];
487  if (wip->wi_flags2 & WIF2_LOCAL_SSM)
488  weapon_range = wip->lssm_lock_range;
489  else
490  weapon_range = MIN(wip->lifetime * wip->max_speed, wip->weapon_range);
491 
492  if(weapon_range > longest_range_so_far)
493  longest_range_so_far = weapon_range;
494  }
495 
496  return longest_range_so_far;
497 }
498 
507 int valid_turret_enemy(object *objp, object *turret_parent)
508 {
509  if ( objp == turret_parent ) {
510  return 0;
511  }
512 
513  if ( objp->type == OBJ_ASTEROID ) {
514  return 1;
515  }
516 
517  if ( objp->type == OBJ_SHIP ) {
518  Assert( objp->instance >= 0 );
519  ship *shipp;
520  ship_info *sip;
521  shipp = &Ships[objp->instance];
522  sip = &Ship_info[shipp->ship_info_index];
523 
524  // don't fire at ships with protected bit set!!!
525  if ( objp->flags & OF_PROTECTED ) {
526  return 0;
527  }
528 
529  // don't shoot at ships without collision check
530  if (!(objp->flags & OF_COLLIDES)) {
531  return 0;
532  }
533 
534  // don't shoot at arriving ships
535  if (shipp->flags & SF_ARRIVING) {
536  return 0;
537  }
538 
539  // Goober5000 - don't fire at cargo containers (now specified in ship_types)
540  if ( (sip->class_type >= 0) && !(Ship_types[sip->class_type].ai_bools & STI_AI_TURRETS_ATTACK) ) {
541  return 0;
542  }
543 
544  return 1;
545  }
546 
547  if ( objp->type == OBJ_WEAPON ) {
548  Assert( objp->instance >= 0 );
549  weapon *wp = &Weapons[objp->instance];
551 
552  if (wip->subtype == WP_LASER && !(wip->wi_flags3 & WIF3_TURRET_INTERCEPTABLE)) { // If the thing can't be shot down, don't try. -MageKing17
553  return 0;
554  }
555 
557  return 0;
558  }
559 
560  if ( (wip->wi_flags2 & WIF2_LOCAL_SSM) && (wp->lssm_stage == 3) ) {
561  return 0;
562  }
563 
564  if ( !iff_x_attacks_y(obj_team(turret_parent), wp->team) ) {
565  return 0;
566  }
567 
568  return 1;
569  }
570 
571  return 0;
572 }
573 
574 extern int Player_attacking_enabled;
576 {
577  object *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
578  ship *shipp;
579  ship_subsys *ss = eeo->turret_subsys;
580  float dist, dist_comp;
581  bool turret_has_no_target = false;
582 
583  // Don't look for bombs when weapon system is not ok
584  if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
585  return;
586  }
587 
588  if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
589  return;
590  }
591 
592 #ifndef NDEBUG
593  if (!Player_attacking_enabled && (objp == Player_obj)) {
594  return;
595  }
596 #endif
597 
598  if ( objp->type == OBJ_SHIP ) {
599  shipp = &Ships[objp->instance];
600 
601  // check on enemy team
602  if ( !iff_matches_mask(shipp->team, eeo->enemy_team_mask) ) {
603  return;
604  }
605 
606  // check if protected
607  if (objp->flags & OF_PROTECTED) {
608  return;
609  }
610 
611  // check if beam protected
612  if (eeo->eeo_flags & EEOF_BEAM) {
613  if (objp->flags & OF_BEAM_PROTECTED) {
614  return;
615  }
616  }
617 
618  // check if flak protected
619  if (eeo->eeo_flags & EEOF_FLAK) {
620  if (objp->flags & OF_FLAK_PROTECTED) {
621  return;
622  }
623  }
624 
625  // check if laser protected
626  if (eeo->eeo_flags & EEOF_LASER) {
627  if (objp->flags & OF_LASER_PROTECTED) {
628  return;
629  }
630  }
631 
632  // check if missile protected
633  if (eeo->eeo_flags & EEOF_MISSILE) {
634  if (objp->flags & OF_MISSILE_PROTECTED) {
635  return;
636  }
637  }
638 
639  // don't shoot at small ships if we shouldn't
640  if (eeo->eeo_flags & EEOF_BIG_ONLY) {
641  if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
642  return;
643  }
644  }
645 
646  // don't shoot at big ships if we shouldn't
647  if (eeo->eeo_flags & EEOF_SMALL_ONLY) {
648  if ((Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
649  return;
650  }
651  }
652 
653  // check if turret flagged to only target tagged ships
654  // Note: retail behaviour was turrets with tagged-only could fire at bombs
655  // and could fire their spawn weapons
656  // this check is almost redundant; see the almost identical check in ai_fire_from_turret
657  // however if this is removed turrets still track targets but don't fire at them (which looks silly)
658  if (eeo->eeo_flags & EEOF_TAGGED_ONLY) {
659  if (!ship_is_tagged(objp) &&
661  ( !(objp->type == OBJ_WEAPON) && !(turret_weapon_has_flags(&eeo->turret_subsys->weapons, WIF_SPAWN))) )) {
662  return;
663  }
664  }
665 
666  // check if valid target in nebula
668  // BYPASS ocassionally for stealth
669  int try_anyway = FALSE;
670  if ( is_object_stealth_ship(objp) ) {
671  float turret_stealth_find_chance = 0.5f;
672  float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
673  if (frand() > (turret_stealth_find_chance + speed_mod)) {
674  try_anyway = TRUE;
675  }
676  }
677 
678  if (!try_anyway) {
679  return;
680  }
681  }
682 
683  } else {
684  shipp = NULL;
685  }
686 
687  // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
688  vec3d vec_to_target;
689  vm_vec_sub(&vec_to_target, &objp->pos, eeo->tpos);
690  dist = vm_vec_mag_quick(&vec_to_target) - objp->radius;
691 
692  if (dist < 0.0f) {
693  dist = 0.0f;
694  }
695 
696  dist_comp = dist;
697  // if weapon has optimum range set then use it
698  float optimum_range = ss->optimum_range;
699  if (optimum_range > 0.0f) {
700  if (dist < optimum_range) {
701  dist_comp = (2*optimum_range) - dist;
702  }
703  }
704 
705  // if turret has been told to prefer targets from the current direction then do so
706  float favor_one_side = ss->favor_current_facing;
707  if (favor_one_side >= 1.0f) {
708  vm_vec_normalize(&vec_to_target);
709  float dot_to_target = vm_vec_dot(&ss->turret_last_fire_direction, &vec_to_target);
710  dot_to_target = 1.0f - (dot_to_target / favor_one_side);
711  dist_comp *= dot_to_target;
712  }
713 
714  // check if object is a bomb attacking the turret parent
715  // check if bomb is homing on the turret parent ship
716  bool check_weapon = true;
717 
719  check_weapon = false;
720  }
721 
722  if ((objp->type == OBJ_WEAPON) && check_weapon) {
723  //Maybe restrict the number of turrets attacking this bomb
724  if (ss->turret_max_bomb_ownage != -1) {
725  int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
726  if (num_att_turrets > ss->system_info->turret_max_bomb_ownage) {
727  return;
728  }
729  }
730 
731  if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
732  if ( dist_comp < eeo->nearest_homing_bomb_dist ) {
733  if (!(ss->flags & SSF_FOV_REQUIRED) && (eeo->current_enemy == -1)) {
734  turret_has_no_target = true;
735  }
736  if ( (turret_has_no_target) || object_in_turret_fov(objp, ss, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
737  eeo->nearest_homing_bomb_dist = dist_comp;
739  }
740  }
741  // if not homing, check if bomb is flying towards ship
742  } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
743  if ( dist_comp < eeo->nearest_bomb_dist ) {
744  if (!(ss->flags & SSF_FOV_REQUIRED) && (eeo->current_enemy == -1)) {
745  turret_has_no_target = true;
746  }
747  if ( (turret_has_no_target) || object_in_turret_fov(objp, ss, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
748  eeo->nearest_bomb_dist = dist_comp;
749  eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
750  }
751  }
752  }
753  } // end weapon section
754 
755  // maybe recalculate dist for big or huge ship
756 // if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
757 // fvi_ray_boundingbox(min, max, start, direction, hit);
758 // dist = vm_vec_dist_quick(hit, tvec);
759 // }
760 
761  // check for nearest attcker
762  if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
763  ai_info *aip = &Ai_info[shipp->ai_index];
764 
765  // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
766  // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2; // prevents lots of ships from attacking same target
767  int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
768  dist_comp *= (1.0f + 0.1f*num_att_turrets);
769 
770  // return if we're over the cap
771 // int max_turrets = 3 + Game_skill_level * Game_skill_level;
773  if (objp->flags & OF_PLAYER_SHIP) {
775  }
776  // Apply the per-turret limit for small targets, if there is one and this is a small target
777  if (ss->turret_max_target_ownage != -1 && (Ship_info[shipp->ship_info_index].flags & (SIF_SMALL_SHIP))) {
778  max_turrets = MIN(max_turrets, ss->system_info->turret_max_target_ownage);
779  }
780  if (num_att_turrets > max_turrets) {
781  return;
782  }
783 
784  // modify distance based on lethality of objp to my ship
785  float active_lethality = aip->lethality;
786  if (objp->flags & OF_PLAYER_SHIP) {
787  active_lethality += Player_lethality_bump[Game_skill_level];
788  }
789 
790  dist_comp /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
791 
792  // Make level 2 tagged ships more likely to be targeted
793  if (shipp->level2_tag_left > 0.0f) {
794  dist_comp *= 0.3f;
795  }
796 
797  // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
799  // A turret will always target a ship that is attacking itself... self-preservation!
800  if ( aip->targeted_subsys == eeo->turret_subsys ) {
801  dist_comp *= 0.5f; // highest priority
802  }
803  }
804 
805  // maybe update nearest attacker
806  if ( dist_comp < eeo->nearest_attacker_dist ) {
807  if (!(ss->flags & SSF_FOV_REQUIRED) && (eeo->current_enemy == -1)) {
808  turret_has_no_target = true;
809  }
810  if ( (turret_has_no_target) || object_in_turret_fov(objp, ss, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
811  // nprintf(("AI", "Nearest enemy = %s, dist = %7.3f, dot = %6.3f, fov = %6.3f\n", Ships[objp->instance].ship_name, dist, vm_vec_dot(&v2e, tvec), tp->turret_fov));
812  eeo->nearest_attacker_dist = dist_comp;
813  eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
814  }
815  }
816  } // end ship section
817 
818  // check if object is an asteroid attacking the turret parent - taylor
819  if (objp->type == OBJ_ASTEROID) {
820  if ( eeo->turret_parent_objnum == asteroid_collide_objnum(objp) ) {
821  // give priority to the closest asteroid *impact* (ms intervals)
822  dist_comp *= 0.9f + (0.01f * asteroid_time_to_impact(objp));
823 
824  if (dist_comp < eeo->nearest_dist ) {
825  if (!(ss->flags & SSF_FOV_REQUIRED) && (eeo->current_enemy == -1)) {
826  turret_has_no_target = true;
827  }
828  if ( (turret_has_no_target) || object_in_turret_fov(objp, ss, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
829  eeo->nearest_dist = dist_comp;
830  eeo->nearest_objnum = OBJ_INDEX(objp);
831  }
832  }
833  }
834  } // end asteroid selection
835 }
836 
843 {
844  // check if turret has beam weapon
846  if (objp->flags & OF_BEAM_PROTECTED) {
847  return 0;
848  }
849 
851  if (objp->type == OBJ_SHIP && !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
852  return 0;
853  }
854  }
855  }
856 
857  return 1;
858 }
859 
877 int get_nearest_turret_objnum(int turret_parent_objnum, ship_subsys *turret_subsys, int enemy_team_mask, vec3d *tpos, vec3d *tvec, int current_enemy, bool big_only_flag, bool small_only_flag, bool tagged_only_flag, bool beam_flag, bool flak_flag, bool laser_flag, bool missile_flag)
878 {
879  //float weapon_travel_dist;
880  int weapon_system_ok;
881  object *objp;
883  ship_weapon *swp = &turret_subsys->weapons;
884 
885  // list of stuff to go thru
886  ship_obj *so;
887  missile_obj *mo;
888 
889  //wip=&Weapon_info[tp->turret_weapon_type];
890  //weapon_travel_dist = MIN(wip->lifetime * wip->max_speed, wip->weapon_range);
891 
892  //if (wip->wi_flags2 & WIF2_LOCAL_SSM)
893  // weapon_travel_dist=wip->lssm_lock_range;
894 
895  // Set flag based on strength of weapons subsystem. If weapons subsystem is destroyed, don't let turrets fire at bombs
896  weapon_system_ok = 0;
897  if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
898  weapon_system_ok = 1;
899  }
900 
901  // Initialize eeo struct.
902  eeo.turret_parent_objnum = turret_parent_objnum;
903  eeo.weapon_system_ok = weapon_system_ok;
905 
906  // set flags
907  eeo.eeo_flags = 0;
908  if (big_only_flag)
909  eeo.eeo_flags |= EEOF_BIG_ONLY;
910  if (small_only_flag)
911  eeo.eeo_flags |= EEOF_SMALL_ONLY;
912  if (tagged_only_flag)
914 
915  // flags for weapon types
916  if (beam_flag)
917  eeo.eeo_flags |= EEOF_BEAM;
918  else if (flak_flag)
919  eeo.eeo_flags |= EEOF_FLAK;
920  else if (laser_flag)
921  eeo.eeo_flags |= EEOF_LASER;
922  else if (missile_flag)
923  eeo.eeo_flags |= EEOF_MISSILE;
924 
925  eeo.enemy_team_mask = enemy_team_mask;
926  eeo.current_enemy = current_enemy;
927  eeo.tpos = tpos;
928  eeo.tvec = tvec;
929  eeo.turret_subsys = turret_subsys;
930 
931  eeo.nearest_attacker_dist = 99999.0f;
932  eeo.nearest_attacker_objnum = -1;
933 
934  eeo.nearest_homing_bomb_dist = 99999.0f;
936 
937  eeo.nearest_bomb_dist = 99999.0f;
938  eeo.nearest_bomb_objnum = -1;
939 
940  eeo.nearest_dist = 99999.0f;
941  eeo.nearest_objnum = -1;
942 
943  // here goes the new targeting priority setting
944  int n_tgt_priorities;
945  int priority_weapon_idx = -1;
946 
947  // check for turret itself first
948  n_tgt_priorities = turret_subsys->num_target_priorities;
949 
950  // turret had no priorities set for it.. try weapons
951  if (n_tgt_priorities <= 0) {
952  if (swp->num_primary_banks > 0)
953  // first try highest primary slot...
954  priority_weapon_idx = swp->primary_bank_weapons[0];
955  else
956  // ...and then secondary slot
957  priority_weapon_idx = swp->secondary_bank_weapons[0];
958 
959  if (priority_weapon_idx > -1)
960  n_tgt_priorities = Weapon_info[priority_weapon_idx].num_targeting_priorities;
961  }
962 
963  if (n_tgt_priorities > 0) {
964 
965  for(int i = 0; i < n_tgt_priorities; i++) {
966  // courtesy of WMC...
967  ai_target_priority *tt;
968  if (priority_weapon_idx == -1)
969  tt = &Ai_tp_list[turret_subsys->target_priority[i]];
970  else
971  tt = &Ai_tp_list[Weapon_info[priority_weapon_idx].targeting_priorities[i]];
972 
973  int n_types = (int)tt->ship_type.size();
974  int n_s_classes = (int)tt->ship_class.size();
975  int n_w_classes = (int)tt->weapon_class.size();
976 
977  bool found_something;
978  object *ptr = GET_FIRST(&obj_used_list);
979 
980  while (ptr != END_OF_LIST(&obj_used_list)) {
981  found_something = false;
982 
983  if(tt->obj_type > -1 && (ptr->type == tt->obj_type)) {
984  found_something = true;
985  }
986 
987  if( ( n_types > 0 ) && ( ptr->type == OBJ_SHIP ) ) {
988  for (int j = 0; j < n_types; j++) {
989  if ( Ship_info[Ships[ptr->instance].ship_info_index].class_type == tt->ship_type[j] ) {
990  found_something = true;
991  }
992  }
993  }
994 
995  if( ( n_s_classes > 0 ) && ( ptr->type == OBJ_SHIP ) ) {
996  for (int j = 0; j < n_s_classes; j++) {
997  if ( Ships[ptr->instance].ship_info_index == tt->ship_class[j] ) {
998  found_something = true;
999  }
1000  }
1001  }
1002 
1003  if( ( n_w_classes > 0 ) && ( ptr->type == OBJ_WEAPON ) ) {
1004  for (int j = 0; j < n_w_classes; j++) {
1005  if ( Weapons[ptr->instance].weapon_info_index == tt->weapon_class[j] ) {
1006  found_something = true;
1007  }
1008  }
1009  }
1010 
1011  if( ( (tt->wif2_flags != 0) || (tt->wif_flags != 0) ) && (ptr->type == OBJ_WEAPON) ) {
1013  && ( (Weapon_info[Weapons[ptr->instance].weapon_info_index].wi_flags2 & tt->wif2_flags ) == tt->wif2_flags) ) {
1014  found_something = true;
1015  }
1016  }
1017 
1018  if( ( ( tt->sif_flags != 0 ) || ( tt->sif2_flags != 0 ) ) && (ptr->type == OBJ_SHIP) ) {
1019  if( ( (Ship_info[Ships[ptr->instance].ship_info_index].flags & tt->sif_flags) == tt->sif_flags)
1020  && ( (Ship_info[Ships[ptr->instance].ship_info_index].flags2 & tt->sif2_flags) == tt->sif2_flags) ) {
1021  found_something = true;
1022  }
1023  }
1024 
1025  if((tt->obj_flags != 0) && !((ptr->flags & tt->obj_flags) == tt->obj_flags)) {
1026  found_something = true;
1027  }
1028 
1029  if(!(found_something)) {
1030  //we didnt find this object within this priority group
1031  //skip to next without evaluating the object as target
1032  ptr = GET_NEXT(ptr);
1033 
1034  continue;
1035  }
1036 
1037 
1038  evaluate_obj_as_target(ptr, &eeo);
1039 
1040  ptr = GET_NEXT(ptr);
1041  }
1042 
1043  //homing weapon entry...
1044  /*
1045  if ( eeo.nearest_homing_bomb_objnum != -1 ) { // highest priority is an incoming homing bomb
1046  return eeo.nearest_homing_bomb_objnum;
1047  //weapon entry...
1048  } else if ( eeo.nearest_bomb_objnum != -1 ) { // next highest priority is an incoming dumbfire bomb
1049  return eeo.nearest_bomb_objnum;
1050  //ship entry...
1051  } else if ( eeo.nearest_attacker_objnum != -1 ) { // next highest priority is an attacking ship
1052  return eeo.nearest_attacker_objnum;
1053  //something else entry...
1054  } else if ( eeo.nearest_objnum != -1 ) {
1055  return eeo.nearest_objnum;
1056  }
1057  */
1058  // if we got something...
1059  if ( ( eeo.nearest_homing_bomb_objnum != -1 ) ||
1060  ( eeo.nearest_bomb_objnum != -1 ) ||
1061  ( eeo.nearest_attacker_objnum != -1 ) ||
1062  ( eeo.nearest_objnum != -1 ) )
1063  {
1064  // ...start with homing bombs...
1065  int return_objnum = eeo.nearest_homing_bomb_objnum;
1066  float return_distance = eeo.nearest_homing_bomb_dist;
1067 
1068  // ...next test non-homing bombs...
1069  if ( eeo.nearest_bomb_dist < return_distance ) {
1070  return_objnum = eeo.nearest_bomb_objnum;
1071  return_distance = eeo.nearest_bomb_dist;
1072  }
1073 
1074  // ...then attackers...
1075  if ( eeo.nearest_attacker_dist < return_distance ) {
1076  return_objnum = eeo.nearest_attacker_objnum;
1077  return_distance = eeo.nearest_attacker_dist;
1078  }
1079 
1080  // ...and finally the rest of the lot...
1081  if ( eeo.nearest_dist < return_distance ) {
1082  return_objnum = eeo.nearest_objnum;
1083  }
1084 
1085  // ...and return the objnum to the closest target regardless
1086  return return_objnum;
1087  }
1088  }
1089  } else {
1090 
1091  for(int i = 0; i < NUM_TURRET_ORDER_TYPES; i++)
1092  {
1094  switch(turret_subsys->turret_targeting_order[i])
1095  {
1096  case -1:
1097  //Empty priority slot
1098  break;
1099 
1100  case 0:
1101  //Return if a bomb is found
1102  //don't fire anti capital ship turrets at bombs.
1103  if ( !((aip->ai_profile_flags & AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS) && big_only_flag) )
1104  {
1105  // Missile_obj_list
1106  for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
1107  objp = &Objects[mo->objnum];
1108 
1109  Assert(objp->type == OBJ_WEAPON);
1111  {
1112  evaluate_obj_as_target(objp, &eeo);
1113  }
1114  }
1115  // highest priority
1116  if ( eeo.nearest_homing_bomb_objnum != -1 ) { // highest priority is an incoming homing bomb
1117  return eeo.nearest_homing_bomb_objnum;
1118  } else if ( eeo.nearest_bomb_objnum != -1 ) { // next highest priority is an incoming dumbfire bomb
1119  return eeo.nearest_bomb_objnum;
1120  }
1121  }
1122  break;
1123 
1124  case 1:
1125  //Return if a ship is found
1126  // Ship_used_list
1127  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1128  objp = &Objects[so->objnum];
1129  evaluate_obj_as_target(objp, &eeo);
1130  }
1131 
1133  // next highest priority is attacking ship
1134  if ( eeo.nearest_attacker_objnum != -1 ) { // next highest priority is an attacking ship
1135  return eeo.nearest_attacker_objnum;
1136  }
1137  break;
1138 
1139  case 2:
1140  //Return if an asteroid is found
1141  // asteroid check - taylor
1142  asteroid_obj *ao;
1143 
1144  // don't use turrets that are better for other things:
1145  // - no cap ship beams
1146  // - no flak
1147  // - no heat or aspect missiles
1148  // - no spawn type missiles/bombs
1149  // do use for sure:
1150  // - lasers
1151  // - dumbfire type missiles
1152  // - AAA beams
1154  // Asteroid_obj_list
1155  for ( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
1156  objp = &Objects[ao->objnum];
1157  evaluate_obj_as_target(objp, &eeo);
1158  }
1159 
1160  if (eeo.nearest_objnum != -1) {
1161  return eeo.nearest_objnum;
1162  }
1163  }
1164  break;
1165 
1166  default:
1167  Int3(); //Means invalid number passed.
1168  }
1169  }
1170  }
1171 
1172  return -1;
1173 }
1174 
1176 DCF_BOOL(use_parent_target, Use_parent_target)
1177 
1178 
1187 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d *tvec, int current_enemy)
1188 {
1189  int enemy_team_mask, enemy_objnum;
1190  ship_info *sip;
1191 
1192  enemy_team_mask = iff_get_attackee_mask(obj_team(&Objects[objnum]));
1193 
1194  bool big_only_flag = all_turret_weapons_have_flags(&turret_subsys->weapons, WIF_HUGE);
1195  bool small_only_flag = all_turret_weapons_have_flags2(&turret_subsys->weapons, WIF2_SMALL_ONLY);
1196  bool tagged_only_flag = all_turret_weapons_have_flags2(&turret_subsys->weapons, WIF2_TAGGED_ONLY) || (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY);
1197 
1198  bool beam_flag = turret_weapon_has_flags(&turret_subsys->weapons, WIF_BEAM);
1199  bool flak_flag = turret_weapon_has_flags(&turret_subsys->weapons, WIF_FLAK);
1200  bool laser_flag = turret_weapon_has_subtype(&turret_subsys->weapons, WP_LASER);
1201  bool missile_flag = turret_weapon_has_subtype(&turret_subsys->weapons, WP_MISSILE);
1202 
1203  // If a small ship and target_objnum != -1, use that as goal.
1204  ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
1205  sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
1206 
1207  if ((Ship_types[sip->class_type].ship_bools & STI_TURRET_TGT_SHIP_TGT) && (aip->target_objnum != -1)) {
1208  int target_objnum = aip->target_objnum;
1209 
1210  if (Objects[target_objnum].signature == aip->target_signature) {
1211  if (iff_matches_mask(Ships[Objects[target_objnum].instance].team, enemy_team_mask)) {
1212  if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) { // check this flag as well
1213  // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
1214  return target_objnum;
1215  }
1216  }
1217  } else {
1218  aip->target_objnum = -1;
1219  aip->target_signature = -1;
1220  }
1221  // Not small or small with target objnum
1222  } else {
1223  // maybe use aip->target_objnum as next target
1224  if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
1225 
1226  //check if aip->target_objnum is valid target
1227  int target_flags = Objects[aip->target_objnum].flags;
1228  if ( target_flags & OF_PROTECTED ) {
1229  // AL 2-27-98: why is a protected ship being targeted?
1230  set_target_objnum(aip, -1);
1231  return -1;
1232  }
1233 
1234  // maybe use ship target_objnum if valid for turret
1235  // check for beam weapon and beam protected, etc.
1236  bool skip = false;
1237  if ( (target_flags & OF_BEAM_PROTECTED) && beam_flag ) skip = true;
1238  else if ( (target_flags & OF_FLAK_PROTECTED) && flak_flag ) skip = true;
1239  else if ( (target_flags & OF_LASER_PROTECTED) && laser_flag ) skip = true;
1240  else if ( (target_flags & OF_MISSILE_PROTECTED) && missile_flag ) skip = true;
1241 
1242  if (!skip) {
1243  if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
1244  // check for huge weapon and huge ship
1245  if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
1246  // check for tagged only and tagged ship
1247  if ( tagged_only_flag && ship_is_tagged(&Objects[aip->target_objnum]) ) {
1248  // select new target if aip->target_objnum is out of field of view
1249  vec3d v2e;
1250  float dist;
1251  bool in_fov;
1252  dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
1253 
1254  in_fov = turret_fov_test(turret_subsys, tvec, &v2e);
1255 
1256  if (turret_subsys->flags & SSF_FOV_EDGE_CHECK) {
1257  if (in_fov == false)
1258  if (object_in_turret_fov(&Objects[aip->target_objnum], turret_subsys, tvec, tpos, dist + Objects[aip->target_objnum].radius))
1259  in_fov = true;
1260  }
1261  // MODIFY FOR ATTACKING BIG SHIP
1262  // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
1263  if (in_fov) {
1264  return aip->target_objnum;
1265  }
1266  }
1267  }
1268  }
1269  }
1270  }
1271  }
1272 
1273  enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag, small_only_flag, tagged_only_flag, beam_flag, flak_flag, laser_flag, missile_flag);
1274  if ( enemy_objnum >= 0 ) {
1275  Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && beam_flag) );
1276  Assert( !((Objects[enemy_objnum].flags & OF_FLAK_PROTECTED) && flak_flag) );
1277  Assert( !((Objects[enemy_objnum].flags & OF_LASER_PROTECTED) && laser_flag) );
1278  Assert( !((Objects[enemy_objnum].flags & OF_MISSILE_PROTECTED) && missile_flag) );
1279 
1280  if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
1281  Int3();
1282  enemy_objnum = aip->target_objnum;
1283  }
1284  }
1285 
1286  return enemy_objnum;
1287 }
1288 
1289 
1304 {
1305 // vm_vec_unrotate(gpos, &tp->turret_avg_firing_point, &objp->orient);
1306  vm_vec_unrotate(gpos, &tp->pnt, &objp->orient);
1307  vm_vec_add2(gpos, &objp->pos);
1308  vm_vec_unrotate(gvec, &tp->turret_norm, &objp->orient);
1309 }
1310 
1311 void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss);
1312 
1327 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, int use_angles, vec3d *targetp)
1328 {
1329  vec3d * gun_pos;
1330  model_subsystem *tp = ssp->system_info;
1331 
1332  //ship_model_start(objp);
1333 
1335 
1336  //model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
1337  model_instance_find_world_point(gpos, gun_pos, Ships[objp->instance].model_instance_num, tp->turret_gun_sobj, &objp->orient, &objp->pos);
1338 
1339  if (use_angles) {
1341  } else if (tp->flags2 & MSS_FLAG2_SHARE_FIRE_DIRECTION) {
1342  vec3d avg, tmp_pos, tmp_target, enemy_point, turret_norm;
1344 
1345  model_instance_find_world_point(&tmp_pos, &avg, Ships[objp->instance].model_instance_num, tp->turret_gun_sobj, &objp->orient, &objp->pos);
1346 
1347  if (targetp == nullptr) {
1348  Assertion(ssp->turret_enemy_objnum >= 0, "The turret enemy object number %d for %s on ship number %d is invalid.", ssp->turret_enemy_objnum, ssp->sub_name, ssp->parent_objnum);
1349  object *lep = &Objects[ssp->turret_enemy_objnum];
1350 
1351  int best_weapon_tidx = turret_select_best_weapon(ssp, lep);
1352 
1353  //This turret doesn't have any good weapons
1354  if (best_weapon_tidx < 0)
1355  return;
1356 
1357  weapon_info *wip = get_turret_weapon_wip(&ssp->weapons, best_weapon_tidx);
1358 
1359  float weapon_system_strength = ship_get_subsystem_strength(&Ships[ssp->parent_objnum], SUBSYSTEM_WEAPONS);
1360 
1362 
1363  if ((ssp->targeted_subsys != nullptr) && !(ssp->flags & SSF_NO_SS_TARGETING)) {
1365  vm_vec_add2(&enemy_point, &ssp->last_aim_enemy_pos);
1366  } else {
1367  if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
1368  vm_vec_unrotate(&turret_norm, &tp->turret_norm, &objp->orient);
1369  ai_big_pick_attack_point_turret(lep, ssp, &tmp_pos, &turret_norm, &enemy_point, MIN(wip->max_speed * wip->lifetime, wip->weapon_range), tp->turret_fov);
1370  }
1371  else {
1372  enemy_point = ssp->last_aim_enemy_pos;
1373  }
1374  }
1375 
1376  vec3d target_moving_direction = ssp->last_aim_enemy_vel;
1377 
1378  //Try to guess where the enemy will be, and store that spot in predicted_enemy_pos
1380  vm_vec_scale_sub2(&target_moving_direction, &objp->phys_info.vel, wip->vel_inherit_amount);
1381  }
1382 
1383  set_predicted_enemy_pos_turret(&tmp_target, &tmp_pos, objp, &enemy_point, &target_moving_direction, wip->max_speed, ssp->turret_time_enemy_in_range * (weapon_system_strength + 1.0f) / 2.0f);
1384  } else {
1385  tmp_target = *targetp;
1386  }
1387 
1388  vm_vec_normalized_dir(gvec, &tmp_target, &tmp_pos);
1389  } else {
1390  //vector gun_pos2;
1391  //vm_vec_add(&gun_pos2, gpos, gun_pos);
1392  vm_vec_normalized_dir(gvec, targetp, gpos);
1393  }
1394 
1395  //ship_model_stop(objp);
1396 
1397  // per firingpoint based changes go here for turrets
1398 }
1399 
1403 void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss)
1404 {
1405  if (Missiontime >= ss->next_aim_pos_time)
1406  {
1407  ss->last_aim_enemy_pos = En_Objp->pos;
1408  ss->last_aim_enemy_vel = En_Objp->phys_info.vel;
1410  }
1411  else
1412  {
1413  //Update the position based on the velocity (assume no velocity vector change)
1415  }
1416 }
1417 
1418 
1428 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vec3d *predicted_enemy_pos, vec3d *gvec)
1429 {
1430  int ret_val __UNUSED = 0; // to be used in future, see comment @ end of function
1431 
1432  if (ss->turret_enemy_objnum != -1) {
1433  model_subsystem *tp = ss->system_info;
1434  vec3d gun_pos, gun_vec, target_moving_direction;
1435  //float weapon_speed;
1436  float weapon_system_strength;
1437  //HACK HACK HACK -WMC
1438  //This should use the best weapon variable
1439  //It doesn't, because I haven't implemented code to set it yet -WMC
1440  int best_weapon_tidx = turret_select_best_weapon(ss, lep);
1441 
1442  //This turret doesn't have any good weapons
1443  if (best_weapon_tidx < 0)
1444  return 0;
1445 
1446  weapon_info *wip = get_turret_weapon_wip(&ss->weapons, best_weapon_tidx);
1447 
1448  // weapon_system_strength scales time enemy in range in 0..1. So, the lower this is, the worse the aiming will be.
1449  weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
1450 
1451  ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
1452 
1453  //Update "known" position and velocity of target. Only matters if max_aim_update_delay is set.
1455 
1456  //Figure out what point on the ship we want to point the gun at, and store the global location
1457  //in enemy_point.
1458  vec3d enemy_point;
1459  if ((ss->targeted_subsys != NULL) && !(ss->flags & SSF_NO_SS_TARGETING)) {
1460  if (ss->turret_enemy_objnum != -1) {
1462  vm_vec_add2(&enemy_point, &ss->last_aim_enemy_pos);
1463  }
1464  } else {
1465  if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
1466  ai_big_pick_attack_point_turret(lep, ss, &gun_pos, &gun_vec, &enemy_point, tp->turret_fov, MIN(wip->max_speed * wip->lifetime, wip->weapon_range));
1467  } else {
1468  enemy_point = ss->last_aim_enemy_pos;
1469  }
1470  }
1471 
1472  target_moving_direction = ss->last_aim_enemy_vel;
1473 
1474  //Try to guess where the enemy will be, and store that spot in predicted_enemy_pos
1476  vm_vec_scale_sub2(&target_moving_direction, &objp->phys_info.vel, wip->vel_inherit_amount);
1477  }
1478 
1479  set_predicted_enemy_pos_turret(predicted_enemy_pos, &gun_pos, objp, &enemy_point, &target_moving_direction, wip->max_speed, ss->turret_time_enemy_in_range * (weapon_system_strength + 1.0f)/2.0f);
1480 
1481  //Mess with the turret's accuracy if the weapon system is damaged.
1482  if (weapon_system_strength < 0.7f) {
1483  vec3d rand_vec;
1484 
1485  static_randvec(Missiontime >> 18, &rand_vec); // Return same random number for two seconds.
1486  // Add to predicted_enemy_pos value in .45 to 1.5x radius of enemy ship, so will often miss, but not by a huge amount.
1487  vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
1488  }
1489 
1490  //Get the normalized dir between the turret and the predicted enemy position.
1491  //If the dot product is smaller than or equal to the turret's FOV, try and point the gun at it.
1492  vec3d v2e;
1493  vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
1494 
1495  bool in_fov;
1496 
1497  in_fov = turret_fov_test(ss, gvec, &v2e);
1498 
1499  if (ss->flags & SSF_FOV_EDGE_CHECK) {
1500  if (in_fov == false)
1501  in_fov = is_object_radius_in_turret_fov(&Objects[ss->turret_enemy_objnum], ss, gvec, &gun_pos, &v2e, predicted_enemy_pos, 0.0f);
1502  }
1503 
1504  if (in_fov) {
1505  ret_val = model_rotate_gun(Ship_info[shipp->ship_info_index].model_num,
1506  tp, &Objects[parent_objnum].orient,
1508  &Objects[parent_objnum].pos, predicted_enemy_pos, shipp->objnum);
1510  ret_val = model_rotate_gun(Ship_info[shipp->ship_info_index].model_num, tp, &Objects[parent_objnum].orient, &ss->submodel_info_1.angs, &ss->submodel_info_2.angs, &Objects[parent_objnum].pos, predicted_enemy_pos, shipp->objnum, true);
1511  }
1513  ret_val = model_rotate_gun(Ship_info[shipp->ship_info_index].model_num, ss->system_info, &Objects[parent_objnum].orient, &ss->submodel_info_1.angs, &ss->submodel_info_2.angs, &Objects[parent_objnum].pos, predicted_enemy_pos, shipp->objnum, true);
1514  }
1515 
1516  // by default "ret_val" should be set to 1 for multi-part turrets, and 0 for single-part turrets
1517  // but we need to keep retail behavior by default, which means always returning 0 unless a special
1518  // flag is used. the "ret_val" stuff is here is needed/wanted at a later date however.
1519  //return ret_val;
1520 
1521  // return 0 by default (to preserve retail behavior) but allow for a per-subsystem option
1522  // for using the turret normals for firing
1524  return 1;
1525 
1526  return 0;
1527 }
1528 
1534 float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
1535 {
1536  float dot_out;
1537  vec3d subobj_pos, vector_out;
1538 
1539  vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
1540  vm_vec_add2(&subobj_pos, &enemy_objp->pos);
1541 
1542  if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
1543  vec3d turret_norm;
1544 
1545  vm_vec_unrotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
1546  float dot_return = vm_vec_dot(&turret_norm, &vector_out);
1547 
1549  if (dot_return > turret_subsysp->system_info->turret_fov) {
1550  // target is in sight and in fov
1551  return dot_return;
1552  } else {
1553  // target is in sight but is not in turret's fov
1554  return -1.0f;
1555  }
1556  } else {
1557  // target is in sight and we don't care if its in turret's fov or not
1558  return dot_return;
1559  }
1560  } else
1561  return -1.0f;
1562 
1563 }
1564 
1565 // NOTE: Do not change this value unless you understand exactly what it means and what it does.
1566 // It refers to how many (non-destroyed) subsystems (and turrets) will be scanned for possible
1567 // targeting, per turret, per frame. A higher value will process more systems at once,
1568 // but it will be much slower to scan though them. It is not necessary to scan all
1569 // non-destroyed subsystem each frame for each turret. Also, "aifft_max_checks" is balanced
1570 // against the original value, be sure to account for this discrepancy with any changes.
1571 #define MAX_AIFFT_TURRETS 60
1572 
1577 DCF(mf, "Adjusts the maximum number of tries an AI may do when trying to pick a subsystem to attack (Default is 5)")
1578 {
1580 
1581  if (aifft_max_checks <= 0) {
1582  dc_printf("Value must be a non-negative, non-zero integer\n");
1583  dc_printf("aifft_max_checks set to default value of 5\n");
1584 
1585  aifft_max_checks = 5;
1586  }
1587 }
1588 
1589 
1596 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
1597 {
1598  ship *eshipp, *shipp;
1599  ship_info *esip;
1600  ship_subsys *best_subsysp = NULL;
1601  float dot;
1602 
1603  Assert(enemy_objp->type == OBJ_SHIP);
1604 
1605  eshipp = &Ships[enemy_objp->instance];
1606  esip = &Ship_info[eshipp->ship_info_index];
1607 
1608  shipp = &Ships[objp->instance];
1609 
1610  float best_dot = 0.0f;
1611  *dot_out = best_dot;
1612 
1613  // Compute absolute gun position.
1614  vec3d abs_gun_pos;
1615  vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
1616  vm_vec_add2(&abs_gun_pos, &objp->pos);
1617 
1618  // Only pick a turret to attack on large ships.
1619  if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
1620  return best_subsysp;
1621 
1622  // Make sure big or huge ship *actually* has subsystems (ie, knossos)
1623  if (esip->n_subsystems == 0) {
1624  return best_subsysp;
1625  }
1626 
1627  // first build up a list subsystems to traverse
1628  ship_subsys *pss;
1629  aifft_list_size = 0;
1630  for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
1631  model_subsystem *psub = pss->system_info;
1632 
1633  // if we've reached max turrets bail
1635  break;
1636  }
1637 
1638  // Don't process destroyed objects
1639  if ( pss->current_hits <= 0.0f ){
1640  continue;
1641  }
1642 
1643  switch (psub->type) {
1644  case SUBSYSTEM_WEAPONS:
1645  aifft_list[aifft_list_size] = pss;
1646  aifft_rank[aifft_list_size++] = 1.4f;
1647  break;
1648 
1649  case SUBSYSTEM_TURRET:
1650  aifft_list[aifft_list_size] = pss;
1651  aifft_rank[aifft_list_size++] = 1.2f;
1652  break;
1653 
1654  case SUBSYSTEM_SENSORS:
1655  case SUBSYSTEM_ENGINE:
1656  aifft_list[aifft_list_size] = pss;
1657  aifft_rank[aifft_list_size++] = 1.0f;
1658  break;
1659  }
1660  }
1661 
1662  // DKA: 6/28/99 all subsystems can be destroyed.
1663  //Assert(aifft_list_size > 0);
1664  if (aifft_list_size == 0) {
1665  return best_subsysp;
1666  }
1667 
1668  // determine a stride value so we're not checking too many turrets
1670  if(stride <= 0){
1671  stride = 1;
1672  }
1673  int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
1674  int idx;
1675  float dot_fov_modifier = 0.0f;
1676 
1678  if (ssp->system_info->turret_fov < 0)
1679  dot_fov_modifier = ssp->system_info->turret_fov;
1680  }
1681 
1682  for(idx=offset; idx<aifft_list_size; idx+=stride){
1683  dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);
1684 
1685  if ((dot - dot_fov_modifier)* aifft_rank[idx] > best_dot) {
1686  best_dot = (dot - dot_fov_modifier)*aifft_rank[idx];
1687  best_subsysp = aifft_list[idx];
1688  }
1689  }
1690 
1691  Assert(best_subsysp != &eshipp->subsys_list);
1692 
1693  *dot_out = best_dot;
1694  return best_subsysp;
1695 }
1696 
1701 {
1702 // int target_type;
1703 
1705  return 1;
1706  }
1707 
1708 /*
1709  if ( turret->turret_enemy_objnum == -1 ) {
1710  return 1;
1711  }
1712 
1713  target_type = Objects[turret->turret_enemy_objnum].type;
1714  if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
1715  return 1;
1716  }
1717 */
1718  return 0;
1719 
1720 }
1721 
1725 void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsys *turret, ai_info *aip)
1726 {
1727  Assert(weapon_num < MAX_SHIP_WEAPONS);
1728  float wait = 1000.0f;
1729 
1730  if (wip->burst_shots > turret->weapons.burst_counter[weapon_num]) {
1731  wait *= wip->burst_delay;
1732  turret->weapons.burst_counter[weapon_num]++;
1733  } else {
1734  wait *= wip->fire_wait;
1735  if ((wip->burst_shots > 0) && (wip->burst_flags & WBF_RANDOM_LENGTH)) {
1736  turret->weapons.burst_counter[weapon_num] = (myrand() % wip->burst_shots);
1737  } else {
1738  turret->weapons.burst_counter[weapon_num] = 0;
1739  }
1740  }
1741 
1742  int *fs_dest;
1743  if(weapon_num < MAX_SHIP_PRIMARY_BANKS)
1744  fs_dest = &turret->weapons.next_primary_fire_stamp[weapon_num];
1745  else
1746  fs_dest = &turret->weapons.next_secondary_fire_stamp[weapon_num - MAX_SHIP_PRIMARY_BANKS];
1747 
1748  //Check for the new cooldown flag
1749  if(!((wip->wi_flags2 & WIF2_SAME_TURRET_COOLDOWN) || ((wip->burst_shots > 0) && (wip->burst_flags & WBF_FAST_FIRING))))
1750  {
1751 
1752  // make side even for team vs. team
1753  if (MULTI_TEAM) {
1754  // flak guns need to fire more rapidly
1755  if (wip->wi_flags & WIF_FLAK) {
1756  wait *= aip->ai_ship_fire_delay_scale_friendly * 0.5f;
1757  if (aip->ai_class_autoscale)
1758  wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
1759  } else {
1760  wait *= aip->ai_ship_fire_delay_scale_friendly;
1761  if (aip->ai_class_autoscale)
1762  wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
1763  }
1764  } else {
1765  // flak guns need to fire more rapidly
1766  if (wip->wi_flags & WIF_FLAK) {
1767  if (Ships[aip->shipnum].team == Player_ship->team) {
1768  wait *= aip->ai_ship_fire_delay_scale_friendly * 0.5f;
1769  } else {
1770  wait *= aip->ai_ship_fire_delay_scale_hostile * 0.5f;
1771  }
1772  if (aip->ai_class_autoscale)
1773  wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
1774 
1775  } else if (wip->wi_flags & WIF_HUGE) {
1776  // make huge weapons fire independently of team
1777  wait *= aip->ai_ship_fire_delay_scale_friendly;
1778  if (aip->ai_class_autoscale)
1779  wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
1780  } else {
1781  // give team friendly an advantage
1782  if (Ships[aip->shipnum].team == Player_ship->team) {
1783  wait *= aip->ai_ship_fire_delay_scale_friendly;
1784  } else {
1785  wait *= aip->ai_ship_fire_delay_scale_hostile;
1786  }
1787  if (aip->ai_class_autoscale)
1788  wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
1789  }
1790  }
1791  // vary wait time +/- 10%
1792  wait *= frand_range(0.9f, 1.1f);
1793  }
1794 
1795  if(turret->rof_scaler != 1.0f)
1796  wait /= get_adjusted_turret_rof(turret);
1797 
1798  (*fs_dest) = timestamp((int)wait);
1799 }
1800 
1804 int turret_should_fire_aspect(ship_subsys *turret, weapon_info *wip, bool in_sight)
1805 {
1806  if ( in_sight && (turret->turret_time_enemy_in_range >= MIN(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
1807  return 1;
1808  }
1809 
1810  return 0;
1811 }
1812 
1816 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
1817 {
1818  turret->turret_time_enemy_in_range += seconds;
1819 
1820  if ( turret->turret_time_enemy_in_range < 0.0f ) {
1821  turret->turret_time_enemy_in_range = 0.0f;
1822  }
1823 
1826  }
1827 }
1828 
1832 bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *turret_fvec, vec3d *predicted_pos = NULL, float flak_range_override = 100.0f)
1833 {
1834  matrix turret_orient;
1835  int weapon_objnum;
1836  ai_info *parent_aip;
1837  ship *parent_ship;
1838  float flak_range = 0.0f;
1839  weapon_info *wip;
1840  weapon *wp;
1841  object *objp;
1842  bool last_shot_in_salvo = true;
1843 
1844  if (turret->system_info->flags & MSS_FLAG_TURRET_SALVO)
1845  {
1846  if ((turret->turret_next_fire_pos + 1) == (turret->system_info->turret_num_firing_points))
1847  {
1848  last_shot_in_salvo = true;
1849  }
1850  else
1851  {
1852  last_shot_in_salvo = false;
1853  }
1854  }
1855 
1856  //WMC - Limit firing to firestamp
1857  if((!timestamp_elapsed(get_turret_weapon_next_fire_stamp(&turret->weapons, weapon_num))) && last_shot_in_salvo)
1858  return false;
1859 
1860  parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
1861  parent_ship = &Ships[Objects[parent_objnum].instance];
1862  wip = get_turret_weapon_wip(&turret->weapons, weapon_num);
1863  int turret_weapon_class = WEAPON_INFO_INDEX(wip);
1864 
1865 #ifndef NDEBUG
1866  // moved here from check_ok_to_fire
1867  if (turret->turret_enemy_objnum >= 0) {
1868  object *tobjp = &Objects[turret->turret_enemy_objnum];
1869 
1870  // should not get this far. check if ship is protected from beam and weapon is type beam
1871  if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
1872  Int3();
1873  return 0;
1874  }
1875  // should not get this far. check if ship is protected from flak and weapon is type flak
1876  else if ( (wip->wi_flags & WIF_FLAK) && (tobjp->flags & OF_FLAK_PROTECTED) ) {
1877  Int3();
1878  return 0;
1879  }
1880  // should not get this far. check if ship is protected from laser and weapon is type laser
1881  else if ( (wip->subtype == WP_LASER) && (tobjp->flags & OF_LASER_PROTECTED) ) {
1882  Int3();
1883  return 0;
1884  }
1885  // should not get this far. check if ship is protected from missile and weapon is type missile
1886  else if ( (wip->subtype == WP_MISSILE) && (tobjp->flags & OF_MISSILE_PROTECTED) ) {
1887  Int3();
1888  return 0;
1889  }
1890  }
1891 #endif
1892 
1893  if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip)) {
1894  vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
1895  turret->turret_last_fire_direction = *turret_fvec;
1896 
1897  // set next fire timestamp for the turret
1898  if (last_shot_in_salvo)
1899  turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
1900 
1901  // if this weapon is a beam weapon, handle it specially
1902  if (wip->wi_flags & WIF_BEAM) {
1903  // if this beam isn't free to fire
1904  if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
1905  return false;
1906  }
1907  beam_fire_info fire_info;
1908 
1909  // stuff beam firing info
1910  memset(&fire_info, 0, sizeof(beam_fire_info));
1911  fire_info.accuracy = 1.0f;
1912  fire_info.beam_info_index = turret_weapon_class;
1913  fire_info.beam_info_override = NULL;
1914  fire_info.shooter = &Objects[parent_objnum];
1915  fire_info.target = &Objects[turret->turret_enemy_objnum];
1916  if (wip->wi_flags2 & WIF2_ANTISUBSYSBEAM)
1917  fire_info.target_subsys = turret->targeted_subsys;
1918  else
1919  fire_info.target_subsys = NULL;
1920  fire_info.turret = turret;
1921 
1922  // fire a beam weapon
1923  weapon_objnum = beam_fire(&fire_info);
1924 
1925  if (weapon_objnum != -1) {
1926  objp = &Objects[weapon_objnum];
1927 
1928  parent_ship->last_fired_turret = turret;
1929  turret->last_fired_weapon_info_index = turret_weapon_class;
1930 
1931  Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", nullptr, "Beam", objp, "Target", &Objects[turret->turret_enemy_objnum]);
1932  Script_system.RunCondition(CHA_ONTURRETFIRED, 0, NULL, &Objects[parent_objnum]);
1933  Script_system.RemHookVars(4, "Ship", "Weapon", "Beam", "Target");
1934  }
1935 
1936  turret->flags |= SSF_HAS_FIRED; //set fired flag for scripting -nike
1937  return true;
1938  }
1939  // don't fire swam, but set up swarm info instead
1940  else if ((wip->wi_flags & WIF_SWARM) || (wip->wi_flags & WIF_CORKSCREW)) {
1941  ship_weapon *swp = &turret->weapons;
1942  if (swp->current_secondary_bank < 0) {
1943  swp->current_secondary_bank = 0;
1944  }
1945  int bank_to_fire = swp->current_secondary_bank;
1946  if ((turret->system_info->flags2 & MSS_FLAG2_TURRET_USE_AMMO) && (swp->secondary_bank_ammo[bank_to_fire] < 0)) {
1947  if (!(turret->system_info->flags & MSS_FLAG_USE_MULTIPLE_GUNS)) {
1948  swp->current_secondary_bank++;
1949  if (swp->current_secondary_bank >= swp->num_secondary_banks) {
1950  swp->current_secondary_bank = 0;
1951  }
1952  return false;
1953  } else {
1954  return false;
1955  }
1956  } else {
1957  turret_swarm_set_up_info(parent_objnum, turret, wip, turret->turret_next_fire_pos);
1958 
1959  turret->flags |= SSF_HAS_FIRED; //set fired flag for scripting -nike
1960  return true;
1961  }
1962  }
1963  // now do anything else
1964  else {
1965  for (int i = 0; i < wip->shots; i++) {
1966  if (turret->system_info->flags2 & MSS_FLAG2_TURRET_USE_AMMO) {
1967  ship_weapon *swp;
1968  swp = &turret->weapons;
1969  int bank_to_fire, num_slots = turret->system_info->turret_num_firing_points;
1970  if (wip->subtype == WP_LASER) {
1971  int points;
1972  if (swp->num_primary_banks <= 0) {
1973  return false;
1974  }
1975 
1976  if (swp->current_primary_bank < 0){
1977  swp->current_primary_bank = 0;
1978  return false;
1979  }
1980 
1981  int num_primary_banks = swp->num_primary_banks;
1982 
1983  Assert(num_primary_banks > 0);
1984  if (num_primary_banks < 1){
1985  return false;
1986  }
1987 
1988  bank_to_fire = swp->current_primary_bank;
1989 
1990  if (turret->system_info->flags & MSS_FLAG_TURRET_SALVO) {
1991  points = num_slots;
1992  } else {
1993  points = 1;
1994  }
1995 
1996  if (swp->primary_bank_ammo[bank_to_fire] >= 0) {
1997  swp->primary_bank_ammo[bank_to_fire] -= points;
1998  } else if (!(turret->system_info->flags & MSS_FLAG_USE_MULTIPLE_GUNS) && (swp->primary_bank_ammo[bank_to_fire] < 0)) {
1999  swp->current_primary_bank++;
2000  if (swp->current_primary_bank >= swp->num_primary_banks) {
2001  swp->current_primary_bank = 0;
2002  }
2003  return false;
2004  } else {
2005  return false;
2006  }
2007  }
2008  else if (wip->subtype == WP_MISSILE) {
2009  if (swp->num_secondary_banks <= 0) {
2010  return false;
2011  }
2012 
2013  if (swp->current_secondary_bank < 0){
2014  swp->current_secondary_bank = 0;
2015  return false;
2016  }
2017 
2018  bank_to_fire = swp->current_secondary_bank;
2019 
2020  int start_slot, end_slot;
2021 
2022  start_slot = swp->secondary_next_slot[bank_to_fire];
2023  end_slot = start_slot;
2024 
2025  for (int j = start_slot; j <= end_slot; j++) {
2026  swp->secondary_next_slot[bank_to_fire]++;
2027 
2028  if (swp->secondary_next_slot[bank_to_fire] > (num_slots - 1)){
2029  swp->secondary_next_slot[bank_to_fire] = 0;
2030  }
2031 
2032  if (swp->secondary_bank_ammo[bank_to_fire] >= 0) {
2033  swp->secondary_bank_ammo[bank_to_fire]--;
2034  } else if (!(turret->system_info->flags & MSS_FLAG_USE_MULTIPLE_GUNS) && (swp->secondary_bank_ammo[bank_to_fire] < 0)) {
2035  swp->current_secondary_bank++;
2036  if (swp->current_secondary_bank >= swp->num_secondary_banks) {
2037  swp->current_secondary_bank = 0;
2038  }
2039  return false;
2040  } else {
2041  return false;
2042  }
2043  }
2044  }
2045  }
2046  // zookeeper - Firepoints should cycle normally between shots,
2047  // so we need to get the position info separately for each shot
2048  ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, turret_pos, turret_fvec, 1, NULL);
2049 
2050  weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, -1, 1, 0,0.0f, turret);
2051  weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);
2052 
2053  //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));
2054  if (weapon_objnum != -1) {
2055  objp = &Objects[weapon_objnum];
2056  wp = &Weapons[objp->instance];
2057  wip = &Weapon_info[wp->weapon_info_index];
2058 
2059  parent_ship->last_fired_turret = turret;
2061 
2062  wp->target_num = turret->turret_enemy_objnum;
2063  // AL 1-6-97: Store pointer to turret subsystem
2064  wp->turret_subsys = turret;
2065 
2066  Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", objp, "Beam", nullptr, "Target", &Objects[turret->turret_enemy_objnum]);
2067  Script_system.RunCondition(CHA_ONTURRETFIRED, 0, NULL, &Objects[parent_objnum]);
2068  Script_system.RemHookVars(4, "Ship", "Weapon", "Beam", "Target");
2069 
2070  // if the gun is a flak gun
2071  if (wip->wi_flags & WIF_FLAK) {
2072  // show a muzzle flash
2073  flak_muzzle_flash(turret_pos, turret_fvec, &Objects[parent_ship->objnum].phys_info, turret_weapon_class);
2074 
2075  if(predicted_pos != NULL)
2076  {
2077  // pick a firing range so that it detonates properly
2078  flak_pick_range(objp, turret_pos, predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
2079 
2080  // determine what that range was
2081  flak_range = flak_get_range(objp);
2082  }
2083  else
2084  {
2085  flak_set_range(objp, flak_range_override);
2086  }
2087  }
2088  // otherwise just do mflash if the weapon has it
2089  else if (wip->muzzle_flash >= 0) {
2090  mflash_create(turret_pos, turret_fvec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
2091  }
2092 
2093  // in multiplayer (and the master), then send a turret fired packet.
2094  if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
2095  int subsys_index;
2096 
2097  subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
2098  Assert( subsys_index != -1 );
2099  if(wip->wi_flags & WIF_FLAK){
2100  send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
2101  } else {
2102  send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
2103  }
2104  }
2105 
2106  if ( wip->launch_snd != -1 ) {
2107  // Don't play turret firing sound if turret sits on player ship... it gets annoying.
2108  if ( parent_objnum != OBJ_INDEX(Player_obj) || (turret->flags & SSF_PLAY_SOUND_FOR_PLAYER) ) {
2109  snd_play_3d( &Snds[wip->launch_snd], turret_pos, &View_position );
2110  }
2111  }
2112  }
2113  turret->turret_next_fire_pos++;
2114  }
2115  turret->turret_next_fire_pos--;
2116  // reset any animations if we need to
2117  // (I'm not sure how accurate this timestamp would be in practice - taylor)
2118  if (turret->turret_animation_position == MA_POS_READY)
2119  turret->turret_animation_done_time = timestamp(100);
2120  }
2121  }
2122  //Not useful -WMC
2123  else if (!(parent_aip->ai_profile_flags & AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY) && last_shot_in_salvo)
2124  {
2125  float wait = 1000.0f * frand_range(0.9f, 1.1f);
2126  turret->turret_next_fire_stamp = timestamp((int) wait);
2127  }
2128 
2129  turret->flags |= SSF_HAS_FIRED; //set has fired flag for scriptng - nuke
2130 
2131  //Fire animation stuff
2133  return true;
2134 }
2135 
2136 //void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
2138 {
2139  int weapon_objnum;
2140  matrix turret_orient;
2141  vec3d turret_pos, turret_fvec;
2142 
2143  // parent not alive, quick out.
2144  if (Objects[tsi->parent_objnum].type != OBJ_SHIP) {
2145  return;
2146  }
2147 
2148  // if fixed fp make sure to use constant fp
2150  tsi->turret->turret_next_fire_pos = tsi->weapon_num;
2151 
2152  // change firing point
2153  ship_get_global_turret_gun_info(&Objects[tsi->parent_objnum], tsi->turret, &turret_pos, &turret_fvec, 1, NULL);
2154  tsi->turret->turret_next_fire_pos++;
2155 
2156  //check if this really is a swarm. If not, how the hell did it get here?
2158 
2159 
2160  // *If it's a non-homer, then use the last fire direction instead of turret orientation to fix inaccuracy
2161  // problems with non-homing swarm weapons -Et1
2163  && !(Weapon_info[tsi->weapon_class].wi_flags & WIF_HOMING)) )
2164  {
2165  turret_fvec = tsi->turret->turret_last_fire_direction;
2166  }
2167 
2168  // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
2169  vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
2170 
2171  // create weapon and homing info
2172  weapon_objnum = weapon_create(&turret_pos, &turret_orient, tsi->weapon_class, tsi->parent_objnum, -1, 1, 0, 0.0f, tsi->turret);
2173  weapon_set_tracking_info(weapon_objnum, tsi->parent_objnum, tsi->target_objnum, 1, tsi->target_subsys);
2174 
2175  // do other cool stuff if weapon is created.
2176  if (weapon_objnum > -1) {
2177  Weapons[Objects[weapon_objnum].instance].turret_subsys = tsi->turret;
2178  Weapons[Objects[weapon_objnum].instance].target_num = tsi->turret->turret_enemy_objnum;
2179 
2182 
2183  Script_system.SetHookObjects(4, "Ship", &Objects[tsi->parent_objnum], "Weapon", &Objects[weapon_objnum], "Beam", nullptr, "Target", &Objects[tsi->turret->turret_enemy_objnum]);
2185  Script_system.RemHookVars(4, "Ship", "Weapon", "Beam", "Target");
2186 
2187  // muzzle flash?
2188  if (Weapon_info[tsi->weapon_class].muzzle_flash >= 0) {
2189  mflash_create(&turret_pos, &turret_fvec, &Objects[tsi->parent_objnum].phys_info, Weapon_info[tsi->weapon_class].muzzle_flash);
2190  }
2191 
2192  // maybe sound
2193  if ( Weapon_info[tsi->weapon_class].launch_snd != -1 ) {
2194  // Don't play turret firing sound if turret sits on player ship... it gets annoying.
2195  if ( tsi->parent_objnum != OBJ_INDEX(Player_obj) ) {
2197  }
2198  }
2199 
2200  // in multiplayer (and the master), then send a turret fired packet.
2201  if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
2202  int subsys_index;
2203 
2204  subsys_index = ship_get_index_from_subsys(tsi->turret, tsi->parent_objnum );
2205  Assert( subsys_index != -1 );
2206  send_turret_fired_packet( tsi->parent_objnum, subsys_index, weapon_objnum );
2207  }
2208  }
2209 }
2210 
2214 
2218 extern int Nebula_sec_range;
2219 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
2220 {
2221  float weapon_firing_range;
2222 
2223  // *Weapon minimum firing range -Et1
2224  float WeaponMinRange;
2225 
2226  vec3d v2e;
2227  object *lep; // Last enemy pointer
2228  model_subsystem *tp = ss->system_info;
2229  ship_weapon *swp = &ss->weapons;
2230  vec3d predicted_enemy_pos = vmd_zero_vector;
2231  object *objp;
2232  //ai_info *aip;
2233 
2234  // Reset the points to target value
2235  ss->points_to_target = -1.0f;
2236  ss->base_rotation_rate_pct = 0.0f;
2237  ss->gun_rotation_rate_pct = 0.0f;
2238 
2239  if (!Ai_firing_enabled) {
2240  return;
2241  }
2242 
2243  if (ss->current_hits <= 0.0f) {
2244  return;
2245  }
2246 
2247  if ( ship_subsys_disrupted(ss) ){ // AL 1/19/98: Make sure turret isn't suffering disruption effects
2248  return;
2249  }
2250 
2251  // Check turret free
2252  if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
2253  return;
2254  }
2255 
2256  // check if there is any available weapon to fire
2257  int weap_check;
2258  bool valid_weap = false;
2259  for (weap_check = 0; (weap_check < swp->num_primary_banks) && !valid_weap; weap_check++) {
2260  if (swp->primary_bank_weapons[weap_check] >= 0)
2261  valid_weap = true;
2262  }
2263 
2264  if (!valid_weap) {
2265  for (weap_check = 0; (weap_check < ss->weapons.num_secondary_banks) && !valid_weap; weap_check++) {
2266  if (ss->weapons.secondary_bank_weapons[weap_check] >= 0)
2267  valid_weap = true;
2268  }
2269 
2270  if (!valid_weap)
2271  return;
2272  }
2273 
2274  // If beam weapon, check beam free
2276  return;
2277  }
2278 
2282  // setup a reversal (closing) timestamp at 1 second
2283  // (NOTE: this will probably get changed by other parts of the code (swarming, beams, etc) to be
2284  // a more accurate time, but we need to give it a long enough time for the other parts of the code
2285  // to change the timestamp before it gets acted upon - taylor)
2287  } else {
2288  return;
2289  }
2290  }
2291 
2292  Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
2293  objp = &Objects[parent_objnum];
2294  Assert(objp->type == OBJ_SHIP);
2295  Assert( shipp->objnum == parent_objnum );
2296 
2297  // Monitor number of calls to ai_fire_from_turret
2298  Num_ai_firing++;
2299 
2301  {
2302  ss->turret_enemy_objnum = -1;
2303  lep = NULL;
2304  }
2305  else
2306  {
2307  lep = &Objects[ss->turret_enemy_objnum];
2308  }
2309 
2310 
2311  //aip = &Ai_info[Ships[objp->instance].ai_index];
2312  // Wanderer - make sure turrets already have all the data
2313  if ( !(tp->flags & MSS_FLAG_TURRET_MATRIX) )
2314  {
2315  if (!(tp->turret_gun_sobj == tp->subobj_num))
2316  {
2317  model_make_turret_matrix(Ship_info[shipp->ship_info_index].model_num, tp );
2318  }
2319  }
2320 
2321  // Use the turret info for all guns, not one gun in particular.
2322  vec3d gvec, gpos;
2323  ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
2324 
2325  if (tp->flags & MSS_FLAG_TURRET_ALT_MATH) {
2326  vm_matrix_x_matrix( &ss->world_to_turret_matrix, &Objects[parent_objnum].orient, &tp->turret_matrix );
2327  }
2328 
2329  // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
2330  int use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
2331 
2332  if ((tp->flags & MSS_FLAG_FIRE_ON_TARGET) && (ss->points_to_target >= 0.0f))
2333  {
2334  // value probably needs tweaking... could perhaps be made into table option?
2335  if (ss->points_to_target > 0.010f)
2336  return;
2337  }
2338 
2340  return;
2341  }
2342 
2343  // Don't try to fire beyond weapon_limit_range
2344  //WMC - OTC
2345  //weapon_firing_range = MIN(Weapon_info[tp->turret_weapon_type].lifetime * Weapon_info[tp->turret_weapon_type].max_speed, Weapon_info[tp->turret_weapon_type].weapon_range);
2346 
2347  //WMC - build a list of valid weapons. Fire spawns if there are any.
2348  float dist_to_enemy = 0.0f;
2349  if(lep != NULL) {
2351  dist_to_enemy = MAX(0,vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos));
2352  else
2353  dist_to_enemy = MAX(0,vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius);
2354  }
2355 
2356  int valid_weapons[MAX_SHIP_PRIMARY_BANKS + MAX_SHIP_SECONDARY_BANKS];
2357  int num_valid = 0;
2358 
2359  weapon_info *wip;
2360  int num_ships_nearby = num_nearby_fighters(iff_get_attackee_mask(obj_team(&Objects[parent_objnum])), &gpos, 1500.0f);
2361  int i;
2362  int secnum = 0;
2363  for(i = 0; i < (MAX_SHIP_WEAPONS); i++)
2364  {
2365  //WMC - Only fire more than once if we have multiple guns flag set.
2366  if(num_valid > 0 && !(tp->flags & MSS_FLAG_USE_MULTIPLE_GUNS))
2367  break;
2368 
2369  if(i < MAX_SHIP_PRIMARY_BANKS)
2370  {
2371  if(i >= swp->num_primary_banks)
2372  {
2373  //set to MAX_SHIP_PRIMARY_BANKS - 1 since i is incremented at the top of the loop
2374  //otherwise, we would pass over the first secondary weapon in the bank
2375  i = MAX_SHIP_PRIMARY_BANKS - 1; //we are done with primaries
2376  continue;
2377  }
2379  {
2380  continue;
2381  }
2382  wip = &Weapon_info[swp->primary_bank_weapons[i]];
2383  }
2384  else
2385  {
2386  secnum = i - MAX_SHIP_PRIMARY_BANKS;
2387  if(secnum >= swp->num_secondary_banks)
2388  break; //we are done.
2389  if ( !timestamp_elapsed(swp->next_secondary_fire_stamp[secnum]))
2390  {
2391  continue;
2392  }
2393  wip = &Weapon_info[swp->secondary_bank_weapons[secnum]];
2394  }
2395 
2396  //If this is a spawning type weapon, shoot it!
2397  //If 'smart spawn' just use it like normal weapons
2398  if ( (wip->wi_flags & WIF_SPAWN) && !(wip->wi_flags2 & WIF2_SMART_SPAWN) )
2399  {
2400  if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
2401  turret_fire_weapon(i, ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
2402  } else {
2403  ss->turret_next_fire_stamp = timestamp(1000); // Regardless of firing rate, don't check whether should fire for awhile.
2404  }
2405 
2406  //we're done with this weapon mount
2407  continue;
2408  }
2409 
2410 
2411  if(wip->wi_flags2 & WIF2_LOCAL_SSM)
2412  {
2413  weapon_firing_range=wip->lssm_lock_range;
2414  WeaponMinRange = 0.0f;
2415  }
2416  else
2417  {
2418  weapon_firing_range = MIN(wip->lifetime * wip->max_speed, wip->weapon_range);
2419  WeaponMinRange = wip->WeaponMinRange;
2420  }
2421 
2422 
2423  if (wip->wi_flags & WIF_BEAM) {
2424  if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
2425  if (Nebula_sec_range)
2426  {
2427  weapon_firing_range *= BEAM_NEBULA_RANGE_REDUCE_FACTOR;
2428 
2429  // *Scale minimum weapon range in nebula -Et1
2430  //Why? - WMC
2431  //Commented this out, because it doesn't make sense - WMC
2432  //WeaponMinRange *= BEAM_NEBULA_RANGE_REDUCE_FACTOR;
2433  }
2434  }
2435  }
2436 
2437  if(lep != NULL)
2438  {
2439  if( (dist_to_enemy <= weapon_firing_range) && (dist_to_enemy >= WeaponMinRange) )
2440  {
2441  if ( wip->wi_flags & WIF_HUGE ) {
2442  if ( lep->type != OBJ_SHIP ) {
2443  continue;
2444  }
2445  if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
2446  continue;
2447  }
2448  }
2449 
2450  if ( wip->wi_flags2 & WIF2_SMALL_ONLY ) {
2451  if ( (lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
2452  continue;
2453  }
2454  }
2455 
2456  bool tagged_only = ((wip->wi_flags2 & WIF2_TAGGED_ONLY) || (ss->weapons.flags & SW_FLAG_TAGGED_ONLY));
2457 
2458  if (lep->type == OBJ_SHIP) {
2459  // Check if we're targeting a protected ship
2460  if (lep->flags & OF_PROTECTED) {
2461  ss->turret_enemy_objnum = -1;
2462  ss->turret_time_enemy_in_range = 0.0f;
2463  continue;
2464  }
2465 
2466  // Check if we're targeting a beam protected ship with a beam weapon
2467  if ( (lep->flags & OF_BEAM_PROTECTED) && (wip->wi_flags & WIF_BEAM) ) {
2468  ss->turret_enemy_objnum = -1;
2469  ss->turret_time_enemy_in_range = 0.0f;
2470  continue;
2471  }
2472  // Check if we're targeting a flak protected ship with a flak weapon
2473  else if ( (lep->flags & OF_FLAK_PROTECTED) && (wip->wi_flags & WIF_FLAK) ) {
2474  ss->turret_enemy_objnum = -1;
2475  ss->turret_time_enemy_in_range = 0.0f;
2476  continue;
2477  }
2478  // Check if we're targeting a laser protected ship with a laser weapon
2479  else if ( (lep->flags & OF_LASER_PROTECTED) && (wip->subtype == WP_LASER) ) {
2480  ss->turret_enemy_objnum = -1;
2481  ss->turret_time_enemy_in_range = 0.0f;
2482  continue;
2483  }
2484  // Check if we're targeting a missile protected ship with a missile weapon
2485  else if ( (lep->flags & OF_MISSILE_PROTECTED) && (wip->subtype == WP_MISSILE) ) {
2486  ss->turret_enemy_objnum = -1;
2487  ss->turret_time_enemy_in_range = 0.0f;
2488  continue;
2489  }
2490  // Check if weapon or turret is set to tagged-only
2491  // must check here in case turret has multiple weapons and not all are tagged-only
2492  else if (!ship_is_tagged(lep) && tagged_only) {
2493  continue;
2494  }
2495  }
2496  else
2497  {
2498  // check tagged-only for non-ship targets
2499  if (tagged_only && (!(lep->type == OBJ_WEAPON) || (The_mission.ai_profile->flags2 & AIPF2_STRICT_TURRET_TAGGED_ONLY_TARGETING))) {
2500  continue;
2501  }
2502  }
2503 
2504  //Add it to the list
2505  valid_weapons[num_valid++] = i;
2506  }
2507  }
2508  }
2509 
2510  //none of our guns can hit the enemy, so find a new one
2511  if(num_valid < 1)
2512  ss->turret_enemy_objnum = -1;
2513 
2514  // Maybe pick a new enemy, unless targeting has been taken over by scripting
2516  Num_find_turret_enemy++;
2517  int objnum = find_turret_enemy(ss, parent_objnum, &gpos, &gvec, ss->turret_enemy_objnum);
2518  //Assert(objnum < 0 || is_target_beam_valid(tp, objnum));
2519 
2520  if (objnum >= 0 && is_target_beam_valid(&ss->weapons, &Objects[objnum])) {
2521  if (ss->turret_enemy_objnum == -1) {
2522  ss->turret_enemy_objnum = objnum;
2523  ss->targeted_subsys = NULL; // Turret has retargeted; reset subsystem - Valathil for Mantis #2652
2524  ss->turret_enemy_sig = Objects[objnum].signature;
2525  // why return?
2526  return;
2527  } else {
2528  ss->turret_enemy_objnum = objnum;
2529  ss->targeted_subsys = NULL; // Turret has retargeted; reset subsystem - Valathil for Mantis #2652
2530  ss->turret_enemy_sig = Objects[objnum].signature;
2531  }
2532  } else {
2533  ss->turret_enemy_objnum = -1;
2534  }
2535 
2536  if (ss->turret_enemy_objnum != -1) {
2537  float dot = 1.0f;
2538  lep = &Objects[ss->turret_enemy_objnum];
2539  if (( lep->type == OBJ_SHIP ) && !(ss->flags & SSF_NO_SS_TARGETING)) {
2540  ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);
2541  }
2542  ss->turret_next_enemy_check_stamp = timestamp((int) (MAX(dot, 0.5f)*2000.0f) + 1000);
2543  } else {
2544  ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f))); // Check every two seconds
2545  }
2546  }
2547 
2548  // If still don't have an enemy, return. Or, if enemy is protected, return.
2549  if (ss->turret_enemy_objnum != -1) {
2550  // Don't shoot at ship we're docked with.
2552  {
2553  ss->turret_enemy_objnum = -1;
2554  return;
2555  }
2556 
2557  // Goober5000 - Also, don't shoot at a ship we're docking with. Volition
2558  // had this in the code originally but messed it up so that it didn't work.
2559  ai_info *aip = &Ai_info[shipp->ai_index];
2560  if ((aip->mode == AIM_DOCK) && (aip->goal_objnum == ss->turret_enemy_objnum))
2561  {
2562  ss->turret_enemy_objnum = -1;
2563  return;
2564  }
2565 
2567  // This can happen if the enemy was selected before it became protected.
2568  ss->turret_enemy_objnum = -1;
2569  return;
2570  }
2571 
2572  lep = &Objects[ss->turret_enemy_objnum];
2573  } else {
2574  if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
2575  ss->turret_next_fire_stamp = timestamp(500);
2576  }
2577  return;
2578  }
2579 
2580  if ( lep == NULL ){
2581  mprintf(("last enemy is null\n"));
2582  return;
2583  }
2584 
2585  //This can't happen. See above code
2586  //Assert(ss->turret_enemy_objnum != -1);
2587 
2588  float dot;
2589  bool in_fov;
2590  in_fov = turret_fov_test(ss, &gvec, &v2e);
2591 
2592  // Ok, the turret is lined up... now line up a particular gun.
2593  bool ok_to_fire = false;
2594  bool something_was_ok_to_fire=false;
2595  vec3d tv2e; //so flak can get their jitter without screwing up other guns
2596 
2597  if (in_fov ) {
2598 
2599  // Do salvo thing separately - to prevent messing up things
2600  int number_of_firings;
2601  if (tp->flags & MSS_FLAG_TURRET_SALVO)
2602  {
2603  number_of_firings = tp->turret_num_firing_points;
2604  ss->turret_next_fire_pos = 0;
2605  }
2606  else
2607  {
2608  number_of_firings = num_valid;
2609  }
2610 
2611  for(i = 0; i < number_of_firings; i++)
2612  {
2613  if (tp->flags & MSS_FLAG_TURRET_FIXED_FP)
2614  {
2615  int ffp_pos = 0;
2616  int ffp_bank;
2617 
2618  ffp_bank = valid_weapons[i];
2619 
2620  if (ffp_bank < MAX_SHIP_PRIMARY_BANKS)
2621  {
2622  ffp_pos = ffp_bank;
2623  }
2624  else
2625  {
2626  ffp_pos = ffp_bank - MAX_SHIP_PRIMARY_BANKS + ss->weapons.num_primary_banks;
2627  }
2628 
2629  ss->turret_next_fire_pos = ffp_pos;
2630  }
2631 
2632  ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
2633 
2634  // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
2635  vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
2636  dist_to_enemy = vm_vec_normalize(&v2e);
2637  dot = vm_vec_dot(&v2e, &gvec);
2638 
2639  if (tp->flags & MSS_FLAG_TURRET_SALVO)
2640  wip = get_turret_weapon_wip(&ss->weapons, valid_weapons[0]);
2641  else
2642  wip = get_turret_weapon_wip(&ss->weapons, valid_weapons[i]);
2643 
2644  // We're ready to fire... now get down to specifics, like where is the
2645  // actual gun point and normal, not just the one for whole turret.
2646  // moved here as if there are two weapons with indentical fire stamps
2647  // they would have shared the fire point.
2648  tv2e = gvec;
2649 
2650  // make sure to reset this for current weapon
2651  ok_to_fire = false;
2652 
2653  // if the weapon is a flak gun, add some jitter to its aim so it fires in a "cone"
2654  // to make a cool visual effect and make them less lethal
2655  if(wip->wi_flags & WIF_FLAK){
2656  flak_jitter_aim(&tv2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS), wip);
2657  }
2658 
2659  // Fire if:
2660  // dumbfire and nearly pointing at target.
2661  // heat seeking and target in a fairly wide cone.
2662  // aspect seeking and target is locked.
2663  //turret_weapon_class = tp->turret_weapon_type;
2664  bool in_sight = false;
2665 
2667  // we have already passed the FOV test of the turret so...
2668  in_sight = true;
2669  } else {
2670  if (wip->wi_flags & WIF_HOMING_HEAT) {
2671  if (dot > AICODE_TURRET_HEATSEEK_ANGLE) {
2672  in_sight = true;
2673  }
2674  } else {
2675  if (dot > AICODE_TURRET_DUMBFIRE_ANGLE) {
2676  in_sight = true;
2677  }
2678  }
2679  }
2680 
2681  if ( !(wip->wi_flags & WIF_HOMING) )
2682  {
2683  if ((dist_to_enemy < 75.0f) || in_sight)
2684  {
2686  ok_to_fire = true;
2687  }
2688  }
2689  else if ( wip->wi_flags & WIF_HOMING_HEAT )
2690  { // if heat seekers
2691  if ((dist_to_enemy < 50.0f) || in_sight)
2692  {
2694  ok_to_fire = true;
2695  }
2696  }
2697  else if ( wip->wi_flags & WIF_HOMING_ASPECT )
2698  { // if aspect seeker
2699  if ((dist_to_enemy < 50.0f) || in_sight)
2700  {
2702  }
2703  if ( turret_should_fire_aspect(ss, wip, in_sight) )
2704  {
2705  ok_to_fire = true;
2706  }
2707  }
2708  else if ( wip->wi_flags & WIF_HOMING_JAVELIN )
2709  { // if javelin heat seeker
2710  if ((dist_to_enemy < 50.0f) || in_sight)
2711  {
2713  }
2714  // Check if turret should fire and enemy's engines are
2715  // in line of sight
2716  if (turret_should_fire_aspect(ss, wip, in_sight) &&
2718  {
2719  ok_to_fire = true;
2720  }
2721  }
2722 
2723  if ( ok_to_fire && (tp->flags & MSS_FLAG_TURRET_HULL_CHECK) ) {
2724  int model_num = Ship_info[shipp->ship_info_index].model_num;
2725  vec3d end;
2726  vm_vec_scale_add(&end, &gpos, &gvec, model_get_radius(model_num));
2727 
2729  mc_info_init(&hull_check);
2730  hull_check.model_instance_num = shipp->model_instance_num;
2731  hull_check.model_num = model_num;
2732  hull_check.orient = &objp->orient;
2733  hull_check.pos = &objp->pos;
2734  hull_check.p0 = &gpos;
2735  hull_check.p1 = &end;
2736  hull_check.flags = MC_CHECK_MODEL | MC_CHECK_RAY;
2737 
2738  if ( model_collide(&hull_check) ) {
2739  ok_to_fire = false;
2740  }
2741  }
2742 
2743  if ( ok_to_fire )
2744  {
2745  // starting animation checks
2750  }
2751  }
2752 
2753  //Wait until the animation is done to actually fire
2755  {
2756  ok_to_fire = false;
2757  }
2758  }
2759 
2760  if ( ok_to_fire )
2761  {
2762  something_was_ok_to_fire = true;
2763  Num_turrets_fired++;
2764 
2765  //Pass along which gun we are using
2766  if (tp->flags & MSS_FLAG_TURRET_SALVO)
2767  turret_fire_weapon(valid_weapons[0], ss, parent_objnum, &gpos, &tv2e, &predicted_enemy_pos);
2768  else
2769  turret_fire_weapon(valid_weapons[i], ss, parent_objnum, &gpos, &tv2e, &predicted_enemy_pos);
2770  } else {
2771  // make sure salvo fire mode does not turn into autofire
2772  if ((tp->flags & MSS_FLAG_TURRET_SALVO) && ((i + 1) == number_of_firings)) {
2773  ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
2774  turret_set_next_fire_timestamp(valid_weapons[0], wip, ss, parent_aip);
2775  }
2776  }
2777  ss->turret_next_fire_pos++;
2778  }
2779 
2780  if(!something_was_ok_to_fire)
2781  {
2782  mprintf(("nothing ok to fire\n"));
2783 
2784  if (ss->turret_best_weapon >= 0) {
2785  //Impose a penalty on turret accuracy for losing site of its goal, or just not being able to fire.
2787  }
2788  ss->turret_next_fire_stamp = timestamp(500);
2789 
2790  // If nothing is OK to fire (lost track of the target?)
2791  // reset the target (so we don't continue to track what we can't hit)
2793  {
2794  ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
2795  ss->turret_time_enemy_in_range = 0.0f;
2796  }
2797  }
2798  else
2799  {
2800  ss->turret_next_fire_stamp = INT_MAX;
2801  //something did fire, get the lowest valid timestamp
2802  for (i = 0; i < MAX_SHIP_WEAPONS; i++)
2803  {
2804  if (i < MAX_SHIP_PRIMARY_BANKS)
2805  {
2806  //timestamp range is 2 to INT_MAX/2
2807  if (swp->next_primary_fire_stamp[i] < 2) continue;
2808 
2810  {
2812  }
2813  }
2814  else
2815  {
2816  //timestamp range is 2 to INT_MAX/2
2817  if (swp->next_secondary_fire_stamp[i - MAX_SHIP_PRIMARY_BANKS] < 2) continue;
2818 
2820  {
2822  }
2823 
2824  }
2825 
2826  }
2827  // Wanderer -- sanity check
2828  // if timestamp is still at int_max reset the wait to 100 ms instead of locking the turret for the rest of the game
2829  if (ss->turret_next_fire_stamp == INT_MAX)
2830  ss->turret_next_fire_stamp = timestamp(100);
2831  }
2832  }
2833  else
2834  {
2835  // Lost him!
2836  ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
2837  ss->turret_time_enemy_in_range = 0.0f;
2838  }
2839 }
2840 
2841 bool turret_std_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
2842 {
2843  model_subsystem *tp = ss->system_info;
2844  float dot = vm_vec_dot(v2e, gvec);
2845  if (((dot + size_mod) >= tp->turret_fov) && ((dot - size_mod) <= tp->turret_max_fov))
2846  return true;
2847 
2848  return false;
2849 }
2850 
2851 bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
2852 {
2853  model_subsystem *tp = ss->system_info;
2854  float dot = vm_vec_dot(v2e, gvec);
2855  if (((dot + size_mod) >= tp->turret_fov) && ((dot - size_mod) <= tp->turret_max_fov)) {
2856  vec3d of_dst;
2857  vm_vec_rotate( &of_dst, v2e, &ss->world_to_turret_matrix );
2858  if ((of_dst.xyz.x == 0) && (of_dst.xyz.y == 0)) {
2859  return true;
2860  } else {
2861  of_dst.xyz.z = 0;
2862  if (!IS_VEC_NULL_SQ_SAFE(&of_dst)) {
2863  vm_vec_normalize(&of_dst);
2864  // now we have 2d vector with lenght of 1 that points at the targets direction after being rotated to turrets FOR
2865  if ((-of_dst.xyz.y + size_mod) >= tp->turret_y_fov)
2866  return true;
2867  }
2868  }
2869  }
2870  return false;
2871 }
2872 
2873 bool turret_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
2874 {
2875  bool in_fov = false;
2877  in_fov = turret_adv_fov_test(ss, gvec, v2e, size_mod);
2878  else
2879  in_fov = turret_std_fov_test(ss, gvec, v2e, size_mod);
2880 
2881  return in_fov;
2882 }
2883 
2885 {
2886  float tempf = ss->rof_scaler;
2887 
2888  // optional reset switch (negative value)
2889  if (tempf < 0) {
2890  ss->rof_scaler = 1.0f;
2891  return 1.0f;
2892  }
2893 
2894  if (tempf == 0) {
2895  // special case returning the number of firingpoints
2897  tempf = ss->rof_scaler;
2898 
2899  // safety check to avoid div/0 issues
2900  if (tempf == 0) {
2901  ss->rof_scaler = 1.0f;
2902  return 1.0f;
2903  }
2904  }
2905 
2906  return tempf;
2907 }
ship_subsys * target_subsys
Definition: swarm.h:29
#define __UNUSED
Definition: clang.h:23
void mc_info_init(mc_info *mc)
Definition: model.h:1138
int aifft_max_checks
Definition: aiturret.cpp:1576
asteroid_obj Asteroid_obj_list
Definition: asteroid.cpp:50
struct eval_enemy_obj_struct eval_enemy_obj_struct
int target_num
Definition: weapon.h:172
float lethality
Definition: ai.h:546
int model_collide(mc_info *mc_info_obj)
int turret_enemy_sig
Definition: ship.h:338
bool model_anim_start_type(ship_subsys *pss, int animation_type, int subtype, int direction, bool instant)
Definition: modelanim.cpp:566
int muzzle_flash
Definition: weapon.h:463
int flags
Definition: ship.h:150
void send_turret_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum)
Definition: multimsgs.cpp:3212
int timestamp(int delta_ms)
Definition: timer.cpp:226
int launch_snd
Definition: weapon.h:414
int turret_weapon_aggregate_flags2(ship_weapon *swp)
Definition: aiturret.cpp:384
int i
Definition: multi_pxo.cpp:466
#define STI_AI_TURRETS_ATTACK
Definition: ship.h:1008
fix Missiontime
Definition: systemvars.cpp:19
int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d *tvec, int current_enemy)
Definition: aiturret.cpp:1187
model_subsystem * system_info
Definition: ship.h:314
weapon Weapons[MAX_WEAPONS]
Definition: weapons.cpp:78
int primary_bank_weapons[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:103
#define SSF_FOV_REQUIRED
Definition: ship.h:285
vec3d turret_norm
Definition: model.h:186
#define MIN(a, b)
Definition: pstypes.h:296
#define MAX_SHIP_PRIMARY_BANKS
Definition: globals.h:62
#define EEOF_MISSILE
Definition: aiturret.cpp:62
vec3d * pos
Definition: model.h:1113
EModelAnimationPosition turret_animation_position
Definition: ship.h:351
float min_lock_time
Definition: weapon.h:401
int team
Definition: ship.h:606
float vm_vec_mag_quick(const vec3d *v)
Definition: vecmat.cpp:371
matrix * vm_matrix_x_matrix(matrix *dest, const matrix *src0, const matrix *src1)
Definition: vecmat.cpp:1006
#define AIPF2_TURRETS_IGNORE_TARGET_RADIUS
Definition: ai_profiles.h:54
int beam_info_index
Definition: beam.h:57
#define SSF_FOV_EDGE_CHECK
Definition: ship.h:286
void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
Definition: aiturret.cpp:2137
vec3d View_position
Definition: 3dsetup.cpp:20
int num_target_priorities
Definition: ship.h:390
int num_primary_banks
Definition: ship.h:99
object * homing_object
Definition: weapon.h:177
int objnum
Definition: ship.h:537
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
#define OF_LASER_PROTECTED
Definition: object.h:120
float frand_range(float min, float max)
Return a floating point number in the range min..max.
Definition: floating.cpp:50
#define MSS_FLAG2_TURRET_USE_AMMO
Definition: model.h:143
int obj_team(object *objp)
Definition: object.cpp:1843
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
SCP_vector< game_snd > Snds
Definition: gamesnd.cpp:19
object * target
Definition: beam.h:62
float fire_wait
Definition: weapon.h:359
#define MAX_AIFFT_TURRETS
Definition: aiturret.cpp:1571
float Lethality_range_const
Definition: aiturret.cpp:39
int turret_gun_sobj
Definition: model.h:193
submodel_instance_info submodel_info_1
Definition: ship.h:368
float flFrametime
Definition: fredstubs.cpp:22
Definition: weapon.h:163
#define SSF_PLAY_SOUND_FOR_PLAYER
Definition: ship.h:295
bool dock_check_find_docked_object(object *objp, object *other_objp)
Definition: objectdock.cpp:96
int weapon_create(vec3d *pos, matrix *orient, int weapon_type, int parent_obj, int group_id=-1, int is_locked=0, int is_spawned=0, float fof_cooldown=0.0f, ship_subsys *src_turret=NULL)
Definition: weapons.cpp:5246
physics_info phys_info
Definition: object.h:157
#define WIF_HUGE
Definition: weapon.h:64
int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, angles *base_angles, angles *gun_angles, vec3d *pos, vec3d *dst, int obj_idx, bool reset=false)
Definition: modelread.cpp:3904
GLsizei const GLfloat * points
Definition: Glext.h:7583
int turret_max_target_ownage
Definition: ship.h:404
void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, int use_angles, vec3d *targetp)
Definition: aiturret.cpp:1327
int next_primary_fire_stamp[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:113
int weapon_num
Definition: swarm.h:31
int class_type
Definition: ship.h:1167
void mflash_create(vec3d *gun_pos, vec3d *gun_dir, physics_info *pip, int mflash_type, object *local)
int model_instance_num
Definition: ship.h:802
int next_secondary_fire_stamp[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:115
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, vec3d *gun_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range)
Definition: aicode.cpp:6344
int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vec3d *predicted_enemy_pos, vec3d *gvec)
Definition: aiturret.cpp:1428
Assert(pm!=NULL)
uint flags2
Definition: model.h:170
int target_objnum
Definition: ai.h:339
Definition: pstypes.h:88
submodel_instance_info submodel_info_2
Definition: ship.h:369
#define mprintf(args)
Definition: pstypes.h:238
#define WIF_CORKSCREW
Definition: weapon.h:71
int ai_index
Definition: ship.h:538
ship_weapon weapons
Definition: ship.h:362
char sub_name[NAME_LENGTH]
Definition: ship.h:318
float level2_tag_left
Definition: ship.h:728
#define NUM_SKILL_LEVELS
Definition: systemvars.h:150
vec3d last_aim_enemy_pos
Definition: ship.h:395
#define OBJ_ASTEROID
Definition: object.h:44
int ai_class
Definition: ai.h:369
bool turret_weapon_has_subtype(ship_weapon *swp, int subtype)
Definition: aiturret.cpp:411
int Num_turrets_fired
Definition: aiturret.cpp:2213
float turret_max_fov
Definition: model.h:189
#define AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS
Definition: ai_profiles.h:43
int turret_next_enemy_check_stamp
Definition: ship.h:335
ship_subsys * turret
Definition: beam.h:60
#define MC_CHECK_RAY
Definition: model.h:1176
struct vec3d::@225::@227 xyz
#define AIPF2_STRICT_TURRET_TAGGED_ONLY_TARGETING
Definition: ai_profiles.h:68
CButton * team
void static_randvec(int num, vec3d *vp)
[To be described]
Definition: staticrand.cpp:111
ship_subsys * target_subsys
Definition: beam.h:63
int shots
Definition: weapon.h:498
GLclampf f
Definition: Glext.h:7097
vec3d * vm_vec_avg_n(vec3d *dest, int n, const vec3d src[])
Definition: vecmat.cpp:196
#define MSS_FLAG_TURRET_ANIM_WAIT
Definition: model.h:136
#define MAX_OBJECTS
Definition: globals.h:83
int ship_subsystem_in_sight(object *objp, ship_subsys *subsys, vec3d *eye_pos, vec3d *subsys_pos, int do_facing_check, float *dot_out, vec3d *vec_out)
Definition: ship.cpp:14881
weapon_info * get_turret_weapon_wip(ship_weapon *swp, int weapon_num)
Definition: aiturret.cpp:439
#define WIF2_SAME_TURRET_COOLDOWN
Definition: weapon.h:91
#define TRUE
Definition: pstypes.h:399
int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
Definition: aiturret.cpp:222
#define NUM_TURRET_ORDER_TYPES
Definition: ship.h:253
int parent_objnum
Definition: swarm.h:24
void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss)
Definition: aiturret.cpp:1403
ai_info Ai_info[MAX_AI_INFO]
Definition: ai.cpp:23
vec3d * vm_vec_rotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:933
#define Assertion(expr, msg,...)
Definition: clang.h:41
#define WIF2_TAGGED_ONLY
Definition: weapon.h:88
#define WEAPON_INFO_INDEX(wip)
Definition: weapon.h:613
object obj_used_list
Definition: object.cpp:53
#define AICODE_TURRET_MAX_TIME_IN_RANGE
Definition: aiturret.cpp:36
void turret_swarm_set_up_info(int parent_objnum, ship_subsys *turret, weapon_info *wip, int weapon_num)
Definition: swarm.cpp:423
int current_primary_bank
Definition: ship.h:106
int target_signature
Definition: ai.h:340
ship_subsys * targeted_subsys
Definition: ai.h:472
#define SF_ARRIVING
Definition: ship.h:453
#define EEOF_SMALL_ONLY
Definition: aiturret.cpp:57
int valid_turret_enemy(object *objp, object *turret_parent)
Definition: aiturret.cpp:507
int ship_is_tagged(object *objp)
Definition: ship.cpp:17263
float ai_turret_max_aim_update_delay
Definition: ai.h:462
float max_speed
Definition: weapon.h:354
void vm_vec_scale_add2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:284
#define OF_COLLIDES
Definition: object.h:104
int burst_flags
Definition: weapon.h:534
Definition: ai.h:329
#define EEOF_TAGGED_ONLY
Definition: aiturret.cpp:58
uint flags
Definition: ship.h:644
int turret_next_fire_stamp
Definition: ship.h:336
#define WIF_HOMING_ASPECT
Definition: weapon.h:45
#define SSF_HAS_FIRED
Definition: ship.h:284
float weapon_range
Definition: weapon.h:390
#define OF_PLAYER_SHIP
Definition: object.h:109
#define AIPF_USE_ADDITIVE_WEAPON_VELOCITY
Definition: ai_profiles.h:34
object * objp
Definition: lua.cpp:3105
float optimum_range
Definition: ship.h:342
ship_subsys * aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
Definition: aiturret.cpp:1596
int current_secondary_bank
Definition: ship.h:107
#define CHA_ONTURRETFIRED
Definition: scripting.h:70
int subtype
Definition: weapon.h:326
#define Int3()
Definition: pstypes.h:292
int Nebula_sec_range
Definition: hudlock.cpp:307
float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
Definition: aiturret.cpp:1534
ship * shipp
Definition: lua.cpp:9162
float points_to_target
Definition: ship.h:378
vec3d pos
Definition: object.h:152
float flak_get_range(object *objp)
Definition: flak.cpp:154
vec3d * p0
Definition: model.h:1114
vec3d turret_firing_point[MAX_TFP]
Definition: model.h:192
void ship_get_global_turret_info(object *objp, model_subsystem *tp, vec3d *gpos, vec3d *gvec)
Definition: aiturret.cpp:1303
void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsys *turret, ai_info *aip)
Definition: aiturret.cpp:1725
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *turret_fvec, vec3d *predicted_pos=NULL, float flak_range_override=100.0f)
Definition: aiturret.cpp:1832
script_state Script_system("FS2_Open Scripting")
float base_rotation_rate_pct
Definition: ship.h:379
ship_subsys * last_fired_turret
Definition: ship.h:772
int signature
Definition: object.h:145
int subobj_num
Definition: model.h:175
int model_num
Definition: model.h:1110
#define DCF(function_name, help_text)
The potent DCF macro, used to define new debug commands for the console.
Definition: console.h:60
#define IS_VEC_NULL_SQ_SAFE(v)
Definition: vecmat.h:24
#define AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS
Definition: ai_profiles.h:26
int turret_max_bomb_ownage
Definition: model.h:239
int iff_get_attackee_mask(int attacker_team)
Definition: iff_defs.cpp:561
int turret_select_best_weapon(ship_subsys *turret, object *target)
Definition: aiturret.cpp:246
int weapon_info_index
Definition: weapon.h:164
float lifetime
Definition: weapon.h:382
matrix turret_matrix
Definition: model.h:187
int rotation_timestamp
Definition: ship.h:385
int objnum
Definition: ship.h:1483
int ai_profile_flags
Definition: ai.h:463
ship_subsys subsys_list
Definition: ship.h:630
int objnum
Definition: weapon.h:561
float aifft_rank[MAX_AIFFT_TURRETS]
Definition: aiturret.cpp:1574
void model_make_turret_matrix(int model_num, model_subsystem *turret)
Definition: modelread.cpp:3846
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
#define MAX_SHIP_SECONDARY_BANKS
Definition: globals.h:63
void model_instance_find_world_point(vec3d *outpnt, vec3d *mpnt, int model_instance_num, int submodel_num, const matrix *objorient, const vec3d *objpos)
Definition: modelread.cpp:4135
#define EEOF_FLAK
Definition: aiturret.cpp:60
int instance
Definition: object.h:150
void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
Definition: aiturret.cpp:1816
#define WIF_FLAK
Definition: weapon.h:75
matrix * vm_vector_2_matrix(matrix *m, const vec3d *fvec, const vec3d *uvec, const vec3d *rvec)
Definition: vecmat.cpp:850
GLintptr offset
Definition: Glext.h:5497
float ship_get_subsystem_strength(ship *shipp, int type)
Definition: ship.cpp:13446
void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_objnum, int target_is_locked=0, ship_subsys *target_subsys=NULL)
Definition: weapons.cpp:5093
void vm_vec_add2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:178
#define MSS_FLAG_TURRET_FIXED_FP
Definition: model.h:119
void flak_muzzle_flash(vec3d *pos, vec3d *dir, physics_info *pip, int turret_weapon_class)
Definition: flak.cpp:120
int flags
Definition: ship.h:322
ship_subsys * turret_subsys
Definition: weapon.h:183
#define SIF_BIG_SHIP
Definition: ship.h:944
int flags
Definition: model.h:1116
int target_priority[32]
Definition: ship.h:389
#define MAX_SHIP_WEAPONS
Definition: globals.h:64
struct matrix::@228::@230 vec
#define ANIMATION_SUBTYPE_ALL
Definition: modelanim.h:52
#define AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE
Definition: ai_profiles.h:42
int subtype
Definition: lua.cpp:9763
void vm_vec_scale(vec3d *dest, float s)
Definition: vecmat.cpp:248
uint flags
Definition: model.h:169
int timestamp_until(int stamp)
Definition: timer.cpp:242
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
Definition: aicode.cpp:5993
#define AICODE_TURRET_HEATSEEK_ANGLE
Definition: aiturret.cpp:35
int team
Definition: weapon.h:167
#define MSS_FLAG_TURRET_RESET_IDLE
Definition: model.h:123
int primary_bank_ammo[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:121
float Player_lethality_bump[NUM_SKILL_LEVELS]
Definition: aiturret.cpp:45
#define WIF_SWARM
Definition: weapon.h:59
bool turret_weapon_has_flags(ship_weapon *swp, int flags)
Definition: aiturret.cpp:306
#define SW_FLAG_TURRET_LOCK
Definition: ship.h:95
float turret_y_fov
Definition: model.h:190
vec3d * p1
Definition: model.h:1115
int ship_subsys_disrupted(ship_subsys *ss)
Definition: ship.cpp:9033
int flags
Definition: ship.h:1227
ai_profile_t * ai_profile
Definition: missionparse.h:168
#define SIF_SMALL_SHIP
Definition: ship.h:943
int ship_get_index_from_subsys(ship_subsys *ssp, int objnum, int error_bypass)
Definition: ship.cpp:13390
int num_secondary_banks
Definition: ship.h:100
#define OBJ_WEAPON
Definition: object.h:33
int snd_play_3d(game_snd *gs, vec3d *source_pos, vec3d *listen_pos, float radius, vec3d *source_vel, int looping, float vol_scale, int priority, vec3d *sound_fvec, float range_factor, int force, bool is_ambient)
Definition: sound.cpp:594
int mode
Definition: ai.h:336
int shipnum
Definition: ai.h:331
mc_info hull_check
Definition: lua.cpp:5043
#define WP_LASER
Definition: weapon.h:27
int Num_ai_firing
Definition: aiturret.cpp:2211
float WeaponMinRange
Definition: weapon.h:513
int Player_attacking_enabled
Definition: aicode.cpp:1638
float ai_ship_fire_delay_scale_friendly
Definition: ai.h:449
#define WIF3_TURRET_INTERCEPTABLE
Definition: weapon.h:122
#define AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY
Definition: ai_profiles.h:27
int n_subsystems
Definition: ship.h:1270
#define OF_PROTECTED
Definition: object.h:108
#define SUBSYSTEM_TURRET
Definition: model.h:54
int object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
Definition: aiturret.cpp:101
int turret_should_pick_new_target(ship_subsys *turret)
Definition: aiturret.cpp:1700
float nearest_homing_bomb_dist
Definition: aiturret.cpp:80
bool turret_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
Definition: aiturret.cpp:2873
int secondary_bank_weapons[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:104
int Num_ai_classes
Definition: aicode.cpp:195
int model_anim_get_time_type(ship_subsys *pss, int animation_type, int subtype)
Definition: modelanim.cpp:740
float vm_vec_normalized_dir(vec3d *dest, const vec3d *end, const vec3d *start)
Definition: vecmat.cpp:591
float vm_vec_dist(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:355
char * Turret_target_order_names[NUM_TURRET_ORDER_TYPES]
Definition: aiturret.cpp:50
int lssm_stage
Definition: weapon.h:216
#define EEOF_LASER
Definition: aiturret.cpp:61
#define BEAM_NEBULA_RANGE_REDUCE_FACTOR
Definition: aiturret.cpp:37
int Num_find_turret_enemy
Definition: aiturret.cpp:2212
float current_hits
Definition: ship.h:319
int turret_targeting_order[NUM_TURRET_ORDER_TYPES]
Definition: ship.h:341
bool scripting_target_override
Definition: ship.h:345
int weapon_class
Definition: swarm.h:22
Definition: ship.h:534
int wi_flags3
Definition: weapon.h:386
ship_subsys * aifft_list[MAX_AIFFT_TURRETS]
Definition: aiturret.cpp:1573
#define MSS_FLAG_TURRET_ALT_MATH
Definition: model.h:124
int beam_fire(beam_fire_info *fire_info)
Definition: beam.cpp:281
void flak_set_range(object *objp, float range)
Definition: flak.cpp:142
int turret_num_firing_points
Definition: model.h:191
int idx
Definition: multiui.cpp:761
int secondary_bank_ammo[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:134
beam_info * beam_info_override
Definition: beam.h:67
#define SUBSYSTEM_WEAPONS
Definition: model.h:58
#define MSS_FLAG_FIRE_ON_TARGET
Definition: model.h:121
vec3d * vm_vec_unrotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:959
int num_targeting_priorities
Definition: weapon.h:543
int iff_x_attacks_y(int team_x, int team_y)
Definition: iff_defs.cpp:605
#define MSS_FLAG2_TURRET_ONLY_TARGET_IF_CAN_FIRE
Definition: model.h:139
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
#define WIF2_SMALL_ONLY
Definition: weapon.h:90
int turret_max_bomb_ownage
Definition: ship.h:403
#define MSS_FLAG2_SHARE_FIRE_DIRECTION
Definition: model.h:146
#define AIM_DOCK
Definition: ai.h:174
int wi_flags
Definition: weapon.h:384
fix next_aim_pos_time
Definition: ship.h:394
bool is_object_radius_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, vec3d *v2e, vec3d *predicted_pos, float distance)
Definition: aiturret.cpp:119
#define OBJ_INDEX(objp)
Definition: object.h:235
#define MULTI_TEAM
Definition: multi.h:655
ship * Player_ship
Definition: ship.cpp:124
matrix orient
Definition: object.h:153
#define WIF_HOMING_HEAT
Definition: weapon.h:44
int wi_flags2
Definition: weapon.h:385
int get_nearest_turret_objnum(int turret_parent_objnum, ship_subsys *turret_subsys, int enemy_team_mask, vec3d *tpos, vec3d *tvec, int current_enemy, bool big_only_flag, bool small_only_flag, bool tagged_only_flag, bool beam_flag, bool flak_flag, bool laser_flag, bool missile_flag)
Definition: aiturret.cpp:877
#define WIF_BOMB
Definition: weapon.h:63
#define WBF_FAST_FIRING
Definition: weapon.h:149
#define OBJ_SHIP
Definition: object.h:32
float asteroid_time_to_impact(object *asteroid_objp)
Definition: asteroid.cpp:1790
void dc_stuff_float(float *f)
Stuffs a float to the given variable.
GLbitfield flags
Definition: Glext.h:6722
#define WBF_RANDOM_LENGTH
Definition: weapon.h:150
#define SIF_HUGE_SHIP
Definition: ship.h:945
int turret_animation_done_time
Definition: ship.h:352
#define OF_MISSILE_PROTECTED
Definition: object.h:121
ship_subsys * turret_subsys
Definition: aiturret.cpp:73
void dc_stuff_int(int *i)
Stuffs an int to the given variable. Supports binary (0b), hexadecimal (0x), and octal (0o) formats...
int RunCondition(int condition, char format='\0', void *data=NULL, class object *objp=NULL, int more_data=0)
Definition: scripting.cpp:924
bool ai_class_autoscale
Definition: ai.h:437
#define SW_FLAG_TAGGED_ONLY
Definition: ship.h:96
vec3d turret_last_fire_direction
Definition: ship.h:334
void SetHookObjects(int num,...)
Definition: scripting.cpp:556
void vm_vec_sub(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:168
#define SUBSYSTEM_ENGINE
Definition: model.h:53
float gun_rotation_rate_pct
Definition: ship.h:380
vec3d vel
Definition: physics.h:77
int get_turret_weapon_next_fire_stamp(ship_weapon *swp, int weapon_num)
Definition: aiturret.cpp:450
#define MSS_FLAG_TURRET_HULL_CHECK
Definition: model.h:118
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
#define AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY
Definition: ai_profiles.h:47
void model_instance_find_world_dir(vec3d *out_dir, vec3d *in_dir, int model_instance_num, int submodel_num, const matrix *objorient)
Definition: modelread.cpp:4587
int last_objsig_hit
Definition: ai.h:504
int num_nearby_fighters(int enemy_team_mask, vec3d *pos, float threshold)
Definition: aicode.cpp:5765
float turret_time_enemy_in_range
Definition: ship.h:340
if(aifft_max_checks<=0)
Definition: aiturret.cpp:1581
float longest_turret_weapon_range(ship_weapon *swp)
Definition: aiturret.cpp:466
int parent_objnum
Definition: ship.h:316
void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
Definition: multimsgs.cpp:8270
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
bool turret_weapon_has_flags2(ship_weapon *swp, int flags)
Definition: aiturret.cpp:334
#define DCF_BOOL(function_name, bool_variable)
Definition: console.h:71
vec3d pnt
Definition: model.h:178
int secondary_next_slot[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:137
float turret_fov
Definition: model.h:188
int turret_next_fire_pos
Definition: ship.h:339
int turret_max_target_ownage
Definition: model.h:240
float tag_left
Definition: ship.h:725
float favor_current_facing
Definition: ship.h:343
vec3d last_aim_enemy_vel
Definition: ship.h:396
float frand()
Return random value in range 0.0..1.0- (1.0- means the closest number less than 1.0)
Definition: floating.cpp:35
void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
Definition: aiturret.cpp:575
int aifft_list_size
Definition: aiturret.cpp:1575
#define WP_MISSILE
Definition: weapon.h:28
float lssm_lock_range
Definition: weapon.h:492
int set_target_objnum(ai_info *aip, int objnum)
Definition: aicode.cpp:1250
float ai_ship_fire_delay_scale_hostile
Definition: ai.h:450
#define STI_TURRET_TGT_SHIP_TGT
Definition: ship.h:999
#define SUBSYSTEM_SENSORS
Definition: model.h:59
ship_subsys * turret
Definition: swarm.h:28
#define EEOF_BEAM
Definition: aiturret.cpp:59
#define MC_CHECK_MODEL
Definition: model.h:1169
GLenum target
Definition: Glext.h:6872
bool all_turret_weapons_have_flags(ship_weapon *swp, int flags)
Definition: aiturret.cpp:262
bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
Definition: aiturret.cpp:2851
#define WIF_BEAM
Definition: weapon.h:76
ship_subsys * ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos)
Definition: ship.cpp:14963
int asteroid_collide_objnum(object *asteroid_objp)
Definition: asteroid.cpp:1781
ship_subsys * targeted_subsys
Definition: ship.h:344
int Use_parent_target
Definition: aiturret.cpp:1175
float model_get_radius(int modelnum)
Definition: modelread.cpp:3105
ship_obj Ship_obj_list
Definition: ship.cpp:162
object * shooter
Definition: beam.h:58
void RemHookVars(unsigned int num,...)
Definition: scripting.cpp:754
int ship_info_index
Definition: ship.h:539
bool all_turret_weapons_have_flags2(ship_weapon *swp, int flags)
Definition: aiturret.cpp:284
#define MULTIPLAYER_MASTER
Definition: multi.h:130
An overhauled/updated debug console to allow monitoring, testing, and general debugging of new featur...
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
float get_adjusted_turret_rof(ship_subsys *ss)
Definition: aiturret.cpp:2884
#define OF_FLAK_PROTECTED
Definition: object.h:119
#define MSS_FLAG_TURRET_SALVO
Definition: model.h:120
int target_objnum
Definition: swarm.h:26
#define WIF_SPAWN
Definition: weapon.h:47
#define AIPF_HACK_IMPROVE_NON_HOMING_SWARM_TURRET_FIRE_ACCURACY
Definition: ai_profiles.h:28
int num_turrets_attacking(object *turret_parent, int target_objnum)
Definition: aicode.cpp:2137
float burst_delay
Definition: weapon.h:533
int is_target_beam_valid(ship_weapon *swp, object *objp)
Definition: aiturret.cpp:842
#define WIF_HOMING
Definition: weapon.h:129
#define timestamp_elapsed(stamp)
Definition: timer.h:102
#define EEOF_BIG_ONLY
Definition: aiturret.cpp:56
#define AICODE_TURRET_DUMBFIRE_ANGLE
Definition: aiturret.cpp:34
int Game_skill_level
Definition: fredstubs.cpp:170
#define TRIGGER_TYPE_TURRET_FIRED
Definition: modelanim.h:38
int model_instance_num
Definition: model.h:1109
int turret_best_weapon
Definition: ship.h:333
float vm_vec_dot(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:312
float accuracy
Definition: beam.h:61
void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
Definition: aiturret.cpp:2219
int objnum
Definition: asteroid.h:43
bool is_object_stealth_ship(object *objp)
Definition: aicode.cpp:879
void vm_vec_scale_sub2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:293
SCP_vector< int > weapon_class
Definition: ship.h:829
#define TRIGGER_TYPE_TURRET_FIRING
Definition: modelanim.h:36
SCP_vector< int > ship_type
Definition: ship.h:827
object * Player_obj
Definition: object.cpp:56
matrix world_to_turret_matrix
Definition: ship.h:386
void flak_pick_range(object *objp, vec3d *firing_pos, vec3d *predicted_target_pos, float weapon_subsys_strength)
Definition: flak.cpp:37
#define WIF_HOMING_JAVELIN
Definition: weapon.h:55
void dc_printf(const char *format,...)
Prints the given char string to the debug console.
Definition: console.cpp:358
int goal_objnum
Definition: ai.h:355
int Ai_firing_enabled
Definition: aicode.cpp:194
int turret_weapon_aggregate_flags(ship_weapon *swp)
Definition: aiturret.cpp:360
#define WIF2_ANTISUBSYSBEAM
Definition: weapon.h:115
#define MAX(a, b)
Definition: pstypes.h:299
int iff_matches_mask(int team, int mask)
Definition: iff_defs.cpp:628
float vel_inherit_amount
Definition: weapon.h:356
uint flags
Definition: object.h:151
float radius
Definition: object.h:154
int object_is_targetable(object *target, ship *viewer)
Definition: aicode.cpp:1643
mission The_mission
missile_obj Missile_obj_list
Definition: weapons.cpp:84
#define WIF2_SMART_SPAWN
Definition: weapon.h:95
int max_turret_ownage_target[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:131
#define WIF2_LOCAL_SSM
Definition: weapon.h:87
int turret_enemy_objnum
Definition: ship.h:337
int model_num
Definition: lua.cpp:4996
char type
Definition: object.h:146
#define MSS_FLAG_USE_MULTIPLE_GUNS
Definition: model.h:116
vec3d vmd_zero_vector
Definition: vecmat.cpp:24
int max_turret_ownage_player[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:132
#define FALSE
Definition: pstypes.h:400
#define wp(p)
Definition: modelsinc.h:69
#define MSS_FLAG_TURRET_MATRIX
Definition: model.h:110
int targeting_priorities[32]
Definition: weapon.h:542
SCP_vector< ai_target_priority > Ai_tp_list
Definition: ship.cpp:396
matrix * orient
Definition: model.h:1112
int burst_counter[MAX_SHIP_PRIMARY_BANKS+MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:156
int myrand()
Definition: systemvars.cpp:102
SCP_vector< ship_type_info > Ship_types
Definition: ship.cpp:168
float rof_scaler
Definition: ship.h:399
unsigned int obj_flags
Definition: ship.h:831
GLuint GLuint end
Definition: Gl.h:1502
void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, vec3d *attack_point, float fov, float weapon_travel_dist)
Definition: aibig.cpp:208
#define fl2f(fl)
Definition: floating.h:38
int last_fired_weapon_info_index
Definition: ship.h:346
#define AIPF_USE_ONLY_SINGLE_FOV_FOR_TURRETS
Definition: ai_profiles.h:48
int burst_shots
Definition: weapon.h:532
#define SW_FLAG_BEAM_FREE
Definition: ship.h:94
#define SSF_NO_SS_TARGETING
Definition: ship.h:281
#define MSS_FLAG_FIRE_ON_NORMAL
Definition: model.h:117
float vm_vec_normalize(vec3d *v)
Definition: vecmat.cpp:460
#define OF_BEAM_PROTECTED
Definition: object.h:115
GLenum GLsizei stride
Definition: Gl.h:1492
bool turret_std_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
Definition: aiturret.cpp:2841
void flak_jitter_aim(vec3d *dir, float dist_to_target, float weapon_subsys_strength, weapon_info *wip)
Definition: flak.cpp:86
int turret_should_fire_aspect(ship_subsys *turret, weapon_info *wip, bool in_sight)
Definition: aiturret.cpp:1804
SCP_vector< int > ship_class
Definition: ship.h:828