/*

The Mark I SuperScream Grenade Machine v1.0
by Klaus Breuer (sz0759@rzmail.uni-erlangen.de)

This patch adds a lot of new ammo for the grenade launcher, my favourite
weapon: 12 new grenades!

See ImpulseCommands() at the end of this file for a list of
all possible grenades. Also check ClientConnect() in CLIENT.QC.

I wasn't able to add a new variable to the player object (anybody who
can tell me how to do that?), so I used the self.button1 (which isn't
used by the game) to remember which ammo type is selected.

A lot of these ammo types are way too powerful - you might want to
disable them or tone them down (cause them to use more ammo, cause
damage to the user, etc). I just use them for fun and messing about :)

To disable any ammo type, you'll need to look at these functions:
ImpulseCommands()  // Selecting ammo type
ShowGrenadeType()  // Identify currently loaded ammo
W_FireGrenade()    // Select which grenade to fire
ClientConnect()    // Show info on available ammo (in CLIENT.QC)


Notes:

* I purposely made each ammo type totally self-contained, so you can
  easily port them to your own code.
* I tried to document a lot so you can easily make changes.
* This is mostly my own code, but quite a few ideas/code bits were
  from other people. Sorry, I forgot which ones (thought too late of
  making a list), so if you recognize your work, drop me a mail.

*/
void (entity targ, entity inflictor, entity attacker, float damage) T_Damage;
void () player_run;
void(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage;
void(vector org, vector vel, float damage) SpawnBlood;
void() SuperDamageSound;


// called by worldspawn
void() W_Precache =
{
	precache_sound ("weapons/r_exp3.wav");	// new rocket explosion
	precache_sound ("weapons/rocket1i.wav");	// spike gun
	precache_sound ("weapons/sgun1.wav");
	precache_sound ("weapons/guncock.wav");	// player shotgun
	precache_sound ("weapons/ric1.wav");	// ricochet (used in c code)
	precache_sound ("weapons/ric2.wav");	// ricochet (used in c code)
	precache_sound ("weapons/ric3.wav");	// ricochet (used in c code)
	precache_sound ("weapons/spike2.wav");	// super spikes
	precache_sound ("weapons/tink1.wav");	// spikes tink (used in c code)
	precache_sound ("weapons/grenade.wav");	// grenade launcher
	precache_sound ("weapons/bounce.wav");		// grenade bounce
	precache_sound ("weapons/shotgn2.wav");	// super shotgun

        precache_sound ("talk/scum.wav"); // Duke: Scumsucker

};

float() crandom =
{
	return 2*(random() - 0.5);
};

/*
================
W_FireAxe
================
*/
void() W_FireAxe =
{
	local	vector	source;
	local	vector	org;

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

	if (trace_ent.takedamage)
	{
		trace_ent.axhitme = 1;
		SpawnBlood (org, '0 0 0', 20);
		T_Damage (trace_ent, self, self, 20);
	}
	else
	{	// hit wall
		sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_GUNSHOT);
		WriteCoord (MSG_BROADCAST, org_x);
		WriteCoord (MSG_BROADCAST, org_y);
		WriteCoord (MSG_BROADCAST, org_z);
	}
};


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


vector() wall_velocity =
{
	local vector	vel;
	
	vel = normalize (self.velocity);
	vel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5));
	vel = vel + 2*trace_plane_normal;
	vel = vel * 200;
	
	return vel;
};


/*
================
SpawnMeatSpray
================
*/
void(vector org, vector vel) SpawnMeatSpray =
{
	local	entity missile, mpuff;
	local	vector	org;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_NOT;

	makevectors (self.angles);

	missile.velocity = vel;
	missile.velocity_z = missile.velocity_z + 250 + 50*random();

	missile.avelocity = '3000 1000 2000';

// set missile duration
	missile.nextthink = time + 1;
	missile.think = SUB_Remove;

	setmodel (missile, "progs/zom_gib.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, org);
};

/*
================
SpawnBlood
================
*/
void(vector org, vector vel, float damage) SpawnBlood =
{
	particle (org, vel*0.1, 73, damage*2);
};

/*
================
spawn_touchblood
================
*/
void(float damage) spawn_touchblood =
{
	local vector	vel;

	vel = wall_velocity () * 0.2;
	SpawnBlood (self.origin + vel*0.01, vel, damage);
};


/*
================
SpawnChunk
================
*/
void(vector org, vector vel) SpawnChunk =
{
	particle (org, vel*0.02, 0, 10);
};

/*
==============================================================================

MULTI-DAMAGE

Collects multiple small damages into a single damage

==============================================================================
*/

entity	multi_ent;
float	multi_damage;

void() ClearMultiDamage =
{
	multi_ent = world;
	multi_damage = 0;
};

void() ApplyMultiDamage =
{
	if (!multi_ent)
		return;
	T_Damage (multi_ent, self, self, multi_damage);
};

void(entity hit, float damage) AddMultiDamage =
{
	if (!hit)
		return;
	
	if (hit != multi_ent)
	{
		ApplyMultiDamage ();
		multi_damage = damage;
		multi_ent = hit;
	}
	else
		multi_damage = multi_damage + damage;
};

/*
==============================================================================

BULLETS

==============================================================================
*/

/*
================
TraceAttack
================
*/
void(float damage, vector dir) TraceAttack =
{
	local	vector	vel, org;
	
	vel = normalize(dir + v_up*crandom() + v_right*crandom());
	vel = vel + 2*trace_plane_normal;
	vel = vel * 200;

	org = trace_endpos - dir*4;

	if (trace_ent.takedamage)
	{
		SpawnBlood (org, vel*0.2, damage);
		AddMultiDamage (trace_ent, damage);
	}
	else
	{
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_GUNSHOT);
		WriteCoord (MSG_BROADCAST, org_x);
		WriteCoord (MSG_BROADCAST, org_y);
		WriteCoord (MSG_BROADCAST, org_z);
	}
};

/*
================
FireBullets

Used by shotgun, super shotgun, and enemy soldier firing
Go to the trouble of combining multiple pellets into a single damage call.
================
*/
void(float shotcount, vector dir, vector spread) FireBullets =
{
	local	vector direction;
	local	vector	src;
	
	makevectors(self.v_angle);

	src = self.origin + v_forward*10;
	src_z = self.absmin_z + self.size_z * 0.7;

	ClearMultiDamage ();
	while (shotcount > 0)
	{
		direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up;

		traceline (src, src + direction*2048, FALSE, self);
		if (trace_fraction != 1.0)
			TraceAttack (4, direction);

		shotcount = shotcount - 1;
	}
	ApplyMultiDamage ();
};

/*
================
W_FireShotgun
================
*/
void() W_FireShotgun =
{
	local vector dir;

	sound (self, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM);	

	self.punchangle_x = -2;
	
	self.currentammo = self.ammo_shells = self.ammo_shells - 1;
	dir = aim (self, 100000);
	FireBullets (6, dir, '0.04 0.04 0');
};


/*
================
W_FireSuperShotgun
================
*/
void() W_FireSuperShotgun =
{
	local vector dir;

	if (self.currentammo == 1)
	{
		W_FireShotgun ();
		return;
	}
		
	sound (self ,CHAN_WEAPON, "weapons/shotgn2.wav", 1, ATTN_NORM);	

	self.punchangle_x = -4;
	
	self.currentammo = self.ammo_shells = self.ammo_shells - 2;
	dir = aim (self, 100000);
	FireBullets (14, dir, '0.14 0.08 0');
};


/*
==============================================================================

ROCKETS

==============================================================================
*/

void()	s_explode1	=	[0,		s_explode2] {};
void()	s_explode2	=	[1,		s_explode3] {};
void()	s_explode3	=	[2,		s_explode4] {};
void()	s_explode4	=	[3,		s_explode5] {};
void()	s_explode5	=	[4,		s_explode6] {};
void()	s_explode6	=	[5,		SUB_Remove] {};

void() BecomeExplosion =
{
	self.movetype = MOVETYPE_NONE;
	self.velocity = '0 0 0';
	self.touch = SUB_Null;
	setmodel (self, "progs/s_explod.spr");
	self.solid = SOLID_NOT;
	s_explode1 ();
};

void() T_MissileTouch =
{
	local float	damg;

	if (other == self.owner)
		return;		// don't explode on owner

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}

	damg = 100 + random()*20;
	
	if (other.health)
	{
		if (other.classname == "monster_shambler")
			damg = damg * 0.5;	// mostly immune
		T_Damage (other, self, self.owner, damg );
	}

	// don't do radius damage to the other, because all the damage
	// was done in the impact
	T_RadiusDamage (self, self.owner, 120, other);

