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/CollideDebrisShip.cpp $
15 * Routines to detect collisions and do physics, damage, etc for ships and debris
18 * Revision 1.6 2003/05/25 02:30:43 taylor
21 * Revision 1.5 2002/06/18 08:58:53 relnev
22 * last few struct changes
24 * Revision 1.4 2002/06/17 06:33:10 relnev
25 * ryan's struct patch for gcc 2.95
27 * Revision 1.3 2002/06/09 04:41:24 relnev
28 * added copyright header
30 * Revision 1.2 2002/05/03 13:34:33 theoddone33
33 * Revision 1.1.1.1 2002/05/03 03:28:10 root
37 * 12 9/14/99 2:59a Andsager
38 * Limit amount of damage done by debris colliding with ship for SUPERCAP
40 * 11 8/31/99 11:43p Andsager
41 * No ship:debris damage to ship if debris' parent is ship itself
43 * 10 8/10/99 5:49p Andsager
44 * Fix bug - no damage collision against debris.
46 * 9 8/09/99 4:45p Andsager
47 * Add HUD "collision " waring when colliding with asteroids and debris.
49 * 8 7/15/99 9:20a Andsager
50 * FS2_DEMO initial checkin
52 * 7 6/14/99 3:21p Andsager
53 * Allow collisions between ship and its debris. Fix up collision pairs
54 * when large ship is warping out.
56 * 6 1/12/99 10:24a Andsager
57 * Fix asteroid-ship collision bug, with negative collision
59 * 5 10/23/98 1:11p Andsager
60 * Make ship sparks emit correctly from rotating structures.
62 * 4 10/20/98 1:39p Andsager
63 * Make so sparks follow animated ship submodels. Modify
64 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
65 * submodel_num. Add submodel_num to multiplayer hit packet.
67 * 3 10/16/98 1:22p Andsager
68 * clean up header files
70 * 2 10/07/98 10:53a Dave
73 * 1 10/07/98 10:50a Dave
75 * 27 4/24/98 5:35p Andsager
76 * Fix sparks sometimes drawing not on model. If ship is sphere in
77 * collision, don't draw sparks. Modify ship_apply_local_damage() to take
80 * 26 4/02/98 6:29p Lawrance
81 * compile out asteroid references for demo
83 * 25 4/02/98 5:10p Mike
84 * Decrease damage yet more (by 3x) when an asteroid collides with a ship
87 * 24 4/02/98 4:25p Andsager
88 * fix bug calculating max_ship_impulse
90 * 23 3/30/98 10:35a Andsager
91 * Attempt to optimize ship:asteroid collisions. Maybe return if time.
93 * 22 3/25/98 2:37p Andsager
94 * Modify maximum damage done by asteroids, depends on ship's max_vel, not
95 * vel when warping out.
97 * 21 3/25/98 2:15p Andsager
99 * 20 3/25/98 1:36p Mike
100 * Balancing of sm1-06a, Galatea mission.
102 * 19 3/23/98 11:14a Andsager
103 * Modified collide_asteroid_ship to calculate next possible collision
104 * time based on ship afterburner speed.
106 * 18 3/20/98 12:02a Mike
107 * Cap damage done by an asteroid on a huge ship to 1/8 ship strength if a
108 * large ship. If <4000 hull, can do more than 1/8.
110 * 17 3/05/98 3:12p Mike
111 * Fix bugs in asteroid collisions. Throw asteroids at Galataea (needs to
112 * be made general). Add skill level support for asteroid throwing.
114 * 16 3/04/98 11:21p Mike
115 * Less rotation on huge ships in death roll.
116 * Less damage done to huge ships when hit an asteroid at high speed.
118 * 15 3/02/98 2:58p Mike
119 * Make "asteroids" in debug console turn asteroids on/off.
121 * 14 2/19/98 12:46a Lawrance
122 * Further work on asteroids.
124 * 13 2/07/98 2:14p Mike
125 * Improve asteroid splitting. Add ship:asteroid collisions. Timestamp
126 * ship:debris collisions.
128 * 12 2/06/98 7:29p John
129 * Added code to cull out some future asteroid-ship collisions.
131 * 11 2/05/98 12:51a Mike
132 * Early asteroid stuff.
134 * 10 2/04/98 8:57p Lawrance
135 * remove unused variable
137 * 9 2/04/98 6:08p Lawrance
138 * Add a light collision sound, overlay a shield collide sound if
141 * 8 1/05/98 9:07p Andsager
142 * Changed ship_shipor_debris_hit_info struct to more meaninful names
144 * 7 1/02/98 9:08a Andsager
145 * Changed ship:ship and ship:debris collision detection to ignore shields
146 * and collide only against hull. Also, significantly reduced radius of
149 * 6 12/22/97 9:56p Andsager
150 * Implement ship:debris collisions. Generalize and move
151 * ship_ship_or_debris_hit struct from CollideShipShip to ObjCollide.h
153 * 5 11/03/97 11:08p Lawrance
154 * play correct collision sounds.
156 * 4 10/27/97 8:35a John
157 * code for new player warpout sequence
159 * 3 10/01/97 5:55p Lawrance
160 * change call to snd_play_3d() to allow for arbitrary listening position
162 * 2 9/17/97 5:12p John
163 * Restructured collision routines. Probably broke a lot of stuff.
165 * 1 9/17/97 2:14p John
172 #include "objcollide.h"
178 #include "asteroid.h"
181 void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info);
184 extern int Framecount;
185 int Debris_ship_count = 0;
188 // Checks debris-ship collisions. pair->a is debris and pair->b is ship.
189 // Returns 1 if all future collisions between these can be ignored
190 int collide_debris_ship( obj_pair * pair )
193 object *pdebris = pair->a;
194 object *pship = pair->b;
196 // Don't check collisions for warping out player
197 if ( Player->control_mode != PCM_NORMAL ) {
198 if ( pship == Player_obj )
202 SDL_assert( pdebris->type == OBJ_DEBRIS );
203 SDL_assert( pship->type == OBJ_SHIP );
205 /* Debris_ship_count++;
206 if (Debris_ship_count % 100 == 0)
207 nprintf(("AI", "Done %i debris:ship checks in %i frames = %.2f checks/frame\n", Debris_ship_count, Framecount, (float) Debris_ship_count/Framecount));
209 dist = vm_vec_dist( &pdebris->pos, &pship->pos );
210 if ( dist < pdebris->radius + pship->radius ) {
213 // create and initialize ship_ship_hit_info struct
214 collision_info_struct debris_hit_info;
215 memset( &debris_hit_info, -1, sizeof(collision_info_struct) );
217 if ( pdebris->phys_info.mass > pship->phys_info.mass ) {
218 debris_hit_info.heavy = pdebris;
219 debris_hit_info.light = pship;
221 debris_hit_info.heavy = pship;
222 debris_hit_info.light = pdebris;
225 hit = debris_check_collision(pdebris, pship, &hitpos, &debris_hit_info );
230 // do collision physics
231 calculate_ship_ship_collision_physics( &debris_hit_info );
233 if ( debris_hit_info.impulse < 0.5f )
236 // calculate ship damage
237 ship_damage = 0.005f * debris_hit_info.impulse; // Cut collision-based damage in half.
238 // Decrease heavy damage by 2x.
239 if (ship_damage > 5.0f)
240 ship_damage = 5.0f + (ship_damage - 5.0f)/2.0f;
242 // calculate debris damage and set debris damage to greater or debris and ship
243 // debris damage is needed since we can really whack some small debris with afterburner and not do
244 // significant damage to ship but the debris goes off faster than afterburner speed.
245 debris_damage = debris_hit_info.impulse/pdebris->phys_info.mass; // ie, delta velocity of debris
246 debris_damage = (debris_damage > ship_damage) ? debris_damage : ship_damage;
248 // supercaps cap damage at 10-20% max hull ship damage
249 if (Ship_info[Ships[pship->instance].ship_info_index].flags & SIF_SUPERCAP) {
250 float cap_percent_damage = frand_range(0.1f, 0.2f);
251 ship_damage = SDL_min(ship_damage, cap_percent_damage * Ship_info[Ships[pship->instance].ship_info_index].initial_hull_strength);
254 // apply damage to debris
255 debris_hit( pdebris, pship, &hitpos, debris_damage); // speed => damage
256 int quadrant_num, apply_ship_damage;
258 // apply damage to ship unless 1) debris is from ship
259 // apply_ship_damage = !((pship->signature == pdebris->parent_sig) && ship_is_beginning_warpout_speedup(pship));
260 apply_ship_damage = !(pship->signature == pdebris->parent_sig);
262 if ( debris_hit_info.heavy == pship ) {
263 quadrant_num = get_ship_quadrant_from_global(&hitpos, pship);
264 if ((pship->flags & OF_NO_SHIELDS) || !ship_is_shield_up(pship, quadrant_num) ) {
267 if (apply_ship_damage) {
268 ship_apply_local_damage(debris_hit_info.heavy, debris_hit_info.light, &hitpos, ship_damage, quadrant_num, CREATE_SPARKS, debris_hit_info.submodel_num);
271 // don't draw sparks using sphere hit position
272 if (apply_ship_damage) {
273 ship_apply_local_damage(debris_hit_info.light, debris_hit_info.heavy, &hitpos, ship_damage, MISS_SHIELDS, NO_SPARKS);
277 // maybe print Collision on HUD
278 if ( pship == Player_obj ) {
279 hud_start_text_flash(XSTR("Collision", 1431), 2000);
282 collide_ship_ship_do_sound(&hitpos, pship, pdebris, pship==Player_obj);
286 } else { // Bounding spheres don't intersect, set timestamp for next collision check.
287 float ship_max_speed, debris_speed;
291 shipp = &Ships[pship->instance];
293 if (ship_is_beginning_warpout_speedup(pship)) {
294 ship_max_speed = SDL_max(ship_get_max_speed(shipp), ship_get_warp_speed(pship));
296 ship_max_speed = ship_get_max_speed(shipp);
298 ship_max_speed = SDL_max(ship_max_speed, 10.0f);
299 ship_max_speed = SDL_max(ship_max_speed, pship->phys_info.vel.xyz.z);
301 debris_speed = pdebris->phys_info.speed;
303 time = 1000.0f * (dist - pship->radius - pdebris->radius - 10.0f) / (ship_max_speed + debris_speed); // 10.0f is a safety factor
304 time -= 200.0f; // allow one frame slow frame at ~5 fps
307 //nprintf(("AI", "Ship %s debris #%i delay time = %.1f seconds\n", Ships[pship->instance].ship_name, pdebris-Objects, time/1000.0f));
308 pair->next_check_time = timestamp( fl2i(time) );
310 pair->next_check_time = timestamp(0); // check next time
317 // Checks asteroid-ship collisions. pair->a is asteroid and pair->b is ship.
318 // Returns 1 if all future collisions between these can be ignored
319 int collide_asteroid_ship( obj_pair * pair )
321 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
323 if (!Asteroids_enabled)
327 object *pasteroid = pair->a;
328 object *pship = pair->b;
330 // Don't check collisions for warping out player
331 if ( Player->control_mode != PCM_NORMAL ) {
332 if ( pship == Player_obj ) return 0;
335 if (pasteroid->hull_strength < 0.0f)
338 SDL_assert( pasteroid->type == OBJ_ASTEROID );
339 SDL_assert( pship->type == OBJ_SHIP );
341 dist = vm_vec_dist( &pasteroid->pos, &pship->pos );
343 if ( dist < pasteroid->radius + pship->radius ) {
346 // create and initialize ship_ship_hit_info struct
347 collision_info_struct asteroid_hit_info;
348 memset( &asteroid_hit_info, -1, sizeof(collision_info_struct) );
350 if ( pasteroid->phys_info.mass > pship->phys_info.mass ) {
351 asteroid_hit_info.heavy = pasteroid;
352 asteroid_hit_info.light = pship;
354 asteroid_hit_info.heavy = pship;
355 asteroid_hit_info.light = pasteroid;
358 hit = asteroid_check_collision(pasteroid, pship, &hitpos, &asteroid_hit_info );
361 float asteroid_damage;
363 vector asteroid_vel = pasteroid->phys_info.vel;
365 // do collision physics
366 calculate_ship_ship_collision_physics( &asteroid_hit_info );
368 if ( asteroid_hit_info.impulse < 0.5f )
371 // limit damage from impulse by making max impulse (for damage) 2*m*v_max_relative
372 float max_ship_impulse = (2.0f*pship->phys_info.max_vel.xyz.z+vm_vec_mag_quick(&asteroid_vel)) *
373 (pship->phys_info.mass*pasteroid->phys_info.mass) / (pship->phys_info.mass + pasteroid->phys_info.mass);
375 if (asteroid_hit_info.impulse > max_ship_impulse) {
376 ship_damage = 0.001f * max_ship_impulse;
378 ship_damage = 0.001f * asteroid_hit_info.impulse; // Cut collision-based damage in half.
381 // Decrease heavy damage by 2x.
382 if (ship_damage > 5.0f)
383 ship_damage = 5.0f + (ship_damage - 5.0f)/2.0f;
385 if ((ship_damage > 500.0f) && (ship_damage > Ship_info[Ships[pship->instance].ship_info_index].initial_hull_strength/8.0f)) {
386 ship_damage = Ship_info[Ships[pship->instance].ship_info_index].initial_hull_strength/8.0f;
387 nprintf(("AI", "Pinning damage to %s from asteroid at %7.3f (%7.3f percent)\n", Ships[pship->instance].ship_name, ship_damage, 100.0f * ship_damage/Ship_info[Ships[pship->instance].ship_info_index].initial_hull_strength));
390 // Decrease damage during warp out because it's damn annoying when your escoree dies during warp out.
391 if (Ai_info[Ships[pship->instance].ai_index].mode == AIM_WARP_OUT)
394 //nprintf(("AI", "Asteroid damage on %s = %7.3f (%6.2f percent)\n", Ships[pship->instance].ship_name, ship_damage, 100.0f * ship_damage/Ship_info[Ships[pship->instance].ship_info_index].initial_hull_strength));
396 // calculate asteroid damage and set asteroid damage to greater or asteroid and ship
397 // asteroid damage is needed since we can really whack some small asteroid with afterburner and not do
398 // significant damage to ship but the asteroid goes off faster than afterburner speed.
399 asteroid_damage = asteroid_hit_info.impulse/pasteroid->phys_info.mass; // ie, delta velocity of asteroid
400 asteroid_damage = (asteroid_damage > ship_damage) ? asteroid_damage : ship_damage;
402 // apply damage to asteroid
403 asteroid_hit( pasteroid, pship, &hitpos, asteroid_damage); // speed => damage
405 //extern fix Missiontime;
408 if ( asteroid_hit_info.heavy == pship ) {
409 quadrant_num = get_ship_quadrant_from_global(&hitpos, pship);
410 if ((pship->flags & OF_NO_SHIELDS) || !ship_is_shield_up(pship, quadrant_num) ) {
413 ship_apply_local_damage(asteroid_hit_info.heavy, asteroid_hit_info.light, &hitpos, ship_damage, quadrant_num, CREATE_SPARKS, asteroid_hit_info.submodel_num);
414 //if (asteroid_hit_info.heavy->type == OBJ_SHIP) {
415 // nprintf(("AI", "Time = %7.3f, asteroid #%i applying %7.3f damage to ship %s\n", f2fl(Missiontime), pasteroid-Objects, ship_damage, Ships[asteroid_hit_info.heavy->instance].ship_name));
418 // dont draw sparks (using sphere hitpos)
419 ship_apply_local_damage(asteroid_hit_info.light, asteroid_hit_info.heavy, &hitpos, ship_damage, MISS_SHIELDS, NO_SPARKS);
420 //if (asteroid_hit_info.light->type == OBJ_SHIP) {
421 // nprintf(("AI", "Time = %7.3f, asteroid #%i applying %7.3f damage to ship %s\n", f2fl(Missiontime), pasteroid-Objects, ship_damage, Ships[asteroid_hit_info.light->instance].ship_name));
425 // maybe print Collision on HUD
426 if ( pship == Player_obj ) {
427 hud_start_text_flash(XSTR("Collision", 1431), 2000);
430 collide_ship_ship_do_sound(&hitpos, pship, pasteroid, pship==Player_obj);
437 // estimate earliest time at which pair can hit
438 float asteroid_max_speed, ship_max_speed, time;
439 ship *shipp = &Ships[pship->instance];
441 asteroid_max_speed = vm_vec_mag(&pasteroid->phys_info.vel); // Asteroid... vel gets reset, not max vel.z
442 asteroid_max_speed = SDL_max(asteroid_max_speed, 10.0f);
444 if (ship_is_beginning_warpout_speedup(pship)) {
445 ship_max_speed = SDL_max(ship_get_max_speed(shipp), ship_get_warp_speed(pship));
447 ship_max_speed = ship_get_max_speed(shipp);
449 ship_max_speed = SDL_max(ship_max_speed, 10.0f);
450 ship_max_speed = SDL_max(ship_max_speed, pship->phys_info.vel.xyz.z);
453 time = 1000.0f * (dist - pship->radius - pasteroid->radius - 10.0f) / (asteroid_max_speed + ship_max_speed); // 10.0f is a safety factor
454 time -= 200.0f; // allow one frame slow frame at ~5 fps
457 pair->next_check_time = timestamp( fl2i(time) );
459 pair->next_check_time = timestamp(0); // check next time
464 return 0; // no asteroids in demo version