Darkplaces Modmaking
Created By: Supajoe
Contact: #qc
Difficulty Scale: Medium


In this tutorial we will be reconstructing a basic mod that makes use of many DarkPlaces features and/or extensions.

Note that due to the nature of this tutorial, you must wait until you finish it before you compile and run.

I also recommend you create a new mod first to play around with the new DP effects before you add them to any serious
work you are doing, or at least create a backup of your source.

To start, please download darkplacestut.zip here. Inside it are three files needed for this lesson to work - effects.pk3,
game.cfg and playermovement.qc. The other two, credits.txt and dpextensions.qc are (mostly) unneeded but I strongly suggest you read them anyway.

Place playermovement.qc in your mod's source dir, add it one line below misc.qc in progs.src.

Place effects.pk3 and game.cfg in your mod dir.

Now we are going to add the definitions from dpextensions.qc that we will be using in this tut.
(Although normally you would stick dpextensions.qc in your source dir and add it to
progs.src, for this tut we will be doing it through defs.qc)

Paste the following at the bottom of defs.qc

float   EF_NODRAW       = 16;	//Ent isn't rendered at all, can also use on a light source
float   EF_ADDITIVE     = 32;   //Ent is rendered with additive blending
float   EF_BLUE         = 64;	//Ent emits blue light - similar to the QuakeWorld (Ugh) effect
float   EF_RED          = 128;	//Ent emits red light - see above
float   EF_FLAME        = 1024;	//Ent emits smoke and fire particles

.float alpha;		//Alpha value of an ent, default is 1 - 100% visible. To make an ent completely
			// invisible, set its alpha to -1
.float glow_color;	//This is the 8bit color of the glow
.float glow_size;	//Size of the glow in units
.float glow_trail;	//0 = no trail, 1 = trail
.float button3;		//We're only going to use one extra input button for this tut
.float laser_active;	//This is needed by something interesting :P
.float scale;		//Render scale of an ent, the bounding box/clip hull is not affected by this.
			// note that the default for this is 1, 100%. Maximum is somewhere around 15, 1500%

.entity viewmodelforclient;	//Defines this ent as a viewmodel for another ent
.entity exteriormodeltoclient;	//Use this for 3rd person muzzleflashes and such

vector(vector org) getlight = #92;	//Returns a vector with the RGB value of the light on a 0-255 scale

void(vector org, vector velocity, float howmany) te_blood = #405;	//A nice blood effect
void(vector org, vector color) te_explosionrgb = #407;			//Like TE_EXPLOSIONQUAD, but you define 
									// the flash color - scale is RGB, 0-255 
									// converted to 0-1
void(vector org, vector vel, float howmany) te_spark = #411;		//A nice spark effect
void(vector org) te_gunshotquad = #412;		//Like the normal effect but it also produces a nice blue flash
void(vector org) te_spikequad = #413;
void(vector org) te_superspikequad = #414;
void(vector org) te_explosionquad = #415;
void(vector org) te_gunshot = #418;		//Just like the old effect, but now called with a single line
void(vector org) te_spike = #419;
void(vector org) te_superspike = #420;
void(vector org) te_explosion = #421;
void(vector org) te_tarexplosion = #422;
void(vector org) te_wizspike = #423;
void(vector org) te_knightspike = #424;
void(vector org) te_lavasplash = #425;
void(vector org) te_teleport = #426;
void(entity own, vector start, vector end) te_lightning1 = #428;
void(entity own, vector start, vector end) te_lightning2 = #429;
void(entity own, vector start, vector end) te_lightning3 = #430;
void(vector org) te_plasmaburn = #433;		//Produces a light flash and marks walls

float(string name, string value) registercvar = #93;	//Creates a new cvar, ain't that handy?

//Begin value definitions (This is a dummy, only needed because world.qc comes after weapons.qc)
float player_jump_velocity;
float weapon_slot1_damage;
float weapon_slot1_alt_rof;
float weapon_slot1_alt_damage;
float weapon_slot1_rof;
float weapon_slot2_damage;
float weapon_slot2_alt_rof;
float weapon_slot2_alt_damage;
float weapon_slot3_rof;
float weapon_slot3_shots;
float weapon_slot3_alt_rof;
float weapon_slot3_alt_shots;
float weapon_slot4_damage;
float weapon_slot5_damage;
float weapon_slot5_alt_rof;
float weapon_slot5_alt_damage;
float weapon_slot6_rof;
float weapon_slot6_damage;
float weapon_slot7_rof;
float weapon_slot7_damage;
float weapon_slot8_range;
float weapon_slot8_damage;
//End value definitions

//All of this is needed for our slightly modified playermovement.qc - if you are going to 
// start a new project using custom phsyics, read the notes at the bottom of this tutorial
.float ladder_time;

.vector punchvector;
.vector movement;

float(float a, float b) min = #94;
float(float a, float b, float c) min3 = #94;
float(float a, float b, float c, float d) min4 = #94;
float(float a, float b, float c, float d, float e) min5 = #94;
float(float a, float b, float c, float d, float e, float f) min6 = #94;
float(float a, float b, float c, float d, float e, float f, float g) min7 = #94;
float(float a, float b, float c, float d, float e, float f, float g, float h) min8 = #94;
float(float a, float b) max = #95;
float(float a, float b, float c) max3 = #95;
float(float a, float b, float c, float d) max4 = #95;
float(float a, float b, float c, float d, float e) max5 = #95;
float(float a, float b, float c, float d, float e, float f) max6 = #95;
float(float a, float b, float c, float d, float e, float f, float g) max7 = #95;
float(float a, float b, float c, float d, float e, float f, float g, float h) max8 = #95;
float(float minimum, float val, float maximum) bound = #96;
//And the rest of this is needed to invoke FRIK_FILE - comments below this line by LordHavoc float(string s) stof = #81; // get numerical value from a string float(string filename, float mode) fopen = #110; // opens a file inside quake/gamedir/data/ (mode is FILE_READ, // FILE_APPEND, or FILE_WRITE), returns fhandle >= 0 if successful, // or fhandle < 0 if unable to open file for any reason void(float fhandle) fclose = #111; // closes a file string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file float(string s) strlen = #114; // returns how many characters are in a string string(string s1, string s2) strcat = #115; // concatenates two strings (for example "abc", "def" would return "abcdef") // and returns as a tempstring string(string s, float start, float length) substring = #116; // returns a section of a string as a tempstring
vector(string s) stov = #117; // returns vector value from a string string(string s) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to // keep around a tempstring for longer periods of time (tempstrings are replaced often) void(string s) strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again // or it may crash!!!) float FILE_READ = 0; float FILE_APPEND = 1; float FILE_WRITE = 2;

Close defs.qc. Open world.qc.

Go to the very beginning of worldspawn() and paste this:

	registercvar("player_jump_velocity", "270");
	registercvar("weapon_slot1_rof", "0.5");
	registercvar("weapon_slot1_damage", "50");
	registercvar("weapon_slot1_alt_rof", "1");
	registercvar("weapon_slot1_alt_damage", "100");
	registercvar("weapon_slot2_rof", "0.1");
	registercvar("weapon_slot2_damage", "10");
	registercvar("weapon_slot2_alt_rof", "1");
	registercvar("weapon_slot2_alt_damage", "120");
	registercvar("weapon_slot3_rof", "0.5");
	registercvar("weapon_slot3_shots", "7");
	registercvar("weapon_slot3_alt_rof", "1");
	registercvar("weapon_slot3_alt_shots", "14");
	registercvar("weapon_slot4_rof", "0.4");
	registercvar("weapon_slot4_damage", "10");
	registercvar("weapon_slot5_rof", "0.1");
	registercvar("weapon_slot5_damage", "20");
	registercvar("weapon_slot5_alt_rof", "0.5");
	registercvar("weapon_slot5_alt_damage", "60");
	registercvar("weapon_slot6_rof", "0.7");
	registercvar("weapon_slot6_damage", "120");
	registercvar("weapon_slot7_rof", "0.8");
	registercvar("weapon_slot7_damage", "100");
	registercvar("weapon_slot8_range", "600");
	registercvar("weapon_slot8_damage", "30");