//	sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
	self.origin = self.origin - 8*normalize(self.velocity);

	WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_EXPLOSION);
	WriteCoord (MSG_BROADCAST, self.origin_x);
	WriteCoord (MSG_BROADCAST, self.origin_y);
	WriteCoord (MSG_BROADCAST, self.origin_z);

	BecomeExplosion ();
};



/*
================
W_FireRocket
================
*/
void() W_FireRocket =
{
	local	entity missile, mpuff;
	
	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
	
	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLYMISSILE;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;
	missile.angles = vectoangles(missile.velocity);

	missile.touch = T_MissileTouch;

// set missile duration
	missile.nextthink = time + 5;
	missile.think = SUB_Remove;

	setmodel (missile, "progs/missile.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

/*
===============================================================================

LIGHTNING

===============================================================================
*/

/*
=================
LightningDamage
=================
*/
void(vector p1, vector p2, entity from, float damage) LightningDamage =
{
	local entity		e1, e2;
	local vector		f;
	
	f = p2 - p1;
	normalize (f);
	f_x = 0 - f_y;
	f_y = f_x;
	f_z = 0;
	f = f*16;

	e1 = e2 = world;

	traceline (p1, p2, FALSE, self);
	if (trace_ent.takedamage)
	{
		particle (trace_endpos, '0 0 100', 225, damage*4);
		T_Damage (trace_ent, from, from, damage);
		if (self.classname == "player")
		{
			if (other.classname == "player")
				trace_ent.velocity_z = trace_ent.velocity_z + 400;
		}
	}
	e1 = trace_ent;

	traceline (p1 + f, p2 + f, FALSE, self);
	if (trace_ent != e1 && trace_ent.takedamage)
	{
		particle (trace_endpos, '0 0 100', 225, damage*4);
		T_Damage (trace_ent, from, from, damage);
	}
	e2 = trace_ent;

	traceline (p1 - f, p2 - f, FALSE, self);
	if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)
	{
		particle (trace_endpos, '0 0 100', 225, damage*4);
		T_Damage (trace_ent, from, from, damage);
	}
};


void() W_FireLightning =
{
	local	vector		org;

	if (self.ammo_cells < 1)
	{
		self.weapon = W_BestWeapon ();
		W_SetCurrentAmmo ();
		return;
	}

// explode if under water
	if (self.waterlevel > 1)
	{
		T_RadiusDamage (self, self, 35*self.ammo_cells, world);
		self.ammo_cells = 0;
		W_SetCurrentAmmo ();
		return;
	}

	if (self.t_width < time)
	{
		sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
		self.t_width = time + 0.6;
	}
	self.punchangle_x = -2;

	self.currentammo = self.ammo_cells = self.ammo_cells - 1;

	org = self.origin + '0 0 16';
	
	traceline (org, org + v_forward*600, TRUE, self);

	WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
	WriteEntity (MSG_BROADCAST, self);
	WriteCoord (MSG_BROADCAST, org_x);
	WriteCoord (MSG_BROADCAST, org_y);
	WriteCoord (MSG_BROADCAST, org_z);
	WriteCoord (MSG_BROADCAST, trace_endpos_x);
	WriteCoord (MSG_BROADCAST, trace_endpos_y);
	WriteCoord (MSG_BROADCAST, trace_endpos_z);

	LightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);
};



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

void() spike_touch;
void() superspike_touch;


/*
===============
launch_spike

Used for both the player and the ogre
===============
*/
void(vector org, vector dir) launch_spike =
{
	newmis = spawn ();
	newmis.owner = self;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;

	newmis.angles = vectoangles(dir);
	
	newmis.touch = spike_touch;
	newmis.classname = "spike";
	newmis.think = SUB_Remove;
	newmis.nextthink = time + 6;
	setmodel (newmis, "progs/spike.mdl");
	setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);		
	setorigin (newmis, org);

	newmis.velocity = dir * 1000;
};

void() W_FireSuperSpikes =
{
	local vector	dir;
	local entity	old;
	
	sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
	self.attack_finished = time + 0.2;
	self.currentammo = self.ammo_nails = self.ammo_nails - 2;
	dir = aim (self, 1000);
	launch_spike (self.origin + '0 0 16', dir);
	newmis.touch = superspike_touch;
	setmodel (newmis, "progs/s_spike.mdl");
	setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);		
	self.punchangle_x = -2;
};

void(float ox) W_FireSpikes =
{
	local vector	dir;
	local entity	old;
	
	makevectors (self.v_angle);
	
	if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)
	{
		W_FireSuperSpikes ();
		return;
	}

	if (self.ammo_nails < 1)
	{
		self.weapon = W_BestWeapon ();
		W_SetCurrentAmmo ();
		return;
	}

	sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM);
	self.attack_finished = time + 0.2;
	self.currentammo = self.ammo_nails = self.ammo_nails - 1;
	dir = aim (self, 1000);
	launch_spike (self.origin + '0 0 16' + v_right*ox, dir);

	self.punchangle_x = -2;
};



.float hit_z;
void() spike_touch =
{
local float rand;
	if (other == self.owner)
		return;

	if (other.solid == SOLID_TRIGGER)
		return;	// trigger field, do nothing

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}

// hit something that bleeds
	if (other.takedamage)
	{
		spawn_touchblood (9);
		T_Damage (other, self, self.owner, 9);
	}
	else
	{
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		
		if (self.classname == "wizspike")
			WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
		else if (self.classname == "knightspike")
			WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
		else
			WriteByte (MSG_BROADCAST, TE_SPIKE);
		WriteCoord (MSG_BROADCAST, self.origin_x);
		WriteCoord (MSG_BROADCAST, self.origin_y);
		WriteCoord (MSG_BROADCAST, self.origin_z);
	}

	remove(self);

};

void() superspike_touch =
{
local float rand;
	if (other == self.owner)
		return;

	if (other.solid == SOLID_TRIGGER)
		return;	// trigger field, do nothing

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}

// hit something that bleeds
	if (other.takedamage)
	{
		spawn_touchblood (18);
		T_Damage (other, self, self.owner, 18);
	}
	else
	{
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
		WriteCoord (MSG_BROADCAST, self.origin_x);
		WriteCoord (MSG_BROADCAST, self.origin_y);
		WriteCoord (MSG_BROADCAST, self.origin_z);
	}

	remove(self);

};

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

/*
============
The standard bouncing grenade
Well-known and -loved.
============
*/
void() GrenadeExplode =
{
	T_RadiusDamage (self, self.owner, 120, world);

	WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_EXPLOSION);
	WriteCoord (MSG_BROADCAST, self.origin_x);
	WriteCoord (MSG_BROADCAST, self.origin_y);
	WriteCoord (MSG_BROADCAST, self.origin_z);

	BecomeExplosion ();
};

void() GrenadeTouch =
{
	if (other == self.owner)
		return;		// don't explode on owner
	if (other.takedamage == DAMAGE_AIM)
	{
		GrenadeExplode();
		return;
	}
	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);	// bounce sound
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};

/*
================
W_FireNormalGrenade
================
*/
void() W_FireNormalGrenade =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "grenade";

// set missile speed

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);
	
	missile.touch = GrenadeTouch;
	
// set missile duration
	missile.nextthink = time + 2.5;
	missile.think = GrenadeExplode;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};


/*
=====================
Nail Bomb Grenade

As seen in 'Eraser', it bounces to a stop, waits a few moments,
pops up and explodes in a downward shower of nails.
=====================
*/

void(vector org) launch_burst_spike =
{
	newmis = spawn ();
	newmis.owner = self;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;

        newmis.velocity_x=(random()*2 - 1)*2000; //Highspeed, random direction
        newmis.velocity_y=(random()*2 - 1)*2000;
        newmis.velocity_z=(random() - 1)*2000;

	newmis.touch = superspike_touch;
	newmis.classname = "spike";
	newmis.think = SUB_Remove;
	newmis.nextthink = time + 6;
	setmodel (newmis, "progs/spike.mdl");
	setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
	setorigin (newmis, org);

};

void() NailBombBurst = // Explode in a shower of nails
{
 local float nnum = 0;

 // Bompf...
 sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

 // Turn into a fireball...

 WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);   // Light
 WriteByte (MSG_BROADCAST, TE_EXPLOSION);     // Pixel-explosion
 WriteCoord (MSG_BROADCAST, self.origin_x);
 WriteCoord (MSG_BROADCAST, self.origin_y);
 WriteCoord (MSG_BROADCAST, self.origin_z);

 self.movetype = MOVETYPE_NONE;
 self.velocity = '0 0 0';
 self.touch = SUB_Null;
 setmodel (self, "progs/s_explod.spr");
 self.solid = SOLID_NOT;

 // Throw lots of spikes in random directions
 while (nnum < 50)
 {
  launch_burst_spike (self.origin);
  nnum = nnum + 1;
 }

 // Disappear
 remove(self);

};

