De/Creating A Class System: Difference between revisions

From Valve Developer Community
< De
Jump to navigation Jump to search
No edit summary
mNo edit summary
 
(56 intermediate revisions by 15 users not shown)
Line 1: Line 1:
{{LanguageBar|title=Erstellen eines Klassensystems}}
{{Pov}}
{{Toc-right}}
==Einleitung==
==Einleitung==
Hi und willkommen zum Coding Tutorial um ein Klassensystem zuerstellen.
Dieses Coding Tutorial befasst sich mit dem Erstellen eines Klassensystems.
Damit es nicht alzu schwer wird werden wir nur 3 Klassen erstellen.
Sollten wir aber eine Klasse wechseln dann werden wir getötet und bekommen beim Spawn unsere Klasse.
 
Das Klassensystem ist aus meiner Army Mod.
 
Wir nehmen die Klassen Supporter, Medic und Assaulter


Notizen (/Überlegungen):
*Es gibt 3 Klassen (Supporter, Medic und Assaulter)
*Beim ersten Spawn bekommt man eine Standardklasse.
*Wechselt man die Klasse, stirbt man und spawnt mit der neuen Klasse.


==Daten die gebraucht werden==
==Daten, die gebraucht werden==
Als erstes eine Auflistung was wir brauchen an Codedateien.
Als erstes eine Auflistung was man an Codedateien braucht:


Header:
Header:
Line 18: Line 20:
  dlls/player.cpp
  dlls/player.cpp


Das wars auch schon :)


==Der Anfang==
Das Klassensystem wird zu 100% serverseitig sein, der Client kann die Klassen aber wechseln.
 
==Die Deklarationen==
 
Als erstes muss man die Deklarationen machen.
Was man braucht sind einige Variablen und einige Funktionen.
Als Erstes erstellt man ein Enum das die Werte der Klassen enthält!
Das enum kommt in die ''player.h'' über ''class CBasePlayer;''
 
Das Enum macht man erstmal so:
enum
{
Unassigned = 0,
Assaulter,
Supporter,
Medic,
};
 
Später braucht man das für die Vergleiche!
 
Jetzt widme man sich der Klasse ''CBasePlayer''.
Zum Beginn erstellt man einen neuen ''public'' und einen neuen ''private'' Bereich am Ende von ''CBasePlayer'' in der ''player.h''.
Dort schreibt man den gesamten Code für das Klassensystem rein!
 
// Die neuen Bereiche für das Klassensystem!
public:
private:
 
 
Hier nun alles was man an Methoden deklarieren muss und natürlich auch die Variablen:
 
/*
********************************************
**Klassensystem:
**Hier werden die Klassen verteilt!
**Es wird auch auf die richtigen Klassen geprüft!
********************************************
*/
public:
// Methode zum Klassenwechseln
virtual void ChangeClass(int NewClass);
virtual int GetClass();
// Initalisierung des Klassensystems!
void InitClassSystem();
// Überprüft ob wir keinen ungültigen wert setzen für die Klassenvariablen:
void CheckAllClassConVars();
// Hier wird geprüft ob wir die Klasse gewechselt haben:
void OnClassChange();
// Setzt Leben und Armor für den Spieler!
void SetClassStuff();
// Setzt den neuen wert für die Check Variable:
void SetCurrentClassValue();
// Die Klasse holen:
int GetClassValue()const;
// Die Standartklasse holen:
int GetDefaultClassValue()const;
// Schaltmethode die Klassen verteilt:
void SetPlayerClass();
// Legt das Leben für unsere klasse fest:
int  GetClassHealth()const;
// Diese Methoden setzen für jede Klasse das Leben und die Armor
int GetClassMaxHealth()const;
// Legt die Armor für die Klasse Fest:
int  GetClassArmor()const;
// Diese Methoden holen für jede Klasse das Leben und die Armor
int GetClassMaxArmor()const;
private: 
// Wichtige Prüfung auf ersten Spawn:
bool m_bFirstSpawn;
bool IsFirstSpawn();
// Welche Klasse haben wir(enum):
int m_iClass;
// Prüft ob wir die Klasse gewechselt haben:
int m_iCurrentClass;
// Hier setzen wir die Standartklasse!
int m_iDefaultClass;
// Waffen, Leben und Armor der Klassen verteilen:
void SetClassDefault();
void SetClassGroundUnit();
void SetClassSupportUnit();
void SetClassMedic();
        void SetHealthValue( int value );
 
/*
********************************************
**Spielereigenschaften:
**Hier werden Eigenschaften wie Speed, Condition
**und maximale Klassen festgelegt!
********************************************
*/
// Integer für Rüstung:
int m_iArmor;
int m_iMaxArmor;
 
Nun zur Erklärung, von oben nach unten.
 
==public==
 
// Methode zum Klassenwechseln
virtual void ChangeClass(int NewClass);
 
Diese Methode braucht man, damit der Client(Spieler) per Menu/Konsole seine Klasse wechselt.
 
virtual int GetClass();
 
Braucht man, wenn man noch Sachen wie Munition, Leben und Rüstungsinkrementierung für den Supporter/Medic einbauen will.
 
// Initalisierung des Klassensystems!
void InitClassSystem();
 
Diese Methode soll das Klassensystem aus der Spawn Methode von CBasePlayer starten.
Damit werden die Methoden für die Klassenverteilung usw aufgerufen.
Bei der Implementierung ist man dann schlauer.
 
// Überprüft ob man keinen ungültigen wert setzt für die Klassenvariablen:
void CheckAllClassConVars();
 
Diese Methode prüft, ob man einen gültigen Wert eingegeben hat.
Falls wir das nicht Prüfen würden, dann würde eure Mod crashen!
Falls der Wert nicht stimmt, dann müsst ihr einen Wert für die Klasse setzen.
 
// Hier wird geprüft ob man die Klasse gewechselt hat:
void OnClassChange();
 
Diese Methode wird in der Thinkmethode gebraucht.
Falls man die Klasse wechselt wird man gekillt und ein Punkt addiert sonst würde man Punktabzug bekommen.
 
// Setzt Leben und Armor für den Spieler!
void SetClassStuff();
 
Diese Methode setzt für jede Klasse den Wert für das Leben, Maximal Leben und Rüstung.
Leider wird die Rüstung über den Code der Recharger festgelegt, diesen muss man selber ändern.
 
// Setzt den neuen wert für die Check Variable:
void SetCurrentClassValue();
 
Diese Methode ändert den Wert der CheckVariable für die Klassen, sonst würde man immer wieder sterben!
 
// Die Klasse holen:
int GetClassValue()const;
 
Diese Methode holt den Wert der Klasse die man hat.
 
// Die Standartklasse holen:
int GetDefaultClassValue()const;
 
Diese Methode holt vom Server die Standardklasse damit man im Falle einer ungültigen Klasse diese bekommt.
 
// Schaltmethode die Klassen verteilt:
void SetPlayerClass();
 
Diese Methode ist die Steuerzentrale unseres Klassensystems.
Dort wird mit dem Wert der Klassenvariable alles gelenkt. Von der Waffen und Lebensverteilung bis zum setzen des Spielermodels (Das muss man leider noch machen!).
 
 
// Legt das Leben für die Klasse fest:
int  GetClassHealth()const;
 
Diese Methode holt das Leben, das man im Moment hat.
 
// Diese Methoden setzt für jede Klasse das Leben und die Armor
int GetClassMaxHealth()const;
 
Die Methode holt das Maximale Leben, das die Klasse haben kann.
 
// Legt die Armor für die Klasse Fest:
int  GetClassArmor()const;
 
Diese Methode holt die Rüstung, die man im Moment hat.
 
// Diese Methoden holt für jede Klasse das Leben und die Armor
int GetClassMaxArmor()const;
 
