]> icculus.org git repositories - taylor/freespace2.git/blob - src/weapon/shockwave.cpp
clean up stats saving a bit more
[taylor/freespace2.git] / src / weapon / shockwave.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/Weapon/Shockwave.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C file for creating and managing shockwaves
16  *
17  * $Log$
18  * Revision 1.4  2004/09/20 01:31:45  theoddone33
19  * GCC 3.4 fixes.
20  *
21  * Revision 1.3  2002/06/09 04:41:29  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:53  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:11  root
28  * Initial import.
29  *
30  * 
31  * 7     9/01/99 10:15a Dave
32  * 
33  * 6     7/18/99 12:32p Dave
34  * Randomly oriented shockwaves.
35  * 
36  * 5     3/23/99 2:29p Andsager
37  * Fix shockwaves for kamikazi and Fred defined.  Collect together
38  * shockwave_create_info struct.
39  * 
40  * 4     2/26/99 4:14p Dave
41  * Put in the ability to have multiple shockwaves for ships.
42  * 
43  * 3     11/05/98 5:55p Dave
44  * Big pass at reducing #includes
45  * 
46  * 2     10/07/98 10:54a Dave
47  * Initial checkin.
48  * 
49  * 1     10/07/98 10:51a Dave
50  * 
51  * 49    5/18/98 3:04p Lawrance
52  * Play shockwave impact sound
53  * 
54  * 48    5/18/98 12:59a Lawrance
55  * Replace shockwave impact sound with a new "whoosh" sound that
56  * originates from the shockwave center
57  * 
58  * 47    4/15/98 10:17p Mike
59  * Training mission #5.
60  * Fix application of subsystem damage.
61  * 
62  * 46    4/09/98 7:58p John
63  * Cleaned up tmapper code a bit.   Put NDEBUG around some ndebug stuff.
64  * Took out XPARENT flag settings in all the alpha-blended texture stuff.
65  * 
66  * 45    3/31/98 5:19p John
67  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
68  * bunch of debug stuff out of player file.  Made model code be able to
69  * unload models and malloc out only however many models are needed.
70  *  
71  * 
72  * 44    3/30/98 4:02p John
73  * Made machines with < 32 MB of RAM use every other frame of certain
74  * bitmaps.   Put in code to keep track of how much RAM we've malloc'd.
75  * 
76  * 43    3/26/98 5:26p John
77  * added new paging code. nonfunctional.
78  * 
79  * 42    3/04/98 4:11p Lawrance
80  * Have area effects affect asteroids, have asteroids cast an area effect,
81  * fix ship shockwaves
82  * 
83  * 41    2/26/98 10:08p Hoffoss
84  * Rewrote state saving and restoring to fix bugs and simplify the code.
85  * 
86  * 40    2/22/98 12:19p John
87  * Externalized some strings
88  * 
89  * 39    2/20/98 8:32p Lawrance
90  * Make shockwaves affect subsystems more realistically.
91  * 
92  * 38    2/16/98 11:26a Lawrance
93  * ensure shockwave lasts full duration
94  * 
95  * 37    2/16/98 10:04a Lawrance
96  * Fix broken shockwave damage.
97  * 
98  * 36    2/14/98 4:42p Lawrance
99  * pass shockwave object (not parent) to ship_apply_global_damage()
100  * 
101  * 35    2/12/98 11:54p Lawrance
102  * restructure rendering code to use an animation
103  * 
104  * 34    2/11/98 5:38p Dave
105  * Put in a global inited flag for shockwaves.
106  * 
107  * 33    2/02/98 8:47a Andsager
108  * Ship death area damage applied as instantaneous damage for small ships
109  * and shockwaves for large (>50 radius) ships.
110  * 
111  * 32    1/26/98 11:54a Lawrance
112  * Don't allow Navbuoys to be affected by area-effect damage and blasts.
113  * 
114  * 31    1/22/98 11:43p Lawrance
115  * Play sound effect when player is hit by shockwave.
116  * 
117  * 30    1/14/98 4:31p Dave
118  * Made shockwaves apply damage correctly.
119  * 
120  * 29    1/14/98 2:59p Allender
121  * if shockwave came from a weapon, make the shockwave's parent be the
122  * weapons parent.
123  * 
124  * 28    12/30/97 6:44p John
125  * Made g3_Draw_bitmap functions account for aspect of bitmap.
126  * 
127  * 27    12/02/97 4:00p John
128  * Added first rev of thruster glow, along with variable levels of
129  * translucency, which retquired some restructing of palman.
130  * 
131  * 26    11/29/97 2:06p John
132  * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
133  * like they used to incorrectly assume.   Added code to model to read in
134  * thruster radius's.
135  * 
136  * 25    11/19/97 10:20p Lawrance
137  * remove ship_shockwave from ship struct, handled in physics now
138  * 
139  * 24    11/16/97 8:52p Andsager
140  * For shockwaves, update physics damping info in physics code.
141  * 
142  * 23    9/18/97 10:48p Lawrance
143  * comment out unused struct member
144  * 
145  * 22    9/18/97 4:08p John
146  * Cleaned up & restructured ship damage stuff.
147  * 
148  * 21    9/17/97 5:12p John
149  * Restructured collision routines.  Probably broke a lot of stuff.
150  * 
151  * 20    9/04/97 5:10p Andsager
152  * implement physics using moment of inertia and mass (from BSPgen).
153  * Added to phys_info struct.  Updated ship_info, polymodel structs.
154  * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables
155  * 
156  * 19    9/03/97 4:33p John
157  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
158  * to bm_load functions not needed.   Made bmpman keep track of palettes
159  * for bitmaps not mapped into game palettes.
160  * 
161  * 18    8/26/97 3:31p Andsager
162  * scaled shockwave shake duration according to damage
163  * 
164  * 17    8/05/97 1:25a Mike
165  * Make ship death roll be shortened by more damage.
166  * 
167  * 16    7/31/97 5:55p John
168  * made so you pass flags to obj_create.
169  * Added new collision code that ignores any pairs that will never
170  * collide.
171  * 
172  * 15    7/25/97 4:30p Andsager
173  * Save shockwave info
174  * 
175  * 14    7/25/97 1:04p Andsager
176  * Modified physics flag PF_REDUCED_DAMP for damping when object is hit.
177  * Flag is now set in physics_apply_whack/shock and turned off in
178  * physics_sim_vel.  Weapons should not directly set this flag.
179  * 
180  * 13    7/22/97 2:40p Andsager
181  * shockwaves now cause proper rotation of ships
182  * 
183  * 12    7/20/97 7:01p Lawrance
184  * changed names of anim_ files to be more consistent
185  * 
186  * 11    7/18/97 10:52a Lawrance
187  * let player have some control when shockwave hits
188  * 
189  * 10    7/17/97 8:02p Lawrance
190  * tweaking shockwave effect
191  * 
192  * 9     7/16/97 5:51p Lawrance
193  * make shockwaves translucent
194  * 
195  * 8     7/16/97 4:00p Lawrance
196  * render shockwaves by default
197  * 
198  * 7     7/16/97 3:50p Lawrance
199  * render shockwaves first, to fake transparency
200  * 
201  * 6     7/16/97 2:52p Lawrance
202  * make shockwaves objects
203  * 
204  * 5     7/15/97 7:26p Lawrance
205  * make shockwave blast persist over time
206  * 
207  * 4     7/09/97 1:56p Lawrance
208  * add savegame support for shockwaves
209  * 
210  * 3     7/09/97 10:33a Lawrance
211  * make area-effect spheres translucent
212  * 
213  * 2     7/08/97 6:00p Lawrance
214  * implementing shockwaves
215  * 
216  * 1     7/08/97 1:30p Lawrance
217  *
218  * $NoKeywords: $
219  */
220
221 #include "2d.h"
222 #include        "3d.h"
223 #include "weapon.h"
224 #include "ship.h"
225 #include "freespace.h"  // for colors
226 #include "shockwave.h"
227 #include "timer.h"
228 #include "animplay.h"
229 #include "bmpman.h"
230 #include "linklist.h"
231 #include "shiphit.h"
232 #include "gamesnd.h"
233 #include "asteroid.h"
234
235 // -----------------------------------------------------------
236 // Data structures
237 // -----------------------------------------------------------
238
239 // -----------------------------------------------------------
240 // Module-wide globals
241 // -----------------------------------------------------------
242
243 static const char *Shockwave_filenames[MAX_SHOCKWAVE_TYPES] = 
244 {
245 //XSTR:OFF
246         "shockwave01"
247 //XSTR:ON
248 };
249
250 shockwave                       Shockwaves[MAX_SHOCKWAVES];
251 shockwave_info  Shockwave_info[MAX_SHOCKWAVE_TYPES];
252
253 shockwave                       Shockwave_list;
254 int                                     Shockwave_inited = 0;
255
256 // -----------------------------------------------------------
257 // Function macros
258 // -----------------------------------------------------------
259 #define SW_INDEX(sw) (sw-Shockwaves)
260         
261 // -----------------------------------------------------------
262 // Externals
263 // -----------------------------------------------------------
264 extern int Show_area_effect;
265
266 // ------------------------------------------------------------------------------------
267 // shockwave_create()
268 //
269 // Call to create a shockwave
270 //
271 //      input:  parent_objnum   => object number of object spawning the shockwave
272 //                              pos                             =>      vector specifing global position of shockwave center
273 //                              speed                           =>      speed at which shockwave expands (m/s)
274 //                              inner_radius    =>      radius at which damage applied is at maximum
275 //                              outer_radius    => damage decreases linearly to zero from inner_radius to
276 //                                                                              outer_radius.  Outside outer_radius, damage is 0.
277 //                              damage                  =>      the maximum damage (ie within inner_radius)
278 //                              blast                           => the maximux blast (within inner_radius)
279 //                              sw_flag                 => indicates whether shockwave is from weapon or ship explosion
280 //                              delay          => delay in ms before the shockwave actually starts
281 //
282 //      return: success                 =>      object number of shockwave
283 //                              failure                 =>      -1
284 //
285 int shockwave_create(int parent_objnum, vector *pos, shockwave_create_info *sci, int flag, int delay)
286 {
287         int                             i, objnum, real_parent;
288         shockwave               *sw;
289 //      shockwave_info  *si;
290         matrix                  orient;
291
292         for ( i = 0; i < MAX_SHOCKWAVES; i++ ) {
293                 if ( !(Shockwaves[i].flags & SW_USED) ){
294                         break;
295                 }
296         }
297
298         if ( i == MAX_SHOCKWAVES ) {
299                 return -1;
300         }
301
302         // real_parent is the guy who caused this shockwave to happen
303         if ( Objects[parent_objnum].type == OBJ_WEAPON ){
304                 real_parent = Objects[parent_objnum].parent;
305         } else {
306                 real_parent = parent_objnum;
307         }
308
309         sw = &Shockwaves[i];
310         sw->flags = (SW_USED | flag);
311         sw->speed = sci->speed;
312         sw->inner_radius = sci->inner_rad;
313         sw->outer_radius = sci->outer_rad;
314         sw->damage = sci->damage;
315         sw->blast = sci->blast;
316         sw->radius = 1.0f;
317         sw->pos = *pos;
318         sw->num_objs_hit = 0;
319         sw->shockwave_info_index=0;             // only one type for now... type could be passed is as a parameter
320         sw->current_bitmap=-1;
321
322         sw->time_elapsed=0.0f;
323         sw->delay_stamp = delay;
324
325         sw->rot_angle = sci->rot_angle;
326
327 //      si = &Shockwave_info[sw->shockwave_info_index];
328 //      sw->total_time = i2fl(si->num_frames) / si->fps;        // in seconds
329         sw->total_time = sw->outer_radius / sw->speed;
330
331         if ( Objects[parent_objnum].type == OBJ_WEAPON ) {              
332                 sw->weapon_info_index = Weapons[Objects[parent_objnum].instance].weapon_info_index;
333         }
334         else {          
335                 sw->weapon_info_index = -1;
336         }
337
338         orient = vmd_identity_matrix;
339
340         objnum = obj_create( OBJ_SHOCKWAVE, real_parent, i, &orient, &sw->pos, sw->outer_radius, OF_RENDERS );
341
342         if ( objnum == -1 ){
343                 Int3();
344         }
345
346         sw->objnum = objnum;
347
348         list_append(&Shockwave_list, sw);
349
350         return objnum;
351 }
352
353 // ------------------------------------------------------------------------------------
354 // shockwave_delete()
355 //
356 // Delete a shockwave
357 //
358 //      input:  object *objp    =>              pointer to shockwave object
359 //
360 void shockwave_delete(object *objp)
361 {
362         SDL_assert(objp->type == OBJ_SHOCKWAVE);
363         SDL_assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);
364
365         Shockwaves[objp->instance].flags = 0;
366         Shockwaves[objp->instance].objnum = -1; 
367         list_remove(&Shockwave_list, &Shockwaves[objp->instance]);
368 }
369
370 // ------------------------------------------------------------------------------------
371 // shockwave_delete_all()
372 //
373 //
374 void shockwave_delete_all()
375 {
376         shockwave       *sw, *next;
377         
378         sw = GET_FIRST(&Shockwave_list);
379         while ( sw != &Shockwave_list ) {
380                 next = sw->next;
381                 SDL_assert(sw->objnum != -1);
382                 Objects[sw->objnum].flags |= OF_SHOULD_BE_DEAD;
383                 sw = next;
384         }
385 }
386
387 // Set the correct frame of animation for the shockwave
388 void shockwave_set_framenum(int index)
389 {
390         int                             framenum;
391         shockwave               *sw;
392         shockwave_info  *si;
393
394         sw = &Shockwaves[index];
395         si = &Shockwave_info[sw->shockwave_info_index];
396
397         framenum = fl2i(sw->time_elapsed / sw->total_time * si->num_frames + 0.5);
398
399         // ensure we don't go past the number of frames of animation
400         if ( framenum > (si->num_frames-1) ) {
401                 framenum = (si->num_frames-1);
402                 Objects[sw->objnum].flags |= OF_SHOULD_BE_DEAD;
403         }
404
405         if ( framenum < 0 ) {
406                 framenum = 0;
407         }
408
409         sw->current_bitmap = si->bitmap_id + framenum;
410 }
411
412 // ------------------------------------------------------------------------------------
413 // shockwave_move()
414 //
415 //      Simulate a single shockwave.  If the shockwave radius exceeds outer_radius, then
416 // delete the shockwave.
417 //
418 //      input:          ojbp                    =>              object pointer that points to shockwave object
419 //                                      frametime       =>              time to simulate shockwave
420 //
421 void shockwave_move(object *shockwave_objp, float frametime)
422 {
423         shockwave       *sw;
424         object          *objp;
425         float                   blast,damage;
426         int                     i;
427         
428         SDL_assert(shockwave_objp->type == OBJ_SHOCKWAVE);
429         SDL_assert(shockwave_objp->instance  >= 0 && shockwave_objp->instance < MAX_SHOCKWAVES);
430         sw = &Shockwaves[shockwave_objp->instance];
431
432         // if the shockwave has a delay on it
433         if(sw->delay_stamp != -1){
434                 if(timestamp_elapsed(sw->delay_stamp)){
435                         sw->delay_stamp = -1;
436                 } else {
437                         return;
438                 }
439         }
440
441         sw->time_elapsed += frametime;
442 /*
443         if ( sw->time_elapsed > sw->total_time ) {
444                 shockwave_objp->flags |= OF_SHOULD_BE_DEAD;
445         }
446 */
447
448         shockwave_set_framenum(shockwave_objp->instance);
449                 
450         sw->radius += (frametime * sw->speed);
451         if ( sw->radius > sw->outer_radius ) {
452                 sw->radius = sw->outer_radius;
453                 shockwave_objp->flags |= OF_SHOULD_BE_DEAD;
454                 return;
455         }
456
457         // blast ships and asteroids
458         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
459                 if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) ) {
460                         continue;
461                 }
462         
463                 if ( objp->type == OBJ_SHIP ) {
464                         // don't blast navbuoys
465                         if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) {
466                                 continue;
467                         }
468                 }
469
470                 // only apply damage to a ship once from a shockwave
471                 for ( i = 0; i < sw->num_objs_hit; i++ ) {
472                         if ( objp->signature == sw->obj_sig_hitlist[i] ){
473                                 break;
474                         }
475                 }
476                 if ( i < sw->num_objs_hit ){
477                         continue;
478                 }
479
480                 if ( weapon_area_calc_damage(objp, &sw->pos, sw->inner_radius, sw->outer_radius, sw->blast, sw->damage, &blast, &damage, sw->radius) == -1 ){
481                         continue;
482                 }
483
484                 // okay, we have damage applied, record the object signature so we don't repeatedly apply damage
485                 SDL_assert(sw->num_objs_hit < SW_MAX_OBJS_HIT);
486                 if ( sw->num_objs_hit >= SW_MAX_OBJS_HIT) {
487                         sw->num_objs_hit--;
488                 }
489
490                 switch(objp->type) {
491                 case OBJ_SHIP:
492                         sw->obj_sig_hitlist[sw->num_objs_hit++] = objp->signature;
493                         ship_apply_global_damage(objp, shockwave_objp, &sw->pos, damage );
494                         weapon_area_apply_blast(NULL, objp, &sw->pos, blast, 1);
495                         break;
496                 case OBJ_ASTEROID:
497                         asteroid_hit(objp, NULL, NULL, damage);
498                         break;
499                 default:
500                         Int3();
501                         break;
502                 }
503
504
505                 // If this shockwave hit the player, play shockwave impact sound
506                 if ( objp == Player_obj ) {
507                         snd_play( &Snds[SND_SHOCKWAVE_IMPACT], 0.0f, SDL_max(0.4f, damage/Weapon_info[sw->weapon_info_index].damage) );
508                 }
509
510         }       // end for
511 }
512
513 // ------------------------------------------------------------------------------------
514 // shockwave_render()
515 //
516 //      Draw the shockwave identified by handle
517 //
518 //      input:  objp    =>              pointer to shockwave object
519 //
520 void shockwave_render(object *objp)
521 {
522         shockwave               *sw;
523         vertex                  p;
524
525         SDL_assert(objp->type == OBJ_SHOCKWAVE);
526         SDL_assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES);
527
528         sw = &Shockwaves[objp->instance];
529
530         if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){
531                 return;
532         }
533
534         if ( sw->current_bitmap < 0 ){
535                 return;
536         }
537
538         // turn off fogging
539         if(The_mission.flags & MISSION_FLAG_FULLNEB){
540                 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
541         }
542
543         g3_rotate_vertex(&p, &sw->pos );
544
545         gr_set_bitmap(sw->current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f, -1, -1);
546         g3_draw_rotated_bitmap(&p, fl_radian(sw->rot_angle), sw->radius, TMAP_FLAG_TEXTURED);   
547 }
548
549 // ------------------------------------------------------------------------------------
550 // shockwave_init()
551 //
552 // Call once at the start of each level (mission)
553 //
554 void shockwave_level_init()
555 {
556         int i;  
557         shockwave_info  *si;
558
559         // load in shockwaves
560         for ( i=0; i<MAX_SHOCKWAVE_TYPES; i++ ) {
561                 si = &Shockwave_info[i];
562                 si->bitmap_id   = bm_load_animation( Shockwave_filenames[i], &si->num_frames, &si->fps, 1 );
563                 if ( si->bitmap_id < 0 ) {
564                         Error(LOCATION, "Could not load %s anim file\n", Shockwave_filenames[i]);
565                 }
566         }
567         
568         list_init(&Shockwave_list);
569
570         for ( i = 0; i < MAX_SHOCKWAVES; i++ ) {
571                 Shockwaves[i].flags = 0;
572                 Shockwaves[i].objnum = -1;
573         }
574
575         Shockwave_inited = 1;
576 }
577
578 // ------------------------------------------------------------------------------------
579 // shockwave_level_close()
580 //
581 // Call at the close of each level (mission)
582 void shockwave_level_close()
583 {
584         if(Shockwave_inited){
585                 shockwave_delete_all();         
586         }
587         Shockwave_inited = 0;
588 }
589
590 // ------------------------------------------------------------------------------------
591 // shockwave_close()
592 //
593 //      Called at game-shutdown to 
594 //
595 void shockwave_close()
596 {
597 }
598
599 // ------------------------------------------------------------------------------------
600 // shockwave_move_all()
601 //
602 //      Simulate all shockwaves in Shockwave_list
603 //
604 //      input:  frametime       =>              time for last frame in ms
605 //
606 void shockwave_move_all(float frametime)
607 {
608         shockwave       *sw, *next;
609         
610         sw = GET_FIRST(&Shockwave_list);
611         while ( sw != &Shockwave_list ) {
612                 next = sw->next;
613                 SDL_assert(sw->objnum != -1);
614                 shockwave_move(&Objects[sw->objnum], frametime);
615                 sw = next;
616         }
617 }
618
619 // ------------------------------------------------------------------------------------
620 // shockwave_render_all()
621 //
622 //
623 void shockwave_render_all()
624 {
625         shockwave       *sw, *next;
626         
627         sw = GET_FIRST(&Shockwave_list);
628         while ( sw != &Shockwave_list ) {
629                 next = sw->next;
630                 SDL_assert(sw->objnum != -1);
631                 shockwave_render(&Objects[sw->objnum]);
632                 sw = next;
633         }
634 }
635
636 // return the weapon_info_index field for a shockwave
637 int shockwave_weapon_index(int index)
638 {
639         return Shockwaves[index].weapon_info_index;
640 }
641
642 // return the maximum radius for specified shockwave
643 float   shockwave_max_radius(int index)
644 {
645         return Shockwaves[index].outer_radius;
646 }
647
648 void shockwave_page_in()
649 {
650         int i;
651         shockwave_info  *si;
652
653         // load in shockwaves
654         for ( i=0; i<MAX_SHOCKWAVE_TYPES; i++ ) {
655                 si = &Shockwave_info[i];
656                 bm_page_in_texture( si->bitmap_id, si->num_frames );
657         }
658
659 }