void(float damage) spawn_touchblood;
void(vector org, vector vel, float damage) SpawnBlood;
void(vector org, vector vel) SpawnMeatSpray;
float(entity targ) RSee;
float() crandom;
void() player_run;
void() CycleWeaponCommand;
void(float num_bubbles) DeathBubbles;
void(entity p) SetSpeed;
float(entity impactorg, vector enemyorg) DetermineZone;
void(vector org, float pcolor, float number, float index, float strenght) SpawnSparks;
void(entity victim, float flame_num, entity inflictor) Burn;
void(vector eorigin, entity inflictor, float scraps, string dmsg, float barrel) SpawnExplosion;
void(vector org, float radius, entity inflictor) RadiusBurn;
void() BecomeExplosion;
float(entity for, vector what) Infront;
void() player_block1;
void() W_FireKatana;
void(float burning) W_FireCrossbow;
void(string gibname, float dm, vector org) ThrowGib;
vector(float dm, vector org) VelocityForDamage;
void() GibTouch;

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// SPECIAL ABILITIES
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
// Cloaking
//------------------------------------------------------------------------------------

void(entity p) RealInvisOn =
{
	if(p.items & IT_INVISIBILITY)
		return;
	centerprint(p, "You camouflaged yourself.");
	p.items = p.items | IT_INVISIBILITY;
	p.invisible_time = 1;
	p.invisible_finished = time + 9999999;
	p.state = p.state | S_REALINVIS;
};

void(entity p) InvisOn =
{
local entity e;
	if(p.items & IT_INVISIBILITY)
		return;
	p.items = p.items | IT_INVISIBILITY;
	p.invisible_time = 1;
	p.invisible_finished = time + 9999999;
	e = spawn();
	p.movetarget = e;
	setmodel(e, "progs/eyes.mdl");
};

void(entity p) InvisOff =
{
	if(!p.state & S_REALINVIS)
		return;
	centerprint(p, "You are now visible again.");
	p.invisible_finished = 0;
	p.invisible_time = 0;
	p.attack_finished = time + 0.5;
	p.items = p.items - (p.items & IT_INVISIBILITY);
	p.state = p.state - (p.state & S_REALINVIS);
	if(p.movetarget != world)
	{
		remove(p.movetarget);
		p.movetarget = world;
	}
};

float() CheckCloak =
{
	local	vector	source;
	local	vector	org;
	local float d;

	if((self.v_angle_x > 20) || ((!self.flags & FL_ONGROUND) && (!self.state & S_TIEDUP)) || (self.state & S_REALINVIS) || ((self.velocity != '0 0 0') && (self.movetype != MOVETYPE_NONE))) 
		return FALSE;

	makevectors (self.v_angle);
	source = self.origin + '0 0 16';
	traceline (source, source + v_forward*96, FALSE, self);
	if (trace_fraction == 1.0)
		return FALSE;
	
	org = trace_endpos - v_forward*4;

	if (!trace_ent.takedamage)
		return TRUE;
};

//------------------------------------------------------------------------------------
// Climbing
//------------------------------------------------------------------------------------

float() Climb =
{
	local	vector	source;
	local entity oself;
	local float d;

	if((self.v_angle_x > 20) && (self.flags & FL_ONGROUND))
		return FALSE;
	makevectors (self.v_angle);
	source = self.origin + '0 0 -8';
	v_forward_z = 0;
	v_forward = normalize(v_forward);
	if(self.climbjump < (time - 0.5))
		self.climbjump = 0;
	traceline (source, source + v_forward*NINJA_CLIMB_RANGE, FALSE, self);
	d = trace_plane_normal_z;
//	if(d < 0)	//allow to climb on ceiling(negative plane_normal)?
//		d = d * -1;
	if ((trace_fraction == 1.0) || (d > 0.5))
		return FALSE;
	if((!self.climbjump) && (!self.flags & FL_ONGROUND) && (d >= 0))
	{
		source = self.origin + '0 0 8';
		v_forward_z = 0;
		traceline (source, source + v_forward*NINJA_CLIMB_RANGE, FALSE, self);
		if (trace_fraction == 1.0)
		{
//			bprint("climbjump\n");
			self.velocity_z = 420;
			self.climbjump = time;
			sound (self, CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM);
			return FALSE;
		}
	}
	if ((!trace_ent.takedamage) && (!self.climbjump))
		return TRUE;
	return FALSE;
};

//------------------------------------------------------------------------------------
// Berserkmodes
//------------------------------------------------------------------------------------