Note that each time you call registercvar, you must provide two things:
A string to name the cvar and a float for the default value of the cvar.

Also note that Quake will not recognize the cvar until worldspawn is called, meaning
that if you are creating a server configuration cvar you are going to want to call a config
file somewhere in worldspawn. Since we are actually doing this, paste this after the call to InitBodyQue()

	localcmd("exec game.cfg\n");

//Begin value redefinitions
player_jump_velocity 	= cvar("player_jump_velocity");
weapon_slot1_rof 	= cvar("weapon_slot1_rof");
weapon_slot1_damage 	= cvar("weapon_slot1_damage");
weapon_slot1_alt_rof 	= cvar("weapon_slot1_alt_rof");
weapon_slot1_alt_damage = cvar("weapon_slot1_alt_damage");
weapon_slot2_damage 	= cvar("weapon_slot2_damage");
weapon_slot2_alt_rof 	= cvar("weapon_slot2_alt_rof");
weapon_slot2_alt_damage = cvar("weapon_slot2_alt_damage");
weapon_slot3_rof 	= cvar("weapon_slot3_rof");
weapon_slot3_shots 	= cvar("weapon_slot3_shots");
weapon_slot3_alt_rof 	= cvar("weapon_slot3_alt_rof");
weapon_slot3_alt_shots 	= cvar("weapon_slot3_alt_shots");
weapon_slot4_damage 	= cvar("weapon_slot4_damage");
weapon_slot5_damage 	= cvar("weapon_slot5_damage");
weapon_slot5_alt_rof 	= cvar("weapon_slot5_alt_rof");
weapon_slot5_alt_damage = cvar("weapon_slot5_alt_damage");
weapon_slot6_rof 	= cvar("weapon_slot6_rof");
weapon_slot6_damage 	= cvar("weapon_slot6_damage");
weapon_slot7_rof 	= cvar("weapon_slot7_rof");
weapon_slot7_damage 	= cvar("weapon_slot7_damage");
weapon_slot8_range	= cvar("weapon_slot8_range");
weapon_slot8_damage 	= cvar("weapon_slot8_damage");
//End value redefinitions

Note that we need localcmd to do this because the client hasn't spawned yet - so stuffcmd is
useless at this point and wouldn't work for *SERVER* configs anyway. Also double check your
mod dir to make sure you extracted darkplacestut.zip into it. There should be three files:
game.cfg, credits.txt and effects.pk3 (Yes, thats right - DP can load .pk3 files in place
of .paks. Not only is this more efficent but you can also name .pk3's anything you want
and DP will load them correctly. In case you have no clue on how to create pk3 files, its
just a normal .zip renamed to .pk3)

While we still have world.qc open we're going to do a little something to save time.
Paste the the following after the localcmd we just added.

	Init_Log();

Guess what that does? It starts the basic logging system we're going to implement.
Paste this above worldspawn(), now.

void (string foo) Log_It =
{
	local float file;
	file = fopen ("log.txt", FILE_APPEND);
	fputs(file, foo);
	fclose(file);
};

void () Init_Log =
{
	local string h;
	local float file;

	file = fopen ("log.txt", FILE_READ);

	if (!file < 0)
		{
		file = fopen ("log.txt", FILE_APPEND);
		fputs(file, "//Map: ");
		h = world.model;
		fputs(file, h);
		fputs(file, "\n");
		}
	else
		{
		file = fopen ("log.txt", FILE_WRITE);
		fputs(file, "//Log start\n");
		fputs(file, "//Map: ");
		h = world.model;
		fputs(file, h);
		fputs(file, "\n");
		}

	fclose(file);
};

While we are going to add logging functions to client.qc, keep in mind that you can call Log_It("Text")
pretty much anywhere and have it add to the log. Close world.qc.

Open misc.qc. Add this to the top:

void() FireAmbient;

void() flamespawn =
{
	newmis = spawn();
	setorigin(newmis, self.origin + self.dest + '0 0 -5');
	setmodel(newmis, "progs/torchflamebase.spr32");
	newmis.effects = EF_ADDITIVE;
	newmis.scale = self.scale;
	if (newmis.scale == 1)
		makestatic(newmis);
	newmis = spawn();
	setorigin(newmis, self.origin + self.dest);
	setmodel(newmis, "progs/torchflametop.spr32");
	newmis.effects = EF_ADDITIVE;
	newmis.scale = self.scale;
	if (newmis.scale == 1)
		makestatic(newmis);
	if (self.model)
		makestatic (self);
	else
		remove(self);
};

void() torchflame =
{
	FireAmbient();
	self.think = flamespawn;
	self.nextthink = time + 0.1;
};

Find light_torch_small_walltorch(), light_flame_large_yellow(), light_flame_small_yellow() and light_flame_small_white(). Replace them all with this:

void() light_torch_small_walltorch =
{
	self.dest = '0 0 16';
	torchflame();
	precache_model ("progs/flame.mdl");
	setmodel (self, "progs/flame.mdl");
};

void() light_flame_large_yellow =
{
	self.scale = 2;
	self.dest = '0 0 16';
	torchflame();
};

void() light_flame_small_yellow =
{
	self.dest = '0 0 0';
	torchflame();
};

void() light_flame_small_white =
{
	self.dest = '0 0 0';
	torchflame();
	precache_model ("progs/flame2.mdl");
	setmodel (self, "progs/flame2.mdl");
};

Open up weapons.qc. Add this to W_Precache().

	precache_sound ("weapons/pulserifle.wav");	//Some random sound you don't really need
	precache_model ("progs/x_explod.spr");		//New explosion sprite, from Half Life (eeep..)
	precache_model ("progs/smokepuff.spr");		//Smoke puff sprite, used in a few things
	precache_model ("progs/xhair.spr");		//Multi-frame sprite that has all our crosshairs
	precache_model ("progs/muzzleflash.spr");	//First person muzzle flash
	precache_model ("progs/muzzleflash2.spr");	//Third person muzzle flash
	precache_model ("progs/v_pulse.mdl");		//A pulse rifle model
	precache_model ("progs/laserdot.spr");		//Little laser dot suitable for many things
	precache_model ("progs/hud_armor.spr");		//Part of our new hud used in client.qc
	precache_model ("progs/hud_face.spr");		//
	precache_model ("progs/hud_ammo.spr");		//
	precache_model ("progs/torchflametop.spr32");	//New torch sprite provided by LordHavoc
	precache_model ("progs/torchflamebase.spr32");	//

Now add this between W_Precache() and crandom().

void()	x_explode1	=	[0,	x_explode2] {};
void()	x_explode2	=	[1,	x_explode3] {};
void()	x_explode3	=	[2,	x_explode4] {};
void()	x_explode4	=	[3,	x_explode5] {};
void()	x_explode5	=	[4,	x_explode6] {};
void()	x_explode6	=	[5,	x_explode7] {};
void()	x_explode7	=	[6,	x_explode8] {};
void()	x_explode8	=	[7,	x_explode9] {};
void()	x_explode9	=	[8,	x_explode10] {};
void()	x_explode10	=	[9,	x_explode11] {};
void()	x_explode11	=	[10,	x_explode12] {};
void()	x_explode12	=	[11,	x_explode13] {};
void()	x_explode13	=	[12,	x_explode14] {};
void()	x_explode14	=	[13,	x_explode15] {};
void()	x_explode15	=	[14,	SUB_Remove] {};

