2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Object/CollideShipShip.cpp $
15 * Routines to detect collisions and do physics, damage, etc for ships and ships
18 * Revision 1.6 2003/05/25 02:30:43 taylor
21 * Revision 1.5 2002/06/17 06:33:10 relnev
22 * ryan's struct patch for gcc 2.95
24 * Revision 1.4 2002/06/09 04:41:24 relnev
25 * added copyright header
27 * Revision 1.3 2002/06/01 07:12:33 relnev
28 * a few NDEBUG updates.
30 * removed a few warnings.
32 * Revision 1.2 2002/05/07 03:16:48 theoddone33
33 * The Great Newline Fix
35 * Revision 1.1.1.1 2002/05/03 03:28:10 root
39 * 31 9/01/99 5:40p Andsager
40 * Collision resolution between small and CAP during warp
42 * 30 8/24/99 8:55p Dave
43 * Make sure nondimming pixels work properly in tech menu.
45 * 29 7/29/99 12:11a Andsager
48 * 28 7/28/99 11:23p Andsager
49 * Try a different strategy to resolve collisions between ships on same
52 * 27 7/24/99 1:54p Dave
53 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
56 * 26 7/15/99 5:41p Andsager
59 * 25 7/15/99 9:20a Andsager
60 * FS2_DEMO initial checkin
62 * 24 7/12/99 11:49a Andsager
63 * Really fix collision warp-in bug.
65 * 23 7/09/99 5:54p Dave
66 * Seperated cruiser types into individual types. Added tons of new
67 * briefing icons. Campaign screen.
69 * 22 7/08/99 5:49p Andsager
70 * Fixed bug colliding with just warped in Cap ship
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.
76 * 20 4/23/99 12:01p Johnson
79 * 19 4/20/99 3:45p Andsager
80 * Modify ship_apply_local_damage to take a collision normal
82 * 18 4/19/99 12:21p Johnson
83 * Allow ships with invisible polygons which do not collide
85 * 17 3/20/99 2:54p Andsager
86 * Fix collision for cap ships warping in - speed is much greater than
89 * 16 2/05/99 11:07a Andsager
90 * Make cap ships not get shoved around with asteroid collisions
92 * 15 2/02/99 1:18p Andsager
93 * Modify asteroid/cruiser collisions so cruisers don't get bashed so
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.
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
105 * 12 12/03/98 3:14p Andsager
106 * Check in code that checks rotating submodel actually has ship subsystem
108 * 11 11/20/98 2:22p Andsager
109 * Change collision separation h2l_vec
111 * 10 11/19/98 11:47p Andsager
112 * Fix possible divide by zero bug.
114 * 9 11/19/98 11:08p Andsager
115 * Check in of physics and collision detection of rotating submodels
117 * 8 11/13/98 5:06p Johnson
118 * Fix Kulas collision bug
120 * 7 11/05/98 5:55p Dave
121 * Big pass at reducing #includes
123 * 6 10/23/98 1:11p Andsager
124 * Make ship sparks emit correctly from rotating structures.
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.
131 * 4 10/16/98 1:22p Andsager
132 * clean up header files
134 * 3 10/13/98 9:29a Dave
135 * Started neatening up freespace.h. Many variables renamed and
136 * reorganized. Added AlphaColors.[h,cpp]
138 * 2 10/07/98 10:53a Dave
141 * 1 10/07/98 10:50a Dave
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
149 * 104 5/24/98 11:36p Mike
150 * Comment out no-optimize pragmas.
152 * 103 5/24/98 10:50p Mike
153 * Fix problem with ships with propagating explosions not being able to
156 * 102 5/21/98 3:48p Lawrance
157 * prevent player from entering friendly ship docking bays
159 * 101 5/19/98 2:19p Mike
160 * Don't do collision detection between small ship emerging or departing
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!
167 * 99 5/13/98 11:34p Mike
168 * Model caching system.
170 * 98 5/10/98 11:11p Lawrance
171 * Allow ships to collide if in second stage of arrival
173 * 97 5/08/98 5:25p Lawrance
174 * Don't allow collision sounds too play over each so much
176 * 96 5/08/98 3:51p Allender
177 * temporary fix for support ships on clients in multiplayer
179 * 95 5/08/98 11:22a Allender
180 * fix ingame join trouble. Small messaging fix. Enable collisions for
183 * 94 5/07/98 12:24a Hoffoss
184 * Finished up sidewinder force feedback support.
186 * 93 5/03/98 5:40p Mike
187 * Debug info for trapping player collisions.
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
193 * 91 4/28/98 1:00a Andsager
194 * Add collision sanity check
196 * 90 4/28/98 12:23a Chad
197 * removed call which prevented same team coliisions from happening in
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.
205 * 88 4/23/98 4:42p Mike
206 * Support invisible polygons that only enemy ships bump into.
208 * 87 4/20/98 12:36a Mike
209 * Make team vs. team work when player is hostile. Several targeting
212 * 86 4/12/98 2:02p Mike
213 * Make small ships avoid big ships.
214 * Turn on Collide_friendly flag.
216 * 85 4/08/98 4:01p Andsager
217 * Removed assert in calculate_ship_ship_collisions_physics()
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.
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.
229 * 82 3/25/98 2:43p Andsager
230 * comment out asserts
232 * 81 3/25/98 1:19p Mike
233 * Comment out unimportant assert.
235 * 80 3/25/98 10:43a Andsager
236 * Hack for ship_asteroid collisions when erroneous relative_vel >150
238 * 79 3/25/98 12:05a Mike
239 * Comment out line to make sm1-06a playable. Reported to DaveA.
241 * 78 3/23/98 9:20a Andsager
242 * Remove all velocity updates in object code.
244 * 77 3/17/98 12:49a Mike
245 * Improved kamikaze behavior.
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.
251 * 75 3/13/98 12:57p Mike
252 * Remove an SDL_assert that was easy to trip with time compressed 8x.
254 * 74 3/09/98 12:58a Andsager
255 * Don't check asteroids very large displacements in collisions, since
258 * 73 3/09/98 12:16a Andsager
260 * 72 2/22/98 2:48p John
261 * More String Externalization Classification
263 * 71 2/20/98 8:32p Lawrance
264 * Add radius parm to sound_play_3d()
266 * 70 2/12/98 2:16p Andsager
267 * Better ship:ship collision pair next check time estimate
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.
273 * 68 2/05/98 12:51a Mike
274 * Early asteroid stuff.
276 * 67 2/04/98 6:08p Lawrance
277 * Add a light collision sound, overlay a shield collide sound if
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.
284 * 65 1/28/98 2:15p Mike
285 * Make collision damage affect shield, not just hull.
287 * 64 1/28/98 11:06a Andsager
288 * Remove some collision pairs. Add debug code for displaying collision
291 * 63 1/23/98 5:08p Andsager
292 * Collision from rotation is turned on for all ship:ship colliisons.
294 * 62 1/20/98 9:47a Mike
295 * Suppress optimized compiler warnings.
296 * Some secondary weapon work.
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.
302 * 60 1/14/98 2:30p Andsager
303 * Change warning for bad relative velocity
305 * 59 1/12/98 9:26p Andsager
306 * Implement collisions from rotation.
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.
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.
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.
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
325 * 54 12/23/97 5:34p Andsager
326 * Fixed bug colliding against edge of ships without shields.
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
332 * 52 12/17/97 9:39p Lawrance
333 * Always play collide sound when player collides with another ship.
335 * 51 12/17/97 3:55p Andsager
336 * Added separation velocity in ship:ship collision when both ships on
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.
343 * 49 12/08/97 6:23p Lawrance
344 * fix collision sounds (broken since hit pos was changed to local coords)
346 * 48 12/04/97 5:34p Lawrance
347 * let player collide with friendly ships (no damage though) by default
349 * 47 12/04/97 4:05p Allender
350 * comment out hud printf for ship ship collisions
352 * 46 12/03/97 5:44p Andsager
353 * Implement relative velocity collisions in the reference frame of the
356 * 45 12/03/97 12:04p Hoffoss
357 * Made changes so the 8 %'s aren't needed anymore. Can just use 2 again
360 * 44 12/03/97 11:35a Hoffoss
361 * Made changes to HUD messages send throughout the game.
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 %)
367 * 42 11/26/97 3:25p Mike
368 * Decrease large quantities of damage. If > 5.0f, cut out half of amount
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.
375 * 40 11/14/97 9:27a Andsager
376 * Changed debug print statements
378 * 39 11/13/97 6:12p Lawrance
379 * uncomment code that was commented out for build reasons
381 * 38 11/13/97 6:11p Lawrance
382 * call hud_start_collision_flash() when player ship hits another ship
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.
389 * 36 11/12/97 11:53p Mike
390 * Fix code that shows damage taken due to collision to only work for
393 * 35 11/12/97 12:14p Mike
394 * Cut ship:ship collision damage by half again and put in a HUD message
397 * 34 11/12/97 10:03a Mike
398 * Cut damage done due to ship:ship collisions by half.
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
405 * 32 11/09/97 11:24p Andsager
406 * Set small bounce in ship-ship collision. coeffic restitution 0.2
408 * 31 11/09/97 4:39p Lawrance
409 * make 'Collide_friendly' make friendly collisions behave the same way as
412 * 30 11/07/97 4:36p Mike
413 * Change how ships determine they're under attack by dumbfire weapons.
415 * 29 11/06/97 12:27a Mike
416 * Better avoid behavior.
417 * Modify ai_turn_towards_vector() to take a flag parameter.
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.
423 * 27 11/05/97 9:28p Mike
424 * Add ships_are_docking() to allow ships of different teams to dock.
426 * 26 11/05/97 5:50p Andsager
427 * Added hit_time to ship_ship_hit_info to prevent hit_time from getting
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
434 * 24 11/03/97 11:08p Lawrance
435 * play correct collision sounds.
437 * 23 11/03/97 2:07p Lawrance
438 * add ship-to-ship collision sound
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
444 * 21 11/01/97 3:58p Mike
445 * Zero damage caused by ship:ship collisions until it gets balanced.
447 * 20 10/29/97 5:19p Dave
448 * More debugging of server transfer. Put in debrief/brief
449 * transition for multiplayer (w/standalone)
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.
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.
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.
465 * 16 10/27/97 8:35a John
466 * code for new player warpout sequence
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
472 * 14 10/22/97 10:29p Andsager
473 * modify ship_ship_check_collision to allow sphere-polygon collisions
475 * 13 10/19/97 11:45p Mike
476 * Hacked in damage due to gravity well of a planet.
478 * 12 10/19/97 9:41p Andsager
479 * undefine SPHERE_POLY_CHECK
481 * 11 10/19/97 9:34p Andsager
482 * Changed model_collide to take 2nd parameter radius with (default = 0)
484 * 10 10/17/97 1:32a Andsager
485 * add sphere-polygon collision detection
487 * 9 10/01/97 5:55p Lawrance
488 * change call to snd_play_3d() to allow for arbitrary listening position
490 * 8 9/30/97 5:06p Andsager
491 * rename vm_project_point_onto_surface() -> vm_project_name_onto_plane()
493 * 7 9/28/97 2:19p Andsager
494 * fixed bug in getting shield point in ray model collisions
496 * 6 9/25/97 2:54p Andsager
497 * added small bounce to collisions
499 * 5 9/19/97 5:00p Andsager
500 * modify collisions so that damage is first applied to shield and then to
503 * 4 9/18/97 4:08p John
504 * Cleaned up & restructured ship damage stuff.
506 * 3 9/18/97 3:58p Andsager
507 * fix bugs in sphere_sphere collision (sets r and hit_pos)
509 * 2 9/17/97 5:12p John
510 * Restructured collision routines. Probably broke a lot of stuff.
512 * 1 9/17/97 2:14p John
518 #include "objcollide.h"
524 #include "freespace.h"
527 #include "3d.h" // needed for View_position, which is used when playing 3d sound
528 #include "gamesequence.h"
529 #include "hudshield.h"
532 #include "asteroid.h"
534 //#pragma optimize("", off)
535 //#pragma auto_inline(off)
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.
541 #define COLLIDE_DEBUG
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);
548 // calculate the physics of extended two body collisions
549 void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info);
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);
555 static int Collide_friendly = 1;
556 DCF_BOOL( collide_friendly, Collide_friendly )
559 static int Player_collide_sound, AI_collide_sound;
560 static int Player_collide_shield_sound, AI_collide_shield_sound;
562 // Return true if two ships are docking.
563 int ships_are_docking(object *objp1, object *objp2)
565 ai_info *aip1, *aip2;
566 ship *shipp1, *shipp2;
568 shipp1 = &Ships[objp1->instance];
569 shipp2 = &Ships[objp2->instance];
571 aip1 = &Ai_info[shipp1->ai_index];
572 aip2 = &Ai_info[shipp2->ai_index];
574 // for multiplayer clients -- disable the collision stuff for support ships.
576 if ( MULTIPLAYER_CLIENT ) {
577 if ( (Ship_info[shipp1->ship_info_index].flags & SIF_SUPPORT) || (Ship_info[shipp2->ship_info_index].flags & SIF_SUPPORT) ) {
583 if (aip1->ai_flags & AIF_DOCKED) {
584 if (aip1->dock_objnum == objp2-Objects){
589 if (aip1->mode == AIM_DOCK) {
590 if (aip1->goal_objnum == objp2-Objects){
593 } else if (aip2->mode == AIM_DOCK) {
594 if (aip2->goal_objnum == objp1-Objects){
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)
606 if (light_objp->type != OBJ_SHIP)
609 ai_info *aip = &Ai_info[Ships[light_objp->instance].ai_index];
611 if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART)) {
612 if (aip->goal_objnum == OBJ_INDEX(heavy_objp))
619 int ship_ship_check_collision(collision_info_struct *ship_ship_hit_info, vector *hitpos)
621 object *heavy_obj = ship_ship_hit_info->heavy;
622 object *light_obj = ship_ship_hit_info->light;
623 int num; //, player_check=0;
625 SDL_assert( heavy_obj->type == OBJ_SHIP );
626 SDL_assert( light_obj->type == OBJ_SHIP );
628 num = heavy_obj->instance;
629 SDL_assert( num >= 0 );
631 SDL_assert( Ships[num].objnum == OBJ_INDEX(heavy_obj));
634 int player_involved; // flag to indicate that A or B is the Player_obj
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 ) {
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 ) {
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) ) {
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)) {
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)) {
669 //nprintf(("AI", "Frame %i: Collision between %s and %s\n", Framecount, Ships[heavy_obj->instance].ship_name, Ships[light_obj->instance].ship_name));
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) ) {
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.
688 if ( (!(heavy_obj->flags & OF_WAS_RENDERED) && !(light_obj->flags & OF_WAS_RENDERED)) ) {
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)) {
698 if (!(Ship_info[Ships[light_obj->instance].ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
702 // Set up model_collide info
704 memset(&mc, -1, sizeof(mc_info));
706 // vector submodel_hit;
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
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);
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;
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);
731 // should be no ships that can rotate this fast
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);
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
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;
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;
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;
760 int submodel_list[MAX_ROTATING_SUBMODELS];
761 int num_rotating_submodels = 0;
762 int valid_hit_occured = 0;
765 ship_model_start(heavy_obj);
767 if (model_collide(&mc)) {
769 // Set earliest hit time
770 ship_ship_hit_info->hit_time = FLT_MAX;
772 // Do collision the cool new way
773 if ( ship_ship_hit_info->collide_rotate ) {
775 model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj);
777 pm = model_get(Ships[heavy_obj->instance].modelnum);
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;
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;
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;
792 // set angles for last frame
793 angles copy_angles = pm->submodel[submodel_list[i]].angs;
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);
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);
804 // mc.pos = zero // in submodel RF
806 mc.orient = &vmd_identity_matrix;
807 mc.submodel_num = submodel_list[i];
809 if ( model_collide(&mc) ) {
810 if (mc.hit_dist < ship_ship_hit_info->hit_time ) {
811 valid_hit_occured = 1;
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);
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);
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);
828 // submodel_hit = mc.hit_point;
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);
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;
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);
848 // set submodel angle at time of collision
849 // TODO: generalize... what happens when angle passes 0 or 2PI
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;
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);
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);
866 // vm_vec_sub(&temp2, &ship_ship_hit_info->light_collision_cm_pos, &ship_ship_hit_info->hit_pos);
870 // Don't look at this submodel again
871 pm->submodel[submodel_list[i]].blown_off = 1;
876 // Recover and do usual ship_ship collision, but without rotating submodels
877 mc.flags = copy_flags;
880 mc.orient = &heavy_obj->orient;
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;
888 set_hit_struct_info(ship_ship_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
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);
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);
900 // find position in submodel RF of light object at collison
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);
905 // submodel_hit = mc.hit_point;
909 ship_model_stop( heavy_obj );
912 if (valid_hit_occured) {
914 // Collision debug stuff
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;
922 if ((collide_obj != NULL) && (Ship_info[Ships[collide_obj->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
923 char *submode_string = "";
926 extern char *Mode_text[];
927 aip = &Ai_info[Ships[collide_obj->instance].ai_index];
929 if (aip->mode == AIM_CHASE)
930 submode_string = Submode_text[aip->submode];
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));
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;
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;
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;
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);
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);
960 vm_vec_add(hitpos, &ship_ship_hit_info->heavy->pos, &ship_ship_hit_info->r_heavy);
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);
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 );
973 vm_vec_sub(&diff, &submodel_hit, &temp3);
975 if (vm_vec_mag(&diff) > 0.1) {
981 calculate_ship_ship_collision_physics(ship_ship_hit_info);
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];
988 // If a couple of small ships, just move them apart.
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)) {
994 float mass_sum = heavy_obj->phys_info.mass + light_obj->phys_info.mass;
997 lh_ratio = light_obj->phys_info.mass/mass_sum;
998 if (lh_ratio < 0.2f) {
1002 // actually initialize h2l_vec
1003 vm_vec_sub(&h2l_vec, &light_obj->pos, &heavy_obj->pos);
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
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));
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));
1015 vm_vec_scale_add2(&light_obj->phys_info.vel, &light_obj->orient.rvec, +10.0f * (1.0f - lh_ratio));
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));
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));
1025 vm_vec_scale_add2(&light_obj->phys_info.vel, &light_obj->orient.rvec, -10.0f * (1.0f - lh_ratio));
1031 vector perp_rel_vel;
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;
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);
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);
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 );
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);
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 );
1065 return valid_hit_occured;
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)
1073 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
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)) {
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;
1086 *cruiser_mass = 2.0f * heavy->phys_info.mass;
1089 if (*cruiser_mass > light->phys_info.mass) {
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) {
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;
1104 *cruiser_mass = 2.0f * light->phys_info.mass;
1107 if (*cruiser_mass > heavy->phys_info.mass) {
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)
1123 // output: velocity, angular velocity, impulse
1125 // ------------------------------------------------------------------------------------------------
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
1131 void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info)
1133 // important parameters passed thru ship_ship_or_debris_hit
1134 // calculate the whack applied to each ship from collision
1136 // make local copies of hit_struct parameters
1137 object *heavy = ship_ship_hit_info->heavy;
1138 object *light = ship_ship_hit_info->light;
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);
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;
1152 SDL_assert(heavy->phys_info.mass < cruiser_mass);
1153 copy_mass = heavy->phys_info.mass;
1154 heavy->phys_info.mass = cruiser_mass;
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;
1163 coeff_restitution = 0.1f; // relative velocity wrt normal is zero after the collision ( range 0-1 )
1165 // find velocity of each obj at collision point
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 );
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;
1178 // if collision from rotating submodel of heavy obj, add in vel from rotvel of submodel
1179 vector local_vel_from_submodel;
1181 if (ship_ship_hit_info->submodel_rot_hit == 1) {
1182 bool set_model = false;
1184 polymodel *pm = model_get(Ships[heavy->instance].modelnum);
1186 // be sure model is set
1187 if (pm->submodel[ship_ship_hit_info->submodel_num].sii == NULL) {
1189 ship_model_start(heavy);
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);
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;
1205 // must be one of these axes or submodel_rot_hit is incorrectly set
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);
1213 // world coords for r_rot
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
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
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"));
1225 // nprintf(("Physics", "Rotating submodel collision - sub got whacked from behind\n"));
1228 ship_model_stop(heavy);
1231 // didn't collide with submodel
1232 vm_vec_zero(&local_vel_from_submodel);
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);
1240 // Add in effect of rotating submodel
1241 vm_vec_sub2(&v_rel_m, &local_vel_from_submodel);
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)
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
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;
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;
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;
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);
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;
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) {
1293 heavy_denom += 1.0f / heavy->phys_info.mass;
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;
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) {
1313 light_denom += 1.0f / light->phys_info.mass;
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;
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);
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);
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);
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));
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;
1359 heavy->phys_info.mass = copy_mass;
1365 // ------------------------------------------------------------------------------------------------
1368 // input: I_inv_body => inverse moment of inertia matrix in body coordinates
1369 // orient => orientation matrix
1371 // output: I_inv => inverse moment of inertia matrix in world coordinates
1372 // ------------------------------------------------------------------------------------------------
1374 // calculates the inverse moment of inertia matrix from the body matrix and oreint matrix
1376 void get_I_inv (matrix* I_inv, matrix* I_inv_body, matrix* orient)
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)
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);
1388 #define PLANET_DAMAGE_SCALE 4.0f
1389 #define PLANET_DAMAGE_RANGE 3 // If within this factor of radius, apply damage.
1391 fix Last_planet_damage_time = 0;
1392 extern void hud_start_text_flash(char *txt);
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)
1398 float planet_radius;
1401 planet_radius = planet_objp->radius;
1402 dist = vm_vec_dist_quick(&player_objp->pos, &planet_objp->pos);
1404 if (dist > planet_radius*PLANET_DAMAGE_RANGE)
1407 ship_apply_global_damage( player_objp, planet_objp, NULL, PLANET_DAMAGE_SCALE * flFrametime * (float)pow((planet_radius*PLANET_DAMAGE_RANGE)/dist, 3.0f) );
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 );
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)
1421 return (SDL_strncasecmp(Ships[objp->instance].ship_name, NOX("planet"), 6) == 0);
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)
1430 ship_info *sip1, *sip2;
1432 sip1 = &Ship_info[Ships[obj1->instance].ship_info_index];
1433 sip2 = &Ship_info[Ships[obj2->instance].ship_info_index];
1435 if (sip1->flags & SIF_PLAYER_SHIP) {
1436 if (is_planet(obj2)) {
1440 } else if (is_planet(obj1)) {
1441 if (sip2->flags & SIF_PLAYER_SHIP) {
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)
1456 vm_vec_sub(&tpos, global_pos, &objp->pos);
1457 vm_vec_rotate(&rotpos, &tpos, &objp->orient);
1458 return get_quadrant(&rotpos);
1461 #define MIN_REL_SPEED_FOR_LOUD_COLLISION 50 // relative speed of two colliding objects at which we play the "loud" collide sound
1463 void collide_ship_ship_sounds_init()
1465 Player_collide_sound = -1;
1466 AI_collide_sound = -1;
1467 Player_collide_shield_sound = -1;
1468 AI_collide_shield_sound = -1;
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)
1476 // int light_collision=0;
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);
1481 if ( rel_speed > MIN_REL_SPEED_FOR_LOUD_COLLISION ) {
1482 snd_play_3d( &Snds[SND_SHIP_SHIP_HEAVY], world_hit_pos, &View_position );
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 );
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 );
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 ) {
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 );
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 );
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)
1519 ai_info *aip1, *aip2;
1520 ship *ship1, *ship2;
1522 ship1 = &Ships[obj1->instance];
1523 ship2 = &Ships[obj2->instance];
1525 aip1 = &Ai_info[ship1->ai_index];
1526 aip2 = &Ai_info[ship2->ai_index];
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);
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);
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)
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
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);
1558 // don't drive into sfc we just collided with
1559 if (vm_vec_dotprod(&perp, normal) < 0) {
1560 vm_vec_negate(&perp);
1563 // get magnitude of added perp vel
1564 float added_perp_vel_mag = impulse / small_obj->phys_info.mass;
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);
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 )
1579 int player_involved;
1581 object *A = pair->a;
1582 object *B = pair->b;
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;
1590 if ( A->type == OBJ_WAYPOINT ) return 1;
1591 if ( B->type == OBJ_WAYPOINT ) return 1;
1593 SDL_assert( A->type == OBJ_SHIP );
1594 SDL_assert( B->type == OBJ_SHIP );
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;
1601 player_involved = 0;
1604 dist = vm_vec_dist( &A->pos, &B->pos );
1606 // If one of these is a planet, do special stuff.
1607 if (maybe_collide_planet(A, B))
1610 if ( dist < A->radius + B->radius ) {
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 ) {
1624 if (A->phys_info.mass > B->phys_info.mass) {
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));
1639 ship_ship_hit_info.heavy = HeavyOne; // heavy object, generally slower moving
1640 ship_ship_hit_info.light = LightOne; // light object, generally faster moving
1642 vector world_hit_pos;
1644 hit = ship_ship_check_collision(&ship_ship_hit_info, &world_hit_pos);
1646 /* if ((hitpos.x == FastOne->pos.x) && (hitpos.y == FastOne->pos.y) && (hitpos.z == FastOne->pos.z))
1648 if ((hitpos.x == SlowOne->pos.x) && (hitpos.y == SlowOne->pos.y) && (hitpos.z == SlowOne->pos.z))
1650 if ((A == FastOne) && (hitpos.x == FastOne->last_pos.x) && (hitpos.y == FastOne->last_pos.y) && (hitpos.z == FastOne->last_pos.z))
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));
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);
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.
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);
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.
1685 damage = 5.0f + (damage - 5.0f)/2.0f;
1688 do_kamikaze_crash(A, B);
1690 if (ship_ship_hit_info.impulse > 0) {
1694 // q = vm_vec_dist_quick(&A->pos, &B->pos) / (A->radius + B->radius);
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;
1703 // if (A == Player_obj)
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));
1715 if ( player_involved ) {
1716 hud_start_text_flash(XSTR("Collision", 1431), 2000);
1719 // damage *= (max_shields of fastest) / (max_impulse_of_fastest)
1720 // possibly calculate damage both ways and use largest/smallest/avg?
1722 // vm_vec_add(&world_hit_pos, &ship_ship_hit_info.heavy->pos, &ship_ship_hit_info.hit_pos);
1724 collide_ship_ship_do_sound(&world_hit_pos, A, B, player_involved);
1726 // check if we should do force feedback stuff
1727 if (player_involved && (ship_ship_hit_info.impulse > 0)) {
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);
1736 //mprintf(("Ship:Ship damage = %7.3f\n", speed));
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;
1745 vm_vec_cross(&right_angle_vec, &A->orient.v.uvec, &collision_vec);
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));
1757 // nprintf(("AI", "Ship:ship collision: %s and %s.\n", Ships[A->instance].ship_name, Ships[B->instance].ship_name));
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)) {
1770 float dam2 = (100.0f * damage/LightOne->phys_info.mass);
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) ) {
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);
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);
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));
1791 // estimate earliest time at which pair can hit
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;
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
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;
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));
1813 shipA_max_speed = ship_get_max_speed(&Ships[A->instance]);
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);
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));
1824 shipB_max_speed = ship_get_max_speed(&Ships[B->instance]);
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);
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
1835 pair->next_check_time = timestamp( fl2i(time) );
1837 pair->next_check_time = timestamp(0); // check next time
1844 void collect_ship_ship_physics_info(object *heavy, object *light, mc_info *mc_info, collision_info_struct *ship_ship_hit_info)
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]
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
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;
1859 float core_rad = model_get_core_radius( Ships[light->instance].modelnum );
1861 // get info needed for ship_ship_collision_physics
1862 SDL_assert(mc_info->hit_dist > 0);
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);
1869 *light_collide_cm_pos = *mc_info->p0;
1870 vm_vec_scale_add2(light_collide_cm_pos, &displacement, ship_ship_hit_info->hit_time);
1874 vm_vec_sub(r_light, &ship_ship_hit_info->hit_pos, light_collide_cm_pos);
1876 // SDL_assert(vm_vec_mag(&r_light) > core_rad - 0.1);
1877 float mag = float(fabs(vm_vec_mag(r_light) - core_rad));
1879 nprintf(("Physics", "Framecount: %i |r_light - core_rad| > 0.1)\n", Framecount));
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);
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 );
1892 nprintf(("Physics", "Framecount: %i r dot normal > 0\n", Framecount, dot));
1895 vm_vec_zero(heavy_collide_cm_pos);
1897 float q = vm_vec_dist(heavy_collide_cm_pos, light_collide_cm_pos) / (heavy->radius + core_rad);
1899 nprintf(("Physics", "Warning: q = %f. Supposed to be <= 1.0.\n", q));
1902 *r_heavy = ship_ship_hit_info->hit_pos;
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;
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));
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));