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