Page 1 of 1

[Tutorial] Headshots

PostPosted: Thu Jul 30, 2015 11:01 pm
by Dr. Shadowborg
Once again, presented as cut 'n paste code. If you want to know how it works, read the comments.

This code has room for improvement, however, that exercise is left to the user.

in weapons.qc, before W_FireAxe, put this:

Code: Select all
// because spawnmeatspray is defined below W_FireAxe...
void(vector org, vector vel) SpawnMeatSpray;


float(vector headorg, vector shotorg, vector shotdir, float headsize, float hitrange, float damage, float multiplier) CheckForHeadshotDamage =
   local float dist, braingibs;
   local vector endpoint;
   // find out how far away the shot on bbox is from headorigin
   dist = vlen(headorg - shotorg);
   // If the distance from shotorigin and headorigin is too
   // far away, then assume flat out that this was a miss.

   if(dist > hitrange)
    return damage;

   // If the distance is within the headsize, assume that we have
   // a hit
   if(dist <= headsize)
     return ceil(damage*multiplier);
   // Now, we must determine if the shot has hit within valid check range
   // then we need to check if the shot would have hit the head
   if(dist <= hitrange)
      // Set endpoint to compare against headorigin
      // it should go from the shotorg in the amount of dist
      // in the direction of shotdir
      endpoint = shotorg+shotdir*dist;
      // Find out if endpoint has hit within headsize
      // if so, return double damage
      if(vlen(endpoint - headorg) <= headsize)
        // throw some gibs to help highlight headshot
        braingibs = 2;
          SpawnMeatSpray(headorg, (shotdir*(150+ceil(random()*100))));
         braingibs = braingibs - 1;
        return ceil(damage*multiplier);
    // Nothing hit the head, so return normal damage
    return damage;

All targets that you intend to use this with should have .view_ofs set. For example, the same view_ofs of '0 0 22' should work fine for grunts and enforcers.

In traceattack type weapons, usage is as follows:
Code: Select all
if(trace_ent.classname == "player" || (trace_ent.flags & FL_MONSTER))
  damage = damage+CheckForHeadshotDamage((trace_ent.origin+trace_ent.view_ofs), trace_endpos, v_forward, 12, 40, damage, 4);

The above example is for W_FireAxe. Use dir with traceattack. Note that the 12 is the size of the head area, 40 is the valid range for attacks incoming. The 4 is the damage multiplier on successful hits.

For use with projectile weapons, in the touch function:
Code: Select all
mvdir = normalize(self.velocity);

if(other.classname == "player" || (other.flags & FL_MONSTER))
  damage = damage+CheckForHeadshotDamage((other.origin+other.view_ofs), self.origin, mvdir, 12, 40, damage, 2);

The above is an example for use with spike_touch or superspike_touch. Note that I recommend use of 12 for headsize and 40 for test distance in usage against player sized targets. If you are using this against odd creatures (dogs, maybe fiends), you WILL have to modify the code to make appropriate adjustments.