//==========================================================================
// GRENADE FRENZY                   Bill Lefler
//                                  blefler@digital.net
//
// I threw together my favorite weapons patches. Thanks to the original
// authors for their pioneering efforts!
//
//==========================================================================
//==========================================================================
// Modified weapons.qc - "Blaster" patch by Michael Dewberry.
// Created 7.26.96.
// - replaces single shotgun with laser
//==========================================================================
// Bouncing Fragmentation Grenade!    7/28/96 by Steve Bond
// Email: wedge@nuc.net    WWW: http://www.nuc.net/quake
//
// modified by Bill Lefler 7/31/96
//   - only take 2 grenades - 25 pieces of shrapnel.
//                                  "IMPULSE 20" - fire frag grenade
//==========================================================================
// * Cluster-Bouncy-Proximity Grenades  -- Grenades are now bouncy
// proximity mines.  A grenade will lay on the floor and hop at
// intervals.  If a target is detected within range, the grenade will
// hop towards the target and explode.  Grenades won't detect targets
// while they're in the air.  When the grenade launcher is fired, if you
// have less than five ammo you will fire a single grenade which
// will behave as described above.  If you have 5 or more ammo, you'll
// fire a cluster bomb which will explode and release five proximity
// grenades.
//
// Mods by
//         John Spickes jspickes@eng.umd.edu {JDS}
//         Josh Spickes spickesj@wam.umd.edu
//         Allen Seger
//         Bill Lefler 8/1/96 ----> "IMPULSE 21" - fire proximity grenade
//==========================================================================
// PipeBombs
//
// Modifications by AsmodeusB (28/07/96)
//
// Bill Lefler 8/1/96 - changed keys to:
//                                  "IMPULSE 22" - fire pipe bomb
//                                  "IMPULSE 23" - detonate pipe bomps
//==========================================================================

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

void(vector org, vector vec) LaunchLaser;

// 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_model2 ("progs/laser.mdl");
        precache_sound2 ("enforcer/enfire.wav");        // super shotgun
        precache_sound2 ("enforcer/enfstop.wav");       // super shotgun
};

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

Procedures added and modified by Michael Dewberry - "Blaster" patch
================
*/

void( vector org, vector dir ) FireShot_Laser =
{
        local vector spread, direction;
        spread = '0.04 0.04 0.04';

        // On quad damage, make a monster spread triple blast. :)
        if (self.super_damage_finished > time)
        {
                    // For some reason, direction = dir + crandom*spread_x +                                                                                                   // crandom*spread_y ... etc. didn't work.
                // It works in other places in the code though.. weird.

                direction_x = dir_x + crandom()*spread_x;
                direction_y = dir_y + crandom()*spread_y;
                direction_z = dir_z + crandom()*spread_z;
                LaunchLaser( org, direction );

                direction_x = dir_x + crandom()*spread_x;
                direction_y = dir_y + crandom()*spread_y;
                direction_z = dir_z + crandom()*spread_z;
                LaunchLaser( org, direction );

                direction_x = dir_x + crandom()*spread_x;
                direction_y = dir_y + crandom()*spread_y;
                direction_z = dir_z + crandom()*spread_z;
                LaunchLaser( org, direction );
        }
        else
                LaunchLaser( org, dir );
};

// Third and final shot.
void() W_FireShotgun3 =
{
        local vector dir, org;

        player_shot1 ();
        self.punchangle_x = -2;
        if (self.ammo_shells > 0)
        {
                self.currentammo = self.ammo_shells = self.ammo_shells - 1;

                org = self.origin + v_forward*8 + '0 0 16';
                dir = aim (self, 100000);
                FireShot_Laser(org, dir);
        }
};

// Second shot.
void() W_FireShotgun2 =
{
        local vector dir, org;

        player_shot1 ();
        self.punchangle_x = -2;
        if (self.ammo_shells > 0)
        {
                self.currentammo = self.ammo_shells = self.ammo_shells - 1;

                org = self.origin + v_forward*8 + '0 0 16';
                dir = aim (self, 100000);
                FireShot_Laser(org, dir);
        }
        self.think = W_FireShotgun3;
        self.nextthink = 1;

};

// First shot. Only called if there's shotgun ammo, so no need to check for
// enough ammo here.
void() W_FireShotgun =
{
        local vector dir, org;

        org = self.origin + v_forward*8 + '0 0 16';

        self.punchangle_x = -2;

        self.currentammo = self.ammo_shells = self.ammo_shells - 1;
        dir = aim (self, 100000);
        FireShot_Laser(org, dir);
        self.think = W_FireShotgun2;
        self.nextthink = 1;

};


/*
================
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() 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_FireGrenade
================
*/
void() W_FireGrenade =
{
        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);
};


//=============================================================================
void() ProxGrenadeThink;
void() ProxGrenadeTouch;
void() ProxGrenadeDeploy;
void() ProxW_FireGrenade;

