]> icculus.org git repositories - taylor/freespace2.git/blob - src/fireball/fireballs.cpp
fix issue with looping audio streams
[taylor/freespace2.git] / src / fireball / fireballs.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/Fireball/FireBalls.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code to move, render and otherwise deal with fireballs.
16  *
17  * $Log$
18  * Revision 1.6  2004/09/20 01:31:44  theoddone33
19  * GCC 3.4 fixes.
20  *
21  * Revision 1.5  2004/07/04 11:31:43  taylor
22  * amd64 support, compiler warning fixes, don't use software rendering
23  *
24  * Revision 1.4  2002/06/17 06:33:08  relnev
25  * ryan's struct patch for gcc 2.95
26  *
27  * Revision 1.3  2002/06/09 04:41:16  relnev
28  * added copyright header
29  *
30  * Revision 1.2  2002/05/07 03:16:43  theoddone33
31  * The Great Newline Fix
32  *
33  * Revision 1.1.1.1  2002/05/03 03:28:08  root
34  * Initial import.
35  *
36  * 
37  * 19    9/14/99 1:27a Andsager
38  * Better LOD for fireballs when behind.  Move point ahead to get vertex
39  * ahead and then find size.
40  * 
41  * 18    9/12/99 11:42p Dave
42  * 
43  * 17    9/13/99 10:09a Andsager
44  * Add debug console commands to lower model render detail and fireball
45  * LOD for big ship explosiosns.
46  * 
47  * 16    9/09/99 8:53p Dave
48  * Fixed multiplayer degenerate orientation case problem. Make sure warp
49  * effect never goes lower than LOD 1. 
50  * 
51  * 15    9/07/99 12:20a Andsager
52  * LOD less agressive at lower hardware detail level
53  * 
54  * 14    9/06/99 6:47p Jamesa
55  * Fixed build error.
56  * 
57  * 13    9/06/99 3:23p Andsager
58  * Make fireball and weapon expl ani LOD choice look at resolution of the
59  * bitmap
60  * 
61  * 12    9/01/99 10:09a Dave
62  * Pirate bob.
63  * 
64  * 11    8/31/99 10:13p Andsager
65  * Add Knossos warp effect fireball
66  * 
67  * 10    8/30/99 9:59a Dave
68  * Removed explosion LOD stuff.
69  * 
70  * 9     8/28/99 4:54p Dave
71  * Fixed directives display for multiplayer clients for wings with
72  * multiple waves. Fixed hud threat indicator rendering color.
73  * 
74  * 8     8/27/99 9:07p Dave
75  * LOD explosions. Improved beam weapon accuracy.
76  * 
77  * 7     7/09/99 5:54p Dave
78  * Seperated cruiser types into individual types. Added tons of new
79  * briefing icons. Campaign screen.
80  * 
81  * 6     4/28/99 11:13p Dave
82  * Temporary checkin of artillery code.
83  * 
84  * 5     4/23/99 12:01p Johnson
85  * Added SIF_HUGE_SHIP
86  * 
87  * 4     11/05/98 5:55p Dave
88  * Big pass at reducing #includes
89  * 
90  * 3     10/23/98 3:51p Dave
91  * Full support for tstrings.tbl and foreign languages. All that remains
92  * is to make it active in Fred.
93  * 
94  * 2     10/07/98 10:52a Dave
95  * Initial checkin.
96  * 
97  * 1     10/07/98 10:48a Dave
98  * 
99  * 75    5/18/98 2:49p Lawrance
100  * Allow up to double instance of sound for warp holes
101  * 
102  * 74    5/15/98 3:54p John
103  * Added code so that only "perishable" fireballs get removed.
104  * 
105  * 73    4/15/98 9:42a Adam
106  * added 2 more explosion types (1, actually, but placeholder for 2)
107  * 
108  * 72    4/12/98 9:56a John
109  * Made lighting detail flags work.   Made explosions cast light on
110  * highest.
111  * 
112  * 71    4/09/98 7:58p John
113  * Cleaned up tmapper code a bit.   Put NDEBUG around some ndebug stuff.
114  * Took out XPARENT flag settings in all the alpha-blended texture stuff.
115  * 
116  * 70    3/31/98 5:11p John
117  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
118  * bunch of debug stuff out of player file.  Made model code be able to
119  * unload models and malloc out only however many models are needed.
120  * 
121  * 69    3/30/98 4:02p John
122  * Made machines with < 32 MB of RAM use every other frame of certain
123  * bitmaps.   Put in code to keep track of how much RAM we've malloc'd.
124  * 
125  * 68    3/29/98 12:39p John
126  * Made warp in glow page in
127  * 
128  * 67    3/27/98 2:17p Lawrance
129  * Don't play warphole closing for non-captial sized warp effects
130  * 
131  * 66    3/26/98 5:44p Lawrance
132  * take out debug comments for warphole sounds playing
133  * 
134  * 65    3/26/98 5:21p John
135  * Added new code to preload all bitmaps at the start of a level.
136  * Commented it out, though.
137  * 
138  * 64    3/25/98 5:30p Lawrance
139  * Fix bug that was not playing wormhole open sound effect at correct
140  * volume
141  * 
142  * 63    3/25/98 10:56a Lawrance
143  * Make a sound for warphole close as well as warphole open, move to
144  * fireball lib
145  * 
146  * 62    3/18/98 12:36p John
147  * Made hardware have nicer looking warp effect
148  * 
149  * 61    3/18/98 12:01p John
150  * Made warp grid grow faster
151  * 
152  * 60    2/26/98 10:07p Hoffoss
153  * Rewrote state saving and restoring to fix bugs and simplify the code.
154  * 
155  * 59    2/22/98 12:19p John
156  * Externalized some strings
157  * 
158  * 58    2/19/98 4:32p Lawrance
159  * Add asteroid explosion
160  * 
161  * 57    2/12/98 11:53p Lawrance
162  * comment out use of current_frame since not used anywhere
163  * 
164  * 56    2/05/98 9:21p John
165  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
166  * game.
167  * 
168  * 55    1/23/98 5:06p John
169  * Took L out of vertex structure used B (blue) instead.   Took all small
170  * fireballs out of fireball types and used particles instead.  Fixed some
171  * debris explosion things.  Restructured fireball code.   Restructured
172  * some lighting code.   Made dynamic lighting on by default. Made groups
173  * of lasers only cast one light.  Made fireballs not cast light.
174  * 
175  * 54    1/19/98 9:37p Allender
176  * Great Compiler Warning Purge of Jan, 1998.  Used pragma's in a couple
177  * of places since I was unsure of what to do with code.
178  * 
179  * 53    1/15/98 4:58p John
180  * Made warp effect use a looping ani.  Made the scaling up & down be in
181  * software.
182  * 
183  * 52    1/15/98 2:32p John
184  * Added code to optionally take a velocity for a fireball.
185  * 
186  * 51    1/11/98 2:15p John
187  * Changed a lot of stuff that had to do with bitmap loading.   Made cfile
188  * not do callbacks, I put that in global code.   Made only bitmaps that
189  * need to load for a level load.
190  * 
191  * 50    1/02/98 5:04p John
192  * Several explosion related changes.  Made fireballs not be used as
193  * ani's.  Made ship spark system expell particles.  Took away impact
194  * explosion for weapon hitting ship... this needs to get added to weapon
195  * info and makes shield hit more obvious.  Only make sparks when hit
196  * hull, not shields.
197  * 
198  * 49    12/31/97 4:48p John
199  * added some debug info
200  * 
201  * 48    12/30/97 6:44p John
202  * Made g3_Draw_bitmap functions account for aspect of bitmap.
203  * 
204  * 47    12/08/97 11:15a John
205  * added parameter to warpout for life.
206  * 
207  * 46    12/02/97 3:59p John
208  * Added first rev of thruster glow, along with variable levels of
209  * translucency, which retquired some restructing of palman.
210  * 
211  * 45    11/30/97 2:03p John
212  * made warp effect not use an ani for height map.
213  * 
214  * 44    11/16/97 12:20p John
215  * added code to make ships instantly warp in/out if fireball effect
216  * fails.   But, made it so the fireball effect will kill off old sparks
217  * so it can go, so this should never happen.
218  * 
219  * 43    11/16/97 10:48a John
220  * added code to remove the oldest fireball if there weren't enough slots
221  * for a new one.
222  * 
223  * 42    11/01/97 1:49p John
224  * added code to page fireballs in during level load.  Made player warpout
225  * use Adam's new camera movement pattern.  Make ships docked to warping
226  * out ships render correctly.
227  * 
228  * 41    10/29/97 5:05p John
229  * Changed dynamic lighting to only rotate and calculate lighting for
230  * point lights that are close to an object.  Changed lower framerate cap
231  * from 4 to .5.
232  * 
233  * 40    10/24/97 6:24p John
234  * added code to return the life left of a fireball
235  * 
236  * 39    9/14/97 4:49p Lawrance
237  * add some demo debugging code
238  * 
239  * 38    9/12/97 4:02p John
240  * put in ship warp out effect.
241  * put in dynamic lighting for warp in/out
242  * 
243  * 37    9/09/97 4:49p John
244  * Almost done ship warp in code
245  * 
246  * 36    9/08/97 8:39a John
247  * added in code structure for grid
248  * 
249  * 35    9/03/97 5:04p Lawrance
250  * add error checking when restoring structs
251  * 
252  * 34    9/03/97 4:32p John
253  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
254  * to bm_load functions not needed.   Made bmpman keep track of palettes
255  * for bitmaps not mapped into game palettes.
256  * 
257  * 33    8/29/97 2:26p John
258  * first rev of ship warp in effect.  Nothing more than a fireball inside
259  * of freespace, but textest.cpp contains the correct effect code that
260  * needs to be transferred into the game next.
261  * 
262  * 32    8/21/97 5:11p Lawrance
263  * frame numbering for ANI's now is from 0 -> total_frames-1.
264  * 
265  * 31    8/13/97 9:50p Allender
266  * split *_move into *_process_pre and *_process_post functions.
267  * process_pre functions called before object is moved.  _process_post
268  * functions called after object is moved.  Reordered code in ship_post
269  * and weapon_post for multiplayer
270  * 
271  * 30    8/13/97 12:06p Lawrance
272  * supporting multiple types of fireball explosions
273  * 
274  * 29    8/05/97 10:18a Lawrance
275  * my_rand() being used temporarily instead of rand()
276  * 
277  * 28    7/31/97 5:55p John
278  * made so you pass flags to obj_create.
279  * Added new collision code that ignores any pairs that will never
280  * collide.
281  * 
282  * 27    7/24/97 9:54a Lawrance
283  * don't free fireball system if not inited
284  * 
285  * 26    7/23/97 11:35a Lawrance
286  * ensure fireball animations are always freed properly
287  * 
288  * 25    7/21/97 11:41a Lawrance
289  * clean up fireballs at the end of each level
290  * 
291  * 24    7/20/97 6:58p Lawrance
292  * call anim_get_frame() to take advantage of key-framed animation
293  * 
294  * 23    7/11/97 3:57p John
295  * made fireballs translucent.  alpha=1.3x intesity. only for d3d.
296  * 
297  * 22    7/11/97 12:08p John
298  * made fireballs randomly be rotated.
299  * 
300  * 21    7/11/97 11:54a John
301  * added rotated 3d bitmaps.
302  * 
303  * 20    7/11/97 11:19a Lawrance
304  * fix mem leaks, move save code from State.cpp here
305  * 
306  * 19    7/10/97 1:51p Lawrance
307  * sorting anim fireballs
308  * 
309  * 18    6/24/97 6:21p John
310  * added detail flags.
311  * sped up motion debris system a bit.
312  * 
313  * 17    5/14/97 4:08p Lawrance
314  * removing my_index from game arrays
315  * 
316  * 16    5/05/97 10:40p Mike
317  * Make missile exhaust trails look a tad nicer.
318  * 
319  * 15    3/11/97 10:47p Mike
320  * Add a slew of secondary weapons.
321  * Support exhaust blobs.
322  * Add weapons that spawn weapons.
323  * Add remotely detonatable weapons.
324  * Add heat-seeking missiles.
325  * 
326  * 14    2/17/97 5:18p John
327  * Added a bunch of RCS headers to a bunch of old files that don't have
328  * them.
329  *
330  * $NoKeywords: $
331  */
332
333 #include <stdlib.h>
334
335 #include "vecmat.h"
336 #include "tmapper.h"
337 #include "2d.h"
338 #include "3d.h"
339 #include "bmpman.h"
340 #include "model.h"
341 #include "key.h"
342 #include "physics.h"
343 #include "floating.h"
344 #include "model.h"
345 #include "lighting.h"
346 #include "object.h"
347 #include "ship.h"
348 #include "systemvars.h"
349 #include "fireballs.h"
350 #include "linklist.h"
351 #include "gamesnd.h"
352 #include "localize.h"
353
354 #define WARPHOLE_GROW_TIME              (1.5f)  // time for warphole to reach max size (also time to shrink to nothing once it begins to shrink)
355
356 #define MAX_FIREBALL_LOD                                                4
357
358 typedef struct fireball_lod {
359         char    filename[MAX_FILENAME_LEN];
360         int     bitmap_id;
361         int     num_frames;
362         int     fps;
363 } fireball_lod;
364
365 typedef struct fireball_info    {
366         int                                     lod_count;      
367         fireball_lod            lod[4];
368 } fireball_info;
369
370 // flag values for fireball struct flags member
371 #define FBF_WARP_CLOSE_SOUND_PLAYED     (1<<0)
372 #define FBF_WARP_CAPTIAL_SIZE                   (1<<1)
373 #define FBF_WARP_CRUISER_SIZE                   (1<<2)
374
375 typedef struct fireball {                                       
376         int             objnum;                                 // If -1 this object is unused
377         int             fireball_info_index;    // Index into Fireball_info array
378         int             current_bitmap;
379         int             orient;                                 // For fireballs, which orientation.  For warps, 0 is warpin, 1 is warpout
380         int             flags;                                  // see #define FBF_*
381         char            lod;                                            // current LOD
382         float           time_elapsed;                   // in seconds
383         float           total_time;                             // total lifetime of animation in seconds
384 } fireball;
385
386 #define MAX_FIREBALLS   200
387
388 fireball Fireballs[MAX_FIREBALLS];
389
390 fireball_info Fireball_info[MAX_FIREBALL_TYPES];
391
392 int Num_fireballs = 0;
393
394 int fireballs_inited = 0;
395
396 int Warp_glow_bitmap = -1;
397
398 #define FB_INDEX(fb)    (fb-Fireballs)
399
400 // play warp in sound for warp effect
401 void fireball_play_warphole_open_sound(int ship_class, fireball *fb)
402 {
403         int             sound_index;
404         float           range_multiplier = 1.0f;
405         object  *fireball_objp; 
406                 
407         SDL_assert((fb != NULL) && (fb->objnum >= 0));
408         if((fb == NULL) || (fb->objnum < 0)){
409                 return;
410         }
411         fireball_objp = &Objects[fb->objnum];
412
413         sound_index = SND_WARP_IN;
414
415         if((ship_class >= 0) && (ship_class < Num_ship_types)){
416                 if ( Ship_info[ship_class].flags & SIF_HUGE_SHIP ) {
417                         sound_index = SND_CAPITAL_WARP_IN;
418                         fb->flags |= FBF_WARP_CAPTIAL_SIZE;
419                 } else if ( Ship_info[ship_class].flags & SIF_BIG_SHIP ) {
420                         range_multiplier = 6.0f;
421                         fb->flags |= FBF_WARP_CRUISER_SIZE;
422                 }
423         }
424
425         snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius, NULL, 0, 1.0f, SND_PRIORITY_DOUBLE_INSTANCE, NULL, range_multiplier); // play warp sound effect
426 }
427
428 // play warp out sound for warp effect
429 void fireball_play_warphole_close_sound(fireball *fb)
430 {
431         int     sound_index = SND_WARP_OUT;
432
433         object *fireball_objp;
434
435         fireball_objp = &Objects[fb->objnum];
436
437         if ( fb->flags & FBF_WARP_CAPTIAL_SIZE ) {
438                 sound_index = SND_CAPITAL_WARP_OUT;
439         } else {
440                 // AL 27-3-98: Decided that warphole closing is only required for captial ship sized warp effects.
441                 return;
442         }
443
444         snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius); // play warp sound effect
445 }
446
447 void fireball_parse_tbl()
448 {
449         int idx;
450         char base_filename[256] = "";
451
452         // open localization
453         lcl_ext_open();
454
455         try {
456                 read_file_text(NOX("fireball.tbl"));
457                 reset_parse();          
458
459                 int ntypes = 0;
460                 required_string("#Start");
461                 while (required_string_either("#End","$Name:")) {
462                         SDL_assert( ntypes < MAX_FIREBALL_TYPES);
463
464                         // base filename
465                         required_string("$Name:");
466                         stuff_string(base_filename, F_NAME, NULL);
467
468                         // # of lod levels - make sure old fireball.tbl is compatible
469                         Fireball_info[ntypes].lod_count = 1;
470                         if(optional_string("$LOD:")){
471                                 stuff_int(&Fireball_info[ntypes].lod_count);
472                         }
473
474                         // stuff default filename
475                         SDL_strlcpy(Fireball_info[ntypes].lod[0].filename, base_filename, SDL_arraysize(Fireball_info[0].lod[0].filename));
476
477                         // stuff LOD level filenames
478                         for(idx=1; idx<Fireball_info[ntypes].lod_count; idx++){
479                                 if(idx >= MAX_FIREBALL_LOD){
480                                         break;
481                                 }
482
483                                 SDL_snprintf(Fireball_info[ntypes].lod[idx].filename, MAX_FILENAME_LEN, "%s_%d", base_filename, idx);
484                         }
485
486                         ntypes++;
487                 }
488                 required_string("#End");
489         } catch (parse_error_t rval) {
490                 Error(LOCATION, "Unable to parse fireball.tbl!  Code = %i.\n", (int)rval);
491         }
492
493
494         // close localization
495         lcl_ext_close();
496 }
497
498
499 void fireball_load_data()
500 {
501         int                             i, idx;
502         fireball_info   *fd;
503
504         for ( i = 0; i < MAX_FIREBALL_TYPES; i++ ) {
505                 fd = &Fireball_info[i];
506
507                 for(idx=0; idx<fd->lod_count; idx++){
508                         fd->lod[idx].bitmap_id  = bm_load_animation( fd->lod[idx].filename, &fd->lod[idx].num_frames, &fd->lod[idx].fps, 1 );
509                         if ( fd->lod[idx].bitmap_id < 0 ) {
510                                 Error(LOCATION, "Could not load %s anim file\n", fd->lod[idx].filename);
511                         }
512                 }
513         } 
514
515         if ( Warp_glow_bitmap == -1 )   {
516                 Warp_glow_bitmap = bm_load( NOX("warpglow01") );
517         }       
518
519 }
520
521 void fireball_preload()
522 {
523         // Do nothing.  Called before level init, this used to page in warp effect.
524         // Not needed with new BmpMan system.
525 }
526
527 // This will get called at the start of each level.
528 void fireball_init()
529 {
530         int i;
531
532         if ( !fireballs_inited ) {
533                 fireballs_inited = 1;
534
535                 // Do all the processing that happens only once
536                 fireball_parse_tbl();
537                 fireball_load_data();
538         }
539         
540         // Reset everything between levels
541         Num_fireballs = 0;
542         for (i=0; i<MAX_FIREBALLS; i++ )        {
543                 Fireballs[i].objnum     = -1;
544         }
545
546
547 }
548
549 MONITOR( NumFireballsRend );    
550
551 //extern int tcache_hit(int);
552 //extern int Glide_expl_textures_in_frame;
553 //extern float Glide_avg_exp_text_per_sec;
554
555 void fireball_render(object * obj)
556 {
557         int             num;
558         vertex  p;
559         fireball        *fb;
560
561         MONITOR_INC( NumFireballsRend, 1 );     
562         
563         num = obj->instance;
564         fb = &Fireballs[num];
565
566         if ( Fireballs[num].current_bitmap < 0 )
567                 return;
568
569 //      gr_set_color( 0, 100, 0 );
570 //      g3_draw_sphere_ez( &obj->pos, obj->radius );
571 //      return;
572
573         // turn off fogging
574         if(The_mission.flags & MISSION_FLAG_FULLNEB){
575                 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
576         }
577
578         g3_rotate_vertex(&p, &obj->pos );
579
580         switch( fb->fireball_info_index )       {
581         
582                 case FIREBALL_EXPLOSION_MEDIUM:
583                         gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f, -1, -1 );
584                         g3_draw_bitmap(&p, fb->orient, obj->radius, TMAP_FLAG_TEXTURED );
585                         break;
586
587                 case FIREBALL_EXPLOSION_LARGE1:
588                 case FIREBALL_EXPLOSION_LARGE2:
589                 // case FIREBALL_EXPLOSION_LARGE3:
590                         /*
591                         if (!tcache_hit(Fireballs[num].current_bitmap)) {
592                                 // if we're over 200k exp vram upload this frame, change to lower lod
593                                 if (Glide_expl_textures_in_frame > 1024 * 200) {
594                                         // change fireball instance to lower LOD or don't draw
595
596                                         // get current LOD and number of LODs
597                                         int cur_lod = Fireballs[num].lod;
598                                         int num_lods = Fireball_info[Fireballs[num].fireball_info_index].lod_count;
599
600                                         if (num_lods > cur_lod+1) {
601                                                 // bump lod
602                                                 int frame = Fireballs[num].current_bitmap - Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].bitmap_id;
603                                                 Fireballs[num].lod++;
604                                                 Fireballs[num].current_bitmap = Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod+1].bitmap_id + frame;
605                                                 mprintf(("bumping down lod for fireball %s, frame %d\n", Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].filename, frame));
606                                         }
607                                 }
608                         }*/
609
610                 case FIREBALL_ASTEROID:
611                         // Make the big explosions rotate with the viewer.
612                         gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f, -1, -1 );
613                         g3_draw_rotated_bitmap(&p, (i2fl(fb->orient)*PI)/180.0f, obj->radius, TMAP_FLAG_TEXTURED );
614                         break;
615
616                 case FIREBALL_WARP_EFFECT:
617                 case FIREBALL_KNOSSOS_EFFECT: {
618                         
619                                 float percent_life = fb->time_elapsed / fb->total_time;
620
621                                 float rad;
622
623                                 // Code to make effect grow/shrink. 
624                                 float t = fb->time_elapsed;
625                         
626                                 if ( t < WARPHOLE_GROW_TIME )   {
627                                         rad = (float)pow(t/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
628                                         //rad = t*obj->radius/WARPHOLE_GROW_TIME;
629                                         //mprintf(( "T=%.2f, Rad = %.2f\n", t, rad ));
630                                 } else if ( t < fb->total_time - WARPHOLE_GROW_TIME )   {
631                                         rad = obj->radius;
632                                 } else {
633                                         rad = (float)pow((fb->total_time - t)/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
634                                         //rad = (fb->total_time - t )*obj->radius/WARPHOLE_GROW_TIME;
635                                 }
636                                 //rad = obj->radius;
637
638
639                                 warpin_render(&obj->orient, &obj->pos, Fireballs[num].current_bitmap, rad, percent_life, obj->radius );
640                         }
641                         break;
642
643                         
644                 default:
645                         Int3();
646         }
647 }
648
649 // -----------------------------------------------------------------
650 //      fireball_delete()
651 //
652 //      Delete a fireball.  Called by object_delete() code... do not call
653 // directly.
654 //
655 void fireball_delete( object * obj )
656 {
657         int     num;
658
659         num = obj->instance;
660
661         SDL_assert( Fireballs[num].objnum == OBJ_INDEX(obj));
662
663         Fireballs[num].objnum = -1;
664         Num_fireballs--;
665         SDL_assert( Num_fireballs >= 0 );
666 }
667
668 // -----------------------------------------------------------------
669 //      fireball_delete_all()
670 //
671 //      Delete all active fireballs, by calling obj_delete directly.
672 //
673 void fireball_delete_all()
674 {
675         fireball        *fb;
676         int             i;
677
678         for ( i = 0; i < MAX_FIREBALLS; i++ ) {
679                 fb = &Fireballs[i];
680                 if ( fb->objnum != -1 ) {
681                         obj_delete(fb->objnum);
682                 }
683         }
684 }
685
686 void fireball_set_framenum(int num)
687 {
688         int                             framenum;
689         fireball                        *fb;
690         fireball_info   *fd;
691         fireball_lod    *fl;
692
693         fb = &Fireballs[num];
694         fd = &Fireball_info[Fireballs[num].fireball_info_index];
695
696         // valid lod?
697         fl = NULL;
698         if((fb->lod >= 0) && (fb->lod < fd->lod_count)){
699                 fl = &Fireball_info[Fireballs[num].fireball_info_index].lod[(int)fb->lod];
700         }
701         if(fl == NULL){
702                 // argh
703                 return;
704         }
705
706         if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT )    {
707                 float total_time = i2fl(fl->num_frames) / fl->fps;      // in seconds
708
709                 framenum = fl2i(fb->time_elapsed * fl->num_frames / total_time + 0.5);
710
711                 if ( framenum < 0 ) framenum = 0;
712
713                 framenum = framenum % fl->num_frames;
714
715                 if ( fb->orient )       {
716                         // warp out effect plays backwards
717                         framenum = fl->num_frames-framenum-1;
718                         fb->current_bitmap = fl->bitmap_id + framenum;
719                 } else {
720                         fb->current_bitmap = fl->bitmap_id + framenum;
721                 }
722         } else {
723
724                 framenum = fl2i(fb->time_elapsed / fb->total_time * fl->num_frames + 0.5);
725
726                 // ensure we don't go past the number of frames of animation
727                 if ( framenum > (fl->num_frames-1) ) {
728                         framenum = (fl->num_frames-1);
729                         Objects[fb->objnum].flags |= OF_SHOULD_BE_DEAD;
730                 }
731
732                 if ( framenum < 0 ) framenum = 0;
733                 fb->current_bitmap = fl->bitmap_id + framenum;
734         }
735 }
736
737 int fireball_is_perishable(object * obj)
738 {
739         //      return 1;
740         int                     num;
741         fireball                *fb;
742
743         num = obj->instance;
744
745         SDL_assert( Fireballs[num].objnum == OBJ_INDEX(obj) );
746
747         fb = &Fireballs[num];
748
749         if ( fb->fireball_info_index == FIREBALL_EXPLOSION_MEDIUM )     
750                 return 1;
751
752         if ( !((fb->fireball_info_index == FIREBALL_WARP_EFFECT) || (fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)) )     {
753                 if ( !(obj->flags & OF_WAS_RENDERED))   {
754                         return 1;
755                 }
756         }
757
758         return 0;
759 }
760
761
762 // -----------------------------------------------------------------
763 //      fireball_free_one()
764 //
765 //      There are too many fireballs, so delete the oldest small one
766 // to free up a slot.  Returns the fireball slot freed.
767 //
768 int fireball_free_one()
769 {
770         fireball        *fb;
771         int             i;
772
773         int             oldest_objnum = -1, oldest_slotnum = -1;
774 //      float           lifeleft, oldest_lifeleft = 0.0f;
775
776         for ( i = 0; i < MAX_FIREBALLS; i++ ) {
777                 fb = &Fireballs[i];
778
779                 // only remove the ones that aren't warp effects
780                 if ( (fb->objnum>-1) && fireball_is_perishable(&Objects[fb->objnum]) )  {
781
782                 //      lifeleft = fb->total_time - fb->time_elapsed;
783                 //      if ( (oldest_objnum < 0) || (lifeleft < oldest_lifeleft) )      {
784                                 oldest_slotnum = i;
785                 //              oldest_lifeleft = lifeleft;
786                                 oldest_objnum = fb->objnum;
787                 //      }
788                         break;
789                 }
790         }
791
792         if ( oldest_objnum > -1 )       {
793                 obj_delete(oldest_objnum);
794         }
795         return oldest_slotnum;
796 }
797
798 // broke fireball_move into fireball_process_pre and fireball_process_post as was done
799 // with all *_move functions on 8/13 by Mike K. and Mark A.
800 void fireball_process_pre( object *objp, float frame_time)
801 {
802 }
803
804 int fireball_is_warp(object * obj)
805 {
806         int                     num;
807         fireball                *fb;
808
809         num = obj->instance;
810
811         SDL_assert( Fireballs[num].objnum == OBJ_INDEX(obj) );
812
813         fb = &Fireballs[num];
814
815         if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)     
816                 return 1;
817
818         return 0;
819 }
820
821 // mabye play sound effect for warp hole closing
822 void fireball_maybe_play_warp_close_sound(fireball *fb)
823 {
824         float life_left;
825
826         // If not a warphole fireball, do a quick out
827         if ( !(fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)) {
828                 return;
829         }
830
831         // If the warhole close sound has been played, don't play it again!
832         if ( fb->flags & FBF_WARP_CLOSE_SOUND_PLAYED ) {
833                 return;
834         }
835
836         life_left = fb->total_time - fb->time_elapsed;
837
838         if ( life_left < WARPHOLE_GROW_TIME ) {
839                 fireball_play_warphole_close_sound(fb);
840                 fb->flags |= FBF_WARP_CLOSE_SOUND_PLAYED;
841         }
842 }
843
844 MONITOR( NumFireballs );        
845
846 void fireball_process_post(object * obj, float frame_time)
847 {
848         int                     num;
849         fireball                *fb;
850
851         MONITOR_INC( NumFireballs, 1 ); 
852
853         num = obj->instance;
854
855         SDL_assert( Fireballs[num].objnum == OBJ_INDEX(obj) );
856
857         fb = &Fireballs[num];
858
859         fb->time_elapsed += frame_time;
860         if ( fb->time_elapsed > fb->total_time ) {
861                 obj->flags |= OF_SHOULD_BE_DEAD;
862         }
863
864         fireball_maybe_play_warp_close_sound(fb);
865
866         fireball_set_framenum(num);
867 }
868
869 // Returns life left of a fireball in seconds
870 float fireball_lifeleft( object *obj )
871 {
872         int                     num;
873         fireball                *fb;
874
875         num = obj->instance;
876
877         SDL_assert( Fireballs[num].objnum == OBJ_INDEX(obj) );
878
879         fb = &Fireballs[num];
880
881         return fb->total_time - fb->time_elapsed;
882 }
883
884 // Returns life left of a fireball in percent
885 float fireball_lifeleft_percent( object *obj )
886 {
887         int                     num;
888         fireball                *fb;
889
890         num = obj->instance;
891
892         SDL_assert( Fireballs[num].objnum == OBJ_INDEX(obj) );
893
894         fb = &Fireballs[num];
895
896         return (fb->total_time - fb->time_elapsed) / fb->total_time;
897 }
898
899 // determine LOD to use
900 int fireball_get_lod(vector *pos, fireball_info *fd, float size)
901 {
902         vertex v;
903         int x, y, w, h, bm_size;
904         int must_stop = 0;
905         int ret_lod = 1;
906         int behind = 0;
907
908         // bogus
909         if(fd == NULL){
910                 return 1;
911         }
912
913         // start the frame
914         extern float Viewer_zoom;
915         extern int G3_count;
916
917         if(!G3_count){
918                 g3_start_frame(1);
919                 must_stop = 1;
920         }
921         g3_set_view_matrix(&Eye_position, &Eye_matrix, Viewer_zoom);
922
923         // get extents of the rotated bitmap
924         g3_rotate_vertex(&v, pos);
925
926         // if vertex is behind, find size if in front, then drop down 1 LOD
927         if (v.codes & CC_BEHIND) {
928                 float dist = vm_vec_dist_quick(&Eye_position, pos);
929                 vector temp;
930
931                 behind = 1;
932                 vm_vec_scale_add(&temp, &Eye_position, &Eye_matrix.v.fvec, dist);
933                 g3_rotate_vertex(&v, &temp);
934
935                 // if still behind, bail and go with default
936                 if (v.codes & CC_BEHIND) {
937                         behind = 0;
938                 }
939         }
940
941         if(!g3_get_bitmap_dims(fd->lod[0].bitmap_id, &v, size, &x, &y, &w, &h, &bm_size)) {
942                 if (Detail.hardware_textures == 4) {
943                         // straight LOD
944                         if(w <= bm_size/8){
945                                 ret_lod = 3;
946                         } else if(w <= bm_size/2){
947                                 ret_lod = 2;
948                         } else if(w <= (1.56*bm_size)){
949                                 ret_lod = 1;
950                         } else {
951                                 ret_lod = 0;
952                         }               
953                 } else {
954                         // less aggressive LOD for lower detail settings
955                         if(w <= bm_size/8){
956                                 ret_lod = 3;
957                         } else if(w <= bm_size/3){
958                                 ret_lod = 2;
959                         } else if(w <= (1.2*bm_size)){
960                                 ret_lod = 1;
961                         } else {
962                                 ret_lod = 0;
963                         }               
964                 }
965         }
966
967         // if it's behind, bump up LOD by 1
968         if (behind) {
969                 ret_lod++;
970         }
971
972         // end the frame
973         if(must_stop){
974                 g3_end_frame();
975         }
976
977         // return the best lod
978         return SDL_min(ret_lod, fd->lod_count - 1);
979 }
980
981 //      Create a fireball, return object index.
982 int fireball_create( vector * pos, int fireball_type, int parent_obj, float size, int reverse, vector *velocity, float warp_lifetime, int ship_class, matrix *orient_override, int low_res)
983 {
984         int                             n, objnum, fb_lod;
985         object                  *obj;
986         fireball                        *fb;
987         fireball_info   *fd;
988         fireball_lod    *fl;
989
990         SDL_assert( fireball_type > -1 );
991         SDL_assert( fireball_type < MAX_FIREBALL_TYPES );
992
993         fd = &Fireball_info[fireball_type];
994
995         if ( !(Game_detail_flags & DETAIL_FLAG_FIREBALLS) )     {
996                 if ( !((fireball_type == FIREBALL_WARP_EFFECT) || (fireball_type == FIREBALL_KNOSSOS_EFFECT)) ) {
997                         return -1;
998                 }
999         }
1000
1001         if ( (Num_fireballs >= MAX_FIREBALLS) || (num_objects >= MAX_OBJECTS) ) {
1002                 // who cares if we don't create a spark.
1003                 // JAS - Should this code be in?  Is it better to remove an old spark
1004                 // and start a new one, or just not start the new one?
1005                 //if ( fd->type == FIREBALL_TYPE_SMALL )        {
1006                 //      return -1;
1007                 //}
1008
1009                 //mprintf(( "Out of fireball slots, trying to free one up!\n" ));
1010                 // out of slots, so free one up.
1011                 n = fireball_free_one();
1012                 if ( n < 0 )    {
1013                         // If there's still no free slots, then exit
1014                         //mprintf(( "ERROR: Couldn't free one up!!\n" ));
1015                         return -1;
1016                 } else {
1017                         //mprintf(( "Freed one up just fine!!\n" ));
1018                 }
1019         } else {
1020                 for ( n = 0; n < MAX_FIREBALLS; n++ )   {
1021                         if ( Fireballs[n].objnum < 0  ) {
1022                                 break;
1023                         }
1024                 }
1025                 SDL_assert( n != MAX_FIREBALLS );
1026         }
1027
1028         fb = &Fireballs[n];
1029
1030         // get an lod to use    
1031         fb_lod = fireball_get_lod(pos, fd, size);
1032
1033         // change lod if low res is desired
1034         if (low_res) {
1035                 fb_lod++;
1036                 fb_lod = SDL_min(fb_lod, fd->lod_count - 1);
1037         }
1038
1039         // if this is a warpout fireball, never go higher than LOD 1
1040         if(fireball_type == FIREBALL_WARP_EFFECT){
1041                 /*
1042                 if(fb_lod > 1){
1043                         fb_lod = 1;
1044                 }
1045                 */
1046                 fb_lod = 0;
1047         }
1048         fl = &fd->lod[fb_lod];
1049
1050         fb->lod = (char)fb_lod;
1051         fb->flags = 0;
1052         matrix orient;
1053         if(orient_override != NULL){
1054                 orient = *orient_override;
1055         } else {
1056                 if ( parent_obj < 0 )   {
1057                         orient = vmd_identity_matrix;
1058                 } else {
1059                         orient = Objects[parent_obj].orient;
1060                 }
1061         }
1062         
1063         objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, OF_RENDERS);
1064
1065         if (objnum < 0) {
1066                 Int3();                         // Get John, we ran out of objects for fireballs
1067                 return objnum;
1068         }
1069
1070         obj = &Objects[objnum];
1071
1072         fb->fireball_info_index = fireball_type;
1073         fb->time_elapsed = 0.0f;
1074         fb->objnum = objnum;
1075         fb->current_bitmap = -1;
1076         
1077         switch( fb->fireball_info_index )       {
1078
1079                 case FIREBALL_EXPLOSION_MEDIUM: 
1080                         fb->orient = (myrand()>>8) & 7;                                                 // 0 - 7
1081                         break;
1082
1083                 case FIREBALL_EXPLOSION_LARGE1:
1084                 case FIREBALL_EXPLOSION_LARGE2:
1085                 // case FIREBALL_EXPLOSION_LARGE3:
1086                 case FIREBALL_ASTEROID:
1087                         fb->orient = (myrand()>>8) % 360;                                               // 0 - 359
1088                         break;
1089
1090                 case FIREBALL_WARP_EFFECT:
1091                 case FIREBALL_KNOSSOS_EFFECT:
1092                         // Play sound effect for warp hole opening up
1093                         fireball_play_warphole_open_sound(ship_class, fb);
1094
1095                         // warp in type
1096                         if (reverse)    {
1097                                 fb->orient = 1;
1098                                 // if warp out, then reverse the orientation
1099                                 vm_vec_scale( &obj->orient.v.fvec, -1.0f );     // Reverse the forward vector
1100                                 vm_vec_scale( &obj->orient.v.rvec, -1.0f );     // Reverse the right vector
1101                         } else {
1102                                 fb->orient = 0;
1103                         }
1104                         break;
1105
1106                 default:
1107                         Int3();
1108                         break;
1109         }
1110
1111         if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT )    {
1112                 SDL_assert( warp_lifetime > 4.0f );             // Warp lifetime must be at least 4 seconds!
1113                 fb->total_time = warp_lifetime; // in seconds
1114         } else {
1115                 fb->total_time = i2fl(fl->num_frames) / fl->fps;        // in seconds
1116         }
1117         
1118         fireball_set_framenum(n);
1119
1120         if ( velocity ) {
1121                 // Make the explosion move at a constant velocity.
1122                 obj->flags |= OF_PHYSICS;
1123                 obj->phys_info.mass = 1.0f;
1124                 obj->phys_info.side_slip_time_const = 0.0f;
1125                 obj->phys_info.rotdamp = 0.0f;
1126                 obj->phys_info.vel = *velocity;
1127                 obj->phys_info.max_vel = *velocity;
1128                 obj->phys_info.desired_vel = *velocity;
1129                 obj->phys_info.speed = vm_vec_mag(velocity);
1130                 vm_vec_zero(&obj->phys_info.max_rotvel);
1131         }
1132         
1133         Num_fireballs++;
1134         return objnum;
1135 }
1136
1137 // -----------------------------------------------------------------
1138 //      fireball_close()
1139 //
1140 //      Called at game shutdown to clean up the fireball system
1141 //
1142 void fireball_close()
1143 {
1144         if ( !fireballs_inited )
1145                 return;
1146
1147         fireball_delete_all();
1148 }
1149
1150 // -----------------------------------------------------------------
1151 //      fireball_level_close()
1152 //
1153 //      Called when a mission ends... frees up any animations that might
1154 // be partially played
1155 //
1156 void fireball_level_close()
1157 {
1158         if ( !fireballs_inited )
1159                 return;
1160
1161         fireball_delete_all();
1162 }
1163
1164 void fireballs_page_in()
1165 {
1166         int                             i, idx;
1167         fireball_info   *fd;
1168         
1169         for ( i = 0; i < MAX_FIREBALL_TYPES ; i++ ) {
1170                 fd = &Fireball_info[i];
1171
1172                 for(idx=0; idx<fd->lod_count; idx++){
1173                         bm_page_in_texture( fd->lod[idx].bitmap_id, fd->lod[idx].num_frames );
1174                 }
1175         }
1176
1177         bm_page_in_texture( Warp_glow_bitmap );
1178
1179 }