Diese Methode holt die Maximale Rüstung der Klasse (ist leider noch unbrauchbar wegen dem Recharger Code!)
 
==private==
 
// Wichtige Prüfung auf ersten Spawn:
bool m_bFirstSpawn;
 
Diese Variable ist für die Abfrage für den ersten Spawn nötig.
 
bool IsFirstSpawn();
 
Diese Methode gibt Auskunft ob man das Erste mal gespawnt wird.
 
// Welche Klasse hat man(enum):
int m_iClass;
 
Diese Variable speichert den Wert der Klasse:
 
// Prüft, ob man die Klasse gewechselt hat:
int m_iCurrentClass;
 
Das ist die Prüfungsvariable, die den Wert der Momentanen Klasse enthält.
Der Wert wird dann geändert wenn die Klasse gewechselt wird.
 
// Hier setzt man die Standardklasse!
int m_iDefaultClass;
Diese Variable holt die Klasse des Servers und ist gleichzeitig die erste Klasse, die man beim ersten Spawn
bekommt.
 
// Waffen, Leben und Armor der Klassen verteilen:
void SetClassDefault();
void SetClassGroundUnit();
void SetClassSupportUnit();
void SetClassMedic();
 
Das sind die Methoden zum verteilen aller wichtigen Sachen einer Klasse.
(Leben, Maximalleben, Rüstung, Maximalrüstung, Waffen usw.)
 
// Integer für Rüstung:
int m_iArmor;
int m_iMaxArmor;
 
Das sind die Variablen für Rüstung und Maximalrüstung.
 
Jetzt wei0 man was man alles braucht und wieso.
 
Nun kommt man zum Teil in dem man alles Einbaut.
 
==Die Implementation==
 
Da man nun alles hat was man braucht muss man nurnoch alles einbauen!
Alles kommt in die ''player.cpp'' ganz ans Ende.
 
Hier der Code mit Erklärung:
 
Added im Konstruktor:
// Erster Spawn?
m_bFirstSpawn = true;
// Startklasse festlegen:
m_iClass = default_class.GetInt();
 
m_iCurrentClass = m_iClass;
// Die Standardklasse setzen!
m_iDefaultClass = default_class.GetInt();
// Standartwerte 100 für Leben/Armor:
m_iHealth = 100;
m_iArmor = 100;
// Maximal Leben/Armor für jede Klasse:
m_iMaxHealth = 100;
m_iMaxArmor = 100;
 
void CBasePlayer::InitClassSystem()
{
DevMsg("Klassensystem wird initalisiert!\n");
CheckAllClassConVars();
SetPlayerClass();
SetClassStuff();
}
int CBasePlayer::GetClassValue()const
{
return m_iClass;
}
int CBasePlayer::GetDefaultClassValue()const
{
return m_iDefaultClass;
}
bool CBasePlayer::IsFirstSpawn()
{
return m_bFirstSpawn;
}
// Soll die Waffen verteilen:
// Hier ist irgend wo ein Bug!
void CBasePlayer::SetPlayerClass()
{
if(IsFirstSpawn())
{
m_iClass = m_iDefaultClass;
}
        // Jetzt werden die Klassen verteilt:
switch(m_iClass)
{
case Assaulter:
SetClassGroundUnit();
break;
case Supporter:
SetClassSupportUnit();
break;
case Medic:
SetClassMedic();
break;
//case Invalid:
// Muss noch eingebaut werden!
// Hier bekommt man nur die Standardwaffen!
case Unassigned:
// Hier setzt man die Standardklasse!
default:
SetClassDefault();
break;
}
}
void CBasePlayer::OnClassChange()
{
if( m_iClass != m_iCurrentClass )
{   
// Spieler killen und Punkte um 1 erhöhen (wegen Selbstmord)!
CommitSuicide();
IncrementFragCount(1);
 
// Setzen, damit man nicht die selbe Klasse hat!
m_iCurrentClass = m_iClass;
}
}
void CBasePlayer::CheckAllClassConVars()
{
// Hat man eine Klasse, die zwischen Spec-Ops und der letzen Klasse liegt?
if( m_iClass < Assaulter || m_iClass > Medic )
{
m_iClass = Assaulter;
}
 
if( m_iDefaultClass < Assaulter || m_iDefaultClass > Medic )
{
m_iDefaultClass = Assaulter;
}
}
void CBasePlayer::SetClassDefault()
{
Msg("Du hast die Klasse Default!\n");
CheckAllClassConVars();
SetPlayerClass();
}
// Assault:
void CBasePlayer::SetClassGroundUnit()
{
// Standardwerte für Leben/Armor:
m_iHealth = 125;
m_iArmor = 125;
// Maximal Leben/Armor für jede Klasse:
m_iMaxHealth = 125;
m_iMaxArmor = 125;
// Waffen verteilen:
Msg("Du bist jetzt eine Ground Unit!\n");
CBasePlayer::GiveNamedItem( "weapon_357" );
CBasePlayer::GiveNamedItem( "weapon_smg1" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
CBasePlayer::GiveAmmo( Magazin_357*3 , "357" );
CBasePlayer::GiveAmmo( Magazin_SMG1*3        , "SMG1");
CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*1, "smg1_grenade");
CBasePlayer::GiveAmmo( Magazin_Frag*5        , "grenade" );
}
// Supporter
void CBasePlayer::SetClassSupportUnit()
{
// Standartwerte für leben/armor:
m_iHealth = 100;
m_iArmor = 300;
// Maximal Leben/Armor für Klasse:
m_iMaxHealth = 100;
m_iMaxArmor = 300;
// Waffen verteilen:
Msg("Du bist jetzt eine Support Unit!\n");
   
CBasePlayer::GiveNamedItem( "weapon_ar2" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
CBasePlayer::GiveNamedItem( "weapon_357" );
CBasePlayer::GiveAmmo( Magazin_AR2*3, "AR2" );
CBasePlayer::GiveAmmo( Magazin_AR2AltFire*2, "AR2AltFire" );
CBasePlayer::GiveAmmo( Magazin_Frag*3, "grenade" );
CBasePlayer::GiveAmmo( Magazin_357*3 , "357" );
// Eigenschaft des Supporter's aufrufen:
// GetAmmo();
}
// Medic:
void CBasePlayer::SetClassMedic()
{
// Standardwerte für Leben/Armor:
m_iHealth = 100;
m_iArmor = 75;
 
// Maximal Leben/Armor für Klasse:
m_iMaxHealth = 100;
m_iMaxArmor = 75;
// Waffen verteilen:
Msg("Du bist jetzt ein Medic!\n");
CBasePlayer::GiveNamedItem( "weapon_357" );
CBasePlayer::GiveNamedItem( "weapon_smg1" );
CBasePlayer::GiveAmmo( Magazin_357*2, "357" );
CBasePlayer::GiveAmmo( Magazin_SMG1*2 , "SMG1");
CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*3, "smg1_grenade");
}
// Legt das Leben für die Klasse fest:
int  CBasePlayer::GetClassHealth()const
{
return m_iHealth;
}
// Legt die Armor für die Klasse fest:
int  CBasePlayer::GetClassArmor()const
{
return m_iArmor;
}
// Diese Methoden setzen für jede Klasse das Leben und die Armor
int CBasePlayer::GetClassMaxHealth()const
{
return m_iMaxHealth;
}
// Diese Methoden holen für jede Klasse das Leben und die Armor
int CBasePlayer::GetClassMaxArmor()const
{
return m_iMaxArmor;
}
// Setzt Leben und Armor für den Spieler!
void CBasePlayer::SetClassStuff()
{
// Hier Leben setzen:   
SetHealthValue(GetClassHealth());
SetMaxHealthValue(GetClassMaxHealth());
// Variable gehört nicht zu CBasePlayer!!!
// TODO: Das ist eine Private Variable, die ich ändern muss!
SetArmorValue(GetClassArmor());
// SetMaxArmorValue(GetClassMaxArmor());
}
// Klasse per ConVar Wechsel für Client:
void CBasePlayer::ChangeClass(int NewClass)
{
m_iClass = NewClass;
}
 
