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