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/Debris/Debris.cpp $
15 * Code for the pieces of exploding object debris.
18 * Revision 1.5 2003/05/25 02:30:42 taylor
21 * Revision 1.4 2002/06/17 06:33:08 relnev
22 * ryan's struct patch for gcc 2.95
24 * Revision 1.3 2002/06/09 04:41:15 relnev
25 * added copyright header
27 * Revision 1.2 2002/05/28 08:52:03 relnev
28 * implemented two assembly stubs.
30 * cleaned up a few warnings.
32 * added a little demo hackery to make it progress a little farther.
34 * Revision 1.1.1.1 2002/05/03 03:28:08 root
38 * 16 8/01/99 1:13p Dave
39 * Fixed objsnd problem with hull debris pieces.
41 * 15 7/01/99 4:23p Dave
42 * Full support for multiple linked ambient engine sounds. Added "big
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.
49 * 13 5/18/99 11:50a Andsager
50 * Remove unused object type OBJ_GHOST_SAVE
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.
56 * 11 4/23/99 12:01p Johnson
59 * 10 2/26/99 4:14p Dave
60 * Put in the ability to have multiple shockwaves for ships.
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.
66 * 8 12/03/98 3:14p Andsager
67 * Check in code that checks rotating submodel actually has ship subsystem
69 * 7 11/19/98 11:07p Andsager
70 * Check in of physics and collision detection of rotating submodels
72 * 6 11/13/98 10:13a Andsager
73 * simplify collision code
75 * 5 11/05/98 5:55p Dave
76 * Big pass at reducing #includes
78 * 4 10/23/98 1:11p Andsager
79 * Make ship sparks emit correctly from rotating structures.
81 * 3 10/16/98 1:22p Andsager
82 * clean up header files
84 * 2 10/07/98 10:52a Dave
87 * 1 10/07/98 10:48a Dave
89 * 119 8/28/98 3:28p Dave
90 * EMP effect done. AI effects may need some tweaking as required.
92 * 118 5/03/98 5:41p Mike
93 * Add Framecount to nprintf.
95 * 117 4/15/98 10:00p Allender
96 * make debris have own signature set.
98 * 116 4/15/98 9:42a Adam
99 * added 2 more explosion types (1, actually, but placeholder for 2)
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
105 * 114 4/10/98 12:16p Allender
106 * fix ship hit kill and debris packets
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
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.
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.
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.
126 * 109 3/23/98 12:20p Andsager
127 * Enable collision from rotation in ship_debris and ship_debris
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.
136 * 107 3/12/98 6:47p John
137 * MAde arcs on debris further than objrad*50 not render.
139 * 106 3/09/98 2:10p Andsager
140 * Put in checks for debris (other) with excessive velocity.
142 * 105 2/26/98 10:07p Hoffoss
143 * Rewrote state saving and restoring to fix bugs and simplify the code.
145 * 104 2/22/98 12:19p John
146 * Externalized some strings
148 * 103 2/20/98 8:31p Lawrance
149 * Add radius parm to sound_play_3d()
151 * 102 2/10/98 6:43p Lawrance
152 * Moved asteroid code to a separate lib.
154 * 101 2/07/98 2:14p Mike
155 * Improve asteroid splitting. Add ship:asteroid collisions. Timestamp
156 * ship:debris collisions.
158 * 100 2/06/98 7:45p John
159 * Reversed order of asteroid models so blown up ones are smaller.
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
166 * 98 2/06/98 3:08p Mike
167 * More asteroid stuff, including resolving conflicts between the two
168 * asteroid_field structs!
170 * 97 2/06/98 12:25a Mike
171 * More asteroid stuff.
173 * 96 2/05/98 9:41p Mike
174 * Asteroid work, intermediate checkin to resolve compile errors.
176 * 95 2/05/98 9:21p John
177 * Some new Direct3D code. Added code to monitor a ton of stuff in the
180 * 94 2/05/98 12:51a Mike
181 * Early asteroid stuff.
183 * 93 2/03/98 6:01p Andsager
184 * Fix excessive rotvel in debris_create. Check using physics function
185 * check_rotvel_limit.
187 * 92 2/03/98 11:14a Andsager
188 * Temp check in to stop debris being created with excess rotvel
190 * 91 2/02/98 4:45p Mike
191 * Increase translational and rotational velocity imparted to debris
192 * pieces at request of Adam.
194 * 90 1/30/98 2:56p John
195 * Made debris arcs jump around. Made only 2/3 of the chunks have arcing
197 * 89 1/30/98 11:48a John
198 * Made debris arcs cast light. Added sound effects for them.
200 * 88 1/29/98 5:50p John
201 * Made electrical arcing on debris pieces be persistent from frame to
204 * 87 1/29/98 8:39a Andsager
205 * Changed mass and moment of intertia based area vs. volume
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.
212 * 85 1/24/98 4:49p Lawrance
213 * Only delete hull piece if you can find an old one that isn't already
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.
230 #include "fireballs.h"
232 #include "missionparse.h" // For MAX_SPECIES_NAMES
234 #include "objectsnd.h"
235 #include "linklist.h"
236 #include "systemvars.h"
238 #include "multimsgs.h"
239 #include "particle.h"
240 #include "freespace.h"
241 #include "multiutil.h"
242 #include "objcollide.h"
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
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
254 debris Debris[MAX_DEBRIS_PIECES];
256 int Num_debris_pieces = 0;
257 int Debris_inited = 0;
259 int Debris_model = -1;
260 int Debris_vaporize_model = -1;
261 int Debris_num_submodels = 0;
262 const char * Debris_texture_files[MAX_SPECIES_NAMES] = {
263 NOX("debris01a"), // Terran
264 NOX("debris01b"), // Species B
265 NOX("debris01c"), // Shivan
268 int Debris_textures[MAX_SPECIES_NAMES];
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)
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
279 // ---------------------------------------------------------------------------------------
280 // debris_start_death_roll()
282 // Start the sequence of a piece of debris writhing in unholy agony!!!
284 static void debris_start_death_roll(object *debris_obj, debris *debris_p)
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);
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);
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 );
301 debris_obj->flags |= OF_SHOULD_BE_DEAD;
302 // demo_do_flag_dead(OBJ_INDEX(debris_obj));
305 // ---------------------------------------------------------------------------------------
308 // This will get called at the start of each level.
314 if ( !Debris_inited ) {
319 Debris_vaporize_model = -1;
320 Debris_num_submodels = 0;
322 // Reset everything between levels
323 Num_debris_pieces = 0;
324 for (i=0; i<MAX_DEBRIS_PIECES; i++ ) {
326 Debris[i].sound_delay = 0;
330 list_init(&Hull_debris_list);
333 // Page in debris bitmaps at level load
334 void debris_page_in()
338 Debris_model = model_load( NOX("debris01.pof"), 0, NULL );
339 if (Debris_model>-1) {
341 pm = model_get(Debris_model);
342 Debris_num_submodels = pm->n_models;
346 Debris_vaporize_model = model_load( NOX("debris02.pof"), 0, NULL );
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] );
355 bm_page_in_texture( Debris_textures[i] );
360 MONITOR(NumSmallDebrisRend);
361 MONITOR(NumHullDebrisRend);
363 // ---------------------------------------------------------------------------------------
367 void debris_render(object * obj)
378 SDL_assert(num >= 0 && num < MAX_DEBRIS_PIECES );
381 SDL_assert( db->flags & DEBRIS_USED );
383 // Swap in a different texture depending on the species
384 if ( (db->species > -1) && (db->species < MAX_SPECIES_NAMES) ) {
386 pm = model_get( db->model_num );
388 if ( pm && (pm->n_textures == 1) ) {
389 swapped = pm->textures[0];
390 pm->textures[0] = Debris_textures[db->species];
394 model_clear_instance( db->model_num );
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 );
406 MONITOR_INC(NumHullDebrisRend,1);
407 submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos );
409 MONITOR_INC(NumSmallDebrisRend,1);
410 submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos, MR_NO_LIGHTING );
413 if ((swapped!=-1) && pm) {
414 pm->textures[0] = swapped;
418 // Removed the DEBRIS_EXPIRE flag, and remove item from Hull_debris_list
419 void debris_clear_expired_flag(debris *db)
421 if ( db->flags & DEBRIS_EXPIRE ) {
422 db->flags &= ~DEBRIS_EXPIRE;
425 list_remove(Hull_debris_list, db);
426 SDL_assert( Num_hull_pieces >= 0 );
431 // ---------------------------------------------------------------------------------------
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.
437 void debris_delete( object * obj )
443 SDL_assert( Debris[num].objnum == OBJ_INDEX(obj));
447 SDL_assert( Num_debris_pieces >= 0 );
448 if ( db->is_hull && (db->flags & DEBRIS_EXPIRE) ) {
449 debris_clear_expired_flag(db);
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)
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)
468 db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
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);
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)
489 MONITOR(NumSmallDebris);
490 MONITOR(NumHullDebris);
492 // ---------------------------------------------------------------------------------------
493 // debris_process_post()
495 // Do various updates to debris: check if time to die, start fireballs
497 // parameters: obj => pointer to debris object
498 // frame_time => time elapsed since last debris_move() called
500 // Maybe delete debris if it's very far away from player.
501 void debris_process_post(object * obj, float frame_time)
506 int objnum = OBJ_INDEX(obj);
507 SDL_assert( Debris[num].objnum == objnum );
508 debris *db = &Debris[num];
511 MONITOR_INC(NumHullDebris,1);
512 radar_plot_object( obj );
514 if ( timestamp_elapsed(db->sound_delay) ) {
515 obj_snd_assign(objnum, SND_DEBRIS, &vmd_zero_vector, 0);
519 MONITOR_INC(NumSmallDebris,1);
522 if ( db->lifeleft >= 0.0f) {
523 db->lifeleft -= frame_time;
524 if ( db->lifeleft < 0.0f ) {
525 debris_start_death_roll(obj, db);
529 maybe_delete_debris(db); // Make this debris go away if it's very far away.
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
536 if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball)) {
538 // start the next fireball up in the next 50 - 100 ms
539 //db->next_fireball = timestamp_rand(60,80);
541 db->next_fireball = timestamp_rand(db->arc_frequency,db->arc_frequency*2 );
542 db->arc_frequency += 100;
546 int n, n_arcs = ((rand()>>5) % 3)+1; // Create 1-3 sparks
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 );
554 int a = 100, b = 1000;
555 int lifetime = (myrand()%((b)-(a)+1))+(a);
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
565 db->arc_pts[i][0] = v1;
566 db->arc_pts[i][1] = v2;
569 db->arc_pts[i][0] = v2;
570 db->arc_pts[i][1] = v3;
574 db->arc_pts[i][0] = v2;
575 db->arc_pts[i][1] = v4;
584 break; // Don't need to create anymore
589 // rotate v2 out of local coordinates into world.
590 // Use v2 since it is used in every bolt. See above switch().
592 vm_vec_unrotate(&snd_pos, &v2, &obj->orient);
593 vm_vec_add2(&snd_pos, &obj->pos );
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 );
609 // 0.10 second effect
610 snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius );
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);
625 // Maybe move a vertex.... 20% of the time maybe?
627 if ( mr < MY_RAND_MAX/5 ) {
629 submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 );
630 db->arc_pts[i][mr % 2] = v1;
638 // ---------------------------------------------------------------------------------------
639 // debris_find_oldest()
641 // Locate the oldest hull debris chunk. Search through the Hull_debris_list, which is a list
642 // of all the hull debris chunks.
644 int debris_find_oldest()
651 oldest_time = 0x7fffffff;
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;
663 #define DEBRIS_ROTVEL_SCALE 5.0f
664 void calc_debris_physics_properties( physics_info *pi, vector *min, vector *max );
665 // ---------------------------------------------------------------------------------------
668 // Create debris from an object
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)
674 int i, n, objnum, parent_objnum;
681 parent_objnum = OBJ_INDEX(source_obj);
683 SDL_assert( (source_obj->type == OBJ_SHIP ) || (source_obj->type == OBJ_GHOST));
684 SDL_assert( source_obj->instance >= 0 && source_obj->instance < MAX_SHIPS );
685 shipp = &Ships[source_obj->instance];
686 vaporize = (shipp->flags &SF_VAPORIZE);
689 // Make vaporize debris seen from farther away
690 float dist = vm_vec_dist_quick( pos, &Eye_position );
694 if ( dist > 200.0f ) {
695 //mprintf(( "Not creating debris that is %.1f m away\n", dist ));
700 if ( hull_flag && (Num_hull_pieces >= MAX_HULL_PIECES ) ) {
701 // cause oldest hull debris chunk to blow up
702 n = debris_find_oldest();
704 debris_start_death_roll(&Objects[Debris[n].objnum], &Debris[n] );
708 for (n=0; n<MAX_DEBRIS_PIECES; n++ ) {
709 if ( !(Debris[n].flags & DEBRIS_USED) )
713 if ( n == MAX_DEBRIS_PIECES ) {
714 nprintf(("Warning","Frame %i: Could not create debris, no more slots left\n", Framecount));
720 // Create Debris piece n!
722 if (myrand() < MY_RAND_MAX/6) // Make some pieces blow up shortly after explosion.
723 db->lifeleft = 2.0f * ((float) myrand()/(float) MY_RAND_MAX) + 0.5f;
725 db->lifeleft = -1.0f; // large hull pieces stay around forever
727 db->lifeleft = (i2fl(myrand())/i2fl(MY_RAND_MAX))*2.0f+0.1f;
730 // increase lifetime for vaporized debris
732 db->lifeleft *= 3.0f;
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;
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
751 // Only make 1/2 of the pieces have arcs
752 if ( myrand() < (MY_RAND_MAX/3)*2 ) {
753 db->arc_frequency = 1000;
755 db->arc_frequency = 0;
758 db->arc_frequency = 0;
761 if ( model_num < 0 ) {
763 db->model_num = Debris_vaporize_model;
765 db->model_num = Debris_model;
767 db->submodel_num = (myrand()>>4) % Debris_num_submodels;
769 db->model_num = model_num;
770 db->submodel_num = submodel_num;
772 float radius = submodel_get_radius( db->model_num, db->submodel_num );
774 db->next_fireball = timestamp_rand(500,2000); //start one 1/2 - 2 secs later
777 pos = &source_obj->pos;
779 uint flags = OF_RENDERS | OF_PHYSICS;
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"));
790 obj = &Objects[objnum];
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 );
799 // -- No long need shield: bset_shield_strength(obj, 100.0f); // Hey! Set to some meaningful value!
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;
804 obj->hull_strength = 10.0f;
808 vector rotvel, radial_vel, to_center;
811 vm_vec_sub( &to_center,pos, exp_center );
813 vm_vec_zero(&to_center);
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);
822 // set up physics mass and I_inv for hull debris pieces
823 pm = model_get(model_num);
825 min = &pm->submodel[submodel_num].min;
826 max = &pm->submodel[submodel_num].max;
827 calc_debris_physics_properties( &obj->phys_info, min, max );
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
834 if ( Objects[db->source_objnum].radius < MIN_RADIUS_FOR_PERSISTANT_DEBRIS ) {
835 db->flags |= DEBRIS_EXPIRE; // debris can expire
837 list_append(&Hull_debris_list, db);
839 nprintf(("Alan","A forever chunk of debris was created from ship with radius %f\n",Objects[db->source_objnum].radius));
843 scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from blast center (non-hull)
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 );
851 vm_vec_normalize(&to_center);
852 vm_vec_copy_scale(&radial_vel, &to_center, scale );
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.
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
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);
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);
871 #ifdef DEBRIS_SPEED_DEBUG
872 // check that debris is not created with too high a velocity
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);
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);
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);
891 Warning(LOCATION, "Ship has info flag that is not among the following: SMALL, NOT_FLYABLE, HARMLESS, BIG, CAPITAL, SUPERCAP");
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));
899 // make sure rotational velocity does not get too high
904 scale = ( 6.0f + i2fl(myrand()%4) ) / radius;
906 vm_vec_rand_vec_quick(&rotvel);
907 vm_vec_scale(&rotvel, scale);
909 obj->phys_info.flags |= PF_DEAD_DAMP;
910 obj->phys_info.rotvel = rotvel;
911 check_rotvel_limit( &obj->phys_info );
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
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.
923 // ensure vel is valid
924 SDL_assert( !vm_is_vec_nan(&obj->phys_info.vel) );
926 // if ( hull_flag ) {
927 // vm_vec_zero(&obj->phys_info.vel);
928 // vm_vec_zero(&obj->phys_info.rotvel);
934 // ---------------------------------------------------------------------------------------
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.
940 void debris_hit(object *debris_obj, object *other_obj, vector *hitpos, float damage)
942 debris *debris_p = &Debris[debris_obj->instance];
945 // Do a little particle spark shower to show we hit
949 pe.pos = *hitpos; // Where the particles emit from
950 pe.vel = debris_obj->phys_info.vel; // Initial velocity of all the particles
953 vm_vec_sub( &tmp_norm, hitpos, &debris_obj->pos );
954 vm_vec_normalize_safe(&tmp_norm);
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
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 );
972 // multiplayer clients bail here
973 if(MULTIPLAYER_CLIENT){
977 if ( damage < 0.0f ) {
981 debris_obj->hull_strength -= damage;
983 if (debris_obj->hull_strength < 0.0f) {
984 debris_start_death_roll(debris_obj, debris_p );
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);
993 // ---------------------------------------------------------------------------------------
994 // debris_check_collision()
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.
1000 int debris_check_collision(object *pdebris, object *other_obj, vector *hitpos, collision_info_struct *debris_hit_info)
1005 SDL_assert( pdebris->type == OBJ_DEBRIS );
1007 num = pdebris->instance;
1008 SDL_assert( num >= 0 );
1010 SDL_assert( Debris[num].objnum == OBJ_INDEX(pdebris));
1012 // debris_hit_info NULL - so debris-weapon collision
1013 if ( debris_hit_info == NULL ) {
1014 // debris weapon collision
1015 SDL_assert( other_obj->type == OBJ_WEAPON );
1016 mc.model_num = Debris[num].model_num; // Fill in the model to check
1017 mc.submodel_num = Debris[num].submodel_num;
1018 model_clear_instance( mc.model_num );
1019 mc.orient = &pdebris->orient; // The object's orient
1020 mc.pos = &pdebris->pos; // The object's position
1021 mc.p0 = &other_obj->last_pos; // Point 1 of ray to check
1022 mc.p1 = &other_obj->pos; // Point 2 of ray to check
1023 mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL);
1025 if (model_collide(&mc)) {
1026 *hitpos = mc.hit_point_world;
1032 // debris ship collision -- use debris_hit_info to calculate physics
1033 object *ship_obj = other_obj;
1034 SDL_assert( ship_obj->type == OBJ_SHIP );
1036 object *heavy = debris_hit_info->heavy;
1037 object *light = debris_hit_info->light;
1038 object *heavy_obj = heavy;
1039 object *light_obj = light;
1041 vector zero, p0, p1;
1043 vm_vec_sub(&p0, &light->last_pos, &heavy->last_pos);
1044 vm_vec_sub(&p1, &light->pos, &heavy->pos);
1046 mc.pos = &zero; // The object's position
1047 mc.p0 = &p0; // Point 1 of ray to check
1048 mc.p1 = &p1; // Point 2 of ray to check
1050 // find the light object's position in the heavy object's reference frame at last frame and also in this frame.
1051 vector p0_temp, p0_rotated;
1053 // Collision detection from rotation enabled if at rotaion is less than 30 degree in frame
1054 // This should account for all ships
1055 if ( (vm_vec_mag_squared(&heavy->phys_info.rotvel) * flFrametime*flFrametime) < (PI*PI/36) ) {
1056 // collide_rotate calculate (1) start position and (2) relative velocity
1057 debris_hit_info->collide_rotate = 1;
1058 vm_vec_rotate(&p0_temp, &p0, &heavy->last_orient);
1059 vm_vec_unrotate(&p0_rotated, &p0_temp, &heavy->orient);
1060 mc.p0 = &p0_rotated; // Point 1 of ray to check
1061 vm_vec_sub(&debris_hit_info->light_rel_vel, &p1, &p0_rotated);
1062 vm_vec_scale(&debris_hit_info->light_rel_vel, 1/flFrametime);
1064 debris_hit_info->collide_rotate = 0;
1065 vm_vec_sub(&debris_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel);
1070 if ( debris_hit_info->heavy == ship_obj ) { // ship is heavier, so debris is sphere. Check sphere collision against ship poly model
1071 mc.model_num = Ships[ship_obj->instance].modelnum; // Fill in the model to check
1072 mc.orient = &ship_obj->orient; // The object's orient
1073 mc.radius = pdebris->radius;
1074 mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
1076 // copy important data
1077 int copy_flags = mc.flags; // make a copy of start end positions of sphere in big ship RF
1078 vector copy_p0, copy_p1;
1082 // first test against the sphere - if this fails then don't do any submodel tests
1083 mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE;
1085 int submodel_list[MAX_ROTATING_SUBMODELS];
1086 int num_rotating_submodels = 0;
1089 ship_model_start(ship_obj);
1091 if (model_collide(&mc)) {
1093 // Set earliest hit time
1094 debris_hit_info->hit_time = FLT_MAX;
1096 // Do collision the cool new way
1097 if ( debris_hit_info->collide_rotate ) {
1098 // We collide with the sphere, find the list of rotating submodels and test one at a time
1099 model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj);
1101 // Get polymodel and turn off all rotating submodels, collide against only 1 at a time.
1102 pm = model_get(Ships[heavy_obj->instance].modelnum);
1104 // turn off all rotating submodels and test for collision
1105 for (int i=0; i<num_rotating_submodels; i++) {
1106 pm->submodel[submodel_list[i]].blown_off = 1;
1109 // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
1110 mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
1112 // check each submodel in turn
1113 for (int i=0; i<num_rotating_submodels; i++) {
1114 // turn on submodel for collision test
1115 pm->submodel[submodel_list[i]].blown_off = 0;
1117 // set angles for last frame (need to set to prev to get p0)
1118 angles copy_angles = pm->submodel[submodel_list[i]].angs;
1120 // find the start and end positions of the sphere in submodel RF
1121 pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs;
1122 world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos);
1124 pm->submodel[submodel_list[i]].angs = copy_angles;
1125 world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos);
1129 // mc.pos = zero // in submodel RF
1131 mc.orient = &vmd_identity_matrix;
1132 mc.submodel_num = submodel_list[i];
1134 if ( model_collide(&mc) ) {
1135 if ( mc.hit_dist < debris_hit_info->hit_time ) {
1138 // set up debris_hit_info common
1139 set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_ROT_HIT);
1140 model_find_world_point(&debris_hit_info->hit_pos, &mc.hit_point, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
1142 // set up debris_hit_info for rotating submodel
1143 if (debris_hit_info->edge_hit == 0) {
1144 model_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1147 // find position in submodel RF of light object at collison
1148 vector int_light_pos, diff;
1149 vm_vec_sub(&diff, mc.p1, mc.p0);
1150 vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist);
1151 model_find_world_point(&debris_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
1154 // Don't look at this submodel again
1155 pm->submodel[submodel_list[i]].blown_off = 1;
1160 // Recover and do usual ship_ship collision, but without rotating submodels
1161 mc.flags = copy_flags;
1164 mc.orient = &heavy_obj->orient;
1166 // usual ship_ship collision test
1167 if ( model_collide(&mc) ) {
1168 // check if this is the earliest hit
1169 if (mc.hit_dist < debris_hit_info->hit_time) {
1172 set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1174 // get collision normal if not edge hit
1175 if (debris_hit_info->edge_hit == 0) {
1176 model_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1179 // find position in submodel RF of light object at collison
1181 vm_vec_sub(&diff, mc.p1, mc.p0);
1182 vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1187 ship_model_stop( ship_obj );
1191 // Debris is heavier obj
1192 mc.model_num = Debris[num].model_num; // Fill in the model to check
1193 mc.submodel_num = Debris[num].submodel_num;
1194 model_clear_instance( mc.model_num );
1195 mc.orient = &pdebris->orient; // The object's orient
1196 mc.radius = model_get_core_radius( Ships[ship_obj->instance].modelnum );
1198 // check for collision between debris model and ship sphere
1199 mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL | MC_CHECK_SPHERELINE);
1201 mc_ret_val = model_collide(&mc);
1204 set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1206 // set normal if not edge hit
1207 if ( !debris_hit_info->edge_hit ) {
1208 vm_vec_unrotate(&debris_hit_info->collision_normal, &mc.hit_normal, &heavy->orient);
1211 // find position in submodel RF of light object at collison
1213 vm_vec_sub(&diff, mc.p1, mc.p0);
1214 vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1222 // SET PHYSICS PARAMETERS
1223 // already have (hitpos - heavy) and light_cm_pos
1224 // get heavy cm pos - already have light_cm_pos
1225 debris_hit_info->heavy_collision_cm_pos = zero;
1227 // get r_heavy and r_light
1228 debris_hit_info->r_heavy = debris_hit_info->hit_pos;
1229 vm_vec_sub(&debris_hit_info->r_light, &debris_hit_info->hit_pos, &debris_hit_info->light_collision_cm_pos);
1231 // set normal for edge hit
1232 if ( debris_hit_info->edge_hit ) {
1233 vm_vec_copy_normalize(&debris_hit_info->collision_normal, &debris_hit_info->r_light);
1234 vm_vec_negate(&debris_hit_info->collision_normal);
1238 vm_vec_add(hitpos, &debris_hit_info->heavy->pos, &debris_hit_info->r_heavy);
1247 // ---------------------------------------------------------------------------------------
1248 // debris_get_team()
1250 // Return the team field for a debris object
1252 int debris_get_team(object *objp)
1254 SDL_assert( objp->type == OBJ_DEBRIS );
1255 SDL_assert( objp->instance >= 0 && objp->instance < MAX_DEBRIS_PIECES );
1256 return Debris[objp->instance].team;
1259 // fills in debris physics properties when created, specifically mass and moment of inertia
1260 void calc_debris_physics_properties( physics_info *pi, vector *mins, vector *maxs )
1262 float dx, dy, dz, mass;
1263 dx = maxs->xyz.x - mins->xyz.x;
1264 dy = maxs->xyz.y - mins->xyz.y;
1265 dz = maxs->xyz.z - mins->xyz.z;
1267 // John, with new bspgen, just set pi->mass = mass
1268 mass = 0.12f * dx * dy * dz;
1269 pi->mass = (float) pow(mass, 0.6666667f) * 4.65f;
1271 pi->I_body_inv.v.rvec.xyz.x = 12.0f / (pi->mass * (dy*dy + dz*dz));
1272 pi->I_body_inv.v.rvec.xyz.y = 0.0f;
1273 pi->I_body_inv.v.rvec.xyz.z = 0.0f;
1275 pi->I_body_inv.v.uvec.xyz.x = 0.0f;
1276 pi->I_body_inv.v.uvec.xyz.y = 12.0f / (pi->mass * (dx*dx + dz*dz));
1277 pi->I_body_inv.v.uvec.xyz.z = 0.0f;
1279 pi->I_body_inv.v.fvec.xyz.x = 0.0f;
1280 pi->I_body_inv.v.fvec.xyz.y = 0.0f;
1281 pi->I_body_inv.v.fvec.xyz.z = 12.0f / (pi->mass * (dx*dx + dy*dy));