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