]> icculus.org git repositories - taylor/freespace2.git/blob - src/debris/debris.cpp
Freespace 1 support
[taylor/freespace2.git] / src / debris / debris.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/Debris/Debris.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code for the pieces of exploding object debris.
16  *
17  * $Log$
18  * Revision 1.5  2003/05/25 02:30:42  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.4  2002/06/17 06:33:08  relnev
22  * ryan's struct patch for gcc 2.95
23  *
24  * Revision 1.3  2002/06/09 04:41:15  relnev
25  * added copyright header
26  *
27  * Revision 1.2  2002/05/28 08:52:03  relnev
28  * implemented two assembly stubs.
29  *
30  * cleaned up a few warnings.
31  *
32  * added a little demo hackery to make it progress a little farther.
33  *
34  * Revision 1.1.1.1  2002/05/03 03:28:08  root
35  * Initial import.
36  *
37  * 
38  * 16    8/01/99 1:13p Dave
39  * Fixed objsnd problem with hull debris pieces.
40  * 
41  * 15    7/01/99 4:23p Dave
42  * Full support for multiple linked ambient engine sounds. Added "big
43  * damage" flag.
44  * 
45  * 14    7/01/99 11:44a Dave
46  * Updated object sound system to allow multiple obj sounds per ship.
47  * Added hit-by-beam sound. Added killed by beam sound.
48  * 
49  * 13    5/18/99 11:50a Andsager
50  * Remove unused object type OBJ_GHOST_SAVE
51  * 
52  * 12    5/14/99 11:50a Andsager
53  * Added vaporize for SMALL ships hit by HUGE beams.  Modified dying
54  * frame.  Enlarged debris shards and range at which visible.
55  * 
56  * 11    4/23/99 12:01p Johnson
57  * Added SIF_HUGE_SHIP
58  * 
59  * 10    2/26/99 4:14p Dave
60  * Put in the ability to have multiple shockwaves for ships.
61  * 
62  * 9     1/20/99 6:04p Dave
63  * Another bit of stuff for beam weapons. Ships will properly use them
64  * now, although they're really deadly.
65  * 
66  * 8     12/03/98 3:14p Andsager
67  * Check in code that checks rotating submodel actually has ship subsystem
68  * 
69  * 7     11/19/98 11:07p Andsager
70  * Check in of physics and collision detection of rotating submodels
71  * 
72  * 6     11/13/98 10:13a Andsager
73  * simplify collision code
74  * 
75  * 5     11/05/98 5:55p Dave
76  * Big pass at reducing #includes
77  * 
78  * 4     10/23/98 1:11p Andsager
79  * Make ship sparks emit correctly from rotating structures.
80  * 
81  * 3     10/16/98 1:22p Andsager
82  * clean up header files
83  * 
84  * 2     10/07/98 10:52a Dave
85  * Initial checkin.
86  * 
87  * 1     10/07/98 10:48a Dave
88  * 
89  * 119   8/28/98 3:28p Dave
90  * EMP effect done. AI effects may need some tweaking as required.
91  * 
92  * 118   5/03/98 5:41p Mike
93  * Add Framecount to nprintf.
94  * 
95  * 117   4/15/98 10:00p Allender
96  * make debris have own signature set.
97  * 
98  * 116   4/15/98 9:42a Adam
99  * added 2 more explosion types (1, actually, but placeholder for 2)
100  * 
101  * 115   4/13/98 4:52p Allender
102  * remove AI_frametime and us flFrametime instead.  Make lock warning work
103  * in multiplayer for aspect seeking missiles.  Debris fixups
104  * 
105  * 114   4/10/98 12:16p Allender
106  * fix ship hit kill and debris packets
107  * 
108  * 113   4/09/98 5:43p Allender
109  * multiplayer network object fixes.  debris and self destructed ships
110  * should all sync up.  Fix problem where debris pieces (hull pieces) were
111  * not getting a net signature
112  * 
113  * 112   4/01/98 5:34p John
114  * Made only the used POFs page in for a level.   Reduced some interp
115  * arrays.    Made custom detail level work differently.
116  * 
117  * 111   3/31/98 5:11p John
118  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
119  * bunch of debug stuff out of player file.  Made model code be able to
120  * unload models and malloc out only however many models are needed.
121  * 
122  * 110   3/26/98 5:21p John
123  * Added new code to preload all bitmaps at the start of a level.
124  * Commented it out, though.
125  * 
126  * 109   3/23/98 12:20p Andsager
127  * Enable collision from rotation in ship_debris and ship_debris
128  * collisions.
129  * 
130  * 108   3/19/98 12:09p John
131  * Fixed a bug using 6 characters.   r_heavy was using local coordinates
132  * instead of world so all asteroid-ship and debris-ship hitpos's were in
133  * the wrong spot.  Ok, I rearreanged some code to make it clearer also.
134  * 
135  * 
136  * 107   3/12/98 6:47p John
137  * MAde arcs on debris further than objrad*50 not render.
138  * 
139  * 106   3/09/98 2:10p Andsager
140  * Put in checks for debris (other) with excessive velocity.
141  * 
142  * 105   2/26/98 10:07p Hoffoss
143  * Rewrote state saving and restoring to fix bugs and simplify the code.
144  * 
145  * 104   2/22/98 12:19p John
146  * Externalized some strings
147  * 
148  * 103   2/20/98 8:31p Lawrance
149  * Add radius parm to sound_play_3d()
150  * 
151  * 102   2/10/98 6:43p Lawrance
152  * Moved asteroid code to a separate lib.
153  * 
154  * 101   2/07/98 2:14p Mike
155  * Improve asteroid splitting.  Add ship:asteroid collisions.  Timestamp
156  * ship:debris collisions.
157  * 
158  * 100   2/06/98 7:45p John
159  * Reversed order of asteroid models so blown up ones are smaller.  
160  * 
161  * 99    2/06/98 7:28p John
162  * Made debris and particles not get created if > 200 m from eye.   Added
163  * max_velocity to asteroid's physics info for aiding in throwing out
164  * collision pairs.
165  * 
166  * 98    2/06/98 3:08p Mike
167  * More asteroid stuff, including resolving conflicts between the two
168  * asteroid_field structs!
169  * 
170  * 97    2/06/98 12:25a Mike
171  * More asteroid stuff.
172  * 
173  * 96    2/05/98 9:41p Mike
174  * Asteroid work, intermediate checkin to resolve compile errors.
175  * 
176  * 95    2/05/98 9:21p John
177  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
178  * game.
179  * 
180  * 94    2/05/98 12:51a Mike
181  * Early asteroid stuff.
182  * 
183  * 93    2/03/98 6:01p Andsager
184  * Fix excessive rotvel in debris_create.  Check using physics function
185  * check_rotvel_limit.
186  * 
187  * 92    2/03/98 11:14a Andsager
188  * Temp check in to stop debris being created with excess rotvel
189  * 
190  * 91    2/02/98 4:45p Mike
191  * Increase translational and rotational velocity imparted to debris
192  * pieces at request of Adam.
193  * 
194  * 90    1/30/98 2:56p John
195  * Made debris arcs jump around.  Made only 2/3 of the chunks have arcing
196  * 
197  * 89    1/30/98 11:48a John
198  * Made debris arcs cast light.  Added sound effects for them.
199  * 
200  * 88    1/29/98 5:50p John
201  * Made electrical arcing on debris pieces be persistent from frame to
202  * frame
203  * 
204  * 87    1/29/98 8:39a Andsager
205  * Changed mass and moment of intertia based area vs. volume
206  * 
207  * 86    1/27/98 11:02a John
208  * Added first rev of sparks.   Made all code that calls model_render call
209  * clear_instance first.   Made debris pieces not render by default when
210  * clear_instance is called.
211  * 
212  * 85    1/24/98 4:49p Lawrance
213  * Only delete hull piece if you can find an old one that isn't already
214  * about to die.
215  * 
216  * 84    1/23/98 5:06p John
217  * Took L out of vertex structure used B (blue) instead.   Took all small
218  * fireballs out of fireball types and used particles instead.  Fixed some
219  * debris explosion things.  Restructured fireball code.   Restructured
220  * some lighting code.   Made dynamic lighting on by default. Made groups
221  * of lasers only cast one light.  Made fireballs not cast light.
222  * 
223  * $NoKeywords: $
224  */
225
226 #include "3d.h"
227 #include "bmpman.h"
228 #include "object.h"
229 #include "debris.h"
230 #include "fireballs.h"
231 #include "radar.h"
232 #include "missionparse.h"               // For MAX_SPECIES_NAMES
233 #include "gamesnd.h"
234 #include "objectsnd.h"
235 #include "linklist.h"
236 #include "systemvars.h"
237 #include "multi.h"
238 #include "multimsgs.h"
239 #include "particle.h"
240 #include "freespace.h"
241 #include "multiutil.h"
242 #include "objcollide.h"
243 #include "timer.h"
244
245 #define MAX_LIFE                                                                        10.0f
246 #define MIN_RADIUS_FOR_PERSISTANT_DEBRIS        50              // ship radius at which debris from it becomes persistant
247 #define DEBRIS_SOUND_DELAY                                              2000    // time to start debris sound after created
248
249 // limit the number of hull debris chunks that can exist.  
250 #define MAX_HULL_PIECES         10
251 int             Num_hull_pieces;                // number of hull pieces in existance
252 debris  Hull_debris_list;               // head of linked list for hull debris chunks, for quick search
253
254 debris Debris[MAX_DEBRIS_PIECES];
255
256 int Num_debris_pieces = 0;
257 int Debris_inited = 0;
258
259 int Debris_model = -1;
260 int Debris_vaporize_model = -1;
261 int Debris_num_submodels = 0;
262 char * Debris_texture_files[MAX_SPECIES_NAMES] = { 
263         NOX("debris01a"),       // Terran
264         NOX("debris01b"),       // Species B
265         NOX("debris01c"),       // Shivan
266         };
267
268 int Debris_textures[MAX_SPECIES_NAMES];
269
270 #define MAX_DEBRIS_DIST                                 10000.0f                        //      Debris goes away if it's this far away.
271 #define DEBRIS_DISTANCE_CHECK_TIME              (10*1000)               //      Check every 10 seconds.
272 #define DEBRIS_INDEX(dp) (dp-Debris)
273
274 #define MAX_SPEED_SMALL_DEBRIS          200                                     // maximum velocity of small debris piece
275 #define MAX_SPEED_BIG_DEBRIS                    150                                     // maximum velocity of big debris piece
276 #define MAX_SPEED_CAPITAL_DEBRIS        100                                     // maximum velocity of capital debris piece
277 #define DEBRIS_SPEED_DEBUG
278
279 // ---------------------------------------------------------------------------------------
280 // debris_start_death_roll()
281 //
282 //      Start the sequence of a piece of debris writhing in unholy agony!!!
283 //
284 static void debris_start_death_roll(object *debris_obj, debris *debris_p)
285 {
286         if (debris_p->is_hull)  {
287                 // tell everyone else to blow up the piece of debris
288                 if( MULTIPLAYER_MASTER )
289                         send_debris_update_packet(debris_obj,DEBRIS_UPDATE_NUKE);
290
291                 int fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
292                 fireball_create( &debris_obj->pos, fireball_type, OBJ_INDEX(debris_obj), debris_obj->radius*1.75f);
293
294                 // only play debris destroy sound if hull piece and it has been around for at least 2 seconds
295                 if ( Missiontime > debris_p->time_started + 2*F1_0 ) {
296                         snd_play_3d( &Snds[SND_MISSILE_IMPACT1], &debris_obj->pos, &View_position, debris_obj->radius );
297                         
298                 }
299         }
300
301         debris_obj->flags |= OF_SHOULD_BE_DEAD;
302 //      demo_do_flag_dead(OBJ_INDEX(debris_obj));
303 }
304
305 // ---------------------------------------------------------------------------------------
306 // debris_init()
307 //
308 // This will get called at the start of each level.
309 //
310 void debris_init()
311 {
312         int i;
313
314         if ( !Debris_inited ) {
315                 Debris_inited = 1;
316         }
317
318         Debris_model = -1;
319         Debris_vaporize_model = -1;
320         Debris_num_submodels = 0;
321                 
322         // Reset everything between levels
323         Num_debris_pieces = 0;
324         for (i=0; i<MAX_DEBRIS_PIECES; i++ )    {
325                 Debris[i].flags = 0;
326                 Debris[i].sound_delay = 0;
327         }
328                 
329         Num_hull_pieces = 0;
330         list_init(&Hull_debris_list);
331 }
332
333 // Page in debris bitmaps at level load
334 void debris_page_in()
335 {
336         int i;
337
338         Debris_model = model_load( NOX("debris01.pof"), 0, NULL );
339         if (Debris_model>-1)    {
340                 polymodel * pm;
341                 pm = model_get(Debris_model);
342                 Debris_num_submodels = pm->n_models;
343         }
344
345 #ifndef MAKE_FS1
346         Debris_vaporize_model = model_load( NOX("debris02.pof"), 0, NULL );
347 #endif
348
349         for (i=0; i<MAX_SPECIES_NAMES; i++ )    {
350                 nprintf(( "Paging", "Paging in debris texture '%s'\n", Debris_texture_files[i] ));
351                 Debris_textures[i] = bm_load( Debris_texture_files[i] );
352                 if ( Debris_textures[i] < 0 ) { 
353                         Warning( LOCATION, "Couldn't load species %d debris\ntexture, '%s'\n", i, Debris_texture_files[i] );
354                 }
355                 bm_page_in_texture( Debris_textures[i] );
356         }
357         
358 }
359
360 MONITOR(NumSmallDebrisRend);
361 MONITOR(NumHullDebrisRend);
362
363 // ---------------------------------------------------------------------------------------
364 // debris_render()
365 //
366 //
367 void debris_render(object * obj)
368 {
369         int                     i, num, swapped;
370         polymodel       *pm;
371         debris          *db;
372
373
374         swapped = -1;
375         pm = NULL;      
376         num = obj->instance;
377
378         Assert(num >= 0 && num < MAX_DEBRIS_PIECES );
379         db = &Debris[num];
380
381         Assert( db->flags & DEBRIS_USED );
382
383         // Swap in a different texture depending on the species
384         if ( (db->species > -1) && (db->species < MAX_SPECIES_NAMES) )  {
385
386                 pm = model_get( db->model_num );
387
388                 if ( pm && (pm->n_textures == 1) ) {
389                         swapped = pm->textures[0];
390                         pm->textures[0] = Debris_textures[db->species];
391                 }
392         }
393
394         model_clear_instance( db->model_num );
395
396         // Only render electrical arcs if within 500m of the eye (for a 10m piece)
397         if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f )        {
398                 for (i=0; i<MAX_DEBRIS_ARCS; i++ )      {
399                         if ( timestamp_valid( db->arc_timestamp[i] ) )  {
400                                 model_add_arc( db->model_num, db->submodel_num, &db->arc_pts[i][0], &db->arc_pts[i][1], MARC_TYPE_NORMAL );
401                         }
402                 }
403         }
404
405         if ( db->is_hull )      {
406                 MONITOR_INC(NumHullDebrisRend,1);
407                 submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos );
408         } else {
409                 MONITOR_INC(NumSmallDebrisRend,1);
410                 submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos, MR_NO_LIGHTING );
411         }
412
413         if ((swapped!=-1) && pm)        {
414                 pm->textures[0] = swapped;
415         }
416 }
417
418 // Removed the DEBRIS_EXPIRE flag, and remove item from Hull_debris_list
419 void debris_clear_expired_flag(debris *db)
420 {
421         if ( db->flags & DEBRIS_EXPIRE ) {
422                 db->flags &= ~DEBRIS_EXPIRE;
423                 if ( db->is_hull ) {
424                         Num_hull_pieces--;
425                         list_remove(Hull_debris_list, db);
426                         Assert( Num_hull_pieces >= 0 );
427                 }
428         }
429 }
430
431 // ---------------------------------------------------------------------------------------
432 // debris_delete()
433 //
434 // Delete the debris object.  This is only ever called via obj_delete().  Do not call directly.
435 // Use debris_start_death_roll() if you want to force a debris piece to die.
436 //
437 void debris_delete( object * obj )
438 {
439         int             num;
440         debris  *db;
441
442         num = obj->instance;
443         Assert( Debris[num].objnum == OBJ_INDEX(obj));
444
445         db = &Debris[num];
446
447         Assert( Num_debris_pieces >= 0 );
448         if ( db->is_hull && (db->flags & DEBRIS_EXPIRE) ) {
449                 debris_clear_expired_flag(db);
450         }
451
452         db->flags = 0;
453         Num_debris_pieces--;
454 }
455
456 //      If debris piece *db is far away from all players, make it go away very soon.
457 //      In single player game, delete if MAX_DEBRIS_DIST from player.
458 //      In multiplayer game, delete if MAX_DEBRIS_DIST from all players.
459 void maybe_delete_debris(debris *db)
460 {
461         object  *objp;
462
463         if (timestamp_elapsed(db->next_distance_check)) {
464                 if (!(Game_mode & GM_MULTIPLAYER)) {            //      In single player game, just check against player.
465                         if (vm_vec_dist_quick(&Player_obj->pos, &Objects[db->objnum].pos) > MAX_DEBRIS_DIST)
466                                 db->lifeleft = 0.1f;
467                         else
468                                 db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
469                 } else {
470                         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
471                                 if (objp->flags & OF_PLAYER_SHIP) {
472                                         if (vm_vec_dist_quick(&objp->pos, &Objects[db->objnum].pos) < MAX_DEBRIS_DIST) {
473                                                 db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
474                                                 return;
475                                         }
476                                 }
477                         }
478                         db->lifeleft = 0.1f;
479                 }
480         }
481 }
482
483 // broke debris_move into debris_process_pre and debris_process_post as was done with all
484 // *_move functions on 8/13 by MK and MA.
485 void debris_process_pre( object *objp, float frame_time)
486 {
487 }
488
489 MONITOR(NumSmallDebris);
490 MONITOR(NumHullDebris);
491
492 // ---------------------------------------------------------------------------------------
493 // debris_process_post()
494 //
495 // Do various updates to debris:  check if time to die, start fireballs
496 //
497 // parameters:          obj                     =>              pointer to debris object
498 //                                              frame_time      =>              time elapsed since last debris_move() called
499 //
500 //      Maybe delete debris if it's very far away from player.
501 void debris_process_post(object * obj, float frame_time)
502 {
503         int i, num;
504         num = obj->instance;
505
506         int objnum = OBJ_INDEX(obj);
507         Assert( Debris[num].objnum == objnum );
508         debris *db = &Debris[num];
509
510         if ( db->is_hull ) {
511                 MONITOR_INC(NumHullDebris,1);
512                 radar_plot_object( obj );
513
514                 if ( timestamp_elapsed(db->sound_delay) ) {
515                         obj_snd_assign(objnum, SND_DEBRIS, &vmd_zero_vector, 0);
516                         db->sound_delay = 0;
517                 }
518         } else {
519                 MONITOR_INC(NumSmallDebris,1);
520         }
521
522         if ( db->lifeleft >= 0.0f) {
523                 db->lifeleft -= frame_time;
524                 if ( db->lifeleft < 0.0f )      {
525                         debris_start_death_roll(obj, db);
526                 }
527         }
528
529         maybe_delete_debris(db);        //      Make this debris go away if it's very far away.
530
531         // ================== DO THE ELECTRIC ARCING STUFF =====================
532         if ( db->arc_frequency <= 0 )   {
533                 return;                 // If arc_frequency <= 0, this piece has no arcs on it
534         }
535
536         if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball))      {
537
538                 // start the next fireball up in the next 50 - 100 ms
539                 //db->next_fireball = timestamp_rand(60,80);            
540
541                 db->next_fireball = timestamp_rand(db->arc_frequency,db->arc_frequency*2 );
542                 db->arc_frequency += 100;       
543
544                 if (db->is_hull)        {
545
546                         int n, n_arcs = ((rand()>>5) % 3)+1;            // Create 1-3 sparks
547
548                         vector v1, v2, v3, v4;
549                         submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 );
550                         submodel_get_two_random_points( db->model_num, db->submodel_num, &v3, &v4 );
551
552                         n = 0;
553
554                         int a = 100, b = 1000;
555                         int lifetime = (myrand()%((b)-(a)+1))+(a);
556
557                         // Create the spark effects
558                         for (i=0; i<MAX_DEBRIS_ARCS; i++ )      {
559                                 if ( !timestamp_valid( db->arc_timestamp[i] ) ) {
560                                         //db->arc_timestamp[i] = timestamp_rand(400,1000);      // live up to a second
561                                         db->arc_timestamp[i] = timestamp(lifetime);     // live up to a second
562
563                                         switch( n )     {
564                                         case 0:
565                                                 db->arc_pts[i][0] = v1;
566                                                 db->arc_pts[i][1] = v2;
567                                                 break;
568                                         case 1:
569                                                 db->arc_pts[i][0] = v2;
570                                                 db->arc_pts[i][1] = v3;
571                                                 break;
572
573                                         case 2:
574                                                 db->arc_pts[i][0] = v2;
575                                                 db->arc_pts[i][1] = v4;
576                                                 break;
577
578                                         default:
579                                                 Int3();
580                                         }
581                                                 
582                                         n++;
583                                         if ( n == n_arcs )
584                                                 break;  // Don't need to create anymore
585                                 }
586                         }
587
588         
589                         // rotate v2 out of local coordinates into world.
590                         // Use v2 since it is used in every bolt.  See above switch().
591                         vector snd_pos;
592                         vm_vec_unrotate(&snd_pos, &v2, &obj->orient);
593                         vm_vec_add2(&snd_pos, &obj->pos );
594
595                         //Play a sound effect
596                         if ( lifetime > 750 )   {
597                                 // 1.00 second effect
598                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_05], &snd_pos, &View_position, obj->radius );
599                         } else if ( lifetime >  500 )   {
600                                 // 0.75 second effect
601                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_04], &snd_pos, &View_position, obj->radius );
602                         } else if ( lifetime >  250 )   {
603                                 // 0.50 second effect
604                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_03], &snd_pos, &View_position, obj->radius );
605                         } else if ( lifetime >  100 )   {
606                                 // 0.25 second effect
607                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_02], &snd_pos, &View_position, obj->radius );
608                         } else {
609                                 // 0.10 second effect
610                                 snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius );
611                         }
612
613                 }
614
615
616
617         }
618
619         for (i=0; i<MAX_DEBRIS_ARCS; i++ )      {
620                 if ( timestamp_valid( db->arc_timestamp[i] ) )  {
621                         if ( timestamp_elapsed( db->arc_timestamp[i] ) )        {
622                                 // Kill off the spark
623                                 db->arc_timestamp[i] = timestamp(-1);
624                         } else {
625                                 // Maybe move a vertex....  20% of the time maybe?
626                                 int mr = myrand();
627                                 if ( mr < RAND_MAX/5 )  {
628                                         vector v1, v2;
629                                         submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 );
630                                         db->arc_pts[i][mr % 2] = v1;
631                                 }
632                         }
633                 }
634         }
635
636 }
637
638 // ---------------------------------------------------------------------------------------
639 // debris_find_oldest()
640 //
641 // Locate the oldest hull debris chunk.  Search through the Hull_debris_list, which is a list
642 // of all the hull debris chunks.
643 //
644 int debris_find_oldest()
645 {
646         int             oldest_index;
647         fix             oldest_time;
648         debris  *db;
649
650         oldest_index = -1;
651         oldest_time = 0x7fffffff;
652
653         for ( db = GET_FIRST(&Hull_debris_list); db != END_OF_LIST(&Hull_debris_list); db = GET_NEXT(db) ) {
654                 if ( (db->time_started < oldest_time) && !(Objects[db->objnum].flags & OF_SHOULD_BE_DEAD) ) {
655                         oldest_index = DEBRIS_INDEX(db);
656                         oldest_time = db->time_started;
657                 }
658         }
659
660         return oldest_index;
661 }
662
663 #define DEBRIS_ROTVEL_SCALE     5.0f
664 void calc_debris_physics_properties( physics_info *pi, vector *min, vector *max );
665 // ---------------------------------------------------------------------------------------
666 // debris_create()
667 //
668 // Create debris from an object
669 //
670 //      exp_force:      Explosion force, used to assign velocity to pieces.
671 //                                      1.0f assigns velocity like before.  2.0f assigns twice as much to non-inherited part of velocity
672 object *debris_create(object *source_obj, int model_num, int submodel_num, vector *pos, vector *exp_center, int hull_flag, float exp_force)
673 {
674         int             i, n, objnum, parent_objnum;
675         object  *obj;
676         ship            *shipp;
677         debris  *db;    
678         polymodel *pm;
679         int vaporize;
680
681         parent_objnum = OBJ_INDEX(source_obj);
682
683         Assert( (source_obj->type == OBJ_SHIP ) || (source_obj->type == OBJ_GHOST));
684         Assert( source_obj->instance >= 0 && source_obj->instance < MAX_SHIPS );        
685         shipp = &Ships[source_obj->instance];
686         vaporize = (shipp->flags &SF_VAPORIZE);
687
688         if ( !hull_flag )       {
689                 // Make vaporize debris seen from farther away
690                 float dist = vm_vec_dist_quick( pos, &Eye_position );
691                 if (vaporize) {
692                         dist /= 2.0f;
693                 }
694                 if ( dist > 200.0f ) {
695                         //mprintf(( "Not creating debris that is %.1f m away\n", dist ));
696                         return NULL;
697                 }
698         }
699
700         if ( hull_flag && (Num_hull_pieces >= MAX_HULL_PIECES ) ) {
701                 // cause oldest hull debris chunk to blow up
702                 n = debris_find_oldest();
703                 if ( n >= 0 ) {
704                         debris_start_death_roll(&Objects[Debris[n].objnum], &Debris[n] );
705                 }
706         }
707
708         for (n=0; n<MAX_DEBRIS_PIECES; n++ ) {
709                 if ( !(Debris[n].flags & DEBRIS_USED) )
710                         break;
711         }
712
713         if ( n == MAX_DEBRIS_PIECES ) {
714                 nprintf(("Warning","Frame %i: Could not create debris, no more slots left\n", Framecount));
715                 return NULL;
716         }
717
718         db = &Debris[n];
719
720         // Create Debris piece n!
721         if ( hull_flag ) {
722                 if (rand() < RAND_MAX/6)        // Make some pieces blow up shortly after explosion.
723                         db->lifeleft = 2.0f * ((float) myrand()/(float) RAND_MAX) + 0.5f;
724                 else
725                         db->lifeleft = -1.0f;           // large hull pieces stay around forever
726         }       else {
727                 db->lifeleft = (i2fl(myrand())/i2fl(RAND_MAX))*2.0f+0.1f;
728         }
729
730         // increase lifetime for vaporized debris
731         if (vaporize) {
732                 db->lifeleft *= 3.0f;
733         }
734         db->flags |= DEBRIS_USED;
735         db->is_hull = hull_flag;
736         db->source_objnum = parent_objnum;
737         db->source_sig = source_obj->signature;
738         db->ship_info_index = shipp->ship_info_index;
739         db->team = shipp->team;
740         db->fire_timeout = 0;   // if not changed, timestamp_elapsed() will return false
741         db->time_started = Missiontime;
742         db->species = Ship_info[shipp->ship_info_index].species;
743         db->next_distance_check = (myrand() % 2000) + 4*DEBRIS_DISTANCE_CHECK_TIME;
744
745         for (i=0; i<MAX_DEBRIS_ARCS; i++ )      {
746                 db->arc_timestamp[i] = timestamp(-1);
747                 //      vector  arc_pts[MAX_DEBRIS_ARCS][2];            // The endpoints of each arc
748         }
749
750         if ( db->is_hull )      {
751                 // Only make 1/2 of the pieces have arcs
752                 if ( myrand() < RAND_MAX*2/3 )  {
753                         db->arc_frequency = 1000;
754                 } else {
755                         db->arc_frequency = 0;
756                 }
757         } else {
758                 db->arc_frequency = 0;
759         }
760
761         if ( model_num < 0 )    {
762                 if (vaporize) {
763                         db->model_num = Debris_vaporize_model;
764                 } else {
765                         db->model_num = Debris_model;
766                 }
767                 db->submodel_num = (myrand()>>4) % Debris_num_submodels;
768         } else {
769                 db->model_num = model_num;
770                 db->submodel_num = submodel_num;
771         }
772         float radius = submodel_get_radius( db->model_num, db->submodel_num );
773
774         db->next_fireball = timestamp_rand(500,2000);   //start one 1/2 - 2 secs later
775
776         if ( pos == NULL )
777                 pos = &source_obj->pos;
778
779         uint flags = OF_RENDERS | OF_PHYSICS;
780         if ( hull_flag )        
781                 flags |= OF_COLLIDES;
782         objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &source_obj->orient, pos, radius, flags );
783         if ( objnum == -1 ) {
784                 mprintf(("Couldn't create debris object -- out of object slots\n"));
785                 return NULL;
786         }
787
788         db->objnum = objnum;
789         
790         obj = &Objects[objnum];
791
792         // assign the network signature.  The signature will be 0 for non-hull pieces, but since that
793         // is our invalid signature, it should be okay.
794         obj->net_signature = 0;
795         if ( (Game_mode & GM_MULTIPLAYER) && hull_flag ) {
796                 obj->net_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
797         }
798
799         // -- No long need shield: bset_shield_strength(obj, 100.0f);           //      Hey!  Set to some meaningful value!
800
801         if (source_obj->type == OBJ_SHIP) {
802                 obj->hull_strength = Ship_info[Ships[source_obj->instance].ship_info_index].initial_hull_strength/8.0f;
803         } else
804                 obj->hull_strength = 10.0f;
805
806         Num_debris_pieces++;
807
808         vector rotvel, radial_vel, to_center;
809
810         if ( exp_center )
811                 vm_vec_sub( &to_center,pos, exp_center );
812         else
813                 vm_vec_zero(&to_center);
814
815         float scale;
816
817         if ( hull_flag )        {
818                 float t;
819                 scale = exp_force * i2fl((myrand()%20) + 10);   // for radial_vel away from location of blast center
820                 db->sound_delay = timestamp(DEBRIS_SOUND_DELAY);
821
822                 // set up physics mass and I_inv for hull debris pieces
823                 pm = model_get(model_num);
824                 vector *min, *max;
825                 min = &pm->submodel[submodel_num].min;
826                 max = &pm->submodel[submodel_num].max;
827                 calc_debris_physics_properties( &obj->phys_info, min, max );
828
829                 // limit the amount of time that fireballs appear
830                 // let fireball length be linked to radius of ship.  Range is .33 radius => 3.33 radius seconds.
831                 t = 1000*Objects[db->source_objnum].radius/3 + myrand()%(fl2i(1000*3*Objects[db->source_objnum].radius));
832                 db->fire_timeout = timestamp(fl2i(t));          // fireballs last from 5 - 30 seconds
833                 
834                 if ( Objects[db->source_objnum].radius < MIN_RADIUS_FOR_PERSISTANT_DEBRIS ) {
835                         db->flags |= DEBRIS_EXPIRE;     // debris can expire
836                         Num_hull_pieces++;
837                         list_append(&Hull_debris_list, db);
838                 } else {
839                         nprintf(("Alan","A forever chunk of debris was created from ship with radius %f\n",Objects[db->source_objnum].radius));
840                 }
841         }
842         else {
843                 scale = exp_force * i2fl((myrand()%20) + 10);   // for radial_vel away from blast center (non-hull)
844         }
845
846         if ( vm_vec_mag_squared( &to_center ) < 0.1f )  {
847                 vm_vec_rand_vec_quick(&radial_vel);
848                 vm_vec_scale(&radial_vel, scale );
849         }
850         else {
851                 vm_vec_normalize(&to_center);
852                 vm_vec_copy_scale(&radial_vel, &to_center, scale );
853         }
854
855         //      MK: This next line causes debris pieces to get between 50% and 100% of the parent ship's
856         //      velocity.  What would be very cool is if the rotational velocity of the parent would become 
857         // translational velocity of the debris piece.  This would be based on the location of the debris
858         //      piece in the parent object.
859
860         // DA: here we need to vel_from_rot = w x to_center, where w is world is unrotated to world coords and offset is the 
861         // displacement fromt the center of the parent object to the center of the debris piece
862
863         vector world_rotvel, vel_from_rotvel;
864         vm_vec_unrotate ( &world_rotvel, &source_obj->phys_info.rotvel, &source_obj->orient );
865         vm_vec_crossprod ( &vel_from_rotvel, &world_rotvel, &to_center );
866         vm_vec_scale ( &vel_from_rotvel, DEBRIS_ROTVEL_SCALE);
867
868         vm_vec_add (&obj->phys_info.vel, &radial_vel, &source_obj->phys_info.vel);
869         vm_vec_add2(&obj->phys_info.vel, &vel_from_rotvel);
870
871 #ifdef DEBRIS_SPEED_DEBUG
872         // check that debris is not created with too high a velocity
873         if (hull_flag) {
874                 int ship_info_flag = Ship_info[Ships[source_obj->instance].ship_info_index].flags;
875                 if (ship_info_flag & (SIF_SMALL_SHIP | SIF_NOT_FLYABLE | SIF_HARMLESS)) {
876                         if (vm_vec_mag_squared(&obj->phys_info.vel) > MAX_SPEED_SMALL_DEBRIS*MAX_SPEED_SMALL_DEBRIS) {
877                                 float scale = MAX_SPEED_SMALL_DEBRIS / vm_vec_mag(&obj->phys_info.vel);
878                                 vm_vec_scale(&obj->phys_info.vel, scale);
879                         }
880                 } else if (ship_info_flag & SIF_BIG_SHIP) {
881                         if (vm_vec_mag_squared(&obj->phys_info.vel) > MAX_SPEED_BIG_DEBRIS*MAX_SPEED_BIG_DEBRIS) {
882                                 float scale = MAX_SPEED_BIG_DEBRIS / vm_vec_mag(&obj->phys_info.vel);
883                                 vm_vec_scale(&obj->phys_info.vel, scale);
884                         }
885                 } else if (ship_info_flag & SIF_HUGE_SHIP) {
886                         if (vm_vec_mag_squared(&obj->phys_info.vel) > MAX_SPEED_CAPITAL_DEBRIS*MAX_SPEED_CAPITAL_DEBRIS) {
887                                 float scale = MAX_SPEED_CAPITAL_DEBRIS / vm_vec_mag(&obj->phys_info.vel);
888                                 vm_vec_scale(&obj->phys_info.vel, scale);
889                         }
890                 } else {
891                         Warning(LOCATION, "Ship has info flag that is not among the following:  SMALL, NOT_FLYABLE, HARMLESS, BIG, CAPITAL, SUPERCAP");
892                 }
893         }
894 #endif
895
896 //      vm_vec_scale_add(&obj->phys_info.vel, &radial_vel, &source_obj->phys_info.vel, frand()/2.0f + 0.5f);
897 //      nprintf(("Andsager","object vel from rotvel: %0.2f, %0.2f, %0.2f\n",vel_from_rotvel.x, vel_from_rotvel.y, vel_from_rotvel.z));
898
899 // make sure rotational velocity does not get too high
900         if (radius < 1.0) {
901                 radius = 1.0f;
902         }
903
904         scale = ( 6.0f + i2fl(myrand()%4) ) / radius;
905
906         vm_vec_rand_vec_quick(&rotvel);
907         vm_vec_scale(&rotvel, scale);
908
909         obj->phys_info.flags |= PF_DEAD_DAMP;
910         obj->phys_info.rotvel = rotvel;
911         check_rotvel_limit( &obj->phys_info );
912
913
914         // blow out his reverse thrusters. Or drag, same thing.
915         obj->phys_info.rotdamp = 10000.0f;
916         obj->phys_info.side_slip_time_const = 10000.0f;
917         obj->phys_info.flags |= (PF_REDUCED_DAMP | PF_DEAD_DAMP);       // set damping equal for all axis and not changable
918
919         vm_vec_zero(&obj->phys_info.max_vel);           // make so he can't turn on his own VOLITION anymore.
920         vm_vec_zero(&obj->phys_info.max_rotvel);        // make so he can't change speed on his own VOLITION anymore.
921
922
923         // ensure vel is valid
924         Assert( !vm_is_vec_nan(&obj->phys_info.vel) );
925
926 //      if ( hull_flag )        {
927 //              vm_vec_zero(&obj->phys_info.vel);
928 //              vm_vec_zero(&obj->phys_info.rotvel);
929 //      }
930
931         return obj;
932 }
933
934 // ---------------------------------------------------------------------------------------
935 // debris_hit()
936 //
937 //      Alas, poor debris_obj got whacked.  Fortunately, we know who did it, where and how hard, so we
938 //      can do something about it.
939 //
940 void debris_hit(object *debris_obj, object *other_obj, vector *hitpos, float damage)
941 {
942         debris  *debris_p = &Debris[debris_obj->instance];
943
944
945         // Do a little particle spark shower to show we hit
946         {
947                 particle_emitter        pe;
948
949                 pe.pos = *hitpos;                                                               // Where the particles emit from
950                 pe.vel = debris_obj->phys_info.vel;             // Initial velocity of all the particles
951
952                 vector tmp_norm;
953                 vm_vec_sub( &tmp_norm, hitpos, &debris_obj->pos );
954                 vm_vec_normalize_safe(&tmp_norm);
955                         
956                 pe.normal = tmp_norm;                   // What normal the particle emit around
957                 pe.normal_variance = 0.3f;              //      How close they stick to that normal 0=good, 1=360 degree
958                 pe.min_rad = 0.20f;                             // Min radius
959                 pe.max_rad = 0.40f;                             // Max radius
960
961                 // Sparks for first time at this spot
962                 pe.num_low = 10;                                // Lowest number of particles to create
963                 pe.num_high = 10;                       // Highest number of particles to create
964                 pe.normal_variance = 0.3f;              //      How close they stick to that normal 0=good, 1=360 degree
965                 pe.min_vel = 0.0f;                              // How fast the slowest particle can move
966                 pe.max_vel = 10.0f;                             // How fast the fastest particle can move
967                 pe.min_life = 0.25f;                    // How long the particles live
968                 pe.max_life = 0.75f;                    // How long the particles live
969                 particle_emit( &pe, PARTICLE_FIRE, 0 );
970         }
971
972         // multiplayer clients bail here
973         if(MULTIPLAYER_CLIENT){
974                 return;
975         }
976
977         if ( damage < 0.0f ) {
978                 damage = 0.0f;
979         }
980
981         debris_obj->hull_strength -= damage;
982
983         if (debris_obj->hull_strength < 0.0f) {
984                 debris_start_death_roll(debris_obj, debris_p );
985         } else {
986                 // otherwise, give all the other players an update on the debris
987                 if(MULTIPLAYER_MASTER){
988                         send_debris_update_packet(debris_obj,DEBRIS_UPDATE_UPDATE);
989                 }
990         }
991 }
992
993 // ---------------------------------------------------------------------------------------
994 // debris_check_collision()
995 //
996 //      See if poor debris object *obj got whacked by evil *other_obj at point *hitpos.
997 // NOTE: debris_hit_info pointer NULL for debris:weapon collision, otherwise debris:ship collision.
998 //      Return true if hit, else return false.
999 //
1000 #ifndef PLAT_UNIX
1001 #pragma warning ( push )
1002 #pragma warning ( disable : 4701 )
1003 #endif
1004 int debris_check_collision(object *pdebris, object *other_obj, vector *hitpos, collision_info_struct *debris_hit_info)
1005 {
1006         mc_info mc;
1007         int             num;
1008
1009         Assert( pdebris->type == OBJ_DEBRIS );
1010
1011         num = pdebris->instance;
1012         Assert( num >= 0 );
1013
1014         Assert( Debris[num].objnum == OBJ_INDEX(pdebris));      
1015
1016         // debris_hit_info NULL - so debris-weapon collision
1017         if ( debris_hit_info == NULL ) {
1018                 // debris weapon collision
1019                 Assert( other_obj->type == OBJ_WEAPON );
1020                 mc.model_num = Debris[num].model_num;   // Fill in the model to check
1021                 mc.submodel_num = Debris[num].submodel_num;
1022                 model_clear_instance( mc.model_num );
1023                 mc.orient = &pdebris->orient;                                   // The object's orient
1024                 mc.pos = &pdebris->pos;                                                 // The object's position
1025                 mc.p0 = &other_obj->last_pos;                           // Point 1 of ray to check
1026                 mc.p1 = &other_obj->pos;                                        // Point 2 of ray to check
1027                 mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL);
1028
1029                 if (model_collide(&mc)) {
1030                         *hitpos = mc.hit_point_world;
1031                 }
1032
1033                 return mc.num_hits;
1034         }
1035         
1036         // debris ship collision -- use debris_hit_info to calculate physics
1037         object *ship_obj = other_obj;
1038         Assert( ship_obj->type == OBJ_SHIP );
1039
1040         object *heavy = debris_hit_info->heavy;
1041         object *light = debris_hit_info->light;
1042         object *heavy_obj = heavy;
1043         object *light_obj = light;
1044
1045         vector zero, p0, p1;
1046         vm_vec_zero(&zero);
1047         vm_vec_sub(&p0, &light->last_pos, &heavy->last_pos);
1048         vm_vec_sub(&p1, &light->pos, &heavy->pos);
1049
1050         mc.pos = &zero;                                                         // The object's position
1051         mc.p0 = &p0;                                                                    // Point 1 of ray to check
1052         mc.p1 = &p1;                                                                    // Point 2 of ray to check
1053
1054         // find the light object's position in the heavy object's reference frame at last frame and also in this frame.
1055         vector p0_temp, p0_rotated;
1056                 
1057         // Collision detection from rotation enabled if at rotaion is less than 30 degree in frame
1058         // This should account for all ships
1059         if ( (vm_vec_mag_squared(&heavy->phys_info.rotvel) * flFrametime*flFrametime) < (PI*PI/36) ) {
1060                 // collide_rotate calculate (1) start position and (2) relative velocity
1061                 debris_hit_info->collide_rotate = 1;
1062                 vm_vec_rotate(&p0_temp, &p0, &heavy->last_orient);
1063                 vm_vec_unrotate(&p0_rotated, &p0_temp, &heavy->orient);
1064                 mc.p0 = &p0_rotated;                            // Point 1 of ray to check
1065                 vm_vec_sub(&debris_hit_info->light_rel_vel, &p1, &p0_rotated);
1066                 vm_vec_scale(&debris_hit_info->light_rel_vel, 1/flFrametime);
1067         } else {
1068                 debris_hit_info->collide_rotate = 0;
1069                 vm_vec_sub(&debris_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel);
1070         }
1071
1072         int mc_ret_val = 0;
1073
1074         if ( debris_hit_info->heavy == ship_obj ) {     // ship is heavier, so debris is sphere. Check sphere collision against ship poly model
1075                 mc.model_num = Ships[ship_obj->instance].modelnum;              // Fill in the model to check
1076                 mc.orient = &ship_obj->orient;                                                          // The object's orient
1077                 mc.radius = pdebris->radius;
1078                 mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
1079
1080                 // copy important data
1081                 int copy_flags = mc.flags;  // make a copy of start end positions of sphere in  big ship RF
1082                 vector copy_p0, copy_p1;
1083                 copy_p0 = *mc.p0;
1084                 copy_p1 = *mc.p1;
1085
1086                 // first test against the sphere - if this fails then don't do any submodel tests
1087                 mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE;
1088
1089                 int submodel_list[MAX_ROTATING_SUBMODELS];
1090                 int num_rotating_submodels = 0;
1091                 polymodel *pm;
1092
1093                 ship_model_start(ship_obj);
1094
1095                 if (model_collide(&mc)) {
1096
1097                         // Set earliest hit time
1098                         debris_hit_info->hit_time = FLT_MAX;
1099
1100                         // Do collision the cool new way
1101                         if ( debris_hit_info->collide_rotate ) {
1102                                 // We collide with the sphere, find the list of rotating submodels and test one at a time
1103                                 model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj);
1104
1105                                 // Get polymodel and turn off all rotating submodels, collide against only 1 at a time.
1106                                 pm = model_get(Ships[heavy_obj->instance].modelnum);
1107
1108                                 // turn off all rotating submodels and test for collision
1109                                 for (int i=0; i<num_rotating_submodels; i++) {
1110                                         pm->submodel[submodel_list[i]].blown_off = 1;
1111                                 }
1112
1113                                 // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
1114                                 mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
1115
1116                                 // check each submodel in turn
1117                                 for (int i=0; i<num_rotating_submodels; i++) {
1118                                         // turn on submodel for collision test
1119                                         pm->submodel[submodel_list[i]].blown_off = 0;
1120
1121                                         // set angles for last frame (need to set to prev to get p0)
1122                                         angles copy_angles = pm->submodel[submodel_list[i]].angs;
1123
1124                                         // find the start and end positions of the sphere in submodel RF
1125                                         pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs;
1126                                         world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos);
1127
1128                                         pm->submodel[submodel_list[i]].angs = copy_angles;
1129                                         world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos);
1130
1131                                         mc.p0 = &p0;
1132                                         mc.p1 = &p1;
1133                                         // mc.pos = zero        // in submodel RF
1134
1135                                         mc.orient = &vmd_identity_matrix;
1136                                         mc.submodel_num = submodel_list[i];
1137
1138                                         if ( model_collide(&mc) ) {
1139                                                 if ( mc.hit_dist < debris_hit_info->hit_time ) {
1140                                                         mc_ret_val = 1;
1141
1142                                                         // set up debris_hit_info common
1143                                                         set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_ROT_HIT);
1144                                                         model_find_world_point(&debris_hit_info->hit_pos, &mc.hit_point, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
1145
1146                                                         // set up debris_hit_info for rotating submodel
1147                                                         if (debris_hit_info->edge_hit == 0) {
1148                                                                 model_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1149                                                         }
1150
1151                                                         // find position in submodel RF of light object at collison
1152                                                         vector int_light_pos, diff;
1153                                                         vm_vec_sub(&diff, mc.p1, mc.p0);
1154                                                         vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist);
1155                                                         model_find_world_point(&debris_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
1156                                                 }
1157                                         }
1158                                         // Don't look at this submodel again
1159                                         pm->submodel[submodel_list[i]].blown_off = 1;
1160                                 }
1161
1162                         }
1163
1164                         // Recover and do usual ship_ship collision, but without rotating submodels
1165                         mc.flags = copy_flags;
1166                         *mc.p0 = copy_p0;
1167                         *mc.p1 = copy_p1;
1168                         mc.orient = &heavy_obj->orient;
1169
1170                         // usual ship_ship collision test
1171                         if ( model_collide(&mc) )       {
1172                                 // check if this is the earliest hit
1173                                 if (mc.hit_dist < debris_hit_info->hit_time) {
1174                                         mc_ret_val = 1;
1175
1176                                         set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1177
1178                                         // get collision normal if not edge hit
1179                                         if (debris_hit_info->edge_hit == 0) {
1180                                                 model_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1181                                         }
1182
1183                                         // find position in submodel RF of light object at collison
1184                                         vector diff;
1185                                         vm_vec_sub(&diff, mc.p1, mc.p0);
1186                                         vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1187
1188                                 }
1189                         }
1190
1191                         ship_model_stop( ship_obj );
1192                 }
1193
1194         } else {
1195                 // Debris is heavier obj
1196                 mc.model_num = Debris[num].model_num;           // Fill in the model to check
1197                 mc.submodel_num = Debris[num].submodel_num;
1198                 model_clear_instance( mc.model_num );
1199                 mc.orient = &pdebris->orient;                           // The object's orient
1200                 mc.radius = model_get_core_radius( Ships[ship_obj->instance].modelnum );
1201
1202                 // check for collision between debris model and ship sphere
1203                 mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL | MC_CHECK_SPHERELINE);
1204
1205                 mc_ret_val = model_collide(&mc);
1206
1207                 if (mc_ret_val) {
1208                         set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1209
1210                         // set normal if not edge hit
1211                         if ( !debris_hit_info->edge_hit ) {
1212                                 vm_vec_unrotate(&debris_hit_info->collision_normal, &mc.hit_normal, &heavy->orient);
1213                         }
1214
1215                         // find position in submodel RF of light object at collison
1216                         vector diff;
1217                         vm_vec_sub(&diff, mc.p1, mc.p0);
1218                         vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1219
1220                 }
1221         }
1222
1223
1224         if ( mc_ret_val )       {
1225
1226                 // SET PHYSICS PARAMETERS
1227                 // already have (hitpos - heavy) and light_cm_pos
1228                 // get heavy cm pos - already have light_cm_pos
1229                 debris_hit_info->heavy_collision_cm_pos = zero;
1230
1231                 // get r_heavy and r_light
1232                 debris_hit_info->r_heavy = debris_hit_info->hit_pos;
1233                 vm_vec_sub(&debris_hit_info->r_light, &debris_hit_info->hit_pos, &debris_hit_info->light_collision_cm_pos);
1234
1235                 // set normal for edge hit
1236                 if ( debris_hit_info->edge_hit ) {
1237                         vm_vec_copy_normalize(&debris_hit_info->collision_normal, &debris_hit_info->r_light);
1238                         vm_vec_negate(&debris_hit_info->collision_normal);
1239                 }
1240
1241                 // get world hitpos
1242                 vm_vec_add(hitpos, &debris_hit_info->heavy->pos, &debris_hit_info->r_heavy);
1243
1244                 return 1;
1245         } else {
1246                 // no hit
1247                 return 0;
1248         }
1249 }
1250 #ifndef PLAT_UNIX
1251 #pragma warning ( pop )
1252 #endif
1253
1254 // ---------------------------------------------------------------------------------------
1255 // debris_get_team()
1256 //
1257 //      Return the team field for a debris object
1258 //
1259 int debris_get_team(object *objp)
1260 {
1261         Assert( objp->type == OBJ_DEBRIS );
1262         Assert( objp->instance >= 0 && objp->instance < MAX_DEBRIS_PIECES );
1263         return Debris[objp->instance].team;
1264 }
1265
1266 // fills in debris physics properties when created, specifically mass and moment of inertia
1267 void calc_debris_physics_properties( physics_info *pi, vector *mins, vector *maxs )
1268 {
1269         float dx, dy, dz, mass;
1270         dx = maxs->xyz.x - mins->xyz.x;
1271         dy = maxs->xyz.y - mins->xyz.y;
1272         dz = maxs->xyz.z - mins->xyz.z;
1273
1274         // John, with new bspgen, just set pi->mass = mass
1275         mass = 0.12f * dx * dy * dz;
1276         pi->mass = (float) pow(mass, 0.6666667f) * 4.65f;
1277
1278         pi->I_body_inv.v.rvec.xyz.x = 12.0f / (pi->mass *  (dy*dy + dz*dz));
1279         pi->I_body_inv.v.rvec.xyz.y = 0.0f;
1280         pi->I_body_inv.v.rvec.xyz.z = 0.0f;
1281
1282         pi->I_body_inv.v.uvec.xyz.x = 0.0f;
1283         pi->I_body_inv.v.uvec.xyz.y = 12.0f / (pi->mass *  (dx*dx + dz*dz));
1284         pi->I_body_inv.v.uvec.xyz.z = 0.0f;
1285
1286         pi->I_body_inv.v.fvec.xyz.x = 0.0f;
1287         pi->I_body_inv.v.fvec.xyz.y = 0.0f;
1288         pi->I_body_inv.v.fvec.xyz.z = 12.0f / (pi->mass *  (dx*dx + dy*dy));
1289 }
1290