Created By: ShockMan
eMail: shockman@brutality.com
ICQ UIN: 1017564
Difficulty Scale: Medium\Advanced


Intruduction
Well, this will enable a new weapon when you press '4' twise. The weapon is erh... totaly insane... J


Step 1
First open up weapons.qc and find
void() superspike_touch =
{
	blah... blah...
};
and after that function add:


.entity owner2;
.float weapon2;

void() rifle_rebound =
{
	self.velocity = normalize(self.velocity) * 1000;
	self.movetype = MOVETYPE_FLYMISSILE;
	self.angles = vectoangles(self.velocity);
	self.think = SUB_Remove;
	self.nextthink = self.ltime;
	
	self.owner = world;
};

void() rifle_touch =
{
	local float rand;

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

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

	// hit something that bleeds
	if (other.takedamage)
	{
		spawn_touchblood (14);
		self.owner = self.owner2;
    		T_Damage (other, self, self.owner, 14);	
	}
	else
	{
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
		WriteCoord (MSG_BROADCAST, self.origin_x);
		WriteCoord (MSG_BROADCAST, self.origin_y);
		WriteCoord (MSG_BROADCAST, self.origin_z);
	
		if (self.aflag == 10)
		{
			remove(self);
			return;
		}

		self.aflag = self.aflag + 1; // count bounces
	}
	
	// rebound
	self.movetype = MOVETYPE_BOUNCE;
	self.think = rifle_rebound;
	self.ltime = self.nextthink;
	self.nextthink = time + 0.1;
};

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

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

	newmis.velocity = dir * 1000;
};

void() W_FireRifle =
{
	local vector	dir;
	local entity	old;
	
	makevectors (self.v_angle);

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

	self.owner2 = self.owner;

	sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
	self.currentammo = self.ammo_nails = self.ammo_nails - 3;
	dir = aim (self, 1000);
	launch_spike2 (self.origin + '0 0 16' + v_right*6, dir);
	self.punchangle_x = -2;
};


Step 2
If you are lazy and don't wan't to learn anything just go to step 7. But for the little
more advanced people i will now explain what this does (WARNIG: if you don't want to learn
anything from this tutorial skip this step!!)

You still here? so u wanna learn something... ok, we start from the begining... first we have:

.entity owner2;
.float weapon2;
this just declares a entity variable, and a float variable, and since they aren't inside a function it is just like we would
have typed it in defs.qc, but sometimes its better to have them with the other code, so you can access
them easy...

Then we need to jump down to the line (since its the function that will be called first its most logical
we start with it):

void() W_FireRifle =  This line starts the function (like u didn't know)
{
	local vector	dir;  define a vector
	local entity	old;  define a entity
	
	makevectors (self.v_angle); This makes v_forward the forward
	 				       looking angle, v_right to your right and v_up is up.

	if (self.ammo_nails < 3) if you don't have 3 nails... 
	{
		self.weapon = W_BestWeapon ();  ...change to your best weapon, that you
             					                have ammo for.
		W_SetCurrentAmmo (); Set your ammo to the correct one.
		return;  and exit this function.
	}

	self.owner2 = self.owner;  its needed to create a "false" .owner field, you will notice
					   why soon

	sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);  play a sound
	self.currentammo = self.ammo_nails = self.ammo_nails - 3;  Remove the 3 nails
	dir = aim (self, 1000);  set dir to your aiming angle. NOTE: if you want the weapon to
				      autoaim (up/down) change 1000 to 10000 
	launch_spike2 (self.origin + '0 0 16' + v_forward*4, dir); Call our new Launch_Spike
                   							         function. first parameter 
          									 (self.origin + '0 0 16' + v_forward*4)
      									 	 is where to shoot from, self.origin is
    										 your origin, '0 0 16' meens +16 units on
 									         the Z axel (up), and v_forward*4 is abit
   									         forward, so it don't start in the middle
 									         of you
	self.punchangle_x = -2; make you shake abit (can be set lower if you want...
	                               The lower the more shake
};  end the function


Step 3
Ok, that was the first function... lets get on with it...


void(vector org, vector dir) launch_spike2 = here is our new launch_spike function, it takes
						           2 parameters, first a vector; org, its the origin where to
     						           start from, then another vector (actualy its not a vector
	                			           its a angle); dir, the direction to fire.
{
	newmis = spawn (); Spawn a new entity
	newmis.owner = self; set the entity's owner to the player (self)
	newmis.movetype = MOVETYPE_FLYMISSILE; make the new entity a flying entity
	newmis.solid = SOLID_BBOX; and a solid entity

	newmis.angles = vectoangles(dir);  make the entity face the way it is traveling (very useful!!)
	
	newmis.touch = rifle_touch; call rifle_touch if the entity touches something
	newmis.classname = "spike"; set the classname to "spike"
	newmis.think = SUB_Remove; set the think function to remove the entity...
	newmis.nextthink = time + 14; ... after 14 sec
	setmodel (newmis, "progs/spike.mdl");  a model for the entity
	setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);  the entites size		
	setorigin (newmis, org);  the entities start position

	newmis.velocity = dir * 1000; And finally, set it on its way!! (entity.velocity = dir * float is a
			              		  very useful formula...)
};

Step 4
That wasn't to bad, was it? but now we come to the real stuff.


