// g_misc.c
#include "g_local.h"
#include "fields.h"
#include "..\qcommon\configstring.h"
#include "..\qcommon\ef_flags.h"

#define		INVINCIBLE		1
#define		GLASS			2

#define		AREA_PORTAL_START_OPEN		1

#define		GLASS_PAINCHUNKS			2
#define		BREAKABLE_NOT_IN_CAM		8

void FindCorrectOrigin(edict_t *ent, vec3_t origin)
{
	// to accomodate BMODELS, which are dumb...
	if ((ent->solid == SOLID_BSP) && (VectorCompare(ent->s.origin, vec3_origin)))
	{
		VectorScale(ent->absmax, .5, origin);
		VectorMA(origin, .5, ent->absmin, origin);
	}
	else
	{
		VectorAdd(ent->mins, ent->maxs, origin);
		VectorMA(ent->s.origin, .5, origin, origin);
	}
	return;
}

/*QUAKED func_group (0 0 0) ?
Used to group brushes together just for editor convenience.
*/

//=====================================================

void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
{
	ent->count ^= 1;		// toggle state
//	gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
	gi.SetAreaPortalState (ent->style, ent->count);
}

/*QUAKED func_areaportal (0 0 0) ?

This is a non-visible object that divides the world into
areas that are seperated when this portal is not activated.
Usually enclosed in the middle of a door.
*/
void SP_func_areaportal (edict_t *ent)
{
//	ent->use = Use_Areaportal;  according to our crack design staff,
//  areaportals are ALWAYS triggered by doors.  This stuff is already handled in door_use_areaportals,
//	so we don' need it n'more
	ent->count = 0;		// always start closed;

}

//=====================================================


/*
=================
Misc functions
=================
*/
void VelocityForDamage (int damage, vec3_t v)
{
	v[0] = gi.flrand(-100.0F, 100.0F);
	v[1] = gi.flrand(-100.0F, 100.0F);
	v[2] = gi.flrand(100.0F, 300.0F);

	if (damage < 50)
		VectorScale (v, 0.7, v);
	else 
		VectorScale (v, 1.2, v);
}

void ClipGibVelocity (edict_t *ent)
{
	if (ent->velocity[0] < -300)
		ent->velocity[0] = -300;
	else if (ent->velocity[0] > 300)
		ent->velocity[0] = 300;
	if (ent->velocity[1] < -300)
		ent->velocity[1] = -300;
	else if (ent->velocity[1] > 300)
		ent->velocity[1] = 300;
	if (ent->velocity[2] < 200)
		ent->velocity[2] = 200;	// always some upwards
	else if (ent->velocity[2] > 500)
		ent->velocity[2] = 500;
}


/*
=================
gibs
=================
*/
void gib_think (edict_t *self)
{
	self->s.frame++;
	self->nextthink = level.time + FRAMETIME;

	if (self->s.frame == 10)
	{
		self->think = G_FreeEdict;
		self->nextthink = level.time + gi.flrand(8.0F, 18.0F);
	}
}

void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, mtexinfo_t *surf)
{
	vec3_t	normal_angles, right;

	if (!self->groundentity)
		return;

	self->touch = NULL;

	if (plane)
	{
		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), .6, ATTN_NORM, 0);

		vectoangles (plane->normal, normal_angles);
		AngleVectors (normal_angles, NULL, right, NULL);
		vectoangles (right, self->s.angles);

		if (self->s.modelindex == sm_meat_index)
		{
			self->s.frame++;
			self->think = gib_think;
			self->nextthink = level.time + FRAMETIME;
		}
	}
}

void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
	G_FreeEdict (self);
}

void ThrowGib (edict_t *self, char *gibname, int damage, int type)
{
	edict_t *gib;
	vec3_t	vd;
	vec3_t	origin;
	vec3_t	size;
	float	vscale;

	gib = G_Spawn();

	VectorScale (self->size, 0.5, size);
	VectorAdd (self->absmin, size, origin);
	gib->s.origin[0] = origin[0] + gi.flrand(-size[0], size[0]);
	gib->s.origin[1] = origin[1] + gi.flrand(-size[1], size[1]);
	gib->s.origin[2] = origin[2] + gi.flrand(-size[2], size[2]);

	gi.setmodel (gib, gibname);
	gib->solid = SOLID_NOT;
//	gib->s.effects |= EF_GIB;
	gib->flags |= FL_NO_KNOCKBACK;
	gib->takedamage = DAMAGE_YES;
	gib->die = gib_die;

	if (type == GIB_ORGANIC)
	{
		gib->movetype = MOVETYPE_TOSS;
		gib->touch = gib_touch;
		vscale = 0.5;
	}
	else
	{
		gib->movetype = MOVETYPE_BOUNCE;
		vscale = 1.0;
	}

	VelocityForDamage (damage, vd);
	VectorMA (self->velocity, vscale, vd, gib->velocity);
	ClipGibVelocity (gib);
	gib->avelocity[0] = gi.flrand(0.0F, 600.0F);
	gib->avelocity[1] = gi.flrand(0.0F, 600.0F);
	gib->avelocity[2] = gi.flrand(0.0F, 600.0F);

	gib->think = G_FreeEdict;
	gib->nextthink = level.time + gi.flrand(10.0F, 20.0F);

	gi.linkentity (gib);
}

void ThrowHead (edict_t *self, char *gibname, int damage, int type)
{
	vec3_t	vd;
	float	vscale;

	self->s.skinnum = 0;
	self->s.frame = 0;
	VectorClear (self->mins);
	VectorClear (self->maxs);

	gi.setmodel (self, gibname);
	self->solid = SOLID_NOT;
	self->s.sound = 0;
	self->flags |= FL_NO_KNOCKBACK;
	self->svflags &= ~SVF_MONSTER;
	self->takedamage = DAMAGE_YES;
	self->die = gib_die;

	if (type == GIB_ORGANIC)
	{
		self->movetype = MOVETYPE_TOSS;
		self->touch = gib_touch;
		vscale = 0.5;
	}
	else
	{
		self->movetype = MOVETYPE_BOUNCE;
		vscale = 1.0;
	}

	VelocityForDamage (damage, vd);
	VectorMA (self->velocity, vscale, vd, self->velocity);
	ClipGibVelocity (self);

	self->avelocity[YAW] = gi.flrand(-600.0F, 600.0F);

	self->think = G_FreeEdict;
	self->nextthink = level.time + gi.flrand(10.0F, 20.0F);

	gi.linkentity (self);
}

