]> icculus.org git repositories - taylor/freespace2.git/blob - src/object/collidedebrisship.cpp
make first pass at async popups
[taylor/freespace2.git] / src / object / collidedebrisship.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/CollideDebrisShip.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Routines to detect collisions and do physics, damage, etc for ships and debris
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/18 08:58:53  relnev
22  * last few struct changes
23  *
24  * Revision 1.4  2002/06/17 06:33:10  relnev
25  * ryan's struct patch for gcc 2.95
26  *
27  * Revision 1.3  2002/06/09 04:41:24  relnev
28  * added copyright header
29  *
30  * Revision 1.2  2002/05/03 13:34:33  theoddone33
31  * More stuff compiles
32  *
33  * Revision 1.1.1.1  2002/05/03 03:28:10  root
34  * Initial import.
35  *
36  * 
37  * 12    9/14/99 2:59a Andsager
38  * Limit amount of damage done by debris colliding with ship for SUPERCAP
39  * 
40  * 11    8/31/99 11:43p Andsager
41  * No ship:debris damage to ship if debris' parent is ship itself
42  * 
43  * 10    8/10/99 5:49p Andsager
44  * Fix bug - no damage collision against debris.
45  * 
46  * 9     8/09/99 4:45p Andsager
47  * Add HUD "collision " waring when colliding with asteroids and debris.
48  * 
49  * 8     7/15/99 9:20a Andsager
50  * FS2_DEMO initial checkin
51  * 
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.
55  * 
56  * 6     1/12/99 10:24a Andsager
57  * Fix asteroid-ship collision bug, with negative collision
58  * 
59  * 5     10/23/98 1:11p Andsager
60  * Make ship sparks emit correctly from rotating structures.
61  * 
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.
66  * 
67  * 3     10/16/98 1:22p Andsager
68  * clean up header files
69  * 
70  * 2     10/07/98 10:53a Dave
71  * Initial checkin.
72  * 
73  * 1     10/07/98 10:50a Dave
74  * 
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
78  * parameter no_spark.
79  * 
80  * 26    4/02/98 6:29p Lawrance
81  * compile out asteroid references for demo
82  * 
83  * 25    4/02/98 5:10p Mike
84  * Decrease damage yet more (by 3x) when an asteroid collides with a ship
85  * during warp out.
86  * 
87  * 24    4/02/98 4:25p Andsager
88  * fix bug calculating max_ship_impulse
89  * 
90  * 23    3/30/98 10:35a Andsager
91  * Attempt to optimize ship:asteroid collisions.  Maybe return if time.
92  * 
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.
96  * 
97  * 21    3/25/98 2:15p Andsager
98  * 
99  * 20    3/25/98 1:36p Mike
100  * Balancing of sm1-06a, Galatea mission.
101  * 
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.
105  * 
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.
109  * 
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.
113  * 
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.
117  * 
118  * 15    3/02/98 2:58p Mike
119  * Make "asteroids" in debug console turn asteroids on/off.
120  * 
121  * 14    2/19/98 12:46a Lawrance
122  * Further work on asteroids.
123  * 
124  * 13    2/07/98 2:14p Mike
125  * Improve asteroid splitting.  Add ship:asteroid collisions.  Timestamp
126  * ship:debris collisions.
127  * 
128  * 12    2/06/98 7:29p John
129  * Added code to cull out some future asteroid-ship collisions.
130  * 
131  * 11    2/05/98 12:51a Mike
132  * Early asteroid stuff.
133  * 
134  * 10    2/04/98 8:57p Lawrance
135  * remove unused variable
136  * 
137  * 9     2/04/98 6:08p Lawrance
138  * Add a light collision sound, overlay a shield collide sound if
139  * applicable.
140  * 
141  * 8     1/05/98 9:07p Andsager
142  * Changed ship_shipor_debris_hit_info struct to more meaninful names
143  * 
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
147  * sphere.
148  * 
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
152  * 
153  * 5     11/03/97 11:08p Lawrance
154  * play correct collision sounds.
155  * 
156  * 4     10/27/97 8:35a John
157  * code for new player warpout sequence
158  * 
159  * 3     10/01/97 5:55p Lawrance
160  * change call to snd_play_3d() to allow for arbitrary listening position
161  * 
162  * 2     9/17/97 5:12p John
163  * Restructured collision routines.  Probably broke a lot of stuff.
164  * 
165  * 1     9/17/97 2:14p John
166  * Initial revision
167  *
168  * $NoKeywords: $
169  */
170
171
172 #include "objcollide.h"
173 #include "ship.h"
174 #include "debris.h"
175 #include "player.h"
176 #include "shiphit.h"
177 #include "timer.h"
178 #include "asteroid.h"
179 #include "hud.h"
180
181 void calculate_ship_ship_collision_physics(collision_info_struct *ship_ship_hit_info);
182
183 /*
184 extern int Framecount;
185 int Debris_ship_count = 0;
186 */
187
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 )
191 {
192         float dist;
193         object *pdebris = pair->a;
194         object *pship = pair->b;
195
196                 // Don't check collisions for warping out player
197         if ( Player->control_mode != PCM_NORMAL )       {
198                 if ( pship == Player_obj )
199                         return 0;
200         }
201
202         SDL_assert( pdebris->type == OBJ_DEBRIS );
203         SDL_assert( pship->type == OBJ_SHIP );
204
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));
208 */
209         dist = vm_vec_dist( &pdebris->pos, &pship->pos );
210         if ( dist < pdebris->radius + pship->radius )   {
211                 int hit;
212                 vector  hitpos;
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) );
216
217                 if ( pdebris->phys_info.mass > pship->phys_info.mass ) {
218                         debris_hit_info.heavy = pdebris;
219                         debris_hit_info.light = pship;
220                 } else {
221                         debris_hit_info.heavy = pship;
222                         debris_hit_info.light = pdebris;
223                 }
224
225                 hit = debris_check_collision(pdebris, pship, &hitpos, &debris_hit_info );
226                 if ( hit ) {
227                         float           ship_damage;    
228                         float           debris_damage;
229
230                         // do collision physics
231                         calculate_ship_ship_collision_physics( &debris_hit_info );
232
233                         if ( debris_hit_info.impulse < 0.5f )
234                                 return 0;
235
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;
241
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;
247
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);
252                         }
253
254                         // apply damage to debris
255                         debris_hit( pdebris, pship, &hitpos, debris_damage);            // speed => damage
256                         int quadrant_num, apply_ship_damage;
257
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);
261
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) ) {
265                                         quadrant_num = -1;
266                                 }
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);
269                                 }
270                         } else {
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);
274                                 }
275                         }
276
277                         // maybe print Collision on HUD
278                         if ( pship == Player_obj ) {                                    
279                                 hud_start_text_flash(XSTR("Collision", 1431), 2000);
280                         }
281
282                         collide_ship_ship_do_sound(&hitpos, pship, pdebris, pship==Player_obj);
283
284                         return 0;
285                 }
286         } else {        //      Bounding spheres don't intersect, set timestamp for next collision check.
287                 float   ship_max_speed, debris_speed;
288                 float   time;
289                 ship *shipp;
290
291                 shipp = &Ships[pship->instance];
292
293                 if (ship_is_beginning_warpout_speedup(pship)) {
294                         ship_max_speed = SDL_max(ship_get_max_speed(shipp), ship_get_warp_speed(pship));
295                 } else {
296                         ship_max_speed = ship_get_max_speed(shipp);
297                 }
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);
300
301                 debris_speed = pdebris->phys_info.speed;
302
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
305
306                 if (time > 100) {
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) );
309                 } else {
310                         pair->next_check_time = timestamp(0);   // check next time
311                 }
312         }
313
314         return 0;
315 }
316
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 )
320 {
321 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
322
323         if (!Asteroids_enabled)
324                 return 0;
325
326         float           dist;
327         object  *pasteroid = pair->a;
328         object  *pship = pair->b;
329
330                 // Don't check collisions for warping out player
331         if ( Player->control_mode != PCM_NORMAL )       {
332                 if ( pship == Player_obj ) return 0;
333         }
334
335         if (pasteroid->hull_strength < 0.0f)
336                 return 0;
337
338         SDL_assert( pasteroid->type == OBJ_ASTEROID );
339         SDL_assert( pship->type == OBJ_SHIP );
340
341         dist = vm_vec_dist( &pasteroid->pos, &pship->pos );
342
343         if ( dist < pasteroid->radius + pship->radius ) {
344                 int hit;
345                 vector  hitpos;
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) );
349
350                 if ( pasteroid->phys_info.mass > pship->phys_info.mass ) {
351                         asteroid_hit_info.heavy = pasteroid;
352                         asteroid_hit_info.light = pship;
353                 } else {
354                         asteroid_hit_info.heavy = pship;
355                         asteroid_hit_info.light = pasteroid;
356                 }
357
358                 hit = asteroid_check_collision(pasteroid, pship, &hitpos, &asteroid_hit_info );
359                 if ( hit ) {
360                         float           ship_damage;    
361                         float           asteroid_damage;
362
363                         vector asteroid_vel = pasteroid->phys_info.vel;
364
365                         // do collision physics
366                         calculate_ship_ship_collision_physics( &asteroid_hit_info );
367
368                         if ( asteroid_hit_info.impulse < 0.5f )
369                                 return 0;
370
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);
374
375                         if (asteroid_hit_info.impulse > max_ship_impulse) {
376                                 ship_damage = 0.001f * max_ship_impulse;
377                         } else {
378                                 ship_damage = 0.001f * asteroid_hit_info.impulse;       //      Cut collision-based damage in half.
379                         }
380
381                         //      Decrease heavy damage by 2x.
382                         if (ship_damage > 5.0f)
383                                 ship_damage = 5.0f + (ship_damage - 5.0f)/2.0f;
384
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));
388                         }
389
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)
392                                 ship_damage /= 3.0f;
393
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));
395
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;
401
402                         // apply damage to asteroid
403                         asteroid_hit( pasteroid, pship, &hitpos, asteroid_damage);              // speed => damage
404
405                         //extern fix Missiontime;
406
407                         int quadrant_num;
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) ) {
411                                         quadrant_num = -1;
412                                 }
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));
416                                 //}
417                         } else {
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));
422                                 //}
423                         }
424
425                         // maybe print Collision on HUD
426                         if ( pship == Player_obj ) {                                    
427                                 hud_start_text_flash(XSTR("Collision", 1431), 2000);
428                         }
429
430                         collide_ship_ship_do_sound(&hitpos, pship, pasteroid, pship==Player_obj);
431
432                         return 0;
433                 }
434
435                 return 0;
436         } else {
437                 // estimate earliest time at which pair can hit
438                 float asteroid_max_speed, ship_max_speed, time;
439                 ship *shipp = &Ships[pship->instance];
440
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);
443
444                 if (ship_is_beginning_warpout_speedup(pship)) {
445                         ship_max_speed = SDL_max(ship_get_max_speed(shipp), ship_get_warp_speed(pship));
446                 } else {
447                         ship_max_speed = ship_get_max_speed(shipp);
448                 }
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);
451
452
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
455
456                 if (time > 100) {
457                         pair->next_check_time = timestamp( fl2i(time) );
458                 } else {
459                         pair->next_check_time = timestamp(0);   // check next time
460                 }
461                 return 0;
462         }
463 #else
464         return 0;       // no asteroids in demo version
465 #endif
466 }
467