// g_misc.c

#include "g_local.h"


/*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;
	ent->count = 0;		// always start closed;
}

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


/*
=================
Misc functions
=================
*/
void VelocityForDamage (int damage, vec3_t v)
{
	v[0] = 300.0 * crandom();
	v[1] = 300.0 * crandom();
	v[2] = 200.0 + 400.0 * random();

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

void ClipGibVelocity (edict_t *ent)
{
	if (ent->velocity[0] < -400)
		ent->velocity[0] = -400;
	else if (ent->velocity[0] > 400)
		ent->velocity[0] = 400;
	if (ent->velocity[1] < -400)
		ent->velocity[1] = -400;
	else if (ent->velocity[1] > 400)
		ent->velocity[1] = 400;
	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 + 8 + random()*10;
	}
}

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

	if (!self->groundentity)
		return;

	self->touch = NULL;

	if (plane)
	{
		// JOSEPH 29-MAR-99
		//gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
		// END JOSEPH

		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;
		}
	}
}

// RAFAEL
/* Ridah, this has been diasabled in the latest code
void gib_touchacid (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
	vec3_t normal_angles, right;

	if (other->takedamage)
	{
		// T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0);
		T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
		G_FreeEdict (self);
	}

	if (!self->groundentity)
		return;

	if (plane)
	{
		// JOSEPH 29-MAR-99
		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
		// END JOSEPH

		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, int mdx_part, int mdx_subobject)
{
	G_FreeEdict (self);
}

edict_t *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] + crandom() * size[0];
	gib->s.origin[1] = origin[1] + crandom() * size[1];
	gib->s.origin[2] = origin[2] + crandom() * 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->s.renderfx2 |= RF2_NOSHADOW;
	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] = random()*600;
	gib->avelocity[1] = random()*600;
	gib->avelocity[2] = random()*600;

	gib->think = G_FreeEdict;
	gib->nextthink = level.time + 10 + random()*10;

	gi.linkentity (gib);

	return 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);

//	self->s.modelindex2 = 0;
	gi.setmodel (self, gibname);
	self->solid = SOLID_NOT;
	self->s.effects |= EF_GIB;
	self->s.effects &= ~EF_FLIES;
	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] = crandom()*600;

	self->think = G_FreeEdict;
	self->nextthink = level.time + 10 + random()*10;

	gi.linkentity (self);
}





void ThrowClientHead (edict_t *self, int damage)
{
	vec3_t	vd;
	char	*gibname;
/*
	if (rand()&1)
	{
		gibname = "models/objects/gibs/head2/tris.md2";
		self->s.skinnum = 1;		// second skin is player
	}
	else
	{
		gibname = "models/objects/gibs/skull/tris.md2";
		self->s.skinnum = 0;
	}
*/
	gibname = "models/props/gibs/gib3.mdx";
	self->s.skinnum = 0;

	self->s.origin[2] += 32;
	self->s.frame = 0;

//	gi.setmodel (self, gibname);
	self->s.num_parts = 0;
	self->s.modelindex = gi.modelindex( gibname );

	VectorSet (self->mins, -16, -16, 0);
	VectorSet (self->maxs, 16, 16, 16);

	self->takedamage = DAMAGE_NO;
	self->solid = SOLID_NOT;
//	self->s.effects |= EF_GIB;
	self->s.sound = 0;
// ANDI	self->flags |= FL_NO_KNOCKBACK;
	self->svflags |= SVF_NOCLIENT; // ANDI FIXED SZ_GETSPACE

	self->movetype = MOVETYPE_BOUNCE;
	VelocityForDamage (damage, vd);
	VectorAdd (self->velocity, vd, self->velocity);

	if (self->client)	// bodies in the queue don't have a client anymore
	{
		self->client->anim_priority = ANIM_DEATH;
		self->client->anim_end = self->s.frame;
	}
	else
	{
		self->think = NULL;
		self->nextthink = 0;
	}

	gi.linkentity (self);
}

/*
=============
GibEntity

  Gibs an entity.. can be used for client's or AI
=============
*/
void GibEntity( edict_t *self, edict_t *inflictor, float damage )
{
	vec3_t	dir, vel;
	int		m, n;

	// turn off flames
	if (self->onfiretime)
		self->onfiretime = 0;

	if (inflictor->client || VectorCompare( inflictor->velocity, vec3_origin) )
		VectorSubtract( self->s.origin, inflictor->s.origin, dir );
	else
		VectorCopy( inflictor->velocity, dir );

	VectorNormalize( dir );

	// JOSEPH 12-MAY-99-B
	// send the client-side gib message
	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_GIBS);
	gi.WritePosition (self->s.origin);
	gi.WriteDir (dir);
	gi.WriteByte ( 16 );	// number of gibs
	gi.WriteByte ( (damage*2 > 128 ? 128 : (int)(damage*2)) );	// scale of direction to add to velocity
	gi.WriteByte ( 16 );	// random origin offset scale
	gi.WriteByte ( 100 );	// random velocity scale
	gi.multicast (self->s.origin, MULTICAST_PVS);
	// END JOSEPH

	for (n= 0; n < 6; n++)
	{

		VelocityForDamage (damage, vel);

		// spawn a decal in this direction
		for (m=0; m<4; m++)	// try 4 times with a different Z value
		{
			vec3_t	bdir;
			vec3_t	dorg;
			trace_t	tr;

			VectorCopy( vel, bdir );
			bdir[2] -= random()*bdir[2]*(1+4*random());

			VectorAdd( self->s.origin, bdir, dorg );

			tr = gi.trace( self->s.origin, NULL, NULL, dorg, self, MASK_SOLID );

			if (tr.fraction < 1 && tr.ent == &g_edicts[0] && !(tr.surface->flags & SURF_SKY))
			{
				float rnd;

				rnd = (1.5 + 5.0*random());
				if (SurfaceSpriteEffect(SFX_SPRITE_SURF_BLOOD1, (unsigned char)(SFX_BLOOD_WIDTH*rnd), (unsigned char)(SFX_BLOOD_HEIGHT*rnd),
										tr.ent, tr.endpos, tr.plane.normal))
				{
					break;
				}
			} 

		}
	}

	if (!self->client)	// Ridah, added this check, since in deathmatch, the body is left there, just only clients with parental lock ENABLEd will see them, since they don't see the gibs
		ThrowClientHead (self, damage);

	self->takedamage = DAMAGE_NO;
}

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

// JOSEPH 22-FEB-99
void Think_Debris (edict_t *ent)
{
	if (!ent->misstime--)
	{	
		ent->nextthink = 0;
		G_FreeEdict(ent);
	}
	else
	{
		if (ent->misstime <= 15)
		{
			if (ent->misstime == 15)
			{
				ent->s.renderfx2 |= RF2_PASSALPHA;
				ent->s.effects = 1;		// this is full alpha now
			}
			
			ent->s.effects += (255/15);
		
			if (ent->s.effects > 255)
				ent->s.effects = 255;
		}

		//ent->s.angles[1] += ent->avelocity[1];
		ent->nextthink = level.time + 0.1;
	}
}

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] = 50 * crandom();
	v[1] = 50 * crandom();
	v[2] = 50 + 50 * crandom();
	VectorMA (self->velocity, speed, v, chunk->velocity);
	chunk->movetype = MOVETYPE_BOUNCE;
	chunk->solid = SOLID_NOT;
	chunk->avelocity[0] = random()*600;
	chunk->avelocity[1] = random()*600;
	chunk->avelocity[2] = random()*600;
	chunk->think = G_FreeEdict;
	chunk->nextthink = level.time + 5 + random()*5;
	chunk->s.frame = 0;
	chunk->flags = 0;
	chunk->classname = "debris";
	chunk->takedamage = DAMAGE_YES;
	chunk->die = debris_die;
	
	chunk->think = Think_Debris;
	chunk->misstime = 20;
	chunk->nextthink = level.time + 0.1;
    chunk->s.renderfx2 |= RF2_NOSHADOW;
	chunk->avelocity[1] = ((rand()&15)-8);
	//chunk->s.angles[0] = 90;
	gi.linkentity (chunk);
}
// END JOSEPH

// JOSEPH 5-JUN-99-B
void ThrowDebris_stuff (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] = 150 * crandom();
	v[1] = 150 * crandom();
	v[2] = 150 + 50 * crandom();
	VectorMA (self->velocity, speed, v, chunk->velocity);
	chunk->movetype = MOVETYPE_BOUNCE;
	chunk->solid = SOLID_NOT;
	chunk->avelocity[0] = random()*600;
	chunk->avelocity[1] = random()*600;
	chunk->avelocity[2] = random()*600;
	chunk->think = G_FreeEdict;
	chunk->nextthink = level.time + 5 + random()*5;
	chunk->s.frame = 0;
	chunk->flags = 0;
	chunk->classname = "debris";
	chunk->takedamage = DAMAGE_YES;
	chunk->die = debris_die;
	
	chunk->think = Think_Debris;
	chunk->misstime = 40;
	chunk->nextthink = level.time + 0.1;
    chunk->s.renderfx2 |= RF2_NOSHADOW;
	chunk->avelocity[1] = ((rand()&15)-8);
	chunk->lightit = 30;
	gi.linkentity (chunk);
}
// END JOSEPH