void() NailBombThink =
// If enough time has passed, hop up and explode
{
        self.nextthink = time + 0.5;
        self.think = NailBombThink;

        if (self.wait < time)
        {
         self.velocity_x=0;
         self.velocity_y=0;
         self.velocity_z=750;
         if (self.flags & FL_ONGROUND) self.flags = self.flags - FL_ONGROUND;
         self.nextthink = time + 0.2;
         self.think = NailBombBurst;
        }
};

void() NailBombTouch =
{
	if (other == self.owner)
		return;		// don't explode on owner
	if (other.takedamage == DAMAGE_AIM)
		return;
	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);	// bounce sound
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};

void() W_FireNailBomb =
{
	local	entity missile, mpuff;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "grenade";
        missile.wait = time + 2 + 5*random(); //blow up after a few seconds

// set missile speed

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = NailBombTouch;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
        missile.nextthink = time + 1; // Gets live real quick
        missile.think = NailBombThink;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
======================
Bouncing cluster bomb

The grenade bursts into 5 sub-munitions, which hop about.
If they reach near a target, they jump towards it and explode.
They'll explode on their own sooner or later.
PS: This lovely is what got me started - it's slightly modified (grenades
hopped for ever) from the CBP Grenade, designed by:
                John Spickes jspickes@eng.umd.edu {JDS}
                Josh Spickes spickesj@wam.umd.edu
                Allen Seger
Thanks, guys.
=======================
*/

void() BCGrenadeThink =
// Hop about, looking for a victim
{
        local entity head;
        local vector dir;
        if(self.flags & FL_ONGROUND) {
        // Don't detect targets if I'm not on the ground
                head = findradius(self.origin, 300);
                while(head) {
                        if (head.takedamage) {
                                if (self.avelocity == '0 0 0')
                                        self.avelocity = '300 300 300';
                                self.origin_z = self.origin_z + 1;
                                dir = normalize(head.origin - self.origin);
                                self.velocity = dir*200 + '0 0 500';
                                if (self.flags & FL_ONGROUND)
                                        self.flags = self.flags - FL_ONGROUND;

                                self.nextthink = time + random()*1.5;
                                self.think = GrenadeExplode;
                                return;
                        }
                        head = head.chain;
                }
        }
        self.nextthink = time + 0.5;
        self.think = BCGrenadeThink;
        if(self.wait < time) // Time to jump
        {
         if (random() < 0.3) self.think = GrenadeExplode;
         self.wait = time + 5 + 5*random();
         if (self.avelocity == '0 0 0')
                self.avelocity = '300 300 300';
	 self.origin_z = self.origin_z + 1;
         self.velocity_x=(random()*400 - 200);
         self.velocity_y=(random()*400 - 200);
         self.velocity_z = 500;
         if (self.flags & FL_ONGROUND)
                self.flags = self.flags - FL_ONGROUND;
        }
};

void() BCGrenadeTouch =
{
	if (other == self.owner)
		return;		// don't explode on owner
	if (other.takedamage == DAMAGE_AIM)
		return; // Don't explode on target impact
	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);	// bounce sound
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};

void() BCGrenadeDeploy =
// Spray out 5 submunitions
{
        local float gnum = 0;
        local entity missile;

        while(gnum < 5) {
                missile = spawn ();
                missile.owner = self.owner;
                missile.movetype = MOVETYPE_BOUNCE;
                missile.solid = SOLID_BBOX;
                missile.classname = "grenade";
                missile.wait = time + 5 + 5*random();
                missile.think = BCGrenadeThink;
                missile.nextthink = time + 0.5;
                missile.velocity_z = 500;
                missile.velocity_x = 400*random() - 200;
                missile.velocity_y = 400*random() - 200;
                gnum = gnum + 1;
                missile.avelocity = '300 300 300';
                missile.angles = vectoangles(missile.velocity);
                missile.touch = BCGrenadeTouch;
                setmodel (missile, "progs/grenade.mdl");
                setsize (missile, '0 0 0', '0 0 0');
                setorigin (missile, self.origin);
        }
        self.think = GrenadeExplode;
        self.nextthink = time + 0.1;
};

void() W_FireBCGrenade =
{
	local	entity missile, mpuff;

	
	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "grenade";
        missile.wait = time + 5 + 5*random();

// set missile speed

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = BCGrenadeTouch;

        // If we have enough for a cluster, fire a cluster, otherwise
        // fire a bouncer.
        if (self.ammo_rockets > 4) {
                self.currentammo = self.ammo_rockets = self.ammo_rockets - 5;
                missile.nextthink = time + 2+(random()*3);
                missile.think = BCGrenadeDeploy;
        }
        else {
                self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
                missile.think = BCGrenadeThink;
                missile.nextthink = time + 2+(random()*2);
        }

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
=====================
Ricochet grenade

Flies at high speed and bounces for a fair while before exploding.
Unless it hits somebody first >:)
======================
*/
void() RicochetTouch =
{
	if (other == self.owner)
		return;		// don't explode on owner
	if (other.takedamage == DAMAGE_AIM)
	{
		GrenadeExplode();
		return;
	}
	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);	// bounce sound

        self.velocity = self.velocity * 1.2; // Keep your speed

	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};


void() W_FireRicochet =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
        missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;
        missile.velocity_z = 200;
	missile.angles = vectoangles(missile.velocity);

	missile.nextthink = time + 5 + random()*5;
	missile.think = GrenadeExplode;
        missile.touch = RicochetTouch;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
=====================
Glue bomb

Flies straight like a missile and sticks to the target, possibly floating
in the air.
Then it blows.
======================
*/

void() GlueStick =
{
 if (random() < 0.02) // Check if it blows
 {
  self.think = GrenadeExplode;  // Boom
  self.touch = GrenadeExplode;
 }
 else
 {
  self.think = GlueStick;       // Keep sticking
  self.touch = GlueStick;
 }

  if ((self.enemy != world) && (self.enemy.health > 1))
  {
   self.origin=self.enemy.origin; // stick with the target
   if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
  }
  self.nextthink = time + 0.1;

};

void() GlueTouch =
{
	if (other == self.owner)
		return;		// don't stick to owner

	sound (self, CHAN_WEAPON, "misc/outwater.wav", 1, ATTN_NORM);
        self.think = GlueStick;
        self.touch = GlueStick;
        self.nextthink = time + 0.1;
        self.velocity = self.velocity * 0; // Stop right there (floats)
	self.avelocity = '0 0 0';
        self.enemy = other;
};

void() W_FireGlue =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
        missile.movetype = MOVETYPE_FLYMISSILE;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;
	missile.angles = vectoangles(missile.velocity);

	missile.nextthink = time + 5; // Blow after 5s uninterrupted flight
	missile.think = GrenadeExplode;
        missile.touch = GlueTouch;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
=====================
MG Point Defense

Gets live after a few seconds, turns into a nailgun,
shooting at everything in sight. Explodes after a while.
=====================
*/

void(vector org, vector d) MGShoot =
// Shoot once in direction d
{
  sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
  launch_spike (org + '0 0 16', d);
  newmis.touch = superspike_touch;
  setmodel (newmis, "progs/spike.mdl");
  setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
};

void() MGThink =
// Scan for anything alive
{
        local entity head;
        local vector dir;

        self.nextthink = time + 1; // 1s pause between target searches
        self.think = MGThink;

        head = findradius(self.origin, 1500); // Look for opponents
        while(head)  // found something
        {
         if (head.takedamage)  // can be damaged
         {
          if ((self.owner != head) && (head.classname != "monster_zombie")) // Not owner or zombie
          {
           if ((coop && (head.classname == "player")) != 1) // Not player in coop
           {
            if (visible(head) == 1) // Can see it!
            {
             dir = normalize(head.origin - self.origin); // Get direction
             self.ideal_yaw = vectoyaw(head.origin - self.origin);
             self.angles_y = self.ideal_yaw;
  	     self.angles_x = 0;
  	     self.angles_z = 0;

             MGShoot (self.origin,dir); // Shoot at it
            }
           }
          }
         }
         head = head.chain; // next
        }

        if (self.wait < time) // self-destruct
        {
         self.nextthink = time + 0.2;
         self.think = GrenadeExplode;
        }
};

void() MGDeploy =
// Turn into an MG.
// Since I can't build an MG shape yet, I'm using a standard player head
{
 setmodel (self, "progs/h_player.mdl");
 self.angles = '0 0 0';
 sound (self, CHAN_WEAPON, "misc/outwater.wav", 1, ATTN_NORM);

 self.think = MGThink;
 self.nextthink = time + random()*2;
 self.wait = time + 20 + random()*20; // Active lifespan
};

void() MGTouch =
{
	if (other == self.owner)
		return;		// don't explode on owner
	if (other.takedamage == DAMAGE_AIM)
		return;
	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);	// bounce sound
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};

