FS2_Open
Open source remastering of the Freespace 2 engine
shipfx.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #include "asteroid/asteroid.h"
14 #include "bmpman/bmpman.h"
15 #include "cmdline/cmdline.h"
16 #include "debris/debris.h"
17 #include "debugconsole/console.h"
18 #include "fireball/fireballs.h"
20 #include "gamesnd/gamesnd.h"
21 #include "globalincs/linklist.h"
22 #include "hud/hudmessage.h"
23 #include "io/timer.h"
24 #include "lighting/lighting.h"
25 #include "math/fvi.h"
26 #include "model/model.h"
27 #include "network/multi.h"
28 #include "network/multimsgs.h"
29 #include "network/multiutil.h"
30 #include "object/object.h"
31 #include "object/objectdock.h"
32 #include "object/objectsnd.h"
33 #include "parse/parselo.h"
34 #include "parse/scripting.h"
35 #include "particle/particle.h"
36 #include "playerman/player.h"
37 #include "render/3d.h" // needed for View_position, which is used when playing a 3D sound
38 #include "ship/ship.h"
39 #include "ship/shipfx.h"
40 #include "ship/shiphit.h"
41 #include "weapon/muzzleflash.h"
42 #include "weapon/shockwave.h"
43 #include "weapon/weapon.h"
44 
45 #ifndef NDEBUG
46 extern float flFrametime;
47 extern int Framecount;
48 #endif
49 
50 extern int Cmdline_tbp;
51 
52 #define SHIP_CANNON_BITMAP "argh"
54 
56 
57 extern float splode_level;
58 
60 {
61  Assert(submodel_num != -1);
62 
63  // maybe no active sparks on submodel
64  if (shipp->num_hits == 0) {
65  return;
66  }
67 
68  for (int spark_num=0; spark_num<shipp->num_hits; spark_num++) {
69  if (shipp->sparks[spark_num].submodel_num == submodel_num) {
70  shipp->sparks[spark_num].end_time = timestamp(1);
71  }
72  }
73 }
74 
75 void model_get_rotating_submodel_axis(vec3d *model_axis, vec3d *world_axis, int model_instance_num, int submodel_num, matrix *objorient);
76 
82 void shipfx_subsystem_maybe_create_live_debris(object *ship_objp, ship *ship_p, ship_subsys *subsys, vec3d *exp_center, float exp_mag)
83 {
84  // initializations
85  ship *shipp = &Ships[ship_objp->instance];
86  polymodel *pm = model_get(Ship_info[ship_p->ship_info_index].model_num);
88  int submodel_num = subsys->system_info->subobj_num;
90 
91  object *live_debris_obj;
92  int i, num_live_debris, live_debris_submodel;
93 
94  // get number of live debris objects to create
95  num_live_debris = pm->submodel[submodel_num].num_live_debris;
96  if ((num_live_debris <= 0) || (subsys->flags & SSF_NO_LIVE_DEBRIS)) {
97  return;
98  }
99 
100  // copy angles
101  angles copy_angs = pm->submodel[submodel_num].angs;
102  angles zero_angs = {0.0f, 0.0f, 0.0f};
103 
104  // make sure the axis point is set
105  vec3d model_axis, world_axis, rotvel, world_axis_pt;
106  matrix m_rot; // rotation for debris orient about axis
107 
108  if (pm->submodel[submodel_num].movement_type == MOVEMENT_TYPE_ROT || pm->submodel[submodel_num].movement_type == MOVEMENT_TYPE_INTRINSIC_ROTATE) {
109  if ( !sii->axis_set ) {
110  model_init_submodel_axis_pt(sii, pm->id, submodel_num);
111  }
112 
113  // get the rotvel
114  model_get_rotating_submodel_axis(&model_axis, &world_axis, shipp->model_instance_num, submodel_num, &ship_objp->orient);
115  vm_vec_copy_scale(&rotvel, &world_axis, sii->cur_turn_rate);
116 
117  model_instance_find_world_point(&world_axis_pt, &sii->pt_on_axis, shipp->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
118 
119  vm_quaternion_rotate(&m_rot, vm_vec_mag((vec3d*)&sii->angs), &model_axis);
120  } else {
121  //fix to allow non rotating submodels to use live debris
122  vm_vec_zero(&rotvel);
123  vm_set_identity(&m_rot);
124  vm_vec_zero(&world_axis_pt);
125  }
126 
127  // create live debris pieces
128  for (i=0; i<num_live_debris; i++) {
129  live_debris_submodel = pm->submodel[submodel_num].live_debris[i];
130  vec3d start_world_pos, start_model_pos, end_world_pos;
131 
132  // get start world pos
133  vm_vec_zero(&start_world_pos);
134  model_instance_find_world_point(&start_world_pos, &pm->submodel[live_debris_submodel].offset, shipp->model_instance_num, live_debris_submodel, &ship_objp->orient, &ship_objp->pos );
135 
136  // convert to model coord of underlying submodel
137  // set angle to zero
138  pm->submodel[submodel_num].angs = zero_angs;
139  world_find_model_instance_point(&start_model_pos, &start_world_pos, pmi, submodel_num, &ship_objp->orient, &ship_objp->pos);
140 
141  // rotate from submodel coord to world coords
142  // reset angle to current angle
143  pm->submodel[submodel_num].angs = copy_angs;
144  model_instance_find_world_point(&end_world_pos, &start_model_pos, shipp->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
145 
146  int fireball_type = fireball_ship_explosion_type(&Ship_info[ship_p->ship_info_index]);
147  if(fireball_type < 0) {
148  fireball_type = FIREBALL_EXPLOSION_MEDIUM;
149  }
150  // create fireball here.
151  fireball_create(&end_world_pos, fireball_type, FIREBALL_MEDIUM_EXPLOSION, OBJ_INDEX(ship_objp), pm->submodel[live_debris_submodel].rad);
152 
153  // create debris
154  live_debris_obj = debris_create(ship_objp, pm->id, live_debris_submodel, &end_world_pos, exp_center, 1, exp_mag);
155 
156  // only do if debris is created
157  if (live_debris_obj) {
158  // get radial velocity of debris
159  vec3d delta_x, radial_vel;
160  vm_vec_sub(&delta_x, &end_world_pos, &world_axis_pt);
161  vm_vec_cross(&radial_vel, &rotvel, &delta_x);
162 
163  if (Ship_info[ship_p->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
164  // set velocity to cross center of knossos device
165  vec3d rand_vec, vec_to_center;
166 
167  float vel_mag = vm_vec_mag_quick(&radial_vel) * 1.3f * (0.9f + 0.2f*frand());
168  vm_vec_normalized_dir(&vec_to_center, &world_axis_pt, &end_world_pos);
169  vm_vec_rand_vec_quick(&rand_vec);
170  vm_vec_scale_add2(&vec_to_center, &rand_vec, 0.2f);
171  vm_vec_scale_add2(&live_debris_obj->phys_info.vel, &vec_to_center, vel_mag);
172 
173  } else {
174  // Get rotation of debris object
175  matrix copy = live_debris_obj->orient;
176  vm_matrix_x_matrix(&live_debris_obj->orient, &copy, &m_rot);
177 
178  // Add radial velocity (at least as large as exp velocity)
179  vec3d temp_vel; // explosion velocity with ship_obj velocity removed
180  vm_vec_sub(&temp_vel, &live_debris_obj->phys_info.vel, &ship_objp->phys_info.vel);
181 
182  // find magnitudes of radial and temp velocity
183  float vel_mag = vm_vec_mag(&temp_vel);
184  float rotvel_mag = vm_vec_mag(&radial_vel);
185 
186  if (rotvel_mag > 0.1) {
187  float scale = (1.2f + 0.2f * frand()) * vel_mag / rotvel_mag;
188  // always add *at least* rotvel
189  if (scale < 1) {
190  scale = 1.0f;
191  }
192 
193  if (exp_mag > 1) { // whole ship going down
194  scale = exp_mag;
195  }
196 
197  if (Ship_info[ship_p->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
198  scale = 1.0f;
199  }
200 
201  vm_vec_scale_add2(&live_debris_obj->phys_info.vel, &radial_vel, scale);
202  }
203 
204  // scale up speed of debris if ship_obj > 125, but not for knossos
205  if (ship_objp->radius > 250 && !(Ship_info[ship_p->ship_info_index].flags & SIF_KNOSSOS_DEVICE)) {
206  vm_vec_scale(&live_debris_obj->phys_info.vel, ship_objp->radius/250.0f);
207  }
208  }
209 
210  shipfx_debris_limit_speed(&Debris[live_debris_obj->instance], ship_p);
211  }
212  }
213 }
214 
216 {
217  int found = FALSE;
218 
219  // go through list of ship subsystems and find name
220  ship_subsys *pss = NULL;
221  for (pss=GET_FIRST(&shipp->subsys_list); pss!=END_OF_LIST(&shipp->subsys_list); pss=GET_NEXT(pss)) {
222  if ( subsystem_stricmp(pss->system_info->subobj_name, name) == 0) {
223  found = TRUE;
224  break;
225  }
226  }
227 
228  // set its blown off flag to TRUE
229  Assert(found);
230  if (found) {
231  pss->submodel_info_1.blown_off = 1;
232  }
233 }
234 
235 
241 {
242  // if ship has live debris, detonate that subsystem now
243  // search for any live debris
244 
245  ship *shipp = &Ships[ship_objp->instance];
246  polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
248 
249  // no subsystems -> no live debris.
250  if (Ship_info[shipp->ship_info_index].n_subsystems == 0) {
251  return;
252  }
253 
254  int live_debris_submodel = -1;
255  for (int idx=0; idx<pm->num_debris_objects; idx++) {
256  if (pm->submodel[pm->debris_objects[idx]].is_live_debris) {
257  live_debris_submodel = pm->debris_objects[idx];
258 
259  // get submodel that produces live debris
260  int model_get_parent_submodel_for_live_debris( int model_num, int live_debris_model_num );
261  int parent = model_get_parent_submodel_for_live_debris(pm->id, live_debris_submodel);
262  Assert(parent != -1);
263 
264  // check if already blown off (ship model set)
265  if ( !pmi->submodel[parent].blown_off ) {
266 
267  // get ship_subsys for live_debris
268  // Go through all subsystems and look for submodel the subsystems with "parent" submodel.
269  ship_subsys *pss = NULL;
270  for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
271  if (pss->system_info->subobj_num == parent) {
272  break;
273  }
274  }
275 
276  Assert (pss != NULL);
277  if (pss != NULL) {
278  if (pss->system_info != NULL) {
279  vec3d exp_center, tmp = ZERO_VECTOR;
280  model_instance_find_world_point(&exp_center, &tmp, shipp->model_instance_num, parent, &ship_objp->orient, &ship_objp->pos );
281 
282  // if not blown off, blow it off
283  shipfx_subsystem_maybe_create_live_debris(ship_objp, shipp, pss, &exp_center, 3.0f);
284 
285  // now set subsystem as blown off, so we only get one copy
286  pm->submodel[parent].blown_off = 1;
288  }
289  }
290  }
291  }
292  }
293 }
294 
295 void shipfx_blow_off_subsystem(object *ship_objp, ship *ship_p,ship_subsys *subsys, vec3d *exp_center, bool no_explosion)
296 {
297  vec3d subobj_pos;
298  int model_num = Ship_info[ship_p->ship_info_index].model_num;
299 
300  model_subsystem *psub = subsys->system_info;
301 
302  get_subsystem_world_pos(ship_objp, subsys, &subobj_pos);
303 
304  // get rid of sparks on submodel that is destroyed
306 
307  // create debris shards
308  if (!(subsys->flags & SSF_VANISHED) && !no_explosion) {
309  shipfx_blow_up_model(ship_objp, model_num, psub->subobj_num, 50, &subobj_pos );
310 
311  // create live debris objects, if any
312  // TODO: some MULITPLAYER implcations here!!
313  shipfx_subsystem_maybe_create_live_debris(ship_objp, ship_p, subsys, exp_center, 1.0f);
314 
315  int fireball_type = fireball_ship_explosion_type(&Ship_info[ship_p->ship_info_index]);
316  if(fireball_type < 0) {
317  fireball_type = FIREBALL_EXPLOSION_MEDIUM;
318  }
319  // create first fireball
320  fireball_create( &subobj_pos, fireball_type, FIREBALL_MEDIUM_EXPLOSION, OBJ_INDEX(ship_objp), psub->radius );
321  }
322 }
323 
324 
325 void shipfx_blow_up_hull(object *obj, int model, vec3d *exp_center)
326 {
327  int i;
328  polymodel * pm;
329  ushort sig_save;
330 
331 
332  pm = model_get(model);
333  if (!pm) return;
334 
335  // in multiplayer, send a debris_hull_create packet. Save/restore the debris signature
336  // when in misison only (since we can create debris pieces before mission starts)
337  sig_save = 0;
338 
339  if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) ) {
342  }
343 
344  bool try_live_debris = true;
345  for (i=0; i<pm->num_debris_objects; i++ ) {
346  if (! pm->submodel[pm->debris_objects[i]].is_live_debris) {
347  vec3d tmp = ZERO_VECTOR;
348  model_find_world_point(&tmp, &pm->submodel[pm->debris_objects[i]].offset, model, 0, &obj->orient, &obj->pos );
349  debris_create( obj, model, pm->debris_objects[i], &tmp, exp_center, 1, 3.0f );
350  } else {
351  if ( try_live_debris ) {
352  // only create live debris once
353  // this creates *all* the live debris for *all* the currently live subsystems.
354  try_live_debris = false;
356  }
357  }
358  // in multiplayer we need to increment the network signature for each piece of debris we create
359  if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) ) {
361  }
362  }
363 
364  // restore the debris signature to it's original value.
365  if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) ) {
367  }
368 }
369 
370 
374 void shipfx_blow_up_model(object *obj,int model, int submodel, int ndebris, vec3d *exp_center )
375 {
376  int i;
377 
378  // if in a multiplayer game -- seed the random number generator with a value that will be the same
379  // on all clients in the game -- the net_signature of the object works nicely -- since doing so should
380  // ensure that all pieces of debris will get scattered in same direction on all machines
381  if ( Game_mode & GM_MULTIPLAYER )
382  srand( obj->net_signature );
383 
384  // made a change to allow anyone but multiplayer client to blow up hull. Clients will do it when
385  // they get the create packet
386  if ( submodel == 0 ) {
387  shipfx_blow_up_hull(obj,model, exp_center );
388  }
389 
390  for (i=0; i<ndebris; i++ ) {
391  vec3d pnt1, pnt2;
392 
393  // Gets two random points on the surface of a submodel
395  submodel_get_two_random_points(model, submodel, &pnt1, &pnt2 );
396  } else {
397  submodel_get_two_random_points_better(model, submodel, &pnt1, &pnt2 );
398  }
399 
400  vec3d tmp, outpnt;
401 
402  vm_vec_avg( &tmp, &pnt1, &pnt2 );
403  model_find_world_point(&outpnt, &tmp, model,submodel, &obj->orient, &obj->pos );
404 
405  debris_create( obj, -1, -1, &outpnt, exp_center, 0, 1.0f );
406  }
407 }
408 
409 
410 // =================================================
411 // SHIP WARP IN EFFECT CODE
412 // =================================================
413 
414 
418 static float shipfx_calculate_effect_radius( object *objp, int warp_dir )
419 {
420  float rad;
421 
422  // if docked, we need to calculate the overall cross-sectional radius around the z-axis (longitudinal axis)
423  if (object_is_docked(objp))
424  {
426  }
427  // if it's not docked, we can save a lot of work by just using width and height
428  else
429  {
430  //WMC - see if a radius was specified
432  if(warp_dir == WD_WARP_IN && sip->warpin_radius > 0.0f)
433  {
434  return sip->warpin_radius;
435  }
436  else if(warp_dir == WD_WARP_OUT && sip->warpout_radius > 0.0f)
437  {
438  return sip->warpout_radius;
439  }
440 
441  float w, h;
442  polymodel *pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
443 
444  w = pm->maxs.xyz.x - pm->mins.xyz.x;
445  h = pm->maxs.xyz.y - pm->mins.xyz.y;
446 
447  if ( w > h )
448  rad = w / 2.0f;
449  else
450  rad = h / 2.0f;
451  }
452 
453  return rad*3.0f;
454 }
455 
456 // How long the stage 1 & stage 2 of warp in effect lasts.
457 // There are different times for small, medium, and large ships.
458 // The appropriate values are picked depending on the ship's
459 // radius.
460 #define SHIPFX_WARP_DELAY (2.0f) // time for warp effect to ramp up before ship moves into it.
461 
462 // Give object objp, calculate how long it should take the
463 // ship to go through the warp effect and how fast the ship
464 // should go. For reference, capital ship of 2780m
465 // should take 7 seconds to fly through. Fighters of 30,
466 // should take 1.5 seconds to fly through.
467 
468 #define LARGEST_RAD 1390.0f
469 #define LARGEST_RAD_TIME 7.0f
470 
471 #define SMALLEST_RAD 15.0f
472 #define SMALLEST_RAD_TIME 1.5f
473 
474 float shipfx_calculate_warp_time(object *objp, int warp_dir)
475 {
476  if(objp->type == OBJ_SHIP)
477  {
478  ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
479 
480  //Warpin time defined
481  if( (warp_dir == WD_WARP_IN) && (sip->warpin_time > 0.0f)) {
482  return (float)sip->warpin_time/1000.0f;
483  //Warpout time defined
484  } else if( (warp_dir == WD_WARP_OUT) && (sip->warpout_time > 0.0f)) {
485  return (float)sip->warpout_time/1000.0f;
486  //Warpin defined
487  } else if ( (warp_dir == WD_WARP_IN) && (sip->warpin_speed != 0.0f) ) {
488  return ship_class_get_length(sip) / sip->warpin_speed;
489  //Warpout defined
490  } else if ( (warp_dir == WD_WARP_OUT) && (sip->warpout_speed != 0.0f) ) {
491  return ship_class_get_length(sip) / sip->warpout_speed;
492  //Player warpout defined
493  } else if ( (warp_dir == WD_WARP_OUT) && (objp == Player_obj) && (sip->warpout_player_speed != 0.0f) ) {
494  return ship_class_get_length(sip) / sip->warpout_player_speed;
495  //Player warpout not defined
496  } else if ( (warp_dir == WD_WARP_OUT) && (objp == Player_obj) ) {
498  }
499 
500  }
501  // Find rad_percent from 0 to 1, 0 being smallest ship, 1 being largest
502  float rad_percent = (objp->radius-SMALLEST_RAD) / (LARGEST_RAD-SMALLEST_RAD);
503  CLAMP(rad_percent, 0.0f, 1.0f);
504 
505  float rad_time = rad_percent*(LARGEST_RAD_TIME-SMALLEST_RAD_TIME) + SMALLEST_RAD_TIME;
506 
507  return rad_time;
508 }
509 
510 float shipfx_calculate_warp_dist(object *objp)
511 {
512  float length;
513 
514  Assert(objp->type == OBJ_SHIP);
515  if (objp->type != OBJ_SHIP) {
516  length = 2.0f * objp->radius;
517  } else {
518  length = ship_class_get_length(&Ship_info[Ships[objp->instance].ship_info_index]);
519  }
520  return length;
521 }
522 
523 // This is called to actually warp this object in
524 // after all the flashy fx are done, or if the flashy
525 // fx don't work for some reason.
526 void shipfx_actually_warpin(ship *shipp, object *objp)
527 {
528  shipp->flags &= (~SF_ARRIVING_STAGE_1);
529  shipp->flags &= (~SF_ARRIVING_STAGE_2);
530 
531  // let physics in on it too.
532  objp->phys_info.flags &= (~PF_WARP_IN);
533 }
534 
535 // Validate reference_objnum
537 {
538  object *special_objp;
539 
540  // must be a valid object
541  if ((objnum < 0) || (objnum >= MAX_OBJECTS))
542  return 0;
543 
544  special_objp = &Objects[objnum];
545 
546  // must be a ship
547  if (special_objp->type != OBJ_SHIP)
548  return 0;
549 
550  // must be a knossos
551  if (!(Ship_info[Ships[special_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE))
552  return 0;
553 
554  return 1;
555 }
556 
557 // JAS - code to start the ship doing the warp in effect
558 // This also starts the animating 3d effect playing.
559 // There are two modes, stage 1 and stage 2. Stage 1 is
560 // when the ship just invisibly waits for a certain time
561 // period after the effect starts, and then stage 2 begins,
562 // where the ships flies through the effect at a set
563 // velocity so it gets through in a certain amount of
564 // time.
565 void shipfx_warpin_start( object *objp )
566 {
567  ship *shipp = &Ships[objp->instance];
568 
569  if (shipp->flags & SF_ARRIVING)
570  {
571  mprintf(( "Ship '%s' is already arriving!\n", shipp->ship_name ));
572  Int3();
573  return;
574  }
575 
576  // docked ships who are not dock leaders don't use the warp effect code
577  // (the dock leader takes care of the whole group)
578  if (object_is_docked(objp) && !(shipp->flags & SF_DOCK_LEADER))
579  {
580  return;
581  }
582 
583  //WMC - Check if scripting handles this.
584  Script_system.SetHookObject("Self", objp);
586  {
587  Script_system.RunCondition(CHA_WARPIN, 0, NULL, objp);
588  Script_system.RemHookVar("Self");
589  return;
590  }
591 
592  // if there is no arrival warp, then skip the whole thing
593  if (shipp->flags & SF_NO_ARRIVAL_WARP)
594  {
595  shipfx_actually_warpin(shipp,objp);
596  return;
597  }
598 
599  shipp->warpin_effect->warpStart();
600 
601  Script_system.RunCondition(CHA_WARPIN, 0, NULL, objp);
602  Script_system.RemHookVar("Self");
603 }
604 
605 void shipfx_warpin_frame( object *objp, float frametime )
606 {
607  ship *shipp;
608 
609  shipp = &Ships[objp->instance];
610 
611  if ( shipp->flags & SF_DYING ) return;
612 
613  shipp->warpin_effect->warpFrame(frametime);
614 }
615 
616 // This is called to actually warp this object out
617 // after all the flashy fx are done, or if the flashy fx
618 // don't work for some reason. OR to skip the flashy fx.
619 void shipfx_actually_warpout(int shipnum)
620 {
621  // Once we get through effect, make the ship go away
622  ship_actually_depart(shipnum);
623 }
624 
625 // compute_special_warpout_stuff();
626 int compute_special_warpout_stuff(object *objp, float *speed, float *warp_time, vec3d *warp_pos)
627 {
628  object *sp_objp = NULL;
629  ship_info *sip;
630  int valid_reference_ship = FALSE, ref_objnum;
631  vec3d facing_normal, vec_to_knossos, center_pos;
632  float dist_to_plane;
633 
634  // knossos warpout only valid in single player
635  if (Game_mode & GM_MULTIPLAYER) {
636  mprintf(("special warpout only for single player\n"));
637  return -1;
638  }
639 
640  // find special warp ship reference
641  valid_reference_ship = FALSE;
642  ref_objnum = Ships[objp->instance].special_warpout_objnum;
643 
644  // Validate reference_objnum
645  if ((ref_objnum >= 0) && (ref_objnum < MAX_OBJECTS)) {
646  sp_objp = &Objects[ref_objnum];
647  if (sp_objp->type == OBJ_SHIP) {
648  if (Ship_info[Ships[sp_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
649  valid_reference_ship = TRUE;
650  }
651  }
652  }
653 
654  if (!valid_reference_ship) {
655  mprintf(("Special warpout reference ship is not a Knossos\n"));
656  return -1;
657  }
658  sip = &Ship_info[Ships[objp->instance].ship_info_index];
659 
660 
661  //find the actual center of the model
662  polymodel *pm = model_get(sip->model_num);
663  vm_vec_add(&center_pos, &objp->pos, &pm->autocenter);
664 
665  // get facing normal from knossos
666  vm_vec_sub(&vec_to_knossos, &sp_objp->pos, &center_pos);
667  facing_normal = sp_objp->orient.vec.fvec;
668  if (vm_vec_dot(&vec_to_knossos, &sp_objp->orient.vec.fvec) > 0) {
669  vm_vec_negate(&facing_normal);
670  }
671 
672  // find position to play the warp ani..
673  dist_to_plane = fvi_ray_plane(warp_pos, &sp_objp->pos, &facing_normal, &center_pos, &objp->orient.vec.fvec, 0.0f);
674 
675  // calculate distance to warpout point.
676  dist_to_plane += pm->mins.xyz.z;
677 
678 
679  if (dist_to_plane < 0) {
680  mprintf(("warpout started too late\n"));
681  return -1;
682  }
683 
684  // validate angle
685  float max_warpout_angle = 0.707f; // 45 degree half-angle cone for small ships
686  if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
687  max_warpout_angle = 0.866f; // 30 degree half-angle cone for BIG or HUGE
688  }
689 
690  if (-vm_vec_dot(&objp->orient.vec.fvec, &facing_normal) < max_warpout_angle) { // within allowed angle
691  Int3();
692  mprintf(("special warpout angle exceeded\n"));
693  return -1;
694  }
695 
696  // Calculate how long to fly through the effect. Not to get to the effect, just through it.
697  *warp_time = shipfx_calculate_warp_time(objp, WD_WARP_OUT);
698 
699  // Calculate speed to fly through
700  *speed = ship_get_warpout_speed(objp);
701 
702  // increase time, because we have extra distance to cover
703  *warp_time += dist_to_plane / *speed;
704 
705  return 0;
706 }
707 
708 
709 void compute_warpout_stuff(object *objp, float *speed, float *warp_time, vec3d *warp_pos)
710 {
711  Assert(objp); // Goober5000
712  ship *shipp = &Ships[objp->instance];
713  ship_info *sip = &Ship_info[shipp->ship_info_index];
714 
715  // If we're warping through the knossos, do something different.
716  if (shipp->special_warpout_objnum >= 0) {
717  if (compute_special_warpout_stuff(objp, speed, warp_time, warp_pos) != -1) {
718  return;
719  } else {
720  mprintf(("Invalid special warp\n"));
721  }
722  }
723 
724  float ship_move_dist, warp_dist;
725  vec3d center_pos;
726 
727 
728  // Calculate how long to fly through the effect. Not to get to the effect, just through it.
729  *warp_time = shipfx_calculate_warp_time(objp, WD_WARP_OUT);
730 
731  // Pick some speed at which we want to go through the warp effect.
732  *speed = ship_get_warpout_speed(objp);
733 
734  if ( objp == Player_obj ) {
735  // Goober5000 - cap at 65
736  *speed = MIN(0.8f*objp->phys_info.max_vel.xyz.z, 65.0f);
737  }
738 
739  // center the effect properly
740  if (object_is_docked(objp))
741  dock_calc_docked_center(&center_pos, objp);
742  else
743  {
744  polymodel *pm = model_get(sip->model_num);
745  vm_vec_unrotate(&center_pos, &pm->autocenter, &objp->orient);
746  vm_vec_add2(&center_pos,&objp->pos);
747  }
748 
749 
750  // If this is a huge ship, set the distance to the length of the ship
751  if (sip->flags & SIF_HUGE_SHIP)
752  {
753  ship_move_dist = 0.5f * ship_class_get_length(sip);
754  }
755  else
756  {
757  float radius;
758 
759  if (object_is_docked(objp))
760  {
761  // we need to get the longitudinal radius of our ship, so find the semilatus rectum along the Z-axis
763  }
764  else
765  radius = objp->radius;
766 
767  // Now we know our speed. Figure out how far the warp effect will be from here.
768  ship_move_dist = (*speed * SHIPFX_WARP_DELAY) + radius*1.5f; // We want to get to 1.5R away from effect
769  if ( ship_move_dist < radius*1.5f ) {
770  ship_move_dist = radius*1.5f;
771  }
772  }
773 
774 
775  // Acount for time to get to warp effect, before we actually go through it.
776  *warp_time += ship_move_dist / *speed;
777 
778  warp_dist = ship_move_dist;
779 
780  // allow for off center
781  if (sip->flags & SIF_HUGE_SHIP) {
782  polymodel *pm = model_get(sip->model_num);
783  warp_dist -= pm->mins.xyz.z;
784  }
785 
786  vm_vec_scale_add( warp_pos, &center_pos, &objp->orient.vec.fvec, warp_dist );
787 }
788 
789 // JAS - code to start the ship doing the warp out effect
790 // This puts the ship into a mode specified by SF_DEPARTING
791 // where it flies forward for a set time period at a set
792 // velocity, then disappears when that time is reached. This
793 // also starts the animating 3d effect playing.
794 void shipfx_warpout_start( object *objp )
795 {
796  ship *shipp;
797  shipp = &Ships[objp->instance];
798 
799  if ( shipp->flags & SF_DEPART_WARP ) {
800  mprintf(( "Ship is already departing!\n" ));
801  return;
802  }
803 
804  Script_system.SetHookObject("Self", objp);
806  {
807  Script_system.RunCondition(CHA_WARPOUT, 0, NULL, objp);
808  Script_system.RemHookVar("Self");
809  return;
810  }
811 
812  // if we're dying return
813  if ( shipp->flags & SF_DYING ) {
814  return;
815  }
816 
817  //return if disabled
818  if ( shipp->flags & SF_DISABLED ){
819  return;
820  }
821 
822  // if we're HUGE, keep alive - set guardian
823  if (Ship_info[shipp->ship_info_index].flags & SIF_HUGE_SHIP) {
825  }
826 
827  // don't send ship depart packets for player ships
828  if ( (MULTIPLAYER_MASTER) && !(objp->flags & OF_PLAYER_SHIP) ){
829  send_ship_depart_packet( objp );
830  }
831 
832  // don't do departure wormhole if ship flag is set which indicates no effect
833  if ( shipp->flags & SF_NO_DEPARTURE_WARP ) {
834  // DKA 5/25/99 If he's going to warpout, set it.
835  // Next line fixes assert in wing cleanup code when no warp effect.
836  shipp->flags |= SF_DEPART_WARP;
837 
839  return;
840  }
841 
842  shipp->warpout_effect->warpStart();
843 
844  Script_system.RunCondition(CHA_WARPOUT, 0, NULL, objp);
845  Script_system.RemHookVar("Self");
846 }
847 
848 void shipfx_warpout_frame( object *objp, float frametime )
849 {
850  ship *shipp;
851  shipp = &Ships[objp->instance];
852 
853  if ( shipp->flags & SF_DYING ) return;
854 
855  //disabled ships should stay on the battlefield if they were disabled during warpout
856  //phreak 5/22/03
857  if (shipp->flags & SF_DISABLED){
858  shipp->flags &= ~(SF_DEPARTING);
859  return;
860  }
861 
862  shipp->warpout_effect->warpFrame(frametime);
863 }
864 
865 
866 //==================================================
867 // Stuff for keeping track of which ships are in
868 // whose shadows.
869 
870 
874 bool shipfx_point_in_shadow( vec3d *p0, matrix *src_orient, vec3d *src_pos, float radius )
875 {
876  mc_info mc;
877  object *objp;
878  ship_obj *so;
879  int n_lights;
880  int idx;
881 
882  vec3d rp0, rp1;
883 
884  vec3d light_dir;
885 
886  // Move rp0 into world coordinates
887  vm_vec_unrotate(&rp0, p0, src_orient);
888  vm_vec_add2(&rp0, src_pos);
889 
890  // get the # of global lights
891  n_lights = light_get_global_count();
892 
893  for(idx=0; idx<n_lights; idx++){
894  // get the light dir for this light
895  light_get_global_dir(&light_dir, idx);
896 
897  // Find rp1
898  vm_vec_scale_add( &rp1, &rp0, &light_dir, 10000.0f );
899 
900  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
901  objp = &Objects[so->objnum];
902 
903  mc_info_init(&mc);
904  mc.model_instance_num = -1;
905  mc.model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;
906  mc.orient = &objp->orient;
907  mc.pos = &objp->pos;
908  mc.p0 = &rp0;
909  mc.p1 = &rp1;
910  mc.flags = MC_CHECK_MODEL;
911 
912  if ( model_collide(&mc) ){
913  return true;
914  }
915  }
916  }
917 
918  // not in shadow
919  return false;
920 }
921 
922 
926 bool shipfx_in_shadow( object * src_obj )
927 {
928  mc_info mc;
929  object *objp;
930  ship_obj *so;
931  int n_lights;
932  int idx;
933 
934  vec3d rp0, rp1;
935  vec3d light_dir;
936 
937  rp0 = src_obj->pos;
938 
939  // get the # of global lights
940  n_lights = light_get_global_count();
941 
942  for(idx=0; idx<n_lights; idx++){
943  // get the direction for this light
944  light_get_global_dir(&light_dir, idx);
945 
946  // Find rp1
947  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
948  objp = &Objects[so->objnum];
949 
950  if ( src_obj != objp ) {
951  vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
952 
953  mc_info_init(&mc);
954  mc.model_instance_num = -1;
955  mc.model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;
956  mc.orient = &objp->orient;
957  mc.pos = &objp->pos;
958  mc.p0 = &rp0;
959  mc.p1 = &rp1;
960  mc.flags = MC_CHECK_MODEL;
961 
962  if ( model_collide(&mc) ) {
963  return true;
964  }
965  }
966  }
967  }
968 
969  // not in shadow
970  return false;
971 }
972 
973 #define w(p) (*((int *) (p)))
974 
977 bool shipfx_eye_in_shadow( vec3d *eye_pos, object * src_obj, int sun_n )
978 {
979  mc_info mc;
980  object *objp;
981  ship_obj *so;
982 
983  vec3d rp0, rp1;
984  vec3d light_dir;
985 
986  // The mc_info struct only needs to be initialized once for this entire function. This is because
987  // every time the mc variable is reused, every parameter that model_collide reads from is reassigned.
988  // Therefore the stale fields in the rest of the struct do not matter because either a) they are never
989  // read from, or b) they are overwritten by the new collision calculation.
990  mc_info_init(&mc);
991 
992  rp0 = *eye_pos;
993 
994  // get the light dir
995  if(!light_get_global_dir(&light_dir, sun_n)){
996  return false;
997  }
998 
999  // Find rp1
1000  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1001  objp = &Objects[so->objnum];
1002 
1003  if ( src_obj != objp ) {
1004  vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
1005 
1006  mc.model_instance_num = Ships[objp->instance].model_instance_num;
1007  mc.model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;
1008  mc.orient = &objp->orient;
1009  mc.pos = &objp->pos;
1010  mc.p0 = &rp0;
1011  mc.p1 = &rp1;
1012  mc.flags = MC_CHECK_MODEL;
1013 
1014  if (model_collide(&mc)) {
1015  return true;
1016  }
1017  }
1018  }
1019 
1020  // Check all the big hull debris pieces.
1021  debris *db = Debris;
1022 
1023  int i;
1024  for ( i = 0; i < MAX_DEBRIS_PIECES; i++, db++ ) {
1025  if ( !(db->flags & DEBRIS_USED) || !db->is_hull ){
1026  continue;
1027  }
1028 
1029  objp = &Objects[db->objnum];
1030 
1031  vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
1032 
1033  mc.model_instance_num = -1;
1034  mc.model_num = db->model_num; // Fill in the model to check
1035  mc.submodel_num = db->submodel_num;
1037  mc.orient = &objp->orient; // The object's orient
1038  mc.pos = &objp->pos; // The object's position
1039  mc.p0 = &rp0; // Point 1 of ray to check
1040  mc.p1 = &rp1; // Point 2 of ray to check
1041  mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL);
1042 
1043  if (model_collide(&mc)) {
1044  return true;
1045  }
1046  }
1047 
1048  // check cockpit model
1049  if( Viewer_obj != NULL && Viewer_mode != VM_TOPDOWN ) {
1050  if ( Viewer_obj->type == OBJ_SHIP && Viewer_obj->instance >= 0 ) {
1051  ship *shipp = &Ships[Viewer_obj->instance];
1052  ship_info *sip = &Ship_info[shipp->ship_info_index];
1053 
1054  if(sip->cockpit_model_num > 0) {
1055  vm_vec_scale_add( &rp1, &rp0, &light_dir, Viewer_obj->radius*2.0f );
1056  vec3d pos,eye_posi;
1057  matrix eye_ori;
1058  ship_get_eye(&eye_posi, &eye_ori, Viewer_obj, false);
1059  vm_vec_unrotate(&pos, &sip->cockpit_offset, &eye_ori);
1060  vm_vec_add2(&pos, &eye_posi);
1061 
1062  mc.model_instance_num = -1;
1063  mc.model_num = sip->cockpit_model_num;
1064  mc.submodel_num = -1;
1065  mc.orient = &Eye_matrix;
1066  mc.pos = &pos;
1067  mc.p0 = &rp0;
1068  mc.p1 = &rp1;
1069  mc.flags = MC_CHECK_MODEL;
1070 
1071  int mc_result = model_collide(&mc);
1072  mc.pos = NULL;
1073 
1074  if( mc_result ) {
1075  if ( mc.t_poly ) {
1077  int tmap_num = w(mc.t_poly+40);
1078 
1079  Assertion (tmap_num < MAX_MODEL_TEXTURES, "Texture map index (%i) exceeded max", tmap_num);
1080  if (tmap_num >= MAX_MODEL_TEXTURES) { return 0; }
1081  if( !(pm->maps[tmap_num].is_transparent) && strcmp(bm_get_filename(mc.hit_bitmap), "glass.dds") ) {
1082  return true;
1083  }
1084  }
1085 
1086  if ( mc.f_poly ) {
1087  return true;
1088  }
1089 
1090  if ( mc.bsp_leaf ) {
1091  if ( mc.bsp_leaf->tmap_num < 255 ) {
1093  int tmap_num = mc.bsp_leaf->tmap_num;
1094 
1095  Assertion (tmap_num < MAX_MODEL_TEXTURES, "Texture map index (%i) exceeded max", tmap_num);
1096  if (tmap_num >= MAX_MODEL_TEXTURES) { return 0; }
1097  if ( !(pm->maps[tmap_num].is_transparent) && strcmp(bm_get_filename(mc.hit_bitmap), "glass.dds") ) {
1098  return true;
1099  }
1100  } else {
1101  return true;
1102  }
1103  }
1104  }
1105  }
1106 
1107  if ( sip->flags2 & SIF2_SHOW_SHIP_MODEL ) {
1108  vm_vec_scale_add( &rp1, &rp0, &light_dir, Viewer_obj->radius*10.0f );
1109 
1110  mc.model_instance_num = -1;
1111  mc.model_num = sip->model_num;
1112  mc.submodel_num = -1;
1113  mc.orient = &Viewer_obj->orient;
1114  mc.pos = &Viewer_obj->pos;
1115  mc.p0 = &rp0;
1116  mc.p1 = &rp1;
1117  mc.flags = MC_CHECK_MODEL;
1118 
1119  if( model_collide(&mc) ) {
1120  if ( mc.t_poly ) {
1121  polymodel *pm = model_get(sip->model_num);
1122  int tmap_num = w(mc.t_poly+40);
1123 
1124  Assertion (tmap_num < MAX_MODEL_TEXTURES, "Texture map index (%i) exceeded max", tmap_num);
1125  if (tmap_num >= MAX_MODEL_TEXTURES) { return 0; }
1126  if ( !(pm->maps[tmap_num].is_transparent) && strcmp(bm_get_filename(mc.hit_bitmap),"glass.dds") ) {
1127  return true;
1128  }
1129  }
1130 
1131  if ( mc.f_poly ) {
1132  return true;
1133  }
1134 
1135  if ( mc.bsp_leaf ) {
1136  if ( mc.bsp_leaf->tmap_num < 255 ) {
1137  polymodel *pm = model_get(sip->model_num);
1138  int tmap_num = mc.bsp_leaf->tmap_num;
1139 
1140  Assertion (tmap_num < MAX_MODEL_TEXTURES, "Texture map index (%i) exceeded max", tmap_num);
1141  if (tmap_num >= MAX_MODEL_TEXTURES) { return 0; }
1142  if ( !(pm->maps[tmap_num].is_transparent) && strcmp(bm_get_filename(mc.hit_bitmap), "glass.dds") ) {
1143  return true;
1144  }
1145  } else {
1146  return true;
1147  }
1148  }
1149  }
1150  }
1151  }
1152  }
1153 
1154  // check asteroids
1155  asteroid *ast = Asteroids;
1156 
1158  {
1159  return false;
1160  }
1161 
1162  for (i = 0 ; i < MAX_ASTEROIDS; i++, ast++)
1163  {
1164  if (!(ast->flags & AF_USED))
1165  {
1166  continue;
1167  }
1168 
1169  objp = &Objects[ast->objnum];
1170 
1171  vm_vec_scale_add( &rp1, &rp0, &light_dir, objp->radius*10.0f );
1172 
1173  mc.model_instance_num = -1;
1174  mc.model_num = Asteroid_info[ast->asteroid_type].model_num[ast->asteroid_subtype]; // Fill in the model to check
1175  mc.submodel_num = -1;
1177  mc.orient = &objp->orient; // The object's orient
1178  mc.pos = &objp->pos; // The object's position
1179  mc.p0 = &rp0; // Point 1 of ray to check
1180  mc.p1 = &rp1; // Point 2 of ray to check
1181  mc.flags = MC_CHECK_MODEL;
1182 
1183  if (model_collide(&mc)) {
1184  return true;
1185  }
1186  }
1187 
1188  // not in shadow
1189  return false;
1190 }
1191 
1192 //=====================================================================================
1193 // STUFF FOR DOING SHIP GUN FLASHES
1194 //=====================================================================================
1195 
1196 #define MAX_FLASHES 128 // How many flashes total
1197 #define FLASH_LIFE_PRIMARY 0.25f // How long flash lives
1198 #define FLASH_LIFE_SECONDARY 0.50f // How long flash lives
1199 
1200 
1201 typedef struct ship_flash {
1202  int objnum; // object number of parent ship
1203  int obj_signature; // signature of that object
1204  int light_num; // which light in the model this uses
1205  float life; // how long this should be around
1206  float max_life; // how long this has been around.
1207 } ship_flash;
1208 
1212 
1217 {
1218  int i;
1219 
1220  for (i=0; i<MAX_FLASHES; i++ ) {
1221  Ship_flash[i].objnum = -1; // mark as unused
1222  }
1223  Ship_flash_highest = -1;
1224  Ship_flash_inited = 1;
1225 }
1226 
1227 
1231 void shipfx_flash_create(object *objp, int model_num, vec3d *gun_pos, vec3d *gun_dir, int is_primary, int weapon_info_index)
1232 {
1233  int i;
1234  int objnum = OBJ_INDEX(objp);
1235 
1236  Assert(Ship_flash_inited);
1237 
1238  polymodel *pm = model_get(model_num);
1239  int closest_light = -1;
1240  float d, closest_dist = 0.0f;
1241 
1242  // ALWAYS do this - since this is called once per firing
1243  // if this is a cannon type weapon, create a muzzle flash
1244  // HACK - let the flak guns do this on their own since they fire so quickly
1245  if ((Weapon_info[weapon_info_index].muzzle_flash >= 0) && !(Weapon_info[weapon_info_index].wi_flags & WIF_FLAK)) {
1246  vec3d real_dir;
1247  vm_vec_rotate(&real_dir, gun_dir,&objp->orient);
1248  mflash_create(gun_pos, &real_dir, &objp->phys_info, Weapon_info[weapon_info_index].muzzle_flash, objp);
1249  }
1250 
1251  if ( pm->num_lights < 1 ) return;
1252 
1253  for (i=0; i<pm->num_lights; i++ ) {
1254  d = vm_vec_dist( &pm->lights[i].pos, gun_pos );
1255 
1256  if ( pm->lights[i].type == BSP_LIGHT_TYPE_WEAPON ) {
1257  if ( (closest_light==-1) || (d<closest_dist) ) {
1258  closest_light = i;
1259  closest_dist = d;
1260  }
1261  }
1262  }
1263 
1264  if ( closest_light == -1 ) return;
1265 
1266  int first_slot = -1;
1267 
1268  for (i=0; i<=Ship_flash_highest; i++ ) {
1269  if ( (first_slot==-1) && (Ship_flash[i].objnum < 0) ) {
1270  first_slot = i;
1271  }
1272 
1273  if ( (Ship_flash[i].objnum == objnum) && (Ship_flash[i].obj_signature==objp->signature) ) {
1274  if ( Ship_flash[i].light_num == closest_light ) {
1275  // This is already flashing!
1276  Ship_flash[i].life = 0.0f;
1277  if ( is_primary ) {
1278  Ship_flash[i].max_life = FLASH_LIFE_PRIMARY;
1279  } else {
1280  Ship_flash[i].max_life = FLASH_LIFE_SECONDARY;
1281  }
1282  return;
1283  }
1284  }
1285  }
1286 
1287  if ( first_slot == -1 ) {
1288  if ( Ship_flash_highest < MAX_FLASHES-1 ) {
1289  Ship_flash_highest++;
1290  first_slot = Ship_flash_highest;
1291  } else {
1292  return; // out of flash slots
1293  }
1294  }
1295 
1296  Assert( Ship_flash[first_slot].objnum == -1 );
1297 
1298  Ship_flash[first_slot].objnum = objnum;
1299  Ship_flash[first_slot].obj_signature = objp->signature;
1300  Ship_flash[first_slot].life = 0.0f; // Start it up
1301  if ( is_primary ) {
1302  Ship_flash[first_slot].max_life = FLASH_LIFE_PRIMARY;
1303  } else {
1304  Ship_flash[first_slot].max_life = FLASH_LIFE_SECONDARY;
1305  }
1306  Ship_flash[first_slot].light_num = closest_light;
1307 }
1308 
1314 void shipfx_flash_light_model(object *objp, int model_num)
1315 {
1316  int i, objnum = OBJ_INDEX(objp);
1317  polymodel *pm = model_get(model_num);
1318 
1319  for (i=0; i<=Ship_flash_highest; i++ ) {
1320  if ( (Ship_flash[i].objnum == objnum) && (Ship_flash[i].obj_signature==objp->signature) ) {
1321  float v = (Ship_flash[i].max_life - Ship_flash[i].life)/Ship_flash[i].max_life;
1322 
1323  pm->lights[Ship_flash[i].light_num].value += v / 255.0f;
1324  }
1325  }
1326 }
1327 
1331 void shipfx_flash_do_frame(float frametime)
1332 {
1333  ship_flash *sf;
1334  int kill_it = 0;
1335  int i;
1336 
1337  for (i=0, sf = &Ship_flash[0]; i<=Ship_flash_highest; i++, sf++ ) {
1338  if ( sf->objnum > -1 ) {
1339  if ( Objects[sf->objnum].signature != sf->obj_signature ) {
1340  kill_it = 1;
1341  }
1342  sf->life += frametime;
1343  if ( sf->life >= sf->max_life ) kill_it = 1;
1344 
1345  if (kill_it) {
1346  sf->objnum = -1;
1347  if ( i == Ship_flash_highest ) {
1348  while( (Ship_flash_highest>0) && (Ship_flash[Ship_flash_highest].objnum == -1) ) {
1349  Ship_flash_highest--;
1350  }
1351  }
1352  }
1353  }
1354  }
1355 
1356 }
1357 
1358 float Particle_width = 1.2f;
1359 DCF(particle_width, "Sets multiplier for angular width of the particle spew ( 0 - 5)")
1360 {
1361  float value;
1362 
1363  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1364  dc_printf("Particle_width : %f\n", Particle_width);
1365  return;
1366  }
1367 
1369 
1370  CLAMP(value, 0.0, 5.0);
1371  Particle_width = value;
1372 
1373  dc_printf("Particle_width set to %f\n", Particle_width);
1374 }
1375 
1376 float Particle_number = 1.2f;
1377 DCF(particle_num, "Sets multiplier for the number of particles created")
1378 {
1379 
1380  float value;
1381 
1382  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1383  dc_printf("Particle_number : %f\n", Particle_number);
1384  return;
1385  }
1386 
1387  dc_stuff_float(&value);
1388 
1389  CLAMP(value, 0.0, 5.0);
1390  Particle_number = value;
1391 
1392  dc_printf("Particle_number set to %f\n", Particle_number);
1393 }
1394 
1395 float Particle_life = 1.2f;
1396 DCF(particle_life, "Multiplier for the lifetime of particles created")
1397 {
1398  float value;
1399 
1400  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1401  dc_printf("Particle_life : %f\n", Particle_life);
1402  return;
1403  }
1404 
1405  dc_stuff_float(&value);
1406 
1407  CLAMP(value, 0.0, 5.0);
1408  Particle_life = value;
1409 
1410  dc_printf("Particle_life set to %f\n", Particle_life);
1411 }
1412 
1413 // Make sparks fly off of ship n.
1414 // sn = spark number to spark, corrosponding to element in
1415 // ship->hitpos array. If this isn't -1, it is a just
1416 // got hit by weapon spark, otherwise pick one randomally.
1417 void shipfx_emit_spark( int n, int sn )
1418 {
1419  int create_spark = 1;
1420  object * obj;
1421  vec3d outpnt;
1422  ship *shipp = &Ships[n];
1423  float ship_radius, spark_scale_factor;
1424 
1425  ship_info *sip = &Ship_info[shipp->ship_info_index];
1426  if(sn > -1 && sip->impact_spew.n_high <= 0)
1427  return;
1428 
1429  if(sn < 0 && sip->damage_spew.n_high <= 0)
1430  return;
1431 
1432  if ( shipp->num_hits <= 0 )
1433  return;
1434 
1435  // get radius of ship
1436  ship_radius = model_get_radius(sip->model_num);
1437 
1438  // get spark_scale_factor -- how much to increase ship sparks, based on radius
1439  if (ship_radius > 40) {
1440  spark_scale_factor = 1.0f;
1441  } else if (ship_radius > 20) {
1442  spark_scale_factor = (ship_radius - 20.0f) / 20.0f;
1443  } else {
1444  spark_scale_factor = 0.0f;
1445  }
1446 
1447  float spark_time_scale = 1.0f + spark_scale_factor * (Particle_life - 1.0f);
1448  float spark_width_scale = 1.0f + spark_scale_factor * (Particle_width - 1.0f);
1449  float spark_num_scale = 1.0f + spark_scale_factor * (Particle_number - 1.0f);
1450 
1451  obj = &Objects[shipp->objnum];
1452 
1453  float hull_percent = get_hull_pct(obj);
1454  if (hull_percent < 0.001) {
1455  hull_percent = 0.001f;
1456  }
1457  float fraction = 0.1f * obj->radius / hull_percent;
1458  if (fraction > 1.0f) {
1459  fraction = 1.0f;
1460  }
1461 
1462  int spark_num;
1463  if ( sn == -1 ) {
1464  spark_num = myrand() % shipp->num_hits;
1465  } else {
1466  spark_num = sn;
1467  }
1468 
1469  // don't display sparks that have expired
1470  if ( timestamp_elapsed(shipp->sparks[spark_num].end_time) ) {
1471  return;
1472  }
1473 
1474  // get spark position
1475  if (shipp->sparks[spark_num].submodel_num != -1) {
1476  model_instance_find_world_point(&outpnt, &shipp->sparks[spark_num].pos, shipp->model_instance_num, shipp->sparks[spark_num].submodel_num, &obj->orient, &obj->pos);
1477  } else {
1478  // rotate sparks correctly with current ship orient
1479  vm_vec_unrotate(&outpnt, &shipp->sparks[spark_num].pos, &obj->orient);
1480  vm_vec_add2(&outpnt,&obj->pos);
1481  }
1482 
1483  //WMC - I have two options here:
1484  //(A) Break backwards compatibility and nobody will ever notice (but for this comment)
1485  //(B) implement some hackish workaround with the new warpeffect system just for this
1486  // utterly minor and trivial optimization/graphical thing
1487  //(C) Contact me if (A) is REALLY necessary
1488  //
1489  // phreak: Mantis 1676 - Re-enable warpout clipping.
1490 
1491  if ((shipp->flags & (SF_ARRIVING|SF_DEPART_WARP)) && (shipp->warpout_effect))
1492  {
1493  vec3d warp_pnt, tmp;
1494  matrix warp_orient;
1495 
1496  shipp->warpout_effect->getWarpPosition(&warp_pnt);
1497  shipp->warpout_effect->getWarpOrientation(&warp_orient);
1498 
1499  vm_vec_sub( &tmp, &outpnt, &warp_pnt );
1500 
1501  if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) < 0.0f )
1502  {
1503  if (shipp->flags & SF_ARRIVING)// if in front of warp plane, don't create.
1504  create_spark = 0;
1505  } else {
1506  if (shipp->flags & SF_DEPART_WARP)
1507  create_spark = 0;
1508  }
1509  }
1510 
1511  if ( create_spark ) {
1512 
1513  particle_emitter pe;
1514  particle_effect pef;
1515 
1516  pe.pos = outpnt; // Where the particles emit from
1517 
1518  if ( shipp->flags & (SF_ARRIVING|SF_DEPART_WARP) ) {
1519  // No velocity if going through warp.
1520  pe.vel = vmd_zero_vector;
1521  } else {
1522  // Initial velocity of all the particles.
1523  // 0.0f = 0% of parent's.
1524  // 1.0f = 100% of parent's.
1525  vm_vec_copy_scale( &pe.vel, &obj->phys_info.vel, 0.7f );
1526  }
1527 
1528  // TODO: add velocity from rotation if submodel is rotating
1529  // v_rot = w x r
1530 
1531  // r = outpnt - model_find_world_point(0)
1532 
1533  // w = model_find_world_dir(
1534  // model_find_world_dir(&out_dir, &in_dir, model_num, submodel_num, &objorient, &objpos);
1535 
1536  vec3d tmp_norm, tmp_vel;
1537  vm_vec_sub( &tmp_norm, &outpnt, &obj->pos );
1538  vm_vec_normalize_safe(&tmp_norm);
1539 
1540  tmp_vel = obj->phys_info.vel;
1541  if ( vm_vec_normalize_safe(&tmp_vel) > 1.0f ) {
1542  vm_vec_scale_add2(&tmp_norm,&tmp_vel, -2.0f);
1543  vm_vec_normalize_safe(&tmp_norm);
1544  }
1545 
1546  pe.normal = tmp_norm; // What normal the particle emit around
1547 
1548  if (sn > -1)
1549  pef = sip->impact_spew;
1550  else
1551  pef = sip->damage_spew;
1552 
1553  pe.min_rad = pef.min_rad;
1554  pe.max_rad = pef.max_rad;
1555  pe.min_vel = pef.min_vel; // How fast the slowest particle can move
1556  pe.max_vel = pef.max_vel; // How fast the fastest particle can move
1557 
1558  // first time through - set up end time and make heavier initially
1559  if ( sn > -1 ) {
1560  // Sparks for first time at this spot
1561  if (sip->flags & SIF_FIGHTER) {
1562  if (hull_percent > 0.6f) {
1563  // sparks only once when hull > 60%
1564  float spark_duration = (float)pow(2.0f, -5.0f*(hull_percent-1.3f)) * (1.0f + 0.6f*(frand()-0.5f)); // +- 30%
1565  shipp->sparks[spark_num].end_time = timestamp( (int) (1000.0f * spark_duration) );
1566  } else {
1567  // spark never ends when hull < 60% (~277 hr)
1568  shipp->sparks[spark_num].end_time = timestamp( 100000000 );
1569  }
1570  }
1571  pe.num_low = pef.n_low; // Lowest number of particles to create (hardware)
1572  pe.num_high = pef.n_high;
1573  pe.normal_variance = pef.variance; // How close they stick to that normal 0=good, 1=360 degree
1574  pe.min_life = pef.min_life; // How long the particles live
1575  pe.max_life = pef.max_life; // How long the particles live
1576 
1577  particle_emit( &pe, PARTICLE_FIRE, 0 );
1578  } else {
1579  if (pef.n_high > 1) {
1580  pe.num_low = pef.n_low;
1581  pe.num_high = pef.n_high;
1582  } else {
1583  pe.num_low = (int) (20.0f * spark_num_scale);
1584  pe.num_high = (int) (50.0f * spark_num_scale);
1585  }
1586 
1587  if (pef.variance > 0.0f) {
1588  pe.normal_variance = pef.variance;
1589  } else {
1590  pe.normal_variance = 0.2f * spark_width_scale;
1591  }
1592 
1593  if (pef.max_life > 0.0f) {
1594  pe.min_life = pef.min_life;
1595  pe.max_life = pef.max_life;
1596  } else {
1597  pe.min_life = 0.7f * spark_time_scale;
1598  pe.max_life = 1.5f * spark_time_scale;
1599  }
1600 
1601  particle_emit( &pe, PARTICLE_SMOKE, 0 );
1602  }
1603  }
1604 
1605  // Select time to do next spark
1606  shipp->next_hit_spark = timestamp_rand(50,100);
1607 }
1608 
1609 
1610 
1611 //=====================================================================================
1612 // STUFF FOR DOING LARGE SHIP EXPLOSIONS
1613 //=====================================================================================
1614 
1617 
1618 DCF_BOOL(bs_exp_fire_low, Bs_exp_fire_low)
1619 DCF(bs_exp_fire_time_mult, "Sets multiplier time between fireball in big ship explosion")
1620 {
1621  float value;
1622 
1623  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1624  dc_printf("Bs_exp_fire_time_mult : %f\n", Bs_exp_fire_time_mult);
1625  return;
1626  }
1627 
1628  dc_stuff_float(&value);
1629 
1630  CLAMP(value, 0.1f, 5.0f);
1631  Bs_exp_fire_time_mult = value;
1632  dc_printf("Bs_exp_fire_time_mult set to %f\n", Bs_exp_fire_time_mult);
1633 }
1634 
1635 
1636 #define DEBRIS_NONE 0
1637 #define DEBRIS_DRAW 1
1638 #define DEBRIS_FREE 2
1639 
1640 typedef struct clip_ship {
1641  object* parent_obj;
1642  float length_left; // uncomsumed length
1645  vec3d local_pivot; // world center of mass position of half ship
1646  vec3d model_center_disp_to_orig_center; // displacement from half ship center to original model center
1647  vec3d clip_plane_norm; // clip plane normal (local [0,0,1] or [0,0,-1])
1648  float cur_clip_plane_pt; // displacement from half ship clip plane to original model center
1652 } clip_ship;
1653 
1654 typedef struct split_ship {
1655  int used; // 0 if not used, 1 if used
1661 } split_ship;
1662 
1663 
1664 static SCP_vector<split_ship> Split_ships;
1665 
1666 static int get_split_ship()
1667 {
1668  int i;
1669  split_ship addition;
1670 
1671  // check for an existing free slot
1672  int max_size = (int)Split_ships.size();
1673  for (i = 0; i < max_size; i++) {
1674  if (!Split_ships[i].used)
1675  return i;
1676  }
1677 
1678  // nope. we'll have to add a new one
1679  memset( &addition, 0, sizeof(split_ship) );
1680 
1681  Split_ships.push_back(addition);
1682 
1683  return (Split_ships.size() - 1);
1684 }
1685 
1686 static void maybe_fireball_wipe(clip_ship* half_ship, int* sound_handle);
1687 static void split_ship_init( ship* shipp, split_ship* split_shipp )
1688 {
1689  object* parent_ship_obj = &Objects[shipp->objnum];
1690  matrix* orient = &parent_ship_obj->orient;
1691  for (int ii=0; ii<NUM_SUB_EXPL_HANDLES; ++ii) {
1692  split_shipp->sound_handle[ii] = shipp->sub_expl_sound_handle[ii];
1693  }
1694 
1695  // play 3d sound for shockwave explosion
1696  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 );
1697 
1698  // initialize both ships
1699  split_shipp->front_ship.parent_obj = parent_ship_obj;
1700  split_shipp->back_ship.parent_obj = parent_ship_obj;
1701  split_shipp->explosion_flash_timestamp = timestamp((int)(0.00075f*parent_ship_obj->radius));
1702  split_shipp->explosion_flash_started = 0;
1703  split_shipp->front_ship.orient = *orient;
1704  split_shipp->back_ship.orient = *orient;
1705  split_shipp->front_ship.next_fireball = timestamp_rand(0, 100);
1706  split_shipp->back_ship.next_fireball = timestamp_rand(0, 100);
1707 
1708  split_shipp->front_ship.clip_plane_norm = vmd_z_vector;
1709  vm_vec_copy_scale(&split_shipp->back_ship.clip_plane_norm, &vmd_z_vector, -1.0f);
1710 
1711  // find the point at which the ship splits (relative to its pivot)
1712  polymodel* pm = model_get(Ship_info[shipp->ship_info_index].model_num);
1713  float init_clip_plane_dist;
1714  if (pm->num_split_plane > 0) {
1715  int index = rand()%pm->num_split_plane;
1716  init_clip_plane_dist = pm->split_plane[index];
1717  } else {
1718  init_clip_plane_dist = 0.5f * (0.5f - frand())*pm->core_radius;
1719  }
1720 
1721  split_shipp->back_ship.cur_clip_plane_pt = init_clip_plane_dist;
1722  split_shipp->front_ship.cur_clip_plane_pt = init_clip_plane_dist;
1723 
1724  float dist;
1725  dist = (split_shipp->front_ship.cur_clip_plane_pt+pm->maxs.xyz.z)/2.0f;
1726  vm_vec_copy_scale(&split_shipp->front_ship.local_pivot, &orient->vec.fvec, dist);
1727  vm_vec_make(&split_shipp->front_ship.model_center_disp_to_orig_center, 0.0f, 0.0f, -dist);
1728  dist = (split_shipp->back_ship.cur_clip_plane_pt +pm->mins.xyz.z)/2.0f;
1729  vm_vec_copy_scale(&split_shipp->back_ship.local_pivot, &orient->vec.fvec, dist);
1730  vm_vec_make(&split_shipp->back_ship.model_center_disp_to_orig_center, 0.0f, 0.0f, -dist);
1731  vm_vec_add2(&split_shipp->front_ship.local_pivot, &parent_ship_obj->pos );
1732  vm_vec_add2(&split_shipp->back_ship.local_pivot, &parent_ship_obj->pos );
1733 
1734  // find which debris pieces are in the front and back split ships
1735  for (int i=0; i<pm->num_debris_objects; i++ ) {
1736  vec3d temp_pos = ZERO_VECTOR;
1737  vec3d tmp = ZERO_VECTOR;
1738  vec3d tmp1 = pm->submodel[pm->debris_objects[i]].offset;
1739  // tmp is world position, temp_pos is world_pivot, tmp1 is offset from world_pivot (in ship local coord)
1740  model_find_world_point(&tmp, &tmp1, pm->id, -1, &vmd_identity_matrix, &temp_pos );
1741  if (tmp.xyz.z > init_clip_plane_dist) {
1742  split_shipp->front_ship.draw_debris[i] = DEBRIS_DRAW;
1743  split_shipp->back_ship.draw_debris[i] = DEBRIS_NONE;
1744  } else {
1745  split_shipp->front_ship.draw_debris[i] = DEBRIS_NONE;
1746  split_shipp->back_ship.draw_debris[i] = DEBRIS_DRAW;
1747  }
1748  }
1749 
1750  // set up physics
1751  physics_init( &split_shipp->front_ship.phys_info );
1752  physics_init( &split_shipp->back_ship.phys_info );
1754  split_shipp->back_ship.phys_info.flags |= (PF_ACCELERATES | PF_DEAD_DAMP);
1755  split_shipp->front_ship.phys_info.side_slip_time_const = 10000.0f;
1756  split_shipp->back_ship.phys_info.side_slip_time_const = 10000.0f;
1757  split_shipp->front_ship.phys_info.rotdamp = 10000.0f;
1758  split_shipp->back_ship.phys_info.rotdamp = 10000.0f;
1759 
1760  // set up explosion vel and relative velocities (assuming mass depends on length)
1761  float front_length = pm->maxs.xyz.z - split_shipp->front_ship.cur_clip_plane_pt;
1762  float back_length = split_shipp->back_ship.cur_clip_plane_pt - pm->mins.xyz.z;
1763  float ship_length = front_length + back_length;
1764  split_shipp->front_ship.length_left = front_length;
1765  split_shipp->back_ship.length_left = back_length;
1766 
1767  float expl_length_scale = (ship_length - 200.0f) / 2000.0f;
1768  // s_r_f effects speed of "wipe" and rotvel
1769  float speed_reduction_factor = (1.0f + 0.001f*parent_ship_obj->radius);
1770  float explosion_time = (3.0f + expl_length_scale + (frand()-0.5f)) * speed_reduction_factor;
1771  float long_length = MAX(front_length, back_length);
1772  float expl_vel = long_length / explosion_time;
1773  split_shipp->front_ship.explosion_vel = expl_vel;
1774  split_shipp->back_ship.explosion_vel = -expl_vel;
1775 
1776  float rel_vel = (0.6f + 0.2f*frand()) * expl_vel * speed_reduction_factor;
1777  float front_vel = rel_vel * back_length / ship_length;
1778  float back_vel = -rel_vel * front_length / ship_length;
1779 
1780  // set up rotational vel
1781  vec3d rotvel;
1782  vm_vec_rand_vec_quick(&rotvel);
1783  rotvel.xyz.z = 0.0f;
1784  vm_vec_normalize(&rotvel);
1785  vm_vec_scale(&rotvel, 0.15f / speed_reduction_factor);
1786  split_shipp->front_ship.phys_info.rotvel = rotvel;
1787  vm_vec_copy_scale(&split_shipp->back_ship.phys_info.rotvel, &rotvel, -(front_length*front_length)/(back_length*back_length));
1788  split_shipp->front_ship.phys_info.rotvel.xyz.z = parent_ship_obj->phys_info.rotvel.xyz.z;
1789  split_shipp->back_ship.phys_info.rotvel.xyz.z = parent_ship_obj->phys_info.rotvel.xyz.z;
1790 
1791 
1792  // modify vel of each split ship based on rotvel of parent ship obj
1793  vec3d temp_rotvel = parent_ship_obj->phys_info.rotvel;
1794  temp_rotvel.xyz.z = 0.0f;
1795  vec3d vel_from_rotvel;
1796  vm_vec_cross(&vel_from_rotvel, &temp_rotvel, &split_shipp->front_ship.local_pivot);
1797  vm_vec_cross(&vel_from_rotvel, &temp_rotvel, &split_shipp->back_ship.local_pivot);
1798 
1799  // set up velocity and make initial fireballs and particles
1800  split_shipp->front_ship.phys_info.vel = parent_ship_obj->phys_info.vel;
1801  split_shipp->back_ship.phys_info.vel = parent_ship_obj->phys_info.vel;
1802  maybe_fireball_wipe(&split_shipp->front_ship, (int*)&split_shipp->sound_handle);
1803  maybe_fireball_wipe(&split_shipp->back_ship, (int*)&split_shipp->sound_handle);
1804  vm_vec_scale_add2(&split_shipp->front_ship.phys_info.vel, &orient->vec.fvec, front_vel);
1805  vm_vec_scale_add2(&split_shipp->back_ship.phys_info.vel, &orient->vec.fvec, back_vel);
1806 
1807  // HANDLE LIVE DEBRIS - blow off if not already gone
1809 }
1810 
1811 
1812 static void half_ship_render_ship_and_debris(clip_ship* half_ship,ship *shipp)
1813 {
1814  polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
1815 
1816  // get rotated clip plane normal and world coord of original ship center
1817  vec3d orig_ship_world_center, clip_plane_norm, model_clip_plane_pt, debris_clip_plane_pt;
1818  vm_vec_unrotate(&clip_plane_norm, &half_ship->clip_plane_norm, &half_ship->orient);
1819  vm_vec_unrotate(&orig_ship_world_center, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
1820  vm_vec_add2(&orig_ship_world_center, &half_ship->local_pivot);
1821 
1822  // get debris clip plane pt and draw debris
1823  vm_vec_unrotate(&debris_clip_plane_pt, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
1824  vm_vec_add2(&debris_clip_plane_pt, &half_ship->local_pivot);
1825  g3_start_user_clip_plane( &debris_clip_plane_pt, &clip_plane_norm);
1826 
1827  // set up render flags
1828  uint render_flags = MR_DEPRECATED_NORMAL;
1829 
1830  for (int i=0; i<pm->num_debris_objects; i++ ) {
1831  // draw DEBRIS_FREE in test only
1832  if (half_ship->draw_debris[i] == DEBRIS_DRAW) {
1833  vec3d temp_pos = orig_ship_world_center;
1834  vec3d tmp = ZERO_VECTOR;
1835  vec3d tmp1 = pm->submodel[pm->debris_objects[i]].offset;
1836 
1837  // determine if explosion front has past debris piece
1838  // 67 ~ dist expl moves in 2 frames -- maybe fraction works better
1839  int is_live_debris = pm->submodel[pm->debris_objects[i]].is_live_debris;
1840  int create_debris = 0;
1841  // front ship
1842  if (half_ship->explosion_vel > 0) {
1843  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) {
1844  create_debris = 1;
1845  }
1846  // back ship
1847  } else {
1848  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) {
1849  create_debris = 1;
1850  }
1851  }
1852 
1853  // Draw debris, but not live debris
1854  if ( !is_live_debris ) {
1855  model_find_world_point(&tmp, &tmp1, pm->id, -1, &half_ship->orient, &temp_pos);
1856  submodel_render_DEPRECATED(pm->id, pm->debris_objects[i], &half_ship->orient, &tmp, render_flags, -1, shipp->ship_replacement_textures);
1857  }
1858 
1859  // make free piece of debris
1860  if ( create_debris ) {
1861  half_ship->draw_debris[i] = DEBRIS_FREE; // mark debris to not render with model
1862  vec3d center_to_debris, debris_vel, radial_vel;
1863  // check if last debris piece, ie, debris_count == 0
1864  int debris_count = 0;
1865  for (int j=0; j<pm->num_debris_objects; j++ ) {
1866  if (half_ship->draw_debris[j] == DEBRIS_DRAW) {
1867  debris_count++;
1868  }
1869  }
1870  // do debris create here, but not for live debris
1871  // debris vel (1) split ship vel (2) split ship rotvel (3) random
1872  if ( !is_live_debris ) {
1873  object* debris_obj;
1874  debris_obj = debris_create(half_ship->parent_obj, pm->id, pm->debris_objects[i], &tmp, &half_ship->local_pivot, 1, 1.0f);
1875  // AL: make sure debris_obj isn't NULL!
1876  if ( debris_obj ) {
1877  vm_vec_scale(&debris_obj->phys_info.rotvel, 4.0f);
1878  debris_obj->orient = half_ship->orient;
1879 
1880  vm_vec_sub(&center_to_debris, &tmp, &half_ship->local_pivot);
1881  vm_vec_cross(&debris_vel, &center_to_debris, &half_ship->phys_info.rotvel);
1882  vm_vec_add2(&debris_vel, &half_ship->phys_info.vel);
1883  vm_vec_copy_normalize(&radial_vel, &center_to_debris);
1884  float radial_mag = 10.0f + 30.0f*frand();
1885  vm_vec_scale_add2(&debris_vel, &radial_vel, radial_mag);
1886  debris_obj->phys_info.vel = debris_vel;
1887  shipfx_debris_limit_speed(&Debris[debris_obj->instance], shipp);
1888  }
1889  }
1890  }
1891  }
1892  }
1893 
1894  // get model clip plane pt and draw model
1895  vec3d temp;
1896  vm_vec_make(&temp, 0.0f, 0.0f, half_ship->cur_clip_plane_pt);
1897  vm_vec_unrotate(&model_clip_plane_pt, &temp, &half_ship->orient);
1898  vm_vec_add2(&model_clip_plane_pt, &orig_ship_world_center);
1899  g3_start_user_clip_plane( &model_clip_plane_pt, &clip_plane_norm );
1900  model_render_DEPRECATED(pm->id, &half_ship->orient, &orig_ship_world_center, render_flags, -1, -1, shipp->ship_replacement_textures);
1901 }
1902 
1904 {
1905  polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
1906 
1907  // get rotated clip plane normal and world coord of original ship center
1908  vec3d orig_ship_world_center, clip_plane_norm, model_clip_plane_pt, debris_clip_plane_pt;
1909  vm_vec_unrotate(&clip_plane_norm, &half_ship->clip_plane_norm, &half_ship->orient);
1910  vm_vec_unrotate(&orig_ship_world_center, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
1911  vm_vec_add2(&orig_ship_world_center, &half_ship->local_pivot);
1912 
1913  // get debris clip plane pt and draw debris
1914  vm_vec_unrotate(&debris_clip_plane_pt, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
1915  vm_vec_add2(&debris_clip_plane_pt, &half_ship->local_pivot);
1916 
1917  // set up render flags
1918  uint render_flags = MR_NORMAL;
1919 
1920  if ( Rendering_to_shadow_map ) {
1921  render_flags |= MR_NO_TEXTURING | MR_NO_LIGHTING;
1922  }
1923 
1924  for (int i = 0; i < pm->num_debris_objects; i++ ) {
1925  // draw DEBRIS_FREE in test only
1926  if (half_ship->draw_debris[i] == DEBRIS_DRAW) {
1927  vec3d temp_pos = orig_ship_world_center;
1928  vec3d tmp = ZERO_VECTOR;
1929  vec3d tmp1 = pm->submodel[pm->debris_objects[i]].offset;
1930 
1931  // determine if explosion front has past debris piece
1932  // 67 ~ dist expl moves in 2 frames -- maybe fraction works better
1933  int is_live_debris = pm->submodel[pm->debris_objects[i]].is_live_debris;
1934  int create_debris = 0;
1935  // front ship
1936  if (half_ship->explosion_vel > 0) {
1937  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) {
1938  create_debris = 1;
1939  }
1940  // back ship
1941  } else {
1942  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) {
1943  create_debris = 1;
1944  }
1945  }
1946 
1947  // Draw debris, but not live debris
1948  if ( !is_live_debris ) {
1949  model_find_world_point(&tmp, &tmp1, pm->id, -1, &half_ship->orient, &temp_pos);
1950  model_render_params render_info;
1951 
1952  render_info.set_clip_plane(debris_clip_plane_pt, clip_plane_norm);
1954  render_info.set_flags(render_flags);
1955 
1956  submodel_render_queue(&render_info, scene, pm->id, pm->debris_objects[i], &half_ship->orient, &tmp);
1957  }
1958 
1959  // make free piece of debris
1960  if ( create_debris ) {
1961  half_ship->draw_debris[i] = DEBRIS_FREE; // mark debris to not render with model
1962  vec3d center_to_debris, debris_vel, radial_vel;
1963  // check if last debris piece, ie, debris_count == 0
1964  int debris_count = 0;
1965  for (int j=0; j<pm->num_debris_objects; j++ ) {
1966  if (half_ship->draw_debris[j] == DEBRIS_DRAW) {
1967  debris_count++;
1968  }
1969  }
1970  // do debris create here, but not for live debris
1971  // debris vel (1) split ship vel (2) split ship rotvel (3) random
1972  if ( !is_live_debris ) {
1973  object* debris_obj;
1974  debris_obj = debris_create(half_ship->parent_obj, pm->id, pm->debris_objects[i], &tmp, &half_ship->local_pivot, 1, 1.0f);
1975  // AL: make sure debris_obj isn't NULL!
1976  if ( debris_obj ) {
1977  vm_vec_scale(&debris_obj->phys_info.rotvel, 4.0f);
1978  debris_obj->orient = half_ship->orient;
1979 
1980  vm_vec_sub(&center_to_debris, &tmp, &half_ship->local_pivot);
1981  vm_vec_cross(&debris_vel, &center_to_debris, &half_ship->phys_info.rotvel);
1982  vm_vec_add2(&debris_vel, &half_ship->phys_info.vel);
1983  vm_vec_copy_normalize(&radial_vel, &center_to_debris);
1984  float radial_mag = 10.0f + 30.0f*frand();
1985  vm_vec_scale_add2(&debris_vel, &radial_vel, radial_mag);
1986  debris_obj->phys_info.vel = debris_vel;
1987  shipfx_debris_limit_speed(&Debris[debris_obj->instance], shipp);
1988  }
1989  }
1990  }
1991  }
1992  }
1993 
1994  // get model clip plane pt and draw model
1995  vec3d temp;
1996  vm_vec_make(&temp, 0.0f, 0.0f, half_ship->cur_clip_plane_pt);
1997  vm_vec_unrotate(&model_clip_plane_pt, &temp, &half_ship->orient);
1998  vm_vec_add2(&model_clip_plane_pt, &orig_ship_world_center);
1999 
2000  model_render_params render_info;
2001 
2002  render_info.set_flags(render_flags);
2003  render_info.set_clip_plane(model_clip_plane_pt, clip_plane_norm);
2005 
2006  model_render_queue(&render_info, scene, pm->id, &half_ship->orient, &orig_ship_world_center);
2007 }
2008 
2010 {
2011  Split_ships.clear();
2012 
2013  if(Ship_cannon_bitmap != -1){
2016  }
2017 }
2018 
2020 {
2021  int i;
2022 
2023  i = get_split_ship();
2024 
2025  Split_ships[i].used = 1;
2026  shipp->large_ship_blowup_index = i;
2027 
2028  split_ship_init(shipp, &Split_ships[i] );
2029 }
2030 
2032 {
2033  if(db == NULL || shipp == NULL)
2034  return;
2035 
2036  object *ship_objp = &Objects[shipp->objnum];
2037  physics_info *pi = &Objects[db->objnum].phys_info;
2038  ship_info *sip = &Ship_info[shipp->ship_info_index];
2039 
2040  float curspeed = vm_vec_mag(&pi->vel);
2041  if(sip->debris_min_speed >= 0.0f && sip->debris_max_speed >= 0.0f)
2042  {
2043  float debris_speed = (( sip->debris_max_speed - sip->debris_min_speed ) * frand()) + sip->debris_min_speed;
2044  if(fabs(curspeed) >= 0.001f)
2045  {
2046  float scale = debris_speed / curspeed;
2047  vm_vec_scale(&pi->vel, scale);
2048  }
2049  else
2050  {
2051  vm_vec_copy_scale(&pi->vel, &ship_objp->orient.vec.fvec, debris_speed);
2052  }
2053  }
2054  else if(sip->debris_min_speed >= 0.0f)
2055  {
2056  if(curspeed < sip->debris_min_speed)
2057  {
2058  if(fabs(curspeed) >= 0.001f)
2059  {
2060  float scale = sip->debris_min_speed / curspeed;
2061  vm_vec_scale(&pi->vel, scale);
2062  }
2063  else
2064  {
2065  vm_vec_copy_scale(&pi->vel, &ship_objp->orient.vec.fvec, sip->debris_min_speed);
2066  }
2067  }
2068  }
2069  else if(sip->debris_max_speed >= 0.0f)
2070  {
2071  if(curspeed > sip->debris_max_speed)
2072  {
2073  if(fabs(curspeed) >= 0.001f)
2074  {
2075  float scale = sip->debris_max_speed / curspeed;
2076  vm_vec_scale(&pi->vel, scale);
2077  }
2078  else
2079  {
2080  vm_vec_copy_scale(&pi->vel, &ship_objp->orient.vec.fvec, sip->debris_max_speed);
2081  }
2082  }
2083  }
2084 
2085  //WMC - Rotational velocity user cap
2086  float currotvel = vm_vec_mag(&pi->rotvel);
2087  if(sip->debris_min_rotspeed >= 0.0f && sip->debris_max_rotspeed >= 0.0f)
2088  {
2089  float debris_rotspeed = (( sip->debris_max_rotspeed - sip->debris_min_rotspeed ) * frand()) + sip->debris_min_rotspeed;
2090  if(fabs(currotvel) >= 0.001f)
2091  {
2092  float scale = debris_rotspeed / currotvel;
2093  vm_vec_scale(&pi->rotvel, scale);
2094  }
2095  else
2096  {
2097  vm_vec_copy_scale(&pi->rotvel, &ship_objp->orient.vec.uvec, debris_rotspeed);
2098  }
2099  }
2100  else if(sip->debris_min_rotspeed >= 0.0f)
2101  {
2102  if(curspeed < sip->debris_min_rotspeed)
2103  {
2104  if(fabs(currotvel) >= 0.001f)
2105  {
2106  float scale = sip->debris_min_rotspeed / currotvel;
2107  vm_vec_scale(&pi->rotvel, scale);
2108  }
2109  else
2110  {
2111  vm_vec_copy_scale(&pi->rotvel, &ship_objp->orient.vec.uvec, sip->debris_min_rotspeed);
2112  }
2113  }
2114  }
2115  else if(sip->debris_max_rotspeed >= 0.0f)
2116  {
2117  curspeed = vm_vec_mag(&pi->rotvel);
2118  if(curspeed > sip->debris_max_rotspeed)
2119  {
2120  if(fabs(currotvel) >= 0.001f)
2121  {
2122  float scale = sip->debris_max_rotspeed / currotvel;
2123  vm_vec_scale(&pi->rotvel, scale);
2124  }
2125  else
2126  {
2127  vm_vec_copy_scale(&pi->rotvel, &ship_objp->orient.vec.uvec, sip->debris_max_rotspeed);
2128  }
2129  }
2130  }
2131 
2132  int ship_type = sip->class_type;
2133  if(ship_type > -1)
2134  {
2135  if(vm_vec_mag(&pi->vel) > Ship_types[ship_type].debris_max_speed) {
2136  float scale = Ship_types[ship_type].debris_max_speed / vm_vec_mag(&pi->vel);
2137  vm_vec_scale(&pi->vel, scale);
2138  }
2139  }
2140 
2141  Assert(is_valid_vec(&pi->vel));
2142  Assert(is_valid_vec(&pi->rotvel));
2143 }
2144 
2145 // ----------------------------------------------------------------------------
2146 // uses list of model z values with constant increment to find the radius of the
2147 // cross section at the current model z value
2149 {
2150  if (pm->num_xc < 2) {
2151  return 0.0f;
2152  }
2153 
2154  float index, increment;
2155  increment = (pm->xc[pm->num_xc-1].z - pm->xc[0].z) / (float)(pm->num_xc - 1);
2156  index = (z - pm->xc[0].z) / increment;
2157 
2158  if (index < 0.5f) {
2159  return pm->xc[0].radius;
2160  } else if (index > (pm->num_xc - 1.0f - 0.5f)) {
2161  return pm->xc[pm->num_xc-1].radius;
2162  } else {
2163  int floor_index = (int)floor(index);
2164  int ceil_index = (int)ceil(index);
2165  return MAX(pm->xc[ceil_index].radius, pm->xc[floor_index].radius);
2166  }
2167 }
2168 
2172 int get_sound_time_played(int snd_id, int handle)
2173 {
2174  if (handle == -1) {
2175  return 100000;
2176  }
2177 
2178  int time_left = snd_time_remaining(handle);
2179  int duration = snd_get_duration(snd_id);
2180 
2181  return (duration - time_left);
2182 }
2183 
2190 void do_sub_expl_sound(float radius, vec3d* sound_pos, int* sound_handle)
2191 {
2192  int sound_index, handle;
2193  // multiplier for range (near and far distances) to apply attenuation
2194  float sound_range = 1.0f + 0.0043f*radius;
2195 
2196  int handle_index = rand()%NUM_SUB_EXPL_HANDLES;
2197 
2198  sound_index = SND_SHIP_EXPLODE_1;
2199  handle = sound_handle[handle_index];
2200 
2201  if (handle == -1) {
2202  // if no handle, get one
2203  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 );
2204  } else if (!snd_is_playing(handle)) {
2205  // if sound not playing and old, get new one
2206  // I don't think will happen with SND_PRIORITY_MUST_PLAY
2207  if (get_sound_time_played(Snds[sound_index].id, handle) > 400) {
2208  snd_stop(sound_handle[handle_index]);
2209  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 );
2210  }
2211  } else if (get_sound_time_played(Snds[sound_index].id, handle) > 750) {
2212  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 );
2213  }
2214 }
2215 
2219 static void maybe_fireball_wipe(clip_ship* half_ship, int* sound_handle)
2220 {
2221  // maybe make fireball to cover wipe.
2222  if ( timestamp_elapsed(half_ship->next_fireball) ) {
2223  if ( half_ship->length_left > 0.2f*fl_abs(half_ship->explosion_vel) ) {
2224  ship_info *sip = &Ship_info[Ships[half_ship->parent_obj->instance].ship_info_index];
2225 
2226  polymodel* pm = model_get(sip->model_num);
2227 
2228  vec3d model_clip_plane_pt, orig_ship_world_center, temp;
2229 
2230  vm_vec_unrotate(&orig_ship_world_center, &half_ship->model_center_disp_to_orig_center, &half_ship->orient);
2231  vm_vec_add2(&orig_ship_world_center, &half_ship->local_pivot);
2232 
2233  vm_vec_make(&temp, 0.0f, 0.0f, half_ship->cur_clip_plane_pt);
2234  vm_vec_unrotate(&model_clip_plane_pt, &temp, &half_ship->orient);
2235  vm_vec_add2(&model_clip_plane_pt, &orig_ship_world_center);
2236  vm_vec_rand_vec_quick(&temp);
2237  vm_vec_scale(&temp, 0.1f*frand());
2238  vm_vec_add2(&model_clip_plane_pt, &temp);
2239 
2240  float rad = get_model_cross_section_at_z(half_ship->cur_clip_plane_pt, pm);
2241  if (rad < 1) {
2242  // changed from 0.4 & 0.6 to 0.6 & 0.9 as later 1.5 multiplier was removed
2243  rad = half_ship->parent_obj->radius * frand_range(0.6f, 0.9f);
2244  } else {
2245  // make fireball radius (1.5 +/- .1) * model_cross_section value
2246  // changed from 1.4 & 1.6 to 2.1 & 2.4 as later 1.5 multiplier was removed
2247  rad *= frand_range(2.1f, 2.4f);
2248  }
2249 
2250  rad = MIN(rad, half_ship->parent_obj->radius);
2251 
2252  //defaults to 1.0 now that multiplier was applied to the static values above
2253  rad *= sip->prop_exp_rad_mult;
2254 
2255  int fireball_type = fireball_ship_explosion_type(sip);
2256  if(fireball_type < 0) {
2258  }
2259  int low_res_fireballs = Bs_exp_fire_low;
2260  fireball_create(&model_clip_plane_pt, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(half_ship->parent_obj), rad, 0, &half_ship->parent_obj->phys_info.vel, 0.0f, -1, NULL, low_res_fireballs);
2261 
2262  // start the next fireball up (3-4 per frame) + 30%
2263  int time_low, time_high;
2264  time_low = int(650 * Bs_exp_fire_time_mult);
2265  time_high = int(900 * Bs_exp_fire_time_mult);
2266  half_ship->next_fireball = timestamp_rand(time_low, time_high);
2267 
2268  // do sound
2269  do_sub_expl_sound(half_ship->parent_obj->radius, &model_clip_plane_pt, sound_handle);
2270 
2271  // do particles
2272  particle_emitter pe;
2273  particle_effect pef = sip->split_particles;
2274 
2275  pe.num_low = pef.n_low; // Lowest number of particles to create
2276  pe.num_high = pef.n_high; // Highest number of particles to create
2277  pe.pos = model_clip_plane_pt; // Where the particles emit from
2278  pe.vel = half_ship->phys_info.vel; // Initial velocity of all the particles
2279 
2280  float range = 1.0f + 0.002f*half_ship->parent_obj->radius;
2281 
2282  if (pef.max_life > 0.0f) {
2283  pe.min_life = pef.min_life;
2284  pe.max_life = pef.max_life;
2285  } else {
2286  pe.min_life = 0.5f*range; // How long the particles live
2287  pe.max_life = 6.0f*range; // How long the particles live
2288  }
2289 
2290  pe.normal = vmd_x_vector; // What normal the particle emit around
2291  pe.normal_variance = pef.variance; // How close they stick to that normal 0=on normal, 1=180, 2=360 degree
2292 
2293  if (pef.max_vel > 0.0f) {
2294  pe.min_vel = pef.min_vel;
2295  pe.max_vel = pef.max_vel;
2296  } else {
2297  pe.min_vel = 0.0f; // How fast the slowest particle can move
2298  pe.max_vel = half_ship->explosion_vel; // How fast the fastest particle can move
2299  }
2300  float scale = half_ship->parent_obj->radius * 0.01f;
2301  if (pef.max_rad > 0.0f) {
2302  pe.min_rad = pef.min_rad;
2303  pe.max_rad = pef.max_rad;
2304  } else {
2305  pe.min_rad = 0.5f*scale; // Min radius
2306  pe.max_rad = 1.5f*scale; // Max radius
2307  }
2308 
2309  if (pe.num_high > 0) {
2310  particle_emit( &pe, PARTICLE_SMOKE2, 0, range );
2311  }
2312 
2313  } else {
2314  // time out forever
2315  half_ship->next_fireball = timestamp(-1);
2316  }
2317  }
2318 }
2319 
2320 void big_explosion_flash(float);
2321 
2325 int shipfx_large_blowup_do_frame(ship *shipp, float frametime)
2326 {
2327  Assert( shipp->large_ship_blowup_index > -1 );
2328  Assert( shipp->large_ship_blowup_index < (int)Split_ships.size() );
2329 
2330  split_ship *the_split_ship = &Split_ships[shipp->large_ship_blowup_index];
2331  Assert( the_split_ship->used ); // Get John
2332 
2333  // Do fireballs, particles, shockwave here
2334  // Note parent ship is still valid, vel and pos updated in obj_move_all
2335 
2336  if ( timestamp_elapsed(the_split_ship->explosion_flash_timestamp) ) {
2337  if ( !the_split_ship->explosion_flash_started ) {
2338  object* objp = &Objects[shipp->objnum];
2339  if (objp->flags & OF_WAS_RENDERED) {
2340  float excess_dist = vm_vec_dist(&Player_obj->pos, &objp->pos) - 2.0f*objp->radius - Player_obj->radius;
2341  float intensity = 1.0f - 0.1f*excess_dist / objp->radius;
2342 
2343  if (intensity > 1) {
2344  intensity = 1.0f;
2345  }
2346 
2347  if (intensity > 0.1f && Ship_info[shipp->ship_info_index].flags2 & SIF2_FLASH) {
2348  big_explosion_flash(intensity);
2349  }
2350  }
2351  the_split_ship->explosion_flash_started = 1;
2352  }
2353  }
2354 
2355  physics_sim(&the_split_ship->front_ship.local_pivot, &the_split_ship->front_ship.orient, &the_split_ship->front_ship.phys_info, frametime);
2356  physics_sim(&the_split_ship->back_ship.local_pivot, &the_split_ship->back_ship.orient, &the_split_ship->back_ship.phys_info, frametime);
2357  the_split_ship->front_ship.length_left -= the_split_ship->front_ship.explosion_vel*frametime;
2358  the_split_ship->back_ship.length_left += the_split_ship->back_ship.explosion_vel *frametime;
2359  the_split_ship->front_ship.cur_clip_plane_pt += the_split_ship->front_ship.explosion_vel*frametime;
2360  the_split_ship->back_ship.cur_clip_plane_pt += the_split_ship->back_ship.explosion_vel *frametime;
2361 
2362  float length_left = MAX( the_split_ship->front_ship.length_left, the_split_ship->back_ship.length_left );
2363 
2364  if ( length_left < 0 ) {
2365  the_split_ship->used = 0;
2366  return 1;
2367  }
2368 
2369  maybe_fireball_wipe(&the_split_ship->front_ship, (int*)&the_split_ship->sound_handle);
2370  maybe_fireball_wipe(&the_split_ship->back_ship, (int*)&the_split_ship->sound_handle);
2371  return 0;
2372 }
2373 
2375 {
2376  Assert( shipp->large_ship_blowup_index > -1 );
2377  Assert( shipp->large_ship_blowup_index < (int)Split_ships.size() );
2378 
2379  split_ship *the_split_ship = &Split_ships[shipp->large_ship_blowup_index];
2380  Assert( the_split_ship->used ); // Get John
2381 
2382  if (the_split_ship->front_ship.length_left > 0) {
2383  half_ship_render_ship_and_debris(&the_split_ship->front_ship,shipp);
2384  }
2385 
2386  if (the_split_ship->back_ship.length_left > 0) {
2387  half_ship_render_ship_and_debris(&the_split_ship->back_ship,shipp);
2388  }
2389 
2391 }
2392 
2394 {
2395  Assert( shipp->large_ship_blowup_index > -1 );
2396  Assert( shipp->large_ship_blowup_index < (int)Split_ships.size() );
2397 
2398  split_ship *the_split_ship = &Split_ships[shipp->large_ship_blowup_index];
2399  Assert( the_split_ship->used ); // Get John
2400 
2401  if (the_split_ship->front_ship.length_left > 0) {
2402  shipfx_queue_render_ship_halves_and_debris(scene, &the_split_ship->front_ship,shipp);
2403  }
2404 
2405  if (the_split_ship->back_ship.length_left > 0) {
2406  shipfx_queue_render_ship_halves_and_debris(scene, &the_split_ship->back_ship,shipp);
2407  }
2408 }
2409 
2410 // ================== DO THE ELECTRIC ARCING STUFF =====================
2411 // Creates any new ones, moves old ones.
2412 
2413 const float MAX_ARC_LENGTH_PERCENTAGE = 0.25f;
2414 
2415 const float MAX_EMP_ARC_TIMESTAMP = 150.0f;
2416 
2418 {
2419  int i;
2420  int should_arc;
2421  object *obj = &Objects[shipp->objnum];
2422  int model_num = Ship_info[shipp->ship_info_index].model_num;
2423 
2424  should_arc = 1;
2425  int disrupted_arc=0;
2426 
2427  float damage = get_hull_pct(obj);
2428 
2429  if (damage < 0) {
2430  damage = 0.0f;
2431  }
2432 
2433  // don't draw an arc based on damage
2434  if ( damage > 0.30f ) {
2435  // Don't do spark.
2436  should_arc = 0;
2437  }
2438 
2439  // SUSHI: If the lightning type is NONE, we can skip this
2440  if (Ship_info[shipp->ship_info_index].damage_lightning_type == SLT_NONE)
2441  should_arc = 0;
2442 
2443  // we should draw an arc
2444  if( shipp->emp_intensity > 0.0f){
2445  should_arc = 1;
2446  }
2447 
2448  if ((ship_subsys_disrupted(shipp,SUBSYSTEM_ENGINE)) ||
2451  {
2452  disrupted_arc=1;
2453  should_arc=1;
2454  }
2455 
2456  // Kill off old sparks
2457  for(i=0; i<MAX_SHIP_ARCS; i++){
2458  if(timestamp_valid(shipp->arc_timestamp[i]) && timestamp_elapsed(shipp->arc_timestamp[i])){
2459  shipp->arc_timestamp[i] = timestamp(-1);
2460  }
2461  }
2462 
2463  // if we shouldn't draw an arc, return
2464  if(!should_arc){
2465  return;
2466  }
2467 
2468  if (!timestamp_valid(shipp->arc_next_time)) {
2469  // start the next fireball up in the next 10 seconds or so...
2470  int freq;
2471 
2472  // if the emp effect is active or its disrupted
2473  if((shipp->emp_intensity > 0.0f) || (disrupted_arc)){
2474  freq = fl2i(MAX_EMP_ARC_TIMESTAMP);
2475  }
2476  // otherwise if we're arcing based upon damage
2477  else {
2478  freq = fl2i((damage+0.1f)*5000.0f);
2479  }
2480 
2481  // set the next arc time
2482  shipp->arc_next_time = timestamp_rand(freq*2,freq*4);
2483  }
2484 
2485  if ( timestamp_elapsed(shipp->arc_next_time) ) {
2486 
2487  shipp->arc_next_time = timestamp(-1); // invalid, so it gets restarted next frame
2488 
2489  int n, n_arcs = ((rand()>>5) % 3)+1; // Create 1-3 sparks
2490 
2491  vec3d v1, v2, v3, v4;
2492  if ( Cmdline_old_collision_sys ) {
2493  submodel_get_two_random_points(model_num, -1, &v1, &v2);
2494  submodel_get_two_random_points(model_num, -1, &v3, &v4);
2495  } else {
2496  submodel_get_two_random_points_better(model_num, -1, &v1, &v2);
2497  submodel_get_two_random_points_better(model_num, -1, &v3, &v4);
2498  }
2499 
2500  // For large ships, cap the length to be 25% of max radius
2501  if ( obj->radius > 200.0f ) {
2502  float max_dist = obj->radius * MAX_ARC_LENGTH_PERCENTAGE;
2503 
2504  vec3d tmp;
2505  float d;
2506 
2507  // Cap arc 2->1
2508  vm_vec_sub( &tmp, &v1, &v2 );
2509  d = vm_vec_mag_quick( &tmp );
2510  if ( d > max_dist ) {
2511  vm_vec_scale_add( &v1, &v2, &tmp, max_dist / d );
2512  }
2513 
2514  // Cap arc 2->3
2515  vm_vec_sub( &tmp, &v3, &v2 );
2516  d = vm_vec_mag_quick( &tmp );
2517  if ( d > max_dist ) {
2518  vm_vec_scale_add( &v3, &v2, &tmp, max_dist / d );
2519  }
2520 
2521 
2522  // Cap arc 2->4
2523  vm_vec_sub( &tmp, &v4, &v2 );
2524  d = vm_vec_mag_quick( &tmp );
2525  if ( d > max_dist ) {
2526  vm_vec_scale_add( &v4, &v2, &tmp, max_dist / d );
2527  }
2528 
2529  }
2530 
2531  n = 0;
2532 
2533  float factor = 1.0f + 0.0025f*obj->radius;
2534  int a = (int) (factor*100.0f);
2535  int b = (int) (factor*1000.0f);
2536  int lifetime = (myrand()%((b)-(a)+1))+(a);
2537 
2538  // Create the arc effects
2539  for (i=0; i<MAX_SHIP_ARCS; i++ ) {
2540  if ( !timestamp_valid( shipp->arc_timestamp[i] ) ) {
2541  shipp->arc_timestamp[i] = timestamp(lifetime); // live up to a second
2542 
2543  switch( n ) {
2544  case 0:
2545  shipp->arc_pts[i][0] = v1;
2546  shipp->arc_pts[i][1] = v2;
2547  break;
2548  case 1:
2549  shipp->arc_pts[i][0] = v2;
2550  shipp->arc_pts[i][1] = v3;
2551  break;
2552 
2553  case 2:
2554  shipp->arc_pts[i][0] = v2;
2555  shipp->arc_pts[i][1] = v4;
2556  break;
2557 
2558  default:
2559  Int3();
2560  }
2561 
2562  // determine what kind of arc to create
2563  if((shipp->emp_intensity > 0.0f) || (disrupted_arc)){
2564  shipp->arc_type[i] = MARC_TYPE_EMP;
2565  } else {
2566  shipp->arc_type[i] = MARC_TYPE_NORMAL;
2567  }
2568 
2569  n++;
2570  if ( n == n_arcs )
2571  break; // Don't need to create anymore
2572  }
2573 
2574  // rotate v2 out of local coordinates into world.
2575  // Use v2 since it is used in every bolt. See above switch().
2576  vec3d snd_pos;
2577  vm_vec_unrotate(&snd_pos, &v2, &obj->orient);
2578  vm_vec_add2(&snd_pos, &obj->pos );
2579 
2580  //Play a sound effect
2581  if ( lifetime > 750 ) {
2582  // 1.00 second effect
2583  snd_play_3d( &Snds[SND_DEBRIS_ARC_05], &snd_pos, &View_position, obj->radius );
2584  } else if ( lifetime > 500 ) {
2585  // 0.75 second effect
2586  snd_play_3d( &Snds[SND_DEBRIS_ARC_04], &snd_pos, &View_position, obj->radius );
2587  } else if ( lifetime > 250 ) {
2588  // 0.50 second effect
2589  snd_play_3d( &Snds[SND_DEBRIS_ARC_03], &snd_pos, &View_position, obj->radius );
2590  } else if ( lifetime > 100 ) {
2591  // 0.25 second effect
2592  snd_play_3d( &Snds[SND_DEBRIS_ARC_02], &snd_pos, &View_position, obj->radius );
2593  } else {
2594  // 0.10 second effect
2595  snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius );
2596  }
2597  }
2598  }
2599 
2600  // maybe move arc points around
2601  for (i=0; i<MAX_SHIP_ARCS; i++ ) {
2602  if ( timestamp_valid( shipp->arc_timestamp[i] ) ) {
2603  if ( !timestamp_elapsed( shipp->arc_timestamp[i] ) ) {
2604  // Maybe move a vertex.... 20% of the time maybe?
2605  int mr = myrand();
2606  if ( mr < RAND_MAX/5 ) {
2607  vec3d v1, v2;
2608  if ( Cmdline_old_collision_sys ) {
2609  submodel_get_two_random_points(model_num, -1, &v1, &v2);
2610  } else {
2611  submodel_get_two_random_points_better(model_num, -1, &v1, &v2);
2612  }
2613 
2614  vec3d static_one;
2615 
2616  if ( mr % 2 ) {
2617  static_one = shipp->arc_pts[i][0];
2618  } else {
2619  static_one = shipp->arc_pts[i][1];
2620  }
2621 
2622  // For large ships, cap the length to be 25% of max radius
2623  if ( obj->radius > 200.0f ) {
2624  float max_dist = obj->radius * MAX_ARC_LENGTH_PERCENTAGE;
2625 
2626  vec3d tmp;
2627  float d;
2628 
2629  // Cap arc 2->1
2630  vm_vec_sub( &tmp, &v1, &static_one );
2631  d = vm_vec_mag_quick( &tmp );
2632  if ( d > max_dist ) {
2633  vm_vec_scale_add( &v1, &static_one, &tmp, max_dist / d );
2634  }
2635  }
2636 
2637  shipp->arc_pts[i][mr % 2] = v1;
2638  }
2639  }
2640  }
2641  }
2642 }
2643 
2645 int l_big_count = 2;
2647 float l_max_radius = 3000.0f;
2649 {
2650  /*
2651  ship_info *sip;
2652  object *objp;
2653  int stamp, count;
2654  vec3d v1, v2, n1, n2, temp, temp2;
2655  bolt_info binfo;
2656 
2657  // sanity checks
2658  Assert(shipp != NULL);
2659  if(shipp == NULL){
2660  return;
2661  }
2662  Assert(shipp->ship_info_index >= 0);
2663  if(shipp->ship_info_index < 0){
2664  return;
2665  }
2666  Assert(shipp->objnum >= 0);
2667  if(shipp->objnum < 0){
2668  return;
2669  }
2670 
2671  // get some pointers
2672  sip = &Ship_info[shipp->ship_info_index];
2673  objp = &Objects[shipp->objnum];
2674 
2675  // if this is not a nebula mission, don't do anything
2676  if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){
2677  shipp->lightning_stamp = -1;
2678  return;
2679  }
2680 
2681  // if this not a cruiser or big ship
2682  if(!((sip->flags & SIF_CRUISER) || (sip->flags & SIF_BIG_SHIP) || (sip->flags & SIF_HUGE_SHIP))){
2683  shipp->lightning_stamp = -1;
2684  return;
2685  }
2686 
2687  // determine stamp and count values
2688  if(sip->flags & SIF_CRUISER){
2689  stamp = (int)((float)(Nebl_cruiser_min + ((Nebl_cruiser_max - Nebl_cruiser_min) * Nebl_intensity)) * frand_range(0.8f, 1.1f));
2690  count = l_cruiser_count;
2691  }
2692  else {
2693  if(sip->flags & SIF_HUGE_SHIP){
2694  stamp = (int)((float)(Nebl_supercap_min + ((Nebl_supercap_max - Nebl_supercap_min) * Nebl_intensity)) * frand_range(0.8f, 1.1f));
2695  count = l_huge_count;
2696  } else {
2697  stamp = (int)((float)(Nebl_cap_min + ((Nebl_cap_max - Nebl_cap_min) * Nebl_intensity)) * frand_range(0.8f, 1.1f));
2698  count = l_big_count;
2699  }
2700  }
2701 
2702  // if his timestamp is unset
2703  if(shipp->lightning_stamp == -1){
2704  shipp->lightning_stamp = timestamp(stamp);
2705  return;
2706  }
2707  // if his timestamp is currently unelapsed
2708  if(!timestamp_elapsed(shipp->lightning_stamp)){
2709  return;
2710  }
2711 
2712  mprintf(("SHIP BOLT\n"));
2713 
2714  // restamp him first
2715  shipp->lightning_stamp = timestamp(stamp);
2716 
2717  // ah, now we can create some lightning bolts
2718  count = (int)frand_range(0.0f, (float)count);
2719  while(count > 0){
2720  // get 2 points on the hull of the ship
2721  submodel_get_two_random_points(shipp->modelnum, 0, &v1, &v2, &n1, &n2);
2722 
2723  // make up to 2 bolts
2724  if(objp->radius > l_max_radius){
2725  vm_vec_scale_add(&temp2, &v1, &n1, l_max_radius);
2726  } else {
2727  vm_vec_scale_add(&temp2, &v1, &n1, objp->radius);
2728  }
2729  vm_vec_unrotate(&temp, &temp2, &objp->orient);
2730  vm_vec_add2(&temp, &objp->pos);
2731  vm_vec_unrotate(&temp2, &v1, &objp->orient);
2732  vm_vec_add2(&temp2, &objp->pos);
2733 
2734  // create the bolt
2735  binfo.start = temp;
2736  binfo.strike = temp2;
2737  binfo.num_strikes = 3;
2738  binfo.noise = 0.045f;
2739  binfo.life = 375;
2740  binfo.delay = (int)frand_range(0.0f, 1600.0f);
2741  nebl_bolt(&binfo);
2742  count--;
2743 
2744  // done
2745  if(count <= 0){
2746  break;
2747  }
2748 
2749  // one more
2750  if(objp->radius > l_max_radius){
2751  vm_vec_scale_add(&temp2, &v2, &n2, l_max_radius);
2752  } else {
2753  vm_vec_scale_add(&temp2, &v2, &n2, objp->radius);
2754  }
2755  vm_vec_unrotate(&temp, &temp2, &objp->orient);
2756  vm_vec_add2(&temp, &objp->pos);
2757  vm_vec_unrotate(&temp2, &v2, &objp->orient);
2758  vm_vec_add2(&temp2, &objp->pos);
2759 
2760  // create the bolt
2761  binfo.start = temp;
2762  binfo.strike = temp2;
2763  binfo.num_strikes = 3;
2764  binfo.noise = 0.045f;
2765  binfo.life = 375;
2766  binfo.delay = (int)frand_range(0.0f, 1600.0f);
2767  nebl_bolt(&binfo);
2768  count--;
2769  }
2770  */
2771 }
2772 
2777 {
2778  ship_info *sip;
2779  object *objp;
2780  polymodel *pm;
2781  vec3d temp, dir, shockwave_pos;
2782  vec3d head = vmd_zero_vector;
2783  vec3d tail = vmd_zero_vector;
2784  float step, cur;
2785  int idx;
2786 
2787  // sanity checks
2788  Assert(shipp != NULL);
2789  if(shipp == NULL){
2790  return;
2791  }
2792  Assert(shipp->ship_info_index >= 0);
2793  if(shipp->ship_info_index < 0){
2794  return;
2795  }
2796  Assert(shipp->objnum >= 0);
2797  if(shipp->objnum < 0){
2798  return;
2799  }
2800  Assert(sci != NULL);
2801  if (sci == NULL) {
2802  return;
2803  }
2804 
2805  // get some pointers
2806  sip = &Ship_info[shipp->ship_info_index];
2807  objp = &Objects[shipp->objnum];
2808 
2809  if(sip->shockwave_count <= 0){
2810  return;
2811  }
2812 
2813  // get vectors at the head and tail of the object, dead center
2814  pm = model_get(sip->model_num);
2815  if(pm == NULL){
2816  return;
2817  }
2818  head.xyz.x = pm->submodel[0].offset.xyz.x;
2819  head.xyz.y = pm->submodel[0].offset.xyz.y;
2820  head.xyz.z = pm->maxs.xyz.z;
2821 
2822  tail.xyz.x = pm->submodel[0].offset.xyz.x;
2823  tail.xyz.y = pm->submodel[0].offset.xyz.y;
2824  tail.xyz.z = pm->mins.xyz.z;
2825 
2826  // transform the vectors into world coords
2827  vm_vec_unrotate(&temp, &head, &objp->orient);
2828  vm_vec_add(&head, &temp, &objp->pos);
2829  vm_vec_unrotate(&temp, &tail, &objp->orient);
2830  vm_vec_add(&tail, &temp, &objp->pos);
2831 
2832  // now create as many shockwaves as needed
2833  vm_vec_sub(&dir, &head, &tail);
2834  step = 1.0f / ((float)sip->shockwave_count + 1.0f);
2835  cur = step;
2836  for(idx=0; idx<sip->shockwave_count; idx++){
2837  // get the shockwave position
2838  temp = dir;
2839  vm_vec_scale(&temp, cur);
2840  vm_vec_add(&shockwave_pos, &tail, &temp);
2841 
2842  // if knossos device, make shockwave in center
2843  if (Ship_info[shipp->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
2844  shockwave_pos = Objects[shipp->objnum].pos;
2845  }
2846 
2847  // create the shockwave
2848  shockwave_create_info sci2 = *sci;
2849  sci2.blast = (sci->blast / (float)sip->shockwave_count) * frand_range(0.75f, 1.25f);
2850  sci2.damage = (sci->damage / (float)sip->shockwave_count) * frand_range(0.75f, 1.25f);
2851  sci2.speed = sci->speed * frand_range(0.75f, 1.25f);
2852  sci2.rot_angles.p = frand_range(0.0f, 1.99f*PI);
2853  sci2.rot_angles.b = frand_range(0.0f, 1.99f*PI);
2854  sci2.rot_angles.h = frand_range(0.0f, 1.99f*PI);
2855 
2856  shockwave_create(shipp->objnum, &shockwave_pos, &sci2, SW_SHIP_DEATH, (int)frand_range(0.0f, 350.0f));
2857 
2858  // next shockwave
2859  cur += step;
2860  }
2861 }
2862 
2863 extern int model_should_render_engine_glow(int objnum, int bank_obj);
2864 int Wash_on = 1;
2865 DCF_BOOL(engine_wash, Wash_on)
2866 #define ENGINE_WASH_CHECK_INTERVAL 250 // (4x sec)
2867 
2874 {
2875  int idx, j;
2876  object *objp, *max_ship_intensity_objp;
2877  int started_with_no_wash = shipp->wash_intensity <= 0 ? 1 : 0;
2878 
2879  if (!Wash_on) {
2880  return;
2881  }
2882 
2883  Assert(shipp != NULL);
2884  Assert(shipp->objnum >= 0);
2885  objp = &Objects[shipp->objnum];
2886  ship_obj *so;
2887 
2888  float dist_sqr, inset_depth, dot_to_ship, max_ship_intensity;
2889  polymodel *pm;
2890 
2891  float max_wash_dist, half_angle, radius_mult;
2892 
2893  // if this is not a fighter or bomber, we don't care
2894  if ((objp->type != OBJ_SHIP) || !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ) {
2895  return;
2896  }
2897 
2898  // is it time to check for engine wash
2899  int time_to_next_hit = timestamp_until(shipp->wash_timestamp);
2900  if (time_to_next_hit < 0) {
2901  if (time_to_next_hit < -ENGINE_WASH_CHECK_INTERVAL) {
2902  time_to_next_hit = 0;
2903  }
2904 
2905  // keep interval constant independent of framerate
2906  shipp->wash_timestamp = timestamp(ENGINE_WASH_CHECK_INTERVAL + time_to_next_hit);
2907 
2908  // initialize wash params
2909  shipp->wash_intensity = 0.0f;
2910  vm_vec_zero(&shipp->wash_rot_axis);
2911  max_ship_intensity_objp = NULL;
2912  max_ship_intensity = 0;
2913  } else {
2914  return;
2915  }
2916 
2917  // only do damage if we're within half of the max wash distance
2918  int do_damage = 0;
2919 
2920  // go thru Ship_used_list and check if we're in wash from CAP or SUPERCAP (SIF_HUGE)
2921  for (so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so)) {
2922  if (so->objnum < 0) {
2923  continue;
2924  }
2925 
2926  object *wash_objp = &Objects[so->objnum];
2927 
2928  if (!wash_objp || (wash_objp->instance < 0) || (wash_objp->type != OBJ_SHIP)) {
2929  continue;
2930  }
2931 
2932  ship *wash_shipp = &Ships[wash_objp->instance];
2933  ship_info *wash_sip = &Ship_info[wash_shipp->ship_info_index];
2934 
2935  // don't do small ships
2936  if ( (wash_sip->flags & SIF_SMALL_SHIP) ) {
2937  continue;
2938  }
2939 
2940  pm = model_get(wash_sip->model_num);
2941  float ship_intensity = 0;
2942 
2943  // if engines disabled, no engine wash
2944  if (ship_get_subsystem_strength(wash_shipp, SUBSYSTEM_ENGINE) < 0.01) {
2945  continue;
2946  }
2947 
2948  float speed_scale;
2949  if (wash_objp->phys_info.speed > 20.0f)
2950  speed_scale = 1.0f;
2951  else
2952  speed_scale = wash_objp->phys_info.speed/20.0f;
2953 
2954  for (idx = 0; idx < pm->n_thrusters; idx++) {
2955  thruster_bank *bank = &pm->thrusters[idx];
2956  vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
2957  bool submodel_rotation = false;
2958 
2959  // make sure this engine is functional before we try to process a wash from it
2960  if ( !model_should_render_engine_glow(OBJ_INDEX(wash_objp), bank->obj_num) ) {
2961  continue;
2962  }
2963 
2964  // check if thruster bank has engine wash
2965  if (bank->wash_info_pointer == NULL) {
2966  // if huge, give default engine wash
2967  if ((wash_sip->flags & SIF_HUGE_SHIP) && !Engine_wash_info.empty()) {
2968  bank->wash_info_pointer = &Engine_wash_info[0];
2969  nprintf(("wash", "Adding default engine wash to ship %s", wash_sip->name));
2970  } else {
2971  continue;
2972  }
2973  }
2974 
2975  engine_wash_info *ewp = bank->wash_info_pointer;
2976  half_angle = ewp->angle;
2977  radius_mult = ewp->radius_mult;
2978 
2979 
2980  // If bank is attached to a submodel, prepare to account for rotations
2981  //
2982  // TODO: This won't work in the ship lab, because the lab code doesn't
2983  // set the the necessary submodel instance info needed here. The second
2984  // condition is thus a hack to disable the feature while in the lab, and
2985  // can be removed if the lab is re-structured accordingly. -zookeeper
2986  if ( bank->submodel_num > -1 && pm->submodel[bank->submodel_num].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
2987  model_find_submodel_offset(&submodel_static_offset, wash_sip->model_num, bank->submodel_num);
2988 
2989  submodel_rotation = true;
2990  }
2991 
2992  for (j=0; j<bank->num_points; j++) {
2993  vec3d world_thruster_pos, world_thruster_norm, apex, thruster_to_ship, apex_to_ship, temp;
2994  vec3d loc_pos = bank->points[j].pnt;
2995  vec3d loc_norm = bank->points[j].norm;
2996 
2997  if ( submodel_rotation ) {
2998  vm_vec_sub(&loc_pos, &bank->points[j].pnt, &submodel_static_offset);
2999 
3000  // Gets the final offset and normal in the ship's frame of reference
3001  temp = loc_pos;
3002  find_submodel_instance_point_normal(&loc_pos, &loc_norm, wash_shipp->model_instance_num, bank->submodel_num, &temp, &loc_norm);
3003  }
3004 
3005  // get world pos of thruster
3006  vm_vec_unrotate(&world_thruster_pos, &loc_pos, &wash_objp->orient);
3007  vm_vec_add2(&world_thruster_pos, &wash_objp->pos);
3008 
3009  // get world norm of thruster;
3010  vm_vec_unrotate(&world_thruster_norm, &loc_norm, &wash_objp->orient);
3011 
3012  // get vector from thruster to ship
3013  vm_vec_sub(&thruster_to_ship, &objp->pos, &world_thruster_pos);
3014 
3015  // check if on back side of thruster
3016  dot_to_ship = vm_vec_dot(&thruster_to_ship, &world_thruster_norm);
3017  if (dot_to_ship > 0) {
3018 
3019  // get max wash distance
3020  max_wash_dist = MAX(ewp->length, bank->points[j].radius * ewp->radius_mult);
3021 
3022  // check if within dist range
3023  dist_sqr = vm_vec_mag_squared(&thruster_to_ship);
3024  if (dist_sqr < max_wash_dist*max_wash_dist) {
3025 
3026  // check if inside the sphere
3027  if ( dist_sqr < ((radius_mult * radius_mult) * (bank->points[j].radius * bank->points[j].radius)) ) {
3028  vm_vec_cross(&temp, &world_thruster_norm, &thruster_to_ship);
3029  vm_vec_scale_add2(&shipp->wash_rot_axis, &temp, dot_to_ship / dist_sqr);
3030  ship_intensity += (1.0f - dist_sqr / (max_wash_dist*max_wash_dist));
3031  if (!do_damage) {
3032  if (dist_sqr < 0.25 * max_wash_dist * max_wash_dist) {
3033  do_damage = 1;
3034  }
3035  }
3036  } else {
3037  // check if inside cone - first fine apex of cone
3038  inset_depth = (bank->points[j].radius / fl_tan(half_angle));
3039  vm_vec_scale_add(&apex, &world_thruster_pos, &world_thruster_norm, -inset_depth);
3040  vm_vec_sub(&apex_to_ship, &objp->pos, &apex);
3041  vm_vec_normalize(&apex_to_ship);
3042 
3043  // check if inside cone angle
3044  if (vm_vec_dot(&apex_to_ship, &world_thruster_norm) > cosf(half_angle)) {
3045  vm_vec_cross(&temp, &world_thruster_norm, &thruster_to_ship);
3046  vm_vec_scale_add2(&shipp->wash_rot_axis, &temp, dot_to_ship / dist_sqr);
3047  ship_intensity += (1.0f - dist_sqr / (max_wash_dist*max_wash_dist));
3048  if (!do_damage) {
3049  if (dist_sqr < 0.25 * max_wash_dist * max_wash_dist) {
3050  do_damage = 1;
3051  }
3052  }
3053  }
3054  }
3055  }
3056  }
3057  }
3058  }
3059 
3060  shipp->wash_intensity += ship_intensity * speed_scale;
3061 
3062  if (ship_intensity > max_ship_intensity) {
3063  max_ship_intensity = ship_intensity;
3064  max_ship_intensity_objp = wash_objp;
3065  }
3066  }
3067 
3068  // apply damage at rate of 1%/sec
3069  if (shipp->wash_intensity > 0) {
3070  Assert(max_ship_intensity_objp != NULL);
3071 
3072  nprintf(("wash", "Wash intensity %.2f\n", shipp->wash_intensity));
3073 
3074  float damage;
3075  if (!do_damage) {
3076  damage = 0;
3077  } else {
3078  damage = (0.001f * 0.003f * ENGINE_WASH_CHECK_INTERVAL * shipp->ship_max_hull_strength * shipp->wash_intensity);
3079  }
3080 
3081  ship_apply_wash_damage(objp, max_ship_intensity_objp, damage);
3082 
3083  // if we had no wash before now, add the wash object sound
3084  if(started_with_no_wash){
3085  if(shipp != Player_ship){
3087  } else {
3089  }
3090  }
3091  }
3092  // if we've got no wash, kill any wash object sounds from this guy
3093  else {
3094  if(shipp != Player_ship){
3096  } else {
3099  }
3100  }
3101 }
3102 
3107 {
3109 }
3110 
3115 {
3116  if(Player_engine_wash_loop != -1){
3119  }
3120 }
3121 
3123 {
3124 public:
3125  static const int TYPE_NONE;
3126  static const int TYPE_FLOAT;
3127  static const int TYPE_IMAGE;
3128  static const int TYPE_INT;
3129  static const int TYPE_SOUND;
3130  static const int TYPE_STRING;
3131 private:
3132  int Type;
3133  union StorageUnion
3134  {
3135  float su_Float;
3136  int su_Image;
3137  int su_Int;
3138  int su_Sound;
3139  char *su_String;
3140  } StorageUnion;
3141 public:
3142  //TYPE_NONE
3143  CombinedVariable();
3144  //TYPE_FLOAT
3145  CombinedVariable(float n_Float);
3146  //TYPE_INT
3147  CombinedVariable(int n_Int);
3148  //TYPE_IMAGE, TYPE_SOUND
3149  CombinedVariable(int n_Int, ubyte type_override);
3150  //TYPE_STRING
3151  CombinedVariable(char *n_String);
3152  //All types
3154 
3155  //Returns 1 if buffer was successfully written to
3156  int getFloat(float *output);
3157  //Returns handle or < 0 on failure/wrong type
3158  int getHandle();
3159  //Returns handle, or < 0 on failure/wrong type
3160  int getImage();
3161  //Returns 1 if buffer was successfully written to
3162  int getInt(int *output);
3163  //Returns handle, or < 0 on failure/wrong type
3164  int getSound();
3165  //Returns 1 if buffer was successfully written to
3166  int getString(char *output, size_t output_max);
3167 
3168  //Returns true if TYPE_NONE
3169  bool isEmpty();
3170 };
3171 
3172 //Workaround for MSVC6
3173 const int CombinedVariable::TYPE_NONE=0;
3174 const int CombinedVariable::TYPE_FLOAT = 1;
3175 const int CombinedVariable::TYPE_IMAGE = 2;
3176 const int CombinedVariable::TYPE_INT = 3;
3177 const int CombinedVariable::TYPE_SOUND = 4;
3178 const int CombinedVariable::TYPE_STRING = 5;
3179 
3180 //Member functions
3182 {
3183  Type = TYPE_NONE;
3184 }
3185 
3187 {
3188  Type = TYPE_FLOAT;
3189  StorageUnion.su_Float = n_Float;
3190 }
3191 
3193 {
3194  Type = TYPE_INT;
3195  StorageUnion.su_Int = n_Int;
3196 }
3197 
3199 {
3200  if(type_override == TYPE_IMAGE)
3201  {
3202  Type = TYPE_IMAGE;
3203  StorageUnion.su_Image = n_Int;
3204  }
3205  else if(type_override == TYPE_SOUND)
3206  {
3207  Type = TYPE_SOUND;
3208  StorageUnion.su_Sound = n_Int;
3209  }
3210  else
3211  {
3212  Type = TYPE_INT;
3213  StorageUnion.su_Int = n_Int;
3214  }
3215 }
3216 
3218 {
3219  Type = TYPE_STRING;
3220  StorageUnion.su_String = (char *)vm_malloc(strlen(n_String)+1);
3221  strcpy(StorageUnion.su_String, n_String);
3222 }
3223 
3225 {
3226  if(Type == TYPE_STRING)
3227  {
3228  vm_free(StorageUnion.su_String);
3229  }
3230 }
3231 
3232 int CombinedVariable::getFloat(float *output)
3233 {
3234  if(Type == TYPE_FLOAT)
3235  {
3236  *output = StorageUnion.su_Float;
3237  return 1;
3238  }
3239  if(Type == TYPE_IMAGE)
3240  {
3241  *output = i2fl(StorageUnion.su_Image);
3242  return 1;
3243  }
3244  if(Type == TYPE_INT)
3245  {
3246  *output = i2fl(StorageUnion.su_Int);
3247  return 1;
3248  }
3249  if(Type == TYPE_SOUND)
3250  {
3251  *output = i2fl(StorageUnion.su_Sound);
3252  return 1;
3253  }
3254  if(Type == TYPE_STRING)
3255  {
3256  *output = (float)atof(StorageUnion.su_String);
3257  return 1;
3258  }
3259  return 0;
3260 }
3262 {
3263  int i = 0;
3264  if(this->getInt(&i))
3265  return i;
3266  else
3267  return -1;
3268 }
3270 {
3271  if(Type == TYPE_IMAGE)
3272  return this->getHandle();
3273  else
3274  return -1;
3275 }
3277 {
3278  if(output == NULL)
3279  return 0;
3280 
3281  if(Type == TYPE_FLOAT)
3282  {
3283  *output = fl2i(StorageUnion.su_Float);
3284  return 1;
3285  }
3286  if(Type == TYPE_IMAGE)
3287  {
3288  *output = StorageUnion.su_Image;
3289  return 1;
3290  }
3291  if(Type == TYPE_INT)
3292  {
3293  *output = StorageUnion.su_Int;
3294  return 1;
3295  }
3296  if(Type == TYPE_SOUND)
3297  {
3298  *output = StorageUnion.su_Sound;
3299  return 1;
3300  }
3301  if(Type == TYPE_STRING)
3302  {
3303  *output = atoi(StorageUnion.su_String);
3304  return 1;
3305  }
3306 
3307  return 0;
3308 }
3310 {
3311  if(Type == TYPE_SOUND)
3312  return this->getHandle();
3313  else
3314  return -1;
3315 }
3316 int CombinedVariable::getString(char *output, size_t output_max)
3317 {
3318  if(output == NULL || output_max == 0)
3319  return 0;
3320 
3321  if(Type == TYPE_FLOAT)
3322  {
3323  snprintf(output, output_max, "%f", StorageUnion.su_Float);
3324  return 1;
3325  }
3326  if(Type == TYPE_IMAGE)
3327  {
3328  if(bm_is_valid(StorageUnion.su_Image))
3329  snprintf(output, output_max, "%s", bm_get_filename(StorageUnion.su_Image));
3330  return 1;
3331  }
3332  if(Type == TYPE_INT)
3333  {
3334  snprintf(output, output_max, "%i", StorageUnion.su_Int);
3335  return 1;
3336  }
3337  if(Type == TYPE_SOUND)
3338  {
3339  Error(LOCATION, "Sound CombinedVariables are not supported yet.");
3340  /*if(snd_is_valid(StorageUnion.su_Sound))
3341  snprintf(output, output_max, "%s", snd_get_filename(StorageUnion.su_Sound));*/
3342  return 1;
3343  }
3344  if(Type == TYPE_STRING)
3345  {
3346  strncpy(output, StorageUnion.su_String, output_max);
3347  return 1;
3348  }
3349  return 0;
3350 }
3352 {
3353  return (Type != TYPE_NONE);
3354 }
3355 
3357 {
3358  if(dest == NULL || src == NULL || num == 0)
3359  return;
3360 
3361  char buf[NAME_LENGTH*2];
3362  buf[sizeof(buf)-1] = '\0';
3363 
3364  flag_def_list *sp = NULL;
3365  CombinedVariable *dp = NULL;
3366  for(size_t i = 0; i < num; i++)
3367  {
3368  sp = &src[i];
3369  dp = &dest[i];
3370 
3371  snprintf(buf, sizeof(buf)-1, "+%s:", sp->name);
3372  if(optional_string(buf))
3373  {
3374  switch(sp->var)
3375  {
3376  case CombinedVariable::TYPE_FLOAT:
3377  {
3378  float f = 0.0f;
3379  stuff_float(&f);
3380  *dp = CombinedVariable(f);
3381  break;
3382  }
3383  case CombinedVariable::TYPE_INT:
3384  {
3385  int myInt = 0;
3386  stuff_int(&myInt);
3387  *dp = CombinedVariable(myInt);
3388  break;
3389  }
3390  case CombinedVariable::TYPE_IMAGE:
3391  {
3392  char buf2[MAX_FILENAME_LEN];
3394  int idx = bm_load(buf2);
3395  *dp = CombinedVariable(idx, CombinedVariable::TYPE_IMAGE);
3396  break;
3397  }
3398  case CombinedVariable::TYPE_SOUND:
3399  {
3400  char buf2[MAX_FILENAME_LEN];
3402  int idx = gamesnd_get_by_name(buf);
3403  *dp = CombinedVariable(idx, CombinedVariable::TYPE_SOUND);
3404  break;
3405  }
3406  case CombinedVariable::TYPE_STRING:
3407  {
3408  char buf2[MAX_NAME_LEN + MAX_FILENAME_LEN];
3410  *dp = CombinedVariable(buf2);
3411  break;
3412  }
3413  }
3414  }
3415  }
3416 }
3417 
3418 #define WV_ANIMATION 0
3419 #define WV_RADIUS 1
3420 #define WV_SPEED 2
3421 #define WV_TIME 3
3422 
3424  {"Animation", WV_ANIMATION, CombinedVariable::TYPE_STRING},
3425  {"Radius", WV_RADIUS, CombinedVariable::TYPE_FLOAT},
3426  {"Speed", WV_SPEED, CombinedVariable::TYPE_FLOAT},
3427  {"Time", WV_TIME, CombinedVariable::TYPE_FLOAT},
3428 };
3429 
3430 //********************-----CLASS: WarpEffect-----********************//
3432 {
3433  this->clear();
3434 }
3435 
3436 WarpEffect::WarpEffect(object *n_objp, int n_direction)
3437 {
3438  this->clear();
3439  if(n_objp != NULL && n_objp->type == OBJ_SHIP && n_objp->instance > -1 && Ships[n_objp->instance].ship_info_index > -1)
3440  {
3441  objp = n_objp;
3442  direction = n_direction;
3443 
3444  //Setup courtesy variables
3445  shipp = &Ships[objp->instance];
3446  sip = &Ship_info[shipp->ship_info_index];
3447  }
3448 }
3449 
3451 {
3452  objp = NULL;
3453  direction = WD_NONE;
3454  shipp = NULL;
3455  sip = NULL;
3456 }
3457 
3459 {
3460  if(objp == NULL)
3461  return false;
3462 
3463  return true;
3464 }
3465 
3467 {
3468 }
3470 {
3471 }
3472 
3474 {
3475  if(!this->isValid())
3476  return 0;
3477 
3478  return 1;
3479 }
3480 
3481 int WarpEffect::warpFrame(float frametime)
3482 {
3483  return 0;
3484 }
3485 
3487 {
3488  return 0;
3489 }
3490 
3492 {
3493  return 0;
3494 }
3495 
3497 {
3498  return 0;
3499 }
3500 
3502 {
3503  return 0;
3504 }
3505 
3507 {
3508  if(!this->isValid())
3509  return 0;
3510 
3511  shipp->flags &= (~SF_ARRIVING_STAGE_1);
3512  shipp->flags &= (~SF_ARRIVING_STAGE_2);
3513  shipp->flags &= (~SF_DEPART_WARP);
3514 
3515  // let physics in on it too.
3516  objp->phys_info.flags &= (~PF_WARP_IN);
3517  objp->phys_info.flags &= (~PF_WARP_OUT);
3518 
3519  if(direction == WD_WARP_OUT)
3521 
3522  return 1;
3523 }
3524 
3526 {
3527  if(!this->isValid())
3528  return 0;
3529 
3530  *output = objp->pos;
3531  return 1;
3532 }
3533 
3535 {
3536  if(!this->isValid())
3537  return 0;
3538 
3539  *output = objp->orient;
3540  return 1;
3541 }
3542 
3543 //********************-----CLASS: WE_Default-----********************//
3544 WE_Default::WE_Default(object *n_objp, int n_direction)
3545  :WarpEffect(n_objp, n_direction)
3546 {
3547  if(!this->isValid())
3548  return;
3549 
3550  portal_objp = NULL;
3551  stage_duration[0] = 0;
3552 
3553  pos = vmd_zero_vector;
3554  fvec = vmd_zero_vector;
3555 }
3556 
3558 {
3559  if(!this->isValid())
3560  return 0;
3561 
3562  if(direction == WD_WARP_OUT && objp == Player_obj)
3563  {
3564  HUD_printf(NOX("Subspace drive engaged"));
3565  }
3566 
3567  portal_objp = NULL;
3569  {
3570  portal_objp = &Objects[shipp->special_warpin_objnum];
3571  }
3573  {
3574  portal_objp = &Objects[shipp->special_warpout_objnum];
3575  }
3576 
3577  float warpout_speed = 0.0f;
3578  float warp_time = 0.0f;
3579  if(direction == WD_WARP_IN)
3580  {
3581  polymodel *pm = model_get(sip->model_num);
3582  vm_vec_scale_add( &pos, &objp->pos, &objp->orient.vec.fvec, (pm != NULL) ? -pm->mins.xyz.z : objp->radius );
3583 
3584  // Effect time is 'SHIPFX_WARP_DELAY' (1.5 secs) seconds to start, 'shipfx_calculate_warp_time'
3585  // for ship to go thru, and 'SHIPFX_WARP_DELAY' (1.5 secs) to go away.
3587  }
3588  else
3589  {
3590  compute_warpout_stuff(objp, &warpout_speed, &warp_time, &pos);
3591  warp_time += SHIPFX_WARP_DELAY;
3592  }
3593 
3594  radius = shipfx_calculate_effect_radius(objp, direction);
3595  // cap radius to size of knossos
3596  if(portal_objp != NULL)
3597  {
3598  // cap radius to size of knossos
3599  radius = MIN(radius, 0.8f*portal_objp->radius);
3600  }
3601 
3602  int warp_objnum = -1;
3603  if (direction == WD_WARP_OUT)
3604  {
3605  // maybe special warpout
3606  int fireball_type = ((portal_objp != NULL) || (sip->warpout_type == WT_KNOSSOS) || (sip->warpout_type == WT_DEFAULT_THEN_KNOSSOS)) ? FIREBALL_KNOSSOS : FIREBALL_WARP;
3607 
3608  // create fireball
3609  warp_objnum = fireball_create(&pos, fireball_type, FIREBALL_WARP_EFFECT, OBJ_INDEX(objp), radius, 1, NULL, warp_time, shipp->ship_info_index, NULL, 0, 0, sip->warpout_snd_start, sip->warpout_snd_end);
3610  }
3611  else if (direction == WD_WARP_IN)
3612  {
3613  // maybe special warpin
3614  int fireball_type = ((portal_objp != NULL) || (sip->warpin_type == WT_KNOSSOS)) ? FIREBALL_KNOSSOS : FIREBALL_WARP;
3615 
3616  // create fireball
3617  warp_objnum = fireball_create(&pos, fireball_type, FIREBALL_WARP_EFFECT, OBJ_INDEX(objp), radius, 0, NULL, warp_time, shipp->ship_info_index, NULL, 0, 0, sip->warpin_snd_start, sip->warpin_snd_end);
3618  }
3619  else
3620  {
3621  Int3();
3622  }
3623 
3624  //WMC - bail
3625  // JAS: This must always be created, if not, just warp the ship in/out
3626  if (warp_objnum < 0)
3627  {
3628  this->warpEnd();
3629  return 0;
3630  }
3631 
3632  fvec = Objects[warp_objnum].orient.vec.fvec;
3633 
3634  stage_time_start = total_time_start = timestamp();
3635  if(direction == WD_WARP_IN)
3636  {
3637  stage_duration[1] = fl2i(SHIPFX_WARP_DELAY*1000.0f);
3638  stage_duration[2] = fl2i(shipfx_calculate_warp_time(objp, WD_WARP_IN)*1000.0f);
3639  stage_time_end = timestamp(stage_duration[1]);
3640  total_time_end = stage_duration[1] + stage_duration[2];
3641  shipp->flags |= SF_ARRIVING_STAGE_1;
3642  }
3643  else if(direction == WD_WARP_OUT)
3644  {
3645  shipp->flags |= SF_DEPART_WARP;
3646 
3647  // Make the warp effect stage 1 last SHIP_WARP_TIME1 seconds.
3648  if ( objp == Player_obj ) {
3649  stage_duration[1] = fl2i(fireball_lifeleft(&Objects[warp_objnum])*1000.0f);
3650  total_time_end = timestamp(fl2i(warp_time*1000.0f));
3651  } else {
3652  total_time_end = timestamp(fl2i(warp_time*2.0f*1000.0f));
3653  }
3654 
3655  // 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;
3656 
3657  // Set ship's velocity to 'speed'
3658  // This should actually be an AI that flies from the current
3659  // position through 'shipp->warp_effect_pos' in 'warp_time'
3660  // and keeps going
3661  if ( objp != Player_obj || Player_use_ai ) {
3662  vec3d vel;
3663  vel = objp->orient.vec.fvec;
3664  vm_vec_scale( &vel, warpout_speed );
3665  objp->phys_info.vel = vel;
3666  objp->phys_info.desired_vel = vel;
3667  objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
3668  objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
3669  objp->phys_info.prev_ramp_vel.xyz.z = warpout_speed;
3670  objp->phys_info.forward_thrust = 1.0f; // How much the forward thruster is applied. 0-1.
3671  }
3672  }
3673 
3674  return 1;
3675 }
3676 
3677 int WE_Default::warpFrame(float frametime)
3678 {
3679  if(direction == WD_WARP_IN)
3680  {
3681  if ((shipp->flags & SF_ARRIVING_STAGE_1) && timestamp_elapsed(stage_time_end))
3682  {
3683  // let physics know the ship is going to warp in.
3684  objp->phys_info.flags |= PF_WARP_IN;
3685 
3686  // done doing stage 1 of warp, so go on to stage 2
3687  shipp->flags &= (~SF_ARRIVING_STAGE_1);
3688  shipp->flags |= SF_ARRIVING_STAGE_2;
3689 
3690  float warp_time = shipfx_calculate_warp_time(objp, WD_WARP_IN);
3691  float speed = shipfx_calculate_warp_dist(objp) / warp_time; // How long it takes to move through warp effect
3692 
3693  // Make ship move at velocity so that it moves two radii in warp_time seconds.
3694  vec3d vel;
3695  vel = objp->orient.vec.fvec;
3696  vm_vec_scale( &vel, speed );
3697  objp->phys_info.vel = vel;
3698  objp->phys_info.desired_vel = vel;
3699  objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
3700  objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
3701  objp->phys_info.prev_ramp_vel.xyz.z = speed;
3702  objp->phys_info.forward_thrust = 0.0f; // How much the forward thruster is applied. 0-1.
3703 
3704  stage_time_end = timestamp(fl2i(warp_time*1000.0f));
3705  }
3706  else if ( (shipp->flags & SF_ARRIVING_STAGE_2) && timestamp_elapsed(stage_time_end) )
3707  {
3708  // done doing stage 2 of warp, so turn off arriving flag
3709  this->warpEnd();
3710 
3711  // notify physics to slow down
3712  if (sip->flags & SIF_SUPERCAP) {
3713  // let physics know this is a special warp in
3715  }
3716  }
3717  }
3718  else if(direction == WD_WARP_OUT)
3719  {
3720  vec3d tempv;
3721  float warp_pos; // position of warp effect in object's frame of reference
3722 
3723  vm_vec_sub( &tempv, &objp->pos, &pos );
3724  warp_pos = -vm_vec_dot( &tempv, &fvec );
3725 
3726 
3727  // Find the closest point on line from center of wormhole
3728  vec3d cpos;
3729 
3730  fvi_ray_plane(&cpos, &objp->pos, &fvec, &pos, &fvec, 0.0f );
3731 
3732  if ( objp == Player_obj ) {
3733  // Code for player warpout frame
3734 
3735  if ( (Player->control_mode==PCM_WARPOUT_STAGE2) && (warp_pos > objp->radius) ) {
3737  }
3738 
3739  if ( timestamp_elapsed(total_time_end) ) {
3740 
3741  // Something went wrong... oh well, warp him out anyway.
3743  mprintf(( "Hmmm... player ship warpout time elapsed, but he wasn't in warp stage 3.\n" ));
3744  }
3745 
3746  this->warpEnd();
3747  }
3748 
3749  } else {
3750  // Code for all non-player ships warpout frame
3751 
3752  int timed_out = timestamp_elapsed(total_time_end);
3753  if ( timed_out ) {
3754  int delta_ms = timestamp_until(total_time_end);
3755  if (delta_ms > 1000.0f * frametime ) {
3756  nprintf(("AI", "Frame %i: Ship %s missed departue cue by %7.3f seconds.\n", Framecount, shipp->ship_name, - (float) delta_ms/1000.0f));
3757  }
3758  }
3759 
3760  // MWA 10/21/97 -- added shipp->flags & SF_NO_DEPARTURE_WARP part of next if statement. For ships
3761  // that don't get a wormhole effect, I wanted to drop into this code immediately.
3762  if ( (warp_pos > objp->radius) || (shipp->flags & SF_NO_DEPARTURE_WARP) || timed_out ) {
3763  this->warpEnd();
3764  }
3765  }
3766  }
3767 
3768  return 1;
3769 }
3770 
3772 {
3773  if(!this->isValid())
3774  return 0;
3775 
3776  g3_start_user_clip_plane( &pos, &fvec );
3777  return 1;
3778 }
3779 
3781 {
3782  if(!this->isValid())
3783  return 0;
3784 
3785  render_info->set_clip_plane(pos, fvec);
3786 
3787  return 1;
3788 }
3789 
3791 {
3792  return 1;
3793 }
3794 
3796 {
3797  if(!this->isValid())
3798  return 0;
3799 
3800  *output = pos;
3801  return 1;
3802 }
3803 
3805 {
3806  if (!this->isValid())
3807  {
3808  return 0;
3809  }
3810 
3811  vm_vector_2_matrix(output, &objp->orient.vec.fvec, NULL, NULL);
3812  return 1;
3813 }
3814 
3815 //********************-----CLASS: WE_BSG-----********************//
3816 WE_BSG::WE_BSG(object *n_objp, int n_direction)
3817  :WarpEffect(n_objp, n_direction)
3818 {
3819  //Zero animation and such
3820  anim = shockwave = -1;
3821  anim_fps = shockwave_fps = 0;
3822  anim_nframes = shockwave_nframes = 0;
3823  anim_total_time = shockwave_total_time = 0;
3824 
3825  //Setup anim name
3826  char tmp_name[MAX_FILENAME_LEN];
3827  memset(tmp_name, 0, MAX_FILENAME_LEN);
3828  if(direction == WD_WARP_IN)
3829  strcpy_s( tmp_name, sip->warpin_anim );
3830  else if(direction == WD_WARP_OUT)
3831  strcpy_s( tmp_name, sip->warpout_anim );
3832  strlwr(tmp_name);
3833 
3834  if(strlen(tmp_name))
3835  {
3836  //Load anim
3837  anim = bm_load_either(tmp_name, &anim_nframes, &anim_fps, NULL, 1);
3838  if(anim > -1)
3839  {
3840  anim_total_time = fl2i(((float)anim_nframes / (float)anim_fps) * 1000.0f);
3841  }
3842 
3843  //Setup shockwave name
3844  strncat(tmp_name, "-shockwave", MAX_FILENAME_LEN-1);
3845 
3846  //Load shockwave
3847  shockwave = bm_load_either(tmp_name, &shockwave_nframes, &shockwave_fps, NULL, 1);
3848  if(shockwave > -1)
3849  {
3850  shockwave_total_time = fl2i(((float)shockwave_nframes / (float)shockwave_fps) * 1000.0f);
3851  }
3852  }
3853  //Set radius
3854  tube_radius = 0.0f;
3855  shockwave_radius = 0.0f;
3856 
3857  //Use the warp radius for shockwave radius, not tube radius
3858  if(direction == WD_WARP_IN)
3859  shockwave_radius = sip->warpin_radius;
3860  else
3861  shockwave_radius = sip->warpout_radius;
3862 
3863  polymodel *pm = model_get(sip->model_num);
3864  if(pm == NULL)
3865  {
3866  autocenter = vmd_zero_vector;
3867  z_offset_max = objp->radius;
3868  z_offset_min = -objp->radius;
3869 
3870  if(tube_radius <= 0.0f)
3871  tube_radius = objp->radius;
3872 
3873  if(shockwave_radius <= 0.0f)
3874  shockwave_radius = objp->radius;
3875  }
3876  else
3877  {
3878  //Autogenerate everything from ship dimensions
3879  if(tube_radius <= 0.0f)
3880  tube_radius = MAX((pm->maxs.xyz.y - pm->mins.xyz.y), (pm->maxs.xyz.x - pm->mins.xyz.x))/2.0f;
3881  autocenter = pm->autocenter;
3882  z_offset_max = pm->maxs.xyz.z - pm->autocenter.xyz.z;
3883  z_offset_min = pm->mins.xyz.z - pm->autocenter.xyz.z;
3884 
3885  if (shockwave_radius <= 0.0f)
3886  shockwave_radius = z_offset_max - z_offset_min;
3887  }
3888 
3889  //*****Timing
3890  stage = -1;
3891  if(direction == WD_WARP_IN)
3892  {
3893  stage_duration[0] = sip->warpin_time;
3894  stage_duration[1] = MAX(anim_total_time - sip->warpin_time, shockwave_total_time);
3895  }
3896  else
3897  {
3898  stage_duration[0] = sip->warpout_time;
3899  stage_duration[1] = MAX(anim_total_time - sip->warpout_time, shockwave_total_time);
3900  }
3901  stage_time_start = stage_time_end = total_time_start = total_time_end = timestamp();
3902 
3903  //*****Graphics
3904  batcher.allocate(1);
3905 
3906  //*****Sound
3907  snd_range_factor = 1.0f;
3908  snd_start = snd_end = -1;
3909  snd_start_gs = snd_end_gs = NULL;
3910 
3911  //*****Instance
3912  pos = vmd_zero_vector;
3913 }
3914 
3916 {
3917  if(anim > -1)
3918  bm_unload(anim);
3919  if(shockwave > -1)
3921 }
3922 
3924 {
3925  if(anim > -1)
3927  if(shockwave > -1)
3929 }
3930 
3932 {
3933  if(!WarpEffect::warpStart())
3934  return 0;
3935 
3936  //WMC - bail
3937  if (anim < 0 && shockwave < 0)
3938  {
3939  this->warpEnd();
3940  return 0;
3941  }
3942 
3943  //WMC - If object is docked now, update data:
3944  if(object_is_docked(objp))
3945  {
3947  z_offset_min = -z_offset_max;
3948  if(tube_radius <= 0.0f)
3949  {
3952  tube_radius = MAX(x_radius, y_radius);
3953  }
3954 
3955  shockwave_radius = z_offset_max - z_offset_min;
3956 
3957  vec3d dock_center;
3958  dock_calc_docked_center(&dock_center, objp);
3959  vm_vec_sub(&autocenter, &dock_center, &objp->pos);
3960  }
3961 
3962  if(direction == WD_WARP_IN)
3963  shipp->flags |= SF_ARRIVING_STAGE_1;
3964  else
3965  shipp->flags |= SF_DEPART_WARP;
3966 
3967  //*****Sound
3968  int gs_start_index = -1;
3969  int gs_end_index = -1;
3970  if(direction == WD_WARP_IN)
3971  {
3972  shipp->flags |= SF_ARRIVING_STAGE_1;
3973  gs_start_index = sip->warpin_snd_start;
3974  gs_end_index = sip->warpin_snd_end;
3975  }
3976  else if(direction == WD_WARP_OUT)
3977  {
3978  shipp->flags |= SF_DEPART_WARP;
3979  gs_start_index = sip->warpout_snd_start;
3980  gs_end_index = sip->warpout_snd_end;
3981  }
3982  else
3983  {
3984  this->warpEnd();
3985  return 0;
3986  }
3987 
3988  if(gs_start_index > -1)
3989  {
3990  snd_start_gs = &Snds[gs_start_index];
3991  snd_start = snd_play_3d(snd_start_gs, &objp->pos, &View_position, 0.0f, NULL, 0, 1, SND_PRIORITY_SINGLE_INSTANCE, NULL, snd_range_factor);
3992  }
3993  if(gs_end_index > -1)
3994  {
3995  snd_end_gs = &Snds[gs_end_index];
3996  snd_end = -1;
3997  }
3998 
3999  stage = 0;
4000  int total_duration = 0;
4001  for(int i = 0; i < WE_BSG_NUM_STAGES; i++)
4002  total_duration += stage_duration[i];
4003 
4004  total_time_start = timestamp();
4005  total_time_end = timestamp(total_duration);
4006 
4007  stage_time_start = total_time_start;
4008  stage_time_end = timestamp(stage_duration[stage]);
4009 
4010  return 1;
4011 }
4012 
4013 int WE_BSG::warpFrame(float frametime)
4014 {
4015  if(!this->isValid())
4016  return 0;
4017 
4018  while( timestamp_elapsed(stage_time_end ))
4019  {
4020  stage++;
4021  if(stage < WE_BSG_NUM_STAGES)
4022  {
4023  stage_time_start = timestamp();
4024  stage_time_end = timestamp(stage_duration[stage]);
4025  }
4026  switch(stage)
4027  {
4028  case 1:
4029  if(direction == WD_WARP_IN)
4030  {
4031  shipp->flags &= (~SF_ARRIVING_STAGE_1);
4032  shipp->flags |= SF_ARRIVING_STAGE_2;
4033  }
4034  break;
4035  default:
4036  this->warpEnd();
4037  return 0;
4038  }
4039  }
4040 
4041  switch(stage)
4042  {
4043  case 0:
4044  case 1:
4045  vm_vec_unrotate(&pos, &autocenter, &objp->orient);
4046  vm_vec_add2(&pos, &objp->pos);
4047  break;
4048  default:
4049  this->warpEnd();
4050  return 0;
4051  }
4052 
4053  if(snd_start > -1)
4054  snd_update_3d_pos(snd_start, snd_start_gs, &objp->pos, 0.0f, snd_range_factor);
4055 
4056  return 1;
4057 }
4058 
4060 {
4061  if(!this->isValid())
4062  return 0;
4063 
4064  if(direction == WD_WARP_OUT && stage > 0)
4065  {
4066  vec3d position;
4067  vm_vec_scale_add(&position, &objp->pos, &objp->orient.vec.fvec, objp->radius);
4068  g3_start_user_clip_plane( &position, &objp->orient.vec.fvec );
4069  }
4070  return 1;
4071 }
4072 
4074 {
4075  if(!this->isValid())
4076  return 0;
4077 
4078  if(direction == WD_WARP_OUT && stage > 0)
4079  {
4080  vec3d position;
4081  vm_vec_scale_add(&position, &objp->pos, &objp->orient.vec.fvec, objp->radius);
4082 
4083  render_info->set_clip_plane(position, objp->orient.vec.fvec);
4084  }
4085 
4086  return 1;
4087 }
4088 
4090 {
4091  if(!this->isValid())
4092  return 0;
4093 
4094  if(anim < 0 && shockwave < 0)
4095  return 0;
4096 
4097  // SUSHI: Turning off Zbuffering results in the FTL effect showing up through ship hulls.
4098  // The effect is slightly degraded by leaving it on, but ATM it's worth the tradeoff.
4099  // turn off zbuffering
4100  //int saved_zbuffer_mode = gr_zbuffer_get();
4101  //gr_zbuffer_set(GR_ZBUFF_NONE);
4102 
4103  if(anim > -1)
4104  {
4105  //Figure out which frame we're on
4106  int anim_frame = fl2i( ((float)(timestamp() - total_time_start)/1000.0f) * (float)anim_fps);
4107 
4108  if ( anim_frame < anim_nframes )
4109  {
4110  //Set the correct frame
4111  //gr_set_bitmap(anim + anim_frame, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f);
4112 
4113  //Do warpout geometry
4114  vec3d start, end;
4115  vm_vec_scale_add(&start, &pos, &objp->orient.vec.fvec, z_offset_min);
4116  vm_vec_scale_add(&end, &pos, &objp->orient.vec.fvec, z_offset_max);
4117 
4118  //Render the warpout effect
4119  batch_add_beam(anim + anim_frame, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT | TMAP_HTL_3D_UNLIT, &start, &end, tube_radius*2.0f, 1.0f);
4120  }
4121  }
4122 
4123  if(stage == 1 && shockwave > -1)
4124  {
4125  int shockwave_frame = fl2i( ((float)(timestamp() - stage_time_start)/1000.0f) * (float)shockwave_fps);
4126 
4127  if(shockwave_frame < shockwave_nframes)
4128  {
4129  vertex p;
4130  extern int Cmdline_nohtl;
4131 
4132  memset(&p, 0, sizeof(p));
4133 
4134  if(Cmdline_nohtl) {
4135  g3_rotate_vertex(&p, &pos );
4136  }else{
4137  g3_transfer_vertex(&p, &pos);
4138  }
4139 
4140  batch_add_bitmap(shockwave + shockwave_frame, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD, &p, 0, shockwave_radius, 1.0f);
4141  }
4142  }
4143 
4144  // restore zbuffer mode
4145  //gr_zbuffer_set(saved_zbuffer_mode);
4146  return 1;
4147 }
4148 
4150 {
4151  if(snd_start > -1)
4152  snd_stop(snd_start);
4153  if(snd_end_gs != NULL)
4154  snd_end = snd_play_3d(snd_end_gs, &objp->pos, &View_position, 0.0f, NULL, 0, 1.0f, SND_PRIORITY_SINGLE_INSTANCE, NULL, snd_range_factor);
4155 
4156  return WarpEffect::warpEnd();
4157 }
4158 
4159 //WMC - These two functions are used to fool collision detection code
4160 //And do player warpout
4162 {
4163  if(!this->isValid())
4164  return 0;
4165 
4166  vec3d position;
4167  vm_vec_scale_add(&position, &objp->pos, &objp->orient.vec.fvec, objp->radius);
4168 
4169  *output = position;
4170  return 1;
4171 }
4172 
4174 {
4175  if (!this->isValid())
4176  {
4177  return 0;
4178  }
4179 
4180  vm_vector_2_matrix(output, &objp->orient.vec.fvec, NULL, NULL);
4181  return 1;
4182 }
4183 
4184 //********************-----CLASS: WE_Homeworld-----********************//
4185 WE_Homeworld::WE_Homeworld(object *n_objp, int n_direction)
4186  :WarpEffect(n_objp, n_direction)
4187 {
4188  if(!this->isValid())
4189  return;
4190 
4191  //Stage and time
4192  stage = 0;
4193  stage_time_start = stage_time_end = timestamp();
4194 
4195  //Stage duration presets
4196  stage_duration[0] = 0;
4197  stage_duration[1] = 1000;
4198  stage_duration[2] = 0;
4199  stage_duration[3] = -1;
4200  stage_duration[4] = 0;
4201  stage_duration[5] = 1000;
4202 
4203  //Configure stage duration 3
4204  if(direction == WD_WARP_IN)
4205  stage_duration[3] = sip->warpin_time - (stage_duration[1] + stage_duration[2] + stage_duration[4] + stage_duration[5]);
4206  else if(direction == WD_WARP_OUT)
4207  stage_duration[3] = sip->warpout_time - (stage_duration[1] + stage_duration[2] + stage_duration[4] + stage_duration[5]);
4208  if(stage_duration[3] <= 0)
4209  stage_duration[3] = 3000;
4210 
4211  //Anim
4212  if(direction == WD_WARP_IN)
4213  anim = bm_load_either(sip->warpin_anim, &anim_nframes, &anim_fps, NULL, 1);
4214  else if(direction == WD_WARP_OUT)
4215  anim = bm_load_either(sip->warpout_anim, &anim_nframes, &anim_fps, NULL, 1);
4216  else
4217  anim = -1;
4218 
4219  pos = vmd_zero_vector;
4220  fvec = vmd_zero_vector;
4221 
4222  polymodel *pm = model_get(sip->model_num);
4223  width_full = sip->warpin_radius;
4224  if(pm != NULL)
4225  {
4226  if(width_full <= 0.0f)
4227  {
4228  width_full = pm->maxs.xyz.x - pm->mins.xyz.x;
4229  height_full = pm->maxs.xyz.y - pm->mins.xyz.y;
4230  z_offset_max = pm->maxs.xyz.z;
4231  z_offset_min = pm->mins.xyz.z;
4232  }
4233  }
4234  else
4235  {
4236  if(width_full <= 0.0f)
4237  {
4238  width_full = 2.0f*objp->radius;
4239  }
4240  height_full = width_full;
4241  z_offset_max = objp->radius;
4242  z_offset_min = -objp->radius;
4243  }
4244  //WMC - This scales up or down the sound depending on ship size, with ~100m diameter ship as base
4245  //REMEMBER: Radius != diameter
4246  snd_range_factor = sqrt(width_full*width_full+height_full*height_full)/141.421356f;
4247 
4248  if(width_full <= 0.0f)
4249  width_full = 1.0f;
4250  if(height_full <= 0.0f)
4251  height_full = 1.0f;
4252  width = width_full;
4253  height = 0.0f;
4254 
4255  //Sound
4256  snd = -1;
4257  snd_gs = NULL;
4258 }
4259 
4261 {
4262  if(anim > -1)
4263  bm_unload(anim);
4264 }
4265 
4267 {
4268  if(!this->isValid())
4269  return 0;
4270 
4271  if(anim < 0)
4272  {
4273  this->warpEnd();
4274  return 0;
4275  }
4276 
4277  stage = 1;
4278  total_time_start = timestamp();
4279  total_time_end = 0;
4280  for(int i = 0; i < WE_HOMEWORLD_NUM_STAGES; i++)
4281  {
4282  total_time_end += stage_duration[i];
4283  }
4284  stage_time_start = total_time_start;
4285  stage_time_end = timestamp(stage_duration[stage]);
4286 
4287  //Position
4288  vm_vec_scale_add(&pos, &objp->pos, &objp->orient.vec.fvec, z_offset_max);
4289  fvec = objp->orient.vec.fvec;
4290  if(direction == WD_WARP_OUT)
4291  vm_vec_negate(&fvec);
4292 
4293  width = width_full;
4294  height = 0.0f;
4295 
4296  int gs_index = -1;
4297  if(direction == WD_WARP_IN)
4298  {
4299  shipp->flags |= SF_ARRIVING_STAGE_1;
4300  gs_index = sip->warpin_snd_start;
4301  }
4302  else if(direction == WD_WARP_OUT)
4303  {
4304  shipp->flags |= SF_DEPART_WARP;
4305  gs_index = sip->warpout_snd_start;
4306  }
4307  else
4308  {
4309  this->warpEnd();
4310  return 0;
4311  }
4312 
4313  if(gs_index > -1)
4314  {
4315  snd_gs = &Snds[gs_index];
4316  snd = snd_play_3d(snd_gs, &pos, &View_position, 0.0f, NULL, 0, 1, SND_PRIORITY_SINGLE_INSTANCE, NULL, snd_range_factor);
4317  }
4318 
4319  return 1;
4320 }
4321 
4322 int WE_Homeworld::warpFrame(float frametime)
4323 {
4324  if(!this->isValid())
4325  return 0;
4326 
4327  //Setup stage
4328  while( timestamp_elapsed(stage_time_end ))
4329  {
4330  stage++;
4331  if(stage < WE_HOMEWORLD_NUM_STAGES)
4332  {
4333  stage_time_start = timestamp();
4334  stage_time_end = timestamp(stage_duration[stage]);
4335  }
4336  switch(stage)
4337  {
4338  case 2:
4339  break;
4340  case 3:
4341  if(direction == WD_WARP_IN)
4342  {
4343  objp->phys_info.flags |= PF_WARP_IN;
4344  shipp->flags &= (~SF_ARRIVING_STAGE_1);
4345  shipp->flags |= SF_ARRIVING_STAGE_2;
4346  }
4347 
4348  break;
4349  case 4:
4350  break;
4351  case 5:
4352  break;
4353  default:
4354  this->warpEnd();
4355  return 0;
4356  }
4357  }
4358 
4359  //Process stage
4360  float progress = ((float)timestamp() - (float)stage_time_start)/((float)stage_time_end - (float)stage_time_start);
4361  switch(stage)
4362  {
4363  case 1:
4364  height = height_full * progress;
4365  break;
4366  case 2:
4367  break;
4368  case 3:
4369  vm_vec_scale_add(&pos, &objp->pos, &objp->orient.vec.fvec, z_offset_max - progress*(z_offset_max-z_offset_min));
4370  break;
4371  case 4:
4372  break;
4373  case 5:
4374  height = height_full * (1.0f-progress);
4375  break;
4376  default:
4377  this->warpEnd();
4378  return 0;
4379  }
4380 
4381  //Update sound
4382  if(snd > -1)
4383  snd_update_3d_pos(snd, snd_gs, &pos, 0.0f, snd_range_factor);
4384 
4385  return 1;
4386 }
4387 
4389 {
4390  if(!this->isValid())
4391  return 0;
4392 
4393  g3_start_user_clip_plane( &pos, &fvec );
4394  return 1;
4395 }
4396 
4398 {
4399  if(!this->isValid())
4400  return 0;
4401 
4402  render_info->set_clip_plane(pos, fvec);
4403  return 1;
4404 }
4405 
4407 {
4408  if(!this->isValid())
4409  return 0;
4410 
4411  int frame = 0;
4412  if(anim_fps > 0)
4413  frame = fl2i( (int)(((float)(timestamp() - (float)total_time_start)/1000.0f) * (float)anim_fps) % anim_nframes);
4414 
4415  //Set the correct frame
4416 // gr_set_bitmap(anim + frame, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f);
4417 // g3_draw_polygon(&pos, &objp->orient, width, height, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT);
4419 
4420  return 1;
4421 }
4422 
4424 {
4425  if(snd > -1)
4426  snd_stop(snd);
4427  return WarpEffect::warpEnd();
4428 }
4429 
4431 {
4432  if(!this->isValid())
4433  return 0;
4434 
4435  *output = pos;
4436  return 1;
4437 }
4438 
4440 {
4441  if (!this->isValid())
4442  {
4443  return 0;
4444  }
4445 
4446  vm_vector_2_matrix(output, &fvec, NULL, NULL);
4447  return 1;
4448 }
4449 
4450 //********************-----CLASS: WE_Hyperspace----********************//
4451 WE_Hyperspace::WE_Hyperspace(object *n_objp, int n_direction)
4452  :WarpEffect(n_objp, n_direction)
4453 {
4454  total_duration = 0;
4455  if(direction == WD_WARP_IN)
4456  {
4457  total_duration = sip->warpin_time;
4458  decel_exp = sip->warpin_decel_exp;
4459  }
4460  else if(direction == WD_WARP_OUT)
4461  {
4462  total_duration = sip->warpout_time;
4463  accel_exp = sip->warpout_accel_exp;
4464  }
4465  if(total_duration <= 0)
4466  total_duration = 1000;
4467 
4468  total_time_start = total_time_end = timestamp();
4469  pos_final = vmd_zero_vector;
4470  scale_factor = 750.0f * objp->radius;
4471 
4472  initial_velocity = 1.0f;
4474  if (p_objp != NULL)
4475  initial_velocity = (float) p_objp->initial_velocity * sip->max_speed / 100.0f;
4476 }
4477 
4479 {
4480  if(!this->isValid())
4481  return 0;
4482 
4483  total_time_start = timestamp();
4484  total_time_end = timestamp(total_duration);
4485 
4486  if(direction == WD_WARP_IN)
4487  {
4488  shipp->flags |= SF_ARRIVING_STAGE_1;
4489  objp->phys_info.flags |= PF_WARP_IN;
4490  objp->phys_info.vel.xyz.z = (scale_factor / sip->warpin_time)*1000.0f;
4491  objp->flags &= ~OF_PHYSICS;
4492  }
4493  else if(direction == WD_WARP_OUT)
4494  {
4495  shipp->flags |= SF_DEPART_WARP;
4496  }
4497  else
4498  {
4499  this->warpEnd();
4500  }
4501 
4502  pos_final = objp->pos;
4503 
4504  return 1;
4505 }
4506 
4507 int WE_Hyperspace::warpFrame(float frametime)
4508 {
4509  if(!this->isValid())
4510  return 0;
4511 
4512  if(timestamp_elapsed(total_time_end))
4513  {
4514  objp->pos = pos_final;
4515  if(direction == WD_WARP_OUT)
4516  objp->phys_info.vel.xyz.z = 0.0f;
4517  else
4518  {
4519  vec3d vel;
4520  vel = objp->orient.vec.fvec;
4521  vm_vec_scale( &vel, initial_velocity );
4522  objp->phys_info.vel = vel;
4523  objp->phys_info.desired_vel = vel;
4524  shipp->flags |= SF_ARRIVING_STAGE_2;
4525  }
4526  objp->flags |= OF_PHYSICS;
4527  this->warpEnd();
4528  }
4529  else
4530  {
4531  // How far along in the effect we are, in range of 0.0..1.0.
4532  float progress = ((float)timestamp() - (float)total_time_start)/(float)total_duration;
4533  float scale = 0.0f;
4534  if(direction == WD_WARP_IN)
4535  {
4536  scale = scale_factor*(1.0f-pow((1.0f-progress), decel_exp))-scale_factor;
4537 
4538  // Makes sure that the velocity won't drop below the ship's initial
4539  // velocity during the warpin. Ideally it should be done more
4540  // smoothly than this.
4541  scale = MIN(scale, (initial_velocity * (total_duration / 1000) * -(1.0f - progress)));
4542  }
4543  else
4544  {
4545  scale = scale_factor*pow(progress, accel_exp);
4546 
4547  // Makes sure the warpout velocity won't drop below the ship's last
4548  // known real velocity.
4549  scale += initial_velocity * (total_duration / 1000) * progress;
4550  }
4551  vm_vec_scale_add(&objp->pos, &pos_final, &objp->orient.vec.fvec, scale);
4552  }
4553  return 1;
4554 }
void mc_info_init(mc_info *mc)
Definition: model.h:1138
vec3d max
Definition: model.h:343
#define PF_WARP_IN
Definition: physics.h:27
float debris_max_rotspeed
Definition: ship.h:1262
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
int model_collide(mc_info *mc_info_obj)
int Framecount
Definition: systemvars.cpp:22
int muzzle_flash
Definition: weapon.h:463
#define MOVEMENT_TYPE_ROT
Definition: model.h:34
bsp_info * submodel
Definition: model.h:764
int timestamp(int delta_ms)
Definition: timer.cpp:226
virtual int warpStart()
Definition: shipfx.cpp:4478
struct split_ship split_ship
ubyte tmap_num
Definition: model.h:279
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
float p
Definition: pstypes.h:111
model_subsystem * system_info
Definition: ship.h:314
0.75 second spark sound effect
Definition: gamesnd.h:213
virtual void pageIn()
Definition: shipfx.cpp:3466
#define SIF2_SHOW_SHIP_MODEL
Definition: ship.h:921
#define SF_DEPART_WARP
Definition: ship.h:449
#define MIN(a, b)
Definition: pstypes.h:296
vec3d * pos
Definition: model.h:1113
matrix orient
Definition: shipfx.cpp:1643
#define TMAP_FLAG_SOFT_QUAD
Definition: tmapper.h:79
float warpout_speed
Definition: ship.h:1220
void set_ship_submodel_as_blown_off(ship *shipp, char *name)
Definition: shipfx.cpp:215
int Cmdline_old_collision_sys
Definition: cmdline.cpp:489
void model_find_world_point(vec3d *outpnt, vec3d *mpnt, int model_num, int submodel_num, const matrix *objorient, const vec3d *objpos)
Definition: modelread.cpp:4099
int warpout_type
Definition: ship.h:1223
#define NUM_SUB_EXPL_HANDLES
Definition: ship.h:518
vec3d min
Definition: model.h:342
float debris_max_speed
Definition: ship.h:1260
float vm_vec_mag_quick(const vec3d *v)
Definition: vecmat.cpp:371
matrix * vm_matrix_x_matrix(matrix *dest, const matrix *src0, const matrix *src1)
Definition: vecmat.cpp:1006
#define PF_WARP_OUT
Definition: physics.h:29
int live_debris[MAX_LIVE_DEBRIS]
Definition: model.h:353
#define FLASH_LIFE_SECONDARY
Definition: shipfx.cpp:1198
#define MR_NORMAL
Definition: model.h:858
vec3d vmd_z_vector
Definition: vecmat.cpp:27
vec3d View_position
Definition: 3dsetup.cpp:20
int objnum
Definition: ship.h:537
int warpin_type
Definition: ship.h:1213
int Game_mode
Definition: systemvars.cpp:24
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
vec3d rotvel
Definition: physics.h:78
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
vec3d mins
Definition: model.h:746
int getWarpPosition(vec3d *output)
Definition: shipfx.cpp:3795
int wash_timestamp
Definition: ship.h:666
void shipfx_flash_do_frame(float frametime)
Definition: shipfx.cpp:1331
int warpin_snd_start
Definition: ship.h:1208
float frand_range(float min, float max)
Return a floating point number in the range min..max.
Definition: floating.cpp:50
#define FLASH_LIFE_PRIMARY
Definition: shipfx.cpp:1197
polymodel * model_get(int model_num)
Definition: modelread.cpp:3134
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
#define SIF_SUPERCAP
Definition: ship.h:901
0.50 second spark sound effect
Definition: gamesnd.h:212
int batch_add_beam(int texture, int tmap_flags, vec3d *start, vec3d *end, float width, float intensity)
Definition: grbatch.cpp:968
#define w(p)
Definition: shipfx.cpp:973
DCF(particle_num,"Sets multiplier for the number of particles created")
Definition: shipfx.cpp:1377
void RemHookVar(char *name)
Definition: scripting.cpp:749
SCP_vector< game_snd > Snds
Definition: gamesnd.cpp:19
int debris_objects[MAX_DEBRIS_OBJECTS]
Definition: model.h:742
void g3_stop_user_clip_plane()
Definition: 3dsetup.cpp:408
float vm_vec_mag(const vec3d *v)
Definition: vecmat.cpp:325
submodel_instance_info submodel_info_1
Definition: ship.h:368
#define FIREBALL_LARGE_EXPLOSION
Definition: fireballs.h:24
float warpout_player_speed
Definition: ship.h:1225
virtual int warpShipRender()
Definition: shipfx.cpp:4406
#define MULTI_SIG_DEBRIS
Definition: multiutil.h:35
vec3d desired_vel
Definition: physics.h:70
GLuint index
Definition: Glext.h:5608
#define PLAYER_WARPOUT_SPEED
Definition: player.h:279
asteroid Asteroids[MAX_ASTEROIDS]
Definition: asteroid.cpp:63
float ship_get_warpout_speed(object *objp)
Definition: ship.cpp:17306
virtual int warpShipRender()
Definition: shipfx.cpp:4089
ushort multi_get_next_network_signature(int what_kind)
Definition: multiutil.cpp:168
WE_Default(object *n_objp, int n_direction)
Definition: shipfx.cpp:3544
physics_info phys_info
Definition: object.h:157
int id
Definition: model.h:732
WE_BSG(object *n_objp, int n_direction)
Definition: shipfx.cpp:3816
float value
Definition: model.h:579
int model_num
Definition: ship.h:1189
void shipfx_do_shockwave_stuff(ship *shipp, shockwave_create_info *sci)
Definition: shipfx.cpp:2776
int warpShipClip()
Definition: shipfx.cpp:3771
void shipfx_blow_up_hull(object *obj, int model, vec3d *exp_center)
Definition: shipfx.cpp:325
void shipfx_warpin_frame(object *objp, float frametime)
Definition: shipfx.cpp:605
ubyte g3_transfer_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:84
int class_type
Definition: ship.h:1167
#define SF_DOCK_LEADER
Definition: ship.h:455
void mflash_create(vec3d *gun_pos, vec3d *gun_dir, physics_info *pip, int mflash_type, object *local)
#define OF_WAS_RENDERED
Definition: object.h:113
int model_instance_num
Definition: ship.h:802
Assert(pm!=NULL)
#define fl_tan(fl)
Definition: floating.h:39
#define MAX_MODEL_TEXTURES
Definition: globals.h:78
virtual int warpStart()
Definition: shipfx.cpp:3473
Definition: pstypes.h:88
float Bs_exp_fire_time_mult
Definition: shipfx.cpp:1616
#define mprintf(args)
Definition: pstypes.h:238
int shockwave_count
Definition: ship.h:1246
ushort net_signature
Definition: object.h:163
#define SSF_NO_LIVE_DEBRIS
Definition: ship.h:289
#define SF_ARRIVING_STAGE_2
Definition: ship.h:452
float side_slip_time_const
Definition: physics.h:44
virtual int getWarpOrientation(matrix *output)
Definition: shipfx.cpp:3534
const float MAX_ARC_LENGTH_PERCENTAGE
Definition: shipfx.cpp:2413
int subsystem_stricmp(const char *str1, const char *str2)
Definition: parselo.cpp:3648
hull_check p0
Definition: lua.cpp:5051
float warpout_radius
Definition: ship.h:1216
int shockwave_create(int parent_objnum, vec3d *pos, shockwave_create_info *sci, int flag, int delay)
Definition: shockwave.cpp:64
matrix Eye_matrix
Definition: 3dsetup.cpp:26
const float MAX_EMP_ARC_TIMESTAMP
Definition: shipfx.cpp:2415
int objnum
Definition: shipfx.cpp:1202
void obj_snd_delete_type(int objnum, int sndnum, ship_subsys *ss)
Definition: objectsnd.cpp:812
void set_flags(uint flags)
#define MARC_TYPE_NORMAL
Definition: model.h:252
#define MR_NO_LIGHTING
Definition: model.h:867
int light_get_global_dir(vec3d *pos, int n)
Definition: lighting.cpp:621
struct vec3d::@225::@227 xyz
shockwave ignition
Definition: gamesnd.h:170
int next_hit_spark
Definition: ship.h:580
#define TMAP_HTL_3D_UNLIT
Definition: tmapper.h:63
vec3d max_vel
Definition: physics.h:49
GLclampf f
Definition: Glext.h:7097
#define PF_SPECIAL_WARP_IN
Definition: physics.h:28
vec3d norm
Definition: model.h:441
int getWarpOrientation(matrix *output)
Definition: shipfx.cpp:3804
#define MAX_OBJECTS
Definition: globals.h:83
int submodel_num
Definition: ship.h:525
int model_num
Definition: debris.h:34
int gamesnd_get_by_name(const char *name)
Definition: gamesnd.cpp:301
#define TRUE
Definition: pstypes.h:399
virtual void pageIn()
Definition: shipfx.cpp:3923
dc_stuff_float & value
Definition: shipfx.cpp:1368
int snd_is_playing(int sig)
Definition: sound.cpp:1047
float max_life
Definition: shipfx.cpp:1206
int l_cruiser_count
Definition: shipfx.cpp:2644
vec3d * vm_vec_rotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:933
#define Assertion(expr, msg,...)
Definition: clang.h:41
float factor
Definition: lua.cpp:440
#define SHIPFX_WARP_DELAY
Definition: shipfx.cpp:460
int end_time
Definition: ship.h:526
vec3d maxs
Definition: model.h:746
bool Rendering_to_shadow_map
Definition: gropengltnl.cpp:88
#define CHA_WARPIN
Definition: scripting.h:44
virtual int warpShipRender()
Definition: shipfx.cpp:3496
#define SF_ARRIVING
Definition: ship.h:453
#define SSF_VANISHED
Definition: ship.h:290
void shipfx_engine_wash_level_init()
Definition: shipfx.cpp:3106
float angle
Definition: ship.h:1490
void vm_vec_scale_add2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:284
#define FIREBALL_EXPLOSION_LARGE1
Definition: fireballs.h:32
uint flags
Definition: ship.h:644
virtual int warpShipClip()
Definition: shipfx.cpp:3486
int Ship_cannon_bitmap
Definition: shipfx.cpp:53
hull_check orient
Definition: lua.cpp:5049
#define OF_PLAYER_SHIP
Definition: object.h:109
void find_submodel_instance_point_normal(vec3d *outpnt, vec3d *outnorm, int model_instance_num, int submodel_num, const vec3d *submodel_pnt, const vec3d *submodel_norm)
Definition: modelread.cpp:4302
float min_rad
Definition: ship.h:979
int warpin_time
Definition: ship.h:1211
object * objp
Definition: lua.cpp:3105
GLenum GLenum GLenum GLenum GLenum scale
Definition: Glext.h:8503
void clear()
Definition: shipfx.cpp:3450
GLfloat GLfloat GLfloat v2
Definition: Glext.h:5640
#define Int3()
Definition: pstypes.h:292
bool isValid()
Definition: shipfx.cpp:3458
bsp_collision_leaf * bsp_leaf
Definition: model.h:1133
int obj_signature
Definition: shipfx.cpp:1203
void snd_update_3d_pos(int soundnum, game_snd *gs, vec3d *new_pos, float radius, float range_factor)
Definition: sound.cpp:693
#define MAX_DEBRIS_PIECES
Definition: debris.h:57
GLuint in
Definition: Glext.h:9087
ship * shipp
Definition: lua.cpp:9162
int special_warpin_objnum
Definition: ship.h:740
int n_thrusters
Definition: model.h:771
vec3d pos
Definition: object.h:152
#define VM_TOPDOWN
Definition: systemvars.h:43
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
float emp_intensity
Definition: ship.h:717
virtual int warpEnd()
Definition: shipfx.cpp:3506
vec3d * p0
Definition: model.h:1114
hull_check submodel_num
Definition: lua.cpp:5048
int bm_release(int handle, int clear_render_targets)
Frees both a bitmap's data and it's associated slot.
Definition: bmpman.cpp:2603
static const int TYPE_IMAGE
Definition: shipfx.cpp:3127
float vm_vec_mag_squared(const vec3d *v)
Definition: vecmat.cpp:339
void particle_emit(particle_emitter *pe, int type, int optional_data, float range)
Definition: particle.cpp:495
thruster_bank * thrusters
Definition: model.h:775
int asteroid_subtype
Definition: asteroid.h:103
script_state Script_system("FS2_Open Scripting")
virtual int getWarpOrientation(matrix *output)
Definition: shipfx.cpp:4173
void ship_get_eye(vec3d *eye_pos, matrix *eye_orient, object *obj, bool do_slew, bool from_origin)
Definition: ship.cpp:13227
int signature
Definition: object.h:145
int subobj_num
Definition: model.h:175
void do_sub_expl_sound(float radius, vec3d *sound_pos, int *sound_handle)
Definition: shipfx.cpp:2190
int next_fireball
Definition: shipfx.cpp:1651
int model_num
Definition: model.h:1110
void shipfx_do_damaged_arcs_frame(ship *shipp)
Definition: shipfx.cpp:2417
void stuff_float(float *f)
Definition: parselo.cpp:2328
Engine wash (looped)
Definition: gamesnd.h:112
#define SHIP_GUARDIAN_THRESHOLD_DEFAULT
Definition: ship.h:47
#define SIF2_FLASH
Definition: ship.h:920
int num_lights
Definition: model.h:749
void shipfx_stop_engine_wash_sound()
Definition: shipfx.cpp:3114
int obj_snd_assign(int objnum, int sndnum, vec3d *pos, int main, int flags, ship_subsys *associated_sub)
Definition: objectsnd.cpp:705
void submodel_render_DEPRECATED(int model_num, int submodel_num, matrix *orient, vec3d *pos, uint flags=MR_DEPRECATED_NORMAL, int objnum=-1, int *replacement_textures=NULL, int render=MODEL_RENDER_ALL)
GLint GLsizei width
Definition: Gl.h:1505
0.10 second spark sound effect
Definition: gamesnd.h:210
#define CHA_WARPOUT
Definition: scripting.h:43
void ship_actually_depart(int shipnum, int method)
Definition: ship.cpp:7748
~WE_BSG()
Definition: shipfx.cpp:3915
void g3_start_user_clip_plane(const vec3d *plane_point, const vec3d *plane_normal)
Definition: 3dsetup.cpp:381
float life
Definition: shipfx.cpp:1205
void shipfx_debris_limit_speed(debris *db, ship *shipp)
Definition: shipfx.cpp:2031
int asteroid_type
Definition: asteroid.h:102
#define DEBRIS_FREE
Definition: shipfx.cpp:1638
int flags
Definition: asteroid.h:99
int num_initial_asteroids
Definition: asteroid.h:135
int submodel_num
Definition: debris.h:35
int objnum
Definition: ship.h:1483
int bm_is_valid(int handle)
Checks if the bitmap indexed by handle is valid.
Definition: bmpman.cpp:1006
int explosion_flash_started
Definition: shipfx.cpp:1659
ship_subsys subsys_list
Definition: ship.h:630
int object_is_docked(object *objp)
Definition: object.cpp:2019
void model_find_submodel_offset(vec3d *outpnt, int model_num, int sub_model_num)
Definition: modelread.cpp:4041
vec3d pos
Definition: model.h:577
int Ship_flash_inited
Definition: shipfx.cpp:1209
#define SIF_BOMBER
Definition: ship.h:886
void model_render_queue(model_render_params *interp, draw_list *scene, int model_num, matrix *orient, vec3d *pos)
int is_hull
Definition: debris.h:37
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
void model_instance_find_world_point(vec3d *outpnt, vec3d *mpnt, int model_instance_num, int submodel_num, const matrix *objorient, const vec3d *objpos)
Definition: modelread.cpp:4135
void shipfx_actually_warpout(int shipnum)
Definition: shipfx.cpp:619
int instance
Definition: object.h:150
float Particle_number
Definition: shipfx.cpp:1376
#define FIREBALL_MEDIUM_EXPLOSION
Definition: fireballs.h:23
#define WT_DEFAULT_THEN_KNOSSOS
Definition: ship.h:1108
#define WIF_FLAK
Definition: weapon.h:75
void shipfx_large_blowup_queue_render(draw_list *scene, ship *shipp)
Definition: shipfx.cpp:2393
matrix * vm_vector_2_matrix(matrix *m, const vec3d *fvec, const vec3d *uvec, const vec3d *rvec)
Definition: vecmat.cpp:850
GLenum GLint * range
Definition: Glext.h:7096
float ship_get_subsystem_strength(ship *shipp, int type)
Definition: ship.cpp:13446
float core_radius
Definition: model.h:758
void dock_calc_docked_center(vec3d *dest, object *objp)
Definition: objectdock.cpp:126
#define DEBRIS_DRAW
Definition: shipfx.cpp:1637
Definition: debris.h:23
void vm_vec_add2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:178
int Cmdline_nohtl
Definition: cmdline.cpp:438
void shipfx_maybe_create_live_debris_at_ship_death(object *ship_objp)
Definition: shipfx.cpp:240
WarpEffect * warpout_effect
Definition: ship.h:576
float radius_mult
Definition: ship.h:1491
ubyte arc_type[MAX_SHIP_ARCS]
Definition: ship.h:713
#define PARTICLE_FIRE
Definition: particle.h:51
ship_spark sparks[MAX_SHIP_HITS]
Definition: ship.h:582
float max_vel
Definition: ship.h:984
int flags
Definition: ship.h:322
void big_explosion_flash(float)
Definition: fredstubs.cpp:164
vec3d autocenter
Definition: model.h:755
int num_live_debris
Definition: model.h:352
#define SIF_BIG_SHIP
Definition: ship.h:944
int flags
Definition: model.h:1116
void shipfx_actually_warpin(ship *shipp, object *objp)
Definition: shipfx.cpp:526
struct matrix::@228::@230 vec
unsigned int uint
Definition: pstypes.h:64
physics_info phys_info
Definition: shipfx.cpp:1644
virtual int getWarpPosition(vec3d *output)
Definition: shipfx.cpp:3525
virtual int warpFrame(float frametime)
Definition: shipfx.cpp:3481
float debris_min_speed
Definition: ship.h:1259
WE_Hyperspace(object *n_objp, int n_direction)
Definition: shipfx.cpp:4451
float ship_max_hull_strength
Definition: ship.h:597
void vm_vec_scale(vec3d *dest, float s)
Definition: vecmat.cpp:248
int timestamp_until(int stamp)
Definition: timer.cpp:242
#define nprintf(args)
Definition: pstypes.h:239
#define TMAP_FLAG_GOURAUD
Definition: tmapper.h:40
#define GM_MULTIPLAYER
Definition: systemvars.h:18
int compute_special_warpout_stuff(object *objp, float *speed, float *warp_time, vec3d *warp_pos)
Definition: shipfx.cpp:626
int gameseq_get_state_idx(char *s)
int explosion_flash_timestamp
Definition: shipfx.cpp:1658
float rotdamp
Definition: physics.h:43
virtual ~WE_Homeworld()
Definition: shipfx.cpp:4260
#define MR_NO_TEXTURING
Definition: model.h:868
#define PARTICLE_SMOKE
Definition: particle.h:52
int * ship_replacement_textures
Definition: ship.h:751
#define WD_WARP_OUT
Definition: shipfx.h:156
GLboolean GLboolean GLboolean GLboolean a
Definition: Glext.h:5781
int fireball_ship_explosion_type(ship_info *sip)
Definition: fireballs.cpp:993
int shipfx_large_blowup_do_frame(ship *shipp, float frametime)
Definition: shipfx.cpp:2325
vec3d * p1
Definition: model.h:1115
int ship_subsys_disrupted(ship_subsys *ss)
Definition: ship.cpp:9033
int flags
Definition: ship.h:1227
float get_hull_pct(object *objp)
Definition: object.cpp:271
void model_clear_instance(int model_num)
Definition: modelread.cpp:4624
virtual int warpShipQueueRender(draw_list *scene)
Definition: shipfx.cpp:3501
#define SIF_SMALL_SHIP
Definition: ship.h:943
int Ship_flash_highest
Definition: shipfx.cpp:1210
float speed
Definition: physics.h:79
ship_flash Ship_flash[MAX_FLASHES]
Definition: shipfx.cpp:1211
virtual int getWarpPosition(vec3d *output)
Definition: shipfx.cpp:4161
int snd_play_3d(game_snd *gs, vec3d *source_pos, vec3d *listen_pos, float radius, vec3d *source_vel, int looping, float vol_scale, int priority, vec3d *sound_fvec, float range_factor, int force, bool is_ambient)
Definition: sound.cpp:594
int num_points
Definition: model.h:446
#define WE_BSG_NUM_STAGES
Definition: shipfx.h:232
texture_map maps[MAX_MODEL_TEXTURES]
Definition: model.h:762
int sound_handle[NUM_SUB_EXPL_HANDLES]
Definition: shipfx.cpp:1660
float explosion_vel
Definition: shipfx.cpp:1649
void stuff_string(char *outstr, int type, int len, char *terminators)
Definition: parselo.cpp:1189
virtual void pageOut()
Definition: shipfx.cpp:3469
#define PF_ACCELERATES
Definition: physics.h:18
float length
Definition: ship.h:1492
void engine_wash_ship_process(ship *shipp)
Definition: shipfx.cpp:2873
virtual int warpFrame(float frametime)
Definition: shipfx.cpp:4507
void world_find_model_instance_point(vec3d *out, vec3d *world_pt, const polymodel_instance *pmi, int submodel_num, const matrix *orient, const vec3d *pos)
Definition: modelread.cpp:4215
void vm_set_identity(matrix *m)
Definition: vecmat.cpp:150
#define MC_SUBMODEL
Definition: model.h:1178
void submodel_render_queue(model_render_params *render_info, draw_list *scene, int model_num, int submodel_num, matrix *orient, vec3d *pos)
static const int TYPE_INT
Definition: shipfx.cpp:3128
submodel_instance * submodel
Definition: model.h:100
int warpout_snd_end
Definition: ship.h:1218
float min_vel
Definition: ship.h:983
const char * bm_get_filename(int handle)
Gets the filename of the bitmap indexed by handle, which must exist.
Definition: bmpman.cpp:727
float dock_calc_max_cross_sectional_radius_perpendicular_to_axis(object *objp, axis_type axis)
Definition: objectdock.cpp:161
GLdouble GLdouble z
Definition: Glext.h:5451
int flags
Definition: debris.h:25
void shipfx_warpout_frame(object *objp, float frametime)
Definition: shipfx.cpp:848
float z
Definition: model.h:601
bool can_move
Definition: model.h:328
#define vm_vec_negate(v)
Definition: vecmat.h:70
int sub_expl_sound_handle[NUM_SUB_EXPL_HANDLES]
Definition: ship.h:707
float Particle_life
Definition: shipfx.cpp:1395
object * Viewer_obj
Definition: object.cpp:57
int submodel_num
Definition: model.h:452
ubyte g3_rotate_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:97
void physics_init(physics_info *pi)
Definition: physics.cpp:49
int warpout_snd_start
Definition: ship.h:1217
ubyte draw_debris[MAX_DEBRIS_OBJECTS]
Definition: shipfx.cpp:1650
float warpout_accel_exp
Definition: ship.h:1222
int cockpit_model_num
Definition: ship.h:1188
int warpin_snd_end
Definition: ship.h:1209
#define fl_abs(fl)
Definition: floating.h:31
int Cmdline_tbp