static int shakeIntensity[] = {7, 17, 35, 55, 80};

void ShakeCameras (vec3_t origin, int amount, int radius, int delta)
{
	edict_t *curSearch = NULL;
	float shakeAmount;
	vec3_t dist;
	float	distf;
	int i, j = 0;

	CRadiusContent rad(origin, radius);

	for(i = 0; i < rad.getNumFound(); i++)
	{
		curSearch = rad.foundEdict(i);

		if(curSearch->client != NULL)
		{
			VectorSubtract(curSearch->s.origin, origin, dist);
			distf = VectorLength(dist);
			shakeAmount =  (1.0 - (distf/radius)) * amount;

			j = 4;
			while (j > 0)
			{
				if(shakeAmount > shakeIntensity[j])break;

				j--;
			}

			FX_SetEvent_Data(curSearch, EV_CAMERA_SHAKE_VERYLIGHT + j, delta);
		}
	}
}

void BlindingLight(vec3_t origin, int intensity, float maxalpha, float delta)
{
	edict_t *curBlinded = NULL;
	trace_t	tr;
	vec3_t	endPoint, tempVect;
	float	dist;
	vec3_t	fwd;
	float	val;

	while ((curBlinded = G_Find (curBlinded, FOFS(classname), "player")) != NULL)
	{
		VectorSubtract(origin, curBlinded->s.origin, tempVect);
		dist = VectorLength(tempVect);
		if(dist > intensity)continue;

		VectorCopy(curBlinded->s.origin, endPoint);

		//ss--i think this is valid viewheight
		endPoint[2] += curBlinded->viewheight;// 26; // is there an actual value describing the player's view height?

		gi.trace (origin, NULL, NULL, endPoint, curBlinded, MASK_SHOT, &tr);

		if(tr.fraction <= .99)continue;

		AngleVectors(curBlinded->client->ps.viewangles, fwd, NULL, NULL);
		VectorNormalize(tempVect);
		val = DotProduct(fwd, tempVect);
		if(val > 0)
		{
			if (curBlinded->client->goggles_on)
			{	// Using the light intensifier goggles.	 All flashes are more intense.
				float	blindalpha;

				blindalpha = intensity*.03/dist*val;
				if (blindalpha < 0.4)
				{
					blindalpha += 0.4;
				}
				else
				{
					blindalpha *= 2.0;
				}
				curBlinded->client->blinding_alpha += blindalpha;
			}
			else
			{	// Normal blinding level.
				curBlinded->client->blinding_alpha += intensity*.03/dist*val;
			}
		}

		if (curBlinded->client->goggles_on)
		{	// Flashes should last longer with goggles.
			delta *= 0.5;
			maxalpha += (1.0-maxalpha)*0.5;
		}

		// Cap the alpha
		if(curBlinded->client->blinding_alpha > maxalpha) 
		{
			curBlinded->client->blinding_alpha = maxalpha;
		}

		// add in the delta at which we think the screen flash should go down.
		// use the slowed blind decay time you can...
		if (curBlinded->client->blinding_alpha_delta <= 0 || delta < curBlinded->client->blinding_alpha_delta)
		{
			curBlinded->client->blinding_alpha_delta = delta;
		}
	}

	curBlinded = NULL;

	CRadiusContent rad(origin, intensity);

	for(int i = 0; i < rad.getNumFound(); i++)
	{
		curBlinded = rad.foundEdict(i);

		if (!curBlinded->ai)continue;

		VectorCopy(curBlinded->s.origin, endPoint);
		endPoint[2] += curBlinded->viewheight;
		gi.trace (origin, NULL, NULL, endPoint, curBlinded, MASK_SHOT, &tr);

		if(tr.fraction <= .99)continue;

		curBlinded->ai->GetLookVector(fwd);

		VectorSubtract(origin, curBlinded->s.origin, tempVect);
		dist = VectorNormalize(tempVect);

		val = DotProduct(fwd, tempVect);

//		if(val > -0.75)
		{
			float monster_blind = ((float)intensity*0.5)/dist*(val+1.25);
			if (monster_blind > 25)
			{
				monster_blind = 25;
			}
			curBlinded->ai->MuteSenses(sight_mask, monster_blind, smute_recov_linear, monster_blind);
		}
	}
}

void TearGasEnt(edict_t *ent, float intensity)
{
	if(!strcmp(ent->classname, "player"))
	{
		ent->client->gas_blend += intensity;
		if(ent->client->gas_blend > 1.0)ent->client->gas_blend = 1.0;
	}
	else if(ent->ai)
	{
		ent->ai->MuteSenses(sight_mask, intensity*50, smute_recov_linear, intensity*50);
	}
}

/*
=================
debris
=================
*/
void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
	G_FreeEdict (self);
}

void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
{
	edict_t	*chunk;
	vec3_t	v;

	chunk = G_Spawn();
	VectorCopy (origin, chunk->s.origin);
	gi.setmodel (chunk, modelname);
	v[0] = gi.flrand(-100.0F, 100.0F);
	v[1] = gi.flrand(-100.0F, 100.0F);
	v[2] = gi.flrand(0.0F, 200.0F);
	VectorMA (self->velocity, speed, v, chunk->velocity);
	chunk->movetype = MOVETYPE_BOUNCE;
	chunk->solid = SOLID_NOT;
	chunk->avelocity[0] = gi.flrand(0.0F, 600.0F);
	chunk->avelocity[1] = gi.flrand(0.0F, 600.0F);
	chunk->avelocity[2] = gi.flrand(0.0F, 600.0F);
	chunk->think = G_FreeEdict;
	chunk->nextthink = level.time + gi.flrand(5.0F, 10.0F);
	chunk->s.frame = 0;
	chunk->flags = 0;
	chunk->classname = "debris";
	chunk->takedamage = DAMAGE_YES;
	chunk->die = debris_die;
	gi.linkentity (chunk);
}