// JOSEPH 1-JUN-99
extern void rat_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject);

void BecomeExplosion1 (edict_t *self)
{
	if ((self->classname) && (!strcmp(self->classname, "props_rat")))
	{
		rat_die (self, NULL, NULL, 0, NULL, 0, 0);
	}
	else
	{
		vec3_t	vec;

		VectorClear(vec);
		vec[2] = 1;

		gi.WriteByte (svc_temp_entity);
		gi.WriteByte (TE_EXPLOSION1);
		gi.WritePosition (self->s.origin);
		gi.WriteDir( vec );
		gi.WriteByte( (int)(self->dmg / 2) );
		gi.WriteByte (self->fxdensity);
		gi.multicast (self->s.origin, MULTICAST_PVS);

		{
			edict_t *breakit;
			
			breakit = G_Spawn();
			
			if (breakit)
			{
				VectorCopy (self->s.origin, breakit->s.origin);
				gi.linkentity(breakit);
				gi.sound (breakit, CHAN_VOICE, gi.soundindex("world/explosion1.wav"), 1, ATTN_NORM, 0);
				breakit->think = G_FreeEdict;
				breakit->nextthink = level.time + 5.0;
			}
		}
		
		G_FreeEdict (self);
	}
}
// END JOSEPH

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_cast (.5 .3 0) (-16 -16 -24) (16 16 42) TELEPORT
Target: next path corner
Scriptname: hard-coded script to call when reaching this marker
Pathtarget: gets used when an entity that has
	this path_corner targeted touches it
	Could be used to trigger a button, or a character
Combattarget: when the cast reaches this marker,
	they'll take on this combattarget
*/

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

	if ((other->goal_ent != self) && (other->combat_goalent != self))
		return;

	if (other->health < 0)
		return;
	
	if (self->pathtarget)
	{
		char *savetarget;

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

	// play a sound if there is one
	if (self->name)
		gi.sound( other, CHAN_VOICE, gi.soundindex( self->name ), 1.0, 1, 0 );


	if (self->combattarget)
		other->next_combattarget = self->combattarget;

	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);
	}

	if (other->goal_ent == self)
		other->goal_ent = next;
	else
		other->combat_goalent = next;

	if (self->wait)
	{
		other->cast_info.goal_ent_pausetime = level.time + self->wait;
		other->cast_info.currentmove = other->cast_info.move_stand;
		goto done;
	}

	if (!other->goal_ent && !other->combat_goalent)
	{
		// end of the line
		other->target = NULL;
		other->cast_info.currentmove = other->cast_info.move_stand;
	}
	else	// remember this marker
	{
		if (other->goal_ent == self)
		{
			other->target = other->goal_ent->target;
			VectorSubtract (other->goal_ent->s.origin, other->s.origin, v);
		}
		else	// combattarget
		{
			
			if (!(other->combat_goalent))
			{
				other->target = other->goal_ent->target;
				VectorSubtract (other->goal_ent->s.origin, other->s.origin, v);
			}
			else
			{
				VectorSubtract (other->combat_goalent->s.origin, other->s.origin, v);
				other->combat_goalent->cast_info.aiflags |= AI_GOAL_RUN;
			}

		}
		other->ideal_yaw = vectoyaw (v);
	}

done:

	// Ridah, hard-coded scripting
	if (self->scriptname)
		EP_EventScript( other, self->scriptname );

}

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

	strcpy(self->classname, "path_corner");		// so the existing code still works

	VectorSet (self->mins, -16, -16, -24);
	VectorSet (self->maxs, 16, 16, 42);

	self->solid = SOLID_TRIGGER;
	self->touch = path_corner_cast_touch;
	self->svflags |= SVF_NOCLIENT;

	M_droptofloor(self);
	self->s.origin[2] += 4;

	gi.linkentity (self);
}

/*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
NOTE: Use path_corner_cast for character path's
Target: next path corner
Pathtarget: gets used when an entity that has
	this path_corner targeted touches it
*/

void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_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->cast_info.pausetime = level.time + self->wait;
//		other->cast_info.stand (other);
		return;
	}

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

}

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

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

// JOSEPH 8-FEB-99
/*UAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
Just for the debugging level.  Don't use
*/
// END JOSEPH
void TH_viewthing(edict_t *ent)
{
	ent->s.frame = (ent->s.frame + 1) % 7;
	ent->nextthink = level.time + FRAMETIME;
}

void SP_viewthing(edict_t *ent)
{
	gi.dprintf ("viewthing spawned\n");

	ent->movetype = MOVETYPE_NONE;
	ent->solid = SOLID_BBOX;
	ent->s.renderfx = RF_FRAMELERP;
	VectorSet (ent->mins, -16, -16, -24);
	VectorSet (ent->maxs, 16, 16, 32);
	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
	gi.linkentity (ent);
	ent->nextthink = level.time + 0.5;
	ent->think = TH_viewthing;
	return;
}


/*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)
{
	if (!self->targetname)		// Ridah, 5-7-99, had to add this or the info_null behind the radio gets deleted
		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);
};


#define	MIN_LIGHT_VALUE		128
#define	MIN_LIGHT_INTENSITY	0.2

#define START_OFF		1
#define FLARE			2
#define DYNAMIC			8


void AddLightSource(edict_t *self)
{
	if (!(self->spawnflags & DYNAMIC))
		return;
	
	if (level.num_light_sources >= MAX_LIGHT_SOURCES)
	{
		gi.dprintf("Warning: MAX_LIGHT_SOURCES exceeded\n");
		return;
	}

	if (self->dmg_radius <= 0.0)
		self->dmg_radius = 1.0;

	if (self->light_level < MIN_LIGHT_VALUE)
		self->light_level = MIN_LIGHT_VALUE;

	VectorCopy( self->s.origin, level.light_orgs[level.num_light_sources] );
	VectorCopy( self->rotate, level.light_colors[level.num_light_sources] );
	// light_values[num_light_sources] = ((float) self->light_level) * 3.0;
	level.light_values[level.num_light_sources] = ((float) self->light_level * 1.5);
	// level.light_radius[level.num_light_sources] = self->dmg_radius;
	level.light_styles[level.num_light_sources] = self->style;

	level.num_light_sources++;

	self->svflags |= SVF_NOCLIENT;
}

void LightConfigstrings ()
{
	char	str[256];
	char	fullstr[256];
	int		i, inc, j;

	fullstr[0] = str[0] = '\0';
	inc = 0;
	j = 0;

	for (i=0; i<level.num_light_sources; i++)
	{

		sprintf( str, 
			"%5i %5i %5i %1.1f %1.1f %1.1f %4i %3i",		// "99999 99999 99999 9.9 9.9 9.9 9999 999"
			(int)level.light_orgs[i][0], (int)level.light_orgs[i][1], (int)level.light_orgs[i][2],
			level.light_colors[i][0], level.light_colors[i][1], level.light_colors[i][2],
			(int)level.light_values[i],
			(int)level.light_styles[i]);

		strcat( fullstr, str );

//		if (inc++)
		{
			gi.configstring (CS_JUNIORS + j++, fullstr );

			fullstr[0] = '\0';
			inc = 0;
		}
//		else	// add the seperator
//		{
//			strcat( fullstr, " : " );
//		}
	}

	i -= 1;

	if (inc)
		gi.configstring (CS_JUNIORS + (int)floor(i/2), fullstr );
}

void UpdateDirLights( edict_t *self )
{
	int	i, ent_lights=0;
	vec3_t	vec;
	int j;


	for (i=0; i<level.num_light_sources; i++)
	{
		
		VectorSubtract( level.light_orgs[i], self->s.origin, vec );

// trivia reject 

		// lights less that 100 are rejected by default done in sp_light

		{
			float length;

			length = VectorLength (vec);

			if (((level.light_values[i]) - length) < 0)
				continue;
		}

		if (!gi.inPVS(self->s.origin, level.light_orgs[i]))
			continue;

		{
			vec3_t	spot1;
			vec3_t	spot2;
			trace_t	trace;

			VectorCopy (self->s.origin, spot1);
			spot1[2] += self->viewheight;
			VectorCopy (level.light_orgs[i], spot2);
			trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
			
			if (trace.fraction != 1.0)
				continue;
		}

// passed normal tests. if we've used all lights, look for one to replace

		if (ent_lights >= MAX_MODEL_DIR_LIGHTS)
		{
			for (j=0; j<MAX_MODEL_DIR_LIGHTS; j++)
			{
				if (	(self->s.model_lighting.light_intensities[j] - VectorDistance( self->s.origin, self->s.model_lighting.light_orgs[j] ))
					<	(level.light_values[i] - VectorDistance( self->s.origin, level.light_orgs[i] )))
				{
					break;	// replace this light
				}
			}

			if (j == MAX_MODEL_DIR_LIGHTS)
				continue;		// no light to replace
		}
		else
		{
			j = ent_lights;
		}

// end reject test

		self->s.model_lighting.light_indexes[j] = i;

		self->s.model_lighting.light_intensities[j] = level.light_values[i];
		VectorCopy( level.light_colors[i], self->s.model_lighting.light_colors[j] );
		VectorCopy( level.light_orgs[i], self->s.model_lighting.light_orgs[j] );
		VectorNormalize( vec );
		VectorCopy( vec, self->s.model_lighting.light_vecs[j] );
		self->s.model_lighting.light_styles[j] = level.light_styles[i];

		if (showlights->value)
			NAV_DrawLine( self->s.origin, level.light_orgs[i] );

		if (ent_lights < MAX_MODEL_DIR_LIGHTS)
			ent_lights++;

	}

	if (showlights->value)
		gi.dprintf ("num lights %d\n", ent_lights);

	self->s.model_lighting.num_dir_lights = ent_lights;
}

/*QUAKED junior (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
*/

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

