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