void BecomeExplosion1 (edict_t *self)
{
/*	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION1);
	gi.WritePosition (self->s.origin);
	gi.multicast (self->s.origin, MULTICAST_PVS);*/

	G_FreeEdict (self);
}


void BecomeExplosion2 (edict_t *self)
{
/*	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_EXPLOSION2);
	gi.WritePosition (self->s.origin);
	gi.multicast (self->s.origin, MULTICAST_PVS);*/

	G_FreeEdict (self);
}


/*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT WAITONECYCLE FACEPLAYER
WAITONECYCLE: ignores wait time, goes through waitactions once
FACEPLAYER: will face the player forever, once it reaches this point
Target: next path corner
Pathtarget: gets used when an entity that has
	this path_corner targeted touches it
MoveAction:  animation to play while moving to the next path_corner
Waitaction1: animations to play while waiting
Waitaction2:
Waitaction3:
Waitaction4:
Waitaction5:
Waitaction6:
Wait: time to wait (-1 for forever)
*/

#define MAX_PATHCORNER_WAITACTIONS	6

//for MMoves
#include "ai_private.h"

void SetWaitAction(edict_t *self, int actionNum, char *actionName)
{
	mmove_t **putMoveHere;
	int		i;

	if (actionNum > MAX_PATHCORNER_WAITACTIONS)
	{
		return;
	}

	if (!actionName || !(*actionName))
	{
		return;
	}

	//ahem. NO! fixme, this is a quick thing only
	switch(actionNum)
	{
	case 1:
		putMoveHere = &self->wait_action1;
		break;
	case 2:
		putMoveHere = &self->wait_action2;
		break;
	case 3:
		putMoveHere = &self->wait_action3;
		break;
	case 4:
		putMoveHere = &self->wait_action4;
		break;
	case 5:
		putMoveHere = &self->wait_action5;
		break;
	default:
#if _DEBUG
		gi.dprintf("warning: trying to set invalid path_corner waitaction: %d\n", actionNum);
#endif
	case 6:
		putMoveHere = &self->wait_action6;
		break;
	}

	for (i=0; MMoves[i].ghoulSeqName[0]; i++)//this is yucky too--will prolly need to go through several lists
	{
		//found the move.
		if (!stricmp(MMoves[i].ghoulSeqName, actionName))
		{
			*putMoveHere = &MMoves[i];
			return;
		}
	}
	gi.dprintf("couldn't find path_corner waitaction: %s (using stand)\n", actionName);
	*putMoveHere=&generic_move_stand;
}

void SetMoveAction(edict_t *self, char *actionName)
{
	mmove_t **putMoveHere;
	int		i;

	if (!actionName || !(*actionName))
	{
		return;
	}

	putMoveHere = &self->move_action;

	for (i=0; MMoves[i].ghoulSeqName[0]; i++)//this is yucky too--will prolly need to go through several lists
	{
		//found the move.
		if (!stricmp(MMoves[i].ghoulSeqName, actionName))
		{
			*putMoveHere = &MMoves[i];
			return;
		}
	}
	gi.dprintf("couldn't find path_corner moveaction: %s (using walk)\n", actionName);
	*putMoveHere=&generic_move_walk;
}

mmove_t *GetMoveAction(edict_t *self)
{
	if (!self)
	{
		return NULL;
	}
	return self->move_action;
}

mmove_t *GetWaitAction(edict_t *self, int actionNum)
{
	if ((actionNum > MAX_PATHCORNER_WAITACTIONS) || !self)
	{
		return NULL;
	}

	//ahem. NO! fixme, this is a quick thing only
	switch(actionNum)
	{
	case 1:
		return self->wait_action1;
		break;
	case 2:
		return self->wait_action2;
		break;
	case 3:
		return self->wait_action3;
		break;
	case 4:
		return self->wait_action4;
		break;
	case 5:
		return self->wait_action5;
		break;
	case 6:
		return self->wait_action6;
		break;
	}
#if _DEBUG
		gi.dprintf("warning: trying to get invalid path_corner waitaction: %d\n", actionNum);
#endif
	return NULL;
}

void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, mtexinfo_t *surf)
{
	vec3_t		v;
	edict_t		*next;

	if (other->movetarget != self)
		return;
	
	if (other->enemy)
		return;

	if (self->pathtarget)
	{
		char *savetarget;

		savetarget = self->target;
		self->target = self->pathtarget;
		G_UseTargets (self, other);
		self->target = savetarget;
	}

	if (self->target)
		next = G_PickTarget(self->target);
	else
		next = NULL;

	if ((next) && (next->spawnflags & 1))
	{
		VectorCopy (next->s.origin, v);
		v[2] += next->mins[2];
		v[2] -= other->mins[2];
		VectorCopy (v, other->s.origin);
		next = G_PickTarget(next->target);
	}

	other->goalentity = other->movetarget = next;

	if (self->wait)
	{
//		other->monsterinfo.pausetime = level.time + self->wait;
//		other->monsterinfo.stand (other);
		return;
	}

	if (!other->movetarget)
	{
//		other->monsterinfo.pausetime = level.time + 100000000;
//		other->monsterinfo.stand (other);
	}
	else
	{
		VectorSubtract (other->goalentity->s.origin, other->s.origin, v);
	}
	
}

void pathtest_think(edict_t *who)
{
	if (ai_pathtest->value)
	{
//		FX_MakeSparks(who->s.origin, vec3_up, 2);
	}
	who->nextthink=level.time+0.5;
}