void SP_junior (edict_t *self )
{
	if (self->style >= 32)
	{
		self->use = junior_use;
		if (self->spawnflags & START_OFF)
			gi.configstring (CS_LIGHTS+self->style, "a");
		else
			gi.configstring (CS_LIGHTS+self->style, "m");
	}
	
	self->spawnflags |= DYNAMIC;
	
	if (!(self->light_level))
		self->light_level = 300;
	
	AddLightSource(self);

}


// -----------------------------------------------------------------------------

/*QUAKED lightflare (0 1 0) (-2 -2 -2) (2 2 2) START_OFF FLARE  NORESIZE DYNAMIC
Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
*/

/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF FLARE NORESIZE DYNAMIC
Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
*/

void SP_Flare (edict_t *self);

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

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

	// RAFAEL
	if (self->spawnflags & FLARE)
	{
		SP_Flare (self);
	}
	// flares will never be a level.light_source
	else
	{
		if (!(self->light_level))
			self->light_level = 300;

		if (self->light_level > 100)
			AddLightSource(self);
	}
}

#define RESIZE 2

void SP_Flare (edict_t *self)
{
	lightflare_t	lf;
//	char			lf_str[256], temp_str[256], zero_str[256];//, numstr[2];
//	int				i,j;

	if (num_flares == MAX_LIGHTFLARES-1)
	{
		gi.dprintf("Too many light flares, num_flares == MAX_LIGHTFLARES\n");
		return;
	}

	if (!self->health)	// size
		self->health = 24;

	if (!self->dmg)		// type
		self->dmg = FLARE_NORMAL;

	if (self->dmg > 5)
	{
		gi.dprintf("Not a valid flare color 0,1,2,3,4,5 only\n");
		return;
	}

	lf.lightstyle = (char)self->style;

	if (self->spawnflags & 4)
		lf.lightstyle |= RESIZE;
	
	lf.type = (char) self->dmg;

	lf.size = (float) self->health;

	VectorCopy( self->s.origin, lf.pos );

// NOTE to Rafael this just shows what sort of values are required to define a Sun-Flare
/*
{
	static int done_sun=0;

	if (!done_sun)
	{
		lf.type = FLARE_SUN;					// use self->dmg in editor
		lf.size = 24;

		VectorSet( lf.pos, 1000, 1000, 3000 );	// if the mappers place the sunflare out near the extremes of the map, in the position of the sun, these values will be set accordingly (no need to specify manually)
		done_sun = true;
	}
}
*/

	if (lf.type == FLARE_SUN)
		VectorNormalize( lf.pos );

	// just send it as a formatted string
	{
		char	str[256];

		sprintf( str, 
			"%3i %1i %4.1f %4.1f %4.1f %3.0f",		// "999 9 9999.9 9999.9 9999.9 999" length = 30
			(int)lf.lightstyle,
			(int)lf.type,
			lf.pos[0], lf.pos[1], lf.pos[2],
			lf.size );

		gi.configstring (CS_LIGHTFLARES + num_flares, str );

		num_flares++;

//gi.dprintf( "game flares: %i\n", num_flares );
	}

}

// JOSEPH 12-MAY-99-B
#define LIGHTB	16
#define LIGHTG	32
#define LIGHTR	64
#define LIGHTY	128

/*QUAKED light_sconce (1 1 1) (-4 -4 -8) (4 4 8) START_OFF FLARE NORESIZE DYNAMIC LIGHTB LIGHTG LIGHTR LIGHTY

A sconce??

Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
  
model="models\props\sconce\light.md2"
*/

void SP_light_sconce (edict_t *self)
{
	self->solid = SOLID_NOT;
	self->movetype = MOVETYPE_NONE;
	self->svflags |= SVF_PROP;

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

	if (self->spawnflags & FLARE)
	{
		SP_Flare (self);
	}
	// flares will never be a level.light_source
	else
	{
		if (!(self->light_level))
			self->light_level = 300;

		if (self->light_level > 100)
			AddLightSource(self);
	}

	if (self->spawnflags & LIGHTB)
	{
		self->model = "models/props/sconce/lightb.md2";
	}
	else if (self->spawnflags & LIGHTG)
	{
		self->model = "models/props/sconce/lightg.md2";
	}
	else if (self->spawnflags & LIGHTR)
	{
		self->model = "models/props/sconce/lightr.md2";
	}
	else if (self->spawnflags & LIGHTY)
	{
		self->model = "models/props/sconce/lighty.md2";
	}
	else
	{
		self->model = "models/props/sconce/light.md2";
	}	

	self->s.modelindex = gi.modelindex (self->model);

	self->s.renderfx2 |= RF2_NOSHADOW;

	if ((self->light_level) && (!(self->spawnflags & LIGHTB)))
	{
		self->s.renderfx |= RF_FULLBRIGHT;
		self->flags |= RF_FULLBRIGHT;
	}

 	gi.linkentity (self);
}
// END JOSEPH

// JOSEPH 1-MAR-99
/*QUAKED light_bulb (1 1 1) (-2 -2 -5) (2 2 5) START_OFF FLARE NORESIZE DYNAMIC

A bulb that outputs light

Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
*/
// END JOSEPH
void SP_light_bulb (edict_t *self)
{
	self->solid = SOLID_NOT;
	self->movetype = MOVETYPE_NONE;
	self->svflags |= SVF_PROP;

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

	if (self->spawnflags & FLARE)
	{
		SP_Flare (self);
	}
	// flares will never be a level.light_source
	else
	{
		if (!(self->light_level))
			self->light_level = 300;

		if (self->light_level > 100)
			AddLightSource(self);
	}

	self->model = "models/props/light/tris.md2";
	self->s.modelindex = gi.modelindex (self->model);

	self->s.renderfx2 |= RF2_NOSHADOW;

	if (self->light_level)
	{
		self->s.renderfx |= RF_FULLBRIGHT;
		self->flags |= RF_FULLBRIGHT;
	}

 	gi.linkentity (self);
}
// END JOSEPH

// JOSEPH 16-APR-99
/*QUAKED light_deco_sconce (1 1 1) (-8 -8 -12) (8 8 12) START_OFF FLARE NORESIZE DYNAMIC

A deco sconce

Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
  
model="models\props\decosconce\tris.md2"
*/

void SP_light_deco_sconce (edict_t *self)
{
	self->solid = SOLID_NOT;
	self->movetype = MOVETYPE_NONE;
	self->svflags |= SVF_PROP;

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

	if (self->spawnflags & FLARE)
	{
		SP_Flare (self);
	}
	// flares will never be a level.light_source
	else
	{
		if (!(self->light_level))
			self->light_level = 300;

		if (self->light_level > 100)
			AddLightSource(self);
	}

	self->model = "models/props/decosconce/tris.md2";
	self->s.modelindex = gi.modelindex (self->model);

	self->s.renderfx2 |= RF2_NOSHADOW;

	if (self->light_level)
	{
		self->s.renderfx |= RF_FULLBRIGHT;
		self->flags |= RF_FULLBRIGHT;
	}

 	gi.linkentity (self);
}

// JOSEPH 3-JUN-99
/*QUAKED light_chandelier (1 1 1) (-36 -34 -32) (36 34 32) START_OFF FLARE NORESIZE DYNAMIC

A deco sconce

Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
  
model="models\props\chandelier\tris.md2"
*/

void SP_light_chandelier (edict_t *self)
{
	self->solid = SOLID_NOT;
	self->movetype = MOVETYPE_NONE;
	self->svflags |= SVF_PROP;

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

	if (self->spawnflags & FLARE)
	{
		SP_Flare (self);
	}
	// flares will never be a level.light_source
	else
	{
		if (!(self->light_level))
			self->light_level = 300;

		if (self->light_level > 100)
			AddLightSource(self);
	}

	self->model = "models/props/chandelier/tris.md2";
	self->s.modelindex = gi.modelindex (self->model);

	self->s.renderfx2 |= RF2_NOSHADOW;

	if (self->light_level)
	{
		self->s.renderfx |= RF_FULLBRIGHT;
		self->flags |= RF_FULLBRIGHT;
	}

 	gi.linkentity (self);
}
// END JOSEPH

