FS2_Open
Open source remastering of the Freespace 2 engine
shiphit.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 <algorithm>
14 
15 #include "asteroid/asteroid.h"
16 #include "debris/debris.h"
17 #include "fireball/fireballs.h"
18 #include "freespace2/freespace.h"
20 #include "gamesnd/eventmusic.h"
21 #include "gamesnd/gamesnd.h"
22 #include "globalincs/linklist.h"
23 #include "hud/hud.h"
24 #include "hud/hudmessage.h"
25 #include "hud/hudtarget.h"
26 #include "iff_defs/iff_defs.h"
27 #include "io/joy_ff.h"
28 #include "io/timer.h"
29 #include "mission/missionlog.h"
30 #include "mod_table/mod_table.h"
31 #include "network/multi.h"
32 #include "network/multi_pmsg.h"
33 #include "network/multi_respawn.h"
34 #include "network/multimsgs.h"
35 #include "network/multiutil.h"
36 #include "object/object.h"
37 #include "object/objectdock.h"
38 #include "object/objectsnd.h"
39 #include "parse/parselo.h"
40 #include "parse/scripting.h"
41 #include "playerman/player.h"
42 #include "popup/popup.h"
43 #include "render/3d.h"
44 #include "ship/ship.h"
45 #include "ship/shipfx.h"
46 #include "ship/shiphit.h"
47 #include "weapon/beam.h"
48 #include "weapon/emp.h"
49 #include "weapon/shockwave.h"
50 #include "weapon/weapon.h"
51 
52 //#pragma optimize("", off)
53 //#pragma auto_inline(off)
54 
55 struct ssm_firing_info;
56 extern void ssm_create(object *target, vec3d *start, size_t ssm_index, ssm_firing_info *override, int team);
57 
58 typedef struct spark_pair {
59  int index1, index2;
60  float dist;
61 } spark_pair;
62 
63 #define MAX_SPARK_PAIRS ((MAX_SHIP_HITS * MAX_SHIP_HITS - MAX_SHIP_HITS) / 2)
64 
65 #define BIG_SHIP_MIN_RADIUS 80.0f // ship radius above which death rolls can't be shortened by excessive damage
66 
69 
70 static bool global_damage = false;
71 
72 //WMC - Camera rough draft stuff
73 /*
74 camid dead_get_camera()
75 {
76  static camid dead_camera;
77  if(!dead_camera.isValid())
78  dead_camera = cam_create("Dead camera");
79 
80  return dead_camera;
81 }
82 */
83 
85 {
86  ship_subsys *subsys;
87 
88  if (submodel == -1) {
89  return false;
90  }
91 
92  for ( subsys=GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys) ) {
93  if (subsys->system_info->subobj_num == submodel) {
94  if (subsys->current_hits > 0.0f) {
95  return false;
96  } else {
97  return true;
98  }
99  }
100  }
101 
102  return false;
103 }
104 
105 // do_subobj_destroyed_stuff is called when a subobject for a ship is killed. Separated out
106 // to separate function on 10/15/97 by MWA for easy multiplayer access. It does all of the
107 // cool things like blowing off the model (if applicable, writing the logs, etc)
108 void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vec3d* hitpos, bool no_explosion )
109 {
110  ship_info *sip;
111  object *ship_objp;
112  model_subsystem *psub;
113  vec3d g_subobj_pos;
114  int type, i, log_index;
115 
116  // get some local variables
117  sip = &Ship_info[ship_p->ship_info_index];
118  ship_objp = &Objects[ship_p->objnum];
119  psub = subsys->system_info;
120  type = psub->type;
121  get_subsystem_world_pos(ship_objp, subsys, &g_subobj_pos);
122 
123  // create fireballs when subsys destroy for large ships.
124  object* objp = &Objects[ship_p->objnum];
125 
126  if (!(subsys->flags & SSF_VANISHED) && !no_explosion) {
127  if (objp->radius > 100.0f) {
128  // number of fireballs determined by radius of subsys
129  int num_fireballs;
130  if ( psub->radius < 3 ) {
131  num_fireballs = 1;
132  } else {
133  num_fireballs = 5;
134  }
135 
136  vec3d temp_vec, center_to_subsys, rand_vec;
137  vm_vec_sub(&center_to_subsys, &g_subobj_pos, &objp->pos);
138  for (i=0; i<num_fireballs; i++) {
139  if (i==0) {
140  // make first fireball at hitpos
141  if (hitpos) {
142  temp_vec = *hitpos;
143  } else {
144  temp_vec = g_subobj_pos;
145  }
146  } else {
147  // make other fireballs at random positions, but try to keep on the surface
148  vm_vec_rand_vec_quick(&rand_vec);
149  float dot = vm_vec_dot(&center_to_subsys, &rand_vec);
150  vm_vec_scale_add2(&rand_vec, &center_to_subsys, -dot/vm_vec_mag_squared(&center_to_subsys));
151  vm_vec_scale_add(&temp_vec, &g_subobj_pos, &rand_vec, 0.5f*psub->radius);
152  }
153 
154  // scale fireball size according to size of subsystem, but not less than 10
155  float fireball_rad = psub->radius * 0.2f;
156  if (fireball_rad < 10) {
157  fireball_rad = 10.0f;
158  }
159 
160  vec3d fb_vel;
161  vm_vec_cross(&fb_vel, &objp->phys_info.rotvel, &center_to_subsys);
162  vm_vec_add2(&fb_vel, &objp->phys_info.vel);
163 
164  int fireball_type = fireball_ship_explosion_type(sip);
165  if(fireball_type < 0) {
166  fireball_type = FIREBALL_EXPLOSION_MEDIUM;
167  }
168  fireball_create( &temp_vec, fireball_type, FIREBALL_MEDIUM_EXPLOSION, OBJ_INDEX(objp), fireball_rad, 0, &fb_vel );
169  }
170  }
171  }
172 
173  if ( MULTIPLAYER_MASTER ) {
174  int index;
175 
176  index = ship_get_index_from_subsys(subsys, ship_p->objnum);
177 
178  vec3d hit;
179  if (hitpos) {
180  hit = *hitpos;
181  } else {
182  hit = g_subobj_pos;
183  }
184  send_subsystem_destroyed_packet( ship_p, index, hit );
185  }
186 
187  // next do a quick sanity check on the current hits that we are keeping for the generic subsystems
188  // I think that there might be rounding problems with the floats. This code keeps us safe.
189  if ( ship_p->subsys_info[type].type_count == 1 ) {
190  ship_p->subsys_info[type].aggregate_current_hits = 0.0f;
191  } else {
192  float hits;
193  ship_subsys *ssp;
194 
195  hits = 0.0f;
196  for ( ssp=GET_FIRST(&ship_p->subsys_list); ssp != END_OF_LIST(&ship_p->subsys_list); ssp = GET_NEXT(ssp) ) {
197  // type matches?
198  if ( (ssp->system_info->type == type) && !(ssp->flags & SSF_NO_AGGREGATE) ) {
199  hits += ssp->current_hits;
200  }
201  }
202  ship_p->subsys_info[type].aggregate_current_hits = hits;
203  }
204 
205  // store an event in the event log. Also, determine if all turrets or all
206  // engines have been destroyed (if the subsystem is a turret or engine).
207  // put a disabled or disarmed entry in the log if this is the case
208  //
209  // MWA -- 1/8/98 A problem was found when trying to determine (via sexpression) when some subsystems
210  // were destroyed. The bottom line is that is the psub->name and psub->subobj_name are different,
211  // then direct detection doesn't work. (This scenario happens mainly with turrets and probably with
212  // engines). So, my solution is to encode the ship_info index, and the subsystem index into one
213  // integer, and pass that as the "index" parameter to add_entry. We'll use that information to
214  // print out the info in the mission log.
215  Assert( ship_p->ship_info_index < 65535 );
216 
217  // get the "index" of this subsystem in the ship info structure.
218  for ( i = 0; i < sip->n_subsystems; i++ ) {
219  if ( &(sip->subsystems[i]) == psub )
220  break;
221  }
222  Assert( i < sip->n_subsystems );
223  Assert( i < 65535 );
224  log_index = ((ship_p->ship_info_index << 16) & 0xffff0000) | (i & 0xffff);
225 
226  // Don't log, display info, or play sounds about the activation subsytem
227  // FUBAR/Goober5000 - or about vanishing subsystems, per precedent with ship-vanish
228  int notify = (psub->type != SUBSYSTEM_ACTIVATION) && !(subsys->flags & SSF_VANISHED);
229 
230  if (notify)
231  {
233  if ( ship_objp == Player_obj )
234  {
235  if (!no_explosion) {
236  snd_play( &Snds[SND_SUBSYS_DIE_1], 0.0f );
237  }
238  if (strlen(psub->alt_dmg_sub_name))
239  HUD_printf(XSTR( "Your %s subsystem has been destroyed", 499), psub->alt_dmg_sub_name);
240  else {
241  char r_name[NAME_LENGTH];
242  strcpy_s(r_name, ship_subsys_get_name(subsys));
243  for (i = 0; r_name[i] > 0; i++) {
244  if (r_name[i] == '|')
245  r_name[i] = ' ';
246  }
247  HUD_printf(XSTR( "Your %s subsystem has been destroyed", 499), r_name );
248  }
249  }
250  }
251 
252  if ( psub->type == SUBSYSTEM_TURRET ) {
253  if ( ship_p->subsys_info[type].aggregate_current_hits <= 0.0f ) {
254  // Don't create "disarmed" event for small ships.
255  if (!(Ship_info[ship_p->ship_info_index].flags & SIF_SMALL_SHIP)) {
257  // ship_p->flags |= SF_DISARMED;
258  }
259  }
260  } else if (psub->type == SUBSYSTEM_ENGINE ) {
261  // when an engine is destroyed, we must change the max velocity of the ship
262  // to be some fraction of its normal maximum value
263 
264  if ( ship_p->subsys_info[type].aggregate_current_hits <= 0.0f ) {
266  ship_p->flags |= SF_DISABLED; // add the disabled flag
267  }
268  }
269 
270  if ( psub->subobj_num > -1 ) {
271  shipfx_blow_off_subsystem(ship_objp,ship_p,subsys,&g_subobj_pos,no_explosion);
272  subsys->submodel_info_1.blown_off = 1;
273  }
274 
275  if ( (psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >= 0) ) {
276  subsys->submodel_info_2.blown_off = 1;
277  }
278 
279  if (notify && !no_explosion) {
280  // play sound effect when subsys gets blown up
281  int sound_index=-1;
282  if ( Ship_info[ship_p->ship_info_index].flags & SIF_HUGE_SHIP ) {
283  sound_index=SND_CAPSHIP_SUBSYS_EXPLODE;
284  } else if ( Ship_info[ship_p->ship_info_index].flags & SIF_BIG_SHIP ) {
285  sound_index=SND_SUBSYS_EXPLODE;
286  }
287  if ( sound_index >= 0 ) {
288  snd_play_3d( &Snds[sound_index], &g_subobj_pos, &View_position );
289  }
290  }
291 
292  // make the shipsounds work as they should...
293  if(subsys->subsys_snd_flags & SSSF_ALIVE)
294  {
295  obj_snd_delete_type(ship_p->objnum, subsys->system_info->alive_snd, subsys);
296  subsys->subsys_snd_flags &= ~SSSF_ALIVE;
297  }
299  {
303  }
304  if(subsys->subsys_snd_flags & SSSF_ROTATE)
305  {
306  obj_snd_delete_type(ship_p->objnum, subsys->system_info->rotation_snd, subsys);
307  subsys->subsys_snd_flags &= ~SSSF_ROTATE;
308  }
309  if((subsys->system_info->dead_snd != -1) && !(subsys->subsys_snd_flags & SSSF_DEAD))
310  {
311  obj_snd_assign(ship_p->objnum, subsys->system_info->dead_snd, &subsys->system_info->pnt, 0, OS_SUBSYS_DEAD, subsys);
312  subsys->subsys_snd_flags |= SSSF_DEAD;
313  }
314 }
315 
316 // Return weapon type that is associated with damaging_objp
317 // input: damaging_objp => object pointer responsible for damage
318 // exit: -1 => no weapon type is associated with damage object
319 // >=0 => weapon type associated with damage object
320 int shiphit_get_damage_weapon(object *damaging_objp)
321 {
322  int weapon_info_index = -1;
323 
324  if ( damaging_objp ) {
325  switch(damaging_objp->type) {
326  case OBJ_WEAPON:
327  weapon_info_index = Weapons[damaging_objp->instance].weapon_info_index;
328  break;
329  case OBJ_SHOCKWAVE:
330  weapon_info_index = shockwave_get_weapon_index(damaging_objp->instance);
331  break;
332  case OBJ_BEAM:
334  weapon_info_index = beam_get_weapon_info_index(damaging_objp);
335  }
336  break;
337  default:
338  weapon_info_index = -1;
339  break;
340  }
341  }
342 
343  return weapon_info_index;
344 }
345 
346 // Return range at which this object can apply damage.
347 // Based on object type and subsystem type.
348 float subsys_get_range(object *other_obj, ship_subsys *subsys)
349 {
350  float range;
351 
352  Assert(subsys); // Goober5000
353 
354  if ((other_obj) && (other_obj->type == OBJ_SHOCKWAVE)) { // Goober5000 - check for NULL when via sexp
355  range = shockwave_get_max_radius(other_obj->instance) * 0.75f; // Shockwaves were too lethal to subsystems.
356  } else if ( subsys->system_info->type == SUBSYSTEM_TURRET ) {
357  range = subsys->system_info->radius*3;
358  } else {
359  range = subsys->system_info->radius*2;
360  }
361 
362  return range;
363 }
364 
365 #define MAX_DEBRIS_SHARDS 16 // cap the amount of debris shards that fly off per hit
366 
367 // Make some random debris particles. Previous way was not very random. Create debris 75% of the time.
368 // Don't worry about multiplayer since this debris is the small stuff that cannot collide
369 void create_subsys_debris(object *ship_objp, vec3d *hitpos)
370 {
371  float show_debris = frand();
372 
373  if ( show_debris <= 0.75f ) {
374  int ndebris;
375 
376  ndebris = (int)(show_debris * Detail.num_small_debris) + 1; // number of pieces of debris to create
377 
378  if ( ndebris > MAX_DEBRIS_SHARDS )
379  ndebris = MAX_DEBRIS_SHARDS;
380 
381  //mprintf(( "Damage = %.1f, ndebris=%d\n", show_debris, ndebris ));
382  for (int i=0; i<ndebris; i++ ) {
383  debris_create( ship_objp, -1, -1, hitpos, hitpos, 0, 1.0f );
384  }
385  }
386 }
387 
388 void create_vaporize_debris(object *ship_objp, vec3d *hitpos)
389 {
390  int ndebris;
391  float show_debris = frand();
392 
393  ndebris = (int)(4.0f * ((0.5f + show_debris) * Detail.num_small_debris)) + 5; // number of pieces of debris to create
394 
395  if ( ndebris > MAX_DEBRIS_SHARDS ) {
396  ndebris = MAX_DEBRIS_SHARDS;
397  }
398 
399  //mprintf(( "Damage = %.1f, ndebris=%d\n", show_debris, ndebris ));
400  for (int i=0; i<ndebris; i++ ) {
401  debris_create( ship_objp, -1, -1, hitpos, hitpos, 0, 1.4f );
402  }
403 }
404 
405 #define MAX_SUBSYS_LIST 200 //DTP MAX SUBSYS LIST BUMPED FROM 32 to 200, ahmm 32???
406 
407 typedef struct {
408  float dist;
409  float range;
411 } sublist;
412 
413 // do_subobj_hit_stuff() is called when a collision is detected between a ship and something
414 // else. This is where we see if any sub-objects on the ship should take damage.
415 //
416 // Depending on where the collision occurs, the sub-system and surrounding hull will take
417 // different amounts of damage. The amount of damage a sub-object takes depending on how
418 // close the colliding object is to the center of the sub-object. The remaining hull damage
419 // will be returned to the caller via the damage parameter.
420 //
421 //
422 // 0 -> 0.5 radius : 100% subobject 0% hull
423 // 0.5 -> 1.0 radius : 50% subobject 50% hull
424 // 1.0 -> 2.0 radius : 25% subobject 75% hull
425 // > 2.0 radius : 0% subobject 100% hull
426 //
427 //
428 // The weapon damage is not neccesarily distributed evently between sub-systems when more than
429 // one sub-system is to take damage. Whenever damage is to be assigned to a sub-system, the above
430 // percentages are used. So, if more than one sub-object is taking damage, the second sub-system
431 // to be assigned damage will take less damage. Eg. weapon hits in the 25% damage range of two
432 // subsytems, and the weapon damage is 12. First subsystem takes 3 points damage. Second subsystem
433 // will take 0.25*9 = 2.25 damage. Should be close enough for most cases, and hull would receive
434 // 0.75 * 9 = 6.75 damage.
435 //
436 // Used to use the following constants, but now damage is linearly scaled up to 2x the subsystem
437 // radius. Same damage applied as defined by constants below.
438 //
439 // Returns unapplied damage, which will probably be applied to the hull.
440 //
441 // Shockwave damage is handled here. If other_obj->type == OBJ_SHOCKWAVE, it's a shockwave.
442 // apply the same damage to all subsystems.
443 // Note: A negative damage number means to destroy the corresponding subsystem. For example, call with -SUBSYSTEM_ENGINE to destroy engine.
444 //
445 //WMC - hull_should_apply armor means that the initial subsystem had no armor, so the hull should apply armor instead.
446 
447 float do_subobj_hit_stuff(object *ship_objp, object *other_obj, vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor)
448 {
449  vec3d g_subobj_pos;
450  float damage_left, damage_if_hull;
451  int weapon_info_index;
452  ship_subsys *subsys;
453  ship *ship_p;
454  sublist subsys_list[MAX_SUBSYS_LIST];
455  int subsys_hit_first = -1; // the subsys which should be hit first and take most of the damage; index into subsys_list
456  vec3d hitpos2;
457  float ss_dif_scale = 1.0f; // Nuke: Set a base dificulty scale for compatibility
458 
459  //WMC - first, set this to damage if it isn't NULL, in case we want to return with no damage to subsystems
460  if(hull_should_apply_armor != NULL) {
461  *hull_should_apply_armor = true;
462  }
463 
464  Assert(ship_objp); // Goober5000 (but other_obj might be NULL via sexp)
465  Assert(hitpos); // Goober5000
466 
467  ship_p = &Ships[ship_objp->instance];
468 
469  // Don't damage player subsystems in a training mission.
471  if (ship_objp == Player_obj){
472  return damage;
473  }
474  }
475 
476  // Shockwave damage is applied like weapon damage. It gets consumed.
477  if ((other_obj != NULL) && (other_obj->type == OBJ_SHOCKWAVE)) // Goober5000 check for NULL
478  {
479  // MK, 9/2/99. Shockwaves do zero subsystem damage on small ships.
480  // Goober5000 - added back in via flag
482  return damage;
483  else {
484  damage_left = shockwave_get_damage(other_obj->instance) / 4.0f;
485  damage_if_hull = damage_left;
486  }
487  hitpos2 = other_obj->pos;
488  } else {
489  damage_left = damage;
490  damage_if_hull = damage;
491  hitpos2 = *hitpos;
492  }
493 
494  // scale subsystem damage if appropriate
495  weapon_info_index = shiphit_get_damage_weapon(other_obj); // Goober5000 - a NULL other_obj returns -1
496  if ((weapon_info_index >= 0) && ((other_obj->type == OBJ_WEAPON) ||
497  (Beams_use_damage_factors && (other_obj->type == OBJ_BEAM)))) {
498  if ( Weapon_info[weapon_info_index].wi_flags2 & WIF2_TRAINING ) {
499  return damage_left;
500  }
501  damage_left *= Weapon_info[weapon_info_index].subsystem_factor;
502  damage_if_hull *= Weapon_info[weapon_info_index].armor_factor;
503  }
504 
505 
506 #ifndef NDEBUG
507  float hitpos_dist = vm_vec_dist( hitpos, &ship_objp->pos );
508  if ( hitpos_dist > ship_objp->radius * 2.0f ) {
509  mprintf(( "BOGUS HITPOS PASSED TO DO_SUBOBJ_HIT_STUFF (%.1f > %.1f)!\nInvestigate ship %s (%s), a hit was registered on this ship outside this ship's radius.\n", hitpos_dist, ship_objp->radius * 2.0f, ship_p->ship_name, Ship_info[ship_p->ship_info_index].name ));
510  // Get John ASAP!!!! Someone passed a local coordinate instead of world for hitpos probably.
511  }
512 #endif
513 
514  if (!global_damage) {
515  create_subsys_debris(ship_objp, hitpos);
516  }
517 
518  // First, create a list of the N subsystems within range.
519  // Then, one at a time, process them in order.
520  int count = 0;
521  for ( subsys=GET_FIRST(&ship_p->subsys_list); subsys != END_OF_LIST(&ship_p->subsys_list); subsys = GET_NEXT(subsys) )
522  {
523  model_subsystem *mss = subsys->system_info;
524 
525  //Deal with cheat correctly. If damage is the negative of the subsystem type, then we'll just kill the subsystem
526  //See process_debug_keys() in keycontrol.cpp for details.
527  if (damage < 0.0f) {
528  // single player or multiplayer
529  Assert(Player_ai->targeted_subsys != NULL);
530  if ( (subsys == Player_ai->targeted_subsys) && (subsys->current_hits > 0.0f) ) {
531  Assert(mss->type == (int) -damage);
532  if (!(subsys->flags & SSF_NO_AGGREGATE)) {
533  ship_p->subsys_info[mss->type].aggregate_current_hits -= subsys->current_hits;
534  if (ship_p->subsys_info[mss->type].aggregate_current_hits < 0.0f) {
535  ship_p->subsys_info[mss->type].aggregate_current_hits = 0.0f;
536  }
537  }
538  subsys->current_hits = 0.0f;
539  do_subobj_destroyed_stuff( ship_p, subsys, hitpos );
540  continue;
541  } else {
542  continue;
543  }
544  }
545 
546  if (subsys->current_hits > 0.0f) {
547  float dist, range;
548 
549  if (Fixed_turret_collisions && submodel_num != -1 && submodel_num == mss->turret_gun_sobj) {
550  // Special case:
551  // if the subsystem is a turret and the hit submodel is its barrel,
552  // get the distance between the hit and the turret barrel center
553  find_submodel_instance_world_point(&g_subobj_pos, ship_p->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
554  dist = vm_vec_dist_quick(&hitpos2, &g_subobj_pos);
555 
556  // Damage attenuation range of barrel radius * 2 makes full damage
557  // be taken regardless of where the barrel is hit
558  range = submodel_get_radius(Ship_info[ship_p->ship_info_index].model_num, submodel_num) * 2;
559  } else {
560  // Default behavior:
561  // get the distance between the hit and the subsystem center
562  get_subsystem_world_pos(ship_objp, subsys, &g_subobj_pos);
563  dist = vm_vec_dist_quick(&hitpos2, &g_subobj_pos);
564 
565  range = subsys_get_range(other_obj, subsys);
566  }
567 
568  if ( dist < range) {
569  if (Damage_impacted_subsystem_first && submodel_num != -1 && (submodel_num == mss->subobj_num || submodel_num == mss->turret_gun_sobj)) {
570  // If the hit impacted this subsystem's submodel, then make sure this subsys
571  // gets dealt damage first, even if another subsystem is closer to the hit location
572  subsys_hit_first = count;
573  }
574 
575  if (mss->flags2 & MSS_FLAG2_COLLIDE_SUBMODEL) {
576  if (submodel_num != -1 && submodel_num != mss->subobj_num && submodel_num != mss->turret_gun_sobj) {
577  // If this subsystem only wants to take damage when its submodel receives
578  // a direct hit and the current hit did not do so, skip it.
579  continue;
580  }
581  }
582 
583  subsys_list[count].dist = dist;
584  subsys_list[count].range = range;
585  subsys_list[count].ptr = subsys;
586  count++;
587 
588  if (count >= MAX_SUBSYS_LIST){
589  break;
590  }
591  }
592  }
593  }
594 
595  int dmg_type_idx = -1;
596  int parent_armor_flags = 0;
597 
598  if(ship_p->armor_type_idx > -1)
599  parent_armor_flags = Armor_types[ship_p->armor_type_idx].flags;
600 
601  if (other_obj)
602  {
603  if(other_obj->type == OBJ_SHOCKWAVE) {
604  dmg_type_idx = shockwave_get_damage_type_idx(other_obj->instance);
605  } else if(other_obj->type == OBJ_WEAPON) {
606  dmg_type_idx = Weapon_info[Weapons[other_obj->instance].weapon_info_index].damage_type_idx;
607  } else if(other_obj->type == OBJ_BEAM) {
608  dmg_type_idx = Weapon_info[beam_get_weapon_info_index(other_obj)].damage_type_idx;
609  } else if(other_obj->type == OBJ_ASTEROID) {
610  dmg_type_idx = Asteroid_info[Asteroids[other_obj->instance].asteroid_type].damage_type_idx;
611  } else if(other_obj->type == OBJ_DEBRIS) {
612  dmg_type_idx = Debris[other_obj->instance].damage_type_idx;
613  } else if(other_obj->type == OBJ_SHIP) {
614  dmg_type_idx = Ships[other_obj->instance].collision_damage_type_idx;
615  }
616  }
617 
618  // Now scan the sorted list of subsystems in range.
619  // Apply damage to the nearest one first (exception: subsys_hit_first),
620  // subtracting off damage as we go.
621  int i, j;
622  for (j=0; j<count; j++)
623  {
624  float dist, range;
625  ship_subsys *subsystem;
626 
627  int min_index = -1;
628 
629  if (Damage_impacted_subsystem_first && subsys_hit_first > -1) {
630  min_index = subsys_hit_first;
631 
632  subsys_hit_first = -1;
633  } else {
634  float min_dist = 9999999.9f;
635 
636  for (i=0; i<count; i++) {
637  if (subsys_list[i].dist < min_dist) {
638  min_dist = subsys_list[i].dist;
639  min_index = i;
640  }
641  }
642  }
643 
644  Assert(min_index != -1);
645 
646  float damage_to_apply = 0.0f;
647  subsystem = subsys_list[min_index].ptr;
648  range = subsys_list[min_index].range;
649  dist = subsys_list[min_index].dist;
650  subsys_list[min_index].dist = 9999999.9f; // Make sure we don't use this one again.
651 
652  Assert(range > 0.0f); // Goober5000 - avoid div-0 below
653 
654  // only do this for the closest affected subsystem
655  if ( (j == 0) && (!(parent_armor_flags & SAF_IGNORE_SS_ARMOR))) {
656  if(subsystem->armor_type_idx > -1)
657  {
658  damage = Armor_types[subsystem->armor_type_idx].GetDamage(damage, dmg_type_idx, 1.0f); // Nuke: I don't think we need to apply damage sacaling to this one, using 1.0f
659  if(hull_should_apply_armor) {
660  *hull_should_apply_armor = false;
661  }
662  }
663  }
664  // HORRIBLE HACK!
665  // MK, 9/4/99
666  // When Helios bombs are dual fired against the Juggernaut in sm3-01 (FS2), they often
667  // miss their target. There is code dating to FS1 in the collision code to detect that a bomb or
668  // missile has somehow missed its target. It gets its lifeleft set to 0.1 and then it detonates.
669  // Unfortunately, the shockwave damage was cut by 4 above. So boost it back up here.
670  if ((weapon_info_index >= 0) && (dist < 10.0f) && ((other_obj) && (other_obj->type == OBJ_SHOCKWAVE))) { // Goober5000 check for NULL
671  damage_left *= 4.0f * Weapon_info[weapon_info_index].subsystem_factor;
672  damage_if_hull *= 4.0f * Weapon_info[weapon_info_index].armor_factor;
673  }
674 
675  if ( dist < range/2.0f ) {
676  if (subsystem->flags & SSF_DAMAGE_AS_HULL)
677  damage_to_apply = damage_if_hull;
678  else
679  damage_to_apply = damage_left;
680  } else if ( dist < range ) {
681  if (subsystem->flags & SSF_DAMAGE_AS_HULL)
682  damage_to_apply = damage_if_hull * (1.0f - dist/range);
683  else
684  damage_to_apply = damage_left * (1.0f - dist/range);
685  }
686 
687  // if we're not in CLIENT_NODAMAGE multiplayer mode (which is a the NEW way of doing things)
688  if ( (damage_to_apply > 0.1f) && !(MULTIPLAYER_CLIENT) )
689  {
690  // Decrease damage to subsystems to player ships.
691  if (ship_objp->flags & OF_PLAYER_SHIP){
693  }
694 
695  // Goober5000 - subsys guardian
696  if (subsystem->subsys_guardian_threshold > 0)
697  {
698  float min_subsys_strength = 0.01f * subsystem->subsys_guardian_threshold * subsystem->max_hits;
699  if ( (subsystem->current_hits - (damage_to_apply * ss_dif_scale)) < min_subsys_strength ) {
700  // find damage needed to take object to min subsys strength
701  damage_to_apply = subsystem->current_hits - min_subsys_strength;
702 
703  // make sure damage is positive
704  damage_to_apply = MAX(0, damage_to_apply);
705  }
706  }
707 
708  // decrease the damage left to apply to the ship subsystems
709  // WMC - since armor aborbs damage, subtract the amount of damage before we apply armor
710  damage_left -= (damage_to_apply * ss_dif_scale);
711 
712  // if this subsystem doesn't carry damage then subtract it off of our total return
713  if (subsystem->system_info->flags & MSS_FLAG_CARRY_NO_DAMAGE) {
714  if ((other_obj->type != OBJ_SHOCKWAVE) || (!(subsystem->system_info->flags & MSS_FLAG_CARRY_SHOCKWAVE))) {
715  float subsystem_factor = 0.0f;
716  if ((weapon_info_index >= 0) && ((other_obj->type == OBJ_WEAPON) || (other_obj->type == OBJ_SHOCKWAVE))) {
717  if (subsystem->flags & SSF_DAMAGE_AS_HULL) {
718  subsystem_factor = Weapon_info[weapon_info_index].armor_factor;
719  } else {
720  subsystem_factor = Weapon_info[weapon_info_index].subsystem_factor;
721  }
722  }
723  if (subsystem_factor > 0.0f) {
724  damage -= ((MIN(subsystem->current_hits, (damage_to_apply * ss_dif_scale))) / subsystem_factor);
725  } else {
726  damage -= MIN(subsystem->current_hits, (damage_to_apply * ss_dif_scale));
727  }
728  }
729  }
730 
731  //Apply armor to damage
732  if (subsystem->armor_type_idx >= 0) {
733  // Nuke: this will finally factor it in to damage_to_apply and i wont need to factor it in anywhere after this
734  damage_to_apply = Armor_types[subsystem->armor_type_idx].GetDamage(damage_to_apply, dmg_type_idx, ss_dif_scale);
735  } else { // Nuke: no get damage call to apply difficulty scaling, so factor it in now
736  damage_to_apply *= ss_dif_scale;
737  }
738 
739  subsystem->current_hits -= damage_to_apply;
740  if (!(subsystem->flags & SSF_NO_AGGREGATE)) {
741  ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits -= damage_to_apply;
742  }
743 
744  if (subsystem->current_hits < 0.0f) {
745  damage_left -= subsystem->current_hits;
746  if (!(subsystem->flags & SSF_NO_AGGREGATE)) {
747  ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits -= subsystem->current_hits;
748  }
749  subsystem->current_hits = 0.0f; // set to 0 so repair on subsystem takes immediate effect
750  }
751 
752  if ( ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits < 0.0f ){
753  ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits = 0.0f;
754  }
755 
756  // multiplayer clients never blow up subobj stuff on their own
757  if ( (subsystem->current_hits <= 0.0f) && !MULTIPLAYER_CLIENT) {
758  do_subobj_destroyed_stuff( ship_p, subsystem, hitpos );
759  }
760 
761  if (damage_left <= 0) { // no more damage to distribute, so stop checking
762  damage_left = 0.0f;
763  break;
764  }
765  }
766  }
767 
768  if (damage < 0.0f) {
769  damage = 0.0f;
770  }
771 
772  // Note: I changed this to return damage_left and it completely screwed up balance.
773  // It had taken a few MX-50s to destory an Anubis (with 40% hull), then it took maybe ten.
774  // So, I left it alone. -- MK, 4/15/98
775 
776  return damage;
777 }
778 
779 // Store who/what killed the player, so we can tell the player how he died
780 void shiphit_record_player_killer(object *killer_objp, player *p)
781 {
782  switch (killer_objp->type) {
783 
784  case OBJ_WEAPON:
787  p->killer_species = Ship_info[Ships[Objects[killer_objp->parent].instance].ship_info_index].species;
788 
789  if ( &Objects[killer_objp->parent] == Player_obj ) {
790  // killed by a missile?
793  } else {
795  }
796  }
797 
798  // in multiplayer, record callsign of killer if killed by another player
799  if ( (Game_mode & GM_MULTIPLAYER) && ( Objects[killer_objp->parent].flags & OF_PLAYER_SHIP) ) {
800  int pnum;
801 
802  pnum = multi_find_player_by_object( &Objects[killer_objp->parent] );
803  if ( pnum != -1 ) {
805  } else {
806  nprintf(("Network", "Couldn't find player object of weapon for killer of %s\n", p->callsign));
807  }
808  } else {
811  }
812  break;
813 
814  case OBJ_SHOCKWAVE:
817  p->killer_species = Ship_info[Ships[Objects[killer_objp->parent].instance].ship_info_index].species;
818 
819  if ( &Objects[killer_objp->parent] == Player_obj ) {
821  }
822 
823  if ( (Game_mode & GM_MULTIPLAYER) && ( Objects[killer_objp->parent].flags & OF_PLAYER_SHIP) ) {
824  int pnum;
825 
826  pnum = multi_find_player_by_object( &Objects[killer_objp->parent] );
827  if ( pnum != -1 ) {
829  } else {
830  nprintf(("Network", "Couldn't find player object of shockwave for killer of %s\n", p->callsign));
831  }
832  } else {
835  }
836  break;
837 
838  case OBJ_SHIP:
840  p->killer_weapon_index=-1;
841  p->killer_species = Ship_info[Ships[killer_objp->instance].ship_info_index].species;
842 
843  if ( Ships[killer_objp->instance].flags & SF_EXPLODED ) {
845  }
846 
847  if ( Ships[Objects[p->objnum].instance].wash_killed ) {
849  }
850 
851  // in multiplayer, record callsign of killer if killed by another player
852  if ( (Game_mode & GM_MULTIPLAYER) && (killer_objp->flags & OF_PLAYER_SHIP) ) {
853  int pnum;
854 
855  pnum = multi_find_player_by_object( killer_objp );
856  if ( pnum != -1 ) {
858  } else {
859  nprintf(("Network", "Couldn't find player object for killer of %s\n", p->callsign));
860  }
861  } else {
864  }
865  break;
866 
867  case OBJ_DEBRIS:
868  case OBJ_ASTEROID:
869  if ( killer_objp->type == OBJ_DEBRIS ) {
871  } else {
873  }
874  p->killer_weapon_index=-1;
875  p->killer_species = -1;
876  p->killer_parent_name[0] = '\0';
877  break;
878 
879  case OBJ_BEAM:
880  int beam_obj;
881  beam_obj = beam_get_parent(killer_objp);
882  p->killer_species = -1;
884  if(beam_obj != -1){
885  if((Objects[beam_obj].type == OBJ_SHIP) && (Objects[beam_obj].instance >= 0)){
886  strcpy_s(p->killer_parent_name, Ships[Objects[beam_obj].instance].ship_name);
888  }
889  p->killer_species = Ship_info[Ships[Objects[beam_obj].instance].ship_info_index].species;
890  } else {
892  }
893  break;
894 
895  case OBJ_NONE:
896  if ( Game_mode & GM_MULTIPLAYER ) {
897  Int3();
898  }
899  p->killer_objtype=-1;
900  p->killer_weapon_index=-1;
901  p->killer_parent_name[0]=0;
902  p->killer_species = -1;
903  break;
904 
905  default:
906  Int3();
907  break;
908  }
909 }
910 
911 // Say dead stuff.
912 void show_dead_message(object *ship_objp, object *other_obj)
913 {
914  player *player_p;
915 
916  // not doing anything when a non player dies.
917  if ( !(ship_objp->flags & OF_PLAYER_SHIP) ){
918  return;
919  }
920 
921  if(other_obj == NULL){
922  return;
923  }
924 
925  // Get a pointer to the player (we are assured a player ship was killed)
926  if ( Game_mode & GM_NORMAL ) {
927  player_p = Player;
928  } else {
929  // in multiplayer, get a pointer to the player that died.
930  int pnum = multi_find_player_by_object( ship_objp );
931  if ( pnum == -1 ) {
932  return;
933  }
934  player_p = Net_players[pnum].m_player;
935  }
936 
937  // multiplayer clients should already have this information.
938  if ( !MULTIPLAYER_CLIENT ){
939  shiphit_record_player_killer( other_obj, player_p );
940  }
941 
942  // display a hud message is the guy killed isn't me (multiplayer only)
943  /*
944  if ( (Game_mode & GM_MULTIPLAYER) && (ship_obj != Player_obj) ) {
945  char death_text[256];
946 
947  player_generate_death_text( player_p, death_text );
948  HUD_sourced_printf(HUD_SOURCE_HIDDEN, death_text);
949  }
950  */
951 }
952 
953 /* JAS: THIS DOESN'T SEEM TO BE USED, SO I COMMENTED IT OUT
954 // Apply damage to a ship, destroying if necessary, etc.
955 // Returns portion of damage that exceeds ship shields, ie the "unused" portion of the damage.
956 // Note: This system does not use the mesh shield. It applies damage to the overall ship shield.
957 float apply_damage_to_ship(object *objp, float damage)
958 {
959  float _ss;
960 
961  add_shield_strength(objp, -damage);
962 
963  // check if shields are below 0%, if so take leftover damage and apply to ship integrity
964  if ((_ss = get_shield_strength(objp)) < 0.0f ) {
965  damage = -_ss;
966  set_shield_strength(objp, 0.0f);
967  } else
968  damage = 0.0f;
969 
970  return damage;
971 }
972 */
973 
974 // Do music processing for a ship hit.
975 void ship_hit_music(object *ship_objp, object *other_obj)
976 {
977  Assert(ship_objp); // Goober5000
978  Assert(other_obj); // Goober5000
979 
980  ship* ship_p = &Ships[ship_objp->instance];
981  object *parent;
982 
983  // Switch to battle track when a ship is hit by fire
984  //
985  // If the ship hit has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive
986  // ship, so don't start the battle music
987  if (!stricmp(Ai_class_names[Ai_info[ship_p->ai_index].ai_class], NOX("none")))
988  return;
989 
990  // Only start if ship hit and firing ship are from different teams
991  int attackee_team, attacker_team;
992 
993  attackee_team = Ships[ship_objp->instance].team;
994 
995  // avoid uninitialized value by matching them
996  attacker_team = attackee_team;
997 
998  switch ( other_obj->type )
999  {
1000  case OBJ_SHIP:
1001  attacker_team = Ships[other_obj->instance].team;
1002 
1003  // Nonthreatening ship collided with ship, no big deal
1004  if ( !stricmp(Ai_class_names[Ai_info[Ships[other_obj->instance].ai_index].ai_class], NOX("none")) )
1005  return;
1006 
1007  break;
1008 
1009  case OBJ_WEAPON:
1010  // parent of weapon is object num of ship that fired it
1011  parent = &Objects[other_obj->parent];
1012  if (parent->signature == other_obj->parent_sig)
1013  attacker_team = Ships[parent->instance].team;
1014  break;
1015 
1016  default:
1017  // Unexpected object type collided with ship, no big deal.
1018  return;
1019  }
1020 
1021  // start the music if it was an attacking ship
1022  if (iff_x_attacks_y(attacker_team, attackee_team))
1024 }
1025 
1026 // Make sparks fly off a ship.
1027 // Currently used in misison_parse to create partially damaged ships.
1028 // NOTE: hitpos is in model coordinates on the detail[0] submodel (highest detail hull)
1029 // WILL NOT WORK RIGHT IF ON A ROTATING SUBMODEL
1030 void ship_hit_sparks_no_rotate(object *ship_objp, vec3d *hitpos)
1031 {
1032  ship *ship_p = &Ships[ship_objp->instance];
1033 
1034  int n = ship_p->num_hits;
1035  if (n >= MAX_SHIP_HITS) {
1036  n = rand() % MAX_SHIP_HITS;
1037  } else {
1038  ship_p->num_hits++;
1039  }
1040 
1041  // No rotation. Just make the spark
1042  ship_p->sparks[n].pos = *hitpos;
1043  ship_p->sparks[n].submodel_num = -1;
1044 
1045  shipfx_emit_spark(ship_objp->instance, n); // Create the first wave of sparks
1046 
1047  if ( n == 0 ) {
1048  ship_p->next_hit_spark = timestamp(0); // when a hit spot will spark
1049  }
1050 }
1051 
1052 // find the max number of sparks allowed for ship
1053 // limited for fighter by hull % others by radius.
1054 int get_max_sparks(object* ship_objp)
1055 {
1056  Assert(ship_objp->type == OBJ_SHIP);
1057  Assert((ship_objp->instance >= 0) && (ship_objp->instance < MAX_SHIPS));
1058  if(ship_objp->type != OBJ_SHIP){
1059  return 1;
1060  }
1061  if((ship_objp->instance < 0) || (ship_objp->instance >= MAX_SHIPS)){
1062  return 1;
1063  }
1064 
1065  ship *ship_p = &Ships[ship_objp->instance];
1066  ship_info* si = &Ship_info[ship_p->ship_info_index];
1067  if (si->flags & SIF_FIGHTER) {
1068  float hull_percent = ship_objp->hull_strength / ship_p->ship_max_hull_strength;
1069 
1070  if (hull_percent > 0.8f) {
1071  return 1;
1072  } else if (hull_percent > 0.3f) {
1073  return 2;
1074  } else {
1075  return 3;
1076  }
1077  } else {
1078  int num_sparks = (int) (ship_objp->radius * 0.08f);
1079  if (num_sparks < 3) {
1080  return 3;
1081  } else if (num_sparks > MAX_SHIP_HITS) {
1082  return MAX_SHIP_HITS;
1083  } else {
1084  return num_sparks;
1085  }
1086  }
1087 }
1088 
1089 
1090 // helper function to std::sort, sorting spark pairs by distance
1091 int spark_compare(const spark_pair &pair1, const spark_pair &pair2)
1092 {
1093  Assert(pair1.dist >= 0);
1094  Assert(pair2.dist >= 0);
1095 
1096  return (pair1.dist < pair2.dist);
1097 }
1098 
1099 // for big ships, when all spark slots are filled, make intelligent choice of one to be recycled
1100 int choose_next_spark(object *ship_objp, vec3d *hitpos)
1101 {
1102  int i, j, count, num_sparks, num_spark_pairs, spark_num;
1103  vec3d world_hitpos[MAX_SHIP_HITS];
1104  spark_pair spark_pairs[MAX_SPARK_PAIRS];
1105  ship *shipp = &Ships[ship_objp->instance];
1106 
1107  // only choose next spark when all slots are full
1108  Assert(get_max_sparks(ship_objp) == Ships[ship_objp->instance].num_hits);
1109 
1110  // get num_sparks
1111  num_sparks = shipp->num_hits;
1112  Assert(num_sparks <= MAX_SHIP_HITS);
1113 
1114  // get num_spark_paris -- only sort these
1115  num_spark_pairs = (num_sparks * num_sparks - num_sparks) / 2;
1116 
1117  // get the world hitpos for all sparks
1118  for (spark_num=0; spark_num<num_sparks; spark_num++) {
1119  if (shipp->sparks[spark_num].submodel_num != -1) {
1120  model_instance_find_world_point(&world_hitpos[spark_num], &shipp->sparks[spark_num].pos, shipp->model_instance_num, shipp->sparks[spark_num].submodel_num, &ship_objp->orient, &ship_objp->pos);
1121  } else {
1122  // rotate sparks correctly with current ship orient
1123  vm_vec_unrotate(&world_hitpos[spark_num], &shipp->sparks[spark_num].pos, &ship_objp->orient);
1124  vm_vec_add2(&world_hitpos[spark_num], &ship_objp->pos);
1125  }
1126  }
1127 
1128  // check we're not making a spark in the same location as a current one
1129  for (i=0; i<num_sparks; i++) {
1130  float dist = vm_vec_dist_squared(&world_hitpos[i], hitpos);
1131  if (dist < 1) {
1132  return i;
1133  }
1134  }
1135 
1136  // not same location, so maybe do random recyling
1137  if (frand() > 0.5f) {
1138  return (rand() % num_sparks);
1139  }
1140 
1141  // initialize spark pairs
1142  for (i=0; i<num_spark_pairs; i++) {
1143  spark_pairs[i].index1 = 0;
1144  spark_pairs[i].index2 = 0;
1145  spark_pairs[i].dist = FLT_MAX;
1146  }
1147 
1148  // set spark pairs
1149  count = 0;
1150  for (i=1; i<num_sparks; i++) {
1151  for (j=0; j<i; j++) {
1152  spark_pairs[count].index1 = i;
1153  spark_pairs[count].index2 = j;
1154  spark_pairs[count++].dist = vm_vec_dist_squared(&world_hitpos[i], &world_hitpos[j]);
1155  }
1156  }
1157  Assert(count == num_spark_pairs);
1158 
1159  // sort pairs
1160  std::sort(spark_pairs, spark_pairs + count, spark_compare);
1161  //mprintf(("Min spark pair dist %.1f\n", spark_pairs[0].dist));
1162 
1163  // look through the first few sorted pairs, counting number of indices of closest pair
1164  int index1 = spark_pairs[0].index1;
1165  int index2 = spark_pairs[0].index2;
1166  int count1 = 0;
1167  int count2 = 0;
1168 
1169  for (i=1; i<6; i++) {
1170  if (spark_pairs[i].index1 == index1) {
1171  count1++;
1172  }
1173  if (spark_pairs[i].index2 == index1) {
1174  count1++;
1175  }
1176  if (spark_pairs[i].index1 == index2) {
1177  count2++;
1178  }
1179  if (spark_pairs[i].index2 == index2) {
1180  count2++;
1181  }
1182  }
1183 
1184  // recycle spark which has most indices in sorted list of pairs
1185  if (count1 > count2) {
1186  return index1;
1187  } else {
1188  return index2;
1189  }
1190 }
1191 
1192 
1193 // Make sparks fly off a ship.
1194 void ship_hit_create_sparks(object *ship_objp, vec3d *hitpos, int submodel_num)
1195 {
1196  vec3d tempv;
1197  ship *shipp = &Ships[ship_objp->instance];
1198  ship_info *sip = &Ship_info[shipp->ship_info_index];
1199 
1200  int n, max_sparks;
1201 
1202  n = shipp->num_hits;
1203  max_sparks = get_max_sparks(ship_objp);
1204 
1205  if (n >= max_sparks) {
1206  if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1207  // large ship, choose intelligently
1208  n = choose_next_spark(ship_objp, hitpos);
1209  } else {
1210  // otherwise, normal choice
1211  n = rand() % max_sparks;
1212  }
1213  } else {
1214  shipp->num_hits++;
1215  }
1216 
1217  bool instancing = false;
1218  // decide whether to do instancing
1219  if (submodel_num != -1) {
1220  polymodel *pm = model_get(sip->model_num);
1221  if (pm->detail[0] != submodel_num) {
1222  // submodel is not hull
1223  // OPTIMIZE ... check if submodel can not rotate
1224  instancing = true;
1225  }
1226  }
1227 
1228  if (instancing) {
1229  // get the hit position in the subobject RF
1230  vec3d temp_zero, temp_x, temp_y, temp_z;
1231  model_instance_find_world_point(&temp_zero, &vmd_zero_vector, shipp->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
1232  model_instance_find_world_point(&temp_x, &vmd_x_vector, shipp->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
1233  model_instance_find_world_point(&temp_y, &vmd_y_vector, shipp->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
1234  model_instance_find_world_point(&temp_z, &vmd_z_vector, shipp->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
1235 
1236  // find submodel x,y,z axes
1237  vm_vec_sub2(&temp_x, &temp_zero);
1238  vm_vec_sub2(&temp_y, &temp_zero);
1239  vm_vec_sub2(&temp_z, &temp_zero);
1240 
1241  // find displacement from submodel origin
1242  vec3d diff;
1243  vm_vec_sub(&diff, hitpos, &temp_zero);
1244 
1245  // find displacement from submodel origin in submodel RF
1246  shipp->sparks[n].pos.xyz.x = vm_vec_dot(&diff, &temp_x);
1247  shipp->sparks[n].pos.xyz.y = vm_vec_dot(&diff, &temp_y);
1248  shipp->sparks[n].pos.xyz.z = vm_vec_dot(&diff, &temp_z);
1249  shipp->sparks[n].submodel_num = submodel_num;
1250  shipp->sparks[n].end_time = timestamp(-1);
1251  } else {
1252  // Rotate hitpos into ship_objp's frame of reference.
1253  vm_vec_sub(&tempv, hitpos, &ship_objp->pos);
1254  vm_vec_rotate(&shipp->sparks[n].pos, &tempv, &ship_objp->orient);
1255  shipp->sparks[n].submodel_num = -1;
1256  shipp->sparks[n].end_time = timestamp(-1);
1257  }
1258 
1259  // Create the first wave of sparks
1260  shipfx_emit_spark(ship_objp->instance, n);
1261 
1262  if ( n == 0 ) {
1263  shipp->next_hit_spark = timestamp(0); // when a hit spot will spark
1264  }
1265 }
1266 
1267 // Called from ship_hit_kill() when we detect the player has been killed.
1268 void player_died_start(object *killer_objp)
1269 {
1270  nprintf(("Network", "starting my player death\n"));
1272 
1273 /* vm_vec_scale_add(&Dead_camera_pos, &Player_obj->pos, &Player_obj->orient.fvec, -10.0f);
1274  vm_vec_scale_add2(&Dead_camera_pos, &Player_obj->orient.uvec, 3.0f);
1275  vm_vec_scale_add2(&Dead_camera_pos, &Player_obj->orient.rvec, 5.0f);
1276 */
1277 
1278  // Create a good vector for the camera to move along during death sequence.
1279  object *other_objp = NULL;
1280 
1281  // on multiplayer clients, there have been occasions where we haven't been able to determine
1282  // the killer of a ship (due to bogus/mismatched/timed-out signatures on the client side). If
1283  // we don't know the killer, use the Player_obj as the other_objp for camera position.
1284  if ( killer_objp ) {
1285  switch (killer_objp->type) {
1286  case OBJ_WEAPON:
1287  case OBJ_SHOCKWAVE:
1288  other_objp = &Objects[killer_objp->parent];
1289  break;
1290  case OBJ_SHIP:
1291  case OBJ_DEBRIS:
1292  case OBJ_ASTEROID:
1293  case OBJ_NONE: // Something that just got deleted due to also dying -- it happened to me! --MK.
1294  other_objp = killer_objp;
1295  break;
1296 
1297  case OBJ_BEAM:
1298  int beam_obj_parent;
1299  beam_obj_parent = beam_get_parent(killer_objp);
1300  if(beam_obj_parent == -1){
1301  other_objp = killer_objp;
1302  } else {
1303  other_objp = &Objects[beam_obj_parent];
1304  }
1305  break;
1306 
1307  default:
1308  Int3(); // Killed by an object of a peculiar type. What is it?
1309  other_objp = killer_objp; // Enable to continue, just in case we shipped it with this bug...
1310  }
1311  } else {
1312  other_objp = Player_obj;
1313  }
1314 
1315  vm_vec_add(&Original_vec_to_deader, &Player_obj->orient.vec.fvec, &Player_obj->orient.vec.rvec);
1316  vm_vec_scale(&Original_vec_to_deader, 2.0f);
1317  vm_vec_add2(&Original_vec_to_deader, &Player_obj->orient.vec.uvec);
1318  vm_vec_normalize(&Original_vec_to_deader);
1319 
1320  vec3d vec_from_killer;
1321  vec3d *side_vec;
1322  float dist;
1323 
1324  Assert(other_objp != NULL);
1325 
1326  if (Player_obj == other_objp) {
1327  dist = 50.0f;
1328  vec_from_killer = Player_obj->orient.vec.fvec;
1329  } else {
1330  dist = vm_vec_normalized_dir(&vec_from_killer, &Player_obj->pos, &other_objp->pos);
1331  }
1332 
1333  if (dist > 100.0f)
1334  dist = 100.0f;
1335  vm_vec_scale_add(&Dead_camera_pos, &Player_obj->pos, &vec_from_killer, dist);
1336 
1337  float dot = vm_vec_dot(&Player_obj->orient.vec.rvec, &vec_from_killer);
1338  if (fl_abs(dot) > 0.8f)
1339  side_vec = &Player_obj->orient.vec.fvec;
1340  else
1341  side_vec = &Player_obj->orient.vec.rvec;
1342 
1343  vm_vec_scale_add2(&Dead_camera_pos, side_vec, 10.0f);
1344 
1345  Player_ai->target_objnum = -1; // Clear targeting. Otherwise, camera pulls away from player as soon as he blows up.
1346 
1347  // stop any playing emp effect
1348  emp_stop_local();
1349 }
1350 
1351 
1352 //#define DEATHROLL_TIME 3000 // generic deathroll is 3 seconds (3 * 1000 milliseconds) - Moved to ships.tbl
1353 #define MIN_PLAYER_DEATHROLL_TIME 1000 // at least one second deathroll for a player
1354 #define DEATHROLL_ROTVEL_CAP 6.3f // maximum added deathroll rotvel in rad/sec (about 1 rev / sec)
1355 #define DEATHROLL_ROTVEL_MIN 0.8f // minimum added deathroll rotvel in rad/sec (about 1 rev / 12 sec)
1356 #define DEATHROLL_MASS_STANDARD 50 // approximate mass of lightest ship
1357 #define DEATHROLL_VELOCITY_STANDARD 70 // deathroll rotvel is scaled according to ship velocity
1358 #define DEATHROLL_ROTVEL_SCALE 4 // constant determines how quickly deathroll rotvel is ramped up (smaller is faster)
1359 
1360 void saturate_fabs(float *f, float max)
1361 {
1362  if ( fl_abs(*f) > max) {
1363  if (*f > 0.0f)
1364  *f = max;
1365  else
1366  *f = -max;
1367  }
1368 }
1369 
1370 // function to do generic things when a ship explodes
1371 void ship_generic_kill_stuff( object *objp, float percent_killed )
1372 {
1373  float rotvel_mag;
1374  int delta_time;
1375  ship *sp;
1376 
1377  Assert(objp->type == OBJ_SHIP);
1378  Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS );
1379  if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1380  return;
1381  }
1382  sp = &Ships[objp->instance];
1383  ship_info *sip = &Ship_info[sp->ship_info_index];
1384 
1385  ai_announce_ship_dying(objp);
1386 
1387  ship_stop_fire_primary(objp); //mostly for stopping fighter beam looping sounds -Bobboau
1388 
1389  sp->flags |= SF_DYING;
1391  delta_time = (int) (sip->death_roll_base_time);
1392 
1393  // For smaller ships, subtract off time proportional to excess damage delivered.
1394  if (objp->radius < BIG_SHIP_MIN_RADIUS)
1395  delta_time -= (int) (1.01f - 4*percent_killed);
1396 
1397  // Cut down cargo death rolls. Looks a little silly. -- MK, 3/30/98.
1398  if (sip->flags & SIF_CARGO) {
1399  delta_time /= 4;
1400  }
1401 
1402  // Prevent bogus timestamps.
1403  if (delta_time < 2)
1404  delta_time = 2;
1405 
1406  if (objp->flags & OF_PLAYER_SHIP) {
1407  // Note: Kamikaze ships have no minimum death time.
1408  if (!(Ai_info[Ships[objp->instance].ai_index].ai_flags & AIF_KAMIKAZE) && (delta_time < MIN_PLAYER_DEATHROLL_TIME))
1409  delta_time = MIN_PLAYER_DEATHROLL_TIME;
1410  }
1411 
1412  //nprintf(("AI", "ShipHit.cpp: Frame %i, Gametime = %7.3f, Ship %s will die in %7.3f seconds.\n", Framecount, f2fl(Missiontime), Ships[objp->instance].ship_name, (float) delta_time/1000.0f));
1413 
1414  // Make big ships have longer deathrolls.
1415  // This is debug code by MK to increase the deathroll time so ships have time to evade the shockwave.
1416  // Perhaps deathroll time should be specified in ships.tbl.
1417  float damage = ship_get_exp_damage(objp);
1418 
1419  if (damage >= 250.0f)
1420  delta_time += 3000 + (int)(damage*4.0f + 4.0f*objp->radius);
1421 
1423  delta_time = 2;
1424 
1425  // Knossos gets 7-10 sec to die, time for "little" explosions
1426  if (Ship_info[sp->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
1427  delta_time = 7000 + (int)(frand() * 3000.0f);
1428  Ship_info[sp->ship_info_index].explosion_propagates = 0;
1429  }
1430 
1431  // Goober5000 - ship-specific deathroll time, woot
1432  if (sp->special_exp_deathroll_time > 0)
1433  {
1434  delta_time = sp->special_exp_deathroll_time;
1435 
1436  // prevent bogus timestamps, per comment several lines up
1437  if (delta_time < 2)
1438  delta_time = 2;
1439  }
1440 
1441  sp->death_time = sp->final_death_time = timestamp(delta_time); // Give him 3 secs to explode
1442 
1443  //SUSHI: What are the chances of an instant vaporization? Check the ship type first (objecttypes.tbl), then the ship (ships.tbl)
1444  ship_type_info *stp = &Ship_types[sip->class_type];
1445  float vapChance = stp->vaporize_chance;
1446  if (sip->vaporize_chance > 0.0f)
1447  vapChance = sip->vaporize_chance;
1448 
1449  if (sp->flags & SF_VAPORIZE || frand() < vapChance) {
1450  // Assert(Ship_info[sp->ship_info_index].flags & SIF_SMALL_SHIP);
1451 
1452  // LIVE FOR 100 MS
1453  sp->final_death_time = timestamp(100);
1454  }
1455 
1456  //nprintf(("AI", "Time = %7.3f: final_death_time set to %7.3f\n", (float) timestamp_ticker/1000.0f, (float) sp->final_death_time/1000.0f));
1457 
1458  sp->pre_death_explosion_happened = 0; // The little fireballs haven't came in yet.
1459 
1460  sp->next_fireball = timestamp(0); //start one right away
1461 
1462  ai_deathroll_start(objp);
1463 
1464  // play death roll begin sound
1466  if (objp == Player_obj)
1467  joy_ff_deathroll();
1468 
1469  // apply a whack
1470  // rotational velocity proportional to original translational velocity, with a bit added in.
1471  // Also, preserve half of original rotational velocity.
1472 
1473  // At standard speed (70) and standard mass (50), deathroll rotvel should be capped at DEATHROLL_ROTVEL_CAP
1474  // Minimum deathroll velocity is set DEATHROLL_ROTVEL_MIN
1475  // At lower speed, lower death rotvel (scaled linearly)
1476  // At higher mass, lower death rotvel (scaled logarithmically)
1477  // variable scale calculates the deathroll rotational velocity magnitude
1478  float logval = (float) log10(objp->phys_info.mass / (0.05f*DEATHROLL_MASS_STANDARD));
1479  float velval = ((vm_vec_mag_quick(&objp->phys_info.vel) + 3.0f) / DEATHROLL_VELOCITY_STANDARD);
1481 
1482  rotvel_mag = (float) DEATHROLL_ROTVEL_MIN * 2.0f/(logval + 2.0f);
1483  rotvel_mag += (float) (p1 * velval/logval) * 0.75f;
1484 
1485  // set so maximum velocity from rotation is less than 200
1486  if (rotvel_mag*objp->radius > 150) {
1487  rotvel_mag = 150.0f / objp->radius;
1488  }
1489 
1490  if (object_is_dead_docked(objp)) {
1491  // don't change current rotvel
1492  sp->deathroll_rotvel = objp->phys_info.rotvel;
1493  } else {
1494  // if added rotvel is too random, we should decrease the random component, putting a const in front of the rotvel.
1495  sp->deathroll_rotvel = objp->phys_info.rotvel;
1496  sp->deathroll_rotvel.xyz.x += (frand() - 0.5f) * 2.0f * rotvel_mag;
1498  sp->deathroll_rotvel.xyz.y += (frand() - 0.5f) * 3.0f * rotvel_mag;
1500  sp->deathroll_rotvel.xyz.z += (frand() - 0.5f) * 6.0f * rotvel_mag;
1501  // make z component 2x larger than larger of x,y
1502  float largest_mag = MAX(fl_abs(sp->deathroll_rotvel.xyz.x), fl_abs(sp->deathroll_rotvel.xyz.y));
1503  if (fl_abs(sp->deathroll_rotvel.xyz.z) < 2.0f*largest_mag) {
1504  sp->deathroll_rotvel.xyz.z *= (2.0f * largest_mag / fl_abs(sp->deathroll_rotvel.xyz.z));
1505  }
1507  // nprintf(("Physics", "Frame: %i rotvel_mag: %5.2f, rotvel: (%4.2f, %4.2f, %4.2f)\n", Framecount, rotvel_mag, sp->deathroll_rotvel.x, sp->deathroll_rotvel.y, sp->deathroll_rotvel.z));
1508  }
1509 
1510 
1511  // blow out his reverse thrusters. Or drag, same thing.
1512  objp->phys_info.rotdamp = (float) DEATHROLL_ROTVEL_SCALE / rotvel_mag;
1513  objp->phys_info.side_slip_time_const = 10000.0f;
1514 
1515  vm_vec_zero(&objp->phys_info.max_vel); // make so he can't turn on his own VOLITION anymore.
1516 
1517  vm_vec_zero(&objp->phys_info.max_rotvel); // make so he can't change speed on his own VOLITION anymore.
1518 
1519 }
1520 
1521 // called from ship_hit_kill if the ship is vaporized
1523 {
1524  object *ship_objp;
1525 
1526  // sanity
1527  Assert(shipp != NULL);
1528  if(shipp == NULL){
1529  return;
1530  }
1531  Assert((shipp->objnum >= 0) && (shipp->objnum < MAX_OBJECTS));
1532  if((shipp->objnum < 0) || (shipp->objnum >= MAX_OBJECTS)){
1533  return;
1534  }
1535  ship_objp = &Objects[shipp->objnum];
1536 
1537  // create debris shards
1538  create_vaporize_debris(ship_objp, &ship_objp->pos);
1539 }
1540 
1541 // *ship_objp was hit and we've determined he's been killed! By *other_obj!
1542 void ship_hit_kill(object *ship_objp, object *other_obj, float percent_killed, int self_destruct)
1543 {
1544  Assert(ship_objp); // Goober5000 - but not other_obj, not only for sexp but also for self-destruct
1545 
1546  Script_system.SetHookObject("Self", ship_objp);
1547  if(other_obj != NULL)
1548  Script_system.SetHookObject("Killer", other_obj);
1549  else
1550  Script_system.SetHookObject("Killer", 0);
1551 
1553  {
1554  //WMC - Do scripting stuff
1555  Script_system.RunCondition(CHA_DEATH, 0, NULL, ship_objp);
1556  Script_system.RemHookVars(2, "Self", "Killer");
1557  return;
1558  }
1559 
1560  ship *sp;
1561  char *killer_ship_name;
1562  int killer_damage_percent = 0;
1563  int killer_index = -1;
1564  object *killer_objp = NULL;
1565 
1566  sp = &Ships[ship_objp->instance];
1567  show_dead_message(ship_objp, other_obj);
1568 
1569  if (ship_objp == Player_obj) {
1570  player_died_start(other_obj);
1571  }
1572 
1573  // maybe vaporize him
1574  if(sp->flags & SF_VAPORIZE){
1575  ship_vaporize(sp);
1576  }
1577 
1578  // hehe
1579  extern void game_tst_mark(object *objp, ship *shipp);
1580  game_tst_mark(ship_objp, sp);
1581 
1582  // single player and multiplayer masters evaluate the scoring and kill stuff
1583  if ( !MULTIPLAYER_CLIENT ) {
1584  killer_index = scoring_eval_kill( ship_objp );
1585 
1586  // ship is destroyed -- send this event to the mission log stuff to record this event. Try to find who
1587  // killed this ship. scoring_eval_kill above should leave the obj signature of the ship who killed
1588  // this guy (or a -1 if no one got the kill).
1589  killer_ship_name = NULL;
1590  killer_damage_percent = -1;
1591  if ( killer_index >= 0 ) {
1592  object *objp;
1593  int sig;
1594 
1595  sig = sp->damage_ship_id[killer_index];
1596  for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
1597  if ( objp->signature == sig ){
1598  break;
1599  }
1600  }
1601  // if the object isn't around, the try to find the object in the list of ships which has exited
1602  if ( objp != END_OF_LIST(&obj_used_list) ) {
1603  Assert ( (objp->type == OBJ_SHIP ) || (objp->type == OBJ_GHOST) ); // I suppose that this should be true
1604  killer_ship_name = Ships[objp->instance].ship_name;
1605 
1606  killer_objp = objp;
1607  } else {
1608  int ei;
1609 
1611  if ( ei != -1 ){
1612  killer_ship_name = Ships_exited[ei].ship_name;
1613  }
1614  }
1615  killer_damage_percent = (int)(sp->damage_ship[killer_index]/sp->total_damage_received * 100.0f);
1616  }
1617 
1618  if(!self_destruct){
1619  // multiplayer
1620  if(Game_mode & GM_MULTIPLAYER){
1621  char name1[256] = "";
1622  char name2[256] = "";
1623  int np_index;
1624 
1625  // get first name
1626  np_index = multi_find_player_by_object(ship_objp);
1627  if((np_index >= 0) && (np_index < MAX_PLAYERS) && (Net_players[np_index].m_player != NULL)){
1628  strcpy_s(name1, Net_players[np_index].m_player->callsign);
1629  } else {
1630  strcpy_s(name1, sp->ship_name);
1631  }
1632 
1633  // argh
1634  if((killer_objp != NULL) || (killer_ship_name != NULL)){
1635 
1636  // second name
1637  if(killer_objp == NULL){
1638  strcpy_s(name2, killer_ship_name);
1639  } else {
1640  np_index = multi_find_player_by_object(killer_objp);
1641  if((np_index >= 0) && (np_index < MAX_PLAYERS) && (Net_players[np_index].m_player != NULL)){
1642  strcpy_s(name2, Net_players[np_index].m_player->callsign);
1643  } else {
1644  strcpy_s(name2, killer_ship_name);
1645  }
1646  }
1647  }
1648 
1649  mission_log_add_entry(LOG_SHIP_DESTROYED, name1, name2, killer_damage_percent);
1650  } else {
1651  // DKA: 8/23/99 allow message log in single player with no killer name
1652  //if(killer_ship_name != NULL){
1653  mission_log_add_entry(LOG_SHIP_DESTROYED, sp->ship_name, killer_ship_name, killer_damage_percent);
1654  //}
1655  }
1656  }
1657 
1658  // maybe praise the player for this kill
1659  if ( (killer_damage_percent > 10) && (other_obj != NULL) ) {
1660  if (other_obj->parent_sig == Player_obj->signature) {
1662  }
1663  else if ((other_obj->parent_type == OBJ_SHIP) || (other_obj->parent_type == OBJ_START)) {
1664  ship_maybe_praise_self(sp, &Ships[Objects[other_obj->parent].instance]);
1665  }
1666  }
1667  }
1668 
1669  ship_generic_kill_stuff( ship_objp, percent_killed );
1670 
1671  // mwa -- removed 2/25/98 -- why is this here? ship_objp->flags &= ~(OF_PLAYER_SHIP);
1672  // if it is for observers, must deal with it a separate way!!!!
1673  if ( MULTIPLAYER_MASTER ) {
1674  // check to see if this ship needs to be respawned
1675  multi_respawn_check(ship_objp);
1676 
1677  // send the kill packet to all players
1678  // maybe send vaporize packet to all players
1679  send_ship_kill_packet( ship_objp, other_obj, percent_killed, self_destruct );
1680  }
1681 
1682  // if a non-player is dying, play a scream
1683  if ( !(ship_objp->flags & OF_PLAYER_SHIP) ) {
1684  ship_maybe_scream(sp);
1685  }
1686 
1687  // if the player is dying, have wingman lament
1688  if ( ship_objp == Player_obj ) {
1690  }
1691 
1692  Script_system.RunCondition(CHA_DEATH, 0, NULL, ship_objp);
1693  Script_system.RemHookVars(2, "Self", "Killer");
1694 }
1695 
1696 // function to simply explode a ship where it is currently at
1697 void ship_self_destruct( object *objp )
1698 {
1699  Assert ( objp->type == OBJ_SHIP );
1700 
1701  // try and find a player
1702  if((Game_mode & GM_MULTIPLAYER) && (multi_find_player_by_object(objp) >= 0)){
1703  int np_index = multi_find_player_by_object(objp);
1704  if((np_index >= 0) && (np_index < MAX_PLAYERS) && (Net_players[np_index].m_player != NULL)){
1705  mission_log_add_entry(LOG_SELF_DESTRUCTED, Net_players[np_index].m_player->callsign, NULL );
1706  } else {
1708  }
1709  } else {
1711  }
1712 
1713  // check to see if this ship needs to be respawned
1714  if(MULTIPLAYER_MASTER){
1715  // player ship?
1716  int np_index = multi_find_player_by_object(objp);
1717  if((np_index >= 0) && (np_index < MAX_PLAYERS) && MULTI_CONNECTED(Net_players[np_index]) && (Net_players[np_index].m_player != NULL)){
1718  char msg[512] = "";
1719  sprintf(msg, "%s %s", Net_players[np_index].m_player->callsign, XSTR("Self destructed", 1476));
1720 
1721  // send a message
1722  send_game_chat_packet(Net_player, msg, MULTI_MSG_ALL, NULL, NULL, 2);
1723 
1724  // printf
1726  HUD_printf(msg);
1727  }
1728  }
1729  }
1730 
1731  // self destruct
1732  ship_hit_kill(objp, NULL, 1.0f, 1);
1733 }
1734 
1735 extern int Homing_hits, Homing_misses;
1736 
1737 // Call this instead of physics_apply_whack directly to
1738 // deal with two docked ships properly.
1739 // Goober5000 - note... hit_pos is in *local* coordinates
1740 void ship_apply_whack(vec3d *force, vec3d *hit_pos, object *objp)
1741 {
1742  if (objp == Player_obj) {
1743  nprintf(("Sandeep", "Playing stupid joystick effect\n"));
1744  vec3d test;
1745  vm_vec_unrotate(&test, force, &objp->orient);
1746 
1747  game_whack_apply( -test.xyz.x, -test.xyz.y );
1748  }
1749 
1750  if (object_is_docked(objp))
1751  {
1752  float overall_mass = dock_calc_total_docked_mass(objp);
1753 
1754  // Goober5000 - this code attempts to account properly for whacking a docked object as one mass.
1755  // It isn't perfect, because physics doesn't completely account for it (particularly because it
1756  // still uses the moment of inertia for the whacked object, not for all objects). Commenting
1757  // out the contents of the block restores the Volition behavior, but it doesn't calculate the
1758  // correct torque.
1759  // Addendum: this block is now not executed for docked fighters or bombers because the whack
1760  // looks like the fighter is doing evasive maneuvers
1761  if ((objp->type != OBJ_SHIP) || !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)))
1762  {
1763  vec3d world_hit_pos, world_center_pos;
1764 
1765  // calc world hit pos of the hit ship
1766  vm_vec_unrotate(&world_hit_pos, hit_pos, &objp->orient);
1767  vm_vec_add2(&world_hit_pos, &objp->pos);
1768 
1769  // calc overall world center of ships
1770  dock_calc_docked_center(&world_center_pos, objp);
1771 
1772  // the new hitpos is the vector from world center to world hitpos
1773  vm_vec_sub(hit_pos, &world_hit_pos, &world_center_pos);
1774  }
1775 
1776  // whack it
1777  physics_apply_whack(force, hit_pos, &objp->phys_info, &objp->orient, overall_mass);
1778  }
1779  else
1780  {
1781  physics_apply_whack(force, hit_pos, &objp->phys_info, &objp->orient, objp->phys_info.mass);
1782  }
1783 }
1784 
1785 // If a ship is dying and it gets hit, shorten its deathroll.
1786 // But, if it's a player, don't decrease below MIN_PLAYER_DEATHROLL_TIME
1787 void shiphit_hit_after_death(object *ship_objp, float damage)
1788 {
1789  float percent_killed;
1790  int delta_time, time_remaining;
1791  ship *shipp = &Ships[ship_objp->instance];
1792  ship_info *sip = &Ship_info[shipp->ship_info_index];
1793 
1794  // Since the explosion has two phases (final_death_time and really_final_death_time)
1795  // we should only shorten the deathroll time if that is the phase we're in.
1796  // And you can tell by seeing if the timestamp is valid, since it gets set to
1797  // invalid after it does the first large explosion.
1798  if ( !timestamp_valid(shipp->final_death_time) ) {
1799  return;
1800  }
1801 
1802  // Don't adjust vaporized ship
1803  if (shipp->flags & SF_VAPORIZE) {
1804  return;
1805  }
1806 
1807  // Don't shorten deathroll on very large ships.
1808  if (ship_objp->radius > BIG_SHIP_MIN_RADIUS)
1809  return;
1810 
1811  percent_killed = damage/shipp->ship_max_hull_strength;
1812  if (percent_killed > 1.0f)
1813  percent_killed = 1.0f;
1814 
1815  delta_time = (int) (4 * sip->death_roll_base_time * percent_killed);
1816  time_remaining = timestamp_until(shipp->final_death_time);
1817 
1818  //nprintf(("AI", "Gametime = %7.3f, Time until %s dies = %7.3f, delta = %7.3f\n", f2fl(Missiontime), Ships[ship_objp->instance].ship_name, (float)time_remaining/1000.0f, delta_time));
1819  if (ship_objp->flags & OF_PLAYER_SHIP)
1820  if (time_remaining < MIN_PLAYER_DEATHROLL_TIME)
1821  return;
1822 
1823  // nprintf(("AI", "Subtracting off %7.3f seconds from deathroll, reducing to %7.3f\n", (float) delta_time/1000.0f, (float) (time_remaining - delta_time)/1000.0f));
1824 
1825  delta_time = time_remaining - delta_time;
1826  if (ship_objp->flags & OF_PLAYER_SHIP)
1827  if (delta_time < MIN_PLAYER_DEATHROLL_TIME)
1828  delta_time = MIN_PLAYER_DEATHROLL_TIME;
1829 
1830  // Prevent bogus timestamp.
1831  if (delta_time < 2)
1832  delta_time = 2;
1833 
1834  shipp->final_death_time = timestamp(delta_time); // Adjust time until explosion.
1835 }
1836 
1837 MONITOR( ShipHits )
1838 MONITOR( ShipNumDied )
1839 
1840 int maybe_shockwave_damage_adjust(object *ship_objp, object *other_obj, float *damage)
1841 {
1842  ship_subsys *subsys;
1843  ship *shipp;
1844  float dist, nearest_dist = FLT_MAX;
1845  vec3d g_subobj_pos;
1846  float max_damage;
1847  float inner_radius, outer_radius;
1848 
1849  Assert(ship_objp); // Goober5000 (but not other_obj in case of sexp)
1850  Assert(damage); // Goober5000
1851 
1852  Assert(ship_objp->type == OBJ_SHIP);
1853 
1854  if (!other_obj) {
1855  return 0;
1856  }
1857 
1858  if (other_obj->type != OBJ_SHOCKWAVE) {
1859  return 0;
1860  }
1861 
1862  if (!(Ship_info[Ships[ship_objp->instance].ship_info_index].flags & SIF_HUGE_SHIP)) {
1863  return 0;
1864  }
1865 
1866  shipp = &Ships[ship_objp->instance];
1867 
1868  // find closest subsystem distance to shockwave origin
1869  for (subsys=GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys) ) {
1870  get_subsystem_world_pos(ship_objp, subsys, &g_subobj_pos);
1871  dist = vm_vec_dist_quick(&g_subobj_pos, &other_obj->pos);
1872 
1873  if (dist < nearest_dist) {
1874  nearest_dist = dist;
1875  }
1876  }
1877 
1878  // get max damage and adjust if needed to account for shockwave created from destroyed weapon
1879  max_damage = shockwave_get_damage(other_obj->instance);
1880  if (shockwave_get_flags(other_obj->instance) & SW_WEAPON_KILL) {
1881  max_damage *= 4.0f;
1882  }
1883 
1884  outer_radius = shockwave_get_max_radius(other_obj->instance);
1885  inner_radius = shockwave_get_min_radius(other_obj->instance);
1886 
1887  // scale damage
1888  // floor of 25%, max if within inner_radius, linear between
1889  if (nearest_dist > outer_radius) {
1890  *damage = max_damage / 4.0f;
1891  } else if (nearest_dist < inner_radius) {
1892  *damage = max_damage;
1893  } else {
1894  *damage = max_damage * (1.0f - 0.75f * (nearest_dist - inner_radius) / (outer_radius - inner_radius));
1895  }
1896 
1897  return 1;
1898 }
1899 
1900 // ------------------------------------------------------------------------
1901 // ship_do_damage()
1902 //
1903 // Do damage assessment on a ship. This should only be called
1904 // internally by ship_apply_global_damage and ship_apply_local_damage
1905 //
1906 //
1907 // input: ship_objp => object pointer for ship receiving damage
1908 // other_obj => object pointer to object causing damage
1909 // hitpos => impact world pos on the ship
1910 // TODO: get a better value for hitpos
1911 // damage => damage to apply to the ship
1912 // quadrant => which part of shield takes damage, -1 if not shield hit
1913 // submodel_num=> which submodel was hit, -1 if none in particular
1914 // wash_damage => 1 if damage is done by engine wash
1915 // Goober5000 - sanity checked this whole function in the case that other_obj is NULL, which
1916 // will happen with the explosion-effect sexp
1917 void ai_update_lethality(object *ship_objp, object *weapon_obj, float damage);
1918 static void ship_do_damage(object *ship_objp, object *other_obj, vec3d *hitpos, float damage, int quadrant, int submodel_num, int wash_damage=0)
1919 {
1920 // mprintf(("doing damage\n"));
1921 
1922  ship *shipp;
1923  float subsystem_damage = damage; // damage to be applied to subsystems
1924  int other_obj_is_weapon;
1925  int other_obj_is_beam;
1926  int other_obj_is_shockwave;
1927  int other_obj_is_asteroid;
1928  int other_obj_is_debris;
1929  int other_obj_is_ship;
1930  float difficulty_scale_factor = 1.0f;
1931 
1932  Assert(ship_objp); // Goober5000
1933  Assert(hitpos); // Goober5000
1934 
1935  Assert(ship_objp->instance >= 0);
1936  Assert(ship_objp->type == OBJ_SHIP);
1937  shipp = &Ships[ship_objp->instance];
1938 
1939  // maybe adjust damage done by shockwave for BIG|HUGE
1940  maybe_shockwave_damage_adjust(ship_objp, other_obj, &damage);
1941 
1942  // Goober5000 - check to see what other_obj is
1943  if (other_obj)
1944  {
1945  other_obj_is_weapon = ((other_obj->type == OBJ_WEAPON) && (other_obj->instance >= 0) && (other_obj->instance < MAX_WEAPONS));
1946  other_obj_is_beam = ((other_obj->type == OBJ_BEAM) && (other_obj->instance >= 0) && (other_obj->instance < MAX_BEAMS));
1947  other_obj_is_shockwave = ((other_obj->type == OBJ_SHOCKWAVE) && (other_obj->instance >= 0) && (other_obj->instance < MAX_SHOCKWAVES));
1948  other_obj_is_asteroid = ((other_obj->type == OBJ_ASTEROID) && (other_obj->instance >= 0) && (other_obj->instance < MAX_ASTEROIDS));
1949  other_obj_is_debris = ((other_obj->type == OBJ_DEBRIS) && (other_obj->instance >= 0) && (other_obj->instance < MAX_DEBRIS_PIECES));
1950  other_obj_is_ship = ((other_obj->type == OBJ_SHIP) && (other_obj->instance >= 0) && (other_obj->instance < MAX_SHIPS));
1951  }
1952  else
1953  {
1954  other_obj_is_weapon = 0;
1955  other_obj_is_beam = 0;
1956  other_obj_is_shockwave = 0;
1957  other_obj_is_asteroid = 0;
1958  other_obj_is_debris = 0;
1959  other_obj_is_ship = 0;
1960  }
1961 
1962  // update lethality of ship doing damage - modified by Goober5000
1963  if (other_obj_is_weapon || other_obj_is_shockwave) {
1964  ai_update_lethality(ship_objp, other_obj, damage);
1965  }
1966 
1967  // if this is a weapon
1968  if (other_obj_is_weapon)
1969  damage *= weapon_get_damage_scale(&Weapon_info[Weapons[other_obj->instance].weapon_info_index], other_obj, ship_objp);
1970 
1971  MONITOR_INC( ShipHits, 1 );
1972 
1973  // Don't damage player ship in the process of warping out.
1975  if ( ship_objp == Player_obj ){
1976  return;
1977  }
1978  }
1979 
1980  if ( other_obj_is_weapon ) {
1981  // for tvt and dogfight missions, don't scale damage
1983  }
1984  else {
1985  // Do a little "skill" balancing for the player in single player and coop multiplayer
1986  if (ship_objp->flags & OF_PLAYER_SHIP) {
1987  // Nuke - store it in a couple factor and we will apply it where needed
1988  difficulty_scale_factor *= The_mission.ai_profile->player_damage_scale[Game_skill_level];
1989  }
1990  }
1991  }
1992 
1993  // if this is not a laser, or i'm not a multiplayer client
1994  // apply pain to me
1995 
1996  // Goober5000: make sure other_obj doesn't cause a read violation!
1997  if (other_obj && !(Ship_info[Ships[Player_obj->instance].ship_info_index].flags2 & SIF2_NO_PAIN_FLASH))
1998  {
1999  // For the record, ship_hit_pain seems to simply be the red flash that appears
2000  // on the screen when you're hit.
2001  int special_check = !MULTIPLAYER_CLIENT;
2002 
2003  // now the actual checks
2004  if (other_obj->type == OBJ_BEAM)
2005  {
2007  if (((Weapon_info[beam_get_weapon_info_index(other_obj)].subtype != WP_LASER) || special_check) && (Player_obj != NULL) && (ship_objp == Player_obj))
2008  {
2009  ship_hit_pain(damage * difficulty_scale_factor, quadrant);
2010  }
2011  }
2012  if (other_obj_is_weapon)
2013  {
2015  if (((Weapon_info[Weapons[other_obj->instance].weapon_info_index].subtype != WP_LASER) || special_check) && (Player_obj != NULL) && (ship_objp == Player_obj))
2016  {
2017  ship_hit_pain(damage * difficulty_scale_factor, quadrant);
2018  }
2019  }
2020  } // read violation sanity check
2021 
2022 
2023  // If the ship is invulnerable, do nothing
2024  if (ship_objp->flags & OF_INVULNERABLE) {
2025  return;
2026  }
2027 
2028  // if ship is already dying, shorten deathroll.
2029  if (shipp->flags & SF_DYING) {
2030  shiphit_hit_after_death(ship_objp, (damage * difficulty_scale_factor));
2031  return;
2032  }
2033 
2034  // If we hit the shield, reduce it's strength and found
2035  // out how much damage is left over.
2036  if ( quadrant >= 0 && !(ship_objp->flags & OF_NO_SHIELDS) ) {
2037 // mprintf(("applying damage ge to shield\n"));
2038  float shield_factor = -1.0f;
2039  int weapon_info_index;
2040 
2041  weapon_info_index = shiphit_get_damage_weapon(other_obj);
2042  if ( weapon_info_index >= 0 ) {
2043  shield_factor = Weapon_info[weapon_info_index].shield_factor;
2044  }
2045 
2046  if ( shield_factor >= 0.0f ) {
2047  damage *= shield_factor;
2048  subsystem_damage *= shield_factor;
2049  }
2050 
2051  if ( damage > 0.0f ) {
2052 
2053  float piercing_pct = 0.0f;
2054  int dmg_type_idx = -1;
2055 
2056  //Do armor stuff
2057  if(other_obj_is_weapon) {
2058  dmg_type_idx = Weapon_info[Weapons[other_obj->instance].weapon_info_index].damage_type_idx;
2059  } else if(other_obj_is_beam) {
2060  dmg_type_idx = Weapon_info[beam_get_weapon_info_index(other_obj)].damage_type_idx;
2061  } else if(other_obj_is_shockwave) {
2062  dmg_type_idx = shockwave_get_damage_type_idx(other_obj->instance);
2063  } else if(other_obj_is_asteroid) {
2064  dmg_type_idx = Asteroid_info[Asteroids[other_obj->instance].asteroid_type].damage_type_idx;
2065  } else if(other_obj_is_debris) {
2066  dmg_type_idx = Debris[other_obj->instance].damage_type_idx;
2067  } else if(other_obj_is_ship) {
2068  dmg_type_idx = Ships[other_obj->instance].collision_damage_type_idx;
2069  }
2070 
2071  if(shipp->shield_armor_type_idx != -1)
2072  {
2073  piercing_pct = Armor_types[shipp->shield_armor_type_idx].GetShieldPiercePCT(dmg_type_idx);
2074  }
2075 
2076  float pre_shield = damage; // Nuke: don't use the difficulty scaling in here, since its also applied in Armor_type.GetDamage. Don't want it to apply twice
2077  float pre_shield_ss = subsystem_damage; // Nuke - same here
2078 
2079  if (piercing_pct > 0.0f) {
2080  damage = pre_shield * (1.0f - piercing_pct);
2081  }
2082 
2083  // Nuke: apply pre_shield difficulty scaling here, since it was meant to be applied through damage
2084  pre_shield *= difficulty_scale_factor;
2085 
2086  if(shipp->shield_armor_type_idx != -1)
2087  {
2088  // Nuke: this call will decide when to use the damage factor, but it will get used, unless the modder is dumb (like setting +Difficulty Scale Type: to 'manual' and not manually applying it in their calculations)
2089  damage = Armor_types[shipp->shield_armor_type_idx].GetDamage(damage, dmg_type_idx, difficulty_scale_factor);
2090  } else { // Nuke: if that didn't get called, difficulty would not be applied to damage so apply it here
2091  damage *= difficulty_scale_factor;
2092  }
2093 
2094  damage = apply_damage_to_shield(ship_objp, quadrant, damage);
2095 
2096  if (damage > 0.0f) {
2097  subsystem_damage *= (damage / pre_shield);
2098  } else {
2099  subsystem_damage = 0.0f;
2100  }
2101 
2102  if (piercing_pct > 0.0f) {
2103  damage += (piercing_pct * pre_shield);
2104  subsystem_damage += (piercing_pct * pre_shield_ss);
2105  }
2106  }
2107 
2108  // if shield damage was increased, don't carry over leftover damage at scaled level
2109  if ( shield_factor > 1.0f ) {
2110  damage /= shield_factor;
2111 
2112  subsystem_damage /= shield_factor;
2113  }
2114  }
2115 
2116  // Apply leftover damage to the ship's subsystem and hull.
2117  if ( (damage > 0.0f) || (subsystem_damage > 0.0f) ) {
2118  int weapon_info_index;
2119  float pre_subsys = subsystem_damage; // Nuke: should be the last time we need to do this in this function
2120  bool apply_hull_armor = true;
2121  bool apply_diff_scale = true;
2122 
2123  subsystem_damage = do_subobj_hit_stuff(ship_objp, other_obj, hitpos, submodel_num, subsystem_damage, &apply_hull_armor);
2124 
2125  if (subsystem_damage > 0.0f) {
2126  damage *= (subsystem_damage / pre_subsys);
2127  } else {
2128  damage = 0.0f;
2129  }
2130 
2131  //Do armor stuff
2132  if (apply_hull_armor)
2133  {
2134  int dmg_type_idx = -1;
2135  if(other_obj_is_weapon) {
2136  dmg_type_idx = Weapon_info[Weapons[other_obj->instance].weapon_info_index].damage_type_idx;
2137  } else if(other_obj_is_beam) {
2138  dmg_type_idx = Weapon_info[beam_get_weapon_info_index(other_obj)].damage_type_idx;
2139  } else if(other_obj_is_shockwave) {
2140  dmg_type_idx = shockwave_get_damage_type_idx(other_obj->instance);
2141  } else if(other_obj_is_asteroid) {
2142  dmg_type_idx = Asteroid_info[Asteroids[other_obj->instance].asteroid_type].damage_type_idx;
2143  } else if(other_obj_is_debris) {
2144  dmg_type_idx = Debris[other_obj->instance].damage_type_idx;
2145  } else if(other_obj_is_ship) {
2146  dmg_type_idx = Ships[other_obj->instance].collision_damage_type_idx;
2147  }
2148 
2149  if(shipp->armor_type_idx != -1)
2150  {
2151  damage = Armor_types[shipp->armor_type_idx].GetDamage(damage, dmg_type_idx, difficulty_scale_factor);
2152  apply_diff_scale = false;
2153  }
2154  }
2155  // Nuke: this is done incase difficulty scaling is not applied into damage by getDamage() above
2156  if (apply_diff_scale) {
2157  damage *= difficulty_scale_factor; // Nuke: we can finally stop doing this now
2158  }
2159 
2160  // continue with damage?
2161  if (damage > 0.0f) {
2162  weapon_info_index = shiphit_get_damage_weapon(other_obj); // Goober5000 - a NULL other_obj returns -1
2163  if ( weapon_info_index >= 0 ) {
2164  if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
2165  damage /= 4;
2166  }
2167 
2168  damage *= Weapon_info[weapon_info_index].armor_factor;
2169  }
2170 
2171  // if ship is flagged as can not die, don't let it die
2172  if (shipp->ship_guardian_threshold > 0) {
2173  float min_hull_strength = 0.01f * shipp->ship_guardian_threshold * shipp->ship_max_hull_strength;
2174  if ( (ship_objp->hull_strength - damage) < min_hull_strength ) {
2175  // find damage needed to take object to min hull strength
2176  damage = ship_objp->hull_strength - min_hull_strength;
2177 
2178  // make sure damage is positive
2179  damage = MAX(0, damage);
2180  }
2181  }
2182 
2183  // multiplayer clients don't do damage
2184  if (((Game_mode & GM_MULTIPLAYER) && MULTIPLAYER_CLIENT)) {
2185  } else {
2186  // Check if this is simulated damage.
2187  weapon_info_index = shiphit_get_damage_weapon(other_obj);
2188  if ( weapon_info_index >= 0 ) {
2189  if (Weapon_info[weapon_info_index].wi_flags2 & WIF2_TRAINING) {
2190 // diag_printf2("Simulated Hull for Ship %s hit, dropping from %.32f to %d.\n", shipp->ship_name, (int) ( ship_objp->sim_hull_strength * 100 ), (int) ( ( ship_objp->sim_hull_strength - damage ) * 100 ) );
2191  ship_objp->sim_hull_strength -= damage;
2192  ship_objp->sim_hull_strength = MAX( 0, ship_objp->sim_hull_strength );
2193  return;
2194  }
2195  }
2196  ship_objp->hull_strength -= damage;
2197  }
2198 
2199  // let damage gauge know that player ship just took damage
2200  if ( Player_obj == ship_objp ) {
2202  }
2203 
2204  // DB - removed 1/12/99 - scoring code properly bails if MULTIPLAYER_CLIENT
2205  // in multiplayer, if I am not the host, get out of this function here!!
2206  //if ( MULTIPLAYER_CLIENT ) {
2207  //return;
2208  //}
2209 
2210  if (other_obj)
2211  {
2212  switch (other_obj->type)
2213  {
2214  case OBJ_SHOCKWAVE:
2215  scoring_add_damage(ship_objp,other_obj,damage);
2216  break;
2217  case OBJ_ASTEROID:
2218  // don't call scoring for asteroids
2219  break;
2220  case OBJ_WEAPON:
2221  if((other_obj->parent < 0) || (other_obj->parent >= MAX_OBJECTS)){
2222  scoring_add_damage(ship_objp, NULL, damage);
2223  } else {
2224  scoring_add_damage(ship_objp, &Objects[other_obj->parent], damage);
2225  }
2226  break;
2227  case OBJ_BEAM://give kills for fighter beams-Bobboau
2228  {
2229  int bobjn = beam_get_parent(other_obj);
2230 
2231  // Goober5000 - only count beams fired by fighters or bombers unless the ai profile says different
2232  if (bobjn >= 0)
2233  {
2236  !(Objects[bobjn].flags & OF_PLAYER_SHIP) ) {
2237  bobjn = -1;
2238  }
2239  }
2240 
2241  if(bobjn == -1){
2242  scoring_add_damage(ship_objp, NULL, damage);
2243  } else {
2244  scoring_add_damage(ship_objp, &Objects[bobjn], damage);
2245  }
2246  break;
2247  }
2248  default:
2249  break;
2250  }
2251  } // other_obj
2252 
2253  if (ship_objp->hull_strength <= 0.0f) {
2254  MONITOR_INC( ShipNumDied, 1 );
2255 
2256  ship_info *sip = &Ship_info[shipp->ship_info_index];
2257 
2258  // If massive beam hitting small ship, vaporize otherwise normal damage pipeline
2259  // Only vaporize once
2260  // multiplayer clients should skip this
2261  if(!MULTIPLAYER_CLIENT) {
2262  if ( !(shipp->flags & SF_VAPORIZE) ) {
2263  // Only small ships can be vaporized
2264  if (sip->flags & (SIF_SMALL_SHIP)) {
2265  if (other_obj) { // Goober5000 check for NULL
2266  if (other_obj->type == OBJ_BEAM)
2267  {
2268  int beam_weapon_info_index = beam_get_weapon_info_index(other_obj);
2269  if ( (beam_weapon_info_index > -1) && (Weapon_info[beam_weapon_info_index].wi_flags & (WIF_HUGE)) ) {
2270  // Flag as vaporized
2271  shipp->flags |= SF_VAPORIZE;
2272  }
2273  }
2274  }
2275  }
2276  }
2277  }
2278 
2279  // maybe engine wash death
2280  if (wash_damage) {
2281  shipp->wash_killed = 1;
2282  }
2283 
2284  float percent_killed = -get_hull_pct(ship_objp);
2285  if (percent_killed > 1.0f){
2286  percent_killed = 1.0f;
2287  }
2288 
2289  if ( !(shipp->flags & SF_DYING) && !MULTIPLAYER_CLIENT) { // if not killed, then kill
2290  ship_hit_kill(ship_objp, other_obj, percent_killed, 0);
2291  }
2292  }
2293  }
2294  }
2295 
2296  // if the hitting object is a weapon, maybe do some fun stuff here
2297  if(other_obj_is_weapon)
2298  {
2299  weapon_info *wip;
2300  Assert(other_obj->instance >= 0);
2301  if (other_obj->instance < 0) {
2302  return;
2303  }
2304  Assert(Weapons[other_obj->instance].weapon_info_index >= 0);
2305  if (Weapons[other_obj->instance].weapon_info_index < 0) {
2306  return;
2307  }
2308  wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
2309 
2310  // if its a leech weapon - NOTE - unknownplayer: Perhaps we should do something interesting like direct the leeched energy into the attacker ?
2311  if (wip->wi_flags & WIF_ENERGY_SUCK) {
2312  // reduce afterburner fuel
2313  shipp->afterburner_fuel -= wip->afterburner_reduce;
2314  shipp->afterburner_fuel = (shipp->afterburner_fuel < 0.0f) ? 0.0f : shipp->afterburner_fuel;
2315 
2316  // reduce weapon energy
2317  shipp->weapon_energy -= wip->weapon_reduce;
2318  shipp->weapon_energy = (shipp->weapon_energy < 0.0f) ? 0.0f : shipp->weapon_energy;
2319  }
2320  }
2321 }
2322 
2323 // Goober5000
2324 void ship_apply_tag(int ship_num, int tag_level, float tag_time, object *target, vec3d *start, int ssm_index, int ssm_team)
2325 {
2326  // set time first tagged
2327  if (Ships[ship_num].time_first_tagged == 0)
2328  Ships[ship_num].time_first_tagged = Missiontime;
2329 
2330  // do tag effect
2331  if (tag_level == 1)
2332  {
2333 // mprintf(("TAGGED %s for %f seconds\n", Ships[ship_num].ship_name, tag_time));
2334  Ships[ship_num].tag_left = tag_time;
2335  Ships[ship_num].tag_total = tag_time;
2336  }
2337  else if (tag_level == 2)
2338  {
2339 // mprintf(("Level 2 TAGGED %s for %f seconds\n", Ships[ship_num].ship_name, tag_time));
2340  Ships[ship_num].level2_tag_left = tag_time;
2341  Ships[ship_num].level2_tag_total = tag_time;
2342  }
2343  else if (tag_level == 3)
2344  {
2345  // tag C creates an SSM strike, yay -Bobboau
2346  Assert(target);
2347  Assert(start);
2348  if (ssm_index < 0) // TAG-C? Is that you? -MageKing17
2349  return;
2350 
2351  ssm_create(target, start, ssm_index, NULL, ssm_team);
2352  }
2353 }
2354 
2355 // This gets called to apply damage when something hits a particular point on a ship.
2356 // This assumes that whoever called this knows if the shield got hit or not.
2357 // hitpos is in world coordinates.
2358 // if quadrant is not -1, then that part of the shield takes damage properly.
2359 void ship_apply_local_damage(object *ship_objp, object *other_obj, vec3d *hitpos, float damage, int quadrant, bool create_spark, int submodel_num, vec3d *hit_normal)
2360 {
2361  Assert(ship_objp); // Goober5000
2362  Assert(other_obj); // Goober5000
2363 
2364  ship *ship_p = &Ships[ship_objp->instance];
2365  weapon *wp = &Weapons[other_obj->instance];
2366  bool create_sparks = true;
2367 
2368  // If got hit by a weapon, tell the AI so it can react. Only do this line in single player,
2369  // or if I am the master in a multiplayer game
2370  if ((other_obj->type == OBJ_WEAPON) && ( !(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER )) {
2371  // If weapon hits ship on same team and that ship not targeted and parent of weapon not player,
2372  // don't do damage.
2373  // Ie, player can always do damage. AI can only damage team if that ship is targeted.
2374  if (wp->target_num != OBJ_INDEX(ship_objp)) {
2375  if ((ship_p->team == wp->team) && !(Objects[other_obj->parent].flags & OF_PLAYER_SHIP) ) {
2376  return;
2377  }
2378  }
2379  }
2380 
2381  // only want to check the following in single player or if I am the multiplayer game server
2382  // Added OBJ_BEAM for traitor detection - FUBAR
2383  if ( !MULTIPLAYER_CLIENT && ((other_obj->type == OBJ_SHIP) || (other_obj->type == OBJ_WEAPON) || (other_obj->type == OBJ_BEAM)) ) {
2384  ai_ship_hit(ship_objp, other_obj, hitpos, quadrant, hit_normal);
2385  }
2386 
2387  // Cut damage done on the player by 4x in training missions, but do full accredidation
2389  if (ship_objp == Player_obj){
2390  damage /= 4.0f;
2391  }
2392  }
2393 
2394  // maybe tag the ship
2395  if(!MULTIPLAYER_CLIENT && (other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_BEAM)) {
2396  weapon_info *wip = NULL;
2397 
2398  if (other_obj->type == OBJ_WEAPON)
2399  wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
2400  else if (other_obj->type == OBJ_BEAM)
2401  wip = &Weapon_info[Beams[other_obj->instance].weapon_info_index];
2402 
2403  Assert(wip != NULL);
2404 
2405  if (wip->wi_flags & WIF_TAG) {
2406  // ssm stuff
2407  vec3d *start = hitpos;
2408  int ssm_index = wip->SSM_index;
2409 
2410  ship_apply_tag(ship_objp->instance, wip->tag_level, wip->tag_time, ship_objp, start, ssm_index, wp->team);
2411  }
2412  }
2413 
2414 #ifndef NDEBUG
2415  if (other_obj->type == OBJ_WEAPON) {
2417  if (wip->wi_flags & WIF_HOMING) {
2418  Homing_hits++;
2419  // nprintf(("AI", " Hit! Hits = %i/%i\n", Homing_hits, (Homing_hits + Homing_misses)));
2420  }
2421  }
2422 #endif
2423 
2424  if ( Event_Music_battle_started == 0 ) {
2425  ship_hit_music(ship_objp, other_obj);
2426  }
2427 
2428 
2429  if (damage < 0.0f){
2430  damage = 0.0f;
2431  }
2432 
2433  // evaluate any possible player stats implications
2434  scoring_eval_hit(ship_objp,other_obj);
2435 
2436  global_damage = false;
2437  ship_do_damage(ship_objp, other_obj, hitpos, damage, quadrant, submodel_num );
2438 
2439  // DA 5/5/98: move ship_hit_create_sparks() after do_damage() since number of sparks depends on hull strength
2440  // doesn't hit shield and we want sparks
2441  if ((quadrant == MISS_SHIELDS) && create_spark) {
2442  // check if subsys destroyed
2443  if ( !is_subsys_destroyed(ship_p, submodel_num) ) {
2444  // Simulated weapons don't cause sparks
2445  if(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_BEAM) {
2446  weapon_info *wip = NULL;
2447 
2448  if (other_obj->type == OBJ_WEAPON)
2449  wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
2450  else if (other_obj->type == OBJ_BEAM)
2451  wip = &Weapon_info[Beams[other_obj->instance].weapon_info_index];
2452 
2453  Assert(wip != NULL);
2454 
2455  if (wip->wi_flags2 & WIF2_TRAINING) {
2456  create_sparks = false;
2457  }
2458  }
2459 
2460  if (create_sparks) {
2461  ship_hit_create_sparks(ship_objp, hitpos, submodel_num);
2462  }
2463  }
2464  //fireball_create( hitpos, FIREBALL_SHIP_EXPLODE1, OBJ_INDEX(ship_objp), 0.25f );
2465  }
2466 }
2467 
2468 extern int Cmdline_nohtl;
2469 
2470 // This gets called to apply damage when a damaging force hits a ship, but at no
2471 // point in particular. Like from a shockwave. This routine will see if the
2472 // shield got hit and if so, apply damage to it.
2473 // You can pass force_center==NULL if you the damage doesn't come from anywhere,
2474 // like for debug keys to damage an object or something. It will
2475 // assume damage is non-directional and will apply it correctly.
2476 void ship_apply_global_damage(object *ship_objp, object *other_obj, vec3d *force_center, float damage )
2477 {
2478  Assert(ship_objp); // Goober5000 (but not other_obj in case of sexp)
2479 
2480  vec3d tmp, world_hitpos;
2481  global_damage = true;
2482 
2483  if ( force_center ) {
2484  int shield_quad;
2485  vec3d local_hitpos;
2486 
2487  // find world hitpos
2488  vm_vec_sub( &tmp, force_center, &ship_objp->pos );
2489  vm_vec_normalize_safe( &tmp );
2490  vm_vec_scale_add( &world_hitpos, &ship_objp->pos, &tmp, ship_objp->radius );
2491 
2492  // Rotate world_hitpos into local coordinates (local_hitpos)
2493  vm_vec_sub(&tmp, &world_hitpos, &ship_objp->pos );
2494  vm_vec_rotate( &local_hitpos, &tmp, &ship_objp->orient );
2495 
2496  // shield_quad = quadrant facing the force_center
2497  shield_quad = get_quadrant(&local_hitpos, ship_objp);
2498 
2499  // world_hitpos use force_center for shockwave
2500  // Goober5000 check for NULL
2501  if (Cmdline_nohtl && (other_obj != NULL) && (other_obj->type == OBJ_SHOCKWAVE) && (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & SIF_HUGE_SHIP))
2502  {
2503  world_hitpos = *force_center;
2504  }
2505 
2506  // Do damage on local point
2507  ship_do_damage(ship_objp, other_obj, &world_hitpos, damage, shield_quad, -1 );
2508  } else {
2509  // Since an force_center wasn't specified, this is probably just a debug key
2510  // to kill an object. So pick a shield quadrant and a point on the
2511  // radius of the object.
2512  vm_vec_scale_add( &world_hitpos, &ship_objp->pos, &ship_objp->orient.vec.fvec, ship_objp->radius );
2513 
2514  for (int i=0; i<ship_objp->n_quadrants; i++){
2515  ship_do_damage(ship_objp, other_obj, &world_hitpos, damage/ship_objp->n_quadrants, i, -1);
2516  }
2517  }
2518 
2519  // AL 3-30-98: Show flashing blast icon if player ship has taken blast damage
2520  if ( ship_objp == Player_obj ) {
2521  // only show blast icon if playing on medium skill or lower -> unknownplayer: why? I think this should be changed.
2522  // Goober5000 - agreed; commented out
2523  //if ( Game_skill_level <= 2 ) {
2524  hud_start_text_flash(XSTR("Blast", 1428), 2000);
2525  //}
2526  }
2527 
2528  // evaluate any player stats scoring conditions (specifically, blasts from remotely detonated secondary weapons)
2529  scoring_eval_hit(ship_objp,other_obj,1);
2530 }
2531 
2532 void ship_apply_wash_damage(object *ship_objp, object *other_obj, float damage)
2533 {
2534  vec3d world_hitpos, direction_vec, rand_vec;
2535 
2536  // Since an force_center wasn't specified, this is probably just a debug key
2537  // to kill an object. So pick a shield quadrant and a point on the
2538  // radius of the object
2539  vm_vec_rand_vec_quick(&rand_vec);
2540  vm_vec_scale_add(&direction_vec, &ship_objp->orient.vec.fvec, &rand_vec, 0.5f);
2541  vm_vec_normalize_quick(&direction_vec);
2542  vm_vec_scale_add( &world_hitpos, &ship_objp->pos, &direction_vec, ship_objp->radius );
2543 
2544  // Do damage to hull and not to shields
2545  global_damage = true;
2546  ship_do_damage(ship_objp, other_obj, &world_hitpos, damage, -1, -1, 1);
2547 
2548  // AL 3-30-98: Show flashing blast icon if player ship has taken blast damage
2549  if ( ship_objp == Player_obj ) {
2550  // only show blast icon if playing on medium skill or lower
2551  // Goober5000 - commented out
2552  //if ( Game_skill_level <= 2 ) {
2553  hud_start_text_flash(XSTR("Engine Wash", 1429), 2000);
2554  //}
2555  }
2556 
2557  // evaluate any player stats scoring conditions (specifically, blasts from remotely detonated secondary weapons)
2558  scoring_eval_hit(ship_objp,other_obj,1);
2559 }
2560 
2561 // player pain
2562 void ship_hit_pain(float damage, int quadrant)
2563 {
2564 
2565  ship *shipp = &Ships[Player_obj->instance];
2566  ship_info *sip = &Ship_info[shipp->ship_info_index];
2567 
2568  if (!(Player_obj->flags & OF_INVULNERABLE))
2569  {
2570  if (Shield_pain_flash_factor != 0.0f && quadrant >= 0)
2571  {
2572  float effect = (Shield_pain_flash_factor * Player_obj->shield_quadrant[quadrant] * 4) / shipp->ship_max_shield_strength;
2573 
2575  effect -= Shield_pain_flash_factor;
2576 
2577  game_flash((sip->shield_color[0] * effect) / 255.0f, (sip->shield_color[1] * effect) / 255.0f, (sip->shield_color[2] * effect) / 255.0f);
2578  }
2579  else
2580  game_flash(damage * Generic_pain_flash_factor / 15.0f, -damage * Generic_pain_flash_factor / 30.0f, -damage * Generic_pain_flash_factor / 30.0f);
2581  }
2582 
2583  // kill any active popups when you get hit.
2584  if ( Game_mode & GM_MULTIPLAYER ){
2586  }
2587 }
void player_died_start(object *killer_objp)
Definition: shiphit.cpp:1268
float submodel_get_radius(int modelnum, int submodelnum)
Definition: modelread.cpp:3123
int target_num
Definition: weapon.h:172
#define OF_INVULNERABLE
Definition: object.h:107
void game_flash(float r, float g, float b)
Definition: fredstubs.cpp:40
int timestamp(int delta_ms)
Definition: timer.cpp:226
#define MULTIPLAYER_CLIENT
Definition: multi.h:132
float sim_hull_strength
Definition: object.h:161
int i
Definition: multi_pxo.cpp:466
fix Missiontime
Definition: systemvars.cpp:19
vec3d deathroll_rotvel
Definition: ship.h:573
ubyte wash_killed
Definition: ship.h:548
ubyte pre_death_explosion_happened
Definition: ship.h:547
model_subsystem * system_info
Definition: ship.h:314
weapon Weapons[MAX_WEAPONS]
Definition: weapons.cpp:78
SCP_vector< ArmorType > Armor_types
Definition: ship.cpp:170
int rotation_snd
Definition: model.h:204
void hud_gauge_popup_start(int gauge_index, int time)
Start a gauge to pop-up.
Definition: hud.cpp:3036
float tag_total
Definition: ship.h:724
#define MIN(a, b)
Definition: pstypes.h:296
#define SIF2_NO_PAIN_FLASH
Definition: ship.h:929
#define SSSF_ROTATE
Definition: ship.h:304
ai_info * Player_ai
Definition: ai.cpp:24
int team
Definition: ship.h:606
vec3d Dead_camera_pos
Definition: shiphit.cpp:67
char * ship_subsys_get_name(ship_subsys *ss)
Definition: ship.cpp:15001
float vm_vec_mag_quick(const vec3d *v)
Definition: vecmat.cpp:371
int flags
Definition: player.h:104
void mission_log_add_entry(int type, char *pname, char *sname, int info_index)
Definition: missionlog.cpp:184
vec3d vmd_z_vector
Definition: vecmat.cpp:27
vec3d View_position
Definition: 3dsetup.cpp:20
int objnum
Definition: ship.h:537
int Game_mode
Definition: systemvars.cpp:24
vec3d Original_vec_to_deader
Definition: shiphit.cpp:68
vec3d rotvel
Definition: physics.h:78
int shockwave_get_weapon_index(int index)
Definition: shockwave.cpp:741
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
int game_type
Definition: missionparse.h:138
void saturate_fabs(float *f, float max)
Definition: shiphit.cpp:1360
polymodel * model_get(int model_num)
Definition: modelread.cpp:3134
#define PLAYER_FLAGS_KILLED_SELF_MISSILES
Definition: player.h:53
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
#define MAX_BEAMS
Definition: beam.h:40
int killer_species
Definition: player.h:181
net_player * Net_player
Definition: multi.cpp:94
SCP_vector< game_snd > Snds
Definition: gamesnd.cpp:19
#define BIG_SHIP_MIN_RADIUS
Definition: shiphit.cpp:65
int turret_gun_sobj
Definition: model.h:193
char parent_type
Definition: object.h:149
submodel_instance_info submodel_info_1
Definition: ship.h:368
#define LOG_SHIP_SUBSYS_DESTROYED
Definition: missionlog.h:27
char ** Ai_class_names
Definition: aicode.cpp:268
Definition: weapon.h:163
GLuint index
Definition: Glext.h:5608
asteroid Asteroids[MAX_ASTEROIDS]
Definition: asteroid.cpp:63
bool Beams_use_damage_factors
Definition: mod_table.cpp:36
physics_info phys_info
Definition: object.h:157
player * m_player
Definition: multi.h:459
#define WIF_HUGE
Definition: weapon.h:64
int get_quadrant(vec3d *hit_pnt, object *shipobjp)
Definition: shield.cpp:988
#define MAX_SHIPS
Definition: globals.h:37
int turret_gun_rotation_snd
Definition: model.h:197
#define MSS_FLAG2_COLLIDE_SUBMODEL
Definition: model.h:141
void ship_hit_pain(float damage, int quadrant)
Definition: shiphit.cpp:2562
int model_num
Definition: ship.h:1189
float ship_max_shield_strength
Definition: ship.h:596
void emp_stop_local()
Definition: emp.cpp:420
float max_hits
Definition: ship.h:320
int class_type
Definition: ship.h:1167
int model_instance_num
Definition: ship.h:802
float vm_vec_normalize_quick(vec3d *src)
Definition: vecmat.cpp:529
void shiphit_record_player_killer(object *killer_objp, player *p)
Definition: shiphit.cpp:780
float dist
Definition: shiphit.cpp:60
int Homing_hits
Definition: weapons.cpp:4709
Assert(pm!=NULL)
uint flags2
Definition: model.h:170
int target_objnum
Definition: ai.h:339
float weapon_reduce
Definition: weapon.h:452
captial ship subsystem destroyed
Definition: gamesnd.h:128
int detail[MAX_MODEL_DETAIL_LEVELS]
Definition: model.h:738
Definition: pstypes.h:88
submodel_instance_info submodel_info_2
Definition: ship.h:369
#define mprintf(args)
Definition: pstypes.h:238
int choose_next_spark(object *ship_objp, vec3d *hitpos)
Definition: shiphit.cpp:1100
int ai_index
Definition: ship.h:538
int beam_get_parent(object *bm)
Definition: beam.cpp:579
float level2_tag_left
Definition: ship.h:728
float side_slip_time_const
Definition: physics.h:44
#define OBJ_ASTEROID
Definition: object.h:44
int ai_class
Definition: ai.h:369
int ship_stop_fire_primary(object *obj)
Definition: ship.cpp:10711
bool Damage_impacted_subsystem_first
Definition: mod_table.cpp:21
#define SAF_IGNORE_SS_ARMOR
Definition: ship.h:243
void obj_snd_delete_type(int objnum, int sndnum, ship_subsys *ss)
Definition: objectsnd.cpp:812
void ai_announce_ship_dying(object *dying_objp)
Definition: aicode.cpp:13506
#define PLAYER_FLAGS_KILLED_BY_ENGINE_WASH
Definition: player.h:51
#define SIF_CARGO
Definition: ship.h:884
int subsys_guardian_threshold
Definition: ship.h:324
int shockwave_get_damage_type_idx(int index)
Definition: shockwave.cpp:777
struct vec3d::@225::@227 xyz
float afterburner_fuel
Definition: ship.h:648
int dead_snd
Definition: model.h:203
int next_hit_spark
Definition: ship.h:580
CButton * team
vec3d max_vel
Definition: physics.h:49
GLclampf f
Definition: Glext.h:7097
#define MAX_OBJECTS
Definition: globals.h:83
int submodel_num
Definition: ship.h:525
#define SW_WEAPON_KILL
Definition: shockwave.h:24
#define OF_NO_SHIELDS
Definition: object.h:110
ai_info Ai_info[MAX_AI_INFO]
Definition: ai.cpp:23
int damage_type_idx
Definition: debris.h:28
vec3d * vm_vec_rotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:933
int spark_compare(const spark_pair &pair1, const spark_pair &pair2)
Definition: shiphit.cpp:1091
int index1
Definition: shiphit.cpp:59
object obj_used_list
Definition: object.cpp:53
int end_time
Definition: ship.h:526
model_subsystem * subsystems
Definition: ship.h:1271
int subsys_snd_flags
Definition: ship.h:383
#define PLAYER_FLAGS_KILLED_SELF_UNKNOWN
Definition: player.h:52
void ship_hit_create_sparks(object *ship_objp, vec3d *hitpos, int submodel_num)
Definition: shiphit.cpp:1194
ship_subsys * targeted_subsys
Definition: ai.h:472
void joy_ff_deathroll()
Definition: joy-unix.cpp:590
#define SSF_VANISHED
Definition: ship.h:290
int n_quadrants
Definition: object.h:158
ship_subsys * ptr
Definition: shiphit.cpp:410
float range
Definition: shiphit.cpp:409
void vm_vec_scale_add2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:284
int death_roll_base_time
Definition: ship.h:1244
float weapon_energy
Definition: ship.h:640
uint flags
Definition: ship.h:644
#define MAX_SUBSYS_LIST
Definition: shiphit.cpp:405
#define OF_PLAYER_SHIP
Definition: object.h:109
object * objp
Definition: lua.cpp:3105
float vaporize_chance
Definition: ship.h:1044
int subtype
Definition: weapon.h:326
#define Int3()
Definition: pstypes.h:292
vec3d max_rotvel
Definition: physics.h:52
#define AIF_KAMIKAZE
Definition: ai.h:55
float vaporize_chance
Definition: ship.h:1248
fix time_first_tagged
Definition: ship.h:726
float weapon_get_damage_scale(weapon_info *wip, object *wep, object *target)
Definition: weapons.cpp:7137
#define MAX_DEBRIS_PIECES
Definition: debris.h:57
ship * shipp
Definition: lua.cpp:9162
int turret_base_rotation_snd
Definition: model.h:195
vec3d pos
Definition: object.h:152
float subsystem_factor
Definition: weapon.h:378
ship_subsys_info subsys_info[SUBSYSTEM_MAX]
Definition: ship.h:632
void ship_apply_whack(vec3d *force, vec3d *hit_pos, object *objp)
Definition: shiphit.cpp:1740
int get_max_sparks(object *ship_objp)
Definition: shiphit.cpp:1054
hull_check submodel_num
Definition: lua.cpp:5048
int ai_flags
Definition: ai.h:330
float vm_vec_mag_squared(const vec3d *v)
Definition: vecmat.cpp:339
#define OS_SUBSYS_DEAD
Definition: objectsnd.h:21
char callsign[CALLSIGN_LEN+1]
Definition: player.h:91
script_state Script_system("FS2_Open Scripting")
int signature
Definition: object.h:145
int subobj_num
Definition: model.h:175
GLenum type
Definition: Gl.h:1492
#define HUD_DAMAGE_GAUGE
Definition: hudgauges.h:44
#define MAX_SPARK_PAIRS
Definition: shiphit.cpp:63
int obj_snd_assign(int objnum, int sndnum, vec3d *pos, int main, int flags, ship_subsys *associated_sub)
Definition: objectsnd.cpp:705
#define DEATHROLL_ROTVEL_MIN
Definition: shiphit.cpp:1355
int weapon_info_index
Definition: weapon.h:164
int weapon_info_index
Definition: beam.h:117
int asteroid_type
Definition: asteroid.h:102
ship_subsys subsys_list
Definition: ship.h:630
#define WIF2_TRAINING
Definition: weapon.h:94
void ship_maybe_praise_player(ship *deader_sp)
Definition: ship.cpp:15532
float subsys_damage_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:110
int object_is_docked(object *objp)
Definition: object.cpp:2019
char alt_dmg_sub_name[NAME_LENGTH]
Definition: model.h:174
float Shield_pain_flash_factor
Definition: mod_table.cpp:38
#define SIF_BOMBER
Definition: ship.h:886
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
int instance
Definition: object.h:150
void ship_vaporize(ship *shipp)
Definition: shiphit.cpp:1522
#define FIREBALL_MEDIUM_EXPLOSION
Definition: fireballs.h:23
GLenum GLint * range
Definition: Glext.h:7096
Definition: player.h:85
void dock_calc_docked_center(vec3d *dest, object *objp)
Definition: objectdock.cpp:126
void vm_vec_add2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:178
#define OBJ_START
Definition: object.h:35
int type_flags
Definition: multi.h:493
float level2_tag_total
Definition: ship.h:727
ship_spark sparks[MAX_SHIP_HITS]
Definition: ship.h:582
int flags
Definition: ship.h:322
#define SIF_BIG_SHIP
Definition: ship.h:944
#define AIPF_INCLUDE_BEAMS_IN_STAT_CALCS
Definition: ai_profiles.h:36
struct matrix::@228::@230 vec
#define LOG_SHIP_DISABLED
Definition: missionlog.h:29
int subtype
Definition: lua.cpp:9763
#define PF_REDUCED_DAMP
Definition: physics.h:22
float ship_max_hull_strength
Definition: ship.h:597
void vm_vec_scale(vec3d *dest, float s)
Definition: vecmat.cpp:248
uint flags
Definition: model.h:169
int timestamp_until(int stamp)
Definition: timer.cpp:242
#define nprintf(args)
Definition: pstypes.h:239
int scoring_eval_kill(object *ship_objp)
Definition: scoring.cpp:590
#define GM_MULTIPLAYER
Definition: systemvars.h:18
#define MIN_PLAYER_DEATHROLL_TIME
Definition: shiphit.cpp:1353
detail_levels Detail
Definition: systemvars.cpp:478
int team
Definition: weapon.h:167
#define PLAYER_FLAGS_KILLED_BY_EXPLOSION
Definition: player.h:48
float rotdamp
Definition: physics.h:43
#define DEATHROLL_ROTVEL_SCALE
Definition: shiphit.cpp:1358
void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, int server_msg)
Definition: multimsgs.cpp:615
int fireball_ship_explosion_type(ship_info *sip)
Definition: fireballs.cpp:993
void ship_maybe_lament()
Definition: ship.cpp:15738
int flags
Definition: ship.h:1227
ai_profile_t * ai_profile
Definition: missionparse.h:168
float get_hull_pct(object *objp)
Definition: object.cpp:271
#define SIF_SMALL_SHIP
Definition: ship.h:943
int ship_get_index_from_subsys(ship_subsys *ssp, int objnum, int error_bypass)
Definition: ship.cpp:13390
netgame_info Netgame
Definition: multi.cpp:97
int damage_type_idx
Definition: weapon.h:519
#define OBJ_WEAPON
Definition: object.h:33
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 SSM_index
Definition: weapon.h:466
void create_vaporize_debris(object *ship_objp, vec3d *hitpos)
Definition: shiphit.cpp:388
#define WP_LASER
Definition: weapon.h:27
#define SSF_DAMAGE_AS_HULL
Definition: ship.h:293
float hull_strength
Definition: object.h:160
#define OBJ_DEBRIS
Definition: object.h:37
void vm_vec_sub2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:187
int parent
Definition: object.h:147
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
#define NG_TYPE_TEAM
Definition: multi.h:650
float damage_ship[MAX_DAMAGE_SLOTS]
Definition: ship.h:694
int tag_level
Definition: weapon.h:460
int object_is_dead_docked(object *objp)
Definition: object.cpp:2025
void ai_update_lethality(object *ship_objp, object *weapon_obj, float damage)
Definition: aicode.cpp:14881
#define MAX_SHIP_HITS
Definition: ship.h:56
int n_subsystems
Definition: ship.h:1270
#define SSSF_TURRET_ROTATION
Definition: ship.h:305
#define SUBSYSTEM_TURRET
Definition: model.h:54
int special_exp_deathroll_time
Definition: ship.h:591
#define DEATHROLL_VELOCITY_STANDARD
Definition: shiphit.cpp:1357
hull_check p1
Definition: lua.cpp:5052
void send_subsystem_destroyed_packet(ship *shipp, int index, vec3d world_hitpos)
Definition: multimsgs.cpp:4482
int snd_play(game_snd *gs, float pan, float vol_scale, int priority, bool is_voice_msg)
Definition: sound.cpp:517
#define fl_abs(fl)
Definition: floating.h:31
int type_count
Definition: ship.h:417
float vm_vec_normalized_dir(vec3d *dest, const vec3d *end, const vec3d *start)
Definition: vecmat.cpp:591
float vm_vec_dist(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:355
#define MAX_PLAYERS
Definition: pstypes.h:32
#define SSSF_ALIVE
Definition: ship.h:302
float aggregate_current_hits
Definition: ship.h:419
#define PCM_WARPOUT_STAGE2
Definition: player.h:60
void ship_maybe_scream(ship *sp)
Definition: ship.cpp:15789
int collision_damage_type_idx
Definition: ship.h:798
#define MONITOR(function_name)
Definition: pstypes.h:454
float current_hits
Definition: ship.h:319
void do_subobj_destroyed_stuff(ship *ship_p, ship_subsys *subsys, vec3d *hitpos, bool no_explosion)
Definition: shiphit.cpp:108
int armor_type_idx
Definition: ship.h:325
Definition: ship.h:534
float shockwave_get_max_radius(int index)
Definition: shockwave.cpp:750
void SetHookObject(char *name, object *objp)
Definition: scripting.cpp:551
void multi_respawn_check(object *objp)
float apply_damage_to_shield(object *objp, int quadrant, float damage)
Definition: shield.cpp:644
int killer_objtype
Definition: player.h:180
void shipfx_blow_off_subsystem(object *ship_objp, ship *ship_p, ship_subsys *subsys, vec3d *exp_center, bool no_explosion)
Definition: shipfx.cpp:295
float vm_vec_normalize_safe(vec3d *v)
Definition: vecmat.cpp:471
vec3d vmd_y_vector
Definition: vecmat.cpp:26
debris Debris[MAX_DEBRIS_PIECES]
Definition: debris.cpp:41
float player_damage_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:108
#define MSS_FLAG_CARRY_NO_DAMAGE
Definition: model.h:115
vec3d * vm_vec_unrotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:959
beam Beams[MAX_BEAMS]
Definition: beam.cpp:60
float vm_vec_dist_squared(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:344
void ship_apply_global_damage(object *ship_objp, object *other_obj, vec3d *force_center, float damage)
Definition: shiphit.cpp:2476
#define SF_DISABLED
Definition: ship.h:448
int iff_x_attacks_y(int team_x, int team_y)
Definition: iff_defs.cpp:605
vec3d vmd_x_vector
Definition: vecmat.cpp:25
int damage_ship_id[MAX_DAMAGE_SLOTS]
Definition: ship.h:695
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
GLclampd n
Definition: Glext.h:7286
#define MONITOR_INC(function_name, inc)
Definition: pstypes.h:457
int Homing_misses
Definition: weapons.cpp:4709
#define MISS_SHIELDS
Definition: shiphit.h:23
int wi_flags
Definition: weapon.h:384
#define vm_vec_zero(v)
Definition: vecmat.h:37
uint flags
Definition: physics.h:37
char killer_parent_name[NAME_LENGTH]
Definition: player.h:183
#define MULTI_MSG_ALL
Definition: multi_pmsg.h:27
void ship_hit_sparks_no_rotate(object *ship_objp, vec3d *hitpos)
Definition: shiphit.cpp:1030
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
ubyte shield_color[3]
Definition: ship.h:1362
int objnum
Definition: player.h:124
#define OBJ_INDEX(objp)
Definition: object.h:235
int shield_armor_type_idx
Definition: ship.h:797
void ship_maybe_praise_self(ship *deader_sp, ship *killer_sp)
Definition: ship.cpp:15572
#define LOG_SHIP_DESTROYED
Definition: missionlog.h:20
matrix orient
Definition: object.h:153
int ship_guardian_threshold
Definition: ship.h:601
int wi_flags2
Definition: weapon.h:385
#define NOX(s)
Definition: pstypes.h:473
bool end_string_at_first_hash_symbol(char *src)
Definition: parselo.cpp:3833
GLuint start
Definition: Gl.h:1502
#define OBJ_SHIP
Definition: object.h:32
#define GM_STANDALONE_SERVER
Definition: systemvars.h:27
void ship_hit_music(object *ship_objp, object *other_obj)
Definition: shiphit.cpp:975
void vm_vec_rand_vec_quick(vec3d *rvec)
Definition: vecmat.cpp:1379
int Num_weapon_types
Definition: weapons.cpp:105
#define SIF_HUGE_SHIP
Definition: ship.h:945
void ship_apply_wash_damage(object *ship_objp, object *other_obj, float damage)
Definition: shiphit.cpp:2532
void scoring_add_damage(object *ship_objp, object *other_obj, float damage)
Definition: scoring.cpp:492
int beam_get_weapon_info_index(object *bm)
Definition: beam.cpp:608
int control_mode
Definition: player.h:134
int RunCondition(int condition, char format='\0', void *data=NULL, class object *objp=NULL, int more_data=0)
Definition: scripting.cpp:924
void game_whack_apply(float x, float y)
Definition: fredstubs.cpp:138
int alive_snd
Definition: model.h:202
int final_death_time
Definition: ship.h:569
void vm_vec_sub(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:168
void create_subsys_debris(object *ship_objp, vec3d *hitpos)
Definition: shiphit.cpp:369
#define SUBSYSTEM_ENGINE
Definition: model.h:53
vec3d vel
Definition: physics.h:77
float radius
Definition: model.h:179
float total_damage_received
Definition: ship.h:693
#define MISSION_TYPE_TRAINING
Definition: missionparse.h:63
ship death roll
Definition: gamesnd.h:74
#define SF_VAPORIZE
Definition: ship.h:470
int shockwave_get_flags(int index)
Definition: shockwave.cpp:786
#define DEATHROLL_ROTVEL_CAP
Definition: shiphit.cpp:1354
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
int death_roll_snd
Definition: ship.h:675
if(aifft_max_checks<=0)
Definition: aiturret.cpp:1581
#define SF_EXPLODED
Definition: ship.h:467
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
float Generic_pain_flash_factor
Definition: mod_table.cpp:37
int Event_Music_battle_started
Definition: eventmusic.cpp:39
vec3d pnt
Definition: model.h:178
int next_fireball
Definition: ship.h:578
float tag_left
Definition: ship.h:725
float vm_vec_dist_quick(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:417
#define NAME_LENGTH
Definition: globals.h:15
float frand()
Return random value in range 0.0..1.0- (1.0- means the closest number less than 1.0)
Definition: floating.cpp:35
void shiphit_hit_after_death(object *ship_objp, float damage)
Definition: shiphit.cpp:1787
#define WP_MISSILE
Definition: weapon.h:28
#define SSF_NO_AGGREGATE
Definition: ship.h:294
#define MULTI_CONNECTED(np)
Definition: multi.h:136
int submodel
Definition: lua.cpp:5007
bool Fixed_turret_collisions
Definition: mod_table.cpp:20
player * Player
void hud_start_text_flash(char *txt, int t, int interval)
void ship_generic_kill_stuff(object *objp, float percent_killed)
Definition: shiphit.cpp:1371
GLenum target
Definition: Glext.h:6872
void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hitpos, int shield_quadrant, vec3d *hit_normal)
Definition: aicode.cpp:14934
#define OBJ_SHOCKWAVE
Definition: object.h:41
float shockwave_get_min_radius(int index)
Definition: shockwave.cpp:759
void ai_deathroll_start(object *ship_obj)
Definition: aicode.cpp:15299
subsystem gets destroyed on player ship
Definition: gamesnd.h:100
void send_ship_kill_packet(class object *, class object *, float, unsigned char)
Definition: fredstubs.cpp:160
char subobj_name[MAX_NAME_LEN]
Definition: model.h:172
#define WIF_ENERGY_SUCK
Definition: weapon.h:74
#define MAX_SHOCKWAVES
Definition: shockwave.h:26
void physics_apply_whack(vec3d *impulse, vec3d *pos, physics_info *pi, matrix *orient, float mass)
Definition: physics.cpp:776
#define PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE
Definition: player.h:54
void ship_hit_kill(object *ship_objp, object *other_obj, float percent_killed, int self_destruct)
Definition: shiphit.cpp:1542
void RemHookVars(unsigned int num,...)
Definition: scripting.cpp:754
int ship_info_index
Definition: ship.h:539
GLfloat GLfloat p
Definition: Glext.h:8373
#define MULTIPLAYER_MASTER
Definition: multi.h:130
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
#define SIF_KNOSSOS_DEVICE
Definition: ship.h:912
float shield_factor
Definition: weapon.h:378
#define PF_DEAD_DAMP
Definition: physics.h:24
#define SIF_FIGHTER
Definition: ship.h:885
other ship subsystem destroyed
Definition: gamesnd.h:126
void ship_apply_local_damage(object *ship_objp, object *other_obj, vec3d *hitpos, float damage, int quadrant, bool create_spark, int submodel_num, vec3d *hit_normal)
Definition: shiphit.cpp:2359
#define WIF_HOMING
Definition: weapon.h:129
SCP_vector< float > shield_quadrant
Definition: object.h:159
void game_tst_mark(class object *, class ship *)
Definition: fredstubs.cpp:212
void show_dead_message(object *ship_objp, object *other_obj)
Definition: shiphit.cpp:912
SCP_vector< exited_ship > Ships_exited
Definition: ship.cpp:152
vec3d * get_subsystem_world_pos(object *parent_obj, ship_subsys *subsys, vec3d *world_pos)
Definition: hudtarget.cpp:4395
int index2
Definition: shiphit.cpp:59
#define WIF_PUNCTURE
Definition: weapon.h:49
#define MAX_WEAPONS
Definition: globals.h:71
int Game_skill_level
Definition: fredstubs.cpp:170
int armor_type_idx
Definition: ship.h:796
float vm_vec_dot(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:312
GLint GLsizei count
Definition: Gl.h:1491
bool is_subsys_destroyed(ship *shipp, int submodel)
Definition: shiphit.cpp:84
void ssm_create(object *target, vec3d *start, size_t ssm_index, ssm_firing_info *override, int team)
void ship_self_destruct(object *objp)
Definition: shiphit.cpp:1697
object * Player_obj
Definition: object.cpp:56
int killer_weapon_index
Definition: player.h:182
#define SUBSYSTEM_ACTIVATION
Definition: model.h:62
float shockwave_get_damage(int index)
Definition: shockwave.cpp:768
#define MAX_DEBRIS_SHARDS
Definition: shiphit.cpp:365
void scoring_eval_hit(object *hit_obj, object *other_obj, int from_blast)
Definition: scoring.cpp:1188
object * debris_create(object *source_obj, int model_num, int submodel_num, vec3d *pos, vec3d *exp_center, int hull_flag, float exp_force)
Definition: debris.cpp:462
#define SF_DYING
Definition: ship.h:447
int shiphit_get_damage_weapon(object *damaging_objp)
Definition: shiphit.cpp:320
polymodel * pm
Definition: lua.cpp:1598
#define MAX(a, b)
Definition: pstypes.h:299
void HUD_printf(const char *format,...)
Definition: hudmessage.cpp:527
#define MAX_ASTEROIDS
Definition: asteroid.h:24
float mass
Definition: physics.h:39
#define OBJ_NONE
Definition: object.h:31
#define OBJ_BEAM
Definition: object.h:46
float afterburner_reduce
Definition: weapon.h:453
float dist
Definition: shiphit.cpp:408
#define FIREBALL_EXPLOSION_MEDIUM
Definition: fireballs.h:28
vec3d * vm_vec_cross(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:645
#define GM_NORMAL
Definition: systemvars.h:19
uint flags
Definition: object.h:151
float radius
Definition: object.h:154
int num_small_debris
Definition: systemvars.h:170
int ship_find_exited_ship_by_signature(int signature)
Definition: ship.cpp:5353
mission The_mission
void ship_apply_tag(int ship_num, int tag_level, float tag_time, object *target, vec3d *start, int ssm_index, int ssm_team)
Definition: shiphit.cpp:2324
#define MSS_FLAG_CARRY_SHOCKWAVE
Definition: model.h:125
float subsys_get_range(object *other_obj, ship_subsys *subsys)
Definition: shiphit.cpp:348
float dock_calc_total_docked_mass(object *objp)
Definition: objectdock.cpp:152
char type
Definition: object.h:146
void find_submodel_instance_world_point(vec3d *outpnt, int model_instance_num, int submodel_num, const matrix *objorient, const vec3d *objpos)
Definition: modelread.cpp:4431
vec3d vmd_zero_vector
Definition: vecmat.cpp:24
int multi_find_player_by_object(object *objp)
Definition: multiutil.cpp:500
#define wp(p)
Definition: modelsinc.h:69
bool IsConditionOverride(int action, object *objp=NULL)
Definition: scripting.cpp:938
net_player Net_players[MAX_PLAYERS]
Definition: multi.cpp:93
SCP_vector< asteroid_info > Asteroid_info
Definition: asteroid.cpp:62
float armor_factor
Definition: weapon.h:378
int num_hits
Definition: ship.h:581
int death_time
Definition: ship.h:570
float do_subobj_hit_stuff(object *ship_objp, object *other_obj, vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor)
Definition: shiphit.cpp:447
void gameseq_post_event(int event)
#define stricmp(s1, s2)
Definition: config.h:271
void vm_vec_add(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:159
float ship_get_exp_damage(object *objp)
Definition: ship.cpp:16949
#define DEATHROLL_MASS_STANDARD
Definition: shiphit.cpp:1356
#define OBJ_GHOST
Definition: object.h:39
#define timestamp_valid(stamp)
Definition: timer.h:104
#define AIPF_SHOCKWAVES_DAMAGE_SMALL_SHIP_SUBSYSTEMS
Definition: ai_profiles.h:29
#define LOG_SHIP_DISARMED
Definition: missionlog.h:30
vec3d pos
Definition: ship.h:524
int parent_sig
Definition: object.h:148
char ship_name[NAME_LENGTH]
Definition: ship.h:604
SCP_vector< ship_type_info > Ship_types
Definition: ship.cpp:168
int maybe_shockwave_damage_adjust(object *ship_objp, object *other_obj, float *damage)
Definition: shiphit.cpp:1840
#define LOG_SELF_DESTRUCTED
Definition: missionlog.h:39
struct spark_pair spark_pair
float tag_time
Definition: weapon.h:459
#define NG_TYPE_DOGFIGHT
Definition: multi.h:651
int event_music_battle_start()
Definition: eventmusic.cpp:732
float vm_vec_normalize(vec3d *v)
Definition: vecmat.cpp:460
int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_obj, float size, int reverse, vec3d *velocity, float warp_lifetime, int ship_class, matrix *orient_override, int low_res, int extra_flags, int warp_open_sound, int warp_close_sound)
Definition: fireballs.cpp:788
#define strcpy_s(...)
Definition: safe_strings.h:67
#define WIF_TAG
Definition: weapon.h:77
#define SSSF_DEAD
Definition: ship.h:303
#define CHA_DEATH
Definition: scripting.h:45
int Cmdline_nohtl
Definition: cmdline.cpp:438
void shipfx_emit_spark(int n, int sn)
Definition: shipfx.cpp:1417