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