/*QUAKED light_pendant (1 1 1) (-16 -16 -4) (16 16 4) START_OFF FLARE NORESIZE DYNAMIC

A pendant light

Non-displayed light.
Default light value is 300.
Default style is 0.
If targeted, will toggle between on and off.
Default _cone value is 10 (used to set size of light for spotlights)
movedir will be the color of the flare
health is size for lightflare
dmg will specify the flare type
FLARE_NORMAL    0
FLARE_SUN		1
FLARE_AMBER		2
FLARE_RED		3
FLARE_BLUE		4
FLARE_GREEN		5
  
model="models\props\pendant\tris.md2"
*/

void SP_light_pendant (edict_t *self)
{
	self->solid = SOLID_NOT;
	self->movetype = MOVETYPE_NONE;
	self->svflags |= SVF_PROP;

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

	if (self->spawnflags & FLARE)
	{
		SP_Flare (self);
	}
	// flares will never be a level.light_source
	else
	{
		if (!(self->light_level))
			self->light_level = 300;

		if (self->light_level > 100)
			AddLightSource(self);
	}

	self->model = "models/props/pendant/tris.md2";
	self->s.modelindex = gi.modelindex (self->model);

	self->s.renderfx2 |= RF2_NOSHADOW;

	if (self->light_level)
	{
		self->s.renderfx |= RF_FULLBRIGHT;
		self->flags |= RF_FULLBRIGHT;
	}

 	gi.linkentity (self);
}
// END JOSEPH

/*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST SURF2_ALPHA
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;

	// RAFAEL
	if (self->spawnflags & 32)
		self->s.renderfx2 |= RF2_SURF_ALPHA;

	// 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, csurface_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, vec3_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);
}

// JOSEPH 18-FEB-99
/*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST SURF2_ALPHA
Any brush that you want to explode or break apart.  If you want an
explosion, set dmg and it will do a radius explosion of that amount
at the center of the bursh.

If targeted it will not be shootable.

health defaults to 100.

type - type of debris ("glass", "wood" or "metal")
 type default is "glass"

mass defaults to 75.  This determines how much debris is emitted when
it explodes.  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.

"dmg"  how much radius damage should be done, defaults to 0

"fxdensity" size of explosion 1 - 100 (default is 10)
*/
// END JOSEPH
void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject)
{
	vec3_t	origin;
	vec3_t	chunkorigin;
	vec3_t	size;
	int		count;
	int		mass;

	PlayerNoise( attacker, attacker->s.origin, PNOISE_WEAPON );

	// bmodel origins are (0 0 0), we need to adjust that here
	VectorScale (self->size, 0.5, size);
	VectorAdd (self->absmin, size, origin);
	VectorCopy (origin, self->s.origin);

	self->takedamage = DAMAGE_NO;

	if (self->dmg)
		T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);

	VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity);
	VectorNormalize (self->velocity);
	VectorScale (self->velocity, 250, self->velocity);

	// start chunks towards the center
	VectorScale (size, 0.5, size);

	mass = self->mass;
	if (!mass)
		mass = 75;

	// big chunks
	if (mass >= 100)
	{
		count = mass / 100;
		if (count > 8)
			count = 8;
		if ((!self->type) || ((self->type) && (!strcmp(self->type, "glass"))))
		{	
			while(count--)
			{
				chunkorigin[0] = origin[0] + crandom() * size[0];
				chunkorigin[1] = origin[1] + crandom() * size[1];
				chunkorigin[2] = origin[2] + crandom() * size[2];
				ThrowDebris (self, "models/props/glass/glass1.md2", 1, chunkorigin);
			}
		}
		else if (((self->type) && (!strcmp(self->type, "wood"))))
		{	
			while(count--)
			{
				chunkorigin[0] = origin[0] + crandom() * size[0];
				chunkorigin[1] = origin[1] + crandom() * size[1];
				chunkorigin[2] = origin[2] + crandom() * size[2];
				ThrowDebris (self, "models/props/wood/wood1.md2", 1, chunkorigin);
			}
		}
		else if (((self->type) && (!strcmp(self->type, "metal"))))
		{	
			while(count--)
			{
				chunkorigin[0] = origin[0] + crandom() * size[0];
				chunkorigin[1] = origin[1] + crandom() * size[1];
				chunkorigin[2] = origin[2] + crandom() * size[2];
				ThrowDebris (self, "models/props/metal/metal1.md2", 1, chunkorigin);
			}
		}
	}

	// small chunks
	count = mass / 25;
	if (count > 16)
		count = 16;
	if ((!self->type) || ((self->type) && (!strcmp(self->type, "glass"))))
	{		
		while(count--)
		{
			chunkorigin[0] = origin[0] + crandom() * size[0];
			chunkorigin[1] = origin[1] + crandom() * size[1];
			chunkorigin[2] = origin[2] + crandom() * size[2];
			ThrowDebris (self, "models/props/glass/glass2.md2", 2, chunkorigin);
		}
	}	
	else if (((self->type) && (!strcmp(self->type, "wood"))))
	{	
		while(count--)
		{	
			chunkorigin[0] = origin[0] + crandom() * size[0];
			chunkorigin[1] = origin[1] + crandom() * size[1];
			chunkorigin[2] = origin[2] + crandom() * size[2];
			ThrowDebris (self, "models/props/wood/wood2.md2", 2, chunkorigin);		
		}
	}
	else if (((self->type) && (!strcmp(self->type, "metal"))))
	{	
		while(count--)
		{
			chunkorigin[0] = origin[0] + crandom() * size[0];
			chunkorigin[1] = origin[1] + crandom() * size[1];
			chunkorigin[2] = origin[2] + crandom() * size[2];
			ThrowDebris (self, "models/props/metal/metal2.md2", 2, chunkorigin);		
		}
	}


	G_UseTargets (self, attacker);

	if (self->dmg)
		BecomeExplosion1 (self);
	else
		G_FreeEdict (self);
}
// END JOSEPH

void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
{
	func_explosive_explode (self, self, other, self->health, vec3_origin, 0, 0);
}

// JOSEPH 7-MAR-99
void func_explosive_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
	if (!(other->svflags & SVF_MONSTER))
		return;

	if (!(other->onfiretime > 0))
		return;

	func_explosive_explode (self, self, other, self->health, vec3_origin, 0, 0);
}
// END JOSEPH

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

void SP_func_explosive (edict_t *self)
{
	//if (deathmatch->value)
	//{	// auto-remove for deathmatch
	///	G_FreeEdict (self);
	//	return;
	//}

	self->movetype = MOVETYPE_PUSH;

	// JOSEPH 10-FEB-99
	if (self->spawnflags & 8)
		self->s.renderfx2 |= RF2_SURF_ALPHA;
	// END JOSEPH

	gi.modelindex ("models/props/glass/glass1.md2");
	gi.modelindex ("models/props/glass/glass2.md2");

	gi.setmodel (self, self->model);

	if (self->spawnflags & 1)
	{
		self->svflags |= SVF_NOCLIENT;
		self->solid = SOLID_NOT;
		self->use = func_explosive_spawn;
	}
	else
	{
		self->solid = SOLID_BSP;
		if (self->targetname)
			self->use = func_explosive_use;
	}

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

	if (self->use != func_explosive_use)
	{
		if (!self->health)
			self->health = 100;
		self->die = func_explosive_explode;
		self->takedamage = DAMAGE_YES;
	}

	// JOSEPH 7-MARCH-99
	self->touch = func_explosive_touch;
	// END JOSEPH

	gi.linkentity (self);
}

// JOSEPH 8-FEB-99
/*UAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
Large exploding box.  You can override its mass (100),
health (80), and dmg (150).
*/
// END JOSEPH
void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)

{
	float	ratio;
	vec3_t	v;

	if ((!other->groundentity) || (other->groundentity == self))
		return;

	ratio = (float)other->mass / (float)self->mass;
	VectorSubtract (self->s.origin, other->s.origin, v);
	M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME);
}

