/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health AUTOANIMATE - Will cycle it's anim DEADSOLID - Stay solid even when destroyed (in case damage model is rather large). NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists USE_MODEL - When used, will toggle to it's usemodel (model name + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage PLAYER_USE - Player can use it with the use button NO_EXPLOSION - By default, will explode when it dies...this is your override. "model" arbitrary .md3 file to display "health" how much health to have - default is zero (not breakable) If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving "targetname" when used, dies and displays damagemodel, if any (if not, removes itself) "target" What to use when it dies "target2" What to use when it's repaired "target3" What to use when it's used while it's broken "paintarget" target to fire when hit (but not destroyed) "count" the amount of armor/health/ammo given (default 50) "gravity" if set to 1, this will be affected by gravity "radius" Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1) (.5) is half as many chunks, (2) is twice as many chunks Damage: default is none "splashDamage" - damage to do (will make it explode on death) "splashRadius" - radius for above damage "team" - This cannot take damage from members of this team: "player" "neutral" "enemy" "material" - default is "8 - MAT_NONE" - choose from this list: 0 = MAT_METAL (grey metal) 1 = MAT_GLASS 2 = MAT_ELECTRICAL (sparks only) 3 = MAT_ELEC_METAL (METAL chunks and sparks) 4 = MAT_DRK_STONE (brown stone chunks) 5 = MAT_LT_STONE (tan stone chunks) 6 = MAT_GLASS_METAL (glass and METAL chunks) 7 = MAT_METAL2 (blue/grey metal) 8 = MAT_NONE (no chunks-DEFAULT) 9 = MAT_GREY_STONE (grey colored stone) 10 = MAT_METAL3 (METAL and METAL2 chunk combo) 11 = MAT_CRATE1 (yellow multi-colored crate chunks) 12 = MAT_GRATE1 (grate chunks--looks horrible right now) 13 = MAT_ROPE (for yavin_trial, no chunks, just wispy bits ) 14 = MAT_CRATE2 (red multi-colored crate chunks) 15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout ) FIXME/TODO: set size better? multiple damage models? custom explosion effect/sound? */ void SP_misc_model_breakable( gentity_t *ent ) { char damageModel[MAX_QPATH]; char chunkModel[MAX_QPATH]; char useModel[MAX_QPATH]; int len; // Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this. G_SpawnInt( "material", "8", (int*)&ent->material ); G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer CacheChunkEffects( ent->material ); misc_model_breakable_init( ent ); len = strlen( ent->model ) - 4; strncpy( damageModel, ent->model, len ); damageModel[len] = 0; //chop extension strncpy( chunkModel, damageModel, sizeof(chunkModel)); strncpy( useModel, damageModel, sizeof(useModel)); if (ent->takedamage) { //Dead/damaged model if( !(ent->spawnflags & 8) ) { //no dmodel strcat( damageModel, "_d1.md3" ); ent->s.modelindex2 = G_ModelIndex( damageModel ); } //Chunk model strcat( chunkModel, "_c1.md3" ); ent->s.modelindex3 = G_ModelIndex( chunkModel ); } //Use model if( ent->spawnflags & 32 ) { //has umodel strcat( useModel, "_u1.md3" ); ent->sound1to2 = G_ModelIndex( useModel ); } if ( !ent->mins[0] && !ent->mins[1] && !ent->mins[2] ) { VectorSet (ent->mins, -16, -16, -16); } if ( !ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2] ) { VectorSet (ent->maxs, 16, 16, 16); } if ( ent->spawnflags & 2 ) { ent->s.eFlags |= EF_ANIM_ALLFAST; } G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); gi.linkentity (ent); if ( ent->spawnflags & 128 ) {//Can be used by the player's BUTTON_USE ent->svFlags |= SVF_PLAYER_USABLE; } if ( ent->team && ent->team[0] ) { ent->noDamageTeam = TranslateTeamName( ent->team ); if ( ent->noDamageTeam == TEAM_FREE ) { G_Error("team name %s not recognized\n", ent->team); } } ent->team = NULL; //HACK if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 ) {//run a think G_EffectIndex( "fighter_explosion2" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ); ent->e_ThinkFunc = thinkF_TieFighterThink; ent->nextthink = level.time + FRAMETIME; } float grav = 0; G_SpawnFloat( "gravity", "0", &grav ); if ( grav ) {//affected by gravity G_SetAngles( ent, ent->s.angles ); G_SetOrigin( ent, ent->currentOrigin ); misc_model_breakable_gravity_init( ent, qtrue ); } }
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION START_OFF SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health AUTOANIMATE - Will cycle it's anim DEADSOLID - Stay solid even when destroyed (in case damage model is rather large). NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists USE_MODEL - When used, will toggle to it's usemodel (model + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage PLAYER_USE - Player can use it with the use button NO_EXPLOSION - By default, will explode when it dies...this is your override. START_OFF - Will start off and will not appear until used. "model" arbitrary .md3 file to display "modelscale" "x" uniform scale "modelscale_vec" "x y z" scale model in each axis - height, width and length - bbox will scale with it "health" how much health to have - default is zero (not breakable) If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving "targetname" when used, dies and displays damagemodel (model + "_d1.md3"), if any (if not, removes itself) "target" What to use when it dies "target2" What to use when it's repaired "target3" What to use when it's used while it's broken "paintarget" target to fire when hit (but not destroyed) "count" the amount of armor/health/ammo given (default 50) "radius" Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1) (.5) is half as many chunks, (2) is twice as many chunks "NPC_targetname" - Only the NPC with this name can damage this "forcevisible" - When you turn on force sight (any level), you can see these draw through the entire level... "redCrosshair" - crosshair turns red when you look at this "gravity" if set to 1, this will be affected by gravity "throwtarget" if set (along with gravity), this thing, when used, will throw itself at the entity whose targetname matches this string "mass" if gravity is on, this determines how much damage this thing does when it hits someone. Default is the size of the object from one corner to the other, that works very well. Only override if this is an object whose mass should be very high or low for it's size (density) Damage: default is none "splashDamage" - damage to do (will make it explode on death) "splashRadius" - radius for above damage "team" - This cannot take damage from members of this team: "player" "neutral" "enemy" "material" - default is "8 - MAT_NONE" - choose from this list: 0 = MAT_METAL (grey metal) 1 = MAT_GLASS 2 = MAT_ELECTRICAL (sparks only) 3 = MAT_ELEC_METAL (METAL chunks and sparks) 4 = MAT_DRK_STONE (brown stone chunks) 5 = MAT_LT_STONE (tan stone chunks) 6 = MAT_GLASS_METAL (glass and METAL chunks) 7 = MAT_METAL2 (blue/grey metal) 8 = MAT_NONE (no chunks-DEFAULT) 9 = MAT_GREY_STONE (grey colored stone) 10 = MAT_METAL3 (METAL and METAL2 chunk combo) 11 = MAT_CRATE1 (yellow multi-colored crate chunks) 12 = MAT_GRATE1 (grate chunks--looks horrible right now) 13 = MAT_ROPE (for yavin_trial, no chunks, just wispy bits ) 14 = MAT_CRATE2 (red multi-colored crate chunks) 15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout ) */ void SP_misc_model_breakable( gentity_t *ent ) { char damageModel[MAX_QPATH]; char chunkModel[MAX_QPATH]; char useModel[MAX_QPATH]; int len; // Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this. G_SpawnInt( "material", "8", (int*)&ent->material ); G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer qboolean bHasScale = G_SpawnVector("modelscale_vec", "0 0 0", ent->s.modelScale); if (!bHasScale) { float temp; G_SpawnFloat( "modelscale", "0", &temp); if (temp != 0.0f) { ent->s.modelScale[ 0 ] = ent->s.modelScale[ 1 ] = ent->s.modelScale[ 2 ] = temp; bHasScale = qtrue; } } CacheChunkEffects( ent->material ); misc_model_breakable_init( ent ); len = strlen( ent->model ) - 4; assert(ent->model[len]=='.');//we're expecting ".md3" strncpy( damageModel, ent->model, sizeof(damageModel) ); damageModel[len] = 0; //chop extension strncpy( chunkModel, damageModel, sizeof(chunkModel)); strncpy( useModel, damageModel, sizeof(useModel)); if (ent->takedamage) { //Dead/damaged model if( !(ent->spawnflags & 8) ) { //no dmodel strcat( damageModel, "_d1.md3" ); ent->s.modelindex2 = G_ModelIndex( damageModel ); } //Chunk model strcat( chunkModel, "_c1.md3" ); ent->s.modelindex3 = G_ModelIndex( chunkModel ); } //Use model if( ent->spawnflags & 32 ) { //has umodel strcat( useModel, "_u1.md3" ); ent->sound1to2 = G_ModelIndex( useModel ); } if ( !ent->mins[0] && !ent->mins[1] && !ent->mins[2] ) { VectorSet (ent->mins, -16, -16, -16); } if ( !ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2] ) { VectorSet (ent->maxs, 16, 16, 16); } // Scale up the tie-bomber bbox a little. if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_bomber.md3", ent->model ) == 0 ) { VectorSet (ent->mins, -80, -80, -80); VectorSet (ent->maxs, 80, 80, 80); //ent->s.modelScale[ 0 ] = ent->s.modelScale[ 1 ] = ent->s.modelScale[ 2 ] *= 2.0f; //bHasScale = qtrue; } if (bHasScale) { //scale the x axis of the bbox up. ent->maxs[0] *= ent->s.modelScale[0];//*scaleFactor; ent->mins[0] *= ent->s.modelScale[0];//*scaleFactor; //scale the y axis of the bbox up. ent->maxs[1] *= ent->s.modelScale[1];//*scaleFactor; ent->mins[1] *= ent->s.modelScale[1];//*scaleFactor; //scale the z axis of the bbox up and adjust origin accordingly ent->maxs[2] *= ent->s.modelScale[2]; float oldMins2 = ent->mins[2]; ent->mins[2] *= ent->s.modelScale[2]; ent->s.origin[2] += (oldMins2-ent->mins[2]); } if ( ent->spawnflags & 2 ) { ent->s.eFlags |= EF_ANIM_ALLFAST; } G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); gi.linkentity (ent); if ( ent->spawnflags & 128 ) {//Can be used by the player's BUTTON_USE ent->svFlags |= SVF_PLAYER_USABLE; } if ( ent->team && ent->team[0] ) { ent->noDamageTeam = (team_t)GetIDForString( TeamTable, ent->team ); if ( ent->noDamageTeam == TEAM_FREE ) { G_Error("team name %s not recognized\n", ent->team); } } ent->team = NULL; //HACK if ( ent->model && Q_stricmp( "models/map_objects/ships/x_wing_nogear.md3", ent->model ) == 0 ) { if( ent->splashDamage > 0 && ent->splashRadius > 0 ) { ent->s.loopSound = G_SoundIndex( "sound/vehicles/x-wing/loop.wav" ); ent->s.eFlags |= EF_LESS_ATTEN; } } else if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 ) {//run a think G_EffectIndex( "explosions/fighter_explosion2" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" ); /* G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" );*/ G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" ); /* G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" );*/ G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ); RegisterItem( FindItemForWeapon( WP_TIE_FIGHTER )); ent->s.eFlags |= EF_LESS_ATTEN; if( ent->splashDamage > 0 && ent->splashRadius > 0 ) { ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" ); //ent->e_ThinkFunc = thinkF_TieFighterThink; //ent->e_UseFunc = thinkF_TieFighterThink; //ent->nextthink = level.time + FRAMETIME; ent->e_UseFunc = useF_TieFighterUse; // Yeah, I could have just made this value changable from the editor, but I // need it immediately! float light; vec3_t color; qboolean lightSet, colorSet; // if the "color" or "light" keys are set, setup constantLight lightSet = qtrue;//G_SpawnFloat( "light", "100", &light ); light = 255; //colorSet = "1 1 1"//G_SpawnVector( "color", "1 1 1", color ); colorSet = qtrue; color[0] = 1; color[1] = 1; color[2] = 1; if ( lightSet || colorSet ) { int r, g, b, i; r = color[0] * 255; if ( r > 255 ) { r = 255; } g = color[1] * 255; if ( g > 255 ) { g = 255; } b = color[2] * 255; if ( b > 255 ) { b = 255; } i = light / 4; if ( i > 255 ) { i = 255; } ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 ); } } } else if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_bomber.md3", ent->model ) == 0 ) { G_EffectIndex( "ships/tiebomber_bomb_falling" ); G_EffectIndex( "ships/tiebomber_explosion2" ); G_EffectIndex( "explosions/fighter_explosion2" ); G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ); ent->e_ThinkFunc = thinkF_TieBomberThink; ent->nextthink = level.time + FRAMETIME; ent->attackDebounceTime = level.time + 1000; // We only take damage from a heavy weapon class missiles. ent->flags |= FL_DMG_BY_HEAVY_WEAP_ONLY; ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" ); ent->s.eFlags |= EF_LESS_ATTEN; } float grav = 0; G_SpawnFloat( "gravity", "0", &grav ); if ( grav ) {//affected by gravity G_SetAngles( ent, ent->s.angles ); G_SetOrigin( ent, ent->currentOrigin ); G_SpawnString( "throwtarget", NULL, &ent->target4 ); // used to throw itself at something misc_model_breakable_gravity_init( ent, qtrue ); } // Start off. if ( ent->spawnflags & 4096 ) { ent->spawnContents = ent->contents; // It Navs can temporarly turn it "on" ent->s.solid = 0; ent->contents = 0; ent->clipmask = 0; ent->svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->count = 0; } int forceVisible = 0; G_SpawnInt( "forcevisible", "0", &forceVisible ); if ( forceVisible ) {//can see these through walls with force sight, so must be broadcast //ent->svFlags |= SVF_BROADCAST; ent->s.eFlags |= EF_FORCE_VISIBLE; } int redCrosshair = 0; G_SpawnInt( "redCrosshair", "0", &redCrosshair ); if ( redCrosshair ) {//can see these through walls with force sight, so must be broadcast ent->flags |= FL_RED_CROSSHAIR; } }