Created By: legion
eMail: legion@keg.zymurgy.org
Difficulty Scale: Easy


This is a how-to guide on improving the grunt. You will be changing the weapons that the grunts will be using as well as making it move while in combat. This should make the grunts just a tad bit more difficult to kill. The things you learn in this lesson can be applied to other monsters as well. If you wish to make a bot, the lessons learned here should be applied to your bot to make it move while in combat.

Step 1
Using your favorite editor, open the file called fight.qc. At the end of this file, you will be creating a new function. We will call this function monster_movetogoal.
At the bottom of the file, add the following:
      void(float  dist) monster_movetogoal =
      {
         local float ofs;

         if (vlen (self.enemy.origin - self.origin) > 180)
         {
            movetogoal (dist);
            return;
         }

         if (self.lefty)
            ofs = 90;
         else
            ofs = -90;
         
         if (walkmove (self.angles_y + ofs, dist) )
            return;

         self.lefty = 1 - self.lefty;

         walkmove (self.angles_y - ofs, dist);

      };
The above function should bear a pretty strong simularity to another function called ai_run_slide. You can find this function in ai.qc.
The function vlen is used to determine the length of a vector. This length is always positive. The vector that we are using is the vector from the enemy's origin to the monster's origin. The length of this vector, then, tells how far the target is from the monster.
The function movetogoal is the function that moves the entity a certain amount per frame. The larger the value, the faster the entity will move.
The function walkmove is like the movetogoal function, it can move the entity a certain amount per frame. Unlike movetogoal, you specify the direction it moves as well as the amount. In addition, if the entity can move in that direction, it will return a value of TRUE. If it can not move, it will return a value of FALSE. The movetogoal function does not return any value when executed.
Step 2
Using your favorite editor, open the file called soldier.qc. You will be searching for the functions used when the soldier attacks. In this case, you will be looking for army_atk1 to army_atk9 functions. In each of those functions, you will be adding a call to the function monster_movetogoal that you created.
Find the army_atk1 function. You will be adding the monster_movetogoal function call by changing the function this way:
BEFORE
void() army_atk1 = [$shoot1, army_atk2] {ai_face ();};
AFTER
void() army_atk1 = [$shoot1, army_atk2] {ai_face (); monster_movetogoal (15);};
The modification is highlighted in BLUE. Make sure you add the function call AFTER ai_face.
The function ai_face is the function that makes the monster face its target. Unlike walkmove, vlen, and movetogoal this is a function written in QuakeC. You can find this function in fight.qc.
Step 3
After you have made the changes to the army_atk1 to army_atk9 functions, search for the function called army_fire. Delete this entire function. You will be replacing it with a new army_fire function.
After deleting the function, replace it with this:
      void() army_fire =
      {
         local vector   dir;
         local entity   en;

         ai_face ();

         sound (self, CHAN_WEAPON, "soldier/sattcl1.wav", 1, ATTN_NORM);

         en = self.enemy;

         dir = en.origin - self.origin;
         dir = normalize (dir);

         self.v_angle = vectoangles (dir);

         if (self.v_angle_x > 180)
            self.v_angle_x = self.v_angle_x - 360; // corrects an ID mistake

         self.v_angle_x = self.v_angle_x * -1;     // corrects an ID mistake

         if (self.v_angle_x > 80)
            self.v_angle_x = 80;

         if (self.v_angle_x < -80)
            self.v_angle_x = -80;

         self.ammo_cells = 10;
         self.ammo_rockets = 5;

         if (vlen (self.enemy.origin - self.origin) > 250)
         {
            W_FireRocket ();
         }
         else
         {
            makevectors (self.v_angle);
            W_FireLightning ();
         }
      };
The function normalize converts a vector of a certain length to the vector of length one. A vector has two components: the direction and the magnitude. Velocity, for instance, is a vector. A velocity has a direction and a magnitude we often call speed. The normalize function, then, changes this vector in such a way so that only its magnitude changes but not its direction.
The function vectoangles calculates the pitch, the bearing, and roll of a given vector. To give you an idea what these three things are, the pitch is how much you move your head up or down. That is, you move your chin up or down. The bearing is how much you turn your neck left or right. And the roll is how much you turn your head side ways. That is, you turn your neck so that one of your ears is now "pointing" towards the ground while the other ear now points up. Since this function calculates three components, these components are conveniently stored as a vector. You, as a human, must keep track of this when you make your patch. In the above function, the vector self.v_angle stores these three components. This vector is not the same as the vector dir which has a direction and magnitude. The vector self.v_angle is used to store the pitch, yaw, and roll.
The function makevectors calculates three vectors using a given pitch, yaw, and roll. Remember, the pitch, yaw, and roll are stored in a single vector. So the parameter that makevector uses is a vector. Now this function takes this one vector and calculates three new vectors. These vectors are called V_FORWARD, V_UP, and V_RIGHT. These vectors are true vectors. That is, these vectors have a direction and magnitude. The magnitude of each vector is one. We often call such vectors unit vectors.
The functions W_FireRocket and W_FireLightning fires the rocket and lightning gun respectively. The W_FireLightning function uses the vector V_FORWARD to determine which direction to fire. W_FireRocket uses the self.v_angle vector. These two functions can be found in weapons.qc.
You are finished with the tutorial. Compile and have fun.