void() W_FireMG =
{
	local	entity missile, mpuff;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "grenade";
        missile.wait = time + 2 + 5*random(); //blow up after a few seconds

// set missile speed

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = MGTouch;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
        missile.nextthink = time + 2 + random()*5; // Takes a few seconds to deploy
        missile.think = MGDeploy;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
===============
Vampire Bolt

Gives YOU the HP the opponent lost by being hit with this
================
*/
void() VampTouch =
{
	local float	damg;
        local float     oldhealth,healthgained;
        local string    s;

	if (other == self.owner)
		return;		// don't explode on owner

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}

	damg = 5 + random()*15;

	if ((other.health) && (other.classname == "player"))
	{
         oldhealth = other.health;
         T_Damage (other, self, self.owner, damg );
         spawn_touchblood(damg);
         if (other.health <= 0) { healthgained = oldhealth; }
                           else { healthgained = oldhealth-other.health; }
         T_Heal(self.owner,healthgained,0);
         sprint(self.owner, "You drain ");
	 s = ftos(healthgained);
	 sprint(self.owner, s);
	 sprint(self.owner, " health\n");
         // Tell victim about it
         sprint(other,self.owner.netname);
         sprint(other," drains ");
         sprint(other, s);
	 sprint(other, " health from you\n");
	}

        remove(self);
};


void() W_FireVamp =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLYMISSILE;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;
	missile.angles = vectoangles(missile.velocity);

	missile.touch = VampTouch;

// set missile duration
	missile.nextthink = time + 5;
	missile.think = SUB_Remove;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

/*
==========
Medic Bolt

Heals the target by a small amount
==========
*/
void() MedicTouch =
{
        local string    s;
        local float     heal_p,oldhealth;

	if (other == self.owner)
		return;		// don't explode on owner

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}
        if (other.classname != "player") { remove(self); return; }

	heal_p = 5 + random()*15;

	if (other.health)
	{
         oldhealth = other.health;
         T_Heal(other,heal_p,0);
         heal_p = other.health - oldhealth;
         sprint(self.owner, "You heal ");
	 s = ftos(heal_p);
	 sprint(self.owner, s);
	 sprint(self.owner, " health\n");
         sprint(other,self.owner.netname);
         sprint(other," heals ");
         sprint(other, s);
         sprint(other, " health for you\n");
	}

        remove(self);
};

void() W_FireMedic =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLYMISSILE;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;
	missile.angles = vectoangles(missile.velocity);

	missile.touch = MedicTouch;

// set missile duration
	missile.nextthink = time + 5;
	missile.think = SUB_Remove;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

/*
==========
Armor Piercing

Goes straight through the targets until it hits a wall
==========
*/

void() APRemove =
{
 WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);  // Piiing
 WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
 WriteCoord (MSG_BROADCAST, self.origin_x);
 WriteCoord (MSG_BROADCAST, self.origin_y);
 WriteCoord (MSG_BROADCAST, self.origin_z);
 remove(self);
};

void() APTouch =
{
 local float dam;

	if (other == self.owner)
		return;		// don't explode on owner

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}

        if (other.takedamage) // damage it
        {
         dam = 50 + random()*20;
         spawn_touchblood(dam);
         T_Damage (other, self, self.owner, dam );
        }
        else
        {
         APRemove; // Disappear against the wall
        }

};

void() W_FireAP =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLY;
	missile.solid = SOLID_TRIGGER;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;

	missile.angles = vectoangles(missile.velocity);

	missile.touch = APTouch;

// set missile duration
	missile.nextthink = time + 5;   // Disappear after 5s
	missile.think = SUB_Remove;

	setmodel (missile, "progs/spike.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

/*
==============
Radio Tag Bomb

Is detonated by command only and can be disarmed by command as well.
If it hits a valid target, it'll stick to it and can be blown at
any time. Otherwise, it acts like the pipe bomb from Duke3D.
==============
*/

void() DetonateRTB =
// Detonate all owned RTBs in range (10000)
{
 local entity head;

 head = findradius (self.origin, 10000); // Search
 while(head) // Found something
 {
  if((head.classname == "RTB") && (head.owner == self)) // It's a bomb, belonging to us
  {
   head.think = GrenadeExplode; // Tell it to blow up...
   head.nextthink = time; // ...right away
  }
  head = head.chain; // Check next object
 }
};

void() DisarmRTB =
// Disarm all owned RTBs in range (10000)
{
 local entity head;

 head = findradius (self.origin, 10000); // Start looking
 while(head) // Found an object
 {
  if((head.classname == "RTB") && (head.owner == self)) // It's our RTB
  {
   sprint (self,"You untagged "); // Notify owner
   if (head.enemy.classname == "player")
   {
    sprint (self,head.enemy.netname);
    sprint (self,"\n");
    sprint (head.enemy,self.netname); // Tell target
    sprint (head.enemy," untagged his RTB\n");
   }
   else // Not a human player; return name of monster
   {
    sprint (self, "a ");
    sprint (self, head.enemy.classname);
    sprint (self,"\n");
   }
   head.movetype = MOVETYPE_NONE; // Remove yourself immediately
   head.velocity = '0 0 0';
   head.touch = SUB_Null;
   head.think = SUB_Remove;
   head.nextthink = time;
   }
  head = head.chain; // Check next
  }
 return;
};

void() TaggedRTBTouch =
{
 self.origin = self.enemy.origin; // stick with enemy
 if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
};

void() RTBTouch =
{
 if (other == self.owner) return; // don't explode on owner

 if (other.takedamage == DAMAGE_AIM)
 {
  if (!self.enemy)
  {
   sound(self, CHAN_WEAPON, "weapons/tink1.wav", 1, ATTN_NORM);
   self.think = GrenadeExplode;
  }
  self.enemy = other;
  self.origin = self.enemy.origin; // Stick to target
  if (self.effects != EF_DIMLIGHT) // We're not glowing yet
  {
   sprint (self.owner, "You tagged "); // Tell owner about it
   if (self.enemy.classname == "player") // Tagged an opponent
   {
    sprint (self.owner, self.enemy.netname);
    sprint (self.owner, "\n");
    sprint (self.enemy, "You have been tagged by "); // tell opponent
    sprint (self.enemy, self.owner.netname);
    sprint (self.enemy, "\n");
   }
   else // Not human player; get name of monster
   {
    sprint (self.owner, "a ");
    sprint (self.owner, self.enemy.classname);
    sprint (self.owner, "\n");
   }
   self.effects = EF_DIMLIGHT; // Start glowing
   self.think = TaggedRTBTouch;
   self.touch = TaggedRTBTouch;
   self.nextthink = time;
  }
 }
 sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);  // bounce sound
 if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
};

void() W_FireRTB =
{
	local	entity missile, mpuff;


	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "RTB"; // Radio Tag Bomb
        missile.wait = time + 5 + 5*random();

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = RTBTouch;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
        missile.think = GrenadeExplode;
        missile.nextthink = time + 10; // Explode if didn't touch anything after 10s

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
==========
Ammo Cache

Grenade turns into backpack which contains 20 of each ammo, if the
user has as much. Useful for feeding your buddies or building caches.
==========
*/

void() AmmoCacheDeploy =
// Turn into backpack
{
 self.flags = FL_ITEM;
 self.solid = SOLID_TRIGGER;
 self.movetype = MOVETYPE_TOSS;

 self.origin_z = self.origin_z + 20; // Pop up a bit
 self.angles = self.angles * 0; // Stand upright
 setmodel (self, "progs/backpack.mdl");
 setsize(self, '-16 -16 0', '16 16 56');
 sound (self, CHAN_WEAPON, "misc/outwater.wav", 1, ATTN_NORM); // Deploy sound
 self.touch = BackpackTouch;
 self.nextthink = time + 1200;    // remove after 20 minutes
 self.think = SUB_Remove;
};

void() AmmoCacheTouch =
{
	if (other == self.owner) return; // don't bounce off owner
	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);	// bounce sound
	if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
};

