|
TUTORIAL 20 - Spielerklassen
von Fritz
Dieses Tutorial zeigt wie man Klassen verschiedener Fähigkeiten/Eigenschaften
aufbauen kann, zwischen denen der Spieler dann wählt. In diesem
Konkreten Beispiel werden sich die Klassen nur in den Waffen unterscheiden,
mit denen der Spieler startet (respawn), die Klasse wird durch Wahl
des Playermodels festgelegt.
Wir benötigen dazu 4 Schritte, also los:
Folgende Dateien werden bearbeitet:
- bg_public.h
- g_local.h
- g_client.c
1. Definitionen
Zuerst brauchen wir einen Typ, um die verschiedenen Klassen unterscheiden
zu können. Für den Anfang werden wir uns mit 3 Klassen begnügen.
Öffne bg_public.h und füge unter team_t folgende
Struktur ein (Z 530):
typedef enum {
TEAM_FREE,
TEAM_RED,
TEAM_BLUE,
TEAM_SPECTATOR,
TEAM_NUM_TEAMS
} team_t;
typedef enum {
PCLASS_BFG,
PCLASS_LIGHTNING,
PCLASS_RAILGUN,
PCLASS_NUM_CLASSES
} pclass_t;
Jetzt spendieren wir jedem Spieler 2 Variablen dieses neuen Typs, um
sowohl die aktuelle wie auch die zukünftige (nach dem nächsten
respawn) Klasse festlegen zu können. Der geeignete Ort für
beide ist clientPersistant_t in g_local.h (Z 235)
typedef struct {
clientConnected_t connected;
usercmd_t cmd; // we would lose angles if not persistant
qboolean localClient; // true if "ip" info key is "localhost"
qboolean initialSpawn; // the first spawn should be at a cool location
qboolean predictItemPickup; // based on cg_predictItems userinfo
qboolean pmoveFixed; //
char netname[MAX_NETNAME];
int maxHealth; // for handicapping
int enterTime; // level.time the client entered the game
playerTeamState_t teamState; // status in teamplay games
int voteCount; // to prevent people from constantly calling votes
int teamVoteCount; // to prevent people from constantly calling votes
qboolean teamInfo; // send team overlay updates?
pclass_t playerclass; // Die aktuelle Klasse des Spielers
pclass_t newplayerclass; // Die Klasse, welcher der Spieler nach...
// ...dem nächsten respawn angehört
} clientPersistant_t;
Da jeder Client eine Variable des Typs clientPersistant_t besitzt,
haben nun alle Spieler (und keine andere entity) genau eine Klasse.
2. Zuweisung der Klasse (g_client.c)
Nun muss der Spieler die Möglichkeit haben, seine gewünschte
Klasse festzulegen. Der Einfachheit halber werden wir die Klasse an
die Wahl des Playermodels koppeln. Günstigerweise wird die Funktion ClientUserInfoChanged aufgerufen, sobald der Spieler eine seiner
persönliche Einstellungen (zB name, team, handicap und eben auch
model) ändert (Z 765).
// set model
if( g_gametype.integer >= GT_TEAM ) {
Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) );
Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) );
} else {
Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );
Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) );
}
if (!Q_stricmp (model, "biker/red"))
client->pers.newplayerclass = PCLASS_BFG;
else if (!Q_stricmp (model, "anarki/blue"))
client->pers.newplayerclass = PCLASS_LIGHTNING;
else if (!Q_stricmp (model, "grunt/red"))
client->pers.newplayerclass = PCLASS_RAILGUN;
else {
client->pers.newplayerclass = PCLASS_BFG;
Q_strncpyz( model, "biker/red", sizeof( model ) );
}
Hier wird die zukünftige Klasse abhängig vom gewählten
Model bestimmt. Man könnte nun mit Hilfe weiterer Models noch mehr
Klassen festlegen oder auch verschiedene Models der selben Klasse zuweisen.
3. Die Klasse aktualisieren (g_client.c)
Sobald der Spieler das Spiel betritt oder stirbt und respawnt, soll
die Klasse falls erforderlich gewechselt werden. Füge dazu in ClientSpawn folgendes ein (Z 1170):
ent->watertype = 0;
ent->flags = 0;
client->pers.playerclass = client->pers.newplayerclass;
VectorCopy (playerMins, ent->r.mins);
VectorCopy (playerMaxs, ent->r.maxs);
Damit ist das Klassensystem fertig! Du kannst versuchen zu compilieren,
allerdings wird sich im Spiel noch kein Effekt feststellen lassen.
4. Klasseneigenschaften (g_client.c)
Nun müssen wir den Spielern noch unterschiedliche Fähigkeiten
und Eigenschaften geben, abhängig von ihrer Klasse. Immernoch in
ClientSpawn fügst du folgendes hinzu (Z 1185):
client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
client->ps.ammo[WP_GAUNTLET] = -1;
client->ps.ammo[WP_GRAPPLING_HOOK] = -1;
//Waffenzuteilung entsprechend der Klasse
switch (client->pers.playerclass){
case PCLASS_BFG:
client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BFG );
client->ps.ammo[WP_BFG] = 20;
break;
case PCLASS_LIGHTNING:
client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_LIGHTNING );
client->ps.ammo[WP_LIGHTNING] = 60;
break;
case PCLASS_RAILGUN:
default:
client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_RAILGUN );
client->ps.ammo[WP_RAILGUN] = 20;
break;
}
Was unsere Klassen also nun letztendlich unterscheidet, sind lediglich
die Waffen, mit denen sie respawnen. Durch Abfrage der Variable client->pers.playerclas (evt noch mit ent-> davor) lässt sich aber an beliebigen anderen
Stellen im Code zwischen den Klassen unterscheiden und damit kann Klassenspezifisches
Verhalten erzeugt werden.
Eine Ernsthafte Mod wird ihre Klassen wohl kaum am gewählten Playermodel
unterscheiden, sondern eher eine Konsolenvariable zur Verfügung
stellen und evt nach deren Änderung das Model des Spielers entsprechend
ändern.
Eine erstklassige Hausaufgabe !!! (Tip für alle, die es versuchen
wollen: schau dir mal an, wie trap_SetUserinfo benutzt wird)
<<
Tutorial 19 | | Themenübersicht >> |