Inside3D tutorials.
Created By: Anarchy
eMail: anarchy@gamesnet.net
Difficulty Scale: Medium +


Intruduction
From what I've seen of the weapon tutorials here on Inside 3D, is, that they always deal with changing existing weapon code, and never adding new weapons. This tutorial will make a new sniper rifle, which I believe requires enough changes to warrent a new set of code. Also note that the goal of this tutorial is to teach you the steps of adding new weapons, and not to make a realistic sniper rifle, so keep that in mind as you read. Enjoy


Step 1 - defs.qc
Each weapon in Quake is defined in defs.qc using a IT_WeaponName format. IT_SHOTGUN, IT_NAILGUN, etc. Now find the section of code that says:

// items
float	IT_AXE					= 4096;
float	IT_SHOTGUN				= 1;
float	IT_SUPER_SHOTGUN			= 2;
float	IT_NAILGUN				= 4;
float	IT_SUPER_NAILGUN			= 8;
float	IT_GRENADE_LAUNCHER		= 16;
float	IT_ROCKET_LAUNCHER	 	= 32;
float	IT_LIGHTNING			 	= 64;
float	IT_EXTRA_WEAPON			= 128;
Now, as you see, each item is assigned a number. Now although Romero was kind enough to leave the number 128 open, we are going to add a totally diffrent one. So, as tempting as IT_EXTRA_WEAPON may be, leave it alone. Now, it may seem that you need to assign numbers that are a power of 2, well this is not true, Carmack only did so because he wanted to the corresponding bitfields to be the same, since we dont want any bit fields, well just add an odd number.
// items
float	IT_AXE					= 4096;
float	IT_SHOTGUN				= 1;
float	IT_SUPER_SHOTGUN			= 2;
float	IT_NAILGUN				= 4;
float	IT_SUPER_NAILGUN			= 8;
float	IT_GRENADE_LAUNCHER		= 16;
float	IT_ROCKET_LAUNCHER	 	= 32;
float	IT_LIGHTNING			 	= 64;
float	IT_EXTRA_WEAPON			= 128;
float	IT_SNIPER				= 17; // Declaration in place!


Step 2 - weapons.qc
This is probobly the biggest section, so pay attention. First off, we want to write a function that will be called when the weapon is fired. When you stop to think about it, this sniper rifle is going to be similar to the shotgun, just more conecentrated, so we can use some of its code. Copy the W_FireShotgun code, and paste it under W_FireSuperShotgun(). Now change the name to W_FireSniper. Change the concentration of the bullets as indicated in the comments. You should have something like this:

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

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

	self.punchangle_x = -2;
	
	self.currentammo = self.ammo_shells = self.ammo_shells - 1;
	dir = aim (self, 100000);
	FireBullets (50, dir, '0.01 0.01 0'); //This is the line to change
};
Ok, we're halfway done with this step. Now we need to tell Quake what to make the weapon look like. Find the section of code that looks like this in W_SetCurrentAmmo:
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;
	}
Now, copy the shotgun part of this, and paste it under the shotgun part, and make the modifications in the comments. Now it should look like this:
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_SNIPER) //Change shotgun to sniper
	{
		self.currentammo = self.ammo_shells;
		self.weaponmodel = "progs/v_shot.mdl";
		self.weaponframe = 0;
		self.items = self.items | IT_SHELLS; //Ok rest is fine
	}
Ok, now we need to find the section of code that tells Quake what to call when the weapon is fired. Look for thisin the W_Attack() function:
	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;
	}
Since we're in the habit of copying code to make life easier, copy the Rocket Launcher code, and paste it below the RocketLauncher part, as usual, make the changes in comments, you should get something like this:
	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_SNIPER) //Change to sniper
	{
		player_rocket1();
                	W_FireSniper(); //The function we just 'wrote'
		self.attack_finished = time + 5; //5 seconds sounds fair
	}
Now scroll down to the next function. This is the change weapon function which tells Quake to chnage to a new weapon. Find the W_ChangeWeapon() function, the beggining should look like this:
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;
	}
OK, to add to the fun, we are going to make the rifle selectable by pressing the number 2, twice, so add this code:
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)
	{
		if(self.weapon == IT_SHOTGUN)
		{		
			fl = IT_SNIPER;
			if (self.ammo_shells < 1)
			am = 1;
		}
		else 
		{
		fl = IT_SHOTGUN;
		if (self.ammo_shells < 1)
		am = 1;
	}
Now,. since you probobly wont want to make a level with your new entity, lets add it to the cheat code section, so you can play with it right away :) Find CheatCommand() and make these changes:
self.items = self.items | 
		IT_AXE |
		IT_SHOTGUN |
		IT_SUPER_SHOTGUN |
		IT_NAILGUN |
		IT_SUPER_NAILGUN |
		IT_SNIPER |
		IT_GRENADE_LAUNCHER |
		IT_ROCKET_LAUNCHER |
		IT_KEY1 | IT_KEY2;


Step 3 - items.qc
All we do in this step is make sure you get the sniper rifle whenever you have 100 shells, and you pick up the super shotgun. Do a search for the Weapon_Touch function. In that function find:

else if (self.classname == "weapon_supershotgun")
	{
		if (leave && (other.items & IT_SUPER_SHOTGUN) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT_SUPER_SHOTGUN;
		other.ammo_shells = other.ammo_shells + 5;
	}
Now, add the sniper code as shown below:
else if (self.classname == "weapon_supershotgun")
	{
		if(other.shells = 100 && (other.items & IT_SUPER_SHOTGUN))
		{
		new = IT_SNIPER;
		other.ammo_shells = other.ammo_shells + 20;
		}
		if (leave && (other.items & IT_SUPER_SHOTGUN) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT_SUPER_SHOTGUN;
		other.ammo_shells = other.ammo_shells + 5;
	}


Step 4
OK, compile and run it, thanks for spending time reading my tutorial, please send feedback to anarchy@gamesnet.net, maybe I'll write another one..... ( Do it! Give him feedback! -Ed ;)