Da man ja schon weiß, wofür die ganzen Methoden sind, man nurnoch schauen wie alles läuft :)
 
Aber nun mal zum Einbauen der ConVars, die man braucht:
 
// Der Server hat für alle die selbe Standardklasse!
// Der Admin kann die Standartklasse ändern!
ConVar default_class("default_class", "3", FCVAR_ARCHIVE, "Variable für Standardklasse!");
 
Nun hat der Server seine Default-Klasse, die jeder Spieler beim Spawnen bekommt.
Diese kann man dann über die Konsole ändern :)
 
Man kann auch noch mehr Settings für den Admin zur Verfügung stellen:
 
// Maximale Klassen erlauben(Admins!)
// TODO: Muss noch aktiviert werden im Code!
ConVar max_assaulter("max_assaulter", "3", FCVAR_ARCHIVE, "Variable für Maximale Assaulter!");
ConVar max_supporter("max_supporter", "3", FCVAR_ARCHIVE, "Variable für Maximale Supporter!");
ConVar max_medic("max_medic", "3", FCVAR_ARCHIVE, "Variable für Maximale Medics!");
 
// Klassen erlauben(Admins):
// TODO: Muss noch aktiviert werden im Code!
ConVar allow_assaulter("allow_assaulter", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Ground Unit!");
ConVar allow_supporter("allow_supporter", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Support Unit!");
ConVar allow_medic("allow_medic", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Medic!");
 
Diese muss man aber selbst implementieren!
 
Nun muss man nur noch dem Spieler die Möglichkeit zum Ändern der Klasse geben!
Und dann muss man noch die Prüfungen der Klassen einbauen!
 
Dazu muss man einfach in der Methode OnClientCommand folgendes am Ende einbauen:
 
else if ( !stricmp( cmd, "class" ) )
{
if ( engine->Cmd_Argc() < 2 )
return true;
int iClass = atoi( engine->Cmd_Argv(1) );
ChangeClass(iClass);
    return true;
}
 
Hiermit wird eine ConVar simuliert.
Aber man kann durch Class und dann die Klassennummer, also Class 1, die Klasse ändern!
Nun muss man aber die Prüfungsmethode aufrufen und dann hat man es schon!
 
Das macht man in der Methode PostThink();
 
Dort setzt man am Ende einfach folgendes ein:
 
OnClassChange();
 
Das wars :) wenn alles stimmt dann dürfte das Klassensystem funktionieren ;)
 
==Wichtiges zum Code==
 
Damit der Code ohne Probleme läuft, muss man noch einiges fixen!
 
Erstmal muss man noch folgende Methoden im Public-bereich hinzufügen:
 
// Deklarationen:
// Added von mir:
void SetHealthValue( int value );
int  GetHealthValue();
void SetMaxHealthValue(int MaxValue);
int  GetMaxHealthValue();
void IncrementHealthValue( int nCount );
   
   
Als erstes müssen wir uns an die Deklarationen machen.
int    GetArmorValue();
Was wir brauchen sind einige Variablen und einige Funktionen.
Aber als erstes werden wir ein Enum erstellen das die Werte unserer Klassen enthält!


Das Enum machen wir erstmal so:
// Implementierung:
  enum
void CBasePlayer::SetHealthValue( int value )
{
m_iHealth = value;
}
void CBasePlayer::SetMaxHealthValue( int MaxValue )
{
m_iMaxHealth = MaxValue;
}
int CBasePlayer::GetHealthValue()
{
return m_iHealth;
}
int CBasePlayer::GetMaxHealthValue()
{
return m_iMaxHealth;
}
  void CBasePlayer::IncrementHealthValue( int nCount )
{
m_iHealth += nCount;
if (m_iMaxHealth > 0 && m_iHealth > m_iMaxHealth)
m_iHealth = m_iMaxHealth;
}
int CBasePlayer::GetArmorValue()
  {
  {
  Unassigned=0,
return m_ArmorValue;
  Assaulter,
  Supporter,
  Medic
  }
  }


Später brauchen wir das für die Vergleiche!
Damit kann man die Armor und das Health setzen (je nachdem, wieviel die Klassen haben sollen).
Aber auch Medic und Supporter können so ihr Leben/Rüstung regenerieren!
 
Nun muss man noch die Munitionsverteilung ändern.
Entweder man gibt die Munitionszahlen selber ein oder man erstellt eine Header wie folgt:
Header definition starten:
#ifndef _Magazin_H
#define _Magazin_H
 
Die Magazinsdefinitionen:
// Nur verändern, wenn man die Munition der Waffenmagazine im script/code ändert!
// Legt die Magazine fest(Magazin und dann größe in Kugeln)!
#define Magazin_None          0
#define Magazin_Pistole      20
#define Magazin_357            6
#define Magazin_SMG1          50
#define Magazin_SMG1_Granates  1
#define Magazin_AR2          100
#define Magazin_AR2AltFire    1
#define Magazin_RPG            1
#define Magazin_Slam          1
#define Magazin_Frag          1
#define Magazin_Shotgun        8
#define Magazin_Crossbow      1
 
Die maximale Definition eines Magazins:
// Legt die Mazimale Muntion/Magazine fest:
#define Max_Magazin_None          Magazin_None*0
#define Max_Magazin_Pistole        Magazin_Pistole*16
#define Max_Magazin_357            Magazin_357*6
#define Max_Magazin_SMG1          Magazin_SMG1*8
#define Max_Magazin_SMG1_Granates  Magazin_SMG1_Granates*7
#define Max_Magazin_AR2            Magazin_AR2*3
#define Max_Magazin_AR2AltFire    Magazin_AR2AltFire*2
#define Max_Magazin_RPG            Magazin_RPG*25
#define Max_Magazin_Slam          Magazin_Slam*10
#define Max_Magazin_Frag          Magazin_Frag*10
#define Max_Magazin_Shotgun        Magazin_Shotgun*13
#define Max_Magazin_Crossbow      Magazin_Crossbow*50
 
Die Magazinsdefinition beenden:
#endif //_Magazin_H
 
Ich würde zur zweiten Methode raten, da man hier auch die Munitionsdefinitions in den gamerules.cpp ändern kann und die maximale Munition einbinden kann!


Jetzt werden wir uns der Klasse CBasePlayer wiedmen.
Und man sollte die Default Klasse per CooVars setzen oder die Default Klasse aus dem Coden komplett entfernen!


Als erstes Brauchen wir 2 Variablen.
Einmal eine für die Klasse und einmal eine für die Prüfung ob die Klasse später gültig ist.


Dazu machen wir am ende der Klasse einfach einen public und einen private bereich:


public:
==Klassenmenu öffnen==
private:
 
Damit man das Klassen system später aufrufen kann, solte man in der baseviewport.cpp dieses eintragen:
 
  // sub dialogs
  #include "clientscoreboarddialog.h"
  #include "spectatorgui.h"
  #include "teammenu.h"
  #include "classmenu.h"
  #include "vguitextwindow.h"
  #include "IGameUIFuncs.h"
  #include "mapoverview.h"
  #include "hud.h"
  #include "NavProgress.h"
 
Jetzt den Befehl zum Aufrufen:
 
  CON_COMMAND( chooseclass, "Opens a menu for class choose" )
  {
  if ( !gViewPortInterface )
  return;
 
  gViewPortInterface->ShowPanel( "class", true );
  }


Jetzt setzen wir die neuen Variablen in den Private bereich
Etwas weiter unten das hier:


public:
  void CBaseViewport::CreateDefaultPanels( void )
private:
   {
   //Variable die unsere Klasse speichert
   #ifndef _XBOX
   int m_iClass;
  AddNewPanel( CreatePanelByName( PANEL_SCOREBOARD ) );
  AddNewPanel( CreatePanelByName( PANEL_INFO ) );
  AddNewPanel( CreatePanelByName( PANEL_SPECGUI ) );
  AddNewPanel( CreatePanelByName( PANEL_SPECMENU ) );
  AddNewPanel( CreatePanelByName( PANEL_NAV_PROGRESS ) );
 
  AddNewPanel( CreatePanelByName( PANEL_TEAM ) );
  AddNewPanel( CreatePanelByName( PANEL_CLASS ) );
    
    
  //Variable zur Prüfung auf gültige Klasse
  // AddNewPanel( CreatePanelByName( PANEL_BUY ) );
   int m_iCurrentClass;
  #endif
  }
 
Und zuletzt das hier:
 
  IViewPortPanel* CBaseViewport::CreatePanelByName(const char *szPanelName)
   {
  ...
  else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 )
  {
  newpanel = new CTeamMenu( this );
  }
  else if ( Q_strcmp(PANEL_CLASS, szPanelName) == 0 )
  {
  newpanel = new CTeamMenu( this );
  }
  ...
  }
 
