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