/*
=============
ProxGrenadeThink

{JDS}
For Proximity grenades, look for nearby targets and hop around.
=============
*/

void() ProxGrenadeThink =
{
        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, 200);
                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 = ProxGrenadeThink;
        if(self.wait < time)
        {
                // Time to jump.
                self.wait = time + 15 + 15*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() ProxGrenadeTouch =
{
        if (other == self.owner)
                return;         // don't explode on owner
        if (other.takedamage == DAMAGE_AIM)
        {
    // {JDS}  Don't explode on contect with enemies.
                return;
        }
        sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);  // bounce sound
        if (self.velocity == '0 0 0')
                self.avelocity = '0 0 0';
};

/*
================
ProxGrenadeDeploy

{JDS}
Explode and spray out five new proximity grenades.
================
*/
void() ProxGrenadeDeploy =
{
        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 + 15 + 15*random();
                missile.think = ProxGrenadeThink;
                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 = ProxGrenadeTouch;
                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;
};

/*
================
ProxW_FireGrenade
================
*/
void() ProxW_FireGrenade =
{
        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 + 15 + 15*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 = ProxGrenadeTouch;

        // 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 = ProxGrenadeDeploy;
        }
        else {
                self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
                missile.nextthink = time + 2+(random()*2);
                missile.think = ProxGrenadeThink;
        }

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


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

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

};

/*========================================================================
BOUNCING FRAGMENTATION GRENADE - Version .9
7/28/96 - Steve Bond  email:wedge@nuc.net
http://www.nuc.net/quake
=======================================================================*/

//This code segment handles the impact of shrapnel
//this is merely id's superspike_touch code, reworked
void() shrapnel_touch =
{
local float rand;
// has the shrapnel hit a switch?
        if (other.solid == SOLID_TRIGGER)
                return; // trigger field, do nothing

// has the shrapnel hit the sky?
        if (pointcontents(self.origin) == CONTENT_SKY)
        {
                remove(self);
                return;
        }

// has the shrapnel hit a living thing?
        if (other.takedamage)
        {
                spawn_touchblood (33);
                T_Damage (other, self, self.owner, 33);
        }
        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);
};

/*
Code to spawn ONE randomly directed piece of shrapnel
this is id's launch_spike code, reworked
Pass a vector to this function to determine the shrap's origin
*/
void (vector org, float spin) launch_shrapnel=
{
        local float xdir,ydir,zdir;

//Assign a random direction for the shrapnel to fly in
        xdir = 110 * random() - 55;
        ydir = 110 * random() - 55;
        zdir = 50 * random() - 25;

        newmis = spawn ();
        newmis.owner = self;
        newmis.movetype = MOVETYPE_BOUNCE;
        self.touch = SUB_Null;
        newmis.solid = SOLID_BBOX;

        newmis.touch = shrapnel_touch;
        newmis.classname = "spike";
        newmis.think = SUB_Remove;

// this is how many seconds(?) the nails can exist.
        newmis.nextthink = time + 6;

// speed at which to move the shrapnel
        newmis.velocity_x = xdir * 5;
        newmis.velocity_y = ydir * 5;
        newmis.velocity_z = zdir * 4;

/*
as best I can figure, AVELOCITY means "attitude velocity"
or something thereabouts. Anyway, it makes shit spin on
the x,y,z axes by the given velocity for each axis. In this
case, it makes the shrapnel spin in flight. Good stuff.
this segment assigns one of five preset attitudes for
each piece of shrapnel, based on the number passed to the
function.
*/
        if (spin == 0)
        newmis.avelocity='250 300 400';
        if (spin == 1)
        newmis.avelocity='400 250 300';
        if (spin == 2)
        newmis.avelocity='300 400 250';
        if (spin == 3)
        newmis.avelocity='300 300 300';
        if (spin == 4)
        newmis.avelocity='400 250 400';

        setmodel (newmis, "progs/s_spike.mdl");
        setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
        setorigin (newmis, org);
};

// This code segment detonates the 'second stage' of the grenade
// (After it has popped up)
void()  BouncerExplode =
{

// Do the damage
        T_RadiusDamage (self, self.owner, 120, world);
// Tell the network
        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);

// Let Quake handle the explosion and deallocation of the grenade
        self.solid=SOLID_NOT;
        BecomeExplosion ();

