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