]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/shiphit.cpp
fix some mouse handling issues
[taylor/freespace2.git] / src / ship / shiphit.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/ShipHit.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code to deal with a ship getting hit by something, be it a missile, dog, or ship.
16  *
17  * $Log$
18  * Revision 1.6  2002/07/13 19:47:02  theoddone33
19  * Fix some more warnings
20  *
21  * Change demo building, edit Makefile if you want the demo.
22  *
23  * Revision 1.5  2002/06/17 06:33:11  relnev
24  * ryan's struct patch for gcc 2.95
25  *
26  * Revision 1.4  2002/06/09 04:41:26  relnev
27  * added copyright header
28  *
29  * Revision 1.3  2002/06/02 04:26:34  relnev
30  * warning cleanup
31  *
32  * Revision 1.2  2002/05/03 13:34:34  theoddone33
33  * More stuff compiles
34  *
35  * Revision 1.1.1.1  2002/05/03 03:28:10  root
36  * Initial import.
37  *
38  * 
39  * 61    9/14/99 3:26a Dave
40  * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
41  * respawn-too-early problem. Made a few crash points safe.
42  * 
43  * 60    9/13/99 4:52p Dave
44  * RESPAWN FIX
45  * 
46  * 59    9/09/99 11:17a Jimb
47  * Removed bogus Int3() in do_subobj_hit_stuff(). 
48  * 
49  * 58    9/07/99 4:01p Dave
50  * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX
51  * does everything properly (setting up address when binding). Remove
52  * black rectangle background from UI_INPUTBOX.
53  * 
54  * 57    9/03/99 11:39p Mikek
55  * Fix problem in sm3-01 of dual-fired Helios bombs only doing 1/4 damage.
56  * 
57  * 56    9/03/99 2:24p Mikek
58  * Decrease overall damage done to player at Medium and Hard.  I believe
59  * this was the major culprit in Flak cannons being so nasty.  The firing
60  * rate seems to not have been an issue.
61  * 
62  * 55    9/02/99 11:55a Mikek
63  * Zero damage to small ship subsystems from shockwaves.
64  * 
65  * 54    9/01/99 10:15a Dave
66  * 
67  * 53    8/27/99 10:42a Andsager
68  * Don't reposition shockwave for BIG only HUGE ships
69  * 
70  * 52    8/27/99 1:34a Andsager
71  * Modify damage by shockwaves for BIG|HUGE ships.  Modify shockwave damge
72  * when weapon blows up.
73  * 
74  * 51    8/26/99 10:46p Andsager
75  * Apply shockwave damage to lethality.
76  * 
77  * 50    8/26/99 8:52p Dave
78  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
79  * 
80  * 49    8/26/99 5:14p Andsager
81  * 
82  * 48    8/26/99 9:45a Dave
83  * First pass at easter eggs and cheats.
84  * 
85  * 47    8/23/99 11:59a Andsager
86  * Force choice of big fireball when Knossos destroyed.  Allow logging of
87  * ship destroyed when no killer_name (ie, from debug).
88  * 
89  * 46    8/22/99 5:53p Dave
90  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
91  * instead of ship designations for multiplayer players.
92  * 
93  * 45    8/22/99 1:19p Dave
94  * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
95  * which d3d cards are detected.
96  * 
97  * 44    8/20/99 5:09p Andsager
98  * Second pass on Knossos device explosion
99  * 
100  * 43    8/20/99 1:42p Andsager
101  * Frist pass on Knossos explosion.
102  * 
103  * 42    8/06/99 9:46p Dave
104  * Hopefully final changes for the demo.
105  * 
106  * 41    8/03/99 11:02p Dave
107  * Maybe fixed sync problems in multiplayer.
108  * 
109  * 40    8/02/99 1:59p Dave
110  * Fixed improper damage application to subobjects on a "big damage" ship.
111  * 
112  * 39    7/24/99 1:54p Dave
113  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
114  * missions.
115  * 
116  * 38    7/19/99 7:20p Dave
117  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
118  * pre-rendering.
119  * 
120  * 37    7/09/99 12:51a Andsager
121  * Modify engine wash (1) less damage (2) only at a closer range (3) no
122  * damage when engine is disabled
123  * 
124  * 36    6/30/99 5:53p Dave
125  * Put in new anti-camper code.
126  * 
127  * 35    6/28/99 4:51p Andsager
128  * Add ship-guardian sexp (does not allow ship to be killed)
129  * 
130  * 34    6/21/99 7:25p Dave
131  * netplayer pain packet. Added type E unmoving beams.
132  * 
133  * 33    6/10/99 3:43p Dave
134  * Do a better job of syncing text colors to HUD gauges.
135  * 
136  * 32    5/27/99 12:14p Andsager
137  * Some fixes for live debris when more than one subsys on ship with live
138  * debris.  Set subsys strength (when 0) blows off subsystem.
139  * sexp_hits_left_subsystem works for SUBSYSTEM_UNKNOWN.
140  * 
141  * 31    5/21/99 5:03p Andsager
142  * Add code to display engine wash death.  Modify ship_kill_packet
143  * 
144  * 30    5/21/99 1:44p Andsager
145  * Add engine wash gauge
146  * 
147  * 29    5/14/99 11:50a Andsager
148  * Added vaporize for SMALL ships hit by HUGE beams.  Modified dying
149  * frame.  Enlarged debris shards and range at which visible.
150  * 
151  * 28    5/12/99 2:55p Andsager
152  * Implemented level 2 tag as priority in turret object selection
153  * 
154  * 27    5/11/99 10:16p Andsager
155  * First pass on engine wash effect.  Rotation (control input), damage,
156  * shake.  
157  * 
158  * 26    4/28/99 11:13p Dave
159  * Temporary checkin of artillery code.
160  * 
161  * 25    4/23/99 12:01p Johnson
162  * Added SIF_HUGE_SHIP
163  * 
164  * 24    4/20/99 3:43p Andsager
165  * Added normal parameter to ship_apply_local_damage for case of ship_ship
166  * collision.
167  * 
168  * 23    4/16/99 5:54p Dave
169  * Support for on/off style "stream" weapons. Real early support for
170  * target-painting lasers.
171  * 
172  * 22    3/29/99 6:17p Dave
173  * More work on demo system. Got just about everything in except for
174  * blowing ships up, secondary weapons and player death/warpout.
175  * 
176  * 21    3/19/99 9:51a Dave
177  * Checkin to repair massive source safe crash. Also added support for
178  * pof-style nebulae, and some new weapons code.
179  * 
180  * 21    3/15/99 6:45p Daveb
181  * Put in rough nebula bitmap support.
182  * 
183  * 20    3/10/99 6:51p Dave
184  * Changed the way we buffer packets for all clients. Optimized turret
185  * fired packets. Did some weapon firing optimizations.
186  * 
187  * 19    2/26/99 6:01p Andsager
188  * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
189  * 
190  * 18    2/26/99 5:39p Johnson
191  * Put in some handling code for an assert().
192  * 
193  * 17    2/11/99 5:22p Andsager
194  * Fixed bugs, generalized block Sexp_variables
195  * 
196  * 16    2/05/99 10:38a Johnson
197  * Fixed improper object array reference. 
198  * 
199  * 15    2/04/99 1:23p Andsager
200  * Apply max spark limit to ships created in mission parse
201  * 
202  * 14    1/30/99 1:29a Dave
203  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
204  * screen.  Fixed beam weapon death messages.
205  * 
206  * 13    1/29/99 2:08a Dave
207  * Fixed beam weapon collisions with players. Reduced size of scoring
208  * struct for multiplayer. Disabled PXO.
209  * 
210  * 12    1/27/99 10:25p Andsager
211  * Added OEM sparks - make intelligent choice of next spark location for
212  * cruiser and cap ships
213  * 
214  * 11    1/25/99 5:03a Dave
215  * First run of stealth, AWACS and TAG missile support. New mission type
216  * :)
217  * 
218  * 10    1/21/99 4:22p Anoop
219  * Removed bogus Int3() from show_dead_message(...)
220  * 
221  * 9     1/12/99 5:45p Dave
222  * Moved weapon pipeline in multiplayer to almost exclusively client side.
223  * Very good results. Bandwidth goes down, playability goes up for crappy
224  * connections. Fixed object update problem for ship subsystems.
225  * 
226  * 8     12/23/98 2:53p Andsager
227  * Added ship activation and gas collection subsystems, removed bridge
228  * 
229  * 7     11/17/98 4:27p Andsager
230  * Stop sparks from emitting from destroyed subobjects
231  * 
232  * 6     11/09/98 2:11p Dave
233  * Nebula optimizations.
234  * 
235  * 5     10/26/98 9:42a Dave
236  * Early flak gun support.
237  * 
238  * 4     10/20/98 1:39p Andsager
239  * Make so sparks follow animated ship submodels.  Modify
240  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
241  * submodel_num.  Add submodel_num to multiplayer hit packet.
242  * 
243  * 3     10/13/98 9:29a Dave
244  * Started neatening up freespace.h. Many variables renamed and
245  * reorganized. Added AlphaColors.[h,cpp]
246  * 
247  * 2     10/07/98 10:53a Dave
248  * Initial checkin.
249  * 
250  * 1     10/07/98 10:51a Dave
251  * 
252  * 145   9/01/98 6:48p Dave
253  * Energy suck weapon. Removed a couple of now-bogus asserts in tracker
254  * code.
255  * 
256  * 144   8/25/98 1:48p Dave
257  * First rev of EMP effect. Player side stuff basically done. Next comes
258  * AI code.
259  * 
260  * 143   6/09/98 10:31a Hoffoss
261  * Created index numbers for all xstr() references.  Any new xstr() stuff
262  * added from here on out should be added to the end if the list.  The
263  * current list count can be found in FreeSpace.cpp (search for
264  * XSTR_SIZE).
265  * 
266  * 142   5/24/98 8:49p Allender
267  * put in Int3() to try and trap nasty error in multiplayer
268  * 
269  * 141   5/22/98 12:08p Mike
270  * Don't create "Disarmed" event for small ships.
271  * 
272  * 140   5/22/98 11:00a Mike
273  * Balance sm3-09a.
274  * 
275  * 139   5/18/98 12:41a Allender
276  * fixed subsystem problems on clients (i.e. not reporting properly on
277  * damage indicator).  Fixed ingame join problem with respawns.  minor
278  * comm menu stuff
279  * 
280  * 138   5/13/98 6:54p Dave
281  * More sophistication to PXO interface. Changed respawn checking so
282  * there's no window for desynchronization between the server and the
283  * clients.
284  * 
285  * 137   5/11/98 11:37a Mike
286  * Don't allow deathroll duration for large ships to be shortened by
287  * excess damage.
288  * 
289  * 136   5/10/98 11:30p Mike
290  * Better firing of bombs, less likely to go into strafe mode.
291  * 
292  * 135   5/07/98 12:24a Hoffoss
293  * Finished up sidewinder force feedback support.
294  * 
295  * 134   5/06/98 8:47a Andsager
296  * Initial check in, allow ship sparks to turn on and off depending on
297  * damage.
298  * 
299  * 133   5/06/98 1:12a Allender
300  * fix sequencing names, added nprintf to help respawn debugging
301  * 
302  * 132   5/04/98 4:07p Andsager
303  * Reduce deathroll rotvel cap and average by 25%.
304  * 
305  * 131   5/04/98 11:12a Mike
306  * Make kamikaze ships detonate on impact -- no death roll.
307  * 
308  * 130   4/30/98 12:17a Andsager
309  * Increase deathroll time for big ships and decrease max rotvel for big
310  * ships
311  * 
312  * 129   4/27/98 6:02p Dave
313  * Modify how missile scoring works. Fixed a team select ui bug. Speed up
314  * multi_lag system. Put in new main hall.
315  * 
316  * 128   4/27/98 2:23p Andsager
317  * Create fireballs on destroying subsystems of big ships.  Limit number
318  * of sparks on small ships.
319  * 
320  * 127   4/24/98 5:35p Andsager
321  * Fix sparks sometimes drawing not on model.  If ship is sphere in
322  * collision, don't draw sparks.  Modify ship_apply_local_damage() to take
323  * parameter no_spark.
324  * 
325  * 126   4/23/98 4:47p Andsager
326  * Make deathroll rotvel have z component largest.
327  * 
328  * 125   4/22/98 9:10a Andsager
329  * Resrore z component of deathroll rotvel, as before.  
330  * 
331  * 124   4/21/98 11:20p Andsager
332  * Modify deatroll rotvel. Fix bug getting only positive values.  Add
333  * random to current rotvel, make z rotvel always larger than x or y.
334  * Move call to shipfx_large_blowup_init into Ship.cpp.
335  * 
336  * 123   4/21/98 10:38a Mike
337  * Don't allow player to take _any_ kind of damage past stage 2 of
338  * warpout.
339  * 
340  * 122   4/20/98 5:10p John
341  * Took out cap of number of shards using num_particles.
342  * 
343  * 121   4/17/98 1:17p Mike
344  * Fix bug with uninitialized damage_to_apply.
345  * 
346  * 120   4/17/98 11:05a Mike
347  * Make number of pieces of small debris created by subsystem hits be
348  * based on detail level.
349  * Fix location of turret subsystem.  Was doubly globalizing, very bad on
350  * large ships!
351  * 
352  * 119   4/16/98 3:40p Mike
353  * Make subsys destroyed messages only get sent once.
354  * 
355  * 118   4/15/98 11:06p Mike
356  * Better balance of damage done to subsystems by shockwaves.
357  * 
358  * 117   4/15/98 10:13p Mike
359  * Fix application of damage to subsystems.
360  * 
361  * 116   4/14/98 9:15p John
362  * Added flag to specify which ships get the new large ship exploding
363  * effect.
364  * 
365  * 115   4/14/98 4:56p John
366  * Hooked in Andsager's large ship exploding code, but it is temporarily
367  * disabled.
368  * 
369  * 114   4/12/98 11:16a John
370  * Made palette red hit effect more proportional to damage.
371  * 
372  * 113   4/10/98 12:16p Allender
373  * fix ship hit kill and debris packets
374  * 
375  * 112   4/10/98 1:38a Allender
376  * fix bug with -1 vs 255 (for current secondary bank).  Undid previous
377  * code for ship_kill packets.
378  * 
379  * 111   4/09/98 5:44p Allender
380  * multiplayer network object fixes.  debris and self destructed ships
381  * should all sync up.  Fix problem where debris pieces (hull pieces) were
382  * not getting a net signature
383  * 
384  * 110   4/09/98 5:27p Mike
385  * Speed up deathrolls for smaller ships a bit.
386  * 
387  * 109   4/06/98 7:13p Allender
388  * generate the death text
389  * 
390  * 108   4/01/98 1:48p Allender
391  * major changes to ship collision in multiplayer.  Clients now do own
392  * ship/ship collisions (with their own ship only)  Modifed the hull
393  * update packet to be sent quicker when object is target of player.
394  * 
395  * 107   3/31/98 5:19p John
396  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
397  * bunch of debug stuff out of player file.  Made model code be able to
398  * unload models and malloc out only however many models are needed.
399  *  
400  * 
401  * 106   3/30/98 2:38p Mike
402  * Add asteroid_density to detail level support.
403  * No force explosion damage in training missions.
404  * Make cargo deathrolls shorter.
405  * 
406  * 105   3/30/98 1:08a Lawrance
407  * Implement "blast" icon.  Blink HUD icon when player ship is hit by a
408  * blast.
409  * 
410  * 104   3/24/98 4:26p Lawrance
411  * Make finding out if player killed self easier and more reliable
412  * 
413  * 103   3/24/98 2:16p Mike
414  * Fix bug with variation in death roll rotational velocity.  It wasn't
415  * getting initialized!
416  * 
417  * 102   3/21/98 3:36p Lawrance
418  * Let damage gauge know when player has taken damage.
419  * 
420  * 101   3/21/98 3:37p Mike
421  * Fix/optimize attacking of big ships.
422  * 
423  * 100   3/19/98 5:35p Lawrance
424  * Correctly inform player if killed by ship explosion.
425  *
426  * $NoKeywords: $
427  */
428
429 #include "pstypes.h"
430 #include "object.h"
431 #include "fvi.h"
432 #include "physics.h"
433 #include "vecmat.h"
434 #include "ship.h"
435 #include "model.h"
436 #include "key.h"
437 #include "weapon.h"
438 #include "radar.h"
439 #include "2d.h"
440 #include "3d.h"
441 #include "floating.h"
442 #include "ai.h"
443 #include "ailocal.h"
444 #include "fireballs.h"
445 #include "debris.h"
446 #include "hud.h"
447 #include "timer.h"
448 #include "cfile.h"
449 #include "missionlog.h"
450 #include "missionparse.h"
451 #include "bmpman.h"
452 #include "joy.h"
453 #include "joy_ff.h"
454 #include "player.h"
455 #include "parselo.h"
456 #include "freespace.h"
457 #include "sound.h"
458 #include "linklist.h"
459 #include "hudets.h"
460 #include "hudtarget.h"
461 #include "multi.h"
462 #include "multiutil.h"
463 #include "aigoals.h"
464 #include "gamesnd.h"
465 #include "eventmusic.h"
466 #include "shipfx.h"
467 #include "sexp.h"
468 #include "gamesequence.h"
469 #include "objectsnd.h"
470 #include "cmeasure.h"
471 #include "animplay.h"
472 #include "controlsconfig.h"
473 #include "afterburner.h"
474 #include "shockwave.h"
475 #include "hudsquadmsg.h"
476 #include "swarm.h"
477 #include "shiphit.h"
478 #include "multimsgs.h"
479 #include "particle.h"
480 #include "multi_respawn.h"
481 #include "popup.h"
482 #include "emp.h"
483 #include "beam.h"
484 #include "demo.h"
485 #include "multi_pmsg.h"
486
487 //#pragma optimize("", off)
488 //#pragma auto_inline(off)
489
490 typedef struct spark_pair {
491         int index1, index2;
492         float dist;
493 } spark_pair;
494
495 #define MAX_SPARK_PAIRS         ((MAX_SHIP_HITS * MAX_SHIP_HITS - MAX_SHIP_HITS) / 2)
496
497 #define BIG_SHIP_MIN_RADIUS     80.0f   //      ship radius above which death rolls can't be shortened by excessive damage
498
499 vector  Dead_camera_pos;
500 vector  Original_vec_to_deader;
501
502 //      Decrease damage applied to a subsystem based on skill level.
503 float   Skill_level_subsys_damage_scale[NUM_SKILL_LEVELS] = {0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
504
505 bool is_subsys_destroyed(ship *shipp, int submodel)
506 {
507         ship_subsys *subsys;
508
509         if (submodel == -1) {
510                 return false;
511         }
512
513         for ( subsys=GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys) ) {
514                 if (subsys->system_info->subobj_num == submodel) {
515                         if (subsys->current_hits > 0) {
516                                 return false;
517                         } else {
518                                 return true;
519                         }
520                 }
521         }
522
523         return false;
524 }
525
526 // do_subobj_destroyed_stuff is called when a subobject for a ship is killed.  Separated out
527 // to separate function on 10/15/97 by MWA for easy multiplayer access.  It does all of the
528 // cool things like blowing off the model (if applicable, writing the logs, etc)
529 void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vector* hitpos )
530 {
531         ship_info *sip;
532         object *ship_obj;
533         model_subsystem *psub;
534         vector  g_subobj_pos;
535         int type, i, log_index;
536
537         // get some local variables
538         sip = &Ship_info[ship_p->ship_info_index];
539         ship_obj = &Objects[ship_p->objnum];
540         psub = subsys->system_info;
541         type = psub->type;
542         get_subsystem_world_pos(ship_obj, subsys, &g_subobj_pos);
543
544         // create fireballs when subsys destroy for large ships.
545         object* objp = &Objects[ship_p->objnum];
546         if (objp->radius > 100.0f) {
547                 // number of fireballs determined by radius of subsys
548                 int num_fireballs;
549                 if ( psub->radius < 3 ) {
550                         num_fireballs = 1;
551                 } else {
552                          num_fireballs = 5;
553                 }
554
555                 vector temp_vec, center_to_subsys, rand_vec;
556                 vm_vec_sub(&center_to_subsys, &g_subobj_pos, &objp->pos);
557                 for (int i=0; i<num_fireballs; i++) {
558                         if (i==0) {
559                                 // make first fireball at hitpos
560                                 if (hitpos) {
561                                         temp_vec = *hitpos;
562                                 } else {
563                                         temp_vec = g_subobj_pos;
564                                 }
565                         } else {
566                                 // make other fireballs at random positions, but try to keep on the surface
567                                 vm_vec_rand_vec_quick(&rand_vec);
568                                 float dot = vm_vec_dotprod(&center_to_subsys, &rand_vec);
569                                 vm_vec_scale_add2(&rand_vec, &center_to_subsys, -dot/vm_vec_mag_squared(&center_to_subsys));
570                                 vm_vec_scale_add(&temp_vec, &g_subobj_pos, &rand_vec, 0.5f*psub->radius);
571                         }
572
573                         // scale fireball size according to size of subsystem, but not less than 10
574                         float fireball_rad = psub->radius * 0.2f;
575                         if (fireball_rad < 10) {
576                                 fireball_rad = 10.0f;
577                         }
578
579                         vector fb_vel;
580                         vm_vec_crossprod(&fb_vel, &objp->phys_info.rotvel, &center_to_subsys);
581                         vm_vec_add2(&fb_vel, &objp->phys_info.vel);
582                         fireball_create( &temp_vec, FIREBALL_EXPLOSION_MEDIUM, OBJ_INDEX(objp), fireball_rad, 0, &fb_vel );
583                 }
584         }
585
586         if ( MULTIPLAYER_MASTER ) {
587                 int index;
588
589                 index = ship_get_index_from_subsys(subsys, ship_p->objnum);
590                 
591                 vector hit;
592                 if (hitpos) {
593                         hit = *hitpos;
594                 } else {
595                         hit = g_subobj_pos;
596                 }
597                 send_subsystem_destroyed_packet( ship_p, index, hit );
598         }
599
600         // next do a quick sanity check on the current hits that we are keeping for the generic subsystems
601         // I think that there might be rounding problems with the floats.  This code keeps us safe.
602         if ( ship_p->subsys_info[type].num == 1 ) {
603                 ship_p->subsys_info[type].current_hits = 0.0f;
604         } else {
605                 float hits;
606                 ship_subsys *ssp;
607
608                 hits = 0.0f;
609                 for ( ssp=GET_FIRST(&ship_p->subsys_list); ssp != END_OF_LIST(&ship_p->subsys_list); ssp = GET_NEXT(ssp) ) {
610                         if ( ssp->system_info->type == type )
611                                 hits += ssp->current_hits;
612                 }
613                 ship_p->subsys_info[type].current_hits = hits;
614         }
615
616         // store an event in the event log.  Also, determine if all turrets or all
617         // engines have been destroyed (if the subsystem is a turret or engine).
618         // put a disabled or disarmed entry in the log if this is the case
619         // 
620         // MWA -- 1/8/98  A problem was found when trying to determine (via sexpression) when some subsystems
621         // were destroyed.  The bottom line is that is the psub->name and psub->subobj_name are different,
622         // then direct detection doesn't work.  (This scenario happens mainly with turrets and probably with
623         // engines).  So, my solution is to encode the ship_info index, and the subsystem index into one
624         // integer, and pass that as the "index" parameter to add_entry.  We'll use that information to
625         // print out the info in the mission log.
626         SDL_assert( ship_p->ship_info_index < 65535 );
627
628         // get the "index" of this subsystem in the ship info structure.
629         for ( i = 0; i < sip->n_subsystems; i++ ) {
630                 if ( &(sip->subsystems[i]) == psub )
631                         break;
632         }
633         SDL_assert( i < sip->n_subsystems );
634         SDL_assert( i < 65535 );
635         log_index = ((ship_p->ship_info_index << 16) & 0xffff0000) | (i & 0xffff);
636
637 #ifndef MAKE_FS1
638         // Don't log or display info about the activation subsytem
639         int display = (psub->type != SUBSYSTEM_ACTIVATION);
640 #else
641         int display = 1;
642 #endif
643         if (display) {
644                 mission_log_add_entry(LOG_SHIP_SUBSYS_DESTROYED, ship_p->ship_name, psub->subobj_name, log_index );
645                 if ( ship_obj == Player_obj ) {
646                         snd_play( &Snds[SND_SUBSYS_DIE_1], 0.0f );
647                         HUD_printf(XSTR( "Your %s subsystem has been destroyed", 499), psub->name);
648                 }
649         }
650
651         if ( psub->type == SUBSYSTEM_TURRET ) {
652                 if ( ship_p->subsys_info[type].current_hits == 0.0f ) {
653                         //      Don't create "disarmed" event for small ships.
654                         if (!(Ship_info[ship_p->ship_info_index].flags & SIF_SMALL_SHIP)) {
655                                 mission_log_add_entry(LOG_SHIP_DISARMED, ship_p->ship_name, NULL );
656                                 // ship_p->flags |= SF_DISARMED;
657                         }
658                 }
659         } else if (psub->type == SUBSYSTEM_ENGINE ) {
660                 // when an engine is destroyed, we must change the max velocity of the ship
661                 // to be some fraction of its normal maximum value
662
663                 if ( ship_p->subsys_info[type].current_hits == 0.0f ) {
664                         mission_log_add_entry(LOG_SHIP_DISABLED, ship_p->ship_name, NULL );
665                         ship_p->flags |= SF_DISABLED;                           // add the disabled flag
666                 }
667         }
668
669         if ( psub->subobj_num > -1 )    {
670                 shipfx_blow_off_subsystem(ship_obj,ship_p,subsys,&g_subobj_pos);
671                 subsys->submodel_info_1.blown_off = 1;
672         }
673
674         if ( (psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >-1) )               {
675                 subsys->submodel_info_2.blown_off = 1;
676         }
677
678         // play sound effect when subsys gets blown up
679         int sound_index=-1;
680         if ( Ship_info[ship_p->ship_info_index].flags & SIF_HUGE_SHIP ) {
681                 sound_index=SND_CAPSHIP_SUBSYS_EXPLODE;
682         } else if ( Ship_info[ship_p->ship_info_index].flags & SIF_BIG_SHIP ) {
683                 sound_index=SND_SUBSYS_EXPLODE;
684         }
685         if ( sound_index >= 0 ) {
686                 snd_play_3d( &Snds[sound_index], &g_subobj_pos, &View_position );
687         }
688 }
689
690 // Return weapon type that is associated with damaging_objp
691 // input:       damaging_objp           =>      object pointer responsible for damage
692 //      exit:           -1              =>      no weapon type is associated with damage object
693 //                              >=0     =>      weapon type associated with damage object
694 int shiphit_get_damage_weapon(object *damaging_objp)
695 {
696         int weapon_info_index = -1;
697
698         if ( damaging_objp ) {
699                 switch(damaging_objp->type) {
700                 case OBJ_WEAPON:
701                         weapon_info_index = Weapons[damaging_objp->instance].weapon_info_index;
702                         break;
703                 case OBJ_SHOCKWAVE:
704                         weapon_info_index = shockwave_weapon_index(damaging_objp->instance);
705                         break;
706                 default:
707                         weapon_info_index = -1;
708                         break;
709                 }
710         }
711
712         return weapon_info_index;
713 }
714
715 //      Return range at which this object can apply damage.
716 //      Based on object type and subsystem type.
717 float subsys_get_range(object *other_obj, ship_subsys *subsys)
718 {
719         float   range;
720
721         if (other_obj->type == OBJ_SHOCKWAVE) {
722                 range = Shockwaves[other_obj->instance].outer_radius * 0.75f;   //      Shockwaves were too lethal to subsystems.
723         } else if ( subsys->system_info->type == SUBSYSTEM_TURRET ) {
724                 range = subsys->system_info->radius*3;
725         } else {
726                 range = subsys->system_info->radius*2;
727         }
728
729         return range;
730 }
731
732 #define MAX_DEBRIS_SHARDS       16              // cap the amount of debris shards that fly off per hit
733
734 // Make some random debris particles.  Previous way was not very random.  Create debris 75% of the time.
735 // Don't worry about multiplayer since this debris is the small stuff that cannot collide
736 void create_subsys_debris(object *ship_obj, vector *hitpos)
737 {
738         float show_debris = frand();
739         
740         if ( show_debris <= 0.75f ) {
741                 int ndebris;
742
743                 ndebris = (int)(show_debris * Detail.num_small_debris) + 1;                     // number of pieces of debris to create
744
745                 if ( ndebris > MAX_DEBRIS_SHARDS )
746                         ndebris = MAX_DEBRIS_SHARDS;
747
748                 //mprintf(( "Damage = %.1f, ndebris=%d\n", show_debris, ndebris ));
749                 for (int i=0; i<ndebris; i++ )  {
750                         debris_create( ship_obj, -1, -1, hitpos, hitpos, 0, 1.0f );
751                 }
752         }
753 }
754
755 void create_vaporize_debris(object *ship_obj, vector *hitpos)
756 {
757         int ndebris;
758         float show_debris = frand();
759
760         ndebris = (int)(4.0f * ((0.5f + show_debris) * Detail.num_small_debris)) + 5;                   // number of pieces of debris to create
761
762         if ( ndebris > MAX_DEBRIS_SHARDS ) {
763                 ndebris = MAX_DEBRIS_SHARDS;
764         }
765
766         //mprintf(( "Damage = %.1f, ndebris=%d\n", show_debris, ndebris ));
767         for (int i=0; i<ndebris; i++ )  {
768                 debris_create( ship_obj, -1, -1, hitpos, hitpos, 0, 1.4f );
769         }
770 }
771
772 #define MAX_SUBSYS_LIST 32
773
774 typedef struct {
775         float   dist;
776         float   range;
777         ship_subsys     *ptr;
778 } sublist;
779
780 // do_subobj_hit_stuff() is called when a collision is detected between a ship and something
781 // else.  This is where we see if any sub-objects on the ship should take damage.
782 //
783 //      Depending on where the collision occurs, the sub-system and surrounding hull will take 
784 // different amounts of damage.  The amount of damage a sub-object takes depending on how
785 // close the colliding object is to the center of the sub-object.  The remaining hull damage
786 // will be returned to the caller via the damage parameter.
787 //
788 //
789 // 0   -> 0.5 radius   : 100% subobject    0%  hull
790 // 0.5 -> 1.0 radius   :  50% subobject   50%  hull
791 // 1.0 -> 2.0 radius   :  25% subobject   75%  hull
792 //     >  2.0 radius   :   0% subobject  100%  hull
793 //
794 //
795 // The weapon damage is not neccesarily distributed evently between sub-systems when more than
796 // one sub-system is to take damage.  Whenever damage is to be assigned to a sub-system, the above
797 // percentages are used.  So, if more than one sub-object is taking damage, the second sub-system
798 // to be assigned damage will take less damage.  Eg. weapon hits in the 25% damage range of two
799 // subsytems, and the weapon damage is 12.  First subsystem takes 3 points damage.  Second subsystem
800 // will take 0.25*9 = 2.25 damage.  Should be close enough for most cases, and hull would receive 
801 // 0.75 * 9 = 6.75 damage.
802 //
803 //      Used to use the following constants, but now damage is linearly scaled up to 2x the subsystem
804 //      radius.  Same damage applied as defined by constants below.
805 //
806 //      Returns unapplied damage, which will probably be applied to the hull.
807 //
808 // Shockwave damage is handled here.  If other_obj->type == OBJ_SHOCKWAVE, it's a shockwave.
809 // apply the same damage to all subsystems.
810 //      Note: A negative damage number means to destroy the corresponding subsystem.  For example, call with -SUBSYSTEM_ENGINE to destroy engine.
811
812 float do_subobj_hit_stuff(object *ship_obj, object *other_obj, vector *hitpos, float damage)
813 {
814         vector                  g_subobj_pos;
815         float                           damage_left;
816         int                             weapon_info_index;
817         ship_subsys             *subsys;
818         ship                            *ship_p;
819         sublist                 subsys_list[MAX_SUBSYS_LIST];
820         vector                  hitpos2;
821
822         ship_p = &Ships[ship_obj->instance];
823
824         //      Don't damage player subsystems in a training mission.
825         if ( The_mission.game_type & MISSION_TYPE_TRAINING ) {
826                 if (ship_obj == Player_obj){
827                         return damage;
828                 }
829         }
830
831         //      Shockwave damage is applied like weapon damage.  It gets consumed.
832         if (other_obj->type == OBJ_SHOCKWAVE) {
833                 //      MK, 9/2/99.  Shockwaves do zero subsystem damage on small ships.
834                 if ( Ship_info[ship_p->ship_info_index].flags & (SIF_SMALL_SHIP))
835                         return damage;
836                 else {
837
838                         damage_left = Shockwaves[other_obj->instance].damage/4.0f;
839                 }
840                 hitpos2 = other_obj->pos;
841         } else {
842                 damage_left = damage;
843                 hitpos2 = *hitpos;
844         }
845
846         // scale subsystem damage if appropriate
847         weapon_info_index = shiphit_get_damage_weapon(other_obj);
848         if ((other_obj->type == OBJ_WEAPON) && ( weapon_info_index >= 0 )) {
849                 damage_left *= Weapon_info[weapon_info_index].subsystem_factor;
850         }
851
852
853 #ifndef NDEBUG
854         float hitpos_dist = vm_vec_dist( hitpos, &ship_obj->pos );      
855         if ( hitpos_dist > ship_obj->radius * 2.0f )    {
856                 mprintf(( "BOGUS HITPOS PASSED TO DO_SUBOBJ_HIT_STUFF (%.1f > %.1f)!\n", hitpos_dist, ship_obj->radius * 2.0f ));
857                 // Int3();      // Get John ASAP!!!!  Someone passed a local coordinate instead of world for hitpos probably.
858         }
859 #endif
860
861         create_subsys_debris(ship_obj, hitpos);
862
863         //      First, create a list of the N subsystems within range.
864         //      Then, one at a time, process them in order.
865         int     count = 0;
866         for ( subsys=GET_FIRST(&ship_p->subsys_list); subsys != END_OF_LIST(&ship_p->subsys_list); subsys = GET_NEXT(subsys) ) {
867                 #ifndef NDEBUG
868                 //      Debug option.  If damage is negative of subsystem type, then just destroy that subsystem.
869                 if (damage < 0.0f) {
870                         // single player or multiplayer
871                         SDL_assert(Player_ai->targeted_subsys != NULL);
872                         if ( (subsys == Player_ai->targeted_subsys) && (subsys->current_hits > 0) ) {
873                                 SDL_assert(subsys->system_info->type == (int) -damage);
874                                 ship_p->subsys_info[subsys->system_info->type].current_hits -= subsys->current_hits;
875                                 if (ship_p->subsys_info[subsys->system_info->type].current_hits < 0) {
876                                         ship_p->subsys_info[subsys->system_info->type].current_hits = 0.0f;
877                                 }
878                                 subsys->current_hits = 0.0f;
879                                 do_subobj_destroyed_stuff( ship_p, subsys, hitpos );
880                                 continue;
881                         } else {
882                                 continue;
883                         }
884                 }
885                 #endif
886                 
887                 if (subsys->current_hits > 0.0f) {
888                         float   dist;
889
890                         // calculate the distance between the hit and the subsystem center
891                         get_subsystem_world_pos(ship_obj, subsys, &g_subobj_pos);
892                         dist = vm_vec_dist_quick(&hitpos2, &g_subobj_pos);
893
894                         float range = subsys_get_range(other_obj, subsys);
895
896                         if ( dist < range) {
897                                 subsys_list[count].dist = dist;
898                                 subsys_list[count].range = range;
899                                 subsys_list[count].ptr = subsys;
900                                 count++;
901
902                                 if (count >= MAX_SUBSYS_LIST){
903                                         break;
904                                 }
905                         }
906                 }
907         }
908
909         //      Now scan the sorted list of subsystems in range.
910         //      Apply damage to the nearest one first, subtracting off damage as we go.
911         int     i, j;
912         for (j=0; j<count; j++) {
913                 float   dist, range;
914                 ship_subsys     *subsys;
915
916                 int     min_index = -1;
917                 float   min_dist = 9999999.9f;
918
919                 for (i=0; i<count; i++) {
920                         if (subsys_list[i].dist < min_dist) {
921                                 min_dist = subsys_list[i].dist;
922                                 min_index = i;
923                         }
924                 }
925                 SDL_assert(min_index != -1);
926
927                 float   damage_to_apply = 0.0f;
928                 subsys = subsys_list[min_index].ptr;
929                 range = subsys_list[min_index].range;
930                 dist = subsys_list[min_index].dist;
931                 subsys_list[min_index].dist = 9999999.9f;       //      Make sure we don't use this one again.
932
933                 //      HORRIBLE HACK!
934                 //      MK, 9/4/99
935                 //      When Helios bombs are dual fired against the Juggernaut in sm3-01 (FS2), they often
936                 //      miss their target.  There is code dating to FS1 in the collision code to detect that a bomb or
937                 //      missile has somehow missed its target.  It gets its lifeleft set to 0.1 and then it detonates.
938                 //      Unfortunately, the shockwave damage was cut by 4 above.  So boost it back up here.
939                 if ((dist < 10.0f) && (other_obj->type == OBJ_SHOCKWAVE)) {
940                         damage_left *= 4.0f * Weapon_info[weapon_info_index].subsystem_factor;;
941                 }
942
943 //              if (damage_left > 100.0f)
944 //                      nprintf(("AI", "Applying %7.3f damage to subsystem %7.3f units away.\n", damage_left, dist));
945
946                 if ( dist < range/2.0f ) {
947                         damage_to_apply = damage_left;
948                 } else if ( dist < range ) {
949                         damage_to_apply = damage_left * (1.0f - dist/range);
950                 }
951
952                 // if we're not in CLIENT_NODAMAGE multiplayer mode (which is a the NEW way of doing things)
953                 if (damage_to_apply > 0.1f && !(MULTIPLAYER_CLIENT) && !(Game_mode & GM_DEMO_PLAYBACK)) {
954                         //      Decrease damage to subsystems to player ships.
955                         if (ship_obj->flags & OF_PLAYER_SHIP){
956                                 damage_to_apply *= Skill_level_subsys_damage_scale[Game_skill_level];
957                         }
958                 
959                         subsys->current_hits -= damage_to_apply;
960                         ship_p->subsys_info[subsys->system_info->type].current_hits -= damage_to_apply;
961                         damage_left -= damage_to_apply;         // decrease the damage left to apply to the ship subsystems
962
963                         if (subsys->current_hits < 0.0f) {
964                                 damage_left -= subsys->current_hits;
965                                 ship_p->subsys_info[subsys->system_info->type].current_hits -= subsys->current_hits;
966                                 subsys->current_hits = 0.0f;                                    // set to 0 so repair on subsystem takes immediate effect
967                         }
968
969                         if ( ship_p->subsys_info[subsys->system_info->type].current_hits < 0.0f ){
970                                 ship_p->subsys_info[subsys->system_info->type].current_hits = 0.0f;
971                         }
972
973                         // multiplayer clients never blow up subobj stuff on their own
974                         if ( (subsys->current_hits <= 0.0f) && !MULTIPLAYER_CLIENT && !(Game_mode & GM_DEMO_PLAYBACK)){
975                                 do_subobj_destroyed_stuff( ship_p, subsys, hitpos );
976                         }
977
978                         if (damage_left <= 0)   { // no more damage to distribute, so stop checking
979                         //      damage_left = 0.0f;
980                                 break;
981                         }
982                 }
983 //nprintf(("AI", "j=%i, sys = %s, dam = %6.1f, dam left = %6.1f, subhits = %5.0f\n", j, subsys->system_info->name, damage_to_apply, damage_left, subsys->current_hits));
984         }
985
986         //      Note: I changed this to return damage_left and it completely screwed up balance.
987         //      It had taken a few MX-50s to destory an Anubis (with 40% hull), then it took maybe ten.
988         //      So, I left it alone. -- MK, 4/15/98
989         return damage;
990 }
991
992 // Store who/what killed the player, so we can tell the player how he died
993 void shiphit_record_player_killer(object *killer_objp, player *p)
994 {
995         switch (killer_objp->type) {
996
997         case OBJ_WEAPON:
998                 p->killer_objtype=OBJ_WEAPON;
999                 p->killer_weapon_index=Weapons[killer_objp->instance].weapon_info_index;
1000                 p->killer_species = Ship_info[Ships[Objects[killer_objp->parent].instance].ship_info_index].species;
1001
1002                 if ( &Objects[killer_objp->parent] == Player_obj ) {
1003                         // killed by a missile?
1004                         if(Weapon_info[p->killer_weapon_index].subtype == WP_MISSILE){
1005                                 p->flags |= PLAYER_FLAGS_KILLED_SELF_MISSILES;
1006                         } else {
1007                                 p->flags |= PLAYER_FLAGS_KILLED_SELF_UNKNOWN;
1008                         }
1009                 }
1010
1011                 // in multiplayer, record callsign of killer if killed by another player
1012                 if ( (Game_mode & GM_MULTIPLAYER) && ( Objects[killer_objp->parent].flags & OF_PLAYER_SHIP) ) {
1013                         int pnum;
1014
1015                         pnum = multi_find_player_by_object( &Objects[killer_objp->parent] );
1016                         if ( pnum != -1 ) {
1017                                 SDL_strlcpy(p->killer_parent_name, Net_players[pnum].player->callsign, SDL_arraysize(p->killer_parent_name));
1018                         } else {
1019                                 nprintf(("Network", "Couldn't find player object of weapon for killer of %s\n", p->callsign));
1020                         }
1021                 } else {
1022                         SDL_strlcpy(p->killer_parent_name, Ships[Objects[killer_objp->parent].instance].ship_name, SDL_arraysize(p->killer_parent_name));
1023                 }
1024                 break;
1025
1026         case OBJ_SHOCKWAVE:
1027                 p->killer_objtype=OBJ_SHOCKWAVE;
1028                 p->killer_weapon_index=shockwave_weapon_index(killer_objp->instance);
1029                 p->killer_species = Ship_info[Ships[Objects[killer_objp->parent].instance].ship_info_index].species;
1030
1031                 if ( &Objects[killer_objp->parent] == Player_obj ) {
1032                         p->flags |= PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE;
1033                 }
1034
1035                 if ( (Game_mode & GM_MULTIPLAYER) && ( Objects[killer_objp->parent].flags & OF_PLAYER_SHIP) ) {
1036                         int pnum;
1037
1038                         pnum = multi_find_player_by_object( &Objects[killer_objp->parent] );
1039                         if ( pnum != -1 ) {
1040                                 SDL_strlcpy(p->killer_parent_name, Net_players[pnum].player->callsign, SDL_arraysize(p->killer_parent_name));
1041                         } else {
1042                                 nprintf(("Network", "Couldn't find player object of shockwave for killer of %s\n", p->callsign));
1043                         }
1044                 } else {
1045                         SDL_strlcpy(p->killer_parent_name, Ships[Objects[killer_objp->parent].instance].ship_name, SDL_arraysize(p->killer_parent_name));
1046                 }
1047                 break;
1048
1049         case OBJ_SHIP:
1050                 p->killer_objtype=OBJ_SHIP;
1051                 p->killer_weapon_index=-1;
1052                 p->killer_species = Ship_info[Ships[killer_objp->instance].ship_info_index].species;
1053
1054                 if ( Ships[killer_objp->instance].flags & SF_EXPLODED ) {
1055                         p->flags |= PLAYER_FLAGS_KILLED_BY_EXPLOSION;
1056                 }
1057
1058                 if ( Ships[Objects[p->objnum].instance].wash_killed ) {
1059                         p->flags |= PLAYER_FLAGS_KILLED_BY_ENGINE_WASH;
1060                 }
1061
1062                 // in multiplayer, record callsign of killer if killed by another player
1063                 if ( (Game_mode & GM_MULTIPLAYER) && (killer_objp->flags & OF_PLAYER_SHIP) ) {
1064                         int pnum;
1065
1066                         pnum = multi_find_player_by_object( killer_objp );
1067                         if ( pnum != -1 ) {
1068                                 SDL_strlcpy(p->killer_parent_name, Net_players[pnum].player->callsign, SDL_arraysize(p->killer_parent_name));
1069                         } else {
1070                                 nprintf(("Network", "Couldn't find player object for killer of %s\n", p->callsign));
1071                         }
1072                 } else {
1073                         SDL_strlcpy(p->killer_parent_name, Ships[killer_objp->instance].ship_name, SDL_arraysize(p->killer_parent_name));
1074                 }
1075                 break;
1076         
1077         case OBJ_DEBRIS:
1078         case OBJ_ASTEROID:
1079                 if ( killer_objp->type == OBJ_DEBRIS ) {
1080                         p->killer_objtype = OBJ_DEBRIS;
1081                 } else {
1082                         p->killer_objtype = OBJ_ASTEROID;
1083                 }
1084                 p->killer_weapon_index=-1;
1085                 p->killer_species = SPECIES_NONE;
1086                 p->killer_parent_name[0] = '\0';
1087                 break;
1088
1089         case OBJ_BEAM:
1090                 int beam_obj;
1091                 beam_obj = beam_get_parent(killer_objp);
1092                 p->killer_species = SPECIES_NONE;               
1093                 p->killer_objtype = OBJ_BEAM;
1094                 if(beam_obj != -1){                     
1095                         if((Objects[beam_obj].type == OBJ_SHIP) && (Objects[beam_obj].instance >= 0)){
1096                                 SDL_strlcpy(p->killer_parent_name, Ships[Objects[beam_obj].instance].ship_name, SDL_arraysize(p->killer_parent_name));
1097                         }
1098                         p->killer_species = Ship_info[Ships[Objects[beam_obj].instance].ship_info_index].species;
1099                 } else {                        
1100                         SDL_strlcpy(p->killer_parent_name, "", SDL_arraysize(p->killer_parent_name));
1101                 }
1102                 break;
1103         
1104         case OBJ_NONE:
1105                 if ( Game_mode & GM_MULTIPLAYER ) {
1106                         Int3();
1107                 }
1108                 p->killer_objtype=-1;
1109                 p->killer_weapon_index=-1;
1110                 p->killer_parent_name[0]=0;
1111                 p->killer_species = SPECIES_NONE;
1112                 break;
1113
1114         default:
1115                 Int3();
1116                 break;
1117         }
1118 }
1119
1120 //      Say dead stuff.
1121 void show_dead_message(object *ship_obj, object *other_obj)
1122 {
1123         int pnum;
1124         player *player_p;
1125
1126         // not doing anything when a non player dies.
1127         if ( !(ship_obj->flags & OF_PLAYER_SHIP) ){
1128                 return;
1129         }
1130
1131         if(other_obj == NULL){
1132                 return;
1133         }
1134
1135         // Get a pointer to the player (we are assured a player ship was killed)
1136         if ( Game_mode & GM_NORMAL ) {
1137                 player_p = Player;
1138         } else {
1139                 // in multiplayer, get a pointer to the player that died.
1140                 pnum = multi_find_player_by_object( ship_obj );
1141                 if ( pnum == -1 ) {
1142                         //Int3();                               // this condition is bad bad bad -- get Allender
1143                         return;
1144                 }
1145                 player_p = Net_players[pnum].player;
1146         }
1147
1148         // multiplayer clients should already have this information.
1149         if ( !MULTIPLAYER_CLIENT ){
1150                 shiphit_record_player_killer( other_obj, player_p );
1151         }
1152
1153         // display a hud message is the guy killed isn't me (multiplayer only)
1154         /*
1155         if ( (Game_mode & GM_MULTIPLAYER) && (ship_obj != Player_obj) ) {
1156                 char death_text[256];
1157
1158                 player_generate_death_text( player_p, death_text );
1159                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, death_text);
1160         }
1161         */
1162 }
1163
1164 /* JAS: THIS DOESN'T SEEM TO BE USED, SO I COMMENTED IT OUT
1165 //      Apply damage to a ship, destroying if necessary, etc.
1166 //      Returns portion of damage that exceeds ship shields, ie the "unused" portion of the damage.
1167 //      Note: This system does not use the mesh shield.  It applies damage to the overall ship shield.
1168 float apply_damage_to_ship(object *objp, float damage)
1169 {
1170         float   _ss;
1171
1172         add_shield_strength(objp, -damage);
1173
1174         // check if shields are below 0%, if so take leftover damage and apply to ship integrity
1175         if ((_ss = get_shield_strength(objp)) < 0.0f ) {
1176                 damage = -_ss;
1177                 set_shield_strength(objp, 0.0f);
1178         } else
1179                 damage = 0.0f;
1180
1181         return damage;
1182 }
1183 */
1184
1185 //      Do music processing for a ship hit.
1186 void ship_hit_music(object *ship_obj, object *other_obj)
1187 {
1188         ship* ship_p = &Ships[ship_obj->instance];
1189
1190         // Switch to battle track when a ship is hit by fire 
1191         //
1192         // If the ship hit has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive
1193         // ship, so don't start the battle music        
1194         if (SDL_strcasecmp(Ai_class_names[Ai_info[ship_p->ai_index].ai_class], NOX("none"))) {
1195                 int team_1, team_2;
1196                 // Only start if ship hit and firing ship are from different teams
1197                 team_1 = Ships[ship_obj->instance].team;
1198                 switch ( other_obj->type ) {
1199                         case OBJ_SHIP:
1200                                 team_2 = Ships[other_obj->instance].team;
1201                                 if ( !SDL_strcasecmp(Ai_class_names[Ai_info[Ships[other_obj->instance].ai_index].ai_class], NOX("none")) ) {
1202                                         team_1 = team_2;
1203                                 }
1204                                 break;
1205
1206                         case OBJ_WEAPON:
1207                                 // parent of weapon is object num of ship that fired it
1208                                 team_2 = Ships[Objects[other_obj->parent].instance].team;       
1209                                 break;
1210
1211                         default:
1212                                 team_2 = team_1;
1213                                 break;  // Unexpected object type collided with ship, no big deal.
1214                 } // end switch
1215
1216                 if ( team_1 != team_2 )
1217                         event_music_battle_start();
1218         }
1219 }
1220
1221 //      Make sparks fly off a ship.
1222 // Currently used in misison_parse to create partially damaged ships.
1223 // NOTE: hitpos is in model coordinates on the detail[0] submodel (highest detail hull)
1224 // WILL NOT WORK RIGHT IF ON A ROTATING SUBMODEL
1225 void ship_hit_sparks_no_rotate(object *ship_obj, vector *hitpos)
1226 {
1227         ship            *ship_p = &Ships[ship_obj->instance];
1228
1229         int n = ship_p->num_hits;
1230         if (n >= MAX_SHIP_HITS) {
1231                 n = rand() % MAX_SHIP_HITS;
1232         } else {
1233                 ship_p->num_hits++;
1234         }
1235
1236         // No rotation.  Just make the spark
1237         ship_p->sparks[n].pos = *hitpos;
1238         ship_p->sparks[n].submodel_num = -1;
1239
1240         shipfx_emit_spark(ship_obj->instance, n);               // Create the first wave of sparks
1241
1242         if ( n == 0 )   {
1243                 ship_p->next_hit_spark = timestamp(0);          // when a hit spot will spark
1244         }
1245 }
1246
1247 // find the max number of sparks allowed for ship
1248 // limited for fighter by hull % others by radius.
1249 int get_max_sparks(object* ship_obj)
1250 {
1251         SDL_assert(ship_obj->type == OBJ_SHIP);
1252         SDL_assert((ship_obj->instance >= 0) && (ship_obj->instance < MAX_SHIPS));
1253         if(ship_obj->type != OBJ_SHIP){
1254                 return 1;
1255         }
1256         if((ship_obj->instance < 0) || (ship_obj->instance >= MAX_SHIPS)){
1257                 return 1;
1258         }
1259
1260         ship *ship_p = &Ships[ship_obj->instance];
1261         ship_info* si = &Ship_info[ship_p->ship_info_index];
1262         if (si->flags & SIF_FIGHTER) {
1263                 float hull_percent = ship_obj->hull_strength / Ship_info[ship_p->ship_info_index].initial_hull_strength;
1264
1265                 if (hull_percent > 0.8f) {
1266                         return 1;
1267                 } else if (hull_percent > 0.3f) {
1268                         return 2;
1269                 } else {
1270                         return 3;
1271                 }
1272         } else {
1273                 int num_sparks = (int) (ship_obj->radius * 0.08f);
1274                 if (num_sparks < 3) {
1275                         return 3;
1276                 } else if (num_sparks > MAX_SHIP_HITS) {
1277                         return MAX_SHIP_HITS;
1278                 } else {
1279                         return num_sparks;
1280                 }
1281         }
1282 }
1283
1284
1285 // helper function to qsort, sorting spark pairs by distance
1286 int spark_compare( const void *elem1, const void *elem2 )
1287 {
1288         spark_pair *pair1 = (spark_pair *) elem1;
1289         spark_pair *pair2 = (spark_pair *) elem2;
1290
1291         SDL_assert(pair1->dist >= 0);
1292         SDL_assert(pair2->dist >= 0);
1293
1294         if ( pair1->dist <  pair2->dist ) {
1295                 return -1;
1296         } else {
1297                 return 1;
1298         }
1299 }
1300
1301 // for big ships, when all spark slots are filled, make intelligent choice of one to be recycled
1302 int choose_next_spark(object *ship_obj, vector *hitpos)
1303 {
1304         int i, j, count, num_sparks, num_spark_pairs, spark_num;
1305         vector world_hitpos[MAX_SHIP_HITS];
1306         spark_pair spark_pairs[MAX_SPARK_PAIRS];
1307         ship *shipp = &Ships[ship_obj->instance];
1308
1309         // only choose next spark when all slots are full
1310         SDL_assert(get_max_sparks(ship_obj) == Ships[ship_obj->instance].num_hits);
1311
1312         // get num_sparks
1313         num_sparks = Ships[ship_obj->instance].num_hits;
1314         SDL_assert(num_sparks <= MAX_SHIP_HITS);
1315
1316         // get num_spark_paris -- only sort these
1317         num_spark_pairs = (num_sparks * num_sparks - num_sparks) / 2;
1318
1319         // get the world hitpos for all sparks
1320         bool model_started = false;
1321         for (spark_num=0; spark_num<num_sparks; spark_num++) {
1322                 if (shipp->sparks[spark_num].submodel_num != -1) {
1323                         if ( !model_started) {
1324                                 model_started = true;
1325                                 ship_model_start(ship_obj);
1326                         }
1327                         model_find_world_point(&world_hitpos[spark_num], &shipp->sparks[spark_num].pos, shipp->modelnum, shipp->sparks[spark_num].submodel_num, &ship_obj->orient, &ship_obj->pos);
1328                 } else {
1329                         // rotate sparks correctly with current ship orient
1330                         vm_vec_unrotate(&world_hitpos[spark_num], &shipp->sparks[spark_num].pos, &ship_obj->orient);
1331                         vm_vec_add2(&world_hitpos[spark_num], &ship_obj->pos);
1332                 }
1333         }
1334
1335         if (model_started) {
1336                 ship_model_stop(ship_obj);
1337         }
1338
1339         // check we're not making a spark in the same location as a current one
1340         for (i=0; i<num_sparks; i++) {
1341                 float dist = vm_vec_dist_squared(&world_hitpos[i], hitpos);
1342                 if (dist < 1) {
1343                         return i;
1344                 }
1345         }
1346
1347         // not same location, so maybe do random recyling
1348         if (frand() > 0.5f) {
1349                 return (rand() % num_sparks);
1350         }
1351
1352         // initialize spark pairs
1353         for (i=0; i<num_spark_pairs; i++) {
1354                 spark_pairs[i].index1 = 0;
1355                 spark_pairs[i].index2 = 0;
1356                 spark_pairs[i].dist = FLT_MAX;
1357         }
1358
1359         // set spark pairs
1360         count = 0;
1361         for (i=1; i<num_sparks; i++) {
1362                 for (j=0; j<i; j++) {
1363                         spark_pairs[count].index1 = i;
1364                         spark_pairs[count].index2 = j;
1365                         spark_pairs[count++].dist = vm_vec_dist_squared(&world_hitpos[i], &world_hitpos[j]);
1366                 }
1367         }
1368         SDL_assert(count == num_spark_pairs);
1369
1370         // sort pairs
1371         qsort(spark_pairs, count, sizeof(spark_pair), spark_compare);
1372         //mprintf(("Min spark pair dist %.1f\n", spark_pairs[0].dist));
1373
1374         // look through the first few sorted pairs, counting number of indices of closest pair
1375         int index1 = spark_pairs[0].index1;
1376         int index2 = spark_pairs[0].index2;
1377         int count1 = 0;
1378         int count2 = 0;
1379
1380         for (i=1; i<6; i++) {
1381                 if (spark_pairs[i].index1 == index1) {
1382                         count1++;
1383                 }
1384                 if (spark_pairs[i].index2 == index1) {
1385                         count1++;
1386                 }
1387                 if (spark_pairs[i].index1 == index2) {
1388                         count2++;
1389                 }
1390                 if (spark_pairs[i].index2 == index2) {
1391                         count2++;
1392                 }
1393         }
1394
1395         // recycle spark which has most indices in sorted list of pairs
1396         if (count1 > count2) {
1397                 return index1;
1398         } else {
1399                 return index2;
1400         }
1401 }
1402
1403
1404 //      Make sparks fly off a ship.
1405 void ship_hit_create_sparks(object *ship_obj, vector *hitpos, int submodel_num)
1406 {
1407         vector  tempv;
1408         ship            *ship_p = &Ships[ship_obj->instance];
1409
1410         int n, max_sparks;
1411
1412         n = ship_p->num_hits;
1413         max_sparks = get_max_sparks(ship_obj);
1414
1415         if (n >= max_sparks)    {
1416                 if ( Ship_info[ship_p->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP) ) {
1417                         // large ship, choose intelligently
1418                         n = choose_next_spark(ship_obj, hitpos);
1419                 } else {
1420                         // otherwise, normal choice
1421                         n = rand() % max_sparks;
1422                 }
1423         } else {
1424                 ship_p->num_hits++;
1425         }
1426
1427         ship *pship = &Ships[ship_obj->instance];
1428
1429         bool instancing = false;
1430         // decide whether to do instancing
1431         if (submodel_num != -1) {
1432                 polymodel *pm = model_get(pship->modelnum);
1433                 if (pm->detail[0] != submodel_num) {
1434                         // submodel is not hull
1435                         // OPTIMIZE ... check if submodel can not rotate
1436                         instancing = true;
1437                 }
1438         }
1439
1440         if (instancing) {
1441                 // get the hit position in the subobject RF
1442                 ship_model_start(ship_obj);
1443                 vector temp_zero, temp_x, temp_y, temp_z;
1444                 model_find_world_point(&temp_zero, &vmd_zero_vector, pship->modelnum, submodel_num, &ship_obj->orient, &ship_obj->pos);
1445                 model_find_world_point(&temp_x, &vmd_x_vector, pship->modelnum, submodel_num, &ship_obj->orient, &ship_obj->pos);
1446                 model_find_world_point(&temp_y, &vmd_y_vector, pship->modelnum, submodel_num, &ship_obj->orient, &ship_obj->pos);
1447                 model_find_world_point(&temp_z, &vmd_z_vector, pship->modelnum, submodel_num, &ship_obj->orient, &ship_obj->pos);
1448                 ship_model_stop(ship_obj);
1449
1450                 // find submodel x,y,z axes
1451                 vm_vec_sub2(&temp_x, &temp_zero);
1452                 vm_vec_sub2(&temp_y, &temp_zero);
1453                 vm_vec_sub2(&temp_z, &temp_zero);
1454
1455                 // find displacement from submodel origin
1456                 vector diff;
1457                 vm_vec_sub(&diff, hitpos, &temp_zero);
1458
1459                 // find displacement from submodel origin in submodel RF
1460                 ship_p->sparks[n].pos.xyz.x = vm_vec_dotprod(&diff, &temp_x);
1461                 ship_p->sparks[n].pos.xyz.y = vm_vec_dotprod(&diff, &temp_y);
1462                 ship_p->sparks[n].pos.xyz.z = vm_vec_dotprod(&diff, &temp_z);
1463                 ship_p->sparks[n].submodel_num = submodel_num;
1464                 ship_p->sparks[n].end_time = timestamp(-1);
1465         } else {
1466                 // Rotate hitpos into ship_obj's frame of reference.
1467                 vm_vec_sub(&tempv, hitpos, &ship_obj->pos);
1468                 vm_vec_rotate(&ship_p->sparks[n].pos, &tempv, &ship_obj->orient);
1469                 ship_p->sparks[n].submodel_num = -1;
1470                 ship_p->sparks[n].end_time = timestamp(-1);
1471         }
1472
1473         // Create the first wave of sparks
1474         shipfx_emit_spark(ship_obj->instance, n);
1475
1476         if ( n == 0 )   {
1477                 ship_p->next_hit_spark = timestamp(0);          // when a hit spot will spark
1478         }
1479 }
1480
1481 //      Called from ship_hit_kill() when we detect the player has been killed.
1482 void player_died_start(object *killer_objp)
1483 {
1484         nprintf(("Network", "starting my player death\n"));
1485         gameseq_post_event(GS_EVENT_DEATH_DIED);        
1486         
1487 /*      vm_vec_scale_add(&Dead_camera_pos, &Player_obj->pos, &Player_obj->orient.v.fvec, -10.0f);
1488         vm_vec_scale_add2(&Dead_camera_pos, &Player_obj->orient.v.uvec, 3.0f);
1489         vm_vec_scale_add2(&Dead_camera_pos, &Player_obj->orient.v.rvec, 5.0f);
1490 */
1491
1492         //      Create a good vector for the camera to move along during death sequence.
1493         object  *other_objp = NULL;
1494
1495         // on multiplayer clients, there have been occasions where we haven't been able to determine
1496         // the killer of a ship (due to bogus/mismatched/timed-out signatures on the client side).  If
1497         // we don't know the killer, use the Player_obj as the other_objp for camera position.
1498         if ( killer_objp ) {
1499                 switch (killer_objp->type) {
1500                 case OBJ_WEAPON:
1501                 case OBJ_SHOCKWAVE:
1502                         other_objp = &Objects[killer_objp->parent];
1503                         break;
1504                 case OBJ_SHIP:
1505                 case OBJ_DEBRIS:
1506                 case OBJ_ASTEROID:
1507                 case OBJ_NONE:  //      Something that just got deleted due to also dying -- it happened to me! --MK.           
1508                         other_objp = killer_objp;
1509                         break;
1510
1511                 case OBJ_BEAM:
1512                         int beam_obj_parent;
1513                         beam_obj_parent = beam_get_parent(killer_objp);
1514                         if(beam_obj_parent == -1){
1515                                 other_objp = killer_objp;
1516                         } else {
1517                                 other_objp = &Objects[beam_obj_parent];
1518                         }
1519                         break;
1520
1521                 default:
1522                         Int3();         //      Killed by an object of a peculiar type.  What is it?
1523                         other_objp = killer_objp;       //      Enable to continue, just in case we shipped it with this bug...
1524                 }
1525         } else {
1526                 other_objp = Player_obj;
1527         }
1528
1529         vm_vec_add(&Original_vec_to_deader, &Player_obj->orient.v.fvec, &Player_obj->orient.v.rvec);
1530         vm_vec_scale(&Original_vec_to_deader, 2.0f);
1531         vm_vec_add2(&Original_vec_to_deader, &Player_obj->orient.v.uvec);
1532         vm_vec_normalize(&Original_vec_to_deader);
1533
1534         vector  vec_from_killer;
1535         vector  *side_vec;
1536         float           dist;
1537
1538         SDL_assert(other_objp != NULL);
1539
1540         if (Player_obj == other_objp) {
1541                 dist = 50.0f;
1542                 vec_from_killer = Player_obj->orient.v.fvec;
1543         } else {
1544                 dist = vm_vec_normalized_dir(&vec_from_killer, &Player_obj->pos, &other_objp->pos);
1545         }
1546
1547         if (dist > 100.0f)
1548                 dist = 100.0f;
1549         vm_vec_scale_add(&Dead_camera_pos, &Player_obj->pos, &vec_from_killer, dist);
1550
1551         float   dot = vm_vec_dot(&Player_obj->orient.v.rvec, &vec_from_killer);
1552         if (fl_abs(dot) > 0.8f)
1553                 side_vec = &Player_obj->orient.v.fvec;
1554         else
1555                 side_vec = &Player_obj->orient.v.rvec;
1556         
1557         vm_vec_scale_add2(&Dead_camera_pos, side_vec, 10.0f);
1558
1559         Player_ai->target_objnum = -1;          //      Clear targeting.  Otherwise, camera pulls away from player as soon as he blows up.
1560
1561         // stop any playing emp effect
1562         emp_stop_local();
1563 }
1564
1565
1566 #define DEATHROLL_TIME                                          3000                    //      generic deathroll is 3 seconds (3 * 1000 milliseconds)
1567 #define MIN_PLAYER_DEATHROLL_TIME               1000                    // at least one second deathroll for a player
1568 #define DEATHROLL_ROTVEL_CAP                            6.3f                    // maximum added deathroll rotvel in rad/sec (about 1 rev / sec)
1569 #define DEATHROLL_ROTVEL_MIN                            0.8f                    // minimum added deathroll rotvel in rad/sec (about 1 rev / 12 sec)
1570 #define DEATHROLL_MASS_STANDARD                 50                              // approximate mass of lightest ship
1571 #define DEATHROLL_VELOCITY_STANDARD     70                              // deathroll rotvel is scaled according to ship velocity
1572 #define DEATHROLL_ROTVEL_SCALE                  4                               // constant determines how quickly deathroll rotvel is ramped up  (smaller is faster)
1573
1574 void ai_announce_ship_dying(object *dying_objp);
1575
1576 void saturate_fabs(float *f, float max)
1577 {
1578         if ( fl_abs(*f) > max) {
1579                 if (*f > 0)
1580                         *f = max;
1581                 else
1582                         *f = -max;
1583         }
1584 }
1585
1586 // function to do generic things when a ship explodes
1587 void ship_generic_kill_stuff( object *objp, float percent_killed )
1588 {
1589         float rotvel_mag;
1590         int     delta_time;
1591         ship    *sp;
1592
1593         SDL_assert(objp->type == OBJ_SHIP);
1594         SDL_assert(objp->instance >= 0 && objp->instance < MAX_SHIPS );
1595         if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1596                 return;
1597         }
1598         sp = &Ships[objp->instance];
1599         ship_info *sip = &Ship_info[sp->ship_info_index];
1600
1601         // if recording demo
1602         if(Game_mode & GM_DEMO_RECORD){
1603                 demo_POST_ship_kill(objp);
1604         }
1605
1606         ai_announce_ship_dying(objp);
1607
1608         sp->flags |= SF_DYING;
1609         objp->phys_info.flags |= (PF_DEAD_DAMP | PF_REDUCED_DAMP);
1610         delta_time = (int) (DEATHROLL_TIME);
1611
1612         //      For smaller ships, subtract off time proportional to excess damage delivered.
1613         if (objp->radius < BIG_SHIP_MIN_RADIUS)
1614                 delta_time -=  (int) (1.01f - 4*percent_killed);
1615
1616         //      Cut down cargo death rolls.  Looks a little silly. -- MK, 3/30/98.
1617         if (sip->flags & SIF_CARGO) {
1618                 delta_time /= 4;
1619         }
1620         
1621         //      Prevent bogus timestamps.
1622         if (delta_time < 2)
1623                 delta_time = 2;
1624         
1625         if (objp->flags & OF_PLAYER_SHIP) {
1626                 //      Note: Kamikaze ships have no minimum death time.
1627                 if (!(Ai_info[Ships[objp->instance].ai_index].ai_flags & AIF_KAMIKAZE) && (delta_time < MIN_PLAYER_DEATHROLL_TIME))
1628                         delta_time = MIN_PLAYER_DEATHROLL_TIME;
1629         }
1630
1631         //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));
1632
1633         //      Make big ships have longer deathrolls.
1634         //      This is debug code by MK to increase the deathroll time so ships have time to evade the shockwave.
1635         //      Perhaps deathroll time should be specified in ships.tbl.
1636         float damage = ship_get_exp_damage(objp);
1637
1638         if (damage >= 250.0f)
1639                 delta_time += 3000 + (int)(damage*4.0f + 4.0f*objp->radius);
1640
1641         if (Ai_info[sp->ai_index].ai_flags & AIF_KAMIKAZE)
1642                 delta_time = 2;
1643
1644         // Knossos gets 7-10 sec to die, time for "little" explosions
1645         if (Ship_info[sp->ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
1646                 delta_time = 7000 + (int)(frand() * 3000.0f);
1647                 Ship_info[sp->ship_info_index].explosion_propagates = 0;
1648         }
1649         sp->final_death_time = timestamp(delta_time);   // Give him 3 secs to explode
1650
1651         if (sp->flags & SF_VAPORIZE) {
1652                 // SDL_assert(Ship_info[sp->ship_info_index].flags & SIF_SMALL_SHIP);
1653
1654                 // LIVE FOR 100 MS
1655                 sp->final_death_time = timestamp(100);
1656         }
1657
1658         //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));
1659
1660         sp->pre_death_explosion_happened = 0;                           // The little fireballs haven't came in yet.
1661
1662         sp->next_fireball = timestamp(0);       //start one right away
1663
1664         ai_deathroll_start(objp);
1665
1666         // play death roll begin sound
1667         sp->death_roll_snd = snd_play_3d( &Snds[SND_DEATH_ROLL], &objp->pos, &View_position, objp->radius );
1668         if (objp == Player_obj)
1669                 joy_ff_deathroll();
1670
1671         // apply a whack
1672         //      rotational velocity proportional to original translational velocity, with a bit added in.
1673         //      Also, preserve half of original rotational velocity.
1674
1675         // At standard speed (70) and standard mass (50), deathroll rotvel should be capped at DEATHROLL_ROTVEL_CAP
1676         // Minimum deathroll velocity is set    DEATHROLL_ROTVEL_MIN
1677         // At lower speed, lower death rotvel (scaled linearly)
1678         // At higher mass, lower death rotvel (scaled logarithmically)
1679         // variable scale calculates the deathroll rotational velocity magnitude
1680         float logval = (float) log10(objp->phys_info.mass / (0.05f*DEATHROLL_MASS_STANDARD));
1681         float velval = ((vm_vec_mag_quick(&objp->phys_info.vel) + 3.0f) / DEATHROLL_VELOCITY_STANDARD);
1682         float   p1 = (float) (DEATHROLL_ROTVEL_CAP - DEATHROLL_ROTVEL_MIN);
1683
1684         rotvel_mag = (float) DEATHROLL_ROTVEL_MIN * 2.0f/(logval + 2.0f);
1685         rotvel_mag += (float) (p1 * velval/logval) * 0.75f;
1686
1687         // set so maximum velocity from rotation is less than 200
1688         if (rotvel_mag*objp->radius > 150) {
1689                 rotvel_mag = 150.0f / objp->radius;
1690         }
1691
1692         if (sp->dock_objnum_when_dead != -1) {
1693                 // don't change current rotvel
1694                 sp->deathroll_rotvel = objp->phys_info.rotvel;
1695         } else {
1696                 // if added rotvel is too random, we should decrease the random component, putting a const in front of the rotvel.
1697                 sp->deathroll_rotvel = objp->phys_info.rotvel;
1698                 sp->deathroll_rotvel.xyz.x += (frand() - 0.5f) * 2.0f * rotvel_mag;
1699                 saturate_fabs(&sp->deathroll_rotvel.xyz.x, 0.75f*DEATHROLL_ROTVEL_CAP);
1700                 sp->deathroll_rotvel.xyz.y += (frand() - 0.5f) * 3.0f * rotvel_mag;
1701                 saturate_fabs(&sp->deathroll_rotvel.xyz.y, 0.75f*DEATHROLL_ROTVEL_CAP);
1702                 sp->deathroll_rotvel.xyz.z += (frand() - 0.5f) * 6.0f * rotvel_mag;
1703                 // make z component  2x larger than larger of x,y
1704                 float largest_mag = max(fl_abs(sp->deathroll_rotvel.xyz.x), fl_abs(sp->deathroll_rotvel.xyz.y));
1705                 if (fl_abs(sp->deathroll_rotvel.xyz.z) < 2.0f*largest_mag) {
1706                         sp->deathroll_rotvel.xyz.z *= (2.0f * largest_mag / fl_abs(sp->deathroll_rotvel.xyz.z));
1707                 }
1708                 saturate_fabs(&sp->deathroll_rotvel.xyz.z, 0.75f*DEATHROLL_ROTVEL_CAP);
1709                 // nprintf(("Physics", "Frame: %i rotvel_mag: %5.2f, rotvel: (%4.2f, %4.2f, %4.2f)\n", Framecount, rotvel_mag, sp->deathroll_rotvel.xyz.x, sp->deathroll_rotvel.xyz.y, sp->deathroll_rotvel.xyz.z));
1710         }
1711
1712         
1713         // blow out his reverse thrusters. Or drag, same thing.
1714         objp->phys_info.rotdamp = (float) DEATHROLL_ROTVEL_SCALE / rotvel_mag;
1715         objp->phys_info.side_slip_time_const = 10000.0f;
1716
1717         vm_vec_zero(&objp->phys_info.max_vel);          // make so he can't turn on his own VOLITION anymore.
1718
1719         vm_vec_zero(&objp->phys_info.max_rotvel);       // make so he can't change speed on his own VOLITION anymore.
1720
1721 }
1722
1723 // called from ship_hit_kill if the ship is vaporized
1724 void ship_vaporize(ship *shipp)
1725 {
1726         object *ship_obj;
1727
1728         // sanity
1729         SDL_assert(shipp != NULL);
1730         if(shipp == NULL){
1731                 return;
1732         }
1733         SDL_assert((shipp->objnum >= 0) && (shipp->objnum < MAX_OBJECTS));
1734         if((shipp->objnum < 0) || (shipp->objnum >= MAX_OBJECTS)){
1735                 return;
1736         }
1737         ship_obj = &Objects[shipp->objnum];
1738
1739         // create debris shards
1740         create_vaporize_debris(ship_obj, &ship_obj->pos);
1741 }
1742
1743 //      *ship_obj was hit and we've determined he's been killed!  By *other_obj!
1744 void ship_hit_kill(object *ship_obj, object *other_obj, float percent_killed, int self_destruct)
1745 {
1746         ship *sp;
1747         char *killer_ship_name;
1748         int killer_damage_percent = 0;
1749         object *killer_objp = NULL;
1750
1751         sp = &Ships[ship_obj->instance];
1752         show_dead_message(ship_obj, other_obj);
1753
1754         if (ship_obj == Player_obj) {
1755                 player_died_start(other_obj);
1756         }
1757
1758         // maybe vaporize him
1759         if(sp->flags & SF_VAPORIZE){
1760                 ship_vaporize(sp);
1761         }
1762
1763         // hehe
1764         extern void game_tst_mark(object *objp, ship *shipp);
1765         game_tst_mark(ship_obj, sp);
1766
1767         // single player and multiplayer masters evaluate the scoring and kill stuff
1768         if ( !MULTIPLAYER_CLIENT && !(Game_mode & GM_DEMO_PLAYBACK)) {
1769                 scoring_eval_kill( ship_obj );
1770
1771                 // ship is destroyed -- send this event to the mission log stuff to record this event.  Try to find who
1772                 // killed this ship.  scoring_eval_kill above should leave the obj signature of the ship who killed
1773                 // this guy (or a -1 if no one got the kill).
1774                 killer_ship_name = NULL;
1775                 killer_damage_percent = -1;
1776                 if ( sp->damage_ship_id[0] != -1 ) {
1777                         object *objp;
1778                         int sig;
1779
1780                         sig = sp->damage_ship_id[0];
1781                         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
1782                                 if ( objp->signature == sig ){
1783                                         break;
1784                                 }
1785                         }
1786                         // if the object isn't around, the try to find the object in the list of ships which has exited                 
1787                         if ( objp != END_OF_LIST(&obj_used_list) ) {
1788                                 SDL_assert ( (objp->type == OBJ_SHIP ) || (objp->type == OBJ_GHOST) );                                  // I suppose that this should be true
1789                                 killer_ship_name = Ships[objp->instance].ship_name;
1790
1791                                 killer_objp = objp;
1792                         } else {
1793                                 int ei;
1794
1795                                 ei = ship_find_exited_ship_by_signature( sig );
1796                                 if ( ei != -1 ){
1797                                         killer_ship_name = Ships_exited[ei].ship_name;
1798                                 }
1799                         }
1800                         killer_damage_percent = (int)(sp->damage_ship[0] * 100.0f);
1801                 }               
1802
1803                 if(!self_destruct){
1804                         // multiplayer
1805                         if(Game_mode & GM_MULTIPLAYER){
1806                                 char name1[256] = "";
1807                                 char name2[256] = "";
1808                                 int np_index;
1809
1810                                 // get first name                               
1811                                 np_index = multi_find_player_by_object(ship_obj);                               
1812                                 if((np_index >= 0) && (np_index < MAX_PLAYERS) && (Net_players[np_index].player != NULL)){
1813                                         SDL_strlcpy(name1, Net_players[np_index].player->callsign, SDL_arraysize(name1));
1814                                 } else {
1815                                         SDL_strlcpy(name1, sp->ship_name, SDL_arraysize(name1));
1816                                 }
1817
1818                                 // argh
1819                                 if((killer_objp != NULL) || (killer_ship_name != NULL)){
1820
1821                                         // second name
1822                                         if(killer_objp == NULL){
1823                                                 SDL_strlcpy(name2, killer_ship_name, SDL_arraysize(name2));
1824                                         } else {
1825                                                 np_index = multi_find_player_by_object(killer_objp);
1826                                                 if((np_index >= 0) && (np_index < MAX_PLAYERS) && (Net_players[np_index].player != NULL)){
1827                                                         SDL_strlcpy(name2, Net_players[np_index].player->callsign, SDL_arraysize(name2));
1828                                                 } else {
1829                                                         SDL_strlcpy(name2, killer_ship_name, SDL_arraysize(name2));
1830                                                 }
1831                                         }                                       
1832                                 }
1833
1834                                 mission_log_add_entry(LOG_SHIP_DESTROYED, name1, name2, killer_damage_percent);
1835                         } else {
1836                                 // DKA: 8/23/99 allow message log in single player with no killer name
1837                                 //if(killer_ship_name != NULL){
1838                                 mission_log_add_entry(LOG_SHIP_DESTROYED, sp->ship_name, killer_ship_name, killer_damage_percent);
1839                                 //}
1840                         }
1841                 }
1842
1843                 // maybe praise the player for this kill
1844                 if ( (killer_damage_percent > 10) && (other_obj != NULL) && (other_obj->parent_sig == Player_obj->signature) ) {
1845                         ship_maybe_praise_player(sp);
1846                 }
1847         }
1848
1849         ship_generic_kill_stuff( ship_obj, percent_killed );
1850
1851         // mwa -- removed 2/25/98 -- why is this here?  ship_obj->flags &= ~(OF_PLAYER_SHIP);
1852         // if it is for observers, must deal with it a separate way!!!!
1853         if ( MULTIPLAYER_MASTER ) {
1854                 // check to see if this ship needs to be respawned
1855                 multi_respawn_check(ship_obj);          
1856                         
1857                 // send the kill packet to all players
1858                 // maybe send vaporize packet to all players
1859                 send_ship_kill_packet( ship_obj, other_obj, percent_killed, self_destruct );
1860         }       
1861
1862         // If ship from a player wing ship has died, then maybe play a scream
1863         if ( !(ship_obj->flags & OF_PLAYER_SHIP) && (sp->flags & SF_FROM_PLAYER_WING) ) {
1864                 ship_maybe_scream(sp);
1865         }
1866
1867         // If player is dying, have wingman lament (only in single player)
1868         if ( (Game_mode & GM_NORMAL) && (ship_obj == Player_obj) ) {
1869                 ship_maybe_lament();
1870         }
1871 }
1872
1873 // function to simply explode a ship where it is currently at
1874 void ship_self_destruct( object *objp )
1875 {       
1876         SDL_assert ( objp->type == OBJ_SHIP );
1877
1878         // try and find a player
1879         if((Game_mode & GM_MULTIPLAYER) && (multi_find_player_by_object(objp) >= 0)){
1880                 int np_index = multi_find_player_by_object(objp);
1881                 if((np_index >= 0) && (np_index < MAX_PLAYERS) && (Net_players[np_index].player != NULL)){
1882                         mission_log_add_entry(LOG_SELF_DESTRUCT, Net_players[np_index].player->callsign, NULL );
1883                 } else {
1884                         mission_log_add_entry(LOG_SELF_DESTRUCT, Ships[objp->instance].ship_name, NULL );
1885                 }
1886         } else {
1887                 mission_log_add_entry(LOG_SELF_DESTRUCT, Ships[objp->instance].ship_name, NULL );
1888         }
1889         
1890         // check to see if this ship needs to be respawned
1891         if(MULTIPLAYER_MASTER){
1892                 // player ship?
1893                 int np_index = multi_find_player_by_object(objp);
1894                 if((np_index >= 0) && (np_index < MAX_PLAYERS) && MULTI_CONNECTED(Net_players[np_index]) && (Net_players[np_index].player != NULL)){
1895                         char msg[512] = "";
1896                         SDL_snprintf(msg, SDL_arraysize(msg), "%s %s", Net_players[np_index].player->callsign, XSTR("Self destructed", 1476));
1897
1898                         // send a message
1899                         send_game_chat_packet(Net_player, msg, MULTI_MSG_ALL, NULL, NULL, 2);
1900
1901                         // printf
1902                         if(!(Game_mode & GM_STANDALONE_SERVER)){
1903                                 HUD_printf(msg);
1904                         }
1905                 }
1906         }
1907
1908         // self destruct
1909         ship_hit_kill(objp, NULL, 1.0f, 1);     
1910 }
1911
1912 extern int Homing_hits, Homing_misses;
1913
1914 // Call this instead of physics_apply_whack directly to 
1915 // deal with two ships docking properly.
1916 void ship_apply_whack(vector *force, vector *new_pos, object *objp)
1917 {
1918         if (objp->type == OBJ_SHIP)     {
1919                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_SUPPORT | SIF_CARGO)) {
1920                         int     docked_to_objnum;       
1921                         ai_info *aip;
1922
1923                         aip = &Ai_info[Ships[objp->instance].ai_index];
1924                 
1925                         if (aip->ai_flags & AIF_DOCKED) {
1926                                 docked_to_objnum = aip->dock_objnum;
1927                                 if (aip->dock_objnum != -1) {
1928                                         physics_apply_whack(force, new_pos, &Objects[docked_to_objnum].phys_info, &Objects[docked_to_objnum].orient, Objects[docked_to_objnum].phys_info.mass + objp->phys_info.mass);
1929                                         return;
1930                                 }
1931                         }
1932                 }
1933         }
1934         if (objp == Player_obj) {
1935                 nprintf(("Sandeep", "Playing stupid joystick effect\n"));
1936                 vector test;
1937                 vm_vec_unrotate(&test, force, &objp->orient);
1938
1939                 game_whack_apply( -test.xyz.x, -test.xyz.y );
1940         }
1941                                         
1942         physics_apply_whack(force, new_pos, &objp->phys_info, &objp->orient, objp->phys_info.mass);
1943 }
1944
1945 float Skill_level_player_damage_scale[NUM_SKILL_LEVELS] = {0.25f, 0.5f, 0.65f, 0.85f, 1.0f};
1946
1947
1948 // If a ship is dying and it gets hit, shorten its deathroll.
1949 //      But, if it's a player, don't decrease below MIN_PLAYER_DEATHROLL_TIME
1950 void shiphit_hit_after_death(object *ship_obj, float damage)
1951 {
1952         float   percent_killed;
1953         int     delta_time, time_remaining;
1954         ship    *shipp = &Ships[ship_obj->instance];
1955         ship_info       *sip = &Ship_info[shipp->ship_info_index];
1956
1957         // Since the explosion has two phases (final_death_time and really_final_death_time)
1958         // we should only shorten the deathroll time if that is the phase we're in.
1959         // And you can tell by seeing if the timestamp is valid, since it gets set to
1960         // invalid after it does the first large explosion.
1961         if ( !timestamp_valid(shipp->final_death_time) )        {
1962                 return;
1963         }
1964
1965         // Don't adjust vaporized ship
1966         if (shipp->flags & SF_VAPORIZE) {
1967                 return;
1968         }
1969
1970         //      Don't shorten deathroll on very large ships.
1971         if (ship_obj->radius > BIG_SHIP_MIN_RADIUS)
1972                 return;
1973
1974         percent_killed = damage/sip->initial_hull_strength;
1975         if (percent_killed > 1.0f)
1976                 percent_killed = 1.0f;
1977
1978         delta_time = (int) (4 * DEATHROLL_TIME * percent_killed);
1979         time_remaining = timestamp_until(shipp->final_death_time);
1980
1981         //nprintf(("AI", "Gametime = %7.3f, Time until %s dies = %7.3f, delta = %7.3f\n", f2fl(Missiontime), Ships[ship_obj->instance].ship_name, (float)time_remaining/1000.0f, delta_time));
1982         if (ship_obj->flags & OF_PLAYER_SHIP)
1983                 if (time_remaining < MIN_PLAYER_DEATHROLL_TIME)
1984                         return;
1985
1986         // 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));
1987
1988         delta_time = time_remaining - delta_time;
1989         if (ship_obj->flags & OF_PLAYER_SHIP)
1990                 if (delta_time < MIN_PLAYER_DEATHROLL_TIME)
1991                         delta_time = MIN_PLAYER_DEATHROLL_TIME;
1992
1993         //      Prevent bogus timestamp.
1994         if (delta_time < 2)
1995                 delta_time = 2;
1996
1997         shipp->final_death_time = timestamp(delta_time);        // Adjust time until explosion.
1998 }
1999
2000 MONITOR( ShipHits );
2001 MONITOR( ShipNumDied );
2002
2003 int maybe_shockwave_damage_adjust(object *ship_obj, object *other_obj, float *damage)
2004 {
2005         ship_subsys *subsys;
2006         ship *shipp;
2007         float dist, nearest_dist = FLT_MAX;
2008         vector g_subobj_pos;
2009         float max_damage;
2010         shockwave *sw;
2011
2012         SDL_assert(ship_obj->type == OBJ_SHIP);
2013         if (other_obj->type != OBJ_SHOCKWAVE) {
2014                 return 0;
2015         }
2016
2017         if (!(Ship_info[Ships[ship_obj->instance].ship_info_index].flags & SIF_HUGE_SHIP)) {
2018                 return 0;
2019         }
2020
2021         shipp = &Ships[ship_obj->instance];
2022         sw = &Shockwaves[other_obj->instance];
2023
2024         // find closest subsystem distance to shockwave origin
2025         for (subsys=GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys) ) {
2026                 get_subsystem_world_pos(ship_obj, subsys, &g_subobj_pos);
2027                 dist = vm_vec_dist_quick(&g_subobj_pos, &other_obj->pos);
2028
2029                 if (dist < nearest_dist) {
2030                         nearest_dist = dist;
2031                 }
2032         }
2033
2034         // get max damage and adjust if needed to account for shockwave created from destroyed weapon
2035         max_damage = sw->damage;
2036         if (sw->flags & SW_WEAPON_KILL) {
2037                 max_damage *= 4.0f;
2038         }
2039
2040         // scale damage
2041         // floor of 25%, max if within inner_radius, linear between
2042         if (nearest_dist > sw->outer_radius) {
2043                 *damage = max_damage / 4.0f;
2044         } else if (nearest_dist < sw->inner_radius) {
2045                 *damage = max_damage;
2046         } else {
2047                 *damage = max_damage * (1.0f - 0.75f * (nearest_dist - sw->inner_radius) / (sw->outer_radius - sw->inner_radius));
2048         }
2049
2050         return 1;
2051 }
2052
2053 // ------------------------------------------------------------------------
2054 // ship_do_damage()
2055 //
2056 // Do damage assessment on a ship.    This should only be called
2057 // internally by ship_apply_global_damage and ship_apply_local_damage
2058 //
2059 // 
2060 //      input:  ship_obj                =>              object pointer for ship receiving damage
2061 //                              other_obj       =>              object pointer to object causing damage
2062 //                              hitpos          =>              impact world pos on the ship 
2063 //                              TODO:   get a better value for hitpos
2064 //                              damage          =>              damage to apply to the ship
2065 //                              shield_quadrant => which part of shield takes damage, -1 if not shield hit
2066 //                              wash_damage     =>              1 if damage is done by engine wash
2067 void ai_update_lethality(object *ship_obj, object *weapon_obj, float damage);
2068 static void ship_do_damage(object *ship_obj, object *other_obj, vector *hitpos, float damage, int shield_quadrant, int wash_damage=0)
2069 {
2070         ship *shipp;    
2071         float subsystem_damage = damage;                        // damage to be applied to subsystems
2072
2073         SDL_assert(ship_obj->instance >= 0);
2074         SDL_assert(ship_obj->type == OBJ_SHIP);
2075         shipp = &Ships[ship_obj->instance];
2076
2077         // maybe adjust damage done by shockwave for BIG|HUGE
2078         maybe_shockwave_damage_adjust(ship_obj, other_obj, &damage);
2079
2080         // update lethality of ship doing damage
2081         int update_lethality = FALSE;
2082         update_lethality = ((other_obj != NULL) && (other_obj->type == OBJ_WEAPON) && (other_obj->instance >= 0) && (other_obj->instance < MAX_WEAPONS));
2083         update_lethality = update_lethality || ((other_obj != NULL) && (other_obj->type == OBJ_SHOCKWAVE) && (other_obj->instance >= 0) && (other_obj->instance < MAX_SHOCKWAVES));
2084         if (update_lethality) {
2085                 ai_update_lethality(ship_obj, other_obj, damage);
2086         }
2087
2088         // if this is a weapon
2089         if((other_obj != NULL) && (other_obj->type == OBJ_WEAPON) && (other_obj->instance >= 0) && (other_obj->instance < MAX_WEAPONS)){
2090                 damage *= weapon_get_damage_scale(&Weapon_info[Weapons[other_obj->instance].weapon_info_index], other_obj, ship_obj);
2091         }
2092
2093         MONITOR_INC( ShipHits, 1 );
2094
2095         //      Don't damage player ship in the process of warping out.
2096         if ( Player->control_mode >= PCM_WARPOUT_STAGE2 )       {
2097                 if ( ship_obj == Player_obj ){
2098                         return;
2099                 }
2100         }
2101
2102         if ( (other_obj != NULL) && (other_obj->type == OBJ_WEAPON) ) {         
2103                 // for tvt and dogfight missions, don't scale damage
2104                 if( (Game_mode & GM_MULTIPLAYER) && ((Netgame.type_flags & NG_TYPE_TEAM) || (Netgame.type_flags & NG_TYPE_DOGFIGHT)) ){
2105                 } else {
2106                         // Do a little "skill" balancing for the player in single player and coop multiplayer
2107                         if (ship_obj->flags & OF_PLAYER_SHIP)   {
2108                                 damage *= Skill_level_player_damage_scale[Game_skill_level];
2109                                 subsystem_damage *= Skill_level_player_damage_scale[Game_skill_level];
2110                         }               
2111                 }
2112         }
2113
2114         // if this is not a laser, or i'm not a multiplayer client
2115         // apply pain to me
2116         if((other_obj != NULL) && ((Weapon_info[Weapons[other_obj->instance].weapon_info_index].subtype != WP_LASER) || !MULTIPLAYER_CLIENT) && (Player_obj != NULL) && (ship_obj == Player_obj)){
2117                 ship_hit_pain(damage);
2118         }       
2119
2120         // If the ship is invulnerable, do nothing
2121         if (ship_obj->flags & OF_INVULNERABLE)  {
2122                 return;
2123         }
2124
2125         //      if ship is already dying, shorten deathroll.
2126         if (shipp->flags & SF_DYING) {
2127                 shiphit_hit_after_death(ship_obj, damage);
2128                 return;
2129         }
2130         
2131         //      If we hit the shield, reduce it's strength and found
2132         // out how much damage is left over.
2133         if ( shield_quadrant > -1 && !(ship_obj->flags & OF_NO_SHIELDS) )       {
2134                 float shield_factor = -1.0f;
2135                 int     weapon_info_index;              
2136
2137                 weapon_info_index = shiphit_get_damage_weapon(other_obj);
2138                 if ( weapon_info_index >= 0 ) {
2139                         shield_factor = Weapon_info[weapon_info_index].shield_factor;
2140                 }
2141
2142                 if ( shield_factor >= 0 ) {
2143                         damage *= shield_factor;
2144                         subsystem_damage *= shield_factor;
2145                 }
2146
2147                 if ( damage > 0 ) {
2148                         float pre_shield = damage;
2149
2150                         damage = apply_damage_to_shield(ship_obj, shield_quadrant, damage);
2151
2152                         if(damage > 0.0f){
2153                                 subsystem_damage *= (damage / pre_shield);
2154                         } else {
2155                                 subsystem_damage = 0.0f;
2156                         }
2157                 }
2158
2159                 // if shield damage was increased, don't carry over leftover damage at scaled level
2160                 if ( shield_factor > 1 ) {
2161                         damage /= shield_factor;
2162
2163                         subsystem_damage /= shield_factor;
2164                 }
2165         }
2166                         
2167         // Apply leftover damage to the ship's subsystem and hull.
2168         if ( (damage > 0.0f) || (subsystem_damage > 0.0f) )     {
2169                 int     weapon_info_index;              
2170
2171                 float pre_subsys = subsystem_damage;
2172                 subsystem_damage = do_subobj_hit_stuff(ship_obj, other_obj, hitpos, subsystem_damage);
2173                 if(subsystem_damage > 0.0f){
2174                         damage *= (subsystem_damage / pre_subsys);
2175                 } else {
2176                         damage = 0.0f;
2177                 }
2178
2179                 // continue with damage?
2180                 if(damage > 0.0){
2181                         weapon_info_index = shiphit_get_damage_weapon(other_obj);
2182                         if ( weapon_info_index >= 0 ) {
2183                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
2184                                         damage /= 4;
2185                                 }
2186
2187                                 damage *= Weapon_info[weapon_info_index].armor_factor;
2188                         }
2189
2190                         // if ship is flagged as can not die, don't let it die
2191                         if (ship_obj->flags & OF_GUARDIAN) {
2192                                 float min_hull_strength = 0.01f * Ship_info[Ships[ship_obj->instance].ship_info_index].initial_hull_strength;
2193                                 if ( (ship_obj->hull_strength - damage) < min_hull_strength ) {
2194                                         // find damage needed to take object to min hull strength
2195                                         damage = ship_obj->hull_strength - min_hull_strength;
2196
2197                                         // make sure damage is positive
2198                                         damage = max(0, damage);
2199                                 }
2200                         }
2201
2202                         // multiplayer clients don't do damage
2203                         if(((Game_mode & GM_MULTIPLAYER) && MULTIPLAYER_CLIENT) || (Game_mode & GM_DEMO_PLAYBACK)){
2204                         } else {
2205                                 ship_obj->hull_strength -= damage;              
2206                         }
2207
2208                         // let damage gauge know that player ship just took damage
2209                         if ( Player_obj == ship_obj ) {
2210                                 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
2211                         }
2212                 
2213                         // DB - removed 1/12/99 - scoring code properly bails if MULTIPLAYER_CLIENT
2214                         // in multiplayer, if I am not the host, get out of this function here!!
2215                         //if ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ) {
2216                                 //return;
2217                         //}             
2218
2219                         switch (other_obj->type) {
2220                         case OBJ_SHOCKWAVE:
2221                                 scoring_add_damage(ship_obj,other_obj,damage);
2222                                 break;
2223                         case OBJ_ASTEROID:
2224                                 // don't call scoring for asteroids
2225                                 break;
2226                         case OBJ_WEAPON:
2227                                 if((other_obj->parent < 0) || (other_obj->parent >= MAX_OBJECTS)){
2228                                         scoring_add_damage(ship_obj, NULL, damage);
2229                                 } else {
2230                                         scoring_add_damage(ship_obj, &Objects[other_obj->parent], damage);
2231                                 }
2232                                 break;
2233                         default:
2234                                 break;
2235                         }
2236
2237                         if (ship_obj->hull_strength <= 0.0f) {
2238
2239                                 MONITOR_INC( ShipNumDied, 1 );
2240
2241                                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
2242
2243                                 // If massive beam hitting small ship, vaporize  otherwise normal damage pipeline
2244                                 // Only vaporize once
2245                                 // multiplayer clients should skip this
2246                                 if(!MULTIPLAYER_CLIENT){
2247                                         if ( !(shipp->flags & SF_VAPORIZE) ) {
2248                                                 // Only small ships can be vaporized
2249                                                 if (sip->flags & (SIF_SMALL_SHIP)) {
2250                                                         if (other_obj->type == OBJ_BEAM) {
2251                                                                 int beam_weapon_info_index = beam_get_weapon_info_index(other_obj);
2252                                                                 if ( (beam_weapon_info_index > -1) && (Weapon_info[beam_weapon_info_index].wi_flags & (WIF_BEAM|WIF_HUGE)) ) {                                                          
2253                                                                         // Flag as vaporized
2254                                                                         shipp->flags |= SF_VAPORIZE;                                                            
2255                                                                 }
2256                                                         }
2257                                                 }
2258                                         }
2259                                 }
2260                                 
2261                                 // maybe engine wash death
2262                                 if (wash_damage) {
2263                                         shipp->wash_killed = 1;
2264                                 }
2265
2266                                 float percent_killed = -ship_obj->hull_strength/sip->initial_hull_strength;
2267                                 if (percent_killed > 1.0f){
2268                                         percent_killed = 1.0f;
2269                                 }
2270
2271                                 if ( !(shipp->flags & SF_DYING) && !MULTIPLAYER_CLIENT && !(Game_mode & GM_DEMO_PLAYBACK)){  // if not killed, then kill
2272                                         ship_hit_kill(ship_obj, other_obj, percent_killed, 0);
2273                                 }
2274                         }
2275                 }
2276         }
2277
2278         // if the hitting object is a weapon, maybe do some fun stuff here
2279         if(other_obj->type == OBJ_WEAPON){
2280                 weapon_info *wip;
2281                 SDL_assert(other_obj->instance >= 0);
2282                 if(other_obj->instance < 0){
2283                         return;
2284                 }
2285                 SDL_assert(Weapons[other_obj->instance].weapon_info_index >= 0);
2286                 if(Weapons[other_obj->instance].weapon_info_index < 0){
2287                         return;
2288                 }
2289                 wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
2290
2291                 // if its a leech weapon
2292                 if(wip->wi_flags & WIF_ENERGY_SUCK){
2293                         // reduce afterburner fuel
2294                         shipp->afterburner_fuel -= wip->afterburner_reduce;
2295                         shipp->afterburner_fuel = (shipp->afterburner_fuel < 0.0f) ? 0.0f : shipp->afterburner_fuel;
2296
2297                         // reduce weapon energy
2298                         shipp->weapon_energy -= wip->weapon_reduce;
2299                         shipp->weapon_energy = (shipp->weapon_energy < 0.0f) ? 0.0f : shipp->weapon_energy;
2300                 }
2301         }
2302 }
2303
2304 // This gets called to apply damage when something hits a particular point on a ship.
2305 // This assumes that whoever called this knows if the shield got hit or not.
2306 // hitpos is in world coordinates.
2307 // if shield_quadrant is not -1, then that part of the shield takes damage properly.
2308 void ship_apply_local_damage(object *ship_obj, object *other_obj, vector *hitpos, float damage, int shield_quadrant, bool create_spark, int submodel_num, vector *hit_normal)
2309 {
2310         ship *ship_p    = &Ships[ship_obj->instance];   
2311
2312         //      If got hit by a weapon, tell the AI so it can react.  Only do this line in single player,
2313         // or if I am the master in a multiplayer game
2314         if ( other_obj->type == OBJ_WEAPON && ( !(Game_mode & GM_MULTIPLAYER) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)) )) {
2315                 weapon  *wp;
2316
2317                 wp = &Weapons[other_obj->instance];
2318                 //      If weapon hits ship on same team and that ship not targeted and parent of weapon not player,
2319                 //      don't do damage.
2320                 //      Ie, player can always do damage.  AI can only damage team if that ship is targeted.
2321                 if (wp->target_num != ship_obj-Objects) {
2322                         if ((ship_p->team == wp->team) && !(Objects[other_obj->parent].flags & OF_PLAYER_SHIP) ) {
2323                                 /*char  ship_name[64];
2324
2325                                 if (other_obj->parent_type == OBJ_SHIP) {
2326                                         strcpy(ship_name, Ships[Objects[other_obj->parent].instance].ship_name);
2327                                 } else
2328                                         strcpy(ship_name, XSTR("[not a ship]",-1));
2329                                 */
2330                                 // nprintf(("AI", "Ignoring hit on %s by weapon #%i, parent = %s\n", ship_p->ship_name, other_obj-Objects, ship_name));
2331                                 return;
2332                         }
2333                 }
2334         }
2335
2336         // only want to check the following in single player or if I am the multiplayer game server
2337         if ( !MULTIPLAYER_CLIENT && !(Game_mode & GM_DEMO_PLAYBACK) && ((other_obj->type == OBJ_SHIP) || (other_obj->type == OBJ_WEAPON)) ){
2338                 ai_ship_hit(ship_obj, other_obj, hitpos, shield_quadrant, hit_normal);
2339         }
2340
2341         //      Cut damage done on the player by 4x in training missions, but do full accredidation
2342         if ( The_mission.game_type & MISSION_TYPE_TRAINING ){
2343                 if (ship_obj == Player_obj){
2344                         damage /= 4.0f;
2345                 }
2346         }       
2347
2348         // send a packet in multiplayer -- but don't sent it if the ship is already dying.  Clients can
2349         // take care of dealing with ship hits after a ship is already dead.
2350         // if ( (MULTIPLAYER_MASTER) && !(ship_p->flags & SF_DYING) ){
2351                 // if this is a player ship which is not mine, send him a ship hit packet
2352                 // int np_index = multi_find_player_by_object(ship_obj);
2353                 // if((np_index > 0) && (np_index < MAX_PLAYERS) && (np_index != MY_NET_PLAYER_NUM) && MULTI_CONNECTED(Net_players[np_index])){
2354                         // send_ship_hit_packet( ship_obj, other_obj, hitpos, damage, shield_quadrant, submodel_num, &Net_players[np_index]);
2355                 // }
2356         // }
2357
2358         // maybe tag the ship
2359         if(!MULTIPLAYER_CLIENT && (other_obj->type == OBJ_WEAPON) && (Weapon_info[Weapons[other_obj->instance].weapon_info_index].wi_flags & WIF_TAG)) {
2360                 if (Weapon_info[Weapons[other_obj->instance].weapon_info_index].tag_level == 1) {
2361                         Ships[ship_obj->instance].tag_left = Weapon_info[Weapons[other_obj->instance].weapon_info_index].tag_time;
2362                         Ships[ship_obj->instance].tag_total = Ships[ship_obj->instance].tag_left;
2363                         if (Ships[ship_obj->instance].time_first_tagged == 0) {
2364                                 Ships[ship_obj->instance].time_first_tagged = Missiontime;
2365                         }
2366                         mprintf(("TAGGED %s for %f seconds\n", Ships[ship_obj->instance].ship_name, Ships[ship_obj->instance].tag_left));
2367                 } else if (Weapon_info[Weapons[other_obj->instance].weapon_info_index].tag_level == 2) {
2368                         Ships[ship_obj->instance].level2_tag_left = Weapon_info[Weapons[other_obj->instance].weapon_info_index].tag_time;
2369                         Ships[ship_obj->instance].level2_tag_total = Ships[ship_obj->instance].level2_tag_left;
2370                         if (Ships[ship_obj->instance].time_first_tagged == 0) {
2371                                 Ships[ship_obj->instance].time_first_tagged = Missiontime;
2372                         }
2373                         mprintf(("Level 2 TAGGED %s for %f seconds\n", Ships[ship_obj->instance].ship_name, Ships[ship_obj->instance].level2_tag_left));
2374                 } else {
2375                         Int3(); // unknown tag level
2376                 }
2377         }
2378
2379
2380 #ifndef NDEBUG
2381         if (other_obj->type == OBJ_WEAPON) {
2382                 weapon_info     *wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
2383                 if (wip->wi_flags & WIF_HOMING) {
2384                         Homing_hits++;
2385                         // nprintf(("AI", " Hit!  Hits = %i/%i\n", Homing_hits, (Homing_hits + Homing_misses)));
2386                 }
2387         }
2388 #endif
2389
2390         
2391         if ( Event_Music_battle_started == 0 )  {
2392                 ship_hit_music(ship_obj, other_obj);
2393         }
2394         
2395
2396         if (damage < 0.0f){
2397                 damage = 0.0f;
2398         }
2399
2400         // evaluate any possible player stats implications
2401         scoring_eval_hit(ship_obj,other_obj);
2402
2403         ship_do_damage(ship_obj, other_obj, hitpos, damage, shield_quadrant );
2404
2405         // DA 5/5/98: move ship_hit_create_sparks() after do_damage() since number of sparks depends on hull strength
2406         // doesn't hit shield and we want sparks
2407         if ((shield_quadrant == MISS_SHIELDS) && create_spark)  {
2408                 // check if subsys destroyed
2409                 if ( !is_subsys_destroyed(ship_p, submodel_num) ) {
2410                         ship_hit_create_sparks(ship_obj, hitpos, submodel_num);
2411                 }
2412                 //fireball_create( hitpos, FIREBALL_SHIP_EXPLODE1, OBJ_INDEX(ship_obj), 0.25f );
2413         }
2414 }
2415
2416
2417
2418 // This gets called to apply damage when a damaging force hits a ship, but at no 
2419 // point in particular.  Like from a shockwave.   This routine will see if the
2420 // shield got hit and if so, apply damage to it.
2421 // You can pass force_center==NULL if you the damage doesn't come from anywhere,
2422 // like for debug keys to damage an object or something.  It will 
2423 // assume damage is non-directional and will apply it correctly.   
2424 void ship_apply_global_damage(object *ship_obj, object *other_obj, vector *force_center, float damage )
2425 {                               
2426         vector tmp, world_hitpos;
2427
2428         if ( force_center )     {
2429                 int shield_quad;
2430                 vector local_hitpos;
2431
2432                 // find world hitpos
2433                 vm_vec_sub( &tmp, force_center, &ship_obj->pos );
2434                 vm_vec_normalize_safe( &tmp );
2435                 vm_vec_scale_add( &world_hitpos, &ship_obj->pos, &tmp, ship_obj->radius );
2436
2437                 // Rotate world_hitpos into local coordinates (local_hitpos)
2438                 vm_vec_sub(&tmp, &world_hitpos, &ship_obj->pos );
2439                 vm_vec_rotate( &local_hitpos, &tmp, &ship_obj->orient );
2440
2441                 // shield_quad = quadrant facing the force_center
2442                 shield_quad = get_quadrant(&local_hitpos);
2443
2444                 // world_hitpos use force_center for shockwave
2445                 if (other_obj->type == OBJ_SHOCKWAVE && Ship_info[Ships[ship_obj->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
2446                         world_hitpos = *force_center;
2447                 }
2448
2449                 // Do damage on local point             
2450                 ship_do_damage(ship_obj, other_obj, &world_hitpos, damage, shield_quad );
2451         } else {
2452                 // Since an force_center wasn't specified, this is probably just a debug key
2453                 // to kill an object.   So pick a shield quadrant and a point on the
2454                 // radius of the object.   
2455                 vm_vec_scale_add( &world_hitpos, &ship_obj->pos, &ship_obj->orient.v.fvec, ship_obj->radius );
2456
2457                 for (int i=0; i<MAX_SHIELD_SECTIONS; i++){
2458                         ship_do_damage(ship_obj, other_obj, &world_hitpos, damage/MAX_SHIELD_SECTIONS, i);
2459                 }
2460         }
2461
2462         // AL 3-30-98: Show flashing blast icon if player ship has taken blast damage
2463         if ( ship_obj == Player_obj ) {
2464                 // only show blast icon if playing on medium skill or lower
2465                 if ( Game_skill_level <= 2 ) {
2466                         hud_start_text_flash(XSTR("Blast", 1428), 2000);
2467                 }
2468         }
2469
2470         // evaluate any player stats scoring conditions (specifically, blasts from remotely detonated secondary weapons)
2471         scoring_eval_hit(ship_obj,other_obj,1); 
2472 }
2473
2474 void ship_apply_wash_damage(object *ship_obj, object *other_obj, float damage)
2475 {
2476         vector world_hitpos, direction_vec, rand_vec;
2477
2478         // Since an force_center wasn't specified, this is probably just a debug key
2479         // to kill an object.   So pick a shield quadrant and a point on the
2480         // radius of the object
2481         vm_vec_rand_vec_quick(&rand_vec);
2482         vm_vec_scale_add(&direction_vec, &ship_obj->orient.v.fvec, &rand_vec, 0.5f);
2483         vm_vec_normalize_quick(&direction_vec);
2484         vm_vec_scale_add( &world_hitpos, &ship_obj->pos, &direction_vec, ship_obj->radius );
2485
2486         // Do damage to hull and not to shields
2487         ship_do_damage(ship_obj, other_obj, &world_hitpos, damage, -1, 1);
2488
2489         // AL 3-30-98: Show flashing blast icon if player ship has taken blast damage
2490         if ( ship_obj == Player_obj ) {
2491                 // only show blast icon if playing on medium skill or lower
2492                 if ( Game_skill_level <= 2 ) {
2493                         hud_start_text_flash(XSTR("Engine Wash", 1429), 2000);
2494                 }
2495         }
2496
2497         // evaluate any player stats scoring conditions (specifically, blasts from remotely detonated secondary weapons)
2498         scoring_eval_hit(ship_obj,other_obj,1);
2499 }
2500
2501 // player pain
2502 void ship_hit_pain(float damage)
2503 {
2504         game_flash( damage/15.0f, -damage/30.0f, -damage/30.0f );
2505
2506         // kill any active popups when you get hit.
2507         if ( Game_mode & GM_MULTIPLAYER ){
2508                 popup_kill_any_active();
2509         }
2510 }
2511