#include "g_local.h"

#define		GENERIC_DAMAGE	6

int snd_boardbreak;
int	snd_glassbreak;
int	snd_metalbreak;
int	snd_ceramicbreak;
int snd_chaircreak;
int snd_chairthrow;
int snd_chairhitground;

// JOSEPH 1-28-00
void DropToFloorG( gentity_t *ent )
{
	vec3_t   dest;
	trace_t	 tr;	
	
	VectorSet( dest, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2] - 4096 );
	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );

	if ( tr.startsolid ) {
		return;
	}

	ent->s.groundEntityNum = tr.entityNum;

	G_SetOrigin( ent, tr.endpos );

	ent->nextthink = level.time + FRAMETIME;
}

void DropToFloor( gentity_t *ent )
{
	vec3_t   dest;
	trace_t	 tr;	
	
	VectorSet( dest, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2] - 4096 );
	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );

	if ( tr.startsolid ) {
		return;
	}

	if (fabs(ent->r.currentOrigin[2] - tr.endpos[2]) > 1.0)
	{
		tr.endpos[2] = (ent->r.currentOrigin[2] - 1.0);
	}

	ent->s.groundEntityNum = tr.entityNum;

	G_SetOrigin( ent, tr.endpos );

	ent->think = DropToFloorG;
	ent->nextthink = level.time + FRAMETIME;
}

void moveit (gentity_t *ent, float yaw, float dist)
{
	vec3_t	 move;
	vec3_t	 origin;
	trace_t	 tr;
	vec3_t	 mins, maxs;

	yaw = yaw*M_PI*2 / 360;
	
	move[0] = cos(yaw)*dist;
	move[1] = sin(yaw)*dist;
	move[2] = 0;

	VectorAdd( ent->r.currentOrigin, move, origin );

	mins[0] = ent->r.mins[0];
	mins[1] = ent->r.mins[1];
	mins[2] = ent->r.mins[2] + .01;

	maxs[0] = ent->r.maxs[0];
	maxs[1] = ent->r.maxs[1];
	maxs[2] = ent->r.maxs[2] - .01;

  	trap_Trace( &tr, ent->r.currentOrigin, mins, maxs, origin, ent->s.number, MASK_SHOT );

	if ((tr.endpos[0] != origin[0]) || (tr.endpos[1] != origin[1]))
	{
		mins[0] = ent->r.mins[0] - 2.0;
		mins[1] = ent->r.mins[1] - 2.0;
		maxs[0] = ent->r.maxs[0] + 2.0;
		maxs[1] = ent->r.maxs[1] + 2.0;
	
 		trap_Trace( &tr, ent->r.currentOrigin, mins, maxs, origin, ent->s.number, MASK_SHOT );
	}

	VectorCopy( tr.endpos, ent->r.currentOrigin );	

	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );

	trap_LinkEntity( ent );

	//DropToFloor( ent );
}

void touch_props_box_32 (gentity_t *self, gentity_t *other, trace_t *trace)
{
	float	ratio;
	vec3_t	v;

	if ( other->r.currentOrigin[2] > (self->r.currentOrigin[2]+10+15))
		return;
	
	ratio = 2.5;
	VectorSubtract (self->r.currentOrigin, other->r.currentOrigin, v);
	moveit (self, vectoyaw(v), (20 * ratio * FRAMETIME)*.001);
}

/*QUAKED props_box_32 (1 0 0) (-16 -16 -16) (16 16 16) 

*/
void SP_props_box_32 (gentity_t *self)
{
	self->s.modelindex = G_ModelIndex( "models/mapobjects/boxes/box32.md3" );

	self->clipmask   = CONTENTS_SOLID;
	self->r.contents = CONTENTS_SOLID;
	self->r.svFlags  = SVF_USE_CURRENT_ORIGIN;

	VectorSet (self->r.mins, -16, -16, -16);
	VectorSet (self->r.maxs, 16, 16, 16);
	
	self->touch = touch_props_box_32;

	trap_LinkEntity( self );

	self->think = DropToFloor;
	self->nextthink = level.time + FRAMETIME;
}

void touch_props_box_48 (gentity_t *self, gentity_t *other, trace_t *trace)
{
	float	ratio;
	vec3_t	v;

	if ( other->r.currentOrigin[2] > (self->r.currentOrigin[2]+10+23))
		return;
	
	ratio = 2.0;
	VectorSubtract (self->r.currentOrigin, other->r.currentOrigin, v);
	moveit (self, vectoyaw(v), (20 * ratio * FRAMETIME)*.001);
}

/*QUAKED props_box_48 (1 0 0) (-24 -24 -24) (24 24 24) 

*/
void SP_props_box_48 (gentity_t *self)
{
	self->s.modelindex = G_ModelIndex( "models/mapobjects/boxes/box48.md3" );

	self->clipmask   = CONTENTS_SOLID;
	self->r.contents = CONTENTS_SOLID;
	self->r.svFlags  = SVF_USE_CURRENT_ORIGIN;

	VectorSet (self->r.mins, -24, -24, -24);
	VectorSet (self->r.maxs, 24, 24, 24);
	
	self->touch = touch_props_box_48;

	trap_LinkEntity( self );

	self->think = DropToFloor;
	self->nextthink = level.time + FRAMETIME;
}

void touch_props_box_64 (gentity_t *self, gentity_t *other, trace_t *trace)
{
	float	ratio;
	vec3_t	v;

	if ( other->r.currentOrigin[2] > (self->r.currentOrigin[2]+10+31))
		return;
	
	ratio = 1.5;
	VectorSubtract (self->r.currentOrigin, other->r.currentOrigin, v);
	moveit (self, vectoyaw(v), (20 * ratio * FRAMETIME)*.001);
}

/*QUAKED props_box_64 (1 0 0) (-32 -32 -32) (32 32 32)  

*/
void SP_props_box_64 (gentity_t *self)
{
	self->s.modelindex = G_ModelIndex( "models/mapobjects/boxes/box64.md3" );

	self->clipmask   = CONTENTS_SOLID;
	self->r.contents = CONTENTS_SOLID;
	self->r.svFlags  = SVF_USE_CURRENT_ORIGIN;

	VectorSet (self->r.mins, -32, -32, -32);
	VectorSet (self->r.maxs, 32, 32, 32);
	
	self->touch = touch_props_box_64;

	trap_LinkEntity( self );

	self->think = DropToFloor;
	self->nextthink = level.time + FRAMETIME;
}
// END JOSEPH

// Rafael

void Psmoke_think (gentity_t *ent)
{
	gentity_t *tent;

	ent->count ++;

	if (ent->count == 30)
		ent->think = G_FreeEntity;

	tent = G_TempEntity (ent->s.origin, EV_SMOKE);
	VectorCopy (ent->s.origin, tent->s.origin);
	tent->s.time = 3000;
	tent->s.time2 = 100;
	tent->s.density = 0;
	tent->s.angles2[0] = 4;
	tent->s.angles2[1] = 32;
	tent->s.angles2[2] = 50;

	ent->nextthink = level.time + FRAMETIME;
}

void prop_smoke (gentity_t *ent)
{	
	gentity_t *Psmoke;

	Psmoke = G_Spawn ();
	VectorCopy ( ent->r.currentOrigin, Psmoke->s.origin );
	Psmoke->think = Psmoke_think;
	Psmoke->nextthink = level.time + FRAMETIME;
}

/*QUAKED props_sparks (.8 .46 .16) (-8 -8 -8) (8 8 8) ELECTRIC
the default direction is strait up use info_no_null for alt direction

delay = how long till next spark effect
wait = life of the spark with some random variance default 1.0 sec
health = random number of sparks upto specified amount default 8

start_size default 8 along the x
end_size default 8 along the y
by changing the size will change the spawn origin of the individual spark
ei 16 x 8 or 24 x 32 would cause the sparks to spawn that many units from 
the origin

speed controls how quickly the sparks will travel default is 2
*/

void PGUNsparks_use (gentity_t *ent, gentity_t *self, gentity_t *activator )
{
	gentity_t *tent;

	tent = G_TempEntity (ent->r.currentOrigin, EV_GUNSPARKS);
	VectorCopy (ent->r.currentOrigin, tent->s.origin);
	VectorCopy (ent->r.currentAngles, tent->s.angles);
	tent->s.density = ent->health;
	tent->s.angles2[2] = ent->speed;
	
}

void Psparks_think (gentity_t *ent)
{
	gentity_t	*tent;

//(SA) MOVE TO CLIENT!
	return;


	if (ent->spawnflags & 1)
		tent = G_TempEntity (ent->r.currentOrigin, EV_SPARKS_ELECTRIC);
	else
		tent = G_TempEntity (ent->r.currentOrigin, EV_SPARKS);
	VectorCopy (ent->r.currentOrigin, tent->s.origin);
	VectorCopy (ent->r.currentAngles, tent->s.angles);
	tent->s.density = ent->health;
	tent->s.frame = ent->wait;
	tent->s.angles2[0] = ent->start_size;
	tent->s.angles2[1] = ent->end_size;
	tent->s.angles2[2] = ent->speed;
	
	ent->nextthink = level.time + FRAMETIME + ent->delay + (rand()%600);
}

void sparks_angles_think (gentity_t *ent)
{

	gentity_t *target = NULL;
	vec3_t		vec;

	if (ent->target)
		target = G_Find (NULL, FOFS(targetname), ent->target);

	if (!target)
	{
		VectorSet (ent->r.currentAngles, 0, 0, 1);
	}
	else
	{
		VectorSubtract (ent->s.origin, target->s.origin, vec);
		VectorNormalize( vec );
		VectorCopy (vec, ent->r.currentAngles);
	}

	trap_LinkEntity (ent);

	ent->nextthink = level.time + FRAMETIME;
	if (!Q_stricmp (ent->classname, "props_sparks"))
		ent->think = Psparks_think;
	else
		ent->use = PGUNsparks_use;

}

void SP_props_sparks (gentity_t *ent)
{
	// (SA) don't use in multiplayer right now since it makes decyphering net messages almost impossible
	if(g_gametype.integer != GT_SINGLE_PLAYER) {
		ent->think = G_FreeEntity;
		return;
	}

	G_SetOrigin (ent, ent->s.origin); 
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_GENERAL;
	
	ent->think = sparks_angles_think;
	ent->nextthink = level.time + FRAMETIME;

	if (!ent->health)
		ent->health = 8;

	if (!ent->wait)
		ent->wait = 1200;
	else
		ent->wait *= 1000;

	if (!ent->start_size) 
		ent->start_size = 8;
	
	if (!ent->end_size) 
		ent->end_size = 8;
	
	if (!ent->speed)
		ent->speed = 2;

	trap_LinkEntity (ent);

}

/*QUAKED props_gunsparks (.8 .46 .16) (-8 -8 -8) (8 8 8)
the default direction is strait up use info_no_null for alt direction

this entity must be used to see the effect

"speed" default is 20
"health" number to spawn default is 4
*/

void SP_props_gunsparks (gentity_t *ent)
{
	G_SetOrigin (ent, ent->s.origin); 
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_GENERAL;
	
	ent->think = sparks_angles_think;
	ent->nextthink = level.time + FRAMETIME;

	if (!ent->speed)
		ent->speed = 20;

	if (!ent->health)
		ent->health = 4;

	trap_LinkEntity (ent);

}

/*QUAKED props_smokedust (.8 .46 .16) (-8 -8 -8) (8 8 8)
health = how many pieces 16 is default
*/

void smokedust_use (gentity_t *ent, gentity_t *self, gentity_t *activator )
{
	int			i;
	gentity_t	*tent;
	vec3_t		forward;

	AngleVectors (ent->r.currentAngles, forward, NULL, NULL);

	for (i=0; i<ent->health; i++)
	{
		tent = G_TempEntity (ent->r.currentOrigin, EV_SMOKE);	
		VectorCopy (ent->r.currentOrigin, tent->s.origin);
		VectorCopy (forward, tent->s.origin2);
		tent->s.time = 1000;
		tent->s.time2 = 750;
		tent->s.density = 3;
	}
}

void SP_SmokeDust (gentity_t *ent)
{

	ent->use = smokedust_use;
	
	G_SetOrigin (ent, ent->s.origin); 
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_GENERAL;
	
	if (!ent->health)
		ent->health = 16;
	trap_LinkEntity (ent);
}


/*QUAKED props_dust (.7 .3 .16) (-8 -8 -8) (8 8 8) WHITE
you should give this ent a target use a not null 
or you could set its angles in the editor
*/

void dust_use (gentity_t *ent, gentity_t *self, gentity_t *activator )
{
	gentity_t	*tent;
	vec3_t		forward;

	if (ent->target)
	{
		tent = G_TempEntity (ent->r.currentOrigin, EV_DUST);
		VectorCopy (ent->r.currentOrigin, tent->s.origin);
		VectorCopy (ent->r.currentAngles, tent->s.angles);	
		if (ent->spawnflags & 1)
			tent->s.density = 1;
	}
	else
	{
	
		AngleVectors (ent->r.currentAngles, forward, NULL, NULL);

		tent = G_TempEntity (ent->r.currentOrigin, EV_DUST);	
		VectorCopy (ent->r.currentOrigin, tent->s.origin);
		VectorCopy (forward, tent->s.angles);
		if (ent->spawnflags & 1)
			tent->s.density = 1;
	}
}

void dust_angles_think (gentity_t *ent)
{
	gentity_t *target;
	vec3_t		vec;

	target = G_Find (NULL, FOFS(targetname), ent->target);

	if (!target)
		return;

	VectorSubtract (ent->s.origin, target->s.origin, vec);
	VectorCopy (vec, ent->r.currentAngles);
	trap_LinkEntity (ent);

}

void SP_Dust (gentity_t *ent)
{
	ent->use = dust_use;
	G_SetOrigin (ent, ent->s.origin); 
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_GENERAL;
	
	if (ent->target)
	{
		ent->think = dust_angles_think;
		ent->nextthink = level.time + FRAMETIME;
	}

	trap_LinkEntity (ent);
}

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