void()	smokepuff1	=	[0,	smokepuff2] {};
void()	smokepuff2	=	[1,	smokepuff3] {};
void()	smokepuff3	=	[2,	smokepuff4] {};
void()	smokepuff4	=	[3,	smokepuff5] {};
void()	smokepuff5	=	[4,	smokepuff6] {};
void()	smokepuff6	=	[5,	smokepuff7] {};
void()	smokepuff7	=	[6,	smokepuff8] {};
void()	smokepuff8	=	[7,	smokepuff9] {};
void()	smokepuff9	=	[8,	smokepuff10] {};
void()	smokepuff10	=	[9,	smokepuff11] {};
void()	smokepuff11	=	[10,	smokepuff12] {};
void()	smokepuff12	=	[11,	smokepuff13] {};
void()	smokepuff13	=	[12,	smokepuff14] {};
void()	smokepuff14	=	[13,	smokepuff15] {};
void()	smokepuff15	=	[14,	smokepuff16] {};
void()	smokepuff16	=	[15,	smokepuff17] {};
void()	smokepuff17	=	[16,	smokepuff18] {};
void()	smokepuff18	=	[17,	smokepuff19] {};
void()	smokepuff19	=	[18,	SUB_Remove] {};

void(vector org) SpawnSmoke =	//Spawns a puff of smoke
{
	newmis = spawn();
	newmis.movetype = MOVETYPE_NONE;
	newmis.classname = "smoke";
	newmis.touch = SUB_Null;
	newmis.velocity = '0 0 0';  //Note that you could mess with the movetype and give it a z velocity for rising smoke..
	newmis.effects = newmis.effects | EF_ADDITIVE;
	setmodel(newmis, "progs/smokepuff.spr");
	newmis.solid = SOLID_NOT;
	setsize(newmis, '0 0 0', '0 0 0');
	setorigin(newmis, org);
	newmis.think = smokepuff1;
	newmis.nextthink = time + 0.1;
};

void () LaserThink =		//Updates the laserdot position - this should be done with velocity
{				// instead of changing the origin, but I'm tired..
	local vector org;

        makevectors(self.owner.v_angle);
	org = self.owner.origin + v_up * 18;

        traceline (org, org + v_forward * 4096, FALSE, self);	//Should be higher

	if (self.owner.weapon != IT_ROCKET_LAUNCHER)		//Laser only works with our l'cher
		{
		self.owner.laser_active = 0;			//Tells Quake our laser is off
		remove(self);
		}

        if (trace_fraction == 1.0)
	{
		// Move sight inside player if something strange happens
        	setorigin(self, self.owner.origin );
		return;
	}

	self.angles = vectoangles(v_forward);		//Make sure the sprite looks right
	setorigin(self, trace_endpos - v_forward*4);	//Also make sure it doesn't end up in a wall
	self.nextthink = time + 0.05;
};

void ()	SpawnLaser =		//Spawns a rocket guidance laser for a player
{
	local entity foo;

        self.laser_active = 1;	//Laser is active

	foo = spawn ();
	foo.owner = self;
	foo.movetype = MOVETYPE_NOCLIP;
	foo.solid = SOLID_NOT;

	setmodel (foo, "progs/laserdot.spr");
	foo.classname = "sight";
	foo.effects = foo.effects | EF_ADDITIVE;

	setorigin( foo, self.origin );

	foo.think = LaserThink;
	foo.nextthink = time + 0.05;	//Change this as you please
};

Basicly from top to bottom you just added:
* Two sprite animations
* A function to spawn a puff of smoke somewhere - you must provide a vector
* Two more functions for our laser guided rocket

Go down to W_FireAxe() and replace it with this.

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

	makevectors (self.v_angle);
	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)
	{
		local float axedmg;
		axedmg = weapon_slot1_damage;	//Server defined damage
		trace_ent.axhitme = 1;
		if (!trace_ent.solid == SOLID_BSP)	//Fixes a minor issue where doors would 'bleed'
			te_blood(org, '0 0 0', axedmg/2);
		if (trace_ent.solid == SOLID_BSP && self.super_damage_finished)
			te_gunshotquad(org);
		else	te_gunshot(org);
		T_Damage (trace_ent, self, self, axedmg);
	}
	else
	{	// hit wall
		sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
		if (self.super_damage_finished)
			te_gunshotquad(org);
		else	te_gunshot(org);
	}
};

void() W_AltFireAxe =		//Alternate fire routine for the axe
{
	local	vector	source;
	local	vector	org;

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

	if (trace_ent.takedamage)
	{
		local float axedmg;
		axedmg = weapon_slot1_alt_damage;
		trace_ent.axhitme = 1;
		if (!trace_ent.solid == SOLID_BSP)
			te_blood(org, '0 0 0', axedmg/2);
		if (trace_ent.solid == SOLID_BSP && self.super_damage_finished)
			te_gunshotquad(org);
		else	te_gunshot(org);
		T_Damage (trace_ent, self, self, axedmg);
	}
	else
	{	// hit wall
		sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
		if (self.super_damage_finished)
			te_gunshotquad(org);
		else	te_gunshot(org);
	}
};

Hopefully you should understand what we just added. If you don't
understand it, don't panic! We just messed around with the axe firing
function to make use of some new stuff we defined in defs.qc and also
added a alternate firing function for later on.

Go down to spawn_touchblood(). Find this line:

	SpawnBlood (self.origin + vel*0.01, vel, damage);

Replace it with this:

	te_blood(self.origin + vel*0.01, vel, damage);

Go down to TraceAttack(). Replace it with this:

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)
	{
		if (!trace_ent.solid == SOLID_BSP)
			te_blood(org, vel*0.2, damage);
		if (trace_ent.solid == SOLID_BSP && self.super_damage_finished)
			te_gunshotquad(org);
		else	te_gunshot(org);
		AddMultiDamage (trace_ent, damage);
	}

	else
	{

	if (self.super_damage_finished)
		te_gunshotquad(org);
	else	te_gunshot(org);
	}
};

Go down to FireBullets(). Replace! Replace! REPLACE!

void(float shotcount, vector dir, vector spread) FireBullets =
{
	local	vector direction, src;
	local	float	damg;				//Addition

	makevectors(self.v_angle);

	src = self.origin + v_forward*10;
	src_z = self.absmin_z + self.size_z * 0.7;
	damg = weapon_slot2_damage;			//Addition

	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 (damg, direction);

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

Go down to W_FireShotgun(). Replace it with this:

void() SpawnMuzzleFlash =			//Spawns a first person muzzleflash
{
	newmis = spawn();
	newmis.movetype = MOVETYPE_NONE;
	newmis.solid = SOLID_NOT;
	newmis.viewmodelforclient = self;
	newmis.owner = self;
	newmis.classname = "flash";
	newmis.effects = newmis.effects + EF_ADDITIVE;
	setmodel(newmis, "progs/muzzleflash.spr");
	setsize(newmis, '0 0 0', '0 0 0');
	setorigin(newmis, '64 -16 -28');	//Don't worry about this showing up in weird places on higher resolutions -
						// all viewmodels are scaled to fit the res perfectly
	newmis.think = SUB_Remove;
	newmis.nextthink = time + 0.07;		//Adjust this as you wish
};

void(vector org) SpawnMuzzleFlash2 =
{
	newmis = spawn();
	newmis.movetype = MOVETYPE_NONE;
	newmis.classname = "flash";
	newmis.touch = SUB_Null;
	newmis.effects = newmis.effects + EF_ADDITIVE;
	setmodel(newmis, "progs/muzzleflash2.spr");
	newmis.solid = SOLID_NOT;
	setsize(newmis, '0 0 0', '0 0 0');
	setorigin(newmis, org);
	newmis.exteriormodeltoclient = self;
	newmis.think = SUB_Remove;
	newmis.angles = self.angles;
	newmis.nextthink = time + 0.07;
};

void() W_FireShotgun =
{
	local vector dir;

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

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

	self.punchangle_x = -2;
	
	self.currentammo = self.ammo_shells = self.ammo_shells - 1;
	dir = aim (self, 100000);
	FireBullets (3, dir, '0.1 0.1 0');
	SpawnMuzzleFlash();
	makevectors (self.angles);		//Hack, needed for 3rd person muzzleflashes to look right
	SpawnMuzzleFlash2(self.origin + v_forward*16 + v_right * 8 + '0 0 16');
};

void() AltGrenadeExplode =
{
	local float damg;

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

	damg = weapon_slot2_alt_damage;

	T_RadiusDamage (self, self.owner, damg, world);

	te_spark(self.origin, '0 0 0', 150);	//For bonus points, make the amount of sparks spawned random and give
						// them a random velocity
	te_plasmaburn(self.origin);

	self.movetype = MOVETYPE_NONE;
	sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);	//TE_EXPLOSION automagickly plays this sound
	self.velocity = '0 0 0';
	self.touch = SUB_Null;
	self.effects = self.effects - (self.effects & EF_FLAME);
	self.effects = self.effects + EF_ADDITIVE;
	setmodel (self, "progs/x_explod.spr");
	self.solid = SOLID_NOT;
	x_explode1 ();
};

