]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/shipfx.cpp
fix compiling issues with lang changes
[taylor/freespace2.git] / src / ship / shipfx.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/Ship/ShipFX.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Routines for ship effects (as in special)
16  *
17  * $Log$
18  * Revision 1.7  2004/07/04 11:39:06  taylor
19  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
20  *
21  * Revision 1.6  2003/05/25 02:30:44  taylor
22  * Freespace 1 support
23  *
24  * Revision 1.5  2002/09/04 01:12:11  relnev
25  * changes to screen backup/mouse drawing code.  removed a few warnings.
26  *
27  * Revision 1.4  2002/06/17 06:33:11  relnev
28  * ryan's struct patch for gcc 2.95
29  *
30  * Revision 1.3  2002/06/09 04:41:26  relnev
31  * added copyright header
32  *
33  * Revision 1.2  2002/05/07 03:16:52  theoddone33
34  * The Great Newline Fix
35  *
36  * Revision 1.1.1.1  2002/05/03 03:28:10  root
37  * Initial import.
38  *
39  * 
40  * 51    9/13/99 4:53p Andsager
41  * 
42  * 50    9/13/99 10:09a Andsager
43  * Add debug console commands to lower model render detail and fireball
44  * LOD for big ship explosiosns.
45  * 
46  * 49    9/08/99 10:44p Andsager
47  * Make HUGE ships not die when warping out, after warp effect started.
48  * 
49  * 48    9/06/99 10:16p Andsager
50  * Modify big ship explosions.  Less frequent fireballs, fewer particles,
51  * larger fireballs
52  * 
53  * 47    9/05/99 11:10a Andsager
54  * Fix up which split ship get debris pieces.  Dont render debris pieces
55  * for split ships until they are near clip plane.
56  * 
57  * 46    9/02/99 12:55a Mikek
58  * Scale engine wash by speed.
59  * 
60  * 45    9/01/99 10:15a Dave
61  * 
62  * 44    8/31/99 10:13p Andsager
63  * Add Knossos warp effect fireball
64  * 
65  * 43    8/23/99 11:09a Andsager
66  * Round 2 of Knossos explosion
67  * 
68  * 42    8/20/99 2:16p Andsager
69  * Make only supercaps slow down extra fast after warping in.
70  * 
71  * 41    8/20/99 1:42p Andsager
72  * Frist pass on Knossos explosion.
73  * 
74  * 40    8/19/99 4:36p Andsager
75  * Cleaned up special_warpout
76  * 
77  * 39    8/16/99 10:04p Andsager
78  * Add special-warp-dist and special-warpout-name sexp for Knossos device
79  * warpout.
80  * 
81  * 38    8/16/99 2:01p Andsager
82  * Knossos warp-in warp-out.
83  * 
84  * 37    8/13/99 10:49a Andsager
85  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
86  * modes dont collide big ships.
87  * 
88  * 36    8/05/99 2:06a Dave
89  * Whee.
90  * 
91  * 35    7/18/99 12:32p Dave
92  * Randomly oriented shockwaves.
93  * 
94  * 34    7/09/99 12:52a Andsager
95  * Modify engine wash (1) less damage (2) only at a closer range (3) no
96  * damage when engine is disabled
97  * 
98  * 33    7/06/99 10:45a Andsager
99  * Modify engine wash to work on any ship that is not small.  Add AWACS
100  * ask for help.
101  * 
102  * 32    7/02/99 9:55p Dave
103  * Player engine wash sound.
104  * 
105  * 31    7/02/99 4:31p Dave
106  * Much more sophisticated lightning support.
107  * 
108  * 30    7/01/99 4:23p Dave
109  * Full support for multiple linked ambient engine sounds. Added "big
110  * damage" flag.
111  * 
112  * 29    7/01/99 11:44a Dave
113  * Updated object sound system to allow multiple obj sounds per ship.
114  * Added hit-by-beam sound. Added killed by beam sound.
115  * 
116  * 28    6/17/99 12:06p Andsager
117  * Add a fireball for each live debris piece.
118  * 
119  * 27    6/14/99 3:21p Andsager
120  * Allow collisions between ship and its debris.  Fix up collision pairs
121  * when large ship is warping out.
122  * 
123  * 26    5/27/99 12:14p Andsager
124  * Some fixes for live debris when more than one subsys on ship with live
125  * debris.  Set subsys strength (when 0) blows off subsystem.
126  * sexp_hits_left_subsystem works for SUBSYSTEM_UNKNOWN.
127  * 
128  * 25    5/26/99 11:46a Dave
129  * Added ship-blasting lighting and made the randomization of lighting
130  * much more customizable.
131  * 
132  * 24    5/25/99 10:05a Andsager
133  * Fix assert with wing warping out with no warp effect.
134  * 
135  * 23    5/24/99 5:45p Dave
136  * Added detail levels to the nebula, with a decent speedup. Split nebula
137  * lightning into its own section.
138  * 
139  * 22    5/21/99 5:03p Andsager
140  * Add code to display engine wash death.  Modify ship_kill_packet
141  * 
142  * 21    5/19/99 11:09a Andsager
143  * Turn on engine wash.  Check every 1/4 sec.
144  * 
145  * 20    5/18/99 1:30p Dave
146  * Added muzzle flash table stuff.
147  * 
148  * 19    5/17/99 10:33a Dave
149  * Fixed warning.
150  * 
151  * 18    5/11/99 10:16p Andsager
152  * First pass on engine wash effect.  Rotation (control input), damage,
153  * shake.  
154  * 
155  * 17    5/09/99 6:00p Dave
156  * Lots of cool new effects. E3 build tweaks.
157  * 
158  * 16    4/28/99 11:13p Dave
159  * Temporary checkin of artillery code.
160  * 
161  * 15    3/29/99 6:17p Dave
162  * More work on demo system. Got just about everything in except for
163  * blowing ships up, secondary weapons and player death/warpout.
164  * 
165  * 14    3/28/99 5:58p Dave
166  * Added early demo code. Make objects move. Nice and framerate
167  * independant, but not much else. Don't use yet unless you're me :)
168  * 
169  * 13    3/23/99 2:29p Andsager
170  * Fix shockwaves for kamikazi and Fred defined.  Collect together
171  * shockwave_create_info struct.
172  * 
173  * 12    3/20/99 3:03p Andsager
174  * Fix get_model_cross_section_at_z() from getting invalid index .
175  * 
176  * 11    3/19/99 9:51a Dave
177  * Checkin to repair massive source safe crash. Also added support for
178  * pof-style nebulae, and some new weapons code.
179  * 
180  * 10    2/26/99 4:14p Dave
181  * Put in the ability to have multiple shockwaves for ships.
182  * 
183  * 9     1/29/99 12:47a Dave
184  * Put in sounds for beam weapon. A bunch of interface screens (tech
185  * database stuff).
186  * 
187  * 8     1/28/99 9:10a Andsager
188  * Particles increased in width, life, number.  Max particles increased
189  * 
190  * 7     1/27/99 9:56a Dave
191  * Temporary checkin of beam weapons for Dan to make cool sounds.
192  * 
193  * 6     1/11/99 12:42p Andsager
194  * Add live debris - debris which is created from a destroyed subsystem,
195  * when the ship is still alive
196  * 
197  * 5     11/18/98 10:29a Johnson
198  * fix assert in shipfx_remove_submodel_ship_sparks.  submodel may not
199  * have spark and be destroyed.
200  * 
201  * 4     11/17/98 4:27p Andsager
202  * Stop sparks from emitting from destroyed subobjects
203  * 
204  * 3     10/20/98 1:39p Andsager
205  * Make so sparks follow animated ship submodels.  Modify
206  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
207  * submodel_num.  Add submodel_num to multiplayer hit packet.
208  * 
209  * 2     10/07/98 10:53a Dave
210  * Initial checkin.
211  * 
212  * 1     10/07/98 10:51a Dave
213  * 
214  *
215  * $NoKeywords: $
216  */
217
218 #include "pstypes.h"
219 #include "systemvars.h"
220 #include "ship.h"
221 #include "fireballs.h"
222 #include "debris.h"
223 #include "hudtarget.h"
224 #include "shipfx.h"
225 #include "multi.h"
226 #include "gamesnd.h"
227 #include "timer.h"
228 #include "3d.h"                 // needed for View_position, which is used when playing a 3D sound
229 #include "multi.h"
230 #include "multiutil.h"
231 #include "hud.h"
232 #include "fvi.h"
233 #include "gamesequence.h"
234 #include "lighting.h"
235 #include "linklist.h"
236 #include "particle.h"
237 #include "multimsgs.h"
238 #include "multiutil.h"
239 #include "bmpman.h"
240 #include "freespace.h"
241 #include "muzzleflash.h"
242 #include "demo.h"
243 #include "shiphit.h"
244 #include "neblightning.h"
245 #include "objectsnd.h"
246
247 #ifndef NDEBUG
248 extern float flFrametime;
249 extern int Framecount;
250 #endif
251
252 #define SHIP_CANNON_BITMAP                      "argh"
253 int Ship_cannon_bitmap = -1;
254
255 int Player_engine_wash_loop = -1;
256
257 void shipfx_remove_submodel_ship_sparks(ship *shipp, int submodel_num)
258 {
259         SDL_assert(submodel_num != -1);
260
261         // maybe no active sparks on submodel
262         if (shipp->num_hits == 0) {
263                 return;
264         }
265         
266         for (int spark_num=0; spark_num<shipp->num_hits; spark_num++) {
267                 if (shipp->sparks[spark_num].submodel_num == submodel_num) {
268                         shipp->sparks[spark_num].end_time = timestamp(1);
269                 }
270         }
271 }
272
273 // Check if subsystem has live debris and create
274 // DKA: 5/26/99 make velocity of debris scale according to size of debris subobject (at least for large subobjects)
275 void shipfx_subsystem_mabye_create_live_debris(object *ship_obj, ship *ship_p, ship_subsys *subsys, vector *exp_center, float exp_mag)
276 {
277         // initializations
278         polymodel *pm = model_get(ship_p->modelnum);
279         int submodel_num = subsys->system_info->subobj_num;
280         submodel_instance_info *sii = &subsys->submodel_info_1;
281
282         object *live_debris_obj;
283         int i, num_live_debris, live_debris_submodel;
284
285         // get number of live debris objects to create
286         num_live_debris = pm->submodel[submodel_num].num_live_debris;
287         if (num_live_debris <= 0) {
288                 return;
289         }
290
291         ship_model_start(ship_obj);
292
293         // copy angles
294         angles copy_angs = pm->submodel[submodel_num].angs;
295         angles zero_angs = {0.0f, 0.0f, 0.0f};
296
297         // make sure the axis point is set
298         if ( !sii->axis_set ) {
299                 model_init_submodel_axis_pt(sii, ship_p->modelnum, submodel_num);
300         }
301
302         // get the rotvel
303         vector model_axis, world_axis, rotvel, world_axis_pt;
304         void model_get_rotating_submodel_axis(vector *model_axis, vector *world_axis, int modelnum, int submodel_num, object *obj);
305         model_get_rotating_submodel_axis(&model_axis, &world_axis, ship_p->modelnum, submodel_num, ship_obj);
306         vm_vec_copy_scale(&rotvel, &world_axis, sii->cur_turn_rate);
307
308         model_find_world_point(&world_axis_pt, &sii->pt_on_axis, ship_p->modelnum, submodel_num, &ship_obj->orient, &ship_obj->pos);
309
310         matrix m_rot;   // rotation for debris orient about axis
311         vm_quaternion_rotate(&m_rot, vm_vec_mag((vector*)&sii->angs), &model_axis);
312
313
314         // create live debris pieces
315         for (i=0; i<num_live_debris; i++) {
316                 live_debris_submodel = pm->submodel[submodel_num].live_debris[i];
317                 vector start_world_pos, start_model_pos, end_world_pos;
318
319                 // get start world pos
320                 vm_vec_zero(&start_world_pos);
321                 model_find_world_point(&start_world_pos, &pm->submodel[live_debris_submodel].offset, ship_p->modelnum, live_debris_submodel, &ship_obj->orient, &ship_obj->pos );
322
323                 // convert to model coord of underlying submodel
324                 // set angle to zero
325                 pm->submodel[submodel_num].angs = zero_angs;
326                 world_find_model_point(&start_model_pos, &start_world_pos, pm, submodel_num, &ship_obj->orient, &ship_obj->pos);
327
328                 // rotate from submodel coord to world coords
329                 // reset angle to current angle
330                 pm->submodel[submodel_num].angs = copy_angs;
331                 model_find_world_point(&end_world_pos, &start_model_pos, ship_p->modelnum, submodel_num, &ship_obj->orient, &ship_obj->pos);
332
333                 // create fireball here.
334                 fireball_create(&end_world_pos, FIREBALL_EXPLOSION_MEDIUM, OBJ_INDEX(ship_obj), pm->submodel[live_debris_submodel].rad);
335
336                 // create debris
337                 live_debris_obj = debris_create(ship_obj, ship_p->modelnum, live_debris_submodel, &end_world_pos, exp_center, 1, exp_mag);
338
339                 // only do if debris is created
340                 if (live_debris_obj) {
341                         // get radial velocity of debris
342                         vector delta_x, radial_vel;
343                         vm_vec_sub(&delta_x, &end_world_pos, &world_axis_pt);
344                         vm_vec_crossprod(&radial_vel, &rotvel, &delta_x);
345
346                         if (Ship_info[ship_p->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
347                                 // set velocity to cross center of knossos device
348                                 vector rand_vec, vec_to_center;
349
350                                 float vel_mag = vm_vec_mag_quick(&radial_vel) * 1.3f * (0.9f + 0.2f*frand());
351                                 vm_vec_normalized_dir(&vec_to_center, &world_axis_pt, &end_world_pos);
352                                 vm_vec_rand_vec_quick(&rand_vec);
353                                 vm_vec_scale_add2(&vec_to_center, &rand_vec, 0.2f);
354                                 vm_vec_scale_add2(&live_debris_obj->phys_info.vel, &vec_to_center, vel_mag);
355
356                         } else {
357                                 // Get rotation of debris object
358                                 matrix copy = live_debris_obj->orient;
359                                 vm_matrix_x_matrix(&live_debris_obj->orient, &copy, &m_rot);
360
361                                 // Add radial velocity (at least as large as exp velocity)
362                                 vector temp_vel;        // explosion velocity with ship_obj velocity removed
363                                 vm_vec_sub(&temp_vel, &live_debris_obj->phys_info.vel, &ship_obj->phys_info.vel);
364
365                                 // find magnitudes of radial and temp velocity
366                                 float vel_mag = vm_vec_mag(&temp_vel);
367                                 float rotvel_mag = vm_vec_mag(&radial_vel);
368
369                                 if (rotvel_mag > 0.1) {
370                                         float scale = (1.2f + 0.2f * frand()) * vel_mag / rotvel_mag;
371                                         // always add *at least* rotvel
372                                         if (scale < 1) {
373                                                 scale = 1.0f;
374                                         }
375
376                                         if (exp_mag > 1) {      // whole ship going down
377                                                 scale = exp_mag;
378                                         }
379
380                                         if (Ship_info[ship_p->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
381                                                 scale = 1.0f;
382                                         }
383
384                                         vm_vec_scale_add2(&live_debris_obj->phys_info.vel, &radial_vel, scale);
385                                 }
386
387                                 // scale up speed of debris if ship_obj > 125, but not for knossos
388                                 if (ship_obj->radius > 250 && !(Ship_info[ship_p->ship_info_index].flags & SIF_KNOSSOS_DEVICE)) {
389                                         vm_vec_scale(&live_debris_obj->phys_info.vel, ship_obj->radius/250.0f);
390                                 }
391                         }
392                 }
393         }
394
395         ship_model_stop(ship_obj);
396 }
397
398 void set_ship_submodel_as_blown_off(ship *shipp, char *name)
399 {
400         int found =     FALSE;
401
402         // go through list of ship subsystems and find name
403         ship_subsys     *pss = NULL;
404         for (pss=GET_FIRST(&shipp->subsys_list); pss!=END_OF_LIST(&shipp->subsys_list); pss=GET_NEXT(pss)) {
405                 if ( SDL_strcasecmp(pss->system_info->name, name) == 0) {
406                         found = TRUE;
407                         break;
408                 }
409         }
410
411         // set its blown off flag to TRUE
412         SDL_assert(found);
413         if (found) {
414                 pss->submodel_info_1.blown_off = 1;
415         }
416 }
417
418
419 // Create debris for ship submodel which has live debris (at ship death)
420 // when ship submodel has not already been blown off (and hence liberated live debris)
421 void shipfx_maybe_create_live_debris_at_ship_death( object *ship_obj )
422 {
423         // if ship has live debris, detonate that subsystem now
424         // search for any live debris
425
426         ship *shipp = &Ships[ship_obj->instance];
427         polymodel *pm = model_get(shipp->modelnum);
428
429         int live_debris_submodel = -1;
430         for (int idx=0; idx<pm->num_debris_objects; idx++) {
431                 if (pm->submodel[pm->debris_objects[idx]].is_live_debris) {
432                         live_debris_submodel = pm->debris_objects[idx];
433
434                         // get submodel that produces live debris
435                         int model_get_parent_submodel_for_live_debris( int model_num, int live_debris_model_num );
436                         int parent = model_get_parent_submodel_for_live_debris( shipp->modelnum, live_debris_submodel);
437                         SDL_assert(parent != -1);
438
439                         // set model values only once (esp blown off)
440                         ship_model_start(ship_obj);
441
442                         // check if already blown off  (ship model set)
443                         if ( !pm->submodel[parent].blown_off ) {
444                 
445                                 // get ship_subsys for live_debris
446                                 // Go through all subsystems and look for submodel the subsystems with "parent" submodel.
447                                 ship_subsys     *pss = NULL;
448                                 for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
449                                         if (pss->system_info->subobj_num == parent) {
450                                                 break;
451                                         }
452                                 }
453
454                                 SDL_assert (pss != NULL);
455                                 if (pss != NULL) {
456                                         vector exp_center, tmp = ZERO_VECTOR;
457                                         model_find_world_point(&exp_center, &tmp, shipp->modelnum, parent, &ship_obj->orient, &ship_obj->pos );
458
459                                         // if not blown off, blow it off
460                                         shipfx_subsystem_mabye_create_live_debris(ship_obj, shipp, pss, &exp_center, 3.0f);
461
462                                         // now set subsystem as blown off, so we only get one copy
463                                         pm->submodel[parent].blown_off = 1;
464                                         set_ship_submodel_as_blown_off(&Ships[ship_obj->instance], pss->system_info->name);
465                                 }
466                         }
467                 }
468         }
469
470         // clean up
471         ship_model_stop(ship_obj);
472
473 }
474
475 void shipfx_blow_off_subsystem(object *ship_obj,ship *ship_p,ship_subsys *subsys, vector *exp_center)
476 {
477         vector subobj_pos;
478
479         model_subsystem *psub = subsys->system_info;
480
481         get_subsystem_world_pos(ship_obj, subsys, &subobj_pos);
482
483 /*
484         if ( psub->turret_gun_sobj > -1 )
485                 debris_create( ship_obj, ship_p->modelnum, psub->turret_gun_sobj, &subobj_pos, exp_center, 0, 1.0f );
486
487         if ( psub->subobj_num > -1 )
488                 debris_create( ship_obj, ship_p->modelnum, psub->subobj_num, &subobj_pos, exp_center, 0, 1.0f );
489 */
490         // get rid of sparks on submodel that is destroyed
491         shipfx_remove_submodel_ship_sparks(ship_p, psub->subobj_num);
492
493         // create debris shards
494         shipfx_blow_up_model(ship_obj, ship_p->modelnum, psub->subobj_num, 50, &subobj_pos );
495
496         // create live debris objects, if any
497         // TODO:  some MULITPLAYER implcations here!!
498         shipfx_subsystem_mabye_create_live_debris(ship_obj, ship_p, subsys, exp_center, 1.0f);
499
500         // create first fireball
501         fireball_create( &subobj_pos, FIREBALL_EXPLOSION_MEDIUM, OBJ_INDEX(ship_obj), psub->radius );
502 }
503
504
505 void shipfx_blow_up_hull(object *obj, int model, vector *exp_center)
506 {
507         int i;
508         polymodel * pm;
509         ushort sig_save;
510
511
512         pm = model_get(model);
513         if (!pm) return;
514
515         // in multiplayer, send a debris_hull_create packet.  Save/restore the debris signature
516         // when in misison only (since we can create debris pieces before mission starts)
517         sig_save = 0;
518         if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) ) {
519                 sig_save = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
520                 multi_set_network_signature( (ushort)(Ships[obj->instance].arrival_distance), MULTI_SIG_DEBRIS );
521         }
522
523         bool try_live_debris = true;
524         for (i=0; i<pm->num_debris_objects; i++ )       {
525                 if (! pm->submodel[pm->debris_objects[i]].is_live_debris) {
526                         vector tmp = ZERO_VECTOR;               
527                         model_find_world_point(&tmp, &pm->submodel[pm->debris_objects[i]].offset, model, 0, &obj->orient, &obj->pos );
528                         debris_create( obj, model, pm->debris_objects[i], &tmp, exp_center, 1, 3.0f );
529                 } else {
530                         if ( try_live_debris ) {
531                                 // only create live debris once
532                                 // this creates *all* the live debris for *all* the currently live subsystems.
533                                 try_live_debris = false;
534                                 shipfx_maybe_create_live_debris_at_ship_death(obj);
535                         }
536                 }
537         }
538
539         // restore the ship signature to it's original value.
540         if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) ) {
541                 multi_set_network_signature( sig_save, MULTI_SIG_DEBRIS );
542         }
543 }
544
545
546 // Creates "ndebris" pieces of debris on random verts of the the "submodel" in the 
547 // ship's model.
548 void shipfx_blow_up_model(object *obj,int model, int submodel, int ndebris, vector *exp_center )
549 {
550         int i;
551
552         // if in a multiplayer game -- seed the random number generator with a value that will be the same
553         // on all clients in the game -- the net_signature of the object works nicely -- since doing so should
554         // ensure that all pieces of debris will get scattered in same direction on all machines
555         if ( Game_mode & GM_MULTIPLAYER )
556                 srand( obj->net_signature );
557
558         // made a change to allow anyone but multiplayer client to blow up hull.  Clients will do it when
559         // they get the create packet
560         if ( submodel == 0 ) {
561                 shipfx_blow_up_hull(obj,model, exp_center );
562         }
563
564         for (i=0; i<ndebris; i++ )      {
565                 vector pnt1, pnt2;
566
567                 // Gets two random points on the surface of a submodel
568                 submodel_get_two_random_points(model, submodel, &pnt1, &pnt2 );
569
570                 vector tmp, outpnt;
571
572                 vm_vec_avg( &tmp, &pnt1, &pnt2 );
573                 model_find_world_point(&outpnt, &tmp, model,submodel, &obj->orient, &obj->pos );
574
575                 debris_create( obj, -1, -1, &outpnt, exp_center, 0, 1.0f );
576         }
577 }
578
579
580 // =================================================
581 //          SHIP WARP IN EFFECT CODE
582 // =================================================
583
584
585 // Given an ship, find the radius of it as viewed from the front.
586 static float shipfx_calculate_effect_radius( object *objp )
587 {
588         float w,h,rad;
589         ship *shipp = &Ships[objp->instance];
590
591         polymodel *pm = model_get( shipp->modelnum );
592
593         w = pm->maxs.xyz.x - pm->mins.xyz.x;
594         h = pm->maxs.xyz.y - pm->mins.xyz.y;
595         
596         if ( w > h )    {
597                 rad = w / 2.0f;
598         } else {
599                 rad = h / 2.0f;
600         }
601
602         object *docked_objp = ai_find_docked_object( objp );
603
604         //      If ship is docked then center wormhold about their center and make radius large enough.
605         if ( docked_objp ) {
606                 ship *docked_shipp = &Ships[docked_objp->instance];
607                 
608                 pm = model_get( docked_shipp->modelnum );
609                 
610                 w = pm->maxs.xyz.x - pm->mins.xyz.x;
611                 h = pm->maxs.xyz.y - pm->mins.xyz.y;
612                 
613                 if ( w > h )    {
614                         rad += w / 2.0f;
615                 } else {
616                         rad += h / 2.0f;
617                 }
618         }
619         return rad*3.0f;
620 }
621
622 // How long the stage 1 & stage 2 of warp in effect lasts.
623 // There are different times for small, medium, and large ships.
624 // The appropriate values are picked depending on the ship's
625 // radius.
626 #define SHIPFX_WARP_DELAY       (2.0f)          // time for warp effect to ramp up before ship moves into it.
627
628 // Give object objp, calculate how long it should take the
629 // ship to go through the warp effect and how fast the ship
630 // should go.   For reference,  capital ship of 2780m 
631 // should take 7 seconds to fly through.   Fighters of 30, 
632 // should take 1.5 seconds to fly through.
633
634 #define LARGEST_RAD 1390.0f 
635 #define LARGEST_RAD_TIME 7.0f
636
637 #define SMALLEST_RAD 15.0f
638 #define SMALLEST_RAD_TIME 1.5f
639
640
641 static float shipfx_calculate_warp_time( object * objp )
642 {
643         // Find rad_percent from 0 to 1, 0 being smallest ship, 1 being largest
644         float rad_percent = (objp->radius-SMALLEST_RAD) / (LARGEST_RAD-SMALLEST_RAD);
645         if ( rad_percent < 0.0f ) {
646                 rad_percent = 0.0f;
647         } else if ( rad_percent > 1.0f )        {
648                 rad_percent = 1.0f;
649         }
650         float rad_time = rad_percent*(LARGEST_RAD_TIME-SMALLEST_RAD_TIME) + SMALLEST_RAD_TIME;
651
652         return rad_time;
653 }
654
655 // calculate warp speed
656 float shipfx_calculate_warp_speed(object *objp)
657 {
658         float length;
659
660         SDL_assert(objp->type == OBJ_SHIP);
661         if (objp->type != OBJ_SHIP) {
662                 length = 2.0f * objp->radius;
663         } else {
664                 length = ship_get_length(&Ships[objp->instance]);
665         }
666         return length / shipfx_calculate_warp_time(objp);
667 }
668
669
670 // This is called to actually warp this object in
671 // after all the flashy fx are done, or if the flashy 
672 // fx don't work for some reason.
673 void shipfx_actually_warpin(    ship *shipp, object *objp )
674 {
675         shipp->flags &= (~SF_ARRIVING_STAGE_1);
676         shipp->flags &= (~SF_ARRIVING_STAGE_2);
677
678         // let physics in on it too.
679         objp->phys_info.flags &= (~PF_WARP_IN);
680 }
681
682 // JAS - code to start the ship doing the warp in effect
683 // This also starts the animating 3d effect playing.
684 // There are two modes, stage 1 and stage 2.   Stage 1 is
685 // when the ship just invisibly waits for a certain time
686 // period after the effect starts, and then stage 2 begins,
687 // where the ships flies through the effect at a set
688 // velocity so it gets through in a certain amount of
689 // time.
690 void shipfx_warpin_start( object *objp )
691 {
692         ship *shipp;
693         float effect_time, effect_radius;
694         
695         shipp = &Ships[objp->instance];
696
697         if ( shipp->flags & SF_ARRIVING )       {
698                 mprintf(( "Ship is already arriving!\n" ));
699                 return;
700         }
701
702         // post a warpin event
703         if(Game_mode & GM_DEMO_RECORD){
704                 demo_POST_warpin(objp->signature, shipp->flags);
705         }
706
707         // if there is no arrival warp, then skip the whole thing
708         if ( shipp->flags & SF_NO_ARRIVAL_WARP )        {
709                 shipfx_actually_warpin(shipp,objp);
710                 return;
711         }
712         
713         // VALIDATE special_warp_objnum
714         if (shipp->special_warp_objnum >= 0) {
715                         
716                 int ref_objnum = shipp->special_warp_objnum;
717                 int valid_reference_ship = FALSE;
718
719                 // Validate reference_objnum
720                 if ((ref_objnum >= 0) && (ref_objnum < MAX_OBJECTS)) {
721                         object *sp_objp = &Objects[ref_objnum];
722                         if (sp_objp->type == OBJ_SHIP) {
723                                 if (Ship_info[Ships[sp_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
724                                         valid_reference_ship = TRUE;
725                                 }
726                         }
727                 }
728
729                 if (valid_reference_ship != TRUE) {
730                         shipp->special_warp_objnum = -1;
731                 }
732         }
733
734         // only move warp effect pos if not special warp in.
735         if (shipp->special_warp_objnum >= 0) {
736                 SDL_assert(!(Game_mode & GM_MULTIPLAYER));
737                 polymodel *pm;
738                 pm = model_get(shipp->modelnum);
739                 vm_vec_scale_add(&shipp->warp_effect_pos, &objp->pos, &objp->orient.v.fvec, -pm->mins.xyz.z);
740         } else {
741                 vm_vec_scale_add( &shipp->warp_effect_pos, &objp->pos, &objp->orient.v.fvec, objp->radius );
742         }
743         
744         // The ending zero mean this is a warp-in effect
745         if ( !(shipp->flags & SF_INITIALLY_DOCKED) ) {
746                 int warp_objnum;
747
748                 // Effect time is 'SHIPFX_WARP_DELAY' (1.5 secs) seconds to start, 'shipfx_calculate_warp_time' 
749                 // for ship to go thru, and 'SHIPFX_WARP_DELAY' (1.5 secs) to go away.
750                 effect_time = shipfx_calculate_warp_time(objp) + SHIPFX_WARP_DELAY + SHIPFX_WARP_DELAY;
751                 effect_radius = shipfx_calculate_effect_radius(objp);
752
753                 // maybe special warpin
754                 if (shipp->special_warp_objnum >= 0) {
755                         // cap radius to size of knossos
756                         effect_radius = SDL_min(effect_radius, 0.8f*Objects[shipp->special_warp_objnum].radius);
757                         warp_objnum = fireball_create(&shipp->warp_effect_pos, FIREBALL_KNOSSOS_EFFECT, shipp->special_warp_objnum, effect_radius, 0, NULL, effect_time, shipp->ship_info_index);
758                 } else {
759                         warp_objnum = fireball_create(&shipp->warp_effect_pos, FIREBALL_WARP_EFFECT, OBJ_INDEX(objp), effect_radius, 0, NULL, effect_time, shipp->ship_info_index);
760                 }
761                 if (warp_objnum < 0 )   {       // JAS: This must always be created, if not, just warp the ship in
762                         shipfx_actually_warpin(shipp,objp);
763                         return;
764                 }
765
766                 shipp->warp_effect_fvec = Objects[warp_objnum].orient.v.fvec;
767                 // maybe negate if special warp effect
768                 if (shipp->special_warp_objnum >= 0) {
769                         if (vm_vec_dotprod(&shipp->warp_effect_fvec, &objp->orient.v.fvec) < 0) {
770                                 vm_vec_negate(&shipp->warp_effect_fvec);
771                         }
772                 }
773
774
775                 shipp->final_warp_time = timestamp(fl2i(SHIPFX_WARP_DELAY*1000.0f));
776                 shipp->flags |= SF_ARRIVING_STAGE_1;
777
778                 // see if this ship is docked with anything, and if so, make docked ship be "arriving"
779                 /*
780                 if ( Ai_info[shipp->ai_index].dock_objnum != -1 ) {
781                         Ships[Ai_info[shipp->ai_index].dock_objnum].final_warp_time = timestamp(fl2i(warp_time*1000.0f));
782                         Ships[Ai_info[shipp->ai_index].dock_objnum].flags |= SF_ARRIVING_STAGE_1;
783                 }
784                 */
785         }
786 }
787
788 void shipfx_warpin_frame( object *objp, float frametime )
789 {
790         ship *shipp;
791
792         shipp = &Ships[objp->instance];
793
794         if ( shipp->flags & SF_DYING ) return;
795
796         if ( shipp->flags & SF_ARRIVING_STAGE_1 )       {
797                 if ( timestamp_elapsed(shipp->final_warp_time) ) {
798
799                         // let physics know the ship is going to warp in.
800                         objp->phys_info.flags |= PF_WARP_IN;
801
802                         // done doing stage 1 of warp, so go on to stage 2
803                         shipp->flags &= (~SF_ARRIVING_STAGE_1);
804                         shipp->flags |= SF_ARRIVING_STAGE_2;
805
806                         float warp_time = shipfx_calculate_warp_time(objp);
807                         float speed = shipfx_calculate_warp_speed(objp);                        // How long it takes to move through warp effect
808
809                         // Make ship move at velocity so that it moves two radius's in warp_time seconds.
810                         vector vel;
811                         vel = objp->orient.v.fvec;
812                         vm_vec_scale( &vel, speed );
813                         objp->phys_info.vel = vel;
814                         objp->phys_info.desired_vel = vel;
815                         objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
816                         objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
817                         objp->phys_info.prev_ramp_vel.xyz.z = speed;
818                         objp->phys_info.forward_thrust = 0.0f;          // How much the forward thruster is applied.  0-1.
819
820                         shipp->final_warp_time = timestamp(fl2i(warp_time*1000.0f));
821
822                         /*
823                         // see if this ship is docked with anything, and if so, make docked ship be "arriving"
824                         if ( Ai_info[shipp->ai_index].dock_objnum != -1 ) {
825                                 Ships[Ai_info[shipp->ai_index].dock_objnum].flags &= (~SF_ARRIVING_STAGE_1);
826                                 Ships[Ai_info[shipp->ai_index].dock_objnum].flags |= SF_ARRIVING_STAGE_2;
827                                 Ships[Ai_info[shipp->ai_index].dock_objnum].final_warp_time = timestamp(fl2i(warp_time*1000.0f));
828                         }
829                         */
830                 } 
831         } else if ( shipp->flags & SF_ARRIVING_STAGE_2 )        {
832                 if ( timestamp_elapsed(shipp->final_warp_time) ) {
833                         // done doing stage 2 of warp, so turn off arriving flag
834                         shipfx_actually_warpin(shipp,objp);
835
836                         // notify physics to slow down
837                         if (Ship_info[shipp->ship_info_index].flags & SIF_SUPERCAP) {
838                                 // let physics know this is a special warp in
839                                 objp->phys_info.flags |= PF_SPECIAL_WARP_IN;
840                         }
841                 }
842         }
843
844 }
845  
846 // This is called to actually warp this object out
847 // after all the flashy fx are done, or if the flashy 
848 // fx don't work for some reason.  OR to skip the flashy
849 // fx.
850 void shipfx_actually_warpout( ship *shipp, object *objp )
851 {
852         // Once we get through effect, make the ship go away
853
854         if ( objp == Player_obj )       {
855                 // Normally, this will never get called for the player. If it
856                 // does, it is because some error (like the warpout effect
857                 // couldn't start) so go ahead an warp the player out.
858                 // All this does is set the event to go to debriefing, the
859                 // same thing that happens after the player warp out effect
860                 // ends.
861                 gameseq_post_event( GS_EVENT_DEBRIEF ); // proceed to debriefing
862         } else {
863                 object *docked_objp;
864                 
865                 // Code for objects except player ship warping out
866                 objp->flags |= OF_SHOULD_BE_DEAD;
867                 // check to see if departing ship is docked with anything and if so, mark that object
868                 // gone as well
869                 docked_objp = ai_find_docked_object( objp );
870                 if ( docked_objp ) {
871                         docked_objp->flags |= OF_SHOULD_BE_DEAD;
872                 }
873
874                 ship_departed( objp->instance );
875                 if ( docked_objp ){
876                         ship_departed( docked_objp->instance );
877                 }
878         }
879 }
880
881 // compute_special_warpout_stuff();
882 int compute_special_warpout_stuff(object *objp, float *speed, float *warp_time, vector *warp_pos)
883 {
884         object  *sp_objp = NULL;
885         ship            *shipp;
886         int             valid_refenence_ship = FALSE, ref_objnum;
887         vector  facing_normal, vec_to_knossos;
888         float           dist_to_plane;
889
890         // knossos warpout only valid in single player
891         if (Game_mode & GM_MULTIPLAYER) {
892                 mprintf(("special warpout only for single player\n"));
893                 return -1;
894         }
895
896         // find special warp ship reference
897         valid_refenence_ship = FALSE;
898         ref_objnum = Ships[objp->instance].special_warp_objnum;
899
900         // Validate reference_objnum
901         if ((ref_objnum >= 0) && (ref_objnum < MAX_OBJECTS)) {
902                 sp_objp = &Objects[ref_objnum];
903                 if (sp_objp->type == OBJ_SHIP) {
904                         shipp = &Ships[sp_objp->instance];
905                         if (Ship_info[shipp->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
906                                 valid_refenence_ship = TRUE;
907                         }
908                 }
909         }
910         
911         if (!valid_refenence_ship) {
912                 Int3();
913                 mprintf(("special warpout reference ship not found\n"));
914                 return -1;
915         }
916
917         // get facing normal from knossos
918         vm_vec_sub(&vec_to_knossos, &sp_objp->pos, &objp->pos);
919         facing_normal = sp_objp->orient.v.fvec;
920         if (vm_vec_dotprod(&vec_to_knossos, &sp_objp->orient.v.fvec) > 0) {
921                 vm_vec_negate(&facing_normal);
922         }
923
924         // find position to play the warp ani..
925         dist_to_plane = fvi_ray_plane(warp_pos, &sp_objp->pos, &facing_normal, &objp->pos, &objp->orient.v.fvec, 0.0f);
926
927         // calculate distance to warpout point.
928         polymodel *pm = model_get(Ships[objp->instance].modelnum);
929         dist_to_plane += pm->mins.xyz.z;
930
931         if (dist_to_plane < 0) {
932                 mprintf(("warpout started too late\n"));
933                 return -1;
934         }
935
936         // validate angle
937         float max_warpout_angle = 0.707f;       // 45 degree half-angle cone for small ships
938         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
939                 max_warpout_angle = 0.866f;     // 30 degree half-angle cone for BIG or HUGE
940         }
941
942         if (-vm_vec_dotprod(&objp->orient.v.fvec, &facing_normal) < max_warpout_angle) {        // within allowed angle
943                 Int3();
944                 mprintf(("special warpout angle exceeded\n"));
945                 return -1;
946         }
947
948         // Calculate speed needed to get 
949         *speed = shipfx_calculate_warp_speed(objp);
950         
951         // Calculate how long to fly through the effect.  Not to get to the effect, just through it.
952         *warp_time = shipfx_calculate_warp_time(objp);
953         *warp_time += dist_to_plane / *speed;
954
955         return 0;
956 }
957
958
959 void compute_warpout_stuff(object *objp, float *speed, float *warp_time, vector *warp_pos)
960 {
961         // If we're warping through the knossos, do something different.
962         if (Ships[objp->instance].special_warp_objnum >= 0) {
963                 if (compute_special_warpout_stuff(objp, speed, warp_time, warp_pos) != -1) {
964                         return;
965                 } else {
966                         mprintf(("Invalid special warp\n"));
967                 }
968         }
969
970         object  *docked_objp = ai_find_docked_object( objp );
971         vector  center_pos = objp->pos;
972         float           radius, ship_move_dist, warp_dist;
973
974         radius = objp->radius;
975
976         //      If ship is docked then center wormhold about their center and make radius large enough.
977         if ( docked_objp ) {
978                 vm_vec_avg(&center_pos, &objp->pos, &docked_objp->pos);
979                 radius += docked_objp->radius;
980         }
981
982         // Calculate how long to fly through the effect.  Not to get to the effect, just through it.
983         *warp_time = shipfx_calculate_warp_time(objp);
984
985         // Pick some speed at which we want to go through the warp effect.  
986         // This is determined by shipfx_calculate_warp_time specifying how long
987         // it should take to go through the effect, or 2R.
988         *speed = shipfx_calculate_warp_speed(objp);
989
990         if ( objp == Player_obj )       {
991                 *speed = 0.8f*objp->phys_info.max_vel.xyz.z;
992         }
993
994         // Now we know our speed. Figure out how far the warp effect will be from here.  
995         ship_move_dist = (*speed * SHIPFX_WARP_DELAY) + radius*1.5f;            // We want to get to 1.5R away from effect
996         if ( ship_move_dist < radius*1.5f ) {
997                 ship_move_dist = radius*1.5f;
998         }
999
1000         // If this is a huge ship, set the distance to the length of the ship
1001         if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
1002                 ship_move_dist = 0.5f * ship_get_length(&Ships[objp->instance]);
1003         }
1004
1005         // Acount for time to get to warp effect, before we actually go through it.
1006         *warp_time += ship_move_dist / *speed;
1007
1008         warp_dist = ship_move_dist;
1009         // allow for off center
1010         if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
1011                 polymodel *pm = model_get(Ships[objp->instance].modelnum);
1012                 warp_dist -= pm->mins.xyz.z;
1013         }
1014
1015         vm_vec_scale_add( warp_pos, &center_pos, &objp->orient.v.fvec, warp_dist );
1016 }
1017
1018 // JAS - code to start the ship doing the warp out effect
1019 // This puts the ship into a mode specified by SF_DEPARTING
1020 // where it flies forward for a set time period at a set
1021 // velocity, then disappears when that time is reached.  This
1022 // also starts the animating 3d effect playing.
1023 void shipfx_warpout_start( object *objp )
1024 {
1025         float warp_time;
1026         ship *shipp;
1027         shipp = &Ships[objp->instance];
1028
1029         if (    shipp->flags & SF_DEPART_WARP ) {
1030                 mprintf(( "Ship is already departing!\n" ));
1031                 return;
1032         }
1033
1034         // if we're dying return
1035         if ( shipp->flags & SF_DYING ) {
1036                 return;
1037         }
1038
1039         // if we're HUGE, keep alive - set guardian
1040         if (Ship_info[shipp->ship_info_index].flags & SIF_HUGE_SHIP) {
1041                 objp->flags |= OF_GUARDIAN;
1042         }
1043
1044         // post a warpin event
1045         if(Game_mode & GM_DEMO_RECORD){
1046                 demo_POST_warpout(objp->signature, shipp->flags);
1047         }
1048
1049         // don't send ship depart packets for player ships
1050         if ( (MULTIPLAYER_MASTER) && !(objp->flags & OF_PLAYER_SHIP) ){
1051                 send_ship_depart_packet( objp );
1052         }
1053
1054         // don't do departure wormhole if ship flag is set which indicates no effect
1055         if ( shipp->flags & SF_NO_DEPARTURE_WARP ) {
1056                 // DKA 5/25/99 If he's going to warpout, set it.  
1057                 // Next line fixes assert in wing cleanup code when no warp effect.
1058                 shipp->flags |= SF_DEPART_WARP;
1059
1060                 shipfx_actually_warpout(shipp, objp);
1061                 return;
1062         }
1063
1064         if ( objp == Player_obj )       {
1065                 HUD_printf(XSTR( "Subspace node activated", 498) );
1066         }
1067
1068         float   speed, effect_time, effect_radius;
1069         vector  warp_pos;
1070         // warp time from compute warpout stuff includes time to get up to warp_pos
1071         compute_warpout_stuff(objp, &speed, &warp_time, &warp_pos);
1072         shipp->warp_effect_pos = warp_pos;
1073
1074         // The ending one means this is a warp-out effect
1075         int warp_objnum;
1076         // Effect time is 'SHIPFX_WARP_DELAY' (1.5 secs) seconds to start, 'shipfx_calculate_warp_time' 
1077         // for ship to go thru, and 'SHIPFX_WARP_DELAY' (1.5 secs) to go away.
1078         // effect_time = shipfx_calculate_warp_time(objp) + SHIPFX_WARP_DELAY + SHIPFX_WARP_DELAY;
1079         effect_time = warp_time + SHIPFX_WARP_DELAY;
1080         effect_radius = shipfx_calculate_effect_radius(objp);
1081
1082         // maybe special warpout
1083         if (shipp->special_warp_objnum >= 0) {
1084                 // cap radius to size of knossos
1085                 effect_radius = SDL_min(effect_radius, 0.8f*Objects[shipp->special_warp_objnum].radius);
1086                 warp_objnum = fireball_create(&shipp->warp_effect_pos, FIREBALL_KNOSSOS_EFFECT, shipp->special_warp_objnum, effect_radius, 1, NULL, effect_time, shipp->ship_info_index);
1087         } else {
1088                 warp_objnum = fireball_create(&shipp->warp_effect_pos, FIREBALL_WARP_EFFECT, OBJ_INDEX(objp), effect_radius, 1, NULL, effect_time, shipp->ship_info_index);
1089         }
1090         if (warp_objnum < 0 )   {       // JAS: This must always be created, if not, just warp the ship in
1091                 shipfx_actually_warpout(shipp,objp);
1092                 return;
1093         }
1094
1095         shipp->warp_effect_fvec = Objects[warp_objnum].orient.v.fvec;
1096         // maybe negate if special warp effect
1097         if (shipp->special_warp_objnum >= 0) {
1098                 if (vm_vec_dotprod(&shipp->warp_effect_fvec, &objp->orient.v.fvec) > 0) {
1099                         vm_vec_negate(&shipp->warp_effect_fvec);
1100                 }
1101         }
1102
1103         // Make the warp effect stage 1 last SHIP_WARP_TIME1 seconds.
1104         if ( objp == Player_obj )       {
1105                 warp_time = fireball_lifeleft(&Objects[warp_objnum]);
1106                 shipp->final_warp_time = timestamp(fl2i(warp_time*1000.0f));
1107         } else {
1108                 shipp->final_warp_time = timestamp(fl2i(warp_time*2.0f*1000.0f));
1109         }
1110         shipp->flags |= SF_DEPART_WARP;
1111
1112 //      mprintf(( "Warp time = %.4f , effect time = %.4f ms\n", warp_time*1000.0f, effect_time ));
1113
1114         // This is a hack to make the ship go at the right speed to go from it's current position to the warp_effect_pos;
1115         
1116         // Set ship's velocity to 'speed'
1117         // This should actually be an AI that flies from the current
1118         // position through 'shipp->warp_effect_pos' in 'warp_time'
1119         // and keeps going 
1120         if ( objp != Player_obj )       {
1121                 vector vel;
1122                 vel = objp->orient.v.fvec;
1123                 vm_vec_scale( &vel, speed );
1124                 objp->phys_info.vel = vel;
1125                 objp->phys_info.desired_vel = vel;
1126                 objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
1127                 objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
1128                 objp->phys_info.prev_ramp_vel.xyz.z = speed;
1129                 objp->phys_info.forward_thrust = 1.0f;          // How much the forward thruster is applied.  0-1.
1130
1131                 // special case for HUGE ships
1132                 if (Ship_info[shipp->ship_info_index].flags & SIF_HUGE_SHIP) {
1133 //                      objp->phys_info.flags |= PF_SPECIAL_WARP_OUT;
1134                 }
1135         }
1136
1137 }
1138
1139 void shipfx_warpout_frame( object *objp, float frametime )
1140 {
1141         ship *shipp;
1142         shipp = &Ships[objp->instance];
1143
1144         if ( shipp->flags & SF_DYING ) return;
1145
1146         vector tempv;
1147         float warp_pos; // position of warp effect in object's frame of reference
1148
1149         vm_vec_sub( &tempv, &objp->pos, &shipp->warp_effect_pos );
1150         warp_pos = -vm_vec_dot( &tempv, &shipp->warp_effect_fvec );
1151
1152
1153         // Find the closest point on line from center of wormhole
1154         vector pos;
1155 //      float dist;
1156
1157         fvi_ray_plane(&pos,&objp->pos,&shipp->warp_effect_fvec,&shipp->warp_effect_pos, &shipp->warp_effect_fvec, 0.0f );
1158 //      dist = vm_vec_dist( &pos, &objp->pos );
1159
1160 //      mprintf(( "Warp pos = %.1f, rad=%.1f, center dist = %.1f\n", warp_pos, objp->radius, dist ));
1161
1162         if ( objp == Player_obj )       {
1163                 // Code for player warpout frame
1164
1165                 if ( (Player->control_mode==PCM_WARPOUT_STAGE2) && (warp_pos > objp->radius) )  {
1166                         gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2 );
1167                 }
1168
1169                 if ( timestamp_elapsed(shipp->final_warp_time) ) {
1170
1171                         // Something went wrong... oh well, warp him out anyway.
1172                         if ( Player->control_mode != PCM_WARPOUT_STAGE3 )       {
1173                                 mprintf(( "Hmmm... player ship warpout time elapsed, but he wasn't in warp stage 3.\n" ));
1174                         }
1175
1176                         gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_DONE );
1177                         ship_departed( objp->instance );                                                                // mark log entry for the player
1178                 }
1179
1180         } else {
1181                 // Code for all non-player ships warpout frame
1182
1183                 int timed_out = timestamp_elapsed(shipp->final_warp_time);
1184                 if ( timed_out )        {
1185 //                      mprintf(("Frame %i: Ship %s missed departue cue.\n", Framecount, shipp->ship_name ));
1186                         int     delta_ms = timestamp_until(shipp->final_warp_time);
1187                         if (delta_ms > 1000.0f * frametime ) {
1188                                 nprintf(("AI", "Frame %i: Ship %s missed departue cue by %7.3f seconds.\n", Framecount, shipp->ship_name, - (float) delta_ms/1000.0f));
1189                         }
1190                 }
1191
1192                 // MWA 10/21/97 -- added shipp->flags & SF_NO_DEPARTURE_WARP part of next if statement.  For ships
1193                 // that don't get a wormhole effect, I wanted to drop into this code immediately.
1194                 if ( (warp_pos > objp->radius)  || (shipp->flags & SF_NO_DEPARTURE_WARP) || timed_out ) {
1195                         shipfx_actually_warpout( shipp, objp );
1196                 } 
1197         }
1198 }
1199
1200
1201 //==================================================
1202 // Stuff for keeping track of which ships are in
1203 // whose shadows.
1204
1205
1206 // Given point p0, in object's frame of reference, find if 
1207 // it can see the sun.
1208 int shipfx_point_in_shadow( vector *p0, matrix *src_orient, vector *src_pos, float radius )
1209 {
1210         mc_info mc;
1211         object *objp;
1212         ship_obj *so;
1213         ship *shipp;
1214         int n_lights;
1215         int idx;
1216
1217         vector rp0, rp1;
1218
1219         vector light_dir;
1220
1221         // Move rp0 into world coordinates      
1222         vm_vec_unrotate(&rp0, p0, src_orient);
1223         vm_vec_add2(&rp0, src_pos);
1224
1225         // get the # of global lights
1226         n_lights = light_get_global_count();
1227         
1228         for(idx=0; idx<n_lights; idx++){
1229                 // get the light dir for this light
1230                 light_get_global_dir(&light_dir, idx);
1231
1232                 // Find rp1
1233                 vm_vec_scale_add( &rp1, &rp0, &light_dir, 10000.0f );
1234
1235                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )    {
1236                         objp = &Objects[so->objnum];
1237                         shipp = &Ships[objp->instance];
1238
1239                         mc.model_num = shipp->modelnum;
1240                         mc.orient = &objp->orient;
1241                         mc.pos = &objp->pos;
1242                         mc.p0 = &rp0;
1243                         mc.p1 = &rp1;
1244                         mc.flags = MC_CHECK_MODEL;      
1245
1246                         if ( model_collide(&mc) ){
1247                                 return 1;
1248                         }
1249                 }
1250         }
1251
1252         // not in shadow
1253         return 0;
1254 }
1255
1256
1257 // Given an ship see if it is in a shadow.
1258 int shipfx_in_shadow( object * src_obj )
1259 {
1260         mc_info mc;
1261         object *objp;
1262         ship_obj *so;
1263         ship *shipp;
1264         int n_lights;
1265         int idx;
1266
1267         vector rp0, rp1;
1268         vector light_dir;
1269
1270         rp0 = src_obj->pos;
1271         
1272         // get the # of global lights
1273         n_lights = light_get_global_count();
1274
1275         for(idx=0; idx<n_lights; idx++){
1276                 // get the direction for this light
1277                 light_get_global_dir(&light_dir, idx);
1278
1279                 // Find rp1
1280                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )    {
1281                         objp = &Objects[so->objnum];
1282
1283                         if ( src_obj != objp )  {
1284                                 shipp = &Ships[objp->instance];
1285
1286                                 vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
1287
1288                                 mc.model_num = shipp->modelnum;
1289                                 mc.orient = &objp->orient;
1290                                 mc.pos = &objp->pos;
1291                                 mc.p0 = &rp0;
1292                                 mc.p1 = &rp1;
1293                                 mc.flags = MC_CHECK_MODEL;      
1294
1295         //                      mc.flags |= MC_CHECK_SPHERELINE;
1296         //                      mc.radius = src_obj->radius;
1297
1298                                 if ( model_collide(&mc) )       {
1299                                         return 1;
1300                                 }
1301                         }
1302                 }
1303         }
1304
1305         // not in shadow
1306         return 0;
1307 }
1308
1309 // Given world point see if it is in a shadow.
1310 int shipfx_eye_in_shadow( vector *eye_pos, object * src_obj, int sun_n )
1311 {
1312         mc_info mc;
1313         object *objp;
1314         ship_obj *so;
1315         ship *shipp;    
1316
1317         vector rp0, rp1;
1318         vector light_dir;
1319
1320         rp0 = *eye_pos; 
1321         
1322         // get the light dir
1323         if(!light_get_global_dir(&light_dir, sun_n)){
1324                 return 0;
1325         }
1326
1327         // Find rp1
1328         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )    {
1329                 objp = &Objects[so->objnum];
1330
1331                 if ( src_obj != objp )  {
1332                         shipp = &Ships[objp->instance];
1333
1334                         vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
1335
1336                         ship_model_start(objp);
1337
1338                         mc.model_num = shipp->modelnum;
1339                         mc.orient = &objp->orient;
1340                         mc.pos = &objp->pos;
1341                         mc.p0 = &rp0;
1342                         mc.p1 = &rp1;
1343                         mc.flags = MC_CHECK_MODEL;      
1344
1345         //                      mc.flags |= MC_CHECK_SPHERELINE;
1346         //                      mc.radius = src_obj->radius;
1347
1348                         int hit = model_collide(&mc);
1349
1350                         ship_model_stop(objp);
1351
1352                         if (hit) {
1353                                 return 1;
1354                         }
1355                 }
1356         }
1357
1358         // Check all the big hull debris pieces.
1359         debris  *db = Debris;
1360
1361         int i;
1362         for ( i = 0; i < MAX_DEBRIS_PIECES; i++, db++ ) {
1363                 if ( !(db->flags & DEBRIS_USED) || !db->is_hull ){
1364                         continue;
1365                 }
1366
1367                 objp = &Objects[db->objnum];
1368
1369                 vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
1370
1371                 mc.model_num = db->model_num;   // Fill in the model to check
1372                 mc.submodel_num = db->submodel_num;
1373                 model_clear_instance( mc.model_num );
1374                 mc.orient = &objp->orient;                                      // The object's orient
1375                 mc.pos = &objp->pos;                                                    // The object's position
1376                 mc.p0 = &rp0;                           // Point 1 of ray to check
1377                 mc.p1 = &rp1;                                   // Point 2 of ray to check
1378                 mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL);
1379
1380                 if (model_collide(&mc)) {
1381                         return 1;
1382                 }
1383         }       
1384
1385         // not in shadow
1386         return 0;
1387 }
1388
1389 //=====================================================================================
1390 // STUFF FOR DOING SHIP GUN FLASHES
1391 //=====================================================================================
1392
1393 #define MAX_FLASHES     128                     // How many flashes total
1394 #define FLASH_LIFE_PRIMARY              0.25f                   // How long flash lives
1395 #define FLASH_LIFE_SECONDARY    0.50f                   // How long flash lives
1396
1397
1398 typedef struct ship_flash {
1399         int     objnum;                                 // object number of parent ship
1400         int     obj_signature;                  // signature of that object
1401         int     light_num;                              // which light in the model this uses
1402         float   life;                                           // how long this should be around
1403         float max_life;                         // how long this has been around.
1404 } ship_flash;
1405
1406 int Ship_flash_inited = 0;
1407 int Ship_flash_highest = -1;
1408 ship_flash Ship_flash[MAX_FLASHES];
1409
1410 // Resets the ship flash stuff. Call before each level.
1411 void shipfx_flash_init()
1412 {
1413         int i;
1414         
1415         for (i=0; i<MAX_FLASHES; i++ )  {
1416                 Ship_flash[i].objnum = -1;                      // mark as unused
1417         }
1418         Ship_flash_highest = -1;
1419         Ship_flash_inited = 1;  
1420 }
1421
1422
1423 // Given that a ship fired a weapon, light up the model
1424 // accordingly.
1425 void shipfx_flash_create(object *objp, ship * shipp, vector *gun_pos, vector *gun_dir, int is_primary, int weapon_info_index)
1426 {
1427         int i;
1428         int objnum = OBJ_INDEX(objp);
1429
1430         SDL_assert(Ship_flash_inited);
1431
1432         polymodel *pm = model_get( shipp->modelnum );
1433         int closest_light = -1;
1434         float d, closest_dist = 0.0f;
1435
1436         // ALWAYS do this - since this is called once per firing
1437         // if this is a cannon type weapon, create a muzzle flash
1438         // HACK - let the flak guns do this on their own since they fire so quickly
1439         if((Weapon_info[weapon_info_index].wi_flags & WIF_MFLASH) && !(Weapon_info[weapon_info_index].wi_flags & WIF_FLAK)){
1440                 // spiffy new flash stuff
1441                 mflash_create(gun_pos, gun_dir, Weapon_info[weapon_info_index].muzzle_flash);           
1442         }
1443
1444         if ( pm->num_lights < 1 ) return;
1445
1446         for (i=0; i<pm->num_lights; i++ )       {
1447                 d = vm_vec_dist( &pm->lights[i].pos, gun_pos );
1448         
1449                 if ( pm->lights[i].type == BSP_LIGHT_TYPE_WEAPON ) {
1450                         if ( (closest_light==-1) || (d<closest_dist) )  {
1451                                 closest_light = i;
1452                                 closest_dist = d;
1453                         }
1454                 }
1455         }
1456
1457         if ( closest_light == -1 ) return;
1458
1459         int first_slot = -1;
1460
1461         for (i=0; i<=Ship_flash_highest; i++ )  {
1462                 if ( (first_slot==-1) && (Ship_flash[i].objnum < 0) )   {
1463                         first_slot = i;
1464                 }
1465
1466                 if ( (Ship_flash[i].objnum == objnum) && (Ship_flash[i].obj_signature==objp->signature) )       {
1467                         if ( Ship_flash[i].light_num == closest_light ) {
1468                                 // This is already flashing!
1469                                 Ship_flash[i].life = 0.0f;
1470                                 if ( is_primary )       {
1471                                         Ship_flash[i].max_life = FLASH_LIFE_PRIMARY;
1472                                 } else {
1473                                         Ship_flash[i].max_life = FLASH_LIFE_SECONDARY;
1474                                 }
1475                                 return;
1476                         }
1477                 }
1478         }
1479
1480         if ( first_slot == -1 ) {
1481                 if ( Ship_flash_highest < MAX_FLASHES-1 )       {
1482                         Ship_flash_highest++;
1483                         first_slot = Ship_flash_highest;
1484                 } else {
1485                         //mprintf(( "SHIP_FLASH: Out of flash spots!\n" ));
1486                         return;         // out of flash slots
1487                 }
1488         }
1489
1490         SDL_assert( Ship_flash[first_slot].objnum == -1 );
1491
1492         Ship_flash[first_slot].objnum = objnum;
1493         Ship_flash[first_slot].obj_signature = objp->signature;
1494         Ship_flash[first_slot].life = 0.0f;             // Start it up
1495         if ( is_primary )       {
1496                 Ship_flash[first_slot].max_life = FLASH_LIFE_PRIMARY;
1497         } else {
1498                 Ship_flash[first_slot].max_life = FLASH_LIFE_SECONDARY;
1499         }
1500         Ship_flash[first_slot].light_num = closest_light;               
1501 }
1502
1503 // Sets the flash lights in the model used by this
1504 // ship to the appropriate values.  There might not
1505 // be any flashes linked to this ship in which
1506 // case this function does nothing.
1507 void shipfx_flash_light_model( object *objp, ship * shipp )
1508 {
1509         int i, objnum = OBJ_INDEX(objp);
1510         polymodel *pm = model_get( shipp->modelnum );
1511
1512         for (i=0; i<=Ship_flash_highest; i++ )  {
1513                 if ( (Ship_flash[i].objnum == objnum) && (Ship_flash[i].obj_signature==objp->signature) )       {
1514                         float v = (Ship_flash[i].max_life - Ship_flash[i].life)/Ship_flash[i].max_life;
1515
1516                         pm->lights[Ship_flash[i].light_num].value += v / 255.0f;
1517                 }
1518         }
1519
1520 }
1521
1522 // Does whatever processing needs to be done each frame.
1523 void shipfx_flash_do_frame(float frametime)
1524 {
1525         ship_flash *sf;
1526         int kill_it = 0;
1527         int i;
1528
1529         for (i=0, sf = &Ship_flash[0]; i<=Ship_flash_highest; i++, sf++ )       {
1530                 if ( sf->objnum > -1 )  {
1531                         if ( Objects[sf->objnum].signature != sf->obj_signature )       {
1532                                 kill_it = 1;
1533                         }
1534                         sf->life += frametime;
1535                         if ( sf->life >= sf->max_life ) kill_it = 1;
1536
1537                         if (kill_it) {
1538                                 sf->objnum = -1;
1539                                 if ( i == Ship_flash_highest )  {
1540                                         while( (Ship_flash_highest>0) && (Ship_flash[Ship_flash_highest].objnum == -1) )        {
1541                                                 Ship_flash_highest--;
1542                                         }
1543                                 }
1544                         }       
1545                 }
1546         }       
1547
1548 }
1549
1550 float Particle_width = 1.2f;
1551 DCF(particle_width, "Multiplier for angular width of the particle spew")
1552 {
1553         if ( Dc_command ) {
1554                 dc_get_arg(ARG_FLOAT);
1555                 if ( (Dc_arg_float >= 0 ) && (Dc_arg_float <= 5) ) {
1556                         Particle_width = Dc_arg_float;
1557                 } else {
1558                         dc_printf( "Illegal value for particle width. (Must be from 0-5) \n\n");
1559                 }
1560         }
1561 }
1562
1563 float Particle_number = 1.2f;
1564 DCF(particle_num, "Multiplier for the number of particles created")
1565 {
1566         if ( Dc_command ) {
1567                 dc_get_arg(ARG_FLOAT);
1568                 if ( (Dc_arg_float >= 0 ) && (Dc_arg_float <= 5) ) {
1569                         Particle_number = Dc_arg_float;
1570                 } else {
1571                         dc_printf( "Illegal value for particle num. (Must be from 0-5) \n\n");
1572                 }
1573         }
1574 }
1575
1576 float Particle_life = 1.2f;
1577 DCF(particle_life, "Multiplier for the lifetime of particles created")
1578 {
1579         if ( Dc_command ) {
1580                 dc_get_arg(ARG_FLOAT);
1581                 if ( (Dc_arg_float >= 0 ) && (Dc_arg_float <= 5) ) {
1582                         Particle_life = Dc_arg_float;
1583                 } else {
1584                         dc_printf( "Illegal value for particle life. (Must be from 0-5) \n\n");
1585                 }
1586         }
1587 }
1588
1589 // Make sparks fly off of ship n.
1590 // sn = spark number to spark, corrosponding to element in
1591 //      ship->hitpos array.  If this isn't -1, it is a just
1592 //      got hit by weapon spark, otherwise pick one randomally.
1593 void shipfx_emit_spark( int n, int sn )
1594 {
1595         int create_spark = 1;
1596         object * obj;
1597         vector outpnt;
1598         ship *shipp = &Ships[n];
1599         float ship_radius, spark_scale_factor;
1600         
1601         if ( shipp->num_hits <= 0 )
1602                 return;
1603
1604         // get radius of ship
1605         ship_radius = model_get_radius(Ship_info[shipp->ship_info_index].modelnum);
1606
1607         // get spark_scale_factor -- how much to increase ship sparks, based on radius
1608         if (ship_radius > 40) {
1609                 spark_scale_factor = 1.0f;
1610         } else if (ship_radius > 20) {
1611                 spark_scale_factor = (ship_radius - 20.0f) / 20.0f;
1612         } else {
1613                 spark_scale_factor = 0.0f;
1614         }
1615
1616         float spark_time_scale  = 1.0f + spark_scale_factor * (Particle_life   - 1.0f);
1617         float spark_width_scale = 1.0f + spark_scale_factor * (Particle_width  - 1.0f);
1618         float spark_num_scale   = 1.0f + spark_scale_factor * (Particle_number - 1.0f);
1619
1620         obj = &Objects[shipp->objnum];
1621         ship_info* si = &Ship_info[shipp->ship_info_index];
1622
1623         float hull_percent = obj->hull_strength / si->initial_hull_strength;
1624         if (hull_percent < 0.001) {
1625                 hull_percent = 0.001f;
1626         }
1627
1628         /*
1629         float fraction = 0.1f * obj->radius / hull_percent;
1630         if (fraction > 1.0f) {
1631                 fraction = 1.0f;
1632         }
1633         */
1634
1635         int spark_num;
1636         if ( sn == -1 ) {
1637                 spark_num = myrand() % shipp->num_hits;
1638         } else {
1639                 spark_num = sn;
1640         }
1641
1642         // don't display sparks that have expired
1643         if ( timestamp_elapsed(shipp->sparks[spark_num].end_time) ) {
1644                 return;
1645         }
1646
1647         // get spark position
1648         if (shipp->sparks[spark_num].submodel_num != -1) {
1649                 ship_model_start(obj);
1650                 model_find_world_point(&outpnt, &shipp->sparks[spark_num].pos, shipp->modelnum, shipp->sparks[spark_num].submodel_num, &obj->orient, &obj->pos);
1651                 ship_model_stop(obj);
1652         } else {
1653                 // rotate sparks correctly with current ship orient
1654                 vm_vec_unrotate(&outpnt, &shipp->sparks[spark_num].pos, &obj->orient);
1655                 vm_vec_add2(&outpnt,&obj->pos);
1656         }
1657
1658         if ( shipp->flags & (SF_ARRIVING|SF_DEPART_WARP) ) {
1659                 vector tmp;
1660                 vm_vec_sub( &tmp, &outpnt, &shipp->warp_effect_pos );
1661                 if ( vm_vec_dot( &tmp, &shipp->warp_effect_fvec ) < 0.0f )      {
1662                         // if in front of warp plane, don't create.
1663                         create_spark = 0;
1664                 }
1665         }
1666
1667         if ( create_spark )     {
1668
1669                 particle_emitter        pe;
1670
1671                 pe.pos = outpnt;                                // Where the particles emit from
1672
1673                 if ( shipp->flags & (SF_ARRIVING|SF_DEPART_WARP) ) {
1674                         // No velocity if going through warp.
1675                         pe.vel = vmd_zero_vector;
1676                 } else {
1677                         // Initial velocity of all the particles.
1678                         // 0.0f = 0% of parent's.
1679                         // 1.0f = 100% of parent's.
1680                         vm_vec_copy_scale( &pe.vel, &obj->phys_info.vel, 0.7f );
1681                 }
1682
1683                 // TODO: add velocity from rotation if submodel is rotating
1684                 // v_rot = w x r
1685
1686                 // r = outpnt - model_find_world_point(0)
1687
1688                 // w = model_find_world_dir(
1689                 // model_find_world_dir(&out_dir, &in_dir, model_num, submodel_num, &objorient, &objpos);
1690
1691                 vector tmp_norm, tmp_vel;
1692                 vm_vec_sub( &tmp_norm, &outpnt, &obj->pos );
1693                 vm_vec_normalize_safe(&tmp_norm);
1694
1695                 tmp_vel = obj->phys_info.vel;
1696                 if ( vm_vec_normalize_safe(&tmp_vel) > 1.0f )   {
1697                         vm_vec_scale_add2(&tmp_norm,&tmp_vel, -2.0f);
1698                         vm_vec_normalize_safe(&tmp_norm);
1699                 }
1700                                 
1701                 pe.normal = tmp_norm;                   // What normal the particle emit around
1702                 pe.normal_variance = 0.3f;              //      How close they stick to that normal 0=good, 1=360 degree
1703                 pe.min_rad = 0.20f;                             // Min radius
1704                 pe.max_rad = 0.50f;                             // Max radius
1705
1706                 // first time through - set up end time and make heavier initially
1707                 if ( sn > -1 )  {
1708                         // Sparks for first time at this spot
1709                         if (si->flags & SIF_FIGHTER) {
1710                                 if (hull_percent > 0.6f) {
1711                                         // sparks only once when hull > 60%
1712                                         float spark_duration = (float)pow(2.0f, -5.0f*(hull_percent-1.3f)) * (1.0f + 0.6f*(frand()-0.5f));      // +- 30%
1713                                         shipp->sparks[spark_num].end_time = timestamp( (int) (1000.0f * spark_duration) );
1714                                 } else {
1715                                         // spark never ends when hull < 60% (~277 hr)
1716                                         shipp->sparks[spark_num].end_time = timestamp( 100000000 );
1717                                 }
1718                         }
1719
1720                         pe.num_low  = 25;                               // Lowest number of particles to create (hardware)
1721                         pe.num_high = 30;                               // Highest number of particles to create (hardware)
1722                         pe.normal_variance = 1.0f;      //      How close they stick to that normal 0=good, 1=360 degree
1723                         pe.min_vel = 2.0f;                              // How fast the slowest particle can move
1724                         pe.max_vel = 12.0f;                             // How fast the fastest particle can move
1725                         pe.min_life = 0.05f;                            // How long the particles live
1726                         pe.max_life = 0.55f;                            // How long the particles live
1727
1728                         particle_emit( &pe, PARTICLE_FIRE, 0 );
1729                 } else {
1730
1731                         pe.min_rad = 0.7f;                              // Min radius
1732                         pe.max_rad = 1.3f;                              // Max radius
1733                         pe.num_low  = int (20 * spark_num_scale);               // Lowest number of particles to create (hardware)
1734                         pe.num_high = int (50 * spark_num_scale);               // Highest number of particles to create (hardware)
1735                         pe.normal_variance = 0.2f * spark_width_scale;          //      How close they stick to that normal 0=good, 1=360 degree
1736                         pe.min_vel = 3.0f;                              // How fast the slowest particle can move
1737                         pe.max_vel = 12.0f;                             // How fast the fastest particle can move
1738                         pe.min_life = 0.35f*2.0f * spark_time_scale;            // How long the particles live
1739                         pe.max_life = 0.75f*2.0f * spark_time_scale;            // How long the particles live
1740                         
1741                         particle_emit( &pe, PARTICLE_SMOKE, 0 );
1742                 }
1743         }
1744
1745         // Select time to do next spark
1746 //      Ships[n].next_hit_spark = timestamp_rand(100,500);
1747         shipp->next_hit_spark = timestamp_rand(50,100);
1748 }
1749
1750
1751
1752 //=====================================================================================
1753 // STUFF FOR DOING LARGE SHIP EXPLOSIONS
1754 //=====================================================================================
1755
1756 int     Bs_exp_fire_low = 1;
1757 float   Bs_exp_fire_time_mult = 1.0f;
1758
1759 DCF_BOOL(bs_exp_fire_low, Bs_exp_fire_low)
1760 DCF(bs_exp_fire_time_mult, "Multiplier time between fireball in big ship explosion")
1761 {
1762         if ( Dc_command ) {
1763                 dc_get_arg(ARG_FLOAT);
1764                 if ( (Dc_arg_float >= 0.1 ) && (Dc_arg_float <= 5) ) {
1765                         Bs_exp_fire_time_mult = Dc_arg_float;
1766                 } else {
1767                         dc_printf( "Illegal value for bs_exp_fire_time_mult. (Must be from 0.1-5) \n\n");
1768                 }
1769         }
1770 }
1771
1772 #define MAX_SPLIT_SHIPS         3               // How many can explode at once.  Each one is about 1K.
1773
1774 #define DEBRIS_NONE                     0
1775 #define DEBRIS_DRAW                     1
1776 #define DEBRIS_FREE                     2
1777
1778 typedef struct clip_ship {
1779         object*                 parent_obj;
1780         float                   length_left;    // uncomsumed length
1781         matrix                  orient;
1782         physics_info    phys_info;
1783         vector                  local_pivot;                                                            // world center of mass position of half ship
1784         vector                  model_center_disp_to_orig_center;       // displacement from half ship center to original model center
1785         vector                  clip_plane_norm;                                                        // clip plane normal (local [0,0,1] or [0,0,-1])
1786         float                           cur_clip_plane_pt;                                              // displacement from half ship clip plane to original model center
1787         float                           explosion_vel;
1788         ubyte                           draw_debris[MAX_DEBRIS_OBJECTS];
1789         int                             next_fireball;
1790 } clip_ship;
1791
1792 typedef struct split_ship {
1793         int                             used;                                   // 0 if not used, 1 if used
1794         clip_ship               front_ship;
1795         clip_ship               back_ship;
1796         int                             explosion_flash_timestamp;
1797         int                             explosion_flash_started;
1798         int                             sound_handle[NUM_SUB_EXPL_HANDLES];
1799 } split_ship;
1800
1801
1802 static split_ship Split_ships[MAX_SPLIT_SHIPS];
1803 static int Split_ships_inited = 0;
1804
1805 static void split_ship_init_system()
1806 {
1807         int i;
1808         for (i=0; i<MAX_SPLIT_SHIPS; i++ )      {
1809                 Split_ships[i].used = 0;
1810         }
1811         Split_ships_inited = 1;
1812 }
1813
1814 static void maybe_fireball_wipe(clip_ship* half_ship, int* sound_handle);
1815 static void split_ship_init( ship* shipp, split_ship* split_ship )
1816 {
1817         object* parent_ship_obj = &Objects[shipp->objnum];
1818         matrix* orient = &parent_ship_obj->orient;
1819         for (int ii=0; ii<NUM_SUB_EXPL_HANDLES; ii++) {
1820                 split_ship->sound_handle[ii] = shipp->sub_expl_sound_handle[ii];
1821         }
1822
1823         // play 3d sound for shockwave explosion
1824         snd_play_3d( &Snds[SND_SHOCKWAVE_EXPLODE], &parent_ship_obj->pos, &View_position, 0.0f, NULL, 0, 1.0f, SND_PRIORITY_SINGLE_INSTANCE, NULL, 3.0f );
1825
1826         // initialize both ships
1827         split_ship->front_ship.parent_obj = parent_ship_obj;
1828         split_ship->back_ship.parent_obj  = parent_ship_obj;
1829         split_ship->explosion_flash_timestamp = timestamp((int)(0.00075f*parent_ship_obj->radius));
1830         split_ship->explosion_flash_started = 0;
1831         split_ship->front_ship.orient = *orient;
1832         split_ship->back_ship.orient  = *orient;
1833         split_ship->front_ship.next_fireball = timestamp_rand(0, 100);
1834         split_ship->back_ship.next_fireball  = timestamp_rand(0, 100);
1835
1836         split_ship->front_ship.clip_plane_norm = vmd_z_vector;
1837         vm_vec_copy_scale(&split_ship->back_ship.clip_plane_norm, &vmd_z_vector, -1.0f);
1838
1839         // find the point at which the ship splits (relative to its pivot)
1840         polymodel* pm = model_get(shipp->modelnum);
1841         float init_clip_plane_dist;
1842         if (pm->num_split_plane > 0) {
1843                 int index = rand()%pm->num_split_plane;
1844                 init_clip_plane_dist = pm->split_plane[index];
1845         } else {
1846                 init_clip_plane_dist = 0.5f * (0.5f - frand())*pm->core_radius;
1847         }
1848
1849         split_ship->back_ship.cur_clip_plane_pt =  init_clip_plane_dist;
1850         split_ship->front_ship.cur_clip_plane_pt = init_clip_plane_dist;
1851
1852         float dist;
1853         dist = (split_ship->front_ship.cur_clip_plane_pt+pm->maxs.xyz.z)/2.0f;
1854         vm_vec_copy_scale(&split_ship->front_ship.local_pivot, &orient->v.fvec, dist);
1855         (void) vm_vec_make(&split_ship->front_ship.model_center_disp_to_orig_center, 0.0f, 0.0f, -dist);
1856         dist = (split_ship->back_ship.cur_clip_plane_pt +pm->mins.xyz.z)/2.0f;
1857         vm_vec_copy_scale(&split_ship->back_ship.local_pivot, &orient->v.fvec, dist);
1858         (void) vm_vec_make(&split_ship->back_ship.model_center_disp_to_orig_center, 0.0f, 0.0f, -dist);
1859         vm_vec_add2(&split_ship->front_ship.local_pivot, &parent_ship_obj->pos );
1860         vm_vec_add2(&split_ship->back_ship.local_pivot,  &parent_ship_obj->pos );
1861         
1862         // find which debris pieces are in the front and back split ships
1863         for (int i=0; i<pm->num_debris_objects; i++ )   {
1864                 vector temp_pos = ZERO_VECTOR;
1865                 vector tmp = ZERO_VECTOR;               
1866                 vector tmp1 = pm->submodel[pm->debris_objects[i]].offset;
1867                 // tmp is world position,  temp_pos is world_pivot,  tmp1 is offset from world_pivot (in ship local coord)
1868                 model_find_world_point(&tmp, &tmp1, shipp->modelnum, -1, &vmd_identity_matrix, &temp_pos );
1869                 if (tmp.xyz.z > init_clip_plane_dist) {
1870                         split_ship->front_ship.draw_debris[i] = DEBRIS_DRAW;
1871                         split_ship->back_ship.draw_debris[i]  = DEBRIS_NONE;
1872                 } else {
1873                         split_ship->front_ship.draw_debris[i] = DEBRIS_NONE;
1874                         split_ship->back_ship.draw_debris[i]  = DEBRIS_DRAW;
1875                 }
1876         }
1877
1878         /*
1879         // set the remaining debris slots to not draw
1880         for (i=pm->num_debris_objects; i<MAX_DEBRIS_OBJECTS; i++) {
1881                 split_ship->front_ship.draw_debris[i] = DEBRIS_NONE;
1882                 split_ship->back_ship.draw_debris[i]  = DEBRIS_NONE;
1883         } */
1884
1885         // set up physics 
1886         physics_init( &split_ship->front_ship.phys_info );
1887         physics_init( &split_ship->back_ship.phys_info );
1888         split_ship->front_ship.phys_info.flags  |= (PF_ACCELERATES | PF_DEAD_DAMP);
1889         split_ship->back_ship.phys_info.flags |= (PF_ACCELERATES | PF_DEAD_DAMP);
1890         split_ship->front_ship.phys_info.side_slip_time_const = 10000.0f;
1891         split_ship->back_ship.phys_info.side_slip_time_const =  10000.0f;
1892         split_ship->front_ship.phys_info.rotdamp = 10000.0f;
1893         split_ship->back_ship.phys_info.rotdamp =  10000.0f;
1894
1895         // set up explosion vel and relative velocities (assuming mass depends on length)
1896         float front_length = pm->maxs.xyz.z - split_ship->front_ship.cur_clip_plane_pt;
1897         float back_length  = split_ship->back_ship.cur_clip_plane_pt - pm->mins.xyz.z;
1898         float ship_length = front_length + back_length;
1899         split_ship->front_ship.length_left = front_length;
1900         split_ship->back_ship.length_left  = back_length;
1901
1902         float expl_length_scale = (ship_length - 200.0f) / 2000.0f;
1903         // s_r_f effects speed of "wipe" and rotvel
1904         float speed_reduction_factor = (1.0f + 0.001f*parent_ship_obj->radius);
1905         float explosion_time = (3.0f + expl_length_scale + (frand()-0.5f)) * speed_reduction_factor;
1906         float long_length = SDL_max(front_length, back_length);
1907         float expl_vel = long_length / explosion_time;
1908         split_ship->front_ship.explosion_vel = expl_vel;
1909         split_ship->back_ship.explosion_vel  = -expl_vel;
1910
1911         float rel_vel = (0.6f + 0.2f*frand()) * expl_vel * speed_reduction_factor;
1912         float front_vel = rel_vel * back_length / ship_length;
1913         float back_vel = -rel_vel * front_length / ship_length;
1914         // mprintf(("rel_vel %.1f, expl_vel %.1f\n", rel_vel, expl_vel));
1915
1916         // set up rotational vel
1917         vector rotvel;
1918         vm_vec_rand_vec_quick(&rotvel);
1919         rotvel.xyz.z = 0.0f;
1920         vm_vec_normalize(&rotvel);
1921         vm_vec_scale(&rotvel, 0.15f / speed_reduction_factor);
1922         split_ship->front_ship.phys_info.rotvel = rotvel;
1923         vm_vec_copy_scale(&split_ship->back_ship.phys_info.rotvel, &rotvel, -(front_length*front_length)/(back_length*back_length));
1924         split_ship->front_ship.phys_info.rotvel.xyz.z = parent_ship_obj->phys_info.rotvel.xyz.z;
1925         split_ship->back_ship.phys_info.rotvel.xyz.z  = parent_ship_obj->phys_info.rotvel.xyz.z;
1926
1927
1928         // modify vel of each split ship based on rotvel of parent ship obj
1929         vector temp_rotvel = parent_ship_obj->phys_info.rotvel;
1930         temp_rotvel.xyz.z = 0.0f;
1931         vector vel_from_rotvel;
1932         vm_vec_crossprod(&vel_from_rotvel, &temp_rotvel, &split_ship->front_ship.local_pivot);
1933         //      vm_vec_scale_add2(&split_ship->front_ship.phys_info.vel, &vel_from_rotvel, 0.5f);
1934         vm_vec_crossprod(&vel_from_rotvel, &temp_rotvel, &split_ship->back_ship.local_pivot);
1935         //      vm_vec_scale_add2(&split_ship->back_ship.phys_info.vel, &vel_from_rotvel, 0.5f);
1936
1937         // set up velocity and make initial fireballs and particles
1938         split_ship->front_ship.phys_info.vel = parent_ship_obj->phys_info.vel;
1939         split_ship->back_ship.phys_info.vel  = parent_ship_obj->phys_info.vel;
1940         maybe_fireball_wipe(&split_ship->front_ship, (int*)&split_ship->sound_handle);
1941         maybe_fireball_wipe(&split_ship->back_ship,  (int*)&split_ship->sound_handle);
1942         vm_vec_scale_add2(&split_ship->front_ship.phys_info.vel, &orient->v.fvec, front_vel);
1943         vm_vec_scale_add2(&split_ship->back_ship.phys_info.vel,  &orient->v.fvec, back_vel);
1944
1945         // HANDLE LIVE DEBRIS - blow off if not already gone
1946         shipfx_maybe_create_live_debris_at_ship_death( parent_ship_obj );
1947 }
1948
1949
1950 static void half_ship_render_ship_and_debris(clip_ship* half_ship,ship *shipp)
1951 {
1952         SDL_assert( Split_ships_inited );
1953
1954         polymodel *pm = model_get(shipp->modelnum);
1955
1956         // get rotated clip plane normal and world coord of original ship center
1957         vector orig_ship_world_center, clip_plane_norm, model_clip_plane_pt, debris_clip_plane_pt;
1958         vm_vec_unrotate(&clip_plane_norm, &half_ship->clip_plane_norm, &half_ship->orient);
1959         vm_vec_unrotate(&orig_ship_world_center, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
1960         vm_vec_add2(&orig_ship_world_center, &half_ship->local_pivot);
1961
1962         // *out_pivot = orig_ship_world_center;
1963
1964         // get debris clip plane pt and draw debris
1965         vm_vec_unrotate(&debris_clip_plane_pt, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
1966         vm_vec_add2(&debris_clip_plane_pt, &half_ship->local_pivot);
1967         g3_start_user_clip_plane( &debris_clip_plane_pt, &clip_plane_norm);
1968
1969         // set up render flags
1970         uint render_flags = MR_NORMAL;
1971
1972         for (int i=0; i<pm->num_debris_objects; i++ )   {
1973                 // draw DEBRIS_FREE in test only
1974                 if (half_ship->draw_debris[i] == DEBRIS_DRAW) {
1975                         vector temp_pos = orig_ship_world_center;
1976                         vector tmp = ZERO_VECTOR;
1977                         vector tmp1 = pm->submodel[pm->debris_objects[i]].offset;
1978
1979                         // determine if explosion front has past debris piece
1980                         // 67 ~ dist expl moves in 2 frames -- maybe fraction works better
1981                         int is_live_debris = pm->submodel[pm->debris_objects[i]].is_live_debris;
1982                         int create_debris = 0;
1983                         // front ship
1984                         if (half_ship->explosion_vel > 0) {
1985                                 if (half_ship->cur_clip_plane_pt > tmp1.xyz.z + pm->submodel[pm->debris_objects[i]].max.xyz.z - 0.1f*half_ship->explosion_vel) {
1986                                         create_debris = 1;
1987                                 }
1988                                 // is the debris visible
1989 //                              if (half_ship->cur_clip_plane_pt > tmp1.xyz.z + pm->submodel[pm->debris_objects[i]].min.xyz.z - 0.5f*half_ship->explosion_vel) {
1990 //                                      render_debris = 1;
1991 //                              }
1992                         // back ship
1993                         } else {
1994                                 if (half_ship->cur_clip_plane_pt < tmp1.xyz.z + pm->submodel[pm->debris_objects[i]].min.xyz.z - 0.1f*half_ship->explosion_vel) {
1995                                         create_debris = 1;
1996                                 }
1997                                 // is the debris visible
1998 //                              if (half_ship->cur_clip_plane_pt < tmp1.xyz.z + pm->submodel[pm->debris_objects[i]].max.xyz.z - 0.5f*half_ship->explosion_vel) {
1999 //                                      render_debris = 1;
2000 //                              }
2001                         }
2002
2003                         // Draw debris, but not live debris
2004                         if ( !is_live_debris ) {
2005                                 model_find_world_point(&tmp, &tmp1, shipp->modelnum, -1, &half_ship->orient, &temp_pos);
2006                                 submodel_render(shipp->modelnum, pm->debris_objects[i], &half_ship->orient, &tmp, render_flags);
2007                         }
2008
2009                         // make free piece of debris
2010                         if ( create_debris ) {
2011                                 half_ship->draw_debris[i] = DEBRIS_FREE;                // mark debris to not render with model
2012                                 vector center_to_debris, debris_vel, radial_vel;
2013                                 // check if last debris piece, ie, debris_count == 0
2014                                 int debris_count = 0;
2015                                 for (int j=0; j<pm->num_debris_objects; j++ ) {
2016                                         if (half_ship->draw_debris[j] == DEBRIS_DRAW) {
2017                                                 debris_count++;
2018                                         }
2019                                 } 
2020                                 // do debris create here, but not for live debris
2021                                 // debris vel (1) split ship vel (2) split ship rotvel (3) random
2022                                 if ( !is_live_debris ) {
2023                                         object* debris_obj;
2024                                         debris_obj = debris_create(half_ship->parent_obj, shipp->modelnum, pm->debris_objects[i], &tmp, &half_ship->local_pivot, 1, 1.0f);
2025                                         // AL: make sure debris_obj isn't NULL!
2026                                         if ( debris_obj ) {
2027                                                 vm_vec_scale(&debris_obj->phys_info.rotvel, 4.0f);
2028                                                 debris_obj->orient = half_ship->orient;
2029                                                 // if (debris_count > 0) {
2030                                                         //mprintf(( "base rotvel %.1f, debris rotvel mag %.2f\n", vm_vec_mag(&half_ship->phys_info.rotvel), vm_vec_mag(&debris_obj->phys_info.rotvel) ));
2031                                                         vm_vec_sub(&center_to_debris, &tmp, &half_ship->local_pivot);
2032                                                         vm_vec_crossprod(&debris_vel, &center_to_debris, &half_ship->phys_info.rotvel);
2033                                                         vm_vec_add2(&debris_vel, &half_ship->phys_info.vel);
2034                                                         vm_vec_copy_normalize(&radial_vel, &center_to_debris);
2035                                                         float radial_mag = 10.0f + 30.0f*frand();
2036                                                         vm_vec_scale_add2(&debris_vel, &radial_vel, radial_mag);
2037                                                         debris_obj->phys_info.vel = debris_vel;
2038                                                 /* } else {
2039                                                         debris_obj->phys_info.vel = half_ship->phys_info.vel;
2040                                                         debris_obj->phys_info.rotvel = half_ship->phys_info.rotvel;
2041                                                 } */
2042                                         }
2043                                 }
2044                         }
2045                 }
2046         }
2047
2048         // get model clip plane pt and draw model
2049         vector temp;
2050         (void) vm_vec_make(&temp, 0.0f, 0.0f, half_ship->cur_clip_plane_pt);
2051         vm_vec_unrotate(&model_clip_plane_pt, &temp, &half_ship->orient);
2052         vm_vec_add2(&model_clip_plane_pt, &orig_ship_world_center);
2053         g3_start_user_clip_plane( &model_clip_plane_pt, &clip_plane_norm );
2054         model_render(shipp->modelnum, &half_ship->orient, &orig_ship_world_center, render_flags);
2055 }
2056
2057 void shipfx_large_blowup_level_init()
2058 {
2059         split_ship_init_system();
2060
2061         if(Ship_cannon_bitmap != -1){
2062                 bm_unload(Ship_cannon_bitmap);
2063                 Ship_cannon_bitmap = bm_load(SHIP_CANNON_BITMAP);
2064         }
2065 }
2066
2067 // Returns 0 if couldn't init
2068 int shipfx_large_blowup_init(ship *shipp)
2069 {
2070
2071         if ( !Split_ships_inited )      {
2072                 split_ship_init_system();
2073         }
2074
2075         int i;
2076         for (i=0; i<MAX_SPLIT_SHIPS; i++ )      {
2077                 if ( Split_ships[i].used == 0 ) {
2078                         break;
2079                 }
2080         }
2081
2082         if ( i >= MAX_SPLIT_SHIPS )     {
2083                 mprintf(( "Not enough split ship slots!! See John!\n" ));
2084                 Int3();
2085                 return 0;
2086         }
2087
2088         Split_ships[i].used = 1;
2089         shipp->large_ship_blowup_index = i;
2090
2091         split_ship_init(shipp, &Split_ships[i] );
2092         
2093         return 1;
2094 }
2095
2096 // ----------------------------------------------------------------------------
2097 // uses list of model z values with constant increment to find the radius of the 
2098 // cross section at the current model z value
2099 float get_model_cross_section_at_z(float z, polymodel* pm)
2100 {
2101         if (pm->num_xc < 2) {
2102                 return 0.0f;
2103         }
2104
2105         float index, increment;
2106         increment = (pm->xc[pm->num_xc-1].z - pm->xc[0].z) / (float)(pm->num_xc - 1);
2107         index = (z - pm->xc[0].z) / increment;
2108
2109         if (index < 0.5f) {
2110                 return pm->xc[0].radius;
2111         } else if (index > (pm->num_xc - 1.0f - 0.5f)) {
2112                 return pm->xc[pm->num_xc-1].radius;
2113         } else {
2114                 int floor_index = (int)floor(index);
2115                 int ceil_index  = (int)ceil(index);
2116                 return SDL_max(pm->xc[ceil_index].radius, pm->xc[floor_index].radius);
2117         }
2118 }
2119
2120 // returns how long sound has been playing
2121 int get_sound_time_played(int snd_id, int handle)
2122 {
2123         if (handle == -1) {
2124                 return 100000;
2125         }
2126
2127         int bits_per_sample, frequency;
2128         snd_get_format(snd_id, &bits_per_sample, &frequency);
2129         int time_left = snd_time_remaining(handle, bits_per_sample, frequency);
2130         int duration = snd_get_duration(snd_id);
2131         
2132         return (duration - time_left);
2133 }
2134
2135 // sound manager for big ship sub explosions sounds.
2136 // forces playing of sub-explosion sounds.  keeps track of active sounds, plays them for >= 750 ms
2137 // when sound has played >= 750, sound is stopped and new instance is started 
2138 void do_sub_expl_sound(float radius, vector* sound_pos, int* sound_handle)
2139 {
2140         int sound_index, handle;
2141         // multiplier for range (near and far distances) to apply attenuation
2142         float sound_range = 1.0f + 0.0043f*radius;
2143
2144         int handle_index = rand()%NUM_SUB_EXPL_HANDLES;
2145         //mprintf(("handle_index %d\n", *handle_index));
2146
2147         // sound_index = get_sub_explosion_sound_index(handle_index);
2148         sound_index = SND_SHIP_EXPLODE_1;
2149         handle = sound_handle[handle_index];
2150
2151
2152         // mprintf(("dist to sound %.1f snd_indx: %d, h1: %d, h2: %d\n", vm_vec_dist(&Player_obj->pos, sound_pos), next_sound_index, sound_handle[0], sound_handle[1]));
2153
2154         if (handle == -1) {
2155                 // if no handle, get one
2156                 sound_handle[handle_index] = snd_play_3d( &Snds[sound_index], sound_pos, &View_position, 0.0f, NULL, 0, 0.6f, SND_PRIORITY_MUST_PLAY, NULL, sound_range );
2157         } else if (!snd_is_playing(handle)) {
2158                 // if sound not playing and old, get new one
2159                 // I don't think will happen with SND_PRIORITY_MUST_PLAY
2160                 if (get_sound_time_played(Snds[sound_index].id, handle) > 400) {
2161                         //mprintf(("sound not playing %d, time_played %d, stopped\n", handle, get_sound_time_played(Snds[sound_index].id, handle)));
2162                         snd_stop(sound_handle[handle_index]);
2163                         sound_handle[handle_index] = snd_play_3d( &Snds[sound_index], sound_pos, &View_position, 0.0f, NULL, 0, 0.6f, SND_PRIORITY_MUST_PLAY, NULL, sound_range );
2164                 }
2165         } else if (get_sound_time_played(Snds[sound_index].id, handle) > 750) {
2166                 //mprintf(("time %f, cur sound %d time_played %d num sounds %d\n", f2fl(Missiontime), handle_index, get_sound_time_played(Snds[sound_index].id, handle), snd_num_playing() ));
2167                 sound_handle[handle_index] = snd_play_3d( &Snds[sound_index], sound_pos, &View_position, 0.0f, NULL, 0, 0.6f, SND_PRIORITY_MUST_PLAY, NULL, sound_range );
2168         }
2169 }
2170
2171 // maybe create a fireball along model clip plane
2172 // also maybe plays explosion sound
2173 static void maybe_fireball_wipe(clip_ship* half_ship, int* sound_handle)
2174 {
2175         // maybe make fireball to cover wipe.
2176         if ( timestamp_elapsed(half_ship->next_fireball) ) {
2177                 if ( half_ship->length_left > 0.2f*fl_abs(half_ship->explosion_vel) )   {
2178
2179                         polymodel* pm = model_get(Ships[half_ship->parent_obj->instance].modelnum);
2180
2181                         vector model_clip_plane_pt, orig_ship_world_center, temp;
2182
2183                         vm_vec_unrotate(&orig_ship_world_center, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
2184                         vm_vec_add2(&orig_ship_world_center, &half_ship->local_pivot);
2185
2186                         (void) vm_vec_make(&temp, 0.0f, 0.0f, half_ship->cur_clip_plane_pt);
2187                         vm_vec_unrotate(&model_clip_plane_pt, &temp, &half_ship->orient);
2188                         vm_vec_add2(&model_clip_plane_pt, &orig_ship_world_center);
2189                         vm_vec_rand_vec_quick(&temp);
2190                         vm_vec_scale(&temp, 0.1f*frand());
2191                         vm_vec_add2(&model_clip_plane_pt, &temp);
2192
2193                         float rad = get_model_cross_section_at_z(half_ship->cur_clip_plane_pt, pm);
2194                         if (rad < 1) {
2195                                 rad = half_ship->parent_obj->radius * frand_range(0.4f, 0.6f);
2196                         } else {
2197                                 // make fireball radius (1.5 +/- .1) * model_cross_section value
2198                                 rad *= frand_range(1.4f, 1.6f);
2199                         }
2200
2201                         rad *= 1.5f;
2202                         rad = SDL_min(rad, half_ship->parent_obj->radius);
2203
2204                         // mprintf(("xc %.1f model %.1f\n", rad, half_ship->parent_obj->radius*0.25));
2205                         int fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
2206                         int low_res_fireballs = Bs_exp_fire_low;
2207                         fireball_create(&model_clip_plane_pt, fireball_type, OBJ_INDEX(half_ship->parent_obj), rad, 0, &half_ship->parent_obj->phys_info.vel, 0.0f, -1, NULL, low_res_fireballs);
2208
2209                         // start the next fireball up (3-4 per frame) + 30%
2210                         int time_low, time_high;
2211                         time_low = int(650 * Bs_exp_fire_time_mult);
2212                         time_high = int(900 * Bs_exp_fire_time_mult);
2213                         half_ship->next_fireball = timestamp_rand(time_low, time_high);
2214
2215                         // do sound
2216                         do_sub_expl_sound(half_ship->parent_obj->radius, &model_clip_plane_pt, sound_handle);
2217
2218                         // do particles
2219                         particle_emitter        pe;
2220
2221                         pe.num_low = 40;                                        // Lowest number of particles to create
2222                         pe.num_high = 80;                               // Highest number of particles to create
2223                         pe.pos = model_clip_plane_pt;   // Where the particles emit from
2224                         pe.vel = half_ship->phys_info.vel;              // Initial velocity of all the particles
2225
2226 #if defined(FS2_DEMO) || defined(FS1_DEMO)
2227                         float range = 1.0f + 0.002f*half_ship->parent_obj->radius * 5.0f;
2228 #else 
2229                         float range = 1.0f + 0.002f*half_ship->parent_obj->radius;
2230 #endif
2231
2232 #if defined(FS2_DEMO) || defined(FS1_DEMO)
2233                         pe.min_life = 2.0f*range;                               // How long the particles live
2234                         pe.max_life = 10.0f*range;                              // How long the particles live
2235 #else
2236                         pe.min_life = 0.5f*range;                               // How long the particles live
2237                         pe.max_life = 6.0f*range;                               // How long the particles live
2238 #endif
2239                         pe.normal = vmd_x_vector;               // What normal the particle emit around
2240                         pe.normal_variance = 2.0f;              //      How close they stick to that normal 0=on normal, 1=180, 2=360 degree
2241                         pe.min_vel = 0.0f;                              // How fast the slowest particle can move
2242                         pe.max_vel = half_ship->explosion_vel;                          // How fast the fastest particle can move
2243
2244 #if defined(FS2_DEMO) || defined(FS1_DEMO)
2245                         float scale = half_ship->parent_obj->radius * 0.02f;
2246 #else
2247                         float scale = half_ship->parent_obj->radius * 0.01f;
2248 #endif
2249                         pe.min_rad = 0.5f*scale;                                // Min radius
2250                         pe.max_rad = 1.5f*scale;                                // Max radius
2251
2252                         particle_emit( &pe, PARTICLE_SMOKE2, 0, range );
2253
2254                 } else {
2255                         // time out forever
2256                         half_ship->next_fireball = timestamp(-1);
2257                 }
2258         }
2259 }
2260
2261
2262 // Returns 1 when explosion is done
2263 int shipfx_large_blowup_do_frame(ship *shipp, float frametime)
2264 {
2265         // DAVE:  I made this not do any movement just to try to get things working...
2266         // return 0;
2267
2268         SDL_assert( Split_ships_inited );
2269         SDL_assert( shipp->large_ship_blowup_index > -1 );
2270
2271         split_ship *the_split_ship = &Split_ships[shipp->large_ship_blowup_index];
2272         SDL_assert( the_split_ship->used );             // Get John
2273
2274         // Do fireballs, particles, shockwave here
2275         // Note parent ship is still valid, vel and pos updated in obj_move_all
2276
2277         if ( timestamp_elapsed(the_split_ship->explosion_flash_timestamp) ) {
2278                 if ( !the_split_ship->explosion_flash_started ) {
2279                         object* objp = &Objects[shipp->objnum];
2280                         if (objp->flags & OF_WAS_RENDERED) {
2281                                 float excess_dist = vm_vec_dist(&Player_obj->pos, &objp->pos) - 2.0f*objp->radius - Player_obj->radius;
2282                                 float intensity = 1.0f - 0.1f*excess_dist / objp->radius;
2283
2284                                 if (intensity > 1) {
2285                                         intensity = 1.0f;
2286                                 }
2287
2288                                 if (intensity > 0.1f) {
2289                                         // big_explosion_flash(intensity);
2290                                 }
2291                         }
2292                         the_split_ship->explosion_flash_started = 1;
2293                 }
2294         }
2295
2296         physics_sim(&the_split_ship->front_ship.local_pivot, &the_split_ship->front_ship.orient, &the_split_ship->front_ship.phys_info, frametime);
2297         physics_sim(&the_split_ship->back_ship.local_pivot,  &the_split_ship->back_ship.orient,  &the_split_ship->back_ship.phys_info,  frametime);
2298         the_split_ship->front_ship.length_left -= the_split_ship->front_ship.explosion_vel*frametime;
2299         the_split_ship->back_ship.length_left  += the_split_ship->back_ship.explosion_vel *frametime;
2300         the_split_ship->front_ship.cur_clip_plane_pt += the_split_ship->front_ship.explosion_vel*frametime;
2301         the_split_ship->back_ship.cur_clip_plane_pt  += the_split_ship->back_ship.explosion_vel *frametime;
2302
2303         float length_left = SDL_max( the_split_ship->front_ship.length_left, the_split_ship->back_ship.length_left );
2304
2305         //      mprintf(( "Blowup frame, dist = %.1f \n", length_left ));
2306
2307         if ( length_left < 0 )  {
2308                 the_split_ship->used = 0;
2309                 return 1;
2310         }
2311
2312         maybe_fireball_wipe(&the_split_ship->front_ship, (int*)&the_split_ship->sound_handle);
2313         maybe_fireball_wipe(&the_split_ship->back_ship,  (int*)&the_split_ship->sound_handle);
2314         return 0;
2315 }
2316
2317 void shipfx_large_blowup_render(ship* shipp)
2318 {
2319 // This actually renders the original model like it should render.
2320 //      object *objp = &Objects[shipp->objnum];
2321 //      model_render( shipp->modelnum, &objp->orient, &objp->pos, MR_NORMAL );
2322 //      return;
2323
2324         SDL_assert( Split_ships_inited );
2325         SDL_assert( shipp->large_ship_blowup_index > -1 );
2326
2327         split_ship *the_split_ship = &Split_ships[shipp->large_ship_blowup_index];
2328         SDL_assert( the_split_ship->used );             // Get John
2329
2330         // vector front_global_pivot, back_global_pivot;
2331
2332         if (the_split_ship->front_ship.length_left > 0) {
2333                 half_ship_render_ship_and_debris(&the_split_ship->front_ship,shipp);
2334         }
2335
2336         if (the_split_ship->back_ship.length_left > 0) {
2337                 half_ship_render_ship_and_debris(&the_split_ship->back_ship,shipp);
2338         }
2339
2340         g3_stop_user_clip_plane();                      
2341 }
2342
2343
2344 // ================== DO THE ELECTRIC ARCING STUFF =====================
2345 // Creates any new ones, moves old ones.
2346
2347 #define MAX_ARC_LENGTH_PERCENTAGE 0.25f
2348
2349 #define MAX_EMP_ARC_TIMESTAMP            (150.0f)
2350
2351 void shipfx_do_damaged_arcs_frame( ship *shipp )
2352 {
2353         int i;
2354         int should_arc;
2355         object *obj = &Objects[shipp->objnum];
2356         ship_info * sip = &Ship_info[shipp->ship_info_index];
2357
2358         should_arc = 1;
2359
2360         float damage = obj->hull_strength / sip->initial_hull_strength; 
2361
2362         if (damage < 0) {
2363                 damage = 0.0f;
2364         }
2365
2366         // don't draw an arc based on damage
2367         if ( damage > 0.30f )   {
2368                 // Don't do spark.
2369                 should_arc = 0;
2370         }
2371
2372         // we should draw an arc
2373         if( shipp->emp_intensity > 0.0f){
2374                 should_arc = 1;
2375         }
2376
2377         // Kill off old sparks
2378         for(i=0; i<MAX_SHIP_ARCS; i++){
2379                 if(timestamp_valid(shipp->arc_timestamp[i]) && timestamp_elapsed(shipp->arc_timestamp[i])){                     
2380                         shipp->arc_timestamp[i] = timestamp(-1);
2381                 }
2382         }
2383
2384         // if we shouldn't draw an arc, return
2385         if(!should_arc){
2386                 return;
2387         }
2388
2389         if (!timestamp_valid(shipp->arc_next_time))     {
2390                 // start the next fireball up in the next 10 seconds or so... 
2391                 int freq;
2392                 
2393                 // if the emp effect is active
2394                 if(shipp->emp_intensity > 0.0f){
2395                         freq = fl2i(MAX_EMP_ARC_TIMESTAMP);
2396                 }
2397                 // otherwise if we're arcing based upon damage
2398                 else {
2399                         freq = fl2i((damage+0.1f)*5000.0f);
2400                 }
2401
2402                 // set the next arc time
2403                 shipp->arc_next_time = timestamp_rand(freq*2,freq*4);
2404         }
2405
2406         if ( timestamp_elapsed(shipp->arc_next_time) )  {
2407
2408                 shipp->arc_next_time = timestamp(-1);           // invalid, so it gets restarted next frame
2409
2410                 //mprintf(( "Creating new ship arc!\n" ));
2411
2412                 int n, n_arcs = ((myrand()>>5) % 3)+1;          // Create 1-3 sparks
2413
2414                 vector v1, v2, v3, v4;
2415                 submodel_get_two_random_points( shipp->modelnum, -1, &v1, &v2 );
2416                 submodel_get_two_random_points( shipp->modelnum, -1, &v3, &v4 );
2417
2418                 // For large ships, cap the length to be 25% of max radius
2419                 if ( obj->radius > 200.0f )     {
2420                         float max_dist = obj->radius * MAX_ARC_LENGTH_PERCENTAGE;
2421                         
2422                         vector tmp;
2423                         float d;
2424
2425                         // Cap arc 2->1
2426                         vm_vec_sub( &tmp, &v1, &v2 );
2427                         d = vm_vec_mag_quick( &tmp );
2428                         if ( d > max_dist )     {
2429                                 vm_vec_scale_add( &v1, &v2, &tmp, max_dist / d );
2430                         }
2431
2432                         // Cap arc 2->3
2433                         vm_vec_sub( &tmp, &v3, &v2 );
2434                         d = vm_vec_mag_quick( &tmp );
2435                         if ( d > max_dist )     {
2436                                 vm_vec_scale_add( &v3, &v2, &tmp, max_dist / d );
2437                         }
2438
2439
2440                         // Cap arc 2->4
2441                         vm_vec_sub( &tmp, &v4, &v2 );
2442                         d = vm_vec_mag_quick( &tmp );
2443                         if ( d > max_dist )     {
2444                                 vm_vec_scale_add( &v4, &v2, &tmp, max_dist / d );
2445                         }
2446                         
2447                 }
2448                 
2449                 n = 0;
2450
2451 //              int a = 100, b = 1000;
2452                 float factor = 1.0f + 0.0025f*obj->radius;
2453                 int a = (int) (factor*100.0f);
2454                 int b = (int) (factor*1000.0f);
2455                 int lifetime = (myrand()%((b)-(a)+1))+(a);
2456
2457                 // Create the arc effects
2458                 for (i=0; i<MAX_SHIP_ARCS; i++ )        {
2459                         if ( !timestamp_valid( shipp->arc_timestamp[i] ) )      {
2460                                 //shipp->arc_timestamp[i] = timestamp_rand(400,1000);   // live up to a second
2461                                 shipp->arc_timestamp[i] = timestamp(lifetime);  // live up to a second
2462
2463                                 switch( n )     {
2464                                 case 0:
2465                                         shipp->arc_pts[i][0] = v1;
2466                                         shipp->arc_pts[i][1] = v2;
2467                                         break;
2468                                 case 1:
2469                                         shipp->arc_pts[i][0] = v2;
2470                                         shipp->arc_pts[i][1] = v3;
2471                                         break;
2472
2473                                 case 2:
2474                                         shipp->arc_pts[i][0] = v2;
2475                                         shipp->arc_pts[i][1] = v4;
2476                                         break;
2477
2478                                 default:
2479                                         Int3();
2480                                 }
2481
2482                                 // determine what kind of arc to create
2483                                 if(shipp->emp_intensity > 0.0f){
2484                                         shipp->arc_type[i] = MARC_TYPE_EMP;
2485                                 } else {
2486                                         shipp->arc_type[i] = MARC_TYPE_NORMAL;
2487                                 }
2488                                         
2489                                 n++;
2490                                 if ( n == n_arcs )
2491                                         break;  // Don't need to create anymore
2492                         }
2493         
2494                         // rotate v2 out of local coordinates into world.
2495                         // Use v2 since it is used in every bolt.  See above switch().
2496                         vector snd_pos;
2497                         vm_vec_unrotate(&snd_pos, &v2, &obj->orient);
2498                         vm_vec_add2(&snd_pos, &obj->pos );
2499
2500                         //Play a sound effect
2501                         if ( lifetime > 750 )   {
2502                                 // 1.00 second effect
2503                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_05], &snd_pos, &View_position, obj->radius );
2504                         } else if ( lifetime >  500 )   {
2505                                 // 0.75 second effect
2506                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_04], &snd_pos, &View_position, obj->radius );
2507                         } else if ( lifetime >  250 )   {
2508                                 // 0.50 second effect
2509                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_03], &snd_pos, &View_position, obj->radius );
2510                         } else if ( lifetime >  100 )   {
2511                                 // 0.25 second effect
2512                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_02], &snd_pos, &View_position, obj->radius );
2513                         } else {
2514                                 // 0.10 second effect
2515                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius );
2516                         }
2517                 }
2518         }
2519
2520         // maybe move arc points around
2521         for (i=0; i<MAX_SHIP_ARCS; i++ )        {
2522                 if ( timestamp_valid( shipp->arc_timestamp[i] ) )       {
2523                         if ( !timestamp_elapsed( shipp->arc_timestamp[i] ) )    {                                                       
2524                                 // Maybe move a vertex....  20% of the time maybe?
2525                                 int mr = myrand();
2526                                 if ( mr < MY_RAND_MAX/5 )       {
2527                                         vector v1, v2;
2528                                         submodel_get_two_random_points( shipp->modelnum, -1, &v1, &v2 );
2529
2530                                         vector static_one;
2531
2532                                         if ( mr % 2 )   {
2533                                                 static_one = shipp->arc_pts[i][0];
2534                                         } else {
2535                                                 static_one = shipp->arc_pts[i][1];
2536                                         }
2537
2538                                         // For large ships, cap the length to be 25% of max radius
2539                                         if ( obj->radius > 200.0f )     {
2540                                                 float max_dist = obj->radius * MAX_ARC_LENGTH_PERCENTAGE;
2541                                                 
2542                                                 vector tmp;
2543                                                 float d;
2544
2545                                                 // Cap arc 2->1
2546                                                 vm_vec_sub( &tmp, &v1, &static_one );
2547                                                 d = vm_vec_mag_quick( &tmp );
2548                                                 if ( d > max_dist )     {
2549                                                         vm_vec_scale_add( &v1, &static_one, &tmp, max_dist / d );
2550                                                 }
2551                                         }
2552
2553                                         shipp->arc_pts[i][mr % 2] = v1;
2554                                 }
2555                         }
2556                 }
2557         }
2558 }
2559
2560 int l_cruiser_count = 1;
2561 int l_big_count = 2;
2562 int l_huge_count = 3;
2563 float l_max_radius = 3000.0f;
2564 void shipfx_do_lightning_frame( ship *shipp )
2565 {
2566         /*
2567         ship_info *sip;
2568         object *objp;
2569         int stamp, count;
2570         vector v1, v2, n1, n2, temp, temp2;
2571         bolt_info binfo;
2572
2573         // sanity checks
2574         SDL_assert(shipp != NULL);
2575         if(shipp == NULL){
2576                 return;
2577         } 
2578         SDL_assert(shipp->ship_info_index >= 0);
2579         if(shipp->ship_info_index < 0){
2580                 return;
2581         }       
2582         SDL_assert(shipp->objnum >= 0);
2583         if(shipp->objnum < 0){
2584                 return;
2585         }       
2586
2587         // get some pointers
2588         sip = &Ship_info[shipp->ship_info_index];
2589         objp = &Objects[shipp->objnum]; 
2590
2591         // if this is not a nebula mission, don't do anything
2592         if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){
2593                 shipp->lightning_stamp = -1;
2594                 return;
2595         }
2596         
2597         // if this not a cruiser or big ship
2598         if(!((sip->flags & SIF_CRUISER) || (sip->flags & SIF_BIG_SHIP) || (sip->flags & SIF_HUGE_SHIP))){
2599                 shipp->lightning_stamp = -1;
2600                 return;
2601         }
2602
2603         // determine stamp and count values
2604         if(sip->flags & SIF_CRUISER){
2605                 stamp = (int)((float)(Nebl_cruiser_min + ((Nebl_cruiser_max - Nebl_cruiser_min) * Nebl_intensity)) * frand_range(0.8f, 1.1f));
2606                 count = l_cruiser_count;
2607         } 
2608         else {
2609                 if(sip->flags & SIF_HUGE_SHIP){
2610                         stamp = (int)((float)(Nebl_supercap_min + ((Nebl_supercap_max - Nebl_supercap_min) * Nebl_intensity)) * frand_range(0.8f, 1.1f));
2611                         count = l_huge_count;
2612                 } else {
2613                         stamp = (int)((float)(Nebl_cap_min + ((Nebl_cap_max - Nebl_cap_min) * Nebl_intensity)) * frand_range(0.8f, 1.1f));
2614                         count = l_big_count;
2615                 }
2616         }
2617
2618         // if his timestamp is unset
2619         if(shipp->lightning_stamp == -1){
2620                 shipp->lightning_stamp = timestamp(stamp);
2621                 return;
2622         }
2623         // if his timestamp is currently unelapsed
2624         if(!timestamp_elapsed(shipp->lightning_stamp)){
2625                 return;
2626         }
2627
2628         mprintf(("SHIP BOLT\n"));
2629
2630         // restamp him first
2631         shipp->lightning_stamp = timestamp(stamp);
2632
2633         // ah, now we can create some lightning bolts
2634         count = (int)frand_range(0.0f, (float)count);
2635         while(count > 0){
2636                 // get 2 points on the hull of the ship
2637                 submodel_get_two_random_points(shipp->modelnum, 0, &v1, &v2, &n1, &n2);         
2638
2639                 // make up to 2 bolts
2640                 if(objp->radius > l_max_radius){
2641                         vm_vec_scale_add(&temp2, &v1, &n1, l_max_radius);
2642                 } else {
2643                         vm_vec_scale_add(&temp2, &v1, &n1, objp->radius);
2644                 }
2645                 vm_vec_unrotate(&temp, &temp2, &objp->orient);
2646                 vm_vec_add2(&temp, &objp->pos);
2647                 vm_vec_unrotate(&temp2, &v1, &objp->orient);
2648                 vm_vec_add2(&temp2, &objp->pos);
2649
2650                 // create the bolt
2651                 binfo.start = temp;
2652                 binfo.strike = temp2;
2653                 binfo.num_strikes = 3;
2654                 binfo.noise = 0.045f;
2655                 binfo.life = 375;
2656                 binfo.delay = (int)frand_range(0.0f, 1600.0f);
2657                 nebl_bolt(&binfo);
2658                 count--;
2659         
2660                 // done
2661                 if(count <= 0){
2662                         break;
2663                 }
2664
2665                 // one more             
2666                 if(objp->radius > l_max_radius){
2667                         vm_vec_scale_add(&temp2, &v2, &n2, l_max_radius);
2668                 } else {
2669                         vm_vec_scale_add(&temp2, &v2, &n2, objp->radius);
2670                 }
2671                 vm_vec_unrotate(&temp, &temp2, &objp->orient);
2672                 vm_vec_add2(&temp, &objp->pos);
2673                 vm_vec_unrotate(&temp2, &v2, &objp->orient);
2674                 vm_vec_add2(&temp2, &objp->pos);
2675
2676                 // create the bolt
2677                 binfo.start = temp;
2678                 binfo.strike = temp2;
2679                 binfo.num_strikes = 3;
2680                 binfo.noise = 0.045f;
2681                 binfo.life = 375;
2682                 binfo.delay = (int)frand_range(0.0f, 1600.0f);
2683                 nebl_bolt(&binfo);              
2684                 count--;
2685         }
2686         */
2687 }
2688
2689 // do all shockwaves for a ship blowing up
2690 void shipfx_do_shockwave_stuff(ship *shipp, shockwave_create_info *sci)
2691 {
2692         ship_info *sip;
2693         object *objp;
2694         polymodel *pm;
2695         vector temp, dir, shockwave_pos;
2696         vector head = vmd_zero_vector;
2697         vector tail = vmd_zero_vector;  
2698         float step, cur;
2699         int idx;
2700
2701         // sanity checks
2702         SDL_assert(shipp != NULL);
2703         if(shipp == NULL){
2704                 return;
2705         } 
2706         SDL_assert(shipp->ship_info_index >= 0);
2707         if(shipp->ship_info_index < 0){
2708                 return;
2709         }       
2710         SDL_assert(shipp->objnum >= 0);
2711         if(shipp->objnum < 0){
2712                 return;
2713         }
2714         SDL_assert(sci != NULL);
2715         if (sci == NULL) {
2716                 return;
2717         }
2718
2719         // get some pointers
2720         sip = &Ship_info[shipp->ship_info_index];
2721         objp = &Objects[shipp->objnum]; 
2722
2723         SDL_assert(sip->shockwave_count > 0);
2724         if(sip->shockwave_count <= 0){
2725                 return;
2726         }
2727
2728         // get vectors at the head and tail of the object, dead center          
2729         pm = model_get(shipp->modelnum);
2730         if(pm == NULL){
2731                 return;
2732         }
2733         head.xyz.x = pm->submodel[0].offset.xyz.x;
2734         head.xyz.y = pm->submodel[0].offset.xyz.y;
2735         head.xyz.z = pm->maxs.xyz.z;
2736
2737         tail.xyz.x = pm->submodel[0].offset.xyz.x;
2738         tail.xyz.y = pm->submodel[0].offset.xyz.y;
2739         tail.xyz.z = pm->mins.xyz.z;
2740
2741         // transform the vectors into world coords
2742         vm_vec_unrotate(&temp, &head, &objp->orient);
2743         vm_vec_add(&head, &temp, &objp->pos);
2744         vm_vec_unrotate(&temp, &tail, &objp->orient);
2745         vm_vec_add(&tail, &temp, &objp->pos);
2746
2747         // now create as many shockwaves as needed
2748         vm_vec_sub(&dir, &head, &tail);
2749         step = 1.0f / ((float)sip->shockwave_count + 1.0f);
2750         cur = step;
2751         for(idx=0; idx<sip->shockwave_count; idx++){
2752                 // get the shockwave position           
2753                 temp = dir;
2754                 vm_vec_scale(&temp, cur);
2755                 vm_vec_add(&shockwave_pos, &tail, &temp);
2756
2757                 // if knossos device, make shockwave in center
2758                 if (Ship_info[shipp->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
2759                         shockwave_pos = Objects[shipp->objnum].pos;
2760                 }
2761
2762                 // create the shockwave
2763                 shockwave_create_info sci2;
2764                 sci2.blast = (sci->blast / (float)sip->shockwave_count) * frand_range(0.75f, 1.25f);
2765                 sci2.damage = (sci->damage / (float)sip->shockwave_count) * frand_range(0.75f, 1.25f);
2766                 sci2.inner_rad = sci->inner_rad;
2767                 sci2.outer_rad = sci->outer_rad;
2768                 sci2.speed = sci->speed * frand_range(0.75f, 1.25f);
2769                 sci2.rot_angle = frand_range(0.0f, 359.0f);
2770
2771                 shockwave_create(shipp->objnum, &shockwave_pos, &sci2, SW_SHIP_DEATH, (int)frand_range(0.0f, 350.0f));
2772                 // shockwave_create(shipp->objnum, &objp->pos, sip->shockwave_speed, sip->inner_rad, sip->outer_rad, sip->damage, sip->blast, SW_SHIP_DEATH);
2773
2774                 // next shockwave
2775                 cur += step;
2776         }
2777 }
2778
2779 int Wash_on = 1;
2780 DCF_BOOL(engine_wash, Wash_on);
2781 #define ENGINE_WASH_CHECK_INTERVAL              250     // (4x sec)
2782 // Do engine wash effect for ship
2783 // Assumes length of engine wash is greater than radius of engine wash hemisphere
2784 void engine_wash_ship_process(ship *shipp)
2785 {
2786         int idx, j;             
2787         object *objp, *ship_objp, *max_ship_intensity_objp;
2788         int started_with_no_wash = shipp->wash_intensity <= 0 ? 1 : 0;
2789
2790         if (!Wash_on) {
2791                 return;
2792         }
2793
2794         SDL_assert(shipp != NULL);
2795         SDL_assert(shipp->objnum >= 0);
2796         objp = &Objects[shipp->objnum];
2797         ship_obj *so;
2798         ship_objp = NULL;
2799
2800         vector world_thruster_pos, world_thruster_norm, apex, thruster_to_ship, apex_to_ship, temp;
2801         float dist_sqr, inset_depth, dot_to_ship, max_ship_intensity;
2802         polymodel *pm;
2803
2804         float max_wash_dist, half_angle, radius_mult;
2805
2806         // if this is not a fighter or bomber, we don't care
2807         if ((objp->type != OBJ_SHIP) || !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ) {
2808                 return;
2809         }
2810
2811         // is it time to check for engine wash 
2812         int time_to_next_hit = timestamp_until(shipp->wash_timestamp);
2813         if (time_to_next_hit < 0) {
2814                 if (time_to_next_hit < -ENGINE_WASH_CHECK_INTERVAL) {
2815                         time_to_next_hit = 0;
2816                 }
2817
2818                 // keep interval constant independent of framerate
2819                 shipp->wash_timestamp = timestamp(ENGINE_WASH_CHECK_INTERVAL + time_to_next_hit);
2820
2821                 // initialize wash params
2822                 shipp->wash_intensity = 0.0f;
2823                 vm_vec_zero(&shipp->wash_rot_axis);
2824                 max_ship_intensity_objp = NULL;
2825                 max_ship_intensity = 0;
2826         } else {
2827                 return;
2828         }
2829
2830         // only do damage if we're within half of the max wash distance
2831         int do_damage = 0;
2832
2833         // go thru Ship_used_list and check if we're in wash from CAP or SUPERCAP (SIF_HUGE)
2834         for (so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so)) {
2835                 ship_objp = &Objects[so->objnum];
2836
2837                 // don't do small ships
2838                 if ( (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) ) {
2839                         continue;
2840                 }
2841
2842                 pm = model_get(Ships[ship_objp->instance].modelnum);
2843                 float ship_intensity = 0;
2844
2845                 // if engines disabled, no engine wash
2846                 if (ship_get_subsystem_strength(&Ships[ship_objp->instance], SUBSYSTEM_ENGINE) < 0.01) {
2847                         continue;
2848                 }
2849
2850                 float   speed_scale;
2851                 if (ship_objp->phys_info.speed > 20.0f)
2852                         speed_scale = 1.0f;
2853                 else
2854                         speed_scale = ship_objp->phys_info.speed/20.0f;
2855
2856                 for (idx = 0; idx < pm->n_thrusters; idx++) {
2857                         thruster_bank *bank = &pm->thrusters[idx];
2858
2859                         // check if thruster bank has engine wash
2860                         if (bank->wash_info_index < 0) {
2861                                 // if huge, give default engine wash
2862                                 if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
2863                                         bank->wash_info_index = 0;
2864                                         nprintf(("wash", "Adding default engine wash to ship %s", Ship_info[Ships[ship_objp->instance].ship_info_index].name));
2865                                 } else {
2866                                         continue;
2867                                 }
2868                         }
2869
2870                         engine_wash_info *ewp = &Engine_wash_info[(int)bank->wash_info_index];
2871                         half_angle = ewp->angle;
2872                         radius_mult = ewp->radius_mult;
2873
2874                         for (j=0; j<bank->num_slots; j++) {
2875                                 // get world pos of thruster
2876                                 vm_vec_unrotate(&world_thruster_pos, &bank->pnt[j], &ship_objp->orient);
2877                                 vm_vec_add2(&world_thruster_pos, &ship_objp->pos);
2878                                 
2879                                 // get world norm of thruster;
2880                                 vm_vec_unrotate(&world_thruster_norm, &bank->norm[j], &ship_objp->orient);
2881
2882                                 // get vector from thruster to ship
2883                                 vm_vec_sub(&thruster_to_ship, &objp->pos, &world_thruster_pos);
2884
2885                                 // check if on back side of thruster
2886                                 dot_to_ship = vm_vec_dotprod(&thruster_to_ship, &world_thruster_norm);
2887                                 if (dot_to_ship > 0) {
2888
2889                                         // get max wash distance
2890                                         max_wash_dist = SDL_max(ewp->length, bank->radius[j]*ewp->radius_mult);
2891
2892                                         // check if within dist range
2893                                         dist_sqr = vm_vec_mag_squared(&thruster_to_ship);
2894                                         if (dist_sqr < max_wash_dist*max_wash_dist) {
2895
2896                                                 // check if inside the sphere
2897                                                 if (dist_sqr < radius_mult*radius_mult*bank->radius[j]*bank->radius[j]) {
2898                                                         vm_vec_crossprod(&temp, &world_thruster_norm, &thruster_to_ship);
2899                                                         vm_vec_scale_add2(&shipp->wash_rot_axis, &temp, dot_to_ship / dist_sqr);
2900 //                                                      shipp->wash_intensity += (1.0f - dist_sqr / (max_wash_dist*max_wash_dist));
2901                                                         ship_intensity += (1.0f - dist_sqr / (max_wash_dist*max_wash_dist));
2902                                                         if (!do_damage) {
2903                                                                 if (dist_sqr < 0.25 * max_wash_dist * max_wash_dist) {
2904                                                                         do_damage = 1;
2905                                                                 }
2906                                                         }
2907                                                 } else {
2908                                                         // check if inside cone - first fine apex of cone
2909                                                         inset_depth = float(bank->radius[j] / tan(half_angle));
2910                                                         vm_vec_scale_add(&apex, &world_thruster_pos, &world_thruster_norm, -inset_depth);
2911                                                         vm_vec_sub(&apex_to_ship, &objp->pos, &apex);
2912                                                         vm_vec_normalize(&apex_to_ship);
2913
2914                                                         // check if inside cone angle
2915                                                         if (vm_vec_dotprod(&apex_to_ship, &world_thruster_norm) > cos(half_angle)) {
2916                                                                 vm_vec_crossprod(&temp, &world_thruster_norm, &thruster_to_ship);
2917                                                                 vm_vec_scale_add2(&shipp->wash_rot_axis, &temp, dot_to_ship / dist_sqr);
2918 //                                                              shipp->wash_intensity += (1.0f - dist_sqr / (max_wash_dist*max_wash_dist));
2919                                                                 ship_intensity += (1.0f - dist_sqr / (max_wash_dist*max_wash_dist));
2920                                                                 if (!do_damage) {
2921                                                                         if (dist_sqr < 0.25 * max_wash_dist * max_wash_dist) {
2922                                                                                 do_damage = 1;
2923                                                                         }
2924                                                                 }
2925                                                         }
2926                                                 }
2927                                         }
2928                                 }
2929                         }
2930                 }
2931                 shipp->wash_intensity += ship_intensity * speed_scale;
2932                 if (ship_intensity > max_ship_intensity) {
2933                         max_ship_intensity = ship_intensity;
2934                         max_ship_intensity_objp = ship_objp;
2935                 }
2936         }
2937
2938         // apply damage at rate of 1%/sec
2939         if (shipp->wash_intensity > 0) {
2940                 SDL_assert(max_ship_intensity_objp != NULL);
2941
2942                 nprintf(("wash", "Wash intensity %.2f\n", shipp->wash_intensity));
2943
2944                 float damage;
2945                 if (!do_damage) {
2946                         damage = 0;
2947                 } else {
2948                         damage = (0.001f * 0.003f * ENGINE_WASH_CHECK_INTERVAL * Ship_info[shipp->ship_info_index].initial_hull_strength * shipp->wash_intensity);
2949                 }
2950
2951                 ship_apply_wash_damage(&Objects[shipp->objnum], max_ship_intensity_objp, damage);
2952
2953                 // if we had no wash before now, add the wash object sound
2954                 if(started_with_no_wash){
2955                         if(shipp != Player_ship){
2956                                 obj_snd_assign(shipp->objnum, SND_ENGINE_WASH, &vmd_zero_vector, 1);
2957                         } else {                                
2958                                 Player_engine_wash_loop = snd_play_looping( &Snds[SND_ENGINE_WASH], 0.0f, 1.0f);
2959                         }
2960                 }
2961         } 
2962         // if we've got no wash, kill any wash object sounds from this guy
2963         else {
2964                 if(shipp != Player_ship){
2965                         obj_snd_delete(shipp->objnum, SND_ENGINE_WASH);
2966                 } else {
2967                         snd_stop(Player_engine_wash_loop);
2968                         Player_engine_wash_loop = -1;
2969                 }
2970         }
2971 }
2972
2973 // engine wash level init
2974 void shipfx_engine_wash_level_init()
2975 {
2976         Player_engine_wash_loop = -1;
2977 }
2978
2979 // pause engine wash sounds
2980 void shipfx_stop_engine_wash_sound()
2981 {
2982         if(Player_engine_wash_loop != -1){
2983                 snd_stop(Player_engine_wash_loop);
2984                 Player_engine_wash_loop = -1;
2985         }
2986 }