Hunter-Killers (homing rockets)

1996-06-22 InsideQC tutorials kleskby 0 91
Author: Sestze
Source: https://www.insideqc.com/qctut/qctut-83.shtml


Step 1.
Add the following code to weapons.qc, near the top.


/*
=================
Hunter Killer
By: Sestze (enjoy!)
=================
*/

//Function Prototypes
void() W_HunterFire;
void() T_HunterTouch;
void() S_HunterThink;
entity(entity startz, float cr_range, float wall) Checkrad;

//Fire Function
void() W_HunterFire =
{
 sound(self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM); //Classic rocket fire
 
 self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; //Subtract a rocket
 
 local entity hunter; //Make an entity
 
 hunter = spawn(); //Spawn it
 hunter.classname = "Hunter killer"; //Give it a classname
 hunter.owner = self; //You own this baby.
 hunter.oldenemy = self; //You will need this
 hunter.movetype = MOVETYPE_FLY; //Flies around
 hunter.solid = SOLID_BBOX; //Solid - Touch on edge, block (Pickable items and missiles)
 
 makevectors(self.v_angle); //Make vectors from your current viewing angle
 hunter.velocity = v_forward * 240; //Fly forward at 240 units (trust me, you will want this)
 hunter.angles = vectoangles(hunter.velocity); //Set the angles for the object
 
 hunter.nextthink = time + 0.25; //Quarter second out, activate
 hunter.think = S_HunterThink; //Activation and thinking function
 
 hunter.touch = T_HunterTouch; //Touch Function
 
 setmodel(hunter, "progs/missile.mdl"); //Set the model
 setsize(hunter, '0 0 0', '0 0 0'); //Set the size
 setorigin(hunter, self.origin + '0 0 16'); //Set the origin
};

//Touch function
void() T_HunterTouch =
{
 if(other == self.owner) //Don't want it blowin up on ya, do ya?
 return;

 T_RadiusDamage (self, self.oldenemy, 100, world); //Damage!
 
 WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); //Temp entity - Explosion
 WriteByte (MSG_BROADCAST, TE_EXPLOSION);
 WriteCoord (MSG_BROADCAST, self.origin_x);
 WriteCoord (MSG_BROADCAST, self.origin_y);
 WriteCoord (MSG_BROADCAST, self.origin_z);
 BecomeExplosion();
};

//Think code
void() S_HunterThink =
{
 //Hunter finds an enemy
 if((self.enemy == world) || (self.enemy.health < 0)) //Checks if enemy is dead or doesnt have enemy
 self.enemy = Checkrad(self, 1000, TRUE);
 
 //Declarations
 local vector vele, vels; //velocity enemy, velocity self
 local vector ph; //Place holder
 
 //The tack to opponent
 vele = (self.enemy.origin + '0 0 16') - self.origin; //Finds the unit vector towards the enemy
 vele = normalize(vele);
 
 //Current tack
 vels = normalize(self.velocity); //defines current vector
 
 //Check tack to enemy
 traceline(self.origin, self.origin + vele * 10000, FALSE, self); //Traceline to enemy
 
 if((trace_fraction != 0) && (self.enemy != world))
 {
 //Placeholder
 ph = vele + vels + vels + vels + vels; //Enemy velocity plus 4 times self velocity
 
 //Placeholder over three
 ph = ph * (1/5); //Averaging
 
 //Setvelocity and here we go! 
 self.velocity = normalize(vels + ph); //Set the velocity
 self.velocity = self.velocity * 240; //And you are on your way
 }
 
 //Check current tack 320 units ahead
 traceline(self.origin, self.origin + vels * 320, FALSE, self); //Traceline in front (check for walls)
 
 if((trace_fraction < 0.2))
 {
 ph = trace_plane_normal; //Find direction that is perpindicular to the wall
 ph = ph + vels + vels; //Add that direction plus two times your own
 ph = ph * (1/3); //Averaging
 
 self.velocity = normalize(vels + ph); //Set the velocity
 self.velocity = self.velocity * 240; //And you are on your way
 }
 
 //Set the angles 
 self.angles = vectoangles(self.velocity); //Set angle
 self.aflag = self.aflag + 1; //Add to "idle hands" counter
 
 if(self.aflag >= 500) //5.25 seconds is up....
 {
 self.owner = world; //No more owner, it will attack spawner
 }
 
 //Hunter thinks
 self.nextthink = time + 0.01; //Nextthink will be quick to avoid running into walls
};

//Check radius function
entity(entity startz, float cr_range, float wall) Checkrad =
{
 local entity finder; //The .chain entity
 local float cr_ran; //The range holder
 local entity blodger; //Entity placeholder
 cr_ran = 1200; //Set to above the detection radius
 
 finder = findradius(startz.origin, cr_range); //Finds anything within cr_range units of self
 
 while(finder)
 {
 if((CanDamage(finder, self)) || (!wall)) //If I can see him, or if I just ignore walls alltogether
 {
 if(finder.health > 0) //If he lives
 {
 if(finder != self.owner) //If he aint my daddy
 {
 if(vlen(finder.origin - self.origin) < cr_ran) //If the range of that guy is less than the previous
 {
 blodger = finder; //Blodger holds who that is
 cr_ran = vlen(finder.origin - self.origin); //resets cr_ran for next time
 }
 }
 }
 }
 finder = finder.chain; //Loop chain
 }
 if(blodger != world) //If Blodger isn't the world
 return blodger; //blodger is returned
 else //If blodger = world
 return world; //return the world
};


Step 2.
Scroll down to this line: 977 of weapons.qc. Where it should read: W_FireRocket(); Replace W_FireRocket with W_HunterFire();

 

A quick rundown of what happens (in english):

The Missile is spawned via the W_HunterFire() function, and it flys at 240 units, it constantly checks a radius of 1000 units for an enemy or other silly bugger, and targets them. It then flies towards the target, trying to avoid walls and other odd things on the way. On contact, it makes itself into an explosion, then calls the T_RadiusDamage function, dealing 100 damage, at the epicenter.

The reason why I used an entity function to find opponents was for simplicity for any other homing weapons in future. It finds anything within a giant radius (or a small one, your call), and finds the closest one possible.

Alright, I'm done here, Have fun with new rockets that fly after opponents.



Tags: tutorial, quakec, qc, insideqc
Owned by kleskby#1837
Telegram
Close

You have no new notifications.


You have no new messages.