extern void G_ExplodeMissile( gentity_t *ent );

void propExplosionLarge (gentity_t *ent)
{
	gentity_t *bolt;

	bolt = G_Spawn();
	bolt->classname = "props_explosion_large";
	bolt->nextthink = level.time + FRAMETIME;
	bolt->think = G_ExplodeMissile;
	bolt->s.eType = ET_MISSILE;
	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	
	bolt->s.weapon = WP_NONE;

	bolt->s.eFlags = EF_BOUNCE_HALF;
	bolt->r.ownerNum = ent->s.number;
	bolt->parent = ent;
	bolt->damage = ent->health;
	bolt->splashDamage = ent->health;
	bolt->splashRadius = ent->health * 1.5;
	bolt->methodOfDeath = MOD_GRENADE;
	bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
	bolt->clipmask = MASK_SHOT;

	VectorCopy (ent->r.currentOrigin, bolt->s.pos.trBase );
	VectorCopy (ent->r.currentOrigin, bolt->r.currentOrigin);
}

void propExplosion (gentity_t *ent)
{
	gentity_t *bolt;

	extern void G_ExplodeMissile( gentity_t *ent );
	bolt = G_Spawn();
	bolt->classname = "props_explosion";
	bolt->nextthink = level.time + FRAMETIME;
	bolt->think = G_ExplodeMissile;
	bolt->s.eType = ET_MISSILE;
	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	
	bolt->s.weapon = WP_NONE;

	bolt->s.eFlags = EF_BOUNCE_HALF;
	bolt->r.ownerNum = ent->s.number;
	bolt->parent = ent;
	bolt->damage = ent->health;
	bolt->splashDamage = ent->health;
	bolt->splashRadius = ent->health * 1.5;
	bolt->methodOfDeath = MOD_GRENADE;
	bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
	bolt->clipmask = MASK_SHOT;

	VectorCopy (ent->r.currentOrigin, bolt->s.pos.trBase );
	VectorCopy (ent->r.currentOrigin, bolt->r.currentOrigin);
}

void InitProp ( gentity_t *ent ) {
	float		light;
	vec3_t		color;
	qboolean	lightSet, colorSet;
	char		*sound;

	if (!Q_stricmp (ent->classname, "props_bench"))
		ent->s.modelindex2 = G_ModelIndex( "models/furniture/bench/bench_sm.md3" );
	else if (!Q_stricmp (ent->classname, "props_radio"))
		ent->s.modelindex2 = G_ModelIndex( "models/mapobjects/electronics/radio1.md3" );
	else if (!Q_stricmp (ent->classname, "props_locker_tall"))
		ent->s.modelindex2 = G_ModelIndex( "models/furniture/storage/lockertall.md3" );
	else if (!Q_stricmp (ent->classname, "props_flippy_table"))
		ent->s.modelindex2 = G_ModelIndex ( "models/furniture/table/woodflip.md3");
	else if (!Q_stricmp (ent->classname, "props_crate_32x64"))
		ent->s.modelindex2 = G_ModelIndex ( "models/furniture/crate/crate32x64.md3");
	else if (!Q_stricmp (ent->classname, "props_58x112tablew"))
		ent->s.modelindex2 = G_ModelIndex ( "models/furniture/table/56x112tablew.md3");
	else if (!Q_stricmp (ent->classname, "props_castlebed"))
		ent->s.modelindex2 = G_ModelIndex ( "models/furniture/bed/castlebed.md3");
	else if (!Q_stricmp (ent->classname, "props_radioSEVEN"))
		ent->s.modelindex2 = G_ModelIndex( "models/mapobjects/electronics/radios.md3" );
	
	// if the "loopsound" key is set, use a constant looping sound when moving
	if ( G_SpawnString( "noise", "100", &sound ) ) {
		ent->s.loopSound = G_SoundIndex( sound );
	}

	// if the "color" or "light" keys are set, setup constantLight
	lightSet = G_SpawnFloat( "light", "100", &light );
	colorSet = G_SpawnVector( "color", "1 1 1", color );
	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 );
	}
	
	ent->isProp = qtrue;

	ent->moverState = MOVER_POS1;
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);
}

void props_bench_think (gentity_t *ent)
{
	ent->s.frame++;

	if (ent->s.frame < 28)
		ent->nextthink = level.time + (FRAMETIME/2);
	else
	{
		ent->clipmask = 0;
		ent->r.contents = 0;
		ent->takedamage = qfalse;
		
		G_UseTargets (ent, NULL);
	}

}

void props_bench_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->think = props_bench_think;
	ent->nextthink = level.time + FRAMETIME;
}

/*QUAKED props_bench (.8 .6 .2) ? 
requires an origin brush
health = 10 by default
*/
void SP_Props_Bench (gentity_t *ent)
{

	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 10;

	ent->takedamage = qtrue;

	ent->clipmask = CONTENTS_SOLID;

	ent->die = props_bench_die;

	trap_LinkEntity (ent);
}

void props_radio_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{

	propExplosion (ent);
	
	ent->takedamage = qfalse;

	G_UseTargets (ent, NULL);

	G_FreeEntity (ent);
}

/*QUAKED props_radio (.8 .6 .2) ? 
requires an origin brush
health = defaults to 100
*/
void SP_Props_Radio (gentity_t *ent)
{

	// Ridah, had to add this so I could load castle18dk7
	if (!ent->model) {
		G_Printf( S_COLOR_RED "props_radio with NULL model\n" );
		return;
	}

	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 100;

	ent->takedamage = qtrue;

	ent->die = props_radio_die;

	trap_LinkEntity (ent);
	
}


void props_radio_dieSEVEN (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{

	int i;

	propExplosion (ent);

	for (i=0; i<20; i++)
		Spawn_Shard (ent, inflictor, 1, ent->count);

	Prop_Break_Sound (ent);
	
	ent->takedamage = qfalse;
	ent->die = NULL;
	
	trap_LinkEntity (ent);

	G_UseTargets (ent, NULL);

	G_FreeEntity (ent);
}

/*QUAKED props_radioSEVEN (.8 .6 .2) ? 
requires an origin brush
health = defaults to 100

  
  the models dims are
  x 32
  y 136
  z 32

  if you want more explosions you'll need func explosive

  it will fire all its targets upon death
*/
void SP_Props_RadioSEVEN (gentity_t *ent)
{

	if (!ent->model) {
		G_Printf( S_COLOR_RED "props_radio with NULL model\n" );
		return;
	}

	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 100;

	ent->takedamage = qtrue;

	ent->die = props_radio_dieSEVEN;

	ent->count = 2; // metal shard and sound

	trap_LinkEntity (ent);
	
}


void locker_tall_think (gentity_t *ent)
{
	if (ent->s.frame == 30)
	{
		G_UseTargets (ent, NULL);
		
	}
	else
	{
		ent->s.frame++;
		ent->nextthink = level.time + (FRAMETIME/2);
	}
	
}

void props_locker_tall_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->think = locker_tall_think;
	ent->nextthink = level.time + FRAMETIME;

	ent->takedamage = qfalse;

	G_UseTargets (ent, NULL);
}

/*QUAKED props_locker_tall (.8 .6 .2) ? 
requires an origin brush
*/
void SP_Props_Locker_Tall (gentity_t *ent)
{

	// Ridah, had to add this so I could load castle18dk7
	if (!ent->model) {
		G_Printf( S_COLOR_RED "props_locker_tall with NULL model\n" );
		return;
	}

	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 100;

	ent->takedamage = qtrue;

	ent->die = props_locker_tall_die;

	trap_LinkEntity (ent);

}