void barrel_explode (edict_t *self)
{
	vec3_t	org;
	float	spd;
	vec3_t	save;

	T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL);

	VectorCopy (self->s.origin, save);
	VectorMA (self->absmin, 0.5, self->size, self->s.origin);

	// a few big chunks
	spd = 1.5 * (float)self->dmg / 200.0;
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass1.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass1.md2", spd, org);

	// bottom corners
	spd = 1.75 * (float)self->dmg / 200.0;
	VectorCopy (self->absmin, org);
	ThrowDebris (self, "models/props/glass/glass3.md2", spd, org);
	VectorCopy (self->absmin, org);
	org[0] += self->size[0];
	ThrowDebris (self, "models/props/glass/glass3.md2", spd, org);
	VectorCopy (self->absmin, org);
	org[1] += self->size[1];
	ThrowDebris (self, "models/props/glass/glass3.md2", spd, org);
	VectorCopy (self->absmin, org);
	org[0] += self->size[0];
	org[1] += self->size[1];
	ThrowDebris (self, "models/props/glass/glass3.md2", spd, org);

	// a bunch of little chunks
	spd = 2 * self->dmg / 200;
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);
	org[0] = self->s.origin[0] + crandom() * self->size[0];
	org[1] = self->s.origin[1] + crandom() * self->size[1];
	org[2] = self->s.origin[2] + crandom() * self->size[2];
	ThrowDebris (self, "models/props/glass/glass2.md2", spd, org);

	VectorCopy (save, self->s.origin);
	if (self->groundentity)
		BecomeExplosion2 (self);
	else
		BecomeExplosion1 (self);
}

void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject)
{
	self->takedamage = DAMAGE_NO;
	self->nextthink = level.time + 2 * FRAMETIME;
	self->think = barrel_explode;
	self->activator = attacker;
}

// JOSEPH 26-AUG-98
// JOSEPH TEMP
void SP_props_trashcanA (edict_t *self);

void SP_misc_explobox (edict_t *self)
{
	// JOSEPH TEMP
    SP_props_trashcanA (self);
	return;

// END JOSEPH	

	//if (deathmatch->value)
	//{	// auto-remove for deathmatch
	//	G_FreeEdict (self);
	//	return;
	//}

	gi.modelindex ("models/props/glass/glass1.md2");
	gi.modelindex ("models/props/glass/glass2.md2");
	gi.modelindex ("models/props/glass/glass3.md2");


	self->solid = SOLID_BBOX;
	self->movetype = MOVETYPE_STEP;

	self->model = "models/objects/barrels/tris.md2";
	self->s.modelindex = gi.modelindex (self->model);
	VectorSet (self->mins, -16, -16, 0);
	VectorSet (self->maxs, 16, 16, 40);

	if (!self->mass)
		self->mass = 400;
	if (!self->health)
		self->health = 10;
	if (!self->dmg)
		self->dmg = 150;

	self->die = barrel_delay;
	self->takedamage = DAMAGE_YES;
	self->cast_info.aiflags = AI_NOSTEP;

	self->touch = barrel_touch;

	self->think = M_droptofloor;
	self->nextthink = level.time + 2 * FRAMETIME;

	gi.linkentity (self);
}

// JOSEPH 8-FEB-99
/*UAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
*/
// END JOSEPH
void SP_light_mine1 (edict_t *ent)
{
	ent->movetype = MOVETYPE_NONE;
	ent->solid = SOLID_BBOX;
	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2");
	gi.linkentity (ent);
}

// JOSEPH 8-FEB-99
/*UAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
*/
// END JOSEPH
void SP_light_mine2 (edict_t *ent)
{
	ent->movetype = MOVETYPE_NONE;
	ent->solid = SOLID_BBOX;
	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2");
	gi.linkentity (ent);
}

// JOSEPH 8-FEB-99
/*UAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
Intended for use with the target_spawner
*/
// END JOSEPH
void SP_misc_gib_arm (edict_t *ent)
{
	gi.setmodel (ent, "models/objects/gibs/arm/tris.md2");
	ent->solid = SOLID_NOT;
	ent->s.effects |= EF_GIB;
	ent->takedamage = DAMAGE_YES;
	ent->die = gib_die;
	ent->movetype = MOVETYPE_TOSS;
	ent->svflags |= SVF_MONSTER;
	ent->deadflag = DEAD_DEAD;
	ent->avelocity[0] = random()*200;
	ent->avelocity[1] = random()*200;
	ent->avelocity[2] = random()*200;
	ent->think = G_FreeEdict;
	ent->nextthink = level.time + 30;
	gi.linkentity (ent);
}

// JOSEPH 8-FEB-99 
/*UAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8)
Intended for use with the target_spawner
*/
// END JOSEPH
void SP_misc_gib_leg (edict_t *ent)
{
	gi.setmodel (ent, "models/objects/gibs/leg/tris.md2");
	ent->solid = SOLID_NOT;
	ent->s.effects |= EF_GIB;
	ent->takedamage = DAMAGE_YES;
	ent->die = gib_die;
	ent->movetype = MOVETYPE_TOSS;
	ent->svflags |= SVF_MONSTER;
	ent->deadflag = DEAD_DEAD;
	ent->avelocity[0] = random()*200;
	ent->avelocity[1] = random()*200;
	ent->avelocity[2] = random()*200;
	ent->think = G_FreeEdict;
	ent->nextthink = level.time + 30;
	gi.linkentity (ent);
}

// JOSEPH 8-FEB-99
/*UAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8)
Intended for use with the target_spawner
*/
// END JOSEPH
void SP_misc_gib_head (edict_t *ent)
{
	gi.setmodel (ent, "models/objects/gibs/head/tris.md2");
	ent->solid = SOLID_NOT;
	ent->s.effects |= EF_GIB;
	ent->takedamage = DAMAGE_YES;
	ent->die = gib_die;
	ent->movetype = MOVETYPE_TOSS;
	ent->svflags |= SVF_MONSTER;
	ent->deadflag = DEAD_DEAD;
	ent->avelocity[0] = random()*200;
	ent->avelocity[1] = random()*200;
	ent->avelocity[2] = random()*200;
	ent->think = G_FreeEdict;
	ent->nextthink = level.time + 30;
	gi.linkentity (ent);
}

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

/*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:%2i", self->health / 60, self->health % 60);
		if (self->message[3] == ' ')
			self->message[3] = '0';
		return;
	}

	if (self->style == 2)
	{
		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60);
		if (self->message[3] == ' ')
			self->message[3] = '0';
		if (self->message[6] == ' ')
			self->message[6] = '0';
		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:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
		if (self->message[3] == ' ')
			self->message[3] = '0';
		if (self->message[6] == ' ')
			self->message[6] = '0';
	}

	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 = 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 teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
	edict_t		*dest;
	int			i;

	if (!(other->client))
		return;
	dest = G_Find (NULL, FOFS(targetname), self->target);
	if (!dest)
	{
		gi.dprintf ("Couldn't find destination\n");
		return;
	}

	// unlink to make sure it can't possibly interfere with KillBox
	gi.unlinkentity (other);

	VectorCopy (dest->s.origin, other->s.origin);
	VectorCopy (dest->s.origin, other->s.old_origin);
	other->s.origin[2] += 10;

	// clear the velocity and hold them in place briefly
	VectorClear (other->velocity);
	other->client->ps.pmove.pm_time = 160>>3;		// hold time
	other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;

	// draw the teleport splash at source and on the player
	self->owner->s.event = EV_PLAYER_TELEPORT;
	other->s.event = EV_PLAYER_TELEPORT;

	// set angles
	for (i=0 ; i<3 ; i++)
		other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);

	VectorClear (other->s.angles);
	VectorClear (other->client->ps.viewangles);
	VectorClear (other->client->v_angle);

	// kill anything at the destination
	KillBox (other);

	gi.linkentity (other);
}

/*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
*/
void SP_misc_teleporter (edict_t *ent)
{
	edict_t		*trig;

	if (!ent->target)
	{
		gi.dprintf ("teleporter without a target.\n");
		G_FreeEdict (ent);
		return;
	}

	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
	ent->s.skinnum = 1;
	ent->s.effects = EF_TELEPORTER;
	ent->s.sound = gi.soundindex ("world/amb10.wav");
	ent->solid = SOLID_BBOX;

	VectorSet (ent->mins, -32, -32, -24);
	VectorSet (ent->maxs, 32, 32, -16);
	gi.linkentity (ent);

	trig = G_Spawn ();
	trig->touch = teleporter_touch;
	trig->solid = SOLID_TRIGGER;
	trig->target = ent->target;
	trig->owner = ent;
	VectorCopy (ent->s.origin, trig->s.origin);
	VectorSet (trig->mins, -8, -8, 8);
	VectorSet (trig->maxs, 8, 8, 24);
	gi.linkentity (trig);
	
}

/*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
Point teleporters at these.
*/
void SP_misc_teleporter_dest (edict_t *ent)
{
	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
	ent->s.skinnum = 0;
	ent->solid = SOLID_BBOX;
//	ent->s.effects |= EF_FLIES;
	VectorSet (ent->mins, -32, -32, -24);
	VectorSet (ent->maxs, 32, 32, -16);
	gi.linkentity (ent);
}

// JOSEPH 8-FEB-99
/*UAKED misc_amb4 (1 0 0) (-16 -16 -16) (16 16 16)
Mal's amb4 loop entity
*/
// END JOSEPH
static int amb4sound;

void amb4_think (edict_t *ent)
{
	ent->nextthink = level.time + 2.7;
	gi.sound(ent, CHAN_VOICE, amb4sound, 1, ATTN_NONE, 0);
}

