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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "debris/debris.h"
13 #include "render/3d.h"
14 #include "fireball/fireballs.h"
15 #include "radar/radar.h"
16 #include "gamesnd/gamesnd.h"
17 #include "object/objectsnd.h"
18 #include "globalincs/linklist.h"
19 #include "particle/particle.h"
20 #include "freespace2/freespace.h"
21 #include "object/objcollide.h"
22 #include "io/timer.h"
23 #include "species_defs/species_defs.h"
24 #include "ship/ship.h"
25 #include "ship/shipfx.h"
26 #include "radar/radarsetup.h"
27 #include "network/multi.h"
28 #include "network/multimsgs.h"
29 #include "network/multiutil.h"
30 #include "weapon/weapon.h"
31 #include "cmdline/cmdline.h"
32 
33 #define MAX_LIFE									10.0f
34 #define MIN_RADIUS_FOR_PERSISTANT_DEBRIS	50		// ship radius at which debris from it becomes persistant
35 #define DEBRIS_SOUND_DELAY						2000	// time to start debris sound after created
36 #define MAX_HULL_PIECES			MAX_DEBRIS_PIECES // limit the number of hull debris chunks that can exist.
37 
38 int		Num_hull_pieces;		// number of hull pieces in existance
39 debris	Hull_debris_list;		// head of linked list for hull debris chunks, for quick search
40 
41 debris Debris[MAX_DEBRIS_PIECES];
42 
43 int Num_debris_pieces = 0;
44 int Debris_inited = 0;
45 
46 int Debris_model = -1;
47 int Debris_vaporize_model = -1;
48 int Debris_num_submodels = 0;
49 
50 #define	MAX_DEBRIS_DIST					10000.0f			//	Debris goes away if it's this far away.
51 #define	DEBRIS_DISTANCE_CHECK_TIME		(10*1000)		//	Check every 10 seconds.
52 #define	DEBRIS_INDEX(dp) (dp-Debris)
53 
54 #define	MAX_SPEED_SMALL_DEBRIS		200					// maximum velocity of small debris piece
55 #define	MAX_SPEED_BIG_DEBRIS			150					// maximum velocity of big debris piece
56 #define	MAX_SPEED_CAPITAL_DEBRIS	100					// maximum velocity of capital debris piece
57 
58 /**
59  * Start the sequence of a piece of debris writhing in unholy agony!!!
60  */
debris_start_death_roll(object * debris_obj,debris * debris_p)61 static void debris_start_death_roll(object *debris_obj, debris *debris_p)
62 {
63 	if (debris_p->is_hull)	{
64 		// tell everyone else to blow up the piece of debris
65 		if( MULTIPLAYER_MASTER )
66 			send_debris_update_packet(debris_obj,DEBRIS_UPDATE_NUKE);
67 
68 		int fireball_type = fireball_ship_explosion_type(&Ship_info[debris_p->ship_info_index]);
69 		if(fireball_type < 0) {
70 			fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
71 		}
72 		fireball_create( &debris_obj->pos, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(debris_obj), debris_obj->radius*1.75f);
73 
74 		// only play debris destroy sound if hull piece and it has been around for at least 2 seconds
75 		if ( Missiontime > debris_p->time_started + 2*F1_0 ) {
76 			snd_play_3d( &Snds[SND_MISSILE_IMPACT1], &debris_obj->pos, &View_position, debris_obj->radius );
77 
78 		}
79 	}
80 
81   	debris_obj->flags |= OF_SHOULD_BE_DEAD;
82 }
83 
84 /**
85  * This will get called at the start of each level.
86  */
debris_init()87 void debris_init()
88 {
89 	int i;
90 
91 	if ( !Debris_inited ) {
92 		Debris_inited = 1;
93 	}
94 
95 	Debris_model = -1;
96 	Debris_vaporize_model = -1;
97 	Debris_num_submodels = 0;
98 
99 	// Reset everything between levels
100 	Num_debris_pieces = 0;
101 	for (i=0; i<MAX_DEBRIS_PIECES; i++ )	{
102 		Debris[i].flags = 0;
103 		Debris[i].sound_delay = 0;
104 		Debris[i].objnum = -1;
105 	}
106 
107 	Num_hull_pieces = 0;
108 	list_init(&Hull_debris_list);
109 }
110 
111 /**
112  * Page in debris bitmaps at level load
113  */
debris_page_in()114 void debris_page_in()
115 {
116 	uint i;
117 
118 	Debris_model = model_load( NOX("debris01.pof"), 0, NULL );
119 	if (Debris_model >= 0)	{
120 		polymodel * pm;
121 		pm = model_get(Debris_model);
122 		Debris_num_submodels = pm->n_models;
123 	}
124 
125 	Debris_vaporize_model = model_load( NOX("debris02.pof"), 0, NULL );
126 
127 	for (i=0; i<Species_info.size(); i++ )
128 	{
129 		species_info *species = &Species_info[i];
130 
131 		nprintf(( "Paging", "Paging in debris texture '%s'\n", species->debris_texture.filename));
132 
133 		species->debris_texture.bitmap_id = bm_load(species->debris_texture.filename);
134 		if (species->debris_texture.bitmap_id < 0)
135 		{
136 			Warning( LOCATION, "Couldn't load species %s debris\ntexture, '%s'\n", species->species_name, species->debris_texture.filename);
137 		}
138 
139 		bm_page_in_texture(species->debris_texture.bitmap_id);
140 	}
141 
142 }
143 
144 MONITOR(NumSmallDebrisRend)
MONITOR(NumHullDebrisRend)145 MONITOR(NumHullDebrisRend)
146 
147 /**
148  * Render debris
149  */
150 void debris_render(object * obj)
151 {
152 	int			i, num, swapped;
153 	polymodel	*pm;
154 	debris		*db;
155 
156 
157 	swapped = -1;
158 	pm = NULL;
159 	num = obj->instance;
160 
161 	Assert(num >= 0 && num < MAX_DEBRIS_PIECES);
162 	db = &Debris[num];
163 
164 	Assert(db->flags & DEBRIS_USED);
165 
166 	texture_info *tbase = NULL;
167 
168 	model_clear_instance( db->model_num );
169 
170 	// Swap in a different texture depending on the species
171 	if (db->species >= 0)
172 	{
173 		pm = model_get( db->model_num );
174 
175 		//WMC - Someday, we should have glowing debris.
176 		if ( pm != NULL && (pm->n_textures == 1) ) {
177 			tbase = &pm->maps[0].textures[TM_BASE_TYPE];
178 			swapped = tbase->GetTexture();
179 			tbase->SetTexture(Species_info[db->species].debris_texture.bitmap_id);
180 		}
181 	}
182 
183 	// Only render electrical arcs if within 500m of the eye (for a 10m piece)
184 	if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f )	{
185 		for (i=0; i<MAX_DEBRIS_ARCS; i++ )	{
186 			if ( timestamp_valid( db->arc_timestamp[i] ) )	{
187 				model_add_arc( db->model_num, db->submodel_num, &db->arc_pts[i][0], &db->arc_pts[i][1], MARC_TYPE_NORMAL );
188 			}
189 		}
190 	}
191 
192 	if ( db->is_hull )	{
193 		MONITOR_INC(NumHullDebrisRend,1);
194 		submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos );
195 	} else {
196 		MONITOR_INC(NumSmallDebrisRend,1);
197 		submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos, MR_NO_LIGHTING );
198 	}
199 
200 	if (tbase != NULL && (swapped!=-1) && pm)	{
201 		tbase->SetTexture(swapped);
202 	}
203 }
204 
205 /**
206  * Removed the ::DEBRIS_EXPIRE flag, and remove item from ::Hull_debris_list
207  */
debris_clear_expired_flag(debris * db)208 void debris_clear_expired_flag(debris *db)
209 {
210 	if ( db->flags & DEBRIS_EXPIRE ) {
211 		db->flags &= ~DEBRIS_EXPIRE;
212 		if ( db->is_hull ) {
213 			Num_hull_pieces--;
214 			list_remove(Hull_debris_list, db);
215 			Assert( Num_hull_pieces >= 0 );
216 		}
217 	}
218 }
219 
220 /**
221  * Delete the debris object.
222  * This is only ever called via obj_delete().  Do not call directly.
223  * Use debris_start_death_roll() if you want to force a debris piece to die.
224  */
debris_delete(object * obj)225 void debris_delete( object * obj )
226 {
227 	int		num;
228 	debris	*db;
229 
230 	num = obj->instance;
231 	Assert( Debris[num].objnum == OBJ_INDEX(obj));
232 
233 	db = &Debris[num];
234 
235 	Assert( Num_debris_pieces >= 0 );
236 	if ( db->is_hull && (db->flags & DEBRIS_EXPIRE) ) {
237 		debris_clear_expired_flag(db);
238 	}
239 
240 	db->flags = 0;
241 	db->objnum = -1;
242 	Num_debris_pieces--;
243 }
244 
245 /**
246  * If debris piece *db is far away from all players, make it go away very soon.
247  * In single player game, delete if MAX_DEBRIS_DIST from player.
248  * In multiplayer game, delete if MAX_DEBRIS_DIST from all players.
249  */
maybe_delete_debris(debris * db)250 void maybe_delete_debris(debris *db)
251 {
252 	object	*objp;
253 
254 	if (timestamp_elapsed(db->next_distance_check) && timestamp_elapsed(db->must_survive_until)) {
255 		if (!(Game_mode & GM_MULTIPLAYER)) {		//	In single player game, just check against player.
256 			if (vm_vec_dist_quick(&Player_obj->pos, &Objects[db->objnum].pos) > MAX_DEBRIS_DIST)
257 				db->lifeleft = 0.1f;
258 			else
259 				db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
260 		} else {
261 			for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
262 				if (objp->flags & OF_PLAYER_SHIP) {
263 					if (vm_vec_dist_quick(&objp->pos, &Objects[db->objnum].pos) < MAX_DEBRIS_DIST) {
264 						db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
265 						return;
266 					}
267 				}
268 			}
269 			db->lifeleft = 0.1f;
270 		}
271 	}
272 }
273 
274 MONITOR(NumSmallDebris)
MONITOR(NumHullDebris)275 MONITOR(NumHullDebris)
276 
277 /**
278  * Do various updates to debris:  check if time to die, start fireballs
279  * Maybe delete debris if it's very far away from player.
280  *
281  * @param obj			pointer to debris object
282  * @param frame_time	time elapsed since last debris_move() called
283  */
284 void debris_process_post(object * obj, float frame_time)
285 {
286 	int i, num;
287 	num = obj->instance;
288 
289 	int objnum = OBJ_INDEX(obj);
290 	Assert( Debris[num].objnum == objnum );
291 	debris *db = &Debris[num];
292 
293 	if ( db->is_hull ) {
294 		MONITOR_INC(NumHullDebris,1);
295 		radar_plot_object( obj );
296 
297 		if ( timestamp_elapsed(db->sound_delay) ) {
298 			obj_snd_assign(objnum, SND_DEBRIS, &vmd_zero_vector, 0);
299 			db->sound_delay = 0;
300 		}
301 	} else {
302 		MONITOR_INC(NumSmallDebris,1);
303 	}
304 
305 	if ( db->lifeleft >= 0.0f) {
306 		db->lifeleft -= frame_time;
307 		if ( db->lifeleft < 0.0f )	{
308 			debris_start_death_roll(obj, db);
309 		}
310 	}
311 
312 	maybe_delete_debris(db);	//	Make this debris go away if it's very far away.
313 
314 	// ================== DO THE ELECTRIC ARCING STUFF =====================
315 	if ( db->arc_frequency <= 0 )	{
316 		return;			// If arc_frequency <= 0, this piece has no arcs on it
317 	}
318 
319 	if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball))	{
320 
321 		db->next_fireball = timestamp_rand(db->arc_frequency,db->arc_frequency*2 );
322 		db->arc_frequency += 100;
323 
324 		if (db->is_hull)	{
325 
326 			int n, n_arcs = ((rand()>>5) % 3)+1;		// Create 1-3 sparks
327 
328 			vec3d v1, v2, v3, v4;
329 
330 			if ( Cmdline_old_collision_sys ) {
331 				submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 );
332 				submodel_get_two_random_points( db->model_num, db->submodel_num, &v3, &v4 );
333 			} else {
334 				submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v1, &v2 );
335 				submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v3, &v4 );
336 			}
337 
338 			n = 0;
339 
340 			int a = 100, b = 1000;
341 			int lifetime = (myrand()%((b)-(a)+1))+(a);
342 
343 			// Create the spark effects
344 			for (i=0; i<MAX_DEBRIS_ARCS; i++ )	{
345 				if ( !timestamp_valid( db->arc_timestamp[i] ) )	{
346 
347 					db->arc_timestamp[i] = timestamp(lifetime);	// live up to a second
348 
349 					switch( n )	{
350 					case 0:
351 						db->arc_pts[i][0] = v1;
352 						db->arc_pts[i][1] = v2;
353 						break;
354 					case 1:
355 						db->arc_pts[i][0] = v2;
356 						db->arc_pts[i][1] = v3;
357 						break;
358 
359 					case 2:
360 						db->arc_pts[i][0] = v2;
361 						db->arc_pts[i][1] = v4;
362 						break;
363 
364 					default:
365 						Int3();
366 					}
367 
368 					n++;
369 					if ( n == n_arcs )
370 						break;	// Don't need to create anymore
371 				}
372 			}
373 
374 
375 			// rotate v2 out of local coordinates into world.
376 			// Use v2 since it is used in every bolt.  See above switch().
377 			vec3d snd_pos;
378 			vm_vec_unrotate(&snd_pos, &v2, &obj->orient);
379 			vm_vec_add2(&snd_pos, &obj->pos );
380 
381 			//Play a sound effect
382 			if ( lifetime > 750 )	{
383 				// 1.00 second effect
384 				snd_play_3d( &Snds[SND_DEBRIS_ARC_05], &snd_pos, &View_position, obj->radius );
385 			} else if ( lifetime >  500 )	{
386 				// 0.75 second effect
387 				snd_play_3d( &Snds[SND_DEBRIS_ARC_04], &snd_pos, &View_position, obj->radius );
388 			} else if ( lifetime >  250 )	{
389 				// 0.50 second effect
390 				snd_play_3d( &Snds[SND_DEBRIS_ARC_03], &snd_pos, &View_position, obj->radius );
391 			} else if ( lifetime >  100 )	{
392 				// 0.25 second effect
393 				snd_play_3d( &Snds[SND_DEBRIS_ARC_02], &snd_pos, &View_position, obj->radius );
394 			} else {
395 				// 0.10 second effect
396 				snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius );
397 			}
398 		}
399 	}
400 
401 	for (i=0; i<MAX_DEBRIS_ARCS; i++ )	{
402 		if ( timestamp_valid( db->arc_timestamp[i] ) )	{
403 			if ( timestamp_elapsed( db->arc_timestamp[i] ) )	{
404 				// Kill off the spark
405 				db->arc_timestamp[i] = timestamp(-1);
406 			} else {
407 				// Maybe move a vertex....  20% of the time maybe?
408 				int mr = myrand();
409 				if ( mr < RAND_MAX/5 )	{
410 					vec3d v1, v2;
411 
412 					if ( Cmdline_old_collision_sys ) {
413 						submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 );
414 					} else {
415 						submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v1, &v2 );
416 					}
417 
418 					db->arc_pts[i][mr % 2] = v1;
419 				}
420 			}
421 		}
422 	}
423 }
424 
425 /**
426  * Locate the oldest hull debris chunk.  Search through the ::Hull_debris_list, which is a list
427  * of all the hull debris chunks.
428  */
debris_find_oldest()429 int debris_find_oldest()
430 {
431 	int		oldest_index;
432 	fix		oldest_time;
433 	debris	*db;
434 
435 	oldest_index = -1;
436 	oldest_time = 0x7fffffff;
437 
438 	for ( db = GET_FIRST(&Hull_debris_list); db != END_OF_LIST(&Hull_debris_list); db = GET_NEXT(db) ) {
439 		if ( (db->time_started < oldest_time) && !(Objects[db->objnum].flags & OF_SHOULD_BE_DEAD) ) {
440 			oldest_index = DEBRIS_INDEX(db);
441 			oldest_time = db->time_started;
442 		}
443 	}
444 
445 	return oldest_index;
446 }
447 
448 #define	DEBRIS_ROTVEL_SCALE	5.0f
449 void calc_debris_physics_properties( physics_info *pi, vec3d *min, vec3d *max );
450 
451 /**
452  * Create debris from an object
453  *
454  * @param source_obj	Source object
455  * @param model_num		Model number
456  * @param submodel_num	Sub-model number
457  * @param pos			Position in vector space
458  * @param exp_center	Explosion center in vector space
459  * @param hull_flag		Hull flag settings
460  * @param exp_force		Explosion force, used to assign velocity to pieces. 1.0f assigns velocity like before. 2.0f assigns twice as much to non-inherited part of velocity
461  */
debris_create(object * source_obj,int model_num,int submodel_num,vec3d * pos,vec3d * exp_center,int hull_flag,float exp_force)462 object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d *pos, vec3d *exp_center, int hull_flag, float exp_force)
463 {
464 	int		i, n, objnum, parent_objnum;
465 	object	*obj;
466 	ship		*shipp;
467 	debris	*db;
468 	polymodel *pm;
469 	int vaporize;
470 	physics_info *pi = NULL;
471 	ship_info *sip = NULL;
472 
473 	parent_objnum = OBJ_INDEX(source_obj);
474 
475 	Assert( (source_obj->type == OBJ_SHIP ) || (source_obj->type == OBJ_GHOST));
476 	Assert( source_obj->instance >= 0 && source_obj->instance < MAX_SHIPS );
477 	shipp = &Ships[source_obj->instance];
478 	sip = &Ship_info[shipp->ship_info_index];
479 	vaporize = (shipp->flags &SF_VAPORIZE);
480 
481 	if ( !hull_flag )	{
482 		// Make vaporize debris seen from farther away
483 		float dist = vm_vec_dist_quick( pos, &Eye_position );
484 		if (vaporize) {
485 			dist /= 2.0f;
486 		}
487 		if ( dist > 200.0f ) {
488 			return NULL;
489 		}
490 	}
491 
492 	if ( hull_flag && (Num_hull_pieces >= MAX_HULL_PIECES ) ) {
493 		// cause oldest hull debris chunk to blow up
494 		n = debris_find_oldest();
495 		if ( n >= 0 ) {
496 			debris_start_death_roll(&Objects[Debris[n].objnum], &Debris[n] );
497 		}
498 	}
499 
500 	for (n=0; n<MAX_DEBRIS_PIECES; n++ ) {
501 		if ( !(Debris[n].flags & DEBRIS_USED) )
502 			break;
503 	}
504 
505 	if (n == MAX_DEBRIS_PIECES) {
506 		n = debris_find_oldest();
507 
508 		if (n >= 0)
509 			debris_start_death_roll(&Objects[Debris[n].objnum], &Debris[n]);
510 
511 		nprintf(("Warning","Frame %i: Could not create debris, no more slots left\n", Framecount));
512 		return NULL;
513 	}
514 
515 	db = &Debris[n];
516 
517 	//WMC - We must survive until now, at least.
518 	db->must_survive_until = timestamp();
519 
520 	if(hull_flag && sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
521 	{
522 		db->lifeleft = (( sip->debris_max_lifetime - sip->debris_min_lifetime ) * frand()) + sip->debris_min_lifetime;
523 	}
524 	else
525 	{
526 		// Create Debris piece n!
527 		if ( hull_flag ) {
528 			if (rand() < RAND_MAX/6)	// Make some pieces blow up shortly after explosion.
529 				db->lifeleft = 2.0f * ((float) myrand()/(float) RAND_MAX) + 0.5f;
530 			else
531 				db->lifeleft = -1.0f;		// large hull pieces stay around forever
532 		} else {
533 			db->lifeleft = (i2fl(myrand())/i2fl(RAND_MAX))*2.0f+0.1f;
534 		}
535 	}
536 
537 	//WMC - Oh noes, we may need to change lifeleft
538 	if(hull_flag)
539 	{
540 		if(sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
541 		{
542 			db->must_survive_until = timestamp(fl2i(sip->debris_min_lifetime * 1000.0f));
543 			db->lifeleft = (( sip->debris_max_lifetime - sip->debris_min_lifetime ) * frand()) + sip->debris_min_lifetime;
544 		}
545 		else if(sip->debris_min_lifetime >= 0.0f)
546 		{
547 			db->must_survive_until = timestamp(fl2i(sip->debris_min_lifetime * 1000.0f));
548 			if(db->lifeleft < sip->debris_min_lifetime)
549 				db->lifeleft = sip->debris_min_lifetime;
550 		}
551 		else if(sip->debris_max_lifetime >= 0.0f)
552 		{
553 			if(db->lifeleft > sip->debris_max_lifetime)
554 				db->lifeleft = sip->debris_max_lifetime;
555 		}
556 	}
557 
558 	// increase lifetime for vaporized debris
559 	if (vaporize) {
560 		db->lifeleft *= 3.0f;
561 	}
562 	db->flags |= DEBRIS_USED;
563 	db->is_hull = hull_flag;
564 	db->source_objnum = parent_objnum;
565 	db->source_sig = source_obj->signature;
566 	db->ship_info_index = shipp->ship_info_index;
567 	db->team = shipp->team;
568 	db->fire_timeout = 0;	// if not changed, timestamp_elapsed() will return false
569 	db->time_started = Missiontime;
570 	db->species = Ship_info[shipp->ship_info_index].species;
571 	db->next_distance_check = (myrand() % 2000) + 4*DEBRIS_DISTANCE_CHECK_TIME;
572 	db->parent_alt_name = shipp->alt_type_index;
573 	db->damage_mult = 1.0f;
574 
575 	for (i=0; i<MAX_DEBRIS_ARCS; i++ )	{
576 		db->arc_timestamp[i] = timestamp(-1);
577 	}
578 
579 	if ( db->is_hull )	{
580 		// Percent of debris pieces with arcs controlled via table (default 50%)
581 		if (frand() < sip->debris_arc_percent) {
582 			db->arc_frequency = 1000;
583 		} else {
584 			db->arc_frequency = 0;
585 		}
586 	} else {
587 		db->arc_frequency = 0;
588 	}
589 
590 	if ( model_num < 0 )	{
591 		if (vaporize) {
592 			db->model_num = Debris_vaporize_model;
593 		} else {
594 			db->model_num = Debris_model;
595 		}
596 		db->submodel_num = (myrand()>>4) % Debris_num_submodels;
597 	} else {
598 		db->model_num = model_num;
599 		db->submodel_num = submodel_num;
600 	}
601 	float radius = submodel_get_radius( db->model_num, db->submodel_num );
602 
603 	db->next_fireball = timestamp_rand(500,2000);	//start one 1/2 - 2 secs later
604 
605 	if ( pos == NULL )
606 		pos = &source_obj->pos;
607 
608 	uint flags = OF_RENDERS | OF_PHYSICS;
609 	if ( hull_flag )
610 		flags |= OF_COLLIDES;
611 	objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &source_obj->orient, pos, radius, flags );
612 	if ( objnum == -1 ) {
613 		mprintf(("Couldn't create debris object -- out of object slots\n"));
614 		return NULL;
615 	}
616 
617 	db->objnum = objnum;
618 
619 	obj = &Objects[objnum];
620 	pi = &obj->phys_info;
621 
622 	// assign the network signature.  The signature will be 0 for non-hull pieces, but since that
623 	// is our invalid signature, it should be okay.
624 	obj->net_signature = 0;
625 
626 	if ( (Game_mode & GM_MULTIPLAYER) && hull_flag ) {
627 		obj->net_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
628 	}
629 
630 	if (source_obj->type == OBJ_SHIP) {
631 		obj->hull_strength = Ships[source_obj->instance].ship_max_hull_strength/8.0f;
632 	} else
633 		obj->hull_strength = 10.0f;
634 
635 	if (hull_flag) {
636 		if(sip->debris_min_hitpoints >= 0.0f && sip->debris_max_hitpoints >= 0.0f)
637 		{
638 			obj->hull_strength = (( sip->debris_max_hitpoints - sip->debris_min_hitpoints ) * frand()) + sip->debris_min_hitpoints;
639 		}
640 		else if(sip->debris_min_hitpoints >= 0.0f)
641 		{
642 			if(obj->hull_strength < sip->debris_min_hitpoints)
643 				obj->hull_strength = sip->debris_min_hitpoints;
644 		}
645 		else if(sip->debris_max_hitpoints >= 0.0f)
646 		{
647 			if(obj->hull_strength > sip->debris_max_hitpoints)
648 				obj->hull_strength = sip->debris_max_hitpoints;
649 		}
650 		db->damage_mult = sip->debris_damage_mult;
651 	}
652 
653 	Num_debris_pieces++;
654 
655 	vec3d rotvel, radial_vel, to_center;
656 
657 	if ( exp_center )
658 		vm_vec_sub( &to_center,pos, exp_center );
659 	else
660 		vm_vec_zero(&to_center);
661 
662 	float scale;
663 
664 	if ( hull_flag )	{
665 		float t;
666 		scale = exp_force * i2fl((myrand()%20) + 10);	// for radial_vel away from location of blast center
667 		db->sound_delay = timestamp(DEBRIS_SOUND_DELAY);
668 
669 		// set up physics mass and I_inv for hull debris pieces
670 		pm = model_get(model_num);
671 		vec3d *min, *max;
672 		min = &pm->submodel[submodel_num].min;
673 		max = &pm->submodel[submodel_num].max;
674 		calc_debris_physics_properties( &obj->phys_info, min, max );
675 
676 		// limit the amount of time that fireballs appear
677 		// let fireball length be linked to radius of ship.  Range is .33 radius => 3.33 radius seconds.
678 		t = 1000*Objects[db->source_objnum].radius/3 + myrand()%(fl2i(1000*3*Objects[db->source_objnum].radius));
679 		db->fire_timeout = timestamp(fl2i(t));		// fireballs last from 5 - 30 seconds
680 
681 		if ( Objects[db->source_objnum].radius < MIN_RADIUS_FOR_PERSISTANT_DEBRIS ) {
682 			db->flags |= DEBRIS_EXPIRE;	// debris can expire
683 			Num_hull_pieces++;
684 			list_append(&Hull_debris_list, db);
685 		} else {
686 			nprintf(("Alan","A forever chunk of debris was created from ship with radius %f\n",Objects[db->source_objnum].radius));
687 		}
688 	}
689 	else {
690 		scale = exp_force * i2fl((myrand()%20) + 10);	// for radial_vel away from blast center (non-hull)
691 	}
692 
693 	if ( vm_vec_mag_squared( &to_center ) < 0.1f )	{
694 		vm_vec_rand_vec_quick(&radial_vel);
695 		vm_vec_scale(&radial_vel, scale );
696 	}
697 	else {
698 		vm_vec_normalize(&to_center);
699 		vm_vec_copy_scale(&radial_vel, &to_center, scale );
700 	}
701 
702 	// 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
703 	// displacement fromt the center of the parent object to the center of the debris piece
704 	vec3d world_rotvel, vel_from_rotvel;
705 	vm_vec_unrotate ( &world_rotvel, &source_obj->phys_info.rotvel, &source_obj->orient );
706 	vm_vec_crossprod ( &vel_from_rotvel, &world_rotvel, &to_center );
707 	vm_vec_scale ( &vel_from_rotvel, DEBRIS_ROTVEL_SCALE);
708 
709 	vm_vec_add (&obj->phys_info.vel, &radial_vel, &source_obj->phys_info.vel);
710 	vm_vec_add2(&obj->phys_info.vel, &vel_from_rotvel);
711 
712 	// make sure rotational velocity does not get too high
713 	if (radius < 1.0) {
714 		radius = 1.0f;
715 	}
716 
717 	scale = ( 6.0f + i2fl(myrand()%4) ) / radius;
718 
719 	vm_vec_rand_vec_quick(&rotvel);
720 	vm_vec_scale(&rotvel, scale);
721 
722 	pi->flags |= PF_DEAD_DAMP;
723 	pi->rotvel = rotvel;
724 	check_rotvel_limit( &obj->phys_info );
725 
726 	// check that debris is not created with too high a velocity
727 	if (hull_flag)
728 	{
729 		shipfx_debris_limit_speed(db, shipp);
730 	}
731 
732 	// blow out his reverse thrusters. Or drag, same thing.
733 	pi->rotdamp = 10000.0f;
734 	pi->side_slip_time_const = 10000.0f;
735 	pi->flags |= (PF_REDUCED_DAMP | PF_DEAD_DAMP);	// set damping equal for all axis and not changable
736 
737 	vm_vec_zero(&pi->max_vel);		// make so he can't turn on his own VOLITION anymore.
738 	vm_vec_zero(&pi->max_rotvel);	// make so he can't change speed on his own VOLITION anymore.
739 
740 	// ensure vel is valid
741 	Assert( !vm_is_vec_nan(&obj->phys_info.vel) );
742 
743 	return obj;
744 }
745 
746 /**
747  * Alas, poor debris_obj got whacked.  Fortunately, we know who did it, where and how hard, so we
748  * can do something about it.
749  */
debris_hit(object * debris_obj,object * other_obj,vec3d * hitpos,float damage)750 void debris_hit(object *debris_obj, object *other_obj, vec3d *hitpos, float damage)
751 {
752 	debris	*debris_p = &Debris[debris_obj->instance];
753 
754 	// Do a little particle spark shower to show we hit
755 	{
756 		particle_emitter	pe;
757 
758 		pe.pos = *hitpos;								// Where the particles emit from
759 		pe.vel = debris_obj->phys_info.vel;		// Initial velocity of all the particles
760 
761 		vec3d tmp_norm;
762 		vm_vec_sub( &tmp_norm, hitpos, &debris_obj->pos );
763 		vm_vec_normalize_safe(&tmp_norm);
764 
765 		pe.normal = tmp_norm;			// What normal the particle emit around
766 		pe.normal_variance = 0.3f;		//	How close they stick to that normal 0=good, 1=360 degree
767 		pe.min_rad = 0.20f;				// Min radius
768 		pe.max_rad = 0.40f;				// Max radius
769 
770 		// Sparks for first time at this spot
771 		pe.num_low = 10;				// Lowest number of particles to create
772 		pe.num_high = 10;			// Highest number of particles to create
773 		pe.normal_variance = 0.3f;		//	How close they stick to that normal 0=good, 1=360 degree
774 		pe.min_vel = 0.0f;				// How fast the slowest particle can move
775 		pe.max_vel = 10.0f;				// How fast the fastest particle can move
776 		pe.min_life = 0.25f;			// How long the particles live
777 		pe.max_life = 0.75f;			// How long the particles live
778 		particle_emit( &pe, PARTICLE_FIRE, 0 );
779 	}
780 
781 	// multiplayer clients bail here
782 	if(MULTIPLAYER_CLIENT){
783 		return;
784 	}
785 
786 	if ( damage < 0.0f ) {
787 		damage = 0.0f;
788 	}
789 
790 	debris_obj->hull_strength -= damage;
791 
792 	if (debris_obj->hull_strength < 0.0f) {
793 		debris_start_death_roll(debris_obj, debris_p );
794 	} else {
795 		// otherwise, give all the other players an update on the debris
796 		if(MULTIPLAYER_MASTER){
797 			send_debris_update_packet(debris_obj,DEBRIS_UPDATE_UPDATE);
798 		}
799 	}
800 }
801 
802 /**
803  * See if poor debris object *obj got whacked by evil *other_obj at point *hitpos.
804  * NOTE: debris_hit_info pointer NULL for debris:weapon collision, otherwise debris:ship collision.
805  * @return true if hit, else return false.
806  */
debris_check_collision(object * pdebris,object * other_obj,vec3d * hitpos,collision_info_struct * debris_hit_info)807 int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, collision_info_struct *debris_hit_info)
808 {
809 	mc_info	mc;
810 	mc_info_init(&mc);
811 	int		num;
812 
813 	Assert( pdebris->type == OBJ_DEBRIS );
814 
815 	num = pdebris->instance;
816 	Assert( num >= 0 );
817 
818 	Assert( Debris[num].objnum == OBJ_INDEX(pdebris));
819 
820 	// debris_hit_info NULL - so debris-weapon collision
821 	if ( debris_hit_info == NULL ) {
822 		// debris weapon collision
823 		Assert( other_obj->type == OBJ_WEAPON );
824 		mc.model_instance_num = -1;
825 		mc.model_num = Debris[num].model_num;	// Fill in the model to check
826 		mc.submodel_num = Debris[num].submodel_num;
827 		model_clear_instance( mc.model_num );
828 		mc.orient = &pdebris->orient;					// The object's orient
829 		mc.pos = &pdebris->pos;							// The object's position
830 		mc.p0 = &other_obj->last_pos;				// Point 1 of ray to check
831 		mc.p1 = &other_obj->pos;					// Point 2 of ray to check
832 		mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL);
833 
834 		if (model_collide(&mc)) {
835 			*hitpos = mc.hit_point_world;
836 		}
837 
838 		weapon *wp = &Weapons[other_obj->instance];
839 		wp->collisionOccured = true;
840 		memcpy(&wp->collisionInfo, &mc, sizeof(mc_info));
841 
842 		return mc.num_hits;
843 	}
844 
845 	// debris ship collision -- use debris_hit_info to calculate physics
846 	object *ship_obj = other_obj;
847 	Assert( ship_obj->type == OBJ_SHIP );
848 
849 	object *heavy = debris_hit_info->heavy;
850 	object *light = debris_hit_info->light;
851 	object *heavy_obj = heavy;
852 	object *light_obj = light;
853 
854 	vec3d zero, p0, p1;
855 	vm_vec_zero(&zero);
856 	vm_vec_sub(&p0, &light->last_pos, &heavy->last_pos);
857 	vm_vec_sub(&p1, &light->pos, &heavy->pos);
858 
859 	mc.pos = &zero;								// The object's position
860 	mc.p0 = &p0;									// Point 1 of ray to check
861 	mc.p1 = &p1;									// Point 2 of ray to check
862 
863 	// find the light object's position in the heavy object's reference frame at last frame and also in this frame.
864 	vec3d p0_temp, p0_rotated;
865 
866 	// Collision detection from rotation enabled if at rotaion is less than 30 degree in frame
867 	// This should account for all ships
868 	if ( (vm_vec_mag_squared(&heavy->phys_info.rotvel) * flFrametime*flFrametime) < (PI*PI/36) ) {
869 		// collide_rotate calculate (1) start position and (2) relative velocity
870 		debris_hit_info->collide_rotate = 1;
871 		vm_vec_rotate(&p0_temp, &p0, &heavy->last_orient);
872 		vm_vec_unrotate(&p0_rotated, &p0_temp, &heavy->orient);
873 		mc.p0 = &p0_rotated;				// Point 1 of ray to check
874 		vm_vec_sub(&debris_hit_info->light_rel_vel, &p1, &p0_rotated);
875 		vm_vec_scale(&debris_hit_info->light_rel_vel, 1/flFrametime);
876 	} else {
877 		debris_hit_info->collide_rotate = 0;
878 		vm_vec_sub(&debris_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel);
879 	}
880 
881 	int mc_ret_val = 0;
882 
883 	if ( debris_hit_info->heavy == ship_obj ) {	// ship is heavier, so debris is sphere. Check sphere collision against ship poly model
884 		mc.model_instance_num = Ships[ship_obj->instance].model_instance_num;
885 		mc.model_num = Ship_info[Ships[ship_obj->instance].ship_info_index].model_num;	// Fill in the model to check
886 		mc.orient = &ship_obj->orient;								// The object's orient
887 		mc.radius = pdebris->radius;
888 		mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
889 
890 		// copy important data
891 		int copy_flags = mc.flags;  // make a copy of start end positions of sphere in  big ship RF
892 		vec3d copy_p0, copy_p1;
893 		copy_p0 = *mc.p0;
894 		copy_p1 = *mc.p1;
895 
896 		// first test against the sphere - if this fails then don't do any submodel tests
897 		mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE;
898 
899 		SCP_vector<int> submodel_vector;
900 		polymodel *pm;
901 		polymodel_instance *pmi;
902 
903 		ship_model_start(ship_obj);
904 
905 		if (model_collide(&mc)) {
906 
907 			// Set earliest hit time
908 			debris_hit_info->hit_time = FLT_MAX;
909 
910 			// Do collision the cool new way
911 			if ( debris_hit_info->collide_rotate ) {
912 				SCP_vector<int>::iterator smv;
913 
914 				// We collide with the sphere, find the list of rotating submodels and test one at a time
915 				model_get_rotating_submodel_list(&submodel_vector, heavy_obj);
916 
917 				// Get polymodel and turn off all rotating submodels, collide against only 1 at a time.
918 				pm = model_get(Ship_info[Ships[heavy_obj->instance].ship_info_index].model_num);
919 				pmi = model_get_instance(Ships[heavy_obj->instance].model_instance_num);
920 
921 				// turn off all rotating submodels and test for collision
922 				for (smv = submodel_vector.begin(); smv != submodel_vector.end(); ++smv) {
923 					pmi->submodel[*smv].collision_checked = true;
924 				}
925 
926 				// reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
927 				mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
928 
929 				// check each submodel in turn
930 				for (smv = submodel_vector.begin(); smv != submodel_vector.end(); ++smv) {
931 					// turn on submodel for collision test
932 					pmi->submodel[*smv].collision_checked = false;
933 
934 					// set angles for last frame (need to set to prev to get p0)
935 					angles copy_angles = pmi->submodel[*smv].angs;
936 
937 					// find the start and end positions of the sphere in submodel RF
938 					pmi->submodel[*smv].angs = pmi->submodel[*smv].prev_angs;
939 					world_find_model_instance_point(&p0, &light_obj->last_pos, pm, pmi, *smv, &heavy_obj->last_orient, &heavy_obj->last_pos);
940 
941 					pmi->submodel[*smv].angs = copy_angles;
942 					world_find_model_instance_point(&p1, &light_obj->pos, pm, pmi, *smv, &heavy_obj->orient, &heavy_obj->pos);
943 
944 					mc.p0 = &p0;
945 					mc.p1 = &p1;
946 
947 					mc.orient = &vmd_identity_matrix;
948 					mc.submodel_num = *smv;
949 
950 					if ( model_collide(&mc) ) {
951 						if ( mc.hit_dist < debris_hit_info->hit_time ) {
952 							mc_ret_val = 1;
953 
954 							// set up debris_hit_info common
955 							set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_ROT_HIT);
956 							model_instance_find_world_point(&debris_hit_info->hit_pos, &mc.hit_point, mc.model_num, mc.model_instance_num, mc.hit_submodel, &heavy_obj->orient, &zero);
957 
958 							// set up debris_hit_info for rotating submodel
959 							if (debris_hit_info->edge_hit == 0) {
960 								model_instance_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
961 							}
962 
963 							// find position in submodel RF of light object at collison
964 							vec3d int_light_pos, diff;
965 							vm_vec_sub(&diff, mc.p1, mc.p0);
966 							vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist);
967 							model_instance_find_world_point(&debris_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.model_instance_num, mc.hit_submodel, &heavy_obj->orient, &zero);
968 						}
969 					}
970 					// Don't look at this submodel again
971 					pmi->submodel[*smv].collision_checked = true;
972 				}
973 			}
974 
975 			// Recover and do usual ship_ship collision, but without rotating submodels
976 			mc.flags = copy_flags;
977 			*mc.p0 = copy_p0;
978 			*mc.p1 = copy_p1;
979 			mc.orient = &heavy_obj->orient;
980 
981 			// usual ship_ship collision test
982 			if ( model_collide(&mc) )	{
983 				// check if this is the earliest hit
984 				if (mc.hit_dist < debris_hit_info->hit_time) {
985 					mc_ret_val = 1;
986 
987 					set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
988 
989 					// get collision normal if not edge hit
990 					if (debris_hit_info->edge_hit == 0) {
991 						model_instance_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
992 					}
993 
994 					// find position in submodel RF of light object at collison
995 					vec3d diff;
996 					vm_vec_sub(&diff, mc.p1, mc.p0);
997 					vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
998 
999 				}
1000 			}
1001 		}
1002 
1003 	} else {
1004 		// Debris is heavier obj
1005 		mc.model_instance_num = -1;
1006 		mc.model_num = Debris[num].model_num;		// Fill in the model to check
1007 		mc.submodel_num = Debris[num].submodel_num;
1008 		model_clear_instance( mc.model_num );
1009 		mc.orient = &pdebris->orient;				// The object's orient
1010 		mc.radius = model_get_core_radius(Ship_info[Ships[ship_obj->instance].ship_info_index].model_num);
1011 
1012 		// check for collision between debris model and ship sphere
1013 		mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL | MC_CHECK_SPHERELINE);
1014 
1015 		mc_ret_val = model_collide(&mc);
1016 
1017 		if (mc_ret_val) {
1018 			set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1019 
1020 			// set normal if not edge hit
1021 			if ( !debris_hit_info->edge_hit ) {
1022 				vm_vec_unrotate(&debris_hit_info->collision_normal, &mc.hit_normal, &heavy->orient);
1023 			}
1024 
1025 			// find position in submodel RF of light object at collison
1026 			vec3d diff;
1027 			vm_vec_sub(&diff, mc.p1, mc.p0);
1028 			vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1029 
1030 		}
1031 	}
1032 
1033 
1034 	if ( mc_ret_val )	{
1035 
1036 		// SET PHYSICS PARAMETERS
1037 		// already have (hitpos - heavy) and light_cm_pos
1038 		// get heavy cm pos - already have light_cm_pos
1039 		debris_hit_info->heavy_collision_cm_pos = zero;
1040 
1041 		// get r_heavy and r_light
1042 		debris_hit_info->r_heavy = debris_hit_info->hit_pos;
1043 		vm_vec_sub(&debris_hit_info->r_light, &debris_hit_info->hit_pos, &debris_hit_info->light_collision_cm_pos);
1044 
1045 		// set normal for edge hit
1046 		if ( debris_hit_info->edge_hit ) {
1047 			vm_vec_copy_normalize(&debris_hit_info->collision_normal, &debris_hit_info->r_light);
1048 			vm_vec_negate(&debris_hit_info->collision_normal);
1049 		}
1050 
1051 		// get world hitpos
1052 		vm_vec_add(hitpos, &debris_hit_info->heavy->pos, &debris_hit_info->r_heavy);
1053 
1054 		return 1;
1055 	} else {
1056 		// no hit
1057 		return 0;
1058 	}
1059 }
1060 
1061 /**
1062  * Return the team field for a debris object
1063  */
debris_get_team(object * objp)1064 int debris_get_team(object *objp)
1065 {
1066 	Assert( objp->type == OBJ_DEBRIS );
1067 	Assert( objp->instance >= 0 && objp->instance < MAX_DEBRIS_PIECES );
1068 	return Debris[objp->instance].team;
1069 }
1070 
1071 /**
1072  * Fills in debris physics properties when created, specifically mass and moment of inertia
1073  */
calc_debris_physics_properties(physics_info * pi,vec3d * mins,vec3d * maxs)1074 void calc_debris_physics_properties( physics_info *pi, vec3d *mins, vec3d *maxs )
1075 {
1076 	float dx, dy, dz, mass;
1077 	dx = maxs->xyz.x - mins->xyz.x;
1078 	dy = maxs->xyz.y - mins->xyz.y;
1079 	dz = maxs->xyz.z - mins->xyz.z;
1080 
1081 	// John, with new bspgen, just set pi->mass = mass
1082 	mass = 0.12f * dx * dy * dz;
1083 	pi->mass = (float) pow(mass, 0.6666667f) * 4.65f;
1084 
1085 	pi->I_body_inv.vec.rvec.xyz.x = 12.0f / (pi->mass *  (dy*dy + dz*dz));
1086 	pi->I_body_inv.vec.rvec.xyz.y = 0.0f;
1087 	pi->I_body_inv.vec.rvec.xyz.z = 0.0f;
1088 
1089 	pi->I_body_inv.vec.uvec.xyz.x = 0.0f;
1090 	pi->I_body_inv.vec.uvec.xyz.y = 12.0f / (pi->mass *  (dx*dx + dz*dz));
1091 	pi->I_body_inv.vec.uvec.xyz.z = 0.0f;
1092 
1093 	pi->I_body_inv.vec.fvec.xyz.x = 0.0f;
1094 	pi->I_body_inv.vec.fvec.xyz.y = 0.0f;
1095 	pi->I_body_inv.vec.fvec.xyz.z = 12.0f / (pi->mass *  (dx*dx + dy*dy));
1096 }
1097