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