/*QUAKED props_chair_chat(.8 .6 .2) (-16 -16 0) (16 16 32)
point entity
health = default = 10
wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

/*QUAKED props_chair_chatarm(.8 .6 .2) (-16 -16 0) (16 16 32)
point entity
health = default = 10
wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

/*QUAKED props_chair_side (.8 .6 .2) (-16 -16 0) (16 16 32)
point entity
health = default = 10
wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/


/*QUAKED props_chair_hiback (.8 .6 .2) (-16 -16 0) (16 16 32)
point entity
health = default = 10
wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

/*QUAKED props_chair (.8 .6 .2) (-16 -16 0) (16 16 32)
point entity
health = default = 10
wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/
void Props_Chair_Think (gentity_t *self);
void Props_Chair_Touch (gentity_t *self, gentity_t *other, trace_t *trace);
void Props_Chair_Die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);

void Just_Got_Thrown (gentity_t *self)
{
	float	len;
	vec3_t	vec;
	qboolean prop_hits = qfalse;

	len = 0;

	if (self->s.groundEntityNum == -1)
	{
		self->nextthink = level.time + FRAMETIME;
		
		if (self->enemy)
		{
			gentity_t *player;
	
			player = AICast_FindEntityForName( "player" );

			if (player && player != self->enemy)
			{
				prop_hits = qtrue;
				G_Damage ( self->enemy, self, self, NULL, NULL, 5, 0, MOD_CRUSH );
				
				self->die = Props_Chair_Die;
	
				self->die (self, self, NULL, 10, 0);
			}
		}
			
		return;
	}
	else
	{
		// RF, alert AI of sound event
		AICast_AudibleEvent( self->s.number, self->r.currentOrigin, 384 );

		G_AddEvent( self, EV_GENERAL_SOUND, snd_chairhitground );
		VectorSubtract (self->r.currentOrigin, self->s.origin2, vec);
		len = VectorLength (vec);
				
		{
			trace_t		trace;
			vec3_t		end;
			gentity_t	*traceEnt;
			gentity_t   *player;

			VectorCopy (self->r.currentOrigin, end);
			end[2] += 1;

			trap_Trace( &trace, self->r.currentOrigin, self->r.mins, self->r.maxs, end, self->s.number, MASK_SHOT );

			traceEnt = &g_entities[ trace.entityNum ];

			if (trace.startsolid)
			{
				player = AICast_FindEntityForName( "player" );

				if (traceEnt == player && traceEnt->health >= 0)
				{
					// pick the chair back up
					self->active = qtrue;
					self->r.ownerNum = player->s.number;
					player->active = qtrue;
					player->melee = self;
					self->nextthink = level.time + 50;

					self->think = Props_Chair_Think;
					self->touch = NULL;
					self->die = Props_Chair_Die;
					self->s.eType = ET_MOVER;
					self->s.dmgFlags = HINT_CHAIR;	// so client knows what kind of mover it is for cursorhints

					player->client->ps.eFlags |= EF_MELEE_ACTIVE;

					trap_LinkEntity (self);

					return;
				}
				else
					len = 9999;
			}
		}
		
	}

	self->think = Props_Chair_Think;
	self->touch = Props_Chair_Touch;
	self->die = Props_Chair_Die;
	self->s.eType = ET_MOVER;
	self->s.dmgFlags = HINT_CHAIR;	// so client knows what kind of mover it is for cursorhints
	
	self->nextthink = level.time + FRAMETIME;

	self->r.ownerNum = self->s.number;
	
	if (len > 256)
	{
		self->die (self, self, NULL, 10, 0);
	}

}

void Props_TurnLightsOff (gentity_t *self)
{
	if (!Q_stricmp (self->classname, "props_desklamp"))
	{
		if (self->target)
		{
			G_UseTargets (self, NULL);
			self->target = NULL;
		}
	}
}

void Props_Activated (gentity_t *self)
{
	vec3_t	angles;
	vec3_t	dest;
	vec3_t	forward, right;
	vec3_t	velocity;
	vec3_t	prop_ang;

	gentity_t *prop;
			
	gentity_t	*owner;

	owner = &g_entities[self->r.ownerNum];

	self->nextthink = level.time + 50;

	if (!owner->client)
	{
		return;
	}

	Props_TurnLightsOff (self);

	if (owner->active == qfalse)
	{

		owner->melee = NULL;

		self->physicsObject = qtrue;
		self->physicsBounce = 0.2;

		self->s.groundEntityNum = -1;
		
		self->s.pos.trType = TR_GRAVITY;
		self->s.pos.trTime = level.time;

		self->active = qfalse;

		G_AddEvent( owner, EV_GENERAL_SOUND, snd_chairthrow );

		AngleVectors( owner->client->ps.viewangles, velocity, NULL, NULL );
		VectorScale( velocity, 250, velocity );
		velocity[2] += 100 + crandom() * 25;
		VectorCopy ( velocity, self->s.pos.trDelta );

		self->think = NULL;
		self->nextthink = 0;

		prop = G_Spawn ();
		prop->s.modelindex = self->s.modelindex;
		G_SetOrigin (prop, self->r.currentOrigin);

		VectorCopy (owner->client->ps.viewangles, prop_ang);
		prop_ang[0] = 0;

		G_SetAngle (prop, prop_ang); 

		prop->clipmask   = CONTENTS_SOLID;
		prop->r.contents = CONTENTS_SOLID;
		prop->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
		prop->isProp = qtrue;

		VectorSet (prop->r.mins, -12, -12, 0);
		VectorSet (prop->r.maxs, 12, 12, 48);

		prop->physicsObject = qtrue;
		prop->physicsBounce = 0.2;

		VectorCopy (owner->client->ps.origin, prop->s.pos.trBase);
		
		VectorCopy (self->s.pos.trDelta, prop->s.pos.trDelta);

		prop->s.pos.trType = TR_GRAVITY;
		prop->s.pos.trTime = level.time;

		prop->active = qfalse;

		prop->health = self->health;

		prop->duration = self->health; 

		prop->count = self->count;

		prop->think = Just_Got_Thrown;
		prop->nextthink = level.time + FRAMETIME;
		
		prop->takedamage = qtrue;

		prop->wait = self->wait;

		prop->classname = self->classname;

		prop->s.groundEntityNum = -1;

		VectorCopy (self->r.currentOrigin, prop->s.origin2);
		
		prop->die = Props_Chair_Die;

		prop->r.ownerNum = owner->s.number;

		trap_LinkEntity (prop);
		
		G_FreeEntity (self);

		return;
	}
	else
	{
		if (!Q_stricmp (self->classname, "props_chair_hiback"))
		{
			self->s.frame = 23;
			self->s.density = 1;
		}
		else if (!Q_stricmp (self->classname, "props_chair"))
		{
			self->s.frame = 28;
			self->s.density = 1;
		}
		else if (!Q_stricmp (self->classname, "props_chair_side"))
		{
			self->s.frame = 23;
			self->s.density = 1;
		}
	}
	
	trap_UnlinkEntity (self);

	// move the entity in step with the activators movement
	VectorCopy (owner->client->ps.viewangles, angles);
	angles[0] = 0;

	self->s.apos.trBase[YAW] = owner->client->ps.viewangles[YAW];

	AngleVectors (angles, forward, right, NULL);
	VectorCopy (owner->r.currentOrigin, dest);

	VectorCopy (dest, self->r.currentOrigin);
	VectorCopy (dest, self->s.pos.trBase);

	self->s.eType = ET_PROP;

	trap_LinkEntity (self);

}

void Prop_Check_Ground (gentity_t *self);

void Props_Chair_Think (gentity_t *self)
{
	trace_t tr;

	if (self->active)
	{
		Props_Activated (self);
		return;
	}

	trap_UnlinkEntity (self);

	BG_EvaluateTrajectory( &self->s.pos, level.time, self->s.pos.trBase );

	if (level.time > self->s.pos.trDuration)
	{
		VectorClear (self->s.pos.trDelta);
		self->s.pos.trDuration = 0;
		self->s.pos.trType = TR_STATIONARY;
	}
	else
	{
		vec3_t mins, maxs;
		
		VectorCopy (self->r.mins, mins);
		VectorCopy (self->r.maxs, maxs);
		
		mins[2] += 1;
		
		trap_Trace( &tr, self->r.currentOrigin, mins, maxs, self->s.pos.trBase, self->s.number, MASK_SHOT );
		
		if ( tr.fraction == 1 ) 
			VectorCopy( self->s.pos.trBase, self->r.currentOrigin );
		else
		{
			VectorCopy ( self->r.currentOrigin, self->s.pos.trBase);
			VectorClear (self->s.pos.trDelta);
			self->s.pos.trDuration = 0;
			self->s.pos.trType = TR_STATIONARY;
		}
			
	}

	if ( self->s.groundEntityNum == -1 ) {

		self->physicsObject = qtrue;
		self->physicsBounce = 0.2;

		self->s.pos.trDelta[2] -= 200;

		self->s.pos.trType = TR_GRAVITY;
		self->s.pos.trTime = level.time;

		self->active = qfalse;

		self->think = Just_Got_Thrown;

		if ( self->s.pos.trType != TR_GRAVITY ) {
			self->s.pos.trType = TR_GRAVITY;
			self->s.pos.trTime = level.time;
		}
	}


	Prop_Check_Ground (self);


	self->nextthink = level.time + 50;
	trap_LinkEntity (self);
}

qboolean Prop_Touch (gentity_t *self, gentity_t *other, vec3_t v)
{

	vec3_t forward;
	vec3_t dest;
	vec3_t angle;
	vec3_t start, end;
	vec3_t	mins, maxs;
	trace_t tr;

	if (!other->client)
		return qfalse;

	vectoangles (v, angle);
	angle[0] = 0;
	AngleVectors (angle, forward, NULL, NULL);
	VectorClear (dest);
	VectorMA (dest, 128, forward, dest);
	VectorMA (self->r.currentOrigin, 32, forward, end);

	VectorCopy (self->r.currentOrigin, start);
	end[2] += 8;
	start[2] += 8;

	VectorCopy (self->r.mins, mins);
	VectorCopy (self->r.maxs, maxs);

	mins[2] += 1;

	trap_Trace( &tr, start, mins, maxs, end, self->s.number, MASK_SHOT );

	if (tr.fraction != 1)
	{
		return qfalse;
	}

	VectorCopy (dest, self->s.pos.trDelta);
	VectorCopy (self->r.currentOrigin, self->s.pos.trBase);
		
	self->s.pos.trDuration = level.time + 100;
	self->s.pos.trTime = level.time;
	self->s.pos.trType = TR_LINEAR;

	self->physicsObject = qtrue;

	return qtrue;
}

void Prop_Check_Ground (gentity_t *self)
{
	vec3_t	mins, maxs;
	vec3_t	start, end;
	trace_t	tr;

	VectorCopy (self->r.currentOrigin, start);
	VectorCopy (self->r.currentOrigin, end);

	end[2] -= 4;
	
	VectorCopy (self->r.mins, mins);
	VectorCopy (self->r.maxs, maxs);

	trap_Trace( &tr, start, mins, maxs, end, self->s.number, MASK_SHOT );

	if (tr.fraction == 1)
		self->s.groundEntityNum = -1;

}

void Props_Chair_Touch (gentity_t *self, gentity_t *other, trace_t *trace)
{
	vec3_t	v;
	qboolean has_moved;

	if (!other->client)
		return;

	if ( other->r.currentOrigin[2] > (self->r.currentOrigin[2]+10+15))
		return;

	if ( self->active ) // someone has activated me
		return;
	
	VectorSubtract (self->r.currentOrigin, other->r.currentOrigin, v);

	has_moved = Prop_Touch (self, other, v);

	if (!has_moved && (other->r.svFlags & SVF_CASTAI))
	{
		// RF, alert AI of sound event
		AICast_AudibleEvent( self->s.number, self->r.currentOrigin, 384 );

		// other could play kick animation here
		Props_Chair_Die (self, other, other, 100, 0);		
		return;
	}

	Prop_Check_Ground (self);

	if (level.time > self->random && has_moved)
	{
		// RF, alert AI of sound event
		AICast_AudibleEvent( self->s.number, self->r.currentOrigin, 384 );

		G_AddEvent( self, EV_GENERAL_SOUND, snd_chaircreak );
		self->random = level.time + 1000 + (rand()%200);
	}

	if (!Q_stricmp (self->classname, "props_desklamp"))
	{
		// player may have picked it up before
		if (self->target)
		{
			G_UseTargets (self, NULL);
			self->target = NULL;
		}
	}

}

void Props_Chair_Animate (gentity_t *ent)
{
	
	ent->touch = NULL;

	if (!Q_stricmp (ent->classname, "props_chair"))
	{
		if (ent->s.frame >= 27)
		{
			ent->s.frame = 27;
			G_UseTargets (ent, NULL);
			ent->think = G_FreeEntity;
			ent->nextthink = level.time + 2000;
			ent->s.time = level.time;
			ent->s.time2 = level.time + 2000;
			return;
		}
		else
		{
			ent->nextthink = level.time + (FRAMETIME/2);
		}
	}
	else if(
		(!Q_stricmp (ent->classname, "props_chair_side")) ||
		(!Q_stricmp (ent->classname, "props_chair_chat")) ||
		(!Q_stricmp (ent->classname, "props_chair_chatarm")) ||
		(!Q_stricmp (ent->classname, "props_chair_hiback"))
		)
	{
		if (ent->s.frame >= 20)
		{
			ent->s.frame = 20;
			G_UseTargets (ent, NULL);
			ent->think = G_FreeEntity;
			ent->nextthink = level.time + 2000;
			ent->s.time = level.time;
			ent->s.time2 = level.time + 2000;
			return;
		}
		else
		{
			ent->nextthink = level.time + (FRAMETIME/2);
		}
	}
	else if (!Q_stricmp (ent->classname, "props_desklamp"))
	{
		if (ent->s.frame >= 11)
		{
			// player may have picked it up before
			if (ent->target)
				G_UseTargets (ent, NULL);

			ent->think = G_FreeEntity;
			ent->nextthink = level.time + 2000;
			ent->s.time = level.time;
			ent->s.time2 = level.time + 2000;
			return;
		}
		else
		{
			ent->nextthink = level.time + (FRAMETIME/2);
		}
	}

	
	ent->s.frame ++;

	if (ent->enemy)
	{	
		float	ratio;
		vec3_t	v;

		ratio = 2.5;
		VectorSubtract (ent->r.currentOrigin, ent->enemy->r.currentOrigin, v);
		moveit (ent, vectoyaw(v), (ent->delay * ratio * FRAMETIME)*.001);
	}

}

void Spawn_Shard (gentity_t *ent, gentity_t *inflictor, int quantity, int type)
{
	gentity_t *sfx;
	vec3_t	dir, start;
				
	VectorCopy (ent->r.currentOrigin, start);

	if (!Q_stricmp (ent->classname, "props_radioSEVEN"))
	{
		start[0] += crandom () * 32;
		start[1] += crandom () * 32;
		VectorSubtract (inflictor->r.currentOrigin, ent->r.currentOrigin, dir);
		VectorNormalize (dir);
	}
	else if (inflictor)
	{
		VectorSubtract (inflictor->r.currentOrigin, ent->r.currentOrigin, dir);
		VectorNormalize (dir);
		VectorNegate (dir, dir);
	}
	else
		VectorSet (dir , 0,0,1);

	sfx = G_Spawn ();

	sfx->s.density = type;

	if (type < 4)
		start[2] += 32;

	G_SetOrigin (sfx, start); 
	G_SetAngle (sfx, ent->r.currentAngles);

	G_AddEvent( sfx, EV_SHARD, DirToByte( dir ));

	sfx->think = G_FreeEntity;

	sfx->nextthink = level.time + 1000;

	sfx->s.frame = quantity;
				
	trap_LinkEntity (sfx);
}

void Prop_Break_Sound (gentity_t *ent)
{
	switch (ent->count)
	{
	case shard_wood: 
		G_AddEvent( ent, EV_GENERAL_SOUND, snd_boardbreak );				
		break;
	case shard_glass:
		G_AddEvent( ent, EV_GENERAL_SOUND, snd_glassbreak );				
		break;
	case shard_metal:
		G_AddEvent( ent, EV_GENERAL_SOUND, snd_metalbreak );				
		break;
	case shard_ceramic:
		G_AddEvent( ent, EV_GENERAL_SOUND, snd_ceramicbreak );				
		break;
	}
}


void Props_Chair_Die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	int		quantity;
	int		type;
		
	if( g_gametype.integer == GT_SINGLE_PLAYER )
	{
		gentity_t *player;
	
		player = AICast_FindEntityForName( "player" );

		if (player && player->melee == ent)
		{
			player->melee = NULL;
			player->active = qfalse;
			player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;

		}
		else if (player && player->s.number == ent->r.ownerNum)
		{
			player->active = qfalse;
			player->melee = NULL;
			player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
		}
	}

	ent->think = Props_Chair_Animate;
	ent->nextthink = level.time + FRAMETIME;

	ent->health = ent->duration;
	ent->delay = damage;
	ent->takedamage = qfalse;
//	ent->enemy = inflictor;

	quantity = ent->wait;
	type = ent->count;

	Spawn_Shard (ent, inflictor, quantity, type);

	Prop_Break_Sound (ent);

	trap_UnlinkEntity (ent);

	ent->clipmask   = 0;
	ent->r.contents = 0;
	ent->s.eType = ET_GENERAL;

	trap_LinkEntity (ent);

}

void Props_Chair_Skyboxtouch (gentity_t *ent)
{

	gentity_t *player;
				
	player = AICast_FindEntityForName( "player" );

	if (player && player->melee == ent)
	{
		player->melee = NULL;
		player->active = qfalse;
		player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
	}
	else if (player && player->s.number == ent->r.ownerNum)
	{
		player->active = qfalse;
		player->melee = NULL;
		player->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
	}

	ent->think = G_FreeEntity;

}

void SP_Props_Chair (gentity_t *ent)
{
	int mass;

	ent->s.modelindex = G_ModelIndex( "models/furniture/chair/chair_office3.md3" );

	ent->delay = 0; // inherits damage value
	
	if(G_SpawnInt("mass", "5", &mass))
		ent->wait = mass;
	else
		ent->wait = 5;
		
	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;
	ent->s.dmgFlags = HINT_CHAIR;	// so client knows what kind of mover it is for cursorhints
	
	ent->isProp = qtrue;

	VectorSet (ent->r.mins, -12, -12, 0);
	VectorSet (ent->r.maxs, 12, 12, 48);

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->health)
		ent->health = 10;

	ent->duration = ent->health; 

	if (!ent->count)
		ent->count = 1;

	ent->think = Props_Chair_Think;
	ent->nextthink = level.time + FRAMETIME;

	ent->touch = Props_Chair_Touch;
	ent->die = Props_Chair_Die;
	ent->takedamage = qtrue;
	trap_LinkEntity (ent);

	snd_boardbreak = G_SoundIndex ( "sound/world/boardbreak.wav");
	snd_glassbreak = G_SoundIndex ( "sound/world/glassbreak.wav");
	snd_metalbreak = G_SoundIndex ( "sound/world/metalbreak.wav");
	snd_ceramicbreak = G_SoundIndex ( "sound/world/ceramicbreak.wav");
	snd_chaircreak = G_SoundIndex ("sound/world/chaircreak.wav");
	
}

void SP_Props_ChairHiback (gentity_t *ent)
{
	int mass;

	// ent->s.modelindex = G_ModelIndex( "models/furniture/chair/hiback.md3" );
	ent->s.modelindex = G_ModelIndex( "models/furniture/chair/hiback5.md3" );
	
	ent->delay = 0; // inherits damage value
	
	if(G_SpawnInt("mass", "5", &mass))
		ent->wait = mass;
	else
		ent->wait = 5;
		
	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;
	ent->s.dmgFlags = HINT_CHAIR;	// so client knows what kind of mover it is for cursorhints
	
	ent->isProp = qtrue;

	VectorSet (ent->r.mins, -12, -12, 0);
	VectorSet (ent->r.maxs, 12, 12, 48);

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->health)
		ent->health = 10;

	ent->duration = ent->health; 

	if (!ent->count)
		ent->count = 1;

	ent->think = Props_Chair_Think;
	ent->nextthink = level.time + FRAMETIME;

	ent->touch = Props_Chair_Touch;
	ent->die = Props_Chair_Die;
	ent->takedamage = qtrue;
	trap_LinkEntity (ent);

	snd_boardbreak = G_SoundIndex ( "sound/world/boardbreak.wav");
	snd_glassbreak = G_SoundIndex ( "sound/world/glassbreak.wav");
	snd_metalbreak = G_SoundIndex ( "sound/world/metalbreak.wav");
	snd_ceramicbreak = G_SoundIndex ( "sound/world/ceramicbreak.wav");
	snd_chaircreak = G_SoundIndex ("sound/world/chaircreak.wav");
}

void SP_Props_ChairSide (gentity_t *ent)
{
	int mass;

	ent->s.modelindex = G_ModelIndex( "models/furniture/chair/sidechair3.md3" );
	
	ent->delay = 0; // inherits damage value
	
	if(G_SpawnInt("mass", "5", &mass))
		ent->wait = mass;
	else
		ent->wait = 5;
		
	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;
	ent->s.dmgFlags = HINT_CHAIR;	// so client knows what kind of mover it is for cursorhints

	ent->isProp = qtrue;

	VectorSet (ent->r.mins, -12, -12, 0);
	VectorSet (ent->r.maxs, 12, 12, 48);

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->health)
		ent->health = 10;

	ent->duration = ent->health; 

	if (!ent->count)
		ent->count = 1;

	ent->think = Props_Chair_Think;
	ent->nextthink = level.time + FRAMETIME;

	ent->touch = Props_Chair_Touch;
	ent->die = Props_Chair_Die;
	ent->takedamage = qtrue;
	trap_LinkEntity (ent);

	snd_boardbreak = G_SoundIndex ( "sound/world/boardbreak.wav");
	snd_glassbreak = G_SoundIndex ( "sound/world/glassbreak.wav");
	snd_metalbreak = G_SoundIndex ( "sound/world/metalbreak.wav");
	snd_ceramicbreak = G_SoundIndex ( "sound/world/ceramicbreak.wav");
	snd_chaircreak = G_SoundIndex ("sound/world/chaircreak.wav");
	snd_chairthrow = G_SoundIndex ("sound/props/throw/chairthudgrunt.wav");
	snd_chairhitground = G_SoundIndex ("sound/props/chair/chairthud.wav");
}

//----(SA)	modified

// can be one of two types, but they have the same animations/etc, so re-use what you can
/*
==============
SP_Props_ChateauChair
==============
*/
void SP_Props_ChateauChair(gentity_t *ent) {
	int mass;

	ent->delay = 0; // inherits damage value
	
	if(G_SpawnInt("mass", "5", &mass))
		ent->wait = mass;
	else
		ent->wait = 5;
		
	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;
	ent->s.dmgFlags = HINT_CHAIR;	// so client knows what kind of mover it is for cursorhints

	ent->isProp = qtrue;

	VectorSet (ent->r.mins, -12, -12, 0);
	VectorSet (ent->r.maxs, 12, 12, 48);

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->health)
		ent->health = 10;

	ent->duration = ent->health; 

	if (!ent->count)
		ent->count = 1;

	ent->think = Props_Chair_Think;
	ent->nextthink = level.time + FRAMETIME;

	ent->touch = Props_Chair_Touch;
	ent->die = Props_Chair_Die;
	ent->takedamage = qtrue;
	trap_LinkEntity (ent);

	snd_boardbreak = G_SoundIndex ( "sound/world/boardbreak.wav");
	snd_glassbreak = G_SoundIndex ( "sound/world/glassbreak.wav");
	snd_metalbreak = G_SoundIndex ( "sound/world/metalbreak.wav");
	snd_ceramicbreak = G_SoundIndex ( "sound/world/ceramicbreak.wav");
	snd_chaircreak = G_SoundIndex ("sound/world/chaircreak.wav");
	snd_chairthrow = G_SoundIndex ("sound/props/throw/chairthudgrunt.wav");
	snd_chairhitground = G_SoundIndex ("sound/props/chair/chairthud.wav");
}


