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