void() SBerserkThink =
{
	local entity stemp;
	if(self.owner.health < 1)
	{
		self.owner.state = self.owner.state - (self.owner.state & S_SBERSERK);
		remove(self);
		return;
	}
	if(self.ltime < time)
	{
//		stuffcmd(self.owner, "-forward\n");
		self.owner.state = self.owner.state - (self.owner.state & S_SBERSERK);
		self.owner.weaponframe = 0;
		self.owner.attack_finished = time + 2;
		self.owner.berserktime = time + BERSERKRELOADTIME;
		self.owner.mode = 0;
		stuffcmd(self.owner, "v_cshift 0 0 0 0\n");
//		SetSpeed(self.owner);
		remove(self);
		return;
	}
	SpawnSparks(self.owner.origin, 70, 10, 8, 600);
	self.nextthink = time + 0.2;
	stemp = self;
	self = self.owner;
	W_FireKatana();
	self = stemp;
};

void() W_SwordBerserk =
{
	local entity e;
	
	if(self.berserktime > time)
		return;
	if(self.state & S_SBERSERK)
		return;	
	sound (self, CHAN_BODY, "player/berserk.wav", 1, ATTN_NORM);
//	stuffcmd(self, "+forward\n");
	self.weaponframe = 11;
	self.state = self.state | S_SBERSERK;
	e = spawn();
	e.owner = self;
	e.nextthink = time + 0.1;
	e.think = SBerserkThink;
	self.attack_finished = time + 4;
	e.ltime = time + 4;
//	SetSpeed(self);
	stuffcmd(self, "v_cshift 255 0 0 80\n");
};

void() CBerserkThink =
{
	local entity stemp;
	if(self.owner.health < 1)
	{
		self.owner.state = self.owner.state - (self.owner.state & S_CBERSERK);
		remove(self);
		return;
	}
	if((self.ltime < time) || (self.owner.ammo_shells < 1))
	{
		self.owner.state = self.owner.state - (self.owner.state & S_CBERSERK);
		self.owner.attack_finished = time + 3;
		self.owner.berserktime = time + BERSERKRELOADTIME;
		self.owner.weapon = W_BestWeapon();
		self.owner.mode = 0;
		stuffcmd(self.owner, "v_cshift 0 0 0 0\n");
		stemp = self;
		self = self.owner;
		W_SetCurrentAmmo();
		self = stemp;
//		SetSpeed(self.owner);
		remove(self);
		return;
	}
        if (self.waterlevel > 2) 
          sound(self, CHAN_WEAPON, "misc/water2.wav", 1, ATTN_NORM);
        else
          sound(self, CHAN_WEAPON, "weapons/fire.wav", 1, ATTN_NORM);
	SpawnSparks(self.owner.origin, 70, 10, 8, 600);
	self.nextthink = time + 0.2;
	stemp = self;
	self = self.owner;
	W_FireCrossbow(0);
	self = stemp;
};

void() W_CbBerserk =
{
	local entity e;
	
	if(self.berserktime > time)
		return;
	if(self.ammo_shells < 1)
		return;
	if(self.state & S_CBERSERK)
		return;	
	sound (self, CHAN_BODY, "player/berserk.wav", 1, ATTN_NORM);
	self.state = self.state | S_CBERSERK;
	e = spawn();
	e.owner = self;
	e.nextthink = time + 0.5;
	e.think = CBerserkThink;
	self.attack_finished = time + 5;
	e.ltime = time + 5;
//	SetSpeed(self);
	stuffcmd(self, "v_cshift 255 0 0 80\n");
};

//------------------------------------------------------------------------------------
// Tie up
//------------------------------------------------------------------------------------

void() TieUp =
{
local vector source;

	if(self.state & S_TIEDUP)
	{
		self.movetype = MOVETYPE_WALK;
		self.state = self.state - (self.state & S_TIEDUP);
		return;
	}

	makevectors (self.v_angle);
	v_forward_z = 0;
	v_right_z = 0;
	v_up_x = 0;
	v_up_y = 0;
	v_forward = normalize(v_forward);
	v_right = normalize(v_right);
	v_up = normalize(v_up);
	source = self.origin + '0 0 -8';
	traceline (source, source + v_forward*NINJA_CLIMB_RANGE, FALSE, self);
	if (trace_fraction == 1.0)
	{
		traceline (source, source - v_forward*NINJA_CLIMB_RANGE, FALSE, self);
		if (trace_fraction == 1.0)
		{
			traceline (source, source + v_right*NINJA_CLIMB_RANGE, FALSE, self);
			if (trace_fraction == 1.0)
			{
				traceline (source, source - v_right*NINJA_CLIMB_RANGE, FALSE, self);
				if (trace_fraction == 1.0)
				{
					traceline (source, source + v_up*NINJA_CLIMB_RANGE, FALSE, self);
					if (trace_fraction == 1.0)
					{
						centerprint(self, "You must be near a wall or ceiling\nto tie yourself up.\n");
						return;
					}
				}
			}
		}
	}
	centerprint(self, "You are tied up\n");
	self.movetype = MOVETYPE_NONE;
	self.state = self.state | S_TIEDUP;
	self.velocity = '0 0 0';
};