/*
==============
SP_Props_ChairChat
==============
*/
void SP_Props_ChairChat(gentity_t *ent) {
	ent->s.modelindex = G_ModelIndex( "models/furniture/chair/chair_chat.md3" );
	SP_Props_ChateauChair(ent);
}
/*
==============
SP_Props_ChairChatArm
==============
*/
void SP_Props_ChairChatArm(gentity_t *ent) {
	ent->s.modelindex = G_ModelIndex( "models/furniture/chair/chair_chatarm.md3" );
	SP_Props_ChateauChair(ent);
}

//----(SA)	end


void Use_DamageInflictor (gentity_t *ent, gentity_t *other, gentity_t *activator )
{
	gentity_t *daent;

	daent = NULL;
	while ((daent = G_Find (daent, FOFS(targetname), daent->target)) != NULL) 
	{
		if ( daent == ent ) {
			G_Printf ("Use_DamageInflictor damaging self.\n");
		} 
		else 
		{
			G_Damage( daent, ent, ent, NULL, NULL, 9999, 0, MOD_CRUSH );	
		}	
	}

	G_FreeEntity (ent);
}

/*QUAKED props_damageinflictor (.8 .6 .6) (-8 -8 -8) (8 8 8)
this entity when used will cause 9999 damage to all entities it is targeting
then it will be removed
*/
void SP_Props_DamageInflictor (gentity_t *ent)
{
	G_SetOrigin (ent, ent->s.origin); 
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_GENERAL;

	ent->use = Use_DamageInflictor;
	trap_LinkEntity (ent);
}

/*QUAKED props_shard_generator (.8 .5 .1) (-4 -4 -4) (4 4 4) 

wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )
  
shard =
shard_glass = 0,
shard_wood = 1,
shard_metal = 2,
shard_ceramic = 3

*/

void Use_Props_Shard_Generator (gentity_t *ent, gentity_t *other, gentity_t *activator)
{
	int quantity;
	int	type;
	gentity_t *inflictor = NULL;

	type = ent->count;
	quantity = ent->wait;

	inflictor = G_Find (NULL, FOFS (targetname), ent->target);

	if (inflictor)
		Spawn_Shard (ent, inflictor, quantity, type);

	G_FreeEntity (ent);
}

void SP_props_shard_generator (gentity_t *ent)
{
	G_SetOrigin (ent, ent->s.origin); 
	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_GENERAL;
	ent->use = Use_Props_Shard_Generator;

	if (!ent->count)
		ent->count = shard_wood;

	if (!ent->wait)
		ent->wait = 5;

	trap_LinkEntity (ent);
}


/*QUAKED props_desklamp (.8 .6 .2) (-16 -16 0) (16 16 32)
point entity
health = default = 10
wait = defaults to 5 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/
void SP_Props_Desklamp (gentity_t *ent)
{
	int mass;

	ent->s.modelindex = G_ModelIndex( "models/furniture/lights/desklamp.md3" );
	
	ent->delay = 0; // inherits damage value
	
	if(G_SpawnInt("mass", "5", &mass))
		ent->wait = mass;
	else
		ent->wait = 2;
		
	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;
	
	ent->isProp = qtrue;
	ent->nopickup = qtrue;

	VectorSet (ent->r.mins, -6, -6, 0);
	VectorSet (ent->r.maxs, 6, 6, 14);

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->health)
		ent->health = 10;

	ent->duration = ent->health; 

	if (!ent->count)
		ent->count = 2;

	ent->think = Props_Chair_Think;
	ent->nextthink = level.time + FRAMETIME;

	ent->touch = Props_Chair_Touch;
	ent->die = Props_Chair_Die;
	ent->takedamage = qtrue;
	trap_LinkEntity (ent);

	snd_boardbreak = G_SoundIndex ( "sound/world/boardbreak.wav");
	snd_glassbreak = G_SoundIndex ( "sound/world/glassbreak.wav");
	snd_metalbreak = G_SoundIndex ( "sound/world/metalbreak.wav");
	snd_ceramicbreak = G_SoundIndex ( "sound/world/ceramicbreak.wav");
	snd_chaircreak = G_SoundIndex ("sound/world/chaircreak.wav");
}

/*QUAKED props_flamebarrel (.8 .6 .2) (-13 -13 0) (13 13 40) SMOKING NOLID OIL -
angle will determine which way the lid will fly off when it explodes

when selecting the OIL spawnflag you have the option of giving it a target
this will ensure that the oil sprite will show up where you want it
( be sure to put it on the floor )
the default is in the middle of the barrel on the floor
*/
void Props_Barrel_Touch (gentity_t *self, gentity_t *other, trace_t *trace)
{
return; // barrels cant move
	
	if (!(self->spawnflags & 4))
		Props_Chair_Touch (self, other, trace);	
}

void Props_Barrel_Animate (gentity_t *ent)
{
	float	ratio;
	vec3_t	v;

	if (ent->s.frame == 14)
	{
		if (ent->spawnflags & 1)
		{
		//	G_UseTargets (ent, NULL);
			ent->think = G_FreeEntity;
			ent->nextthink = level.time + 25000;
			return;
		}
		else 
		{
		//	G_UseTargets (ent, NULL);
			ent->think = G_FreeEntity;
			ent->nextthink = level.time + 25000;
			//ent->s.time = level.time;
			//ent->s.time2 = level.time + 2000;
			return;
		}
	}
	else
	{
		ent->nextthink = level.time + (FRAMETIME/2);
	}

	ent->s.frame ++;

	if (!(ent->spawnflags & 1))
	{
		ratio = 2.5;
		VectorSubtract (ent->r.currentOrigin, ent->enemy->r.currentOrigin, v);
		moveit (ent, vectoyaw(v), (ent->delay * ratio * FRAMETIME)*.001);
	}

}

void barrel_smoke (gentity_t *ent)
{
	gentity_t	*tent;
	vec3_t		point;
	
	VectorCopy (ent->r.currentOrigin, point);

	tent = G_TempEntity (point, EV_SMOKE);
	VectorCopy (point, tent->s.origin);
	tent->s.time = 4000;
	tent->s.time2 = 1000;
	tent->s.density = 0;
	tent->s.angles2[0] = 8;
	tent->s.angles2[1] = 64;
	tent->s.angles2[2] = 50;

}

void smoker_think (gentity_t *ent)
{
	ent->count --;

	if (!ent->count)
		G_FreeEntity (ent);
	else
	{
		barrel_smoke (ent);
		ent->nextthink = level.time + FRAMETIME;
	}

}

void SP_OilSlick (gentity_t *ent)
{
	gentity_t *tent;
	gentity_t	*target = NULL;
	vec3_t		point;

	if (ent->target)
		target = G_Find( NULL, FOFS(targetname), ent->target );
	
	if (target)
	{
		VectorCopy (target->s.origin, point);
		point[2] = ent->r.currentOrigin[2]; // just in case
	}
	else
		VectorCopy (ent->r.currentOrigin, point);

	tent = G_TempEntity (ent->r.currentOrigin, EV_OILSLICK);
	VectorCopy (ent->r.currentOrigin, tent->s.origin);
	tent->s.angles2[0] = 16;
	tent->s.angles2[1] = 48;
	tent->s.angles2[2] = 10000;
	tent->s.density = ent->s.number;
	 
}

void OilParticles_think (gentity_t *ent)
{
	gentity_t *tent;
	gentity_t	*owner;

	owner = &g_entities[ent->s.density];

	if (owner && owner->takedamage && ent->count2 > level.time - 5000)
	{
		ent->nextthink = (level.time + FRAMETIME/2);
		
		tent = G_TempEntity (ent->r.currentOrigin, EV_OILPARTICLES);
		VectorCopy (ent->r.currentOrigin, tent->s.origin);
		tent->s.time = ent->count2;
		tent->s.density = ent->s.density;
		VectorCopy (ent->rotate, tent->s.origin2);
	}
	else
		G_FreeEntity (ent);
}

void Delayed_Leak_Think (gentity_t *ent)
{
	vec3_t point;
	gentity_t *tent;

	VectorCopy (ent->r.currentOrigin, point);

	tent = G_TempEntity (point, EV_OILSLICK);
	VectorCopy (point, tent->s.origin);
		
	tent->s.angles2[0] = 0;
	tent->s.angles2[1] = 0;
	tent->s.angles2[2] = 2000;
	tent->s.density = ent->count;
}

qboolean validOilSlickSpawnPoint (vec3_t point, gentity_t *ent)
{
	trace_t tr;
	vec3_t	end;
	gentity_t *traceEnt;

	VectorCopy (point, end);
	end[2] -= 9999;
	
	trap_Trace( &tr, point, NULL, NULL, end, ent->s.number, MASK_SHOT );

	traceEnt = &g_entities[ tr.entityNum ];

	if (traceEnt && traceEnt->classname)
	{
		if (!Q_stricmp (traceEnt->classname, "worldspawn"))
		{
			if (tr.plane.normal[0] == 0 && tr.plane.normal[1] == 0 && tr.plane.normal[2] == 1)
				return qtrue;
		}
	}

	return qfalse;
	
}

void SP_OilParticles (gentity_t *ent)
{
	gentity_t *OilLeak;
	vec3_t		point;
	vec3_t		vec;
	vec3_t		forward;

// Note to self quick fix
// need to move this to client
return;

	OilLeak = G_Spawn ();

	VectorCopy (ent->r.currentOrigin, point);

	point[2] = ent->pos3[2];

	VectorSubtract (ent->pos3, point, vec);
	vectoangles (vec, vec);
	AngleVectors (vec, forward, NULL, NULL);
	VectorMA (point, 12, forward, point);

	G_SetOrigin (OilLeak, point); 

	G_SetAngle (OilLeak, ent->r.currentAngles);

	VectorCopy (forward, OilLeak->rotate);

	OilLeak->think = OilParticles_think;
	OilLeak->nextthink = level.time + FRAMETIME;

	OilLeak->s.density = ent->s.number;
	OilLeak->count2 = level.time;

	trap_LinkEntity (OilLeak);

}


void Props_Barrel_Pain (gentity_t *ent, gentity_t *attacker, int damage, vec3_t point)
{

	if (ent->health <= 0)
		return;

	if (!(ent->spawnflags & 8))
	{
		SP_OilSlick (ent);
		ent->spawnflags |= 8;
	}

	ent->count2 ++;
		
	if (ent->count2 < 6)
		SP_OilParticles (ent);

}