void SP_misc_amb4 (edict_t *ent)
{
	ent->think = amb4_think;
	ent->nextthink = level.time + 1;
	amb4sound = gi.soundindex ("world/amb4.wav");
	gi.linkentity (ent);
}

// JOSEPH 8-FEB-99
/*UAKED misc_smoke (1 0 0) (-16 -16 -64) (16 16 64) ALPHA1 ALPHA2 ALPHA4 ALPHA6 ALPHA8 SCALE50 
ALPHA1 = 0.1
ALPHA8 = 0.8
SCALE 50 will scale it down 50 percent
*/
// END JOSEPH
void SP_misc_smoke (edict_t *ent)
{
	ent->s.modelindex = gi.modelindex ("sprites/s_smoke4.sp2");
	
	ent->s.effects |= EF_ANIM_ALLFAST;
	
	ent->s.renderfx |= RF_FACINGUP|RF_TRANSLUCENT;
	ent->s.renderfx2 |= ent->spawnflags;

	ent->movetype = MOVETYPE_NONE;
	//ent->solid = SOLID_BBOX;
	
	gi.linkentity (ent);
	
}


// Test vehicle models

void dodgeviper_think( edict_t *self)
{
if ((self->s.angles[1] += 8) > 360)
self->s.angles[1] -= 360;

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

void SP_misc_car (edict_t *self)
{
	self->movetype = MOVETYPE_STEP;
	self->solid = SOLID_BBOX;
	VectorSet (self->mins, -16, -16, -24);
	VectorSet (self->maxs, 16, 16, 32);

// temp for E3 demo, KP2 has characters in the ground
while (!ValidBoxAtLoc(self->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID))
{
self->s.origin[2] += 16;
gi.linkentity (self);
}

	self->s.modelindex = gi.modelindex("models/vehicles/cars/viper/tris.md2");

	self->s.renderfx |= RF_REFL_MAP;

	self->think = dodgeviper_think;
	self->nextthink = level.time + 0.1;
}

// JOSEPH 8-FEB-99
/*UAKED cast_punk_window (1 0 0) (-16 -16 -16) (16 16 16)
*/
// END JOSEPH
static int windowsound;

void usewindow (edict_t *ent, edict_t *other, edict_t *activator)
{
//	ent->use = NULL;
	gi.sound(ent, CHAN_VOICE, windowsound , 1, ATTN_NONE, 0);
}

void thinkdeadwindow (edict_t *ent)
{
	ent->s.frame++;
	if (ent->s.frame < 336)
		ent->nextthink = level.time + 0.1;	
}

void window_die (edict_t *ent, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject)
{
	ent->takedamage = DAMAGE_NO;
	ent->nextthink = level.time + FRAMETIME;
	ent->think = thinkdeadwindow;
	ent->activator = attacker;
	ent->s.frame = 322;
}

void thinkwindow (edict_t *ent)
{
	ent->nextthink = level.time + 0.1;
	ent->s.frame++;
	if (ent->s.frame > 211)
		ent->s.frame = 0;
}

void SP_cast_punk_window (edict_t *ent)
{
	ent->s.modelindex = gi.modelindex ("models/actors/fidelC/tris.md2");
	ent->movetype =  MOVETYPE_STEP;
	ent->solid = SOLID_BBOX;
	gi.linkentity (ent);

	ent->takedamage = DAMAGE_YES;
	// ent->cast_info.aiflags = AI_NOSTEP;

	ent->health = 1;
	ent->use = usewindow;
	ent->think = thinkwindow;
	ent->nextthink = level.time + 0.1;

	windowsound = gi.soundindex ("actors/specific/spec3.wav");
	
	VectorSet (ent->mins, -16, -16, 0);
	VectorSet (ent->maxs, 16, 16, 40);

	ent->die = window_die;

}

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

void SP_refl (edict_t *ent)
{
	ent->s.modelindex = gi.modelindex ("models/vehicles/cars/viper/tris.md2");
	
	ent->s.renderfx |= RF_REFL_MAP;

	ent->movetype = MOVETYPE_NONE;
	
	gi.linkentity (ent);
}


/*QUAKED elps (1 0 0) (-32 -32 -32) (32 32 32)
test entity for mdx bbox hit test
*/

void elpsthink (edict_t *self)
{
	self->nextthink = level.time + 0.1;
	self->s.angles[YAW] += 3.0;
	self->health = 1000;
}

void SP_elps (edict_t *self)
{
	int i;
	
	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/elps_seg/up.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/elps_seg/up.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/elps_seg/rt.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/elps_seg/rt.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/elps_seg/fwd.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/elps_seg/fwd.mdx", &self->s.model_parts[PART_BODY] );

	self->s.num_parts++;
	self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/elps_seg/diag.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_GUN].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/elps_seg/diag.mdx", &self->s.model_parts[PART_GUN] );
	
	self->movetype =  MOVETYPE_STEP;
	self->solid = SOLID_BBOX;
	gi.linkentity (self);

	self->takedamage = DAMAGE_YES;
	
	self->health = 1000;
	self->think = elpsthink;
	self->nextthink = level.time + 0.1;

	VectorSet (self->mins, -64, -64, -64);
	VectorSet (self->maxs, 64, 64, 64);
	
}


/*QUAKED misc_caustic (1 0 0) (-16 -16 -64) (16 16 64) ALPHA1 ALPHA2 ALPHA4 ALPHA6 ALPHA8
ALPHA1 = 0.1
ALPHA8 = 0.8
*/
// Ridah, not used, removed RF2_CAUSTIC flag
/*
void SP_misc_caustic (edict_t *ent)
{
	ent->s.modelindex = gi.modelindex ("sprites/s_smoke4.sp2");
	
	ent->s.renderfx |= RF_TRANSLUCENT;
	ent->s.renderfx2 |= ent->spawnflags;
	ent->s.renderfx2 |= RF2_CAUSTIC;

	ent->movetype = MOVETYPE_NONE;
	
	gi.linkentity (ent);
	
}
*/
/*QUAKED misc_rosie (1 .5 0) (-16 -16 -24) (16 16 32)
*/

void rosie_think (edict_t *self)
{

	self->nextthink = level.time + 0.1;
	self->s.frame ++;

	if (self->s.frame > 390)
		self->s.frame = 0;
}

void SP_misc_rosie (edict_t *self)
{
	int i;

//self->s.origin[2] += 1;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/rosie_seg/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/rosie_seg/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/rosie_seg/legs.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/rosie_seg/legs.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/rosie_seg/body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/rosie_seg/body.mdx", &self->s.model_parts[PART_BODY] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;
	
	gi.linkentity (self);

	self->think = rosie_think;
	self->nextthink = level.time + 0.1;

}


/*QUAKED misc_fidelA (1 .5 0) (-16 -16 -24) (16 16 32)
*/

void fidelA_think (edict_t *self)
{
//return;
	self->nextthink = level.time + 0.1;
	self->s.frame ++;

	if (self->s.frame > 54)
		self->s.frame = 0;


	gi.dprintf ("frame: %d\n", self->s.frame);
}

void SP_misc_fidelA (edict_t *self)
{
	int i;
//return;
//self->s.origin[2] += 1;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fidel_mdx/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_mdx/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fidel_mdx/lower_body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_mdx/lower_body.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fidel_mdx/upper_body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_mdx/upper_body.mdx", &self->s.model_parts[PART_BODY] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;
	
	gi.linkentity (self);

	self->think = fidelA_think;
	self->nextthink = level.time + 0.1;

}


#define ALARM_ON	1
#define ALARM_OFF	2

void Think_Alarm (edict_t *ent)
{
	if (ent->wait < 0)
	{
		// just ring once
		/*
		if (ent->delay)
		{
			ent->delay --;
			ent->wait = ent->count;
		}
		else
		*/
			return;
	}

	gi.sound (ent, CHAN_VOICE, gi.soundindex ("world/alarm.wav"), 1, ATTN_NORM, 0);			
	ent->nextthink = level.time + 4.8;
	ent->wait -= 4.8; 
}

void Use_Alarm (edict_t *ent, edict_t *other, edict_t *activator)
{
	ent->nextthink = level.time + 3;
	ent->think = Think_Alarm;

	if (ent->moveinfo.state == ALARM_OFF)
	{
//		gi.dprintf ("turning alarm on\n");
		ent->moveinfo.state = ALARM_ON;
		ent->wait = ent->count;
		ent->delay = ent->speed;

		// Ridah, do some special handling here
		EP_EventScript( activator, "alarm" );
	}
	else
	{
//		gi.dprintf ("turning alarm off\n");
		ent->moveinfo.state = ALARM_OFF;
		ent->wait = -1;
		ent->delay = ent->speed;
	}
}

/*QUAKED misc_alarm (1 .5 0) (-8 -8 -8) (8 8 8) 
must be triggered to work
count is the duration of the alarm ringing default is 60 sec
*/
void SP_misc_alarm (edict_t *ent)
{
	ent->use = Use_Alarm;
	
	if (!(ent->count))
		ent->count = 60;

	if (!ent->speed || ent->speed < 0)
		ent->delay = ent->speed = 3;

	ent->moveinfo.state = ALARM_OFF;

	// Ridah, NAV code only understands func_button's
	ent->classname = "func_button";
	ent->name = "misc_alarm";
}