void() rifle_touch =  this function is called when the projectile hits something
{
	if (other.solid == SOLID_TRIGGER)
		return;	trigger field, do nothing

	if (pointcontents(self.origin) == CONTENT_SKY)
	{
		remove(self);  remove the projectile if it hits a sky
		return;  and exit the function
	}

	if (other.takedamage) If we hit something that bleeds (monsters, buttons...)
	{
		spawn_touchblood (14);  spawn some blood
		self.owner = self.owner2;  Here is he fake owner again!! (don't worry to much about
            					   it yet)
    		T_Damage (other, self, self.owner, 14);	 do damage to other (the thing that is being
                                                                        hit), and it is self (the projectile) that does
     									the damage, but it is self.owner (the owner of
   									the projectile that gets the frag, or the monsters
     									get angry at)
	}
	else  if it doesn't bleed
	{
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
		WriteCoord (MSG_BROADCAST, self.origin_x);
		WriteCoord (MSG_BROADCAST, self.origin_y);
		WriteCoord (MSG_BROADCAST, self.origin_z);
		
		These lines sends a message to quake that tells quake to spawn a "TE_SUPERSPIKE"
                (its some small grey dots...) you can try to replace it with TE_EXPLOSION, just to 
                see what it does
	
		if (self.aflag == 10) we dont want the projectile to sit in a corner and bounce,
                 			    so remove it after 10 bounces... (this isn't nessesary, 'couse we did
       					    a think function that removes the spike after 14 sec, remember?)
		{
			remove(self);
			return;
		}

		self.aflag = self.aflag + 1; add one to the bounce count

		Just a little note here on .aflag, it is a float variable that is defined in defs.qc, but it is
                only used for doors, so it can be used for some other stuff. if you wan't to save on
                floats (its a good thing to do so, 'couse each .float, .entity or .vector you define
                takes up memory and to many of them might slow things down.) Just be careful what you use
                .aflag for so you dont use it for more than one thing on each entity...
	}
	
	Now, for the Bounce!!
	self.movetype = MOVETYPE_BOUNCE; First set the projectiles movetype to MOVETYPE_BOUNCE
       						           (it will make it bounce, *doh*)
	self.think = rifle_rebound; then set the projectile's think to the bounce code, 'couse we dont
 			                    want it to go there at ones, it gotta get some time to bounce first
	self.ltime = self.nextthink; then we need to store the nextthink in a new variable (so it don't 
					    forget how long it have been flying
	self.nextthink = time + 0.1; and set the nextthink to time + 0.1 (thats about one frame)
};



Step 5
Ok, now for fire_rebound, this is the actual bounce code... first you might wanna know this:

InAngle
| /
|/
|--------------- NORMAL
|\
| \
OutAngle

This shows how "bounceing" works, the main thing this is for is light beems (and other things that aint
affected by gravity?)
The basic thing is that the "inangle" is always the same as the "outangle" but in "reverse" or what I shall
call it... (im not to good at this stuff, hehe...)

Well... back to the code...


void() rifle_rebound =
{
	self.velocity = normalize(self.velocity) * 1000; This first Normalizes (look below) the
 	      							       projectiles velocity then it multiply it by 
								       1000 again...

 Normalize: it returns a vector with the length of 1, collinear to the given vector...
					            
	self.movetype = MOVETYPE_FLYMISSILE; set the movetype to MOVETYPE_MISSILE 
     						               again (else the spike would only bounce 
							       down to the floor, like a grenade)
	self.angles = vectoangles(self.velocity); You have seen this before... yupp, make the
           						     spike face the direction its moveing
	self.think = SUB_Remove; and set the think to SUB_Remove again (so it removes
       					    14 sec after it was shoot)
	self.nextthink = self.ltime;  get how much time it was left
	
	self.owner = world; set self.owner to world (world = nothing... well, almost)
};

Step 6
Ok, now it might be time to explain why we had to use self.owner2, simple... self.owner = the player, then the spike would go right though him and not hurt him... thats no fun... so we set self.owner2 to self.owner and when we shall use self.owner we just set it to self.owner2, got that? if not... don't worry. its not that important.

Well... now we have the Main code there, now its just a few small things to fix before we can get it to work.

Step 7
Now find
void() W_Attack

and replace:
else if (self.weapon == IT_NAILGUN)
{
	player_nail1 ();
}
with:
else if (self.weapon == IT_NAILGUN && self.weapon2 == 0)
{
	player_nail1 ();
}
else if (self.weapon == IT_NAILGUN && self.weapon2 == 1)
{
	player_shot1 ();
	W_FireRifle ();
	self.attack_finished = time + 0.8; // you can change this to make the weapon fire faster
}



Step 8
Now find
void() W_ChangeWeapon

and replace:
else if (self.impulse == 4)
{
	fl = IT_NAILGUN;
	if (self.ammo_nails < 1)
		am = 1;
}
with:
else if (self.impulse == 4)
{
	fl = IT_NAILGUN;
	if (self.weapon2 == 0)
	{
		self.weapon2 = 1;
		if (self.ammo_nails < 3)
			am = 1;
	}		
	else
	{
		self.weapon2 = 0;
		if (self.ammo_nails < 1)
			am = 1;
	}
}

Step 9
Now, thats it!! compile as usual and enjoy... J

If you eighter are to lazy to do this or want to use this weapon with a really cool new models and sounds (all by me =), download it here

And if you find this useful, or use this code somewere... just send me a email. and maybe include my name somewere too?
Bye... and please send me some feedback on this, ok?, ShockMan