void OilSlick_remove_think (gentity_t *ent)
{
	gentity_t *tent;

	tent = G_TempEntity (ent->r.currentOrigin, EV_OILSLICKREMOVE);
	tent->s.density = ent->s.density;	
}

void OilSlick_remove (gentity_t *ent)
{
	gentity_t *remove;

	remove = G_Spawn();
	remove->s.density = ent->s.number;
	remove->think = OilSlick_remove_think;
	remove->nextthink = level.time + 1000;
	VectorCopy (ent->r.currentOrigin, remove->r.currentOrigin);
	trap_LinkEntity (remove);
}

void Props_Barrel_Die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	int quantity;
	int	type;
	vec3_t	dir;

	if (ent->spawnflags & 1)
		ent->s.eFlags = EF_SMOKINGBLACK;

	G_UseTargets ( ent, NULL);	

	if (ent->spawnflags & 4)
	{
		OilSlick_remove (ent);
	}

	ent->health = 100;
	propExplosion (ent);
	ent->health = 0;

	ent->takedamage = qfalse;

	AngleVectors (ent->r.currentAngles, dir, NULL, NULL);
	dir[2] = 1;
	
	if (!(ent->spawnflags & 2))
		fire_flamebarrel ( ent, ent->r.currentOrigin, dir );

	ent->touch = NULL;

	ent->think = Props_Barrel_Animate;
	ent->nextthink = level.time + FRAMETIME;

	ent->health = ent->duration;
	ent->delay = damage;
	ent->enemy = inflictor;

	quantity = ent->wait;
	type = ent->count;

	if (inflictor)
		Spawn_Shard (ent, inflictor, quantity, type);

	Prop_Break_Sound (ent);

	trap_UnlinkEntity (ent);

	ent->clipmask   = 0;
	ent->r.contents = 0;
	ent->s.eType = ET_GENERAL;

	trap_LinkEntity (ent);
}

void Props_OilSlickSlippery (gentity_t *ent)
{
	gentity_t *player;
	vec3_t		vec, kvel, dir;
	float		len;
	
	player = AICast_FindEntityForName( "player" );

	if (player)
	{
		VectorSubtract (player->r.currentOrigin, ent->r.currentOrigin, vec);
		len = VectorLength (vec);
		
		if (len < 64 && player->s.groundEntityNum != -1)
		{
			len = VectorLength (player->client->ps.velocity);

			if (len && !(player->client->ps.pm_time))
			{
				VectorSet (dir, fabs(crandom()), fabs(crandom()), 0);
				VectorScale (dir, 32, kvel);
				VectorAdd (player->client->ps.velocity, kvel, player->client->ps.velocity);

				{
					int		t;

					t = 32 * 2;
					if ( t < 50 ) 
					{
						t = 50;
					}
					if ( t > 200 ) 
					{
						t = 200;
					}
					player->client->ps.pm_time = t;
					player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
				}
	
			}

		}
	}
}

void Props_Barrel_Think (gentity_t *self)
{
	self->active = qfalse;
	Props_Chair_Think (self);

	if (self->spawnflags & 8) // there is an oil slick
		Props_OilSlickSlippery (self);
}

void SP_Props_Flamebarrel (gentity_t *ent)
{
	int mass;

	if (ent->spawnflags & 4)
		ent->s.modelindex = G_ModelIndex( "models/furniture/barrel/barrel_c.md3" );
	else if (ent->spawnflags & 1)
		ent->s.modelindex = G_ModelIndex( "models/furniture/barrel/barrel_d.md3" );
	else
		ent->s.modelindex = G_ModelIndex( "models/furniture/barrel/barrel_b.md3" );

	ent->delay = 0; // inherits damage value
	
	if(G_SpawnInt("mass", "5", &mass))
		ent->wait = mass;
	else
		ent->wait = 10;

	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->r.svFlags  = SVF_USE_CURRENT_ORIGIN;
	ent->s.eType = ET_MOVER;
	
	ent->isProp = qtrue;
	ent->nopickup = qtrue;

	VectorSet (ent->r.mins, -13, -13, 0);
	VectorSet (ent->r.maxs, 13, 13, 36);

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->health)
		ent->health = 20;

	ent->duration = ent->health; 

	ent->count = 2; // metal shards

	ent->think = Props_Barrel_Think;
	ent->nextthink = level.time + FRAMETIME;

	ent->touch = Props_Barrel_Touch;

	ent->die = Props_Barrel_Die;
	
	if (ent->spawnflags & 4)
	{
		ent->pain = Props_Barrel_Pain;
	}

	ent->takedamage = qtrue;
	trap_LinkEntity (ent);
}

/*QUAKED props_crate_64 (.8 .6 .2) (-32 -32 0) (32 32 64) 
breakable pushable

  health = default = 20
wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

/*QUAKED props_crate_32 (.8 .6 .2) (-16 -16 0) (16 16 32) 
breakable pushable

  health = default = 20
wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

/*QUAKED props_crate_32x64 (.8 .6 .2) ?
requires an origin brush

breakable NOT pushable 

brushmodel only

  health = default = 20
wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

void touch_crate_64 (gentity_t *self, gentity_t *other, trace_t *trace)
{
	float	ratio;
	vec3_t	v;

	if ( other->r.currentOrigin[2] > (self->r.currentOrigin[2]+10+31))
		return;
	
	ratio = 1.5;
	VectorSubtract (self->r.currentOrigin, other->r.currentOrigin, v);
	moveit (self, vectoyaw(v), (20 * ratio * FRAMETIME)*.001);
}

void crate_animate (gentity_t *ent)
{
	if (ent->s.frame == 17)
	{
		G_UseTargets (ent, NULL);
		ent->think = G_FreeEntity;
		ent->nextthink = level.time + 2000;
		ent->s.time = level.time;
		ent->s.time2 = level.time + 2000;
		return;
	}
	
	ent->s.frame ++;
	ent->nextthink = level.time + (FRAMETIME/2);
}

void crate_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod) 
{
	int		quantity;
	int		type;

	quantity = ent->wait;
	type = ent->count;

	Spawn_Shard (ent, inflictor, quantity, type);

	ent->takedamage = qfalse;
	ent->think = crate_animate;
	ent->nextthink = level.time + FRAMETIME;
	ent->touch = NULL;

	trap_UnlinkEntity (ent);

	ent->clipmask   = 0;
	ent->r.contents = 0;
	ent->s.eType = ET_GENERAL;

	trap_LinkEntity (ent);

}

void SP_crate_64 (gentity_t *self)
{
	self->s.modelindex = G_ModelIndex( "models/furniture/crate/crate64.md3" );

	self->clipmask   = CONTENTS_SOLID;
	self->r.contents = CONTENTS_SOLID;
	self->r.svFlags  = SVF_USE_CURRENT_ORIGIN;

	VectorSet (self->r.mins, -32, -32, 0);
	VectorSet (self->r.maxs, 32, 32, 64);
	
	self->s.eType = ET_MOVER;
	
	self->isProp = qtrue;
	self->nopickup = qtrue;
	G_SetOrigin (self, self->s.origin); 
	G_SetAngle (self, self->s.angles);

	self->touch = touch_crate_64;
	self->die = crate_die;

	self->takedamage = qtrue;

	if (!self->health)
		self->health = 20;

	if (!self->count)
		self->count = 1;

	if (!self->wait)
		self->wait = 10;

	self->isProp = qtrue;
	self->nopickup = qtrue;

	trap_LinkEntity( self );

	self->think = DropToFloor;
	self->nextthink = level.time + FRAMETIME;
}

void SP_crate_32 (gentity_t *self)
{
	self->s.modelindex = G_ModelIndex( "models/furniture/crate/crate32.md3" );

	self->clipmask   = CONTENTS_SOLID;
	self->r.contents = CONTENTS_SOLID;
	self->r.svFlags  = SVF_USE_CURRENT_ORIGIN;

	VectorSet (self->r.mins, -16, -16, 0);
	VectorSet (self->r.maxs, 16, 16, 32);
	
	self->s.eType = ET_MOVER;
	
	self->isProp = qtrue;
	self->nopickup = qtrue;
	G_SetOrigin (self, self->s.origin); 
	G_SetAngle (self, self->s.angles);

	self->touch = touch_crate_64;
	self->die = crate_die;

	self->takedamage = qtrue;

	if (!self->health)
		self->health = 20;

	if (!self->count)
		self->count = 1;

	if (!self->wait)
		self->wait = 10;

	self->isProp = qtrue;
	self->nopickup = qtrue;

	trap_LinkEntity( self );

	self->think = DropToFloor;
	self->nextthink = level.time + FRAMETIME;
}

//////////////////////////////////////////////

void props_crate32x64_think (gentity_t *ent)
{
	ent->s.frame++;

	if (ent->s.frame < 17)
		ent->nextthink = level.time + (FRAMETIME/2);
	else
	{
		ent->clipmask = 0;
		ent->r.contents = 0;
		ent->takedamage = qfalse;
		
		G_UseTargets (ent, NULL);
	}

}

void props_crate32x64_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->think = props_crate32x64_think;
	ent->nextthink = level.time + FRAMETIME;
}

void SP_Props_Crate32x64 (gentity_t *ent)
{

	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 10;

	ent->takedamage = qtrue;

	ent->clipmask = CONTENTS_SOLID;

	ent->die = props_crate32x64_die;

	trap_LinkEntity (ent);
}

/*QUAKED props_flippy_table (.8 .6 .2) ? - - X_AXIS Y_AXIS LEADER
this entity will need a leader and an origin brush 
!!!!!!!!!!!!!!
just a reminder to put the origin brush in the proper location for the leader and the 
slave so that the table will flip over correctly.
*/

void flippy_table_use (gentity_t *ent, gentity_t *other, gentity_t *activator)
{
	qboolean	is_infront;
	gentity_t	*slave;

	// it would be odd to flip a table if your standing on it
	if (other && other->s.groundEntityNum == ent->s.number)
	{
		// G_Printf ("can't push table over while standing on it\n");
		return;
	}

	ent->use = NULL;
	
	is_infront = infront (ent, other);

	if (is_infront)
	{
		// need to swap the team leader with the slave
		for ( slave = ent ; slave ; slave = slave->teamchain ) 
		{
			if (slave == ent)
			continue;
			
			slave->s.pos.trType = ent->s.pos.trType;
			slave->s.pos.trTime = ent->s.pos.trTime;
			slave->s.pos.trDuration = ent->s.pos.trDuration;
			VectorCopy (ent->s.pos.trBase, slave->s.pos.trBase);
			VectorCopy (ent->s.pos.trDelta, slave->s.pos.trDelta);
			
			slave->s.apos.trType = ent->s.apos.trType;
			slave->s.apos.trTime = ent->s.apos.trTime;
			slave->s.apos.trDuration = ent->s.apos.trDuration;
			VectorCopy (ent->s.apos.trBase, slave->s.apos.trBase);
			VectorCopy (ent->s.apos.trDelta, slave->s.apos.trDelta);

			slave->think = ent->think;
			slave->nextthink = ent->nextthink;

			VectorCopy (ent->pos1, slave->pos1);
			VectorCopy (ent->pos2, slave->pos2);

			slave->speed = ent->speed;

			slave->flags &= ~FL_TEAMSLAVE;
			// make it visible
			trap_LinkEntity (slave);
			
			Use_BinaryMover (slave, other, other);
		}
		
		trap_UnlinkEntity (ent);
	}
	else
		Use_BinaryMover (ent, other, other);
	
}

void flippy_table_animate (gentity_t *ent)
{
	return;
	
	if (ent->s.frame == 9)
	{
		G_UseTargets (ent, NULL);
		ent->think = G_FreeEntity;
		ent->nextthink = level.time + 2000;		
	}
	else
	{
		ent->s.frame++;
		ent->nextthink = level.time + (FRAMETIME/2);
	}
}

void props_flippy_table_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->think = flippy_table_animate;
	ent->nextthink = level.time + FRAMETIME;

	ent->takedamage = qfalse;

	G_UseTargets (ent, NULL);
}

void props_flippy_blocked (gentity_t *ent, gentity_t *other)
{
	vec3_t	velocity;
	vec3_t	angles;
	vec3_t	kvel;
	
	// just for now
	float	angle = ent->r.currentAngles[YAW];

	if (other->client)
	{
		// shoot the player off of it
		VectorCopy( ent->s.apos.trBase, angles );
		angles[YAW] += angle;
		angles[PITCH] = 0;	// always forward

		AngleVectors( angles, velocity, NULL, NULL );
		VectorScale( velocity, 24, velocity );
		velocity[2] += 100 + crandom() * 50;

		VectorScale (velocity, 32, kvel);
		VectorAdd (other->client->ps.velocity, kvel, other->client->ps.velocity);
	}
	else if (other->s.eType == ET_ITEM)
	{
		VectorCopy( ent->s.apos.trBase, angles );
		angles[YAW] += angle;
		angles[PITCH] = 0;	// always forward

		AngleVectors( angles, velocity, NULL, NULL );
		VectorScale( velocity, 150, velocity );
		velocity[2] += 300 + crandom() * 50;

		VectorScale (velocity, 8, kvel);
		other->s.pos.trType = TR_GRAVITY;
		other->s.pos.trTime = level.time;
		VectorCopy( kvel, other->s.pos.trDelta );

		other->s.eFlags |= EF_BOUNCE;
	}
	else
	{
		// just delete it or destroy it
		G_TempEntity( other->s.origin, EV_ITEM_POP );
		G_FreeEntity( other );
		return;
	}
}

