TUTORIAL 14 - Trefferzonen

von Calrathan


Trefferzonen zu implementieren ist keine grosse Sache, wenn man nur einen DM mod basteln will. Aber angesichts der Menge an Realismus mods, die entwickelt werden/wurden, habe ich mich entschlossen etwas grundlegenden Trefferzonencode zu schreiben, der sich in vielerlei Hinsicht weiterentwickeln lässt. Natürlich hat diese Methode Nachteile aber nichts ist perfekt. Das wichtigste Problem ist das Orbb Model, bzw. sein struktureller Unterschied zu den "humanen" Models. Wie gesagt: dieses Tutorial richtet sich an diejenigen, die an einem Realismus mod arbeiten, nicht an DM mod Entwickler. Keine Ahnung wie das bei euch aussieht, aber ich habe im real life noch kein riesiges Auge herumlaufen sehen, wenigstens nicht nüchtern :)


1. Der Grundstein

Bevor wir zu den entscheidenden Stellen kommen, brauchen wir erst einmal ein paar Variablen und Definitionen. Um genau zu sein brauchen wir einige Bitflags, entsprechend verschiedenen Körperregionen. Ich habe dazu den Körper in mehrere vertikale Schichten unterteilt. Dann habe ich ihn in 4 Quadranten unterteilt, um festzustellen zu können, von welcher Seite der Treffer kommt: "LEFT" links "RIGHT" rechts "FRONT" vorn "BACK" hinten. Wir definieren die Flags in bg_public.h ca. bei Zeile 540. Wir nutzen diese Datei, damit wir von überall aus auf diese Definitionen zugreifen können.


// 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,

Ausserdem brauchen wir noch eine Variable zum speichern der Zone des letzen Treffers, suche dazu die Struktur gclient_s in g_local.h ca. Zeile 280, und füge folgende Zeile ein:

	
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. Trefferzone feststellen

Ok. Jetzt haben wir alles was wir brauchen. Lass uns jetzt die Funktion erstellen, die die Trefferzone feststellt. Ich schlage vor, sie direkt vor G_Damage() [im Kommentar fälschlicherweise als T_Damage() bezeichnet] in g_combat.c zu erstellen. Ja, es handelt sich hier um eine Serverdatei, also pass auf, das du im GAME Projekt bist. Jetzt brauchts nur noch copy/paste skills :)


/* 
============
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;

}

Wenn du dir den Code genauer anschaust, siehst du, dass ich lediglich die Position und Höhe des Spielers benutzte, um festzustellen, wo der Schaden zugefügt wurde ( relativ zu den Füssen des Spielers). Ducken wird nicht extra behandelt, da sich beim ducken nur die Beine des Spielers verkürzen. Wenn du mir nicht glaubst, betrachte dich selbst in der 3rd-person ansicht. Nachdem der Körper in Schichten zerteilt ist, muss er noch in vier Quadranten geteilt werden. Dazu erstellen wir einen Vektor vom Zentrum des Spielers zum Eintrittspunkt des Projektils. Dadurch erhalten wir PITCH, YAW und ROLL. Dann vergleichen wir YAW dieses Vektors mit YAW des Projektils und können damit dessen Eintrittswinkel bestimmen. Easy, oder?

3.Den Aufruf implementieren

Wir haben jetzt zwar die Funktion, aber noch tut sie nichts. Warum ? Weil niemand sie aufruft ! Naja, da lässt sich was machen. Wir bleiben in g_combat.c, und gehen in die Mitte von G_Damage(), etwa bei Zeile 1135. Füge einfach die markierten Zeilen ein.


	// 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);
		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;

Ok, damit haben wir die Basisfunktionalität für Trefferzonen implementiert. Alles weitere ( spezielle message of death, Rüstung für einzelne Körperteile usw. ) bleibt dem ambitionierten mod-coder überlassen :) Good luck, and good coding.

<< Tutorial 13 | | Tutorial 15 >>