/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "globalincs/systemvars.h" #include "graphics/2d.h" #include "math/vecmat.h" #include "object/object.h" #include "parse/parselo.h" #include "particle/particle.h" #include "weapon/muzzleflash.h" #include "model/modelrender.h" // --------------------------------------------------------------------------------------------------------------------- // MUZZLE FLASH DEFINES/VARS // // muzzle flash info - read from a table typedef struct mflash_blob_info { char name[MAX_FILENAME_LEN]; int anim_id; float offset; float radius; mflash_blob_info( const mflash_blob_info& mbi ) { strcpy_s( name, mbi.name ); anim_id = mbi.anim_id; offset = mbi.offset; radius = mbi.radius; } mflash_blob_info() : anim_id( -1 ), offset( 0.0 ), radius( 0.0 ) { name[ 0 ] = '\0'; } mflash_blob_info& operator=( const mflash_blob_info& r ) { strcpy_s( name, r.name ); anim_id = r.anim_id; offset = r.offset; radius = r.radius; return *this; } } mflash_blob_info; typedef struct mflash_info { char name[MAX_FILENAME_LEN]; int used_this_level; SCP_vector blobs; mflash_info() : used_this_level( 0 ) { name[ 0 ] = '\0'; } mflash_info( const mflash_info& mi ) { strcpy_s( name, mi.name ); used_this_level = mi.used_this_level; blobs = mi.blobs; } mflash_info& operator=( const mflash_info& r ) { strcpy_s( name, r.name ); used_this_level = r.used_this_level; blobs = r.blobs; return *this; } } mflash_info; SCP_vector Mflash_info; // --------------------------------------------------------------------------------------------------------------------- // MUZZLE FLASH FUNCTIONS // void parse_mflash_tbl(const char *filename) { uint i; try { read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // header required_string("#Muzzle flash types"); while (optional_string("$Mflash:")) { mflash_info mflash; bool override_mflash = false; required_string("+name:"); stuff_string(mflash.name, F_NAME, MAX_FILENAME_LEN); if (optional_string("+override")) override_mflash = true; // read in all blobs while (optional_string("+blob_name:")) { mflash_blob_info mblob; stuff_string(mblob.name, F_NAME, MAX_FILENAME_LEN); required_string("+blob_offset:"); stuff_float(&mblob.offset); required_string("+blob_radius:"); stuff_float(&mblob.radius); mflash.blobs.push_back(mblob); } for (i = 0; i < Mflash_info.size(); i++) { if (!stricmp(mflash.name, Mflash_info[i].name)) { if (override_mflash) { Mflash_info[i] = mflash; } break; } } // no matching name exists so add as new if (i == Mflash_info.size()) { Mflash_info.push_back(mflash); } // a mflash of the same name exists, don't add it again else { if (!override_mflash) { Warning(LOCATION, "Muzzle flash \"%s\" already exists! Using existing entry instead.", mflash.name); } } } // close required_string("#end"); } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what())); return; } } // initialize muzzle flash stuff for the whole game void mflash_game_init() { // parse main table first parse_mflash_tbl("mflash.tbl"); // look for any modular tables parse_modular_table(NOX("*-mfl.tbm"), parse_mflash_tbl); } void mflash_mark_as_used(int index) { if (index < 0) return; Assert( index < (int)Mflash_info.size() ); Mflash_info[index].used_this_level++; } void mflash_page_in(bool load_all) { uint i, idx; int num_frames, fps; // load up all anims for ( i = 0; i < Mflash_info.size(); i++) { // skip if it's not used if ( !load_all && !Mflash_info[i].used_this_level ) continue; // blobs size_t original_num_blobs = Mflash_info[i].blobs.size(); int original_idx = 1; for ( idx = 0; idx < Mflash_info[i].blobs.size(); ) { mflash_blob_info* mfbip = &Mflash_info[i].blobs[idx]; mfbip->anim_id = bm_load_either(mfbip->name, &num_frames, &fps, NULL, true); if ( mfbip->anim_id >= 0 ) { bm_page_in_xparent_texture( mfbip->anim_id ); ++idx; } else { Warning(LOCATION, "Muzleflash \"%s\", blob [%d/" SIZE_T_ARG "]\nMuzzleflash blob \"%s\" not found! Deleting.", Mflash_info[i].name, original_idx, original_num_blobs, Mflash_info[i].blobs[idx].name); Mflash_info[i].blobs.erase( Mflash_info[i].blobs.begin() + idx ); } ++original_idx; } } } // initialize muzzle flash stuff for the level void mflash_level_init() { uint i, idx; // reset all anim usage for this level for ( i = 0; i < Mflash_info.size(); i++) { for ( idx = 0; idx < Mflash_info[i].blobs.size(); idx++) { Mflash_info[i].used_this_level = 0; } } } // shutdown stuff for the level void mflash_level_close() { uint i, idx; // release all anims for ( i = 0; i < Mflash_info.size(); i++) { // blobs for ( idx = 0; idx < Mflash_info[i].blobs.size(); idx++) { if ( Mflash_info[i].blobs[idx].anim_id < 0 ) continue; bm_release( Mflash_info[i].blobs[idx].anim_id ); Mflash_info[i].blobs[idx].anim_id = -1; } } } // create a muzzle flash on the guy void mflash_create(vec3d *gun_pos, vec3d *gun_dir, physics_info *pip, int mflash_type, object *local) { // mflash *mflashp; mflash_info *mi; mflash_blob_info *mbi; uint idx; // standalone server should never create trails if(Game_mode & GM_STANDALONE_SERVER){ return; } // illegal value if ( (mflash_type < 0) || (mflash_type >= (int)Mflash_info.size()) ) return; // create the actual animations mi = &Mflash_info[mflash_type]; if (local != NULL) { int attached_objnum = OBJ_INDEX(local); // This muzzle flash is in local space, so its world position must be derived to apply scaling. vec3d gun_world_pos; vm_vec_unrotate(&gun_world_pos, gun_pos, &Objects[attached_objnum].orient); vm_vec_add2(&gun_world_pos, &Objects[attached_objnum].pos); for (idx = 0; idx < mi->blobs.size(); idx++) { mbi = &mi->blobs[idx]; // bogus anim if (mbi->anim_id < 0) continue; // fire it up particle::particle_info p; vm_vec_scale_add(&p.pos, gun_pos, gun_dir, mbi->offset); vm_vec_zero(&p.vel); //vm_vec_scale_add(&p.vel, &pip->rotvel, &pip->vel, 1.0f); p.type = particle::PARTICLE_BITMAP; p.optional_data = mbi->anim_id; p.attached_objnum = attached_objnum; p.attached_sig = local->signature; // Scale the radius of the muzzle flash effect so that it always appears some minimum width in pixels. p.rad = model_render_get_diameter_clamped_to_min_pixel_size(&gun_world_pos, mbi->radius * 2.0f, Min_pizel_size_muzzleflash) / 2.0f; particle::create(&p); } } else { for (idx = 0; idx < mi->blobs.size(); idx++) { mbi = &mi->blobs[idx]; // bogus anim if (mbi->anim_id < 0) continue; // fire it up particle::particle_info p; vm_vec_scale_add(&p.pos, gun_pos, gun_dir, mbi->offset); vm_vec_scale_add(&p.vel, &pip->rotvel, &pip->vel, 1.0f); p.type = particle::PARTICLE_BITMAP; p.optional_data = mbi->anim_id; p.attached_objnum = -1; p.attached_sig = 0; // Scale the radius of the muzzle flash effect so that it always appears some minimum width in pixels. p.rad = model_render_get_diameter_clamped_to_min_pixel_size(&p.pos, mbi->radius * 2.0f, Min_pizel_size_muzzleflash) / 2.0f; particle::create(&p); } } } // lookup type by name int mflash_lookup(char *name) { uint idx; // look it up for (idx = 0; idx < Mflash_info.size(); idx++) { if ( !stricmp(name, Mflash_info[idx].name) ) return idx; } // couldn't find it return -1; }