==Bonus==
 
Nun kann man sich noch einen Spaß erlauben und jeder Klasse eine bestimmte Geschwindigkeit zuweisen.
 
Dazu braucht man die hl2_player.cpp und die player.h/.cpp


Jetzt haben wir schonmal die Variablen aber wir setzen noch eine Standatklasse die man beim ersten Spawn bekommen soll.
Als erstes muss man ausfindig machen, wo der Spieler seine Laufgeschwindigkeit einstellt!


  public:
Das macht man hier:
  private:
 
   int m_iClass;
 
   int m_iCurrentClass;
  #ifdef HL2MP
   int m_iDefaultClass;
  #define HL2_WALK_SPEED 150
  #define HL2_NORM_SPEED 190
  #define HL2_SPRINT_SPEED 320
  #else
   #define HL2_WALK_SPEED hl2_walkspeed.GetFloat()
   #define HL2_NORM_SPEED hl2_normspeed.GetFloat()
   #define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
#endif


Zum Schluß brauchen wir noch eine Variable die prüft ob wir das erstemal spawnen.
Die Erklärung ist simpel.
Wenn HL2MP definiert ist, dann werden Standardwerte gesetzt:


  public:
  #ifdef HL2MP
private:
   #define HL2_WALK_SPEED 150
  int m_iClass;
   #define HL2_NORM_SPEED 190
   int m_iCurrentClass;
   #define HL2_SPRINT_SPEED 320
   int m_iDefaultClass;
   bool m_bFirstSpawn;


Da jede Klasse unterschiedliches Leben und Rüstung besitzt müssen wir noch Variablen dafür deklarieren.
Ansonsten kann man alles über die ConVars machen, aber in HL2DM geht das nur über die Standardwerte!


Wir brauchen nur für Rüstung noch Variablen!
#else
Diese setzen wir alle private:
  #define HL2_WALK_SPEED hl2_walkspeed.GetFloat()
  #define HL2_NORM_SPEED hl2_normspeed.GetFloat()
  #define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
#endif


public:
Aber nun muss man eine Steuerung bauen für den Speed!
private:
  int m_iClass;
  int m_iCurrentClass;
  int m_iDefaultClass;
  bool m_bFirstSpawn;
  int m_iArmor;
  int m_iMaxArmor;


Dazu muss man nur eine Kleinigkeit zum Basisspieler hinzufügen.
player.h öffnen und folgendes im letzten Public-bereich hinzufügen:


Okay das wäre alles an Variablen!
// Hier wird die Spielergeschwindigkeit gesetzt:
void SetWalkSpeed(int WalkSpeed);
void SetNormSpeed(int NormSpeed);
void SetSprintSpeed(int SprintSpeed);


Jetzt kommen unsere Methodendeklarationen.
Damit kann man die Werte setzen, aber nun braucht man noch die Variablen der Klasse:


Als erstes brauchen wir eine Methode die unsere Klasse wechselt. Diese setzen wir im public bereich.
// Spielergeschwindigkeit:
int m_iWalkSpeed;
int m_iNormSpeed;
int m_iSprintSpeed;


Wir nennen sie mal void ChangeClass(int iClass):
Nun noch die Methoden zum holen der Werte:
public:
  void ChangeClass(int iClass);
private:
  int m_iClass;
  int m_iCurrentClass;
  int m_iDefaultClass;
  bool m_bFirstSpawn;
  int m_iArmor;
  int m_iMaxArmor;


Jetzt brauchen wir noch eine Methode die die gültigkeit deer Klasse prüft.
int GetWalkSpeed();
Diese nennen wir void CheckClassValue();
int GetNormSpeed();
int GetSprintSpeed();


Diese kommt auch in den public bereich.
Jetzt zur Implementierung:


Okay jetzt brauchen wir aber noch eine Methode die Prüft ob wir das erste mal Spawnen.
void CBasePlayer::SetWalkSpeed(int WalkSpeed)
Diese taufen wir auf den Namen bool IsFirstSpawn();
{
m_iWalkSpeed=WalkSpeed;
}
void CBasePlayer::SetNormSpeed(int NormSpeed)
{
m_iNormSpeed=NormSpeed;
}
void CBasePlayer::SetSprintSpeed(int SprintSpeed)
{
m_iSprintSpeed=SprintSpeed;
}
int CBasePlayer::GetWalkSpeed()  
{
return m_iWalkSpeed;
}
int CBasePlayer::GetNormSpeed()
{
return m_iNormSpeed;
}
int CBasePlayer::GetSprintSpeed()
{
return m_iSprintSpeed;
}


Diese setzen wir auch public.
Jetzt muss man nur noch 2 Sachen tun!


Jetzt brauchen wir aber noch die Metoden die unsere Klassen verteil und noch Methoden die unsere Klassen setzt!
Als erstes muss man sagen, dass die Get Methoden die Geschwindigkeit steuern.


Zum verteilen nehmen wir mal void SetPlayerClass();
Das macht man wie folgt:
Diese Methode setzen wir public.


Und die Klassensetzen wir per void SetClassAssaulter(); ,void SetClassSupporter(); und void SetClassMedic();.
  #define HL2_WALK_SPEED CBasePlayer::GetWalkSpeed()
Diese kommen wie unsere Variablen Private!
  #define HL2_NORM_SPEED CBasePlayer::GetNormSpeed()
  #define HL2_SPRINT_SPEED CBasePlayer::GetSprintSpeed()


Das wars auch schon fast.


Jetzt brauchen wir noch eine Methode die uns beim Wechsel der Klasse tötet und uns einen Punkt gibt da wir sonst als Spieler einen Minuspunkt bekommen.
Jetzt muss man nurnoch definieren welche Geschwindigkeit eure Klasse haben soll.


Diese nennen wir void OnClassChange(); und setzen sie public!
Dazu mmuss man nur in den SetClass Methoden die neuen SetSpeed Methoden einbauen.


Da jede Klasse unterschiedliche Rüstung und Leben hat müssen wir noch Funktionen zum holen des Lebens und der Armor machen!
Dann kann man eine Klasse schnell einbauen.
Dazu nehmen wir int GetHealth(), int GetMaxHealth(), int GetArmor(), int GetMaxArmor();


