]> icculus.org git repositories - taylor/freespace2.git/blob - src/fireball/fireballs.cpp
merge in updated platform code
[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;    
432
433         object *fireball_objp;
434
435         fireball_objp = &Objects[fb->objnum];
436
437         sound_index = SND_WARP_OUT;
438
439         if ( fb->flags & FBF_WARP_CAPTIAL_SIZE ) {
440                 sound_index = SND_CAPITAL_WARP_OUT;
441         } else {
442                 // AL 27-3-98: Decided that warphole closing is only required for captial ship sized warp effects.
443                 return;
444         }
445
446         snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius); // play warp sound effect
447 }
448
449 void fireball_parse_tbl()
450 {
451         int     rval, idx;
452         char base_filename[256] = "";
453
454         // open localization
455         lcl_ext_open();
456
457         if ((rval = setjmp(parse_abort)) != 0) {
458                 Error(LOCATION, "Unable to parse fireball.tbl!  Code = %i.\n", rval);
459         }
460         else {
461                 read_file_text(NOX("fireball.tbl"));
462                 reset_parse();          
463         }
464
465         int ntypes = 0;
466         required_string("#Start");
467         while (required_string_either("#End","$Name:")) {
468                 SDL_assert( ntypes < MAX_FIREBALL_TYPES);
469
470                 // base filename
471                 required_string("$Name:");
472                 stuff_string(base_filename, F_NAME, NULL);
473
474                 // # of lod levels - make sure old fireball.tbl is compatible
475                 Fireball_info[ntypes].lod_count = 1;
476                 if(optional_string("$LOD:")){
477                         stuff_int(&Fireball_info[ntypes].lod_count);
478                 }
479
480                 // stuff default filename
481                 SDL_strlcpy(Fireball_info[ntypes].lod[0].filename, base_filename, sizeof(Fireball_info[0].lod[0].filename));
482
483                 // stuff LOD level filenames
484                 for(idx=1; idx<Fireball_info[ntypes].lod_count; idx++){
485                         if(idx >= MAX_FIREBALL_LOD){
486                                 break;
487                         }
488
489                         SDL_snprintf(Fireball_info[ntypes].lod[idx].filename, MAX_FILENAME_LEN, "%s_%d", base_filename, idx);
490                 }
491
492                 ntypes++;
493         }
494         required_string("#End");
495
496         // close localization
497         lcl_ext_close();
498 }
499
500
501 void fireball_load_data()
502 {
503         int                             i, idx;
504         fireball_info   *fd;
505
506         for ( i = 0; i < MAX_FIREBALL_TYPES; i++ ) {
507                 fd = &Fireball_info[i];
508
509                 for(idx=0; idx<fd->lod_count; idx++){
510                         fd->lod[idx].bitmap_id  = bm_load_animation( fd->lod[idx].filename, &fd->lod[idx].num_frames, &fd->lod[idx].fps, 1 );
511                         if ( fd->lod[idx].bitmap_id < 0 ) {
512                                 Error(LOCATION, "Could not load %s anim file\n", fd->lod[idx].filename);
513                         }
514                 }
515         } 
516
517         if ( Warp_glow_bitmap == -1 )   {
518                 Warp_glow_bitmap = bm_load( NOX("warpglow01") );
519         }       
520
521 }
522
523 void fireball_preload()
524 {
525         // Do nothing.  Called before level init, this used to page in warp effect.
526         // Not needed with new BmpMan system.
527 }
528
529 // This will get called at the start of each level.
530 void fireball_init()
531 {
532         int i;
533
534         if ( !fireballs_inited ) {
535                 fireballs_inited = 1;
536
537                 // Do all the processing that happens only once
538                 fireball_parse_tbl();
539                 fireball_load_data();
540         }
541         
542         // Reset everything between levels
543         Num_fireballs = 0;
544         for (i=0; i<MAX_FIREBALLS; i++ )        {
545                 Fireballs[i].objnum     = -1;
546         }
547
548
549 }
550
551 MONITOR( NumFireballsRend );    
552
553 //extern int tcache_hit(int);
554 //extern int Glide_expl_textures_in_frame;
555 //extern float Glide_avg_exp_text_per_sec;
556
557 void fireball_render(object * obj)
558 {
559         int             num;
560         vertex  p;
561         fireball        *fb;
562
563         MONITOR_INC( NumFireballsRend, 1 );     
564         
565         num = obj->instance;
566         fb = &Fireballs[num];
567
568         if ( Fireballs[num].current_bitmap < 0 )
569                 return;
570
571 //      gr_set_color( 0, 100, 0 );
572 //      g3_draw_sphere_ez( &obj->pos, obj->radius );
573 //      return;
574
575         // turn off fogging
576         if(The_mission.flags & MISSION_FLAG_FULLNEB){
577                 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
578         }
579
580         g3_rotate_vertex(&p, &obj->pos );
581
582         switch( fb->fireball_info_index )       {
583         
584                 case FIREBALL_EXPLOSION_MEDIUM:
585                         gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f, -1, -1 );
586                         g3_draw_bitmap(&p, fb->orient, obj->radius, TMAP_FLAG_TEXTURED );
587                         break;
588
589                 case FIREBALL_EXPLOSION_LARGE1:
590                 case FIREBALL_EXPLOSION_LARGE2:
591                 // case FIREBALL_EXPLOSION_LARGE3:
592                         /*
593                         if (!tcache_hit(Fireballs[num].current_bitmap)) {
594                                 // if we're over 200k exp vram upload this frame, change to lower lod
595                                 if (Glide_expl_textures_in_frame > 1024 * 200) {
596                                         // change fireball instance to lower LOD or don't draw
597
598                                         // get current LOD and number of LODs
599                                         int cur_lod = Fireballs[num].lod;
600                                         int num_lods = Fireball_info[Fireballs[num].fireball_info_index].lod_count;
601
602                                         if (num_lods > cur_lod+1) {
603                                                 // bump lod
604                                                 int frame = Fireballs[num].current_bitmap - Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].bitmap_id;
605                                                 Fireballs[num].lod++;
606                                                 Fireballs[num].current_bitmap = Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod+1].bitmap_id + frame;
607                                                 mprintf(("bumping down lod for fireball %s, frame %d\n", Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].filename, frame));
608                                         }
609                                 }
610                         }*/
611
612                 case FIREBALL_ASTEROID:
613                         // Make the big explosions rotate with the viewer.
614                         gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f, -1, -1 );
615                         g3_draw_rotated_bitmap(&p, (i2fl(fb->orient)*PI)/180.0f, obj->radius, TMAP_FLAG_TEXTURED );
616                         break;
617
618                 case FIREBALL_WARP_EFFECT:
619                 case FIREBALL_KNOSSOS_EFFECT: {
620                         
621                                 float percent_life = fb->time_elapsed / fb->total_time;
622
623                                 float rad;
624
625                                 // Code to make effect grow/shrink. 
626                                 float t = fb->time_elapsed;
627                         
628                                 if ( t < WARPHOLE_GROW_TIME )   {
629                                         rad = (float)pow(t/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
630                                         //rad = t*obj->radius/WARPHOLE_GROW_TIME;
631                                         //mprintf(( "T=%.2f, Rad = %.2f\n", t, rad ));
632                                 } else if ( t < fb->total_time - WARPHOLE_GROW_TIME )   {
633                                         rad = obj->radius;
634                                 } else {
635                                         rad = (float)pow((fb->total_time - t)/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
636                                         //rad = (fb->total_time - t )*obj->radius/WARPHOLE_GROW_TIME;
637                                 }
638                                 //rad = obj->radius;
639
640
641                                 warpin_render(&obj->orient, &obj->pos, Fireballs[num].current_bitmap, rad, percent_life, obj->radius );
642                         }
643                         break;
644
645                         
646                 default:
647                         Int3();
648         }
649 }
650
651 // -----------------------------------------------------------------
652 //      fireball_delete()
653 //
654 //      Delete a fireball.  Called by object_delete() code... do not call
655 // directly.
656 //
657 void fireball_delete( object * obj )
658 {
659         int     num;
660         fireball        *fb;
661
662         num = obj->instance;
663         fb = &Fireballs[num];
664
665         SDL_assert( fb->objnum == OBJ_INDEX(obj));
666
667         Fireballs[num].objnum = -1;
668         Num_fireballs--;
669         SDL_assert( Num_fireballs >= 0 );
670 }
671
672 // -----------------------------------------------------------------
673 //      fireball_delete_all()
674 //
675 //      Delete all active fireballs, by calling obj_delete directly.
676 //
677 void fireball_delete_all()
678 {
679         fireball        *fb;
680         int             i;
681
682         for ( i = 0; i < MAX_FIREBALLS; i++ ) {
683                 fb = &Fireballs[i];
684                 if ( fb->objnum != -1 ) {
685                         obj_delete(fb->objnum);
686                 }
687         }
688 }
689
690 void fireball_set_framenum(int num)
691 {
692         int                             framenum;
693         fireball                        *fb;
694         fireball_info   *fd;
695         fireball_lod    *fl;
696
697         fb = &Fireballs[num];
698         fd = &Fireball_info[Fireballs[num].fireball_info_index];
699
700         // valid lod?
701         fl = NULL;
702         if((fb->lod >= 0) && (fb->lod < fd->lod_count)){
703                 fl = &Fireball_info[Fireballs[num].fireball_info_index].lod[(int)fb->lod];
704         }
705         if(fl == NULL){
706                 // argh
707                 return;
708         }
709
710         if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT )    {
711                 float total_time = i2fl(fl->num_frames) / fl->fps;      // in seconds
712
713                 framenum = fl2i(fb->time_elapsed * fl->num_frames / total_time + 0.5);
714
715                 if ( framenum < 0 ) framenum = 0;
716
717                 framenum = framenum % fl->num_frames;
718
719                 if ( fb->orient )       {
720                         // warp out effect plays backwards
721                         framenum = fl->num_frames-framenum-1;
722                         fb->current_bitmap = fl->bitmap_id + framenum;
723                 } else {
724                         fb->current_bitmap = fl->bitmap_id + framenum;
725                 }
726         } else {
727
728                 framenum = fl2i(fb->time_elapsed / fb->total_time * fl->num_frames + 0.5);
729
730                 // ensure we don't go past the number of frames of animation
731                 if ( framenum > (fl->num_frames-1) ) {
732                         framenum = (fl->num_frames-1);
733                         Objects[fb->objnum].flags |= OF_SHOULD_BE_DEAD;
734                 }
735
736                 if ( framenum < 0 ) framenum = 0;
737                 fb->current_bitmap = fl->bitmap_id + framenum;
738         }
739 }
740
741 int fireball_is_perishable(object * obj)
742 {
743         //      return 1;
744         int                     num, objnum;
745         fireball                *fb;
746
747         num = obj->instance;
748         objnum = OBJ_INDEX(obj);
749         SDL_assert( Fireballs[num].objnum == objnum );
750
751         fb = &Fireballs[num];
752
753         if ( fb->fireball_info_index == FIREBALL_EXPLOSION_MEDIUM )     
754                 return 1;
755
756         if ( !((fb->fireball_info_index == FIREBALL_WARP_EFFECT) || (fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)) )     {
757                 if ( !(obj->flags & OF_WAS_RENDERED))   {
758                         return 1;
759                 }
760         }
761
762         return 0;
763 }
764
765
766 // -----------------------------------------------------------------
767 //      fireball_free_one()
768 //
769 //      There are too many fireballs, so delete the oldest small one
770 // to free up a slot.  Returns the fireball slot freed.
771 //
772 int fireball_free_one()
773 {
774         fireball        *fb;
775         int             i;
776
777         int             oldest_objnum = -1, oldest_slotnum = -1;
778         float           lifeleft, oldest_lifeleft = 0.0f;
779
780         for ( i = 0; i < MAX_FIREBALLS; i++ ) {
781                 fb = &Fireballs[i];
782
783                 // only remove the ones that aren't warp effects
784                 if ( (fb->objnum>-1) && fireball_is_perishable(&Objects[fb->objnum]) )  {
785
786                         lifeleft = fb->total_time - fb->time_elapsed;
787                         if ( (oldest_objnum < 0) || (lifeleft < oldest_lifeleft) )      {
788                                 oldest_slotnum = i;
789                                 oldest_lifeleft = lifeleft;
790                                 oldest_objnum = fb->objnum;
791                         }
792                         break;
793                 }
794         }
795
796         if ( oldest_objnum > -1 )       {
797                 obj_delete(oldest_objnum);
798         }
799         return oldest_slotnum;
800 }
801
802 // broke fireball_move into fireball_process_pre and fireball_process_post as was done
803 // with all *_move functions on 8/13 by Mike K. and Mark A.
804 void fireball_process_pre( object *objp, float frame_time)
805 {
806 }
807
808 int fireball_is_warp(object * obj)
809 {
810         int                     num, objnum;
811         fireball                *fb;
812
813         num = obj->instance;
814         objnum = OBJ_INDEX(obj);
815         SDL_assert( Fireballs[num].objnum == objnum );
816
817         fb = &Fireballs[num];
818
819         if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)     
820                 return 1;
821
822         return 0;
823 }
824
825 // mabye play sound effect for warp hole closing
826 void fireball_maybe_play_warp_close_sound(fireball *fb)
827 {
828         float life_left;
829
830         // If not a warphole fireball, do a quick out
831         if ( !(fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)) {
832                 return;
833         }
834
835         // If the warhole close sound has been played, don't play it again!
836         if ( fb->flags & FBF_WARP_CLOSE_SOUND_PLAYED ) {
837                 return;
838         }
839
840         life_left = fb->total_time - fb->time_elapsed;
841
842         if ( life_left < WARPHOLE_GROW_TIME ) {
843                 fireball_play_warphole_close_sound(fb);
844                 fb->flags |= FBF_WARP_CLOSE_SOUND_PLAYED;
845         }
846 }
847
848 MONITOR( NumFireballs );        
849
850 void fireball_process_post(object * obj, float frame_time)
851 {
852         int                     num, objnum;
853         fireball                *fb;
854
855         MONITOR_INC( NumFireballs, 1 ); 
856
857         num = obj->instance;
858         objnum = OBJ_INDEX(obj);
859         SDL_assert( Fireballs[num].objnum == objnum );
860
861         fb = &Fireballs[num];
862
863         fb->time_elapsed += frame_time;
864         if ( fb->time_elapsed > fb->total_time ) {
865                 obj->flags |= OF_SHOULD_BE_DEAD;
866         }
867
868         fireball_maybe_play_warp_close_sound(fb);
869
870         fireball_set_framenum(num);
871 }
872
873 // Returns life left of a fireball in seconds
874 float fireball_lifeleft( object *obj )
875 {
876         int                     num, objnum;
877         fireball                *fb;
878
879         num = obj->instance;
880         objnum = OBJ_INDEX(obj);
881         SDL_assert( Fireballs[num].objnum == objnum );
882
883         fb = &Fireballs[num];
884
885         return fb->total_time - fb->time_elapsed;
886 }
887
888 // Returns life left of a fireball in percent
889 float fireball_lifeleft_percent( object *obj )
890 {
891         int                     num, objnum;
892         fireball                *fb;
893
894         num = obj->instance;
895         objnum = OBJ_INDEX(obj);
896         SDL_assert( Fireballs[num].objnum == objnum );
897
898         fb = &Fireballs[num];
899
900         return (fb->total_time - fb->time_elapsed) / fb->total_time;
901 }
902
903 // determine LOD to use
904 int fireball_get_lod(vector *pos, fireball_info *fd, float size)
905 {
906         vertex v;
907         int x, y, w, h, bm_size;
908         int must_stop = 0;
909         int ret_lod = 1;
910         int behind = 0;
911
912         // bogus
913         if(fd == NULL){
914                 return 1;
915         }
916
917         // start the frame
918         extern float Viewer_zoom;
919         extern int G3_count;
920
921         if(!G3_count){
922                 g3_start_frame(1);
923                 must_stop = 1;
924         }
925         g3_set_view_matrix(&Eye_position, &Eye_matrix, Viewer_zoom);
926
927         // get extents of the rotated bitmap
928         g3_rotate_vertex(&v, pos);
929
930         // if vertex is behind, find size if in front, then drop down 1 LOD
931         if (v.codes & CC_BEHIND) {
932                 float dist = vm_vec_dist_quick(&Eye_position, pos);
933                 vector temp;
934
935                 behind = 1;
936                 vm_vec_scale_add(&temp, &Eye_position, &Eye_matrix.v.fvec, dist);
937                 g3_rotate_vertex(&v, &temp);
938
939                 // if still behind, bail and go with default
940                 if (v.codes & CC_BEHIND) {
941                         behind = 0;
942                 }
943         }
944
945         if(!g3_get_bitmap_dims(fd->lod[0].bitmap_id, &v, size, &x, &y, &w, &h, &bm_size)) {
946                 if (Detail.hardware_textures == 4) {
947                         // straight LOD
948                         if(w <= bm_size/8){
949                                 ret_lod = 3;
950                         } else if(w <= bm_size/2){
951                                 ret_lod = 2;
952                         } else if(w <= (1.56*bm_size)){
953                                 ret_lod = 1;
954                         } else {
955                                 ret_lod = 0;
956                         }               
957                 } else {
958                         // less aggressive LOD for lower detail settings
959                         if(w <= bm_size/8){
960                                 ret_lod = 3;
961                         } else if(w <= bm_size/3){
962                                 ret_lod = 2;
963                         } else if(w <= (1.2*bm_size)){
964                                 ret_lod = 1;
965                         } else {
966                                 ret_lod = 0;
967                         }               
968                 }
969         }
970
971         // if it's behind, bump up LOD by 1
972         if (behind) {
973                 ret_lod++;
974         }
975
976         // end the frame
977         if(must_stop){
978                 g3_end_frame();
979         }
980
981         // return the best lod
982         return min(ret_lod, fd->lod_count - 1);
983 }
984
985 //      Create a fireball, return object index.
986 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)
987 {
988         int                             n, objnum, fb_lod;
989         object                  *obj;
990         fireball                        *fb;
991         fireball_info   *fd;
992         fireball_lod    *fl;
993
994         SDL_assert( fireball_type > -1 );
995         SDL_assert( fireball_type < MAX_FIREBALL_TYPES );
996
997         fd = &Fireball_info[fireball_type];
998
999         if ( !(Game_detail_flags & DETAIL_FLAG_FIREBALLS) )     {
1000                 if ( !((fireball_type == FIREBALL_WARP_EFFECT) || (fireball_type == FIREBALL_KNOSSOS_EFFECT)) ) {
1001                         return -1;
1002                 }
1003         }
1004
1005         if ( (Num_fireballs >= MAX_FIREBALLS) || (num_objects >= MAX_OBJECTS) ) {
1006                 // who cares if we don't create a spark.
1007                 // JAS - Should this code be in?  Is it better to remove an old spark
1008                 // and start a new one, or just not start the new one?
1009                 //if ( fd->type == FIREBALL_TYPE_SMALL )        {
1010                 //      return -1;
1011                 //}
1012
1013                 //mprintf(( "Out of fireball slots, trying to free one up!\n" ));
1014                 // out of slots, so free one up.
1015                 n = fireball_free_one();
1016                 if ( n < 0 )    {
1017                         // If there's still no free slots, then exit
1018                         //mprintf(( "ERROR: Couldn't free one up!!\n" ));
1019                         return -1;
1020                 } else {
1021                         //mprintf(( "Freed one up just fine!!\n" ));
1022                 }
1023         } else {
1024                 for ( n = 0; n < MAX_FIREBALLS; n++ )   {
1025                         if ( Fireballs[n].objnum < 0  ) {
1026                                 break;
1027                         }
1028                 }
1029                 SDL_assert( n != MAX_FIREBALLS );
1030         }
1031
1032         fb = &Fireballs[n];
1033
1034         // get an lod to use    
1035         fb_lod = fireball_get_lod(pos, fd, size);
1036
1037         // change lod if low res is desired
1038         if (low_res) {
1039                 fb_lod++;
1040                 fb_lod = min(fb_lod, fd->lod_count - 1);
1041         }
1042
1043         // if this is a warpout fireball, never go higher than LOD 1
1044         if(fireball_type == FIREBALL_WARP_EFFECT){
1045                 /*
1046                 if(fb_lod > 1){
1047                         fb_lod = 1;
1048                 }
1049                 */
1050                 fb_lod = 0;
1051         }
1052         fl = &fd->lod[fb_lod];
1053
1054         fb->lod = (char)fb_lod;
1055         fb->flags = 0;
1056         matrix orient;
1057         if(orient_override != NULL){
1058                 orient = *orient_override;
1059         } else {
1060                 if ( parent_obj < 0 )   {
1061                         orient = vmd_identity_matrix;
1062                 } else {
1063                         orient = Objects[parent_obj].orient;
1064                 }
1065         }
1066         
1067         objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, OF_RENDERS);
1068
1069         if (objnum < 0) {
1070                 Int3();                         // Get John, we ran out of objects for fireballs
1071                 return objnum;
1072         }
1073
1074         obj = &Objects[objnum];
1075
1076         fb->fireball_info_index = fireball_type;
1077         fb->time_elapsed = 0.0f;
1078         fb->objnum = objnum;
1079         fb->current_bitmap = -1;
1080         
1081         switch( fb->fireball_info_index )       {
1082
1083                 case FIREBALL_EXPLOSION_MEDIUM: 
1084                         fb->orient = (myrand()>>8) & 7;                                                 // 0 - 7
1085                         break;
1086
1087                 case FIREBALL_EXPLOSION_LARGE1:
1088                 case FIREBALL_EXPLOSION_LARGE2:
1089                 // case FIREBALL_EXPLOSION_LARGE3:
1090                 case FIREBALL_ASTEROID:
1091                         fb->orient = (myrand()>>8) % 360;                                               // 0 - 359
1092                         break;
1093
1094                 case FIREBALL_WARP_EFFECT:
1095                 case FIREBALL_KNOSSOS_EFFECT:
1096                         // Play sound effect for warp hole opening up
1097                         fireball_play_warphole_open_sound(ship_class, fb);
1098
1099                         // warp in type
1100                         if (reverse)    {
1101                                 fb->orient = 1;
1102                                 // if warp out, then reverse the orientation
1103                                 vm_vec_scale( &obj->orient.v.fvec, -1.0f );     // Reverse the forward vector
1104                                 vm_vec_scale( &obj->orient.v.rvec, -1.0f );     // Reverse the right vector
1105                         } else {
1106                                 fb->orient = 0;
1107                         }
1108                         break;
1109
1110                 default:
1111                         Int3();
1112                         break;
1113         }
1114
1115         if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT )    {
1116                 SDL_assert( warp_lifetime > 4.0f );             // Warp lifetime must be at least 4 seconds!
1117                 fb->total_time = warp_lifetime; // in seconds
1118         } else {
1119                 fb->total_time = i2fl(fl->num_frames) / fl->fps;        // in seconds
1120         }
1121         
1122         fireball_set_framenum(n);
1123
1124         if ( velocity ) {
1125                 // Make the explosion move at a constant velocity.
1126                 obj->flags |= OF_PHYSICS;
1127                 obj->phys_info.mass = 1.0f;
1128                 obj->phys_info.side_slip_time_const = 0.0f;
1129                 obj->phys_info.rotdamp = 0.0f;
1130                 obj->phys_info.vel = *velocity;
1131                 obj->phys_info.max_vel = *velocity;
1132                 obj->phys_info.desired_vel = *velocity;
1133                 obj->phys_info.speed = vm_vec_mag(velocity);
1134                 vm_vec_zero(&obj->phys_info.max_rotvel);
1135         }
1136         
1137         Num_fireballs++;
1138         return objnum;
1139 }
1140
1141 // -----------------------------------------------------------------
1142 //      fireball_close()
1143 //
1144 //      Called at game shutdown to clean up the fireball system
1145 //
1146 void fireball_close()
1147 {
1148         if ( !fireballs_inited )
1149                 return;
1150
1151         fireball_delete_all();
1152 }
1153
1154 // -----------------------------------------------------------------
1155 //      fireball_level_close()
1156 //
1157 //      Called when a mission ends... frees up any animations that might
1158 // be partially played
1159 //
1160 void fireball_level_close()
1161 {
1162         if ( !fireballs_inited )
1163                 return;
1164
1165         fireball_delete_all();
1166 }
1167
1168 void fireballs_page_in()
1169 {
1170         int                             i, idx;
1171         fireball_info   *fd;
1172         
1173         for ( i = 0; i < MAX_FIREBALL_TYPES ; i++ ) {
1174                 fd = &Fireball_info[i];
1175
1176                 for(idx=0; idx<fd->lod_count; idx++){
1177                         bm_page_in_texture( fd->lod[idx].bitmap_id, fd->lod[idx].num_frames );
1178                 }
1179         }
1180
1181         bm_page_in_texture( Warp_glow_bitmap );
1182
1183 }