/*QUAKED misc_corky_rosie_mdx (0 0 1) (-16 -16 -24) (16 16 34)
*/

void SP_misc_corky_rosie_mdx (edict_t *self)
{

	int i;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/bitch/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/bitch/legs.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/legs.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/bitch/body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/body.mdx", &self->s.model_parts[PART_BODY] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;

	gi.linkentity (self);
	
}


/*QUAKED misc_corky_fatguy_mdx (0 0 1) (-16 -16 -24) (16 16 34)
*/

void SP_misc_corky_fatguy_mdx (edict_t *self)
{

	int i;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fatguy_mdx/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fatguy_mdx/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fatguy_mdx/lower_body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fatguy_mdx/lower_body.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fatguy_mdx/upper_body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fatguy_mdx/upper_body.mdx", &self->s.model_parts[PART_BODY] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;

	gi.linkentity (self);
	
}


/*QUAKED misc_corky_fidel_mdx_pcx (0 0 1) (-16 -16 -24) (16 16 34)
*/
/*QUAKED misc_corky_fidel_mdx_tga (0 0 1) (-16 -16 -24) (16 16 34)
*/

void SP_misc_corky_fidel_mdx_tga (edict_t *self)
{
	int i;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fidel_tga/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_tga/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fidel_tga/lower_body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_tga/lower_body.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fidel_tga/upper_body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_tga/upper_body.mdx", &self->s.model_parts[PART_BODY] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;

	gi.linkentity (self);
	
}

void SP_misc_corky_fidel_mdx_pcx (edict_t *self)
{
	int i;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/thug/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/thug/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/thug/legs.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/thug/legs.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/thug/body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/thug/body.mdx", &self->s.model_parts[PART_BODY] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;

	gi.linkentity (self);
	

}

/*QUAKED misc_barry_fidelc_maya (0 0 1) (-16 -16 -24) (16 16 34)
*/
/*QUAKED misc_barry_bitch (0 0 1) (-16 -16 -24) (16 16 34)
*/

void misc_barry_fidelc_maya_think (edict_t *self)
{
	self->s.frame++;
	self->nextthink = level.time + 0.1;

	if (self->s.frame >= 761)
		self->s.frame = 0;

	gi.dprintf ("frame: %d\n", self->s.frame);
}

void SP_misc_barry_fidelc_maya (edict_t *self)
{
	int i;	

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/bitch/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].baseskin = self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/bitch/legs.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].baseskin = self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/legs.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/bitch/body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].baseskin = self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/body.mdx", &self->s.model_parts[PART_BODY] );

/*
	int i;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/bitch/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/bitch/legs.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/legs.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/bitch/body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/body.mdx", &self->s.model_parts[PART_BODY] );

	self->s.num_parts++;
	self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/bitch/gun.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/bitch/gun.mdx", &self->s.model_parts[PART_GUN] );
*/
/*
	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fidel_seg/head.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].baseskin = self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_seg/head.mdx", &self->s.model_parts[PART_HEAD] );

	self->s.num_parts++;
	self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fidel_seg/legs.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_LEGS].baseskin = self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_seg/legs.mdx", &self->s.model_parts[PART_LEGS] );

	self->s.num_parts++;
	self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fidel_seg/body.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_BODY].baseskin = self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_seg/body.mdx", &self->s.model_parts[PART_BODY] );

	self->s.num_parts++;
	self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/fidel_seg/gun.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/fidel_seg/gun.mdx", &self->s.model_parts[PART_GUN] );
*/
	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;

	self->think = misc_barry_fidelc_maya_think;
	self->nextthink = level.time + 0.1;
	gi.linkentity (self);
	
}

/*QUAKED misc_barry_cube_mdx (0 0 1) (-16 -16 -24) (16 16 34)
*/

void misc_barry_cube_mdx_think (edict_t *self)
{
	self->s.frame++;
	self->nextthink = level.time + 0.1;
	if (self->s.frame >= 20)
		self->s.frame = 0;
}

void SP_misc_barry_cube_mdx (edict_t *self)
{

	int i;

	memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS);

	self->s.num_parts++;
	self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/cube_mdx/cube.mdx");
	for (i=0; i<MAX_MODELPART_OBJECTS; i++)
		self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum;
	gi.GetObjectBounds( "models/actors/cube_mdx/cube.mdx", &self->s.model_parts[PART_HEAD] );

	self->movetype = MOVETYPE_NONE;

	self->s.renderfx2 |= RF2_DIR_LIGHTS;

	self->think = misc_barry_cube_mdx_think;
	self->nextthink = level.time + 0.1;
	gi.linkentity (self);

}


/*QUAKED path_attractor (.4 .3 .8) (-16 -16 -24) (16 16 48)
When this is placed on the map it will attract actors by name
from the episodic ai routines

default delay is 10 sec.  
*/

void SP_path_attractor (edict_t *self)
{
	self->movetype = MOVETYPE_NONE;
	self->solid = SOLID_NOT;
	VectorSet (self->mins, -16, -16, -24);
	VectorSet (self->maxs,  16,  16,  48);
	
	if (!self->delay)
		self->delay = 10;

	AI_Ent_droptofloor( self );
}


// JOSEPH 16-DEC-98

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

/*void CutSceneThink (edict_t *ent)
{
	 ent->nextthink = level.time + 0.1;

	 //if (level.time > (ent->nextthink + ent->wait))
	 // JOSEPH 7-DEC-98
	 if (!ent->wait--)
	   EndCutScene (ent);
}

void CutSceneThinkEnd (edict_t *ent)
{
	EndCutScene (ent);
}

void CutSceneThinkStart (edict_t *ent)
{
	if (!ent->count)
		return;
	
	ent->count--;

	BeginCutScene (ent);

	// JOSEPH 7-DEC-98
	ent->think = CutSceneThink;
	ent->nextthink = level.time + 0.1;
}

void Touch_CutScene (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
	ent->think = CutSceneThinkStart;
	ent->nextthink = level.time + 0.1;
}

void SP_misc_cut_scene (edict_t *self)
{
	
	VectorSet (self->mins, -16, -16, -16);
	VectorSet (self->maxs,  16,  16,  16);

	self->solid = SOLID_TRIGGER;
	self->touch = Touch_CutScene;
	self->svflags |= SVF_NOCLIENT;

	gi.linkentity (self);
	
	if (!self->wait)
		self->wait = 60;

	self->wait=10; // just for now

	self->count = 1;

}*/

// JOSEPH 25-FEB-99
/*QUAKED misc_cutscene_trigger (0 0 1) ?

Targets a misc_cutscene_camera

  target - camera to target
  debugprint - set to 1 to print out camera end position and angles	
  duration - fade in time.	  
*/
// END JOSEPH

