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 the
6 * source.
7 *
8 */
9
10
11
12 #include "fireball/fireballs.h"
13 #include "graphics/tmapper.h"
14 #include "render/3d.h"
15 #include "model/model.h"
16 #include "object/object.h"
17 #include "ship/ship.h"
18 #include "gamesnd/gamesnd.h"
19 #include "localization/localize.h"
20 #include "cmdline/cmdline.h"
21 #include "parse/parselo.h"
22 #include "globalincs/pstypes.h"
23 #include "asteroid/asteroid.h"
24
25 #include <stdlib.h>
26
27
28 // make use of the LOD checker for tbl/tbm parsing (from weapons.cpp)
29 extern SCP_vector<lod_checker> LOD_checker;
30
31 static SCP_vector<color> LOD_color;
32
33 int Warp_model;
34 int Knossos_warp_ani_used;
35
36 #define WARPHOLE_GROW_TIME (2.35f) // time for warphole to reach max size (also time to shrink to nothing once it begins to shrink)
37
38 #define MAX_FIREBALL_LOD 4
39
40 #define MAX_FIREBALLS 200
41
42 #define MAX_WARP_LOD 0
43
44 fireball Fireballs[MAX_FIREBALLS];
45
46 fireball_info Fireball_info[MAX_FIREBALL_TYPES];
47
48 int fireball_used[MAX_FIREBALL_TYPES];
49
50 int Num_fireballs = 0;
51 int Num_fireball_types = 0;
52
53 int fireballs_inited = 0;
54
55 int Warp_glow_bitmap = -1;
56 int Warp_ball_bitmap = -1;
57
58 #define FB_INDEX(fb) (fb-Fireballs)
59
60 /**
61 * Play warp in sound for warp effect
62 */
fireball_play_warphole_open_sound(int ship_class,fireball * fb)63 void fireball_play_warphole_open_sound(int ship_class, fireball *fb)
64 {
65 int sound_index;
66 float range_multiplier = 1.0f;
67 object *fireball_objp;
68
69 Assert((fb != NULL) && (fb->objnum >= 0));
70 if((fb == NULL) || (fb->objnum < 0)){
71 return;
72 }
73 fireball_objp = &Objects[fb->objnum];
74
75 sound_index = SND_WARP_IN;
76
77 if(fb->warp_open_sound_index > -1) {
78 sound_index = fb->warp_open_sound_index;
79 } else if((ship_class >= 0) && (ship_class < Num_ship_classes)){
80 if ( Ship_info[ship_class].flags & SIF_HUGE_SHIP ) {
81 sound_index = SND_CAPITAL_WARP_IN;
82 fb->flags |= FBF_WARP_CAPITAL_SIZE;
83 } else if ( Ship_info[ship_class].flags & SIF_BIG_SHIP ) {
84 range_multiplier = 6.0f;
85 fb->flags |= FBF_WARP_CRUISER_SIZE;
86 }
87 }
88
89 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
90 }
91
92 /**
93 * Play warp out sound for warp effect
94 */
fireball_play_warphole_close_sound(fireball * fb)95 void fireball_play_warphole_close_sound(fireball *fb)
96 {
97 int sound_index;
98
99 object *fireball_objp;
100
101 fireball_objp = &Objects[fb->objnum];
102
103 sound_index = SND_WARP_OUT;
104
105 if ( fb->warp_close_sound_index > -1 ) {
106 sound_index = fb->warp_close_sound_index;
107 } else if ( fb->flags & FBF_WARP_CAPITAL_SIZE ) {
108 sound_index = SND_CAPITAL_WARP_OUT;
109 } else {
110 return;
111 }
112
113 snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius); // play warp sound effect
114 }
115
116 /**
117 * Set default colors for each explosion type (original values from object.cpp)
118 */
fireball_set_default_color(int idx)119 static void fireball_set_default_color(int idx)
120 {
121 Assert( (idx >= 0) && (idx < MAX_FIREBALL_TYPES) );
122
123 switch (idx)
124 {
125 case FIREBALL_EXPLOSION_LARGE1:
126 case FIREBALL_EXPLOSION_LARGE2:
127 case FIREBALL_EXPLOSION_MEDIUM:
128 case FIREBALL_ASTEROID:
129 Fireball_info[idx].exp_color[0] = 1.0f;
130 Fireball_info[idx].exp_color[1] = 0.5f;
131 Fireball_info[idx].exp_color[2] = 0.125f;
132 break;
133
134 case FIREBALL_WARP:
135 Fireball_info[idx].exp_color[0] = 0.75f;
136 Fireball_info[idx].exp_color[1] = 0.75f;
137 Fireball_info[idx].exp_color[2] = 1.0f;
138 break;
139
140
141 case FIREBALL_KNOSSOS:
142 Fireball_info[idx].exp_color[0] = 0.75f;
143 Fireball_info[idx].exp_color[1] = 1.0f;
144 Fireball_info[idx].exp_color[2] = 0.75f;
145 break;
146
147 default:
148 Fireball_info[idx].exp_color[0] = 1.0f;
149 Fireball_info[idx].exp_color[1] = 1.0f;
150 Fireball_info[idx].exp_color[2] = 1.0f;
151 break;
152 }
153 }
154
155 /**
156 * Parse fireball tbl
157 *
158 * NOTE: we can't be too trusting here so a tbm will only modify the LOD count, not add an entry
159 */
parse_fireball_tbl(const char * filename)160 void parse_fireball_tbl(const char *filename)
161 {
162 int rval;
163 lod_checker lod_check;
164 color fb_color;
165
166 // open localization
167 lcl_ext_open();
168
169 if ((rval = setjmp(parse_abort)) != 0) {
170 mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", filename, rval));
171 lcl_ext_close();
172 return;
173 }
174
175 read_file_text(filename, CF_TYPE_TABLES);
176 reset_parse();
177
178 required_string("#Start");
179
180 while (required_string_either("#End", "$Name:")) {
181 memset( &lod_check, 0, sizeof(lod_checker) );
182
183 // base filename
184 required_string("$Name:");
185 stuff_string(lod_check.filename, F_NAME, MAX_FILENAME_LEN);
186
187 lod_check.override = -1;
188
189 // these entries should only be in TBMs, and it has to include at least one
190 if ( Parsing_modular_table ) {
191 if (optional_string("+Explosion_Medium")) {
192 lod_check.override = FIREBALL_EXPLOSION_MEDIUM;
193 } else if (optional_string("+Warp_Effect")) {
194 lod_check.override = FIREBALL_WARP;
195 } else if (optional_string("+Knossos_Effect")) {
196 lod_check.override = FIREBALL_KNOSSOS;
197 } else if (optional_string("+Asteroid")) {
198 lod_check.override = FIREBALL_ASTEROID;
199 } else if (optional_string("+Explosion_Large1")) {
200 lod_check.override = FIREBALL_EXPLOSION_LARGE1;
201 } else if (optional_string("+Explosion_Large2")){
202 lod_check.override = FIREBALL_EXPLOSION_LARGE2;
203 } else {
204 required_string("+Custom_Fireball");
205 stuff_int(&lod_check.override);
206 }
207 }
208
209 lod_check.num_lods = 1;
210
211 // Do we have an LOD num
212 if (optional_string("$LOD:")) {
213 stuff_int(&lod_check.num_lods);
214 }
215
216 if (lod_check.num_lods > MAX_FIREBALL_LOD) {
217 lod_check.num_lods = MAX_FIREBALL_LOD;
218 }
219
220 // check for particular lighting color
221 if ( optional_string("$Light color:") ) {
222 int r, g, b;
223
224 stuff_int(&r);
225 stuff_int(&g);
226 stuff_int(&b);
227
228 CLAMP(r, 0, 255);
229 CLAMP(g, 0, 255);
230 CLAMP(b, 0, 255);
231
232 gr_init_color(&fb_color, r, g, b);
233 } else {
234 // to keep things simple, we just use 0 alpha to indicate that a default value should be used
235 memset( &fb_color, 0, sizeof(color) );
236 }
237
238 // we may use one filename for multiple entries so we'll have to handle dupes post parse
239 LOD_checker.push_back(lod_check);
240 LOD_color.push_back(fb_color);
241 }
242
243 required_string("#End");
244
245 // close localization
246 lcl_ext_close();
247 }
248
fireball_parse_tbl()249 void fireball_parse_tbl()
250 {
251 int i = 0, j;
252 SCP_vector<lod_checker>::iterator lod;
253
254 memset( &Fireball_info, 0, sizeof(fireball_info) * MAX_FIREBALL_TYPES );
255
256
257 parse_fireball_tbl("fireball.tbl");
258
259 // look for any modular tables
260 parse_modular_table(NOX("*-fbl.tbm"), parse_fireball_tbl);
261
262 // we've got our list so pass it off for final checking and loading.
263 // we assume that entries in fireball.tbl are in the correct order
264 for (lod = LOD_checker.begin(); lod != LOD_checker.end(); ++lod) {
265 if ( (i < MAX_FIREBALL_TYPES) && (lod->override < 0) ) {
266 strcpy_s( Fireball_info[i].lod[0].filename, lod->filename );
267 Fireball_info[i].lod_count = lod->num_lods;
268 Num_fireball_types++;
269
270 if (LOD_color[i].alpha == 255) {
271 Fireball_info[i].exp_color[0] = (LOD_color[i].red / 255.0f);
272 Fireball_info[i].exp_color[1] = (LOD_color[i].green / 255.0f);
273 Fireball_info[i].exp_color[2] = (LOD_color[i].blue / 255.0f);
274 } else {
275 fireball_set_default_color(i);
276 }
277 }
278 i++;
279 }
280
281 // having to do this twice is less than optimal, but less error prone too.
282 // this handles (and should only have to handle) TBM related entries
283 i = 0;
284 for (lod = LOD_checker.begin(); lod != LOD_checker.end(); ++lod) {
285 // try entry replacement
286 if ( (lod->override >= 0) && (lod->override < Num_fireball_types) ) {
287 strcpy_s( Fireball_info[lod->override].lod[0].filename, lod->filename );
288 Fireball_info[lod->override].lod_count = lod->num_lods;
289
290 if (LOD_color[i].alpha == 255) {
291 Fireball_info[lod->override].exp_color[0] = (LOD_color[i].red / 255.0f);
292 Fireball_info[lod->override].exp_color[1] = (LOD_color[i].green / 255.0f);
293 Fireball_info[lod->override].exp_color[2] = (LOD_color[i].blue / 255.0f);
294 } else {
295 fireball_set_default_color(lod->override);
296 }
297 }
298 }
299
300 // fill in extra LOD filenames
301 for (i = 0; i < Num_fireball_types; i++) {
302 for (j = 1; j < Fireball_info[i].lod_count; j++) {
303 sprintf( Fireball_info[i].lod[j].filename, "%s_%d", Fireball_info[i].lod[0].filename, j);
304 }
305 }
306
307 // done
308 LOD_checker.clear();
309 }
310
311
fireball_load_data()312 void fireball_load_data()
313 {
314 int i, idx;
315 fireball_info *fd;
316
317 for ( i = 0; i < Num_fireball_types; i++ ) {
318 fd = &Fireball_info[i];
319
320 for(idx=0; idx<fd->lod_count; idx++){
321 // we won't use a warp effect lod greater than MAX_WARP_LOD so don't load it either
322 if ( (i == FIREBALL_WARP) && (idx > MAX_WARP_LOD) )
323 continue;
324
325 fd->lod[idx].bitmap_id = bm_load_animation( fd->lod[idx].filename, &fd->lod[idx].num_frames, &fd->lod[idx].fps, NULL, 1 );
326 if ( fd->lod[idx].bitmap_id < 0 ) {
327 Error(LOCATION, "Could not load %s anim file\n", fd->lod[idx].filename);
328 }
329 }
330 }
331
332 if ( Warp_glow_bitmap == -1 ) {
333 Warp_glow_bitmap = bm_load( NOX("warpglow01") );
334 }
335 if ( Warp_ball_bitmap == -1 ) {
336 Warp_ball_bitmap = bm_load( NOX("warpball01") );
337 }
338 }
339
340 // This will get called at the start of each level.
fireball_init()341 void fireball_init()
342 {
343 int i;
344
345 if ( !fireballs_inited ) {
346 fireballs_inited = 1;
347
348 // Do all the processing that happens only once
349 fireball_parse_tbl();
350 fireball_load_data();
351 }
352
353 // Reset everything between levels
354 Num_fireballs = 0;
355 for (i=0; i<MAX_FIREBALLS; i++ ) {
356 Fireballs[i].objnum = -1;
357 }
358
359 // Goober5000 - reset Knossos warp flag
360 Knossos_warp_ani_used = 0;
361
362 mprintf(("Loading warp model\n"));
363 Warp_model = -1;
364
365 // Goober5000 - check for existence of file before trying to load it
366 if (cf_exists_full("warp.pof", CF_TYPE_MODELS))
367 {
368 Warp_model = model_load("warp.pof", 0, NULL, 0);
369 }
370
371 mprintf((" %d\n", Warp_model));
372 }
373
MONITOR(NumFireballsRend)374 MONITOR( NumFireballsRend )
375
376 void fireball_render(object * obj)
377 {
378 int num;
379 vertex p;
380 fireball *fb;
381
382 MONITOR_INC( NumFireballsRend, 1 );
383
384 num = obj->instance;
385 fb = &Fireballs[num];
386
387 if ( Fireballs[num].current_bitmap < 0 )
388 return;
389
390 // turn off fogging
391 if(The_mission.flags & MISSION_FLAG_FULLNEB){
392 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
393 }
394
395 if(Cmdline_nohtl) {
396 g3_rotate_vertex(&p, &obj->pos );
397 }else{
398 g3_transfer_vertex(&p, &obj->pos);
399 }
400
401 switch( fb->fireball_render_type ) {
402
403 case FIREBALL_MEDIUM_EXPLOSION:
404 batch_add_bitmap (
405 Fireballs[num].current_bitmap,
406 TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD,
407 &p,
408 fb->orient,
409 obj->radius
410 );
411 break;
412
413 case FIREBALL_LARGE_EXPLOSION:
414 // Make the big explosions rotate with the viewer.
415 batch_add_bitmap_rotated (
416 Fireballs[num].current_bitmap,
417 TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD,
418 &p,
419 (i2fl(fb->orient)*PI)/180.0f,
420 obj->radius
421 );
422 break;
423
424 case FIREBALL_WARP_EFFECT: {
425
426 float percent_life = fb->time_elapsed / fb->total_time;
427
428 float rad;
429
430 // Code to make effect grow/shrink.
431 float t = fb->time_elapsed;
432
433 if ( t < WARPHOLE_GROW_TIME ) {
434 rad = (float)pow(t/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
435 } else if ( t < fb->total_time - WARPHOLE_GROW_TIME ) {
436 rad = obj->radius;
437 } else {
438 rad = (float)pow((fb->total_time - t)/WARPHOLE_GROW_TIME,0.4f)*obj->radius;
439 }
440
441 warpin_render(obj, &obj->orient, &obj->pos, Fireballs[num].current_bitmap, rad, percent_life, obj->radius, (Fireballs[num].flags & FBF_WARP_3D) );
442 }
443 break;
444
445
446 default:
447 Int3();
448 }
449 }
450
451 /**
452 * Delete a fireball.
453 * Called by object_delete() code... do not call directly.
454 */
fireball_delete(object * obj)455 void fireball_delete( object * obj )
456 {
457 int num;
458 fireball *fb;
459
460 num = obj->instance;
461 fb = &Fireballs[num];
462
463 Assert( fb->objnum == OBJ_INDEX(obj));
464
465 Fireballs[num].objnum = -1;
466 Num_fireballs--;
467 Assert( Num_fireballs >= 0 );
468 }
469
470 /**
471 * Delete all active fireballs, by calling obj_delete directly.
472 */
fireball_delete_all()473 void fireball_delete_all()
474 {
475 fireball *fb;
476 int i;
477
478 for ( i = 0; i < MAX_FIREBALLS; i++ ) {
479 fb = &Fireballs[i];
480 if ( fb->objnum != -1 ) {
481 obj_delete(fb->objnum);
482 }
483 }
484 }
485
fireball_set_framenum(int num)486 void fireball_set_framenum(int num)
487 {
488 int framenum;
489 fireball *fb;
490 fireball_info *fd;
491 fireball_lod *fl;
492
493 fb = &Fireballs[num];
494 fd = &Fireball_info[Fireballs[num].fireball_info_index];
495
496 // valid lod?
497 fl = NULL;
498 if((fb->lod >= 0) && (fb->lod < fd->lod_count)){
499 fl = &Fireball_info[Fireballs[num].fireball_info_index].lod[fb->lod];
500 }
501 if(fl == NULL){
502 // argh
503 return;
504 }
505
506 if ( fb->fireball_render_type == FIREBALL_WARP_EFFECT ) {
507 float total_time = i2fl(fl->num_frames) / fl->fps; // in seconds
508
509 framenum = fl2i(fb->time_elapsed * fl->num_frames / total_time + 0.5);
510
511 if ( framenum < 0 ) framenum = 0;
512
513 framenum = framenum % fl->num_frames;
514
515 if ( fb->orient ) {
516 // warp out effect plays backwards
517 framenum = fl->num_frames-framenum-1;
518 fb->current_bitmap = fl->bitmap_id + framenum;
519 } else {
520 fb->current_bitmap = fl->bitmap_id + framenum;
521 }
522 } else {
523
524 framenum = fl2i(fb->time_elapsed / fb->total_time * fl->num_frames + 0.5);
525
526 // ensure we don't go past the number of frames of animation
527 if ( framenum > (fl->num_frames-1) ) {
528 framenum = (fl->num_frames-1);
529 Objects[fb->objnum].flags |= OF_SHOULD_BE_DEAD;
530 }
531
532 if ( framenum < 0 ) framenum = 0;
533 fb->current_bitmap = fl->bitmap_id + framenum;
534 }
535 }
536
fireball_is_perishable(object * obj)537 int fireball_is_perishable(object * obj)
538 {
539 // return 1;
540 int num, objnum;
541 fireball *fb;
542
543 num = obj->instance;
544 objnum = OBJ_INDEX(obj);
545 Assert( Fireballs[num].objnum == objnum );
546
547 fb = &Fireballs[num];
548
549 if ( fb->fireball_render_type == FIREBALL_MEDIUM_EXPLOSION )
550 return 1;
551
552 if ( !(fb->fireball_render_type == FIREBALL_WARP_EFFECT) ) {
553 if ( !(obj->flags & OF_WAS_RENDERED)) {
554 return 1;
555 }
556 }
557
558 return 0;
559 }
560
561
562 /**
563 * There are too many fireballs, so delete the oldest small one
564 * to free up a slot.
565 *
566 * @return The fireball slot freed.
567 */
fireball_free_one()568 int fireball_free_one()
569 {
570 fireball *fb;
571 int i;
572
573 int oldest_objnum = -1, oldest_slotnum = -1;
574 float lifeleft, oldest_lifeleft = 0.0f;
575
576 for ( i = 0; i < MAX_FIREBALLS; i++ ) {
577 fb = &Fireballs[i];
578
579 // only remove the ones that aren't warp effects
580 if ( (fb->objnum >= 0) && fireball_is_perishable(&Objects[fb->objnum]) ) {
581
582 lifeleft = fb->total_time - fb->time_elapsed;
583 if ( (oldest_objnum < 0) || (lifeleft < oldest_lifeleft) ) {
584 oldest_slotnum = i;
585 oldest_lifeleft = lifeleft;
586 oldest_objnum = fb->objnum;
587 }
588 break;
589 }
590 }
591
592 if ( oldest_objnum > -1 ) {
593 obj_delete(oldest_objnum);
594 }
595 return oldest_slotnum;
596 }
597
fireball_is_warp(object * obj)598 int fireball_is_warp(object * obj)
599 {
600 int num, objnum;
601 fireball *fb;
602
603 num = obj->instance;
604 objnum = OBJ_INDEX(obj);
605 Assert( Fireballs[num].objnum == objnum );
606
607 fb = &Fireballs[num];
608
609 if ( fb->fireball_render_type == FIREBALL_WARP_EFFECT)
610 return 1;
611
612 return 0;
613 }
614
615 // maybe play sound effect for warp hole closing
fireball_maybe_play_warp_close_sound(fireball * fb)616 void fireball_maybe_play_warp_close_sound(fireball *fb)
617 {
618 float life_left;
619
620 // If not a warphole fireball, do a quick out
621 if ( !(fb->fireball_render_type == FIREBALL_WARP_EFFECT)) {
622 return;
623 }
624
625 // If the warhole close sound has been played, don't play it again!
626 if ( fb->flags & FBF_WARP_CLOSE_SOUND_PLAYED ) {
627 return;
628 }
629
630 life_left = fb->total_time - fb->time_elapsed;
631
632 if ( life_left < WARPHOLE_GROW_TIME ) {
633 fireball_play_warphole_close_sound(fb);
634 fb->flags |= FBF_WARP_CLOSE_SOUND_PLAYED;
635 }
636 }
637
MONITOR(NumFireballs)638 MONITOR( NumFireballs )
639
640 void fireball_process_post(object * obj, float frame_time)
641 {
642 int num, objnum;
643 fireball *fb;
644
645 MONITOR_INC( NumFireballs, 1 );
646
647 num = obj->instance;
648 objnum = OBJ_INDEX(obj);
649 Assert( Fireballs[num].objnum == objnum );
650
651 fb = &Fireballs[num];
652
653 fb->time_elapsed += frame_time;
654 if ( fb->time_elapsed > fb->total_time ) {
655 obj->flags |= OF_SHOULD_BE_DEAD;
656 }
657
658 fireball_maybe_play_warp_close_sound(fb);
659
660 fireball_set_framenum(num);
661 }
662
663 /**
664 * Returns life left of a fireball in seconds
665 */
fireball_lifeleft(object * obj)666 float fireball_lifeleft( object *obj )
667 {
668 int num, objnum;
669 fireball *fb;
670
671 num = obj->instance;
672 objnum = OBJ_INDEX(obj);
673 Assert( Fireballs[num].objnum == objnum );
674
675 fb = &Fireballs[num];
676
677 return fb->total_time - fb->time_elapsed;
678 }
679
680 /**
681 * Returns life left of a fireball in percent
682 */
fireball_lifeleft_percent(object * obj)683 float fireball_lifeleft_percent( object *obj )
684 {
685 int num, objnum;
686 fireball *fb;
687
688 num = obj->instance;
689 objnum = OBJ_INDEX(obj);
690 Assert( Fireballs[num].objnum == objnum );
691
692 fb = &Fireballs[num];
693
694 float p = (fb->total_time - fb->time_elapsed) / fb->total_time;
695 if (p < 0)p=0.0f;
696 return p;
697 }
698
699 /**
700 * Determine LOD to use
701 */
fireball_get_lod(vec3d * pos,fireball_info * fd,float size)702 int fireball_get_lod(vec3d *pos, fireball_info *fd, float size)
703 {
704 vertex v;
705 int x, y, w, h, bm_size;
706 int must_stop = 0;
707 int ret_lod = 1;
708 int behind = 0;
709
710 // bogus
711 if(fd == NULL){
712 return 1;
713 }
714
715 // start the frame
716 extern int G3_count;
717
718 if(!G3_count){
719 g3_start_frame(1);
720 must_stop = 1;
721 }
722 g3_set_view_matrix(&Eye_position, &Eye_matrix, Eye_fov);
723
724 // get extents of the rotated bitmap
725 g3_rotate_vertex(&v, pos);
726
727 // if vertex is behind, find size if in front, then drop down 1 LOD
728 if (v.codes & CC_BEHIND) {
729 float dist = vm_vec_dist_quick(&Eye_position, pos);
730 vec3d temp;
731
732 behind = 1;
733 vm_vec_scale_add(&temp, &Eye_position, &Eye_matrix.vec.fvec, dist);
734 g3_rotate_vertex(&v, &temp);
735
736 // if still behind, bail and go with default
737 if (v.codes & CC_BEHIND) {
738 behind = 0;
739 }
740 }
741
742 if(!g3_get_bitmap_dims(fd->lod[0].bitmap_id, &v, size, &x, &y, &w, &h, &bm_size)) {
743 if (Detail.hardware_textures == 4) {
744 // straight LOD
745 if(w <= bm_size/8){
746 ret_lod = 3;
747 } else if(w <= bm_size/2){
748 ret_lod = 2;
749 } else if(w <= (1.56*bm_size)){
750 ret_lod = 1;
751 } else {
752 ret_lod = 0;
753 }
754 } else {
755 // less aggressive LOD for lower detail settings
756 if(w <= bm_size/8){
757 ret_lod = 3;
758 } else if(w <= bm_size/3){
759 ret_lod = 2;
760 } else if(w <= (1.2*bm_size)){
761 ret_lod = 1;
762 } else {
763 ret_lod = 0;
764 }
765 }
766 }
767
768 // if it's behind, bump up LOD by 1
769 if (behind) {
770 ret_lod++;
771 }
772
773 // end the frame
774 if(must_stop){
775 g3_end_frame();
776 }
777
778 // return the best lod
779 return MIN(ret_lod, fd->lod_count - 1);
780 }
781
782 /**
783 * Create a fireball, return object index.
784 */
fireball_create(vec3d * pos,int fireball_type,int render_type,int parent_obj,float size,int reverse,vec3d * velocity,float warp_lifetime,int ship_class,matrix * orient_override,int low_res,int extra_flags,int warp_open_sound,int warp_close_sound)785 int fireball_create( vec3d * pos, int fireball_type, int render_type, int parent_obj, float size, int reverse, vec3d *velocity, float warp_lifetime, int ship_class, matrix *orient_override, int low_res, int extra_flags, int warp_open_sound, int warp_close_sound)
786 {
787 int n, objnum, fb_lod;
788 object *obj;
789 fireball *fb;
790 fireball_info *fd;
791 fireball_lod *fl;
792
793 Assert( fireball_type > -1 );
794 Assert( fireball_type < Num_fireball_types );
795
796 fd = &Fireball_info[fireball_type];
797
798 // check to make sure this fireball type exists
799 if (!fd->lod_count)
800 return -1;
801
802 if ( !(Game_detail_flags & DETAIL_FLAG_FIREBALLS) ) {
803 if ( !((fireball_type == FIREBALL_WARP) || (fireball_type == FIREBALL_KNOSSOS)) ) {
804 return -1;
805 }
806 }
807
808 if ( (Num_fireballs >= MAX_FIREBALLS) || (Num_objects >= MAX_OBJECTS) ) {
809
810 // out of slots, so free one up.
811 n = fireball_free_one();
812 if ( n < 0 ) {
813 return -1;
814 }
815 } else {
816 for ( n = 0; n < MAX_FIREBALLS; n++ ) {
817 if ( Fireballs[n].objnum < 0 ) {
818 break;
819 }
820 }
821 Assert( n != MAX_FIREBALLS );
822 }
823
824 fb = &Fireballs[n];
825
826 // get an lod to use
827 fb_lod = fireball_get_lod(pos, fd, size);
828
829 // change lod if low res is desired
830 if (low_res) {
831 fb_lod++;
832 fb_lod = MIN(fb_lod, fd->lod_count - 1);
833 }
834
835 // if this is a warpout fireball, never go higher than LOD 1
836 if(fireball_type == FIREBALL_WARP){
837 fb_lod = MAX_WARP_LOD;
838 }
839 fl = &fd->lod[fb_lod];
840
841 fb->lod = (char)fb_lod;
842
843 fb->flags = extra_flags;
844 fb->warp_open_sound_index = warp_open_sound;
845 fb->warp_close_sound_index = warp_close_sound;
846
847 matrix orient;
848 if(orient_override != NULL){
849 orient = *orient_override;
850 } else {
851 if ( parent_obj < 0 ) {
852 orient = vmd_identity_matrix;
853 } else {
854 orient = Objects[parent_obj].orient;
855 }
856 }
857
858 objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, OF_RENDERS);
859
860 if (objnum < 0) {
861 Int3(); // Get John, we ran out of objects for fireballs
862 return objnum;
863 }
864
865 obj = &Objects[objnum];
866
867 fb->fireball_info_index = fireball_type;
868 fb->fireball_render_type = render_type;
869 fb->time_elapsed = 0.0f;
870 fb->objnum = objnum;
871 fb->current_bitmap = -1;
872
873 switch( fb->fireball_render_type ) {
874
875 case FIREBALL_MEDIUM_EXPLOSION:
876 fb->orient = (myrand()>>8) & 7; // 0 - 7
877 break;
878
879 case FIREBALL_LARGE_EXPLOSION:
880 fb->orient = (myrand()>>8) % 360; // 0 - 359
881 break;
882
883 case FIREBALL_WARP_EFFECT:
884 // Play sound effect for warp hole opening up
885 fireball_play_warphole_open_sound(ship_class, fb);
886
887 // warp in type
888 if (reverse) {
889 fb->orient = 1;
890 // if warp out, then reverse the orientation
891 vm_vec_scale( &obj->orient.vec.fvec, -1.0f ); // Reverse the forward vector
892 vm_vec_scale( &obj->orient.vec.rvec, -1.0f ); // Reverse the right vector
893 } else {
894 fb->orient = 0;
895 }
896 break;
897
898 default:
899 Int3();
900 break;
901 }
902
903 if ( fb->fireball_render_type == FIREBALL_WARP_EFFECT ) {
904 Assert( warp_lifetime >= 4.0f ); // Warp lifetime must be at least 4 seconds!
905 if ( warp_lifetime < 4.0f )
906 warp_lifetime = 4.0f;
907 fb->total_time = warp_lifetime; // in seconds
908 } else {
909 fb->total_time = i2fl(fl->num_frames) / fl->fps; // in seconds
910 }
911
912 fireball_set_framenum(n);
913
914 if ( velocity ) {
915 // Make the explosion move at a constant velocity.
916 obj->flags |= OF_PHYSICS;
917 obj->phys_info.mass = 1.0f;
918 obj->phys_info.side_slip_time_const = 0.0f;
919 obj->phys_info.rotdamp = 0.0f;
920 obj->phys_info.vel = *velocity;
921 obj->phys_info.max_vel = *velocity;
922 obj->phys_info.desired_vel = *velocity;
923 obj->phys_info.speed = vm_vec_mag(velocity);
924 vm_vec_zero(&obj->phys_info.max_rotvel);
925 }
926
927 Num_fireballs++;
928 return objnum;
929 }
930
931 /**
932 * Called at game shutdown to clean up the fireball system
933 */
fireball_close()934 void fireball_close()
935 {
936 if ( !fireballs_inited )
937 return;
938
939 fireball_delete_all();
940 }
941
fireballs_page_in()942 void fireballs_page_in()
943 {
944 int i, idx;
945 fireball_info *fd;
946
947 for ( i = 0; i < Num_fireball_types; i++ ) {
948 if((i < NUM_DEFAULT_FIREBALLS) || fireball_used[i]){
949 fd = &Fireball_info[i];
950
951 // if this is a Knossos ani, only load if Knossos_warp_ani_used is true
952 if ( (i == FIREBALL_KNOSSOS) && !Knossos_warp_ani_used)
953 continue;
954
955 for(idx=0; idx<fd->lod_count; idx++) {
956 // we won't use a warp effect lod greater than MAX_WARP_LOD so don't load it either
957 if ( (i == FIREBALL_WARP) && (idx > MAX_WARP_LOD) )
958 continue;
959
960 bm_page_in_texture( fd->lod[idx].bitmap_id, fd->lod[idx].num_frames );
961 }
962 }
963 }
964
965 bm_page_in_texture( Warp_glow_bitmap );
966 bm_page_in_texture( Warp_ball_bitmap );
967 }
968
fireball_get_color(int idx,float * red,float * green,float * blue)969 void fireball_get_color(int idx, float *red, float *green, float *blue)
970 {
971 Assert( red && blue && green );
972
973 if ( (idx < 0) || (idx >= Num_fireball_types) ) {
974 Int3();
975
976 *red = 1.0f;
977 *green = 1.0f;
978 *blue = 1.0f;
979
980 return;
981 }
982
983 fireball_info *fbi = &Fireball_info[idx];
984
985 *red = fbi->exp_color[0];
986 *green = fbi->exp_color[1];
987 *blue = fbi->exp_color[2];
988 }
989
fireball_ship_explosion_type(ship_info * sip)990 int fireball_ship_explosion_type(ship_info *sip)
991 {
992 Assert( sip != NULL );
993
994 int index = -1;
995 int ship_fireballs = (int)sip->explosion_bitmap_anims.size();
996 int objecttype_fireballs = -1;
997
998 if (sip->class_type >= 0) {
999 objecttype_fireballs = (int)Ship_types[sip->class_type].explosion_bitmap_anims.size();
1000 }
1001
1002 if(ship_fireballs > 0){
1003 index = sip->explosion_bitmap_anims[rand()%ship_fireballs];
1004 } else if(objecttype_fireballs > 0){
1005 index = Ship_types[sip->class_type].explosion_bitmap_anims[rand()%objecttype_fireballs];
1006 }
1007
1008 return index;
1009 }
1010
fireball_asteroid_explosion_type(asteroid_info * aip)1011 int fireball_asteroid_explosion_type(asteroid_info *aip)
1012 {
1013 Assert( aip != NULL );
1014
1015 if (aip->explosion_bitmap_anims.empty())
1016 return -1;
1017
1018 int index = -1;
1019 int roid_fireballs = (int)aip->explosion_bitmap_anims.size();
1020
1021 if (roid_fireballs > 0) {
1022 index = aip->explosion_bitmap_anims[rand()%roid_fireballs];
1023 }
1024
1025 return index;
1026 }
1027
fireball_wormhole_intensity(object * obj)1028 float fireball_wormhole_intensity( object *obj )
1029 {
1030 int num, objnum;
1031 fireball *fb;
1032
1033 num = obj->instance;
1034 objnum = OBJ_INDEX(obj);
1035 Assertion( Fireballs[num].objnum == objnum, "Basic sanity check. Fireballs[num].objnum (%d) should == objnum (%d)", Fireballs[num].objnum, objnum );
1036
1037 fb = &Fireballs[num];
1038
1039 float t = fb->time_elapsed;
1040 float rad;
1041
1042 if ( t < WARPHOLE_GROW_TIME ) {
1043 rad = (float)pow(t/WARPHOLE_GROW_TIME,0.4f);
1044 } else if ( t < fb->total_time - WARPHOLE_GROW_TIME ) {
1045 rad = 1;
1046 } else {
1047 rad = (float)pow((fb->total_time - t)/WARPHOLE_GROW_TIME,0.4f);
1048 }
1049 return rad;
1050 }
1051