//------------------------------------------------------------------------------------
// Ambush
//------------------------------------------------------------------------------------

entity() PlayerInView;
void() Ambush =
{
local entity e, gchain;
local vector destination, test;
local float bad;

	if(!self.items & IT_KEY2)
	{
		centerprint(self, "You need the rune of ambush first");
		return;
	}

	gchain = find(world, classname, "gchain");
	while(gchain != world)
	{
		if(gchain.owner == self)
		{
			if(self.state & S_TIEDUP)
			{
				self.movetype = MOVETYPE_WALK;
				self.state = self.state - (self.state & S_TIEDUP);
				return;
			}
			self.state = self.state - (self.state & S_NOGRAV);
			remove(gchain);
		}
		gchain = find(gchain, classname, "gchain");
	}

	e = PlayerInView();
	if(e == world)
	{
		centerprint(self, "No one found to ambush.");
		return;
	}
	bad = FALSE;
	makevectors(e.v_angle);
	v_forward_z = 0;
	v_forward = normalize(v_forward);
	destination = e.origin - v_forward * 50 + '0 0 3';
	if(pointcontents(destination) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.maxs;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.mins;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.mins;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.mins;
	test_z = destination_z + self.maxs_z;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.maxs;
	test_z = destination_z + self.mins_z;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.mins;
	test_y = destination_y + self.maxs_y;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.maxs;
	test_y = destination_y + self.mins_y;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.mins;
	test_x = destination_x + self.maxs_x;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	test = destination + self.maxs;
	test_x = destination_x + self.mins_x;
	if(pointcontents(test) != CONTENT_EMPTY)
		bad = TRUE;

	if(bad)
	{
		centerprint(self, "Not enough room to ambush there");
		return;
	}
	self.items = self.items - (self.items & IT_KEY2);
	self.origin = destination;
	self.angles = e.angles;
	self.angles_x = self.angles_z = 0;
	self.fixangle = 1;
	centerprint(self, "Ambush");
	InvisOff();
	self.state = self.state - (self.state & S_TIEDUP);
};

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// MISCELLANOUS
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
// Death
//------------------------------------------------------------------------------------

void() NinjaDie =
{
  local entity e;

  if(self.movetarget != world)
  {
	remove(self.movetarget);
	self.movetarget = world;
  }

	if(self.state & S_CBERSERK)
	{
		self.state = self.state - (self.state & S_CBERSERK);
		self.attack_finished = time + 3;
		self.berserktime = time + BERSERKRELOADTIME;
		self.weapon = W_BestWeapon();
		stuffcmd(self, "v_cshift 0 0 0 0\n");
		W_SetCurrentAmmo();
		SetSpeed(self);
	}
	self.state = self.state - (self.state & S_TIEDUP);
};

void() DeathtimerThink =
{
        local string si;

	if(self.owner.health)
	{
	        stuffcmd(self.owner,"v_cshift 0 0 0 0\n");
		remove(self);
		return;
	}
	self.count = self.count + DEATHFADE_STEP;
	if(self.count > 255)
		self.count = 255;
        self.count = ceil(self.count);
        si = ftos(self.count);
        stuffcmd(self.owner,"v_cshift 0 0 0 ");
        stuffcmd(self.owner, si);
	stuffcmd(self.owner, "\n");

	if(self.count >= 255)
	{
		remove(self);
		return;
	}
	self.nextthink = time + DEATHFADE_SMOOTHNESS;
};
	
void() DeathFade =
{
        local   entity flashtimer;

	flashtimer = spawn();
	flashtimer.classname = "deathtimer";
	flashtimer.owner = self;
	flashtimer.count = 0;
	flashtimer.think = DeathtimerThink;
	flashtimer.nextthink = time;
};

//------------------------------------------------------------------------------------
// Bindings
//------------------------------------------------------------------------------------

void(entity p) SetAliases =
{
        stuffcmd(p,"alias ");
        stuffcmd(p, COMMAND_THROWSWORD);
        stuffcmd(p," \"impulse 100\"\n");
        stuffcmd(p,"alias +");
        stuffcmd(p, COMMAND_GRAPPLE);
        stuffcmd(p," \"impulse 101\"\n");
        stuffcmd(p,"alias -");
        stuffcmd(p, COMMAND_GRAPPLE);
        stuffcmd(p," \"impulse 103\"\n");
        stuffcmd(p,"alias ");
        stuffcmd(p, COMMAND_AMBUSH);
        stuffcmd(p," \"impulse 102\"\n");
};

//------------------------------------------------------------------------------------
// Spawn point
//------------------------------------------------------------------------------------

float(entity s) BadSpot =
{
	local entity thing;
	thing = findradius(s.origin, 50);	//slightly bigger radius
	while(thing)
	{
		if ((thing.classname == "player") && (thing.health > 0))
			return TRUE;
		thing = thing.chain;
	}
	return FALSE;
};

entity	lastspawn;
entity() GeNSelectSpawnPoint =
{
	local	entity rspot, spot, e;
	local	entity thing;
	local	float  pcount, sp, rsp;
	
// choose a info_player_deathmatch point

	if (coop)
	{
		lastspawn = find(lastspawn, classname, "info_player_coop");
		if (lastspawn == world)
			lastspawn = find (lastspawn, classname, "info_player_start");
		if (lastspawn != world)
			return lastspawn;
	}
	sp = 0;
	e = find(world, classname, "info_player_deathmatch");
	while(e)
	{
		sp = sp + 1;
		e = find(e, classname, "info_player_deathmatch");
	}
	rsp = rint(random()*(sp - 1) + 1);	//pick a random spawn pos

	sp = 0;
	spot = find(world, classname, "info_player_deathmatch");
	while(spot)
	{
		sp = sp + 1;
		if((sp >= rsp) && (spot != lastspawn) && (!BadSpot(spot)))
		{
//			bprint("found goodspot\n");
			lastspawn = spot;
			return spot;
		}
		spot = find(spot, classname, "info_player_deathmatch");
	}

	sp = 0;
	spot = find(world, classname, "info_player_deathmatch");
	while(spot)
	{
		sp = sp + 1;
		if((sp < rsp) && (spot != lastspawn) && (!BadSpot(spot)))
		{
//			bprint("found goodspot2\n");
			lastspawn = spot;
			return spot;
		}
		spot = find(spot, classname, "info_player_deathmatch");
	}

	sp = 0;
	spot = find(world, classname, "info_player_deathmatch");
	while(spot)
	{
		sp = sp + 1;
		if(rsp == sp)
		{
//			bprint("forced randomspot\n");
			lastspawn = spot;
			return spot;
		}
		spot = find(spot, classname, "info_player_deathmatch");
	}

	spot = find(world, classname, "info_player_start");
	if (!spot)
		error ("GeNSelectSpawnPoint: no info_player_start on level");
	
	return spot;
};

//------------------------------------------------------------------------------------
// Fire
//------------------------------------------------------------------------------------

void() Flame_Think =
{
local float r;
local vector vtemp, dir, boundsize;
local entity e;

        if ((self.enemy.waterlevel >= 2) || (self.enemy.takedamage != DAMAGE_AIM) || (self.attack_finished < time))
	{
                self.velocity = self.velocity + '0 0 100';
		if(self.count == 1)
		{
	                sound (self, CHAN_VOICE, "misc/flamend.wav", 1, ATTN_NORM);
			if(self.enemy.state & S_BURNING)
				self.enemy.state = self.enemy.state - S_BURNING;
			e = findradius(self.enemy.origin, FIRE_RADIUS);
			while(e != world)
			{
				if((e.classname == "player") ||
				   (e.classname == "bomb"))
					if(e != self.enemy)
						Burn(e, NUM_FLAMES, self.enemy);
				e = e.chain;
			}
		}
		r = random();
		if(r > 0.85)
			BecomeExplosion ();
		else 
			remove(self);
		return;

	}
        vtemp = self.enemy.origin;
        boundsize = self.enemy.size;
	makevectors(self.enemy.angles);
        dir_x = mrand() * (boundsize_x * 0.2);
        dir_y = mrand() * (boundsize_y * 0.2);
        dir_z = mrand() * (boundsize_z * 0.4);
        vtemp = vtemp + dir - normalize(self.enemy.velocity) * 5;
        setorigin(self, vtemp);
	r = random();
	
	if(vlen(self.enemy.velocity) > 200)
		self.angles = vectoangles(self.enemy.velocity);
	else
		self.angles = vectoangles('0 0 -10');
	if(vlen(self.enemy.velocity) > REDUCEBURNTIMESPEED)
		self.attack_finished = self.attack_finished - 0.8;
        if(self.count == 1)      //first flame
        {
		if(self.jumptime < time)
		{
			sound (self, CHAN_VOICE, "misc/fireamb2.wav", 0.5, ATTN_NORM);
			self.jumptime = time + 2;
		}
                if ((r < 0.3) && (self.pausetime < time))    //with a chance of 1 to 10 the first flame hurts
                {
                        T_Damage (self.enemy, self, self.owner, FIREDAMAGE, 0, "fire", "");
                        self.pausetime = time + FIREDAMAGETIME;
                        if(self.enemy.flags & FL_CLIENT)
                                stuffcmd(self.enemy,"bf\n");
                }
        }                    
	if(r > 0.8)
	        particle(self.origin, '0 0 10', 0, 5);
        self.nextthink = time + 0.1;	//0.01
};

void(entity victim, float ftype, entity inflictor) SpawnFlame =  //flame type 1 is hurting others not
{
local entity flame;

	flame = spawn ();
        flame.count = ftype;
	flame.owner = inflictor;
	flame.enemy = victim;
	flame.classname = "flame";
	flame.movetype = MOVETYPE_NONE;
	flame.solid = SOLID_NOT;

	setmodel (flame, "progs/nflame.mdl");

	flame.nextthink = time + 0.01;
	flame.think = Flame_Think;

        if (ftype == 1)
	{
                flame.effects = flame.effects | EF_DIMLIGHT;
                flame.pausetime = time + 1;
		flame.attack_finished = time + BURNTIME;
	}
	else
		flame.attack_finished = time + (BURNTIME*2/3) + random() * (BURNTIME/3);
};

void(entity victim, float flame_num, entity inflictor) Burn =
{
local float fn;

	if((victim.classname == "bomb") && (victim.solid != SOLID_NOT))
	{
		victim.solid = SOLID_NOT;
		if(victim.ninja)	//is an explodebox
			SpawnExplosion(victim.origin, inflictor, BIGBARREL_SCRAPS, "bigbarrelburn", 0);
		else
			SpawnExplosion(victim.origin, inflictor, BOMB_SCRAPS, "bombburn", 1);
		if(victim.flags & FL_ITEM)
		{
			victim.model = string_null;
			if (deathmatch == 1)
				victim.nextthink = time + 30;
			victim.think = SUB_regen;
		}
		else
			remove(victim);
		return;
	}
        if ((victim.takedamage != DAMAGE_AIM) || (victim.state & S_BURNING) || (victim.health < 1) || (victim.waterlevel >= 2))
		return;
	if((victim.classname == "player") && (victim.items & IT_SUPER_NAILGUN))
	{
		victim.items = victim.items - (victim.items & IT_SUPER_NAILGUN);
		SpawnExplosion(victim.origin, inflictor, BOMB_SCRAPS, "bombplayerburn", 1);
		return;
	}
        fn = flame_num;

        while(fn > 1)
	{
                fn = fn - 1;
                if((fn + 1) == flame_num)       //the first flame hurts
                        SpawnFlame(victim, 1, inflictor);
                else
                        SpawnFlame(victim, 0, inflictor);
	}
	victim.state = victim.state | S_BURNING;
	if (victim.flags & FL_CLIENT)
	{
		if((victim != inflictor) && (inflictor.classname == "player"))
	                gencenterprint7 (victim, inflictor.netname, " has set you on fire.\nFind water.", "", "", "", "", "");
		stuffcmd (victim,"bf\n");
	}
	sound (victim, CHAN_VOICE, "misc/burn.wav", 1, ATTN_NORM);
};

void(vector org, float radius, entity inflictor) RadiusBurn =
{
local entity victim;
	victim = findradius(org, radius);
	while(victim)
	{
		if(victim.takedamage == DAMAGE_AIM)
		{
			traceline (org, victim.origin, TRUE, world );
			if (trace_fraction == 1)
				Burn(victim, NUM_FLAMES, inflictor);
		}
		if(victim.classname == "bomb")
		{
			traceline (org, victim.origin, TRUE, world );
			if ((trace_fraction == 1) && (victim.solid != SOLID_NOT))
			{
				victim.solid = SOLID_NOT;
				if(victim.ninja)	//is an explodebox
					SpawnExplosion(victim.origin, inflictor, BIGBARREL_SCRAPS, "bigbarrelburn", 0);
				else
					SpawnExplosion(victim.origin, inflictor, BOMB_SCRAPS, "bombburn", 1);
				if(victim.flags & FL_ITEM)
				{
					victim.model = string_null;
					victim.solid = SOLID_NOT;
					if (deathmatch)
						victim.nextthink = time + 30;
					victim.think = SUB_regen;
				}
				else
					remove(victim);
			}
		}
		victim = victim.chain;
	}
};

void() SmokeThink =
{
	local vector vtemp, dir, boundsize;

	if(self.ltime < time)
	{
		remove(self);
		return;
	}

        vtemp = self.enemy.absmin;
        boundsize = self.enemy.size;
	makevectors(self.enemy.angles);
        dir_x = (random() * boundsize_x/2)+boundsize_x/4;
        dir_y = (random() * boundsize_y/2)+boundsize_y/4;
        dir_z = (random() * boundsize_z/3)+boundsize_z/2;
        vtemp = vtemp + dir;

	particle(vtemp, '0 0 100', 0, self.count);
	self.nextthink = time + 0.1;
};

void(entity e, float i, float stime) Smoke =
{
local entity s;
	s = spawn();
	s.nextthink = time + 0.1;
	s.ltime = time + stime;
	s.count = i;
	s.think = SmokeThink;
	s.enemy = e;
	s.classname = "smoke";
};

//------------------------------------------------------------------------------------
// Stuff
//------------------------------------------------------------------------------------

float(entity targ) RSee =
{
	traceline(self.origin, targ.origin, TRUE, self);
	if(trace_fraction == 1)
		return TRUE;
	return FALSE;
};

float(entity p, float since) IsBurning =
{
	local entity e;
	e = find(world, classname, "flame");
	while(e)
	{
		if((e.enemy == p) && (e.count))
			if((BURNTIME - (e.attack_finished - time)) >= since)
				return TRUE;
		e = find(e, classname, "flame");
	}
	return FALSE;
};

void(entity p) NinjaPainSound =
{
local float		rs;

// water pain sounds
	if (p.pain_finished > time)
		return;
	p.pain_finished = time + 0.5;

	rs = rint((random() * 10) + 1);

	p.noise = "";
	if (rs == 1)
		p.noise = "player/pain1.wav";
	else if (rs == 2)
		p.noise = "player/pain2.wav";
	else if (rs == 3)
		p.noise = "player/pain3.wav";
	else if (rs == 4)
		p.noise = "player/pain4.wav";
	else if (rs == 5)
		p.noise = "player/pain5.wav";
	else if (rs == 6)
		p.noise = "player/pain6.wav";
	else if (rs == 7)
		p.noise = "player/lburn1.wav";
	else if (rs == 8)
		p.noise = "player/lburn2.wav";
	else if (rs == 9)
		p.noise = "player/ninpain1.wav";
	else if (rs == 10)
		p.noise = "player/ninpain2.wav";
	else
		p.noise = "player/axhit1.wav";

	sound (p, CHAN_VOICE, p.noise, 1, ATTN_NORM);
	return;
};

float() mrand =
{
	if(random() > 0.5)
		return random() * -1;
	else
		return random();
};

void(entity p) SetSpeed =
{
	local string sp;
	local entity te;

	if (!(p.flags & FL_CLIENT))
		return;	

	stuffcmd(p,"cl_movespeedkey 1\n");
	// Check for any reason why they can't move at all
	if (p.state & S_CANT_MOVE)
	{
		p.velocity = '0 0 0';
		p.jumpheight = 0;
		stuffcmd(p,"m_forward 0\n");
		stuffcmd(p,"m_side 0\n");
		stuffcmd(p,"cl_backspeed 0\n");		//def.: 400
		stuffcmd(p,"cl_forwardspeed 0\n");	//def.: 500
		stuffcmd(p,"cl_sidespeed 0\n");		//def.: 350
		return;
	}
	else if((p.weapon == IT_ROCKET_LAUNCHER) && (p.state & S_SBERSERK))
	{
		stuffcmd(p,"cl_backspeed 400\n");
		stuffcmd(p,"cl_forwardspeed 500\n");
		stuffcmd(p,"cl_sidespeed 350\n");
		return;
        }
	else if(p.weapon == IT_GRENADE_LAUNCHER)
	{
		stuffcmd(p,"m_forward 0.7\n");
		stuffcmd(p,"m_side 0.6\n");
		stuffcmd(p,"cl_backspeed 150\n");
		stuffcmd(p,"cl_forwardspeed 220\n");
		stuffcmd(p,"cl_sidespeed 170\n");
		return;
        }
	else 
	{
		stuffcmd(p,"m_forward 0.8\n");
		stuffcmd(p,"m_side 0.6\n");
		stuffcmd(p,"cl_backspeed 240\n");
		stuffcmd(p,"cl_forwardspeed 250\n");
		stuffcmd(p,"cl_sidespeed 240\n");
		return;
	}
};

float(entity damageinflictor, vector targorg) DetermineZone =
{
local vector d;
local float dist;
local entity test;

	d = damageinflictor.origin - targorg;
	d_z = 0;
	dist = vlen(d);
	d = damageinflictor.origin + dist*normalize(damageinflictor.velocity);

	dist = d_z - targorg_z;
	if((dist >= 24) && (dist < 32))
		return Z_HEAD;
	if(dist >= 16)
		return Z_CHEST;
	if(dist < -5)
		return Z_FEET;
};

void(vector org, float pcolor, float number, float index, float strenght) SpawnSparks =
{
local vector r;

	while(index > 0)
	{
		r_x = strenght * mrand();
		r_y = strenght * mrand();
		r_z = strenght * mrand();
		particle(org, r, pcolor, number);
		index = index - 1;
	}
};

float(entity targ, float t) MyInfront =
{
	local vector	vec;
	local float		dot;
	
	makevectors (self.v_angle);
	vec = normalize (targ.origin - self.origin);
	dot = vec * v_forward;
	
	if ( dot > t)
		return TRUE;
	return FALSE;
};

entity() PlayerInView =
{
local entity e, player;

	local float bestdist = -999;
	local vector playerdir, selfdir;
	local float curdist;

	e = find(world, classname, "player");
	while (e != world)
	{
		playerdir = e.origin - self.origin;
		playerdir = normalize(playerdir);
		selfdir = normalize(self.angles);
		playerdir = playerdir - selfdir;
		curdist = vlen(playerdir);
		traceline(self.origin + '0 0 22', e.origin, FALSE, self);
		if(trace_ent != e)
			traceline(self.origin + '0 0 22', e.origin + e.maxs, FALSE, self);
		if(trace_ent != e)
			traceline(self.origin + '0 0 22', e.origin + e.mins, FALSE, self);
		if ((trace_ent == e) && (MyInfront(e, 0.9)))
		{
			if (bestdist == -999)
			{
				bestdist = curdist;
				player = e;
			}
			else if (curdist < bestdist)
			{
				bestdist = curdist;
				player = e;
			}
		}
		e = find(e, classname, "player");
	}
	return player;
};

float() NumberOfPlayers =
{
	local float np = 0;
	local entity p;

	p = find(world, classname, "player");
	while(p != world)
	{
		np = np + 1;
		p = find(p, classname, "player");
	}
	return rint(np);
};

void() BloodFountainThink =
{
	if(self.ltime < time)
	{
		remove(self);
		return;
	}
	self.origin = self.owner.origin + self.oldvel;
	makevectors(self.angles);
	particle (self.origin, random()*600*v_forward + mrand()*v_up*5 + mrand()*v_forward*5, 70, 10);
	particle (self.origin, random()*600*v_forward + mrand()*v_up*5 + mrand()*v_forward*5, 70, 10);
	particle (self.origin, random()*600*v_forward + mrand()*v_up*5 + mrand()*v_forward*5, 70, 10);
	particle (self.origin, random()*600*v_forward + mrand()*v_up*5 + mrand()*v_forward*5, 70, 10);
	particle (self.origin, random()*600*v_forward + mrand()*v_up*5 + mrand()*v_forward*5, 70, 10);
	particle (self.origin, random()*600*v_forward + mrand()*v_up*5 + mrand()*v_forward*5, 70, 10);
	self.nextthink = time + 0.05;
};

void(entity bleeder, float timer, vector ang, vector org) SpawnBloodFountain =
{
local entity e;

	if(bleeder.classname == "player")
	{
		if(random() > 0.75)
			sound (bleeder, CHAN_VOICE, "player/ninpain1.wav", 1, ATTN_NORM);
		else if(random() > 0.5)
			sound (bleeder, CHAN_VOICE, "player/ninpain2.wav", 1, ATTN_NORM);
		else if(random() > 0.25)
			sound (bleeder, CHAN_VOICE, "player/blood2.wav", 1, ATTN_NORM);
		else
			sound (bleeder, CHAN_VOICE, "player/blood.wav", 1, ATTN_NORM);
	}
	e = spawn();
	if(org == '0 0 0')
		e.oldvel = random()*bleeder.maxs*0.5 + random()*bleeder.mins*0.5;
	else
		e.oldvel = org;
	e.origin = bleeder.origin + e.oldvel;
	e.owner = bleeder;
	e.think = BloodFountainThink;
	e.nextthink = time;
	if(ang == '0 0 0')
	{
		makevectors(bleeder.angles);
		e.angles = mrand()*v_forward + mrand()*v_right + random()*v_up;
	}
	else
		e.angles = ang;
	e.ltime = time + timer;
};

void(float bchan, string soundtoplay, float vol, float batt)bsound =
{
	local entity cl;
	cl = find(world, classname, "player");
	while(cl)
	{
		if(cl.flags & FL_CLIENT)
				sound(cl, bchan, soundtoplay, vol, batt);
		cl = find(cl, classname, "player");
	}
};

float(entity for, vector what) Infront =
{
	local vector	vec;
	local float		dot;
	
	makevectors (for.angles);
	vec = normalize (what - for.origin);
	dot = vec * v_forward;
	
	if ( dot > 0.3)
		return TRUE;
	return FALSE;
};

float() NearlyOnground =
{
local vector temp;

	temp = '0 0 0';
	temp_z = self.mins_z - NINJA_JUMPTOLERANCE;
	traceline(self.origin, self.origin + temp, FALSE, self);
	if(trace_fraction == 1)
		return FALSE;
	return TRUE;
};

void() SUB_DeathBubbles =
{
	DeathBubbles(8);
	self.nextthink = time + 2;
	self.think = SUB_Remove;
};

void(vector org) DeadThrowGib =
{
	local	entity new;
	local vector temp;
	local string gibname;

	if(self.model == "progs/h_player.mdl")	//is a head
		gibname = "progs/zom_gib.mdl";
	else
	{
		if(random() > 0.6)
			gibname = "progs/gib3.mdl";
		else if(random() > 0.3)
			gibname = "progs/gib2.mdl";
		else
			gibname = "progs/gib1.mdl";
	}

	new = spawn();
	temp_x = (mrand()*self.maxs_x + mrand()*self.mins_x)*0.8;
        temp_y = (mrand()*self.maxs_y + mrand()*self.mins_y)*0.8;
        temp_z = (mrand()*self.maxs_z + mrand()*self.mins_z)*0.8;
	new.origin = org + temp;
	setmodel (new, gibname);
	setsize (new, '-3 -3 -3', '3 3 3');
	new.velocity_x = mrand() * 100;
	new.velocity_y = mrand() * 100;
	new.velocity_z = random() * 200;
	new.movetype = MOVETYPE_BOUNCE;
	new.solid = SOLID_TRIGGER;
	new.touch = GibTouch;
	new.avelocity_x = mrand()*600;
	new.avelocity_y = mrand()*600;
	new.avelocity_z = mrand()*600;
	new.think = SUB_Remove;
	new.ltime = time;
	new.owner = self;
	new.nextthink = time + GIB_TIME + random()*GIB_TIME_ADDRANDOM;
	new.frame = 0;
	new.flags = 0;
};

void(entity attacker, float damage) DeadHit =
{
	local	entity new;
	local vector temp;
	local string gibname;

	if (self.pain_finished > time)
		return;

	self.pain_finished = time + 1;
	sound (self, CHAN_VOICE, "player/tornoff2.wav", 1, ATTN_NORM);		
	DeadThrowGib(self.origin);
};

void() DeadGib =
{
	local float i;
	local vector org;
	sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);		
	if(self.model == "progs/h_player.mdl")	//is a head
	{
		i = 0;
		while(i < NINJA_GIBS/2.5)
		{
			DeadThrowGib(self.origin);
			i = i + 1;
		}
	}
	else
	{
		i = 0;
		while(i < NINJA_GIBS)
		{
			DeadThrowGib(self.origin);
			i = i + 1;
		}
	}

	self.skin = 0;
	setmodel (self, "progs/null.mdl");
	self.frame = 0;
	self.nextthink = -1;
	self.movetype = MOVETYPE_NONE;
	self.takedamage = DAMAGE_NO;
	self.solid = SOLID_NOT;
};

void() DeadTouch =
{
	if((other.classname == "player") && (other.health > 0))
	{
		self.solid = SOLID_NOT;
		DeadGib();
	}
};

