PlanetQuake | Code3Arena | Tutorials | << Prev | Tutorial 14 | Next >>


  • Home/News
  • ModSource
  • Compiling
  • Help!!!
  • Submission
  • Contributors
  • Staff
  • Downloads

    < Index >
    1. Mod making 101
    2. Up 'n running
    3. Hello, QWorld!
    4. Infinite Haste
    5. Armor Piercing Rails
    6. Bouncing Rockets
    7. Cloaking
    8. Ladders
    9. Favourite Server
    10. Flame Thrower
    11. Vortex Grenades
    12. Grapple
    13. Lightning Discharge
    14. Locational Damage
    15. Leg Shots
    16. Weapon Switching
    17. Scoreboard frag-rate
    18. Vortex Grenades II
    19. Vulnerable Missiles
    20. Creating Classes
    21. Scrolling Credits
    22. Weapon Dropping
    23. Anti-Gravity Boots
    24. HUD scoreboard
    25. Flashlight and laser
    26. Weapon Positioning
    27. Weapon Reloading
    28. Progressive Zooming
    29. Rotating Doors
    30. Beheading (headshot!)
    31. Alt Weapon Fire
    32. Popup Menus I
    33. Popup Menus II
    34. Cluster Grenades
    35. Homing Rockets
    36. Spreadfire Powerup
    37. Instagib gameplay
    38. Accelerating rockets
    39. Server only Instagib
    40. Advanced Grapple Hook
    41. Unlagging your mod

    < Index >
    1. Entities
    2. Vectors
    3. Good Coding
    4. Compilers I
    5. Compilers II
    6. UI Menu Primer I
    7. UI Menu Primer II
    8. UI Menu Primer III
    9. QVM Communication, Cvars, commands
    10. Metrowerks CodeWarrior
    11. 1.27g code, bugs, batch


  • Quake3 Files
  • Quake3 Forums
  • Q3A Editing Message Board
  • Quake3 Editing


  • SumFuka
  • Calrathan
  • HypoThermia
  • WarZone

    Site Design by:
    ICEmosis Design

    TUTORIAL 14 - Locational Damage!
    by Calrathan

    Locational damage isn't really a big thing if you're just trying to make a DM mod variant, but concidering the large volume of realism mods on the way, I've made some basic locational damage code which has a lot of room for expansion. There are flaws with this method, but unfortunately nothing is perfect. The main problem I'm speaking of is the ORBB model, and how it's structured differently from the human forms. So, as I said before: This tutorial is mainly for those people who are working on a realism mod, not a DM variant. I don't know about you, but I haven't seen a huge walking eyeball in real life lately. Anyway, down to business.


    You can't get down to the nitty gritty without first getting your variables declared and definitions made. In this case, we're going to define a list of binary flags, matching different body locations. The way I set this up is to break the body into multiple vertical layers. Then I broke it up into four quadrants designating which side the attack came from: Front, Left, Right, Back. For those who don't know what binary flags are, they're values which when added to an integer, will turn on only a single bit. 1 turns on 00000001, 2 turns on 00000010, 4 turns on 00000100, and so on. But back to the task at hand. Let's define our flags in bg_public.h around line 435. We're putting it in this file so, just like the MOD [means of death], any file can determine where a client was hit.

    // How many players on the overlay
    #define TEAM_MAXOVERLAY		8
    #define LOCATION_NONE		0x00000000
    // Height layers
    #define LOCATION_HEAD		0x00000001 // [F,B,L,R] Top of head
    #define LOCATION_FACE		0x00000002 // [F] Face [B,L,R] Head
    #define LOCATION_SHOULDER	0x00000004 // [L,R] Shoulder [F] Throat, [B] Neck
    #define LOCATION_CHEST		0x00000008 // [F] Chest [B] Back [L,R] Arm
    #define LOCATION_STOMACH	0x00000010 // [L,R] Sides [F] Stomach [B] Lower Back
    #define LOCATION_GROIN		0x00000020 // [F] Groin [B] Butt [L,R] Hip
    #define LOCATION_LEG		0x00000040 // [F,B,L,R] Legs
    #define LOCATION_FOOT		0x00000080 // [F,B,L,R] Bottom of Feet
    // Relative direction strike came from
    #define LOCATION_LEFT		0x00000100
    #define LOCATION_RIGHT		0x00000200
    #define LOCATION_FRONT		0x00000400
    #define LOCATION_BACK		0x00000800
    // means of death
    typedef enum {

    Sorry, very sorry. When I first uploaded the tutorial I forgot an important definition. You need to define a variable we'll be using which belongs in the client struct. Open up g_local.h and go to somewhere around line 261.

    int		lasthurt_client;	// last client that damaged this client
    int		lasthurt_mod;		// type of damage the client did
    int		lasthurt_location;	// Where the client was hit.
    // timers
    int		respawnTime;		// can respawn when time > this, force after g_forcerespwan
    int		inactivityTime;		// kick players when time > this
    qboolean	inactivityWarning;	// qtrue if the five seoond warning has been given
    int		rewardTime;		// clear the EF_AWARD_IMPRESSIVE, etc when time > this


    There we go. All defined up. Now let's make our function to actually do the checking for the location the damage came from. I'd suggest putting it right before void G_Damage() [mislabed in the comment as T_Damage()] which is around line 415 in g_combat.c. Yes, this is a server file, so make sure you're in the GAME module. From here its a matter of copy/paste.
    int G_LocationDamage(vec3_t point, gentity_t* targ, gentity_t* attacker, int take) {
    	vec3_t bulletPath;
    	vec3_t bulletAngle;
    	int clientHeight;
    	int clientFeetZ;
    	int clientRotation;
    	int bulletHeight;
    	int bulletRotation;	// Degrees rotation around client.
    				// used to check Back of head vs. Face
    	int impactRotation;
    	// First things first.  If we're not damaging them, why are we here? 
    	if (!take) 
    		return 0;
    	// Point[2] is the REAL world Z. We want Z relative to the clients feet
    	// Where the feet are at [real Z]
    	clientFeetZ  = targ->r.currentOrigin[2] + targ->r.mins[2];	
    	// How tall the client is [Relative Z]
    	clientHeight = targ->r.maxs[2] - targ->r.mins[2];
    	// Where the bullet struck [Relative Z]
    	bulletHeight = point[2] - clientFeetZ;
    	// Get a vector aiming from the client to the bullet hit 
    	VectorSubtract(targ->r.currentOrigin, point, bulletPath); 
    	// Convert it into PITCH, ROLL, YAW
    	vectoangles(bulletPath, bulletAngle);
    	clientRotation = targ->client->ps.viewangles[YAW];
    	bulletRotation = bulletAngle[YAW];
    	impactRotation = abs(clientRotation-bulletRotation);
    	impactRotation += 45; // just to make it easier to work with
    	impactRotation = impactRotation % 360; // Keep it in the 0-359 range
    	if (impactRotation < 90)
    		targ->client->lasthurt_location = LOCATION_BACK;
    	else if (impactRotation < 180)
    		targ->client->lasthurt_location = LOCATION_RIGHT;
    	else if (impactRotation < 270)
    		targ->client->lasthurt_location = LOCATION_FRONT;
    	else if (impactRotation < 360)
    		targ->client->lasthurt_location = LOCATION_LEFT;
    		targ->client->lasthurt_location = LOCATION_NONE;
    	// The upper body never changes height, just distance from the feet
    		if (bulletHeight > clientHeight - 2)
    			targ->client->lasthurt_location |= LOCATION_HEAD;
    		else if (bulletHeight > clientHeight - 8)
    			targ->client->lasthurt_location |= LOCATION_FACE;
    		else if (bulletHeight > clientHeight - 10)
    			targ->client->lasthurt_location |= LOCATION_SHOULDER;
    		else if (bulletHeight > clientHeight - 16)
    			targ->client->lasthurt_location |= LOCATION_CHEST;
    		else if (bulletHeight > clientHeight - 26)
    			targ->client->lasthurt_location |= LOCATION_STOMACH;
    		else if (bulletHeight > clientHeight - 29)
    			targ->client->lasthurt_location |= LOCATION_GROIN;
    		else if (bulletHeight < 4)
    			targ->client->lasthurt_location |= LOCATION_FOOT;
    			// The leg is the only thing that changes size when you duck,
    			// so we check for every other parts RELATIVE location, and
    			// whats left over must be the leg. 
    			targ->client->lasthurt_location |= LOCATION_LEG; 
    		// Check the location ignoring the rotation info
    		switch ( targ->client->lasthurt_location & 
    		case LOCATION_HEAD:
    			take *= 1.8;
    		case LOCATION_FACE:
    			if (targ->client->lasthurt_location & LOCATION_FRONT)
    				take *= 5.0; // Faceshots REALLY suck
    				take *= 1.8;
    			if (targ->client->lasthurt_location & (LOCATION_FRONT | LOCATION_BACK))
    				take *= 1.4; // Throat or nape of neck
    				take *= 1.1; // Shoulders
    		case LOCATION_CHEST:
    			if (targ->client->lasthurt_location & (LOCATION_FRONT | LOCATION_BACK))
    				take *= 1.3; // Belly or back
    				take *= 0.8; // Arms
    			take *= 1.2;
    		case LOCATION_GROIN:
    			if (targ->client->lasthurt_location & LOCATION_FRONT)
    				take *= 1.3; // Groin shot
    		case LOCATION_LEG:
    			take *= 0.7;
    		case LOCATION_FOOT:
    			take *= 0.5;
    	return take;
    If you want to look deeper, you'll see that all I've done is use the location and height of the player to determine the location the damage was inflicted, relative to the player's feet. Ducking is compensated for because only the legs change size when you duck. If you don't believe me, go into 3rd person view and check for yourself. And don't spend too much time staring at mynx's butt while you're at it. Anyway, after we split the body up into layers, we split it up into the four quadrants. We did this by drawing a vector in the direction of the bullet's entrance point, from the center of our player. We convert the vector to angles, which gives us PITCH, YAW, and ROLL. We simply take the YAW of the player compared to the YAW of the bullet, and we can determine the angle it struck from. Easy, no?


    We've made our function, but it still does nothing. Why? Because we haven't called it of course! Well, that's an easy fix. Stay in g_combat.c, and go to the middle of G_Damage(), more specifically, somewhere around line 730. Just add the red lines to make it work right.


    The code below was modified since the original tutorial was uploaded. If you have already used this tutorial before this addendum was added, it is HIGHLY suggested you make the change in your code. The error within can cause crashes if a dead body is shot. The first IF statement originally was " if (point && targ && attacker && take) ". The corrected format adds a check for a client with no health [dead], and is as follows: " if (point && targ && targ->health > 0 && attacker && take) ". Good Luck, and thanks for the bug report Leon.

    	// See if it's the player hurting the emeny flag carrier
    	Team_CheckHurtCarrier(targ, attacker);
    	if (targ->client) {
    		// set the last client who damaged the target
    		targ->client->lasthurt_client = attacker->s.number;
    		targ->client->lasthurt_mod = mod;
    		// Modify the damage for location damage
    		if (point && targ && targ->health > 0 && attacker && take)
    			take = G_LocationDamage(point, targ, attacker, take);
    			targ->client->lasthurt_location = LOCATION_NONE; 
    	// do the damage
    	if (take) {
    		targ->health = targ->health - take;
    		if ( targ->client ) {
    			targ->client->ps.stats[STAT_HEALTH] = targ->health;
    That's it for just modifying the damage for locations, and the end of this tutorial. Unless there's much protest, I'll just leave the obituary messages and things such as locational armor to you mod authors. Good luck, and good coding.

    PlanetQuake | Code3Arena | Tutorials | << Prev | Tutorial 14 | Next >>