void SP_path_corner (edict_t *self)
{
//	trace_t		tr;
//	vec3_t		dest;

	if (!self->targetname)
	{
		gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
		G_FreeEdict (self);
		return;
	}

	SetWaitAction(self, 1, st.waitAction1);
	SetWaitAction(self, 2, st.waitAction2);
	SetWaitAction(self, 3, st.waitAction3);
	SetWaitAction(self, 4, st.waitAction4);
	SetWaitAction(self, 5, st.waitAction5);
	SetWaitAction(self, 6, st.waitAction6);

	SetMoveAction(self, st.moveAction);

/*	VectorCopy(self->s.origin, dest);
	dest[2]-=1000;
	gi.trace (self->s.origin, NULL, NULL, dest, self, MASK_SOLID, &tr);
	if (!tr.startsolid)
	{
		VectorCopy (tr.endpos, self->s.origin);
		self->s.origin[2]+=10;
	}
*/
	self->solid = SOLID_TRIGGER;
	self->touch = path_corner_touch;
	VectorSet (self->mins, -8, -8, -8);
	VectorSet (self->maxs, 8, 8, 8);

	//oop, this bit was not welcome. i'll just do the pathfinding from a bit higher up.
//	self->s.origin[2]+=8;

	self->svflags |= SVF_NOCLIENT;
//	self->s.effects |= EF_TELEPORTER;
//	self->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); 
	self->friction = 0;
	self->gravity = 0;
	self->airresistance = 0;

	self->think=pathtest_think;
	self->nextthink=level.time+0.5;

	gi.linkentity (self);
}


/*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold WAITONECYCLE FACEPLAYER
Makes this the target of a monster and it will head here
when first activated before going after the activator.  If
hold is selected, it will stay here.

WAITONECYCLE: ignores wait time, goes through waitactions once
FACEPLAYER: will face the player forever, once it reaches this point
Target: next point_combat
Pathtarget: gets used when an entity that has
	this path_corner targeted touches it
MoveAction:  animation to play while moving to the next point_combat
Waitaction1: animations to play while waiting
Waitaction2:
Waitaction3:
Waitaction4:
Waitaction5:
Waitaction6:
Wait: time to wait (-1 for forever)
*/
void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, mtexinfo_t *surf)
{
	edict_t	*activator;

	if (other->movetarget != self)
		return;

	if (self->target)
	{
		other->target = self->target;
		other->goalentity = other->movetarget = G_PickTarget(other->target);
		if (!other->goalentity)
		{
			gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
			other->movetarget = self;
		}
		self->target = NULL;
	}
	else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY)))
	{
//		other->monsterinfo.pausetime = level.time + 100000000;
//		other->monsterinfo.aiflags |= AI_STAND_GROUND;
//		other->monsterinfo.stand (other);
	}

	if (other->movetarget == self)
	{
		other->target = NULL;
		other->movetarget = NULL;
		other->goalentity = other->enemy;
//		other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
	}

	if (self->pathtarget)
	{
		char *savetarget;

		savetarget = self->target;
		self->target = self->pathtarget;
		if (other->enemy && other->enemy->client)
			activator = other->enemy;
		else if (other->oldenemy && other->oldenemy->client)
			activator = other->oldenemy;
		else if (other->activator && other->activator->client)
			activator = other->activator;
		else
			activator = other;
		G_UseTargets (self, activator);
		self->target = savetarget;
	}
}

void SP_point_combat (edict_t *self)
{
	if (dm->isDM())
	{
		G_FreeEdict (self);
		return;
	}

	SetWaitAction(self, 1, st.waitAction1);
	SetWaitAction(self, 2, st.waitAction2);
	SetWaitAction(self, 3, st.waitAction3);
	SetWaitAction(self, 4, st.waitAction4);
	SetWaitAction(self, 5, st.waitAction5);
	SetWaitAction(self, 6, st.waitAction6);

	SetMoveAction(self, st.moveAction);

	self->solid = SOLID_TRIGGER;
	self->touch = point_combat_touch;
	VectorSet (self->mins, -8, -8, -16);
	VectorSet (self->maxs, 8, 8, 16);
	self->svflags = SVF_NOCLIENT;
	gi.linkentity (self);
};


/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
Used as a positional target for spotlights, etc.
*/
void SP_info_null (edict_t *self)
{
	G_FreeEdict (self);
};


/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
Used as a positional target for lightning.
*/
void SP_info_notnull (edict_t *self)
{
	VectorCopy (self->s.origin, self->absmin);
	VectorCopy (self->s.origin, self->absmax);
};


/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
Non-displayed light.
Default light value is 300.
Style - the light 
0 - Normal, unflickering
1 - Flickering 1
2 - Slow strong Pulse
3 - Candle 1
4 - Fast Stribe
5 - Gentle Pulse 1
6 - Flicker  2
7 - Candle 2
8 - Candle 3
9 - Slow Strobe
10 - Flourscent Flicker
11 - Slow Pulse not Fade to Black
12 - Fast Pulse
13 - Special sky lightning
  	
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
*/

void light_use (edict_t *self, edict_t *other, edict_t *activator)
{
	if (self->spawnflags & SF_LIGHT_START_OFF)
	{
		gi.configstring (CS_LIGHTS+self->style, "m");
		self->spawnflags &= ~SF_LIGHT_START_OFF;
	}
	else
	{
		gi.configstring (CS_LIGHTS+self->style, "a");
		self->spawnflags |= SF_LIGHT_START_OFF;
	}
}

void LightInit (edict_t *self)
{
	if (self->style >= 32)
	{
		self->use = light_use;
		if (self->spawnflags & SF_LIGHT_START_OFF)
			gi.configstring (CS_LIGHTS+self->style, "a");
		else
			gi.configstring (CS_LIGHTS+self->style, "m");
	}
}


void SP_light (edict_t *self)
{
	if (!self->targetname || dm->isDM())
	{
		G_FreeEdict (self);
		return;
	}

	if (self->style >= 32)
	{
		self->use = light_use;
		if (self->spawnflags & SF_LIGHT_START_OFF)
			gi.configstring (CS_LIGHTS+self->style, "a");
		else
			gi.configstring (CS_LIGHTS+self->style, "m");
	}
}


/*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
This is just a solid wall if not inhibited

TRIGGER_SPAWN	the wall will not be present until triggered
				it will then blink in to existance; it will
				kill anything that was in it's way

TOGGLE			only valid for TRIGGER_SPAWN walls
				this allows the wall to be turned on and off

START_ON		only valid for TRIGGER_SPAWN walls
				the wall will initially be present
*/

