]> icculus.org git repositories - taylor/freespace2.git/blob - src/weapon/beam.cpp
implemented osregistry
[taylor/freespace2.git] / src / weapon / beam.cpp
1 /*
2  * $Logfile: /Freespace2/code/Weapon/Beam.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * all sorts of cool stuff about ships
8  *
9  * $Log$
10  * Revision 1.3  2002/06/02 00:31:36  relnev
11  * implemented osregistry
12  *
13  * Revision 1.2  2002/05/07 03:16:53  theoddone33
14  * The Great Newline Fix
15  *
16  * Revision 1.1.1.1  2002/05/03 03:28:11  root
17  * Initial import.
18  *
19  * 
20  * 68    9/09/99 11:40p Dave
21  * Handle an Assert() in beam code. Added supernova sounds. Play the right
22  * 2 end movies properly, based upon what the player did in the mission.
23  * 
24  * 67    9/09/99 2:36p Mikek
25  * Put back in the "1.0f +" in BEAM_TYPE_A aiming.  Not the best way to
26  * correct this.
27  * 
28  * 65    9/08/99 10:29p Dave
29  * Make beam sound pausing and unpausing much safer.
30  * 
31  * 64    9/06/99 12:46a Andsager
32  * Add weapon_explosion_ani LOD
33  * 
34  * 63    9/03/99 5:12p Mikek
35  * Change miss_factor code, making it a lot more likely for beams to miss
36  * and also making them increasingly likely to miss with each subsequent
37  * shot in the multi-shot burst.
38  * 
39  * 62    8/30/99 5:01p Dave
40  * Made d3d do less state changing in the nebula. Use new chat server for
41  * PXO.
42  * 
43  * 61    8/28/99 7:29p Dave
44  * Fixed wingmen persona messaging. Make sure locked turrets don't count
45  * towards the # attacking a player.
46  * 
47  * 60    8/27/99 9:07p Dave
48  * LOD explosions. Improved beam weapon accuracy.
49  * 
50  * 59    8/26/99 10:15a Dave
51  * Don't apply beam whacks to docked ships.
52  * 
53  * 58    7/31/99 1:16p Dave
54  * Use larger font for 1024 HUD flash text box. Make beam weapons aware of
55  * weapon subsystem damage on firing ship.
56  * 
57  * 57    7/22/99 4:00p Dave
58  * Fixed beam weapon muzzle glow rendering. Externalized hud shield info.
59  * 
60  * 56    7/19/99 7:20p Dave
61  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
62  * pre-rendering.
63  * 
64  * 55    7/15/99 9:20a Andsager
65  * FS2_DEMO initial checkin
66  * 
67  * 54    7/09/99 5:54p Dave
68  * Seperated cruiser types into individual types. Added tons of new
69  * briefing icons. Campaign screen.
70  * 
71  * 53    7/08/99 10:53a Dave
72  * New multiplayer interpolation scheme. Not 100% done yet, but still
73  * better than the old way.
74  * 
75  * 52    7/02/99 10:51p Dave
76  * Limit friendly beam fire damage. :(
77  * 
78  * 51    7/01/99 11:44a Dave
79  * Updated object sound system to allow multiple obj sounds per ship.
80  * Added hit-by-beam sound. Added killed by beam sound.
81  * 
82  * 50    6/29/99 7:39p Dave
83  * Lots of small bug fixes.
84  * 
85  * 49    6/29/99 2:53p Dave
86  * Re-enabled beam lighting.
87  * 
88  * 48    6/25/99 3:04p Dave
89  * Removed Int3().
90  * 
91  * 47    6/24/99 3:00p Dave
92  * Stupid bug.
93  * 
94  * 46    6/23/99 4:49p Dave
95  * Temporarily removed beam lighting.
96  * 
97  * 45    6/21/99 7:25p Dave
98  * netplayer pain packet. Added type E unmoving beams.
99  * 
100  * 44    6/18/99 5:16p Dave
101  * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
102  * dialog to PXO screen.
103  * 
104  * 43    6/14/99 10:45a Dave
105  * Made beam weapons specify accuracy by skill level in the weapons.tbl
106  * 
107  * 42    6/09/99 2:55p Andsager
108  * Allow multiple asteroid subtypes (of large, medium, small) and follow
109  * family.
110  * 
111  * 41    6/08/99 6:02p Jamesa
112  * Handle case where type B beam start and end directions are really
113  * close.
114  * 
115  * 40    6/04/99 2:16p Dave
116  * Put in shrink effect for beam weapons.
117  * 
118  * 39    5/14/99 11:47a Andsager
119  * Added  beam_get_weapon_info_index(object *bm)
120  * 
121  * 38    5/12/99 10:43a Andsager
122  * Increase max beam length
123  * 
124  * 37    5/08/99 8:25p Dave
125  * Upped object pairs. First run of nebula lightning.
126  * 
127  * 36    5/05/99 9:02p Dave
128  * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added
129  * rotations, tweaked values, made bitmap selection more random). Fixed
130  * D3D beam weapon clipping problem. Added D3d frame dumping.
131  * 
132  * 35    5/03/99 9:07a Dave
133  * Pirate Bob. Changed beam test code a bit.
134  * 
135  * 34    4/27/99 12:16a Dave
136  * Fixed beam weapon muzzle glow problem. Fixed premature timeout on the
137  * pxo server list screen. Fixed secondary firing for hosts on a
138  * standalone. Fixed wacky multiplayer weapon "shuddering" problem.
139  * 
140  * 33    4/25/99 6:12p Johnson
141  * Fixed bug where multi-shot beams were getting the shot count from the
142  * wrong place.
143  * 
144  * 32    4/25/99 3:36p Dave
145  * Fixed nebula table code. Tweaked beam weapon explosion stuff.
146  * 
147  * 31    4/23/99 2:33p Johnson
148  * Allow beams to shoot at missiles properly.
149  * 
150  * 30    4/23/99 12:01p Johnson
151  * Added SIF_HUGE_SHIP
152  * 
153  * 29    4/22/99 11:06p Dave
154  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
155  * now is to tweak and fix bugs as they come up. No new beam weapon
156  * features.
157  * 
158  * 28    4/21/99 6:15p Dave
159  * Did some serious housecleaning in the beam code. Made it ready to go
160  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
161  * a handy macro for recalculating collision pairs for a given object.
162  * 
163  * 27    4/20/99 6:39p Dave
164  * Almost done with artillery targeting. Added support for downloading
165  * images on the PXO screen.
166  * 
167  * 26    4/19/99 11:01p Dave
168  * More sophisticated targeting laser support. Temporary checkin.
169  * 
170  * 25    4/19/99 4:54p Johnson
171  * Removed infinite loop from beam_render_all()   oops :)
172  * 
173  * 24    4/16/99 5:54p Dave
174  * Support for on/off style "stream" weapons. Real early support for
175  * target-painting lasers.
176  * 
177  * 23    4/04/99 2:13p Dave
178  * Put in area effect beam weapons. May be a bit too expensive though.
179  * Dave
180  * 22    4/02/99 9:55a Dave
181  * Added a few more options in the weapons.tbl for beam weapons. Attempt
182  * at putting "pain" packets into multiplayer.
183  * 
184  * 21    3/31/99 8:24p Dave
185  * Beefed up all kinds of stuff, incluging beam weapons, nebula effects
186  * and background nebulae. Added per-ship non-dimming pixel colors.
187  * 
188  * 20    3/08/99 7:03p Dave
189  * First run of new object update system. Looks very promising.
190  * 
191  * 19    3/04/99 6:09p Dave
192  * Added in sexpressions for firing beams and checking for if a ship is
193  * tagged.
194  * 
195  * 18    3/02/99 9:25p Dave
196  * Added a bunch of model rendering debug code. Started work on fixing
197  * beam weapon wacky firing.
198  * 
199  * 17    2/21/99 1:48p Dave
200  * Some code for monitoring datarate for multiplayer in detail.
201  * 
202  * 16    2/11/99 3:08p Dave
203  * PXO refresh button. Very preliminary squad war support.
204  * 
205  * 15    2/05/99 3:23p Mattf
206  * Made beams a little more forgiving when checking object types.
207  * 
208  * 14    2/05/99 12:52p Dave
209  * Fixed Glide nondarkening textures.
210  * 
211  * 13    2/04/99 6:29p Dave
212  * First full working rev of FS2 PXO support.  Fixed Glide lighting
213  * problems.
214  * 
215  * 12    1/30/99 9:02p Dave
216  * Fixed neb stuff.
217  * 
218  * 11    1/30/99 1:29a Dave
219  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
220  * screen.  Fixed beam weapon death messages.
221  * 
222  * 10    1/29/99 7:08p Johnson
223  * Put in fix for beam weapons which are on dying ships.
224  * 
225  * 9     1/29/99 12:47a Dave
226  * Put in sounds for beam weapon. A bunch of interface screens (tech
227  * database stuff).
228  * 
229  * 8     1/27/99 9:56a Dave
230  * Temporary checkin of beam weapons for Dan to make cool sounds.
231  * 
232  * 7     1/24/99 11:37p Dave
233  * First full rev of beam weapons. Very customizable. Removed some bogus
234  * Int3()'s in low level net code.
235  * 
236  * 6     1/21/99 2:06p Dave
237  * Final checkin for multiplayer testing.
238  * 
239  * 5     1/21/99 10:45a Dave
240  * More beam weapon stuff. Put in warmdown time.
241  * 
242  * 4     1/14/99 12:48a Dave
243  * Todo list bug fixes. Made a pass at putting briefing icons back into
244  * FRED. Sort of works :(
245  * 
246  * 3     1/12/99 12:53a Dave
247  * More work on beam weapons - made collision detection very efficient -
248  * collide against all object types properly - made 3 movement types
249  * smooth. Put in test code to check for possible non-darkening pixels on
250  * object textures.
251  * 
252  * 2     1/08/99 2:08p Dave
253  * Fixed software rendering for pofview. Super early support for AWACS and
254  * beam weapons.
255  * 
256  * 
257  * $NoKeywords: $
258  */
259
260 #include "linklist.h"
261 #include "object.h"
262 #include "objcollide.h"
263 #include "ship.h"
264 #include "freespace.h"
265 #include "3d.h"
266 #include "model.h"
267 #include "alphacolors.h"
268 #include "timer.h"
269 #include "fireballs.h"
270 #include "debris.h"
271 #include "asteroid.h"
272 #include "multi.h"
273 #include "multimsgs.h"
274 #include "bmpman.h"
275 #include "particle.h"
276 #include "shiphit.h"
277 #include "flak.h"
278 #include "3d.h"
279 #include "gamesnd.h"
280 #include "beam.h"
281 #include "hudmessage.h"
282 #include "key.h"
283 #include "lighting.h"
284
285 // ------------------------------------------------------------------------------------------------
286 // BEAM WEAPON DEFINES/VARS
287 //
288
289 // use this to extend a beam to "infinity"
290 #define BEAM_FAR_LENGTH                         30000.0f
291
292 // this is the constant which defines when a beam is an "area" beam. meaning, when we switch on sphereline checking and when 
293 // a beam gets "stopped" by an object. It is a percentage of the object radius which the beam must be wider than
294 #define BEAM_AREA_PERCENT                       0.4f
295
296 // randomness factor - all beam weapon aiming is adjusted by +/- some factor within this range
297 #define BEAM_RANDOM_FACTOR                      0.4f
298
299 #define MAX_BEAMS                                               40
300 #define BEAM_DAMAGE_TIME                        170                     // apply damage 
301 #define MAX_SHOT_POINTS                         30
302 #define SHOT_POINT_TIME                         200                     // 5 arcs a second
303
304 #define TOOLTIME                                                1500.0f
305
306 // max # of collisions we'll allow per frame
307 #define MAX_FRAME_COLLISIONS            5
308
309 // beam flag defines
310 #define BF_SAFETY                                               (1<<0)          // if this is set, don't collide or render for this frame. lifetime still increases though
311 #define BF_SHRINK                                               (1<<1)          // if this is set, the beam is in the warmdown phase
312
313 // beam struct (the actual weapon/object)
314 typedef struct beam {
315         // low-level data
316         int             objnum;                                 // our own objnum
317         int             weapon_info_index;
318         int             sig;                                            // signature for the shooting object
319         object  *objp;                                  // the shooting object (who owns the turret that I am being fired from)
320         object  *target;                                        // target object
321         ship_subsys *target_subsys;     // targeted subsys
322         int             target_sig;                             // target sig
323         ship_subsys *subsys;                            // subsys its being fired from
324         beam            *next, *prev;                   // link list stuff
325         vector  targeting_laser_offset;
326         int             framecount;                             // how many frames the beam has been active
327         int             flags;                                  // see BF_* defines     
328         float           shrink;                                 // shrink factor        
329
330         // beam info    
331         int             warmup_stamp;                   // timestamp for "warming up"
332         int             warmdown_stamp;         // timestamp for "warming down"
333         int             type;                                           // see BEAM_TYPE_* defines in beam.h
334         float           life_left;                              // in seconds
335         float           life_total;                             // total life   
336         // this vector has very special meaning. BEFORE performing collision checks, it basically implies a "direction". meaning
337         // the vector between it and last_start is where the beam will be aiming. AFTER performing collision checks, it is the
338         // literal world collision point on the object (or meaningless, if we hit nothing). The function beam_move_all_pre() is
339         // responsible for then setting it up pre-collision time
340         vector  last_shot;                              
341         vector  last_start;                             
342         int             shot_index;                             // for type D beam weapons
343
344         // recent collisions
345         beam_collision r_collisions[MAX_FRAME_COLLISIONS];                                      // recent collisions
346         int r_collision_count;                                                                                                          // # of recent collisions
347
348         // collision info for this frame
349         beam_collision f_collisions[MAX_FRAME_COLLISIONS];                                      // collisions for the current frame
350         int f_collision_count;                                                                                                          // # of collisions we recorded this frame
351
352         // looping sound info, HANDLE
353         int             beam_sound_loop;                // -1 if none
354
355         // team 
356         char            team;
357
358         // exactly how the beam will behave. by passing this is multiplayer from server to client, we can ensure that
359         // everything looks the same
360         beam_info binfo;
361 } beam;
362
363 beam Beams[MAX_BEAMS];                          // all beams
364 beam Beam_free_list;                                    // free beams
365 beam Beam_used_list;                                    // used beams
366 int Beam_count = 0;                                     // how many beams are in use
367
368 // octant indices. These are "good" pairs of octants to use for beam target
369 #define BEAM_NUM_GOOD_OCTANTS                   8
370 int Beam_good_slash_octants[BEAM_NUM_GOOD_OCTANTS][4] = {               
371         { 2, 5, 1, 0 },                                 // octant, octant, min/max pt, min/max pt
372         { 7, 0, 1, 0 },
373         { 1, 6, 1, 0 },                                 
374         { 6, 1, 0, 1 },
375         { 5, 2, 0, 1 }, 
376         { 0, 7, 0, 1 },         
377         { 7, 1, 1, 0 },
378         { 6, 0, 1, 0 },
379 };
380 int Beam_good_shot_octants[BEAM_NUM_GOOD_OCTANTS][4] = {                
381         { 5, 0, 1, 0 },                                 // octant, octant, min/max pt, min/max pt
382         { 7, 2, 1, 0 },
383         { 7, 1, 1, 0 },                                 
384         { 6, 0, 1, 0 },
385         { 7, 3, 1, 0 }, 
386         { 6, 2, 1, 0 },
387         { 5, 1, 1, 0 },
388         { 4, 0, 1, 0 },
389 };
390
391 // damage cap values for friendly beam fire
392 float Beam_friendly_cap[NUM_SKILL_LEVELS] = { 0.0f, 5.0f, 10.0f, 20.0f, 30.0f };
393
394 // beam lighting effects
395 int Beam_lighting = 1;
396
397 // debug stuff - keep track of how many collision tests we perform a second and how many we toss a second
398 #define BEAM_TEST_STAMP_TIME            4000    // every 4 seconds
399 int Beam_test_stamp = -1;
400 int Beam_test_ints = 0;
401 int Beam_test_ship = 0;
402 int Beam_test_ast = 0;
403 int Beam_test_framecount = 0;
404
405 // beam warmup completion %
406 #define BEAM_WARMUP_PCT(b)                      ( ((float)Weapon_info[b->weapon_info_index].b_info.beam_warmup - (float)timestamp_until(b->warmup_stamp)) / (float)Weapon_info[b->weapon_info_index].b_info.beam_warmup ) 
407
408 // beam warmdown completion %           
409 #define BEAM_WARMDOWN_PCT(b)            ( ((float)Weapon_info[b->weapon_info_index].b_info.beam_warmdown - (float)timestamp_until(b->warmdown_stamp)) / (float)Weapon_info[b->weapon_info_index].b_info.beam_warmdown ) 
410
411 // timestamp for spewing muzzle particles
412 int Beam_muzzle_stamp = -1;
413
414 // link into the physics paused system
415 extern int physics_paused;
416
417 // beam lighting info
418 #define MAX_BEAM_LIGHT_INFO             100
419 typedef struct beam_light_info {
420         beam *bm;                                       // beam casting the light
421         int objnum;                                     // object getting light cast on it
422         ubyte source;                           // 0 to light the shooter, 1 for lighting any ship the beam passes, 2 to light the collision ship
423         vector c_point;                 // collision point for type 2 lights
424 } beam_light_info;
425
426 beam_light_info Beam_lights[MAX_BEAM_LIGHT_INFO];
427 int Beam_light_count = 0;
428
429 float b_whack_small = 500.0f;
430 float b_whack_big = 1500.0f;
431 float b_whack_damage = 150.0f;
432 DCF(b_whack_small, "")
433 {
434         dc_get_arg(ARG_FLOAT);
435         b_whack_small = Dc_arg_float;
436 }
437 DCF(b_whack_big, "")
438 {
439         dc_get_arg(ARG_FLOAT);
440         b_whack_big = Dc_arg_float;
441 }
442 DCF(b_whack_damage, "")
443 {
444         dc_get_arg(ARG_FLOAT);
445         b_whack_damage = Dc_arg_float;
446 }
447
448
449 // ------------------------------------------------------------------------------------------------
450 // BEAM WEAPON FORWARD DECLARATIONS
451 //
452
453 // delete a beam
454 void beam_delete(beam *b);
455
456 // handle a hit on a specific object
457 void beam_handle_collisions(beam *b);
458
459 // fills in binfo
460 void beam_get_binfo(beam *b, float accuracy, int num_shots);
461
462 // aim the beam (setup last_start and last_shot - the endpoints). also recalculates object collision info
463 void beam_aim(beam *b);
464
465 // type A functions
466 void beam_type_a_move(beam *b);
467
468 // type B functions
469 void beam_type_b_move(beam *b);
470
471 // type C functions
472 void beam_type_c_move(beam *b);
473
474 // type D functions
475 void beam_type_d_move(beam *b);
476 void beam_type_d_get_status(beam *b, int *shot_index, int *fire_wait);
477
478 // type e functions
479 void beam_type_e_move(beam *b);
480
481 // given a model #, and an object, stuff 2 good world coord points
482 void beam_get_octant_points(int modelnum, object *objp, int oct_index, int oct_array[BEAM_NUM_GOOD_OCTANTS][4], vector *v1, vector *v2);
483
484 // given an object, return its model num
485 int beam_get_model(object *objp);
486
487 // for a given object, and a firing beam, determine its critical dot product and range
488 void beam_get_cull_vals(object *objp, beam *b, float *cull_dot, float *cull_dist);
489
490 // get the total possible cone for a given beam in radians
491 float beam_get_cone_dot(beam *b);
492
493 // for rendering the beam effect
494 // output top and bottom vectors
495 // fvec == forward vector (eye viewpoint basically. in world coords)
496 // pos == world coordinate of the point we're calculating "around"
497 // w == width of the diff between top and bottom around pos
498 void beam_calc_facing_pts(vector *top, vector *bot, vector *fvec, vector *pos, float w, float z_add);
499
500 // render the muzzle glow for a beam weapon
501 void beam_render_muzzle_glow(beam *b);
502
503 // generate particles for the muzzle glow
504 void beam_generate_muzzle_particles(beam *b);
505
506 // throw some jitter into the aim - based upon shot_aim
507 void beam_jitter_aim(beam *b, float aim);
508
509 // if it is legal for the beam to continue firing
510 // returns -1 if the beam should stop firing immediately
511 // returns 0 if the beam should go to warmdown
512 // returns 1 if the beam can continue along its way
513 int beam_ok_to_fire(beam *b);
514
515 // start the warmup phase for the beam
516 void beam_start_warmup(beam *b);
517
518 // start the firing phase for the beam, return 0 if the beam failed to start, and should be deleted altogether
519 int beam_start_firing(beam *b);
520
521 // start the warmdown phase for the beam
522 void beam_start_warmdown(beam *b);
523
524 // add a collision to the beam for this frame (to be evaluated later)
525 void beam_add_collision(beam *b, object *hit_object, mc_info *cinfo);
526
527 // sort collisions for the frame
528 int beam_sort_collisions_func(const void *e1, const void *e2);
529
530 // get the width of the widest section of the beam
531 float beam_get_widest(beam *b);
532
533 // mark an object as being lit
534 void beam_add_light(beam *b, int objnum, int source, vector *c_point);
535
536 // apply lighting from any beams
537 void beam_apply_lighting();
538
539 // recalculate beam sounds (looping sounds relative to the player)
540 void beam_recalc_sounds(beam *b);
541
542 // apply a whack to a ship
543 void beam_apply_whack(beam *b, object *objp, vector *hit_point);
544
545 // return the amount of damage which should be applied to a ship. basically, filters friendly fire damage 
546 float beam_get_ship_damage(beam *b, object *objp);
547
548 // if the beam is likely to tool a given target before its lifetime expires
549 int beam_will_tool_target(beam *b, object *objp);
550
551
552 // ------------------------------------------------------------------------------------------------
553 // BEAM WEAPON FUNCTIONS
554 //
555
556 // init at game startup
557 void beam_init()
558 {
559         // clear the beams
560         list_init( &Beam_free_list );
561         list_init( &Beam_used_list );
562 }
563
564 // initialize beam weapons for this level
565 void beam_level_init()
566 {
567         // intialize beams
568         int idx;
569
570         Beam_count = 0;
571         list_init( &Beam_free_list );
572         list_init( &Beam_used_list );
573         memset(Beams, 0, sizeof(beam) * MAX_BEAMS);
574
575         // Link all object slots into the free list
576         for (idx=0; idx<MAX_BEAMS; idx++)       {
577                 Beams[idx].objnum = -1;
578                 list_append(&Beam_free_list, &Beams[idx] );
579         }
580
581         // reset muzzle particle spew timestamp
582         Beam_muzzle_stamp = -1;
583 }
584
585 // shutdown beam weapons for this level
586 void beam_level_close()
587 {
588         // clear the beams
589         list_init( &Beam_free_list );
590         list_init( &Beam_used_list );
591 }
592
593 // fire a beam, returns nonzero on success. the innards of the code handle all the rest, foo
594 int beam_fire(beam_fire_info *fire_info)
595 {
596         beam *new_item;
597         weapon_info *wip;
598         ship *firing_ship;
599         int objnum;             
600
601         // sanity check
602         if(fire_info == NULL){
603                 Int3();
604                 return -1;
605         }
606
607         // if we're out of beams, bail
608         if(Beam_count >= MAX_BEAMS){
609                 return -1;
610         }
611
612         // for now, only allow ship targets
613         if((fire_info->target->type != OBJ_SHIP) && (fire_info->target->type != OBJ_ASTEROID) && (fire_info->target->type != OBJ_DEBRIS) && (fire_info->target->type != OBJ_WEAPON)){
614                 return -1;
615         }       
616
617         // make sure the beam_info_index is valid
618         Assert((fire_info->beam_info_index >= 0) && (fire_info->beam_info_index < MAX_WEAPON_TYPES) && (Weapon_info[fire_info->beam_info_index].wi_flags & WIF_BEAM));
619         if((fire_info->beam_info_index < 0) || (fire_info->beam_info_index >= MAX_WEAPON_TYPES) || !(Weapon_info[fire_info->beam_info_index].wi_flags & WIF_BEAM)){
620                 return -1;
621         }
622         wip = &Weapon_info[fire_info->beam_info_index]; 
623
624         // make sure a ship is firing this
625         Assert((fire_info->shooter->type == OBJ_SHIP) && (fire_info->shooter->instance >= 0) && (fire_info->shooter->instance < MAX_SHIPS));
626         if((fire_info->shooter->type != OBJ_SHIP) || (fire_info->shooter->instance < 0) && (fire_info->shooter->instance >= MAX_SHIPS)){
627                 return -1;
628         }
629         firing_ship = &Ships[fire_info->shooter->instance];
630
631         // get a free beam
632         new_item = GET_FIRST(&Beam_free_list);
633         Assert( new_item != &Beam_free_list );          // shouldn't have the dummy element
634         if(new_item == &Beam_free_list){
635                 return -1;
636         }
637
638         // remove from the free list
639         list_remove( &Beam_free_list, new_item );
640         
641         // insert onto the end of used list
642         list_append( &Beam_used_list, new_item );
643
644         // increment counter
645         Beam_count++;   
646
647         // fill in some values
648         new_item->warmup_stamp = -1;
649         new_item->warmdown_stamp = -1;
650         new_item->weapon_info_index = fire_info->beam_info_index;       
651         new_item->objp = fire_info->shooter;
652         new_item->sig = fire_info->shooter->signature;
653         new_item->subsys = fire_info->turret;   
654         new_item->life_left = wip->b_info.beam_life;    
655         new_item->life_total = wip->b_info.beam_life;
656         new_item->r_collision_count = 0;
657         new_item->f_collision_count = 0;
658         new_item->target = fire_info->target;
659         new_item->target_subsys = fire_info->target_subsys;
660         new_item->target_sig = fire_info->target->signature;    
661         new_item->beam_sound_loop = -1;
662         new_item->type = wip->b_info.beam_type;
663         new_item->targeting_laser_offset = fire_info->targeting_laser_offset;
664         new_item->framecount = 0;
665         new_item->flags = 0;
666         new_item->shot_index = 0;
667         new_item->shrink = 1.0f;        
668         new_item->team = (char)firing_ship->team;
669         
670         // if the targeted subsystem is not NULL, force it to be a type A beam
671         if(new_item->target_subsys != NULL){
672                 new_item->type = BEAM_TYPE_A;
673         }
674
675         // type D weapons can only fire at small ships and missiles
676         if(new_item->type == BEAM_TYPE_D){
677                 // if its a targeted ship, get the target ship
678                 if((fire_info->target != NULL) && (fire_info->target->type == OBJ_SHIP) && (fire_info->target->instance >= 0)){         
679                         ship *target_ship = &Ships[fire_info->target->instance];
680                         
681                         // maybe force to be a type A
682                         if(Ship_info[target_ship->ship_info_index].flags & (SIF_BIG_SHIP | SIF_CAPITAL | SIF_SUPERCAP | SIF_FREIGHTER | SIF_DRYDOCK | SIF_CARGO)){
683                                 new_item->type = BEAM_TYPE_A;
684                         }
685                 }
686         }
687         
688         // ----------------------------------------------------------------------
689         // THIS IS THE CRITICAL POINT FOR MULTIPLAYER
690         // beam_get_binfo(...) determines exactly how the beam will behave over the course of its life
691         // it fills in binfo, which we can pass to clients in multiplayer       
692         if(fire_info->beam_info_override != NULL){
693                 new_item->binfo = *fire_info->beam_info_override;
694         } else {
695                 beam_get_binfo(new_item, fire_info->accuracy, wip->b_info.beam_shots);                  // to fill in b_info    - the set of directional aim vectors
696         }       
697
698         // create the associated object
699         objnum = obj_create(OBJ_BEAM, -1, new_item - Beams, &vmd_identity_matrix, &vmd_zero_vector, 1.0f, OF_COLLIDES);
700         if(objnum < 0){
701                 Int3();
702                 beam_delete(new_item);
703                 nprintf(("General", "obj_create() failed for beam weapon! bah!\n"));
704                 return -1;
705         }
706         new_item->objnum = objnum;
707
708         // this sets up all info for the first frame the beam fires
709         beam_aim(new_item);                                             // to fill in shot_point, etc.  
710
711         // check to see if its legal to fire at this guy
712         if(beam_ok_to_fire(new_item) != 1){
713                 beam_delete(new_item);
714                 mprintf(("Killing beam at initial fire because of illegal targeting!!!\n"));
715                 return -1;
716         }
717
718         // if we're a multiplayer master - send a packet
719         if(MULTIPLAYER_MASTER){
720                 send_beam_fired_packet(fire_info->shooter, fire_info->turret, fire_info->target, fire_info->beam_info_index, &new_item->binfo);
721         }
722
723         // start the warmup phase
724         beam_start_warmup(new_item);            
725
726         return objnum;
727 }
728
729 // fire a targeting beam, returns objnum on success. a much much simplified version of a beam weapon
730 // targeting lasers last _one_ frame. For a continuous stream - they must be created every frame.
731 // this allows it to work smoothly in multiplayer (detect "trigger down". every frame just create a targeting laser firing straight out of the
732 // object. this way you get all the advantages of nice rendering and collisions).
733 // NOTE : only references beam_info_index and shooter
734 int beam_fire_targeting(beam_fire_info *fire_info)
735 {
736         beam *new_item;
737         weapon_info *wip;
738         int objnum;     
739         ship *firing_ship;
740
741         // sanity check
742         if(fire_info == NULL){
743                 Int3();
744                 return -1;
745         }
746
747         // if we're out of beams, bail
748         if(Beam_count >= MAX_BEAMS){
749                 return -1;
750         }
751         
752         // make sure the beam_info_index is valid
753         Assert((fire_info->beam_info_index >= 0) && (fire_info->beam_info_index < MAX_WEAPON_TYPES) && (Weapon_info[fire_info->beam_info_index].wi_flags & WIF_BEAM));
754         if((fire_info->beam_info_index < 0) || (fire_info->beam_info_index >= MAX_WEAPON_TYPES) || !(Weapon_info[fire_info->beam_info_index].wi_flags & WIF_BEAM)){
755                 return -1;
756         }
757         wip = &Weapon_info[fire_info->beam_info_index]; 
758
759         // make sure a ship is firing this
760         Assert((fire_info->shooter->type == OBJ_SHIP) && (fire_info->shooter->instance >= 0) && (fire_info->shooter->instance < MAX_SHIPS));
761         if((fire_info->shooter->type != OBJ_SHIP) || (fire_info->shooter->instance < 0) && (fire_info->shooter->instance >= MAX_SHIPS)){
762                 return -1;
763         }
764         firing_ship = &Ships[fire_info->shooter->instance];
765
766
767         // get a free beam
768         new_item = GET_FIRST(&Beam_free_list);
769         Assert( new_item != &Beam_free_list );          // shouldn't have the dummy element
770
771         // remove from the free list
772         list_remove( &Beam_free_list, new_item );
773         
774         // insert onto the end of used list
775         list_append( &Beam_used_list, new_item );
776
777         // increment counter
778         Beam_count++;
779
780         // maybe allocate some extra data based on the beam type
781         Assert(wip->b_info.beam_type == BEAM_TYPE_C);
782         if(wip->b_info.beam_type != BEAM_TYPE_C){
783                 return -1;
784         }
785
786         // fill in some values
787         new_item->warmup_stamp = -1;
788         new_item->warmdown_stamp = -1;
789         new_item->weapon_info_index = fire_info->beam_info_index;       
790         new_item->objp = fire_info->shooter;
791         new_item->sig = 0;
792         new_item->subsys = NULL;
793         new_item->life_left = 0;        
794         new_item->life_total = 0;
795         new_item->r_collision_count = 0;
796         new_item->f_collision_count = 0;
797         new_item->target = NULL;
798         new_item->target_subsys = NULL;
799         new_item->target_sig = 0;
800         new_item->beam_sound_loop = -1;
801         new_item->type = BEAM_TYPE_C;   
802         new_item->targeting_laser_offset = fire_info->targeting_laser_offset;
803         new_item->framecount = 0;
804         new_item->flags = 0;
805         new_item->shot_index = 0;
806         new_item->team = (char)firing_ship->team;
807
808         // type c is a very special weapon type - binfo has no meaning
809
810         // create the associated object
811         objnum = obj_create(OBJ_BEAM, -1, new_item - Beams, &vmd_identity_matrix, &vmd_zero_vector, 1.0f, OF_COLLIDES);
812         if(objnum < 0){
813                 Int3();
814                 beam_delete(new_item);
815                 nprintf(("General", "obj_create() failed for beam weapon! bah!\n"));
816                 return -1;
817         }
818         new_item->objnum = objnum;      
819
820         // this sets up all info for the first frame the beam fires
821         beam_aim(new_item);                                             // to fill in shot_point, etc.          
822         
823         return objnum;
824 }
825
826 // return an object index of the guy who's firing this beam
827 int beam_get_parent(object *bm)
828 {
829         beam *b;
830
831         // get a handle to the beam
832         Assert(bm->type == OBJ_BEAM);
833         Assert(bm->instance >= 0);      
834         if(bm->type != OBJ_BEAM){
835                 return -1;
836         }
837         if(bm->instance < 0){
838                 return -1;
839         }
840         b = &Beams[bm->instance];
841
842         Assert(b->objp != NULL);
843         if(b->objp == NULL){
844                 return -1;
845         }
846
847         // if the object handle is invalid
848         if(b->objp->signature != b->sig){
849                 return -1;
850         }
851
852         // return the handle
853         return OBJ_INDEX(b->objp);
854 }
855
856 // return weapon_info_index of beam
857 int beam_get_weapon_info_index(object *bm)
858 {
859         Assert(bm->type == OBJ_BEAM);
860         if (bm->type != OBJ_BEAM) {
861                 return -1;
862         }
863
864         Assert(bm->instance >= 0 && bm->instance < MAX_BEAMS);
865         if (bm->instance < 0) {
866                 return -1;
867         }
868
869         // return weapon_info_index
870         return Beams[bm->instance].weapon_info_index;
871 }
872
873
874
875 // given a beam object, get the # of collisions which happened during the last collision check (typically, last frame)
876 int beam_get_num_collisions(int objnum)
877 {       
878         // sanity checks
879         if((objnum < 0) || (objnum >= MAX_OBJECTS)){
880                 Int3();
881                 return -1;
882         }
883         if((Objects[objnum].instance < 0) || (Objects[objnum].instance >= MAX_BEAMS)){
884                 Int3();
885                 return -1;
886         }
887         if((Beams[Objects[objnum].instance].objnum != objnum) || (Beams[Objects[objnum].instance].objnum < 0)){
888                 Int3();
889                 return -1;
890         }
891
892         // return the # of recent collisions
893         return Beams[Objects[objnum].instance].r_collision_count;
894 }
895
896 // stuff collision info, returns 1 on success
897 int beam_get_collision(int objnum, int num, int *collision_objnum, mc_info **cinfo)
898 {
899         // sanity checks
900         if((objnum < 0) || (objnum >= MAX_OBJECTS)){
901                 Int3();
902                 return 0;
903         }
904         if((Objects[objnum].instance < 0) || (Objects[objnum].instance >= MAX_BEAMS)){
905                 Int3();
906                 return 0;
907         }
908         if((Beams[Objects[objnum].instance].objnum != objnum) || (Beams[Objects[objnum].instance].objnum < 0)){
909                 Int3();
910                 return 0;
911         }
912         if(num >= Beams[Objects[objnum].instance].r_collision_count){
913                 Int3();
914                 return 0;
915         }
916
917         // return - success
918         *cinfo = &Beams[Objects[objnum].instance].r_collisions[num].cinfo;
919         *collision_objnum = Beams[Objects[objnum].instance].r_collisions[num].c_objnum;
920         return 1;
921 }
922
923 // pause all looping beam sounds
924 void beam_pause_sounds()
925 {
926         beam *moveup = NULL;
927
928         // set all beam volumes to 0    
929         moveup = GET_FIRST(&Beam_used_list);
930         if(moveup == NULL){
931                 return;
932         }
933         while(moveup != END_OF_LIST(&Beam_used_list)){                          
934                 // set the volume to 0, if he has a looping beam sound
935                 if(moveup->beam_sound_loop >= 0){
936                         snd_set_volume(moveup->beam_sound_loop, 0.0f);
937                 }
938
939                 // next beam
940                 moveup = GET_NEXT(moveup);
941         }
942 }
943
944 // unpause looping beam sounds
945 void beam_unpause_sounds()
946 {
947         beam *moveup = NULL;
948
949         // recalc all beam sounds
950         moveup = GET_FIRST(&Beam_used_list);
951         if(moveup == NULL){
952                 return;
953         }
954         while(moveup != END_OF_LIST(&Beam_used_list)){                          
955                 beam_recalc_sounds(moveup);
956
957                 // next beam
958                 moveup = GET_NEXT(moveup);
959         }
960 }
961
962
963 // -----------------------------===========================------------------------------
964 // BEAM MOVEMENT FUNCTIONS
965 // -----------------------------===========================------------------------------
966
967 // move a type A beam weapon
968 void beam_type_a_move(beam *b)
969 {
970         vector dir;
971         vector temp, temp2;     
972
973         // LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
974         // get the "originating point" of the beam for this frame. essentially bashes last_start
975         ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2);
976
977         // if the "warming up" timestamp has not expired
978         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
979                 return;
980         }
981
982         // put the "last_shot" point arbitrarily far away
983         vm_vec_sub(&dir, &b->last_shot, &b->last_start);
984         vm_vec_normalize_quick(&dir);
985         vm_vec_scale_add(&b->last_shot, &b->last_start, &dir, BEAM_FAR_LENGTH);
986         Assert(is_valid_vec(&b->last_shot));
987 }
988
989 // move a type B beam weapon
990 #define BEAM_T(b)                                               ( ((b->binfo.delta_ang / b->life_total) * (b->life_total - b->life_left)) / b->binfo.delta_ang )
991 void beam_type_b_move(beam *b)
992 {               
993         vector actual_dir;
994         vector temp, temp2;
995         float dot_save; 
996
997         // LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
998         // get the "originating point" of the beam for this frame. essentially bashes last_start
999         ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2);
1000
1001         // if the "warming up" timestamp has not expired
1002         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
1003                 return;
1004         }       
1005
1006         // if the two direction vectors are _really_ close together, just use the original direction
1007         dot_save = vm_vec_dot(&b->binfo.dir_a, &b->binfo.dir_b);
1008         if((double)dot_save >= 0.999999999){
1009                 actual_dir = b->binfo.dir_a;
1010         } 
1011         // otherwise move towards the dir       we calculated when firing this beam     
1012         else {
1013                 vm_vec_interp_constant(&actual_dir, &b->binfo.dir_a, &b->binfo.dir_b, BEAM_T(b));
1014         }
1015
1016         // now recalculate shot_point to be shooting through our new point
1017         vm_vec_scale_add(&b->last_shot, &b->last_start, &actual_dir, BEAM_FAR_LENGTH);
1018         int is_valid = is_valid_vec(&b->last_shot);
1019         Assert(is_valid);
1020         if(!is_valid){
1021                 actual_dir = b->binfo.dir_a;
1022                 vm_vec_scale_add(&b->last_shot, &b->last_start, &actual_dir, BEAM_FAR_LENGTH);
1023         }
1024 }
1025
1026 // type C functions
1027 void beam_type_c_move(beam *b)
1028 {       
1029         vector temp;
1030
1031         // ugh
1032         if(b->objp == NULL){
1033                 Int3();
1034                 return;
1035         }
1036
1037         // type c beams only last one frame so we never have to "move" them.                    
1038         temp = b->targeting_laser_offset;
1039         vm_vec_unrotate(&b->last_start, &temp, &b->objp->orient);
1040         vm_vec_add2(&b->last_start, &b->objp->pos);     
1041         vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.fvec, BEAM_FAR_LENGTH);
1042 }
1043
1044 // type D functions
1045 void beam_type_d_move(beam *b)
1046 {
1047         int shot_index, fire_wait;
1048         vector temp, temp2, dir;        
1049
1050         // LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
1051         // get the "originating point" of the beam for this frame. essentially bashes last_start
1052         ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2);
1053
1054         // if the "warming up" timestamp has not expired
1055         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
1056                 return;
1057         }       
1058
1059         // determine what stage of the beam we're in
1060         beam_type_d_get_status(b, &shot_index, &fire_wait);
1061
1062         // if we've changed shot index
1063         if(shot_index != b->shot_index){
1064                 // set the new index
1065                 b->shot_index = shot_index;
1066
1067                 // re-aim
1068                 beam_aim(b);
1069         }
1070
1071         // if we're in the fire wait stage
1072         b->flags &= ~BF_SAFETY;
1073         if(fire_wait){
1074                 b->flags |= BF_SAFETY;
1075         }
1076
1077         // put the "last_shot" point arbitrarily far away
1078         vm_vec_sub(&dir, &b->last_shot, &b->last_start);
1079         vm_vec_normalize_quick(&dir);
1080         vm_vec_scale_add(&b->last_shot, &b->last_start, &dir, BEAM_FAR_LENGTH);
1081         Assert(is_valid_vec(&b->last_shot));
1082 }
1083 void beam_type_d_get_status(beam *b, int *shot_index, int *fire_wait)
1084 {       
1085         float shot_time = b->life_total / (float)b->binfo.shot_count;
1086         float beam_time = b->life_total - b->life_left;
1087
1088         // determine what "shot" we're on       
1089         *shot_index = (int)(beam_time / shot_time);
1090         Assert(*shot_index < b->binfo.shot_count);
1091         if(*shot_index >= b->binfo.shot_count){
1092                 *shot_index = b->binfo.shot_count - 1;
1093         }       
1094
1095         // determine if its the firing or waiting section of the shot (fire happens first, THEN wait)   
1096         *fire_wait = 0;
1097         if(beam_time > ((shot_time * (*shot_index)) + (shot_time * 0.5f))){
1098                 *fire_wait = 1;
1099         }       
1100 }
1101
1102 // type e functions
1103 void beam_type_e_move(beam *b)
1104 {
1105         vector temp, turret_norm;       
1106
1107         // LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
1108         // get the "originating point" of the beam for this frame. essentially bashes last_start
1109         ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &turret_norm, 1, &temp);
1110
1111         // if the "warming up" timestamp has not expired
1112         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
1113                 return;
1114         }       
1115
1116         // put the "last_shot" point arbitrarily far away
1117         vm_vec_scale_add(&b->last_shot, &b->last_start, &turret_norm, BEAM_FAR_LENGTH); 
1118         Assert(is_valid_vec(&b->last_shot));
1119 }
1120
1121 // pre-move (before collision checking - but AFTER ALL OTHER OBJECTS HAVE BEEN MOVED)
1122 void beam_move_all_pre()
1123 {       
1124         beam *b;        
1125         beam *moveup;           
1126
1127         // zero lights for this frame yet
1128         Beam_light_count = 0;
1129
1130         // traverse through all active beams
1131         moveup = GET_FIRST(&Beam_used_list);
1132         while(moveup != END_OF_LIST(&Beam_used_list)){                          
1133                 // get the beam
1134                 b = moveup;
1135
1136                 // unset collision info
1137                 b->f_collision_count = 0;
1138                 
1139                 if(!physics_paused){
1140                         // move the beam
1141                         switch(b->type){
1142                         // type A beam weapons don't move
1143                         case BEAM_TYPE_A :                      
1144                                 beam_type_a_move(b);
1145                                 break;
1146
1147                         // type B beam weapons move across the target somewhat randomly
1148                         case BEAM_TYPE_B :
1149                                 beam_type_b_move(b);
1150                                 break;                          
1151
1152                         // type C beam weapons are attached to a fighter - pointing forward
1153                         case BEAM_TYPE_C:
1154                                 beam_type_c_move(b);
1155                                 break;
1156
1157                         // type D
1158                         case BEAM_TYPE_D:
1159                                 beam_type_d_move(b);
1160                                 break;
1161
1162                         // type E
1163                         case BEAM_TYPE_E:
1164                                 beam_type_e_move(b);
1165                                 break;
1166
1167                         // illegal beam type
1168                         default :
1169                                 Int3();
1170                         }
1171                 }
1172
1173                 // next
1174                 moveup = GET_NEXT(moveup);
1175         }
1176 }
1177
1178 // post-collision time processing for beams
1179 void beam_move_all_post()
1180 {
1181         beam *moveup;   
1182         beam *next_one; 
1183         int bf_status;  
1184         beam_weapon_info *bwi;
1185
1186         // traverse through all active beams
1187         moveup = GET_FIRST(&Beam_used_list);
1188         while(moveup != END_OF_LIST(&Beam_used_list)){                          
1189                  bwi = &Weapon_info[moveup->weapon_info_index].b_info;
1190
1191                  // check the status of the beam
1192                 bf_status = beam_ok_to_fire(moveup);
1193
1194                 // if we're warming up
1195                 if(moveup->warmup_stamp != -1){
1196                         next_one = GET_NEXT(moveup);                    
1197
1198                         // should we be stopping?
1199                         if(bf_status < 0){
1200                                 beam_delete(moveup);
1201                         } else {
1202                                 // if the warming up timestamp has expired, start firing
1203                                 if(timestamp_elapsed(moveup->warmup_stamp)){                                                    
1204                                         // start firing
1205                                         if(!beam_start_firing(moveup)){
1206                                                 beam_delete(moveup);                                                                                            
1207                                         }                       
1208                                         
1209                                         // add a muzzle light for the shooter
1210                                         beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
1211                                 } 
1212                         }
1213
1214                         // next
1215                         moveup = next_one;
1216                         continue;
1217                 } 
1218                 // if we're warming down
1219                 else if(moveup->warmdown_stamp != -1){                  
1220                         next_one = GET_NEXT(moveup);
1221
1222                         // should we be stopping?
1223                         if(bf_status < 0){
1224                                 beam_delete(moveup);
1225                         } else {
1226                                 // if we're done warming down, the beam is finished
1227                                 if(timestamp_elapsed(moveup->warmdown_stamp)){                          
1228                                         beam_delete(moveup);                            
1229                                 }                       
1230                         }
1231
1232                         // next
1233                         moveup = next_one;
1234                         continue;
1235                 }
1236                 
1237                 // otherwise, we're firing away.........                
1238
1239                 // add a muzzle light for the shooter
1240                 beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
1241
1242                 // subtract out the life left for the beam
1243                 if(!physics_paused){
1244                         moveup->life_left -= flFrametime;                                               
1245                 }
1246
1247                 // if we're past the shrink point, start shrinking the beam
1248                 if(moveup->life_left <= (moveup->life_total * bwi->beam_shrink_factor)){
1249                         moveup->flags |= BF_SHRINK;
1250                 }
1251
1252                 // if we're shrinking the beam
1253                 if(moveup->flags & BF_SHRINK){
1254                         moveup->shrink -= bwi->beam_shrink_pct * flFrametime;
1255                         if(moveup->shrink < 0.1f){
1256                                 moveup->shrink = 0.1f;
1257                         }
1258                 }               
1259
1260                 // stop shooting?
1261                 if(bf_status <= 0){
1262                         next_one = GET_NEXT(moveup);
1263
1264                         // if beam should abruptly stop
1265                         if(bf_status == -1){                            
1266                                 beam_delete(moveup);                                                    
1267                         }
1268                         // if the beam should just power down
1269                         else {                  
1270                                 beam_start_warmdown(moveup);
1271                         }
1272                         
1273                         // next beam
1274                         moveup = next_one;
1275                         continue;
1276                 }                               
1277
1278                 // increment framecount
1279                 moveup->framecount++;           
1280                 
1281                 // type c weapons live for one frame only
1282                 if(moveup->type == BEAM_TYPE_C){
1283                         if(moveup->framecount > 1){
1284                                 next_one = GET_NEXT(moveup);
1285                         
1286                                 beam_delete(moveup);                                                    
1287                                 moveup = next_one;
1288                                 continue;
1289                         }
1290                 }
1291                 // done firing, so go into the warmdown phase
1292                 else {
1293                         if((moveup->life_left <= 0.0f) && (moveup->warmdown_stamp == -1)){
1294                                 beam_start_warmdown(moveup);
1295                                 
1296                                 moveup = GET_NEXT(moveup);                      
1297                                 continue;
1298                         }                               
1299                 }               
1300
1301                 // handle any collisions which occured collision (will take care of applying damage to all objects which got hit)
1302                 beam_handle_collisions(moveup);                                         
1303
1304                 // recalculate beam sounds
1305                 beam_recalc_sounds(moveup);
1306
1307                 // next item
1308                 moveup = GET_NEXT(moveup);
1309         }
1310
1311         // apply all beam lighting
1312         beam_apply_lighting();
1313
1314         // process beam culling info
1315 #ifndef NDEBUG
1316         /*
1317         if(Beam_test_stamp == -1){
1318                 Beam_test_stamp = timestamp(BEAM_TEST_STAMP_TIME);
1319                 Beam_test_ints = 0;
1320                 Beam_test_framecount = 0;
1321         } else {
1322                 if(timestamp_elapsed(Beam_test_stamp)){                 
1323                         // report the results
1324                         nprintf(("General", "Performed %f beam ints/frame (%d, %d, %d, %d), over %f seconds\n", (float)Beam_test_ints/(float)Beam_test_framecount, Beam_test_ints, Beam_test_framecount, Beam_test_ship, Beam_test_ast, (float)BEAM_TEST_STAMP_TIME / 1000.0f));
1325
1326                         // reset vars
1327                         Beam_test_stamp = timestamp(BEAM_TEST_STAMP_TIME);
1328                         Beam_test_ints = 0;
1329                         Beam_test_ship = 0;
1330                         Beam_test_ast = 0;
1331                         Beam_test_framecount = 0;
1332                 } else {
1333                         Beam_test_framecount++;
1334                 }
1335         }
1336         */
1337 #endif
1338 }
1339
1340 // -----------------------------===========================------------------------------
1341 // BEAM RENDERING FUNCTIONS
1342 // -----------------------------===========================------------------------------
1343
1344 // render a beam weapon
1345 #define STUFF_VERTICES()        do { verts[0]->u = 0.0f; verts[0]->v = 0.0f;    verts[1]->u = 1.0f; verts[1]->v = 0.0f; verts[2]->u = 1.0f;     verts[2]->v = 1.0f; verts[3]->u = 0.0f; verts[3]->v = 1.0f; } while(0);
1346 #define R_VERTICES()            do { g3_rotate_vertex(verts[0], &bottom1); g3_rotate_vertex(verts[1], &bottom2);        g3_rotate_vertex(verts[2], &top2); g3_rotate_vertex(verts[3], &top1); } while(0);
1347 #define P_VERTICES()            do { for(idx=0; idx<4; idx++){ g3_project_vertex(verts[idx]); } } while(0);
1348 int poly_beam = 0;
1349 void beam_render(beam_weapon_info *bwi, vector *start, vector *shot, float shrink)
1350 {               
1351         int idx, s_idx;
1352         vertex h1[4];                           // halves of a beam section     
1353         vertex *verts[4] = { &h1[0], &h1[1], &h1[2], &h1[3] };  
1354         vector fvec, top1, bottom1, top2, bottom2;
1355         float scale;    
1356
1357         // bogus weapon info index
1358         if(bwi == NULL){
1359                 return;
1360         }
1361
1362         // if the beam start and endpoints are the same
1363         if(vm_vec_same(start, shot)){
1364                 return;
1365         }
1366
1367         // get beam direction
1368         vm_vec_sub(&fvec, shot, start);
1369         vm_vec_normalize_quick(&fvec);          
1370
1371         // turn off backface culling
1372         gr_set_cull(0);
1373         
1374         // draw all sections    
1375         for(s_idx=0; s_idx<bwi->beam_num_sections; s_idx++){
1376                 // calculate the beam points
1377                 scale = frand_range(1.0f - bwi->sections[s_idx].flicker, 1.0f + bwi->sections[s_idx].flicker);
1378                 beam_calc_facing_pts(&top1, &bottom1, &fvec, start, bwi->sections[s_idx].width * scale * shrink, bwi->sections[s_idx].z_add);   
1379                 beam_calc_facing_pts(&top2, &bottom2, &fvec, shot, bwi->sections[s_idx].width * scale * scale * shrink, bwi->sections[s_idx].z_add);                            
1380                 R_VERTICES();                                                                                                                           // rotate and project the vertices
1381                 P_VERTICES();                                           
1382                 STUFF_VERTICES();               // stuff the beam with creamy goodness (texture coords)
1383
1384                 // set the right texture with additive alpha, and draw the poly
1385                 gr_set_bitmap(bwi->sections[s_idx].texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.9999f);              
1386                 g3_draw_poly( 4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT);                        
1387         }               
1388         
1389         // turn backface culling back on
1390         gr_set_cull(1); 
1391 }
1392
1393 // generate particles for the muzzle glow
1394 int hack_time = 100;
1395 DCF(h_time, "")
1396 {
1397         dc_get_arg(ARG_INT);
1398         hack_time = Dc_arg_int;
1399 }
1400 void beam_generate_muzzle_particles(beam *b)
1401 {
1402         int particle_count;
1403         int idx;
1404         weapon_info *wip;
1405         vector turret_norm, turret_pos, particle_pos, particle_dir, p_temp;
1406         matrix m;
1407         particle_info pinfo;
1408
1409         // if our hack stamp has expired
1410         if(!((Beam_muzzle_stamp == -1) || timestamp_elapsed(Beam_muzzle_stamp))){
1411                 return;
1412         }
1413
1414         // never generate anything past about 1/5 of the beam fire time 
1415         if(b->warmup_stamp == -1){
1416                 return;
1417         }
1418
1419         // get weapon info
1420         wip = &Weapon_info[b->weapon_info_index];
1421
1422         // no specified particle for this beam weapon
1423         if(wip->b_info.beam_particle_ani < 0){
1424                 return;
1425         }
1426         
1427         // reset the hack stamp
1428         Beam_muzzle_stamp = timestamp(hack_time);
1429
1430         // randomly generate 10 to 20 particles
1431         particle_count = (int)frand_range(0.0f, (float)wip->b_info.beam_particle_count);
1432
1433         // get turret info - position and normal        
1434         turret_pos = b->subsys->system_info->pnt;
1435         turret_norm = b->subsys->system_info->turret_norm;      
1436
1437         // randomly perturb a vector within a cone around the normal
1438         vm_vector_2_matrix(&m, &turret_norm, NULL, NULL);
1439         for(idx=0; idx<particle_count; idx++){
1440                 // get a random point in the cone
1441                 vm_vec_random_cone(&particle_dir, &turret_norm, wip->b_info.beam_particle_angle, &m);
1442                 p_temp = turret_pos;
1443                 vm_vec_scale_add(&p_temp, &turret_pos, &particle_dir, wip->b_info.beam_muzzle_radius * frand_range(0.75f, 0.9f));
1444
1445                 // transform into world coords          
1446                 vm_vec_unrotate(&particle_pos, &p_temp, &b->objp->orient);
1447                 vm_vec_add2(&particle_pos, &b->objp->pos);
1448                 p_temp = particle_dir;
1449                 vm_vec_unrotate(&particle_dir, &p_temp, &b->objp->orient);
1450
1451                 // now generate some interesting values for the particle
1452                 float p_time_ref = wip->b_info.beam_life + ((float)wip->b_info.beam_warmup / 1000.0f);          
1453                 float p_life = frand_range(p_time_ref * 0.5f, p_time_ref * 0.7f);
1454                 float p_vel = (wip->b_info.beam_muzzle_radius / p_life) * frand_range(0.85f, 1.2f);
1455                 vm_vec_scale(&particle_dir, -p_vel);
1456
1457                 memset(&pinfo, 0, sizeof(particle_info));
1458                 pinfo.pos = particle_pos;
1459                 pinfo.vel = particle_dir;
1460                 pinfo.lifetime = p_life;
1461                 pinfo.attached_objnum = -1;
1462                 pinfo.attached_sig = 0;
1463                 pinfo.rad = wip->b_info.beam_particle_radius;
1464                 pinfo.reverse = 1;
1465                 pinfo.type = PARTICLE_BITMAP;
1466                 pinfo.optional_data = wip->b_info.beam_particle_ani;
1467                 pinfo.tracer_length = -1.0f;            
1468                 particle_create(&pinfo);
1469         }
1470 }
1471
1472 // render the muzzle glow for a beam weapon
1473 void beam_render_muzzle_glow(beam *b)
1474 {
1475         vertex v;
1476         weapon_info *wip = &Weapon_info[b->weapon_info_index];
1477         beam_weapon_info *bwi = &Weapon_info[b->weapon_info_index].b_info;
1478         float pct, rand_val;
1479
1480         // if we don't have a glow bitmap
1481         if(bwi->beam_glow_bitmap < 0){
1482                 return;
1483         }
1484
1485         // if the beam is warming up, scale the glow
1486         if(b->warmup_stamp != -1){              
1487                 // get warmup pct
1488                 pct = BEAM_WARMUP_PCT(b);
1489                 rand_val = 1.0f;
1490         } else
1491         // if the beam is warming down
1492         if(b->warmdown_stamp != -1){
1493                 // get warmup pct
1494                 pct = 1.0f - BEAM_WARMDOWN_PCT(b);
1495                 rand_val = 1.0f;
1496         } 
1497         // otherwise the beam is really firing
1498         else {
1499                 pct = 1.0f;
1500                 rand_val = frand_range(0.90f, 1.0f);
1501         }
1502
1503         // draw the bitmap
1504         g3_rotate_vertex(&v, &b->last_start);
1505         gr_set_bitmap( bwi->beam_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.8f * pct); 
1506         // draw 1 bitmap
1507         g3_draw_bitmap(&v, 0, wip->b_info.beam_muzzle_radius * pct * rand_val, TMAP_FLAG_TEXTURED);
1508         
1509         // maybe draw more
1510         if(pct > 0.3f){
1511                 g3_draw_bitmap(&v, 0, wip->b_info.beam_muzzle_radius * pct * 0.75f * rand_val, TMAP_FLAG_TEXTURED);
1512         }
1513         if(pct > 0.5f){
1514                 g3_draw_bitmap(&v, 0, wip->b_info.beam_muzzle_radius * pct * 0.45f * rand_val, TMAP_FLAG_TEXTURED);
1515         }
1516         if(pct > 0.7f){
1517                 g3_draw_bitmap(&v, 0, wip->b_info.beam_muzzle_radius * pct * 0.25f * rand_val, TMAP_FLAG_TEXTURED);
1518         }
1519 }
1520
1521 // render all beam weapons
1522 void beam_render_all()
1523 {
1524         beam *moveup;           
1525
1526         // traverse through all active beams
1527         moveup = GET_FIRST(&Beam_used_list);
1528         while(moveup != END_OF_LIST(&Beam_used_list)){                          
1529                 // each beam type renders a little bit differently
1530                 if((moveup->warmup_stamp == -1) && (moveup->warmdown_stamp == -1) && !(moveup->flags & BF_SAFETY)){
1531                         // HACK -  if this is the first frame the beam is firing, don't render it
1532                         if(moveup->life_left >= moveup->life_total - 0.0001f){
1533
1534                                 moveup = GET_NEXT(moveup);
1535                                 continue;
1536                         }                       
1537
1538                         // render the beam itself
1539                         Assert(moveup->weapon_info_index >= 0);
1540                         if(moveup->weapon_info_index < 0){
1541                                 moveup = GET_NEXT(moveup);
1542                                 continue;
1543                         }
1544                         beam_render(&Weapon_info[moveup->weapon_info_index].b_info, &moveup->last_start, &moveup->last_shot, moveup->shrink);
1545                 }
1546
1547                 // render the muzzle glow
1548                 beam_render_muzzle_glow(moveup);                
1549
1550                 // maybe generate some muzzle particles
1551                 beam_generate_muzzle_particles(moveup);
1552
1553                 // next item
1554                 moveup = GET_NEXT(moveup);
1555         }       
1556 }
1557
1558 // output top and bottom vectors
1559 // fvec == forward vector (eye viewpoint basically. in world coords)
1560 // pos == world coordinate of the point we're calculating "around"
1561 // w == width of the diff between top and bottom around pos
1562 void beam_calc_facing_pts( vector *top, vector *bot, vector *fvec, vector *pos, float w, float z_add )
1563 {
1564         vector uvec, rvec;
1565         vector temp;    
1566
1567         temp = *pos;
1568
1569         vm_vec_sub( &rvec, &Eye_position, &temp );
1570         vm_vec_normalize( &rvec );      
1571
1572         vm_vec_crossprod(&uvec,fvec,&rvec);
1573         vm_vec_normalize(&uvec);
1574
1575         vm_vec_scale_add( top, &temp, &uvec, w/2.0f );
1576         vm_vec_scale_add( bot, &temp, &uvec, -w/2.0f ); 
1577 }
1578
1579 // light scale factor
1580 float blight = 25.5f;
1581 DCF(blight, "")
1582 {
1583         dc_get_arg(ARG_FLOAT);
1584         blight = Dc_arg_float;
1585 }
1586
1587 // call to add a light source to a small object
1588 void beam_add_light_small(beam *bm, object *objp, vector *pt_override = NULL)
1589 {
1590         weapon_info *wip;
1591         beam_weapon_info *bwi;
1592         float noise;
1593
1594         // no lighting 
1595         if(!Beam_lighting){
1596                 return;
1597         }
1598
1599         // sanity
1600         Assert(bm != NULL);
1601         if(bm == NULL){
1602                 return;
1603         }
1604         Assert(objp != NULL);
1605         if(objp == NULL){
1606                 return;
1607         }
1608         Assert(bm->weapon_info_index >= 0);
1609         wip = &Weapon_info[bm->weapon_info_index];
1610         bwi = &wip->b_info;
1611
1612         // some noise
1613         noise = frand_range(1.0f - bwi->sections[0].flicker, 1.0f + bwi->sections[0].flicker);
1614
1615         // widest part of the beam
1616         float light_rad = beam_get_widest(bm) * blight * noise; 
1617
1618         // nearest point on the beam, and its distance to the ship
1619         vector near_pt;
1620         if(pt_override == NULL){
1621                 float dist;
1622                 vm_vec_dist_to_line(&objp->pos, &bm->last_start, &bm->last_shot, &near_pt, &dist);
1623                 if(dist > light_rad){
1624                         return;
1625                 }
1626         } else {
1627                 near_pt = *pt_override;
1628         }
1629
1630         // average rgb of the beam      
1631         float fr = (float)wip->laser_color_1.red / 255.0f;
1632         float fg = (float)wip->laser_color_1.green / 255.0f;
1633         float fb = (float)wip->laser_color_1.blue / 255.0f;
1634
1635         // add a unique light
1636         // noise *= 0.1f;                       // a little less noise here, since we want the beam to generally cast a bright light
1637         light_add_point_unique(&near_pt, light_rad * 0.0001f, light_rad, 1.0f, fr, fg, fb, OBJ_INDEX(objp));
1638 }
1639
1640 // call to add a light source to a large object
1641 void beam_add_light_large(beam *bm, object *objp, vector *pt0, vector *pt1)
1642 {
1643         weapon_info *wip;
1644         beam_weapon_info *bwi;
1645         float noise;
1646
1647         // no lighting 
1648         if(!Beam_lighting){
1649                 return;
1650         }
1651
1652         // sanity
1653         Assert(bm != NULL);
1654         if(bm == NULL){
1655                 return;
1656         }
1657         Assert(objp != NULL);
1658         if(objp == NULL){
1659                 return;
1660         }
1661         Assert(bm->weapon_info_index >= 0);
1662         wip = &Weapon_info[bm->weapon_info_index];
1663         bwi = &wip->b_info;
1664
1665         // some noise
1666         noise = frand_range(1.0f - bwi->sections[0].flicker, 1.0f + bwi->sections[0].flicker);
1667
1668         // widest part of the beam
1669         float light_rad = beam_get_widest(bm) * blight * noise;         
1670
1671         // average rgb of the beam      
1672         float fr = (float)wip->laser_color_1.red / 255.0f;
1673         float fg = (float)wip->laser_color_1.green / 255.0f;
1674         float fb = (float)wip->laser_color_1.blue / 255.0f;
1675
1676         // add a unique light
1677         noise *= 0.1f;                  // a little less noise here, since we want the beam to generally cast a bright light
1678         light_add_tube(pt0, pt1, 1.0f, light_rad, 1.0f * noise, fr, fg, fb, OBJ_INDEX(objp));
1679 }
1680
1681 // mark an object as being lit
1682 void beam_add_light(beam *b, int objnum, int source, vector *c_point)
1683 {
1684         beam_light_info *l;
1685
1686         // if we're out of light slots!
1687         if(Beam_light_count >= MAX_BEAM_LIGHT_INFO){
1688                 // Int3();
1689                 return;
1690         }
1691
1692         // otherwise add it
1693         l = &Beam_lights[Beam_light_count++];
1694         l->bm = b;
1695         l->objnum = objnum;
1696         l->source = (ubyte)source;
1697         
1698         // only type 2 lights (from collisions) need a collision point
1699         if(c_point != NULL){
1700                 l->c_point = *c_point;
1701         } else {
1702                 Assert(source != 2);
1703                 if(source == 2){
1704                         Beam_light_count--;
1705                 }
1706         }
1707 }
1708
1709 // apply lighting from any beams
1710 void beam_apply_lighting()
1711 {
1712         int idx;
1713         beam_light_info *l;
1714         vector pt, dir;
1715         beam_weapon_info *bwi;
1716
1717         // convert all beam lights into real lights
1718         for(idx=0; idx<Beam_light_count; idx++){
1719                 // get the light
1720                 l = &Beam_lights[idx];          
1721
1722                 // bad object
1723                 if((l->objnum < 0) || (l->objnum >= MAX_OBJECTS) || (l->bm == NULL)){
1724                         continue;
1725                 }
1726
1727                 bwi = &Weapon_info[l->bm->weapon_info_index].b_info;
1728
1729                 // different light types
1730                 switch(l->source){
1731                 // from the muzzle of the gun
1732                 case 0:
1733                         // a few meters in from the of muzzle                   
1734                         vm_vec_sub(&dir, &l->bm->last_start, &l->bm->last_shot);
1735                         vm_vec_normalize_quick(&dir);
1736                         vm_vec_scale_add(&pt, &l->bm->last_start, &dir, bwi->beam_muzzle_radius * 5.0f);
1737
1738                         beam_add_light_small(l->bm, &Objects[l->objnum], &pt);
1739                         break;
1740
1741                 // from the beam passing by
1742                 case 1:
1743                         // object type
1744                         switch(Objects[l->objnum].type){
1745                         case OBJ_SHIP:
1746                                 Assert(Objects[l->objnum].instance >= 0);
1747
1748                                 // large ships
1749                                 if(Ship_info[Ships[Objects[l->objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)){
1750                                         beam_add_light_large(l->bm, &Objects[l->objnum], &l->bm->last_start, &l->bm->last_shot);
1751                                 }
1752                                 // small ships
1753                                 else {
1754                                         beam_add_light_small(l->bm, &Objects[l->objnum]);
1755                                 }
1756                                 break;
1757
1758                         // asteroids get small lights
1759                         case OBJ_ASTEROID:
1760                                 beam_add_light_small(l->bm, &Objects[l->objnum]);
1761                                 break;
1762
1763                         // debris gets small lights
1764                         case OBJ_DEBRIS:
1765                                 beam_add_light_small(l->bm, &Objects[l->objnum]);
1766                                 break;
1767                         }
1768                         break;
1769
1770                 // from a collision
1771                 case 2:
1772                         // a few meters from the collision point                        
1773                         vm_vec_sub(&dir, &l->bm->last_start, &l->c_point);
1774                         vm_vec_normalize_quick(&dir);
1775                         vm_vec_scale_add(&pt, &l->c_point, &dir, bwi->beam_muzzle_radius * 5.0f);
1776
1777                         beam_add_light_small(l->bm, &Objects[l->objnum], &pt);
1778                         break;
1779                 }
1780         }       
1781 }
1782
1783 // -----------------------------===========================------------------------------
1784 // BEAM BOOKKEEPING FUNCTIONS
1785 // -----------------------------===========================------------------------------
1786
1787 // delete a beam
1788 void beam_delete(beam *b)
1789 {
1790         // remove from active list and put on free list
1791         list_remove(&Beam_used_list, b);
1792         list_append(&Beam_free_list, b);
1793
1794         // delete our associated object
1795         // Assert(b->objnum >= 0);
1796         if(b->objnum >= 0){
1797                 obj_delete(b->objnum);
1798         }
1799         b->objnum = -1;
1800
1801         // kill the beam looping sound
1802         if(b->beam_sound_loop != -1){
1803                 snd_stop(b->beam_sound_loop);
1804                 b->beam_sound_loop = -1;
1805         }       
1806
1807         // subtract one
1808         Beam_count--;
1809         Assert(Beam_count >= 0);
1810         nprintf(("General", "Recycled beam (%d beams remaining)\n", Beam_count));
1811 }
1812
1813 // given an object, return its model num
1814 int beam_get_model(object *objp)
1815 {
1816         int subtype;
1817         Assert(objp->instance >= 0);
1818         if(objp->instance < 0){
1819                 return -1;
1820         }
1821
1822         subtype = 0;
1823         switch(objp->type){
1824         case OBJ_SHIP:          
1825                 return Ships[objp->instance].modelnum;
1826
1827         case OBJ_WEAPON:
1828                 Assert(Weapons[objp->instance].weapon_info_index >= 0);
1829                 if(Weapons[objp->instance].weapon_info_index < 0){
1830                         return -1;
1831                 }
1832                 return Weapon_info[Weapons[objp->instance].weapon_info_index].model_num;
1833
1834         case OBJ_DEBRIS:
1835                 Assert(Debris[objp->instance].is_hull);
1836                 if(!Debris[objp->instance].is_hull){
1837                         return -1;
1838                 }
1839                 return Debris[objp->instance].model_num;                
1840
1841 #ifndef FS2_DEMO
1842         case OBJ_ASTEROID:
1843                 subtype = Asteroids[objp->instance].asteroid_subtype;
1844                 Assert(Asteroids[objp->instance].type >= 0);
1845                 if(Asteroids[objp->instance].type < 0){
1846                         return -1;
1847                 }
1848                 return Asteroid_info[Asteroids[objp->instance].type].model_num[subtype];
1849 #endif
1850
1851         default:
1852                 // this shouldn't happen too often
1853                 mprintf(("Beam couldn't find a good find a good object model/type!! (%d)", objp->type));
1854                 return -1;
1855         }
1856
1857         // can't happen
1858         Int3();
1859         return -1;
1860 }
1861
1862 // start the warmup phase for the beam
1863 void beam_start_warmup(beam *b)
1864 {
1865         // set the warmup stamp
1866         b->warmup_stamp = timestamp(Weapon_info[b->weapon_info_index].b_info.beam_warmup);
1867
1868         // start playing warmup sound
1869         if(!(Game_mode & GM_STANDALONE_SERVER) && (Weapon_info[b->weapon_info_index].b_info.beam_warmup_sound >= 0)){           
1870                 snd_play_3d(&Snds[Weapon_info[b->weapon_info_index].b_info.beam_warmup_sound], &b->last_start, &View_position);
1871         }
1872 }
1873
1874 // start the firing phase for the beam, return 0 if the beam failed to start, and should be deleted altogether
1875 int beam_start_firing(beam *b)
1876 {
1877         // kill the warmup stamp so the rest of the code knows its firing
1878         b->warmup_stamp = -1;   
1879
1880         // any special stuff for each weapon type
1881         switch(b->type){
1882         // re-aim type A and D beam weapons here, otherwise they tend to miss           
1883         case BEAM_TYPE_A:
1884         case BEAM_TYPE_D:
1885                 beam_aim(b);
1886                 break;
1887         
1888         case BEAM_TYPE_B:
1889                 break;
1890
1891         case BEAM_TYPE_C:
1892                 break;  
1893
1894         case BEAM_TYPE_E:
1895                 break;
1896
1897         default:
1898                 Int3();
1899         }
1900
1901         // determine if we can legitimately start firing, or if we need to take other action
1902         switch(beam_ok_to_fire(b)){
1903         case -1 :
1904                 return 0;
1905
1906         case 0 :                        
1907                 beam_start_warmdown(b);
1908                 return 1;
1909         }                               
1910                         
1911         // start the beam firing sound now, if we haven't already               
1912         if((b->beam_sound_loop == -1) && (Weapon_info[b->weapon_info_index].b_info.beam_loop_sound >= 0)){                              
1913                 b->beam_sound_loop = snd_play_3d(&Snds[Weapon_info[b->weapon_info_index].b_info.beam_loop_sound], &b->last_start, &View_position, 0.0f, NULL, 1, 1.0, SND_PRIORITY_SINGLE_INSTANCE, NULL, 1.0f, 1);
1914
1915                 // "shot" sound
1916                 snd_play_3d(&Snds[SND_BEAM_SHOT], &b->last_start, &View_position);
1917         }       
1918
1919         // success
1920         return 1;
1921 }
1922
1923 // start the warmdown phase for the beam
1924 void beam_start_warmdown(beam *b)
1925 {
1926         // timestamp
1927         b->warmdown_stamp = timestamp(Weapon_info[b->weapon_info_index].b_info.beam_warmdown);                  
1928
1929         // start the warmdown sound
1930         if(Weapon_info[b->weapon_info_index].b_info.beam_warmdown_sound >= 0){                          
1931                 snd_play_3d(&Snds[Weapon_info[b->weapon_info_index].b_info.beam_warmdown_sound], &b->last_start, &View_position);
1932         }
1933
1934         // kill the beam looping sound 
1935         if(b->beam_sound_loop != -1){
1936                 snd_stop(b->beam_sound_loop);
1937                 b->beam_sound_loop = -1;
1938         }                                               
1939 }
1940
1941 // recalculate beam sounds (looping sounds relative to the player)
1942 void beam_recalc_sounds(beam *b)
1943 {
1944         beam_weapon_info *bwi;
1945         vector pos;     
1946
1947         Assert(b->weapon_info_index >= 0);
1948         if(b->weapon_info_index < 0){
1949                 return;
1950         }
1951         bwi = &Weapon_info[b->weapon_info_index].b_info;
1952
1953         // update the sound position relative to the player
1954         if(b->beam_sound_loop != -1){
1955                 // get the point closest to the player's viewing position
1956                 switch(vm_vec_dist_to_line(&View_position, &b->last_start, &b->last_shot, &pos, NULL)){
1957                 // behind the beam, so use the start pos
1958                 case -1:
1959                         pos = b->last_start;
1960                         break;
1961
1962                 // use the closest point
1963                 case 0:
1964                         // already calculated in vm_vec_dist_to_line(...)
1965                         break;
1966
1967                 // past the beam, so use the shot pos
1968                 case 1:
1969                         pos = b->last_shot;
1970                         break;
1971                 }
1972
1973                 snd_update_3d_pos(b->beam_sound_loop, &Snds[bwi->beam_loop_sound], &pos);
1974         }
1975 }
1976
1977
1978 // -----------------------------===========================------------------------------
1979 // BEAM AIMING FUNCTIONS
1980 // -----------------------------===========================------------------------------
1981
1982 // fills in binfo
1983 void beam_get_binfo(beam *b, float accuracy, int num_shots)
1984 {
1985         vector p2;
1986         int model_num, idx;     
1987         vector oct1, oct2;
1988         vector turret_point, turret_norm;
1989         beam_weapon_info *bwi;
1990         int skill_level;
1991
1992         // where the shot is originating from (b->last_start gets filled in)
1993         ship_get_global_turret_gun_info(b->objp, b->subsys, &turret_point, &turret_norm, 1, &p2);
1994
1995         // get a model # to work with
1996         model_num = beam_get_model(b->target);  
1997         if(model_num < 0){
1998                 return;
1999         }       
2000
2001         // get beam weapon info
2002         Assert(b->weapon_info_index >= 0);
2003         if(b->weapon_info_index < 0){
2004                 return;
2005         }
2006         bwi = &Weapon_info[b->weapon_info_index].b_info;
2007
2008         // skill level
2009         skill_level = Game_skill_level;
2010         if(Game_skill_level >= NUM_SKILL_LEVELS){
2011                 skill_level = NUM_SKILL_LEVELS - 1;
2012         }
2013         if(Game_skill_level < 0){
2014                 skill_level = 0;
2015         }
2016
2017         // stuff num shots even though its only used for type D weapons
2018         b->binfo.shot_count = (ubyte)num_shots;
2019         if(b->binfo.shot_count > MAX_BEAM_SHOTS){
2020                 b->binfo.shot_count = MAX_BEAM_SHOTS;
2021         }
2022
2023         // generate the proper amount of directional vectors    
2024         switch(b->type){        
2025         // pick an accuracy. beam will be properly aimed at actual fire time
2026         case BEAM_TYPE_A:               
2027                 // all we will do is decide whether or not we will hit - type A beam weapons are re-aimed immediately before firing
2028                 b->binfo.shot_aim[0] = frand_range(0.0f, 1.0f + bwi->beam_miss_factor[skill_level] * accuracy);
2029                 b->binfo.shot_count = 1;
2030
2031                 // get random model points, this is useful for big ships, because we never miss when shooting at them                   
2032                 submodel_get_two_random_points(model_num, 0, &b->binfo.dir_a, &b->binfo.dir_b);
2033                 break;  
2034
2035         // just 2 points in the "slash"
2036         case BEAM_TYPE_B:                                                       
2037                 beam_get_octant_points(model_num, b->target, (int)frand_range(0.0f, BEAM_NUM_GOOD_OCTANTS), Beam_good_slash_octants, &oct1, &oct2);             
2038
2039                 // point 1
2040                 vm_vec_sub(&b->binfo.dir_a, &oct1, &turret_point);
2041                 vm_vec_normalize(&b->binfo.dir_a);
2042
2043                 // point 2
2044                 vm_vec_sub(&b->binfo.dir_b, &oct2, &turret_point);
2045                 vm_vec_normalize(&b->binfo.dir_b);      
2046                 
2047                 // delta angle
2048                 b->binfo.delta_ang = fl_abs(vm_vec_delta_ang_norm(&b->binfo.dir_a, &b->binfo.dir_b, NULL));
2049                 break;
2050
2051         // nothing for this beam - its very special case
2052         case BEAM_TYPE_C:
2053                 break;
2054
2055         // type D beams fire at small ship multiple times
2056         case BEAM_TYPE_D:
2057                 // get a bunch of shot aims
2058                 for(idx=0; idx<b->binfo.shot_count; idx++){
2059                         //      MK, 9/3/99: Added pow() function to make increasingly likely to miss with subsequent shots.  30% more likely with each shot.
2060                         float r = ((float) pow(1.3f, idx)) * bwi->beam_miss_factor[skill_level] * accuracy;
2061                         b->binfo.shot_aim[idx] = frand_range(0.0f, 1.0f + r);
2062                 }
2063                 break;
2064
2065         // type e beams just fire straight
2066         case BEAM_TYPE_E:
2067                 b->binfo.shot_aim[0] = 0.0000001f;
2068                 b->binfo.shot_count = 1;
2069                 b->binfo.dir_a = turret_norm;
2070                 b->binfo.dir_b = turret_norm;
2071                 break;
2072
2073         default:
2074                 break;
2075         }
2076 }
2077
2078 // aim the beam (setup last_start and last_shot - the endpoints). also recalculates collision pairs
2079 void beam_aim(beam *b)
2080 {
2081         vector temp, p2;
2082         int model_num;  
2083         
2084         // type C beam weapons have no target
2085         if(b->target == NULL){
2086                 Assert(b->type == BEAM_TYPE_C);
2087                 if(b->type != BEAM_TYPE_C){
2088                         return;
2089                 }
2090         }
2091         // get a model # to work with
2092         else {
2093                 model_num = beam_get_model(b->target);  
2094                 if(model_num < 0){
2095                         return;
2096                 }       
2097         }
2098
2099         // setup our initial shot point and aim direction
2100         switch(b->type){
2101         case BEAM_TYPE_A:       
2102                 // where the shot is originating from (b->last_start gets filled in)
2103                 ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2);
2104
2105                 // if we're targeting a subsystem - shoot directly at it
2106                 if(b->target_subsys != NULL){                   
2107                         // unrotate the center of the subsystem
2108                         vm_vec_unrotate(&b->last_shot, &b->target_subsys->system_info->pnt, &b->target->orient);
2109                         vm_vec_add2(&b->last_shot, &b->target->pos);             
2110                         vm_vec_sub(&temp, &b->last_shot, &b->last_start);
2111                         
2112                         vm_vec_scale_add(&b->last_shot, &b->last_start, &temp, 2.0f);
2113                         break;
2114                 }
2115
2116                 // if we're shooting at a big ship - shoot directly at the model
2117                 if((b->target->type == OBJ_SHIP) && (Ship_info[Ships[b->target->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2118                         // rotate into world coords
2119                         vm_vec_unrotate(&temp, &b->binfo.dir_a, &b->target->orient);
2120                         vm_vec_add2(&temp, &b->target->pos);
2121
2122                         // get the shot point
2123                         vm_vec_sub(&p2, &temp, &b->last_start);
2124                         vm_vec_scale_add(&b->last_shot, &b->last_start, &p2, 2.0f);
2125                         break;
2126                 }
2127                 
2128                 // point at the center of the target, then jitter based on shot_aim
2129                 b->last_shot = b->target->pos;          
2130                 beam_jitter_aim(b, b->binfo.shot_aim[0]);
2131                 break;  
2132
2133         case BEAM_TYPE_B:               
2134                 // where the shot is originating from (b->last_start gets filled in)
2135                 ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2);                             
2136
2137                 // set the shot point
2138                 vm_vec_scale_add(&b->last_shot, &b->last_start, &b->binfo.dir_a, BEAM_FAR_LENGTH);
2139                 Assert(is_valid_vec(&b->last_shot));            
2140                 break;
2141
2142         case BEAM_TYPE_C:
2143                 // start point
2144                 temp = b->targeting_laser_offset;       
2145                 vm_vec_unrotate(&b->last_start, &temp, &b->objp->orient);
2146                 vm_vec_add2(&b->last_start, &b->objp->pos);
2147                 vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.fvec, BEAM_FAR_LENGTH);                
2148                 break;
2149
2150         case BEAM_TYPE_D:                               
2151                 // where the shot is originating from (b->last_start gets filled in)
2152                 ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2);             
2153                 
2154                 // point at the center of the target, then jitter based on shot_aim
2155                 b->last_shot = b->target->pos;          
2156                 beam_jitter_aim(b, b->binfo.shot_aim[b->shot_index]);
2157                 nprintf(("AI", "Frame %i: FIRING\n", Framecount));
2158                 break;
2159
2160         case BEAM_TYPE_E:
2161                 // where the shot is originating from (b->last_start gets filled in)
2162                 ship_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2);             
2163
2164                 // point directly in the direction of the turret
2165                 vm_vec_scale_add(&b->last_shot, &b->last_start, &temp, BEAM_FAR_LENGTH);
2166                 break;
2167
2168         default:
2169                 Int3();
2170         }               
2171
2172         // recalculate object pairs
2173         OBJ_RECALC_PAIRS((&Objects[b->objnum]));
2174 }
2175
2176 // given a model #, and an object, stuff 2 good world coord points
2177 void beam_get_octant_points(int modelnum, object *objp, int oct_index, int oct_array[BEAM_NUM_GOOD_OCTANTS][4], vector *v1, vector *v2)
2178 {       
2179         vector t1, t2, temp;
2180         polymodel *m = model_get(modelnum);
2181
2182         // bad bad bad bad bad bad
2183         if(m == NULL){
2184                 Int3();
2185                 return;
2186         }
2187
2188         Assert((oct_index >= 0) && (oct_index < BEAM_NUM_GOOD_OCTANTS));
2189
2190         // randomly pick octants        
2191         t1 = oct_array[oct_index][2] ? m->octants[oct_array[oct_index][0]].max : m->octants[oct_array[oct_index][0]].min;
2192         t2 = oct_array[oct_index][3] ? m->octants[oct_array[oct_index][1]].max : m->octants[oct_array[oct_index][1]].min;
2193         Assert(!vm_vec_same(&t1, &t2));
2194
2195         // get them in world coords
2196         vm_vec_unrotate(&temp, &t1, &objp->orient);
2197         vm_vec_add(v1, &temp, &objp->pos);
2198         vm_vec_unrotate(&temp, &t2, &objp->orient);
2199         vm_vec_add(v2, &temp, &objp->pos);
2200 }
2201
2202 // throw some jitter into the aim - based upon shot_aim
2203 void beam_jitter_aim(beam *b, float aim)
2204 {
2205         vector forward, circle;
2206         matrix m;
2207         float subsys_strength;
2208
2209         // if the weapons subsystem is damaged or destroyed
2210         if((b->objp != NULL) && (b->objp->signature == b->sig) && (b->objp->type == OBJ_SHIP) && (b->objp->instance >= 0) && (b->objp->instance < MAX_SHIPS)){
2211                 // get subsytem strength
2212                 subsys_strength = ship_get_subsystem_strength(&Ships[b->objp->instance], SUBSYSTEM_WEAPONS);
2213                 
2214                 // when subsytem strength is 0, double the aim error factor
2215                 aim += aim * (1.0f - subsys_strength);
2216         }
2217
2218         // shot aim is a direct linear factor of the target model's radius.
2219         // so, pick a random point on the circle
2220         vm_vec_sub(&forward, &b->last_shot, &b->last_start);
2221         vm_vec_normalize_quick(&forward);
2222         
2223         // vector
2224         vm_vector_2_matrix(&m, &forward, NULL, NULL);
2225
2226         // get a vector on the circle - this should appear to be pretty random
2227         // vm_vec_scale_add(&circle, &b->last_shot, &m.rvec, aim * b->target->radius);
2228         vm_vec_random_in_circle(&circle, &b->last_shot, &m, aim * b->target->radius, 0);
2229         
2230         // get the vector pointing to the circle point
2231         vm_vec_sub(&forward, &circle, &b->last_start);  
2232         vm_vec_scale_add(&b->last_shot, &b->last_start, &forward, 2.0f);
2233 }
2234
2235
2236 // -----------------------------===========================------------------------------
2237 // BEAM COLLISION FUNCTIONS
2238 // -----------------------------===========================------------------------------
2239
2240 // collide a beam with a ship, returns 1 if we can ignore all future collisions between the 2 objects
2241 int beam_collide_ship(obj_pair *pair)
2242 {
2243         beam *b;                
2244         ship *shipp;
2245         ship_info *sip;
2246         mc_info test_collide;           
2247         int model_num;  
2248         float widest;
2249
2250         // bogus
2251         if(pair == NULL){
2252                 return 0;
2253         }
2254
2255         // get the beam
2256         Assert(pair->a->instance >= 0);
2257         Assert(pair->a->type == OBJ_BEAM);
2258         Assert(Beams[pair->a->instance].objnum == OBJ_INDEX(pair->a));
2259         b = &Beams[pair->a->instance];
2260
2261         // Don't check collisions for warping out player if past stage 1.
2262         if ( Player->control_mode >= PCM_WARPOUT_STAGE1)        {
2263                 if ( pair->a == Player_obj ) return 0;
2264                 if ( pair->b == Player_obj ) return 0;
2265         }
2266
2267         // if the "warming up" timestamp has not expired
2268         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){               
2269                 return 0;
2270         }
2271
2272         // if the beam is on "safety", don't collide with anything
2273         if(b->flags & BF_SAFETY){
2274                 return 0;
2275         }
2276         
2277         // if the colliding object is the shooting object, return 1 so this is culled
2278         if(pair->b == b->objp){
2279                 return 1;
2280         }       
2281
2282         // try and get a model
2283         model_num = beam_get_model(pair->b);
2284         if(model_num < 0){
2285                 // Int3();
2286                 return 1;
2287         }
2288         
2289 #ifndef NDEBUG
2290         Beam_test_ints++;
2291         Beam_test_ship++;
2292 #endif
2293
2294         // bad
2295         Assert(pair->b->type == OBJ_SHIP);
2296         Assert(pair->b->instance >= 0);
2297         if((pair->b->type != OBJ_SHIP) || (pair->b->instance < 0)){
2298                 return 1;
2299         }
2300         shipp = &Ships[pair->b->instance];
2301         sip = &Ship_info[shipp->ship_info_index];
2302
2303         // get the widest portion of the beam
2304         widest = beam_get_widest(b);
2305
2306         // do the collision             
2307         test_collide.model_num = model_num;
2308         test_collide.submodel_num = -1;
2309         test_collide.orient = &pair->b->orient;
2310         test_collide.pos = &pair->b->pos;
2311         test_collide.p0 = &b->last_start;
2312         test_collide.p1 = &b->last_shot;
2313
2314         // maybe do a sphereline
2315         if(widest > pair->b->radius * BEAM_AREA_PERCENT){
2316                 test_collide.radius = beam_get_widest(b) * 0.5f;
2317                 test_collide.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;
2318         } else {        
2319                 test_collide.flags = MC_CHECK_MODEL | MC_CHECK_RAY;     
2320         }
2321         model_collide(&test_collide);
2322
2323         // if we got a hit
2324         if(test_collide.num_hits){
2325                 // add to the collision list
2326                 beam_add_collision(b, pair->b, &test_collide);          
2327         }       
2328
2329         // add this guy to the lighting list
2330         beam_add_light(b, OBJ_INDEX(pair->b), 1, NULL);
2331
2332         // reset timestamp to timeout immediately
2333         pair->next_check_time = timestamp(0);
2334                 
2335         return 0;
2336 }
2337
2338 // collide a beam with an asteroid, returns 1 if we can ignore all future collisions between the 2 objects
2339 int beam_collide_asteroid(obj_pair *pair)
2340 {
2341         beam *b;                
2342         mc_info test_collide;           
2343         int model_num;
2344
2345         // bogus
2346         if(pair == NULL){
2347                 return 0;
2348         }
2349
2350         // get the beam
2351         Assert(pair->a->instance >= 0);
2352         Assert(pair->a->type == OBJ_BEAM);
2353         Assert(Beams[pair->a->instance].objnum == OBJ_INDEX(pair->a));
2354         b = &Beams[pair->a->instance];
2355
2356         // if the "warming up" timestamp has not expired
2357         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
2358                 return 0;
2359         }
2360
2361         // if the beam is on "safety", don't collide with anything
2362         if(b->flags & BF_SAFETY){
2363                 return 0;
2364         }
2365         
2366         // if the colliding object is the shooting object, return 1 so this is culled
2367         if(pair->b == b->objp){
2368                 return 1;
2369         }       
2370
2371         // try and get a model
2372         model_num = beam_get_model(pair->b);
2373         if(model_num < 0){
2374                 Int3();
2375                 return 1;
2376         }       
2377
2378 #ifndef NDEBUG
2379         Beam_test_ints++;
2380         Beam_test_ast++;
2381 #endif
2382
2383         // do the collision             
2384         test_collide.model_num = model_num;
2385         test_collide.submodel_num = -1;
2386         test_collide.orient = &pair->b->orient;
2387         test_collide.pos = &pair->b->pos;
2388         test_collide.p0 = &b->last_start;
2389         test_collide.p1 = &b->last_shot;        
2390         test_collide.flags = MC_CHECK_MODEL | MC_CHECK_RAY;
2391         model_collide(&test_collide);
2392
2393         // if we got a hit
2394         if(test_collide.num_hits){
2395                 // add to the collision list
2396                 beam_add_collision(b, pair->b, &test_collide);
2397         }       
2398
2399         // add this guy to the lighting list
2400         beam_add_light(b, OBJ_INDEX(pair->b), 1, NULL);
2401
2402         // reset timestamp to timeout immediately
2403         pair->next_check_time = timestamp(0);
2404                 
2405         return 0;       
2406 }
2407
2408 // collide a beam with a missile, returns 1 if we can ignore all future collisions between the 2 objects
2409 int beam_collide_missile(obj_pair *pair)
2410 {
2411         beam *b;        
2412         mc_info test_collide;           
2413         int model_num;
2414
2415         // bogus
2416         if(pair == NULL){
2417                 return 0;
2418         }
2419
2420         // get the beam
2421         Assert(pair->a->instance >= 0);
2422         Assert(pair->a->type == OBJ_BEAM);
2423         Assert(Beams[pair->a->instance].objnum == OBJ_INDEX(pair->a));
2424         b = &Beams[pair->a->instance];
2425
2426         // if the "warming up" timestamp has not expired
2427         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
2428                 return 0;
2429         }
2430
2431         // if the beam is on "safety", don't collide with anything
2432         if(b->flags & BF_SAFETY){
2433                 return 0;
2434         }
2435         
2436         // if the colliding object is the shooting object, return 1 so this is culled
2437         if(pair->b == b->objp){
2438                 return 1;
2439         }       
2440
2441         // try and get a model
2442         model_num = beam_get_model(pair->b);
2443         if(model_num < 0){
2444                 //Int3();
2445                 return 1;
2446         }
2447
2448 #ifndef NDEBUG
2449         Beam_test_ints++;
2450 #endif
2451
2452         // do the collision             
2453         test_collide.model_num = model_num;
2454         test_collide.submodel_num = -1;
2455         test_collide.orient = &pair->b->orient;
2456         test_collide.pos = &pair->b->pos;
2457         test_collide.p0 = &b->last_start;
2458         test_collide.p1 = &b->last_shot;
2459         test_collide.flags = MC_CHECK_MODEL | MC_CHECK_RAY;
2460         model_collide(&test_collide);
2461
2462         // if we got a hit
2463         if(test_collide.num_hits){
2464                 // add to the collision list
2465                 beam_add_collision(b, pair->b, &test_collide);
2466         }
2467
2468         // reset timestamp to timeout immediately
2469         pair->next_check_time = timestamp(0);
2470
2471         return 0;
2472 }
2473
2474 // collide a beam with debris, returns 1 if we can ignore all future collisions between the 2 objects
2475 int beam_collide_debris(obj_pair *pair)
2476 {       
2477         beam *b;        
2478         mc_info test_collide;           
2479         int model_num;
2480
2481         // bogus
2482         if(pair == NULL){
2483                 return 0;
2484         }
2485
2486         // get the beam
2487         Assert(pair->a->instance >= 0);
2488         Assert(pair->a->type == OBJ_BEAM);
2489         Assert(Beams[pair->a->instance].objnum == OBJ_INDEX(pair->a));
2490         b = &Beams[pair->a->instance];
2491
2492         // if the "warming up" timestamp has not expired
2493         if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
2494                 return 0;
2495         }
2496
2497         // if the beam is on "safety", don't collide with anything
2498         if(b->flags & BF_SAFETY){
2499                 return 0;
2500         }
2501         
2502         // if the colliding object is the shooting object, return 1 so this is culled
2503         if(pair->b == b->objp){
2504                 return 1;
2505         }       
2506
2507         // try and get a model
2508         model_num = beam_get_model(pair->b);
2509         if(model_num < 0){
2510                 // Int3();
2511                 return 1;
2512         }       
2513
2514 #ifndef NDEBUG
2515         Beam_test_ints++;
2516 #endif
2517
2518         // do the collision             
2519         test_collide.model_num = model_num;
2520         test_collide.submodel_num = -1;
2521         test_collide.orient = &pair->b->orient;
2522         test_collide.pos = &pair->b->pos;
2523         test_collide.p0 = &b->last_start;
2524         test_collide.p1 = &b->last_shot;
2525         test_collide.flags = MC_CHECK_MODEL | MC_CHECK_RAY;
2526         model_collide(&test_collide);
2527
2528         // if we got a hit
2529         if(test_collide.num_hits){
2530                 // add to the collision list
2531                 beam_add_collision(b, pair->b, &test_collide);
2532         }       
2533
2534         // add this guy to the lighting list
2535         beam_add_light(b, OBJ_INDEX(pair->b), 1, NULL);
2536
2537         // reset timestamp to timeout immediately
2538         pair->next_check_time = timestamp(0);
2539
2540         return 0;
2541 }
2542
2543 // early-out function for when adding object collision pairs, return 1 if the pair should be ignored
2544 int beam_collide_early_out(object *a, object *b)
2545 {
2546         beam *bm;
2547         weapon_info *bwi;
2548         float cull_dist, cull_dot;
2549         vector dot_test, dot_test2, dist_test;  
2550                 
2551         // get the beam
2552         Assert(a->instance >= 0);
2553         if(a->instance < 0){
2554                 return 1;
2555         }
2556         Assert(a->type == OBJ_BEAM);
2557         if(a->type != OBJ_BEAM){
2558                 return 1;
2559         }
2560         Assert(Beams[a->instance].objnum == OBJ_INDEX(a));
2561         if(Beams[a->instance].objnum != OBJ_INDEX(a)){
2562                 return 1;
2563         }       
2564         bm = &Beams[a->instance];
2565         Assert(bm->weapon_info_index >= 0);
2566         if(bm->weapon_info_index < 0){
2567                 return 1;
2568         }
2569         bwi = &Weapon_info[bm->weapon_info_index];
2570
2571         // if the second object has an invalid instance, bail
2572         if(b->instance < 0){
2573                 return 1;
2574         }
2575
2576         // baseline bails
2577         switch(b->type){
2578         case OBJ_SHIP:
2579                 break;
2580         case OBJ_ASTEROID:
2581                 // targeting lasers only hit ships
2582                 if(bwi->b_info.beam_type == BEAM_TYPE_C){
2583                         return 1;
2584                 }
2585                 break;
2586         case OBJ_DEBRIS:
2587                 // targeting lasers only hit ships
2588                 if(bwi->b_info.beam_type == BEAM_TYPE_C){
2589                         return 1;
2590                 }
2591                 // don't ever collide with non hull pieces
2592                 if(!Debris[b->instance].is_hull){
2593                         return 1;
2594                 }
2595                 break;
2596         case OBJ_WEAPON:
2597                 // targeting lasers only hit ships
2598                 if(bwi->b_info.beam_type == BEAM_TYPE_C){
2599                         return 1;
2600                 }
2601                 // don't ever collide against laser weapons - duh
2602                 if(Weapon_info[Weapons[b->instance].weapon_info_index].subtype == WP_LASER){
2603                         return 1;
2604                 }
2605                 break;
2606         }
2607
2608         // get full cull value
2609         beam_get_cull_vals(b, bm, &cull_dot, &cull_dist);
2610
2611         // if the object fails these conditions, bail
2612         vm_vec_sub(&dist_test, &b->pos, &bm->last_start);
2613         dot_test = dist_test;
2614         vm_vec_sub(&dot_test2, &bm->last_shot, &bm->last_start);
2615         vm_vec_normalize_quick(&dot_test);
2616         vm_vec_normalize_quick(&dot_test2);
2617         // cull_dist == DIST SQUARED FOO!
2618         if((vm_vec_dotprod(&dot_test, &dot_test2) < cull_dot) && (vm_vec_mag_squared(&dist_test) > cull_dist)){
2619                 return 1;
2620         }
2621         
2622         // don't cull
2623         return 0;
2624 }
2625
2626 // add a collision to the beam for this frame (to be evaluated later)
2627 void beam_add_collision(beam *b, object *hit_object, mc_info *cinfo)
2628 {
2629         beam_collision *bc;
2630         int idx;
2631
2632         // if we haven't reached the limit for beam collisions, just add
2633         if(b->f_collision_count < MAX_FRAME_COLLISIONS){
2634                 bc = &b->f_collisions[b->f_collision_count++];
2635                 bc->c_objnum = OBJ_INDEX(hit_object);
2636                 bc->cinfo = *cinfo;
2637
2638                 // done
2639                 return;
2640         }               
2641
2642         // otherwise, we've got to do some checking, ick. 
2643         // I guess we can always just remove the farthest item
2644         bc = NULL;
2645         for(idx=0; idx<MAX_FRAME_COLLISIONS; idx++){
2646                 if((bc == NULL) || (b->f_collisions[idx].cinfo.hit_dist > bc->cinfo.hit_dist)){
2647                         bc = &b->f_collisions[idx];
2648                 }
2649         }
2650
2651         // copy in
2652         Assert(bc != NULL);
2653         if(bc == NULL){
2654                 return;
2655         }
2656         bc->c_objnum = OBJ_INDEX(hit_object);
2657         bc->cinfo = *cinfo;
2658 }
2659
2660 // sort collisions for the frame
2661 int beam_sort_collisions_func(const void *e1, const void *e2)
2662 {
2663         beam_collision *b1 = (beam_collision*)e1;
2664         beam_collision *b2 = (beam_collision*)e2;
2665
2666         return b1->cinfo.hit_dist < b2->cinfo.hit_dist ? -1 : 1;
2667 }
2668
2669 // handle a hit on a specific object
2670 void beam_handle_collisions(beam *b)
2671 {       
2672         int idx, s_idx;
2673         beam_collision r_coll[MAX_FRAME_COLLISIONS];
2674         int r_coll_count = 0;
2675         beam_weapon_info *bwi;
2676         weapon_info *wi;
2677         float widest;   
2678
2679         // early out if we had no collisions
2680         if(b->f_collision_count <= 0){
2681                 return;
2682         }
2683
2684         // get beam weapon info
2685         if((b->weapon_info_index < 0) || (b->weapon_info_index >= Num_weapon_types)){
2686                 Int3();
2687                 return;
2688         }
2689         bwi = &Weapon_info[b->weapon_info_index].b_info;
2690         wi = &Weapon_info[b->weapon_info_index];
2691
2692         // get the widest part of the beam
2693         widest = beam_get_widest(b);
2694
2695         // the first thing we need to do is sort the collisions, from closest to farthest
2696         qsort(b->f_collisions, b->f_collision_count, sizeof(beam_collision), beam_sort_collisions_func);
2697
2698         // now apply all collisions until we reach a ship which "stops" the beam or we reach the end of the list
2699         for(idx=0; idx<b->f_collision_count; idx++){    
2700                 int model_num = -1;
2701                 int do_damage = 0;
2702                 int first_hit = 1;
2703                 int target = b->f_collisions[idx].c_objnum;
2704
2705                 // if we have an invalid object
2706                 if((target < 0) || (target >= MAX_OBJECTS)){
2707                         continue;
2708                 }
2709
2710                 // try and get a model to deal with             
2711                 model_num = beam_get_model(&Objects[target]);
2712                 if(model_num < 0){
2713                         continue;
2714                 }
2715
2716                 // add lighting
2717                 beam_add_light(b, target, 2, &b->f_collisions[idx].cinfo.hit_point_world);
2718
2719                 // add to the recent collision list
2720                 r_coll[r_coll_count].c_objnum = target;
2721                 r_coll[r_coll_count].c_sig = Objects[target].signature;
2722                 r_coll[r_coll_count].c_stamp = -1;
2723                 r_coll[r_coll_count].cinfo = b->f_collisions[idx].cinfo;
2724                 
2725                 // if he was already on the recent collision list, copy his timestamp
2726                 // also, be sure not to play the impact sound again.
2727                 for(s_idx=0; s_idx<b->r_collision_count; s_idx++){
2728                         if((r_coll[r_coll_count].c_objnum == b->r_collisions[s_idx].c_objnum) && (r_coll[r_coll_count].c_sig == b->r_collisions[s_idx].c_sig)){
2729                                 // timestamp
2730                                 r_coll[r_coll_count].c_stamp = b->r_collisions[s_idx].c_stamp;
2731
2732                                 // don't play the impact sound again
2733                                 first_hit = 0;
2734                         }
2735                 }
2736                                 
2737                 // if the damage timestamp has expired or is not set yet, apply damage
2738                 if((r_coll[r_coll_count].c_stamp == -1) || timestamp_elapsed(r_coll[r_coll_count].c_stamp)){
2739                         do_damage = 1;
2740                         r_coll[r_coll_count].c_stamp = timestamp(BEAM_DAMAGE_TIME);
2741                 }
2742
2743                 // if no damage - don't even indicate it has been hit
2744                 if(wi->damage <= 0){
2745                         do_damage = 0;
2746                 }
2747
2748                 // increment collision count
2749                 r_coll_count++;         
2750
2751                 // play the impact sound
2752                 if(first_hit){
2753                         snd_play_3d( &Snds[wi->impact_snd], &b->f_collisions[idx].cinfo.hit_point_world, &Eye_position );
2754                 }
2755                 
2756                 // do damage
2757                 if(do_damage && !physics_paused){               
2758                         // maybe draw an explosion
2759                         if(wi->impact_weapon_expl_index >= 0){
2760                                 int ani_handle = weapon_get_expl_handle(wi->impact_weapon_expl_index, &b->f_collisions[idx].cinfo.hit_point_world, wi->impact_explosion_radius);
2761                                 particle_create( &b->f_collisions[idx].cinfo.hit_point_world, &vmd_zero_vector, 0.0f, wi->impact_explosion_radius, PARTICLE_BITMAP_PERSISTENT, ani_handle );
2762                         }
2763
2764                         switch(Objects[target].type){
2765                         case OBJ_DEBRIS:
2766                                 // hit the debris - the debris hit code takes care of checking for MULTIPLAYER_CLIENT, etc
2767                                 debris_hit(&Objects[target], &Objects[b->objnum], &b->f_collisions[idx].cinfo.hit_point_world, Weapon_info[b->weapon_info_index].damage);
2768                                 break;
2769
2770                         case OBJ_WEAPON:
2771                                 // detonate the missile
2772                                 Assert(Weapon_info[Weapons[Objects[target].instance].weapon_info_index].subtype == WP_MISSILE);
2773                                 if(!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER){
2774                                         weapon_hit(&Objects[target], NULL, &Objects[target].pos);
2775                                 }
2776                                 break;
2777
2778                         case OBJ_ASTEROID:
2779                                 // hit the asteroid
2780                                 if(!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER){
2781                                         asteroid_hit(&Objects[target], &Objects[b->objnum], &b->f_collisions[idx].cinfo.hit_point_world, Weapon_info[b->weapon_info_index].damage);
2782                                 }
2783                                 break;
2784
2785                         case OBJ_SHIP:                          
2786                                 // hit the ship - again, the innards of this code handle multiplayer cases
2787                                 // maybe vaporize ship.
2788                                 ship_apply_local_damage(&Objects[target], &Objects[b->objnum], &b->f_collisions[idx].cinfo.hit_point_world, beam_get_ship_damage(b, &Objects[target]), -1);
2789
2790                                 // if this is the first hit on the player ship. whack him                               
2791                                 if(do_damage){
2792                                         beam_apply_whack(b, &Objects[target], &b->f_collisions[idx].cinfo.hit_point_world);
2793                                 }
2794                                 break;
2795                         }                                                                       
2796                 }                               
2797
2798                 // if the radius of the target is somewhat close to the radius of the beam, "stop" the beam here
2799                 // for now : if its smaller than about 1/3 the radius of the ship
2800                 if(widest <= (Objects[target].radius * BEAM_AREA_PERCENT) && !beam_will_tool_target(b, &Objects[target])){      
2801                         // set last_shot so we know where to properly draw the beam             
2802                         b->last_shot = b->f_collisions[idx].cinfo.hit_point_world;
2803                         Assert(is_valid_vec(&b->last_shot));            
2804
2805                         // done wif the beam
2806                         break;
2807                 }
2808         }
2809
2810         // store the new recent collisions
2811         for(idx=0; idx<r_coll_count; idx++){
2812                 b->r_collisions[idx] = r_coll[idx];
2813         }
2814         b->r_collision_count = r_coll_count;
2815 }
2816
2817 // for a given object, and a firing beam, determine its critical dot product and range
2818 void beam_get_cull_vals(object *objp, beam *b, float *cull_dot, float *cull_dist)
2819 {
2820         switch(objp->type){
2821         // debris and asteroids are classified as slow moving small objects
2822         // use cull_dot == potential cone of beam + 10% and 50.0 meters
2823         case OBJ_DEBRIS:
2824         case OBJ_ASTEROID:
2825                 *cull_dot = 1.0f - ((1.0f - beam_get_cone_dot(b)) * 1.10f);
2826                 *cull_dist = 50.0f * 50.0f;
2827                 return;
2828
2829         // treat missiles as fast-moving small objects
2830         case OBJ_WEAPON:
2831                 *cull_dot = 1.0f - ((1.0f - beam_get_cone_dot(b)) * 1.5f);
2832                 *cull_dist = 300.0f * 300.0f;
2833                 return;
2834
2835         case OBJ_SHIP:
2836                 // for cap ships, only cull for 90deg or better
2837                 /*
2838                 if(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_CAPITAL){
2839                         *cull_dot = 0.0f;
2840                         *cull_dist = 0.0f;
2841                         return;
2842                 }
2843                 */
2844
2845                 // for large ships, cull at some multiple of the radius
2846                 if(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)){
2847                         *cull_dot = 1.0f - ((1.0f - beam_get_cone_dot(b)) * 1.25f);
2848                         
2849                         *cull_dist = (objp->radius * 1.3f) * (objp->radius * 1.3f);
2850                         return;
2851                 }
2852
2853                 // for everthing else, cull the same as missiles
2854                 *cull_dot = 1.0f - ((1.0f - beam_get_cone_dot(b)) * 1.5f);
2855                 *cull_dist = 300.0f * 300.0f;
2856                 return;
2857         }
2858
2859         // BAD BAD BAD - but this code will cause everything to cull properly
2860         Int3();
2861         *cull_dot = 1.0f;
2862         *cull_dist = 0.0f;
2863         return;
2864 }
2865
2866 // FIXME - make sure we are truthfull representing the "cone" for all beam types
2867 // get the total possible cone for a given beam in radians
2868 float beam_get_cone_dot(beam *b)
2869 {
2870         switch(b->type){
2871         case BEAM_TYPE_A:
2872         case BEAM_TYPE_C:
2873         case BEAM_TYPE_D:
2874         case BEAM_TYPE_E:
2875                 // even though these beams don't move, return a _very_ small value
2876                 return (float)cos(fl_radian(50.5f));    
2877                 
2878         case BEAM_TYPE_B:
2879                 return vm_vec_dotprod(&b->binfo.dir_a, &b->binfo.dir_b);
2880
2881         default:
2882                 Int3();
2883         }
2884
2885         Int3();
2886         return 0.0f;
2887 }
2888
2889 // if it is legal for the beam to fire, or continue firing
2890 int beam_ok_to_fire(beam *b)
2891 {
2892         // type C beams are ok to fire all the time
2893         if(Weapon_info[b->weapon_info_index].b_info.beam_type == BEAM_TYPE_C){
2894                 return 1;
2895         }
2896
2897         // if my own object is invalid, stop firing
2898         if(b->objp->signature != b->sig){
2899                 mprintf(("BEAM : killing beam because of invalid parent object SIGNATURE!\n"));
2900                 return -1;
2901         }
2902
2903         // if my own object is a ghost
2904         if(b->objp->type != OBJ_SHIP){
2905                 mprintf(("BEAM : killing beam because of invalid parent object TYPE!\n"));
2906                 return -1;
2907         }       
2908
2909         // if the shooting turret is destroyed  
2910         if(b->subsys->current_hits <= 0.0f){            
2911                 mprintf(("BEAM : killing beam because turret has been destroyed!\n"));
2912                 return -1;
2913         }               
2914
2915         // if the beam will be firing out of its FOV, power it down
2916         vector aim_dir, temp;
2917         vector turret_dir, turret_pos;
2918         vm_vec_sub(&aim_dir, &b->last_shot, &b->last_start);
2919         vm_vec_normalize(&aim_dir);
2920         ship_get_global_turret_gun_info(b->objp, b->subsys, &turret_pos, &turret_dir, 1, &temp);
2921         if(vm_vec_dotprod(&aim_dir, &turret_dir) < b->subsys->system_info->turret_fov){
2922                 mprintf(("BEAM : powering beam down because of FOV condition!\n"));
2923                 return 0;
2924         }
2925
2926         // ok to fire/continue firing
2927         return 1;
2928 }
2929
2930 // get the width of the widest section of the beam
2931 float beam_get_widest(beam *b)
2932 {
2933         int idx;
2934         float widest = -1.0f;
2935
2936         // sanity
2937         Assert(b->weapon_info_index >= 0);
2938         if(b->weapon_info_index < 0){
2939                 return -1.0f;
2940         }
2941
2942         // lookup
2943         for(idx=0; idx<Weapon_info[b->weapon_info_index].b_info.beam_num_sections; idx++){
2944                 if(Weapon_info[b->weapon_info_index].b_info.sections[idx].width > widest){
2945                         widest = Weapon_info[b->weapon_info_index].b_info.sections[idx].width;
2946                 }
2947         }
2948
2949         // return       
2950         return widest * b->shrink;
2951 }  
2952
2953 // apply a whack to a ship
2954 void beam_apply_whack(beam *b, object *objp, vector *hit_point)
2955 {
2956         weapon_info *wip;       
2957         ship *shipp;
2958
2959         // sanity
2960         Assert((b != NULL) && (objp != NULL) && (hit_point != NULL));
2961         if((b == NULL) || (objp == NULL) || (hit_point == NULL)){
2962                 return;
2963         }       
2964         Assert(b->weapon_info_index >= 0);
2965         wip = &Weapon_info[b->weapon_info_index];       
2966         Assert((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >= 0) && (objp->instance < MAX_SHIPS));
2967         if((objp == NULL) || (objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2968                 return;
2969         }
2970         shipp = &Ships[objp->instance];
2971         if((shipp->ai_index < 0) || (shipp->ai_index >= MAX_AI_INFO)){
2972                 return;
2973         }
2974
2975         // don't whack docking ships
2976         if(Ai_info[shipp->ai_index].ai_flags & AIF_DOCKED){
2977                 return;
2978         }
2979
2980         // determine how big of a whack to apply
2981         float whack;
2982         float dist;
2983
2984         if(wip->damage < b_whack_damage){
2985                 whack = b_whack_small;
2986         } else {
2987                 whack = b_whack_big;
2988         }
2989
2990         // whack direction
2991         vector whack_dir, temp;
2992         vm_vec_dist_to_line(&objp->pos, &b->last_start, &b->last_shot, &temp, &dist);
2993         vm_vec_sub(&whack_dir, &objp->pos, &temp);
2994         vm_vec_normalize(&whack_dir);
2995         vm_vec_scale(&whack_dir, whack);
2996
2997         // apply the whack
2998         ship_apply_whack(&whack_dir, hit_point, objp);
2999 }
3000
3001 // return the amount of damage which should be applied to a ship. basically, filters friendly fire damage 
3002 float beam_get_ship_damage(beam *b, object *objp)
3003 {       
3004         // if the beam is on the same team as the object
3005         Assert((objp != NULL) && (b != NULL));
3006         if((objp == NULL) || (b == NULL)){
3007                 return 0.0f;
3008         }
3009         Assert((objp->type == OBJ_SHIP) && (objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3010         if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
3011                 return 0.0f;
3012         }
3013
3014         // same team. yikes
3015         if((b->team == Ships[objp->instance].team) && (Weapon_info[b->weapon_info_index].damage > Beam_friendly_cap[Game_skill_level])){
3016                 return Beam_friendly_cap[Game_skill_level];
3017         }
3018
3019         // normal damage
3020         return Weapon_info[b->weapon_info_index].damage;
3021 }
3022
3023 // if the beam is likely to tool a given target before its lifetime expires
3024 int beam_will_tool_target(beam *b, object *objp)
3025 {
3026         weapon_info *wip = &Weapon_info[b->weapon_info_index];
3027         float damage_in_a_few_seconds;
3028
3029         // sanity
3030         if(objp == NULL){
3031                 return 0;
3032         }
3033
3034         // if the object is not a ship, bail
3035         if(objp->type != OBJ_SHIP){
3036                 return 0;
3037         }
3038         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
3039                 return 0;
3040         }
3041
3042         // if the beam is going to apply more damage in about 1 and a half than the hull of the ship can take
3043         damage_in_a_few_seconds = (TOOLTIME / (float)BEAM_DAMAGE_TIME) * wip->damage;
3044         if(objp->hull_strength < damage_in_a_few_seconds){
3045
3046                 // tooled
3047                 return 1;
3048         }
3049
3050         return 0;
3051 }
3052
3053 float beam_accuracy = 1.0f;
3054 DCF(b_aim, "")
3055 {
3056         dc_get_arg(ARG_FLOAT);
3057         beam_accuracy = Dc_arg_float;
3058 }
3059 DCF(beam_list, "")
3060 {
3061         int idx;
3062         int b_count = 0;
3063
3064         for(idx=0; idx<Num_weapon_types; idx++){
3065                 if(Weapon_info[idx].wi_flags & WIF_BEAM){                       
3066                         b_count++;
3067                         dc_printf("Beam %d : %s\n", b_count, Weapon_info[idx].name);
3068                 }
3069         }
3070 }
3071 void beam_test(int whee)
3072 {
3073         int s1, s2;
3074         object *orion, *fenris;
3075         ship_subsys *orion_turret, *fenris_turret, *fenris_radar, *orion_radar, *lookup;
3076         beam_fire_info f;
3077
3078         nprintf(("General", "Running beam test\n"));
3079
3080         // lookup some stuff 
3081         s1 = ship_name_lookup("GTD Orion 1");
3082         Assert(s1 >= 0);
3083         orion = &Objects[Ships[s1].objnum];
3084         s2 = ship_name_lookup("GTC Fenris 2");
3085         Assert(s2 >= 0);
3086         fenris = &Objects[Ships[s2].objnum];            
3087
3088         // get beam weapons
3089         lookup = GET_FIRST(&Ships[s1].subsys_list);
3090         orion_turret = NULL;
3091         orion_radar = NULL;
3092         while(lookup != END_OF_LIST(&Ships[s1].subsys_list)){
3093                 // turret               
3094                 if((lookup->system_info->type == SUBSYSTEM_TURRET) && !stricmp(lookup->system_info->subobj_name, "turret07")){
3095                         orion_turret = lookup;                  
3096                 }
3097
3098                 // radar
3099                 if(lookup->system_info->type == SUBSYSTEM_RADAR){
3100                         orion_radar = lookup;
3101                 }
3102
3103                 lookup = GET_NEXT(lookup);
3104         }
3105         Assert(orion_turret != NULL);
3106         Assert(orion_radar != NULL);
3107         lookup = GET_FIRST(&Ships[s2].subsys_list);
3108         fenris_turret = NULL;
3109         fenris_radar = NULL;
3110         while(lookup != END_OF_LIST(&Ships[s2].subsys_list)){
3111                 // turret
3112                 if((lookup->system_info->type == SUBSYSTEM_TURRET) && !stricmp(lookup->system_info->subobj_name, "turret07")){
3113                         fenris_turret = lookup;                 
3114                 }
3115
3116                 // radar
3117                 if(lookup->system_info->type == SUBSYSTEM_RADAR){
3118                         fenris_radar = lookup;
3119                 }
3120
3121                 lookup = GET_NEXT(lookup);
3122         }
3123         Assert(fenris_turret != NULL);  
3124         Assert(fenris_radar != NULL);
3125
3126         memset(&f, 0, sizeof(beam_fire_info));
3127         f.accuracy = beam_accuracy;
3128         f.beam_info_index = -1;
3129         f.beam_info_override = NULL;
3130         f.shooter = orion;
3131         f.target = fenris;
3132         f.target_subsys = fenris_turret;
3133         f.turret = orion_turret;
3134
3135         // find the first beam
3136         int idx;        
3137         int beam_first = -1;
3138         int beam_count = 0;
3139
3140         for(idx=0; idx<Num_weapon_types; idx++){
3141                 if(Weapon_info[idx].wi_flags & WIF_BEAM){                       
3142                         beam_count++;
3143                         if(beam_count > 1){
3144                                 beam_first = idx;
3145                                 break;
3146                         }
3147                 }
3148         }       
3149         if(beam_first < 0){
3150                 return;
3151         }
3152         
3153         // maybe fire it, if its valid
3154         f.beam_info_index = beam_first + whee - 1;
3155         if(Weapon_info[f.beam_info_index].wi_flags & WIF_BEAM){
3156                 HUD_printf("Firing %s\n", Weapon_info[f.beam_info_index].name);
3157                 beam_fire(&f);
3158         }
3159 }
3160
3161 void beam_test_new(int whee)
3162 {
3163         int s1, s2, s3;
3164         object *orion, *fenris, *herc2, *herc3, *herc6, *alpha;
3165         ship_subsys *orion_turret, *fenris_turret, *fenris_radar, *orion_radar, *lookup;
3166         beam_fire_info f;
3167
3168         nprintf(("General", "Running beam test\n"));
3169
3170         // lookup some stuff 
3171         s1 = ship_name_lookup("GTD Orion 1");
3172         Assert(s1 >= 0);
3173         orion = &Objects[Ships[s1].objnum];
3174         s2 = ship_name_lookup("GTC Fenris 2");
3175         Assert(s2 >= 0);
3176         fenris = &Objects[Ships[s2].objnum];    
3177         s3 = ship_name_lookup("GTF Hercules 2");
3178         Assert(s3 >= 0);
3179         herc2 = &Objects[Ships[s3].objnum];
3180         s3 = ship_name_lookup("GTF Hercules 3");
3181         Assert(s3 >= 0);
3182         herc3 = &Objects[Ships[s3].objnum];
3183         s3 = ship_name_lookup("GTF Hercules 6");
3184         Assert(s3 >= 0);
3185         herc6 = &Objects[Ships[s3].objnum];
3186         s3 = ship_name_lookup("Alpha 1");
3187         Assert(s3 >= 0);
3188         alpha = &Objects[Ships[s3].objnum];     
3189
3190         // get beam weapons
3191         lookup = GET_FIRST(&Ships[s1].subsys_list);
3192         orion_turret = NULL;
3193         orion_radar = NULL;
3194         while(lookup != END_OF_LIST(&Ships[s1].subsys_list)){
3195                 // turret               
3196                 if((lookup->system_info->type == SUBSYSTEM_TURRET) && !stricmp(lookup->system_info->subobj_name, "turret07")){
3197                         orion_turret = lookup;                  
3198                 }
3199
3200                 // radar
3201                 if(lookup->system_info->type == SUBSYSTEM_RADAR){
3202                         orion_radar = lookup;
3203                 }
3204
3205                 lookup = GET_NEXT(lookup);
3206         }
3207         Assert(orion_turret != NULL);
3208         Assert(orion_radar != NULL);
3209         lookup = GET_FIRST(&Ships[s2].subsys_list);
3210         fenris_turret = NULL;
3211         fenris_radar = NULL;
3212         while(lookup != END_OF_LIST(&Ships[s2].subsys_list)){
3213                 // turret
3214                 if((lookup->system_info->type == SUBSYSTEM_TURRET) && !stricmp(lookup->system_info->subobj_name, "turret03")){
3215                         fenris_turret = lookup;                 
3216                 }
3217
3218                 // radar
3219                 if(lookup->system_info->type == SUBSYSTEM_RADAR){
3220                         fenris_radar = lookup;
3221                 }
3222
3223                 lookup = GET_NEXT(lookup);
3224         }
3225         Assert(fenris_turret != NULL);  
3226         Assert(fenris_radar != NULL);
3227
3228         memset(&f, 0, sizeof(beam_fire_info));
3229         f.accuracy = beam_accuracy;     
3230         f.beam_info_override = NULL;
3231         f.shooter = fenris;
3232         f.target = alpha;
3233         f.target_subsys = NULL;
3234         f.turret = fenris_turret;
3235         f.num_shots = 3;
3236
3237         // find the first beam
3238         int idx;        
3239         int beam_first = -1;
3240         int beam_count = 0;
3241
3242         for(idx=0; idx<Num_weapon_types; idx++){
3243                 if(Weapon_info[idx].wi_flags & WIF_BEAM){
3244                         beam_count++;
3245                         if(beam_count > 1){
3246                                 beam_first = idx;
3247                                 break;
3248                         }                       
3249                 }
3250         }       
3251         if(beam_first < 0){
3252                 return;
3253         }
3254         
3255         // maybe fire it, if its valid
3256         f.beam_info_index = beam_first + whee - 1;
3257         if(Weapon_info[f.beam_info_index].wi_flags & WIF_BEAM){
3258                 HUD_printf("Firing %s\n", Weapon_info[f.beam_info_index].name);
3259                 beam_fire(&f);
3260         }
3261 }