// JOSEPH 19-MAR-99-B
/*QUAKED misc_cutscene_camera (0 0 1) (-16 -16 -16) (16 16 16)
  
Camera to be targeted from a misc_cutscene_trigger		

  targetname - camera target ID 
  
  cameraorigin - X Y Z camera start position

  cameraangle - X Y Z start angles

  rotate - X Y Z rotational velocity during cut scene

  cameravel - [forward] [right] [up] speed to move from initial angle.	

  cameravelrel - [forward] [right] [up] speed to move relative to current frame angle.	

  wait - cut scene length in seconds (default 5)	

  target - next camera to target.	

  target2 - [NOT WORKING!!] camera angle points to this entity (overides other angle commands)	  

  deadticks - fov for this camera;

  duration - fade out time.
  
  reactdelay - time into camera to start fading out   
*/
// END JOSEPH
void CutSceneThink (edict_t *ent)
{
    if (!ent->wait--)
	{
		{
			edict_t	*e;
			int		i;
			int     found = 0;

			// Print end position if required
			if (ent->debugprint)
			{
				// JOSEPH 24-FEB-99
				vec3_t neworigin;
				
				VectorCopy(ent->target_ent->s.origin, neworigin);
				neworigin[2] += 40;
				gi.dprintf("Camera \"%s\" end position %s\n", ent->target_ent->targetname, vtos(neworigin));            
				gi.dprintf("Camera \"%s\" end angles %s\n", ent->target_ent->targetname, vtos(ent->target_ent->s.angles)); 
				// END JOSEPH
			}
			
			// Restore camera start position and angle
			VectorCopy(ent->target_ent->save_avel, ent->target_ent->s.angles);
	        VectorCopy(ent->target_ent->savecameraorigin, ent->target_ent->s.origin);
			
			// No more cameras
			if (!ent->target_ent->target)
				EndCutScene (ent->target_ent);			

			// Find next target entity camera
			if (ent->target_ent->target)
			{
				for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++)
				{
					if ((e->targetname) && (!strcmp(e->targetname, ent->target_ent->target)))
					{
						ent->target_ent = e;
						found = 1;
						break;	
					}
				}	
			}

			// Next camera
			if (found)
			{
				// See if a target2 can be found
				if (ent->target_ent->target2)
				{
					for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++)
					{
						if ((e->targetname) && (!strcmp(e->targetname, ent->target_ent->target2)))
						{
							ent->target_ent->target2_ent = e;
							break;	
						}
					}	
				}
				
				ent->wait = ent->target_ent->wait*10.0;
				if (!ent->wait)
					ent->wait = 5.0*10.0;
				NewCutSceneCamera (ent->target_ent);			
				ent->nextthink = level.time + 0.1;

				// Ridah, Camera accel/decel
				ent->target_ent->timestamp = level.time;
				ent->target_ent->speed = 0;

				// Ridah, play a sound if there is one
				if (ent->target_ent->name)
				{
					edict_t *talkent;

					if (!ent->target_ent->sight_target)
					{
						//gi.dprintf( "Warning: cutsecene camera has voice sound without a \"sight_target\" (speaking charecter's name)\n" );
						gi.positioned_sound( ent->target_ent->s.origin, ent->target_ent, CHAN_AUTO, gi.soundindex( ent->target_ent->name ), 1.0, ATTN_NONE, 0);
					}
					else
					{
						talkent = EP_GetCharacterByName( ent->target_ent->sight_target );
						gi.sound( talkent, CHAN_VOICE, gi.soundindex( ent->target_ent->name ), 1.0, 1, 0);
					}

				}

				// Ridah, scripting
				if (ent->target_ent->scriptname)
				{
					EP_EventScript( &g_edicts[1], ent->target_ent->scriptname );
				}
				
				return;
			}
		}


		// Ridah, scripting
		if (ent->scriptname)
		{
			EP_EventScript( &g_edicts[1], ent->scriptname );
		}

		// No more cameras or target camera not found
		ent->think = 0;
		EndCutScene (ent->target_ent);
	}	
	else
	{
		// Process camera frame
		AdjustCutSceneCamera(ent->target_ent);
	    ent->nextthink = level.time + 0.1;
	}	
}


void CutSceneThinkStart (edict_t *ent)
{
	edict_t	*e;
	int		i;
	
	if (!ent->count)
		return;
	
	// JOSEPH 19-MAR-99-B
	if (ent->duration)
	{
		level.inversefade = 0;
		level.totalfade = ent->duration;
		level.fadeendtime = level.time + level.totalfade;
	}	
	// END JOSEPH	

	ent->count--;

	// Find target entity
	ent->target_ent = 0;

	if (ent->target)
	{
		for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++)
		{
			if ((e->targetname) && (!strcmp(e->targetname, ent->target)))
			{
				ent->target_ent = e;
				break;	
			}
		}	
	}

	if (!ent->target_ent)
		return;

	// See if a target2 can be found
	if (ent->target_ent->target2)
	{
		for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++)
		{
			if ((e->targetname) && (!strcmp(e->targetname, ent->target_ent->target2)))
			{
				ent->target_ent->target2_ent = e;
				break;	
			}
		}	
	}

	// Ridah, Camera accel/decel
	ent->target_ent->timestamp = level.time;
	ent->target_ent->speed = 0;

	// Ridah, play a sound if there is one
	if (ent->target_ent->name)
	{
		edict_t *talkent;

		if (!ent->target_ent->sight_target)
		{
			//gi.dprintf( "Warning: cutsecene camera has voice sound without a \"sight_target\" (speaking charecter's name)\n" );
			gi.positioned_sound( ent->target_ent->s.origin, ent->target_ent, CHAN_AUTO, gi.soundindex( ent->target_ent->name ), 1.0, ATTN_NONE, 0);
		}
		else
		{
			talkent = EP_GetCharacterByName( ent->target_ent->sight_target );
			gi.sound( talkent, CHAN_VOICE, gi.soundindex( ent->target_ent->name ), 1.0, 1, 0);
		}

	}

	// Ridah, scripting
	if (ent->target_ent->scriptname)
	{
		EP_EventScript( &g_edicts[1], ent->target_ent->scriptname );
	}

	ent->wait = ent->target_ent->wait*10.0;
	
	if (!ent->wait)
		ent->wait = 5.0*5.0;
	
	BeginCutScene (ent->target_ent);
	
	ent->think = CutSceneThink;
	ent->nextthink = level.time + 0.1;
}

void Touch_CutScene (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
	CutSceneThinkStart(ent);
}

// JOSEPH 17-MAR-99-B
void Use_CutScene (edict_t *self, edict_t *other, edict_t *activator)
{
	CutSceneThinkStart(self);
}
// END JOSEPH

// OLD
void SP_misc_cut_scene (edict_t *self)
{

}

// JOSEPH 2-MAR-99
void PrecacheCutStuff (char* s)
{
	char	*start;
	char	data[MAX_QPATH];
	int		len;

	if (!s || !s[0])
		return;

	// parse the space seperated precache string
	while (*s)
	{
		start = s;
		while (*s && *s != ' ')
			s++;

		len = s-start;
		if (len >= MAX_QPATH || len < 5)
			gi.error ("Bad precache string");
		memcpy (data, start, len);
		data[len] = 0;
		if (*s)
			s++;

		// determine type based on extension
		if (!strcmp(data+len-3, "md2"))
			gi.modelindex (data);
		else if (!strcmp (data+len-3, "mdx"))
			gi.modelindex (data);
		else if (!strcmp(data+len-3, "wav"))
			gi.soundindex (data);
		if (!strcmp(data+len-3, "pcx"))
			gi.imageindex (data);
	}
}

// JOSEPH 19-MAR-99-B
void SP_misc_cutscene_trigger (edict_t *self)
{
	self->solid = SOLID_TRIGGER;
	gi.setmodel (self, self->model);
    self->svflags |= SVF_NOCLIENT;

	if (self->targetname)
	{	
		self->use = Use_CutScene;
	}
	else
	{
	self->touch = Touch_CutScene;
	}

	if (self->duration)
	{
		level.inversefade = 0;
		level.totalfade = 60.0;
		level.fadeendtime = level.time + level.totalfade;
	}	
	
	gi.linkentity (self);
	self->count = 1;
    
	PrecacheCutStuff (self->name);
}
// END JOSEPH

// JOSEPH 19-MAR-99-B
/*QUAKED misc_use_cutscene (.5 .5 .5) (-8 -8 -8) (8 8 8)
This fixed size trigger targets a misc_cutscene_camera

  target - camera to target
  debugprint - set to 1 to print out camera end position and angles	
  duration - fade in time.	  
*/
void SP_misc_use_cutscene (edict_t *self)
{
	self->use = Use_CutScene;
	
	if (self->duration)
	{
		level.inversefade = 0;
		level.totalfade = 60.0;
		level.fadeendtime = level.time + level.totalfade;
	}	
	
	self->count = 1;
    
	PrecacheCutStuff (self->name);
}
// END JOSEPH

void SP_misc_cutscene_camera (edict_t *self)
{
	VectorSet (self->mins, -16, -16, -16);
	VectorSet (self->maxs,  16,  16,  16);

	VectorClear(self->s.angles);

	if ((self->cameraorigin[0] != 0) || (self->cameraorigin[1] != 0) || (self->cameraorigin[2] != 0))
	{
		VectorCopy(self->cameraorigin, self->s.origin);
		self->s.origin[2] -= 40;
	}
	else
	{
		self->s.origin[0] += 8;
		self->s.origin[1] += 8;
		self->s.origin[2] += 8;
	}

	self->solid = SOLID_NOT;
	self->svflags |= SVF_NOCLIENT;

	gi.linkentity (self);

	if (!self->deadticks)
		self->deadticks = 90;

	PrecacheCutStuff (self->name);
}
// END JOSEPH

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

void beacon_think (edict_t *ent)
{
	edict_t	*ignore;
	vec3_t	start;
	vec3_t	end;
	trace_t	tr;
	vec3_t	forward;

	ignore = ent;
 
	ent->s.angles[YAW] += anglemod(level.time * 0.1);
 
	AngleVectors (ent->s.angles, forward, NULL, NULL );

	VectorCopy (ent->s.origin, start);
	VectorMA (start, 2048, forward, end);

	VectorCopy (forward, ent->movedir);

	tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
	
	VectorCopy (tr.endpos, ent->s.old_origin);

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

void SP_sfx_beacon (edict_t *ent)
{

	ent->movetype = MOVETYPE_NONE;
	ent->solid = SOLID_NOT;
	ent->s.modelindex = 1;			
	
	ent->s.renderfx |= RF_BEAM;
	ent->s.renderfx2 |= RF2_BEAM2;
	ent->s.frame = 64;
	ent->s.effects |= EF_ROTATE;
	ent->nextthink = level.time + 0.1;
	ent->think = beacon_think;
	ent->s.skinnum = 0xf2f2f0f0;

	gi.linkentity (ent);

}