void() W_FireAmmoCache =
{
	local	entity missile, mpuff;

	if ( (self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells) <= 1)
		return; // Not enough ammo

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "grenade";
        missile.wait = time + 5 + 5*random();

        // Load with ammo
        if (self.ammo_shells >= 20)
	{
	 missile.ammo_shells = 20;
	 self.ammo_shells = self.ammo_shells - 20;
	}
	else
	{
	 missile.ammo_shells = self.ammo_shells;
	 self.ammo_shells = 0;
	}

	if (self.ammo_nails >= 20)
	{
	 missile.ammo_nails = 20;
	 self.ammo_nails = self.ammo_nails - 20;
	}
	else
	{
	 missile.ammo_nails = self.ammo_nails;
	 self.ammo_nails = 0;
	}
	if (self.ammo_rockets >= 10)
	{
	 missile.ammo_rockets = 10;
	 self.ammo_rockets = self.ammo_rockets - 10;
	}
	else
	{
	 missile.ammo_rockets = self.ammo_rockets;
	 self.ammo_rockets = 0;
	}

	if (self.ammo_cells >= 20)
	{
	 missile.ammo_cells = 20;
	 self.ammo_cells = self.ammo_cells - 20;
	}
	else
	{
	 missile.ammo_cells = self.ammo_cells;
	 self.ammo_cells = 0;
	}

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = AmmoCacheTouch;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
        missile.think = AmmoCacheDeploy;
        missile.nextthink = time + 2 + random()*5; // Deploy after a few seconds

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
===============
Homing Missiles

These are quite smart, flying through the area looking for a target (not you).
Code is from Iikka Paavolainen (ipaavola@cc.hut.fi), who wrote the
GeniusMissile patch. This is a VERY impressive piece of code, and I only
changed little of it (missile search radius much smaller for speed and
interestng search pattern).

As soon as I have a bit of time, I'll work out exactly what this thing
does and comment it.
===============
*/

void()   HomeThink;
entity() HomeFindTarget;
float(entity targ) visible;
float(entity targ) infront;

void() W_FireHomingMissile =
{
	local	entity missile, mpuff;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLYMISSILE;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 300;
	missile.angles = vectoangles(missile.velocity);

	missile.touch = T_MissileTouch;

// set missile duration
//	missile.nextthink = time + 30;
//	missile.think = SUB_Remove;

        missile.nextthink = time + 0.2;
	missile.think = HomeThink;
	missile.enemy = world;
        missile.ltime=time;

	setmodel (missile, "progs/missile.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

entity() HomeFindTarget =
{
	local entity head, selected;
	local float dist;
	dist = 100000;
	selected = world;
	head = findradius(self.origin, 1500);
	while(head)
	{
                if( (head.health > 1) && (head != self) && (head != self.owner) && head.classname != "door")
		{
                 if ((coop && (head.classname == "player")) != 1) // Not player in coop
                 {
			traceline(self.origin,head.origin,TRUE,self);
			if ( (trace_fraction >= 1) && (vlen(head.origin - self.origin) < dist) )
			{
				selected = head;
				dist = vlen(head.origin - self.origin);
			}
                 }
		}
		head = head.chain;
	}
        if (selected != world)
	{
		sprint (self.owner,"Homing->");
		if (selected.classname == "player")
		{
			sprint (self.owner,selected.netname);
			sprint (selected,self.owner.netname);
			sprint (selected," has a bogey on you!\n");
		}
		else
			sprint (self.owner,selected.classname);
		sprint (self.owner,"\n");
	}
	return selected;
};

void() HomeThink =
{
        local vector dir, vtemp, tm;
        local float r,dist;
        dist=100;
        if(time-self.ltime>=20)
        {
                sprint(self.owner,"Missile self-destructing.\n");
                self.nextthink=time+0.1;
                self.think=SUB_Remove;
                return;
        }
        traceline(self.origin,self.enemy.origin,TRUE,self);
        if ( trace_fraction<1.0 || !(self.enemy) || (self.enemy == world) || (self.enemy.health < 1) )
		self.enemy = HomeFindTarget();

        if (self.enemy != world)
	{
		vtemp = self.enemy.origin + '0 0 10';
		dir = normalize(vtemp - self.origin);
                self.velocity = dir * 300;
		self.angles = vectoangles(self.velocity);
	}
        else
        {
                makevectors(self.angles);
                traceline(self.origin,self.origin+v_forward*dist,TRUE,self);
                if(trace_fraction<1.0)
                {
                        traceline(self.origin,self.origin+v_right*dist,TRUE,self);
                        if(trace_fraction==1.0)
                                self.velocity=v_right*300;
                        else
                        {
                                traceline(self.origin,self.origin-v_right*dist,TRUE,self);
                                if(trace_fraction==1.0)
                                        self.velocity='0 0 0'-v_right*300;
                                else
                                {
                                        traceline(self.origin,self.origin+v_up*dist,TRUE,self);
                                        if(trace_fraction==1.0)
                                                self.velocity=v_up*300;
                                        else
                                        {
                                                traceline(self.origin,self.origin-v_up*dist,TRUE,self);
                                                if(trace_fraction==1.0)
                                                        self.velocity='0 0 0'-v_up*300;
                                                else
                                                {
                                                        traceline(self.origin,self.origin-v_forward*dist,TRUE,self);
                                                        if(trace_fraction==1.0)
                                                                self.velocity='0 0 0'-v_forward*300;
                                                        else
                                                        {
                                                                sprint(self.owner,"Missile in trouble!\n");
                                                                r=random();
                                                                if(r>0.75)
                                                                        self.velocity=v_right*300;
                                                                else
                                                                if(r>0.50)
                                                                        self.velocity='0 0 0'-v_right*300;
                                                                else
                                                                if(r>0.25)
                                                                        self.velocity=v_up*300;
                                                                else
                                                                        self.velocity='0 0 0'-v_up*300;
                                                        }
                                                }
                                        }
                                }
                        }
                        self.angles=vectoangles(self.velocity);
                }
        }
	self.nextthink = time + 0.2;
	self.think=HomeThink;
};

/*
=====================
Nail Launcher Grenade

Pops up, rotates and fires nails before exploding.
Code is from TeamFortress 1.0 by Robin Walker, John Cook and Ian Caughley.
=====================
*/
void() NailLauncherTouch =
{
	if (other == self.owner)
		return;		// don't explode on owner

	// If the Nail Grenade hits a player, it just bounces off

	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};

void() NailLauncherLaunchNail =
// Fire three nails in random directions, parallel to the ground
{
	local float i,j;

	i = 0;
	while (i < 3)
	{
		j = (random() + 2) * 2;
		current_yaw = anglemod( self.angles_y );
		current_yaw = anglemod(current_yaw + j);
		self.angles_y = current_yaw;
		self.angles_x = 0;
		self.angles_z = 0;
		makevectors(self.angles);

		launch_spike(self.origin, v_forward);

		i = i + 1;
	}

	self.nextthink = time + 0.1;

	// Explode?
        if (random() < 0.01)
		self.think = GrenadeExplode;
};

void() NailLauncherNailEm =
// Ready to launch nails
{
	// Rotate and spew Nails
	self.velocity = '0 0 0';

	self.nextthink = time + 0.1;
	self.think = NailLauncherLaunchNail;
};

void() NailLauncherExplode =
// Pop up and launch nails
{
	// Raise into the air
	self.movetype = MOVETYPE_NOCLIP;
	self.velocity = '0 0 100';
	self.avelocity = '0 500 0';
	self.nextthink = time + 0.5;
	self.think = NailLauncherNailEm;
};

void() W_FireNailLauncher =
{
	local	entity missile, mpuff;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "grenade";
        missile.nextthink = time + 2 + 5*random();

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = NailLauncherTouch;
        missile.think = NailLauncherExplode;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
=====
Flare

Fires a flare which sticks to walls (but not doors and plats).
Flies until it hits something (does 1pt damage), then flares up.
After a while it'll die down again.

Cool code is from Steve Bond (wedge@nuc.net).
=====
*/

void() FlareDim =
// Make flare go dim again
{
// Make the flare get dim by setting .effects bit
        self.effects=EF_DIMLIGHT;
// Give it 15 seconds before burnout
	self.nextthink = time + 15;
// After 15 seconds remove the flare from the game
	self.think = SUB_Remove;
};

void() FlareBright =
// Flare brightens up
{
	local vector    vel;
// Make the flare get BRIGHT by setting .effects bit
        self.effects=EF_BRIGHTLIGHT;
// Give it 10 seconds at maximum intensity
	self.nextthink = time + 10;
// After 10 seconds, call flare_dim
	self.think = FlareDim;
// Play a sound for flare ignition
	sound (self, CHAN_WEAPON, "misc/power.wav", 1, ATTN_NORM);

// Set up some red and yellow sparks
	vel = '0 0 150';
	particle (self.origin+vel*0.01,vel,111,150);
        vel = '0 0 120';
	particle (self.origin+vel*0.01,vel,73,200);
};

void() FlareTouch =
{
local float rand;
	if (other == self.owner)
		return;

        if (other.classname == "door" || other.classname == "plat")
                {
                remove(self);
                return;
                }

	if (other.solid == SOLID_TRIGGER)
		return; // trigger field, do nothing

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);
		return;
	}

// has the flare hit something that bleeds?
	if (other.takedamage)
	{
                // If so, spawn some blood
		spawn_touchblood (9);
                // also do one point of damage
		T_Damage (other, self, self.owner, 1);

/*
Don't remove the flare unless it is still flying. Otherwise, monsters or
other players can come up and 'pull' your flare out of the wall
*/
	      if (self.velocity != '0 0 0')
			remove(self);
	}
	else
	{
        // Write information to the network
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_SPIKE);
		WriteCoord (MSG_BROADCAST, self.origin_x);
		WriteCoord (MSG_BROADCAST, self.origin_y);
		WriteCoord (MSG_BROADCAST, self.origin_z);
	}

//Stop the flare, but do not remove it.
	self.movetype = MOVETYPE_NONE;
// Fix it so that flare only partially embeds itself (thanks, Luke)
        self.origin = self.origin - self.velocity * 0.005;
     	self.velocity = '0 0 0';
	self.touch = SUB_Null;
};

void() W_FireFlare =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLY;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 1000;
	missile.angles = vectoangles(missile.velocity);

	missile.touch = FlareTouch;

// set missile duration
	missile.nextthink = time + 3; // Flare brightly after 3s
	missile.think = FlareBright;

	setmodel (missile, "progs/missile.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
};

/*
======
Camera

Launches a camera, which pops up and can be rotated via the cursor keys.
Has to be explicitly removed, but the player can launch several eyes,
always looking through the latest one.

======
*/
void() CameraTouch =
{
	if (other == self.owner)
		return;		// don't react to owner

	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
};

void() RemoveCameras =
{
 local entity head,camtokill;

 // First, set the viewport back to the player
  msg_entity = self; // target of message: the player
  WriteByte(MSG_ONE, 5);
  WriteEntity(MSG_ONE, self); // back to player
  WriteByte(MSG_ONE, 10);
  WriteAngle(MSG_ONE, self.angles_x);
  WriteAngle(MSG_ONE, self.angles_y);
  WriteAngle(MSG_ONE, self.angles_z);
  // Now, remove all cameras
  head = findradius(self.origin, 10000); // Look in a big radius
  while(head)
  {
   if ((head.owner == self) && (head.classname == "camera")) // It's a camera of ours
   {
    camtokill = head;
    head = head.chain;
    sound (camtokill, CHAN_WEAPON, "misc/power.wav", 0.3, ATTN_NORM); // Weird sound as it disappears
    remove(camtokill);
   }
   else
    { head = head.chain; } // Check next
  }
};

void() CameraFilm =
{
 // Shift the viewpoint to the camera
 msg_entity = self.owner;
 WriteByte(MSG_ONE, 5);
 WriteEntity(MSG_ONE, self);
 WriteByte(MSG_ONE, 10);
 WriteAngle(MSG_ONE, self.angles_x);
 WriteAngle(MSG_ONE, self.angles_y);
 WriteAngle(MSG_ONE, self.angles_z);
};

void() CameraActivate =
// Stop the upwards movement and start filming
{
 self.velocity = '0 0 0'; // Stop rising
 current_yaw = current_yaw * 0; // Place yourself horizontally
 self.angles_y = 0;
 self.angles_x = 0;
 self.angles_z = 0;
 makevectors(self.angles);

 self.think = CameraFilm;
 self.nextthink = time + 0.1;
};

void() CameraDeploy =
// Grenade releases a pop-up camera
{
 // Rise into the air
 self.movetype = MOVETYPE_NOCLIP;
 self.velocity = '0 0 100';
 self.think = CameraActivate;
 self.nextthink = time + 0.5;
 // Change into glowing eyes
 setmodel (self, "progs/eyes.mdl");
 self.effects = self.effects | EF_DIMLIGHT; // Weird glow
};

void() W_FireCamera =
{
	local	entity missile, mpuff;

	self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "camera";

// set missile speed

	makevectors (self.v_angle);

	if (self.v_angle_x)
		missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 600;
		missile.velocity_z = 200;
	}

	missile.avelocity = '300 300 300';

	missile.angles = vectoangles(missile.velocity);

	missile.touch = CameraTouch;

// set missile duration
	missile.nextthink = time + 2.5;
	missile.think = CameraDeploy;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin);
};