void func_wall_use (edict_t *self, edict_t *other, edict_t *activator)
{
	if (self->solid == SOLID_NOT)
	{
		self->solid = SOLID_BSP;
		self->svflags &= ~SVF_NOCLIENT;
		KillBox (self);
	}
	else
	{
		self->solid = SOLID_NOT;
		self->svflags |= SVF_NOCLIENT;
	}
	gi.linkentity (self);

	if (!(self->spawnflags & 2))
		self->use = NULL;
}

void SP_func_wall (edict_t *self)
{
	self->movetype = MOVETYPE_PUSH;
	gi.setmodel (self, self->model);

	if (self->spawnflags & 8)
		self->s.effects |= EF_ANIM_ALL;
	if (self->spawnflags & 16)
		self->s.effects |= EF_ANIM_ALLFAST;

	// just a wall
	if ((self->spawnflags & 7) == 0)
	{
		self->solid = SOLID_BSP;
		gi.linkentity (self);
		return;
	}

	// it must be TRIGGER_SPAWN
	if (!(self->spawnflags & 1))
	{
//		gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
		self->spawnflags |= 1;
	}

	// yell if the spawnflags are odd
	if (self->spawnflags & 4)
	{
		if (!(self->spawnflags & 2))
		{
//			gi.dprintf("func_wall START_ON without TOGGLE\n");
			self->spawnflags |= 2;
		}
	}

	self->use = func_wall_use;
	if (self->spawnflags & 4)
	{
		self->solid = SOLID_BSP;
	}
	else
	{
		self->solid = SOLID_NOT;
		self->svflags |= SVF_NOCLIENT;
	}
	gi.linkentity (self);
}


/*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
This is solid bmodel that will fall if it's support it removed.
*/

void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, mtexinfo_t *surf)
{
	// only squash thing we fall on top of
	if (!plane)
		return;
	if (plane->normal[2] < 1.0)
		return;
	if (other->takedamage == DAMAGE_NO)
		return;
	T_Damage (other, self, self, vec3_origin, self->s.origin, self->s.origin, self->dmg, 1, 0, MOD_CRUSH);
}

void func_object_release (edict_t *self)
{
	self->movetype = MOVETYPE_TOSS;
	self->touch = func_object_touch;
}

void func_object_use (edict_t *self, edict_t *other, edict_t *activator)
{
	self->solid = SOLID_BSP;
	self->svflags &= ~SVF_NOCLIENT;
	self->use = NULL;
	KillBox (self);
	func_object_release (self);
}

void SP_func_object (edict_t *self)
{
	gi.setmodel (self, self->model);

	self->mins[0] += 1;
	self->mins[1] += 1;
	self->mins[2] += 1;
	self->maxs[0] -= 1;
	self->maxs[1] -= 1;
	self->maxs[2] -= 1;

	if (!self->dmg)
		self->dmg = 100;

	if (self->spawnflags == 0)
	{
		self->solid = SOLID_BSP;
		self->movetype = MOVETYPE_PUSH;
		self->think = func_object_release;
		self->nextthink = level.time + 2 * FRAMETIME;
	}
	else
	{
		self->solid = SOLID_NOT;
		self->movetype = MOVETYPE_PUSH;
		self->use = func_object_use;
		self->svflags |= SVF_NOCLIENT;
	}

	if (self->spawnflags & 2)
		self->s.effects |= EF_ANIM_ALL;
	if (self->spawnflags & 4)
		self->s.effects |= EF_ANIM_ALLFAST;

	self->clipmask = MASK_MONSTERSOLID;

	gi.linkentity (self);
}

//=====================================================

/*QUAKED target_character (0 0 1) ?
used with target_string (must be on same "team")
"count" is position in the string (starts at 1)
*/

void SP_target_character (edict_t *self)
{
	self->movetype = MOVETYPE_PUSH;
	gi.setmodel (self, self->model);
	self->solid = SOLID_BSP;
	self->s.frame = 12;
	gi.linkentity (self);
	return;
}


/*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
*/

void target_string_use (edict_t *self, edict_t *other, edict_t *activator)
{
	edict_t *e;
	int		n, l;
	char	c;

	l = strlen(self->message);
	for (e = self->teammaster; e; e = e->teamchain)
	{
		if (!e->count)
			continue;
		n = e->count - 1;
		if (n > l)
		{
			e->s.frame = 12;
			continue;
		}

		c = self->message[n];
		if (c >= '0' && c <= '9')
			e->s.frame = c - '0';
		else if (c == '-')
			e->s.frame = 10;
		else if (c == ':')
			e->s.frame = 11;
		else
			e->s.frame = 12;
	}
}

void SP_target_string (edict_t *self)
{
	if (!self->message)
		self->message = "";
	self->use = target_string_use;
}


/*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
target a target_string with this

The default is to be a time of day clock

TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
If START_OFF, this entity must be used before it starts

"style"		0 "xx"
			1 "xx:xx"
			2 "xx:xx:xx"
*/

#define	CLOCK_MESSAGE_SIZE	16

// don't let field width of any clock messages change, or it
// could cause an overwrite after a game load

static void func_clock_reset (edict_t *self)
{
	self->activator = NULL;
	if (self->spawnflags & 1)
	{
		self->health = 0;
		self->wait = self->count;
	}
	else if (self->spawnflags & 2)
	{
		self->health = self->count;
		self->wait = 0;
	}
}

static void func_clock_format_countdown (edict_t *self)
{
	if (self->style == 0)
	{
		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health);
		return;
	}

	if (self->style == 1)
	{
		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%02i", self->health / 60, self->health % 60);
		return;
	}

	if (self->style == 2)
	{
		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%02i:%02i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60);
		return;
	}
}

