예제 #1
0
/*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 );
	}
}
예제 #2
0
/*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;
	}
}