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