void() W_AltFireShotgun =
{
	local entity missile;

	self.effects = self.effects | EF_MUZZLEFLASH;
	
	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";
		
	makevectors (self.v_angle);

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

	missile.avelocity = '0 0 0';
	missile.angles = vectoangles(missile.velocity);
	missile.touch = AltGrenadeExplode;
	missile.effects = missile.effects + EF_FLAME;
	missile.nextthink = time + 5;
	missile.think = AltGrenadeExplode;

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

Go down to W_FireSuperShotgun(). You know the drill.

void() W_FireSuperShotgun =
{
	local vector dir;
	local float shots;

	shots = weapon_slot3_shots;

	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 (shots, dir, '0.14 0.08 0');
	SpawnSmoke(self.origin + v_forward * 16 + '0 0 16');
};

void() W_AltFireSuperShotgun =
{
	local vector dir;
	local float shots;

	if (self.currentammo == 1)
	{
		W_FireSuperShotgun ();
		return;
	}

	shots = weapon_slot3_alt_shots;

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

	self.punchangle_x = -6;
	
	self.currentammo = self.ammo_shells = self.ammo_shells - 2;
	dir = aim (self, 100000);
	FireBullets (shots, dir, '0.14 0.08 0');
	SpawnSmoke(self.origin + v_forward * 16 + v_right * 4 + '0 0 16');
	SpawnSmoke(self.origin + v_forward * 16 + v_right * -4 + '0 0 16');	//I'm so clever
};

Find BecomeExplosion(), T_MissileTouch() and W_FireRocket().
Replace all three functions with this.

void() BecomeExplosion =
{
	te_spark(self.origin, '0 0 0', 150);
	te_plasmaburn(self.origin);

	self.movetype = MOVETYPE_NONE;
	sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
	self.velocity = '0 0 0';
	self.touch = SUB_Null;
	self.effects = self.effects + EF_ADDITIVE;
	setmodel (self, "progs/x_explod.spr");
	self.solid = SOLID_NOT;
	x_explode1 ();
};

void() T_MissileTouch =
{
	local float damg;

	if (other == self.owner)
		return;		// don't explode on owner, because that would suck

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

	self.glow_size = 0;	//Else our fairly long sprite animation would look silly
	damg = weapon_slot7_damage;

	if (other.health)
	{
		if (other.classname == "monster_shambler")
			damg = damg * 2;	// I'm such a radical
		T_Damage (other, self, self.owner, damg );
	}
	
	T_RadiusDamage (self, self.owner, damg, other);

	self.origin = self.origin - 8 * normalize(self.velocity);

	BecomeExplosion ();
};

entity() FindDot =
{
	local entity e, selected;

if (self.owner.laser_active == 1)
	{
         		e = find( world, classname, "sight");
			while (e)
			{
				if (e.classname == "sight" && e.owner == self.owner)
				{
				selected = e;
				return e;
				}
			e = nextent(e);
			}
	}

	return selected;
};

void() MissileThink =
{
        local vector dir, olddir;
        local float turnrate;

        turnrate = 0.1;	//Change as you wish
	if ( !(self.enemy) || (self.enemy == world) )
		self.enemy = FindDot();

	if (self.enemy != world)
	{
                olddir = normalize(self.velocity);
		dir = normalize(self.enemy.origin - self.origin);
                if (olddir_x - dir_x > turnrate)
                        dir_x = olddir_x - turnrate;
                if (olddir_x - dir_x < -1 * turnrate)
                        dir_x = olddir_x + turnrate;
                if (olddir_y - dir_y > turnrate)
                        dir_y = olddir_y - turnrate;
                if (olddir_y - dir_y < -1 * turnrate)
                        dir_y = olddir_y + turnrate;
                if (olddir_z - dir_z > turnrate)
                        dir_z = olddir_z - turnrate;
                if (olddir_z - dir_z < -1 * turnrate)
                        dir_z = olddir_z + turnrate;
                self.velocity = dir * 1500;
		self.angles = vectoangles(self.velocity);
	}

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

void() RocketFly =
{
	self.glow_size = 100;
	self.glow_color = 254;
	self.glow_trail = 1;
	self.velocity = self.velocity * 5;

	self.nextthink = time + 0.05;
	self.think = MissileThink;
};

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;
	missile.classname = "missile";
		
	makevectors (self.v_angle);
	missile.velocity = aim(self, 1000);
	missile.velocity = missile.velocity * 300;
	missile.angles = vectoangles(missile.velocity);
	
	missile.touch = T_MissileTouch;
	missile.nextthink = time + 0.5;
	missile.think = RocketFly;

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

Go down to LightningDamage() and W_FireLightning(). Replace them with this:

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)
	{
		te_blood(trace_endpos, '0 0 100', 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)
	{
		te_blood(trace_endpos, '0 0 100', 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)
	{
		te_blood(trace_endpos, '0 0 100', damage*4);
		T_Damage (trace_ent, from, from, damage);
	}
};

void() W_FireLightning =
{
	local	vector		org;
	local	float		cells;

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

	if (self.waterlevel > 1)
	{
		cells = self.ammo_cells;
		self.ammo_cells = 0;
		W_SetCurrentAmmo ();
		T_RadiusDamage (self, self, (weapon_slot8_damage +5)*cells, world);
		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*weapon_slot8_range, TRUE, self);

	te_lightning2 (self, org, trace_endpos);

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

Now find GrenadeExplode(), GrenadeTouch() and W_FireGrenade() and replace it all with this:

void() GrenadeExplode =
{
	local float damg;

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

	damg = weapon_slot6_damage;

	T_RadiusDamage (self, self.owner, damg, world);

	te_spark(self.origin, '0 0 0', 150);
	te_plasmaburn(self.origin);

	self.movetype = MOVETYPE_NONE;
	sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
	self.velocity = '0 0 0';
	self.touch = SUB_Null;
	self.effects = self.effects - (self.effects & EF_FLAME);
	self.effects = self.effects + EF_ADDITIVE;
	setmodel (self, "progs/x_explod.spr");
	self.solid = SOLID_NOT;
	x_explode1 ();
};

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

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";
		
	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;
	
	missile.nextthink = time + 2.5;
	missile.think = GrenadeExplode;
	missile.effects = missile.effects | EF_FLAME;

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

void() W_AltFireGrenade =
{
	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";
		
	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 = GrenadeExplode;
	
	missile.nextthink = time + 2.5;
	missile.think = GrenadeExplode;
	missile.effects = missile.effects | EF_FLAME;

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

Find W_FireSuperSpikes(), W_FireSpikes(), spike_touch() and superspike_touch() - replace that ENTIRE block of code with this:

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 - 1;
	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);
	newmis.velocity = dir * 2000;
	newmis.glow_color = 208;
	newmis.glow_size = 60;
};

void(float ox) W_FireSpikes =
{
	local vector dir, foo;
	local float foo2;
	
	makevectors (self.v_angle);
	
	if (self.ammo_nails > 0 && self.weapon == IT_SUPER_NAILGUN)
	{
		W_FireSuperSpikes ();
		return;
	}

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

	foo = getlight(self.origin);	//getlight returns the RGB value of light at a point as a vector - 'R G B'
	foo2 = (foo_x + foo_y + foo_z) * 0.333;

	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;
	newmis.think = SUB_Remove;
	newmis.nextthink = time + 5;
	if (foo2 > 50)				//Mess with this
		newmis.velocity = dir * 2000;
	else if (foo2 > 100)
		newmis.velocity = dir * 1500 + v_up * (crandom() * 25) + v_right * (crandom() * 25);
	else if (foo2 > 150)
		newmis.velocity = dir * 1000 + v_up * (crandom() * 50) + v_right * (crandom() * 50);
	else	newmis.velocity = dir * 500 + v_up * (crandom() * 100) + v_right * (crandom() * 100);
};

void() plasma_explode =
{
	local float	damg;
	local vector org;

	if (other == self.owner)
		return;

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

	damg = weapon_slot5_alt_damage;

	if (other.health)
	{
		T_Damage (other, self, self.owner, damg );
	}
	
	T_RadiusDamage (self, self.owner, damg, other);

	te_explosionrgb (self.origin, '0 0 0.9');	//RGB on a 0-255 scale converted to a 0-1 scale
	remove(self);
};

void() W_AltFireSuperSpikes =
{
	local vector	dir;
	local entity	old;
	
	sound (self, CHAN_WEAPON, "weapons/lstart.wav", 1, ATTN_NORM);
	self.effects = self.effects | EF_MUZZLEFLASH;
	self.ammo_cells = self.ammo_cells - 1;
	dir = aim (self, 1000);
	launch_spike (self.origin + '0 0 16', dir);
	newmis.touch = plasma_explode;
	setmodel (newmis, "progs/s_spike.mdl");
	setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);		
	newmis.velocity = dir * 1500;
	newmis.glow_color = 208;
	newmis.glow_size = 240;
};

.float hit_z;		//I'm the one writing this tut and I STILL don't know what this does..
void() spike_touch =
{
local float rand, damg;
	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)
	{
		if (!trace_ent.solid == SOLID_BSP)
			spawn_touchblood (damg);
		if (trace_ent.solid == SOLID_BSP && self.super_damage_finished)
			te_gunshotquad(self.origin);
		else	te_gunshot(self.origin);
		T_Damage (other, self, self.owner, weapon_slot4_damage);
	}
	else
	{
		if (self.owner.super_damage_finished)
			te_spikequad(self.origin);
		else	te_spike(self.origin);
	}
	remove(self);
};

