Click for more information!

Code3Arena

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

menu

  • News
  • Staff
  • Contributors
  • Compiling
  • Help!!!
  • Submission
  • Downloads

    Tutorials
    < 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


      Articles
      < Index >
      1. Entities
      2. Vectors
      3. Good Coding
      4. Compilers I
      5. Compilers II


      Links

    1. Quake3 Files
    2. Quake3 Forums
    3. Q3A Editing Message Board
    4. Quake3 Editing


      Feedback

    5. SumFuka
    6. Calrathan
    7. HypoThermia
    8. AssKicka
    9. WarZone





      Site Design by:
      ICEmosis Design


    10.  
      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.

      1. SETTING UP

      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 {
      	MOD_UNKNOWN,
      	MOD_SHOTGUN,
      	MOD_GAUNTLET,
      
      
      ADDENDUM!

      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
      

      2. CHECKING THE LOCATION

      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.
      
      /* 
      ============
      G_LocationDamage
      ============
      */
      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;
      	else
      		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;
      		else
      			// 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 & 
      				~(LOCATION_BACK | LOCATION_LEFT | LOCATION_RIGHT | LOCATION_FRONT) )
      		{
      		case LOCATION_HEAD:
      			take *= 1.8;
      			break;
      		case LOCATION_FACE:
      			if (targ->client->lasthurt_location & LOCATION_FRONT)
      				take *= 5.0; // Faceshots REALLY suck
      			else
      				take *= 1.8;
      			break;
      		case LOCATION_SHOULDER:
      			if (targ->client->lasthurt_location & (LOCATION_FRONT | LOCATION_BACK))
      				take *= 1.4; // Throat or nape of neck
      			else
      				take *= 1.1; // Shoulders
      			break;
      		case LOCATION_CHEST:
      			if (targ->client->lasthurt_location & (LOCATION_FRONT | LOCATION_BACK))
      				take *= 1.3; // Belly or back
      			else
      				take *= 0.8; // Arms
      			break;
      		case LOCATION_STOMACH:
      			take *= 1.2;
      			break;
      		case LOCATION_GROIN:
      			if (targ->client->lasthurt_location & LOCATION_FRONT)
      				take *= 1.3; // Groin shot
      			break;
      		case LOCATION_LEG:
      			take *= 0.7;
      			break;
      		case LOCATION_FOOT:
      			take *= 0.5;
      			break;
      
      		}
      	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?

      2. MAKING THE CALL

      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.
      	// 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 && attacker && take)
      			take = G_LocationDamage(point, targ, attacker, take);
      		else
      			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 >>