// Launch a hail (20 pieces) of shrapnel
        launch_shrapnel (self.origin + '0 0 -1',0);
        launch_shrapnel (self.origin + '0 0 -1',1);
        launch_shrapnel (self.origin + '0 0 -1',2);
        launch_shrapnel (self.origin + '0 0 -1',3);
        launch_shrapnel (self.origin + '0 0 -1',4);
        launch_shrapnel (self.origin + '0 0 -1',0);
        launch_shrapnel (self.origin + '0 0 -1',1);
        launch_shrapnel (self.origin + '0 0 -1',2);
        launch_shrapnel (self.origin + '0 0 -1',3);
        launch_shrapnel (self.origin + '0 0 -1',4);
        launch_shrapnel (self.origin + '0 0 -1',0);
        launch_shrapnel (self.origin + '0 0 -1',1);
        launch_shrapnel (self.origin + '0 0 -1',2);
        launch_shrapnel (self.origin + '0 0 -1',3);
        launch_shrapnel (self.origin + '0 0 -1',4);
        launch_shrapnel (self.origin + '0 0 -1',0);
        launch_shrapnel (self.origin + '0 0 -1',1);
        launch_shrapnel (self.origin + '0 0 -1',2);
        launch_shrapnel (self.origin + '0 0 -1',3);
        launch_shrapnel (self.origin + '0 0 -1',4);
        launch_shrapnel (self.origin + '0 0 -1',0);
        launch_shrapnel (self.origin + '0 0 -1',1);
        launch_shrapnel (self.origin + '0 0 -1',2);
        launch_shrapnel (self.origin + '0 0 -1',3);
        launch_shrapnel (self.origin + '0 0 -1',4);
};

/*
This code segment makes the 'first stage' of the bouncer pop upward
after it's time has expired.
*/
void() BouncerPopUp=
{
        local entity missile, mpuff;

// Make the grenade stop
        self.movetype=MOVETYPE_NONE;
        self.velocity='0 0 0';
// Draw a tiny explosion (no particles)
        setmodel (self, "progs/s_explod.spr");
        s_explode1 ();
// Make the :FOOMP: grenade launcher sound
        sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
// Spawn and animate the 'second stage'
        missile = spawn ();
        missile.owner = self;
        missile.movetype = MOVETYPE_BOUNCE;
        missile.solid = SOLID_BBOX;
        missile.classname = "grenade";
// Set speed
        missile.velocity = '0 0 650';
        missile.angles = vectoangles(missile.velocity);
        missile.touch = BouncerExplode;
// Set Duration
        missile.nextthink = time + 1;
        missile.think = BouncerExplode;

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

// This code segment handles collisions for the 'first stage'
// Of the grenade (after launch and before popup)

void() BouncerTouch =
{
//Did the grenade hit a 'living' thing?
        if (other.takedamage)
        {
// yes, so play the bounce sound
        sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
// now, exit the function without cause damage to the thing
        return;
        }

// This controls what happens when the grenade hits walls, etz
// It just plays the sound and bounces on
        sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
        if (self.velocity == '0 0 0')
                self.avelocity = '0 0 0';
};

/*
This code segment handles the launching of the Bouncing Fragmentation Grenade
this is a reworked copy of id's W_launchgrenade code
*/
void() W_LaunchBouncer =
{
// If player doesn't have 3 or more rockets, don't launch
        if (self.ammo_rockets < 2)
        {
        return;
        }

        local   entity missile, mpuff;

// Take 2 rockets from the player
        self.currentammo = self.ammo_rockets = self.ammo_rockets - 2;

        //sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
/*
self.punchangle_x (x, y, or z) defines how hard the weapon
recoils (or 'punches' the player, making the screen shake)
*/
        self.punchangle_x = -2;

// This spawns the grenade - it is all id's standard grenade code
        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);

// if the grenade touches anything, BouncerTouch() is called
        missile.touch = BouncerTouch;

// Grenade has a maximum lifespan of 1.5 (seconds?)
// set missile duration
        missile.nextthink = time + 1.5;

// when the grenade's lifespan has expired, BouncerPopUp() is called
        missile.think = BouncerPopUp;

        setmodel (missile, "progs/grenade.mdl");
        setsize (missile, '0 0 0', '0 0 0');
        setorigin (missile, self.origin);
};
/*
===============================================================================
End of Bouncing Fragmentation Grenade code.
===============================================================================
*/

// ------------------------------------------
// Pipe Bombs
// ------------------------------------------

//===========================================================================
// Blow up the toys.  There is a range of 1000 units (pixels?), so you can't
// go wandering off.
//===========================================================================
void() DetPipeBombs =
{
        local entity    head;

        head = findradius (self.origin, 1000);
        while(head)
        {
                if((head.classname == "pipebomb") && (head.owner == self))
                        head.nextthink = time;
                head = head.chain;
        }
};

//===========================================================================
// What happens if it touches something
//===========================================================================

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

//===========================================================================
// Fires a pipe bomb.  Can be detonated at any time.  Doesn't need a special
// weapon selected.
//===========================================================================

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

        if(self.ammo_rockets < 1)       // have to have ammo
          return;
        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 = "pipebomb";

// 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 = PipeBombTouch;
        missile.think = GrenadeExplode;

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

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

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.75;
        }
        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");
};

/*
============
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 ();

        if (self.impulse == 20 )  // Frag Grenade ===
          W_LaunchBouncer();

        if (self.impulse == 21 )  // Proximity Grenade ===
          ProxW_FireGrenade();

        if(self.impulse == 22)    // Pipe Bombs ===
          W_FirePipeBomb();
        if(self.impulse == 23)
          DetPipeBombs();

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