void func_clock_think (edict_t *self)
{
	if (!self->enemy)
	{
		self->enemy = G_Find (NULL, FOFS(targetname), self->target);
		if (!self->enemy)
			return;
	}

	if (self->spawnflags & 1)
	{
		func_clock_format_countdown (self);
		self->health++;
	}
	else if (self->spawnflags & 2)
	{
		func_clock_format_countdown (self);
		self->health--;
	}
	else
	{
		struct tm	*ltime;
		time_t		gmtime;

		time(&gmtime);
		ltime = localtime(&gmtime);
		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%02i:%02i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
	}

	self->enemy->message = self->message;
	self->enemy->use (self->enemy, self, self);

	if (((self->spawnflags & 1) && (self->health > self->wait)) ||
		((self->spawnflags & 2) && (self->health < self->wait)))
	{
		if (self->pathtarget)
		{
			char *savetarget;
			char *savemessage;

			savetarget = self->target;
			savemessage = self->message;
			self->target = self->pathtarget;
			self->message = NULL;
			G_UseTargets (self, self->activator);
			self->target = savetarget;
			self->message = savemessage;
		}

		if (!(self->spawnflags & 8))
			return;

		func_clock_reset (self);

		if (self->spawnflags & 4)
			return;
	}

	self->nextthink = level.time + 1;
}

void func_clock_use (edict_t *self, edict_t *other, edict_t *activator)
{
	if (!(self->spawnflags & 8))
		self->use = NULL;
	if (self->activator)
		return;
	self->activator = activator;
	self->think (self);
}

void SP_func_clock (edict_t *self)
{
	if (!self->target)
	{
		gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
		G_FreeEdict (self);
		return;
	}

	if ((self->spawnflags & 2) && (!self->count))
	{
		gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
		G_FreeEdict (self);
		return;
	}

	if ((self->spawnflags & 1) && (!self->count))
		self->count = 60*60;;

	func_clock_reset (self);

	self->message = (char*)gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL);

	self->think = func_clock_think;

	if (self->spawnflags & 4)
		self->use = func_clock_use;
	else
		self->nextthink = level.time + 1;
}


//=================================================================================

void FinishRespawn(edict_t *ent)
{
	edict_t *newEnt;

	// fixme:  check to see that the respawn location is unoccupied, esp. for deathmatch!
/*	 if (my space is occupied)
	 {
	
	  nextthink = level.time +.1;
		return;
	   }
*/	
	newEnt = G_Spawn();
	VectorCopy(ent->s.origin, newEnt->s.origin);
	VectorCopy(ent->s.angles, newEnt->s.angles);
	newEnt->health = ent->health;
	newEnt->spawnflags = ent->spawnflags;
	newEnt->wait = ent->wait;
	newEnt->accel = ent->accel;
	newEnt->style = ent->style;
	newEnt->targetname = ent->targetname;
	newEnt->target = ent->target;
	ent->respawnFunc(newEnt);

	G_FreeEdict(ent);
}

void InitiateRespawn(edict_t *ent)
{
	edict_t *newEnt;

	newEnt = G_Spawn();
	VectorCopy(ent->spawnOrigin, newEnt->s.origin);
	VectorCopy(ent->spawnAngles, newEnt->s.angles);
	newEnt->respawnFunc = ent->respawnFunc;
	newEnt->health = ent->spawnHealth;
	newEnt->movetype = MOVETYPE_NONE;
	newEnt->solid = SOLID_NOT;
	newEnt->spawnflags = ent->spawnflags;
	newEnt->wait = ent->wait;
	newEnt->accel = ent->accel;
	newEnt->style = ent->style;
	newEnt->targetname = ent->targetname;
	newEnt->target = ent->target;
	newEnt->think = FinishRespawn;

	newEnt->nextthink = level.time + ent->respawnTime;
}

void SetForRespawn(edict_t *ent, void (*spawnFunc)(edict_t *), float respawnDuration)
{
	VectorCopy(ent->s.origin, ent->spawnOrigin);
	VectorCopy(ent->s.angles, ent->spawnAngles);
	ent->spawnHealth = ent->health;
	ent->respawnFunc = spawnFunc;
	ent->respawnTime = respawnDuration;
}


/*QUAKED test_model (1 .5 0) (-16 -16 -16) (16 16 16)
set model to the name of the model to show
*/
void SP_test_model (edict_t *ent)
{
	ent->movetype = MOVETYPE_NONE;
	ent->solid = SOLID_BBOX;
	ent->s.modelindex = gi.modelindex (ent->model);
	VectorSet (ent->mins, -16, -16, -16);
	VectorSet (ent->maxs, 16, 16, 16);
	gi.linkentity (ent);
}


/*QUAKED func_breakable_brush (0 .5 .8) ? INVINCIBLE  PAINCHUNKS DEKKER CANT_DMG_IN_CAMERA_MODE
A breakable brush which has chunk size and type based upon the size and type of the brush.
--------SPAWNFLAGS----------
INVINCIBLE - intended to be used in conjunction with triggers
PAINCHUNKS - spawn debris when hurt (used for windows that shatter when first shot)
DEKKER - only damageable by Dekker, will do damage to folks in the area when breaking
CANT_DMG_IN_CAMERA_MODE -- you can damage this brush normally but it's invincible when player is viewing thru a camera
-------KEYS-----------------
count - # of debris chunks to throw

mass - size of chunks to throw
1 - small chunks
2 - medium chunks
3 - big chunks

health - defaults to 100.  The "dmg" key/value pair will set how much damage will be done on
its destruction (defaults to 0).  The "volume" key/value pair will determine the radius of the
damage.  Damage falls off linearly to zero at this radius.  Volume will default to whatever
"dmg" is set to. 

message - text to print when it dies

material - defines the type of debris to create when the brush breaks
 0 - MAT_NONE, (defaults to stone)
 1 - MAT_BRICK_BROWN,
 2 - MAT_BRICK_DBROWN,
 3 - MAT_BRICK_LBROWN,
 4 - MAT_BRICK_LGREY,
 5 - MAT_BRICK_DGREY,
 6 - MAT_BRICK_RED,

 7 - MAT_GLASS,

 8 - MAT_METAL_LGREY,
 9 - MAT_METAL_DGREY,
10 - MAT_METAL_RUSTY,
11 - MAT_METAL_SHINY,

12 - MAT_ROCK_BROWN,
13 - MAT_ROCK_LBROWN,
14 - MAT_ROCK_DBROWN,
15 - MAT_ROCK_LGREY,
16 - MAT_ROCK_DGREY,

17 - MAT_WOOD_LBROWN,
18 - MAT_WOOD_DBROWN,
19 - MAT_WOOD_LGREY,
20 - MAT_WOOD_DGREY,

21 - MAT_WALL_BLACK,
22 - MAT_WALL_BROWN,
23 - MAT_WALL_DARKBROWN,
24 - MAT_WALL_LIGHTBROWN,
25 - MAT_WALL_GREY,
26 - MAT_WALL_DARKGREY,
27 - MAT_WALL_LIGHTGREY,
28 - MAT_WALL_GREEN,
29 - MAT_WALL_ORANGE,
30 - MAT_WALL_RED,
31 - MAT_WALL_WHITE,
32 - MAT_ROCK_FLESH,
33 - MAT_WALL_STRAW,
34 - MAT_ROCK_SNOW,


surfaceType - defines what sound to use when the brush breaks or takes damage
0 - defaults to stone... sorry Jersey :{ 
14 - wood
17 - stone			
28 - metal
35 - blood (how does blood break? I don't know.)
37 - glass
40 - paper

*/

