2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Asteroid/Asteroid.cpp $
15 * C module for asteroid code
18 * Revision 1.5 2003/05/25 02:30:42 taylor
21 * Revision 1.4 2002/06/18 08:58:53 relnev
22 * last few struct changes
24 * Revision 1.3 2002/06/09 04:41:15 relnev
25 * added copyright header
27 * Revision 1.2 2002/05/07 03:16:43 theoddone33
28 * The Great Newline Fix
30 * Revision 1.1.1.1 2002/05/03 03:28:11 root
34 * 31 9/08/99 10:52a Mikek
35 * Reduce the number of asteroids that can be thrown at a target at
38 * 30 9/06/99 12:46a Andsager
39 * Add weapon_explosion_ani LOD
41 * 29 9/05/99 3:13p Mikek
42 * Slightly decrease number of asteroids thrown at escorted ships.
44 * 28 8/26/99 8:51p Dave
45 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
47 * 27 7/30/99 7:01p Dave
48 * Dogfight escort gauge. Fixed up laser rendering in Glide.
50 * 26 7/19/99 8:58p Andsager
51 * Don't try to throw at objnum -1
53 * 25 7/15/99 9:20a Andsager
54 * FS2_DEMO initial checkin
56 * 24 7/09/99 5:54p Dave
57 * Seperated cruiser types into individual types. Added tons of new
58 * briefing icons. Campaign screen.
60 * 23 6/10/99 11:06a Andsager
61 * Mission designed selection of asteroid types.
63 * 22 6/09/99 2:55p Andsager
64 * Allow multiple asteroid subtypes (of large, medium, small) and follow
67 * 21 6/08/99 7:19p Dave
68 * Fixed asteroid lighting. Was because on non-darkening textures in Glide
71 * 20 6/07/99 1:18p Andsager
72 * Make asteroids choose consistent texture from large to small. Modify
73 * fireball radius of dying asteroids.
75 * 19 5/03/99 10:50p Andsager
76 * Make Asteroid_obj_list. Change get_nearest_turret_objnum() to use
77 * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
80 * 18 4/23/99 12:01p Johnson
83 * 17 4/21/99 6:15p Dave
84 * Did some serious housecleaning in the beam code. Made it ready to go
85 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
86 * a handy macro for recalculating collision pairs for a given object.
88 * 16 4/16/99 2:34p Andsager
89 * Second pass on debris fields
91 * 15 4/15/99 5:27p Andsager
92 * Fix explosion sound for Ships debris
94 * 14 4/15/99 5:00p Andsager
95 * Frist pass on Debris field
97 * 13 2/07/99 8:51p Andsager
98 * Add inner bound to asteroid field. Inner bound tries to stay astroid
99 * free. Wrap when within and don't throw at ships inside.
101 * 12 1/20/99 6:04p Dave
102 * Another bit of stuff for beam weapons. Ships will properly use them
103 * now, although they're really deadly.
105 * 11 1/06/99 2:24p Dave
106 * Stubs and release build fixes.
108 * 10 12/03/98 3:14p Andsager
109 * Check in code that checks rotating submodel actually has ship subsystem
111 * 9 11/19/98 11:07p Andsager
112 * Check in of physics and collision detection of rotating submodels
114 * 8 11/13/98 10:12a Andsager
115 * simplify collision code
117 * 7 11/05/98 5:55p Dave
118 * Big pass at reducing #includes
120 * 6 10/23/98 3:51p Dave
121 * Full support for tstrings.tbl and foreign languages. All that remains
122 * is to make it active in Fred.
124 * 5 10/23/98 1:11p Andsager
125 * Make ship sparks emit correctly from rotating structures.
127 * 4 10/16/98 1:22p Andsager
128 * clean up header files
130 * 3 10/13/98 9:28a Dave
131 * Started neatening up freespace.h. Many variables renamed and
132 * reorganized. Added AlphaColors.[h,cpp]
134 * 2 10/07/98 10:52a Dave
137 * 1 10/07/98 10:48a Dave
139 * 80 7/14/98 3:30p Dave
141 * 79 5/18/98 12:05p Mike
142 * Make sm1-06a a little harder.
144 * 78 5/06/98 12:36p Lawrance
145 * disable ambient asteroid sound for now
147 * 77 4/30/98 12:49a Allender
148 * deal with asteroid problems in multiplayer
150 * 76 4/27/98 1:46p Mike
151 * Make asteroid_density have no meaning.
153 * 75 4/24/98 11:59a Dan
154 * fix bug where a null vector would occur when death_hit_pos was assinged
157 * 74 4/23/98 4:43p Andsager
158 * Don't call asteroid_update_collide() on creating asteroids since it
159 * creates sets of collisions pairs. Pairs are created when objects are
160 * merged in obj_merge_created_list()
162 * 73 4/21/98 1:23a Mike
163 * Tone down difficulty in asteroid toss missions.
165 * 72 4/16/98 11:57a John
166 * Removed debug and unused registry values.
168 * 71 4/08/98 1:27a Lawrance
171 * 70 4/08/98 1:17a Lawrance
172 * Fix bug with red and white brackets being drawn on top of one another.
174 * 69 4/02/98 6:28p Lawrance
175 * remove asteroid code from demo
177 * 68 4/02/98 5:11p Mike
178 * Quick out of asteroid code if none present.
180 * 67 4/02/98 1:34p Mike
181 * Minor difficulty balance, made a bit easier.
183 * 66 4/02/98 1:29p Andsager
185 * 65 4/01/98 5:34p John
186 * Made only the used POFs page in for a level. Reduced some interp
187 * arrays. Made custom detail level work differently.
189 * 64 3/31/98 5:11p John
190 * Removed demo/save/restore. Made NDEBUG defined compile. Removed a
191 * bunch of debug stuff out of player file. Made model code be able to
192 * unload models and malloc out only however many models are needed.
194 * 63 3/30/98 4:02p John
195 * Made machines with < 32 MB of RAM use every other frame of certain
196 * bitmaps. Put in code to keep track of how much RAM we've malloc'd.
198 * 62 3/30/98 2:38p Mike
199 * Add asteroid_density to detail level support.
200 * No force explosion damage in training missions.
201 * Make cargo deathrolls shorter.
203 * 61 3/30/98 10:07a Lawrance
204 * Um, make asteroids work again.
206 * 60 3/29/98 12:55a Lawrance
207 * Get demo build working with limited set of data.
209 * 59 3/26/98 9:19a Lawrance
210 * Support multiple asteroid pofs
212 * 58 3/25/98 3:44p Andsager
213 * Fixed bug in asteroid_sub_create, where last_pos field was invalid in
214 * its creation frame but needed for collision in that frame.
216 * 57 3/25/98 2:03p Sandeep
217 * Remove "f" from top of file.
219 * 56 3/25/98 1:50p Mike
220 * Asteroid destruction (sm1-06a) balancing.
222 * 55 3/25/98 10:43a Andsager
223 * Hack for ship_asteroid collisions when erroneous relative_vel >150
225 * 54 3/23/98 12:20p Andsager
226 * Enable collision from rotation in ship_debris and ship_debris
229 * 53 3/22/98 4:09p Andsager
230 * Make moment of inertia based on model.
232 * 52 3/21/98 3:33p Lawrance
233 * When asteroid collides with an escort ship, make the sound carry
236 * 51 3/19/98 12:09p John
237 * Fixed a bug using 6 characters. r_heavy was using local coordinates
238 * instead of world so all asteroid-ship and debris-ship hitpos's were in
239 * the wrong spot. Ok, I rearreanged some code to make it clearer also.
241 * 50 3/17/98 5:55p Lawrance
242 * Support object linked sounds for asteroids.
244 * 49 3/17/98 3:44p Mike
245 * Make asteroids-thrown-at-target not only happen when an asteroid wraps.
247 * 48 3/17/98 9:53a Lawrance
248 * Asteroid area effect only affect ships
250 * 47 3/17/98 9:25a Allender
251 * some minor asteroid changes for multiplayer
253 * 46 3/17/98 12:16a Allender
254 * asteroids in multiplayer -- minor problems with position being correct
256 * 45 3/14/98 1:44p Mike
257 * Todolist items 3365..3368. Make child asteroids collide properly.
258 * Make asteroid throwing less bunchy, toss asteroids earlier, make
259 * facing-ness not break mission balance.
261 * 44 3/13/98 9:06a John
262 * Made missile thrusters use a different min dist for scaling. Made
263 * missilies not use lighting.
265 * 43 3/12/98 4:01p Lawrance
266 * Only show warning brackets for cruiser/capital ships on the same team
271 #include "asteroid.h"
273 #include "objcollide.h"
274 #include "freespace.h"
277 #include "fireballs.h"
280 #include "particle.h"
281 #include "linklist.h"
282 #include "hudescort.h"
284 #include "multiutil.h"
285 #include "staticrand.h"
286 #include "multimsgs.h"
287 #include "systemvars.h"
288 #include "localize.h"
291 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
293 #define ASTEROID_OBJ_USED (1<<0) // flag used in asteroid_obj struct
294 #define MAX_ASTEROID_OBJS MAX_ASTEROIDS // max number of asteroids tracked in asteroid list
295 asteroid_obj Asteroid_objs[MAX_ASTEROID_OBJS]; // array used to store asteroid object indexes
296 asteroid_obj Asteroid_obj_list; // head of linked list of asteroid_obj structs
298 // Asteroid editor requires first set of entries to be "None" and then "Asteroid XXX"
299 // Any changes to this will require changes to the asteroid editor
300 debris_struct Field_debris_info[] = {
303 { ASTEROID_TYPE_SMALL, "Small Asteroid", },
304 { ASTEROID_TYPE_MEDIUM, "Medium Asteroid", },
305 { ASTEROID_TYPE_BIG, "Large Asteroid",},
307 { ASTEROID_TYPE_SMALL, "Asteroid Small", },
308 { ASTEROID_TYPE_MEDIUM, "Asteroid Medium", },
309 { ASTEROID_TYPE_BIG, "Asteroid Large", },
313 { DEBRIS_TERRAN_SMALL, "Terran Small", },
314 { DEBRIS_TERRAN_MEDIUM, "Terran Medium", },
315 { DEBRIS_TERRAN_LARGE, "Terran Large", },
317 { DEBRIS_VASUDAN_SMALL, "Vasudan Small", },
318 { DEBRIS_VASUDAN_MEDIUM, "Vasudan Medium", },
319 { DEBRIS_VASUDAN_LARGE, "Vasudan Large", },
321 { DEBRIS_SHIVAN_SMALL, "Shivan Small", },
322 { DEBRIS_SHIVAN_MEDIUM, "Shivan Medium", },
323 { DEBRIS_SHIVAN_LARGE, "Shivan Large" },
327 // used for randomly generating debris type when there are multiple sizes.
328 #define SMALL_DEBRIS_WEIGHT 8
329 #define MEDIUM_DEBRIS_WEIGHT 4
330 #define LARGE_DEBRIS_WEIGHT 1
332 int Asteroids_enabled = 1;
333 int Num_asteroid_types;
334 int Num_asteroids = 0;
335 int Asteroid_throw_objnum = -1; // Object index of ship to throw asteroids at.
336 int Next_asteroid_throw;
338 asteroid_info Asteroid_info[MAX_DEBRIS_TYPES];
339 asteroid Asteroids[MAX_ASTEROIDS];
340 asteroid_field Asteroid_field;
342 static int Asteroid_impact_explosion_ani;
343 static float Asteroid_impact_explosion_radius;
345 #define ASTEROID_CHECK_WRAP_TIMESTAMP 2000 // how often an asteroid gets checked for wrapping
346 #define ASTEROID_UPDATE_COLLIDE_TIMESTAMP 2000 // how often asteroid is checked for impending collisions with escort ships
347 #define ASTEROID_MIN_COLLIDE_TIME 24 // time in seconds to check for asteroid colliding
349 // Force updating of pair stuff for asteroid *objp.
350 void asteroid_update_collide(object *objp)
352 // Asteroid has wrapped, update collide objnum and flags
353 Asteroids[objp->instance].collide_objnum = -1;
354 Asteroids[objp->instance].collide_objsig = -1;
355 OBJ_RECALC_PAIRS(objp);
358 // Clear out the Asteroid_obj_list
360 void asteroid_obj_list_init()
364 list_init(&Asteroid_obj_list);
365 for ( i = 0; i < MAX_ASTEROID_OBJS; i++ ) {
366 Asteroid_objs[i].flags = 0;
370 // ---------------------------------------------------
371 // asteroid_obj_list_add()
373 // Function to add a node from the Asteroid_obj_list. Only
374 // called from weapon_create()
375 int asteroid_obj_list_add(int objnum)
379 asteroid *cur_asteroid = &Asteroids[Objects[objnum].instance];
380 index = cur_asteroid - Asteroids;
382 Assert(index >= 0 && index < MAX_ASTEROID_OBJS);
383 Assert(!Asteroid_objs[index].flags & ASTEROID_OBJ_USED);
385 Asteroid_objs[index].flags = 0;
386 Asteroid_objs[index].objnum = objnum;
387 list_append(&Asteroid_obj_list, &Asteroid_objs[index]);
388 Asteroid_objs[index].flags |= ASTEROID_OBJ_USED;
393 // ---------------------------------------------------
394 // missle_obj_list_remove()
396 // Function to remove a node from the Asteroid_obj_list. Only
397 // called from weapon_delete()
398 void asteroid_obj_list_remove(object * obj)
400 int index = obj->instance;
402 Assert(index >= 0 && index < MAX_ASTEROID_OBJS);
403 Assert(Asteroid_objs[index].flags & ASTEROID_OBJ_USED);
405 list_remove(&Asteroid_obj_list, &Asteroid_objs[index]);
406 Asteroid_objs[index].flags = 0;
410 // Prevent speed from getting too huge so it's hard to catch up to an asteroid.
411 float asteroid_cap_speed(int asteroid_info_index, float speed)
413 float max, double_max;
415 max = Asteroid_info[asteroid_info_index].max_speed;
416 double_max = max * 2;
418 while (speed > double_max){
429 // Returns whether position is inside inner bounding volume
430 // sum together the following: 1 inside x, 2 inside y, 4 inside z
431 // inside only when sum = 7
432 int asteroid_in_inner_bound_with_axes(asteroid_field *asfieldp, vector *pos, float delta)
434 Assert(asfieldp->has_inner_bound);
437 if ( (pos->xyz.x > asfieldp->inner_min_bound.xyz.x - delta) && (pos->xyz.x < asfieldp->inner_max_bound.xyz.x + delta) ) {
441 if ( (pos->xyz.y > asfieldp->inner_min_bound.xyz.y - delta) && (pos->xyz.y < asfieldp->inner_max_bound.xyz.y + delta) ) {
445 if ( (pos->xyz.z > asfieldp->inner_min_bound.xyz.z - delta) && (pos->xyz.z < asfieldp->inner_max_bound.xyz.z + delta) ) {
452 // check if asteroid is within inner bound
453 // return 0 if not inside or no inner bound, 1 if inside inner bound
454 int asteroid_in_inner_bound(asteroid_field *asfieldp, vector *pos, float delta) {
456 if (!asfieldp->has_inner_bound) {
460 return (asteroid_in_inner_bound_with_axes(asfieldp, pos, delta) == 7);
463 // repositions asteroid outside the inner box on all 3 axes
464 // moves to the other side of the inner box a distance delta from edge of box
465 void inner_bound_pos_fixup(asteroid_field *asfieldp, vector *pos)
467 if (!asteroid_in_inner_bound(asfieldp, pos, 0)) {
474 for (axis=0; axis<3; axis++) {
475 dist1 = pos->a1d[axis] - asfieldp->inner_min_bound.a1d[axis];
476 dist2 = asfieldp->inner_max_bound.a1d[axis] - pos->a1d[axis];
477 Assert(dist1 >= 0 && dist2 >= 0);
480 pos->a1d[axis] = asfieldp->inner_max_bound.a1d[axis] + dist1;
482 pos->a1d[axis] = asfieldp->inner_min_bound.a1d[axis] - dist2;
488 // Create a single asteroid
489 object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroid_subtype)
496 vector pos, delta_bound;
503 if(asfieldp == NULL){
507 for (n=0; n<MAX_ASTEROIDS; n++ ) {
508 if ( !(Asteroids[n].flags & AF_USED) ){
513 if (n >= MAX_ASTEROIDS) {
514 nprintf(("Warning","Could not create asteroid, no more slots left\n"));
518 if((asteroid_type < 0) || (asteroid_type >= Num_asteroid_types)){
522 // HACK: multiplayer asteroid subtype always 0 to keep subtype in sync
523 if ( Game_mode & GM_MULTIPLAYER) {
524 asteroid_subtype = 0;
527 if( (asteroid_subtype < 0) || (asteroid_subtype >= MAX_ASTEROID_POFS)){
531 asip = &Asteroid_info[asteroid_type];
534 if(asip->modelp[asteroid_subtype] == NULL){
539 asp->type = asteroid_type;
540 asp->asteroid_subtype = asteroid_subtype;
542 asp->flags |= AF_USED;
543 asp->check_for_wrap = timestamp_rand(0, ASTEROID_CHECK_WRAP_TIMESTAMP);
544 asp->check_for_collide = timestamp_rand(0, ASTEROID_UPDATE_COLLIDE_TIMESTAMP);
545 asp->final_death_time = timestamp(-1);
546 asp->collide_objnum = -1;
547 asp->collide_objsig = -1;
548 asp->target_objnum = -1;
550 radius = model_get_radius(asip->model_num[asteroid_subtype]);
552 vm_vec_sub(&delta_bound, &asfieldp->max_bound, &asfieldp->min_bound);
554 // for multiplayer, we want to do a static_rand so that everything behaves the same on all machines
557 if ( Game_mode & GM_NORMAL ) {
558 pos.xyz.x = asfieldp->min_bound.xyz.x + delta_bound.xyz.x * frand();
559 pos.xyz.y = asfieldp->min_bound.xyz.y + delta_bound.xyz.y * frand();
560 pos.xyz.z = asfieldp->min_bound.xyz.z + delta_bound.xyz.z * frand();
562 inner_bound_pos_fixup(asfieldp, &pos);
563 // vm_set_identity(&orient);
564 angs.p = frand() * 2*PI;
565 angs.b = frand() * 2*PI;
566 angs.h = frand() * 2*PI;
568 signature = multi_assign_network_signature( MULTI_SIG_ASTEROID );
569 rand_base = signature;
571 pos.xyz.x = asfieldp->min_bound.xyz.x + delta_bound.xyz.x * static_randf( rand_base++ );
572 pos.xyz.y = asfieldp->min_bound.xyz.y + delta_bound.xyz.y * static_randf( rand_base++ );
573 pos.xyz.z = asfieldp->min_bound.xyz.z + delta_bound.xyz.z * static_randf( rand_base++ );
575 inner_bound_pos_fixup(asfieldp, &pos);
576 // vm_set_identity(&orient);
577 angs.p = static_randf( rand_base++ ) * 2*PI;
578 angs.b = static_randf( rand_base++ ) * 2*PI;
579 angs.h = static_randf( rand_base++ ) * 2*PI;
582 vm_angles_2_matrix(&orient, &angs);
584 objnum = obj_create( OBJ_ASTEROID, -1, n, &orient, &pos, radius, OF_RENDERS | OF_PHYSICS | OF_COLLIDES);
585 // mprintf(("Framecount: %d asteroid create: obj = %d\n", Framecount, objnum));
587 if ( (objnum == -1) || (objnum >= MAX_OBJECTS) ) {
588 mprintf(("Couldn't create asteroid -- out of object slots\n"));
592 asp->objnum = objnum;
594 // Add to Asteroid_used_list
595 asteroid_obj_list_add(objnum);
597 objp = &Objects[objnum];
599 if ( Game_mode & GM_MULTIPLAYER ){
600 objp->net_signature = signature;
610 if ( Game_mode & GM_NORMAL ) {
611 vm_vec_rand_vec_quick(&rotvel);
612 vm_vec_scale(&rotvel, frand()/4.0f + 0.1f);
613 objp->phys_info.rotvel = rotvel;
614 vm_vec_rand_vec_quick(&objp->phys_info.vel);
616 static_randvec( rand_base++, &rotvel );
617 vm_vec_scale(&rotvel, static_randf(rand_base++)/4.0f + 0.1f);
618 objp->phys_info.rotvel = rotvel;
619 static_randvec( rand_base++, &objp->phys_info.vel );
625 if ( Game_mode & GM_NORMAL ) {
626 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));
628 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));
631 vm_vec_scale(&objp->phys_info.vel, speed);
632 objp->phys_info.desired_vel = objp->phys_info.vel;
634 // blow out his reverse thrusters. Or drag, same thing.
635 objp->phys_info.rotdamp = 10000.0f;
636 objp->phys_info.side_slip_time_const = 10000.0f;
637 objp->phys_info.flags |= (PF_REDUCED_DAMP | PF_DEAD_DAMP); // set damping equal for all axis and not changable
639 // Fill in the max_vel field, so the collision pair stuff knows
640 // how fast this can move maximum in order to throw out collisions.
641 // This is in local coordinates, so Z is forward velocity.
642 objp->phys_info.max_vel.xyz.x = 0.0f;
643 objp->phys_info.max_vel.xyz.y = 0.0f;
644 objp->phys_info.max_vel.xyz.z = vm_vec_mag(&objp->phys_info.desired_vel);
646 objp->phys_info.mass = asip->modelp[asteroid_subtype]->rad * 700.0f;
647 objp->phys_info.I_body_inv.v.rvec.xyz.x = 1.0f / (objp->phys_info.mass*asip->modelp[asteroid_subtype]->rad);
648 objp->phys_info.I_body_inv.v.uvec.xyz.y = objp->phys_info.I_body_inv.v.rvec.xyz.x;
649 objp->phys_info.I_body_inv.v.fvec.xyz.z = objp->phys_info.I_body_inv.v.rvec.xyz.x;
650 objp->hull_strength = asip->initial_hull_strength * (0.8f + (float)Game_skill_level/NUM_SKILL_LEVELS)/2.0f;
652 // ensure vel is valid
653 Assert( !vm_is_vec_nan(&objp->phys_info.vel) );
655 // assign a persistant sound to the asteroid
656 // obj_snd_assign(objnum, SND_ASTEROID);
661 // Create asteroids when parent_objp blows up.
662 void asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec)
667 Assert(parent_objp->type == OBJ_ASTEROID);
668 int subtype = Asteroids[parent_objp->instance].asteroid_subtype;
669 new_objp = asteroid_create(&Asteroid_field, asteroid_type, subtype);
671 if (new_objp == NULL)
674 if ( MULTIPLAYER_MASTER ){
675 send_asteroid_create( new_objp, parent_objp, asteroid_type, relvec );
678 // Now, bash some values.
679 vm_vec_scale_add(&new_objp->pos, &parent_objp->pos, relvec, 0.5f * parent_objp->radius);
680 float parent_speed = vm_vec_mag_quick(&parent_objp->phys_info.vel);
682 if ( parent_speed < 0.1f ) {
683 parent_speed = vm_vec_mag_quick(&Asteroid_field.vel);
686 new_objp->phys_info.vel = parent_objp->phys_info.vel;
687 if ( Game_mode & GM_NORMAL )
688 speed = asteroid_cap_speed(asteroid_type, (frand() + 2.0f) * parent_speed);
690 speed = asteroid_cap_speed(asteroid_type, (static_randf(new_objp->net_signature)+2.0f) * parent_speed);
692 vm_vec_scale_add2(&new_objp->phys_info.vel, relvec, speed);
693 if (vm_vec_mag_quick(&new_objp->phys_info.vel) > 80.0f)
694 vm_vec_scale(&new_objp->phys_info.vel, 0.5f);
696 new_objp->phys_info.desired_vel = new_objp->phys_info.vel;
697 vm_vec_scale_add(&new_objp->last_pos, &new_objp->pos, &new_objp->phys_info.vel, -flFrametime);
698 // DA: 4/22/98 We get next line for free when new object (in obj_create_list) is merged.
699 // this line gives too many collision pairs.
700 // asteroid_update_collide(new_objp);
703 // Load in an asteroid model
704 void asteroid_load(int asteroid_info_index, int asteroid_subtype)
709 asip = &Asteroid_info[asteroid_info_index];
711 // pick one of MAX_ASTEROID_POFS models
712 // LOAD ALL TEXTURES USED
713 // static int asteroid_pof_index = rand() % MAX_ASTEROID_POFS;
714 // if (Asteroid_field.debris_genre == DG_ASTEROID) {
715 // pof_index = asteroid_pof_index;
717 // // only 1 pof for ship debris type
721 asip->model_num[asteroid_subtype] = model_load( asip->pof_files[asteroid_subtype], 0, NULL );
723 if (asip->model_num[asteroid_subtype] > -1) {
724 asip->modelp[asteroid_subtype] = model_get(asip->model_num[asteroid_subtype]);
726 // Stuff detail level distances.
727 for (int i = 0; i < asip->num_detail_levels; i++) {
728 asip->modelp[asteroid_subtype]->detail_depth[i] = i2fl(asip->detail_distance[i]);
733 // randomly choose a debris model within the current group
734 int get_debris_from_same_group(int index) {
735 int group_base, group_offset;
737 group_base = (index / 3) * 3;
738 group_offset = index - group_base;
740 // group base + offset
741 // offset is formed by adding 1 or 2 (since 3 in model group) and mapping back in the 0-2 range
742 return group_base + ((group_offset + rand()%2 + 1) % 3);
745 // returns a weight that depends on asteroid size.
746 // the weight is then used to determine the frequencty of different sizes of ship debris
747 int get_debris_weight(int ship_debris_index)
749 switch (ship_debris_index) {
751 case DEBRIS_TERRAN_SMALL:
752 case DEBRIS_VASUDAN_SMALL:
753 case DEBRIS_SHIVAN_SMALL:
754 return SMALL_DEBRIS_WEIGHT;
757 case DEBRIS_TERRAN_MEDIUM:
758 case DEBRIS_VASUDAN_MEDIUM:
759 case DEBRIS_SHIVAN_MEDIUM:
760 return MEDIUM_DEBRIS_WEIGHT;
763 case DEBRIS_TERRAN_LARGE:
764 case DEBRIS_VASUDAN_LARGE:
765 case DEBRIS_SHIVAN_LARGE:
766 return LARGE_DEBRIS_WEIGHT;
777 // Create all the asteroids for the mission, called from
778 void asteroid_create_all()
782 // ship_debris_odds_table keeps track of debris type of the next debris piece
783 // each different type (size) of debris piece has a diffenent weight, smaller weighted more heavily than larger.
784 // choose next type from table ship_debris_odds_table by rand()%max_weighted_range,
785 // the first column in ship_debris_odds_table random number *below* which the debris type in the second column is selected.
786 int ship_debris_odds_table[3][2];
787 int max_weighted_range = 0;
789 if (!Asteroids_enabled)
792 if (Asteroid_field.num_initial_asteroids <= 0 ) {
796 int max_asteroids = Asteroid_field.num_initial_asteroids; // * (1.0f - 0.1f*(MAX_DETAIL_LEVEL-Detail.asteroid_density)));
798 int num_debris_types = 0;
800 // get number of ship debris types
801 if (Asteroid_field.debris_genre == DG_SHIP) {
802 for (idx=0; idx<3; idx++) {
803 if (Asteroid_field.field_debris_type[idx] != -1) {
808 // Calculate the odds table
809 for (idx=0; idx<num_debris_types; idx++) {
810 int debris_weight = get_debris_weight(Asteroid_field.field_debris_type[idx]);
811 ship_debris_odds_table[idx][0] = max_weighted_range + debris_weight;
812 ship_debris_odds_table[idx][1] = Asteroid_field.field_debris_type[idx];
813 max_weighted_range += debris_weight;
817 // Load Asteroid/ship models
818 if (Asteroid_field.debris_genre == DG_SHIP) {
819 for (idx=0; idx<num_debris_types; idx++) {
820 asteroid_load(Asteroid_field.field_debris_type[idx], 0);
823 if (Asteroid_field.field_debris_type[0] != -1) {
824 asteroid_load(ASTEROID_TYPE_SMALL, 0);
825 asteroid_load(ASTEROID_TYPE_MEDIUM, 0);
826 asteroid_load(ASTEROID_TYPE_BIG, 0);
829 if (Asteroid_field.field_debris_type[1] != -1) {
830 asteroid_load(ASTEROID_TYPE_SMALL, 1);
831 asteroid_load(ASTEROID_TYPE_MEDIUM, 1);
832 asteroid_load(ASTEROID_TYPE_BIG, 1);
835 if (Asteroid_field.field_debris_type[2] != -1) {
836 asteroid_load(ASTEROID_TYPE_SMALL, 2);
837 asteroid_load(ASTEROID_TYPE_MEDIUM, 2);
838 asteroid_load(ASTEROID_TYPE_BIG, 2);
842 // load all the asteroid/debris pieces
843 for (i=0; i<max_asteroids; i++) {
844 if (Asteroid_field.debris_genre == DG_ASTEROID) {
845 // For asteroid, load only large asteroids
847 // get a valid subtype
848 int subtype = rand() % 3;
849 while (Asteroid_field.field_debris_type[subtype] == -1) {
850 subtype = (subtype + 1) % 3;
853 asteroid_create(&Asteroid_field, ASTEROID_TYPE_BIG, subtype);
855 Assert(num_debris_types > 0);
857 int rand_choice = rand() % max_weighted_range;
859 for (idx=0; idx<3; idx++) {
860 // for ship debris, choose type according to odds table
861 if (rand_choice < ship_debris_odds_table[idx][0]) {
862 asteroid_create(&Asteroid_field, ship_debris_odds_table[idx][1], 0);
870 // Init asteriod system for the level, called from game_level_init()
871 void asteroid_level_init()
873 Asteroid_field.num_initial_asteroids=0;
875 Next_asteroid_throw = timestamp(1);
876 asteroid_obj_list_init();
879 // return !0 if asteroid should be wrapped, 0 otherwise. Multiplayer clients will always return
880 // 0 from this function. We will force a wrap on the clients when server tells us
881 int asteroid_should_wrap(object *objp, asteroid_field *asfieldp)
883 if ( MULTIPLAYER_CLIENT )
886 if (objp->pos.xyz.x < asfieldp->min_bound.xyz.x) {
890 if (objp->pos.xyz.y < asfieldp->min_bound.xyz.y) {
894 if (objp->pos.xyz.z < asfieldp->min_bound.xyz.z) {
898 if (objp->pos.xyz.x > asfieldp->max_bound.xyz.x) {
902 if (objp->pos.xyz.y > asfieldp->max_bound.xyz.y) {
906 if (objp->pos.xyz.z > asfieldp->max_bound.xyz.z) {
910 // check against inner bound
911 if (asfieldp->has_inner_bound) {
912 if ( (objp->pos.xyz.x > asfieldp->inner_min_bound.xyz.x) && (objp->pos.xyz.x < asfieldp->inner_max_bound.xyz.x)
913 && (objp->pos.xyz.y > asfieldp->inner_min_bound.xyz.y) && (objp->pos.xyz.y < asfieldp->inner_max_bound.xyz.y)
914 && (objp->pos.xyz.z > asfieldp->inner_min_bound.xyz.z) && (objp->pos.xyz.z < asfieldp->inner_max_bound.xyz.z) ) {
923 // Wrap an asteroid from one end of the asteroid field to the other
924 void asteroid_wrap_pos(object *objp, asteroid_field *asfieldp)
926 if (objp->pos.xyz.x < asfieldp->min_bound.xyz.x) {
927 objp->pos.xyz.x = asfieldp->max_bound.xyz.x + (objp->pos.xyz.x - asfieldp->min_bound.xyz.x);
930 if (objp->pos.xyz.y < asfieldp->min_bound.xyz.y) {
931 objp->pos.xyz.y = asfieldp->max_bound.xyz.y + (objp->pos.xyz.y - asfieldp->min_bound.xyz.y);
934 if (objp->pos.xyz.z < asfieldp->min_bound.xyz.z) {
935 objp->pos.xyz.z = asfieldp->max_bound.xyz.z + (objp->pos.xyz.z - asfieldp->min_bound.xyz.z);
938 if (objp->pos.xyz.x > asfieldp->max_bound.xyz.x) {
939 objp->pos.xyz.x = asfieldp->min_bound.xyz.x + (objp->pos.xyz.x - asfieldp->max_bound.xyz.x);
942 if (objp->pos.xyz.y > asfieldp->max_bound.xyz.y) {
943 objp->pos.xyz.y = asfieldp->min_bound.xyz.y + (objp->pos.xyz.y - asfieldp->max_bound.xyz.y);
946 if (objp->pos.xyz.z > asfieldp->max_bound.xyz.z) {
947 objp->pos.xyz.z = asfieldp->min_bound.xyz.z + (objp->pos.xyz.z - asfieldp->max_bound.xyz.z);
950 // wrap on inner bound, check all 3 axes as needed, use of rand ok for multiplayer with send_asteroid_throw()
951 inner_bound_pos_fixup(asfieldp, &objp->pos);
956 // return !0 if this asteroid is a target for any ship, otherwise return 0
957 int asteroid_is_targeted(object *objp)
961 int asteroid_obj_index;
963 asteroid_obj_index=OBJ_INDEX(objp);
965 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
966 ship_objp = &Objects[so->objnum];
967 if ( Ai_info[Ships[ship_objp->instance].ai_index].target_objnum == asteroid_obj_index ) {
975 // Create an asteroid that will hit object *objp in delta_time seconds
976 void asteroid_aim_at_target(object *objp, object *asteroid_objp, float delta_time)
978 vector predicted_center_pos;
982 vm_vec_scale_add(&predicted_center_pos, &objp->pos, &objp->phys_info.vel, delta_time);
983 vm_vec_rand_vec_quick(&rand_vec);
984 vm_vec_scale_add2(&predicted_center_pos, &rand_vec, objp->radius/2.0f);
986 vm_vec_add2(&rand_vec, &objp->orient.v.fvec);
987 if (vm_vec_mag_quick(&rand_vec) < 0.1f)
988 vm_vec_add2(&rand_vec, &objp->orient.v.rvec);
989 vm_vec_normalize(&rand_vec);
991 speed = Asteroid_info[0].max_speed * (frand()/2.0f + 0.5f);
993 vm_vec_copy_scale(&asteroid_objp->phys_info.vel, &rand_vec, -speed);
994 asteroid_objp->phys_info.desired_vel = asteroid_objp->phys_info.vel;
995 vm_vec_scale_add(&asteroid_objp->pos, &predicted_center_pos, &asteroid_objp->phys_info.vel, -delta_time);
996 vm_vec_scale_add(&asteroid_objp->last_pos, &asteroid_objp->pos, &asteroid_objp->phys_info.vel, -flFrametime);
999 int Max_incoming_asteroids[NUM_SKILL_LEVELS] = {3, 4, 5, 7, 10};
1001 // Call once per frame to maybe throw an asteroid at a ship.
1002 // "count" asteroids already targeted on
1003 void maybe_throw_asteroid(int count)
1005 if (!timestamp_elapsed(Next_asteroid_throw)) {
1009 if (Asteroid_throw_objnum == -1) {
1013 nprintf(("AI", "Incoming asteroids: %i\n", count));
1015 if (count > Max_incoming_asteroids[Game_skill_level])
1018 Next_asteroid_throw = timestamp(1000 + 1200 * count/(Game_skill_level+1));
1021 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1022 object *A = &Objects[so->objnum];
1023 if (so->objnum == Asteroid_throw_objnum) {
1024 int subtype = rand() % 3;
1025 while (Asteroid_field.field_debris_type[subtype] == -1) {
1026 subtype = (subtype + 1) % 3;
1028 object *objp = asteroid_create(&Asteroid_field, ASTEROID_TYPE_BIG, subtype);
1030 asteroid_aim_at_target(A, objp, ASTEROID_MIN_COLLIDE_TIME + frand() * 20.0f);
1032 // if asteroid is inside inner bound, kill it
1033 if (asteroid_in_inner_bound(&Asteroid_field, &objp->pos, 0.0f)) {
1034 objp->flags |= OF_SHOULD_BE_DEAD;
1036 Asteroids[objp->instance].target_objnum = so->objnum;
1037 // DA: 4/22/98 We get next line for free when new object (in obj_create_list) is merged.
1038 // this line gives too many collision pairs.
1039 // asteroid_update_collide(objp);
1041 if ( MULTIPLAYER_MASTER ) {
1042 send_asteroid_throw( objp );
1053 void asteroid_delete( object * obj )
1058 num = obj->instance;
1059 Assert( Asteroids[num].objnum == OBJ_INDEX(obj));
1061 asp = &Asteroids[num];
1063 Assert( Num_asteroids >= 0 );
1068 // Delete asteroid from Asteroid_used_list
1069 asteroid_obj_list_remove( obj );
1072 // See if we should reposition the asteroid. Only reposition if oustide the bounding volume and
1073 // the player isn't looking towards the asteroid.
1074 void asteroid_maybe_reposition(object *objp, asteroid_field *asfieldp)
1076 // passive field does not wrap
1077 if (asfieldp->field_type == FT_PASSIVE) {
1081 if ( asteroid_should_wrap(objp, asfieldp) ) {
1082 vector vec_to_asteroid, old_asteroid_pos, old_vel;
1085 old_asteroid_pos = objp->pos;
1086 old_vel = objp->phys_info.vel;
1088 //nprintf(("AI", "Frame %i, reposition #%i\n", Framecount, objp-Objects));
1090 // don't wrap asteroid if it is a target of some ship
1091 if ( !asteroid_is_targeted(objp) ) {
1093 // only wrap if player won't see asteroid disappear/reverse direction
1094 dist = vm_vec_normalized_dir(&vec_to_asteroid, &objp->pos, &Eye_position);
1095 dot = vm_vec_dot(&Eye_matrix.v.fvec, &vec_to_asteroid);
1097 if ((dot < 0.7f) || (dist > 3000.0f)) {
1098 if (Num_asteroids > MAX_ASTEROIDS-10) {
1099 objp->flags |= OF_SHOULD_BE_DEAD;
1101 // check to ensure player won't see asteroid appear either
1102 asteroid_wrap_pos(objp, asfieldp);
1103 Asteroids[objp->instance].target_objnum = -1;
1105 vm_vec_normalized_dir(&vec_to_asteroid, &objp->pos, &Eye_position);
1106 dot = vm_vec_dot(&Eye_matrix.v.fvec, &vec_to_asteroid);
1107 dist = vm_vec_dist_quick(&objp->pos, &Eye_position);
1109 if (( dot > 0.7f) && (dist < 3000.0f)) {
1110 // player would see asteroid pop out other side, so reverse velocity instead of wrapping
1111 objp->pos = old_asteroid_pos;
1112 vm_vec_copy_scale(&objp->phys_info.vel, &old_vel, -1.0f);
1113 objp->phys_info.desired_vel = objp->phys_info.vel;
1114 Asteroids[objp->instance].target_objnum = -1;
1117 // update last pos (after vel is known)
1118 vm_vec_scale_add(&objp->last_pos, &objp->pos, &objp->phys_info.vel, -flFrametime);
1120 asteroid_update_collide(objp);
1122 if ( MULTIPLAYER_MASTER )
1123 send_asteroid_throw( objp );
1130 void lerp(float *goal, float f1, float f2, float scale)
1132 *goal = (f2 - f1) * scale + f1;
1135 void asteroid_process_pre( object *objp, float frame_time)
1137 if (Asteroids_enabled) {
1140 v = &objp->phys_info.vel;
1141 vv = &objp->phys_info.desired_vel;
1143 //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));
1145 // Make vel chase desired_vel
1146 lerp(&objp->phys_info.vel.xyz.x, objp->phys_info.vel.xyz.x, objp->phys_info.desired_vel.xyz.x, flFrametime);
1147 lerp(&objp->phys_info.vel.xyz.y, objp->phys_info.vel.xyz.y, objp->phys_info.desired_vel.xyz.y, flFrametime);
1148 lerp(&objp->phys_info.vel.xyz.z, objp->phys_info.vel.xyz.z, objp->phys_info.desired_vel.xyz.z, flFrametime);
1153 #pragma warning ( push )
1154 #pragma warning ( disable : 4701 )
1156 int asteroid_check_collision(object *pasteroid, object *other_obj, vector *hitpos, collision_info_struct *asteroid_hit_info)
1158 if (!Asteroids_enabled) {
1163 int num, asteroid_subtype;
1165 Assert( pasteroid->type == OBJ_ASTEROID );
1167 num = pasteroid->instance;
1170 Assert( Asteroids[num].objnum == OBJ_INDEX(pasteroid));
1171 asteroid_subtype = Asteroids[num].asteroid_subtype;
1173 // asteroid_hit_info NULL -- asteroid-weapon collision
1174 if ( asteroid_hit_info == NULL ) {
1175 // asteroid weapon collision
1176 Assert( other_obj->type == OBJ_WEAPON );
1177 mc.model_num = Asteroid_info[Asteroids[num].type].model_num[asteroid_subtype]; // Fill in the model to check
1178 model_clear_instance( mc.model_num );
1179 mc.orient = &pasteroid->orient; // The object's orient
1180 mc.pos = &pasteroid->pos; // The object's position
1181 mc.p0 = &other_obj->last_pos; // Point 1 of ray to check
1182 mc.p1 = &other_obj->pos; // Point 2 of ray to check
1183 mc.flags = (MC_CHECK_MODEL);
1185 if (model_collide(&mc))
1186 *hitpos = mc.hit_point_world;
1191 // asteroid ship collision -- use asteroid_hit_info to calculate physics
1192 object *ship_obj = other_obj;
1193 Assert( ship_obj->type == OBJ_SHIP );
1195 object* heavy = asteroid_hit_info->heavy;
1196 object* light = asteroid_hit_info->light;
1197 object *heavy_obj = heavy;
1198 object *light_obj = light;
1200 vector zero, p0, p1;
1201 vm_vec_zero( &zero );
1202 vm_vec_sub( &p0, &light->last_pos, &heavy->last_pos );
1203 vm_vec_sub( &p1, &light->pos, &heavy->pos );
1205 mc.pos = &zero; // The object's position
1206 mc.p0 = &p0; // Point 1 of ray to check
1207 mc.p1 = &p1; // Point 2 of ray to check
1209 // find the light object's position in the heavy object's reference frame at last frame and also in this frame.
1210 vector p0_temp, p0_rotated;
1212 // Collision detection from rotation enabled if at rotaion is less than 30 degree in frame
1213 // This should account for all ships
1214 if ( (vm_vec_mag_squared( &heavy->phys_info.rotvel ) * flFrametime*flFrametime) < (PI*PI/36) ) {
1215 // collide_rotate calculate (1) start position and (2) relative velocity
1216 asteroid_hit_info->collide_rotate = 1;
1217 vm_vec_rotate( &p0_temp, &p0, &heavy->last_orient );
1218 vm_vec_unrotate( &p0_rotated, &p0_temp, &heavy->orient );
1219 mc.p0 = &p0_rotated; // Point 1 of ray to check
1220 vm_vec_sub( &asteroid_hit_info->light_rel_vel, &p1, &p0_rotated );
1221 vm_vec_scale( &asteroid_hit_info->light_rel_vel, 1/flFrametime );
1222 // HACK - this applies to big ships warping in/out of asteroid fields - not sure what it does
1223 if (vm_vec_mag(&asteroid_hit_info->light_rel_vel) > 300) {
1224 // nprintf(("Physics", "Asteroid type %d\n", Asteroids[asteroid_hit_info->light->instance].type));
1225 asteroid_hit_info->collide_rotate = 0;
1226 vm_vec_sub( &asteroid_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel );
1229 asteroid_hit_info->collide_rotate = 0;
1230 vm_vec_sub( &asteroid_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel );
1235 if ( asteroid_hit_info->heavy == ship_obj ) { // ship is heavier, so asteroid is sphere. Check sphere collision against ship poly model
1236 mc.model_num = Ships[ship_obj->instance].modelnum; // Fill in the model to check
1237 mc.orient = &ship_obj->orient; // The object's orient
1238 mc.radius = pasteroid->radius;
1239 mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
1241 // copy important data
1242 int copy_flags = mc.flags; // make a copy of start end positions of sphere in big ship RF
1243 vector copy_p0, copy_p1;
1247 // first test against the sphere - if this fails then don't do any submodel tests
1248 mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE;
1250 int submodel_list[MAX_ROTATING_SUBMODELS];
1251 int num_rotating_submodels = 0;
1254 ship_model_start(ship_obj);
1256 if (model_collide(&mc)) {
1258 // Set earliest hit time
1259 asteroid_hit_info->hit_time = FLT_MAX;
1261 // Do collision the cool new way
1262 if ( asteroid_hit_info->collide_rotate ) {
1263 // We collide with the sphere, find the list of rotating submodels and test one at a time
1264 model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj);
1266 // Get polymodel and turn off all rotating submodels, collide against only 1 at a time.
1267 pm = model_get(Ships[heavy_obj->instance].modelnum);
1269 // turn off all rotating submodels and test for collision
1270 for (int i=0; i<num_rotating_submodels; i++) {
1271 pm->submodel[submodel_list[i]].blown_off = 1;
1274 // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
1275 mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
1278 // check each submodel in turn
1279 for (int i=0; i<num_rotating_submodels; i++) {
1280 // turn on submodel for collision test
1281 pm->submodel[submodel_list[i]].blown_off = 0;
1283 // set angles for last frame (need to set to prev to get p0)
1284 angles copy_angles = pm->submodel[submodel_list[i]].angs;
1286 // find the start and end positions of the sphere in submodel RF
1287 pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs;
1288 world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos);
1290 pm->submodel[submodel_list[i]].angs = copy_angles;
1291 world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos);
1295 // mc.pos = zero // in submodel RF
1297 mc.orient = &vmd_identity_matrix;
1298 mc.submodel_num = submodel_list[i];
1300 if ( model_collide(&mc) ) {
1301 if ( mc.hit_dist < asteroid_hit_info->hit_time ) {
1304 // set up asteroid_hit_info common
1305 set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_ROT_HIT);
1307 // set up asteroid_hit_info for rotating submodel
1308 if (asteroid_hit_info->edge_hit == 0) {
1309 model_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1312 // find position in submodel RF of light object at collison
1313 vector int_light_pos, diff;
1314 vm_vec_sub(&diff, mc.p1, mc.p0);
1315 vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist);
1316 model_find_world_point(&asteroid_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero);
1319 // Don't look at this submodel again
1320 pm->submodel[submodel_list[i]].blown_off = 1;
1325 // Recover and do usual ship_ship collision, but without rotating submodels
1326 mc.flags = copy_flags;
1329 mc.orient = &heavy_obj->orient;
1331 // usual ship_ship collision test
1332 if ( model_collide(&mc) ) {
1333 // check if this is the earliest hit
1334 if (mc.hit_dist < asteroid_hit_info->hit_time) {
1337 set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1339 // get collision normal if not edge hit
1340 if (asteroid_hit_info->edge_hit == 0) {
1341 model_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel);
1344 // find position in submodel RF of light object at collison
1346 vm_vec_sub(&diff, mc.p1, mc.p0);
1347 vm_vec_scale_add(&asteroid_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1352 ship_model_stop( ship_obj );
1356 // Asteroid is heavier obj
1357 mc.model_num = Asteroid_info[Asteroids[num].type].model_num[asteroid_subtype]; // Fill in the model to check
1358 model_clear_instance( mc.model_num );
1359 mc.orient = &pasteroid->orient; // The object's orient
1360 mc.radius = model_get_core_radius( Ships[ship_obj->instance].modelnum );
1362 // check for collision between asteroid model and ship sphere
1363 mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE);
1365 mc_ret_val = model_collide(&mc);
1368 set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_NO_ROT_HIT);
1370 // set normal if not edge hit
1371 if ( !asteroid_hit_info->edge_hit ) {
1372 vm_vec_unrotate(&asteroid_hit_info->collision_normal, &mc.hit_normal, &heavy->orient);
1375 // find position in submodel RF of light object at collison
1377 vm_vec_sub(&diff, mc.p1, mc.p0);
1378 vm_vec_scale_add(&asteroid_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist);
1386 // SET PHYSICS PARAMETERS
1387 // already have (hitpos - heavy) and light_cm_pos
1388 // get heavy cm pos - already have light_cm_pos
1389 asteroid_hit_info->heavy_collision_cm_pos = zero;
1391 // get r_heavy and r_light
1392 asteroid_hit_info->r_heavy = asteroid_hit_info->hit_pos;
1393 vm_vec_sub(&asteroid_hit_info->r_light, &asteroid_hit_info->hit_pos, &asteroid_hit_info->light_collision_cm_pos);
1395 // set normal for edge hit
1396 if ( asteroid_hit_info->edge_hit ) {
1397 vm_vec_copy_normalize(&asteroid_hit_info->collision_normal, &asteroid_hit_info->r_light);
1398 vm_vec_negate(&asteroid_hit_info->collision_normal);
1402 vm_vec_add(hitpos, &asteroid_hit_info->heavy->pos, &asteroid_hit_info->r_heavy);
1411 #pragma warning ( pop )
1415 void asteroid_render(object * obj)
1417 if (Asteroids_enabled) {
1423 num = obj->instance;
1425 Assert((num >= 0) && (num < MAX_ASTEROIDS));
1426 asp = &Asteroids[num];
1428 Assert( asp->flags & AF_USED );
1430 model_clear_instance( Asteroid_info[asp->type].model_num[asp->asteroid_subtype]);
1431 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
1435 // Create a normalized vector generally in the direction from *hitpos to other_obj->pos
1436 void asc_get_relvec(vector *relvec, object *other_obj, vector *hitpos)
1438 vector tvec, rand_vec;
1441 vm_vec_normalized_dir(&tvec, &other_obj->pos, hitpos);
1443 // Try up to three times to get a good vector.
1444 while (count++ < 3) {
1445 vm_vec_rand_vec_quick(&rand_vec);
1446 vm_vec_add(relvec, &tvec, &rand_vec);
1447 float mag = vm_vec_mag_quick(relvec);
1448 if ((mag > 0.2f) && (mag < 1.7f))
1452 vm_vec_normalize_quick(relvec);
1455 // return multiplier on asteroid radius for fireball
1456 float asteroid_get_fireball_scale_multiplier(int num)
1458 if (Asteroids[num].flags & AF_USED) {
1460 switch(Asteroids[num].type) {
1461 case ASTEROID_TYPE_BIG:
1471 Int3(); // this should not happen. asteroid should be used.
1476 // create asteroid explosion
1477 // exit: expected time for explosion anim to last, in seconds
1478 float asteroid_create_explosion(object *objp)
1480 int fireball_objnum;
1481 float explosion_life, fireball_scale_multiplier;
1483 fireball_scale_multiplier = asteroid_get_fireball_scale_multiplier(objp->instance);
1485 fireball_objnum = fireball_create( &objp->pos, FIREBALL_ASTEROID, OBJ_INDEX(objp), objp->radius*fireball_scale_multiplier, 0, &objp->phys_info.vel );
1486 if ( fireball_objnum > -1 ) {
1487 explosion_life = fireball_lifeleft(&Objects[fireball_objnum]);
1489 explosion_life = 0.0f;
1492 return explosion_life;
1495 // play sound when asteroid explodes
1496 void asteriod_explode_sound(object *objp, int type, int play_loud)
1498 int sound_index = -1;
1499 float range_factor = 1.0f; // how many times sound should traver farther than normal
1502 case ASTEROID_TYPE_SMALL:
1503 case ASTEROID_TYPE_MEDIUM:
1505 case DEBRIS_TERRAN_SMALL:
1506 case DEBRIS_TERRAN_MEDIUM:
1507 case DEBRIS_VASUDAN_SMALL:
1508 case DEBRIS_VASUDAN_MEDIUM:
1509 case DEBRIS_SHIVAN_SMALL:
1510 case DEBRIS_SHIVAN_MEDIUM:
1512 sound_index = SND_ASTEROID_EXPLODE_SMALL;
1513 range_factor = 5.0f;
1516 case ASTEROID_TYPE_BIG:
1518 case DEBRIS_TERRAN_LARGE:
1519 case DEBRIS_VASUDAN_LARGE:
1520 case DEBRIS_SHIVAN_LARGE:
1522 sound_index = SND_ASTEROID_EXPLODE_BIG;
1523 range_factor = 10.0f;
1531 Assert(sound_index != -1);
1534 range_factor = 1.0f;
1537 snd_play_3d( &Snds[sound_index], &objp->pos, &Eye_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY, NULL, range_factor );
1540 // asteroid_do_area_effect()
1542 // Do the area effect for an asteroid exploding
1544 // input: asteroid_objp => object pointer to asteriod causing explosion
1545 void asteroid_do_area_effect(object *asteroid_objp)
1548 float damage, blast;
1551 asteroid_info *asip;
1553 asp = &Asteroids[asteroid_objp->instance];
1554 asip = &Asteroid_info[asp->type];
1556 if ( asip->damage <= 0 ) { // do a quick out if there is no damage to apply
1560 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1561 ship_objp = &Objects[so->objnum];
1563 // don't blast navbuoys
1564 if ( ship_get_SIF(ship_objp->instance) & SIF_NAVBUOY ) {
1568 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 )
1571 ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage );
1572 weapon_area_apply_blast(NULL, ship_objp, &asteroid_objp->pos, blast, 0);
1576 // Asteroid asteroid_obj was hit.
1577 // Apply damage. Maybe make it break into smaller asteroids.
1578 // input: asteroid_obj => pointer to asteroid object getting hit
1579 // other_obj => object that hit asteroid, can be NULL if asteroid hit by area effect
1580 // hitpos => world position asteroid was hit, can be NULL if hit by area effect
1581 // damage => amount of damage to apply to asteroid
1582 void asteroid_hit( object * asteroid_obj, object * other_obj, vector * hitpos, float damage )
1584 float explosion_life;
1587 asp = &Asteroids[asteroid_obj->instance];
1589 if (asteroid_obj->flags & OF_SHOULD_BE_DEAD){
1593 if ( MULTIPLAYER_MASTER ){
1594 send_asteroid_hit( asteroid_obj, other_obj, hitpos, damage );
1597 asteroid_obj->hull_strength -= damage;
1599 //nprintf(("AI", "Asteroid collided with %s, hull = %.2f\n", Object_type_names[other_obj->type], asteroid_obj->hull_strength));
1601 if (asteroid_obj->hull_strength < 0.0f) {
1602 if ( asp->final_death_time <= 0 ) {
1603 int play_loud_collision = 0;
1605 explosion_life = asteroid_create_explosion(asteroid_obj);
1606 if ( asp->collide_objnum == OBJ_INDEX(other_obj) ) {
1607 // play_loud_collision = 1;
1609 asteriod_explode_sound(asteroid_obj, asp->type, play_loud_collision);
1610 asteroid_do_area_effect(asteroid_obj);
1612 asp->final_death_time = timestamp( fl2i(explosion_life*1000.0f)/5 ); // Wait till 30% of vclip time before breaking the asteroid up.
1614 asp->death_hit_pos = *hitpos;
1616 asp->death_hit_pos = asteroid_obj->pos;
1617 // randomize hit pos a bit, otherwise we will get a NULL vector when trying to find direction to toss child asteroids
1619 vm_vec_rand_vec_quick(&rand_vec);
1620 vm_vec_add2(&asp->death_hit_pos, &rand_vec);
1623 } else if ( other_obj ) {
1624 if ( other_obj->type == OBJ_WEAPON ) {
1626 wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index];
1627 // If the weapon didn't play any impact animation, play custom asteroid impact animation
1628 if ( wip->impact_weapon_expl_index < 0 ) {
1629 particle_create( hitpos, &vmd_zero_vector, 0.0f, Asteroid_impact_explosion_radius, PARTICLE_BITMAP, Asteroid_impact_explosion_ani );
1634 // evaluate any relevant player scoring implications
1635 scoring_eval_hit(asteroid_obj,other_obj);
1638 // De-init asteroids, called from game_level_close()
1639 void asteroid_level_close()
1643 for (i=0; i<MAX_ASTEROIDS; i++) {
1644 if (Asteroids[i].flags & AF_USED) {
1645 Asteroids[i].flags &= ~AF_USED;
1646 Assert(Asteroids[i].objnum >=0 && Asteroids[i].objnum < MAX_OBJECTS);
1647 Objects[Asteroids[i].objnum].flags |= OF_SHOULD_BE_DEAD;
1651 Asteroid_field.num_initial_asteroids=0;
1654 DCF(asteroids,"Turns asteroids on/off")
1657 dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1658 if ( Dc_arg_type & ARG_TRUE )
1659 Asteroids_enabled = 1;
1660 else if ( Dc_arg_type & ARG_FALSE )
1661 Asteroids_enabled = 0;
1662 else if ( Dc_arg_type & ARG_NONE )
1663 Asteroids_enabled ^= 1;
1666 dc_printf( "Usage: asteroids [bool]\nTurns asteroid system on/off. If nothing passed, then toggles it.\n" );
1669 dc_printf( "asteroids are %s\n", (Asteroids_enabled?"ON":"OFF") );
1672 if ((old_asteroids_enabled == 0) && (Asteroids_enabled == 1)) {
1674 } else if ((old_asteroids_enabled == 1) && (Asteroids_enabled == 0)) {
1680 void hud_target_asteroid()
1683 int start_index = 0, end_index = MAX_ASTEROIDS;
1685 if (Player_ai->target_objnum != -1) {
1686 if (Objects[Player_ai->target_objnum].type == OBJ_ASTEROID) {
1687 start_index = Objects[Player_ai->target_objnum].instance+1;
1688 end_index = start_index-1;
1690 end_index = MAX_ASTEROIDS;
1695 while (i != end_index) {
1696 if (i == MAX_ASTEROIDS)
1699 if (Asteroids[i].flags & AF_USED) {
1700 Assert(Objects[Asteroids[i].objnum].type == OBJ_ASTEROID);
1701 set_target_objnum( Player_ai, Asteroids[i].objnum);
1709 // Return the number of active asteroids
1710 int asteroid_count()
1712 return Num_asteroids;
1715 // See if asteroid should split up. We delay splitting up to allow the explosion animation
1716 // to play for a bit.
1717 void asteroid_maybe_break_up(object *asteroid_obj)
1721 asp = &Asteroids[asteroid_obj->instance];
1723 if ( timestamp_elapsed(asp->final_death_time) ) {
1724 vector relvec, vfh, tvec;
1726 asteroid_obj->flags |= OF_SHOULD_BE_DEAD;
1728 // multiplayer clients won't go through the following code. asteroid_sub_create will send
1729 // a create packet to the client in the above named function
1730 if ( !MULTIPLAYER_CLIENT ) {
1732 switch (asp->type) {
1733 case ASTEROID_TYPE_SMALL:
1735 case ASTEROID_TYPE_MEDIUM:
1736 asc_get_relvec(&relvec, asteroid_obj, &asp->death_hit_pos);
1737 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &relvec);
1739 vm_vec_normalized_dir(&vfh, &asteroid_obj->pos, &asp->death_hit_pos);
1740 vm_vec_copy_scale(&tvec, &vfh, 2.0f);
1741 vm_vec_sub2(&tvec, &relvec);
1742 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &tvec);
1745 case ASTEROID_TYPE_BIG:
1746 asc_get_relvec(&relvec, asteroid_obj, &asp->death_hit_pos);
1747 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_MEDIUM, &relvec);
1749 vm_vec_normalized_dir(&vfh, &asteroid_obj->pos, &asp->death_hit_pos);
1750 vm_vec_copy_scale(&tvec, &vfh, 2.0f);
1751 vm_vec_sub2(&tvec, &relvec);
1752 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_MEDIUM, &tvec);
1754 while (frand() > 0.6f) {
1756 vm_vec_rand_vec_quick(&rvec);
1757 vm_vec_scale_add(&tvec2, &vfh, &rvec, 0.75f);
1758 asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &tvec2);
1763 // ship debris does not break up
1765 case DEBRIS_TERRAN_SMALL:
1766 case DEBRIS_TERRAN_MEDIUM:
1767 case DEBRIS_TERRAN_LARGE:
1768 case DEBRIS_VASUDAN_SMALL:
1769 case DEBRIS_VASUDAN_MEDIUM:
1770 case DEBRIS_VASUDAN_LARGE:
1771 case DEBRIS_SHIVAN_SMALL:
1772 case DEBRIS_SHIVAN_MEDIUM:
1773 case DEBRIS_SHIVAN_LARGE:
1782 asp->final_death_time = timestamp(-1);
1786 int asteroid_get_random_in_cone(vector *pos, vector *dir, float ang, int danger)
1789 vector asteroid_dir;
1791 // just pick the first asteroid which satisfies our condition
1792 for(idx=0; idx<Num_asteroids; idx++){
1793 vm_vec_sub(&asteroid_dir, &Objects[Asteroids[idx].objnum].pos, pos);
1794 vm_vec_normalize_quick(&asteroid_dir);
1796 // if it satisfies the condition
1797 if(vm_vec_dot(dir, &asteroid_dir) >= ang){
1805 void asteroid_test_collide(object *asteroid_obj, object *ship_obj, mc_info *mc)
1807 float asteroid_ray_dist;
1808 vector asteroid_fvec, terminus;
1810 // See if ray from asteroid intersects bounding box of escort ship
1811 asteroid_ray_dist = vm_vec_mag_quick(&asteroid_obj->phys_info.desired_vel) * ASTEROID_MIN_COLLIDE_TIME;
1812 asteroid_fvec = asteroid_obj->phys_info.desired_vel;
1814 if(IS_VEC_NULL(&asteroid_fvec)){
1815 terminus = asteroid_obj->pos;
1817 vm_vec_normalize(&asteroid_fvec);
1818 vm_vec_scale_add(&terminus, &asteroid_obj->pos, &asteroid_fvec, asteroid_ray_dist);
1821 Assert(ship_obj->type == OBJ_SHIP);
1823 ship_model_start(ship_obj);
1825 mc->model_num = Ships[ship_obj->instance].modelnum; // Fill in the model to check
1826 mc->orient = &ship_obj->orient; // The object's orientation
1827 mc->pos = &ship_obj->pos; // The object's position
1828 mc->p0 = &asteroid_obj->pos; // Point 1 of ray to check
1829 mc->p1 = &terminus; // Point 2 of ray to check
1830 // mc->flags = MC_CHECK_MODEL | MC_ONLY_BOUND_BOX;
1831 mc->flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;
1832 mc->radius = asteroid_obj->radius;
1836 ship_model_stop(ship_obj);
1839 // Return !0 is the asteroid will collide with the escort ship within ASTEROID_MIN_COLLIDE_TIME
1841 int asteroid_will_collide(object *asteroid_obj, object *escort_objp)
1845 asteroid_test_collide(asteroid_obj, escort_objp, &mc);
1847 if ( !mc.num_hits ) {
1854 // return !0 if we should warn about asteroid hitting ship, otherwise return 0
1855 int asteroid_valid_ship_to_warn_collide(ship *shipp)
1857 if ( !(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
1861 if ( shipp->flags & (SF_DYING|SF_DEPART_WARP) ) {
1865 if ( (shipp->team != Player_ship->team) || (Player_ship->team == TEAM_TRAITOR) ) {
1872 // See if asteroid will collide with a large ship on the escort list in the next
1873 // ASTEROID_MIN_COLLIDE_TIME seconds.
1874 void asteroid_update_collide_flag(object *asteroid_objp)
1876 int i, num_escorts, escort_objnum, will_collide=0;
1880 asp = &Asteroids[asteroid_objp->instance];
1881 asp->collide_objnum = -1;
1882 asp->collide_objsig = -1;
1884 // multiplayer dogfight
1885 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){
1889 num_escorts = hud_escort_num_ships_on_list();
1891 if ( num_escorts <= 0 ) {
1895 for ( i = 0; i < num_escorts; i++ ) {
1896 escort_objnum = hud_escort_return_objnum(i);
1897 if ( escort_objnum >= 0 ) {
1898 escort_shipp = &Ships[Objects[escort_objnum].instance];
1899 if ( asteroid_valid_ship_to_warn_collide(escort_shipp) ) {
1900 will_collide = asteroid_will_collide(asteroid_objp, &Objects[escort_objnum]);
1901 if ( will_collide ) {
1902 asp->collide_objnum = escort_objnum;
1903 asp->collide_objsig = Objects[escort_objnum].signature;
1910 // ensure that the collide objnum for the asteroid is still valid
1911 void asteroid_verify_collide_objnum(asteroid *asp)
1913 if ( asp->collide_objnum >= 0 ) {
1914 if ( Objects[asp->collide_objnum].signature != asp->collide_objsig ) {
1915 asp->collide_objnum = -1;
1916 asp->collide_objsig = -1;
1921 void asteroid_process_post(object * obj, float frame_time)
1923 if (Asteroids_enabled) {
1925 num = obj->instance;
1927 //Assert( Asteroids[num].objnum == objnum );
1928 asteroid *asp = &Asteroids[num];
1930 // Only wrap if active field
1931 if (Asteroid_field.field_type == FT_ACTIVE) {
1932 if ( timestamp_elapsed(asp->check_for_wrap) ) {
1933 asteroid_maybe_reposition(obj, &Asteroid_field);
1934 asp->check_for_wrap = timestamp(ASTEROID_CHECK_WRAP_TIMESTAMP);
1938 asteroid_verify_collide_objnum(asp);
1940 if ( timestamp_elapsed(asp->check_for_collide) ) {
1941 asteroid_update_collide_flag(obj);
1942 asp->check_for_collide = timestamp(ASTEROID_UPDATE_COLLIDE_TIMESTAMP);
1945 asteroid_maybe_break_up(obj);
1949 // return the object number that the asteroid is about to impact
1950 int asteroid_collide_objnum(object *asteroid_objp)
1952 return Asteroids[asteroid_objp->instance].collide_objnum;
1955 // return the time until the asteroid will impact its collide_objnum
1956 float asteroid_time_to_impact(object *asteroid_objp)
1958 float time=-1.0f, total_dist, speed;
1962 asp = &Asteroids[asteroid_objp->instance];
1964 if ( asp->collide_objnum < 0 ) {
1968 asteroid_test_collide(asteroid_objp, &Objects[asp->collide_objnum], &mc);
1970 if ( mc.num_hits ) {
1971 total_dist = vm_vec_dist(&mc.hit_point_world, &asteroid_objp->pos) - asteroid_objp->radius;
1972 if ( total_dist < 0 ) {
1975 speed = vm_vec_mag(&asteroid_objp->phys_info.vel);
1976 time = total_dist/speed;
1982 // read in a single asteroid section from asteroid.tbl
1983 void asteroid_parse_section()
1985 asteroid_info *asip;
1987 asip = &Asteroid_info[Num_asteroid_types];
1989 required_string("$Name:");
1990 stuff_string(asip->name, F_NAME, NULL);
1992 required_string( "$POF file1:" );
1993 stuff_string_white( asip->pof_files[0] );
1995 required_string( "$POF file2:" );
1996 stuff_string_white( asip->pof_files[1] );
1999 if ( (strstr(asip->name,"Asteroid") != NULL) || (strstr(asip->name, "asteroid") != NULL) ) {
2000 required_string( "$POF file3:" );
2001 stuff_string_white( asip->pof_files[2] );
2004 asip->num_detail_levels = 0;
2006 required_string("$Detail distance:");
2007 asip->num_detail_levels = stuff_int_list(asip->detail_distance, MAX_SHIP_DETAIL_LEVELS, RAW_INTEGER_TYPE);
2009 required_string("$Max Speed:");
2010 stuff_float(&asip->max_speed);
2012 required_string("$Expl inner rad:");
2013 stuff_float(&asip->inner_rad);
2015 required_string("$Expl outer rad:");
2016 stuff_float(&asip->outer_rad);
2018 required_string("$Expl damage:");
2019 stuff_float(&asip->damage);
2021 required_string("$Expl blast:");
2022 stuff_float(&asip->blast);
2024 required_string("$Hitpoints:");
2025 stuff_float(&asip->initial_hull_strength);
2028 // read in data from asteroid.tbl into Asteroid_info[] array
2029 void asteroid_parse_tbl()
2031 char impact_ani_file[FILESPEC_LENGTH];
2033 Num_asteroid_types = 0;
2035 // open localization
2038 read_file_text("asteroid.tbl");
2041 required_string("#Asteroid Types");
2043 while (required_string_either("#End","$Name:")) {
2044 Assert( Num_asteroid_types < MAX_DEBRIS_TYPES );
2045 asteroid_parse_section();
2046 Num_asteroid_types++;
2049 required_string("#End");
2051 // check all read in
2052 Assert(Num_asteroid_types == MAX_DEBRIS_TYPES);
2054 Asteroid_impact_explosion_ani = -1;
2055 required_string("$Impact Explosion:");
2056 stuff_string(impact_ani_file, F_NAME, NULL);
2057 if ( stricmp(impact_ani_file,NOX("none"))) {
2059 Asteroid_impact_explosion_ani = bm_load_animation( impact_ani_file, &num_frames, NULL, 1);
2062 required_string("$Impact Explosion Radius:");
2063 stuff_float(&Asteroid_impact_explosion_radius);
2065 // close localization
2069 // Return number of asteroids expected to collide with a ship.
2070 int count_incident_asteroids()
2072 object *asteroid_objp;
2077 for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
2078 if (asteroid_objp->type == OBJ_ASTEROID ) {
2079 asteroid *asp = &Asteroids[asteroid_objp->instance];
2081 if ( asp->target_objnum >= 0 ) {
2090 // Pick object to throw asteroids at.
2091 // Pick any capital or big ship inside the bounds of the asteroid field.
2092 int set_asteroid_throw_objnum()
2094 if (Asteroid_field.num_initial_asteroids < 1)
2100 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2101 ship_objp = &Objects[so->objnum];
2102 float radius = ship_objp->radius*2.0f;
2104 if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP)) {
2105 if (ship_objp->pos.xyz.x + radius > Asteroid_field.min_bound.xyz.x)
2106 if (ship_objp->pos.xyz.y + radius > Asteroid_field.min_bound.xyz.y)
2107 if (ship_objp->pos.xyz.z + radius > Asteroid_field.min_bound.xyz.z)
2108 if (ship_objp->pos.xyz.x - radius < Asteroid_field.max_bound.xyz.x)
2109 if (ship_objp->pos.xyz.y - radius < Asteroid_field.max_bound.xyz.y)
2110 if (ship_objp->pos.xyz.z - radius < Asteroid_field.max_bound.xyz.z)
2111 if (!asteroid_in_inner_bound(&Asteroid_field, &ship_objp->pos, radius))
2119 void asteroid_frame()
2121 if (Num_asteroids < 1)
2124 // Only throw if active field
2125 if (Asteroid_field.field_type == FT_PASSIVE) {
2129 Asteroid_throw_objnum = set_asteroid_throw_objnum();
2131 maybe_throw_asteroid(count_incident_asteroids());
2134 // Called once, at game start. Do any one-time initializations here
2135 void asteroid_init()
2137 asteroid_parse_tbl();
2140 // Draw brackets around on-screen asteroids that are about to collide, otherwise draw an offscreen indicator
2141 void asteroid_show_brackets()
2143 vertex asteroid_vertex;
2144 object *asteroid_objp, *player_target;
2147 // get pointer to player target, so we don't need to take OBJ_INDEX() of asteroid_objp to compare to target_objnum
2148 if ( Player_ai->target_objnum >= 0 ) {
2149 player_target = &Objects[Player_ai->target_objnum];
2151 player_target = NULL;
2154 for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
2155 if (asteroid_objp->type != OBJ_ASTEROID ) {
2159 asp = &Asteroids[asteroid_objp->instance];
2161 if ( asp->collide_objnum < 0 ) {
2165 if ( asteroid_objp == player_target ) {
2169 g3_rotate_vertex(&asteroid_vertex,&asteroid_objp->pos);
2170 g3_project_vertex(&asteroid_vertex);
2172 if (!(asteroid_vertex.flags & PF_OVERFLOW)) {
2173 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
2174 hud_show_brackets(asteroid_objp, &asteroid_vertex);
2177 // if asteroid is not on screen, draw an offscreen indicator
2178 if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) {
2179 if (asteroid_vertex.codes != 0) {
2181 // dist = vm_vec_dist_quick(&Player_obj->pos, &asteroid_objp->pos);
2182 dist = hud_find_target_distance( asteroid_objp, Player_obj );
2183 gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]);
2184 hud_draw_offscreen_indicator(&asteroid_vertex, &asteroid_objp->pos, dist);
2190 // target the closest danger asteroid to the player
2191 void asteroid_target_closest_danger()
2193 object *asteroid_objp, *closest_asteroid_objp = NULL;
2195 float dist, closest_dist = 999999.0f;
2197 for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
2198 if (asteroid_objp->type != OBJ_ASTEROID ) {
2202 asp = &Asteroids[asteroid_objp->instance];
2204 if ( asp->collide_objnum < 0 ) {
2208 dist = vm_vec_dist_quick(&Player_obj->pos, &asteroid_objp->pos);
2210 if ( dist < closest_dist ) {
2211 closest_dist = dist;
2212 closest_asteroid_objp = asteroid_objp;
2216 if ( closest_asteroid_objp ) {
2217 set_target_objnum( Player_ai, OBJ_INDEX(closest_asteroid_objp) );
2221 void asteroid_page_in()
2223 if (Asteroid_field.num_initial_asteroids > 0 ) {
2226 nprintf(( "Paging", "Paging in asteroids\n" ));
2229 // max of 3 possible debris field models
2230 for (i=0; i<3; i++) {
2231 asteroid_info *asip;
2233 if (Asteroid_field.debris_genre == DG_ASTEROID) {
2235 asip = &Asteroid_info[i];
2237 // ship debris - always full until empty
2238 if (Asteroid_field.field_debris_type[i] != -1) {
2239 asip = &Asteroid_info[Asteroid_field.field_debris_type[i]];
2246 for (k=0; k<3; k++) {
2248 // SHIP DEBRIS - use subtype 0
2249 if (Asteroid_field.debris_genre == DG_SHIP) {
2254 // ASTEROID DEBRIS - use subtype (Asteroid_field.field_debris_type[] != -1)
2255 if (Asteroid_field.field_debris_type[k] == -1) {
2260 asip->modelp[k] = model_get(asip->model_num[k]);
2263 for (j=0; j<asip->modelp[k]->n_textures; j++ ) {
2264 int bitmap_num = asip->modelp[k]->original_textures[j];
2267 // if we're in Glide (and maybe later with D3D), use nondarkening textures
2268 if ( bitmap_num > -1 ) {
2269 if(gr_screen.mode == GR_GLIDE){
2270 bm_page_in_nondarkening_texture( bitmap_num );
2272 bm_page_in_texture( bitmap_num );
2284 // stubbed out functions not used in the demo
2285 void asteroid_init() {}
2286 void asteroid_level_init() {}
2287 void asteroid_level_close() {}
2288 void asteroid_create_all() {}
2289 void asteroid_render( object *asteroid_objp ) {}
2290 void asteroid_delete( object *asteroid_objp ) {}
2291 void asteroid_process_pre( object *asteroid_objp, float frame_time) {}
2292 void asteroid_process_post( object *asteroid_objp, float frame_time) {}
2293 int asteroid_check_collision( object *asteroid_objp, object * other_obj, vector * hitpos, collision_info_struct *asteroid_hit_info ) {return 0;}
2294 void asteroid_hit( object *asteroid_objp, object *other_objp, vector *hitpos, float damage ) {}
2295 void asteroid_save_restore(CFILE *fp) {}
2296 int asteroid_count() {return 0;}
2297 int asteroid_collide_objnum(object *asteroid_objp) {return 0;}
2298 float asteroid_time_to_impact(object *asteroid_objp) {return 0.0f;}
2299 void asteroid_show_brackets() {}
2300 void asteroid_target_closest_danger() {}
2301 void asteroid_page_in() {}
2302 void asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec) {}
2303 void asteroid_frame() {}