void SP_Props_Flipping_Table (gentity_t *ent)
{

	if (!ent->model) {
		G_Printf( S_COLOR_RED "props_Flipping_Table with NULL model\n" );
		return;
	}

	trap_SetBrushModel( ent, ent->model );

	ent->speed = 500;
	ent->angle = 90;

	// ent->spawnflags |= 8;
	if (!(ent->spawnflags & 4) && !(ent->spawnflags & 8))
		G_Printf ("you forgot to select the X or Y Axis\n");

	VectorClear (ent->rotate);

	if		( ent->spawnflags & 4 )	ent->rotate[2] = 1;
	else if	( ent->spawnflags & 8 )	ent->rotate[0] = 1;
	else							ent->rotate[1] = 1;

	ent->spawnflags |= 64; // stay open

	InitMoverRotate( ent );

	VectorCopy( ent->s.origin, ent->s.pos.trBase );
	VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
	VectorCopy( ent->s.apos.trBase, ent->r.currentAngles );

	ent->blocked = props_flippy_blocked;

	if (!ent->health)
		ent->health = 100;

	ent->wait *= 1000;

	//ent->takedamage = qtrue;

	//ent->die = props_flippy_table_die;

	ent->use = flippy_table_use;
		
	trap_LinkEntity (ent);

}


/*QUAKED props_58x112tablew (.8 .6 .2) ?
dimensions are 58 x 112 x 32 (x,y,z)

requires an origin brush

breakable NOT pushable 

brushmodel only

  health = default = 10
wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

void props_58x112tablew_think (gentity_t *ent)
{
	ent->s.frame++;

	if (ent->s.frame < 16)
		ent->nextthink = level.time + (FRAMETIME/2);
	else
	{
		
		ent->clipmask = 0;
		ent->r.contents = 0;
		
		G_UseTargets (ent, NULL);
	}

}

void props_58x112tablew_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->think = props_58x112tablew_think;
	ent->nextthink = level.time + FRAMETIME;
	ent->takedamage = qfalse;
}

void SP_Props_58x112tablew (gentity_t *ent)
{

	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 10;

	ent->takedamage = qtrue;

	ent->clipmask = CONTENTS_SOLID;

	ent->die = props_58x112tablew_die;

	trap_LinkEntity (ent);
}

/*QUAKED props_castlebed (.8 .6 .2) ?
dimensions are 112 x 128 x 80 (x,y,z)

requires an origin brush

breakable NOT pushable 

brushmodel only

  health = default = 20
wait = defaults to 10 how many shards to spawn ( try not to exceed 20 )

shard =
	shard_glass = 0,
	shard_wood = 1,
	shard_metal = 2,
	shard_ceramic = 3

*/

void props_castlebed_touch (gentity_t *ent, gentity_t *other, trace_t *trace)
{
	if (!other->client)
		return;

	if (other->client->ps.pm_flags & PMF_JUMP_HELD 
		&& other->s.groundEntityNum == ent->s.number
		&& !other->client->ps.pm_time)
	{
		G_Damage ( ent, other, other, NULL, NULL, 1, 0, MOD_CRUSH);
	
		// TDB: need sound of bed springs for this
		G_Printf ("SOUND sqweeky\n");
		
		other->client->ps.velocity[2] += 250;

		other->client->ps.pm_time = 250;
		other->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	}
	
}

void props_castlebed_animate (gentity_t *ent)
{
	ent->s.frame ++;

	if (ent->s.frame < 8)
	{
		ent->nextthink = level.time + (FRAMETIME/2);
	}
	else
	{
		ent->clipmask = 0;
		ent->r.contents = 0;
		G_UseTargets (ent, NULL);
	}
}

void props_castlebed_die (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->think = props_castlebed_animate;
	ent->nextthink = level.time + FRAMETIME;
	ent->touch = NULL;
	ent->takedamage = qfalse;

	ent->count = shard_wood;
	Prop_Break_Sound (ent);
}

void SP_props_castlebed (gentity_t *ent)
{
	trap_SetBrushModel( ent, ent->model );

	InitProp (ent);

	if (!ent->health)
		ent->health = 20;

	ent->takedamage = qtrue;

	ent->clipmask = CONTENTS_SOLID;

	ent->die = props_castlebed_die;
	ent->touch = props_castlebed_touch;

	trap_LinkEntity (ent);
}

/*QUAKED props_snowGenerator (3 2 7) ? TOGGLE_ON ALWAYS_ON
entity brush need to be targeted to an info notnull this 
will determine the direction the snow particles will travel.

speed 
gravity
turb

count is the number of snowflurries 3 to 5 would be a good number

duration is how long the effect will last 1 is 1 second
*/

void props_snowGenerator_think (gentity_t *ent)
{
	gentity_t	*tent;
	float		high, wide, deep;
	int			i;
	vec3_t		point;

	if (!(ent->spawnflags & 1))
		return;
	
	high = ent->r.maxs[2] - ent->r.mins[2];
	wide = ent->r.maxs[1] - ent->r.mins[1];
	deep = ent->r.maxs[0] - ent->r.mins[0];

	for (i=0; i<ent->count;i++)
	{
		VectorCopy (ent->pos1, point);

		// we need to randomize to the extent of the brush
		point[0] += crandom() * (deep * 0.5); 
		point[1] += crandom() * (wide * 0.5); 
		point[2] += crandom() * (high * 0.5); 
		
		tent = G_TempEntity (point, EV_SNOWFLURRY);
		VectorCopy (point, tent->s.origin);
		VectorCopy (ent->movedir, tent->s.angles);
		tent->s.time = 2000; // life time
		tent->s.time2 = 1000; // alpha fade start
	}
	
	if (ent->spawnflags & 2)
		ent->nextthink = level.time + FRAMETIME;
	else if (ent->wait < level.time)
		ent->nextthink = level.time + FRAMETIME;
}

void props_snowGenerator_use (gentity_t *ent, gentity_t *other, gentity_t *activator)
{
	if (!(ent->spawnflags & 1))
	{
		ent->spawnflags |= 1;
		ent->think = props_snowGenerator_think;
		ent->nextthink = level.time + FRAMETIME;
		ent->wait = level.time + ent->duration;
	}
	else 
		ent->spawnflags &= ~1;
}

void SP_props_snowGenerator (gentity_t *ent)
{
	vec3_t		center;
	gentity_t	*target = NULL;

	trap_SetBrushModel( ent, ent->model );

	VectorAdd (ent->r.absmin, ent->r.absmax, center);
	VectorScale (center, 0.5, center);

	VectorCopy (center, ent->pos1);
	
	if (!ent->target)
	{
		G_Printf ("snowGenerator at loc %s does not have a target\n", vtos (center));
		return;
	}
	else
	{
		target = G_Find (target, FOFS (targetname), ent->target);
		if (!target)
		{
			G_Printf ("error snowGenerator at loc %s does cant find target %s\n", vtos (center), ent->target);
			return;
		}
		
		VectorSubtract (target->s.origin, ent->s.origin, ent->movedir);
		VectorNormalize (ent->movedir);
	}
	
	ent->r.contents = CONTENTS_TRIGGER;
	ent->r.svFlags = SVF_NOCLIENT;

	if (ent->spawnflags & 1 || ent->spawnflags & 2)
	{
		ent->think = props_snowGenerator_think;
		ent->nextthink = level.time + FRAMETIME;
		
		if (ent->spawnflags & 2)
			ent->spawnflags |= 1;
	}

	ent->use = props_snowGenerator_use;

	if (!(ent->delay))
		ent->delay = 100;
	else 
		ent->delay *= 100;

	if (!(ent->count))
		ent->count = 32;

	if (!(ent->duration))
		ent->duration = 1;

	ent->duration *= 1000;

	trap_LinkEntity (ent);
}

/////////////////////////////
// FIRES AND EXPLOSION PROPS
/////////////////////////////

/*QUAKED props_FireColumn (.3 .2 .7) (-8 -8 -8) (8 8 8) CORKSCREW SMOKE GRAVITY HALFGRAVITY 
this entity will require a target use an infonotnull to specifiy its direction

defaults:
	will leave a flaming trail by default
	will not be affected by gravity

radius = distance flame will corkscrew from origin
speed = default is 900
duration = default is 3 sec
 
start_size = default is 5
end_size = defaults 7 thru 17
count = defaults 100 thru 500 

Pending:   
delay before it happens again use trigger_relay for now
assign a model
*/

void propsFireColumnUse (gentity_t *ent, gentity_t *other, gentity_t *activator)
{
	gentity_t	*tent;
	vec3_t		start, dir;
	
	VectorCopy (ent->s.origin, start);

	AngleVectors (ent->r.currentAngles, dir, NULL, NULL);

	tent = fire_flamebarrel ( ent, start, dir );

	if (!tent)
		return;		

	if (ent->spawnflags & 2)
		tent->s.eType = ET_FIRE_COLUMN_SMOKE;
	else
		tent->s.eType = ET_FIRE_COLUMN;

	if (ent->spawnflags & 4)
		tent->s.pos.trType = TR_GRAVITY;
	else if (ent->spawnflags & 8)
		tent->s.pos.trType = TR_GRAVITY_LOW;
	else
		tent->s.pos.trType = TR_LINEAR;

	if (ent->spawnflags & 1)
	{
		tent->s.density = ent->radius; // corkscrew effect
	}

	tent->flags |= FL_NODRAW;
	//tent->s.eFlags |= EF_NODRAW;

	// TBD
	// lifetime
	if (ent->duration)
		tent->nextthink = level.time + ent->duration;
	
	// speed
	if (ent->speed)
	{
		VectorClear (tent->s.pos.trDelta);
		VectorScale (dir, ent->speed + (crandom() * 100), tent->s.pos.trDelta);
		SnapVector (tent->s.pos.trDelta);			
		VectorCopy (start, tent->r.currentOrigin);
	}

	if (ent->start_size)
		tent->s.angles[1] = ent->start_size;

	if (ent->end_size)
		tent->s.angles[2] = ent->end_size;

	if (ent->count)
		tent->s.angles[0] = ent->count;

	G_SetAngle (tent, ent->r.currentAngles);
}

void propsFireColumnInit (gentity_t *ent)
{
	gentity_t	*target;
	vec3_t		vec;
	vec3_t		angles;

	if (ent->target)
	{
		target = G_Find (NULL, FOFS (targetname), ent->target);	
		VectorSubtract (target->s.origin, ent->s.origin, vec);
		vectoangles (vec, angles);
		G_SetAngle (ent, angles);
	}
	else
	{
		// ok then just up
		VectorSet (vec, 0, 0, 1);
		vectoangles (vec, angles);
		G_SetAngle (ent, angles);
	}

	if (ent->duration)
		ent->duration = ent->duration * 1000;


}

void SP_propsFireColumn (gentity_t *ent)
{
	G_SetOrigin (ent, ent->s.origin);
	ent->think = propsFireColumnInit;
	ent->nextthink = level.time + FRAMETIME;
	ent->use = propsFireColumnUse;
	trap_LinkEntity (ent);
}

/*QUAKED props_ExploPart (.3 .5 .7) (-8 -8 -16) (8 8 16) 
"model" will load a discreet model
"noise" will load looping sound for the model
"target" point to an infonotnull to specify dir default will be up
"speed"	default to 900

"type"  wood concrete or stone
"count"in the absense of a model count will determine the piece to spawn
for wood:
  it can be one of the following 64 48 32 24 16 8
for concrete:
for stone:
*/
#define EXPLOPARTPIECES	8

void props_ExploPartUse (gentity_t *ent, gentity_t *other, gentity_t *activator)
{
//	int i, numpieces;
	gentity_t *part;
	vec3_t	start, dir;

	VectorCopy (ent->s.origin, start);
	AngleVectors (ent->r.currentAngles, dir, NULL, NULL);

	if (ent->s.modelindex)
	{
		part = fire_flamebarrel (ent, start, dir);
		part->s.modelindex = ent->s.modelindex;
	}
	else
	{
		G_Printf ("props_ExploPartUse has not been assigned a model\n");
		return;
	}

	if (part)
	{
		part->s.pos.trType = TR_GRAVITY;
		part->s.eType = ET_EXPLO_PART;

		G_SetAngle (part, ent->r.currentAngles);

		if (ent->speed)
		{
			VectorClear (part->s.pos.trDelta);
			VectorScale (dir, ent->speed + (crandom() * 100), part->s.pos.trDelta);
			SnapVector (part->s.pos.trDelta);			
			VectorCopy (start, part->r.currentOrigin);
		}
	}
	
	G_UseTargets (ent, NULL);
}

void props_ExploPartInit (gentity_t *ent)
{
	gentity_t *target;
	vec3_t		vec, angles;

	if (ent->target)
	{
		target = G_Find (NULL, FOFS (targetname), ent->target);	
		VectorSubtract (target->s.origin, ent->s.origin, vec);
		vectoangles (vec, angles);
		G_SetAngle (ent, angles);
	}
	else
	{
		// ok then just up
		VectorSet (vec, 0, 0, 1);
		vectoangles (vec, angles);
		G_SetAngle (ent, angles);
	}
}

void SP_props_ExploPart (gentity_t *ent)
{
	char *sound;
	char *type;
//	float	bbox;

	if ( ent->model ) 
	{
		ent->s.modelindex = G_ModelIndex( ent->model ); 
	}

	G_SpawnString("type", "wood", &type);
	
	if (!Q_stricmp(type,"wood"))		
	{
		if (ent->count == 64)
			ent->s.modelindex = G_ModelIndex ( "models/shards/2x4a.md3" ); 
		else if (ent->count == 48)
			ent->s.modelindex = G_ModelIndex ( "models/shards/2x4b.md3" ); 
		else if (ent->count == 32)
			ent->s.modelindex = G_ModelIndex ( "models/shards/2x4c.md3" ); 
		else if (ent->count == 24)
			ent->s.modelindex = G_ModelIndex ( "models/shards/2x4d.md3" ); 
		else if (ent->count == 16)
			ent->s.modelindex = G_ModelIndex ( "models/shards/2x4e.md3" ); 
		else if (ent->count == 8)
			ent->s.modelindex = G_ModelIndex ( "models/shards/2x4f.md3" ); 
	}
	else if (!Q_stricmp(type,"concrete"))
	{
	}
	else if (!Q_stricmp(type,"stone"))	
	{
	}
		
	if ( G_SpawnString( "noise", "100", &sound ) ) {
		ent->s.loopSound = G_SoundIndex( sound );
	}

	ent->think = props_ExploPartInit; 
	ent->nextthink = level.time + FRAMETIME;
	
	ent->use = props_ExploPartUse;	
}