void breakable_brush_pain (edict_t *self, edict_t *other, float kick, int damage, vec3_t wherehit)
{
	float			volume;
	vec3_t			origin;
	byte x_max,y_max,z_max;
	int scale,numchunks;
	vec3_t			debrisNorm;

	VectorClear(debrisNorm);
	volume = self->size[0] * self->size[1] * self->size[2];

	// Calc numchunks???
	numchunks = (byte)((volume / 9000) + 2); 

	if (numchunks > 20)
		numchunks = 20;
	else if (numchunks < 10)
		numchunks = 10;

	// sending a scale of 0 will not give you the debris you want
	scale = DEBRIS_SM;

	// Calc scale on breakable brushes
	if (strcmp(self->classname, "func_breakable_brush") == 0)
	{
		// Find scale of debris to throw
		if (volume > 250000)
			scale = DEBRIS_LRG;
		else if (volume > 40000)
			scale = DEBRIS_MED;
		else 
			scale = DEBRIS_SM;
	}

	VectorAdd(self->absmax,self->absmin, origin);
	VectorScale(origin, .5, origin);

	if (self->material > 0)
	{
		if (strcmp(self->classname, "func_breakable_brush") == 0)
		{
			x_max = self->size[0];
			y_max = self->size[1];
			z_max = self->size[2]*0.5;

		}
		else
		{
			x_max = (byte) self->maxs[0];
			y_max = (byte) self->maxs[1];
			z_max = (byte) self->maxs[2];
		}

		VectorSubtract (other->s.origin, origin, debrisNorm);
		VectorNormalize(debrisNorm);
		FX_ThrowDebris(origin,debrisNorm, numchunks, scale, self->material,x_max,y_max,z_max, self->surfaceType);
	}
}

extern void breakable_brush_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
	vec3_t			origin;
	float			volume;

	volume = self->size[0] * self->size[1] * self->size[2];
	
	VectorAdd(self->absmax,self->absmin, origin);
	VectorScale(origin, .5, origin);

	// kef -- possibly spawn some pickups from a broken crate?
	thePickupList.BustCrate(self);
	
	if (self->dmg)
	{
		if (!self->volume)
		{
			self->volume = self->dmg;
		}
		T_RadiusDamage (self, self, self->dmg, self, self->volume, MOD_EXPLOSIVE);	
		gmonster.RadiusDeafen(self, self->dmg, self->dmg);
	}

	BecomeDebris(self,inflictor,attacker,damage,point);
}
	
void breakable_brush_use(edict_t *self, edict_t *other, edict_t *activator)
{
	breakable_brush_die (self, self, other, self->health, vec3_origin);
}

void SP_func_breakable_brush (edict_t *ent)
{
	ent->solid = SOLID_BSP;
	ent->movetype = MOVETYPE_NONE;
	
	if (ent->spawnflags & INVINCIBLE)
	{
		ent->takedamage = DAMAGE_NO;
	}
	else if (ent->spawnflags & BREAKABLE_NOT_IN_CAM)
	{
		ent->takedamage = DAMAGE_YES_BUT_NOT_IN_CAMERA; // 3/1/00 kef -- used to be DAMAGE_YES
	}
	else
	{
		ent->takedamage = DAMAGE_YES;
	}
	
	if ((st.surfaceType > 0) && (st.surfaceType < SURF_NUM))
	{
		ent->surfaceType = st.surfaceType;
	}
	else
	{
		ent->surfaceType = SURF_DEFAULT; // kef -- ARGH!!!
	}

	if (!ent->health)
	{
		if ((ent->material == MAT_GLASS) || (ent->surfaceType == SURF_GLASS))
			ent->health = 50;	// So windows break after one shot.
		else
			ent->health = 100;
	}

	if (ent->targetname)
	{
		ent->use = breakable_brush_use;
	}
	
	ent->die = breakable_brush_die;

	if ((st.material < MATERIAL_NUM) && (st.material >= 0))
		ent->material = st.material;
	else
		ent->material = SURF_NONE;

	if (ent->spawnflags & GLASS_PAINCHUNKS)
	{
		ent->pain = breakable_brush_pain;
	}

	gi.setmodel (ent, ent->model);
	gi.linkentity (ent);
}

/*QUAKED func_trigger_brush (0 .5 .8) ? NO_DRAW START_OFF
A brush that can take damage to a point, then call its triggers.

Health defaults to 100.  

*/

#define		TRIGGER_BRUSH_NO_DRAW			1
#define		TRIGGER_BRUSH_START_OFF			2

extern void trigger_brush_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
	G_UseTargets(self,self);
	G_FreeEdict(self);
}
	
void trigger_brush_use(edict_t *self, edict_t *other, edict_t *activator)
{
	// 1/7/00 kef -- this is what it used to do.
	//trigger_brush_die (self, self, other, self->health, vec3_origin);

	if (self->takedamage == DAMAGE_NO)
	{
		self->takedamage = DAMAGE_YES;
	}
	else
	{
		self->takedamage = DAMAGE_NO;
	}
}

