]> icculus.org git repositories - taylor/freespace2.git/blob - src/weapon/swarm.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / weapon / swarm.cpp
1 /*
2  * $Logfile: /Freespace2/code/Weapon/Swarm.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module for managing swarm missiles
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:53  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:11  root
14  * Initial import.
15  *
16  * 
17  * 8     5/20/99 7:00p Dave
18  * Added alternate type names for ships. Changed swarm missile table
19  * entries.
20  * 
21  * 7     2/25/99 4:19p Dave
22  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
23  * release build warnings. Added more data to the squad war request and
24  * response packets.
25  * 
26  * 6     1/30/99 1:46p Andsager
27  * Clean up code.  Remove int3, replace warning.
28  * 
29  * 5     1/29/99 2:47p Andsager
30  * Put asset back in for turret_swarm_missiles
31  * 
32  * 4     1/29/99 2:25p Andsager
33  * Added turret_swarm_missiles
34  * 
35  * 3     11/05/98 5:55p Dave
36  * Big pass at reducing #includes
37  * 
38  * 2     10/07/98 10:54a Dave
39  * Initial checkin.
40  * 
41  * 1     10/07/98 10:51a Dave
42  * 
43  * 14    5/03/98 5:52p Chad
44  * AL: Fix bug with WIF_SWARM flag getting cleared from weapon info 
45  * 
46  * 13    3/31/98 5:19p John
47  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
48  * bunch of debug stuff out of player file.  Made model code be able to
49  * unload models and malloc out only however many models are needed.
50  *  
51  * 
52  * 12    2/26/98 10:08p Hoffoss
53  * Rewrote state saving and restoring to fix bugs and simplify the code.
54  * 
55  * 11    2/13/98 3:36p Jim
56  * AL: Fix problem with swarm missiles that occurred at low framerates
57  * 
58  * 10    1/14/98 3:02p Sandeep
59  * Fix bug where parent ship dies in the same frame where swarm missiles
60  * are launched.
61  * 
62  * 9     11/06/97 12:27a Mike
63  * Better avoid behavior.
64  * Modify ai_turn_towards_vector() to take a flag parameter.
65  * 
66  * 8     11/03/97 9:42p Lawrance
67  * make swarm paths more random, fix bug with wild swarmers after homing
68  * target dies
69  * 
70  * 7     8/24/97 10:26p Lawrance
71  * implemented homing swarm missiles
72  * 
73  * 6     8/14/97 12:27a Mike
74  * Changed ai_turn_towards_vector() to take bank_override, had to change
75  * numerous calls to it.
76  * 
77  * 5     8/13/97 4:45p Allender
78  * fixed ship_fire_primary and fire_secondary to not take parameter for
79  * determining whether to count ammo or not.  Fixed countermeasure firing
80  * for multiplayer
81  * 
82  * 4     8/13/97 12:06p Lawrance
83  * make swarm bursts more random
84  * 
85  * 3     8/11/97 9:50a Lawrance
86  * make swarmers more erratic
87  * 
88  * 2     8/10/97 6:16p Lawrance
89  * split off swarm missile code into a separate file
90  *
91  * $NoKeywords: $
92  */
93
94 #include "swarm.h"
95 #include "weapon.h"
96 #include "ship.h"
97 #include "timer.h"
98 #include "freespace.h"  // for Missiontime
99 #include "linklist.h"
100
101 #define SWARM_DIST_OFFSET                       2.0             // distance swarm missile should vary from original path
102 #define SWARM_CONE_LENGTH                       10000.0f        // used to pick a target point far in the distance
103 #define SWARM_CHANGE_DIR_TIME           400             // time to force change in direction of swarm missile
104 #define SWARM_ANGLE_CHANGE                      (4*PI/180)                      // in rad
105 #define SWARM_MISSILE_DELAY             150             // time delay between each swarm missile that is fired
106 #define SWARM_TIME_VARIANCE             100             // max time variance when deciding when to change swarm missile course
107
108 #define SWARM_DIST_STOP_SWARMING        300
109
110 #define TURRET_SWARM_VALIDITY_CHECKTIME 5000    // number of ms between checks on turret_swam_info checks
111
112 #define SWARM_USED                                              (1<<0)
113 #define SWARM_POSITIVE_PATH                     (1<<1)
114
115 typedef struct swarm_info {
116         int             flags;
117         int             change_timestamp;
118         vector  original_target;
119         vector  new_target;
120         vector  circle_rvec, circle_uvec;
121         vector  last_offset;
122         uint            change_count;           
123         int             path_num;                       // which path swarm missile is currently following
124         int             homing_objnum;          // object number that swarm missile is homing on, -1 if not homing
125         int             change_time;            // when swarm missile should next update direction, based on missile speed
126         float           angle_offset;
127         float           last_dist;                      // last distance to target
128 } swarm_info;
129
130 typedef struct turret_swarm_info {
131         int                             flags;
132         int                             num_to_launch;
133         int                             parent_objnum;
134         int                             parent_sig;
135         int                             target_objnum;
136         int                             target_sig;
137         ship_subsys*    turret;
138         ship_subsys*    target_subsys;
139         int                             time_to_fire;
140 } turret_swarm_info;
141
142
143 #define MAX_SWARM_MISSILES      50
144 swarm_info      Swarm_missiles[MAX_SWARM_MISSILES];
145
146 #define MAX_TURRET_SWARM_INFO   50
147 turret_swarm_info Turret_swarm_info[MAX_TURRET_SWARM_INFO];
148
149 int Turret_swarm_validity_next_check_time;
150
151 // ------------------------------------------------------------------
152 // swarm_level_init()
153 //
154 // Called at the start of each new mission
155 //
156 void swarm_level_init()
157 {
158         int                                     i;
159         swarm_info                      *swarmp;
160         turret_swarm_info       *tswarmp;
161
162         for ( i = 0; i < MAX_SWARM_MISSILES; i++ ) {
163                 swarmp = &Swarm_missiles[i];
164                 swarmp->flags = 0;
165                 swarmp->change_timestamp = 1;
166                 swarmp->path_num = -1;
167         }
168
169         for (i=0; i<MAX_TURRET_SWARM_INFO; i++) {
170                 tswarmp = &Turret_swarm_info[i];
171                 tswarmp->flags = 0;
172                 tswarmp->num_to_launch = 0;
173                 tswarmp->parent_objnum = -1;
174                 tswarmp->parent_sig       = -1;
175                 tswarmp->target_objnum = -1;
176                 tswarmp->target_sig       = -1;
177                 tswarmp->turret           = NULL;
178                 tswarmp->target_subsys = NULL;
179                 tswarmp->time_to_fire  = 0;
180         }
181
182         Turret_swarm_validity_next_check_time = timestamp(TURRET_SWARM_VALIDITY_CHECKTIME);
183 }
184
185 // ------------------------------------------------------------------
186 // swarm_maybe_fire_missile()
187 //
188 // Check if there are any swarm missiles to fire, and if enough time
189 // has elapsed since last one fired, go ahead and fire it.
190 //
191 // This is called once per ship frame in ship_move()
192 //
193 void swarm_maybe_fire_missile(int shipnum)
194 {
195         ship                    *sp;
196         ship_weapon *swp;
197         int                     weapon_info_index;
198
199         Assert(shipnum >= 0 && shipnum < MAX_SHIPS );
200         sp = &Ships[shipnum];
201
202         if ( sp->num_swarm_missiles_to_fire <= 0 )
203                 return;
204
205         swp = &sp->weapons;
206         if ( swp->current_secondary_bank == -1 ) {
207                 sp->num_swarm_missiles_to_fire = 0;
208                 return;
209         }
210
211         weapon_info_index = swp->secondary_bank_weapons[swp->current_secondary_bank];
212         Assert( weapon_info_index >= 0 && weapon_info_index < MAX_WEAPON_TYPES );
213
214         // if current secondary bank is not a swarm missile, return
215         if ( !(Weapon_info[weapon_info_index].wi_flags & WIF_SWARM) ) {
216                 sp->num_swarm_missiles_to_fire = 0;
217                 return;
218         }
219
220         if ( timestamp_elapsed(sp->next_swarm_fire) ) {
221                 sp->next_swarm_fire = timestamp(SWARM_MISSILE_DELAY);
222                 ship_fire_secondary( &Objects[sp->objnum], 1 );
223                 sp->num_swarm_missiles_to_fire--;
224         }
225 }
226
227 // ------------------------------------------------------------------
228 // swarm_create()
229 //
230 //      Get a free swarm missile entry, and initialize the struct members
231 //
232 int swarm_create()
233 {
234         int                     i;
235         swarm_info      *swarmp = NULL;
236
237         for ( i = 0; i < MAX_SWARM_MISSILES; i++ ) {
238                 swarmp = &Swarm_missiles[i];
239                 if ( !(swarmp->flags & SWARM_USED) ) 
240                         break;          
241         }
242
243         if ( i >= MAX_SWARM_MISSILES ) {
244                 nprintf(("Warning","No more swarm missiles are available\n"));
245                 return -1;
246         }
247
248         swarmp->flags = 0;
249         swarmp->change_timestamp = 1;
250         swarmp->path_num = -1;
251         swarmp->homing_objnum = -1;
252
253         swarmp->flags |= SWARM_USED;
254         return i;
255 }
256
257 // ------------------------------------------------------------------
258 // swarm_delete()
259 //
260 //
261 void swarm_delete(int i)
262 {
263         swarm_info      *swarmp;
264
265         Assert(i >= 0 && i < MAX_SWARM_MISSILES);
266         swarmp = &Swarm_missiles[i];
267
268         if ( !(swarmp->flags & SWARM_USED) ) {
269                 Int3(); // tried to delete a swarm missile that didn't exist, get Alan
270         }
271
272         swarmp->flags = 0;
273 }
274
275 // ------------------------------------------------------------------
276 // swarm_update_direction()
277 //
278 //      Check if we want to update the direction of a swarm missile.
279 //
280 void swarm_update_direction(object *objp, float frametime)
281 {
282         weapon_info     *wip;
283         weapon          *wp;
284         object          *hobjp;
285         swarm_info      *swarmp;
286         vector          obj_to_target;
287         float                   vel, target_dist, radius, missile_speed, missile_dist;
288         physics_info    *pi;
289
290         Assert(objp->instance >= 0 && objp->instance < MAX_WEAPONS);
291
292         wp = &Weapons[objp->instance];
293
294         if (wp->swarm_index == -1) {
295                 return;
296         }
297
298         wip = &Weapon_info[wp->weapon_info_index];
299         hobjp = wp->homing_object;
300         pi = &Objects[wp->objnum].phys_info;
301         swarmp = &Swarm_missiles[wp->swarm_index];
302
303         // check if homing is lost.. if it is then get a new path to move swarm missile along
304         if ( swarmp->homing_objnum != -1 && hobjp == &obj_used_list ) {
305                 swarmp->change_timestamp = 1;
306                 swarmp->path_num = -1;
307                 swarmp->homing_objnum = -1;
308         }
309
310         if ( hobjp != &obj_used_list ) {
311                 swarmp->homing_objnum = OBJ_INDEX(hobjp);
312         }
313
314         if ( timestamp_elapsed(swarmp->change_timestamp) ) {
315
316                 if ( swarmp->path_num == -1 ) {
317                         if ( Objects[objp->parent].type != OBJ_SHIP ) {
318                                 //AL: parent ship died... so just pick some random paths
319                                 swarmp->path_num        = myrand()%4;
320                         } else {
321                                 ship *parent_shipp;
322                                 parent_shipp = &Ships[Objects[objp->parent].instance];
323                                 swarmp->path_num = (parent_shipp->next_swarm_path++)%4;
324
325                                 if ( parent_shipp->next_swarm_path%4 == 0 ) {
326                                         swarmp->flags ^= SWARM_POSITIVE_PATH;
327                                 }
328                         }
329
330                         vm_vec_scale_add(&swarmp->original_target, &objp->pos, &objp->orient.fvec, SWARM_CONE_LENGTH);
331                         swarmp->circle_rvec = objp->orient.rvec;
332                         swarmp->circle_uvec = objp->orient.uvec;
333
334                         swarmp->change_count = 1;
335                         swarmp->change_time = fl2i(SWARM_CHANGE_DIR_TIME + SWARM_TIME_VARIANCE*(frand() - 0.5f) * 2);
336
337                         vm_vec_zero(&swarmp->last_offset);
338
339                         missile_speed = pi->speed;
340                         missile_dist    = missile_speed * swarmp->change_time/1000.0f;
341                         if ( missile_dist < SWARM_DIST_OFFSET ) {
342                                 missile_dist=i2fl(SWARM_DIST_OFFSET);
343                         }
344                         swarmp->angle_offset = (float)(asin(SWARM_DIST_OFFSET / missile_dist));
345                         Assert(!_isnan(swarmp->angle_offset) );
346                 }
347
348                 swarmp->change_timestamp = timestamp(swarmp->change_time);
349
350                 // check if swarm missile is homing, if so need to calculate a new target pos to turn towards
351                 if ( hobjp != &obj_used_list && f2fl(Missiontime - wp->creation_time) > 0.5f ) {
352                         swarmp->original_target = wp->homing_pos;
353
354                         // Calculate a rvec and uvec that will determine the displacement from the
355                         // intended target.  Use crossprod to generate a right vector, from the missile
356                         // up vector and the vector connecting missile to the homing object.
357                         swarmp->circle_uvec = objp->orient.uvec;
358                         swarmp->circle_rvec = objp->orient.rvec;
359
360                         missile_speed = pi->speed;
361                         missile_dist = missile_speed * swarmp->change_time/1000.0f;
362                         swarmp->angle_offset = (float)(asin(SWARM_DIST_OFFSET / missile_dist));
363                         Assert(!_isnan(swarmp->angle_offset) );
364                 }
365
366                 vm_vec_sub(&obj_to_target, &swarmp->original_target, &objp->pos);
367                 target_dist = vm_vec_mag_quick(&obj_to_target);
368                 swarmp->last_dist = target_dist;
369
370                 // If homing swarm missile is close to target, let missile home in on original target
371                 if ( target_dist < SWARM_DIST_STOP_SWARMING ) {
372                         swarmp->new_target = swarmp->original_target;
373                         goto swarm_new_target_calced;
374                 }
375
376                 radius = (float)tan(swarmp->angle_offset) * target_dist;
377                 vector rvec_component, uvec_component;
378
379                 swarmp->change_count++;
380                 if ( swarmp->change_count > 2 ) {
381                         swarmp->flags ^= SWARM_POSITIVE_PATH;
382                         swarmp->change_count = 0;
383                 }
384
385                 // pick a new path number to follow once at center
386                 if ( swarmp->change_count == 1 ) {
387                         swarmp->path_num = swarmp->path_num + myrand()%3;
388                         if ( swarmp->path_num > 3 ) {
389                                 swarmp->path_num = 0;
390                         }
391                 }
392
393                 vm_vec_zero(&rvec_component);
394                 vm_vec_zero(&uvec_component);
395
396                 switch ( swarmp->path_num ) {
397                         case 0: // straight up and down
398                                 if ( swarmp->flags & SWARM_POSITIVE_PATH )
399                                         vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius);
400                                 else
401                                         vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius);
402                                 break;
403
404                         case 1: // left/right
405                                 if ( swarmp->flags & SWARM_POSITIVE_PATH )
406                                         vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius);
407                                 else
408                                         vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius);
409                                 break;
410
411                         case 2: // top/right - bottom/left
412                                 if ( swarmp->flags & SWARM_POSITIVE_PATH ) {
413                                         vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius);
414                                         vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius);
415                                 }
416                                 else {
417                                         vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius);
418                                         vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius);
419                                 }
420                                 break;
421
422                         case 3: // top-left - bottom/right
423                                 if ( swarmp->flags & SWARM_POSITIVE_PATH ) {
424                                         vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius);
425                                         vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius);
426                                 }
427                                 else {
428                                         vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius);
429                                         vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius);
430                                 }
431                                 break;
432                         default:
433                                 Int3();
434                                 break;
435                 }
436
437                 swarmp->new_target = swarmp->original_target;
438                 vm_vec_zero(&swarmp->last_offset);
439                 vm_vec_add(&swarmp->last_offset, &uvec_component, &rvec_component);
440                 vm_vec_add2(&swarmp->new_target, &swarmp->last_offset);
441         }
442         else {
443                 if ( hobjp != &obj_used_list && f2fl(Missiontime - wp->creation_time) > 0.5f ) {
444
445                         swarmp->new_target = swarmp->original_target;
446                         if ( swarmp->last_dist < SWARM_DIST_STOP_SWARMING ) {
447                                 swarmp->new_target = wp->homing_pos;
448                                 goto swarm_new_target_calced;
449                         }
450
451                         vm_vec_add2(&swarmp->new_target, &swarmp->last_offset);
452                 }
453         }
454
455         swarm_new_target_calced:
456
457         ai_turn_towards_vector(&swarmp->new_target, objp, frametime, wip->turn_time, NULL, NULL, 0.0f, 0);
458         vel = vm_vec_mag(&objp->phys_info.desired_vel);
459         vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.fvec, vel);
460 }
461
462 // ------------------------------------------------------------------
463 // turret_swarm_create()
464 //
465 //      Get a free swarm missile entry, and initialize the struct members
466 //
467 int turret_swarm_create()
468 {
469         int i;
470         turret_swarm_info       *tswarmp = NULL;
471
472         for (i=0; i<MAX_TURRET_SWARM_INFO; i++) {
473                 tswarmp = &Turret_swarm_info[i];
474                 if ( !(tswarmp->flags & SWARM_USED) ) 
475                         break;          
476         }
477
478         if ( i >= MAX_TURRET_SWARM_INFO ) {
479                 nprintf(("Warning","No more turret swarm info slots are available\n"));
480                 Int3();
481                 return -1;
482         }
483
484         tswarmp->flags = 0;
485         tswarmp->num_to_launch = 0;
486         tswarmp->parent_objnum = -1;
487         tswarmp->parent_sig = -1;
488         tswarmp->target_objnum = -1;
489         tswarmp->target_sig = -1;
490         tswarmp->turret = NULL;
491         tswarmp->target_subsys = NULL;
492         tswarmp->time_to_fire = 0;
493
494         tswarmp->flags |= SWARM_USED;
495         return i;
496 }
497
498 // ------------------------------------------------------------------
499 // turret_swarm_delete()
500 //
501 void turret_swarm_delete(int i)
502 {
503         turret_swarm_info               *tswarmp;
504
505         Assert(i >= 0 && i < MAX_TURRET_SWARM_INFO);
506         tswarmp = &Turret_swarm_info[i];
507
508         if ( !(tswarmp->flags & SWARM_USED) ) {
509                 Int3(); // tried to delete a swarm missile that didn't exist, get DaveA
510         }
511
512         tswarmp->flags = 0;
513 }
514
515 // Set up turret swarm info struct
516 void turret_swarm_set_up_info(int parent_objnum, ship_subsys *turret, int turret_weapon_class)
517 {
518         turret_swarm_info       *tsi;
519         object *parent_obj, *target_obj;
520         ship *shipp;
521         int tsi_index;
522         weapon_info *wip;
523
524         // weapon info pointer
525         Assert((turret_weapon_class >= 0) && (turret_weapon_class < Num_weapon_types));
526         if((turret_weapon_class < 0) || (turret_weapon_class >= Num_weapon_types)){
527                 return;
528         }
529         wip = &Weapon_info[turret_weapon_class];
530
531         // get ship pointer     
532         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
533         if((parent_objnum < 0) || (parent_objnum >= MAX_OBJECTS)){
534                 return;
535         }
536         parent_obj = &Objects[parent_objnum];
537         Assert(parent_obj->type == OBJ_SHIP);
538         shipp = &Ships[parent_obj->instance];
539         Assert((turret->turret_enemy_objnum >= 0) && (turret->turret_enemy_objnum < MAX_OBJECTS));
540         if((turret->turret_enemy_objnum < 0) || (turret->turret_enemy_objnum >= MAX_OBJECTS)){
541                 return;
542         }
543         target_obj = &Objects[turret->turret_enemy_objnum];
544
545         // valid swarm weapon
546         Assert((wip->wi_flags & WIF_SWARM) && (wip->swarm_count > 0));
547         if(!(wip->wi_flags & WIF_SWARM) || (wip->swarm_count <= 0)){
548                 return;
549         }
550
551         // get turret_swarm_info
552         tsi_index = turret_swarm_create();
553         if (tsi_index == -1) {
554                 return;
555         }       
556
557         // set turret to point to tsi
558         tsi = &Turret_swarm_info[tsi_index];
559         if (turret->turret_swarm_info_index != -1) {
560                 // overlapping firing intervals..., stop old and start new
561                 mprintf(("Overlapping turret swarm firing intervals"));
562                 turret_swarm_delete(turret->turret_swarm_info_index);
563                 turret->turret_swarm_info_index = -1;
564                 shipp->num_turret_swarm_info--;
565         }
566         turret->turret_swarm_info_index = tsi_index;
567
568         // increment ship tsi counter
569         shipp->num_turret_swarm_info++;
570
571         // make sure time is sufficient to launch all the missiles before next volley
572 #ifndef NDEBUG  
573         Assert(wip->swarm_count * SWARM_MISSILE_DELAY < wip->fire_wait * 1000.0f);
574 #endif
575
576         // initialize tsi
577         tsi->num_to_launch = wip->swarm_count;
578         tsi->parent_objnum = parent_objnum;
579         tsi->parent_sig    = parent_obj->signature;
580         tsi->target_objnum = turret->turret_enemy_objnum;
581         tsi->target_sig    = target_obj->signature;
582         tsi->turret = turret;
583         tsi->target_subsys = turret->targeted_subsys;
584         tsi->time_to_fire = 1;  // first missile next frame
585 }
586
587 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys);
588 // check if ship has turret ready to fire swarm type missiles
589 void turret_swarm_maybe_fire_missile(int shipnum)
590 {
591         ship *shipp = &Ships[shipnum];
592         ship_subsys *subsys;
593         turret_swarm_info *tsi;
594         object *parent_obj, *target_obj;
595         int target_objnum, num_turret_swarm_turrets_left;
596
597         // check if ship has any turrets ready to fire
598         if (shipp->num_turret_swarm_info <= 0) {
599                 Assert(shipp->num_turret_swarm_info == 0);
600                 return;
601         }
602
603         // ship obj which has fired turret swarm missiles
604         parent_obj = &Objects[shipp->objnum];
605         num_turret_swarm_turrets_left = shipp->num_turret_swarm_info;
606
607         // search ship subsystems for turrets with valid turret_swarm_info_index
608         for (subsys = GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys)) {
609                 if (subsys->turret_swarm_info_index != -1) {
610
611                         num_turret_swarm_turrets_left--;
612                         Assert(num_turret_swarm_turrets_left >= 0);
613
614                         // get turret_swarm_info
615                         Assert( (subsys->turret_swarm_info_index >= 0) && (subsys->turret_swarm_info_index < MAX_TURRET_SWARM_INFO) );
616                         tsi = &Turret_swarm_info[subsys->turret_swarm_info_index];
617
618                         // check if parent ship is valid (via signature)
619                         if ( (tsi->parent_sig == parent_obj->signature) ) {
620
621                                 // make sure we have the right turret.
622                                 Assert(tsi->turret == subsys);
623
624                                 // check if time to fire
625                                 if (timestamp_elapsed(tsi->time_to_fire)) {
626                                         Assert(tsi->num_to_launch > 0);
627
628                                         // check target still alive
629                                         target_objnum = -1;
630                                         if (tsi->target_objnum > -1) {
631                                                 target_obj= &Objects[tsi->target_objnum];
632
633                                                 if (target_obj->signature == tsi->target_sig) {
634                                                         target_objnum = tsi->target_objnum;
635                                                 } else {
636                                                         // poor target, it died
637                                                         tsi->target_objnum = -1;
638                                                 }
639                                         }
640
641                                         // make sure turret is still alive and fire swarmer
642                                         if (subsys->current_hits > 0) {
643                                                 turret_swarm_fire_from_turret(tsi->turret, tsi->parent_objnum, target_objnum, tsi->target_subsys);
644                                         }
645
646                                         // set timestamp
647                                         tsi->time_to_fire = timestamp(SWARM_MISSILE_DELAY);
648
649                                         // do book keeping
650                                         tsi->num_to_launch--;
651
652                                         if (tsi->num_to_launch == 0) {
653                                                 shipp->num_turret_swarm_info--;
654                                                 turret_swarm_delete(subsys->turret_swarm_info_index);
655                                                 subsys->turret_swarm_info_index = -1;
656                                         }
657                                 }
658                         } else {
659                                 Warning(LOCATION,       "Found turret swarm info on ship: %s with turret: %s, but signature does not match.", shipp->ship_name, subsys->system_info->subobj_name);
660                                 shipp->num_turret_swarm_info--;
661                                 turret_swarm_delete(subsys->turret_swarm_info_index);
662                                 subsys->turret_swarm_info_index = -1;
663                         }
664                 }
665         }
666
667         Assert(num_turret_swarm_turrets_left == 0);
668 }
669
670 // check Turret_swarm_info for info that are invalid - ie, ships died while firing.
671 void turret_swarm_check_validity()
672 {
673         int i;
674         turret_swarm_info *tswarmp;
675         object *ship_obj;
676
677         if (timestamp_elapsed(Turret_swarm_validity_next_check_time)) {
678
679                 // reset timestamp
680                 Turret_swarm_validity_next_check_time = timestamp(TURRET_SWARM_VALIDITY_CHECKTIME);
681
682                 // go through all Turret_swarm_info, check obj and obj->signature
683                 for (i=0; i<MAX_TURRET_SWARM_INFO; i++) {
684                         tswarmp = &Turret_swarm_info[i];
685
686                         if (tswarmp->flags & SWARM_USED) {
687                                 ship_obj = &Objects[tswarmp->parent_objnum];
688                                 if (ship_obj->type == OBJ_SHIP) {
689                                         if (ship_obj->signature == tswarmp->parent_sig) {
690                                                 continue;
691                                         }
692                                 }
693
694                                 turret_swarm_delete(i);
695                         }
696                 }
697         }
698 }
699