Diese Methoden kommen alle public:
Hier mal ein Beispiel meiner Supportunit-Klasse:
<code cpp n>
// Supporter
void CBasePlayer::SetClassSupportUnit()
{
// Code hier rein!
// m_szClassName = ClassNames[CLASS_SUPPORT];
// Standartwerte für leben/armor:
m_iHealth = 100;
m_iArmor = 300;
// Maximal Leben/Armor für Jede Klasse:
m_iMaxHealth = 100;
m_iMaxArmor = 300;
// Waffen verteilen:
Msg("Du bist jetzt eine Support Unit!\n");
switch(GetTeamNumber())
{
case TEAM_REBELS:
// Hier waffenverteilen:
// Rebelen:
CBasePlayer::GiveNamedItem( "weapon_reb_hmg" );
CBasePlayer::GiveNamedItem( "weapon_reb_frag" );
break;
case TEAM_COMBINE:
// Hier waffenverteilen:
// Combine:
CBasePlayer::GiveNamedItem( "weapon_com_hmg" );
CBasePlayer::GiveNamedItem( "weapon_com_frag" );
break;
default:
CBasePlayer::GiveNamedItem( "weapon_ar2" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
break;
}
CBasePlayer::GiveNamedItem( "weapon_ammo_spawner" );
CBasePlayer::GiveAmmo( Magazin_AR2*3, "AR2" );
CBasePlayer::GiveAmmo( Magazin_Frag*3, "grenade" );
SetWalkSpeed(90);
SetNormSpeed(130);
SetSprintSpeed(200);
}
</code>
Ich habe auch schon die Waffenverteilung auf die Teams gesetzt aber das kann ich auch noch erklären ;) Sobald ich das verbessert hab (z.b. mit einer Team-Variable) ;)


public:
Also viel Spaß und schreibt mir falls es Probleme oder Kritik gibt ;)
  void ChangeClass(int iClass);
{{ACategory|Programming}}{{ACategory|Tutorials}}
  void CheckClassValue();
  bool IsFirstSpawn();
  void SetPlayerClass();
  void OnClassChange();
  int GetHealth();
  int GetMaxHealth();
  int GetArmor();
  int GetMaxArmor();
private:
  void SetClassAssaulter();
  void SetClassSupporter();  
  void SetClassMedic();
  int m_iClass;
  int m_iCurrentClass;
  int m_iDefaultClass;
  bool m_bFirstSpawn;
  int m_iArmor;
  int m_iMaxArmor;

Latest revision as of 15:29, 14 January 2025

English (en)Deutsch (de)Translate (Translate)
Broom icon.png
This article or section should be converted to third person to conform to wiki standards.

Einleitung

Dieses Coding Tutorial befasst sich mit dem Erstellen eines Klassensystems.

Notizen (/Überlegungen):

  • Es gibt 3 Klassen (Supporter, Medic und Assaulter)
  • Beim ersten Spawn bekommt man eine Standardklasse.
  • Wechselt man die Klasse, stirbt man und spawnt mit der neuen Klasse.

Daten, die gebraucht werden

Als erstes eine Auflistung was man an Codedateien braucht:

Header:

dlls/player.h

Quellcodedateien

dlls/player.cpp


Das Klassensystem wird zu 100% serverseitig sein, der Client kann die Klassen aber wechseln.

Die Deklarationen

Als erstes muss man die Deklarationen machen. Was man braucht sind einige Variablen und einige Funktionen. Als Erstes erstellt man ein Enum das die Werte der Klassen enthält! Das enum kommt in die player.h über class CBasePlayer;

Das Enum macht man erstmal so:

enum
{
	Unassigned = 0,
	Assaulter,
	Supporter,
	Medic,
};

Später braucht man das für die Vergleiche!

Jetzt widme man sich der Klasse CBasePlayer.

Zum Beginn erstellt man einen neuen public und einen neuen private Bereich am Ende von CBasePlayer in der player.h. Dort schreibt man den gesamten Code für das Klassensystem rein!

// Die neuen Bereiche für das Klassensystem!
public:
private:


Hier nun alles was man an Methoden deklarieren muss und natürlich auch die Variablen:

/*
********************************************
**Klassensystem:
**Hier werden die Klassen verteilt!
**Es wird auch auf die richtigen Klassen geprüft!
********************************************
*/
public:
	// Methode zum Klassenwechseln
	virtual void ChangeClass(int NewClass); 
	virtual int GetClass();

	// Initalisierung des Klassensystems!
	void InitClassSystem();
	// Überprüft ob wir keinen ungültigen wert setzen für die Klassenvariablen:
	void CheckAllClassConVars();

	// Hier wird geprüft ob wir die Klasse gewechselt haben:
	void OnClassChange();
	// Setzt Leben und Armor für den Spieler!
	void SetClassStuff();

	// Setzt den neuen wert für die Check Variable:
	void SetCurrentClassValue();

	// Die Klasse holen:
	int GetClassValue()const;
	// Die Standartklasse holen:
	int GetDefaultClassValue()const;

	// Schaltmethode die Klassen verteilt:
	void SetPlayerClass();

	// Legt das Leben für unsere klasse fest:
	int  GetClassHealth()const;
	// Diese Methoden setzen für jede Klasse das Leben und die Armor
	int GetClassMaxHealth()const;

	// Legt die Armor für die Klasse Fest:
	int  GetClassArmor()const;
	// Diese Methoden holen für jede Klasse das Leben und die Armor
	int GetClassMaxArmor()const;

private:  
	// Wichtige Prüfung auf ersten Spawn:
	bool m_bFirstSpawn;
	bool IsFirstSpawn();
	// Welche Klasse haben wir(enum):
	int m_iClass;
	// Prüft ob wir die Klasse gewechselt haben:
	int m_iCurrentClass;
	// Hier setzen wir die Standartklasse!
	int m_iDefaultClass;

	// Waffen, Leben und Armor der Klassen verteilen:
	void SetClassDefault();
	void SetClassGroundUnit();
	void SetClassSupportUnit();
	void SetClassMedic();
        void SetHealthValue( int value );
/*
********************************************
**Spielereigenschaften:
**Hier werden Eigenschaften wie Speed, Condition
**und maximale Klassen festgelegt!
********************************************
*/
	// Integer für Rüstung:
	int m_iArmor;
	int m_iMaxArmor;

Nun zur Erklärung, von oben nach unten.

public

// Methode zum Klassenwechseln
virtual void ChangeClass(int NewClass); 

Diese Methode braucht man, damit der Client(Spieler) per Menu/Konsole seine Klasse wechselt.

virtual int GetClass();

Braucht man, wenn man noch Sachen wie Munition, Leben und Rüstungsinkrementierung für den Supporter/Medic einbauen will.

// Initalisierung des Klassensystems!
void InitClassSystem();

Diese Methode soll das Klassensystem aus der Spawn Methode von CBasePlayer starten. Damit werden die Methoden für die Klassenverteilung usw aufgerufen. Bei der Implementierung ist man dann schlauer.

// Überprüft ob man keinen ungültigen wert setzt für die Klassenvariablen:
void CheckAllClassConVars();

Diese Methode prüft, ob man einen gültigen Wert eingegeben hat. Falls wir das nicht Prüfen würden, dann würde eure Mod crashen! Falls der Wert nicht stimmt, dann müsst ihr einen Wert für die Klasse setzen.

// Hier wird geprüft ob man die Klasse gewechselt hat:
void OnClassChange();

Diese Methode wird in der Thinkmethode gebraucht. Falls man die Klasse wechselt wird man gekillt und ein Punkt addiert sonst würde man Punktabzug bekommen.

// Setzt Leben und Armor für den Spieler!
void SetClassStuff();

Diese Methode setzt für jede Klasse den Wert für das Leben, Maximal Leben und Rüstung. Leider wird die Rüstung über den Code der Recharger festgelegt, diesen muss man selber ändern.