/*
===========
Nail Probe

Similar to the homing missile, this slow-moving probe fires
nails at the enemy.
The probe self-destructs after 20s or when it runs out of ammo.

This code is a modified Homing Missile, which, in turn, is
a modified Genius Missile - see comment there for original
author.

PS: I used to have the life span set to 60s with no ammo limit -
those things cleared out whole levels without help from me >:)
===========
*/
void()   NailHomeThink;
entity() NailHomeFindTarget;
float(entity targ) visible;
float(entity targ) infront;

void() NailProbeTouch =
{
	if (other == self.owner)
		return;		// don't react to owner

	sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
	if (self.velocity == '0 0 0') // bounce off
		self.avelocity = '0 0 0';
};

void(vector org, vector d) NailProbeShoot =
// Shoot once in direction d
{
  sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
  launch_spike (org + '0 0 16', d);
  self.ammo_nails = self.ammo_nails - 1; // One more nail gone...
  newmis.touch = spike_touch;
  setmodel (newmis, "progs/spike.mdl");
  setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
  self.nextthink = time + 1; // shoot & think slower for a moment
};

void() W_FireNailProbe =
{
	local	entity missile, mpuff;

        self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;

	sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);

	self.punchangle_x = -2;

	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_FLY;
	missile.solid = SOLID_BBOX;

// set missile speed

	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 100;
	missile.angles = vectoangles(missile.velocity);

	missile.touch = NailProbeTouch;
	missile.enemy = world;
        missile.ltime = time;
        missile.ammo_nails = 20; // Missile is armed with 20 nails

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');
	setorigin (missile, self.origin + v_forward*8 + '0 0 16');
        missile.effects = self.effects | EF_DIMLIGHT; // Glow a bit
        missile.nextthink = time + 1; // 1s pause
	missile.think = NailHomeThink;
};

entity() NailHomeFindTarget =
{
	local entity head, selected;
	local float dist;
	dist = 100000;
	selected = world;
	head = findradius(self.origin, 1500);
	while(head)
	{
                if( (head.health > 1) && (head != self) && (head != self.owner) && head.classname != "door")
		{
                 if ((coop && (head.classname == "player")) != 1) // Not player in coop
                 {
                  if (head.classname != "monster_zombie")  // No use shooting zombies...
                  {
		   traceline(self.origin,head.origin,TRUE,self);
		   if ( (trace_fraction >= 1) && (vlen(head.origin - self.origin) < dist) )
		   {
		    selected = head;
		    dist = vlen(head.origin - self.origin);
		   }
                  }
                 }
		}
		head = head.chain;
	}
        if (selected != world)
	{
		sprint (self.owner,"Target->");
		if (selected.classname == "player")
		{
			sprint (self.owner,selected.netname);
			sprint (selected,self.owner.netname);
			sprint (selected," has an eye on you!\n");
		}
		else
			sprint (self.owner,selected.classname);
		sprint (self.owner,"\n");
	}
	return selected;
};

void() NailHomeThink =
{
        local vector dir, vtemp, tm, enemydir;
        local float r,dist;
        dist=100;
        if ((time-self.ltime >= 20) && // Life-span is 20s. Feel free to increment
            (self.ammo_nails <= 0)) // No ammo left?
        {
         sprint(self.owner,"Probe self-destructing.\n");
         self.nextthink=time+0.1;
         self.think=GrenadeExplode; // Go out with a bang
         return;
        }
        traceline(self.origin,self.enemy.origin,TRUE,self);
        if ( trace_fraction<1.0 || !(self.enemy) || (self.enemy == world) || (self.enemy.health < 1) )
		self.enemy = HomeFindTarget();

        if (self.enemy != world) // Valid target
	{
         enemydir = normalize(self.enemy.origin - self.origin); // Get direction
         NailProbeShoot(self.origin,enemydir);
	}
        else
        {
                makevectors(self.angles);
                traceline(self.origin,self.origin+v_forward*dist,TRUE,self);
                if(trace_fraction<1.0)
                {
                        traceline(self.origin,self.origin+v_right*dist,TRUE,self);
                        if(trace_fraction==1.0)
                                self.velocity=v_right*300;
                        else
                        {
                                traceline(self.origin,self.origin-v_right*dist,TRUE,self);
                                if(trace_fraction==1.0)
                                        self.velocity='0 0 0'-v_right*300;
                                else
                                {
                                        traceline(self.origin,self.origin+v_up*dist,TRUE,self);
                                        if(trace_fraction==1.0)
                                                self.velocity=v_up*300;
                                        else
                                        {
                                                traceline(self.origin,self.origin-v_up*dist,TRUE,self);
                                                if(trace_fraction==1.0)
                                                        self.velocity='0 0 0'-v_up*300;
                                                else
                                                {
                                                        traceline(self.origin,self.origin-v_forward*dist,TRUE,self);
                                                        if(trace_fraction==1.0)
                                                                self.velocity='0 0 0'-v_forward*300;
                                                        else
                                                        {
                                                                sprint(self.owner,"Probe in trouble!\n");
                                                                r=random();
                                                                if(r>0.75)
                                                                        self.velocity=v_right*300;
                                                                else
                                                                if(r>0.50)
                                                                        self.velocity='0 0 0'-v_right*300;
                                                                else
                                                                if(r>0.25)
                                                                        self.velocity=v_up*300;
                                                                else
                                                                        self.velocity='0 0 0'-v_up*300;
                                                        }
                                                }
                                        }
                                }
                        }
                        self.angles=vectoangles(self.velocity);
                }
        }
	self.nextthink = time + 0.2;
	self.think=NailHomeThink;
};


