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