]> icculus.org git repositories - taylor/freespace2.git/blob - src/asteroid/asteroid.cpp
Initial revision
[taylor/freespace2.git] / src / asteroid / asteroid.cpp
1 /*
2  * $Logfile: /Freespace2/code/Asteroid/Asteroid.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module for asteroid code
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:11  root
11  * Initial revision
12  *
13  * 
14  * 31    9/08/99 10:52a Mikek
15  * Reduce the number of asteroids that can be thrown at a target at
16  * Medium+.
17  * 
18  * 30    9/06/99 12:46a Andsager
19  * Add weapon_explosion_ani LOD
20  * 
21  * 29    9/05/99 3:13p Mikek
22  * Slightly decrease number of asteroids thrown at escorted ships.
23  * 
24  * 28    8/26/99 8:51p Dave
25  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
26  * 
27  * 27    7/30/99 7:01p Dave
28  * Dogfight escort gauge. Fixed up laser rendering in Glide.
29  * 
30  * 26    7/19/99 8:58p Andsager
31  * Don't try to throw at objnum -1
32  * 
33  * 25    7/15/99 9:20a Andsager
34  * FS2_DEMO initial checkin
35  * 
36  * 24    7/09/99 5:54p Dave
37  * Seperated cruiser types into individual types. Added tons of new
38  * briefing icons. Campaign screen.
39  * 
40  * 23    6/10/99 11:06a Andsager
41  * Mission designed selection of asteroid types.
42  * 
43  * 22    6/09/99 2:55p Andsager
44  * Allow multiple asteroid subtypes (of large, medium, small) and follow
45  * family.
46  * 
47  * 21    6/08/99 7:19p Dave
48  * Fixed asteroid lighting. Was because on non-darkening textures in Glide
49  * only. Dumb.
50  * 
51  * 20    6/07/99 1:18p Andsager
52  * Make asteroids choose consistent texture from large to small.  Modify
53  * fireball radius of dying asteroids.
54  * 
55  * 19    5/03/99 10:50p Andsager
56  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
57  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
58  * obj_used_list.
59  * 
60  * 18    4/23/99 12:01p Johnson
61  * Added SIF_HUGE_SHIP
62  * 
63  * 17    4/21/99 6:15p Dave
64  * Did some serious housecleaning in the beam code. Made it ready to go
65  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
66  * a handy macro for recalculating collision pairs for a given object.
67  * 
68  * 16    4/16/99 2:34p Andsager
69  * Second pass on debris fields
70  * 
71  * 15    4/15/99 5:27p Andsager
72  * Fix explosion sound for Ships debris
73  * 
74  * 14    4/15/99 5:00p Andsager
75  * Frist pass on Debris field
76  * 
77  * 13    2/07/99 8:51p Andsager
78  * Add inner bound to asteroid field.  Inner bound tries to stay astroid
79  * free.  Wrap when within and don't throw at ships inside.
80  * 
81  * 12    1/20/99 6:04p Dave
82  * Another bit of stuff for beam weapons. Ships will properly use them
83  * now, although they're really deadly.
84  * 
85  * 11    1/06/99 2:24p Dave
86  * Stubs and release build fixes.
87  * 
88  * 10    12/03/98 3:14p Andsager
89  * Check in code that checks rotating submodel actually has ship subsystem
90  * 
91  * 9     11/19/98 11:07p Andsager
92  * Check in of physics and collision detection of rotating submodels
93  * 
94  * 8     11/13/98 10:12a Andsager
95  * simplify collision code
96  * 
97  * 7     11/05/98 5:55p Dave
98  * Big pass at reducing #includes
99  * 
100  * 6     10/23/98 3:51p Dave
101  * Full support for tstrings.tbl and foreign languages. All that remains
102  * is to make it active in Fred.
103  * 
104  * 5     10/23/98 1:11p Andsager
105  * Make ship sparks emit correctly from rotating structures.
106  * 
107  * 4     10/16/98 1:22p Andsager
108  * clean up header files
109  * 
110  * 3     10/13/98 9:28a Dave
111  * Started neatening up freespace.h. Many variables renamed and
112  * reorganized. Added AlphaColors.[h,cpp]
113  * 
114  * 2     10/07/98 10:52a Dave
115  * Initial checkin.
116  * 
117  * 1     10/07/98 10:48a Dave
118  * 
119  * 80    7/14/98 3:30p Dave
120  * 
121  * 79    5/18/98 12:05p Mike
122  * Make sm1-06a a little harder.
123  * 
124  * 78    5/06/98 12:36p Lawrance
125  * disable ambient asteroid sound for now
126  * 
127  * 77    4/30/98 12:49a Allender
128  * deal with asteroid problems in multiplayer
129  * 
130  * 76    4/27/98 1:46p Mike
131  * Make asteroid_density have no meaning.
132  * 
133  * 75    4/24/98 11:59a Dan
134  * fix bug where a null vector would occur when death_hit_pos was assinged
135  * to asteroid_pos
136  * 
137  * 74    4/23/98 4:43p Andsager
138  * Don't call   asteroid_update_collide() on creating asteroids since it
139  * creates sets of collisions pairs.  Pairs are created when objects are
140  * merged in obj_merge_created_list()
141  * 
142  * 73    4/21/98 1:23a Mike
143  * Tone down difficulty in asteroid toss missions.
144  * 
145  * 72    4/16/98 11:57a John
146  * Removed debug and unused registry values.
147  * 
148  * 71    4/08/98 1:27a Lawrance
149  * Fix typo.
150  * 
151  * 70    4/08/98 1:17a Lawrance
152  * Fix bug with red and white brackets being drawn on top of one another.
153  * 
154  * 69    4/02/98 6:28p Lawrance
155  * remove asteroid code from demo
156  * 
157  * 68    4/02/98 5:11p Mike
158  * Quick out of asteroid code if none present.
159  * 
160  * 67    4/02/98 1:34p Mike
161  * Minor difficulty balance, made a bit easier.
162  * 
163  * 66    4/02/98 1:29p Andsager
164  * 
165  * 65    4/01/98 5:34p John
166  * Made only the used POFs page in for a level.   Reduced some interp
167  * arrays.    Made custom detail level work differently.
168  * 
169  * 64    3/31/98 5:11p John
170  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
171  * bunch of debug stuff out of player file.  Made model code be able to
172  * unload models and malloc out only however many models are needed.
173  * 
174  * 63    3/30/98 4:02p John
175  * Made machines with < 32 MB of RAM use every other frame of certain
176  * bitmaps.   Put in code to keep track of how much RAM we've malloc'd.
177  * 
178  * 62    3/30/98 2:38p Mike
179  * Add asteroid_density to detail level support.
180  * No force explosion damage in training missions.
181  * Make cargo deathrolls shorter.
182  * 
183  * 61    3/30/98 10:07a Lawrance
184  * Um, make asteroids work again.
185  * 
186  * 60    3/29/98 12:55a Lawrance
187  * Get demo build working with limited set of data.
188  * 
189  * 59    3/26/98 9:19a Lawrance
190  * Support multiple asteroid pofs
191  * 
192  * 58    3/25/98 3:44p Andsager
193  * Fixed bug in asteroid_sub_create, where last_pos field was invalid in
194  * its creation frame but needed for collision in that frame.
195  * 
196  * 57    3/25/98 2:03p Sandeep
197  * Remove "f" from top of file.
198  * 
199  * 56    3/25/98 1:50p Mike
200  * Asteroid destruction (sm1-06a) balancing.
201  * 
202  * 55    3/25/98 10:43a Andsager
203  * Hack for ship_asteroid collisions when erroneous relative_vel >150
204  * 
205  * 54    3/23/98 12:20p Andsager
206  * Enable collision from rotation in ship_debris and ship_debris
207  * collisions.
208  * 
209  * 53    3/22/98 4:09p Andsager
210  * Make moment of inertia based on model.
211  * 
212  * 52    3/21/98 3:33p Lawrance
213  * When asteroid collides with an escort ship, make the sound carry
214  * farther
215  * 
216  * 51    3/19/98 12:09p John
217  * Fixed a bug using 6 characters.   r_heavy was using local coordinates
218  * instead of world so all asteroid-ship and debris-ship hitpos's were in
219  * the wrong spot.  Ok, I rearreanged some code to make it clearer also.
220  * 
221  * 50    3/17/98 5:55p Lawrance
222  * Support object linked sounds for asteroids.
223  * 
224  * 49    3/17/98 3:44p Mike
225  * Make asteroids-thrown-at-target not only happen when an asteroid wraps.
226  * 
227  * 48    3/17/98 9:53a Lawrance
228  * Asteroid area effect only affect ships
229  * 
230  * 47    3/17/98 9:25a Allender
231  * some minor asteroid changes for multiplayer
232  * 
233  * 46    3/17/98 12:16a Allender
234  * asteroids in multiplayer -- minor problems with position being correct
235  * 
236  * 45    3/14/98 1:44p Mike
237  * Todolist items 3365..3368.  Make child asteroids collide properly.
238  * Make asteroid throwing less bunchy, toss asteroids earlier, make
239  * facing-ness not break mission balance.
240  * 
241  * 44    3/13/98 9:06a John
242  * Made missile thrusters use a different min dist for scaling.   Made
243  * missilies not use lighting.
244  * 
245  * 43    3/12/98 4:01p Lawrance
246  * Only show warning brackets for cruiser/capital ships on the same team
247  * 
248  * $NoKeywords: $
249  */
250
251 #include "asteroid.h"
252 #include "object.h"
253 #include "objcollide.h"
254 #include "freespace.h"
255 #include "timer.h"
256 #include "3d.h"
257 #include "fireballs.h"
258 #include "gamesnd.h"
259 #include "bmpman.h"
260 #include "particle.h"
261 #include "linklist.h"
262 #include "hudescort.h"
263 #include "shiphit.h"
264 #include "multiutil.h"
265 #include "staticrand.h"
266 #include "multimsgs.h"
267 #include "systemvars.h"
268 #include "localize.h"
269 #include "multi.h"
270
271 #ifndef FS2_DEMO
272
273 #define                 ASTEROID_OBJ_USED       (1<<0)                          // flag used in asteroid_obj struct
274 #define                 MAX_ASTEROID_OBJS       MAX_ASTEROIDS           // max number of asteroids tracked in asteroid list
275 asteroid_obj    Asteroid_objs[MAX_ASTEROID_OBJS];       // array used to store asteroid object indexes
276 asteroid_obj    Asteroid_obj_list;                                              // head of linked list of asteroid_obj structs
277
278 // Asteroid editor requires first set of entries to be "None" and then "Asteroid XXX"
279 // Any changes to this will require changes to the asteroid editor
280 debris_struct Field_debris_info[] = {
281         { -1,                                                   "None", },
282         { ASTEROID_TYPE_SMALL,          "Asteroid Small", },
283         { ASTEROID_TYPE_MEDIUM, "Asteroid Medium", },
284         { ASTEROID_TYPE_BIG,            "Asteroid Large", },
285
286         { DEBRIS_TERRAN_SMALL,          "Terran Small", },
287         { DEBRIS_TERRAN_MEDIUM, "Terran Medium", },
288         { DEBRIS_TERRAN_LARGE,          "Terran Large", },
289
290         { DEBRIS_VASUDAN_SMALL, "Vasudan Small", },
291         { DEBRIS_VASUDAN_MEDIUM,        "Vasudan Medium", },
292         { DEBRIS_VASUDAN_LARGE, "Vasudan Large", },
293
294         { DEBRIS_SHIVAN_SMALL,          "Shivan Small", },
295         { DEBRIS_SHIVAN_MEDIUM, "Shivan Medium", },
296         { DEBRIS_SHIVAN_LARGE,          "Shivan Large" },
297 };
298
299 // used for randomly generating debris type when there are multiple sizes.
300 #define SMALL_DEBRIS_WEIGHT     8
301 #define MEDIUM_DEBRIS_WEIGHT    4
302 #define LARGE_DEBRIS_WEIGHT     1
303
304 int     Asteroids_enabled = 1;
305 int     Num_asteroid_types;
306 int     Num_asteroids = 0;
307 int     Asteroid_throw_objnum = -1;             //      Object index of ship to throw asteroids at.
308 int     Next_asteroid_throw;
309
310 asteroid_info   Asteroid_info[MAX_DEBRIS_TYPES];
311 asteroid                        Asteroids[MAX_ASTEROIDS];
312 asteroid_field  Asteroid_field;
313
314 static int              Asteroid_impact_explosion_ani;
315 static float    Asteroid_impact_explosion_radius;
316
317 #define ASTEROID_CHECK_WRAP_TIMESTAMP                   2000    // how often an asteroid gets checked for wrapping
318 #define ASTEROID_UPDATE_COLLIDE_TIMESTAMP       2000    // how often asteroid is checked for impending collisions with escort ships
319 #define ASTEROID_MIN_COLLIDE_TIME                               24              // time in seconds to check for asteroid colliding
320
321 //      Force updating of pair stuff for asteroid *objp.
322 void asteroid_update_collide(object *objp)
323 {
324         // Asteroid has wrapped, update collide objnum and flags
325         Asteroids[objp->instance].collide_objnum = -1;
326         Asteroids[objp->instance].collide_objsig = -1;
327         OBJ_RECALC_PAIRS(objp); 
328 }
329
330 // Clear out the Asteroid_obj_list
331 //
332 void asteroid_obj_list_init()
333 {
334         int i;
335
336         list_init(&Asteroid_obj_list);
337         for ( i = 0; i < MAX_ASTEROID_OBJS; i++ ) {
338                 Asteroid_objs[i].flags = 0;
339         }
340 }
341
342 // ---------------------------------------------------
343 // asteroid_obj_list_add()
344 //
345 // Function to add a node from the Asteroid_obj_list.  Only
346 // called from weapon_create()
347 int asteroid_obj_list_add(int objnum)
348 {
349         int index;
350
351         asteroid *cur_asteroid = &Asteroids[Objects[objnum].instance];
352         index = cur_asteroid - Asteroids;
353
354         Assert(index >= 0 && index < MAX_ASTEROID_OBJS);
355         Assert(!Asteroid_objs[index].flags & ASTEROID_OBJ_USED);
356
357         Asteroid_objs[index].flags = 0;
358         Asteroid_objs[index].objnum = objnum;
359         list_append(&Asteroid_obj_list, &Asteroid_objs[index]);
360         Asteroid_objs[index].flags |= ASTEROID_OBJ_USED;
361
362         return index;
363 }
364
365 // ---------------------------------------------------
366 // missle_obj_list_remove()
367 //
368 // Function to remove a node from the Asteroid_obj_list.  Only
369 // called from weapon_delete()
370 void asteroid_obj_list_remove(object * obj)
371 {
372         int index = obj->instance;
373
374         Assert(index >= 0 && index < MAX_ASTEROID_OBJS);
375         Assert(Asteroid_objs[index].flags & ASTEROID_OBJ_USED);
376
377         list_remove(&Asteroid_obj_list, &Asteroid_objs[index]); 
378         Asteroid_objs[index].flags = 0;
379 }
380
381
382 //      Prevent speed from getting too huge so it's hard to catch up to an asteroid.
383 float asteroid_cap_speed(int asteroid_info_index, float speed)
384 {
385         float max, double_max;
386
387         max = Asteroid_info[asteroid_info_index].max_speed;
388         double_max = max * 2;
389
390         while (speed > double_max){
391                 speed *= 0.5f;
392         }
393         
394         if (speed > max){
395                 speed *= 0.75f;
396         }
397
398         return speed;
399 }
400
401 // Returns whether position is inside inner bounding volume
402 // sum together the following: 1 inside x, 2 inside y, 4 inside z
403 // inside only when sum = 7
404 int asteroid_in_inner_bound_with_axes(asteroid_field *asfieldp, vector *pos, float delta)
405 {
406         Assert(asfieldp->has_inner_bound);
407
408         int rval = 0;
409         if ( (pos->x > asfieldp->inner_min_bound.x - delta) && (pos->x < asfieldp->inner_max_bound.x + delta) ) {
410                 rval += 1;
411         }
412
413         if ( (pos->y > asfieldp->inner_min_bound.y - delta) && (pos->y < asfieldp->inner_max_bound.y + delta) ) {
414                 rval += 2;
415         }
416
417         if ( (pos->z > asfieldp->inner_min_bound.z - delta) && (pos->z < asfieldp->inner_max_bound.z + delta) ) {
418                 rval += 4;
419         }
420
421         return rval;
422 }
423
424 // check if asteroid is within inner bound
425 // return 0 if not inside or no inner bound, 1 if inside inner bound
426 int asteroid_in_inner_bound(asteroid_field *asfieldp, vector *pos, float delta) {
427
428         if (!asfieldp->has_inner_bound) {
429                 return 0;
430         }
431
432         return (asteroid_in_inner_bound_with_axes(asfieldp, pos, delta) == 7);
433 }
434
435 // repositions asteroid outside the inner box on all 3 axes
436 // moves to the other side of the inner box a distance delta from edge of box
437 void inner_bound_pos_fixup(asteroid_field *asfieldp, vector *pos)
438 {
439         if (!asteroid_in_inner_bound(asfieldp, pos, 0)) {
440                 return;
441         }
442
443         float dist1, dist2;
444         int axis;
445
446         for (axis=0; axis<3; axis++) {
447                 dist1 = pos->a1d[axis] - asfieldp->inner_min_bound.a1d[axis];
448                 dist2 = asfieldp->inner_max_bound.a1d[axis] - pos->a1d[axis];
449                 Assert(dist1 >= 0 && dist2 >= 0);
450
451                 if (dist1 < dist2) {
452                         pos->a1d[axis] = asfieldp->inner_max_bound.a1d[axis] + dist1;
453                 } else {
454                         pos->a1d[axis] = asfieldp->inner_min_bound.a1d[axis] - dist2;
455                 }
456         }
457 }
458
459
460 // Create a single asteroid 
461 object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroid_subtype)
462 {
463         int                             n, objnum;
464         matrix                  orient;
465         object                  *objp;
466         asteroid                        *asp;
467         asteroid_info   *asip;
468         vector                  pos, delta_bound;
469         angles                  angs;
470         float                           radius;
471         ushort                  signature;
472         int                             rand_base;
473
474         // bogus
475         if(asfieldp == NULL){
476                 return NULL;
477         }
478
479         for (n=0; n<MAX_ASTEROIDS; n++ ) {
480                 if ( !(Asteroids[n].flags & AF_USED) ){
481                         break;
482                 }
483         }
484
485         if (n >= MAX_ASTEROIDS) {
486                 nprintf(("Warning","Could not create asteroid, no more slots left\n"));
487                 return NULL;
488         }
489
490         if((asteroid_type < 0) || (asteroid_type >= Num_asteroid_types)){
491                 return NULL;
492         }
493
494         // HACK: multiplayer asteroid subtype always 0 to keep subtype in sync
495         if ( Game_mode & GM_MULTIPLAYER) {
496                 asteroid_subtype = 0;
497         }       
498
499         if( (asteroid_subtype < 0) || (asteroid_subtype >= MAX_ASTEROID_POFS)){
500                 return NULL;
501         }
502
503         asip = &Asteroid_info[asteroid_type];
504
505         // bogus
506         if(asip->modelp[asteroid_subtype] == NULL){
507                 return NULL;
508         }       
509
510         asp = &Asteroids[n];
511         asp->type = asteroid_type;
512         asp->asteroid_subtype = asteroid_subtype;
513         asp->flags = 0;
514         asp->flags |= AF_USED;
515         asp->check_for_wrap = timestamp_rand(0, ASTEROID_CHECK_WRAP_TIMESTAMP);
516         asp->check_for_collide = timestamp_rand(0, ASTEROID_UPDATE_COLLIDE_TIMESTAMP);
517         asp->final_death_time = timestamp(-1);
518         asp->collide_objnum = -1;
519         asp->collide_objsig = -1;
520         asp->target_objnum = -1;
521
522         radius = model_get_radius(asip->model_num[asteroid_subtype]);
523
524         vm_vec_sub(&delta_bound, &asfieldp->max_bound, &asfieldp->min_bound);
525
526         // for multiplayer, we want to do a static_rand so that everything behaves the same on all machines
527         signature = 0;
528         rand_base = 0;
529         if ( Game_mode & GM_NORMAL ) {
530                 pos.x = asfieldp->min_bound.x + delta_bound.x * frand();
531                 pos.y = asfieldp->min_bound.y + delta_bound.y * frand();
532                 pos.z = asfieldp->min_bound.z + delta_bound.z * frand();
533
534                 inner_bound_pos_fixup(asfieldp, &pos);
535                 // vm_set_identity(&orient);
536                 angs.p = frand() * 2*PI;
537                 angs.b = frand() * 2*PI;
538                 angs.h = frand() * 2*PI;
539         } else {
540                 signature = multi_assign_network_signature( MULTI_SIG_ASTEROID );
541                 rand_base = signature;
542
543                 pos.x = asfieldp->min_bound.x + delta_bound.x * static_randf( rand_base++ );
544                 pos.y = asfieldp->min_bound.y + delta_bound.y * static_randf( rand_base++ );
545                 pos.z = asfieldp->min_bound.z + delta_bound.z * static_randf( rand_base++ );
546
547                 inner_bound_pos_fixup(asfieldp, &pos);
548                 // vm_set_identity(&orient);
549                 angs.p = static_randf( rand_base++ ) * 2*PI;
550                 angs.b = static_randf( rand_base++ ) * 2*PI;
551                 angs.h = static_randf( rand_base++ ) * 2*PI;
552         }
553
554         vm_angles_2_matrix(&orient, &angs);
555
556         objnum = obj_create( OBJ_ASTEROID, -1, n, &orient, &pos, radius, OF_RENDERS | OF_PHYSICS | OF_COLLIDES);
557         // mprintf(("Framecount: %d asteroid create: obj = %d\n", Framecount, objnum));
558         
559         if ( (objnum == -1) || (objnum >= MAX_OBJECTS) ) {
560                 mprintf(("Couldn't create asteroid -- out of object slots\n"));
561                 return NULL;
562         }
563
564         asp->objnum = objnum;
565
566         // Add to Asteroid_used_list
567         asteroid_obj_list_add(objnum);
568
569         objp = &Objects[objnum];
570
571         if ( Game_mode & GM_MULTIPLAYER ){
572                 objp->net_signature = signature;
573         }
574
575         Num_asteroids++;
576
577         if (radius < 1.0) {
578                 radius = 1.0f;
579         }
580
581         vector rotvel;
582         if ( Game_mode & GM_NORMAL ) {
583                 vm_vec_rand_vec_quick(&rotvel);
584                 vm_vec_scale(&rotvel, frand()/4.0f + 0.1f);
585                 objp->phys_info.rotvel = rotvel;
586                 vm_vec_rand_vec_quick(&objp->phys_info.vel);
587         } else {
588                 static_randvec( rand_base++, &rotvel );
589                 vm_vec_scale(&rotvel, static_randf(rand_base++)/4.0f + 0.1f);
590                 objp->phys_info.rotvel = rotvel;
591                 static_randvec( rand_base++, &objp->phys_info.vel );
592         }
593
594
595         float speed;
596
597         if ( Game_mode & GM_NORMAL ) {
598                 speed = asteroid_cap_speed(asteroid_type, asfieldp->speed*frand_range(0.5f + (float) Game_skill_level/NUM_SKILL_LEVELS, 2.0f + (float) (2*Game_skill_level)/NUM_SKILL_LEVELS));
599         } else {
600                 speed = asteroid_cap_speed(asteroid_type, asfieldp->speed*static_randf_range(rand_base++, 0.5f + (float) Game_skill_level/NUM_SKILL_LEVELS, 2.0f + (float) (2*Game_skill_level)/NUM_SKILL_LEVELS));
601         }
602         
603         vm_vec_scale(&objp->phys_info.vel, speed);
604         objp->phys_info.desired_vel = objp->phys_info.vel;
605
606         // blow out his reverse thrusters. Or drag, same thing.
607         objp->phys_info.rotdamp = 10000.0f;
608         objp->phys_info.side_slip_time_const = 10000.0f;
609         objp->phys_info.flags |= (PF_REDUCED_DAMP | PF_DEAD_DAMP);      // set damping equal for all axis and not changable
610
611         // Fill in the max_vel field, so the collision pair stuff knows
612         // how fast this can move maximum in order to throw out collisions.
613         // This is in local coordinates, so Z is forward velocity.
614         objp->phys_info.max_vel.x = 0.0f;
615         objp->phys_info.max_vel.y = 0.0f;
616         objp->phys_info.max_vel.z = vm_vec_mag(&objp->phys_info.desired_vel);
617         
618         objp->phys_info.mass = asip->modelp[asteroid_subtype]->rad * 700.0f;
619         objp->phys_info.I_body_inv.rvec.x = 1.0f / (objp->phys_info.mass*asip->modelp[asteroid_subtype]->rad);
620         objp->phys_info.I_body_inv.uvec.y = objp->phys_info.I_body_inv.rvec.x;
621         objp->phys_info.I_body_inv.fvec.z = objp->phys_info.I_body_inv.rvec.x;
622         objp->hull_strength = asip->initial_hull_strength * (0.8f + (float)Game_skill_level/NUM_SKILL_LEVELS)/2.0f;
623
624         // ensure vel is valid
625         Assert( !vm_is_vec_nan(&objp->phys_info.vel) ); 
626
627         // assign a persistant sound to the asteroid
628 //      obj_snd_assign(objnum, SND_ASTEROID);
629
630         return objp;
631 }
632
633 //      Create asteroids when parent_objp blows up.
634 void asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec)
635 {
636         object  *new_objp;
637         float speed;
638
639         Assert(parent_objp->type == OBJ_ASTEROID);
640         int subtype = Asteroids[parent_objp->instance].asteroid_subtype;
641         new_objp = asteroid_create(&Asteroid_field, asteroid_type, subtype);
642
643         if (new_objp == NULL)
644                 return;
645
646         if ( MULTIPLAYER_MASTER ){
647                 send_asteroid_create( new_objp, parent_objp, asteroid_type, relvec );
648         }
649
650         //      Now, bash some values.
651         vm_vec_scale_add(&new_objp->pos, &parent_objp->pos, relvec, 0.5f * parent_objp->radius);
652         float parent_speed = vm_vec_mag_quick(&parent_objp->phys_info.vel);
653
654         if ( parent_speed < 0.1f ) {
655                 parent_speed = vm_vec_mag_quick(&Asteroid_field.vel);
656         }
657
658         new_objp->phys_info.vel = parent_objp->phys_info.vel;
659         if ( Game_mode & GM_NORMAL )
660                 speed = asteroid_cap_speed(asteroid_type, (frand() + 2.0f) * parent_speed);
661         else
662                 speed = asteroid_cap_speed(asteroid_type, (static_randf(new_objp->net_signature)+2.0f) * parent_speed);
663
664         vm_vec_scale_add2(&new_objp->phys_info.vel, relvec, speed);
665         if (vm_vec_mag_quick(&new_objp->phys_info.vel) > 80.0f)
666                 vm_vec_scale(&new_objp->phys_info.vel, 0.5f);
667
668         new_objp->phys_info.desired_vel = new_objp->phys_info.vel;
669         vm_vec_scale_add(&new_objp->last_pos, &new_objp->pos, &new_objp->phys_info.vel, -flFrametime);
670         // DA: 4/22/98  We get next line for free when new object (in obj_create_list) is merged.
671         // this line gives too many collision pairs.
672         //      asteroid_update_collide(new_objp);
673 }
674
675 // Load in an asteroid model
676 void asteroid_load(int asteroid_info_index, int asteroid_subtype)
677 {
678         asteroid_info   *asip;
679 //      int                             pof_index;
680
681         asip = &Asteroid_info[asteroid_info_index];
682
683         // pick one of MAX_ASTEROID_POFS models
684         // LOAD ALL TEXTURES USED
685 //      static int asteroid_pof_index = rand() % MAX_ASTEROID_POFS;
686 //      if (Asteroid_field.debris_genre == DG_ASTEROID) {
687 //              pof_index = asteroid_pof_index;
688 //      } else {
689 //              // only 1 pof for ship debris type
690 //              pof_index = 0;
691 //      }
692
693         asip->model_num[asteroid_subtype] = model_load( asip->pof_files[asteroid_subtype], 0, NULL );
694
695         if (asip->model_num[asteroid_subtype] > -1) {
696                 asip->modelp[asteroid_subtype] = model_get(asip->model_num[asteroid_subtype]);
697                 
698                 // Stuff detail level distances.
699                 for (int i = 0; i < asip->num_detail_levels; i++) {
700                         asip->modelp[asteroid_subtype]->detail_depth[i] = i2fl(asip->detail_distance[i]);
701                 }
702         }
703 }
704
705 // randomly choose a debris model within the current group
706 int get_debris_from_same_group(int index) {
707         int group_base, group_offset;
708
709         group_base = (index / 3) * 3;
710         group_offset = index - group_base;
711
712         // group base + offset
713         // offset is formed by adding 1 or 2 (since 3 in model group) and mapping back in the 0-2 range
714         return group_base + ((group_offset + rand()%2 + 1) % 3);
715 }
716
717 // returns a weight that depends on asteroid size.
718 // the weight is then used to determine the frequencty of different sizes of ship debris
719 int get_debris_weight(int ship_debris_index)
720 {
721         switch (ship_debris_index) {
722         case DEBRIS_TERRAN_SMALL:
723         case DEBRIS_VASUDAN_SMALL:
724         case DEBRIS_SHIVAN_SMALL:
725                 return SMALL_DEBRIS_WEIGHT;
726                 break;
727
728         case DEBRIS_TERRAN_MEDIUM:
729         case DEBRIS_VASUDAN_MEDIUM:
730         case DEBRIS_SHIVAN_MEDIUM:
731                 return MEDIUM_DEBRIS_WEIGHT;
732                 break;
733
734         case DEBRIS_TERRAN_LARGE:
735         case DEBRIS_VASUDAN_LARGE:
736         case DEBRIS_SHIVAN_LARGE:
737                 return LARGE_DEBRIS_WEIGHT;
738                 break;
739
740         default:
741                 Int3();
742                 return 1;
743                 break;
744         }
745 }
746
747 // Create all the asteroids for the mission, called from 
748 void asteroid_create_all()
749 {
750         int i, idx;
751
752         // ship_debris_odds_table keeps track of debris type of the next debris piece
753         // each different type (size) of debris piece has a diffenent weight, smaller weighted more heavily than larger.
754         // choose next type from table ship_debris_odds_table by rand()%max_weighted_range,
755         //  the first column in ship_debris_odds_table random number *below* which the debris type in the second column is selected.
756         int ship_debris_odds_table[3][2];
757         int max_weighted_range = 0;
758
759         if (!Asteroids_enabled)
760                 return;
761
762         if (Asteroid_field.num_initial_asteroids <= 0 ) {
763                 return;
764         }
765
766         int max_asteroids = Asteroid_field.num_initial_asteroids; // * (1.0f - 0.1f*(MAX_DETAIL_LEVEL-Detail.asteroid_density)));
767
768         int num_debris_types = 0;
769
770         // get number of ship debris types
771         if (Asteroid_field.debris_genre == DG_SHIP) {
772                 for (idx=0; idx<3; idx++) {
773                         if (Asteroid_field.field_debris_type[idx] != -1) {
774                                 num_debris_types++;
775                         }
776                 }
777
778                 // Calculate the odds table
779                 for (idx=0; idx<num_debris_types; idx++) {
780                         int debris_weight = get_debris_weight(Asteroid_field.field_debris_type[idx]);
781                         ship_debris_odds_table[idx][0] = max_weighted_range + debris_weight;
782                         ship_debris_odds_table[idx][1] = Asteroid_field.field_debris_type[idx];
783                         max_weighted_range += debris_weight;
784                 }
785         }
786
787         // Load Asteroid/ship models
788         if (Asteroid_field.debris_genre == DG_SHIP) {
789                 for (idx=0; idx<num_debris_types; idx++) {
790                         asteroid_load(Asteroid_field.field_debris_type[idx], 0);
791                 }
792         } else {
793                 if (Asteroid_field.field_debris_type[0] != -1) {
794                         asteroid_load(ASTEROID_TYPE_SMALL, 0);
795                         asteroid_load(ASTEROID_TYPE_MEDIUM, 0);
796                         asteroid_load(ASTEROID_TYPE_BIG, 0);
797                 }
798
799                 if (Asteroid_field.field_debris_type[1] != -1) {
800                         asteroid_load(ASTEROID_TYPE_SMALL, 1);
801                         asteroid_load(ASTEROID_TYPE_MEDIUM, 1);
802                         asteroid_load(ASTEROID_TYPE_BIG, 1);
803                 }
804
805                 if (Asteroid_field.field_debris_type[2] != -1) {
806                         asteroid_load(ASTEROID_TYPE_SMALL, 2);
807                         asteroid_load(ASTEROID_TYPE_MEDIUM, 2);
808                         asteroid_load(ASTEROID_TYPE_BIG, 2);
809                 }
810         }
811
812         // load all the asteroid/debris pieces
813         for (i=0; i<max_asteroids; i++) {
814                 if (Asteroid_field.debris_genre == DG_ASTEROID) {
815                         // For asteroid, load only large asteroids
816
817                         // get a valid subtype
818                         int subtype = rand() % 3;
819                         while (Asteroid_field.field_debris_type[subtype] == -1) {
820                                 subtype = (subtype + 1) % 3;
821                         }
822
823                         asteroid_create(&Asteroid_field, ASTEROID_TYPE_BIG, subtype);
824                 } else {
825                         Assert(num_debris_types > 0);
826
827                         int rand_choice = rand() % max_weighted_range;
828
829                         for (idx=0; idx<3; idx++) {
830                                 // for ship debris, choose type according to odds table
831                                 if (rand_choice < ship_debris_odds_table[idx][0]) {
832                                         asteroid_create(&Asteroid_field, ship_debris_odds_table[idx][1], 0);
833                                         break;
834                                 }
835                         }
836                 }
837         }
838 }
839
840 // Init asteriod system for the level, called from game_level_init()
841 void asteroid_level_init()
842 {
843         Asteroid_field.num_initial_asteroids=0;
844         Num_asteroids = 0;
845         Next_asteroid_throw = timestamp(1);
846         asteroid_obj_list_init();
847 }
848
849 // return !0 if asteroid should be wrapped, 0 otherwise.  Multiplayer clients will always return
850 // 0 from this function.  We will force a wrap on the clients when server tells us
851 int asteroid_should_wrap(object *objp, asteroid_field *asfieldp)
852 {
853         if ( MULTIPLAYER_CLIENT )
854                 return 0;
855
856         if (objp->pos.x < asfieldp->min_bound.x) {
857                 return 1;
858         }
859
860         if (objp->pos.y < asfieldp->min_bound.y) {
861                 return 1;
862         }
863
864         if (objp->pos.z < asfieldp->min_bound.z) {
865                 return 1;
866         }
867
868         if (objp->pos.x > asfieldp->max_bound.x) {
869                 return 1;
870         }
871
872         if (objp->pos.y > asfieldp->max_bound.y) {
873                 return 1;
874         }
875
876         if (objp->pos.z > asfieldp->max_bound.z) {
877                 return 1;
878         }
879
880         // check against inner bound
881         if (asfieldp->has_inner_bound) {
882                 if ( (objp->pos.x > asfieldp->inner_min_bound.x) && (objp->pos.x < asfieldp->inner_max_bound.x)
883                   && (objp->pos.y > asfieldp->inner_min_bound.y) && (objp->pos.y < asfieldp->inner_max_bound.y)
884                   && (objp->pos.z > asfieldp->inner_min_bound.z) && (objp->pos.z < asfieldp->inner_max_bound.z) ) {
885
886                         return 1;
887                 }
888         }
889
890         return 0;
891 }
892
893 // Wrap an asteroid from one end of the asteroid field to the other
894 void asteroid_wrap_pos(object *objp, asteroid_field *asfieldp)
895 {
896         if (objp->pos.x < asfieldp->min_bound.x) {
897                 objp->pos.x = asfieldp->max_bound.x + (objp->pos.x - asfieldp->min_bound.x);
898         }
899
900         if (objp->pos.y < asfieldp->min_bound.y) {
901                 objp->pos.y = asfieldp->max_bound.y + (objp->pos.y - asfieldp->min_bound.y);
902         }
903         
904         if (objp->pos.z < asfieldp->min_bound.z) {
905                 objp->pos.z = asfieldp->max_bound.z + (objp->pos.z - asfieldp->min_bound.z);
906         }
907
908         if (objp->pos.x > asfieldp->max_bound.x) {
909                 objp->pos.x = asfieldp->min_bound.x + (objp->pos.x - asfieldp->max_bound.x);
910         }
911
912         if (objp->pos.y > asfieldp->max_bound.y) {
913                 objp->pos.y = asfieldp->min_bound.y + (objp->pos.y - asfieldp->max_bound.y);
914         }
915
916         if (objp->pos.z > asfieldp->max_bound.z) {
917                 objp->pos.z = asfieldp->min_bound.z + (objp->pos.z - asfieldp->max_bound.z);
918         }
919
920         // wrap on inner bound, check all 3 axes as needed, use of rand ok for multiplayer with send_asteroid_throw()
921         inner_bound_pos_fixup(asfieldp, &objp->pos);
922
923 }
924
925
926 // return !0 if this asteroid is a target for any ship, otherwise return 0
927 int asteroid_is_targeted(object *objp)
928 {
929         ship_obj        *so;
930         object  *ship_objp;
931         int             asteroid_obj_index;
932
933         asteroid_obj_index=OBJ_INDEX(objp);
934
935         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
936                 ship_objp = &Objects[so->objnum];
937                 if ( Ai_info[Ships[ship_objp->instance].ai_index].target_objnum == asteroid_obj_index ) {
938                         return 1;
939                 }
940         }
941         
942         return 0;
943 }
944
945 //      Create an asteroid that will hit object *objp in delta_time seconds
946 void asteroid_aim_at_target(object *objp, object *asteroid_objp, float delta_time)
947 {
948         vector  predicted_center_pos;
949         vector  rand_vec;
950         float           speed;
951
952         vm_vec_scale_add(&predicted_center_pos, &objp->pos, &objp->phys_info.vel, delta_time);
953         vm_vec_rand_vec_quick(&rand_vec);
954         vm_vec_scale_add2(&predicted_center_pos, &rand_vec, objp->radius/2.0f);
955
956         vm_vec_add2(&rand_vec, &objp->orient.fvec);
957         if (vm_vec_mag_quick(&rand_vec) < 0.1f)
958                 vm_vec_add2(&rand_vec, &objp->orient.rvec);
959         vm_vec_normalize(&rand_vec);
960
961         speed = Asteroid_info[0].max_speed * (frand()/2.0f + 0.5f);
962         
963         vm_vec_copy_scale(&asteroid_objp->phys_info.vel, &rand_vec, -speed);
964         asteroid_objp->phys_info.desired_vel = asteroid_objp->phys_info.vel;
965         vm_vec_scale_add(&asteroid_objp->pos, &predicted_center_pos, &asteroid_objp->phys_info.vel, -delta_time);
966         vm_vec_scale_add(&asteroid_objp->last_pos, &asteroid_objp->pos, &asteroid_objp->phys_info.vel, -flFrametime);
967 }
968
969 int     Max_incoming_asteroids[NUM_SKILL_LEVELS] = {3, 4, 5, 7, 10};
970
971 //      Call once per frame to maybe throw an asteroid at a ship.
972 //      "count" asteroids already targeted on 
973 void maybe_throw_asteroid(int count)
974 {
975         if (!timestamp_elapsed(Next_asteroid_throw)) {
976                 return;
977         }
978
979         if (Asteroid_throw_objnum == -1) {
980                 return;
981         }
982
983         nprintf(("AI", "Incoming asteroids: %i\n", count));
984
985         if (count > Max_incoming_asteroids[Game_skill_level])
986                 return;
987
988         Next_asteroid_throw = timestamp(1000 + 1200 * count/(Game_skill_level+1));
989
990         ship_obj        *so;
991         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
992                 object *A = &Objects[so->objnum];
993                 if (so->objnum == Asteroid_throw_objnum) {
994                         int subtype = rand() % 3;
995                         while (Asteroid_field.field_debris_type[subtype] == -1) {
996                                 subtype = (subtype + 1) % 3;
997                         }
998                         object *objp = asteroid_create(&Asteroid_field, ASTEROID_TYPE_BIG, subtype);
999                         if (objp != NULL) {
1000                                 asteroid_aim_at_target(A, objp, ASTEROID_MIN_COLLIDE_TIME + frand() * 20.0f);
1001
1002                                 // if asteroid is inside inner bound, kill it
1003                                 if (asteroid_in_inner_bound(&Asteroid_field, &objp->pos, 0.0f)) {
1004                                         objp->flags |= OF_SHOULD_BE_DEAD;
1005                                 } else {
1006                                         Asteroids[objp->instance].target_objnum = so->objnum;
1007                                         // DA: 4/22/98  We get next line for free when new object (in obj_create_list) is merged.
1008                                         // this line gives too many collision pairs.
1009                                         // asteroid_update_collide(objp);
1010
1011                                         if ( MULTIPLAYER_MASTER ) {
1012                                                 send_asteroid_throw( objp );
1013                                         }
1014                                 }
1015                         }
1016
1017                         return;
1018                 }
1019         }
1020
1021 }
1022
1023 void asteroid_delete( object * obj )
1024 {
1025         int             num;
1026         asteroid        *asp;
1027
1028         num = obj->instance;
1029         Assert( Asteroids[num].objnum == OBJ_INDEX(obj));
1030
1031         asp = &Asteroids[num];
1032
1033         Assert( Num_asteroids >= 0 );
1034
1035         asp->flags = 0;
1036         Num_asteroids--;
1037
1038         // Delete asteroid from Asteroid_used_list
1039         asteroid_obj_list_remove( obj );
1040 }
1041
1042 // See if we should reposition the asteroid.  Only reposition if oustide the bounding volume and
1043 // the player isn't looking towards the asteroid.
1044 void asteroid_maybe_reposition(object *objp, asteroid_field *asfieldp)
1045 {
1046         // passive field does not wrap
1047         if (asfieldp->field_type == FT_PASSIVE) {
1048                 return;
1049         }
1050
1051         if ( asteroid_should_wrap(objp, asfieldp) ) {
1052                 vector  vec_to_asteroid, old_asteroid_pos, old_vel;
1053                 float           dot, dist;
1054
1055                 old_asteroid_pos = objp->pos;
1056                 old_vel = objp->phys_info.vel;
1057
1058                 //nprintf(("AI", "Frame %i, reposition #%i\n", Framecount, objp-Objects));
1059
1060                 // don't wrap asteroid if it is a target of some ship
1061                 if ( !asteroid_is_targeted(objp) ) {
1062
1063                         // only wrap if player won't see asteroid disappear/reverse direction
1064                         dist = vm_vec_normalized_dir(&vec_to_asteroid, &objp->pos, &Eye_position);
1065                         dot = vm_vec_dot(&Eye_matrix.fvec, &vec_to_asteroid);
1066                         
1067                         if ((dot < 0.7f) || (dist > 3000.0f)) {
1068                                 if (Num_asteroids > MAX_ASTEROIDS-10) {
1069                                         objp->flags |= OF_SHOULD_BE_DEAD;
1070                                 } else {
1071                                         // check to ensure player won't see asteroid appear either
1072                                         asteroid_wrap_pos(objp, asfieldp);
1073                                         Asteroids[objp->instance].target_objnum = -1;
1074
1075                                         vm_vec_normalized_dir(&vec_to_asteroid, &objp->pos, &Eye_position);
1076                                         dot = vm_vec_dot(&Eye_matrix.fvec, &vec_to_asteroid);
1077                                         dist = vm_vec_dist_quick(&objp->pos, &Eye_position);
1078                                         
1079                                         if (( dot > 0.7f) && (dist < 3000.0f)) {
1080                                                 // player would see asteroid pop out other side, so reverse velocity instead of wrapping
1081                                                 objp->pos = old_asteroid_pos;           
1082                                                 vm_vec_copy_scale(&objp->phys_info.vel, &old_vel, -1.0f);
1083                                                 objp->phys_info.desired_vel = objp->phys_info.vel;
1084                                                 Asteroids[objp->instance].target_objnum = -1;
1085                                         }
1086
1087                                         // update last pos (after vel is known)
1088                                         vm_vec_scale_add(&objp->last_pos, &objp->pos, &objp->phys_info.vel, -flFrametime);
1089
1090                                         asteroid_update_collide(objp);
1091
1092                                         if ( MULTIPLAYER_MASTER )
1093                                                 send_asteroid_throw( objp );
1094                                 }
1095                         }
1096                 }
1097         }
1098 }
1099
1100 void lerp(float *goal, float f1, float f2, float scale)
1101 {
1102         *goal = (f2 - f1) * scale + f1;
1103 }
1104
1105 void asteroid_process_pre( object *objp, float frame_time)
1106 {
1107         if (Asteroids_enabled) {
1108                 vector  *v, *vv;
1109
1110                 v = &objp->phys_info.vel;
1111                 vv = &objp->phys_info.desired_vel;
1112
1113                 //nprintf(("AI", "Frm %i: Obj #%2i: Hull: %5.1f Vel: %5.1f %5.1f %5.1f Des: %5.1f %5.1f %5.1f\n", Framecount, objp-Objects, objp->hull_strength, v->x, v->y, v->z, vv->x, vv->y, vv->z));
1114
1115                 //      Make vel chase desired_vel
1116                 lerp(&objp->phys_info.vel.x, objp->phys_info.vel.x, objp->phys_info.desired_vel.x, flFrametime);
1117                 lerp(&objp->phys_info.vel.y, objp->phys_info.vel.y, objp->phys_info.desired_vel.y, flFrametime);
1118                 lerp(&objp->phys_info.vel.z, objp->phys_info.vel.z, objp->phys_info.desired_vel.z, flFrametime);
1119         }
1120 }
1121
1122 #ifndef PLAT_UNIX
1123 #pragma warning ( push )
1124 #pragma warning ( disable : 4701 )
1125 #endif
1126 int asteroid_check_collision(object *pasteroid, object *other_obj, vector *hitpos, collision_info_struct *asteroid_hit_info)
1127 {
1128         if (!Asteroids_enabled) {
1129                 return 0;
1130         }
1131
1132         mc_info mc;
1133         int             num, asteroid_subtype;
1134
1135         Assert( pasteroid->type == OBJ_ASTEROID );
1136
1137         num = pasteroid->instance;
1138         Assert( num >= 0 );
1139
1140         Assert( Asteroids[num].objnum == OBJ_INDEX(pasteroid));
1141         asteroid_subtype = Asteroids[num].asteroid_subtype;
1142
1143         // asteroid_hit_info NULL  --  asteroid-weapon collision
1144         if ( asteroid_hit_info == NULL ) {
1145                 // asteroid weapon collision
1146                 Assert( other_obj->type == OBJ_WEAPON );
1147                 mc.model_num = Asteroid_info[Asteroids[num].type].model_num[asteroid_subtype];  // Fill in the model to check
1148                 model_clear_instance( mc.model_num );
1149                 mc.orient = &pasteroid->orient;                                 // The object's orient
1150                 mc.pos = &pasteroid->pos;                                                       // The object's position
1151                 mc.p0 = &other_obj->last_pos;                           // Point 1 of ray to check
1152                 mc.p1 = &other_obj->pos;                                        // Point 2 of ray to check
1153                 mc.flags = (MC_CHECK_MODEL);
1154
1155                 if (model_collide(&mc))
1156                         *hitpos = mc.hit_point_world;
1157
1158                 return mc.num_hits;
1159         }
1160
1161         // asteroid ship collision -- use asteroid_hit_info to calculate physics
1162         object *ship_obj = other_obj;
1163         Assert( ship_obj->type == OBJ_SHIP );
1164
1165         object* heavy = asteroid_hit_info->heavy;
1166         object* light = asteroid_hit_info->light;
1167         object *heavy_obj = heavy;
1168         object *light_obj = light;
1169
1170         vector zero, p0, p1;
1171         vm_vec_zero( &zero );
1172         vm_vec_sub( &p0, &light->last_pos, &heavy->last_pos );
1173         vm_vec_sub( &p1, &light->pos, &heavy->pos );
1174
1175         mc.pos = &zero;                                                         // The object's position
1176         mc.p0 = &p0;                                                                    // Point 1 of ray to check
1177         mc.p1 = &p1;                                                                    // Point 2 of ray to check
1178
1179         // find the light object's position in the heavy object's reference frame at last frame and also in this frame.
1180         vector p0_temp, p0_rotated;
1181                 
1182         // Collision detection from rotation enabled if at rotaion is less than 30 degree in frame
1183         // This should account for all ships
1184         if ( (vm_vec_mag_squared( &heavy->phys_info.rotvel ) * flFrametime*flFrametime) < (PI*PI/36) ) {
1185                 // collide_rotate calculate (1) start position and (2) relative velocity
1186                 asteroid_hit_info->collide_rotate = 1;
1187                 vm_vec_rotate( &p0_temp, &p0, &heavy->last_orient );
1188                 vm_vec_unrotate( &p0_rotated, &p0_temp, &heavy->orient );
1189                 mc.p0 = &p0_rotated;                            // Point 1 of ray to check
1190                 vm_vec_sub( &asteroid_hit_info->light_rel_vel, &p1, &p0_rotated );
1191                 vm_vec_scale( &asteroid_hit_info->light_rel_vel, 1/flFrametime );
1192                 // HACK - this applies to big ships warping in/out of asteroid fields - not sure what it does
1193                 if (vm_vec_mag(&asteroid_hit_info->light_rel_vel) > 300) {
1194                         // nprintf(("Physics", "Asteroid type %d\n", Asteroids[asteroid_hit_info->light->instance].type));
1195                         asteroid_hit_info->collide_rotate = 0;
1196                         vm_vec_sub( &asteroid_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel );
1197                 }
1198         } else {
1199                 asteroid_hit_info->collide_rotate = 0;
1200                 vm_vec_sub( &asteroid_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel );
1201         }
1202
1203         int mc_ret_val = 0;
1204
1205         if ( asteroid_hit_info->heavy == ship_obj ) {   // ship is heavier, so asteroid is sphere. Check sphere collision against ship poly model
1206                 mc.model_num = Ships[ship_obj->instance].modelnum;              // Fill in the model to check
1207                 mc.orient = &ship_obj->orient;                                                          // The object's orient
1208                 mc.radius = pasteroid->radius;
1209                 mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
1210
1211                 // copy important data
1212                 int copy_flags = mc.flags;  // make a copy of start end positions of sphere in  big ship RF
1213                 vector copy_p0, copy_p1;
1214                 copy_p0 = *mc.p0;
1215                 copy_p1 = *mc.p1;
1216
1217                 // first test against the sphere - if this fails then don't do any submodel tests
1218                 mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE;
1219
1220                 int submodel_list[MAX_ROTATING_SUBMODELS];
1221                 int num_rotating_submodels = 0;
1222                 polymodel *pm;
1223
1224                 ship_model_start(ship_obj);
1225
1226                 if (model_collide(&mc)) {
1227
1228                         // Set earliest hit time
1229                         asteroid_hit_info->hit_time = FLT_MAX;
1230
1231                         // Do collision the cool new way
1232                         if ( asteroid_hit_info->collide_rotate ) {
1233                                 // We collide with the sphere, find the list of rotating submodels and test one at a time
1234                                 model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj);
1235
1236                                 // Get polymodel and turn off all rotating submodels, collide against only 1 at a time.
1237                                 pm = model_get(Ships[heavy_obj->instance].modelnum);
1238
1239                                 // turn off all rotating submodels and test for collision
1240                                 for (int i=0; i<num_rotating_submodels; i++) {
1241                                         pm->submodel[submodel_list[i]].blown_off = 1;
1242                                 }
1243
1244                                 // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
1245                                 mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
1246
1247
1248                                 // check each submodel in turn
1249                                 for (int i=0; i<num_rotating_submodels; i++) {
1250                                         // turn on submodel for collision test
1251                                         pm->submodel[submodel_list[i]].blown_off = 0;
1252
1253                                         // set angles for last frame (need to set to prev to get p0)
1254                                         angles copy_angles = pm->submodel[submodel_list[i]].angs;
1255
1256                                         // find the start and end positions of the sphere in submodel RF
1257                                         pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs;
1258                                         world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos);
1259
1260                                         pm->submodel[submodel_list[i]].angs = copy_angles;
1261                                         world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos);
1262
1263                                         mc.p0 = &p0;
1264                                         mc.p1 = &p1;
1265                                         // mc.pos = zero        // in submodel RF
1266
1267                                         mc.orient = &vmd_identity_matrix;
1268                                         mc.submodel_num = submodel_list[i];
1269
1270                                         if ( model_collide(&mc) ) {
1271                                                 if ( mc.hit_dist < asteroid_hit_info->hit_time ) {
1272                                                         mc_ret_val = 1;
1273
1274                                                         // set up asteroid_hit_info common
1275                                                         set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_ROT_HIT);
1276
1277                                                         // set up asteroid_hit_info for rotating submodel
1278                                                         if (asteroid_hit_info->edge_hit == 0) {
1279                                                                 model_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1280                                                         }
1281
1282                                                         // find position in submodel RF of light object at collison
1283                                                         vector int_light_pos, diff;
1284                                                         vm_vec_sub(&diff, mc.p1, mc.p0);
1285                                                         vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist);
1286                                                         model_find_world_point(&asteroid_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
1287                                                 }
1288                                         }
1289                                         // Don't look at this submodel again
1290                                         pm->submodel[submodel_list[i]].blown_off = 1;
1291                                 }
1292
1293                         }
1294
1295                         // Recover and do usual ship_ship collision, but without rotating submodels
1296                         mc.flags = copy_flags;
1297                         *mc.p0 = copy_p0;
1298                         *mc.p1 = copy_p1;
1299                         mc.orient = &heavy_obj->orient;
1300
1301                         // usual ship_ship collision test
1302                         if ( model_collide(&mc) )       {
1303                                 // check if this is the earliest hit
1304                                 if (mc.hit_dist < asteroid_hit_info->hit_time) {
1305                                         mc_ret_val = 1;
1306
1307                                         set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1308
1309                                         // get collision normal if not edge hit
1310                                         if (asteroid_hit_info->edge_hit == 0) {
1311                                                 model_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1312                                         }
1313
1314                                         // find position in submodel RF of light object at collison
1315                                         vector diff;
1316                                         vm_vec_sub(&diff, mc.p1, mc.p0);
1317                                         vm_vec_scale_add(&asteroid_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1318
1319                                 }
1320                         }
1321
1322                         ship_model_stop( ship_obj );
1323                 }
1324
1325         } else {
1326                 // Asteroid is heavier obj
1327                 mc.model_num = Asteroid_info[Asteroids[num].type].model_num[asteroid_subtype];          // Fill in the model to check
1328                 model_clear_instance( mc.model_num );
1329                 mc.orient = &pasteroid->orient;                         // The object's orient
1330                 mc.radius = model_get_core_radius( Ships[ship_obj->instance].modelnum );
1331
1332                 // check for collision between asteroid model and ship sphere
1333                 mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
1334
1335                 mc_ret_val = model_collide(&mc);
1336
1337                 if (mc_ret_val) {
1338                         set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1339
1340                         // set normal if not edge hit
1341                         if ( !asteroid_hit_info->edge_hit ) {
1342                                 vm_vec_unrotate(&asteroid_hit_info->collision_normal, &mc.hit_normal, &heavy->orient);
1343                         }
1344
1345                         // find position in submodel RF of light object at collison
1346                         vector diff;
1347                         vm_vec_sub(&diff, mc.p1, mc.p0);
1348                         vm_vec_scale_add(&asteroid_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1349
1350                 }
1351         }
1352         
1353
1354         if ( mc_ret_val )       {
1355
1356                 // SET PHYSICS PARAMETERS
1357                 // already have (hitpos - heavy) and light_cm_pos
1358                 // get heavy cm pos - already have light_cm_pos
1359                 asteroid_hit_info->heavy_collision_cm_pos = zero;
1360
1361                 // get r_heavy and r_light
1362                 asteroid_hit_info->r_heavy = asteroid_hit_info->hit_pos;
1363                 vm_vec_sub(&asteroid_hit_info->r_light, &asteroid_hit_info->hit_pos, &asteroid_hit_info->light_collision_cm_pos);
1364
1365                 // set normal for edge hit
1366                 if ( asteroid_hit_info->edge_hit ) {
1367                         vm_vec_copy_normalize(&asteroid_hit_info->collision_normal, &asteroid_hit_info->r_light);
1368                         vm_vec_negate(&asteroid_hit_info->collision_normal);
1369                 }
1370
1371                 // get world hitpos
1372                 vm_vec_add(hitpos, &asteroid_hit_info->heavy->pos, &asteroid_hit_info->r_heavy);
1373
1374                 return 1;
1375         } else {
1376                 // no hit
1377                 return 0;
1378         }
1379 }
1380 #ifndef PLAT_UNIX
1381 #pragma warning ( pop )
1382 #endif
1383
1384
1385 void asteroid_render(object * obj)
1386 {
1387         if (Asteroids_enabled) {
1388                 int                     num;
1389                 polymodel       *pm;
1390                 asteroid                *asp;
1391
1392                 pm = NULL;      
1393                 num = obj->instance;
1394
1395                 Assert((num >= 0) && (num < MAX_ASTEROIDS));
1396                 asp = &Asteroids[num];
1397
1398                 Assert( asp->flags & AF_USED );
1399
1400                 model_clear_instance( Asteroid_info[asp->type].model_num[asp->asteroid_subtype]);
1401                 model_render(Asteroid_info[asp->type].model_num[asp->asteroid_subtype], &obj->orient, &obj->pos, MR_NORMAL|MR_IS_ASTEROID, OBJ_INDEX(obj) );    //      Replace MR_NORMAL with 0x07 for big yellow blobs
1402         }
1403 }
1404
1405 //      Create a normalized vector generally in the direction from *hitpos to other_obj->pos
1406 void asc_get_relvec(vector *relvec, object *other_obj, vector *hitpos)
1407 {
1408         vector  tvec, rand_vec;
1409         int             count = 0;
1410
1411         vm_vec_normalized_dir(&tvec, &other_obj->pos, hitpos);
1412
1413         //      Try up to three times to get a good vector.
1414         while (count++ < 3) {
1415                 vm_vec_rand_vec_quick(&rand_vec);
1416                 vm_vec_add(relvec, &tvec, &rand_vec);
1417                 float mag = vm_vec_mag_quick(relvec);
1418                 if ((mag > 0.2f) && (mag < 1.7f))
1419                         break;
1420         }
1421
1422         vm_vec_normalize_quick(relvec);
1423 }
1424
1425 // return multiplier on asteroid radius for fireball
1426 float asteroid_get_fireball_scale_multiplier(int num)
1427 {
1428         if (Asteroids[num].flags & AF_USED) {
1429
1430                 switch(Asteroids[num].type) {
1431                 case ASTEROID_TYPE_BIG:
1432                         return 1.5f;
1433                         break;
1434
1435                 default:
1436                         return 1.0f;
1437                         break;
1438                 }
1439         }
1440
1441         Int3(); // this should not happen.  asteroid should be used.
1442         return 1.0f;
1443 }
1444
1445
1446 // create asteroid explosion
1447 // exit: expected time for explosion anim to last, in seconds
1448 float asteroid_create_explosion(object *objp)
1449 {
1450         int     fireball_objnum;
1451         float   explosion_life, fireball_scale_multiplier;
1452
1453         fireball_scale_multiplier = asteroid_get_fireball_scale_multiplier(objp->instance);
1454
1455         fireball_objnum = fireball_create( &objp->pos, FIREBALL_ASTEROID, OBJ_INDEX(objp), objp->radius*fireball_scale_multiplier, 0, &objp->phys_info.vel );
1456         if ( fireball_objnum > -1 )     {
1457                 explosion_life = fireball_lifeleft(&Objects[fireball_objnum]);
1458         } else {
1459                 explosion_life = 0.0f;
1460         }
1461
1462         return explosion_life;
1463 }
1464
1465 // play sound when asteroid explodes
1466 void asteriod_explode_sound(object *objp, int type, int play_loud)
1467 {
1468         int     sound_index = -1;
1469         float range_factor = 1.0f;              // how many times sound should traver farther than normal
1470
1471         switch (type) {
1472         case ASTEROID_TYPE_SMALL:
1473         case ASTEROID_TYPE_MEDIUM:
1474         case DEBRIS_TERRAN_SMALL:
1475         case DEBRIS_TERRAN_MEDIUM:
1476         case DEBRIS_VASUDAN_SMALL:
1477         case DEBRIS_VASUDAN_MEDIUM:
1478         case DEBRIS_SHIVAN_SMALL:
1479         case DEBRIS_SHIVAN_MEDIUM:
1480                 sound_index = SND_ASTEROID_EXPLODE_SMALL;
1481                 range_factor = 5.0f;
1482                 break;
1483
1484         case ASTEROID_TYPE_BIG:
1485         case DEBRIS_TERRAN_LARGE:
1486         case DEBRIS_VASUDAN_LARGE:
1487         case DEBRIS_SHIVAN_LARGE:
1488                 sound_index = SND_ASTEROID_EXPLODE_BIG;
1489                 range_factor = 10.0f;
1490                 break;
1491
1492         default:
1493                 Int3();
1494                 return;
1495         }
1496
1497         Assert(sound_index != -1);
1498
1499         if ( !play_loud ) {
1500                 range_factor = 1.0f;
1501         }
1502
1503         snd_play_3d( &Snds[sound_index], &objp->pos, &Eye_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY, NULL, range_factor );
1504 }
1505
1506 //      asteroid_do_area_effect()
1507 //
1508 // Do the area effect for an asteroid exploding
1509 //
1510 // input:       asteroid_objp   =>              object pointer to asteriod causing explosion
1511 void asteroid_do_area_effect(object *asteroid_objp)
1512 {
1513         object                  *ship_objp;
1514         float                           damage, blast;
1515         ship_obj                        *so;
1516         asteroid                        *asp;
1517         asteroid_info   *asip;
1518
1519         asp = &Asteroids[asteroid_objp->instance];
1520         asip = &Asteroid_info[asp->type];
1521
1522         if ( asip->damage <= 0 ) {              // do a quick out if there is no damage to apply
1523                 return;
1524         }
1525
1526         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1527                 ship_objp = &Objects[so->objnum];
1528         
1529                 // don't blast navbuoys
1530                 if ( ship_get_SIF(ship_objp->instance) & SIF_NAVBUOY ) {
1531                         continue;
1532                 }
1533
1534                 if ( weapon_area_calc_damage(ship_objp, &asteroid_objp->pos, asip->inner_rad, asip->outer_rad, asip->blast, asip->damage, &blast, &damage, asip->outer_rad) == -1 )
1535                         continue;
1536
1537                 ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage );
1538                 weapon_area_apply_blast(NULL, ship_objp, &asteroid_objp->pos, blast, 0);
1539         }       // end for
1540 }
1541
1542 //      Asteroid asteroid_obj was hit.
1543 //      Apply damage.  Maybe make it break into smaller asteroids.
1544 // input:       asteroid_obj    =>              pointer to asteroid object getting hit
1545 //                              other_obj               =>              object that hit asteroid, can be NULL if asteroid hit by area effect
1546 //                              hitpos                  =>              world position asteroid was hit, can be NULL if hit by area effect
1547 //                              damage                  =>              amount of damage to apply to asteroid
1548 void asteroid_hit( object * asteroid_obj, object * other_obj, vector * hitpos, float damage )
1549 {
1550         float           explosion_life;
1551         asteroid        *asp;
1552
1553         asp = &Asteroids[asteroid_obj->instance];
1554
1555         if (asteroid_obj->flags & OF_SHOULD_BE_DEAD){
1556                 return;
1557         }
1558
1559         if ( MULTIPLAYER_MASTER ){
1560                 send_asteroid_hit( asteroid_obj, other_obj, hitpos, damage );
1561         }
1562
1563         asteroid_obj->hull_strength -= damage;
1564
1565         //nprintf(("AI", "Asteroid collided with %s, hull = %.2f\n", Object_type_names[other_obj->type], asteroid_obj->hull_strength));
1566
1567         if (asteroid_obj->hull_strength < 0.0f) {
1568                 if ( asp->final_death_time <= 0 ) {
1569                         int play_loud_collision = 0;
1570
1571                         explosion_life = asteroid_create_explosion(asteroid_obj);
1572                         if ( asp->collide_objnum == OBJ_INDEX(other_obj) ) {
1573 //                              play_loud_collision = 1;
1574                         }
1575                         asteriod_explode_sound(asteroid_obj, asp->type, play_loud_collision);
1576                         asteroid_do_area_effect(asteroid_obj);
1577
1578                         asp->final_death_time = timestamp( fl2i(explosion_life*1000.0f)/5 );    // Wait till 30% of vclip time before breaking the asteroid up.
1579                         if ( hitpos ) {
1580                                 asp->death_hit_pos = *hitpos;
1581                         } else {
1582                                 asp->death_hit_pos = asteroid_obj->pos;
1583                                 // randomize hit pos a bit, otherwise we will get a NULL vector when trying to find direction to toss child asteroids
1584                                 vector rand_vec;
1585                                 vm_vec_rand_vec_quick(&rand_vec);
1586                                 vm_vec_add2(&asp->death_hit_pos, &rand_vec);
1587                         }
1588                 }
1589         } else if ( other_obj ) {
1590                 if ( other_obj->type == OBJ_WEAPON ) {
1591                         weapon_info *wip;
1592                         wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
1593                         // If the weapon didn't play any impact animation, play custom asteroid impact animation
1594                         if ( wip->impact_weapon_expl_index < 0 ) {
1595                                 particle_create( hitpos, &vmd_zero_vector, 0.0f, Asteroid_impact_explosion_radius, PARTICLE_BITMAP, Asteroid_impact_explosion_ani );
1596                         }
1597                 }
1598         }
1599
1600         // evaluate any relevant player scoring implications
1601         scoring_eval_hit(asteroid_obj,other_obj);
1602 }
1603
1604 // De-init asteroids, called from game_level_close()
1605 void asteroid_level_close()
1606 {
1607         int     i;
1608
1609         for (i=0; i<MAX_ASTEROIDS; i++) {
1610                 if (Asteroids[i].flags & AF_USED) {
1611                         Asteroids[i].flags &= ~AF_USED;
1612                         Assert(Asteroids[i].objnum >=0 && Asteroids[i].objnum < MAX_OBJECTS);
1613                         Objects[Asteroids[i].objnum].flags |= OF_SHOULD_BE_DEAD;
1614                 }
1615         }
1616
1617         Asteroid_field.num_initial_asteroids=0;
1618 }
1619
1620 DCF(asteroids,"Turns asteroids on/off")
1621 {       
1622         if ( Dc_command )       {       
1623                 dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);                
1624                 if ( Dc_arg_type & ARG_TRUE )   
1625                         Asteroids_enabled = 1;  
1626                 else if ( Dc_arg_type & ARG_FALSE ) 
1627                         Asteroids_enabled = 0;  
1628                 else if ( Dc_arg_type & ARG_NONE ) 
1629                         Asteroids_enabled ^= 1; 
1630         }       
1631         if ( Dc_help )  
1632                 dc_printf( "Usage: asteroids [bool]\nTurns asteroid system on/off.  If nothing passed, then toggles it.\n" );   
1633         
1634         if ( Dc_status )        
1635                 dc_printf( "asteroids are %s\n", (Asteroids_enabled?"ON":"OFF") );      
1636
1637 /*
1638         if ((old_asteroids_enabled == 0) && (Asteroids_enabled == 1)) {
1639                 asteroid_init();
1640         } else if ((old_asteroids_enabled == 1) && (Asteroids_enabled == 0)) {
1641                 asteroid_uninit();
1642         }
1643 */
1644 }
1645
1646 void hud_target_asteroid()
1647 {
1648         int     i;
1649         int     start_index = 0, end_index = MAX_ASTEROIDS;
1650
1651         if (Player_ai->target_objnum != -1) {
1652                 if (Objects[Player_ai->target_objnum].type == OBJ_ASTEROID) {
1653                         start_index = Objects[Player_ai->target_objnum].instance+1;
1654                         end_index = start_index-1;
1655                         if (end_index < 0)
1656                                 end_index = MAX_ASTEROIDS;
1657                 }
1658         }
1659
1660         i = start_index;
1661         while (i != end_index) {
1662                 if (i == MAX_ASTEROIDS)
1663                         i = 0;
1664
1665                 if (Asteroids[i].flags & AF_USED) {
1666                         Assert(Objects[Asteroids[i].objnum].type == OBJ_ASTEROID);
1667                         set_target_objnum( Player_ai, Asteroids[i].objnum);
1668                         break;
1669                 }
1670
1671                 i++;
1672         }
1673 }
1674
1675 // Return the number of active asteroids
1676 int asteroid_count()
1677 {
1678         return Num_asteroids;
1679 }
1680
1681 // See if asteroid should split up.  We delay splitting up to allow the explosion animation
1682 // to play for a bit.
1683 void asteroid_maybe_break_up(object *asteroid_obj)
1684 {
1685         asteroid *asp;
1686
1687         asp = &Asteroids[asteroid_obj->instance];
1688
1689         if ( timestamp_elapsed(asp->final_death_time) ) {
1690                 vector  relvec, vfh, tvec;
1691
1692                 asteroid_obj->flags |= OF_SHOULD_BE_DEAD;
1693
1694                 // multiplayer clients won't go through the following code.  asteroid_sub_create will send
1695                 // a create packet to the client in the above named function
1696                 if ( !MULTIPLAYER_CLIENT ) {
1697
1698                         switch (asp->type) {
1699                         case ASTEROID_TYPE_SMALL:
1700                                 break;
1701                         case ASTEROID_TYPE_MEDIUM:
1702                                 asc_get_relvec(&relvec, asteroid_obj, &asp->death_hit_pos);
1703                                 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &relvec);
1704                         
1705                                 vm_vec_normalized_dir(&vfh, &asteroid_obj->pos, &asp->death_hit_pos);
1706                                 vm_vec_copy_scale(&tvec, &vfh, 2.0f);
1707                                 vm_vec_sub2(&tvec, &relvec);
1708                                 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &tvec);
1709                                 
1710                                 break;
1711                         case ASTEROID_TYPE_BIG:
1712                                 asc_get_relvec(&relvec, asteroid_obj, &asp->death_hit_pos);
1713                                 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_MEDIUM, &relvec);
1714                         
1715                                 vm_vec_normalized_dir(&vfh, &asteroid_obj->pos, &asp->death_hit_pos);
1716                                 vm_vec_copy_scale(&tvec, &vfh, 2.0f);
1717                                 vm_vec_sub2(&tvec, &relvec);
1718                                 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_MEDIUM, &tvec);
1719
1720                                 while (frand() > 0.6f) {
1721                                         vector  rvec, tvec2;
1722                                         vm_vec_rand_vec_quick(&rvec);
1723                                         vm_vec_scale_add(&tvec2, &vfh, &rvec, 0.75f);
1724                                         asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &tvec2);
1725                                 }
1726
1727                                 break;
1728
1729                         // ship debris does not break up
1730                         case    DEBRIS_TERRAN_SMALL:
1731                         case    DEBRIS_TERRAN_MEDIUM:
1732                         case    DEBRIS_TERRAN_LARGE:
1733                         case    DEBRIS_VASUDAN_SMALL:
1734                         case    DEBRIS_VASUDAN_MEDIUM:
1735                         case    DEBRIS_VASUDAN_LARGE:
1736                         case    DEBRIS_SHIVAN_SMALL:
1737                         case    DEBRIS_SHIVAN_MEDIUM:
1738                         case    DEBRIS_SHIVAN_LARGE:
1739                                 break;
1740
1741                         default:
1742                                 Int3();
1743                         }
1744                 }
1745
1746                 asp->final_death_time = timestamp(-1);
1747         }
1748 }
1749
1750 int asteroid_get_random_in_cone(vector *pos, vector *dir, float ang, int danger)
1751 {
1752         int idx;
1753         vector asteroid_dir;
1754
1755         // just pick the first asteroid which satisfies our condition
1756         for(idx=0; idx<Num_asteroids; idx++){
1757                 vm_vec_sub(&asteroid_dir, &Objects[Asteroids[idx].objnum].pos, pos);
1758                 vm_vec_normalize_quick(&asteroid_dir);
1759
1760                 // if it satisfies the condition
1761                 if(vm_vec_dot(dir, &asteroid_dir) >= ang){
1762                         return idx;
1763                 }
1764         }
1765
1766         return -1;
1767 }
1768
1769 void asteroid_test_collide(object *asteroid_obj, object *ship_obj, mc_info *mc)
1770 {
1771         float           asteroid_ray_dist;
1772         vector  asteroid_fvec, terminus;
1773
1774         // See if ray from asteroid intersects bounding box of escort ship
1775         asteroid_ray_dist = vm_vec_mag_quick(&asteroid_obj->phys_info.desired_vel) * ASTEROID_MIN_COLLIDE_TIME;
1776         asteroid_fvec = asteroid_obj->phys_info.desired_vel;    
1777
1778         if(IS_VEC_NULL(&asteroid_fvec)){
1779                 terminus = asteroid_obj->pos;
1780         } else {
1781                 vm_vec_normalize(&asteroid_fvec);
1782                 vm_vec_scale_add(&terminus, &asteroid_obj->pos, &asteroid_fvec, asteroid_ray_dist);
1783         }
1784
1785         Assert(ship_obj->type == OBJ_SHIP);
1786
1787         ship_model_start(ship_obj);
1788
1789         mc->model_num = Ships[ship_obj->instance].modelnum;                     // Fill in the model to check
1790         mc->orient = &ship_obj->orient;                                                                         // The object's orientation
1791         mc->pos = &ship_obj->pos;                                                                                               // The object's position
1792         mc->p0 = &asteroid_obj->pos;                                                                                    // Point 1 of ray to check
1793         mc->p1 = &terminus;                                                                                                             // Point 2 of ray to check
1794 //      mc->flags = MC_CHECK_MODEL | MC_ONLY_BOUND_BOX; 
1795         mc->flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;       
1796         mc->radius = asteroid_obj->radius;
1797
1798         model_collide(mc);
1799
1800         ship_model_stop(ship_obj);
1801 }
1802
1803 // Return !0 is the asteroid will collide with the escort ship within ASTEROID_MIN_COLLIDE_TIME
1804 // seconds
1805 int asteroid_will_collide(object *asteroid_obj, object *escort_objp)
1806 {
1807         mc_info mc;
1808
1809         asteroid_test_collide(asteroid_obj, escort_objp, &mc);
1810
1811         if ( !mc.num_hits ) {
1812                 return 0;
1813         }       
1814
1815         return 1;
1816 }
1817
1818 // return !0 if we should warn about asteroid hitting ship, otherwise return 0
1819 int asteroid_valid_ship_to_warn_collide(ship *shipp)
1820 {
1821         if ( !(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
1822                 return 0;
1823         }
1824
1825         if ( shipp->flags & (SF_DYING|SF_DEPART_WARP) ) {
1826                 return 0;
1827         }
1828
1829         if ( (shipp->team != Player_ship->team) || (Player_ship->team == TEAM_TRAITOR) ) {
1830                 return 0;
1831         }
1832
1833         return 1;
1834 }
1835
1836 // See if asteroid will collide with a large ship on the escort list in the next
1837 // ASTEROID_MIN_COLLIDE_TIME seconds.
1838 void asteroid_update_collide_flag(object *asteroid_objp)
1839 {
1840         int             i, num_escorts, escort_objnum, will_collide=0;
1841         ship            *escort_shipp;
1842         asteroid        *asp;
1843
1844         asp = &Asteroids[asteroid_objp->instance];
1845         asp->collide_objnum = -1;
1846         asp->collide_objsig = -1;
1847
1848         // multiplayer dogfight
1849         if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){
1850                 return;
1851         }
1852
1853         num_escorts = hud_escort_num_ships_on_list();
1854
1855         if ( num_escorts <= 0 ) {
1856                 return;
1857         }
1858
1859         for ( i = 0; i < num_escorts; i++ ) {
1860                 escort_objnum = hud_escort_return_objnum(i);
1861                 if ( escort_objnum >= 0 ) {
1862                         escort_shipp = &Ships[Objects[escort_objnum].instance];
1863                         if ( asteroid_valid_ship_to_warn_collide(escort_shipp) ) {
1864                                 will_collide = asteroid_will_collide(asteroid_objp, &Objects[escort_objnum]);
1865                                 if ( will_collide ) {
1866                                         asp->collide_objnum = escort_objnum;
1867                                         asp->collide_objsig = Objects[escort_objnum].signature;
1868                                 }
1869                         }
1870                 }
1871         }
1872 }
1873
1874 // ensure that the collide objnum for the asteroid is still valid
1875 void asteroid_verify_collide_objnum(asteroid *asp)
1876 {
1877         if ( asp->collide_objnum >= 0 ) {
1878                 if ( Objects[asp->collide_objnum].signature != asp->collide_objsig ) {
1879                         asp->collide_objnum = -1;
1880                         asp->collide_objsig = -1;
1881                 }
1882         }
1883 }
1884
1885 void asteroid_process_post(object * obj, float frame_time)
1886 {
1887         if (Asteroids_enabled) {
1888                 int num;
1889                 num = obj->instance;
1890                 
1891                 //Assert( Asteroids[num].objnum == objnum );
1892                 asteroid        *asp = &Asteroids[num];
1893
1894                 // Only wrap if active field
1895                 if (Asteroid_field.field_type == FT_ACTIVE) {
1896                         if ( timestamp_elapsed(asp->check_for_wrap) ) {
1897                                 asteroid_maybe_reposition(obj, &Asteroid_field);
1898                                 asp->check_for_wrap = timestamp(ASTEROID_CHECK_WRAP_TIMESTAMP);
1899                         }
1900                 }
1901
1902                 asteroid_verify_collide_objnum(asp);
1903
1904                 if ( timestamp_elapsed(asp->check_for_collide) ) {
1905                         asteroid_update_collide_flag(obj);
1906                         asp->check_for_collide = timestamp(ASTEROID_UPDATE_COLLIDE_TIMESTAMP);
1907                 }
1908
1909                 asteroid_maybe_break_up(obj);
1910         }
1911 }
1912
1913 // return the object number that the asteroid is about to impact
1914 int asteroid_collide_objnum(object *asteroid_objp)
1915 {
1916         return Asteroids[asteroid_objp->instance].collide_objnum;
1917 }
1918
1919 // return the time until the asteroid will impact its collide_objnum
1920 float asteroid_time_to_impact(object *asteroid_objp)
1921 {
1922         float           time=-1.0f, total_dist, speed;
1923         asteroid        *asp;
1924         mc_info mc;
1925
1926         asp = &Asteroids[asteroid_objp->instance];
1927
1928         if ( asp->collide_objnum < 0 ) {
1929                 return time;
1930         }
1931         
1932         asteroid_test_collide(asteroid_objp, &Objects[asp->collide_objnum], &mc);
1933
1934         if ( mc.num_hits ) {
1935                 total_dist = vm_vec_dist(&mc.hit_point_world, &asteroid_objp->pos) - asteroid_objp->radius;
1936                 if ( total_dist < 0 ) {
1937                         total_dist = 0.0f;
1938                 }
1939                 speed = vm_vec_mag(&asteroid_objp->phys_info.vel);
1940                 time = total_dist/speed;
1941         }       
1942
1943         return time;
1944 }
1945
1946 // read in a single asteroid section from asteroid.tbl
1947 void asteroid_parse_section()
1948 {
1949         asteroid_info   *asip;
1950         
1951         asip = &Asteroid_info[Num_asteroid_types];
1952
1953         required_string("$Name:");
1954         stuff_string(asip->name, F_NAME, NULL);
1955
1956         required_string( "$POF file1:" );
1957         stuff_string_white( asip->pof_files[0] );
1958
1959         required_string( "$POF file2:" );
1960         stuff_string_white( asip->pof_files[1] );
1961
1962         if ( (strstr(asip->name,"Asteroid") != NULL) || (strstr(asip->name, "asteroid") != NULL) ) {
1963                 required_string( "$POF file3:" );
1964                 stuff_string_white( asip->pof_files[2] );
1965         }
1966
1967         asip->num_detail_levels = 0;
1968
1969         required_string("$Detail distance:");
1970         asip->num_detail_levels = stuff_int_list(asip->detail_distance, MAX_SHIP_DETAIL_LEVELS, RAW_INTEGER_TYPE);
1971
1972         required_string("$Max Speed:");
1973         stuff_float(&asip->max_speed);
1974
1975         required_string("$Expl inner rad:");
1976         stuff_float(&asip->inner_rad);
1977
1978         required_string("$Expl outer rad:");
1979         stuff_float(&asip->outer_rad);
1980
1981         required_string("$Expl damage:");
1982         stuff_float(&asip->damage);
1983
1984         required_string("$Expl blast:");
1985         stuff_float(&asip->blast);
1986
1987         required_string("$Hitpoints:");
1988         stuff_float(&asip->initial_hull_strength);
1989 }
1990
1991 // read in data from asteroid.tbl into Asteroid_info[] array
1992 void asteroid_parse_tbl()
1993 {
1994         char impact_ani_file[FILESPEC_LENGTH];
1995
1996         Num_asteroid_types = 0;
1997
1998         // open localization
1999         lcl_ext_open();
2000
2001         read_file_text("asteroid.tbl");
2002         reset_parse();
2003
2004         required_string("#Asteroid Types");
2005
2006         while (required_string_either("#End","$Name:")) {
2007                 Assert( Num_asteroid_types < MAX_DEBRIS_TYPES );
2008                 asteroid_parse_section();
2009                 Num_asteroid_types++;
2010         }
2011
2012         required_string("#End");
2013
2014         // check all read in
2015         Assert(Num_asteroid_types == MAX_DEBRIS_TYPES);
2016
2017         Asteroid_impact_explosion_ani = -1;
2018         required_string("$Impact Explosion:");
2019         stuff_string(impact_ani_file, F_NAME, NULL);
2020         if ( stricmp(impact_ani_file,NOX("none")))      {
2021                 int num_frames;
2022                 Asteroid_impact_explosion_ani = bm_load_animation( impact_ani_file, &num_frames, NULL, 1);
2023         }
2024
2025         required_string("$Impact Explosion Radius:");
2026         stuff_float(&Asteroid_impact_explosion_radius);
2027
2028         // close localization
2029         lcl_ext_close();
2030 }
2031
2032 //      Return number of asteroids expected to collide with a ship.
2033 int count_incident_asteroids()
2034 {
2035         object  *asteroid_objp;
2036         int             count;
2037
2038         count = 0;
2039
2040         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
2041                 if (asteroid_objp->type == OBJ_ASTEROID ) {
2042                         asteroid *asp = &Asteroids[asteroid_objp->instance];
2043
2044                         if ( asp->target_objnum >= 0 ) {
2045                                 count++;
2046                         }
2047                 }
2048         }
2049
2050         return count;
2051 }
2052
2053 //      Pick object to throw asteroids at.
2054 //      Pick any capital or big ship inside the bounds of the asteroid field.
2055 int set_asteroid_throw_objnum()
2056 {
2057         if (Asteroid_field.num_initial_asteroids < 1)
2058                 return -1;
2059
2060         ship_obj        *so;
2061         object  *ship_objp;
2062
2063         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2064                 ship_objp = &Objects[so->objnum];
2065                 float           radius = ship_objp->radius*2.0f;
2066
2067                 if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP)) {
2068                         if (ship_objp->pos.x + radius > Asteroid_field.min_bound.x)
2069                                 if (ship_objp->pos.y + radius > Asteroid_field.min_bound.y)
2070                                 if (ship_objp->pos.z + radius > Asteroid_field.min_bound.z)
2071                                 if (ship_objp->pos.x - radius < Asteroid_field.max_bound.x)
2072                                 if (ship_objp->pos.y - radius < Asteroid_field.max_bound.y)
2073                                 if (ship_objp->pos.z - radius < Asteroid_field.max_bound.z)
2074                                 if (!asteroid_in_inner_bound(&Asteroid_field, &ship_objp->pos, radius))
2075                                         return so->objnum;
2076                 }
2077         }
2078         return -1;
2079
2080 }
2081
2082 void asteroid_frame()
2083 {
2084         if (Num_asteroids < 1)
2085                 return;
2086
2087         // Only throw if active field
2088         if (Asteroid_field.field_type == FT_PASSIVE) {
2089                 return;
2090         }
2091
2092         Asteroid_throw_objnum = set_asteroid_throw_objnum();
2093
2094         maybe_throw_asteroid(count_incident_asteroids());
2095 }
2096
2097 // Called once, at game start.  Do any one-time initializations here
2098 void asteroid_init()
2099 {
2100         asteroid_parse_tbl();
2101 }
2102
2103 // Draw brackets around on-screen asteroids that are about to collide, otherwise draw an offscreen indicator
2104 void asteroid_show_brackets()
2105 {
2106         vertex  asteroid_vertex;
2107         object  *asteroid_objp, *player_target;
2108         asteroid        *asp;
2109
2110         // get pointer to player target, so we don't need to take OBJ_INDEX() of asteroid_objp to compare to target_objnum
2111         if ( Player_ai->target_objnum >= 0 ) {
2112                 player_target = &Objects[Player_ai->target_objnum];
2113         } else {
2114                 player_target = NULL;
2115         }
2116
2117         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
2118                 if (asteroid_objp->type != OBJ_ASTEROID ) {
2119                         continue;
2120                 }
2121
2122                 asp = &Asteroids[asteroid_objp->instance];
2123
2124                 if ( asp->collide_objnum < 0 ) {
2125                         continue;
2126                 }
2127
2128                 if ( asteroid_objp == player_target ) {
2129                         continue;
2130                 }
2131
2132                 g3_rotate_vertex(&asteroid_vertex,&asteroid_objp->pos);
2133                 g3_project_vertex(&asteroid_vertex);
2134
2135                 if (!(asteroid_vertex.flags & PF_OVERFLOW)) {
2136                         gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
2137                         hud_show_brackets(asteroid_objp, &asteroid_vertex);
2138                 }
2139
2140                 // if asteroid is not on screen, draw an offscreen indicator
2141                 if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
2142                         if (asteroid_vertex.codes != 0) {
2143                                 float dist;
2144 //                              dist = vm_vec_dist_quick(&Player_obj->pos, &asteroid_objp->pos);
2145                                 dist = hud_find_target_distance( asteroid_objp, Player_obj );
2146                                 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
2147                                 hud_draw_offscreen_indicator(&asteroid_vertex, &asteroid_objp->pos, dist);
2148                         }
2149                 }
2150         }
2151 }
2152
2153 // target the closest danger asteroid to the player
2154 void asteroid_target_closest_danger()
2155 {
2156         object  *asteroid_objp, *closest_asteroid_objp = NULL;
2157         asteroid        *asp;
2158         float           dist, closest_dist = 999999.0f;
2159
2160         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
2161                 if (asteroid_objp->type != OBJ_ASTEROID ) {
2162                         continue;
2163                 }
2164
2165                 asp = &Asteroids[asteroid_objp->instance];
2166
2167                 if ( asp->collide_objnum < 0 ) {
2168                         continue;
2169                 }
2170
2171                 dist = vm_vec_dist_quick(&Player_obj->pos, &asteroid_objp->pos);
2172
2173                 if ( dist < closest_dist ) {
2174                         closest_dist = dist;
2175                         closest_asteroid_objp = asteroid_objp;
2176                 }
2177         }
2178
2179         if ( closest_asteroid_objp ) {
2180                 set_target_objnum( Player_ai, OBJ_INDEX(closest_asteroid_objp) );
2181         }
2182 }
2183
2184 void asteroid_page_in()
2185 {
2186         if (Asteroid_field.num_initial_asteroids > 0 ) {
2187                 int i, j, k;
2188
2189                 nprintf(( "Paging", "Paging in asteroids\n" ));
2190
2191
2192                 // max of 3 possible debris field models
2193                 for (i=0; i<3; i++) {
2194                         asteroid_info   *asip;
2195
2196                         if (Asteroid_field.debris_genre == DG_ASTEROID) {
2197                                 // asteroid
2198                                 asip = &Asteroid_info[i];
2199                         } else {
2200                                 // ship debris - always full until empty
2201                                 if (Asteroid_field.field_debris_type[i] != -1) {
2202                                         asip = &Asteroid_info[Asteroid_field.field_debris_type[i]];
2203                                 } else {
2204                                         break;
2205                                 }
2206                         }
2207
2208
2209                         for (k=0; k<3; k++) {
2210
2211                                 // SHIP DEBRIS - use subtype 0
2212                                 if (Asteroid_field.debris_genre == DG_SHIP) {
2213                                         if (k > 0) {
2214                                                 break;
2215                                         }
2216                                 } else {
2217                                         // ASTEROID DEBRIS - use subtype (Asteroid_field.field_debris_type[] != -1)
2218                                         if (Asteroid_field.field_debris_type[k] == -1) {
2219                                                 continue;
2220                                         }
2221                                 }
2222                                 
2223                                 asip->modelp[k] = model_get(asip->model_num[k]);
2224
2225                                 // Page in textures
2226                                 for (j=0; j<asip->modelp[k]->n_textures; j++ )  {
2227                                         int bitmap_num = asip->modelp[k]->original_textures[j];
2228
2229
2230                                         // if we're in Glide (and maybe later with D3D), use nondarkening textures
2231                                         if ( bitmap_num > -1 )  {
2232                                                 if(gr_screen.mode == GR_GLIDE){
2233                                                         bm_page_in_nondarkening_texture( bitmap_num );
2234                                                 } else {
2235                                                         bm_page_in_texture( bitmap_num );
2236                                                 }                               
2237                                         }                                       
2238                                 }
2239
2240                         }
2241                 } 
2242         }
2243 }
2244
2245 #else
2246
2247 // stubbed out functions not used in the demo
2248 void    asteroid_init() {}
2249 void    asteroid_level_init() {}
2250 void    asteroid_level_close() {}
2251 void    asteroid_create_all() {}
2252 void    asteroid_render( object *asteroid_objp ) {}
2253 void    asteroid_delete( object *asteroid_objp ) {}
2254 void    asteroid_process_pre( object *asteroid_objp, float frame_time) {}
2255 void    asteroid_process_post( object *asteroid_objp, float frame_time) {}
2256 int     asteroid_check_collision( object *asteroid_objp, object * other_obj, vector * hitpos, collision_info_struct *asteroid_hit_info ) {return 0;}
2257 void    asteroid_hit( object *asteroid_objp, object *other_objp, vector *hitpos, float damage ) {}
2258 void    asteroid_save_restore(CFILE *fp) {}
2259 int     asteroid_count() {return 0;}
2260 int     asteroid_collide_objnum(object *asteroid_objp) {return 0;}
2261 float asteroid_time_to_impact(object *asteroid_objp) {return 0.0f;}
2262 void    asteroid_show_brackets() {}
2263 void    asteroid_target_closest_danger() {}
2264 void    asteroid_page_in() {}
2265 void    asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec) {}
2266 void    asteroid_frame() {}
2267
2268
2269 #endif