void() W_FireGrenade =
// Choose grenade type to fire according to player.button1
// The USE button is not used, so we use it as a local variable for each player
{
 if (self.button1 ==  1) { W_FireNailBomb (); return; }
 if (self.button1 ==  2) { W_FireNailLauncher (); return; }
 if (self.button1 ==  3) { W_FireBCGrenade (); return; }
 if (self.button1 ==  4) { W_FireRocket (); return; }
 if (self.button1 ==  5) { W_FireRicochet (); return; }
 if (self.button1 ==  6) { W_FireGlue (); return; }
 if (self.button1 ==  7) { W_FireMG (); return; }
 if (self.button1 ==  8) { W_FireVamp (); return; }
 if (self.button1 ==  9) { W_FireMedic (); return; }
 if (self.button1 == 10) { W_FireAP (); return; }
 if (self.button1 == 11) { W_FireRTB (); return; }
 if (self.button1 == 12) { W_FireAmmoCache (); return; }
 if (self.button1 == 13) { W_FireHomingMissile (); return; }
 if (self.button1 == 14) { W_FireFlare (); return; }
 if (self.button1 == 15) { W_FireCamera (); return; }
 if (self.button1 == 16) { W_FireNailProbe (); return; }
 W_FireNormalGrenade (); // Firewall
};

/*
===============================================================================

PLAYER WEAPON USE

===============================================================================
*/

void() W_SetCurrentAmmo =
{
	player_run ();		// get out of any weapon firing states

	self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );

	if (self.weapon == IT_AXE)
	{
		self.currentammo = 0;
		self.weaponmodel = "progs/v_axe.mdl";
		self.weaponframe = 0;
	}
	else if (self.weapon == IT_SHOTGUN)
	{
		self.currentammo = self.ammo_shells;
		self.weaponmodel = "progs/v_shot.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_SHELLS;
	}
	else if (self.weapon == IT_SUPER_SHOTGUN)
	{
		self.currentammo = self.ammo_shells;
		self.weaponmodel = "progs/v_shot2.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_SHELLS;
	}
	else if (self.weapon == IT_NAILGUN)
	{
		self.currentammo = self.ammo_nails;
		self.weaponmodel = "progs/v_nail.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_NAILS;
	}
	else if (self.weapon == IT_SUPER_NAILGUN)
	{
		self.currentammo = self.ammo_nails;
		self.weaponmodel = "progs/v_nail2.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_NAILS;
	}
	else if (self.weapon == IT_GRENADE_LAUNCHER)
	{
		self.currentammo = self.ammo_rockets;
		self.weaponmodel = "progs/v_rock.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_ROCKETS;
	}
	else if (self.weapon == IT_ROCKET_LAUNCHER)
	{
		self.currentammo = self.ammo_rockets;
		self.weaponmodel = "progs/v_rock2.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_ROCKETS;
	}
	else if (self.weapon == IT_LIGHTNING)
	{
		self.currentammo = self.ammo_cells;
		self.weaponmodel = "progs/v_light.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_CELLS;
	}
	else
	{
		self.currentammo = 0;
		self.weaponmodel = "";
		self.weaponframe = 0;
	}
};

float() W_BestWeapon =
{
	local	float	it;
	
	it = self.items;

	if(self.ammo_cells >= 1 && (it & IT_LIGHTNING) )
		return IT_LIGHTNING;
	else if(self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) )
		return IT_SUPER_NAILGUN;
	else if(self.ammo_shells >= 2 && (it & IT_SUPER_SHOTGUN) )
		return IT_SUPER_SHOTGUN;
	else if(self.ammo_nails >= 1 && (it & IT_NAILGUN) )
		return IT_NAILGUN;
	else if(self.ammo_shells >= 1 && (it & IT_SHOTGUN) )
		return IT_SHOTGUN;

/*
	if(self.ammo_rockets >= 1 && (it & IT_ROCKET_LAUNCHER) )
		return IT_ROCKET_LAUNCHER;
	else if(self.ammo_rockets >= 1 && (it & IT_GRENADE_LAUNCHER) )
		return IT_GRENADE_LAUNCHER;

*/

	return IT_AXE;
};

float() W_CheckNoAmmo =
{
	if (self.currentammo > 0)
		return TRUE;

	if (self.weapon == IT_AXE)
		return TRUE;
	
	self.weapon = W_BestWeapon ();

	W_SetCurrentAmmo ();

// drop the weapon down
	return FALSE;
};

/*
============
W_Attack

An attack impulse can be triggered now
============
*/
void()	player_axe1;
void()	player_axeb1;
void()	player_axec1;
void()	player_axed1;
void()	player_shot1;
void()	player_nail1;
void()	player_light1;
void()	player_rocket1;

void() W_Attack =
{
	local	float	r;

	if (!W_CheckNoAmmo ())
		return;

	makevectors	(self.v_angle);			// calculate forward angle for velocity
	self.show_hostile = time + 1;	// wake monsters up

	if (self.weapon == IT_AXE)
	{
		sound (self, CHAN_WEAPON, "weapons/ax1.wav", 1, ATTN_NORM);
		r = random();
		if (r < 0.25)
			player_axe1 ();
		else if (r<0.5)
			player_axeb1 ();
		else if (r<0.75)
			player_axec1 ();
		else
			player_axed1 ();
		self.attack_finished = time + 0.5;
	}
	else if (self.weapon == IT_SHOTGUN)
	{
		player_shot1 ();
		W_FireShotgun ();
		self.attack_finished = time + 0.5;
	}
	else if (self.weapon == IT_SUPER_SHOTGUN)
	{
		player_shot1 ();
		W_FireSuperShotgun ();
		self.attack_finished = time + 0.7;
	}
	else if (self.weapon == IT_NAILGUN)
	{
		player_nail1 ();
	}
	else if (self.weapon == IT_SUPER_NAILGUN)
	{
		player_nail1 ();
	}
	else if (self.weapon == IT_GRENADE_LAUNCHER)
	{
		player_rocket1();
		W_FireGrenade();
		self.attack_finished = time + 0.6;
	}
	else if (self.weapon == IT_ROCKET_LAUNCHER)
	{
		player_rocket1();
		W_FireRocket();
		self.attack_finished = time + 0.8;
	}
	else if (self.weapon == IT_LIGHTNING)
	{
		player_light1();
		self.attack_finished = time + 0.1;
		sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
	}
};

/*
============
W_ChangeWeapon

============
*/
void() W_ChangeWeapon =
{
	local	float	it, am, fl;
	
	it = self.items;
	am = 0;

	if (self.impulse == 1)
	{
		fl = IT_AXE;
	}
	else if (self.impulse == 2)
	{
		fl = IT_SHOTGUN;
		if (self.ammo_shells < 1)
			am = 1;
	}
	else if (self.impulse == 3)
	{
		fl = IT_SUPER_SHOTGUN;
		if (self.ammo_shells < 2)
			am = 1;
	}		
	else if (self.impulse == 4)
	{
		fl = IT_NAILGUN;
		if (self.ammo_nails < 1)
			am = 1;
	}
	else if (self.impulse == 5)
	{
		fl = IT_SUPER_NAILGUN;
		if (self.ammo_nails < 2)
			am = 1;
	}
	else if (self.impulse == 6)
	{
		fl = IT_GRENADE_LAUNCHER;
		if (self.ammo_rockets < 1)
			am = 1;
	}
	else if (self.impulse == 7)
	{
		fl = IT_ROCKET_LAUNCHER;
		if (self.ammo_rockets < 1)
			am = 1;
	}
	else if (self.impulse == 8)
	{
		fl = IT_LIGHTNING;
		if (self.ammo_cells < 1)
			am = 1;
	}

	self.impulse = 0;
	
	if (!(self.items & fl))
	{	// don't have the weapon or the ammo
		sprint (self, "no weapon.\n");
		return;
	}

	if (am)
	{	// don't have the ammo
		sprint (self, "not enough ammo.\n");
		return;
	}

//
// set weapon, set ammo
//
	self.weapon = fl;
	W_SetCurrentAmmo ();
};

