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/Weapon/Shockwave.cpp $
15 * C file for creating and managing shockwaves
18 * Revision 1.3 2002/06/09 04:41:29 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:53 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:11 root
28 * 7 9/01/99 10:15a Dave
30 * 6 7/18/99 12:32p Dave
31 * Randomly oriented shockwaves.
33 * 5 3/23/99 2:29p Andsager
34 * Fix shockwaves for kamikazi and Fred defined. Collect together
35 * shockwave_create_info struct.
37 * 4 2/26/99 4:14p Dave
38 * Put in the ability to have multiple shockwaves for ships.
40 * 3 11/05/98 5:55p Dave
41 * Big pass at reducing #includes
43 * 2 10/07/98 10:54a Dave
46 * 1 10/07/98 10:51a Dave
48 * 49 5/18/98 3:04p Lawrance
49 * Play shockwave impact sound
51 * 48 5/18/98 12:59a Lawrance
52 * Replace shockwave impact sound with a new "whoosh" sound that
53 * originates from the shockwave center
55 * 47 4/15/98 10:17p Mike
56 * Training mission #5.
57 * Fix application of subsystem damage.
59 * 46 4/09/98 7:58p John
60 * Cleaned up tmapper code a bit. Put NDEBUG around some ndebug stuff.
61 * Took out XPARENT flag settings in all the alpha-blended texture stuff.
63 * 45 3/31/98 5:19p John
64 * Removed demo/save/restore. Made NDEBUG defined compile. Removed a
65 * bunch of debug stuff out of player file. Made model code be able to
66 * unload models and malloc out only however many models are needed.
69 * 44 3/30/98 4:02p John
70 * Made machines with < 32 MB of RAM use every other frame of certain
71 * bitmaps. Put in code to keep track of how much RAM we've malloc'd.
73 * 43 3/26/98 5:26p John
74 * added new paging code. nonfunctional.
76 * 42 3/04/98 4:11p Lawrance
77 * Have area effects affect asteroids, have asteroids cast an area effect,
80 * 41 2/26/98 10:08p Hoffoss
81 * Rewrote state saving and restoring to fix bugs and simplify the code.
83 * 40 2/22/98 12:19p John
84 * Externalized some strings
86 * 39 2/20/98 8:32p Lawrance
87 * Make shockwaves affect subsystems more realistically.
89 * 38 2/16/98 11:26a Lawrance
90 * ensure shockwave lasts full duration
92 * 37 2/16/98 10:04a Lawrance
93 * Fix broken shockwave damage.
95 * 36 2/14/98 4:42p Lawrance
96 * pass shockwave object (not parent) to ship_apply_global_damage()
98 * 35 2/12/98 11:54p Lawrance
99 * restructure rendering code to use an animation
101 * 34 2/11/98 5:38p Dave
102 * Put in a global inited flag for shockwaves.
104 * 33 2/02/98 8:47a Andsager
105 * Ship death area damage applied as instantaneous damage for small ships
106 * and shockwaves for large (>50 radius) ships.
108 * 32 1/26/98 11:54a Lawrance
109 * Don't allow Navbuoys to be affected by area-effect damage and blasts.
111 * 31 1/22/98 11:43p Lawrance
112 * Play sound effect when player is hit by shockwave.
114 * 30 1/14/98 4:31p Dave
115 * Made shockwaves apply damage correctly.
117 * 29 1/14/98 2:59p Allender
118 * if shockwave came from a weapon, make the shockwave's parent be the
121 * 28 12/30/97 6:44p John
122 * Made g3_Draw_bitmap functions account for aspect of bitmap.
124 * 27 12/02/97 4:00p John
125 * Added first rev of thruster glow, along with variable levels of
126 * translucency, which retquired some restructing of palman.
128 * 26 11/29/97 2:06p John
129 * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
130 * like they used to incorrectly assume. Added code to model to read in
133 * 25 11/19/97 10:20p Lawrance
134 * remove ship_shockwave from ship struct, handled in physics now
136 * 24 11/16/97 8:52p Andsager
137 * For shockwaves, update physics damping info in physics code.
139 * 23 9/18/97 10:48p Lawrance
140 * comment out unused struct member
142 * 22 9/18/97 4:08p John
143 * Cleaned up & restructured ship damage stuff.
145 * 21 9/17/97 5:12p John
146 * Restructured collision routines. Probably broke a lot of stuff.
148 * 20 9/04/97 5:10p Andsager
149 * implement physics using moment of inertia and mass (from BSPgen).
150 * Added to phys_info struct. Updated ship_info, polymodel structs.
151 * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables
153 * 19 9/03/97 4:33p John
154 * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani
155 * to bm_load functions not needed. Made bmpman keep track of palettes
156 * for bitmaps not mapped into game palettes.
158 * 18 8/26/97 3:31p Andsager
159 * scaled shockwave shake duration according to damage
161 * 17 8/05/97 1:25a Mike
162 * Make ship death roll be shortened by more damage.
164 * 16 7/31/97 5:55p John
165 * made so you pass flags to obj_create.
166 * Added new collision code that ignores any pairs that will never
169 * 15 7/25/97 4:30p Andsager
170 * Save shockwave info
172 * 14 7/25/97 1:04p Andsager
173 * Modified physics flag PF_REDUCED_DAMP for damping when object is hit.
174 * Flag is now set in physics_apply_whack/shock and turned off in
175 * physics_sim_vel. Weapons should not directly set this flag.
177 * 13 7/22/97 2:40p Andsager
178 * shockwaves now cause proper rotation of ships
180 * 12 7/20/97 7:01p Lawrance
181 * changed names of anim_ files to be more consistent
183 * 11 7/18/97 10:52a Lawrance
184 * let player have some control when shockwave hits
186 * 10 7/17/97 8:02p Lawrance
187 * tweaking shockwave effect
189 * 9 7/16/97 5:51p Lawrance
190 * make shockwaves translucent
192 * 8 7/16/97 4:00p Lawrance
193 * render shockwaves by default
195 * 7 7/16/97 3:50p Lawrance
196 * render shockwaves first, to fake transparency
198 * 6 7/16/97 2:52p Lawrance
199 * make shockwaves objects
201 * 5 7/15/97 7:26p Lawrance
202 * make shockwave blast persist over time
204 * 4 7/09/97 1:56p Lawrance
205 * add savegame support for shockwaves
207 * 3 7/09/97 10:33a Lawrance
208 * make area-effect spheres translucent
210 * 2 7/08/97 6:00p Lawrance
211 * implementing shockwaves
213 * 1 7/08/97 1:30p Lawrance
222 #include "freespace.h" // for colors
223 #include "shockwave.h"
225 #include "animplay.h"
227 #include "linklist.h"
230 #include "asteroid.h"
232 // -----------------------------------------------------------
234 // -----------------------------------------------------------
236 // -----------------------------------------------------------
237 // Module-wide globals
238 // -----------------------------------------------------------
240 static char *Shockwave_filenames[MAX_SHOCKWAVE_TYPES] =
247 shockwave Shockwaves[MAX_SHOCKWAVES];
248 shockwave_info Shockwave_info[MAX_SHOCKWAVE_TYPES];
250 shockwave Shockwave_list;
251 int Shockwave_inited = 0;
253 // -----------------------------------------------------------
255 // -----------------------------------------------------------
256 #define SW_INDEX(sw) (sw-Shockwaves)
258 // -----------------------------------------------------------
260 // -----------------------------------------------------------
261 extern int Show_area_effect;
263 // ------------------------------------------------------------------------------------
264 // shockwave_create()
266 // Call to create a shockwave
268 // input: parent_objnum => object number of object spawning the shockwave
269 // pos => vector specifing global position of shockwave center
270 // speed => speed at which shockwave expands (m/s)
271 // inner_radius => radius at which damage applied is at maximum
272 // outer_radius => damage decreases linearly to zero from inner_radius to
273 // outer_radius. Outside outer_radius, damage is 0.
274 // damage => the maximum damage (ie within inner_radius)
275 // blast => the maximux blast (within inner_radius)
276 // sw_flag => indicates whether shockwave is from weapon or ship explosion
277 // delay => delay in ms before the shockwave actually starts
279 // return: success => object number of shockwave
282 int shockwave_create(int parent_objnum, vector *pos, shockwave_create_info *sci, int flag, int delay)
284 int i, objnum, real_parent;
289 for ( i = 0; i < MAX_SHOCKWAVES; i++ ) {
290 if ( !(Shockwaves[i].flags & SW_USED) ){
295 if ( i == MAX_SHOCKWAVES ) {
299 // real_parent is the guy who caused this shockwave to happen
300 if ( Objects[parent_objnum].type == OBJ_WEAPON ){
301 real_parent = Objects[parent_objnum].parent;
303 real_parent = parent_objnum;
307 sw->flags = (SW_USED | flag);
308 sw->speed = sci->speed;
309 sw->inner_radius = sci->inner_rad;
310 sw->outer_radius = sci->outer_rad;
311 sw->damage = sci->damage;
312 sw->blast = sci->blast;
315 sw->num_objs_hit = 0;
316 sw->shockwave_info_index=0; // only one type for now... type could be passed is as a parameter
317 sw->current_bitmap=-1;
319 sw->time_elapsed=0.0f;
320 sw->delay_stamp = delay;
322 sw->rot_angle = sci->rot_angle;
324 si = &Shockwave_info[sw->shockwave_info_index];
325 // sw->total_time = i2fl(si->num_frames) / si->fps; // in seconds
326 sw->total_time = sw->outer_radius / sw->speed;
328 if ( Objects[parent_objnum].type == OBJ_WEAPON ) {
329 sw->weapon_info_index = Weapons[Objects[parent_objnum].instance].weapon_info_index;
332 sw->weapon_info_index = -1;
335 orient = vmd_identity_matrix;
337 objnum = obj_create( OBJ_SHOCKWAVE, real_parent, i, &orient, &sw->pos, sw->outer_radius, OF_RENDERS );
345 list_append(&Shockwave_list, sw);
350 // ------------------------------------------------------------------------------------
351 // shockwave_delete()
353 // Delete a shockwave
355 // input: object *objp => pointer to shockwave object
357 void shockwave_delete(object *objp)
359 Assert(objp->type == OBJ_SHOCKWAVE);
360 Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);
362 Shockwaves[objp->instance].flags = 0;
363 Shockwaves[objp->instance].objnum = -1;
364 list_remove(&Shockwave_list, &Shockwaves[objp->instance]);
367 // ------------------------------------------------------------------------------------
368 // shockwave_delete_all()
371 void shockwave_delete_all()
373 shockwave *sw, *next;
375 sw = GET_FIRST(&Shockwave_list);
376 while ( sw != &Shockwave_list ) {
378 Assert(sw->objnum != -1);
379 Objects[sw->objnum].flags |= OF_SHOULD_BE_DEAD;
384 // Set the correct frame of animation for the shockwave
385 void shockwave_set_framenum(int index)
391 sw = &Shockwaves[index];
392 si = &Shockwave_info[sw->shockwave_info_index];
394 framenum = fl2i(sw->time_elapsed / sw->total_time * si->num_frames + 0.5);
396 // ensure we don't go past the number of frames of animation
397 if ( framenum > (si->num_frames-1) ) {
398 framenum = (si->num_frames-1);
399 Objects[sw->objnum].flags |= OF_SHOULD_BE_DEAD;
402 if ( framenum < 0 ) {
406 sw->current_bitmap = si->bitmap_id + framenum;
409 // ------------------------------------------------------------------------------------
412 // Simulate a single shockwave. If the shockwave radius exceeds outer_radius, then
413 // delete the shockwave.
415 // input: ojbp => object pointer that points to shockwave object
416 // frametime => time to simulate shockwave
418 void shockwave_move(object *shockwave_objp, float frametime)
425 Assert(shockwave_objp->type == OBJ_SHOCKWAVE);
426 Assert(shockwave_objp->instance >= 0 && shockwave_objp->instance < MAX_SHOCKWAVES);
427 sw = &Shockwaves[shockwave_objp->instance];
429 // if the shockwave has a delay on it
430 if(sw->delay_stamp != -1){
431 if(timestamp_elapsed(sw->delay_stamp)){
432 sw->delay_stamp = -1;
438 sw->time_elapsed += frametime;
440 if ( sw->time_elapsed > sw->total_time ) {
441 shockwave_objp->flags |= OF_SHOULD_BE_DEAD;
445 shockwave_set_framenum(shockwave_objp->instance);
447 sw->radius += (frametime * sw->speed);
448 if ( sw->radius > sw->outer_radius ) {
449 sw->radius = sw->outer_radius;
450 shockwave_objp->flags |= OF_SHOULD_BE_DEAD;
454 // blast ships and asteroids
455 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
456 if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) ) {
460 if ( objp->type == OBJ_SHIP ) {
461 // don't blast navbuoys
462 if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) {
467 // only apply damage to a ship once from a shockwave
468 for ( i = 0; i < sw->num_objs_hit; i++ ) {
469 if ( objp->signature == sw->obj_sig_hitlist[i] ){
473 if ( i < sw->num_objs_hit ){
477 if ( weapon_area_calc_damage(objp, &sw->pos, sw->inner_radius, sw->outer_radius, sw->blast, sw->damage, &blast, &damage, sw->radius) == -1 ){
481 // okay, we have damage applied, record the object signature so we don't repeatedly apply damage
482 Assert(sw->num_objs_hit < SW_MAX_OBJS_HIT);
483 if ( sw->num_objs_hit >= SW_MAX_OBJS_HIT) {
489 sw->obj_sig_hitlist[sw->num_objs_hit++] = objp->signature;
490 ship_apply_global_damage(objp, shockwave_objp, &sw->pos, damage );
491 weapon_area_apply_blast(NULL, objp, &sw->pos, blast, 1);
494 asteroid_hit(objp, NULL, NULL, damage);
502 // If this shockwave hit the player, play shockwave impact sound
503 if ( objp == Player_obj ) {
504 snd_play( &Snds[SND_SHOCKWAVE_IMPACT], 0.0f, max(0.4f, damage/Weapon_info[sw->weapon_info_index].damage) );
510 // ------------------------------------------------------------------------------------
511 // shockwave_render()
513 // Draw the shockwave identified by handle
515 // input: objp => pointer to shockwave object
517 void shockwave_render(object *objp)
523 Assert(objp->type == OBJ_SHOCKWAVE);
524 Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);
526 sw = &Shockwaves[objp->instance];
527 si = &Shockwave_info[sw->shockwave_info_index];
529 if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){
533 if ( sw->current_bitmap < 0 ){
538 if(The_mission.flags & MISSION_FLAG_FULLNEB){
539 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
542 g3_rotate_vertex(&p, &sw->pos );
544 gr_set_bitmap(sw->current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f );
545 g3_draw_rotated_bitmap(&p, fl_radian(sw->rot_angle), sw->radius, TMAP_FLAG_TEXTURED);
548 // ------------------------------------------------------------------------------------
551 // Call once at the start of each level (mission)
553 void shockwave_level_init()
558 // load in shockwaves
559 for ( i=0; i<MAX_SHOCKWAVE_TYPES; i++ ) {
560 si = &Shockwave_info[i];
561 si->bitmap_id = bm_load_animation( Shockwave_filenames[i], &si->num_frames, &si->fps, 1 );
562 if ( si->bitmap_id < 0 ) {
563 Error(LOCATION, "Could not load %s anim file\n", Shockwave_filenames[i]);
567 list_init(&Shockwave_list);
569 for ( i = 0; i < MAX_SHOCKWAVES; i++ ) {
570 Shockwaves[i].flags = 0;
571 Shockwaves[i].objnum = -1;
574 Shockwave_inited = 1;
577 // ------------------------------------------------------------------------------------
578 // shockwave_level_close()
580 // Call at the close of each level (mission)
581 void shockwave_level_close()
583 if(Shockwave_inited){
584 shockwave_delete_all();
586 Shockwave_inited = 0;
589 // ------------------------------------------------------------------------------------
592 // Called at game-shutdown to
594 void shockwave_close()
598 // ------------------------------------------------------------------------------------
599 // shockwave_move_all()
601 // Simulate all shockwaves in Shockwave_list
603 // input: frametime => time for last frame in ms
605 void shockwave_move_all(float frametime)
607 shockwave *sw, *next;
609 sw = GET_FIRST(&Shockwave_list);
610 while ( sw != &Shockwave_list ) {
612 Assert(sw->objnum != -1);
613 shockwave_move(&Objects[sw->objnum], frametime);
618 // ------------------------------------------------------------------------------------
619 // shockwave_render_all()
622 void shockwave_render_all()
624 shockwave *sw, *next;
626 sw = GET_FIRST(&Shockwave_list);
627 while ( sw != &Shockwave_list ) {
629 Assert(sw->objnum != -1);
630 shockwave_render(&Objects[sw->objnum]);
635 // return the weapon_info_index field for a shockwave
636 int shockwave_weapon_index(int index)
638 return Shockwaves[index].weapon_info_index;
641 // return the maximum radius for specified shockwave
642 float shockwave_max_radius(int index)
644 return Shockwaves[index].outer_radius;
647 void shockwave_page_in()
652 // load in shockwaves
653 for ( i=0; i<MAX_SHOCKWAVE_TYPES; i++ ) {
654 si = &Shockwave_info[i];
655 bm_page_in_texture( si->bitmap_id, si->num_frames );