]> icculus.org git repositories - taylor/freespace2.git/blob - src/fireball/fireballs.cpp
ryan's struct patch for gcc 2.95
[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.4  2002/06/17 06:33:08  relnev
19  * ryan's struct patch for gcc 2.95
20  *
21  * Revision 1.3  2002/06/09 04:41:16  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:43  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:08  root
28  * Initial import.
29  *
30  * 
31  * 19    9/14/99 1:27a Andsager
32  * Better LOD for fireballs when behind.  Move point ahead to get vertex
33  * ahead and then find size.
34  * 
35  * 18    9/12/99 11:42p Dave
36  * 
37  * 17    9/13/99 10:09a Andsager
38  * Add debug console commands to lower model render detail and fireball
39  * LOD for big ship explosiosns.
40  * 
41  * 16    9/09/99 8:53p Dave
42  * Fixed multiplayer degenerate orientation case problem. Make sure warp
43  * effect never goes lower than LOD 1. 
44  * 
45  * 15    9/07/99 12:20a Andsager
46  * LOD less agressive at lower hardware detail level
47  * 
48  * 14    9/06/99 6:47p Jamesa
49  * Fixed build error.
50  * 
51  * 13    9/06/99 3:23p Andsager
52  * Make fireball and weapon expl ani LOD choice look at resolution of the
53  * bitmap
54  * 
55  * 12    9/01/99 10:09a Dave
56  * Pirate bob.
57  * 
58  * 11    8/31/99 10:13p Andsager
59  * Add Knossos warp effect fireball
60  * 
61  * 10    8/30/99 9:59a Dave
62  * Removed explosion LOD stuff.
63  * 
64  * 9     8/28/99 4:54p Dave
65  * Fixed directives display for multiplayer clients for wings with
66  * multiple waves. Fixed hud threat indicator rendering color.
67  * 
68  * 8     8/27/99 9:07p Dave
69  * LOD explosions. Improved beam weapon accuracy.
70  * 
71  * 7     7/09/99 5:54p Dave
72  * Seperated cruiser types into individual types. Added tons of new
73  * briefing icons. Campaign screen.
74  * 
75  * 6     4/28/99 11:13p Dave
76  * Temporary checkin of artillery code.
77  * 
78  * 5     4/23/99 12:01p Johnson
79  * Added SIF_HUGE_SHIP
80  * 
81  * 4     11/05/98 5:55p Dave
82  * Big pass at reducing #includes
83  * 
84  * 3     10/23/98 3:51p Dave
85  * Full support for tstrings.tbl and foreign languages. All that remains
86  * is to make it active in Fred.
87  * 
88  * 2     10/07/98 10:52a Dave
89  * Initial checkin.
90  * 
91  * 1     10/07/98 10:48a Dave
92  * 
93  * 75    5/18/98 2:49p Lawrance
94  * Allow up to double instance of sound for warp holes
95  * 
96  * 74    5/15/98 3:54p John
97  * Added code so that only "perishable" fireballs get removed.
98  * 
99  * 73    4/15/98 9:42a Adam
100  * added 2 more explosion types (1, actually, but placeholder for 2)
101  * 
102  * 72    4/12/98 9:56a John
103  * Made lighting detail flags work.   Made explosions cast light on
104  * highest.
105  * 
106  * 71    4/09/98 7:58p John
107  * Cleaned up tmapper code a bit.   Put NDEBUG around some ndebug stuff.
108  * Took out XPARENT flag settings in all the alpha-blended texture stuff.
109  * 
110  * 70    3/31/98 5:11p John
111  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
112  * bunch of debug stuff out of player file.  Made model code be able to
113  * unload models and malloc out only however many models are needed.
114  * 
115  * 69    3/30/98 4:02p John
116  * Made machines with < 32 MB of RAM use every other frame of certain
117  * bitmaps.   Put in code to keep track of how much RAM we've malloc'd.
118  * 
119  * 68    3/29/98 12:39p John
120  * Made warp in glow page in
121  * 
122  * 67    3/27/98 2:17p Lawrance
123  * Don't play warphole closing for non-captial sized warp effects
124  * 
125  * 66    3/26/98 5:44p Lawrance
126  * take out debug comments for warphole sounds playing
127  * 
128  * 65    3/26/98 5:21p John
129  * Added new code to preload all bitmaps at the start of a level.
130  * Commented it out, though.
131  * 
132  * 64    3/25/98 5:30p Lawrance
133  * Fix bug that was not playing wormhole open sound effect at correct
134  * volume
135  * 
136  * 63    3/25/98 10:56a Lawrance
137  * Make a sound for warphole close as well as warphole open, move to
138  * fireball lib
139  * 
140  * 62    3/18/98 12:36p John
141  * Made hardware have nicer looking warp effect
142  * 
143  * 61    3/18/98 12:01p John
144  * Made warp grid grow faster
145  * 
146  * 60    2/26/98 10:07p Hoffoss
147  * Rewrote state saving and restoring to fix bugs and simplify the code.
148  * 
149  * 59    2/22/98 12:19p John
150  * Externalized some strings
151  * 
152  * 58    2/19/98 4:32p Lawrance
153  * Add asteroid explosion
154  * 
155  * 57    2/12/98 11:53p Lawrance
156  * comment out use of current_frame since not used anywhere
157  * 
158  * 56    2/05/98 9:21p John
159  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
160  * game.
161  * 
162  * 55    1/23/98 5:06p John
163  * Took L out of vertex structure used B (blue) instead.   Took all small
164  * fireballs out of fireball types and used particles instead.  Fixed some
165  * debris explosion things.  Restructured fireball code.   Restructured
166  * some lighting code.   Made dynamic lighting on by default. Made groups
167  * of lasers only cast one light.  Made fireballs not cast light.
168  * 
169  * 54    1/19/98 9:37p Allender
170  * Great Compiler Warning Purge of Jan, 1998.  Used pragma's in a couple
171  * of places since I was unsure of what to do with code.
172  * 
173  * 53    1/15/98 4:58p John
174  * Made warp effect use a looping ani.  Made the scaling up & down be in
175  * software.
176  * 
177  * 52    1/15/98 2:32p John
178  * Added code to optionally take a velocity for a fireball.
179  * 
180  * 51    1/11/98 2:15p John
181  * Changed a lot of stuff that had to do with bitmap loading.   Made cfile
182  * not do callbacks, I put that in global code.   Made only bitmaps that
183  * need to load for a level load.
184  * 
185  * 50    1/02/98 5:04p John
186  * Several explosion related changes.  Made fireballs not be used as
187  * ani's.  Made ship spark system expell particles.  Took away impact
188  * explosion for weapon hitting ship... this needs to get added to weapon
189  * info and makes shield hit more obvious.  Only make sparks when hit
190  * hull, not shields.
191  * 
192  * 49    12/31/97 4:48p John
193  * added some debug info
194  * 
195  * 48    12/30/97 6:44p John
196  * Made g3_Draw_bitmap functions account for aspect of bitmap.
197  * 
198  * 47    12/08/97 11:15a John
199  * added parameter to warpout for life.
200  * 
201  * 46    12/02/97 3:59p John
202  * Added first rev of thruster glow, along with variable levels of
203  * translucency, which retquired some restructing of palman.
204  * 
205  * 45    11/30/97 2:03p John
206  * made warp effect not use an ani for height map.
207  * 
208  * 44    11/16/97 12:20p John
209  * added code to make ships instantly warp in/out if fireball effect
210  * fails.   But, made it so the fireball effect will kill off old sparks
211  * so it can go, so this should never happen.
212  * 
213  * 43    11/16/97 10:48a John
214  * added code to remove the oldest fireball if there weren't enough slots
215  * for a new one.
216  * 
217  * 42    11/01/97 1:49p John
218  * added code to page fireballs in during level load.  Made player warpout
219  * use Adam's new camera movement pattern.  Make ships docked to warping
220  * out ships render correctly.
221  * 
222  * 41    10/29/97 5:05p John
223  * Changed dynamic lighting to only rotate and calculate lighting for
224  * point lights that are close to an object.  Changed lower framerate cap
225  * from 4 to .5.
226  * 
227  * 40    10/24/97 6:24p John
228  * added code to return the life left of a fireball
229  * 
230  * 39    9/14/97 4:49p Lawrance
231  * add some demo debugging code
232  * 
233  * 38    9/12/97 4:02p John
234  * put in ship warp out effect.
235  * put in dynamic lighting for warp in/out
236  * 
237  * 37    9/09/97 4:49p John
238  * Almost done ship warp in code
239  * 
240  * 36    9/08/97 8:39a John
241  * added in code structure for grid
242  * 
243  * 35    9/03/97 5:04p Lawrance
244  * add error checking when restoring structs
245  * 
246  * 34    9/03/97 4:32p John
247  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
248  * to bm_load functions not needed.   Made bmpman keep track of palettes
249  * for bitmaps not mapped into game palettes.
250  * 
251  * 33    8/29/97 2:26p John
252  * first rev of ship warp in effect.  Nothing more than a fireball inside
253  * of freespace, but textest.cpp contains the correct effect code that
254  * needs to be transferred into the game next.
255  * 
256  * 32    8/21/97 5:11p Lawrance
257  * frame numbering for ANI's now is from 0 -> total_frames-1.
258  * 
259  * 31    8/13/97 9:50p Allender
260  * split *_move into *_process_pre and *_process_post functions.
261  * process_pre functions called before object is moved.  _process_post
262  * functions called after object is moved.  Reordered code in ship_post
263  * and weapon_post for multiplayer
264  * 
265  * 30    8/13/97 12:06p Lawrance
266  * supporting multiple types of fireball explosions
267  * 
268  * 29    8/05/97 10:18a Lawrance
269  * my_rand() being used temporarily instead of rand()
270  * 
271  * 28    7/31/97 5:55p John
272  * made so you pass flags to obj_create.
273  * Added new collision code that ignores any pairs that will never
274  * collide.
275  * 
276  * 27    7/24/97 9:54a Lawrance
277  * don't free fireball system if not inited
278  * 
279  * 26    7/23/97 11:35a Lawrance
280  * ensure fireball animations are always freed properly
281  * 
282  * 25    7/21/97 11:41a Lawrance
283  * clean up fireballs at the end of each level
284  * 
285  * 24    7/20/97 6:58p Lawrance
286  * call anim_get_frame() to take advantage of key-framed animation
287  * 
288  * 23    7/11/97 3:57p John
289  * made fireballs translucent.  alpha=1.3x intesity. only for d3d.
290  * 
291  * 22    7/11/97 12:08p John
292  * made fireballs randomly be rotated.
293  * 
294  * 21    7/11/97 11:54a John
295  * added rotated 3d bitmaps.
296  * 
297  * 20    7/11/97 11:19a Lawrance
298  * fix mem leaks, move save code from State.cpp here
299  * 
300  * 19    7/10/97 1:51p Lawrance
301  * sorting anim fireballs
302  * 
303  * 18    6/24/97 6:21p John
304  * added detail flags.
305  * sped up motion debris system a bit.
306  * 
307  * 17    5/14/97 4:08p Lawrance
308  * removing my_index from game arrays
309  * 
310  * 16    5/05/97 10:40p Mike
311  * Make missile exhaust trails look a tad nicer.
312  * 
313  * 15    3/11/97 10:47p Mike
314  * Add a slew of secondary weapons.
315  * Support exhaust blobs.
316  * Add weapons that spawn weapons.
317  * Add remotely detonatable weapons.
318  * Add heat-seeking missiles.
319  * 
320  * 14    2/17/97 5:18p John
321  * Added a bunch of RCS headers to a bunch of old files that don't have
322  * them.
323  *
324  * $NoKeywords: $
325  */
326
327 #include <stdlib.h>
328
329 #include "vecmat.h"
330 #include "tmapper.h"
331 #include "2d.h"
332 #include "3d.h"
333 #include "bmpman.h"
334 #include "model.h"
335 #include "key.h"
336 #include "physics.h"
337 #include "floating.h"
338 #include "model.h"
339 #include "lighting.h"
340 #include "object.h"
341 #include "ship.h"
342 #include "systemvars.h"
343 #include "fireballs.h"
344 #include "linklist.h"
345 #include "gamesnd.h"
346 #include "localize.h"
347
348 #define WARPHOLE_GROW_TIME              (1.5f)  // time for warphole to reach max size (also time to shrink to nothing once it begins to shrink)
349
350 #define MAX_FIREBALL_LOD                                                4
351
352 typedef struct fireball_lod {
353         char    filename[MAX_FILENAME_LEN];
354         int     bitmap_id;
355         int     num_frames;
356         int     fps;
357 } fireball_lod;
358
359 typedef struct fireball_info    {
360         int                                     lod_count;      
361         fireball_lod            lod[4];
362 } fireball_info;
363
364 // flag values for fireball struct flags member
365 #define FBF_WARP_CLOSE_SOUND_PLAYED     (1<<0)
366 #define FBF_WARP_CAPTIAL_SIZE                   (1<<1)
367 #define FBF_WARP_CRUISER_SIZE                   (1<<2)
368
369 typedef struct fireball {                                       
370         int             objnum;                                 // If -1 this object is unused
371         int             fireball_info_index;    // Index into Fireball_info array
372         int             current_bitmap;
373         int             orient;                                 // For fireballs, which orientation.  For warps, 0 is warpin, 1 is warpout
374         int             flags;                                  // see #define FBF_*
375         char            lod;                                            // current LOD
376         float           time_elapsed;                   // in seconds
377         float           total_time;                             // total lifetime of animation in seconds
378 } fireball;
379
380 #define MAX_FIREBALLS   200
381
382 fireball Fireballs[MAX_FIREBALLS];
383
384 fireball_info Fireball_info[MAX_FIREBALL_TYPES];
385
386 int Num_fireballs = 0;
387
388 int fireballs_inited = 0;
389
390 int Warp_glow_bitmap = -1;
391
392 #define FB_INDEX(fb)    (fb-Fireballs)
393
394 // play warp in sound for warp effect
395 void fireball_play_warphole_open_sound(int ship_class, fireball *fb)
396 {
397         int             sound_index;
398         float           range_multiplier = 1.0f;
399         object  *fireball_objp; 
400                 
401         Assert((fb != NULL) && (fb->objnum >= 0));
402         if((fb == NULL) || (fb->objnum < 0)){
403                 return;
404         }
405         fireball_objp = &Objects[fb->objnum];
406
407         sound_index = SND_WARP_IN;
408
409         if((ship_class >= 0) && (ship_class < Num_ship_types)){
410                 if ( Ship_info[ship_class].flags & SIF_HUGE_SHIP ) {
411                         sound_index = SND_CAPITAL_WARP_IN;
412                         fb->flags |= FBF_WARP_CAPTIAL_SIZE;
413                 } else if ( Ship_info[ship_class].flags & SIF_BIG_SHIP ) {
414                         range_multiplier = 6.0f;
415                         fb->flags |= FBF_WARP_CRUISER_SIZE;
416                 }
417         }
418
419         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
420 }
421
422 // play warp out sound for warp effect
423 void fireball_play_warphole_close_sound(fireball *fb)
424 {
425         int     sound_index;    
426
427         object *fireball_objp;
428
429         fireball_objp = &Objects[fb->objnum];
430
431         sound_index = SND_WARP_OUT;
432
433         if ( fb->flags & FBF_WARP_CAPTIAL_SIZE ) {
434                 sound_index = SND_CAPITAL_WARP_OUT;
435         } else {
436                 // AL 27-3-98: Decided that warphole closing is only required for captial ship sized warp effects.
437                 return;
438         }
439
440         snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius); // play warp sound effect
441 }
442
443 void fireball_parse_tbl()
444 {
445         int     rval, idx;
446         char base_filename[256] = "";
447
448         // open localization
449         lcl_ext_open();
450
451         if ((rval = setjmp(parse_abort)) != 0) {
452                 Error(LOCATION, "Unable to parse fireball.tbl!  Code = %i.\n", rval);
453         }
454         else {
455                 read_file_text(NOX("fireball.tbl"));
456                 reset_parse();          
457         }
458
459         int ntypes = 0;
460         required_string("#Start");
461         while (required_string_either("#End","$Name:")) {
462                 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                 strcpy(Fireball_info[ntypes].lod[0].filename, base_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                         sprintf(Fireball_info[ntypes].lod[idx].filename, "%s_%d", base_filename, idx);
484                 }
485
486                 ntypes++;
487         }
488         required_string("#End");
489
490         // close localization
491         lcl_ext_close();
492 }
493
494
495 void fireball_load_data()
496 {
497         int                             i, idx;
498         fireball_info   *fd;
499
500         for ( i = 0; i < MAX_FIREBALL_TYPES; i++ ) {
501                 fd = &Fireball_info[i];
502
503                 for(idx=0; idx<fd->lod_count; idx++){
504                         fd->lod[idx].bitmap_id  = bm_load_animation( fd->lod[idx].filename, &fd->lod[idx].num_frames, &fd->lod[idx].fps, 1 );
505                         if ( fd->lod[idx].bitmap_id < 0 ) {
506                                 Error(LOCATION, "Could not load %s anim file\n", fd->lod[idx].filename);
507                         }
508                 }
509         } 
510
511         if ( Warp_glow_bitmap == -1 )   {
512                 Warp_glow_bitmap = bm_load( NOX("warpglow01") );
513         }       
514
515 }
516
517 void fireball_preload()
518 {
519         // Do nothing.  Called before level init, this used to page in warp effect.
520         // Not needed with new BmpMan system.
521 }
522
523 // This will get called at the start of each level.
524 void fireball_init()
525 {
526         int i;
527
528         if ( !fireballs_inited ) {
529                 fireballs_inited = 1;
530
531                 // Do all the processing that happens only once
532                 fireball_parse_tbl();
533                 fireball_load_data();
534         }
535         
536         // Reset everything between levels
537         Num_fireballs = 0;
538         for (i=0; i<MAX_FIREBALLS; i++ )        {
539                 Fireballs[i].objnum     = -1;
540         }
541
542
543 }
544
545 MONITOR( NumFireballsRend );    
546
547 //extern int tcache_hit(int);
548 //extern int Glide_expl_textures_in_frame;
549 //extern float Glide_avg_exp_text_per_sec;
550
551 void fireball_render(object * obj)
552 {
553         int             num;
554         vertex  p;
555         fireball        *fb;
556         fireball_info *fd;
557
558         MONITOR_INC( NumFireballsRend, 1 );     
559         
560         num = obj->instance;
561         fb = &Fireballs[num];
562         fd = &Fireball_info[fb->fireball_info_index];
563
564         if ( Fireballs[num].current_bitmap < 0 )
565                 return;
566
567 //      gr_set_color( 0, 100, 0 );
568 //      g3_draw_sphere_ez( &obj->pos, obj->radius );
569 //      return;
570
571         // turn off fogging
572         if(The_mission.flags & MISSION_FLAG_FULLNEB){
573                 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
574         }
575
576         g3_rotate_vertex(&p, &obj->pos );
577
578         switch( fb->fireball_info_index )       {
579         
580                 case FIREBALL_EXPLOSION_MEDIUM:
581                         gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f );
582                         g3_draw_bitmap(&p, fb->orient, obj->radius, TMAP_FLAG_TEXTURED );
583                         break;
584
585                 case FIREBALL_EXPLOSION_LARGE1:
586                 case FIREBALL_EXPLOSION_LARGE2:
587                 // case FIREBALL_EXPLOSION_LARGE3:
588                         /*
589                         if (!tcache_hit(Fireballs[num].current_bitmap)) {
590                                 // if we're over 200k exp vram upload this frame, change to lower lod
591                                 if (Glide_expl_textures_in_frame > 1024 * 200) {
592                                         // change fireball instance to lower LOD or don't draw
593
594                                         // get current LOD and number of LODs
595                                         int cur_lod = Fireballs[num].lod;
596                                         int num_lods = Fireball_info[Fireballs[num].fireball_info_index].lod_count;
597
598                                         if (num_lods > cur_lod+1) {
599                                                 // bump lod
600                                                 int frame = Fireballs[num].current_bitmap - Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].bitmap_id;
601                                                 Fireballs[num].lod++;
602                                                 Fireballs[num].current_bitmap = Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod+1].bitmap_id + frame;
603                                                 mprintf(("bumping down lod for fireball %s, frame %d\n", Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].filename, frame));
604                                         }
605                                 }
606                         }*/
607
608                 case FIREBALL_ASTEROID:
609                         // Make the big explosions rotate with the viewer.
610                         gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f );
611                         g3_draw_rotated_bitmap(&p, (i2fl(fb->orient)*PI)/180.0f, obj->radius, TMAP_FLAG_TEXTURED );
612                         break;
613
614                 case FIREBALL_WARP_EFFECT:
615                 case FIREBALL_KNOSSOS_EFFECT: {
616                         
617                                 float percent_life = fb->time_elapsed / fb->total_time;
618
619                                 float rad;
620
621                                 // Code to make effect grow/shrink. 
622                                 float t = fb->time_elapsed;
623                         
624                                 if ( t < WARPHOLE_GROW_TIME )   {
625                                         rad = (float)pow(t/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
626                                         //rad = t*obj->radius/WARPHOLE_GROW_TIME;
627                                         //mprintf(( "T=%.2f, Rad = %.2f\n", t, rad ));
628                                 } else if ( t < fb->total_time - WARPHOLE_GROW_TIME )   {
629                                         rad = obj->radius;
630                                 } else {
631                                         rad = (float)pow((fb->total_time - t)/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
632                                         //rad = (fb->total_time - t )*obj->radius/WARPHOLE_GROW_TIME;
633                                 }
634                                 //rad = obj->radius;
635
636
637                                 warpin_render(&obj->orient, &obj->pos, Fireballs[num].current_bitmap, rad, percent_life, obj->radius );
638                         }
639                         break;
640
641                         
642                 default:
643                         Int3();
644         }
645 }
646
647 // -----------------------------------------------------------------
648 //      fireball_delete()
649 //
650 //      Delete a fireball.  Called by object_delete() code... do not call
651 // directly.
652 //
653 void fireball_delete( object * obj )
654 {
655         int     num;
656         fireball        *fb;
657
658         num = obj->instance;
659         fb = &Fireballs[num];
660
661         Assert( fb->objnum == OBJ_INDEX(obj));
662
663         Fireballs[num].objnum = -1;
664         Num_fireballs--;
665         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[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, objnum;
741         fireball                *fb;
742
743         num = obj->instance;
744         objnum = OBJ_INDEX(obj);
745         Assert( Fireballs[num].objnum == objnum );
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, objnum;
807         fireball                *fb;
808
809         num = obj->instance;
810         objnum = OBJ_INDEX(obj);
811         Assert( Fireballs[num].objnum == objnum );
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, objnum;
849         fireball                *fb;
850
851         MONITOR_INC( NumFireballs, 1 ); 
852
853         num = obj->instance;
854         objnum = OBJ_INDEX(obj);
855         Assert( Fireballs[num].objnum == objnum );
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, objnum;
873         fireball                *fb;
874
875         num = obj->instance;
876         objnum = OBJ_INDEX(obj);
877         Assert( Fireballs[num].objnum == objnum );
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, objnum;
888         fireball                *fb;
889
890         num = obj->instance;
891         objnum = OBJ_INDEX(obj);
892         Assert( Fireballs[num].objnum == objnum );
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 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         Assert( fireball_type > -1 );
991         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                 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 = 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                 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 }