[Tutorial] Headshots

Discuss programming in the QuakeC language.
Post Reply
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

[Tutorial] Headshots

Post 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;

/*
================
CheckForHeadshotDamage
================
*/

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;
		  while(braingibs)
		   {
		    SpawnMeatSpray(headorg, (shotdir*(150+ceil(random()*100))));
			braingibs = braingibs - 1;
		   }
		  
		  return ceil(damage*multiplier);
		 }
	 }
	 
	 // Nothing hit the head, so return normal damage
	 return damage;
};
Usage:
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.
Post Reply