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