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