/*QUAKED props_decoration (.6 .7 .7) (-8 -8 0) (8 8 16) STARTINVIS DEBRIS ANIMATE KEEPBLOCK TOUCHACTIVATE LOOPING STARTON
"model2" will specify the model to load

"noise"  the looping sound entity is to make

"type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
"count" how much debris ei. default 4 pieces

you will need to specify the bounding box for the entity
"high"  default is 4
"wide"	default is 4

"frames"	how many frames of animation to play
"loop" when the animation is done start again on this frame
"startonframe" on what frame do you want to start the animation
*/

void props_decoration_animate (gentity_t *ent)
{
	
	ent->s.frame ++;
	ent->s.eType = ET_GENERAL;

	if (ent->s.frame > ent->count2)
	{
		if (ent->spawnflags & 32 || ent->spawnflags & 64)
		{
			ent->s.frame = ent->props_frame_state;
			
			if (!(ent->spawnflags & 64))
				ent->takedamage = qfalse;
		}
		else
		{
			ent->s.frame = ent->count2;
			ent->takedamage = qfalse;
			
			return;
		}
	}
	
	ent->nextthink = level.time + 50;
}

void props_decoration_death (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	if (!(ent->spawnflags & 8))
	{
		ent->clipmask   = 0;
		ent->r.contents = 0;
		ent->s.eType = ET_GENERAL;
		trap_LinkEntity (ent);
	}
	
	ent->takedamage = qfalse;

	G_UseTargets (ent, NULL);

	if (ent->spawnflags & 2)
	{
		Spawn_Shard (ent, inflictor, ent->count, ent->key);
	}

	if (ent->spawnflags & 4)
	{
		ent->nextthink = level.time + 50;
		ent->think = props_decoration_animate;
		return;
	}

	G_FreeEntity (ent);
	
}

void Use_props_decoration (gentity_t *ent, gentity_t *self, gentity_t *activator )
{
	if (ent->spawnflags & 1)
	{
		trap_LinkEntity (ent);
		ent->spawnflags &= ~1;
	}
	else if (ent->spawnflags & 4)
	{
		ent->nextthink = level.time + 50;
		ent->think = props_decoration_animate;
	}
	else
	{
		trap_UnlinkEntity (ent);
		ent->spawnflags |= 1;
	}

}

void props_touch (gentity_t *self, gentity_t *other, trace_t *trace)
{
	if (self->spawnflags & 16)
		props_decoration_death (self, other, other, 9999, MOD_CRUSH);
}

void SP_props_decoration (gentity_t *ent)
{
	float		light;
	vec3_t		color;
	qboolean	lightSet, colorSet;
	char		*sound;
	char		*type;
	char		*high;
	char		*wide;
	char		*frames;
	float		height;
	float		width;
	float		num_frames;

	char		*loop;
	
	char		*startonframe;
	
	if ( G_SpawnString ("startonframe", "0", &startonframe) )
	{
		ent->s.frame = atoi (startonframe);
	}

	if ( ent->model2 ) {
		ent->s.modelindex = G_ModelIndex( ent->model2 );
	}

	if ( G_SpawnString( "noise", "100", &sound ) ) {
		ent->s.loopSound = G_SoundIndex( sound );
	}

	if ( (ent->spawnflags & 32) && G_SpawnString( "loop", "100", &loop ) ) {
		ent->props_frame_state = atoi (loop);
	}

	// if the "color" or "light" keys are set, setup constantLight
	lightSet = G_SpawnFloat( "light", "100", &light );
	colorSet = G_SpawnVector( "color", "1 1 1", color );
	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 );
	}

	if (ent->health)
	{
		ent->isProp = qtrue;
		ent->takedamage = qtrue;
		ent->die = props_decoration_death;

		G_SpawnString("type", "wood", &type);
		if (!Q_stricmp(type,"wood"))		ent->key = 1;
		else if (!Q_stricmp(type,"glass"))	ent->key = 0;
		else if (!Q_stricmp(type,"metal"))	ent->key = 2;
		else if (!Q_stricmp(type,"ceramic"))	ent->key = 3;
		else if (!Q_stricmp(type, "rubble"))	ent->key = 4;

		G_SpawnString ("high", "0", &high);
		height = atof (high);

		if (!height)
			height = 4;

		G_SpawnString ("wide", "0", &wide);
		width = atof (wide);

		if (!width)
			width = 4;

		width /= 2;

		if ( Q_stricmp (ent->classname, "props_decorBRUSH") ) 
		{
			VectorSet (ent->r.mins, -width, -width, 0);
			VectorSet (ent->r.maxs, width, width, height);
		}

		ent->clipmask   = CONTENTS_SOLID;
		ent->r.contents = CONTENTS_SOLID;
		ent->s.eType = ET_MOVER;

		G_SpawnString ("frames", "0", &frames);
		num_frames = atof (frames);

		ent->count2 = num_frames;

		if (ent->targetname)
			ent->use = Use_props_decoration;

		ent->touch = props_touch;

	}
	else if (!(ent->health) && ent->spawnflags & 4)
	{
		G_SpawnString ("frames", "0", &frames);
		num_frames = atof (frames);

		ent->count2 = num_frames;
		ent->use = Use_props_decoration;
	}

	if (ent->spawnflags & 64)
	{
		ent->nextthink = level.time + 50;
		ent->think = props_decoration_animate;
	}

	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	
	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!(ent->spawnflags & 1))
		trap_LinkEntity (ent);
	else 
		ent->use = Use_props_decoration;

}

/*QUAKED props_decorBRUSH (.6 .7 .7) ? STARTINVIS DEBRIS ANIMATE KEEPBLOCK - LOOPING STARTON
ANIMATE animate on death
STARTON playanimation on death
must have an origin brush

"model2" will specify the model to load

"noise"  the looping sound entity is to make

"type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
"count" how much debris ei. default 4 pieces

"frames"	how many frames of animation to play
"loop" when the animation is done start again on this frame
"startonframe" on what frame do you want to start the animation
*/

void SP_props_decorBRUSH (gentity_t *self)
{
	
	trap_SetBrushModel( self, self->model );
	
	SP_props_decoration (self);
	
	if ( self->model2 ) {
		self->s.modelindex2 = G_ModelIndex( self->model2 );
	}
	
}


/*QUAKED props_decoration_scale (.6 .7 .7) (-8 -8 0) (8 8 16) STARTINVIS DEBRIS ANIMATE KEEPBLOCK TOUCHACTIVATE LOOPING STARTON

"modelscale" - Scale multiplier (defaults to 1.0 and scales uniformly)
"modelscale_vec" - Set scale per-axis.  Overrides "modelscale", so if you have both the "modelscale" is ignored

"model2" will specify the model to load

"noise"  the looping sound entity is to make

"type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
"count" how much debris ei. default 4 pieces

you will need to specify the bounding box for the entity
"high"  default is 4
"wide"	default is 4

"frames"	how many frames of animation to play
"loop" when the animation is done start again on this frame
"startonframe" on what frame do you want to start the animation
*/

void SP_props_decor_Scale (gentity_t *ent)
{

	float	scale[3] = {1,1,1};
	vec3_t	scalevec;
	
	
	SP_props_decoration (ent);

	ent->s.eType		= ET_GAMEMODEL;
	
	// look for general scaling
	if(G_SpawnFloat( "modelscale", "1", &scale[0])) {
		scale[2] = scale[1] = scale[0];
	}

	// look for axis specific scaling
	if(G_SpawnVector("modelscale_vec", "1 1 1", &scalevec[0])) {
		VectorCopy(scalevec, scale);
	}

	// scale is stored in 'angles2'
	VectorCopy(scale, ent->s.angles2);

	trap_LinkEntity (ent);

}

/*QUAKED props_skyportal (.6 .7 .7) (-8 -8 0) (8 8 16)
"fov" for the skybox default is 90
To have the portal sky fogged, enter any of the following values:
"fogcolor" (r g b) (values 0.0-1.0)
"fognear" distance from entity to start fogging
"fogfar" distance from entity that fog is opaque

*/
void SP_skyportal (gentity_t *ent)
{
	char	*fov;
	vec3_t	fogv;	//----(SA)	
	int		fogn;	//----(SA)	
	int		fogf;	//----(SA)	
	int		isfog = 0;	// (SA)

	float	fov_x;

	G_SpawnString ("fov", "90", &fov);
	fov_x = atof (fov);

//----(SA)	modified
	isfog += G_SpawnVector ("fogcolor", "0 0 0", fogv);
	isfog += G_SpawnInt ("fognear", "0", &fogn);
	isfog += G_SpawnInt ("fogfar", "300", &fogf);

	trap_SetConfigstring( CS_SKYBOXORG, va("%.2f %.2f %.2f %.1f %i %.2f %.2f %.2f %i %i", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], fov_x, (int)isfog, fogv[0], fogv[1], fogv[2], fogn, fogf ) );
//----(SA)	end
}

/*QUAKED props_statue (.6 .3 .2) (-8 -8 0) (8 8 128) HURT DEBRIS ANIMATE KEEPBLOCK
"model2" will specify the model to load

"noise"  the sound entity is to make

"type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
"count" how much debris ei. default 4 pieces

you will need to specify the bounding box for the entity
"high"  default is 4 
"wide"	default is 4

"frames"	how many frames of animation to play
"delay"		how long of a delay before damage is inflicted ei. 0.5 sec or 2.7 sec

"damage"	amount of damage to be inflicted
*/

void props_statue_blocked (gentity_t *ent)
{
	trace_t	trace;
	vec3_t	start, end, mins, maxs;
	vec3_t	forward;
	float	dist;
	gentity_t	*traceEnt;
	float		grav = 128;
	vec3_t		kvel;

	if (!Q_stricmp (ent->classname, "props_statueBRUSH"))
		return;

	VectorCopy (ent->s.origin, start);
	start[2] += 24;

	VectorSet (mins, ent->r.mins[0], ent->r.mins[1], -23);
	VectorSet (maxs, ent->r.maxs[0], ent->r.maxs[1], 23);

	AngleVectors (ent->r.currentAngles, forward, NULL, NULL);

	VectorCopy (start, end);

	dist = ( (ent->r.maxs[2] + 16) / ent->count2) * ent->s.frame;

	VectorMA (end, dist, forward, end);

	trap_Trace (&trace, start, mins, maxs, end, ent->s.number, MASK_SHOT);

	if ( trace.surfaceFlags & SURF_NOIMPACT ) // bogus test but just in case
	{
		return;
	}

	traceEnt = &g_entities[ trace.entityNum ];

	if ( traceEnt->takedamage && traceEnt->client )
	{
		G_Damage (traceEnt, ent, ent, NULL, trace.endpos, ent->damage, 0, MOD_CRUSH);

		// TBD: push client back a bit
		VectorScale (forward, grav, kvel);
		VectorAdd (traceEnt->client->ps.velocity, kvel, traceEnt->client->ps.velocity);

		if (!traceEnt->client->ps.pm_time ) 
		{
			int		t;

			t =	grav * 2;
			if ( t < 50 ) 
			{
				t = 50;
			}
			if ( t > 200 ) 
			{
				t = 200;
			}
			traceEnt->client->ps.pm_time = t;
			traceEnt->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
		}

	}
	else
		G_Damage (traceEnt, ent, ent, NULL, trace.endpos, 9999, 0, MOD_CRUSH);

}

void props_statue_animate (gentity_t *ent)
{

	qboolean takeashot = qfalse;

	ent->s.frame ++;
	ent->s.eType = ET_GENERAL;

	if (ent->s.frame > ent->count2)
	{
		ent->s.frame = ent->count2;
		ent->takedamage = qfalse;
	}

	if (((ent->delay * 1000) + ent->timestamp) > level.time)
	{
		ent->count = 0;
	}
	else if (ent->count == 5)
	{
		takeashot = qtrue;
		ent->count = 0;
	}
	else
		ent->count++;

	if (takeashot)
		props_statue_blocked (ent);

	if (ent->s.frame < ent->count2)
		ent->nextthink = level.time + 50;
}


void props_statue_death (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{

	ent->timestamp = level.time;

	G_AddEvent (ent, EV_GENERAL_SOUND, ent->noise_index);

	if (!(ent->spawnflags & 8))
	{
		ent->clipmask   = 0;
		ent->r.contents = 0;
		ent->s.eType = ET_GENERAL;
		trap_LinkEntity (ent);
	}
	
	ent->takedamage = qfalse;

	G_UseTargets (ent, NULL);

	if (ent->spawnflags & 2)
	{
		Spawn_Shard (ent, inflictor, ent->count, ent->key);
	}

	if (ent->spawnflags & 4)
	{
		ent->nextthink = level.time + 50;
		ent->think = props_statue_animate;
		return;
	}

	G_FreeEntity (ent);
	
}

void props_statue_touch (gentity_t *self, gentity_t *other, trace_t *trace)
{
	props_statue_death (self, other, other, 9999, MOD_CRUSH);
}

void SP_props_statue (gentity_t *ent)
{
	float		light;
	vec3_t		color;
	qboolean	lightSet, colorSet;
	char		*sound;
	char		*type;
	char		*high;
	char		*wide;
	char		*frames;
	float		height;
	float		width;
	float		num_frames;
	
	if ( ent->model2 ) {
		ent->s.modelindex = G_ModelIndex( ent->model2 );
	}

	if ( G_SpawnString( "noise", "100", &sound ) ) {
		ent->noise_index = G_SoundIndex ( sound );
	}

	// if the "color" or "light" keys are set, setup constantLight
	lightSet = G_SpawnFloat( "light", "100", &light );
	colorSet = G_SpawnVector( "color", "1 1 1", color );
	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 );
	}

	ent->isProp = qtrue;
	ent->takedamage = qtrue;
	ent->die = props_statue_death;

	G_SpawnString("type", "wood", &type);
	if (!Q_stricmp(type,"wood"))		ent->key = 1;
	else if (!Q_stricmp(type,"glass"))	ent->key = 0;
	else if (!Q_stricmp(type,"metal"))	ent->key = 2;
	else if (!Q_stricmp(type,"ceramic"))	ent->key = 3;
	else if (!Q_stricmp(type, "rubble"))	ent->key = 4;

	G_SpawnString ("high", "0", &high);
	height = atof (high);
	if (!height)
		height = 4;

	G_SpawnString ("wide", "0", &wide);
	width = atof (wide);

	if (!width)
		width = 4;

	width /= 2;

	if ( Q_stricmp (ent->classname, "props_statueBRUSH") ) 
	{
		VectorSet (ent->r.mins, -width, -width, 0);
		VectorSet (ent->r.maxs, width, width, height);
	}

	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	ent->s.eType = ET_MOVER;

	G_SpawnString ("frames", "0", &frames);
	num_frames = atof (frames);

	ent->count2 = num_frames;

	ent->touch = props_statue_touch;

	ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	
	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!ent->damage)
		ent->damage = 1;

	trap_LinkEntity (ent);
}


