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