void() superspike_touch =
{
local float rand, damg;
	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.health)
		T_Damage (other, self, self.owner, weapon_slot5_damage);

	T_RadiusDamage (self, self.owner, weapon_slot5_damage, other);

	te_plasmaburn(self.origin);

	remove(self);
};

Quit groaning, we're almost done with weapons.qc.

Go down to W_SetCurrentAmmo(). Find this line of code:

	self.weaponmodel = "progs/v_shot.mdl";

And replace it with this:

	self.weaponmodel = "progs/v_pulse.mdl";

Add these lines above W_Attack():

void()	player_altplasma1;
void()	player_aaxe1;
void()	player_aaxeb1;
void()	player_aaxec1;
void()	player_aaxed1;
void()	player_pulse1;
void()	player_pulsegren1;

And replace W_Attack() with this:

void() W_Attack =
{
	local	float	r;

	if (!W_CheckNoAmmo ())
		return;

	makevectors	(self.v_angle);
	self.show_hostile = time + 1;

	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 + weapon_slot1_rof;
	}
	else if (self.weapon == IT_SHOTGUN)
	{
		player_pulse1 ();
	}
	else if (self.weapon == IT_SUPER_SHOTGUN)
	{
		player_shot1 ();
		W_FireSuperShotgun ();
		self.attack_finished = time + weapon_slot3_rof;
	}
	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 + weapon_slot6_rof;
	}
	else if (self.weapon == IT_ROCKET_LAUNCHER)
	{
		player_rocket1();
		W_FireRocket();
		self.attack_finished = time + weapon_slot7_rof;
	}
	else if (self.weapon == IT_LIGHTNING)
	{
		player_light1();
		self.attack_finished = time + 0.1;
		sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
	}
};

void() W_AltAttack =
{
	local float r;

	if (!W_CheckNoAmmo ())
		return;

	makevectors	(self.v_angle);
	self.show_hostile = time + 1;

	if (self.weapon == IT_AXE)
	{
		sound (self, CHAN_WEAPON, "weapons/ax1.wav", 1, ATTN_NORM);
		r = random();
		if (r < 0.25)
			player_aaxe1 ();
		else if (r<0.5)
			player_aaxeb1 ();
		else if (r<0.75)
			player_aaxec1 ();
		else
			player_aaxed1 ();
		self.attack_finished = time + weapon_slot1_alt_rof;
	}
	else if (self.weapon == IT_SHOTGUN && self.ammo_rockets > 0)
	{
		player_pulsegren1();
		W_AltFireShotgun();
		self.attack_finished = time + weapon_slot1_alt_rof;
	}
	else if (self.weapon == IT_SUPER_SHOTGUN)
	{
		player_shot1 ();
		W_AltFireSuperShotgun ();
		self.attack_finished = time + weapon_slot3_alt_rof;
	}
	else if (self.weapon == IT_NAILGUN)
	{
		player_nail1 ();
	}
	else if (self.weapon == IT_SUPER_NAILGUN && self.ammo_cells > 0)
	{
		player_altplasma1();
		W_AltFireSuperSpikes();
		self.attack_finished = time + weapon_slot5_alt_rof;
	}
	else if (self.weapon == IT_GRENADE_LAUNCHER)
	{
		player_rocket1();
		W_AltFireGrenade();
		self.attack_finished = time + weapon_slot6_rof;
	}
	else if (self.weapon == IT_ROCKET_LAUNCHER)
	{
		self.attack_finished = time + weapon_slot7_rof;	//Don't ask
		if (self.laser_active != 1)
		{
			SpawnLaser();
		}
		else
		{
			self.laser_active = 0;
			// Find the sight entity and remove it
			local entity e;
         		e = find( world, classname, "sight");
			while (e)
			{
				if (e.classname == "sight" && e.owner == self)
				{
					remove(e);
					return;
				}
				e = nextent(e);
			}
		}
	}
	else if (self.weapon == IT_LIGHTNING)
	{
		player_light1();
		self.attack_finished = time + 0.1;
		sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
	}
};

Go down to W_WeaponFrame(). Replace it with this:

void() W_WeaponFrame =
{
	if (time < self.attack_finished)
		return;

	if (self.impulse)			//Fixing id's bugs!
		ImpulseCommands ();
	
// check for attack
	if (self.button0)
	{
		SuperDamageSound ();
		W_Attack ();
	}

	if (self.button3)
	{
		SuperDamageSound ();
		W_AltAttack ();
	}
};

Relax now, we're done with weapons.qc. Open player.qc.

What we're going to do here:
* Add animations
* Add some other stuff

Go down to player_nail1 & 2 and player_light1 & 2. Find the line that reads:

	if (!self.button0)

And replace it with this:

	if (!self.button0 && !self.button3)

Do me a favor, add these animation sequences to the bottom of player.qc now.

