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