/*
============
CheatCommand
============
*/
void() CheatCommand =
{
	if (deathmatch || coop)
		return;

	self.ammo_rockets = 100;
	self.ammo_nails = 200;
	self.ammo_shells = 100;
	self.items = self.items |
		IT_AXE |
		IT_SHOTGUN |
		IT_SUPER_SHOTGUN |
		IT_NAILGUN |
		IT_SUPER_NAILGUN |
		IT_GRENADE_LAUNCHER |
		IT_ROCKET_LAUNCHER |
		IT_KEY1 | IT_KEY2;

	self.ammo_cells = 200;
	self.items = self.items | IT_LIGHTNING;

	self.weapon = IT_ROCKET_LAUNCHER;
	self.impulse = 0;
	W_SetCurrentAmmo ();
};

/*
============
CycleWeaponCommand

Go to the next weapon with ammo
============
*/
void() CycleWeaponCommand =
{
	local	float	it, am;

	it = self.items;
	self.impulse = 0;

	while (1)
	{
		am = 0;

		if (self.weapon == IT_LIGHTNING)
		{
			self.weapon = IT_AXE;
		}
		else if (self.weapon == IT_AXE)
		{
			self.weapon = IT_SHOTGUN;
			if (self.ammo_shells < 1)
				am = 1;
		}
		else if (self.weapon == IT_SHOTGUN)
		{
			self.weapon = IT_SUPER_SHOTGUN;
			if (self.ammo_shells < 2)
				am = 1;
		}
		else if (self.weapon == IT_SUPER_SHOTGUN)
		{
			self.weapon = IT_NAILGUN;
			if (self.ammo_nails < 1)
				am = 1;
		}
		else if (self.weapon == IT_NAILGUN)
		{
			self.weapon = IT_SUPER_NAILGUN;
			if (self.ammo_nails < 2)
				am = 1;
		}
		else if (self.weapon == IT_SUPER_NAILGUN)
		{
			self.weapon = IT_GRENADE_LAUNCHER;
			if (self.ammo_rockets < 1)
				am = 1;
		}
		else if (self.weapon == IT_GRENADE_LAUNCHER)
		{
			self.weapon = IT_ROCKET_LAUNCHER;
			if (self.ammo_rockets < 1)
				am = 1;
		}
		else if (self.weapon == IT_ROCKET_LAUNCHER)
		{
			self.weapon = IT_LIGHTNING;
			if (self.ammo_cells < 1)
				am = 1;
		}

		if ( (self.items & self.weapon) && am == 0)
		{
			W_SetCurrentAmmo ();
			return;
		}
	}

};

/*
============
ServerflagsCommand

Just for development
============
*/
void() ServerflagsCommand =
{
	serverflags = serverflags * 2 + 1;
};

void() QuadCheat =
{
	if (deathmatch || coop)
		return;
	self.super_time = 1;
	self.super_damage_finished = time + 30;
	self.items = self.items | IT_QUAD;
	dprint ("quad cheat\n");
};

void(entity p) ShowGrenadeType =
// Show which grenade player p got loaded
{
 if (p.button1 ==  0) { sprint (self,"Standard grenade\n"); return; }
 if (p.button1 ==  1) { sprint (self,"Nail bomb\n"); return; }
 if (p.button1 ==  2) { sprint (self,"Nail launcher\n"); return; }
 if (p.button1 ==  3) { sprint (self,"Bouncing cluster bomb\n"); return; }
 if (p.button1 ==  4) { sprint (self,"RPG\n"); return; }
 if (p.button1 ==  5) { sprint (self,"Ricochet grenade\n"); return; }
 if (p.button1 ==  6) { sprint (self,"Glue grenade\n"); return; }
 if (p.button1 ==  7) { sprint (self,"MG point defense\n"); return; }
 if (p.button1 ==  8) { sprint (self,"Vampire bolt\n"); return; }
 if (p.button1 ==  9) { sprint (self,"Medic bolt\n"); return; }
 if (p.button1 == 10) { sprint (self,"Armor piercing\n"); return; }
 if (p.button1 == 11) { sprint (self,"Radio tag bomb\n"); return; }
 if (p.button1 == 12) { sprint (self,"Ammo cache\n"); return; }
 if (p.button1 == 13) { sprint (self,"Homing missile\n"); return; }
 if (p.button1 == 14) { sprint (self,"Flare\n"); return; }
 if (p.button1 == 15) { sprint (self,"Camera\n"); return; }
 if (p.button1 == 16) { sprint (self,"Nail probe\n"); return; }
 sprint (self,"Unknown grenade; using standard\n");
};

/*
============
ImpulseCommands

============
*/
void() ImpulseCommands =
{
	if (self.impulse >= 1 && self.impulse <= 8)
		W_ChangeWeapon ();

	if (self.impulse == 9)
		CheatCommand ();
	if (self.impulse == 10)
		CycleWeaponCommand ();
	if (self.impulse == 11)
		ServerflagsCommand ();

// *** Grenade selection done here

        if (self.impulse == 99) // Give launcher & ammo
        {
	self.ammo_rockets = 100;
	self.items = self.items | IT_GRENADE_LAUNCHER;
        }

        if (self.impulse == 100) // Next grenade type
        {
                self.button1 = self.button1 + 1;
                if (self.button1 > 20) self.button1 = 0;
                ShowGrenadeType (self);
        }
        if (self.impulse == 101) // Previous grenade type
        {
                self.button1 = self.button1 - 1;
                if (self.button1 < 0) self.button1 = 20;
                ShowGrenadeType (self);
        }
        if (self.impulse == 102) // Show current type
        {
                ShowGrenadeType (self);
        }
        if (self.impulse == 103) // Select Nail Bomb
        {
                self.button1 = 1;
                ShowGrenadeType (self);
        }
        if (self.impulse == 104) // Select Bouncing Cluster Bomb
        {
                self.button1 = 2;
                ShowGrenadeType (self);
        }
        if (self.impulse == 105) // Select RPG
        {
                self.button1 = 3;
                ShowGrenadeType (self);
        }
        if (self.impulse == 106) // Select Ricochet Grenade
        {
                self.button1 = 4;
                ShowGrenadeType (self);
        }
        if (self.impulse == 107) // Select Glue Grenade
        {
                self.button1 = 5;
                ShowGrenadeType (self);
        }
        if (self.impulse == 108) // Select MG Point Defense
        {
                self.button1 = 6;
                ShowGrenadeType (self);
        }
        if (self.impulse == 109) // Select Vampire Bolt
        {
                self.button1 = 7;
                ShowGrenadeType (self);
        }
        if (self.impulse == 110) // Select Medic Bolt
        {
                self.button1 = 8;
                ShowGrenadeType (self);
        }
        if (self.impulse == 111) // Select Armor Piercing
        {
                self.button1 = 9;
                ShowGrenadeType (self);
        }
        if (self.impulse == 112) // Select Radio Tag Bomb
        {
                self.button1 = 10;
                ShowGrenadeType (self);
        }
        if (self.impulse == 113) // Detonate RTB
        {
         DetonateRTB ();
        }
        if (self.impulse == 114) // Disarm RTB
        {
         DisarmRTB ();
        }
        if (self.impulse == 115) // Select Ammo Cache
        {
                self.button1 = 11;
                ShowGrenadeType (self);
        }
        if (self.impulse == 116) // Select Homing Missile
        {
                self.button1 = 12;
                ShowGrenadeType (self);
        }
        if (self.impulse == 117) // Select Nail Launcher
        {
                self.button1 = 13;
                ShowGrenadeType (self);
        }
        if (self.impulse == 118) // Select Flare
        {
                self.button1 = 14;
                ShowGrenadeType (self);
        }
        if (self.impulse == 119) // Select Camera
        {
                self.button1 = 15;
                ShowGrenadeType (self);
        }
        if (self.impulse == 120) // Remove Camera
        {
         RemoveCameras ();
        }
        if (self.impulse == 121) // Nail Probe
        {
                self.button1 = 16;
                ShowGrenadeType (self);
        }

        if (self.impulse == 254) // Talk!
        {
         sound (self, CHAN_WEAPON, "talk/scum.wav", 1, ATTN_NORM);
        }


	if (self.impulse == 255)
		QuadCheat ();

	self.impulse = 0;
};

/*
============
W_WeaponFrame

Called every frame so impulse events can be handled as well as possible
============
*/
void() W_WeaponFrame =
{
	if (time < self.attack_finished)
		return;

	ImpulseCommands ();
	
// check for attack
	if (self.button0)
	{
		SuperDamageSound ();
		W_Attack ();
	}
};

/*
========
SuperDamageSound

Plays sound if needed
========
*/
void() SuperDamageSound =
{
	if (self.super_damage_finished > time)
	{
		if (self.super_sound < time)
		{
			self.super_sound = time + 1;
			sound (self, CHAN_BODY, "items/damage3.wav", 1, ATTN_NORM);
		}
	}
	return;
};