void() player_pulse1   =[$nailatt1, player_pulse2  ] 
{
	self.effects = self.effects | EF_MUZZLEFLASH;

	if (!self.button0)
		{player_run ();return;}
	self.weaponframe = self.weaponframe + 1;
	if (self.weaponframe == 10)
		self.weaponframe = 1;
	SuperDamageSound();
	W_FireShotgun ();
	self.attack_finished = time + 0.2;
};
void() player_pulse2   =[$nailatt2, player_pulse1  ]
{
	self.effects = self.effects | EF_MUZZLEFLASH;

	if (!self.button0)
		{player_run ();return;}
	self.weaponframe = self.weaponframe + 1;
	if (self.weaponframe == 10)
		self.weaponframe = 1;
	SuperDamageSound();
	W_FireShotgun ();
	self.attack_finished = time + 0.2;
};

void()	player_aaxe1 =	[$axatt1, player_aaxe2	] {self.weaponframe=1;};
void()	player_aaxe2 =	[$axatt2, player_aaxe3	] {self.weaponframe=2;};
void()	player_aaxe3 =	[$axatt3, player_aaxe4	] {self.weaponframe=3;W_AltFireAxe();};
void()	player_aaxe4 =	[$axatt4, player_run	] {self.weaponframe=4;};

void()	player_aaxeb1 =	[$axattb1, player_aaxeb2] {self.weaponframe=5;};
void()	player_aaxeb2 =	[$axattb2, player_aaxeb3] {self.weaponframe=6;};
void()	player_aaxeb3 =	[$axattb3, player_aaxeb4] {self.weaponframe=7;W_AltFireAxe();};
void()	player_aaxeb4 =	[$axattb4, player_run	] {self.weaponframe=8;};

void()	player_aaxec1 =	[$axattc1, player_aaxec2] {self.weaponframe=1;};
void()	player_aaxec2 =	[$axattc2, player_aaxec3] {self.weaponframe=2;};
void()	player_aaxec3 =	[$axattc3, player_aaxec4] {self.weaponframe=3;W_AltFireAxe();};
void()	player_aaxec4 =	[$axattc4, player_run	] {self.weaponframe=4;};

void()	player_aaxed1 =	[$axattd1, player_aaxed2] {self.weaponframe=5;};
void()	player_aaxed2 =	[$axattd2, player_aaxed3] {self.weaponframe=6;};
void()	player_aaxed3 =	[$axattd3, player_aaxed4] {self.weaponframe=7;W_AltFireAxe();};
void()	player_aaxed4 =	[$axattd4, player_run	] {self.weaponframe=8;};

void() player_pulsegren1   =[$rockatt1, player_pulsegren2  ] {self.weaponframe=11;};
void() player_pulsegren2   =[$rockatt2, player_pulsegren3  ] {self.weaponframe=12;};
void() player_pulsegren3   =[$rockatt3, player_pulsegren4  ] {self.weaponframe=13;};
void() player_pulsegren4   =[$rockatt4, player_pulsegren5  ] {self.weaponframe=14;};
void() player_pulsegren5   =[$rockatt5, player_pulsegren6  ] {self.weaponframe=0;};
void() player_pulsegren6   =[$rockatt6, player_run  ] {self.weaponframe=0;};

void() player_altplasma1   =[$rockatt1, player_altplasma2  ] {self.weaponframe=9;};
void() player_altplasma2   =[$rockatt2, player_altplasma3  ] {self.weaponframe=10;};
void() player_altplasma3   =[$rockatt3, player_altplasma4  ] {self.weaponframe=11;};
void() player_altplasma4   =[$rockatt4, player_altplasma5  ] {self.weaponframe=0;};
void() player_altplasma5   =[$rockatt5, player_altplasma6  ] {self.weaponframe=0;};
void() player_altplasma6   =[$rockatt6, player_run  ] {self.weaponframe=0;};

Close player.qc. Open client.qc. Add this above PutClientInServer():

void() xhair_think =
{
if (self.owner.health <= 0)
	remove(self);
if (self.owner.weapon == IT_AXE)
	self.frame = 0;
if (self.owner.weapon == IT_SHOTGUN)
	self.frame = 1;
if (self.owner.weapon == IT_SUPER_SHOTGUN)
	self.frame = 2;
if (self.owner.weapon == IT_NAILGUN)
	self.frame = 2;
if (self.owner.weapon == IT_SUPER_NAILGUN)
	self.frame = 3;
if (self.owner.weapon == IT_GRENADE_LAUNCHER)
	self.frame = 1;
if (self.owner.weapon == IT_ROCKET_LAUNCHER)
	self.frame = 2;
if (self.owner.weapon == IT_LIGHTNING)
	self.frame = 2;

self.nextthink = 0.1;
};

void() hud_face_think =
{
if (self.owner.health <= 0)
	remove(self);
if (self.owner.health > 79)
	self.frame = 0;
else if (self.owner.health > 59)
	self.frame = 1;
else if (self.owner.health > 39)
	self.frame = 2;
else if (self.owner.health > 19)
	self.frame = 3;
else	self.frame = 4;

self.nextthink = 0.1;
};

void() hud_armor_think =
{
if (self.owner.health <= 0)
	remove(self);
if (self.owner.armortype == 0) //None
	self.frame = 3;
else if (self.owner.armortype == 0.3) //Green
	self.frame = 0;
else if (self.owner.armortype == 0.6) //Yellow
	self.frame = 1;
else if (self.owner.armortype == 0.8) //Red
	self.frame = 2;

self.nextthink = 0.1;
};

void() hud_ammo_think =
{
if (self.owner.health <= 0)
	remove(self);
if (self.owner.weapon == IT_SHOTGUN)
	{
	if (self.owner.currentammo < 25 && self.owner.currentammo != 0)
		self.frame = 4;
	else if (self.owner.currentammo == 0)
		self.frame = 9;
	else self.frame = 0;
	}
else if (self.owner.weapon == IT_SUPER_SHOTGUN)
	{
	if (self.owner.currentammo < 10 && self.owner.currentammo != 0)
		self.frame = 4;
	else if (self.owner.currentammo == 0)
		self.frame = 9;
	else self.frame = 0;
	}
else if (self.owner.weapon == IT_NAILGUN)
	{
	if (self.owner.currentammo < 40 && self.owner.currentammo != 0)
		self.frame = 5;
	else if (self.owner.currentammo == 0)
		self.frame = 10;
	else self.frame = 1;
	}
else if (self.owner.weapon == IT_SUPER_NAILGUN)
	{
	if (self.owner.currentammo < 40 && self.owner.currentammo != 0)
		self.frame = 5;
	else if (self.owner.currentammo == 0)
		self.frame = 10;
	else self.frame = 1;
	}
else if (self.owner.weapon == IT_GRENADE_LAUNCHER)
	{
	if (self.owner.currentammo < 20 && self.owner.currentammo != 0)
		self.frame = 6;
	else if (self.owner.currentammo == 0)
		self.frame = 11;
	else self.frame = 2;
	}
else if (self.owner.weapon == IT_ROCKET_LAUNCHER)
	{
	if (self.owner.currentammo < 5 && self.owner.currentammo != 0)
		self.frame = 6;
	else if (self.owner.currentammo == 0)
		self.frame = 11;
	else self.frame = 2;
	}
else if (self.owner.weapon == IT_LIGHTNING)
	{
	if (self.owner.currentammo < 12 && self.owner.currentammo != 0)
		self.frame = 7;
	else if (self.owner.currentammo == 0)
		self.frame = 12;
	else self.frame = 3;
	}
else	self.frame = 8;	//Axe

self.nextthink = 0.1;
};