// Setzt den neuen wert für die Check Variable:
void SetCurrentClassValue();

Diese Methode ändert den Wert der CheckVariable für die Klassen, sonst würde man immer wieder sterben!

// Die Klasse holen:
int GetClassValue()const;

Diese Methode holt den Wert der Klasse die man hat.

// Die Standartklasse holen:
int GetDefaultClassValue()const;

Diese Methode holt vom Server die Standardklasse damit man im Falle einer ungültigen Klasse diese bekommt.

// Schaltmethode die Klassen verteilt:
void SetPlayerClass();

Diese Methode ist die Steuerzentrale unseres Klassensystems. Dort wird mit dem Wert der Klassenvariable alles gelenkt. Von der Waffen und Lebensverteilung bis zum setzen des Spielermodels (Das muss man leider noch machen!).


// Legt das Leben für die Klasse fest:
int  GetClassHealth()const;

Diese Methode holt das Leben, das man im Moment hat.

// Diese Methoden setzt für jede Klasse das Leben und die Armor
int GetClassMaxHealth()const;

Die Methode holt das Maximale Leben, das die Klasse haben kann.

// Legt die Armor für die Klasse Fest:
int  GetClassArmor()const;

Diese Methode holt die Rüstung, die man im Moment hat.

// Diese Methoden holt für jede Klasse das Leben und die Armor
int GetClassMaxArmor()const;

Diese Methode holt die Maximale Rüstung der Klasse (ist leider noch unbrauchbar wegen dem Recharger Code!)

private

// Wichtige Prüfung auf ersten Spawn:
bool m_bFirstSpawn;

Diese Variable ist für die Abfrage für den ersten Spawn nötig.

bool IsFirstSpawn();

Diese Methode gibt Auskunft ob man das Erste mal gespawnt wird.

// Welche Klasse hat man(enum):
int m_iClass;

Diese Variable speichert den Wert der Klasse:

// Prüft, ob man die Klasse gewechselt hat:
int m_iCurrentClass;

Das ist die Prüfungsvariable, die den Wert der Momentanen Klasse enthält. Der Wert wird dann geändert wenn die Klasse gewechselt wird.

// Hier setzt man die Standardklasse!
int m_iDefaultClass;

Diese Variable holt die Klasse des Servers und ist gleichzeitig die erste Klasse, die man beim ersten Spawn bekommt.

// Waffen, Leben und Armor der Klassen verteilen:
void SetClassDefault();
void SetClassGroundUnit();
void SetClassSupportUnit();
void SetClassMedic();

Das sind die Methoden zum verteilen aller wichtigen Sachen einer Klasse. (Leben, Maximalleben, Rüstung, Maximalrüstung, Waffen usw.)

// Integer für Rüstung:
int m_iArmor;
int m_iMaxArmor;

Das sind die Variablen für Rüstung und Maximalrüstung.

Jetzt wei0 man was man alles braucht und wieso.

Nun kommt man zum Teil in dem man alles Einbaut.

Die Implementation

Da man nun alles hat was man braucht muss man nurnoch alles einbauen! Alles kommt in die player.cpp ganz ans Ende.

Hier der Code mit Erklärung:

Added im Konstruktor:

	// Erster Spawn?
	m_bFirstSpawn = true;

	// Startklasse festlegen:
	m_iClass = default_class.GetInt();
 
	m_iCurrentClass = m_iClass;
	// Die Standardklasse setzen!
	m_iDefaultClass = default_class.GetInt();

	// Standartwerte 100 für Leben/Armor:
	m_iHealth = 100;
	m_iArmor = 100;

	// Maximal Leben/Armor für jede Klasse:
	m_iMaxHealth = 100;
	m_iMaxArmor = 100;
void CBasePlayer::InitClassSystem()
{
	DevMsg("Klassensystem wird initalisiert!\n");
	CheckAllClassConVars();
	SetPlayerClass();
	SetClassStuff();
}

int CBasePlayer::GetClassValue()const
{
	return m_iClass;
}

int CBasePlayer::GetDefaultClassValue()const
{
	return m_iDefaultClass;
}

bool CBasePlayer::IsFirstSpawn()
{
	return m_bFirstSpawn;
}

// Soll die Waffen verteilen:
// Hier ist irgend wo ein Bug!
void CBasePlayer::SetPlayerClass()
{
	if(IsFirstSpawn())
	{
		m_iClass = m_iDefaultClass;
	}
       // Jetzt werden die Klassen verteilt:
	switch(m_iClass)
	{
	case Assaulter:
		SetClassGroundUnit();
		break;
	case Supporter:
		SetClassSupportUnit();
		break;
	case Medic:
		SetClassMedic();
		break;
	//case Invalid:
		// Muss noch eingebaut werden!
		// Hier bekommt man nur die Standardwaffen!
	case Unassigned:
		// Hier setzt man die Standardklasse!
	default:
		SetClassDefault();
		break;
	}
}

void CBasePlayer::OnClassChange()
{
	if( m_iClass != m_iCurrentClass )
	{    
		// Spieler killen und Punkte um 1 erhöhen (wegen Selbstmord)!
		CommitSuicide();
		IncrementFragCount(1);
 
		// Setzen, damit man nicht die selbe Klasse hat!
		m_iCurrentClass = m_iClass;
	}
}

void CBasePlayer::CheckAllClassConVars()
{
	// Hat man eine Klasse, die zwischen Spec-Ops und der letzen Klasse liegt?
	if( m_iClass < Assaulter || m_iClass > Medic )
	{
		m_iClass = Assaulter;
	}
  
	if( m_iDefaultClass < Assaulter || m_iDefaultClass > Medic )
	{
		m_iDefaultClass = Assaulter;
	}
}

void CBasePlayer::SetClassDefault()
{
	Msg("Du hast die Klasse Default!\n");

	CheckAllClassConVars();
	SetPlayerClass();
}

// Assault:
void CBasePlayer::SetClassGroundUnit()
{
	// Standardwerte für Leben/Armor:
	m_iHealth = 125;
	m_iArmor = 125;

	// Maximal Leben/Armor für jede Klasse:
	m_iMaxHealth = 125;
	m_iMaxArmor = 125;

	// Waffen verteilen:
	Msg("Du bist jetzt eine Ground Unit!\n");

	CBasePlayer::GiveNamedItem( "weapon_357" );	
	CBasePlayer::GiveNamedItem( "weapon_smg1" );
	CBasePlayer::GiveNamedItem( "weapon_frag" );
	
	CBasePlayer::GiveAmmo( Magazin_357*3 ,	"357" );
	CBasePlayer::GiveAmmo( Magazin_SMG1*3         ,	"SMG1");
	CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*1,	"smg1_grenade");
	CBasePlayer::GiveAmmo( Magazin_Frag*5         ,	"grenade" );
}

// Supporter
void CBasePlayer::SetClassSupportUnit()
{
	// Standartwerte für leben/armor:
	m_iHealth = 100;
	m_iArmor = 300;

	// Maximal Leben/Armor für Klasse:
	m_iMaxHealth = 100;
	m_iMaxArmor = 300;

	// Waffen verteilen:
	Msg("Du bist jetzt eine Support Unit!\n");
   
	CBasePlayer::GiveNamedItem( "weapon_ar2" );
	CBasePlayer::GiveNamedItem( "weapon_frag" );
	CBasePlayer::GiveNamedItem( "weapon_357" );	
	
	CBasePlayer::GiveAmmo( Magazin_AR2*3,	"AR2" );
	CBasePlayer::GiveAmmo( Magazin_AR2AltFire*2,	"AR2AltFire" );
	CBasePlayer::GiveAmmo( Magazin_Frag*3,	"grenade" );
	CBasePlayer::GiveAmmo( Magazin_357*3 ,	"357" );
	
	// Eigenschaft des Supporter's aufrufen:
	// GetAmmo();
}

