[C&P Tutorial] Weapon System Overhaul

Need help with a tutorial found on InsideQC.com? Post here.
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

[C&P Tutorial] Weapon System Overhaul

Post by Dr. Shadowborg »

QC TUTORIAL - Weapon System Overhaul

Okay, what we are going to be doing:

1. Overhauling the Weapon System to slightly resemble the one from
Half-Life.

2. This is largly a cut-and-paste tutorial. If you want to learn
from this, I suggest you have a least a basic understanding of
QuakeC, then you should be able to see what's going on in the code.

What advantage will this give us?

2. The ability to have up to a total of about 24 different
weapons. (8 categories are supported by the regular iD1 quake HUD)

This should be enough for even the most gun heavy mod, if you need
even more than this, you may want to consider implimenting altfires.

The downsides?

1. Console give cheat doesn't work on weapons.
(Will still work for ammo and health though)

2. Frikbot will require some modification to it's weapon handling
code for it to use them right. (this goes without saying anyway)

For the purposes of this tutorial, I will only explain the bare minimum needed.

Also note that this code follows a sort of Half-Life
weapon categories system. (i.e. weapons belong to a group)

You will have to provide your own gfx and whatnot to set the hud up
the way you want. (i.e. handgun group, shotgun group, etc.)

Start with Progs106 source code.


In Defs.qc at the very bottom add:

Code: Select all

.float weapon2;
.float items3;
float IT3_AXE					= 1;
float IT3_SHOTGUN				= 2;
float IT3_SUPER_SHOTGUN		= 4;
float IT3_NAILGUN				= 8;
float IT3_SUPER_NAILGUN		= 16;
float IT3_GRENADE_LAUNCHER		= 32;
float IT3_ROCKET_LAUNCHER		= 64;
float IT3_LIGHTNING			= 128;
float IT3_NEWWEAPON = 256;

// Additional bitflags availible
float IT3_NEWWEAPON2 = 512;
float IT3_NEWWEAPON3 = 1024;
float IT3_NEWWEAPON4 = 2048;

float IT3_NEWWEAPON5 = 4096;
float IT3_NEWWEAPON6 = 8192;
float IT3_NEWWEAPON7 = 16384;
float IT3_NEWWEAPON8 = 32768;
float IT3_NEWWEAPON9 = 65536;

float IT3_NEWWEAPON10 = 131072;
float IT3_NEWWEAPON11 = 262144;

float IT3_NEWWEAPON12 = 524288;
float IT3_NEWWEAPON13 = 1048576;
float IT3_NEWWEAPON14 = 2097152;
float IT3_NEWWEAPON15 = 4194304;
float IT3_NEWWEAPON16 = 8388608; 
First an explaination:
Note that I have redundantly defined the original eight weapons here
in addition to a new one for use with the new system. This is
because there may be instances where you could pick up a new weapon
that belongs to a certain category, but the icon for the group would
never show up on your HUD unless you had the first weapon of the
group already!

Now, .float weapon2 will allow us to store whether or not we are
currently using our new weapon. (or even the original weapons!)
We will use a spawnparm for this later so that quake will remember
for us whether or not the IT3_NEWWEAPON was selected.

We will assume for this tutorial that IT3_NEWWEAPON belongs to the
super shotgun group. (Even though it will be a copy of the nailgun.)

.float items3 will allow us to store whether or not we have said
old / new weapon(s). This will use a spawnparm for level changes.

float IT3_NEWWEAPON = 256 defines what bit said weapon will have in
storage.

Feel free to rename any or all of the *_NEWWEAPON** bitflags defined
above for your new weapons once you have a proper understanding of
this tutorial.

Now, save defs.qc and open client.qc.

At the bottom of SetChangeParms add:

Code: Select all

parm10 = self.items3; // items3
parm11 = self.weapon2; // remember what weapon you use between levels
At the bottom of SetNewParms add:

Code: Select all

parm10 = IT3_SHOTGUN | IT3_AXE;
parm11 = IT3_SHOTGUN | IT3_AXE;
At the bottom of SetNewParms

Code: Select all

self.items3 = parm10;
self.weapon2 = parm11;
These set up spawnparms for quake to remember your new weapons.

NOTE: You will need to modify ClientObituary later to properly print
player obituaries when being killed with a weapon. This exercise is
left up to the reader.

Save client.qc, open weapons.qc.

Go to W_FireSpikes and replace this:

Code: Select all

if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)
With this:

Code: Select all

if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN && 
                            (self.weapon2 & IT_SUPER_NAILGUN))

This is a fix for the SNG to ensure that if we ever put any new
weapons into the SNG category that might use nails, it won't eat
two rounds of ammo a pop unless it really IS the SNG being used.

Go to W_SetCurrentAmmo and replace the whole function with the following:

Code: Select all

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)
	{
      // Check to see what weapon we are using in this subcategory.
	  // Note that this is the only weapon here.
	  if(self.weapon2 & IT3_AXE)
	   {
		self.currentammo = 0;
		self.weaponmodel = "progs/v_axe.mdl";
		self.weaponframe = 0;
	   }
	}
	else if (self.weapon == IT_SHOTGUN)
	{
      // Check to see what weapon we are using in this subcategory.
	  // Again only weapon here.
	  if(self.weapon2 & IT3_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)
	{
      // Check to see what weapon we are using in this subcategory.
	  // Note that this group is what IT3_NEWWEAPON belongs to.
	  if(self.weapon2 & IT3_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.weapon2 & IT3_NEWWEAPON) // This is new weapon
	   {
	    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_NAILGUN)
	{
      // Check to see what weapon we are using in this subcategory.
	  // This like the melee and low tier group, this is the only 
	  // weapon here.
	  if(self.weapon2 & IT3_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)
	{
      // Check to see what weapon we are using in this subcategory.
	  // There are more to go...
	  if(self.weapon2 & IT3_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)
	{
      // Check to see what weapon we are using in this subcategory.
	  // Only two more to go...
	  if(self.weapon2 & IT3_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)
	{
      // Check to see what weapon we are using in this subcategory.
	  // Only one more to go...
	  if(self.weapon2 & IT3_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)
    {
      // Check to see what weapon we are using in this subcategory.
	  // Sequence completed!
	  if(self.weapon2 & IT3_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;
	}
};
The above updates W_SetCurrentAmmo to check whether or not you've
selected your old / new weapon(s). Read the comments, they should be
fairly self explanatory.

Next go to W_BestWeapon and replace with this:

Code: Select all

float() W_BestWeapon =
{
	local	float	it;
	
	// since .items3 is where all weapons are held check that instead.
	it = self.items3;

	if (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & IT3_LIGHTNING) )
	 {
	  // Set lightning gun as weapon to use.
	  self.weapon2 = self.weapon2 | IT3_LIGHTNING;
	  // Sets lightning group (exotic) icon as active.
	  return IT_LIGHTNING;
	 }
	if(self.ammo_nails >= 2 && (it & IT3_SUPER_NAILGUN) )
	 {
	  // Set SNG as weapon to use.
	  self.weapon2 = self.weapon2 | IT3_SUPER_NAILGUN;
      // Sets SNG (Heavy weapons / minigun) Group icon as active.
	  return IT_SUPER_NAILGUN;
	 }
	if(self.ammo_nails >= 1 && (it & IT3_NAILGUN) )
	 {
	  // Set Nailgun as weapon to use
	  self.weapon2 = self.weapon2 | IT3_NAILGUN;
	  // Set NG (Machinegun / Assault Rifle) Group icon as active.
	  return IT_NAILGUN;
     }
    // new weapon takes priority over SSG if nails are present.
	if(self.ammo_nails >= 1 && (it & IT3_NEWWEAPON) )
	 {
	  // Set new weapon as weapon to use
	  self.weapon2 = self.weapon2 | IT3_NEWWEAPON;
	  // Because new weapon is part of SSG Group, 
	  // set SSG (Shotgun / FlakCannon) group icon as active
	  return IT_SUPER_SHOTGUN;
     }
	if(self.ammo_shells >= 2 && (it & IT3_SUPER_SHOTGUN) )
	 {
	  // Set Super Shotgun as weapon to use
	  self.weapon2 = self.weapon2 | IT3_SUPER_SHOTGUN;
	  // Set SSG (Shotgun / FlakCannon) group icon as active
	  return IT_SUPER_SHOTGUN;
	 }
	if(self.ammo_shells >= 1 && (it & IT3_SHOTGUN) )
	 {
	  // Set Shotgun as weapon to use
	  self.weapon2 = self.weapon2 | IT3_SHOTGUN;
	  // Set Wimpy (Pistol / .410 Shotshell) group icon as active.
	  return IT_SHOTGUN;
	 }
	// If none of the above, set to Axe as weapon to use.
	self.weapon2 = self.weapon2 | IT3_AXE;
	// Set Axe (melee) group icon as active...Or rather, that's what
	// I'd like to say.  Melee doesn't HAVE a group icon on the HUD!
	return IT_AXE;
};
Again, I've added some comments. Read them if you wish.

Move on to W_CheckNoAmmo and change this:

Code: Select all

	if (self.weapon == IT_AXE)
		return TRUE;
to this:

Code: Select all

    // Fix to ensure that any ammo based melees are properly caught
	if (self.weapon == IT_AXE && (self.weapon2 & IT3_AXE))
		return TRUE;
Remember to modify this if you create any weapons that have infinite
melee weapon ammo.

Now go to W_Attack and replace with this:

Code: Select all

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)
	{
	 // Check to see if your using the axe, if so...AX ATTACK!!11!!
	 if(self.weapon2 & IT3_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)
	{
	 // Don't get to excited.  After all, this shotgun seems so weak
	 // that you could have it misfire in your face several times
	 // and only leave you with a small cut on your face. =P
	 if(self.weapon2 & IT3_SHOTGUN)
	  {
		player_shot1 ();
		W_FireShotgun ();
		self.attack_finished = time + 0.5;
	  }
	}
	else if (self.weapon == IT_SUPER_SHOTGUN)
	{
	 // The only *real* shotgun in Quake, IMO.  And then only just
	 // barely.  =P
	 if(self.weapon2 & IT3_SUPER_SHOTGUN)
	  {
		player_shot1 ();
		W_FireSuperShotgun ();
		self.attack_finished = time + 0.7;
	  }
	 else if(self.weapon2 & IT3_NEWWEAPON) // If using New Weapon, fire!
	  {
	   // Nailgun redundancy.  What?  You expected something fancy?
	   player_nail1();
	  }
	}
	else if (self.weapon == IT_NAILGUN)
	{
	 // Wimpy pincushion gun. AKA Shambler tickler.
	 if(self.weapon2 & IT3_NAILGUN)
	  {
		player_nail1 ();
	  }
	}
	else if (self.weapon == IT_SUPER_NAILGUN)
	{
	 // More dangerous pincushion gun.  Decent if it held more ammo.
	 if(self.weapon2 & IT3_SUPER_NAILGUN)
	  {
		player_nail1 ();
	  }
	}
	else if (self.weapon == IT_GRENADE_LAUNCHER)
	{
	 // Finally something decent.
	 if(self.weapon2 & IT3_GRENADE_LAUNCHER)
	  {
		player_rocket1();
		W_FireGrenade();
		self.attack_finished = time + 0.6;
	  }
	}
	else if (self.weapon == IT_ROCKET_LAUNCHER)
	{
	 // Groovy.
	 if(self.weapon2 & IT3_ROCKET_LAUNCHER)
	  {
		player_rocket1();
		W_FireRocket();
		self.attack_finished = time + 0.8;
	  }
	}
	else if (self.weapon == IT_LIGHTNING)
	{
	 // The famous "don't use while taking a bath" gun.  XD
	 if(self.weapon2 & IT3_LIGHTNING)
	  {
		player_light1();
		self.attack_finished = time + 0.1;
		sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
	  }
	}
};
 
Ignore my nutbar comments in the above. I went insanes making this
after all. ;)

Replace W_ChangeWeapon with this:

Code: Select all

void() W_ChangeWeapon =
{
local float it, am, fl, fl2;

// As with bestweapon, check .items3 NOT .items.
it = self.items3;
am = 0;

if (self.impulse == 1)
{
// melee category
fl = IT_AXE;
// Chop Chop.
fl2 = IT3_AXE;
}
else if (self.impulse == 2)
{
// Wimpy Category
fl = IT_SHOTGUN;
// reach out and tickle somebody.
fl2 = IT3_SHOTGUN;
// if no shells, no tickle.
if (self.ammo_shells < 1)
am = 1;
}
else if (self.impulse == 3)
{
// Note that we check here because there's more than one weapon here!
// First if we are already using the super shotgun, change to our
// new weapon!
if((self.weapon2 & IT3_SUPER_SHOTGUN) && self.weapon == IT_SUPER_SHOTGUN)
{
// remove the super shotgun bit from weapon2.
if(self.items3 & IT3_NEWWEAPON)
  self.weapon2 = self.weapon2 - (self.weapon2 & IT3_SUPER_SHOTGUN);
// Shotgun / FlakCannon group.
fl = IT_SUPER_SHOTGUN;
// Nailgun.  Yeah yeah, I'm lazy.
fl2 = IT3_NEWWEAPON;
// No nails, no prickly from newweapon.
if (self.ammo_nails < 1)
am = 1;
}
else if((self.weapon2 & IT3_NEWWEAPON) && self.weapon == IT_SUPER_SHOTGUN)
{
// Shotgun / FlakCannon group.
fl = IT_SUPER_SHOTGUN;
// Boomstick, never get sucked into a strange time portal without it.
fl2 = IT3_SUPER_SHOTGUN;
// Needs two shells for happy time.
if (self.ammo_shells < 2)
am = 1;
}
else if(self.weapon2 & IT3_NEWWEAPON)
{
// Shotgun / FlakCannon group.
fl = IT_SUPER_SHOTGUN;
// Nailgun.  Yeah yeah, I'm lazy.
fl2 = IT3_NEWWEAPON;
// No nails, no prickly from newweapon.
if (self.ammo_nails < 1)
am = 1;
}
else
{
// Shotgun / FlakCannon group.
fl = IT_SUPER_SHOTGUN;
// Boomstick, never get sucked into a strange time portal without it.
fl2 = IT3_SUPER_SHOTGUN;
// Needs two shells for happy time.
if (self.ammo_shells < 2)
am = 1;
}
}
else if (self.impulse == 4)
{
// Machinegun group
fl = IT_NAILGUN;
// Lesser Pricklegun
fl2 = IT3_NAILGUN;
// Mmmm, Pointy.
if (self.ammo_nails < 1)
am = 1;
}
else if (self.impulse == 5)
{
// Heavy / Minigun group
fl = IT_SUPER_NAILGUN;
// Greater Pricklegun
fl2 = IT3_SUPER_NAILGUN;
// Loves to eat ammo like popcorn
if (self.ammo_nails < 2)
am = 1;
}
else if (self.impulse == 6)
{
// Explosives group
fl = IT_GRENADE_LAUNCHER;
// Grenade.  Wonder if it's legal for deer hunting?.
fl2 = IT3_GRENADE_LAUNCHER;
// No rockets, no boom.
if (self.ammo_rockets < 1)
am = 1;
}
else if (self.impulse == 7)
{
// Anti-Tank Heavy Weapons group
fl = IT_ROCKET_LAUNCHER;
// "Oh boy!  It's Clobberin' time!" - Duke Nukem, ZER0:H0UR
fl2 = IT3_ROCKET_LAUNCHER;
// Please use only specified factory rocket ammo for best results.
if (self.ammo_rockets < 1)
am = 1;
}
else if (self.impulse == 8)
{
// Lightning (Exotic) group
fl = IT_LIGHTNING;
// How DOES the quakeguy carry around all those heavy car batteries? =P
fl2 = IT3_LIGHTNING;
// God this thing must suck energy down like crazy.
if (self.ammo_cells < 1)
am = 1;
}

self.impulse = 0;

if (!(self.items3 & fl2))
{ // 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;
self.weapon2 = self.weapon2 | fl2;
W_SetCurrentAmmo ();
};
More nutbar code comments. Next, replace W_CheatCommand,
CycleWeaponCommand and CycleWeaponReverseCommand with the following:

Code: Select all

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

	self.ammo_rockets = 100;
	self.ammo_nails = 200;
	self.ammo_shells = 100;
	self.ammo_cells = 200;
	
	self.items = self.items | 
		IT_AXE |
		IT_SHOTGUN |
		IT_SUPER_SHOTGUN |
		IT_NAILGUN |
		IT_SUPER_NAILGUN |
		IT_GRENADE_LAUNCHER |
		IT_ROCKET_LAUNCHER |
		IT_LIGHTNING |
		IT_KEY1 | IT_KEY2;
	  
	self.items3 = self.items3 |
        IT3_AXE |
		IT3_SHOTGUN |
		IT3_SUPER_SHOTGUN |
		IT3_NAILGUN |
		IT3_SUPER_NAILGUN |
		IT3_GRENADE_LAUNCHER |
		IT3_ROCKET_LAUNCHER |
		IT3_LIGHTNING |
        IT3_NEWWEAPON;
		
	self.weapon = IT_ROCKET_LAUNCHER;
	self.weapon2 = self.weapon2 | IT3_ROCKET_LAUNCHER;
	self.impulse = 0;
	W_SetCurrentAmmo ();
};

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

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

    // Check items3 instead of items.	
	it = self.items3;
	self.impulse = 0;
	
	while (1)
	{
		am = 0;

        // Only one weapon in this group.  See SSG Group for 
		// multiple weapons in a group.
		if (self.weapon == IT_LIGHTNING)
		{
		   // up a category.
			self.weapon = IT_AXE;
		   // This sets RL
			self.weapon2 = self.weapon2 | IT3_AXE;
		   // weapon to check for
		    wp = IT3_AXE;
		}
		else if (self.weapon == IT_ROCKET_LAUNCHER)
		{

			self.weapon = IT_LIGHTNING;
			self.weapon2 = self.weapon2 | IT3_LIGHTNING;
			wp = IT3_LIGHTNING;
			if (self.ammo_cells < 1)
				am = 1;
		}
		else if (self.weapon == IT_GRENADE_LAUNCHER)
		{
		 // See above.
			self.weapon = IT_ROCKET_LAUNCHER;
		// I'm gonna quit typing these now.
		    self.weapon2 = self.weapon2 = IT3_ROCKET_LAUNCHER;
		   // weapon to check for
		    wp = IT3_ROCKET_LAUNCHER;
			if (self.ammo_rockets < 1)
				am = 1;
		}
		else if (self.weapon == IT_SUPER_NAILGUN)
		{
			self.weapon = IT_GRENADE_LAUNCHER;
			self.weapon2 = IT3_GRENADE_LAUNCHER;
		    wp = IT3_GRENADE_LAUNCHER;
			if (self.ammo_rockets < 1)
				am = 1;
		}
		else if (self.weapon == IT_NAILGUN)
		{
			self.weapon = IT_SUPER_NAILGUN;
           self.weapon2 = self.weapon2 | IT3_SUPER_NAILGUN;
		    wp = IT3_SUPER_NAILGUN;
   			if(self.ammo_nails < 2)
			   am = 1;
		}		
		else if (self.weapon == IT_SUPER_SHOTGUN)
		{
		 // Okay, lets check to see if we gots IT3_NEWWEAPON
		 if((it & IT3_NEWWEAPON) && !(self.weapon2 & IT3_NEWWEAPON))
		  {
           self.weapon = IT_SUPER_SHOTGUN;
		   if(self.weapon2 & IT3_SUPER_SHOTGUN)
		    self.weapon2 = self.weapon2 - (self.weapon2 & IT3_SUPER_SHOTGUN);

           self.weapon2 = self.weapon2 | IT3_NEWWEAPON;
		    wp = IT3_NEWWEAPON;		   
   			if(self.ammo_nails < 1)
			   am = 1;
		  }
		  else // if not then try nailgun.
		  {
		   self.weapon = IT_NAILGUN;
		   self.weapon2 = self.weapon2 | IT3_NAILGUN;
		   wp = IT3_NAILGUN;
			if (self.ammo_nails < 1)
				am = 1;
		  }
		}		
		else if (self.weapon == IT_SHOTGUN)
		{
		 self.weapon = IT_SUPER_SHOTGUN;
		 // Okay, lets check to see if we gots IT3_NEWWEAPON
		 // if so, clear the bit so we can go for SSG.
         if(self.weapon2 & IT3_NEWWEAPON)
		    self.weapon2 = self.weapon2 - (self.weapon2 & IT3_NEWWEAPON);
            self.weapon2 = self.weapon2 | IT3_SUPER_SHOTGUN;
   			wp = IT3_SUPER_SHOTGUN;
			if(self.ammo_shells < 2)
			   am = 1;
		}
		else if (self.weapon == IT_AXE)
		{
		   self.weapon = IT_SHOTGUN;
		   self.weapon2 = self.weapon2 | IT3_SHOTGUN;
		   wp = IT3_SHOTGUN;
			if (self.ammo_shells < 1)
				am = 1;
		}
	
		if ( (it & wp) && am == 0)
		{
			W_SetCurrentAmmo ();
			return;
		}
	}
};

/*
============
CycleWeaponReverseCommand

Go to the prev weapon with ammo
============
*/
void() CycleWeaponReverseCommand =
{
	local	float	it, am, wp;
	
    // Check items3 instead of items.
	it = self.items3;
	self.impulse = 0;

	while (1)
	{
		am = 0;

        // Only one weapon in this group.  See SSG Group for 
		// multiple weapons in a group.
		if (self.weapon == IT_LIGHTNING)
		{
		   // Down a category.
			self.weapon = IT_ROCKET_LAUNCHER;
		   // This sets RL
			self.weapon2 = self.weapon2 | IT3_ROCKET_LAUNCHER;
		   // weapon to check for
		    wp = IT3_ROCKET_LAUNCHER;
		   // Don't forget to check for ammo.
			if (self.ammo_rockets < 1)
				am = 1;
		}
		else if (self.weapon == IT_ROCKET_LAUNCHER)
		{
		 // Down yet another category
			self.weapon = IT_GRENADE_LAUNCHER;
		 // This sets GL
		    self.weapon2 = self.weapon2 | IT3_GRENADE_LAUNCHER;
		   // weapon to check for
		    wp = IT3_GRENADE_LAUNCHER;
		 //	Need Ammos.
			if (self.ammo_rockets < 1)
				am = 1;
		}
		else if (self.weapon == IT_GRENADE_LAUNCHER)
		{
		 // See above.
			self.weapon = IT_SUPER_NAILGUN;
		// I'm gonna quit typing these now.
		    self.weapon2 = self.weapon2 = IT3_SUPER_NAILGUN;
		   // weapon to check for
		    wp = IT3_SUPER_NAILGUN;
			if (self.ammo_nails < 2)
				am = 1;
		}
		else if (self.weapon == IT_SUPER_NAILGUN)
		{
			self.weapon = IT_NAILGUN;
			self.weapon2 = IT3_NAILGUN;
		    wp = IT3_NAILGUN;
			if (self.ammo_nails < 1)
				am = 1;
		}
		else if (self.weapon == IT_NAILGUN)
		{
			self.weapon = IT_SUPER_SHOTGUN;
		 // Okay, lets check to see if we gots IT3_NEWWEAPON
		 if(it & IT3_NEWWEAPON)
		  {
		   if(self.weapon2 & IT3_SUPER_SHOTGUN)
		    self.weapon2 = self.weapon2 - (self.weapon2 & IT3_SUPER_SHOTGUN);

           self.weapon2 = self.weapon2 | IT3_NEWWEAPON;
		    wp = IT3_NEWWEAPON;		   
   			if(self.ammo_nails < 1)
			   am = 1;
		  }
		  else // if not then try super shotgun.
		  {
		   self.weapon2 = self.weapon2 | IT3_SUPER_SHOTGUN;
		   wp = IT3_SUPER_SHOTGUN;
			if (self.ammo_shells < 2)
				am = 1;
		  }
		}		
		else if (self.weapon == IT_SUPER_SHOTGUN)
		{
		 // Okay, lets check to see if we gots IT3_NEWWEAPON
         if(self.weapon2 & IT3_NEWWEAPON)
		  {
		    self.weapon2 = self.weapon2 - (self.weapon2 & IT3_NEWWEAPON);
            self.weapon2 = self.weapon2 | IT3_SUPER_SHOTGUN;
   			wp = IT3_SUPER_SHOTGUN;
			if(self.ammo_shells < 2)
			   am = 1;
		  }
		  else // if not then try next weapon down.
		  {
		   self.weapon = IT_SHOTGUN;
		   self.weapon2 = self.weapon2 | IT3_SHOTGUN;
		   wp = IT3_SHOTGUN;
			if (self.ammo_shells < 1)
				am = 1;
		  }
		}		
		else if (self.weapon == IT_SHOTGUN)
		{
			self.weapon = IT_AXE;
			self.weapon2 = self.weapon2 | IT3_AXE;
			wp = IT3_AXE;
		}
		else if (self.weapon == IT_AXE)
		{
			self.weapon = IT_LIGHTNING;
			self.weapon2 = self.weapon2 | IT3_LIGHTNING;
			wp = IT3_LIGHTNING;
			if (self.ammo_cells < 1)
				am = 1;
		}
	
		if ( (it & wp) && am == 0)
		{
			W_SetCurrentAmmo ();
			return;
		}
	}

};
In ImpulseCommands put this right below QuadCheat:

Code: Select all

// Testing cheat to see if new weapon stuff works.
    if(self.impulse == 47)
	 {
	  self.ammo_nails = self.ammo_nails + 30;
	  self.items = self.items | IT_SUPER_SHOTGUN;
	  self.items3 = self.items3 | IT3_NEWWEAPON;
	  self.weapon2 = self.weapon2 | IT3_NEWWEAPON;
	 }
Save weapons.qc, open items.qc.

Replace RankForWeapon and Deathmatch_Weapon with this:

Code: Select all

float(float w) RankForWeapon =
{
	if (w == IT3_LIGHTNING)
		return 1;
	if (w == IT3_ROCKET_LAUNCHER)
		return 2;
	if (w == IT3_SUPER_NAILGUN)
		return 3;
	if (w == IT3_GRENADE_LAUNCHER)
		return 4;
// New weapon in priority rankings.
	if (w == IT3_NEWWEAPON)
	    return 5;
	if (w == IT3_SUPER_SHOTGUN)
		return 6;
	if (w == IT3_NAILGUN)
		return 7;
	return 8;
};

/*
=============
DRS
Weapon_Better

Change from current weapon to new weapon if it's better.
=============
*/
void(float oldcategory, float new, float category) Weapon_Better =
{
	local float oldweapon, or, nr;

// extract weapon info from .weapon2 depending on what category
    if(oldcategory == IT_AXE)
     {
	  oldweapon = IT3_AXE;
     }
    else if(oldcategory == IT_SHOTGUN)
     {
	  oldweapon = IT3_SHOTGUN;
     }
    else if(oldcategory == IT_SUPER_SHOTGUN)
     {
	  if(self.weapon2 & IT3_SUPER_SHOTGUN)
	   oldweapon = IT3_SHOTGUN;
	  else if(self.weapon2 & IT3_NEWWEAPON)
	   oldweapon = IT3_NEWWEAPON;
     }
    else if(oldcategory == IT_NAILGUN)
     {
	  oldweapon = IT3_NAILGUN;
     }
    else if(oldcategory == IT_SUPER_NAILGUN)
     {
	  oldweapon = IT3_SUPER_NAILGUN;
     }
    else if(oldcategory == IT_GRENADE_LAUNCHER)
     {
	  oldweapon = IT3_GRENADE_LAUNCHER;
     }
    else if(oldcategory == IT_ROCKET_LAUNCHER)
     {
	  oldweapon = IT3_ROCKET_LAUNCHER;
     }
    else if(oldcategory == IT_LIGHTNING)
     {
	  oldweapon = IT3_LIGHTNING;
     }

	 
// change self.weapon if desired
	or = RankForWeapon (oldweapon);
	nr = RankForWeapon (new);
	if ( nr < or )
	 {
// Set to category
		self.weapon = category;
// Need to do some case handling for categories with more than one
// weapon
// if player is using the super shotgun, remove the bitflag.
		if(new == IT3_NEWWEAPON && (self.weapon2 & IT3_SUPER_SHOTGUN))
		 self.weapon2 = self.weapon2 - (self.weapon2 & IT3_SUPER_SHOTGUN);
       
// set weapon2 bitflag.		 
		 self.weapon2 = self.weapon2 | new;
	 }
};
Next go to weapon_touch and replace it with this:

Code: Select all

void() weapon_touch =
{
	local	float	hadammo, category, new, old, gotit;
	local	entity	stemp;
	local	float	leave;

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

	if (deathmatch == 2 || coop)
		leave = 1;
	else
		leave = 0;
	
	if (self.classname == "weapon_nailgun")
	{
		if (leave && (other.items3 & IT3_NAILGUN) )
			return;
		hadammo = other.ammo_nails;			
		new = IT3_NAILGUN;
		category = IT_NAILGUN;
		other.ammo_nails = other.ammo_nails + 30;
	}
	else if (self.classname == "weapon_supernailgun")
	{
		if (leave && (other.items3 & IT3_SUPER_NAILGUN) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT3_SUPER_NAILGUN;
		category = IT_SUPER_NAILGUN;
		other.ammo_nails = other.ammo_nails + 30;
	}
	else if (self.classname == "weapon_supershotgun")
	{
		if (leave && (other.items3 & IT3_SUPER_SHOTGUN) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT3_SUPER_SHOTGUN;
		category = IT_SUPER_SHOTGUN;		
		other.ammo_shells = other.ammo_shells + 5;
	}
	else if (self.classname == "weapon_rocketlauncher")
	{
		if (leave && (other.items3 & IT3_ROCKET_LAUNCHER) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT3_ROCKET_LAUNCHER;
		category = IT_ROCKET_LAUNCHER;
		other.ammo_rockets = other.ammo_rockets + 5;
	}
	else if (self.classname == "weapon_grenadelauncher")
	{
		if (leave && (other.items3 & IT3_GRENADE_LAUNCHER) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT3_GRENADE_LAUNCHER;
		category = IT_GRENADE_LAUNCHER;
		other.ammo_rockets = other.ammo_rockets + 5;
	}
	else if (self.classname == "weapon_lightning")
	{
		if (leave && (other.items3 & IT3_LIGHTNING) )
			return;
		hadammo = other.ammo_rockets;			
		new = IT3_LIGHTNING;
		category = IT_LIGHTNING;		
		other.ammo_cells = other.ammo_cells + 15;
	}
	else if (self.classname == "weapon_newweapon") // new weapon
	{
		if (leave && (other.items3 & IT3_NEWWEAPON) )
			return;
		hadammo = other.ammo_rockets;
// What weapon
		new = IT3_NEWWEAPON;
// What category
		category = IT_SUPER_SHOTGUN;
		other.ammo_nails = other.ammo_nails + 30;
	}
	else
		objerror ("weapon_touch: unknown classname");

	sprint (other, "You got the ");
	sprint (other, self.netname);
	sprint (other, "\n");
// weapon touch sound
	sound (other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM);
	stuffcmd (other, "bf\n");

	bound_other_ammo ();

// change to the weapon
	old = other.weapon;
    
	if(other.items3 & new)
	 gotit = TRUE;
	 
	other.items3 = other.items3 | new;
	
	stemp = self;
	self = other;

	self.items = self.items | category;

   if(!gotit)
	Weapon_Better (old, new, category);

	W_SetCurrentAmmo();

	self = stemp;

	if (leave)
		return;

// remove it in single player, or setup for respawning in deathmatch
	self.model = string_null;
	self.solid = SOLID_NOT;
	if (deathmatch == 1)
		self.nextthink = time + 30;
	self.think = SUB_regen;
	
	activator = other;
	SUB_UseTargets();				// fire all targets / killtargets
};
Add this below weapon_touch:

Code: Select all

/*QUAKED weapon_newweapon (0 .5 .8) (-16 -16 0) (16 16 32)
DRS: New Weapon entity for map testing purposes.
*/

void() weapon_newweapon =
{
	precache_model ("progs/g_nail.mdl");
	setmodel (self, "progs/g_nail.mdl");
	self.weapon = IT_SUPER_SHOTGUN; // Doesn't do anything.
    // meet the new weapon, same as old weapon (nailgun)=P
	self.netname = "New Weapon";
	self.touch = weapon_touch;
	setsize (self, '-16 -16 0', '16 16 56');
	StartItem ();
};
Go down and replace BackpackTouch and DropBackpack with this:

Code: Select all

void() BackpackTouch =
{
	local string	s;
	local	float	best, old, new, category, gotit;
	local		entity	stemp;
	local	float	acount;
	
	if (other.classname != "player")
		return;
	if (other.health <= 0)
		return;

	acount = 0;
	sprint (other, "You get ");

	category = self.items;
	
	if (self.items)
		if ((other.items3 & self.weapon2) == 0)
		{
            other.items = other.items | self.items;
			acount = 1;
			sprint (other, "the ");
			sprint (other, self.netname);
		}

// if the player was using his best weapon, change up to the new one if better		
//	stemp = self;
//	self = other;
//	best = W_BestWeapon();
//	self = stemp;

// change weapons
	other.ammo_shells = other.ammo_shells + self.ammo_shells;
	other.ammo_nails = other.ammo_nails + self.ammo_nails;
	other.ammo_rockets = other.ammo_rockets + self.ammo_rockets;
	other.ammo_cells = other.ammo_cells + self.ammo_cells;

	new = self.weapon2;
	if (!new)
		new = other.weapon2;
	old = other.weapon;
	if(other.items3 & new)
	 gotit = TRUE;
	 
	other.items3 = other.items3 | new;
	
	bound_other_ammo ();

	if (self.ammo_shells)
	{
		if (acount)
			sprint(other, ", ");
		acount = 1;
		s = ftos(self.ammo_shells);
		sprint (other, s);
		sprint (other, " shells");
	}
	if (self.ammo_nails)
	{
		if (acount)
			sprint(other, ", ");
		acount = 1;
		s = ftos(self.ammo_nails);
		sprint (other, s);
		sprint (other, " nails");
	}
	if (self.ammo_rockets)
	{
		if (acount)
			sprint(other, ", ");
		acount = 1;
		s = ftos(self.ammo_rockets);
		sprint (other, s);
		sprint (other, " rockets");
	}
	if (self.ammo_cells)
	{
		if (acount)
			sprint(other, ", ");
		acount = 1;
		s = ftos(self.ammo_cells);
		sprint (other, s);
		sprint (other, " cells");
	}
	
	sprint (other, "\n");
// backpack touch sound
	sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM);
	stuffcmd (other, "bf\n");

// remove the backpack, change self to the player
	remove(self);
	self = other;

// change to the weapon
//	if (!deathmatch)
//		self.weapon = new;
//	else
    if(!gotit)
		Weapon_Better (old, new, category);

	W_SetCurrentAmmo ();
};

/*
===============
DropBackpack
===============
*/
void() DropBackpack =
{
	local entity	item;

	if (!(self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells))
		return;	// nothing in it

	item = spawn();
	item.origin = self.origin - '0 0 24';
	
	item.items = self.weapon;
	if (item.items == IT_AXE)
	 {
	    item.weapon2 = IT3_AXE;
		item.netname = "Axe";
	 }
	else if (item.items == IT_SHOTGUN)
	 {
	    item.weapon2 = IT3_SHOTGUN;	 
		item.netname = "Shotgun";
	 }
	else if (item.items == IT_SUPER_SHOTGUN)
	 {
	  if(self.weapon2 & IT3_NEWWEAPON)
       {
		item.weapon2 = IT3_NEWWEAPON;
		item.netname = "NewWeapon";
       }
      else
       {  
		item.weapon2 = IT3_SUPER_SHOTGUN;
		item.netname = "Double-barrelled Shotgun";
	   }
	 }
	else if (item.items == IT_NAILGUN)
	 {
		item.weapon2 = IT3_NAILGUN;
		item.netname = "Nailgun";
	 }
	else if (item.items == IT_SUPER_NAILGUN)
	 {
		item.weapon2 = IT3_SUPER_NAILGUN;
		item.netname = "Super Nailgun";
	 }
	else if (item.items == IT_GRENADE_LAUNCHER)
	 {
		item.weapon2 = IT3_GRENADE_LAUNCHER;
		item.netname = "Grenade Launcher";
	 }
	else if (item.items == IT_ROCKET_LAUNCHER)
	 {
		item.weapon2 = IT3_ROCKET_LAUNCHER;
		item.netname = "Rocket Launcher";
	 }
	else if (item.items == IT_LIGHTNING)
	 {
		item.weapon2 = IT3_LIGHTNING;
		item.netname = "Thunderbolt";
	 }
	else
		item.netname = "";

	item.ammo_shells = self.ammo_shells;
	item.ammo_nails = self.ammo_nails;
	item.ammo_rockets = self.ammo_rockets;
	item.ammo_cells = self.ammo_cells;

	item.velocity_z = 300;
	item.velocity_x = -100 + (random() * 200);
	item.velocity_y = -100 + (random() * 200);
	
	item.flags = FL_ITEM;
	item.solid = SOLID_TRIGGER;
	item.movetype = MOVETYPE_TOSS;
	setmodel (item, "progs/backpack.mdl");
	setsize (item, '-16 -16 0', '16 16 56');
	item.touch = BackpackTouch;
	
	item.nextthink = time + 120;	// remove after 2 minutes
	item.think = SUB_Remove;
};
And that's all there is to it! Crank up your favorite mapping program
make a map, add weapon_newweapon to it then run it through quake, or
just run quake with your favorite map and use the impulse 47 cheat.
(Just don't forget to take it out later, because in DM this cheat will
work. And that would be bad.)

Read and experiment with the code above. Then go forth and make super
insano-weapon mods!
Last edited by Dr. Shadowborg on Tue Mar 29, 2011 9:51 pm, edited 1 time in total.
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

Woah lol
Safe to say I would of never figured this out lol

I am just going to assume you have to keep the IT3_(oldweaponnames) and are able to use only the NEWWEAPON slots - as you said allow for a total of 24 weapons.

So from looking at this tutorial, making 3+ slots for a single weapon is entirely possible by simply doing things like:

Code: Select all

   else if (self.weapon == IT_SUPER_SHOTGUN)
   {
      // Check to see what weapon we are using in this subcategory.
     // Note that this group is what IT3_NEWWEAPON belongs to.
     if(self.weapon2 & IT3_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.weapon2 & IT3_NEWWEAPON) // This is new weapon
      {
       self.currentammo = self.ammo_nails;
      self.weaponmodel = "progs/v_nail.mdl";
      self.weaponframe = 0;
      self.items = self.items | IT_NAILS;
      }
     else if(self.weapon2 & IT3_OOPPEEGUN) // This is new weapon
      {
       self.currentammo = self.ammo_nails;
      self.weaponmodel = "progs/v_modelnamehere.mdl";
      self.weaponframe = 0;
      self.items = self.items | IT_PLASMA;
      }
   } 


and then

else if (self.weapon == IT_SUPER_SHOTGUN)
   {
    // The only *real* shotgun in Quake, IMO.  And then only just
    // barely.  =P
    if(self.weapon2 & IT3_SUPER_SHOTGUN)
     {
      player_shot1 ();
      W_FireSuperShotgun ();
      self.attack_finished = time + 0.7;
     }
    else if(self.weapon2 & IT3_NEWWEAPON) // If using New Weapon, fire!
     {
      // Nailgun redundancy.  What?  You expected something fancy?
      player_nail1();
     }
    else if(self.weapon2 & IT3_OOPPEEGUN) // If using New Weapon, fire!
     {
      W_FireOOPPEEGUN ();
     }
   } 
apply same logic to all the other events with same concept.


I was also going to ask, as I did come across possibly needing 25 for my mod. However it would be a spawn weapon (with the axe and shotgun).
Would:
IT3_GRAPPLE = 3;
be safe? Axe(1)+Shotgun(2)=3
As the player always has them - it's always true and I always want the player to have it. Would this be a good or bad idea? I know it's "cheating" and is bad coding. However would it cause any issues?
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

Post by Dr. Shadowborg »

ooppee wrote:Woah lol
Safe to say I would of never figured this out lol

I am just going to assume you have to keep the IT3_(oldweaponnames) and are able to use only the NEWWEAPON slots - as you said allow for a total of 24 weapons.
Yes, though you could change their names if you wanted to. (that would fall under the category of replacing the original quake weapons though, rather than keeping them...and going through all the code to replace all instances of IT_(oldweaponnames) to the new weapon names would be a lot of work. Easier to just keep the names as is, and modify the base weapon to do whatever you wanted)
ooppee wrote: So from looking at this tutorial, making 3+ slots for a single weapon is entirely possible by simply doing things like:

...

apply same logic to all the other events with same concept.
Correct. You could name IT3_NEWWEAPON to whatever you want though. (just replace all instances of IT3_NEWWEAPON to the new name you want) Also, you could if you wanted, put all remaining NEWWEAPON definitions into one category.
ooppee wrote: I was also going to ask, as I did come across possibly needing 25 for my mod. However it would be a spawn weapon (with the axe and shotgun).
Would:
IT3_GRAPPLE = 3;
be safe? Axe(1)+Shotgun(2)=3
As the player always has them - it's always true and I always want the player to have it. Would this be a good or bad idea? I know it's "cheating" and is bad coding. However would it cause any issues?
Probably not a good idea. What you *COULD* do is either use a float for say .usegrapple and have the axe code check to see if self.usegrapple was true. Or alternately modify the code to use one of the original "invisible" bitflags that go into self.items. i.e. bitflags 128, 65536 and 8388608. Or use teh unstoppable offhand grapple hook and not bother with weapons. (this is what the Shrak commerical mod did)
Last edited by Dr. Shadowborg on Sun Mar 27, 2011 3:30 pm, edited 1 time in total.
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

Well this covers all I need. Grapple may just be "cut". I am just bringing all the "Official" and "PreRelease" weapons and monsters into 1 game. So DoE and SoA packs will be required.
Copy Pak0.pak of SoA to the mod folder
Copy Pak0.pak of DoE to the mod folder and name it to Pak1.pak
Pak2.pak will be the mods extra files (QTest/Pre-Release/Extra content)
The Grapple is a "official" as it's part of the DoE weapon set BUT only appears in CTF mode in DoE - which was never really played even at release lol - due to 3wave and all - so with it possibly being "cut" - don't think it'd be missed lol
tingtom
Posts: 2
Joined: Fri Mar 25, 2011 10:42 am
Location: here

Post by tingtom »

what exactly does this tutorial do, does it just allow extra weapons?
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

Post by Dr. Shadowborg »

tingtom wrote:what exactly does this tutorial do, does it just allow extra weapons?
Short answer: YES, up to 24. Vanilla Quake only allows for up to 11.

Long answer at top of original post:
Dr. Shadowborg wrote: Okay, what we are going to be doing:

1. Overhauling the Weapon System to slightly resemble the one from
Half-Life.

2. This is largly a cut-and-paste tutorial. If you want to learn
from this, I suggest you have a least a basic understanding of
QuakeC, then you should be able to see what's going on in the code.

What advantage will this give us?

2. The ability to have up to a total of about 24 different
weapons. (8 categories are supported by the regular iD1 quake HUD)

This should be enough for even the most gun heavy mod, if you need
even more than this, you may want to consider implimenting altfires.

The downsides?

1. Console give cheat doesn't work on weapons.
(Will still work for ammo and health though)

2. Frikbot will require some modification to it's weapon handling
code for it to use them right. (this goes without saying anyway)
ceriux
Posts: 2230
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Post by ceriux »

how does vanilla quake allow for 11 weapons?

there are only 8 axe, shotgun, sshotgun, nailgun, snailgun,grenade luncher, rocket launcher, and lightning gun.

to go with that there are only 4 available ammo types.

im a little confused.
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

Defs.qc look at the IT_values

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_SHELLS = 256;
float IT_NAILS = 512;
float IT_ROCKETS = 1024;
float IT_CELLS = 2048;

float IT_ARMOR1 = 8192;
float IT_ARMOR2 = 16384;
float IT_ARMOR3 = 32768;
float IT_SUPERHEALTH = 65536;

float IT_KEY1 = 131072;
float IT_KEY2 = 262144;

float IT_INVISIBILITY = 524288;
float IT_INVULNERABILITY = 1048576;
float IT_SUIT = 2097152;
float IT_QUAD = 4194304;

Doubles each time and "8388608" is the max. So you could apply a:
IT_EVILGUN = 8388608

and there - 10 weapons. If you use the Rogue source which has IT2_ content:

float IT_AXE = 2048; // 1
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; // 8
float IT_SHELLS = 128;
float IT_NAILS = 256;
float IT_ROCKETS = 512;
float IT_CELLS = 1024;
float IT_LAVA_NAILGUN = 4096;
float IT_LAVA_SUPER_NAILGUN = 8192;
float IT_MULTI_GRENADE = 16384; //15
float IT_MULTI_ROCKET = 32768;
float IT_PLASMA_GUN = 65536;
float IT_KEY1 = 131072;
float IT_KEY2 = 262144;
float IT_INVISIBILITY = 524288;
float IT_INVULNERABILITY = 1048576;
float IT_SUIT = 2097152;
float IT_QUAD = 4194304; //23
float IT_GRAPPLE = 8388608;

14 weapons right there


What I think he was saying is you can have 10-11 weapons. However due to the lack of more ammo types you will have to share. So you could add in a Railgun or Chain Lightning Gun which uses Cell ammo. A quad barrel shotgun which uses shells. A Nailgun shotgun which launches 10 nails in a cluster that uses nail ammo ect ect ect
ceriux
Posts: 2230
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Post by ceriux »

ahh ic so each of the IT vaules could be a weapon more or less?
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

Post by Dr. Shadowborg »

ceriux: Yes, though some of them will show up as icons on the HUD / and or cause glow or screen tint effects. (i.e. the original self.items IT_*. A few of the originally defined IT_ do not however do anything in regards to the HUD, such as the axe, IT_SUPERHEALTH, and the undefined 8388608 bit. .items3 is however unaffected by this and as such will never display anything on the HUD, even when using -hipnotic or -rogue.)
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

Post by Dr. Shadowborg »

TUTORIAL ADDENDA:

Allowing monsters to drop a weapon:

Put something like this in the monster's spawn function:

Code: Select all


    self.weapon = IT_SUPER_SHOTGUN;
	self.items3 = self.items3 | IT3_NEWWEAPON;
	self.weapon2 = self.weapon2 | IT3_NEWWEAPON;
To make that nasty little kleptomaniac, the Gremlin more compliant
with this new system:
(note: he will by default NOT know how to use the missionpack1
weapons. Additionally, you WILL have to make some changes to even
get your progs.dat to compile. This is left up to the user as
doing so is beyond the scope of this tutorial and highly dependant
on what you're doing.)

Replace GremlinAttemptWeaponSteal with this:

Code: Select all

float() GremlinAttemptWeaponSteal =
   {
   local vector delta;
   local entity tempself;
   local float best, best2;
   local entity victim;
   local float amount;

   if (self.stoleweapon)
      {
      dprint("gremlin trying to steal a weapon again\n");
      return FALSE;
      }
   if (!(self.enemy.flags & FL_CLIENT))
      {
      return FALSE;
      }

   delta = (self.enemy.origin - self.origin);

   if (vlen(delta) > 100)
      return FALSE;
   if (random() < 0.5)
      return FALSE;
   //
   // we are within range so lets go for it
   //
   victim = self.enemy;
   best = victim.weapon;
   best2 = victim.weapon2;
   // Gremins shun the axe and shotgun.  Guess they don't think
   // much of the shotgun either...
   // NOTE: Add more in-depth checks in the axe and shotgun checks
   // below if you add weapons that should be stealable.
   if (best == IT_AXE || best == IT_SHOTGUN)
      return FALSE;
   // give weapon category to our gremlin
   self.items = self.items | best;
   self.weapon = best;
   // take some ammo while we are at it
   self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS ) );
   if (best == IT_SUPER_SHOTGUN)
      {
	   // Check which weapon
       if(best2 & IT3_SUPER_SHOTGUN)
	    {
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_SUPER_SHOTGUN);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_SUPER_SHOTGUN);
         // if the victim has another weapon from the group,
		 // switch to it for the slot.
		 if(victim.items3 & IT3_NEWWEAPON)
		  victim.weapon2 = victim.weapon2 | IT3_NEWWEAPON;
		 // Set Gremlin's weapon2 to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_SUPER_SHOTGUN;

         amount = victim.ammo_shells;
         if (amount > 20)
            amount = 20;
         victim.ammo_shells = victim.ammo_shells - amount;
         self.ammo_shells = self.ammo_shells + amount;
		 self.items = self.items | IT_SHELLS;
         self.currentammo = self.ammo_shells;
         sprint (victim, "Gremlin stole your Super Shotgun\n");
		}
	   else if(best2 & IT3_NEWWEAPON)
	    {
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_NEWWEAPON);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_NEWWEAPON);
         // if the victim has another weapon from the group,
		 // switch to it for the slot.
		 if(victim.items3 & IT3_SUPER_SHOTGUN)
		  victim.weapon2 = victim.weapon2 | IT3_SUPER_SHOTGUN;
		 // Set Gremlin's weapon to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_NEWWEAPON;

         amount = victim.ammo_nails;
         if (amount > 30)
            amount = 30;
         victim.ammo_nails = victim.ammo_nails - amount;
         self.ammo_nails = self.ammo_nails + amount;
		 self.items = self.items | IT_NAILS;
         self.currentammo = self.ammo_nails;
         sprint (victim, "Gremlin stole your New Weapon\n");
		}
       // if the victim didn't have any other weapon from that
	   // category, remove the category from the victim.
       if(!(victim.items3 & IT3_SUPER_SHOTGUN) && 
	      !(victim.items3 & IT3_NEWWEAPON))
		victim.items = victim.items - (victim.items & best);
      }
   else if (best == IT_NAILGUN)
      {
	   if(best2 & IT3_NAILGUN)
	    {
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_NAILGUN);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_NAILGUN);
		 // Set Gremlin's weapon to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_NAILGUN;

         amount = victim.ammo_nails;
         if (amount > 40)
            amount = 40;
         victim.ammo_nails = victim.ammo_nails - amount;
         self.ammo_nails = self.ammo_nails + amount;
         self.items = self.items | IT_NAILS;
         self.currentammo = self.ammo_nails;
         sprint (victim, "Gremlin stole your Nailgun\n");
        }
       // if the victim didn't have any other weapon from that
	   // category, remove the category from the victim.
       if(!(victim.items3 & IT3_NAILGUN))
		victim.items = victim.items - (victim.items & best);
	  }
   else if (best == IT_SUPER_NAILGUN)
      {
	   if(best2 & IT3_SUPER_NAILGUN)
	    {  
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_SUPER_NAILGUN);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_SUPER_NAILGUN);
		 // Set Gremlin's weapon to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_SUPER_NAILGUN;

         amount = victim.ammo_nails;
         if (amount > 40)
            amount = 40;
         victim.ammo_nails = victim.ammo_nails - amount;
         self.ammo_nails = self.ammo_nails + amount;
         self.items = self.items | IT_NAILS;
         self.currentammo = self.ammo_nails;
         sprint (victim, "Gremlin stole your Super Nailgun\n");
        }
       // if the victim didn't have any other weapon from that
	   // category, remove the category from the victim.
       if(!(victim.items3 & IT3_NAILGUN))
		victim.items = victim.items - (victim.items & best);
	  }
   else if (best == IT_GRENADE_LAUNCHER)
      {
	   if(best2 & IT3_GRENADE_LAUNCHER)
	    { 
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_GRENADE_LAUNCHER);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_GRENADE_LAUNCHER);
		 // Set Gremlin's weapon to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_GRENADE_LAUNCHER;

         amount = victim.ammo_rockets;
         if (amount > 5)
            amount = 5;
         victim.ammo_rockets = victim.ammo_rockets - amount;
         self.ammo_rockets = self.ammo_rockets + amount;
         self.items = self.items | IT_ROCKETS;
         self.currentammo = self.ammo_rockets;
         sprint (victim, "Gremlin stole your Grenade Launcher\n");
        }
       // if the victim didn't have any other weapon from that
	   // category, remove the category from the victim.
       if(!(victim.items3 & IT3_GRENADE_LAUNCHER))
		victim.items = victim.items - (victim.items & best);
	  }
   else if (best == IT_ROCKET_LAUNCHER)
      {
	   if(best2 & IT3_ROCKET_LAUNCHER)
	    { 
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_ROCKET_LAUNCHER);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_ROCKET_LAUNCHER);
		 // Set Gremlin's weapon to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_ROCKET_LAUNCHER;

         amount = victim.ammo_rockets;
         if (amount > 5)
            amount = 5;
         victim.ammo_rockets = victim.ammo_rockets - amount;
         self.ammo_rockets = self.ammo_rockets + amount;
         self.items = self.items | IT_ROCKETS;
         self.currentammo = self.ammo_rockets;
         sprint (victim, "Gremlin stole your Rocket Launcher\n");
        }
       // if the victim didn't have any other weapon from that
	   // category, remove the category from the victim.
       if(!(victim.items3 & IT3_ROCKET_LAUNCHER))
		victim.items = victim.items - (victim.items & best);
	  }
   else if (best == IT_LIGHTNING)
      {
	   if(best2 & IT3_LIGHTNING)
	    {	  
		 // Take weapon away from victim's inventory
		 victim.items3 = victim.items3 - (victim.items3 & IT3_LIGHTNING);
		 // Remove weapon from victim's weapon2
		 victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_LIGHTNING);
		 // Set Gremlin's weapon to what it just stole.
		 self.weapon2 = self.weapon2 | IT3_LIGHTNING;

         amount = victim.ammo_cells;
         if (amount > 40)
            amount = 40;
         victim.ammo_cells = victim.ammo_cells - amount;
         self.ammo_cells = self.ammo_cells + amount;
         self.items = self.items | IT_CELLS;
         self.currentammo = self.ammo_cells;
         sprint (victim, "Gremlin stole your Lightning Gun\n");
        }
       // if the victim didn't have any other weapon from that
	   // category, remove the category from the victim.
       if(!(victim.items3 & IT3_LIGHTNING))
		victim.items = victim.items - (victim.items & best);
	 }

   tempself = self;
   self = victim;
   self.weapon = W_BestWeapon ();
   W_SetCurrentAmmo ();
   self = tempself;

   // tag the gremlin as having stolen a weapon
   self.stoleweapon = TRUE;
   self.attack_finished = time;
   // don't fire the first shot at the person we stole the weapon from
   // all the time
   if (random()>0.65)
      self.lastvictim = victim;
   else
      self.lastvictim = self;
