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