/*QUAKED props_statueBRUSH (.6 .3 .2) ? HURT DEBRIS ANIMATE KEEPBLOCK
needs an origin brush

"model2" will specify the model to load

"noise"  the sound entity is to make

"type" type of debris ("glass", "wood", "metal", "ceramic", "rubble") default is "wood"
"count" how much debris ei. default 4 pieces

"frames"	how many frames of animation to play
"delay"		how long of a delay before damage is inflicted ei. 0.5 sec or 2.7 sec

THE damage has been disabled at the moment
"damage"	amount of damage to be inflicted

*/

void SP_props_statueBRUSH (gentity_t *self)
{
	
	trap_SetBrushModel( self, self->model );
	
	SP_props_statue (self);
	
	if ( self->model2 ) {
		self->s.modelindex2 = G_ModelIndex( self->model2 );
	}

	if (!(self->health))
		self->health = 6;

}

//////////////////////////////////////////////////
// Lockers
//////////////////////////////////////////////////

#define LOCKER_ANIM_USEEND		5
#define LOCKER_ANIM_DEATHSTART	6
#define LOCKER_ANIM_DEATHEND	11

//////////////////////////////////////////////////
void init_locker (gentity_t *ent);
void props_locker_death (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);
//////////////////////////////////////////////////
#define MAX_LOCKER_DEBRIS		5
		
int	locker_debris_model[MAX_LOCKER_DEBRIS];
//////////////////////////////////////////////////

void Spawn_Junk (gentity_t *ent)
{
	gentity_t *sfx;
	vec3_t	dir, start;
				
	VectorCopy (ent->r.currentOrigin, start);

	start[0] += crandom () * 32;
	start[1] += crandom () * 32;
	start[2] += 16;

	VectorSubtract (start, ent->r.currentOrigin, dir);
	VectorNormalize (dir);
	
	sfx = G_Spawn ();

	G_SetOrigin (sfx, start); 
	G_SetAngle (sfx, ent->r.currentAngles);

	G_AddEvent( sfx, EV_JUNK, DirToByte( dir ));

	sfx->think = G_FreeEntity;

	sfx->nextthink = level.time + 1000;

	trap_LinkEntity (sfx);
}

/*
==============
props_locker_endrattle
==============
*/
void props_locker_endrattle(gentity_t *ent) {
	ent->s.frame = 0;	// idle
	ent->think = 0;
	ent->nextthink = 0;
	ent->delay = 0;
}


void props_locker_use(gentity_t *ent, gentity_t *other, gentity_t *activator) {
	if(!ent->delay)
		ent->s.frame = 1;	// rattle when pain starts
	ent->delay = 1;
	ent->think = props_locker_endrattle;
	ent->nextthink = level.time + 1000;	// rattle a sec
}

void props_locker_pain(gentity_t *ent, gentity_t *attacker, int damage, vec3_t point) {
	props_locker_use(ent, attacker, attacker);
}


void init_locker (gentity_t *ent)
{
	ent->isProp = qtrue;
	ent->takedamage = qtrue;
	ent->delay = 0;

	ent->clipmask   = CONTENTS_SOLID;
	ent->r.contents = CONTENTS_SOLID;
	// TODO: change from 'trap' to something else.  'trap' is a misnomer.  it's actually used for other stuff too
	ent->s.eType = ET_TRAP;

	ent->s.frame = 0;	// closed animation

	ent->count2 = LOCKER_ANIM_DEATHEND;

	ent->die = props_locker_death;
	ent->use = props_locker_use;	// trying it rattles the lock (could also allow 'waking' from trigger)
	ent->pain = props_locker_pain;

	// drop origin down 8 so the designer can put the box entity on the floor rather than /in/ the floor
	// remove if you get a new model from jason w/ the origin moved up 8
	ent->s.origin[2] -= 8;

	G_SetOrigin (ent, ent->s.origin); 
	G_SetAngle (ent, ent->s.angles);

	if (!(ent->health))
		ent->health = 1;

	trap_LinkEntity (ent);

}

void props_locker_spawn_item (gentity_t *ent)
{
	gitem_t		*item;
	gentity_t	*drop = NULL;

	item = BG_FindItem (ent->spawnitem);

	if(!item)	// empty
		return;

//	drop = Drop_Item (ent, item, 0, qtrue);
	drop = LaunchItem( item, ent->r.currentOrigin, tv(0, 0, 20), ent->s.number );


	if (!drop) {
		G_Printf("-----> WARNING <-------\n");
		G_Printf("props_locker_spawn_item at %s failed!\n", vtos (ent->r.currentOrigin));
	}
}

extern qhandle_t	trap_R_GetShaderFromModel( qhandle_t modelid, int surfnum, int withlightmap);

void props_locker_mass (gentity_t *ent)
{
	gentity_t	*tent;
	vec3_t		start;
	vec3_t		dir;

	VectorCopy (ent->r.currentOrigin, start);

	start[0] += crandom () * 32;
	start[1] += crandom () * 32;
	start[2] += 16;

	VectorSubtract (start, ent->r.currentOrigin, dir);
	VectorNormalize (dir);

	tent = G_TempEntity( ent->r.currentOrigin, EV_EFFECT);
	VectorCopy (ent->r.currentOrigin, tent->s.origin);
	VectorCopy (dir, tent->s.angles2);
	
 	tent->s.dl_intensity = 0;

	trap_SetConfigstring( CS_TARGETEFFECT, ent->dl_shader );	//----(SA)	allow shader to be set from entity

	tent->s.frame = ent->key;		

	tent->s.eventParm = 8;
	tent->s.density = 100;
}

/*QUAKED props_footlocker (.6 .7 .3) (-12 -21 -12) (12 21 12) ? NO_JUNK
"noise"  the sound entity is to make upon death
the default sounds are:
  "wood"	- "sound/world/boardbreak.wav"
  "glass"	- "sound/world/glassbreak.wav"
  "metal"	- "sound/world/metalbreak.wav"
  "gibs"	- "sound/player/gibsplit1.wav"
  "brick"	- "sound/world/brickfall.wav"
  "stone"	- "sound/world/stonefall.wav"
  "fabric"	- "sound/world/metalbreak.wav"	// (SA) temp

"locknoise" the locked sound to play
"wait"	 denotes how long the wait is going to be before the locked sound is played again default is 1 sec
"health" default is 1

"spawnitem" - will spawn this item unpon death use the pickup_name ei 9mm

"type" - type of debris ("glass", "wood", "metal", "gibs", "brick", "rock", "fabric") default is "wood"
"mass" - defaults to 75.  This determines how much debris is emitted.  You get one large chunk per 100 of mass (up to 8) and one small chunk per 25 of mass (up to 16).  So 800 gives the most.

"dl_shader" needs to be set the same way as a target_effect

TBD: the spawning of junk still pending and animation when used

-------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
model="models/mapobjects/furniture/footlocker.md3"
*/

/*
==============
props_locker_death 
==============
*/
void props_locker_death (gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	ent->takedamage = qfalse;
	ent->s.frame = 2;	// opening animation
	ent->think = 0;
	ent->nextthink = 0;

	trap_UnlinkEntity (ent);
	ent->r.maxs[2] = 11;	// (SA) make the dead bb half height so the item can look like it's sitting inside
	props_locker_spawn_item(ent);
	trap_LinkEntity (ent);

}


void SP_props_footlocker (gentity_t *self)
{
	char	*type;
	char	*sound;
	char	*locked;
	int		mass;

//	trap_SetBrushModel (self, self->model);

	// (SA) if angle is xx or yy, rotate the bounding box 90 deg to match
	// NOTE:	Non axis-aligned orientation not allowed.  It will work, but 
	//			the bounding box will not exactly match the model.
	if(self->s.angles[1] == 90 || self->s.angles[1] == 270) {
		VectorSet(self->r.mins, -21, -12, 0);
		VectorSet(self->r.maxs, 21, 12, 24);
	} else {
		VectorSet(self->r.mins, -12, -21, 0);
		VectorSet(self->r.maxs, 12, 21, 24);
	}

	self->s.modelindex = G_ModelIndex ("models/mapobjects/furniture/footlocker.md3");
										
	if (G_SpawnString ("noise", "NOSOUND", &sound)) 
		self->noise_index = G_SoundIndex (sound);
	
	if (G_SpawnString( "locknoise", "NOSOUND", &locked )) 
		self->soundPos1 = G_SoundIndex (locked);

	if (!(self->wait))
		self->wait = 1000;
	else
		self->wait *= 1000;

	if (G_SpawnInt ("mass", "75", &mass))
		self->count = mass;
	else
		self->count = 75;

	if (G_SpawnString ("type", "wood", &type))  {
		if (!Q_stricmp (type,"wood"))			self->key = 0;
		else if (!Q_stricmp (type,"glass"))		self->key = 1;
		else if (!Q_stricmp (type,"metal"))		self->key = 2;
		else if (!Q_stricmp (type,"gibs"))		self->key = 3;
		else if (!Q_stricmp (type,"brick"))		self->key = 4;
		else if (!Q_stricmp (type,"rock"))		self->key = 5;
		else if (!Q_stricmp (type,"fabric"))	self->key = 6;
	} else {
		self->key = 0;
	}

	self->delay = level.time + self->wait;
	
	init_locker (self);

}

/*QUAKED props_flamethrower (.6 .7 .3) (-8 -8 -8) (8 8 8) TRACKING
the effect occurs when this entity is used
needs to aim at a info_notnull
"duration" how long the effect is going to last for example 1.2 sec 2.7 sec
"random" how long of a random variance so the effect isnt exactly the same each time for example 1.1 sec or 0.2 sec
"size" valid ranges are 1.0 to 0.1
*/
void props_flamethrower_think (gentity_t *ent)
{
	vec3_t		vec, angles;
	gentity_t	*target= NULL;

	if (ent->spawnflags & 1) // tracking
	{
		if (ent->target)
			target = G_Find (NULL, FOFS(targetname), ent->target);

		if (!target)
		{
//			VectorSet (ent->r.currentAngles, 0, 0, 1);	// (SA) wasn't working
			VectorSet (ent->s.apos.trBase, 0, 0, 1);
		}
		else
		{
			VectorSubtract (target->s.origin, ent->s.origin, vec);
			VectorNormalize( vec );
			vectoangles (vec, angles);
//			VectorCopy (angles, ent->r.currentAngles);	// (SA) wasn't working
			VectorCopy (angles, ent->s.apos.trBase);
		}	
	}

	if ((ent->timestamp + ent->duration) > level.time)
	{
		G_AddEvent (ent, EV_FLAMETHROWER_EFFECT, 0);

		ent->nextthink = level.time + 50;

		{
			int rval;
			int	rnd;

 			if (ent->random)
			{
				rval = ent->random * 1000;
				rnd = rand()%rval;
			}
			else 
				rnd = 0;

			ent->timestamp = level.time + rnd;	
			ent->nextthink = ent->timestamp + 50;
		}
	}
	
}

void props_flamethrower_use (gentity_t *ent, gentity_t *other, gentity_t *activator)
{
	int rval;
	int	rnd;

	if (ent->spawnflags & 2)
	{
		ent->spawnflags &=~2;
		ent->think = NULL;		// (SA) wasn't working
		ent->nextthink = 0;	
		return;
	}
	else
	{
		ent->spawnflags |= 2;
	}

 	if (ent->random)
	{
		rval = ent->random * 1000;
		rnd = rand()%rval;
	}
	else 
		rnd = 0;

	ent->timestamp = level.time + rnd;	

	ent->think = props_flamethrower_think;
	ent->nextthink = level.time + 50;

}

void props_flamethrower_init (gentity_t *ent)
{
	gentity_t *target = NULL;
	vec3_t		vec;
	vec3_t		angles;

	if (ent->target)
		target = G_Find (NULL, FOFS(targetname), ent->target);

	if (!target)
	{
//		VectorSet (ent->r.currentAngles, 0, 0, 1);	//----(SA)	
		VectorSet (ent->s.apos.trBase, 0, 0, 1);
	}
	else
	{
		VectorSubtract (target->s.origin, ent->s.origin, vec);
		VectorNormalize( vec );
		vectoangles (vec, angles);
//		VectorCopy (angles, ent->r.currentAngles);	//----(SA)	
		VectorCopy (angles, ent->s.apos.trBase);
	}

	trap_LinkEntity (ent);

}

void SP_props_flamethrower (gentity_t *ent)
{
	char *size;
	float dsize;

	ent->think = props_flamethrower_init;
	ent->nextthink = level.time + 50;
	ent->use = props_flamethrower_use;

	G_SetOrigin (ent, ent->s.origin); 

	if (!(ent->duration))
		ent->duration = 1000;
	else
		ent->duration *= 1000;


	G_SpawnString ("size", "0", &size);
	dsize = atof (size);
	if (!dsize)
		dsize = 1;
	ent->accuracy = dsize;

}
