]> icculus.org git repositories - taylor/freespace2.git/blob - src/object/collideshipship.cpp
Freespace 1 support
[taylor/freespace2.git] / src / object / collideshipship.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/Object/CollideShipShip.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Routines to detect collisions and do physics, damage, etc for ships and ships
16  *
17  * $Log$
18  * Revision 1.6  2003/05/25 02:30:43  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.5  2002/06/17 06:33:10  relnev
22  * ryan's struct patch for gcc 2.95
23  *
24  * Revision 1.4  2002/06/09 04:41:24  relnev
25  * added copyright header
26  *
27  * Revision 1.3  2002/06/01 07:12:33  relnev
28  * a few NDEBUG updates.
29  *
30  * removed a few warnings.
31  *
32  * Revision 1.2  2002/05/07 03:16:48  theoddone33
33  * The Great Newline Fix
34  *
35  * Revision 1.1.1.1  2002/05/03 03:28:10  root
36  * Initial import.
37  *
38  * 
39  * 31    9/01/99 5:40p Andsager
40  * Collision resolution between small and CAP during warp
41  * 
42  * 30    8/24/99 8:55p Dave
43  * Make sure nondimming pixels work properly in tech menu.
44  * 
45  * 29    7/29/99 12:11a Andsager
46  * comment fix
47  * 
48  * 28    7/28/99 11:23p Andsager
49  * Try a different strategy to resolve collisions between ships on same
50  * team.
51  * 
52  * 27    7/24/99 1:54p Dave
53  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
54  * missions.
55  * 
56  * 26    7/15/99 5:41p Andsager
57  * Clean up demo build
58  * 
59  * 25    7/15/99 9:20a Andsager
60  * FS2_DEMO initial checkin
61  * 
62  * 24    7/12/99 11:49a Andsager
63  * Really fix collision warp-in bug.
64  * 
65  * 23    7/09/99 5:54p Dave
66  * Seperated cruiser types into individual types. Added tons of new
67  * briefing icons. Campaign screen.
68  * 
69  * 22    7/08/99 5:49p Andsager
70  * Fixed bug colliding with just warped in Cap ship
71  * 
72  * 21    6/14/99 3:21p Andsager
73  * Allow collisions between ship and its debris.  Fix up collision pairs
74  * when large ship is warping out.
75  * 
76  * 20    4/23/99 12:01p Johnson
77  * Added SIF_HUGE_SHIP
78  * 
79  * 19    4/20/99 3:45p Andsager
80  * Modify ship_apply_local_damage to take a collision normal
81  * 
82  * 18    4/19/99 12:21p Johnson
83  * Allow ships with invisible polygons which do not collide
84  * 
85  * 17    3/20/99 2:54p Andsager
86  * Fix collision for cap ships warping in - speed is much greater than
87  * expected.
88  * 
89  * 16    2/05/99 11:07a Andsager
90  * Make cap ships not get shoved around with asteroid collisions
91  * 
92  * 15    2/02/99 1:18p Andsager
93  * Modify asteroid/cruiser collisions so cruisers don't get bashed so
94  * much.
95  * 
96  * 14    1/12/99 5:45p Dave
97  * Moved weapon pipeline in multiplayer to almost exclusively client side.
98  * Very good results. Bandwidth goes down, playability goes up for crappy
99  * connections. Fixed object update problem for ship subsystems.
100  * 
101  * 13    1/11/99 12:42p Andsager
102  * Add live debris - debris which is created from a destroyed subsystem,
103  * when the ship is still alive
104  * 
105  * 12    12/03/98 3:14p Andsager
106  * Check in code that checks rotating submodel actually has ship subsystem
107  * 
108  * 11    11/20/98 2:22p Andsager
109  * Change collision separation h2l_vec
110  * 
111  * 10    11/19/98 11:47p Andsager
112  * Fix possible divide by zero bug.
113  * 
114  * 9     11/19/98 11:08p Andsager
115  * Check in of physics and collision detection of rotating submodels
116  * 
117  * 8     11/13/98 5:06p Johnson
118  * Fix Kulas collision bug
119  * 
120  * 7     11/05/98 5:55p Dave
121  * Big pass at reducing #includes
122  * 
123  * 6     10/23/98 1:11p Andsager
124  * Make ship sparks emit correctly from rotating structures.
125  * 
126  * 5     10/20/98 1:39p Andsager
127  * Make so sparks follow animated ship submodels.  Modify
128  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
129  * submodel_num.  Add submodel_num to multiplayer hit packet.
130  * 
131  * 4     10/16/98 1:22p Andsager
132  * clean up header files
133  * 
134  * 3     10/13/98 9:29a Dave
135  * Started neatening up freespace.h. Many variables renamed and
136  * reorganized. Added AlphaColors.[h,cpp]
137  * 
138  * 2     10/07/98 10:53a Dave
139  * Initial checkin.
140  * 
141  * 1     10/07/98 10:50a Dave
142  * 
143  * 105   6/09/98 10:31a Hoffoss
144  * Created index numbers for all xstr() references.  Any new xstr() stuff
145  * added from here on out should be added to the end if the list.  The
146  * current list count can be found in FreeSpace.cpp (search for
147  * XSTR_SIZE).
148  * 
149  * 104   5/24/98 11:36p Mike
150  * Comment out no-optimize pragmas.
151  * 
152  * 103   5/24/98 10:50p Mike
153  * Fix problem with ships with propagating explosions not being able to
154  * kamikaze.
155  * 
156  * 102   5/21/98 3:48p Lawrance
157  * prevent player from entering friendly ship docking bays
158  * 
159  * 101   5/19/98 2:19p Mike
160  * Don't do collision detection between small ship emerging or departing
161  * and its parent.
162  * 
163  * 100   5/18/98 4:53p Hoffoss
164  * Some force feedback tweaks and pilot initializations there should have
165  * been happening, but weren't, and not are!
166  * 
167  * 99    5/13/98 11:34p Mike
168  * Model caching system.
169  * 
170  * 98    5/10/98 11:11p Lawrance
171  * Allow ships to collide if in second stage of arrival
172  * 
173  * 97    5/08/98 5:25p Lawrance
174  * Don't allow collision sounds too play over each so much
175  * 
176  * 96    5/08/98 3:51p Allender
177  * temporary fix for support ships on clients in multiplayer
178  * 
179  * 95    5/08/98 11:22a Allender
180  * fix ingame join trouble.  Small messaging fix.  Enable collisions for
181  * friendlies again
182  * 
183  * 94    5/07/98 12:24a Hoffoss
184  * Finished up sidewinder force feedback support.
185  * 
186  * 93    5/03/98 5:40p Mike
187  * Debug info for trapping player collisions.
188  * 
189  * 92    4/28/98 2:28p Allender
190  * temporarily put back in collision out for multiplayers for ships on the
191  * same team since that broke rearm/repair
192  * 
193  * 91    4/28/98 1:00a Andsager
194  * Add collision sanity check
195  * 
196  * 90    4/28/98 12:23a Chad
197  * removed call which prevented same team coliisions from happening in
198  * multiplayer
199  * 
200  * 89    4/24/98 5:35p Andsager
201  * Fix sparks sometimes drawing not on model.  If ship is sphere in
202  * collision, don't draw sparks.  Modify ship_apply_local_damage() to take
203  * parameter no_spark.
204  * 
205  * 88    4/23/98 4:42p Mike
206  * Support invisible polygons that only enemy ships bump into.
207  * 
208  * 87    4/20/98 12:36a Mike
209  * Make team vs. team work when player is hostile.  Several targeting
210  * problems.
211  * 
212  * 86    4/12/98 2:02p Mike
213  * Make small ships avoid big ships.
214  * Turn on Collide_friendly flag.
215  * 
216  * 85    4/08/98 4:01p Andsager
217  * Removed assert in calculate_ship_ship_collisions_physics()
218  * 
219  * 84    4/06/98 1:39a Mike
220  * NDEBUG out some debugt code.
221  * Make ships find a new target if their target is on the same team.
222  * 
223  * 83    3/31/98 5:18p John
224  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
225  * bunch of debug stuff out of player file.  Made model code be able to
226  * unload models and malloc out only however many models are needed.
227  *  
228  * 
229  * 82    3/25/98 2:43p Andsager
230  * comment out asserts
231  * 
232  * 81    3/25/98 1:19p Mike
233  * Comment out unimportant assert.
234  * 
235  * 80    3/25/98 10:43a Andsager
236  * Hack for ship_asteroid collisions when erroneous relative_vel >150
237  * 
238  * 79    3/25/98 12:05a Mike
239  * Comment out line to make sm1-06a playable.   Reported to DaveA.
240  * 
241  * 78    3/23/98 9:20a Andsager
242  * Remove all velocity updates in object code.
243  * 
244  * 77    3/17/98 12:49a Mike
245  * Improved kamikaze behavior.
246  * 
247  * 76    3/16/98 12:02a Mike
248  * Add support for kamikaze mode.  Add AIF_NO_DYNAMIC which means
249  * relentlessly pursue current goal.
250  * 
251  * 75    3/13/98 12:57p Mike
252  * Remove an Assert that was easy to trip with time compressed 8x.
253  * 
254  * 74    3/09/98 12:58a Andsager
255  * Don't check asteroids very large displacements in collisions, since
256  * they may wrap.
257  * 
258  * 73    3/09/98 12:16a Andsager
259  * 
260  * 72    2/22/98 2:48p John
261  * More String Externalization Classification
262  * 
263  * 71    2/20/98 8:32p Lawrance
264  * Add radius parm to sound_play_3d()
265  * 
266  * 70    2/12/98 2:16p Andsager
267  * Better ship:ship collision pair next check time estimate
268  * 
269  * 69    2/09/98 1:45p Andsager
270  * Fix bug in finding earliest possilble ship:ship collision.  Used
271  * max_vel, not afterburner_max_vel.
272  * 
273  * 68    2/05/98 12:51a Mike
274  * Early asteroid stuff.
275  * 
276  * 67    2/04/98 6:08p Lawrance
277  * Add a light collision sound, overlay a shield collide sound if
278  * applicable.
279  * 
280  * 66    2/02/98 4:36p Mike
281  * Prevent damage from occurring between two ships during very last frame
282  * of warpout if docked and on opposite sides.
283  * 
284  * 65    1/28/98 2:15p Mike
285  * Make collision damage affect shield, not just hull.
286  * 
287  * 64    1/28/98 11:06a Andsager
288  * Remove some collision pairs.  Add debug code for displaying collision
289  * pair info.  
290  * 
291  * 63    1/23/98 5:08p Andsager
292  * Collision from rotation is turned on for all ship:ship colliisons.
293  * 
294  * 62    1/20/98 9:47a Mike
295  * Suppress optimized compiler warnings.
296  * Some secondary weapon work.
297  * 
298  * 61    1/19/98 11:56a Sandeep
299  * DA:  remove warning in calculate ship_ship_physics from ship_debris
300  * collision when debris is spawned next to chasing ship.
301  * 
302  * 60    1/14/98 2:30p Andsager
303  * Change warning for bad relative velocity
304  * 
305  * 59    1/12/98 9:26p Andsager
306  * Implement collisions from rotation.
307  * 
308  * 58    1/09/98 9:29a Mike
309  * Enable docked ships to warp out.  Make damage done to a ship not
310  * proportional to its own mass.
311  * 
312  * 57    1/08/98 12:12a Mike
313  * Make ships turn before warping out, if necessary, to avoid a collision.
314  * Warn player if his warpout will collide.  Abort if in stage1.
315  * 
316  * 56    1/05/98 9:08p Andsager
317  * Changed ship_shipor_debris_hit_info struct to more meaninful names.
318  * Begin implementation of collision from rotation.
319  * 
320  * 55    1/02/98 9:08a Andsager
321  * Changed ship:ship and ship:debris collision detection to ignore shields
322  * and collide only against hull.  Also, significantly reduced radius of
323  * sphere.
324  * 
325  * 54    12/23/97 5:34p Andsager
326  * Fixed bug colliding against edge of ships without shields.
327  * 
328  * 53    12/22/97 9:56p Andsager
329  * Implement ship:debris collisions.  Generalize and move
330  * ship_ship_or_debris_hit struct from CollideShipShip to ObjCollide.h
331  * 
332  * 52    12/17/97 9:39p Lawrance
333  * Always play collide sound when player collides with another ship.
334  * 
335  * 51    12/17/97 3:55p Andsager
336  * Added separation velocity in ship:ship collision when both ships on
337  * same team.
338  * 
339  * 50    12/16/97 5:24p Andsager
340  * Modify collision detection criterion.  Somewhat of a hack, but it keeps
341  * ships from getting stuci on each other.  Comment out debug info.
342  * 
343  * 49    12/08/97 6:23p Lawrance
344  * fix collision sounds (broken since hit pos was changed to local coords)
345  * 
346  * 48    12/04/97 5:34p Lawrance
347  * let player collide with friendly ships (no damage though) by default
348  * 
349  * 47    12/04/97 4:05p Allender
350  * comment out hud printf for ship ship collisions
351  * 
352  * 46    12/03/97 5:44p Andsager
353  * Implement relative velocity collisions in the reference frame of the
354  * heavier object.
355  * 
356  * 45    12/03/97 12:04p Hoffoss
357  * Made changes so the 8 %'s aren't needed anymore.  Can just use 2 again
358  * now.
359  * 
360  * 44    12/03/97 11:35a Hoffoss
361  * Made changes to HUD messages send throughout the game.
362  * 
363  * 43    11/28/97 3:51p Mike
364  * Get blasted % symbol to display through HUD_printf.  Had to enter
365  * %%%%%%%% (yes, that's 8x %)
366  * 
367  * 42    11/26/97 3:25p Mike
368  * Decrease large quantities of damage.  If > 5.0f, cut out half of amount
369  * over 5.0f.
370  * 
371  * 41    11/16/97 8:45p Mike
372  * Add SM_ATTACK_FOREVER submode (of AIM_CHASE) and ships better dealing
373  * with their engines being blown out.
374  * 
375  * 40    11/14/97 9:27a Andsager
376  * Changed debug print statements
377  * 
378  * 39    11/13/97 6:12p Lawrance
379  * uncomment code that was commented out for build reasons
380  * 
381  * 38    11/13/97 6:11p Lawrance
382  * call hud_start_collision_flash() when player ship hits another ship
383  * 
384  * 37    11/13/97 4:59p Mike
385  * Add new chase submode: SM_FLY_AWAY.  Deals with a ship colliding with
386  * its target.  Ships were getting hung up on each other because the avoid
387  * code was used to deal with collisions.  It was very bad.
388  * 
389  * 36    11/12/97 11:53p Mike
390  * Fix code that shows damage taken due to collision to only work for
391  * player.
392  * 
393  * 35    11/12/97 12:14p Mike
394  * Cut ship:ship collision damage by half again and put in a HUD message
395  * for large damage.
396  * 
397  * 34    11/12/97 10:03a Mike
398  * Cut damage done due to ship:ship collisions by half.
399  * 
400  * 33    11/10/97 10:50p Mike
401  * Fix bug preventing ships in sm1-03a from warping out together as a
402  * docked pair.  Only worked for support ships and cargo, not a pair of
403  * transports.
404  * 
405  * 32    11/09/97 11:24p Andsager
406  * Set small bounce in ship-ship collision.  coeffic restitution 0.2
407  * 
408  * 31    11/09/97 4:39p Lawrance
409  * make 'Collide_friendly' make friendly collisions behave the same way as
410  * hostile collisions
411  * 
412  * 30    11/07/97 4:36p Mike
413  * Change how ships determine they're under attack by dumbfire weapons.
414  * 
415  * 29    11/06/97 12:27a Mike
416  * Better avoid behavior.
417  * Modify ai_turn_towards_vector() to take a flag parameter.
418  * 
419  * 28    11/05/97 10:32p Mike
420  * Convert Assert() to nprintf when point of collisions is farther apart
421  * than sum of object radii.
422  * 
423  * 27    11/05/97 9:28p Mike
424  * Add ships_are_docking() to allow ships of different teams to dock.
425  * 
426  * 26    11/05/97 5:50p Andsager
427  * Added hit_time to ship_ship_hit_info to prevent hit_time from getting
428  * overwritten.
429  * 
430  * 25    11/03/97 11:21p Andsager
431  * Fixed bug getting shield quad.  Reduced damage in ship-ship.  Collision
432  * normal from sphere center to hit_pos
433  * 
434  * 24    11/03/97 11:08p Lawrance
435  * play correct collision sounds.
436  * 
437  * 23    11/03/97 2:07p Lawrance
438  * add ship-to-ship collision sound
439  * 
440  * 22    11/02/97 10:54p Lawrance
441  * add Collide_friendly, which is changed through debug console to
442  * enable/disable friend-friend collisions
443  * 
444  * 21    11/01/97 3:58p Mike
445  * Zero damage caused by ship:ship collisions until it gets balanced.
446  * 
447  * 20    10/29/97 5:19p Dave
448  * More debugging of server transfer. Put in debrief/brief 
449  * transition for multiplayer (w/standalone)
450  * 
451  * 19    10/29/97 4:56p Andsager
452  * Fixed bugs in collision physics involving normals.  Collided objects
453  * now back up in the direction they came in.
454  * 
455  * 18    10/28/97 4:57p John
456  * Put Andsager's new sphereline collide code officially into the code
457  * base and did a little restructuring.  Fixed a few little bugs with it
458  * and added some simple bounding box elimination and did some timings.
459  * 
460  * 
461  * 17    10/27/97 6:12p Dave
462  * Changed host/server transfer around. Added some multiplayer data to
463  * state save/restore. Made multiplayer quitting more intelligent.
464  * 
465  * 16    10/27/97 8:35a John
466  * code for new player warpout sequence
467  * 
468  * 15    10/25/97 10:12a Andsager
469  * Cleaned up ship_ship_check_collision. Moved SHIP_SPHERE_CHECK to
470  * objCollide.h.  Added some debug code for shield/hull collisions
471  * 
472  * 14    10/22/97 10:29p Andsager
473  * modify ship_ship_check_collision to allow sphere-polygon collisions
474  * 
475  * 13    10/19/97 11:45p Mike
476  * Hacked in damage due to gravity well of a planet.
477  * 
478  * 12    10/19/97 9:41p Andsager
479  * undefine SPHERE_POLY_CHECK
480  * 
481  * 11    10/19/97 9:34p Andsager
482  * Changed model_collide to take 2nd parameter radius with (default = 0)
483  * 
484  * 10    10/17/97 1:32a Andsager
485  * add sphere-polygon collision detection
486  * 
487  * 9     10/01/97 5:55p Lawrance
488  * change call to snd_play_3d() to allow for arbitrary listening position
489  * 
490  * 8     9/30/97 5:06p Andsager
491  * rename vm_project_point_onto_surface() -> vm_project_name_onto_plane()
492  * 
493  * 7     9/28/97 2:19p Andsager
494  * fixed bug in getting shield point in ray model collisions
495  * 
496  * 6     9/25/97 2:54p Andsager
497  * added small bounce to collisions
498  * 
499  * 5     9/19/97 5:00p Andsager
500  * modify collisions so that damage is first applied to shield and then to
501  * hull
502  * 
503  * 4     9/18/97 4:08p John
504  * Cleaned up & restructured ship damage stuff.
505  * 
506  * 3     9/18/97 3:58p Andsager
507  * fix bugs in sphere_sphere collision (sets r and hit_pos)
508  * 
509  * 2     9/17/97 5:12p John
510  * Restructured collision routines.  Probably broke a lot of stuff.
511  * 
512  * 1     9/17/97 2:14p John
513  * Initial revision
514  *
515  * $NoKeywords: $
516  */
517
518 #include "objcollide.h"
519 #include "object.h"
520 #include "model.h"
521 #include "ai.h"
522 #include "ship.h"
523 #include "multi.h"
524 #include "freespace.h"
525 #include "shiphit.h"
526 #include "gamesnd.h"
527 #include "3d.h"                 // needed for View_position, which is used when playing 3d sound
528 #include "gamesequence.h"
529 #include "hudshield.h"
530 #include "joy_ff.h"
531 #include "timer.h"
532 #include "asteroid.h"
533
534 //#pragma optimize("", off)
535 //#pragma auto_inline(off)
536
537 #define COLLISION_FRICTION_FACTOR       0.0
538 #define COLLISION_ROTATION_FACTOR       0.2
539 #define SEP_VEL 5.0f            // separation velocity between two ships that collide on same team.
540
541 #define COLLIDE_DEBUG
542 #undef  COLLIDE_DEBUG
543
544 // GENERAL COLLISIONS FUNCTIONS
545 // calculates the inverse moment of inertia matrix in world coordinates
546 void get_I_inv (matrix* I_inv, matrix* I_inv_body, matrix* orient);
547
548 // calculate the physics of extended two body collisions
549 void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info);
550
551 int ship_hit_shield(object *obj, mc_info *mc, collision_info_struct *sshs);
552 void collect_ship_ship_physics_info(object *heavy, object *light, mc_info *mc_info, collision_info_struct *ship_ship_hit_info);
553
554 #ifndef NDEBUG
555 static int Collide_friendly = 1;
556 DCF_BOOL( collide_friendly, Collide_friendly )
557 #endif
558
559 static int Player_collide_sound, AI_collide_sound;
560 static int Player_collide_shield_sound, AI_collide_shield_sound;
561
562 //      Return true if two ships are docking.
563 int ships_are_docking(object *objp1, object *objp2)
564 {
565         ai_info *aip1, *aip2;
566         ship            *shipp1, *shipp2;
567
568         shipp1 = &Ships[objp1->instance];
569         shipp2 = &Ships[objp2->instance];
570
571         aip1 = &Ai_info[shipp1->ai_index];
572         aip2 = &Ai_info[shipp2->ai_index];
573
574         // for multiplayer clients -- disable the collision stuff for support ships.
575         /*
576         if ( MULTIPLAYER_CLIENT ) {
577                 if ( (Ship_info[shipp1->ship_info_index].flags & SIF_SUPPORT) || (Ship_info[shipp2->ship_info_index].flags & SIF_SUPPORT) ) {
578                         return 1;
579                 }
580         }
581         */
582
583         if (aip1->ai_flags & AIF_DOCKED) {
584                 if (aip1->dock_objnum == objp2-Objects){
585                         return 1;
586                 }
587         }
588
589         if (aip1->mode == AIM_DOCK) {
590                 if (aip1->goal_objnum == objp2-Objects){
591                         return 1;
592                 }
593         } else if (aip2->mode == AIM_DOCK) {
594                 if (aip2->goal_objnum == objp1-Objects){
595                         return 1;
596                 }
597         }
598
599         return 0;
600
601 }
602
603 //      If light_obj emerging from or departing to dock bay in heavy_obj, no collision detection.
604 int bay_emerge_or_depart(object *heavy_objp, object *light_objp)
605 {
606         if (light_objp->type != OBJ_SHIP)
607                 return 0;
608
609         ai_info *aip = &Ai_info[Ships[light_objp->instance].ai_index];
610
611         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART)) {
612                 if (aip->goal_objnum == OBJ_INDEX(heavy_objp))
613                         return 1;
614         }
615
616         return 0;
617 }
618
619 int ship_ship_check_collision(collision_info_struct *ship_ship_hit_info, vector *hitpos)
620 {
621         object *heavy_obj       = ship_ship_hit_info->heavy;
622         object *light_obj = ship_ship_hit_info->light;
623         int     player_involved;        // flag to indicate that A or B is the Player_obj
624         int     num; //, player_check=0;
625
626         Assert( heavy_obj->type == OBJ_SHIP );
627         Assert( light_obj->type == OBJ_SHIP );
628
629         num = heavy_obj->instance;
630         Assert( num >= 0 );
631
632         Assert( Ships[num].objnum == OBJ_INDEX(heavy_obj));
633
634         // AL 12-4-97: we use the player_involved flag to ensure collisions are always
635         //             done with the player, regardless of team.
636         if ( heavy_obj == Player_obj || light_obj == Player_obj ) {
637                 player_involved = 1;
638         } else {
639                 player_involved = 0;
640         }
641
642         // Make ships that are warping in not get collision detection done
643 //      if ( Ships[num].flags & SF_ARRIVING ) return 0;
644         if ( Ships[num].flags & SF_ARRIVING_STAGE_1 ) { 
645                 return 0;
646         }
647
648         // Don't do collision detection for docking ships, since they will always collide while trying to dock
649         if ( ships_are_docking(heavy_obj, light_obj) ) {
650                 return 0;
651         }
652
653         //      If light_obj emerging from or departing to dock bay in heavy_obj, no collision detection.
654         if (bay_emerge_or_depart(heavy_obj, light_obj)) {
655                 return 0;
656         }
657
658         //      Ships which are dying should not do collision detection.
659         //      Also, this is the only clean way I could figure to get ships to not do damage to each other for one frame
660         //      when they are docked and departing.  Due to sequencing, they would not show up as docked, yet they
661         //      would still come through here, so they would harm each other, if on opposing teams. -- MK, 2/2/98
662         if ((heavy_obj->flags & OF_SHOULD_BE_DEAD) || (light_obj->flags & OF_SHOULD_BE_DEAD)) {
663                 return 0;
664         }
665
666         //nprintf(("AI", "Frame %i: Collision between %s and %s\n", Framecount, Ships[heavy_obj->instance].ship_name, Ships[light_obj->instance].ship_name));
667
668 #ifndef NDEBUG
669         //      Don't do collision detection on a pair of ships on the same team.
670         //      Change this someday, but for now, it's a problem.
671         if ( !Collide_friendly ) {              // Collide_friendly is a global value changed via debug console
672                 if ( (!player_involved) && (Ships[heavy_obj->instance].team == Ships[light_obj->instance].team) ) {
673                         return 0;
674                 }
675         }
676 #endif
677
678         //      Apparently we're doing same team collisions.
679         //      But, if both are offscreen, ignore the collision
680         if (Ships[heavy_obj->instance].team == Ships[light_obj->instance].team) {
681 //              if ((Game_mode & GM_MULTIPLAYER) || (!(heavy_obj->flags & OF_WAS_RENDERED) && !(light_obj->flags & OF_WAS_RENDERED)))
682                 // mwa 4/28/98 -- don't understand why GM_MULTIPLAYER was included in this line.  All clients
683                 // need to do all collisions for their own ship. removing the multiplayer part of next if statement.
684
685                 if ( (!(heavy_obj->flags & OF_WAS_RENDERED) && !(light_obj->flags & OF_WAS_RENDERED)) ) {
686                         return 0;
687                 }
688         }
689
690         //      If either of these objects doesn't get collision checks, abort.
691         if (!(Ship_info[Ships[num].ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
692                 return 0;
693         }
694
695         if (!(Ship_info[Ships[light_obj->instance].ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
696                 return 0;
697         }
698
699         // Set up model_collide info
700         mc_info mc;
701         memset(&mc, -1, sizeof(mc_info));
702
703 //      vector submodel_hit;
704
705         // Do in heavy object RF
706         mc.model_num = Ships[num].modelnum;     // Fill in the model to check
707         mc.orient = &heavy_obj->orient;         // The object's orient
708
709         vector zero, p0, p1;
710         vm_vec_zero(&zero);             // we need the physical vector and can not set its value to zero
711         vm_vec_sub(&p0, &light_obj->last_pos, &heavy_obj->last_pos);
712         vm_vec_sub(&p1, &light_obj->pos, &heavy_obj->pos);
713
714         // find the light object's position in the heavy object's reference frame at last frame and also in this frame.
715         vector p0_temp, p0_rotated;
716         
717         // Collision detection from rotation enabled if at max rotaional velocity and 5fps, rotation is less than PI/2
718         // This should account for all ships
719         if ( (vm_vec_mag_squared( &heavy_obj->phys_info.max_rotvel ) * .04) < (PI*PI/4) ) {
720                 // collide_rotate calculate (1) start position and (2) relative velocity
721                 ship_ship_hit_info->collide_rotate = 1;
722                 vm_vec_rotate(&p0_temp, &p0, &heavy_obj->last_orient);
723                 vm_vec_unrotate(&p0_rotated, &p0_temp, &heavy_obj->orient);
724                 mc.p0 = &p0_rotated;                            // Point 1 of ray to check
725                 vm_vec_sub(&ship_ship_hit_info->light_rel_vel, &p1, &p0_rotated);
726                 vm_vec_scale(&ship_ship_hit_info->light_rel_vel, 1/flFrametime);
727         } else {
728                 // should be no ships that can rotate this fast
729                 Int3();
730                 ship_ship_hit_info->collide_rotate = 0;
731                 mc.p0 = &p0;                                                    // Point 1 of ray to check
732                 vm_vec_sub(&ship_ship_hit_info->light_rel_vel, &light_obj->phys_info.vel, &heavy_obj->phys_info.vel);
733         }
734         
735         // Set up collision info
736         mc.pos = &zero;                                         // The object's position
737         mc.p1 = &p1;                                                    // Point 2 of ray to check
738         mc.radius = model_get_core_radius( Ships[light_obj->instance].modelnum );
739         mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);                      // flags
740
741         //      Only check invisible face polygons for ship:ship of different teams.
742         if ( !(Ship_info[Ships[heavy_obj->instance].ship_info_index].flags & SIF_DONT_COLLIDE_INVIS) ) {
743                 if ((heavy_obj->flags & OF_PLAYER_SHIP) || (light_obj->flags & OF_PLAYER_SHIP) || (Ships[heavy_obj->instance].team != Ships[light_obj->instance].team) ) {
744                         mc.flags |= MC_CHECK_INVISIBLE_FACES;
745                 }
746         }
747         
748         // copy important data
749         int copy_flags = mc.flags;  // make a copy of start end positions of sphere in  big ship RF
750         vector copy_p0, copy_p1;
751         copy_p0 = *mc.p0;
752         copy_p1 = *mc.p1;
753
754         // first test against the sphere - if this fails then don't do any submodel tests
755         mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE;
756
757         int submodel_list[MAX_ROTATING_SUBMODELS];
758         int num_rotating_submodels = 0;
759         int valid_hit_occured = 0;
760         polymodel *pm;
761
762         ship_model_start(heavy_obj);
763
764         if (model_collide(&mc)) {
765
766                 // Set earliest hit time
767                 ship_ship_hit_info->hit_time = FLT_MAX;
768
769                 // Do collision the cool new way
770                 if ( ship_ship_hit_info->collide_rotate ) {
771
772                         model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj);
773
774                         pm = model_get(Ships[heavy_obj->instance].modelnum);
775
776                         // turn off all rotating submodels and test for collision
777                         for (int i=0; i<num_rotating_submodels; i++) {
778                                 pm->submodel[submodel_list[i]].blown_off = 1;
779                         }
780
781                         // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
782                         mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
783
784                         // check each submodel in turn
785                         for (int i=0; i<num_rotating_submodels; i++) {
786                                 // turn on submodel for collision test
787                                 pm->submodel[submodel_list[i]].blown_off = 0;
788
789                                 // set angles for last frame
790                                 angles copy_angles = pm->submodel[submodel_list[i]].angs;
791
792                                 // find the start and end positions of the sphere in submodel RF
793                                 pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs;
794                                 world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos);
795
796                                 pm->submodel[submodel_list[i]].angs = copy_angles;
797                                 world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos);
798
799                                 mc.p0 = &p0;
800                                 mc.p1 = &p1;
801                                 // mc.pos = zero        // in submodel RF
802
803                                 mc.orient = &vmd_identity_matrix;
804                                 mc.submodel_num = submodel_list[i];
805
806                                 if ( model_collide(&mc) ) {
807                                         if (mc.hit_dist < ship_ship_hit_info->hit_time ) {
808                                                 valid_hit_occured = 1;
809
810                                                 // set up ship_ship_hit_info common
811                                                 set_hit_struct_info(ship_ship_hit_info, &mc, SUBMODEL_ROT_HIT);
812                                                 model_find_world_point(&ship_ship_hit_info->hit_pos, &mc.hit_point, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
813
814                                                 // set up ship_ship_hit_info for rotating submodel
815                                                 if (ship_ship_hit_info->edge_hit == 0) {
816                                                         model_find_obj_dir(&ship_ship_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
817                                                 }
818
819                                                 // find position in submodel RF of light object at collison
820                                                 vector int_light_pos, diff;
821                                                 vm_vec_sub(&diff, mc.p1, mc.p0);
822                                                 vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist);
823                                                 model_find_world_point(&ship_ship_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
824
825 //                                              submodel_hit = mc.hit_point;
826
827                                                 /*
828                                                 // find position in CM RF of the heavy object at collision
829                                                 vm_vec_sub(&diff, &heavy_obj->pos, &heavy_obj->last_pos);
830                                                 vm_vec_scale_add(&int_heavy_pos, &heavy_obj->last_pos, &diff, mc.hit_dist);
831
832                                                 // Find orientation of heavy at time of collision.  Use last_orientation * delta_orientation.
833                                                 // heavy last orient * (delta_orient * time)
834                                                 matrix m_temp, rot_matrix;
835                                                 float theta;
836                                                 vector rot_axis;
837
838                                                 vm_copy_transpose_matrix(&m_temp, &heavy_obj->last_orient);                     // Mtemp1 = curr ^-1
839                                                 vm_matrix_x_matrix(&rot_matrix, &m_temp, &heavy_obj->orient);           // R = goal * Mtemp1
840                                                 vm_matrix_to_rot_axis_and_angle(&rot_matrix, &theta, &rot_axis);        // determines angle and rotation axis from curr to goal
841                                                 vm_quaternion_rotate(&m_temp, theta * mc.hit_dist, &rot_axis);
842                                                 Assert(is_valid_matrix(&m_temp));
843                                                 vm_matrix_x_matrix(&int_heavy_orient, &heavy_obj->last_orient, &m_temp);
844
845                                                 // set submodel angle at time of collision
846                                                 // TODO: generalize... what happens when angle passes 0 or 2PI
847                                                 angles temp_angs;
848                                                 vm_vec_sub(&diff, (vector*)&pm->submodel[submodel_list[i]].angs, (vector*)&pm->submodel[submodel_list[i]].sii->prev_angs);
849                                                 vm_vec_scale_add((vector*)&temp_angs, (vector *)&pm->submodel[submodel_list[i]].sii->prev_angs, &diff, mc.hit_dist);
850                                                 pm->submodel[submodel_list[i]].angs = temp_angs;
851
852                                                 // find intersection point in submodel RF - THEN advance to end of frametime.
853                                                 vector temp = int_light_pos;
854                                                 world_find_model_point(&int_submodel_pos, &int_light_pos, pm, submodel_list[i], &int_heavy_orient, &int_heavy_pos);
855                                                 vector temp2;
856
857                                                 // Advance to end of frametime
858                                                 pm->submodel[submodel_list[i]].angs = copy_angles;
859                                                 model_find_world_point(&ship_ship_hit_info->light_collision_cm_pos, &int_submodel_pos, mc.model_num, mc.hit_submodel, mc.orient, &zero);
860                                                 vm_vec_sub(&temp2, &ship_ship_hit_info->light_collision_cm_pos, &ship_ship_hit_info->hit_pos);
861                 */
862                 //                                      vector temp2;
863                 //                                      vm_vec_sub(&temp2, &ship_ship_hit_info->light_collision_cm_pos, &ship_ship_hit_info->hit_pos);
864
865                                         }
866                                 }
867                                 // Don't look at this submodel again
868                                 pm->submodel[submodel_list[i]].blown_off = 1;
869                         }
870
871                 }
872
873                 // Recover and do usual ship_ship collision, but without rotating submodels
874                 mc.flags = copy_flags;
875                 *mc.p0 = copy_p0;
876                 *mc.p1 = copy_p1;
877                 mc.orient = &heavy_obj->orient;
878
879                 // usual ship_ship collision test
880                 if ( model_collide(&mc) )       {
881                         // check if this is the earliest hit
882                         if (mc.hit_dist < ship_ship_hit_info->hit_time) {
883                                 valid_hit_occured = 1;
884
885                                 set_hit_struct_info(ship_ship_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
886
887                                 // get hitpos - heavy_pos
888 //                              if ( ship_ship_hit_info->collide_rotate ) {
889 //                                      model_find_world_point(&ship_ship_hit_info->hit_pos, &mc.hit_point, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
890 //                              }
891
892                                 // get collision normal if not edge hit
893                                 if (ship_ship_hit_info->edge_hit == 0) {
894                                         model_find_obj_dir(&ship_ship_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
895                                 }
896
897                                 // find position in submodel RF of light object at collison
898                                 vector diff;
899                                 vm_vec_sub(&diff, mc.p1, mc.p0);
900                                 vm_vec_scale_add(&ship_ship_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
901
902 //                              submodel_hit = mc.hit_point;
903                         }
904                 }
905
906                 ship_model_stop( heavy_obj );
907         }
908
909         if (valid_hit_occured) {
910
911                 // Collision debug stuff
912 #ifdef DEBUG
913                 object *collide_obj = NULL;
914                 if (heavy_obj == Player_obj) {
915                         collide_obj = light_obj;
916                 } else if (light_obj == Player_obj) {
917                         collide_obj = heavy_obj;
918                 }
919                 if ((collide_obj != NULL) && (Ship_info[Ships[collide_obj->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
920                         char    *submode_string = "";
921                         ai_info *aip;
922
923                         extern char *Mode_text[];
924                         aip = &Ai_info[Ships[collide_obj->instance].ai_index];
925
926                         if (aip->mode == AIM_CHASE)
927                                 submode_string = Submode_text[aip->submode];
928
929                         nprintf(("AI", "Player collided with ship %s, AI mode = %s, submode = %s\n", Ships[collide_obj->instance].ship_name, Mode_text[aip->mode], submode_string));
930                 }
931 #endif
932
933                 // Update ai to deal with collisions
934                 if (heavy_obj-Objects == Ai_info[Ships[light_obj->instance].ai_index].target_objnum) {
935                         Ai_info[Ships[light_obj->instance].ai_index].ai_flags |= AIF_TARGET_COLLISION;
936                 }
937                 if (light_obj-Objects == Ai_info[Ships[heavy_obj->instance].ai_index].target_objnum) {
938                         Ai_info[Ships[heavy_obj->instance].ai_index].ai_flags |= AIF_TARGET_COLLISION;
939                 }
940
941                 // SET PHYSICS PARAMETERS
942                 // already have (hitpos - heavy) and light_cm_pos
943                 // get heavy cm pos - already have light_cm_pos
944                 ship_ship_hit_info->heavy_collision_cm_pos = zero;
945
946                 // get r_heavy and r_light
947                 ship_ship_hit_info->r_heavy = ship_ship_hit_info->hit_pos;
948                 vm_vec_sub(&ship_ship_hit_info->r_light, &ship_ship_hit_info->hit_pos, &ship_ship_hit_info->light_collision_cm_pos);
949
950                 // set normal for edge hit
951                 if ( ship_ship_hit_info->edge_hit ) {
952                         vm_vec_copy_normalize(&ship_ship_hit_info->collision_normal, &ship_ship_hit_info->r_light);
953                         vm_vec_negate(&ship_ship_hit_info->collision_normal);
954                 }
955
956                 // get world hitpos
957                 vm_vec_add(hitpos, &ship_ship_hit_info->heavy->pos, &ship_ship_hit_info->r_heavy);
958
959                 /*
960                 vector temp1, temp2, temp3, diff;
961                 vm_vec_add(&temp1, &ship_ship_hit_info->light_collision_cm_pos, &ship_ship_hit_info->r_light);
962                 vm_vec_add(&temp2, &ship_ship_hit_info->heavy_collision_cm_pos, &ship_ship_hit_info->r_heavy);
963                 vm_vec_sub(&diff, &temp2, &temp1);
964
965                 ship_model_start( heavy_obj );
966                 pm = model_get(Ships[heavy_obj->instance].modelnum);
967                 world_find_model_point(&temp3, hitpos, pm, ship_ship_hit_info->submodel_num, &heavy_obj->orient, &heavy_obj->pos);
968                 ship_model_stop( heavy_obj );
969
970                 vm_vec_sub(&diff, &submodel_hit, &temp3);
971
972                 if (vm_vec_mag(&diff) > 0.1) {
973                         Int3();
974                 }       */
975
976
977                 // do physics
978                 calculate_ship_ship_collision_physics(ship_ship_hit_info);
979
980                 // Provide some separation for the case of same team
981                 if (Ships[heavy_obj->instance].team == Ships[light_obj->instance].team) {
982                         ship    *heavy_shipp = &Ships[heavy_obj->instance];
983                         ship    *light_shipp = &Ships[light_obj->instance];
984
985                         //      If a couple of small ships, just move them apart.
986
987                         if ((Ship_info[heavy_shipp->ship_info_index].flags & SIF_SMALL_SHIP) && (Ship_info[light_shipp->ship_info_index].flags & SIF_SMALL_SHIP)) {
988                                 if ((heavy_obj->flags & OF_PLAYER_SHIP) || (light_obj->flags & OF_PLAYER_SHIP)) {
989                                         /*
990                                         vector  h2l_vec;                                                                                
991                                         float           mass_sum = heavy_obj->phys_info.mass + light_obj->phys_info.mass; 
992                                         float           lh_ratio;
993
994                                         lh_ratio = light_obj->phys_info.mass/mass_sum;
995                                         if (lh_ratio < 0.2f) {
996                                                 lh_ratio = 0.2f;
997                                         }
998
999                                         // actually initialize h2l_vec
1000                                         vm_vec_sub(&h2l_vec, &light_obj->pos, &heavy_obj->pos);
1001                                         
1002                                         //      Choose best direction to move objects.  Want to move away from collision point.
1003                                         //      Hmm, maybe this is needlessly complex.  Maybe should use collision point and slide them
1004                                         //      away from that? -- MK, 4/5/98
1005                                         
1006                                         if (vm_vec_dot(&light_obj->phys_info.vel, &h2l_vec) > 0.0f) {
1007                                                 vm_vec_scale_add2(&light_obj->phys_info.vel, &h2l_vec, 10.0f * (1.0f - lh_ratio));
1008                                         } else {
1009                                                 if (vm_vec_dot(&light_obj->orient.rvec, &h2l_vec) < 0.0f) {
1010                                                         vm_vec_scale_add2(&light_obj->phys_info.vel, &light_obj->orient.rvec, -10.0f * (1.0f - lh_ratio));
1011                                                 } else {
1012                                                         vm_vec_scale_add2(&light_obj->phys_info.vel, &light_obj->orient.rvec, +10.0f * (1.0f - lh_ratio));
1013                                                 }
1014                                         }
1015
1016                                         if (vm_vec_dot(&heavy_obj->phys_info.vel, &h2l_vec) < 0.0f) {
1017                                                 vm_vec_scale_add2(&heavy_obj->phys_info.vel, &h2l_vec, 10.0f * (1.0f - lh_ratio));
1018                                         } else {
1019                                                 if (vm_vec_dot(&heavy_obj->orient.rvec, &h2l_vec) < 0.0f) {
1020                                                         vm_vec_scale_add2(&light_obj->phys_info.vel, &light_obj->orient.rvec, +10.0f * (1.0f - lh_ratio));
1021                                                 } else {
1022                                                         vm_vec_scale_add2(&light_obj->phys_info.vel, &light_obj->orient.rvec, -10.0f * (1.0f - lh_ratio));
1023                                                 }
1024                                         }*/
1025
1026                                         vector h_to_l_vec;
1027                                         vector rel_vel_h;
1028                                         vector perp_rel_vel;
1029
1030                                         vm_vec_sub(&h_to_l_vec, &heavy_obj->pos, &light_obj->pos);
1031                                         vm_vec_sub(&rel_vel_h, &heavy_obj->phys_info.vel, &light_obj->phys_info.vel);
1032                                         float mass_sum = light_obj->phys_info.mass + heavy_obj->phys_info.mass;
1033
1034                                         // get comp of rel_vel perp to h_to_l_vec;
1035                                         float mag = vm_vec_dotprod(&h_to_l_vec, &rel_vel_h) / vm_vec_mag_squared(&h_to_l_vec);
1036                                         vm_vec_scale_add(&perp_rel_vel, &rel_vel_h, &h_to_l_vec, -mag);
1037                                         vm_vec_normalize(&perp_rel_vel);
1038
1039                                         vm_vec_scale_add2(&heavy_obj->phys_info.vel, &perp_rel_vel, SEP_VEL * light_obj->phys_info.mass / mass_sum);
1040                                         vm_vec_scale_add2(&light_obj->phys_info.vel, &perp_rel_vel, -SEP_VEL * heavy_obj->phys_info.mass / mass_sum);
1041
1042                                         vm_vec_rotate( &heavy_obj->phys_info.prev_ramp_vel, &heavy_obj->phys_info.vel, &heavy_obj->orient );
1043                                         vm_vec_rotate( &light_obj->phys_info.prev_ramp_vel, &light_obj->phys_info.vel, &light_obj->orient );
1044                                 }
1045                         } else {
1046                                 // add extra velocity to separate the two objects, backing up the direction we came in.
1047                                 // TODO: add effect of velocity from rotating submodel
1048                                 float rel_vel = vm_vec_mag_quick( &ship_ship_hit_info->light_rel_vel);
1049                                 if (rel_vel < 1) {
1050                                         rel_vel = 1.0f;
1051                                 }
1052                                 float           mass_sum = heavy_obj->phys_info.mass + light_obj->phys_info.mass; 
1053                                 vm_vec_scale_add2( &heavy_obj->phys_info.vel, &ship_ship_hit_info->light_rel_vel, SEP_VEL*light_obj->phys_info.mass/(mass_sum*rel_vel) );
1054                                 vm_vec_rotate( &heavy_obj->phys_info.prev_ramp_vel, &heavy_obj->phys_info.vel, &heavy_obj->orient );
1055                                 vm_vec_scale_add2( &light_obj->phys_info.vel, &ship_ship_hit_info->light_rel_vel, -SEP_VEL*heavy_obj->phys_info.mass/(mass_sum*rel_vel) );
1056                                 vm_vec_rotate( &light_obj->phys_info.prev_ramp_vel, &light_obj->phys_info.vel, &light_obj->orient );
1057                         }
1058                 }
1059         }
1060
1061
1062         return valid_hit_occured;
1063 }
1064
1065 // gets modified mass of cruiser in cruiser/asteroid collision so cruisers dont get bumped so hard.
1066 // modified mass is 10x, 4x, or 2x larger than asteroid mass
1067 // returns 1 if modified mass is larger than given mass, 0 otherwise 
1068 int check_special_cruiser_asteroid_collision(object *heavy, object *light, float *cruiser_mass, int *cruiser_light)
1069 {
1070 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
1071         int asteroid_type;
1072
1073         if (heavy->type == OBJ_ASTEROID) {
1074                 Assert(light->type == OBJ_SHIP);
1075                 if (Ship_info[Ships[light->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1076
1077                         asteroid_type = Asteroids[heavy->instance].type;
1078                         if (asteroid_type == 0) {
1079                                 *cruiser_mass = 10.0f * heavy->phys_info.mass;
1080                         } else if (asteroid_type == 1) {
1081                                 *cruiser_mass = 4.0f * heavy->phys_info.mass;
1082                         } else {
1083                                 *cruiser_mass = 2.0f * heavy->phys_info.mass;
1084                         }
1085
1086                         if (*cruiser_mass > light->phys_info.mass) {
1087                                 *cruiser_light = 1;
1088                                 return 1;
1089                         }
1090                 }
1091         } else if (light->type == OBJ_ASTEROID) {
1092                 Assert(heavy->type == OBJ_SHIP);
1093                 if (Ship_info[Ships[heavy->instance].ship_info_index].flags & SIF_BIG_SHIP) {
1094
1095                         asteroid_type = Asteroids[light->instance].type;
1096                         if (asteroid_type == 0) {
1097                                 *cruiser_mass = 10.0f * light->phys_info.mass;
1098                         } else if (asteroid_type == 1) {
1099                                 *cruiser_mass = 4.0f * light->phys_info.mass;
1100                         } else {
1101                                 *cruiser_mass = 2.0f * light->phys_info.mass;
1102                         }
1103
1104                         if (*cruiser_mass > heavy->phys_info.mass) {
1105                                 *cruiser_light = 0;
1106                                 return 1;
1107                         }
1108                 }
1109         }
1110 #endif
1111         return 0;
1112 }
1113
1114 // ------------------------------------------------------------------------------------------------
1115 //              input:          ship_ship_hit           =>              structure containing ship_ship hit info
1116 //              (includes)      A, B                                    =>              objects colliding
1117 //                                              r_A, r_B                                =>              position to collision from center of mass
1118 //                                              collision_normal        =>              collision_normal (outward from B)                       
1119 //
1120 //              output: velocity, angular velocity, impulse
1121 //
1122 // ------------------------------------------------------------------------------------------------
1123 //
1124 // calculates correct physics response to collision between two objects given
1125 //              masses, moments of inertia, velocities, angular velocities, 
1126 //              relative collision positions, and the impulse direction
1127 //
1128 void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info)
1129 {
1130         // important parameters passed thru ship_ship_or_debris_hit
1131         // calculate the whack applied to each ship from collision
1132
1133         // make local copies of hit_struct parameters
1134         object *heavy = ship_ship_hit_info->heavy;
1135         object *light = ship_ship_hit_info->light;
1136
1137         // make cruiser/asteroid collision softer on cruisers.
1138         int special_cruiser_asteroid_collision;
1139         int cruiser_light = 0;
1140         float cruiser_mass = 0.0f, copy_mass = 0.0f;
1141         special_cruiser_asteroid_collision = check_special_cruiser_asteroid_collision(heavy, light, &cruiser_mass, &cruiser_light);
1142
1143         if (special_cruiser_asteroid_collision) {
1144                 if (cruiser_light) {
1145                         Assert(light->phys_info.mass < cruiser_mass);
1146                         copy_mass = light->phys_info.mass;
1147                         light->phys_info.mass = cruiser_mass;
1148                 } else {
1149                         Assert(heavy->phys_info.mass < cruiser_mass);
1150                         copy_mass = heavy->phys_info.mass;
1151                         heavy->phys_info.mass = cruiser_mass;
1152                 }
1153         }
1154
1155         float           coeff_restitution;      // parameter controls amount of bounce
1156         float           v_rel_normal_m;         // relative collision velocity in the direction of the collision normal
1157         vector  v_rel_parallel_m;               // normalized v_rel (Va-Vb) projected onto collision surface
1158         vector  world_rotvel_heavy_m, world_rotvel_light_m, vel_from_rotvel_heavy_m, vel_from_rotvel_light_m, v_rel_m, vel_heavy_m, vel_light_m;
1159
1160         coeff_restitution = 0.1f;               // relative velocity wrt normal is zero after the collision ( range 0-1 )
1161
1162         // find velocity of each obj at collision point
1163
1164         // heavy object is in cm reference frame so we don't get a v_heavy term.
1165         if ( ship_ship_hit_info->collide_rotate ) {
1166                 // if we have collisions from rotation, the effective velocity from rotation of the large body is alreay taken account
1167                 vm_vec_zero( &vel_heavy_m );
1168         } else {
1169                 // take account the effective velocity from rotation
1170                 vm_vec_unrotate(&world_rotvel_heavy_m, &heavy->phys_info.rotvel, &heavy->orient);       // heavy's world rotvel before collision
1171                 vm_vec_crossprod(&vel_from_rotvel_heavy_m, &world_rotvel_heavy_m, &ship_ship_hit_info->r_heavy);        // heavy's velocity from rotvel before collision
1172                 vel_heavy_m = vel_from_rotvel_heavy_m;
1173         }
1174
1175         // if collision from rotating submodel of heavy obj, add in vel from rotvel of submodel
1176         vector local_vel_from_submodel;
1177
1178         if (ship_ship_hit_info->submodel_rot_hit == 1) {
1179                 bool set_model = false;
1180
1181                 polymodel *pm = model_get(Ships[heavy->instance].modelnum);
1182
1183                 // be sure model is set
1184                 if (pm->submodel[ship_ship_hit_info->submodel_num].sii == NULL) {
1185                         set_model = true;
1186                         ship_model_start(heavy);
1187                 }
1188
1189                 // set point on axis of rotating submodel if not already set.
1190                 if (!pm->submodel[ship_ship_hit_info->submodel_num].sii->axis_set) {
1191                         model_init_submodel_axis_pt(pm->submodel[ship_ship_hit_info->submodel_num].sii,  Ships[heavy->instance].modelnum, ship_ship_hit_info->submodel_num);
1192                 }
1193
1194                 vector omega, axis, r_rot;
1195                 if (pm->submodel[ship_ship_hit_info->submodel_num].movement_axis == MOVEMENT_AXIS_X) {
1196                         axis = vmd_x_vector;
1197                 } else if (pm->submodel[ship_ship_hit_info->submodel_num].movement_axis == MOVEMENT_AXIS_Y) {
1198                         axis = vmd_y_vector;
1199                 } else if (pm->submodel[ship_ship_hit_info->submodel_num].movement_axis == MOVEMENT_AXIS_Z) {
1200                         axis = vmd_z_vector;
1201                 } else {
1202                         // must be one of these axes or submodel_rot_hit is incorrectly set
1203                         Int3();
1204                 }
1205
1206                 // get world rotational velocity of rotating submodel
1207                 model_find_obj_dir(&omega, &axis, heavy, ship_ship_hit_info->submodel_num);
1208                 vm_vec_scale(&omega, pm->submodel[ship_ship_hit_info->submodel_num].sii->cur_turn_rate);
1209
1210                 // world coords for r_rot
1211                 vector temp;
1212                 vm_vec_unrotate(&temp, &pm->submodel[ship_ship_hit_info->submodel_num].sii->pt_on_axis, &heavy->orient);
1213                 vm_vec_sub(&r_rot, &ship_ship_hit_info->hit_pos, &temp);
1214 //              vm_vec_rotate(&temp, &r_rot, &heavy->orient);   // to ship coords
1215
1216                 vm_vec_crossprod(&local_vel_from_submodel, &omega, &r_rot);
1217 //              vm_vec_rotate(&temp, &local_vel_from_submodel, &heavy->orient); // to ship coords
1218
1219 //              if (vm_vec_dotprod(&local_vel_from_submodel, &ship_ship_hit_info->collision_normal) > 0) {
1220 //                      nprintf(("Physics", "Rotating submodel collision - got whacked\n"));
1221 //              } else {
1222 //                      nprintf(("Physics", "Rotating submodel collision - sub got whacked from behind\n"));
1223 //              }
1224                 if (set_model) {
1225                         ship_model_stop(heavy);
1226                 }
1227         } else {
1228                 // didn't collide with submodel
1229                 vm_vec_zero(&local_vel_from_submodel);
1230         }
1231
1232         vm_vec_unrotate(&world_rotvel_light_m, &light->phys_info.rotvel, &light->orient);               // light's world rotvel before collision
1233         vm_vec_crossprod(&vel_from_rotvel_light_m, &world_rotvel_light_m, &ship_ship_hit_info->r_light);        // light's velocity from rotvel before collision
1234         vm_vec_add(&vel_light_m, &vel_from_rotvel_light_m, &ship_ship_hit_info->light_rel_vel);
1235         vm_vec_sub(&v_rel_m, &vel_light_m, &vel_heavy_m);
1236
1237         // Add in effect of rotating submodel
1238         vm_vec_sub2(&v_rel_m, &local_vel_from_submodel);
1239
1240         v_rel_normal_m = vm_vec_dotprod(&v_rel_m, &ship_ship_hit_info->collision_normal);// if less than zero, colliding contact taking place
1241                                                                                                                                                                                                         // (v_slow - v_fast) dot (n_fast)
1242
1243         if (v_rel_normal_m > 0) {
1244         //      This can happen in 2 situations.
1245         // (1) The rotational velocity is large enough to cause ships to miss.  In this case, there would most likely
1246         // have been a collision, but at a later time, so reset v_rel_normal_m 
1247
1248         //      (2) We could also have just gotten a slightly incorrect hitpos, where r dot v_rel is nearly zero.  
1249         //      In this case, we know there was a collision, but slight collision and the normal is correct, so reset v_rel_normal_m
1250         //      need a normal direction.  We can just take the -v_light normalized.             v_rel_normal_m = -v_rel_normal_m;
1251                 nprintf(("Physics", "Frame %i reset v_rel_normal_m %f Edge %i\n", Framecount, v_rel_normal_m, ship_ship_hit_info->edge_hit));
1252                 // if (v_rel_normal_m > 5)
1253                 //      Warning(LOCATION, "v_rel_normal_m > 5 %f  Get Dave A.\n", -v_rel_normal_m);
1254                 v_rel_normal_m = -v_rel_normal_m;
1255         }
1256
1257         vector  rotational_impulse_heavy, rotational_impulse_light, delta_rotvel_heavy, delta_rotvel_light;
1258         vector  delta_vel_from_delta_rotvel_heavy, delta_vel_from_delta_rotvel_light, impulse;
1259         float           impulse_mag, heavy_denom, light_denom;
1260         matrix  heavy_I_inv, light_I_inv;
1261
1262         // include a frictional collision impulse F parallel to the collision plane
1263         // F = I * sin (collision_normal, normalized v_rel_m)  [sin is ratio of v_rel_parallel_m to v_rel_m]
1264         // note:  (-) sign is needed to account for the direction of the v_rel_parallel_m
1265         float collision_speed_parallel;
1266         float parallel_mag;
1267         impulse = ship_ship_hit_info->collision_normal;
1268         vm_vec_projection_onto_plane(&v_rel_parallel_m, &v_rel_m, &ship_ship_hit_info->collision_normal);
1269         collision_speed_parallel = vm_vec_normalize_safe(&v_rel_parallel_m);
1270         parallel_mag = float(-COLLISION_FRICTION_FACTOR) * collision_speed_parallel / vm_vec_mag(&v_rel_m);
1271         vm_vec_scale_add2(&impulse, &v_rel_parallel_m, parallel_mag);
1272         
1273         // calculate the effect on the velocity of the collison point per unit impulse
1274         // first find the effect thru change in rotvel
1275         // then find the change in the cm vel
1276         if (heavy == Player_obj) {
1277                 vm_vec_zero( &delta_rotvel_heavy );
1278                 heavy_denom = 1.0f / heavy->phys_info.mass;
1279         } else {
1280                 vm_vec_crossprod(&rotational_impulse_heavy, &ship_ship_hit_info->r_heavy, &impulse);
1281                 get_I_inv(&heavy_I_inv, &heavy->phys_info.I_body_inv, &heavy->orient);
1282                 vm_vec_rotate(&delta_rotvel_heavy, &rotational_impulse_heavy, &heavy_I_inv);
1283                 vm_vec_scale(&delta_rotvel_heavy, float(COLLISION_ROTATION_FACTOR));            // hack decrease rotation (delta_rotvel)
1284                 vm_vec_crossprod(&delta_vel_from_delta_rotvel_heavy, &delta_rotvel_heavy , &ship_ship_hit_info->r_heavy);
1285                 heavy_denom = vm_vec_dotprod(&delta_vel_from_delta_rotvel_heavy, &ship_ship_hit_info->collision_normal);
1286                 if (heavy_denom < 0) {
1287                         // sanity check
1288                         heavy_denom = 0.0f;
1289                 }
1290                 heavy_denom += 1.0f / heavy->phys_info.mass;
1291         } 
1292
1293         // calculate the effect on the velocity of the collison point per unit impulse
1294         // first find the effect thru change in rotvel
1295         // then find the change in the cm vel
1296         if (light == Player_obj) {
1297                 vm_vec_zero( &delta_rotvel_light );
1298                 light_denom = 1.0f / light->phys_info.mass;
1299         } else {
1300                 vm_vec_crossprod(&rotational_impulse_light, &ship_ship_hit_info->r_light, &impulse);
1301                 get_I_inv(&light_I_inv, &light->phys_info.I_body_inv, &light->orient);
1302                 vm_vec_rotate(&delta_rotvel_light, &rotational_impulse_light, &light_I_inv);
1303                 vm_vec_scale(&delta_rotvel_light, float(COLLISION_ROTATION_FACTOR));            // hack decrease rotation (delta_rotvel)
1304                 vm_vec_crossprod(&delta_vel_from_delta_rotvel_light, &delta_rotvel_light, &ship_ship_hit_info->r_light);
1305                 light_denom = vm_vec_dotprod(&delta_vel_from_delta_rotvel_light, &ship_ship_hit_info->collision_normal);
1306                 if (light_denom < 0) {
1307                         // sanity check
1308                         light_denom = 0.0f;
1309                 }
1310                 light_denom += 1.0f / light->phys_info.mass;
1311         } 
1312
1313         // calculate the necessary impulse to achieved the desired relative velocity after the collision
1314         // update damage info in mc
1315         impulse_mag = -(1.0f + coeff_restitution)*v_rel_normal_m / (heavy_denom + light_denom);
1316         ship_ship_hit_info->impulse = impulse_mag;
1317         if (impulse_mag < 0) {
1318                 nprintf(("Physics", "negative impulse mag -- Get Dave A if not Descent Physics\n"));
1319                 impulse_mag = -impulse_mag;
1320         }
1321         
1322         // update the physics info structs for heavy and light objects
1323         // since we have already calculated delta rotvel for heavy and light in world coords
1324         // physics should not have to recalculate this, just change into body coords (done in collide_whack)
1325         vm_vec_scale(&impulse, impulse_mag);
1326         //Assert(impulse_mag < 20e6);
1327         vm_vec_scale(&delta_rotvel_light, impulse_mag); 
1328         physics_collide_whack(&impulse, &delta_rotvel_light, &light->phys_info, &light->orient);
1329         vm_vec_negate(&impulse);
1330         vm_vec_scale(&delta_rotvel_heavy, -impulse_mag);
1331         physics_collide_whack(&impulse, &delta_rotvel_heavy, &heavy->phys_info, &heavy->orient);
1332
1333         // Find final positions
1334         // We will try not to worry about the left over time in the frame
1335         // heavy's position unchanged by collision
1336         // light's position is heavy's position plus relative position from heavy
1337         vm_vec_add(&light->pos, &heavy->pos, &ship_ship_hit_info->light_collision_cm_pos);
1338
1339         // Try to move each body back to its position just before collision occured to prevent interpenetration
1340         // Move away in direction of light and away in direction of normal
1341         vector direction_light; // direction light is moving relative to heavy
1342         vm_vec_sub(&direction_light, &ship_ship_hit_info->light_rel_vel, &local_vel_from_submodel);
1343         vm_vec_normalize_safe(&direction_light);
1344
1345         Assert( !vm_is_vec_nan(&direction_light) );
1346         vm_vec_scale_add2(&heavy->pos, &direction_light,  0.2f * light->phys_info.mass / (heavy->phys_info.mass + light->phys_info.mass));
1347         vm_vec_scale_add2(&light->pos, &direction_light, -0.2f * heavy->phys_info.mass / (heavy->phys_info.mass + light->phys_info.mass));
1348         vm_vec_scale_add2(&heavy->pos, &ship_ship_hit_info->collision_normal, -0.1f * light->phys_info.mass / (heavy->phys_info.mass + light->phys_info.mass));
1349         vm_vec_scale_add2(&light->pos, &ship_ship_hit_info->collision_normal,  0.1f * heavy->phys_info.mass / (heavy->phys_info.mass + light->phys_info.mass));
1350
1351         // restore mass in case of special cruuiser / asteroid collision
1352         if (special_cruiser_asteroid_collision) {
1353                 if (cruiser_light) {
1354                         light->phys_info.mass = copy_mass;
1355                 } else {
1356                         heavy->phys_info.mass = copy_mass;
1357                 }
1358         }
1359 }
1360
1361
1362 // ------------------------------------------------------------------------------------------------
1363 //      get_I_inv()
1364 //
1365 //              input:  I_inv_body      =>              inverse moment of inertia matrix in body coordinates
1366 //                                      orient          =>              orientation matrix
1367 //
1368 //              output: I_inv                   =>              inverse moment of inertia matrix in world coordinates
1369 // ------------------------------------------------------------------------------------------------
1370 //
1371 // calculates the inverse moment of inertia matrix from the body matrix and oreint matrix
1372 //
1373 void get_I_inv (matrix* I_inv, matrix* I_inv_body, matrix* orient)
1374 {
1375         matrix Mtemp1, Mtemp2;
1376         // I_inv = (Rt)(I_inv_body)(R)
1377         // This is opposite to what is commonly seen in books since we are rotating coordianates axes 
1378         // which is equivalent to rotating in the opposite direction (or transpose)
1379
1380         vm_matrix_x_matrix(&Mtemp1, I_inv_body, orient);
1381         vm_copy_transpose_matrix(&Mtemp2, orient);
1382         vm_matrix_x_matrix(I_inv, &Mtemp2, &Mtemp1);
1383 }
1384
1385 #define PLANET_DAMAGE_SCALE     4.0f
1386 #define PLANET_DAMAGE_RANGE     3               //      If within this factor of radius, apply damage.
1387
1388 fix     Last_planet_damage_time = 0;
1389 extern void hud_start_text_flash(char *txt);
1390
1391 //      Procss player_ship:planet damage.
1392 //      If within range of planet, apply damage to ship.
1393 void mcp_1(object *player_objp, object *planet_objp)
1394 {
1395         float   planet_radius;
1396         float   dist;
1397
1398         planet_radius = planet_objp->radius;
1399         dist = vm_vec_dist_quick(&player_objp->pos, &planet_objp->pos);
1400
1401         if (dist > planet_radius*PLANET_DAMAGE_RANGE)
1402                 return;
1403
1404         ship_apply_global_damage( player_objp, planet_objp, NULL, PLANET_DAMAGE_SCALE * flFrametime * (float)pow((planet_radius*PLANET_DAMAGE_RANGE)/dist, 3.0f) );
1405
1406         if ((Missiontime - Last_planet_damage_time > F1_0) || (Missiontime < Last_planet_damage_time)) {
1407                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Too close to planet.  Taking damage!", 465));
1408                 Last_planet_damage_time = Missiontime;
1409                 snd_play_3d( &Snds[SND_ABURN_ENGAGE], &player_objp->pos, &View_position );
1410         }
1411
1412 }
1413
1414 //      Return true if *objp is a planet, else return false.
1415 //      Hack: Just checking first six letters of name.
1416 int is_planet(object *objp)
1417 {
1418         return (strnicmp(Ships[objp->instance].ship_name, NOX("planet"), 6) == 0);
1419 }
1420
1421
1422 //      If exactly one of these is a planet and the other is a player ship, do something special.
1423 //      Return true if this was a ship:planet (or planet_ship) collision and we processed it.
1424 //      Else return false.
1425 int maybe_collide_planet (object *obj1, object *obj2)
1426 {
1427         ship_info       *sip1, *sip2;
1428
1429         sip1 = &Ship_info[Ships[obj1->instance].ship_info_index];
1430         sip2 = &Ship_info[Ships[obj2->instance].ship_info_index];
1431
1432         if (sip1->flags & SIF_PLAYER_SHIP) {
1433                 if (is_planet(obj2)) {
1434                         mcp_1(obj1, obj2);
1435                         return 1;
1436                 }
1437         } else if (is_planet(obj1)) {
1438                 if (sip2->flags & SIF_PLAYER_SHIP) {
1439                         mcp_1(obj2, obj1);
1440                         return 1;
1441                 }
1442         }
1443
1444         return 0;
1445 }
1446
1447 //      Given a global point and an object, get the quadrant number the point belongs to.
1448 int get_ship_quadrant_from_global(vector *global_pos, object *objp)
1449 {
1450         vector  tpos;
1451         vector  rotpos;
1452
1453         vm_vec_sub(&tpos, global_pos, &objp->pos);
1454         vm_vec_rotate(&rotpos, &tpos, &objp->orient);
1455         return get_quadrant(&rotpos);
1456 }
1457
1458 #define MIN_REL_SPEED_FOR_LOUD_COLLISION                50              // relative speed of two colliding objects at which we play the "loud" collide sound
1459
1460 void collide_ship_ship_sounds_init()
1461 {
1462         Player_collide_sound = -1;
1463         AI_collide_sound = -1;
1464         Player_collide_shield_sound = -1;
1465         AI_collide_shield_sound = -1;
1466 }
1467
1468 // determine what sound to play when two ships collide
1469 void collide_ship_ship_do_sound(vector *world_hit_pos, object *A, object *B, int player_involved)
1470 {
1471         vector  rel_vel;
1472         float           rel_speed;
1473         int             light_collision=0;
1474                         
1475         vm_vec_sub(&rel_vel, &A->phys_info.desired_vel, &B->phys_info.desired_vel);
1476         rel_speed = vm_vec_mag_quick(&rel_vel);
1477
1478         if ( rel_speed > MIN_REL_SPEED_FOR_LOUD_COLLISION ) {
1479                 snd_play_3d( &Snds[SND_SHIP_SHIP_HEAVY], world_hit_pos, &View_position );
1480         } else {
1481                 light_collision=1;
1482                 if ( player_involved ) {
1483                         if ( !snd_is_playing(Player_collide_sound) ) {
1484                                 Player_collide_sound = snd_play_3d( &Snds[SND_SHIP_SHIP_LIGHT], world_hit_pos, &View_position );
1485                         }
1486                 } else {
1487                         if ( !snd_is_playing(AI_collide_sound) ) {
1488                                 AI_collide_sound = snd_play_3d( &Snds[SND_SHIP_SHIP_LIGHT], world_hit_pos, &View_position );
1489                         }
1490                 }
1491         }
1492
1493         // maybe play a "shield" collision sound overlay if appropriate
1494         if ( (get_shield_strength(A) > 5) || (get_shield_strength(B) > 5) ) {
1495                 float vol_scale=1.0f;
1496                 if ( light_collision ) {
1497                         vol_scale=0.7f;
1498                 }
1499
1500                 if ( player_involved ) {
1501                         if ( !snd_is_playing(Player_collide_sound) ) {
1502                                 Player_collide_shield_sound = snd_play_3d( &Snds[SND_SHIP_SHIP_SHIELD], world_hit_pos, &View_position );
1503                         }
1504                 } else {
1505                         if ( !snd_is_playing(Player_collide_sound) ) {
1506                                 AI_collide_shield_sound = snd_play_3d( &Snds[SND_SHIP_SHIP_SHIELD], world_hit_pos, &View_position );
1507                         }
1508                 }
1509         }
1510 }
1511
1512 //      obj1 and obj2 collided.
1513 //      If different teams, kamikaze bit set and other ship is large, auto-explode!
1514 void do_kamikaze_crash(object *obj1, object *obj2)
1515 {
1516         ai_info *aip1, *aip2;
1517         ship            *ship1, *ship2;
1518
1519         ship1 = &Ships[obj1->instance];
1520         ship2 = &Ships[obj2->instance];
1521
1522         aip1 = &Ai_info[ship1->ai_index];
1523         aip2 = &Ai_info[ship2->ai_index];
1524
1525         if (ship1->team != ship2->team) {
1526                 if (aip1->ai_flags & AIF_KAMIKAZE) {
1527                         if (Ship_info[ship2->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1528                                 obj1->hull_strength = KAMIKAZE_HULL_ON_DEATH;
1529                                 set_shield_strength(obj1, 0.0f);
1530                         }
1531                 } if (aip2->ai_flags & AIF_KAMIKAZE) {
1532                         if (Ship_info[ship1->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1533                                 obj2->hull_strength = KAMIKAZE_HULL_ON_DEATH;
1534                                 set_shield_strength(obj2, 0.0f);
1535                         }
1536                 }
1537         }
1538 }
1539
1540 // response when hit by fast moving cap ship
1541 void maybe_push_little_ship_from_fast_big_ship(object *big, object *small, float impulse, vector *normal)
1542 {
1543         // Move player out of the way of a BIG|HUGE ship warping in or out
1544         if (Ship_info[Ships[big->instance].ship_info_index].flags & (SIF_CAPITAL|SIF_SUPERCAP)) {
1545                 if (Ship_info[Ships[small->instance].ship_info_index].flags & (SIF_SMALL_SHIP)) {
1546                         float big_speed = vm_vec_mag_quick(&big->phys_info.vel);
1547                         if (big_speed > 3*big->phys_info.max_vel.xyz.z) {
1548                                 // push player away in direction perp to forward of big ship
1549                                 // get perp vec
1550                                 vector temp, perp;
1551                                 vm_vec_sub(&temp, &small->pos, &big->pos);
1552                                 vm_vec_scale_add(&perp, &temp, &big->orient.v.fvec, -vm_vec_dotprod(&temp, &big->orient.v.fvec));
1553                                 vm_vec_normalize_quick(&perp);
1554
1555                                 // don't drive into sfc we just collided with
1556                                 if (vm_vec_dotprod(&perp, normal) < 0) {
1557                                         vm_vec_negate(&perp);
1558                                 }
1559
1560                                 // get magnitude of added perp vel
1561                                 float added_perp_vel_mag = impulse / small->phys_info.mass;
1562
1563                                 // add to vel and ramp vel
1564                                 vm_vec_scale_add2(&small->phys_info.vel, &perp, added_perp_vel_mag);
1565                                 vm_vec_rotate(&small->phys_info.prev_ramp_vel, &small->phys_info.vel, &small->orient);
1566                         }
1567                 }
1568         }
1569 }
1570
1571 // Checks ship-ship collisions.  pair->a and pair->b are ships.
1572 // Returns 1 if all future collisions between these can be ignored
1573 // Always returns 0, since two ships can always collide unless one (1) dies or (2) warps out.
1574 int collide_ship_ship( obj_pair * pair )
1575 {
1576         int     player_involved;
1577         float dist;
1578         object *A = pair->a;
1579         object *B = pair->b;
1580
1581         // Don't check collisions for warping out player if past stage 1.
1582         if ( Player->control_mode > PCM_WARPOUT_STAGE1) {
1583                 if ( A == Player_obj ) return 0;
1584                 if ( B == Player_obj ) return 0;
1585         }
1586
1587         if ( A->type == OBJ_WAYPOINT ) return 1;
1588         if ( B->type == OBJ_WAYPOINT ) return 1;
1589         
1590         Assert( A->type == OBJ_SHIP );
1591         Assert( B->type == OBJ_SHIP );
1592
1593         // If the player is one of the two colliding ships, flag this... it is used in
1594         // several places this function.
1595         if ( A == Player_obj || B == Player_obj ) {
1596                 player_involved = 1;
1597         } else {
1598                 player_involved = 0;
1599         }
1600
1601         dist = vm_vec_dist( &A->pos, &B->pos );
1602
1603         //      If one of these is a planet, do special stuff.
1604         if (maybe_collide_planet(A, B))
1605                 return 0;
1606
1607         if ( dist < A->radius + B->radius )     {
1608                 int             hit;
1609
1610                 object  *HeavyOne, *LightOne;
1611                 // if two objects have the same mass, make the one with the larger pointer address the HeavyOne.
1612                 if ( fl_abs(A->phys_info.mass - B->phys_info.mass) < 1 ) {
1613                         if (A > B) {
1614                                 HeavyOne = A;
1615                                 LightOne = B;
1616                         } else {
1617                                 HeavyOne = B;
1618                                 LightOne = A;
1619                         }
1620                 } else {
1621                         if (A->phys_info.mass > B->phys_info.mass) {
1622                                 HeavyOne = A;
1623                                 LightOne = B;
1624                         } else {
1625                                 HeavyOne = B;
1626                                 LightOne = A;
1627                         }
1628                 }
1629
1630                 // create ship_ship_or_debris_hit
1631                 // inputs       obj A, obj B
1632                 // outputs      hitpos, impulse (for damage), shield hit tri (for quadrant)
1633                 collision_info_struct ship_ship_hit_info;
1634                 memset(&ship_ship_hit_info, -1, sizeof(collision_info_struct));
1635
1636                 ship_ship_hit_info.heavy = HeavyOne;            // heavy object, generally slower moving
1637                 ship_ship_hit_info.light = LightOne;            // light object, generally faster moving
1638
1639                 vector world_hit_pos;
1640
1641                 hit = ship_ship_check_collision(&ship_ship_hit_info, &world_hit_pos);
1642
1643 /*              if ((hitpos.x == FastOne->pos.x) && (hitpos.y == FastOne->pos.y) && (hitpos.z == FastOne->pos.z))
1644                         Int3();
1645                 if ((hitpos.x == SlowOne->pos.x) && (hitpos.y == SlowOne->pos.y) && (hitpos.z == SlowOne->pos.z))
1646                         Int3();
1647                 if ((A == FastOne) && (hitpos.x == FastOne->last_pos.x) && (hitpos.y == FastOne->last_pos.y) && (hitpos.z == FastOne->last_pos.z))
1648                         Int3();
1649 */
1650                 if ( hit ) {
1651                         float           damage;
1652
1653                         if ( Player->control_mode == PCM_WARPOUT_STAGE1 )       {
1654                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
1655                                 HUD_printf(XSTR( "Warpout sequence aborted.", 466));
1656                         }
1657
1658 //                      vector  rel_vec;
1659
1660                         //      Hack, following line would cause a null vector in vm_vec_normalized_dir below.  This should prevent it.
1661                         // FastOne->pos = FastOne->last_pos;
1662 //                      vm_vec_scale_add2(&FastOne->pos, &FastOne->last_pos, 0.01f);
1663 //                      vm_vec_scale(&FastOne->pos, 1.0f/1.01f);
1664
1665                         //      Amount of damage done by a collision changed by MK, 11/19/96.
1666                         //      Now uses relative velocity and ignores shield of objects.  No reason
1667                         //      smacking into a capital ship should damage you 1000 times as much as
1668                         //      smacking into a fighter.  Depends on your velocity and whether you
1669                         //      smack headon or barely glance.
1670
1671                         // Amount of damage done by a collision changed by DA 08/26/97.
1672                         // Amount of damage now depends on impulse imparted by a collision,
1673                         // scaled by max momentum of a ship, so ramming full speed head on into an
1674                         // immovable object should kill you.
1675 //                      vm_vec_sub(&rel_vec, &B->phys_info.vel, &A->phys_info.vel);
1676 //                      damage = vm_vec_mag_quick(&rel_vec);
1677
1678 //                      float impulse = 0.0f;           // HACK!!! Should be something, right?
1679                         damage = 0.005f * ship_ship_hit_info.impulse;   //      Cut collision-based damage in half.
1680                         //      Decrease heavy damage by 2x.
1681                         if (damage > 5.0f){
1682                                 damage = 5.0f + (damage - 5.0f)/2.0f;
1683                         }
1684
1685                         do_kamikaze_crash(A, B);
1686
1687                         if (ship_ship_hit_info.impulse > 0) {
1688                                 float   q;
1689
1690                                 q = vm_vec_dist_quick(&A->pos, &B->pos) / (A->radius + B->radius);
1691
1692 #ifndef NDEBUG
1693 //                              //nprintf(("AI", "Frame %i: %s and %s, dam=%7.2f.  dist/rad=%5.2f. Zeroing.\n", Framecount, Ships[A->instance].ship_name, Ships[B->instance].ship_name, damage, q));
1694 //                              if (damage > 5.0f) {
1695 //                                      if ( player_involved ) {
1696 //                                              object  *other_objp;
1697 //                                              float           dot;
1698 //                                              vector  v2h;
1699 //
1700 //                                              if (A == Player_obj)
1701 //                                                      other_objp = B;
1702 //                                              else
1703 //                                                      other_objp = A;
1704 //
1705 //                                              vm_vec_normalized_dir(&v2h, &ship_ship_hit_info.hit_pos, &Player_obj->pos);
1706 //                                              dot = vm_vec_dot(&Player_obj->orient.fvec, &v2h);
1707 //                                      //      HUD_printf("Collision %s: %i%%. (dot=%5.2f), dist ratio=%5.2f", Ships[other_objp->instance].ship_name, (int) (100.0f * damage/Ship_info[Ships[Player_obj->instance].ship_info_index].initial_hull_strength), dot,
1708 //                                      //              vm_vec_dist_quick(&Player_obj->pos, &other_objp->pos) / (Player_obj->radius + other_objp->radius));
1709 //                                      }
1710 //                              }
1711 #endif
1712                                 if ( player_involved ) {                                        
1713                                         hud_start_text_flash(XSTR("Collision", 1431), 2000);
1714                                 }
1715                         }
1716                         //      damage *= (max_shields of fastest) / (max_impulse_of_fastest)
1717                         // possibly calculate damage both ways and use largest/smallest/avg?
1718
1719 //                      vm_vec_add(&world_hit_pos, &ship_ship_hit_info.heavy->pos, &ship_ship_hit_info.hit_pos);
1720
1721                         collide_ship_ship_do_sound(&world_hit_pos, A, B, player_involved);
1722
1723                         // check if we should do force feedback stuff
1724                         if (player_involved && (ship_ship_hit_info.impulse > 0)) {
1725                                 float scaler;
1726                                 vector v;
1727
1728                                 scaler = -ship_ship_hit_info.impulse / Player_obj->phys_info.mass * 300;
1729                                 vm_vec_copy_normalize(&v, &world_hit_pos);
1730                                 joy_ff_play_vector_effect(&v, scaler);
1731                         }
1732
1733                         //mprintf(("Ship:Ship damage = %7.3f\n", speed));
1734                         #ifndef NDEBUG
1735                         if ( !Collide_friendly ) {
1736                                 if ( Ships[A->instance].team == Ships[B->instance].team ) {
1737                                         vector  collision_vec, right_angle_vec;
1738                                         vm_vec_normalized_dir(&collision_vec, &ship_ship_hit_info.hit_pos, &A->pos);
1739                                         if (vm_vec_dot(&collision_vec, &A->orient.v.fvec) > 0.999f){
1740                                                 right_angle_vec = A->orient.v.rvec;
1741                                         } else {
1742                                                 vm_vec_cross(&right_angle_vec, &A->orient.v.uvec, &collision_vec);
1743                                         }
1744
1745                                         vm_vec_scale_add2( &A->phys_info.vel, &right_angle_vec, +2.0f);
1746                                         vm_vec_scale_add2( &B->phys_info.vel, &right_angle_vec, -2.0f);
1747                                         //nprintf(("AI", "A: [%6.3f %6.3f %6.3f] B: [%6.3f %6.3f %6.3f]\n", A->phys_info.vel.x, A->phys_info.vel.y, A->phys_info.vel.z, B->phys_info.vel.x, B->phys_info.vel.y, B->phys_info.vel.z));
1748
1749                                         return 0;
1750                                 }
1751                         }
1752                         #endif
1753
1754                         // nprintf(("AI", "Ship:ship collision: %s and %s.\n", Ships[A->instance].ship_name, Ships[B->instance].ship_name));
1755
1756                         //      Scale damage based on skill level for player.
1757                         if ((LightOne->flags & OF_PLAYER_SHIP) || (HeavyOne->flags & OF_PLAYER_SHIP)) {
1758                                 damage *= (float) (Game_skill_level*Game_skill_level+1)/(NUM_SKILL_LEVELS+1);
1759                         } else if (Ships[LightOne->instance].team == Ships[HeavyOne->instance].team) {
1760                                 //      Decrease damage if non-player ships and not large.
1761                                 //      Looks dumb when fighters are taking damage from bumping into each other.
1762                                 if ((LightOne->radius < 50.0f) && (HeavyOne->radius <50.0f)) {
1763                                         damage /= 4.0f;
1764                                 }
1765                         }
1766                         
1767                         float dam2 = (100.0f * damage/LightOne->phys_info.mass);
1768
1769                         int     quadrant_num = get_ship_quadrant_from_global(&world_hit_pos, ship_ship_hit_info.heavy);
1770                         //nprintf(("AI", "Ship %s hit in quad #%i\n", Ships[ship_ship_hit_info.heavy->instance].ship_name, quadrant_num));
1771                         if ((ship_ship_hit_info.heavy->flags & OF_NO_SHIELDS) || !ship_is_shield_up(ship_ship_hit_info.heavy, quadrant_num) ) {
1772                                 quadrant_num = -1;
1773                         }
1774
1775                         ship_apply_local_damage(ship_ship_hit_info.heavy, ship_ship_hit_info.light, &world_hit_pos, 100.0f * damage/HeavyOne->phys_info.mass, quadrant_num, CREATE_SPARKS, ship_ship_hit_info.submodel_num, &ship_ship_hit_info.collision_normal);
1776                         hud_shield_quadrant_hit(ship_ship_hit_info.heavy, quadrant_num);
1777
1778                         //nprintf(("AI", "Ship %s hit in quad #%i\n", Ships[ship_ship_hit_info.light->instance].ship_name, quadrant_num));
1779                         // don't draw sparks (using sphere hitpos)
1780                         ship_apply_local_damage(ship_ship_hit_info.light, ship_ship_hit_info.heavy, &world_hit_pos, dam2, MISS_SHIELDS, NO_SPARKS, -1, &ship_ship_hit_info.collision_normal);
1781                         hud_shield_quadrant_hit(ship_ship_hit_info.light, quadrant_num);
1782
1783                         maybe_push_little_ship_from_fast_big_ship(ship_ship_hit_info.heavy, ship_ship_hit_info.light, ship_ship_hit_info.impulse, &ship_ship_hit_info.collision_normal);
1784                         //nprintf(("AI", "Damage to %s = %7.3f\n", Ships[LightOne->instance].ship_name, dam2));
1785                         return 0;
1786                 }                                       
1787         } else {
1788                 // estimate earliest time at which pair can hit
1789
1790                 // cap ships warping in/out can exceed ship's expected velocity
1791                 // if ship is warping in, in stage 1, its velocity is 0, so make ship try to collide next frame
1792                 int sif_a_flags, sif_b_flags;
1793                 sif_a_flags = Ship_info[Ships[A->instance].ship_info_index].flags;
1794                 sif_b_flags = Ship_info[Ships[B->instance].ship_info_index].flags;
1795
1796                 // if ship is huge and warping in or out
1797                 if ( (Ships[A->instance].flags & SF_ARRIVING_STAGE_1) && (sif_a_flags & (SIF_HUGE_SHIP))
1798                         ||(Ships[B->instance].flags & SF_ARRIVING_STAGE_1) && (sif_b_flags & (SIF_HUGE_SHIP)) ) {
1799                         pair->next_check_time = timestamp(0);   // check next time
1800                         return 0;
1801                 }
1802
1803                 // get max of (1) max_vel.z, (2) 10, (3) afterburner_max_vel.z, (4) vel.z (for warping in ships exceeding expected max vel)
1804                 float shipA_max_speed, shipB_max_speed, time;
1805
1806                 // get shipA max speed
1807                 if (ship_is_beginning_warpout_speedup(A)) {
1808                         shipA_max_speed = max(ship_get_max_speed(&Ships[A->instance]), ship_get_warp_speed(A));
1809                 } else {
1810                         shipA_max_speed = ship_get_max_speed(&Ships[A->instance]);
1811                 }
1812
1813                 // Maybe warping in or finished warping in with excessive speed
1814                 shipA_max_speed = max(shipA_max_speed, vm_vec_mag(&A->phys_info.vel));
1815                 shipA_max_speed = max(shipA_max_speed, 10.0f);
1816
1817                 // get shipB max speed
1818                 if (ship_is_beginning_warpout_speedup(B)) {
1819                         shipB_max_speed = max(ship_get_max_speed(&Ships[B->instance]), ship_get_warp_speed(B));
1820                 } else {
1821                         shipB_max_speed = ship_get_max_speed(&Ships[B->instance]);
1822                 }
1823
1824                 // Maybe warping in or finished warping in with excessive speed
1825                 shipB_max_speed = max(shipB_max_speed, vm_vec_mag(&B->phys_info.vel));
1826                 shipB_max_speed = max(shipB_max_speed, 10.0f);
1827
1828                 time = 1000.0f * (dist - A->radius - B->radius) / (shipA_max_speed + shipB_max_speed);
1829                 time -= 200.0f;         // allow one frame slow frame at ~5 fps
1830
1831                 if (time > 0) {
1832                         pair->next_check_time = timestamp( fl2i(time) );
1833                 } else {
1834                         pair->next_check_time = timestamp(0);   // check next time
1835                 }
1836         }
1837         
1838         return 0;
1839 }
1840
1841 void collect_ship_ship_physics_info(object *heavy, object *light, mc_info *mc_info, collision_info_struct *ship_ship_hit_info)
1842 {
1843         // slower moving object [A] is checked at its final position (polygon and position is found on obj)
1844         // faster moving object [B] is reduced to a point and a ray is drawn from its last_pos to pos
1845         // collision code returns hit position and normal on [A]
1846
1847         // estimate location on B that contacts A
1848         // first find orientation of B relative to the normal it collides against.
1849         // then find an approx hit location using the position hit on the bounding box
1850
1851         vector *r_heavy = &ship_ship_hit_info->r_heavy;
1852         vector *r_light = &ship_ship_hit_info->r_light;
1853         vector *heavy_collide_cm_pos = &ship_ship_hit_info->heavy_collision_cm_pos;
1854         vector *light_collide_cm_pos = &ship_ship_hit_info->light_collision_cm_pos;
1855
1856         float core_rad = model_get_core_radius( Ships[light->instance].modelnum );
1857
1858         // get info needed for ship_ship_collision_physics
1859         Assert(mc_info->hit_dist > 0);
1860
1861         // get light_collide_cm_pos
1862         if ( !ship_ship_hit_info->submodel_rot_hit ) {
1863                 vector displacement;
1864                 vm_vec_sub(&displacement, mc_info->p1, mc_info->p0);
1865
1866                 *light_collide_cm_pos = *mc_info->p0;
1867                 vm_vec_scale_add2(light_collide_cm_pos, &displacement, ship_ship_hit_info->hit_time);
1868         }
1869         
1870         // get r_light
1871         vm_vec_sub(r_light, &ship_ship_hit_info->hit_pos, light_collide_cm_pos);
1872
1873 //      Assert(vm_vec_mag(&r_light) > core_rad - 0.1);
1874         float mag = float(fabs(vm_vec_mag(r_light) - core_rad));
1875         if (mag > 0.1) {
1876                 nprintf(("Physics", "Framecount: %i |r_light - core_rad| > 0.1)\n", Framecount));
1877         }
1878
1879         if (ship_ship_hit_info->edge_hit) {
1880         // For an edge hit, just take the closest valid plane normal as the collision normal
1881                 vm_vec_copy_normalize(&ship_ship_hit_info->collision_normal, r_light);
1882                 vm_vec_negate(&ship_ship_hit_info->collision_normal);
1883         }
1884
1885         // r dot n may not be negative if hit by moving model parts.
1886         float dot = vm_vec_dotprod( r_light, &ship_ship_hit_info->collision_normal );
1887         if ( dot > 0 )
1888         {
1889                 nprintf(("Physics", "Framecount: %i r dot normal  > 0\n", Framecount, dot));
1890         }
1891
1892         vm_vec_zero(heavy_collide_cm_pos);
1893
1894         float q = vm_vec_dist(heavy_collide_cm_pos, light_collide_cm_pos) / (heavy->radius + core_rad);
1895         if (q > 1.0f) {
1896                 nprintf(("Physics", "Warning: q = %f.  Supposed to be <= 1.0.\n", q));
1897         }
1898
1899         *r_heavy = ship_ship_hit_info->hit_pos;
1900
1901         // fill in ship_ship_hit_info
1902 //      ship_ship_hit_info->heavy_collision_cm_pos = heavy_collide_cm_pos;
1903 //      ship_ship_hit_info->light_collision_cm_pos = light_collide_cm_pos;
1904 //      ship_ship_hit_info->r_heavy = r_heavy;
1905 //      ship_ship_hit_info->r_light = r_light;
1906
1907 // sphere_sphere_case_handled separately
1908 #ifdef COLLIDE_DEBUG
1909 nprintf(("Physics", "Frame: %i %s info: last_pos: [%4.1f, %4.1f, %4.1f], collide_pos: [%4.1f, %4.1f %4.1f] vel: [%4.1f, %4.1f %4.1f]\n",
1910         Framecount, Ships[heavy->instance].ship_name, heavy->last_pos.x, heavy->last_pos.y, heavy->last_pos.z,
1911         heavy_collide_cm_pos.x, heavy_collide_cm_pos.y, heavy_collide_cm_pos.z,
1912         heavy->phys_info.vel.x, heavy->phys_info.vel.y, heavy->phys_info.vel.z));
1913
1914 nprintf(("Physics", "Frame: %i %s info: last_pos: [%4.1f, %4.1f, %4.1f], collide_pos: [%4.1f, %4.1f, %4.1f] vel: [%4.1f, %4.1f, %4.1f]\n",
1915         Framecount, Ships[light->instance].ship_name, light->last_pos.x, light->last_pos.y, light->last_pos.z,
1916         light_collide_cm_pos.x, light_collide_cm_pos.y, light_collide_cm_pos.z,
1917         light->phys_info.vel.x, light->phys_info.vel.y, light->phys_info.vel.z));
1918 #endif
1919
1920 }       
1921