void() hud_altammo_think =
{
if (self.owner.health <= 0)
	remove(self);
if (self.owner.weapon == IT_SHOTGUN)
	{
	if (self.owner.ammo_rockets < 5 && self.owner.ammo_rockets != 0)
		self.frame = 6;
	else if (self.owner.ammo_rockets == 0)
		self.frame = 11;
	else self.frame = 2;
	}
else if (self.owner.weapon == IT_SUPER_NAILGUN)
	{
	if (self.owner.ammo_cells < 10 && self.owner.ammo_cells != 0)
		self.frame = 7;
	else if (self.owner.ammo_cells == 0)
		self.frame = 12;
	else self.frame = 3;
	}
else	self.frame = 8;	//Alt uses same ammo

self.nextthink = 0.1;
};

void(entity foo) SpawnHUD_Face =
{
	newmis = spawn();
	newmis.owner = foo;
	newmis.movetype = MOVETYPE_NONE;
	newmis.solid = SOLID_NOT;
	newmis.classname = "hud";
	setmodel(newmis, "progs/hud_face.spr");
	newmis.viewmodelforclient = foo;
	newmis.think = hud_face_think;
	newmis.nextthink = 0.1;
	setorigin(newmis, '64 60 -28');
};

void(entity foo) SpawnHUD_Armor =
{
	newmis = spawn();
	newmis.owner = foo;
	newmis.movetype = MOVETYPE_NONE;
	newmis.solid = SOLID_NOT;
	newmis.classname = "hud";
	setmodel(newmis, "progs/hud_armor.spr");
	newmis.viewmodelforclient = foo;
	newmis.think = hud_armor_think;
	newmis.nextthink = 0.1;
	setorigin(newmis, '64 60 -20');
};

void(entity foo) SpawnHUD_Ammo =
{
	newmis = spawn();
	newmis.owner = foo;
	newmis.movetype = MOVETYPE_NONE;
	newmis.solid = SOLID_NOT;
	newmis.classname = "hud";
	setmodel(newmis, "progs/hud_ammo.spr");
	newmis.viewmodelforclient = foo;
	newmis.think = hud_ammo_think;
	newmis.nextthink = 0.1;
	setorigin(newmis, '64 60 -36');
};

void(entity foo) SpawnHUD_AltAmmo =
{
	newmis = spawn();
	newmis.owner = foo;
	newmis.movetype = MOVETYPE_NONE;
	newmis.solid = SOLID_NOT;
	newmis.classname = "hud";
	setmodel(newmis, "progs/hud_ammo.spr");
	newmis.viewmodelforclient = foo;
	newmis.think = hud_altammo_think;
	newmis.nextthink = 0.1;
	setorigin(newmis, '64 60 -44');
};

void(entity foo) SpawnCrosshair =
{
	newmis = spawn();
	newmis.owner = foo;
	newmis.movetype = MOVETYPE_NONE;
	newmis.solid = SOLID_NOT;
	newmis.classname = "hud";
	newmis.effects = newmis.effects | EF_ADDITIVE;
	setmodel(newmis, "progs/xhair.spr");
	newmis.viewmodelforclient = foo;
	newmis.think = xhair_think;
	newmis.nextthink = 0.1;
	setorigin(newmis, '128 0 0');
};

Add this to the bottom of PutClientInServer():

	SpawnCrosshair(self);
	SpawnHUD_Face(self);
	SpawnHUD_Armor(self);
	SpawnHUD_Ammo(self);
	SpawnHUD_AltAmmo(self);

Go down to PlayerDeathThink(). Change the line that reads:

	if (!self.button2 && !self.button1 && !self.button0)

To

	if (!self.button3 && !self.button2 && !self.button1 && !self.button0)

Find these lines:

	self.button2 = 0;
	respawn();

In between them, add a new line stating self.button3 = 0; so it should look like this:

	self.button2 = 0;
	self.button3 = 0;
	respawn();

Go down to PlayerJump(). Change the line that reads:

	self.velocity_z = self.velocity_z + 270;

To

	self.velocity_z = self.velocity_z + player_jump_velocity;

Add this to PlayerPreThink() after the call for WaterMove().

	if (cvar("crosshair"))	//Remove this line if doing a multiplayer mod
		stuffcmd (self, "crosshair 0\n");
	if (cvar("r_lerpsprite"))	//Remove this line if doing a multiplayer mod
		stuffcmd (self, "r_lerpsprite 0\n");
	if (cvar("viewsize") != 120)	//Remove this line if doing a multiplayer mod
		stuffcmd (self, "viewsize 120\n");

And finally, replace ClientObituary() with this:

void(entity targ, entity attacker) ClientObituary =
{
	local	float rnum;
	local	string deathstring, deathstring2;
	rnum = random();

	if ((targ.flags & FL_MONSTER) && attacker.classname == "player")
	{
		Log_It (attacker.netname);

		if (targ.classname == "monster_army")
			Log_It (" retired a Grunt\n");
		if (targ.classname == "monster_demon1")
			Log_It (" exorcised a Fiend\n");
		if (targ.classname == "monster_dog")
			Log_It (" taught a Rottweiler to play dead\n");
		if (targ.classname == "monster_enforcer")
			Log_It (" terminated an Enforcer\n");
		if (targ.classname == "monster_fish")
			Log_It (" made Rotfish sushi\n");
		if (targ.classname == "monster_hell_knight")
			Log_It (" slain a Death Knight\n");
		if (targ.classname == "monster_knight")
			Log_It (" fragged a Knight\n");
		if (targ.classname == "monster_ogre")
			Log_It (" destroyed an Ogre\n");
		if (targ.classname == "monster_shalrath")
			Log_It (" exorcised a Vore\n");
		if (targ.classname == "monster_shambler")
			Log_It (" smashed a Shambler\n");
		if (targ.classname == "monster_tarbaby")
			Log_It (" splattered a Spawn\n");
		if (targ.classname == "monster_wizard")
			Log_It (" shot down a Scrag\n");
		if (targ.classname == "monster_zombie")
			Log_It (" is one step closer to being a full-time Zombie hunter\n");
	}

	if (targ.classname == "player")
	{
		if (attacker.classname == "teledeath")
		{
			bprint (targ.netname);
			bprint (" was telefragged by ");
			bprint (attacker.owner.netname);
			bprint ("\n");
			Log_It (targ.netname);
			Log_It (" was telefragged by ");
			Log_It (attacker.owner.netname);
			Log_It ("\n");

			attacker.owner.frags = attacker.owner.frags + 1;
			return;
		}

		if (attacker.classname == "teledeath2")
		{
			bprint ("Satan's power deflects ");
			bprint (targ.netname);
			bprint ("'s telefrag\n");
			Log_It ("Satan's power deflects ");
			Log_It (targ.netname);
			Log_It ("'s telefrag\n");

			targ.frags = targ.frags - 1;
			return;
		}

		if (attacker.classname == "player")
		{
			if (targ == attacker)
			{
				// killed self
				attacker.frags = attacker.frags - 1;
				bprint (targ.netname);
				Log_It (targ.netname);
				
				if (targ.weapon == 64 && targ.waterlevel > 1)
				{
					bprint (" discharges into the water.\n");
					Log_It (" discharges into the water.\n");
					return;
				}
				if (targ.weapon == IT_GRENADE_LAUNCHER)
					{
					bprint (" tries to put the pin back in\n");
					Log_It (" tries to put the pin back in\n");
					}
				else
					{
					bprint (" becomes bored with life\n");
					Log_It (" becomes bored with life\n");
					}
				return;
			}
			else if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) )
			{
				if (rnum < 0.25)
					deathstring = " mows down a teammate\n";
				else if (rnum < 0.50)
					deathstring = " checks his glasses\n";
				else if (rnum < 0.75)
					deathstring = " gets a frag for the other team\n";
				else
					deathstring = " loses another friend\n";
				bprint (attacker.netname);
				bprint (deathstring);
				Log_It (attacker.netname);
				Log_It (deathstring);
				attacker.frags = attacker.frags - 1;
				return;
			}
			else
			{
				attacker.frags = attacker.frags + 1;

				rnum = attacker.weapon;
				if (rnum == IT_AXE)
				{
					deathstring = " was ax-murdered by ";
					deathstring2 = "\n";
				}
				if (rnum == IT_SHOTGUN)
				{
					deathstring = " chewed on ";
					deathstring2 = "'s boomstick\n";
				}
				if (rnum == IT_SUPER_SHOTGUN)
				{
					deathstring = " ate 2 loads of ";
					deathstring2 = "'s buckshot\n";
				}
				if (rnum == IT_NAILGUN)
				{
					deathstring = " was nailed by ";
					deathstring2 = "\n";
				}
				if (rnum == IT_SUPER_NAILGUN)
				{
					deathstring = " was punctured by ";
					deathstring2 = "\n";
				}
				if (rnum == IT_GRENADE_LAUNCHER)
				{
					deathstring = " eats ";
					deathstring2 = "'s pineapple\n";
					if (targ.health < -40)
					{
						deathstring = " was gibbed by ";
						deathstring2 = "'s grenade\n";
					}
				}
				if (rnum == IT_ROCKET_LAUNCHER)
				{
					deathstring = " rides ";
					deathstring2 = "'s rocket\n";
					if (targ.health < -40)
					{
						deathstring = " was gibbed by ";
						deathstring2 = "'s rocket\n" ;
					}
				}
				if (rnum == IT_LIGHTNING)
				{
					deathstring = " accepts ";
					if (attacker.waterlevel > 1)
						deathstring2 = "'s discharge\n";
					else
						deathstring2 = "'s shaft\n";
				}
				bprint (targ.netname);
				bprint (deathstring);
				bprint (attacker.netname);
				bprint (deathstring2);
				Log_It (targ.netname);
				Log_It (deathstring);
				Log_It (attacker.netname);
				Log_It (deathstring2);
			}
			return;
		}
		else
		{
			targ.frags = targ.frags - 1;
			bprint (targ.netname);
			Log_It (targ.netname);

			// killed by a montser?
			if (attacker.flags & FL_MONSTER)
			{
				if (attacker.classname == "monster_army")
					{
					bprint (" was shot by a Grunt\n");
					Log_It (" was shot by a Grunt\n");
					}
				if (attacker.classname == "monster_demon1")
					{
					bprint (" was eviscerated by a Fiend\n");
					Log_It (" was eviscerated by a Fiend\n");
					}
				if (attacker.classname == "monster_dog")
					{
					bprint (" was mauled by a Rottweiler\n");
					Log_It (" was mauled by a Rottweiler\n");
					}
				if (attacker.classname == "monster_enforcer")
					{
					bprint (" was blasted by an Enforcer\n");
					Log_It (" was blasted by an Enforcer\n");
					}
				if (attacker.classname == "monster_fish")
					{
					bprint (" was fed to the Rotfish\n");
					Log_It (" was fed to the Rotfish\n");
					}
				if (attacker.classname == "monster_hell_knight")
					{
					bprint (" was slain by a Death Knight\n");
					Log_It (" was slain by a Death Knight\n");
					}
				if (attacker.classname == "monster_knight")
					{
					bprint (" was slashed by a Knight\n");
					Log_It (" was slashed by a Knight\n");
					}
				if (attacker.classname == "monster_ogre")
					{
					bprint (" was destroyed by an Ogre\n");
					Log_It (" was destroyed by an Ogre\n");
					}
				if (attacker.classname == "monster_oldone")
					{
					bprint (" became one with Shub-Niggurath\n");
					Log_It (" became one with Shub-Niggurath\n");
					}
				if (attacker.classname == "monster_shalrath")
					{
					bprint (" was exploded by a Vore\n");
					Log_It (" was exploded by a Vore\n");
					}
				if (attacker.classname == "monster_shambler")
					{
					bprint (" was smashed by a Shambler\n");
					Log_It (" was smashed by a Shambler\n");
					}
				if (attacker.classname == "monster_tarbaby")
					{
					bprint (" was slimed by a Spawn\n");
					Log_It (" was slimed by a Spawn\n");
					}
				if (attacker.classname == "monster_wizard")
					{
					bprint (" was scragged by a Scrag\n");
					Log_It (" was scragged by a Scrag\n");
					}
				if (attacker.classname == "monster_zombie")
					{
					bprint (" joins the Zombies\n");
					Log_It (" joins the Zombies\n");
					}
				return;
			}

			// tricks and traps
			if (attacker.classname == "explo_box")
			{
				bprint (" blew up\n");
				Log_It (" blew up\n");
				return;
			}
			if (attacker.solid == SOLID_BSP && attacker != world)
			{	
				bprint (" was squished\n");
				Log_It (" was squished\n");
				return;
			}
			if (attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter")
			{
				bprint (" was spiked\n");
				Log_It (" was spiked\n");
				return;
			}
			if (attacker.classname == "fireball")
			{
				bprint (" ate a lavaball\n");
				Log_It (" ate a lavaball\n");
				return;
			}
			if (attacker.classname == "trigger_changelevel")
			{
				bprint (" tried to leave\n");
				Log_It (" tried to leave\n");
				return;
			}

			// in-water deaths
			rnum = targ.watertype;
			if (rnum == -3)
			{
				if (random() < 0.5)
					{
					bprint (" sleeps with the fishes\n");
					Log_It (" sleeps with the fishes\n");
					}
				else
					{
					bprint (" sucks it down\n");
					Log_It (" sucks it down\n");
					}
				return;
			}
			else if (rnum == -4)
			{
				if (random() < 0.5)
					{
					bprint (" gulped a load of slime\n");
					Log_It (" gulped a load of slime\n");
					}
				else
					{
					bprint (" can't exist on slime alone\n");
					Log_It (" can't exist on slime alone\n");
					}
				return;
			}
			else if (rnum == -5)
			{
				if (targ.health < -15)
				{
					bprint (" burst into flames\n");
					Log_It (" burst into flames\n");
					return;
				}
				if (random() < 0.5)
					{
					bprint (" turned into hot slag\n");
					Log_It (" turned into hot slag\n");
					}
				else
					{
					bprint (" visits the Volcano God\n");
					Log_It (" visits the Volcano God\n");
					}
				return;
			}

			// fell to their death?
			if (targ.deathtype == "falling")
			{
				targ.deathtype = "";
				bprint (" fell to his death\n");
				Log_It (" fell to his death\n");
				return;
			}

			// He's dead, Jim.
			bprint (" died\n");
			Log_It (" died\n");
		}
	}
};

Close client.qc. Thats it for this tutorial, compile and run.
Notes: If you're still scratching your head, bind +button3 to a key to use alternate fire.

       Please note that there is currently an issue with .viewmodeltoclient - .viewmodels can currently 
       clip against the world, so please take this into account when you use it.

       I do know a lot of stuff in this tutorial could be done better, such as the RPG laser.
       If you're feeling bored you could improve these yourself (Such as instead of changing the RPG laser's
       origin you could give it a point to move to and move it with velocity..) but keep in mind the focus of
       this tutorial is to experiment with DarkPlaces. On that note, PLEASE READ DPEXTENSIONS.QC AS MANY TIMES
       AS YOUR MIND CAN HANDLE! You will learn so much more about DarkPlaces features if you experiment with
       them yourself, trust me.

       Please note that by the time this tut was finished, a newer version of playermovement.qc was released.
       Rather then get into an endless cycle of updating the tut to fit every new version I have decided to 
       simply release it as is. Please, if you are going to use playermovement.qc get the latest version of
       DarkPlaces (Which includes dpextensions.qc, which is needed to use playermovement.qc without changes)
       and DPMod (Which contains the latest playermovement.qc in the source) at LordHavoc's site