//   self.attack_state = AS_STRAIGHT;
   // find a recipient
   victim = GremlinFindVictim();
   if (victim != world)
      {
      self.enemy = victim;
      FoundTarget();
      self.attack_finished = time;
      self.search_time = time + 1.0;
      }

   return TRUE;
   };
Next, replace Gremlin DropBackpack with this:

Code: Select all

void() GremlinDropBackpack =
{
	local entity	item;

	item = spawn();
	item.origin = self.origin - '0 0 24';

   self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS ) );
   item.items = self.weapon;
   
    if (item.items == IT_AXE)
	 {
	    item.weapon2 = IT3_AXE;
		item.netname = "Axe";
	 }
	else if (item.items == IT_SHOTGUN)
	 {
	    item.weapon2 = IT3_SHOTGUN;	 
		item.netname = "Shotgun";
	 }
	else if (item.items == IT_SUPER_SHOTGUN)
	 {
	  if(self.weapon2 & IT3_NEWWEAPON)
       {
		item.weapon2 = IT3_NEWWEAPON;
		item.netname = "NewWeapon";
       }
      else
       {  
		item.weapon2 = IT3_SUPER_SHOTGUN;
		item.netname = "Double-barrelled Shotgun";
	   }
	 }
	else if (item.items == IT_NAILGUN)
	 {
		item.weapon2 = IT3_NAILGUN;
		item.netname = "Nailgun";
	 }
	else if (item.items == IT_SUPER_NAILGUN)
	 {
		item.weapon2 = IT3_SUPER_NAILGUN;
		item.netname = "Super Nailgun";
	 }
	else if (item.items == IT_GRENADE_LAUNCHER)
	 {
		item.weapon2 = IT3_GRENADE_LAUNCHER;
		item.netname = "Grenade Launcher";
	 }
	else if (item.items == IT_ROCKET_LAUNCHER)
	 {
		item.weapon2 = IT3_ROCKET_LAUNCHER;
		item.netname = "Rocket Launcher";
	 }
	else if (item.items == IT_LIGHTNING)
	 {
		item.weapon2 = IT3_LIGHTNING;
		item.netname = "Thunderbolt";
	 }
	else
		item.netname = "";

   item.ammo_shells = self.ammo_shells;
	item.ammo_nails = self.ammo_nails;
	item.ammo_rockets = self.ammo_rockets;
	item.ammo_cells = self.ammo_cells;
   if (item.ammo_shells < 0) item.ammo_shells = 0;
   if (item.ammo_nails < 0) item.ammo_nails = 0;
   if (item.ammo_rockets < 0) item.ammo_rockets = 0;
   if (item.ammo_cells < 0) item.ammo_cells = 0;

	item.velocity_z = 300;
	item.velocity_x = -100 + (random() * 200);
	item.velocity_y = -100 + (random() * 200);

	item.flags = FL_ITEM;
	item.solid = SOLID_TRIGGER;
	item.movetype = MOVETYPE_TOSS;
	setmodel (item, "progs/backpack.mdl");
	setsize (item, '-16 -16 0', '16 16 56');
	item.touch = BackpackTouch;

	item.nextthink = time + 120;	// remove after 2 minutes
	item.think = SUB_Remove;
};
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