void SP_func_trigger_brush (edict_t *ent)
{
	ent->solid = SOLID_BSP;
	ent->movetype = MOVETYPE_NONE;
	
	if (!ent->health)
	{
		ent->health = 100;
	}

	ent->takedamage = DAMAGE_YES;
	if (ent->spawnflags & TRIGGER_BRUSH_START_OFF)
	{
		ent->takedamage = DAMAGE_NO;
	}

	if (ent->spawnflags  & TRIGGER_BRUSH_NO_DRAW)
	{
		ent->svflags = SVF_NOCLIENT;
	}

	if (ent->targetname)
	{
		ent->use = trigger_brush_use;
	}
	
	ent->die = trigger_brush_die;

	gi.setmodel (ent, ent->model);
	gi.linkentity (ent);
}

void func_score_use(edict_t *self, edict_t *other, edict_t *activator)
{
	char	*teamname;

	if (activator->client)
	{
		if (self->team)	// Team specific score
		{
			teamname=Info_ValueForKey(activator->client->pers.userinfo,"teamname");
			if (teamname)
			{
				if (!strcmp(self->team,teamname))
				{
					activator->client->resp.score += self->count;
				}
			}
		}
		else
		{
			activator->client->resp.score += self->count;
		}
	}
}

/*QUAKED func_score (1 0 0) (-8 -8 -8) (8 8 8)
Awards certain amount of score to a player
-----KEYS-----
count - amount awarded to player
*/
void SP_func_score (edict_t *ent)
{
	ent->solid = SOLID_NOT;
	ent->takedamage = DAMAGE_NO;
	ent->use = func_score_use;
	gi.linkentity (ent);
}



//ShakeCameras (ent->s.origin, ent->health, ent->health*2);

/*QUAKED func_camerashake (0 .5 .8) (-8 -8 -8) (8 8 8)
Shakes the camera, centered on where the func is
"count" = intensity (from 0 to 100) - default is 30
"health" = radius of shaking (will fall off over distance) - default is 300
"delay" = the duration of the shaking (in seconds) - default is 3
"style" = makes a sound as it shakes the camera - 0 is default
	1 == Dull explosion
*/

#define DEFAULT(a, b) ((a) ? (a):(b))
#define FUNCSHAKE_THINK_TIME .2

void funcshake_think(edict_t *self)
{
	ShakeCameras(self->s.origin, self->count, self->health, DEFAULT_JITTER_DELTA);

	if(self->wait < level.time)
	{
		self->nextthink = 0;
	}
	else
	{
		self->nextthink = level.time + FUNCSHAKE_THINK_TIME;
	}
}

void funcshake_use (edict_t *self, edict_t *other, edict_t *activator)
{
	self->wait = level.time + self->delay;
	self->nextthink = level.time;//eh?

	if(self->style == 1)
	{
		gi.sound (self, CHAN_VOICE, gi.soundindex ("Ambient/Gen/Battle/ExpRvb.wav"), .8, 0, 0);
	}
}

void SP_func_camerashake(edict_t *self)
{
	self->use = funcshake_use;
	self->count = DEFAULT(self->count, 30);
	self->health = DEFAULT(self->health, 300);
	self->delay = DEFAULT(self->delay, 3);

	self->think = funcshake_think;

	if(self->style == 1)
	{
		gi.soundindex("Ambient/Gen/Battle/ExpRvb.wav");
	}
}

/*QUAKED _region (0 1 1) ?

For FOG Areas:
   fog_mode (0 = normal, 1 = additive)
   fog_start (0.0 to infinity) (4.0 recommended)
   fog_end  (fog_start to infinity) (400.0 recommended)
   fog_height (fog_start to infinity)
   fog_density (0.0 to 1.0)
   fog_density_range (-1.0 to 1.0)
   fog_color (vector RGB)
   fog_flags (1 = PULSATE, 2 = HEIGHT, 4 = MAP, 8 = ANTI)
   fog_time (speed)
   fog_distance_cull (distance for culling fog areas)
   fog_chop_size (size bsp should break up (128 default) )
*/


/*QUAKED func_fade (0 .5 .8) (-8 -8 -8) (8 8 8)  REVERSE
"color" color to fade to in red green blue format, from 0.0 to 1.0, (with 1.0 being white like "1.0 1.0 1.0")
"delay" the length of time it'll take to fully fade -- defaults to 5  NOTE: do NOT set this
to zero!  If you want instantaneous, use .1

Reverse flag fades from the color to "normal"

*/

#define FADE_REVERSE	1

void funcfade_think (edict_t *self)
{
	edict_t *player = NULL;

	while ((player = G_Find (player, FOFS(classname), "player")) != NULL)
	{
		if ( self->spawnflags & FADE_REVERSE)
		{
			player->client->fade_alpha -= .1/self->delay;
			if (player->client->fade_alpha < 0)
			{
				player->client->fade_alpha = 0;
				self->nextthink = 0;
			}
			else
			{
				self->nextthink = level.time+.1;
			}
		}
		else
		{
			player->client->fade_alpha += .1/self->delay;
			if (player->client->fade_alpha > 1)
			{
				player->client->fade_alpha = 1;
				self->nextthink = 0;
			}
			else
			{
				self->nextthink = level.time+.1;
			}
		}
	}
}

void funcfade_use (edict_t *self, edict_t *other, edict_t *activator) // put this in g_save
{
	edict_t *player = NULL;

	while ((player = G_Find (player, FOFS(classname), "player")) != NULL)
	{
		VectorCopy(self->intend_velocity, player->client->fade_rgb);
		if (self->spawnflags & FADE_REVERSE)
		{
			player->client->fade_alpha = 1 + .1/self->delay;
		}
		else
		{
			player->client->fade_alpha = 0;//just in case
		}			
	}
	funcfade_think(self);
}

void SP_func_fade(edict_t *self)
{

	VectorClear(self->intend_velocity);
	VectorCopy(st.color, self->intend_velocity);
	self->delay = DEFAULT(self->delay, 5);
	self->use = funcfade_use;
	self->think = funcfade_think;
	self->nextthink = 0;
}