// Medic:
void CBasePlayer::SetClassMedic()
{
	// Standardwerte für Leben/Armor:
	m_iHealth = 100;
	m_iArmor = 75;
 
	// Maximal Leben/Armor für Klasse:
	m_iMaxHealth = 100;
	m_iMaxArmor = 75;

	// Waffen verteilen:
	Msg("Du bist jetzt ein Medic!\n");

	CBasePlayer::GiveNamedItem( "weapon_357" );
	CBasePlayer::GiveNamedItem( "weapon_smg1" );
	
	CBasePlayer::GiveAmmo( Magazin_357*2,	"357" );
	CBasePlayer::GiveAmmo( Magazin_SMG1*2 ,	"SMG1");
	CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*3,	"smg1_grenade");
}

// Legt das Leben für die Klasse fest:
int  CBasePlayer::GetClassHealth()const
{
	return m_iHealth;
}

// Legt die Armor für die Klasse fest:
int  CBasePlayer::GetClassArmor()const
{
	return m_iArmor;
}

// Diese Methoden setzen für jede Klasse das Leben und die Armor
int CBasePlayer::GetClassMaxHealth()const
{
	return m_iMaxHealth;
}

// Diese Methoden holen für jede Klasse das Leben und die Armor
int CBasePlayer::GetClassMaxArmor()const
{
	return m_iMaxArmor;
} 

// Setzt Leben und Armor für den Spieler!
void CBasePlayer::SetClassStuff()
{
	// Hier Leben setzen:    
	SetHealthValue(GetClassHealth());
	SetMaxHealthValue(GetClassMaxHealth());

	// Variable gehört nicht zu CBasePlayer!!!
	// TODO: Das ist eine Private Variable, die ich ändern muss!
	SetArmorValue(GetClassArmor());
	// SetMaxArmorValue(GetClassMaxArmor());
}

// Klasse per ConVar Wechsel für Client:
void CBasePlayer::ChangeClass(int NewClass)
{
	m_iClass = NewClass;
}

Da man ja schon weiß, wofür die ganzen Methoden sind, man nurnoch schauen wie alles läuft :)

Aber nun mal zum Einbauen der ConVars, die man braucht:

// Der Server hat für alle die selbe Standardklasse!
// Der Admin kann die Standartklasse ändern!
ConVar default_class("default_class", "3", FCVAR_ARCHIVE, "Variable für Standardklasse!");

Nun hat der Server seine Default-Klasse, die jeder Spieler beim Spawnen bekommt. Diese kann man dann über die Konsole ändern :)

Man kann auch noch mehr Settings für den Admin zur Verfügung stellen:

// Maximale Klassen erlauben(Admins!)
// TODO: Muss noch aktiviert werden im Code!
ConVar max_assaulter("max_assaulter", "3", FCVAR_ARCHIVE, "Variable für Maximale Assaulter!");
ConVar max_supporter("max_supporter", "3", FCVAR_ARCHIVE, "Variable für Maximale Supporter!");
ConVar max_medic("max_medic", "3", FCVAR_ARCHIVE, "Variable für Maximale Medics!");
// Klassen erlauben(Admins):
// TODO: Muss noch aktiviert werden im Code!
ConVar allow_assaulter("allow_assaulter", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Ground Unit!");
ConVar allow_supporter("allow_supporter", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Support Unit!");
ConVar allow_medic("allow_medic", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Medic!");

Diese muss man aber selbst implementieren!

Nun muss man nur noch dem Spieler die Möglichkeit zum Ändern der Klasse geben! Und dann muss man noch die Prüfungen der Klassen einbauen!

Dazu muss man einfach in der Methode OnClientCommand folgendes am Ende einbauen:

	else if ( !stricmp( cmd, "class" ) )
	{
		if ( engine->Cmd_Argc() < 2 )
			return true;

		int iClass = atoi( engine->Cmd_Argv(1) );
		ChangeClass(iClass);

   		return true;
	}

Hiermit wird eine ConVar simuliert. Aber man kann durch Class und dann die Klassennummer, also Class 1, die Klasse ändern! Nun muss man aber die Prüfungsmethode aufrufen und dann hat man es schon!

Das macht man in der Methode PostThink();

Dort setzt man am Ende einfach folgendes ein:

OnClassChange();

Das wars :) wenn alles stimmt dann dürfte das Klassensystem funktionieren ;)

Wichtiges zum Code

Damit der Code ohne Probleme läuft, muss man noch einiges fixen!

Erstmal muss man noch folgende Methoden im Public-bereich hinzufügen:

// Deklarationen:
	// Added von mir:
	void SetHealthValue( int value );
	int  GetHealthValue();
	void SetMaxHealthValue(int MaxValue);
	int  GetMaxHealthValue();
	void IncrementHealthValue( int nCount );

	int     GetArmorValue();
// Implementierung:

void CBasePlayer::SetHealthValue( int value )
{
	m_iHealth = value;
}

void CBasePlayer::SetMaxHealthValue( int MaxValue )
{
	m_iMaxHealth = MaxValue;
}

int CBasePlayer::GetHealthValue()
{
	return m_iHealth;
}

int CBasePlayer::GetMaxHealthValue()
{
	return m_iMaxHealth;
}

void CBasePlayer::IncrementHealthValue( int nCount )
{ 
	m_iHealth += nCount;
	if (m_iMaxHealth > 0 && m_iHealth > m_iMaxHealth)
		m_iHealth = m_iMaxHealth;
}

int CBasePlayer::GetArmorValue()
{
	return m_ArmorValue;
}

Damit kann man die Armor und das Health setzen (je nachdem, wieviel die Klassen haben sollen). Aber auch Medic und Supporter können so ihr Leben/Rüstung regenerieren!

Nun muss man noch die Munitionsverteilung ändern. Entweder man gibt die Munitionszahlen selber ein oder man erstellt eine Header wie folgt: Header definition starten:

#ifndef _Magazin_H
#define _Magazin_H

Die Magazinsdefinitionen:

// Nur verändern, wenn man die Munition der Waffenmagazine im script/code ändert!
// Legt die Magazine fest(Magazin und dann größe in Kugeln)!
#define Magazin_None           0
#define Magazin_Pistole       20
#define Magazin_357            6
#define Magazin_SMG1          50
#define Magazin_SMG1_Granates  1
#define Magazin_AR2          100
#define Magazin_AR2AltFire     1
#define Magazin_RPG            1
#define Magazin_Slam           1
#define Magazin_Frag           1
#define Magazin_Shotgun        8
#define Magazin_Crossbow       1

Die maximale Definition eines Magazins:

// Legt die Mazimale Muntion/Magazine fest:
#define Max_Magazin_None           Magazin_None*0
#define Max_Magazin_Pistole        Magazin_Pistole*16
#define Max_Magazin_357            Magazin_357*6
#define Max_Magazin_SMG1           Magazin_SMG1*8
#define Max_Magazin_SMG1_Granates  Magazin_SMG1_Granates*7
#define Max_Magazin_AR2            Magazin_AR2*3
#define Max_Magazin_AR2AltFire     Magazin_AR2AltFire*2
#define Max_Magazin_RPG            Magazin_RPG*25
#define Max_Magazin_Slam           Magazin_Slam*10
#define Max_Magazin_Frag           Magazin_Frag*10
#define Max_Magazin_Shotgun        Magazin_Shotgun*13
#define Max_Magazin_Crossbow       Magazin_Crossbow*50

Die Magazinsdefinition beenden:

#endif //_Magazin_H

Ich würde zur zweiten Methode raten, da man hier auch die Munitionsdefinitions in den gamerules.cpp ändern kann und die maximale Munition einbinden kann!

Und man sollte die Default Klasse per CooVars setzen oder die Default Klasse aus dem Coden komplett entfernen!


Klassenmenu öffnen

Damit man das Klassen system später aufrufen kann, solte man in der baseviewport.cpp dieses eintragen:

 // sub dialogs
 #include "clientscoreboarddialog.h"
 #include "spectatorgui.h"
 #include "teammenu.h"
 #include "classmenu.h"
 #include "vguitextwindow.h"
 #include "IGameUIFuncs.h"
 #include "mapoverview.h"
 #include "hud.h"
 #include "NavProgress.h"

Jetzt den Befehl zum Aufrufen:

 CON_COMMAND( chooseclass, "Opens a menu for class choose" )
 {
 	if ( !gViewPortInterface )
 		return;
 
 	gViewPortInterface->ShowPanel( "class", true );
 }

Etwas weiter unten das hier:

 void CBaseViewport::CreateDefaultPanels( void )
 {
 #ifndef _XBOX
 	AddNewPanel( CreatePanelByName( PANEL_SCOREBOARD ) );
 	AddNewPanel( CreatePanelByName( PANEL_INFO ) );
 	AddNewPanel( CreatePanelByName( PANEL_SPECGUI ) );
 	AddNewPanel( CreatePanelByName( PANEL_SPECMENU ) );
 	AddNewPanel( CreatePanelByName( PANEL_NAV_PROGRESS ) );
 
 	AddNewPanel( CreatePanelByName( PANEL_TEAM ) );
 	AddNewPanel( CreatePanelByName( PANEL_CLASS ) );
 
 	// AddNewPanel( CreatePanelByName( PANEL_BUY ) );
 #endif
 }

Und zuletzt das hier:

 IViewPortPanel* CBaseViewport::CreatePanelByName(const char *szPanelName)
 {
 	...
 	else if ( Q_strcmp(PANEL_TEAM, szPanelName) == 0 )
 	{
 		newpanel = new CTeamMenu( this );
 	}
 	else if ( Q_strcmp(PANEL_CLASS, szPanelName) == 0 )
 	{
 		newpanel = new CTeamMenu( this );
 	}
 	...
 }

Bonus

Nun kann man sich noch einen Spaß erlauben und jeder Klasse eine bestimmte Geschwindigkeit zuweisen.

Dazu braucht man die hl2_player.cpp und die player.h/.cpp

Als erstes muss man ausfindig machen, wo der Spieler seine Laufgeschwindigkeit einstellt!

Das macht man hier:


#ifdef HL2MP
 #define	HL2_WALK_SPEED 150
 #define	HL2_NORM_SPEED 190
 #define	HL2_SPRINT_SPEED 320
#else
 #define	HL2_WALK_SPEED hl2_walkspeed.GetFloat()
 #define	HL2_NORM_SPEED hl2_normspeed.GetFloat()
 #define	HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
#endif 

Die Erklärung ist simpel. Wenn HL2MP definiert ist, dann werden Standardwerte gesetzt:

#ifdef HL2MP
 #define	HL2_WALK_SPEED 150
 #define	HL2_NORM_SPEED 190
 #define	HL2_SPRINT_SPEED 320

Ansonsten kann man alles über die ConVars machen, aber in HL2DM geht das nur über die Standardwerte!

#else
 #define	HL2_WALK_SPEED hl2_walkspeed.GetFloat()
 #define	HL2_NORM_SPEED hl2_normspeed.GetFloat()
 #define	HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
#endif 

Aber nun muss man eine Steuerung bauen für den Speed!

Dazu muss man nur eine Kleinigkeit zum Basisspieler hinzufügen. player.h öffnen und folgendes im letzten Public-bereich hinzufügen:

// Hier wird die Spielergeschwindigkeit gesetzt:
void SetWalkSpeed(int WalkSpeed);
void SetNormSpeed(int NormSpeed);
void SetSprintSpeed(int SprintSpeed);

Damit kann man die Werte setzen, aber nun braucht man noch die Variablen der Klasse:

// Spielergeschwindigkeit:
int m_iWalkSpeed; 
int m_iNormSpeed;
int m_iSprintSpeed;

Nun noch die Methoden zum holen der Werte:

int GetWalkSpeed(); 
int GetNormSpeed();
int GetSprintSpeed();

Jetzt zur Implementierung:

void CBasePlayer::SetWalkSpeed(int WalkSpeed)
{
	m_iWalkSpeed=WalkSpeed;
}

void CBasePlayer::SetNormSpeed(int NormSpeed)
{
	m_iNormSpeed=NormSpeed;
}

void CBasePlayer::SetSprintSpeed(int SprintSpeed)
{
	m_iSprintSpeed=SprintSpeed;
}

int CBasePlayer::GetWalkSpeed() 
{
	return m_iWalkSpeed;
}

int CBasePlayer::GetNormSpeed()
{
	return m_iNormSpeed;
}

int CBasePlayer::GetSprintSpeed()
{
	return m_iSprintSpeed;
}

Jetzt muss man nur noch 2 Sachen tun!

Als erstes muss man sagen, dass die Get Methoden die Geschwindigkeit steuern.

Das macht man wie folgt:

 #define	HL2_WALK_SPEED CBasePlayer::GetWalkSpeed()
 #define	HL2_NORM_SPEED CBasePlayer::GetNormSpeed()
 #define	HL2_SPRINT_SPEED CBasePlayer::GetSprintSpeed()

Das wars auch schon fast.

Jetzt muss man nurnoch definieren welche Geschwindigkeit eure Klasse haben soll.

Dazu mmuss man nur in den SetClass Methoden die neuen SetSpeed Methoden einbauen.

Dann kann man eine Klasse schnell einbauen.

Hier mal ein Beispiel meiner Supportunit-Klasse:

// Supporter
void CBasePlayer::SetClassSupportUnit()
{
	// Code hier rein!
	// m_szClassName = ClassNames[CLASS_SUPPORT];

	// Standartwerte für leben/armor:
	m_iHealth = 100;
	m_iArmor = 300;

	// Maximal Leben/Armor für Jede Klasse:
	m_iMaxHealth = 100;
	m_iMaxArmor = 300;

	// Waffen verteilen:
	Msg("Du bist jetzt eine Support Unit!\n");


	switch(GetTeamNumber())
	{
	case TEAM_REBELS:
		// Hier waffenverteilen:
		// Rebelen:
		CBasePlayer::GiveNamedItem( "weapon_reb_hmg" );
		CBasePlayer::GiveNamedItem( "weapon_reb_frag" );
		break;

	case TEAM_COMBINE:
		// Hier waffenverteilen:
		// Combine:
		CBasePlayer::GiveNamedItem( "weapon_com_hmg" );
		CBasePlayer::GiveNamedItem( "weapon_com_frag" );	
		break;

	default:
		CBasePlayer::GiveNamedItem( "weapon_ar2" );
		CBasePlayer::GiveNamedItem( "weapon_frag" );
		break;
	}
	CBasePlayer::GiveNamedItem( "weapon_ammo_spawner" );
	CBasePlayer::GiveAmmo( Magazin_AR2*3,	"AR2" );
	CBasePlayer::GiveAmmo( Magazin_Frag*3,	"grenade" );
	SetWalkSpeed(90);
	SetNormSpeed(130);
	SetSprintSpeed(200);
}

Ich habe auch schon die Waffenverteilung auf die Teams gesetzt aber das kann ich auch noch erklären ;) Sobald ich das verbessert hab (z.b. mit einer Team-Variable) ;)

Also viel Spaß und schreibt mir falls es Probleme oder Kritik gibt ;)