Awesome tutorial :)
Is there a way to make a hierarchy with the "grouped" weapons - so making a auto-switch if weapon is stolen?

I will use the shotgun for example with IT3_NEWWEAPON and having IT3_NEWWEAPON2, IT3_NEWWEAPON3.

Ranking from top to bottom:
IT3_NEWWEAPON3
IT3_NEWWEAPON2
IT3_NEWWEAPON
IT3_SUPERSHOTGUN

Code: Select all

else if(best2 & IT3_NEWWEAPON)
       {
       // Take weapon away from victim's inventory
       victim.items3 = victim.items3 - (victim.items3 & IT3_NEWWEAPON);
       // Remove weapon from victim's weapon2
       victim.weapon2 = victim.weapon2 - (victim.weapon2 & IT3_NEWWEAPON);
         // if the victim has another weapon from the group,
       // switch to it for the slot.
       if(victim.items3 & IT3_SUPER_SHOTGUN)
        victim.weapon2 = victim.weapon2 | IT3_SUPER_SHOTGUN; 
The last line forces a switch to the SUPER_SHOTGUN. Now in the weapon switch command (CycleWeaponCommand/CycleWeaponReverseCommand)
Doing the whole ranking there:
If newweapon3 out, go to newweapon2, if thats out go to newweapon, if that's out go to it3_shotgun.

Now on the last line of:
victim.weapon2 = victim.weapon2 | IT3_SUPER_SHOTGUN;

Would I make that IT3_NEWWEAPON3 for ALL of the shotgun category weapons (minus itself - have it seek NEWWEAPON2). Forces a change to it, and if I force it to that - if it's not available it will automatically attempt IT3_NEWWEAPON2 and so on or would I be looking at various if statements?
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

Post by Dr. Shadowborg »

ooppee wrote:Is there a way to make a hierarchy with the "grouped" weapons - so making a auto-switch if weapon is stolen?
Yes, though currently it's set to the hipnotic default of using bestweapon to switch to another weapon. Checking and setting it to the other weapon in this case is done to prevent W_ChangeWeapon from having a Homer Simpson moment. (if / when you tried to switch back to the weapon group)
ooppee wrote: Would I make that IT3_NEWWEAPON3 for ALL of the shotgun category weapons (minus itself - have it seek NEWWEAPON2). Forces a change to it, and if I force it to that - if it's not available it will automatically attempt IT3_NEWWEAPON2 and so on or would I be looking at various if statements?
If you're saying what I think you're saying, yes.


BTW, I noticed and corrected this yesterday but forgot to mention it:

The original of this tutorial had a mistake in the setnewparms section. It set the two parms to IT_SHOTGUN | IT_AXE when it should have been IT3_SHOTGUN | IT3_AXE. I've since corrected this, but anybody who has used this tutorial should change their code accordingly.
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

http://www.sendspace.com/file/uo17t8

Here's a example of his weapon tut put to use quite heavily. 25 weapons. The 25th was added with a work around - doing this I discovered you can have 168 "different" weapons if you wanted! (and for them to appear on the HUD - CAN be more if you include the IT_AXE and other IT_ values open) It'd be more of alt ammo weapons though. You'd use the IT_ values to do the ammo, and the IT3 would be the weapons. Once a IT_ is earned, it's earned for all IT3's you have and also pick up later. So why I say it'd be for alt ammo weapons mainly.

Tutorial would have to be updated as we did find some bugs. Your tutorial worked great for 2 slot weapons, but once you got into those 3,4,5th weapon in a single slot - came some issues and then the whole ammo checking (same slot weapons with different ammo types). This is just a weapons test. Huge thanks to ShadowBorg for this. I'd find a bug, report it. He'd either do a rewrite of the area or just tell me what I need to do to fix it.
dr_mabuse
Posts: 80
Joined: Sat Sep 03, 2011 6:07 pm

Re: [C&P Tutorial] Weapon System Overhaul

Post by dr_mabuse »

Nice one, but before i use this i have a question:
What is the exact limit for "Vanilla Quakes" weapons?
10 weapons or 11 weapons total?
Post Reply