De/Creating A Class System: Difference between revisions

From Valve Developer Community
< De
Jump to navigation Jump to search
mNo edit summary
mNo edit summary
 
(50 intermediate revisions by 14 users not shown)
Line 1: Line 1:
{{TutPOV}}
{{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.
Das Klassensystem ist aus meiner [http://moonlight-interactive.sponsored.by.clanserver4u.de/armymod/ Army Mod].
Dass ist eine abgeschwächte Version mit nur 3 Klassen aber am ende könnt ihr das erweitern!
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.
Aber der Server bekommt eine Standartklasse die jeder beim ersten Spawnen bekommt :)
 
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 21: Line 21:




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


Das wars auch schon :)
==Die Deklarationen==


==Die Deklarationen==
Als erstes muss man die Deklarationen machen.
Was man braucht sind einige Variablen und einige Funktionen.
Als erstes müssen wir uns an die Deklarationen machen.
Als Erstes erstellt man ein Enum das die Werte der Klassen enthält!
Was wir brauchen sind einige Variablen und einige Funktionen.
Das enum kommt in die ''player.h'' über ''class CBasePlayer;''
Aber als erstes werden wir ein Enum erstellen das die Werte unserer Klassen enthält!


Das Enum machen wir erstmal so:
Das Enum macht man erstmal so:
  enum
  enum
  {
  {
  Unassigned=0,
Unassigned = 0,
  Assaulter,
Assaulter,
  Supporter,
Supporter,
  Medic
Medic,
  }
  };


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


Jetzt werden wir uns der Klasse CBasePlayer wiedmen.
Jetzt widme man sich der Klasse ''CBasePlayer''.
 
Zum beginn werden wir einen neuen public und einen neuen Private bereich am ende von CBasePlayer adden.
Zum Beginn erstellt man einen neuen ''public'' und einen neuen ''private'' Bereich am Ende von ''CBasePlayer'' in der ''player.h''.
Dort schreiben wir den gesamten Code für das Klassensystem rein!
Dort schreibt man den gesamten Code für das Klassensystem rein!


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




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


  /*
  /*
Line 62: Line 61:
  */
  */
  public:
  public:
//Methode zum Klassenwechseln
// Methode zum Klassenwechseln
virtual void ChangeClass(int NewClass);  
virtual void ChangeClass(int NewClass);  
virtual int GetClass();
virtual int GetClass();
  //Initalisierung des Klassensystems!
   
void InitClassSystem();
// Initalisierung des Klassensystems!
//Überprüft ob wir keinen ungültigen wert setzen für die Klassenvariablen:
void InitClassSystem();
void CheckAllClassConVars();
// Überprüft ob wir keinen ungültigen wert setzen für die Klassenvariablen:
  //Hier wird geprüft ob wir die Klasse gewechselt haben:
void CheckAllClassConVars();
void OnClassChange();
   
//Setzt Leben und Armor für den Spieler!
// Hier wird geprüft ob wir die Klasse gewechselt haben:
void SetClassStuff();
void OnClassChange();
  //Setzt den neuen wert für die Check Variable:
// Setzt Leben und Armor für den Spieler!
void SetCurrentClassValue();
void SetClassStuff();
  //Die Klasse holen:
   
int GetClassValue()const;
// Setzt den neuen wert für die Check Variable:
//Die Standartklasse holen:
void SetCurrentClassValue();
int GetDefaultClassValue()const;
   
  //Schaltmethode die Klassen verteilt:
// Die Klasse holen:
void SetPlayerClass();
int GetClassValue()const;
  //Legt das Leben für unsere klasse fest:
// Die Standartklasse holen:
int  GetClassHealth()const;
int GetDefaultClassValue()const;
//Diese Methoden setzen für jede Klasse das Leben und die Armor
   
int GetClassMaxHealth()const;
// Schaltmethode die Klassen verteilt:
  //Legt die Armor für die Klasse Fest:
void SetPlayerClass();
int  GetClassArmor()const;
   
//Diese Methoden holen für jede Klasse das Leben und die Armor
// Legt das Leben für unsere klasse fest:
int GetClassMaxArmor()const;
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 );


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();
  /*
  /*
  ********************************************
  ********************************************
Line 112: Line 121:
  ********************************************
  ********************************************
  */
  */
//Integer für Rüstung:
// Integer für Rüstung:
int m_iArmor;
int m_iArmor;
int m_iMaxArmor;
int m_iMaxArmor;


 
Nun zur Erklärung, von oben nach unten.
 
Nun kommen wir zur Erklärung wofür wir das alles brauchen.
Wir fangen oben an und arbeiten uns runter.


==public==
==public==


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


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


  virtual int GetClass();
  virtual int GetClass();


Brauchen wir wenn wir noch sachen wie Ammo, Leben und Rüstungsincrementierung für den Supporter/Medic einbauen wollen.
Braucht man, wenn man noch Sachen wie Munition, Leben und Rüstungsinkrementierung für den Supporter/Medic einbauen will.


  //Initalisierung des Klassensystems!
  // Initalisierung des Klassensystems!
  void InitClassSystem();
  void InitClassSystem();


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


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


Diese Methode prüft ob wir einen gültigen Wert eingegeben haben!
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 wir das nicht Prüfen würden, dann würde eure Mod crashen!
Falls der Wert nicht stim dann müsst ihr einen Wert für die Klasse setzen!
Falls der Wert nicht stimmt, dann müsst ihr einen Wert für die Klasse setzen.


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


Diese Methode wird in der Thinkmethode gebraucht.
Diese Methode wird in der Thinkmethode gebraucht.
Falls ihr die Klasse wechselt werdet ihr gekillt und einen Punkt sonst würdet ihr Punktabzug bekommen.
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!
  // Setzt Leben und Armor für den Spieler!
  void SetClassStuff();
  void SetClassStuff();


Diese Methode setzt für jede Klasse den Wert für das Leben, Maximal Leben und Rüstung.
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 den müsst ihr ändern.
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:
  // Setzt den neuen wert für die Check Variable:
  void SetCurrentClassValue();
  void SetCurrentClassValue();


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


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


Diese Methode holt uns den Wert der Klasse die wir haben.
Diese Methode holt den Wert der Klasse die man hat.


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


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


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


Diese Methode ist die Steuerzentrale unseres Klassensystems.
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 müsst ihr leider noch machen!)
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 unsere klasse fest:
  // Legt das Leben für die Klasse fest:
  int  GetClassHealth()const;
  int  GetClassHealth()const;


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


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


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


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


Diese Methode holt euch die Rüstung die ihr im Momen habt.
Diese Methode holt die Rüstung, die man im Moment hat.


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


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


==private==
==private==


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


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


  bool IsFirstSpawn();
  bool IsFirstSpawn();


Diese Methode gibt uns auskunft ob wir das erstemal gespawnt sind.
Diese Methode gibt Auskunft ob man das Erste mal gespawnt wird.


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


Diese Variable speichert den Wert unserer Klasse:
Diese Variable speichert den Wert der Klasse:


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


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


  //Hier setzen wir die Standartklasse!
  // Hier setzt man die Standardklasse!
  int m_iDefaultClass;
  int m_iDefaultClass;
Diese Variable holt die Klasse des Servers und ist gleichzeitig die erste Klasse die man beim ersten spawn
Diese Variable holt die Klasse des Servers und ist gleichzeitig die erste Klasse, die man beim ersten Spawn
bekommt
bekommt.


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


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


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


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


Jetzt wisst ihr was wir alles brauchen und wieso.
Jetzt wei0 man was man alles braucht und wieso.


Nun kommen wir zum Teil in dem wir alles Einbauen.
Nun kommt man zum Teil in dem man alles Einbaut.


==Die Implementation==
==Die Implementation==


Da wir nun alles haben was wir brauchen müssen wir nur alles einbauen!
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:
Hier der Code mit Erklärung:


//Added im Konstruktor:
Added im Konstruktor:
  //Erster Spawn?
// Erster Spawn?
  m_bFirstSpawn=true;
m_bFirstSpawn = true;
   
   
  //Startklasse festlegen:
// Startklasse festlegen:
  m_iClass=Default_Class.GetInt();
m_iClass = default_class.GetInt();
    
    
  m_iCurrentClass=m_iClass;
m_iCurrentClass = m_iClass;
  //Die standart Klasse setzen!
// Die Standardklasse setzen!
  m_iDefaultClass=Default_Class.GetInt();
m_iDefaultClass = default_class.GetInt();
   
   
  //Standartwerte 100 für leben/armor:
// Standartwerte 100 für Leben/Armor:
  m_iHealth=100;
m_iHealth = 100;
  m_iArmor=100;
m_iArmor = 100;
   
   
  //Maximal Leben/Armor für Jede Klasse:
// Maximal Leben/Armor für jede Klasse:
  m_iMaxHealth=100;
m_iMaxHealth = 100;
  m_iMaxArmor=100;
m_iMaxArmor = 100;


  void CBasePlayer::InitClassSystem()
  void CBasePlayer::InitClassSystem()
  {
  {
    DevMsg("Klassensystem wird initalisiert!\n");
DevMsg("Klassensystem wird initalisiert!\n");
    CheckAllClassConVars();
CheckAllClassConVars();
    SetPlayerClass();
SetPlayerClass();
    SetClassStuff();
SetClassStuff();
  }
  }
 
 
 
  int CBasePlayer::GetClassValue()const
  int CBasePlayer::GetClassValue()const
  {
  {
  return m_iClass;
return m_iClass;
  }
  }
 
 
  int CBasePlayer::GetDefaultClassValue()const
  int CBasePlayer::GetDefaultClassValue()const
  {
  {
  return m_iDefaultClass;
return m_iDefaultClass;
  }
  }
 
 
  bool CBasePlayer::IsFirstSpawn()
  bool CBasePlayer::IsFirstSpawn()
  {
  {
  return m_bFirstSpawn;
return m_bFirstSpawn;
  }
  }
 
 
  // Soll die Waffen verteilen:
  //Soll die Waffen verteilen:
  // Hier ist irgend wo ein Bug!
  //Hier ist irgend wo ein Bug!
  void CBasePlayer::SetPlayerClass()
  void CBasePlayer::SetPlayerClass()
  {
  {
if(IsFirstSpawn()==true)
if(IsFirstSpawn())
{
{
          m_iClass=m_iDefaultClass;
m_iClass = m_iDefaultClass;
}
}
 
        // Jetzt werden die Klassen verteilt:
      switch(m_iClass)
switch(m_iClass)
      {
{
      case Invalid:
case Assaulter:
            //Muss noch eingebaut werden!
SetClassGroundUnit();
            //Hier bekommt man nur die standart Waffen!
break;
            SetClassDefault();
case Supporter:
      break;
SetClassSupportUnit();
      case Unassigned:
break;
            //Hier setzen wir die Standart-Klasse!
case Medic:
            SetClassDefault();
SetClassMedic();
      break;
break;
      case Assaulter:
//case Invalid:
            SetClassGroundUnit();
// Muss noch eingebaut werden!
      break;
// Hier bekommt man nur die Standardwaffen!
      case Supporter:
case Unassigned:
            SetClassSupportUnit();
// Hier setzt man die Standardklasse!
      break;
default:
      case Medic:
SetClassDefault();
            SetClassMedic();
break;
      break;   
}
      default:
            SetClassDefault();
      break;
      }
  }
  }
 
  void CBasePlayer::OnClassChange()
  void CBasePlayer::OnClassChange()
  {
  {
  if(m_iClass!=m_iCurrentClass)
if( m_iClass != m_iCurrentClass )
  {     
{     
  //Spieler killen und punkte um 1 erhöhen(wegen Selbstmord)!
// Spieler killen und Punkte um 1 erhöhen (wegen Selbstmord)!
  CommitSuicide();
CommitSuicide();
  IncrementFragCount(1);
IncrementFragCount(1);
    
    
  //Setzen damit wir nicht die Selbe Klassen haben!
// Setzen, damit man nicht die selbe Klasse hat!
  m_iCurrentClass=m_iClass;
m_iCurrentClass = m_iClass;
  }
}
  }
  }
 
 
  void CBasePlayer::CheckAllClassConVars()
  void CBasePlayer::CheckAllClassConVars()
  {
  {
  //Haben wir eine Klasse die zwischen Spec-Ops und der letzen Klasse liegt?
// Hat man eine Klasse, die zwischen Spec-Ops und der letzen Klasse liegt?
  if(m_iClass<Assaulter||m_iClass>Medic)
if( m_iClass < Assaulter || m_iClass > Medic )
  {
{
  m_iClass=Assaulter;
m_iClass = Assaulter;
  }
}
    
    
  if(m_iDefaultClass<Assaulter||m_iDefaultClass>Medic)
if( m_iDefaultClass < Assaulter || m_iDefaultClass > Medic )
  {
{
  m_iDefaultClass=Assaulter;
m_iDefaultClass = Assaulter;
  }
}
  }
  }
 
  void CBasePlayer::SetClassDefault()
  void CBasePlayer::SetClassDefault()
  {
  {
  Msg("Du hast die Klasse Default!\n");
Msg("Du hast die Klasse Default!\n");
   
   
  CheckAllClassConVars();
CheckAllClassConVars();
  SetPlayerClass();
SetPlayerClass();
  }
  }
 
 
  // Assault:
  //Assault:
  void CBasePlayer::SetClassGroundUnit()
  void CBasePlayer::SetClassGroundUnit()
  {
  {
  //Standartwerte für leben/armor:
// Standardwerte für Leben/Armor:
  m_iHealth=125;
m_iHealth = 125;
  m_iArmor=125;
m_iArmor = 125;
   
   
  //Maximal Leben/Armor für Jede Klasse:
// Maximal Leben/Armor für jede Klasse:
  m_iMaxHealth=125;
m_iMaxHealth = 125;
  m_iMaxArmor=125;
m_iMaxArmor = 125;
// Waffen verteilen:
Msg("Du bist jetzt eine Ground Unit!\n");
   
   
    //Waffen verteilen:
    Msg("Du bist jetzt eine Ground Unit!\n");
   
  CBasePlayer::GiveNamedItem( "weapon_357" );
  CBasePlayer::GiveNamedItem( "weapon_357" );
  CBasePlayer::GiveNamedItem( "weapon_smg1" );
  CBasePlayer::GiveNamedItem( "weapon_smg1" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
CBasePlayer::GiveAmmo( Magazin_357*3 , "357" );
CBasePlayer::GiveAmmo( Magazin_357*3 , "357" );
CBasePlayer::GiveAmmo( Magazin_SMG1*3        , "SMG1");
CBasePlayer::GiveAmmo( Magazin_SMG1*3        , "SMG1");
CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*1, "smg1_grenade");
CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*1, "smg1_grenade");
CBasePlayer::GiveAmmo( Magazin_Frag*5        , "grenade" );
CBasePlayer::GiveAmmo( Magazin_Frag*5        , "grenade" );
  }
  }
 
 
  // Supporter
  //Supporter
  void CBasePlayer::SetClassSupportUnit()
  void CBasePlayer::SetClassSupportUnit()
  {
  {
  //Standartwerte für leben/armor:
// Standartwerte für leben/armor:
  m_iHealth=100;
m_iHealth = 100;
  m_iArmor=300;
m_iArmor = 300;
   
   
  //Maximal Leben/Armor für Klasse:
// Maximal Leben/Armor für Klasse:
  m_iMaxHealth=100;
m_iMaxHealth = 100;
  m_iMaxArmor=300;
m_iMaxArmor = 300;
 
    //Waffen verteilen:
// Waffen verteilen:
    Msg("Du bist jetzt eine Support Unit!\n");
Msg("Du bist jetzt eine Support Unit!\n");
      
      
  CBasePlayer::GiveNamedItem( "weapon_ar2" );
  CBasePlayer::GiveNamedItem( "weapon_ar2" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
CBasePlayer::GiveNamedItem( "weapon_frag" );
  CBasePlayer::GiveNamedItem( "weapon_357" );
  CBasePlayer::GiveNamedItem( "weapon_357" );
CBasePlayer::GiveAmmo( Magazin_AR2*3, "AR2" );
CBasePlayer::GiveAmmo( Magazin_AR2*3, "AR2" );
CBasePlayer::GiveAmmo( Magazin_AR2AltFire*2, "AR2AltFire" );
CBasePlayer::GiveAmmo( Magazin_AR2AltFire*2, "AR2AltFire" );
CBasePlayer::GiveAmmo( Magazin_Frag*3, "grenade" );
CBasePlayer::GiveAmmo( Magazin_Frag*3, "grenade" );
CBasePlayer::GiveAmmo( Magazin_357*3 , "357" );
CBasePlayer::GiveAmmo( Magazin_357*3 , "357" );
//Eigenschaft des Supporter's aufrufen:
// Eigenschaft des Supporter's aufrufen:
//GetAmmo();
// GetAmmo();
  }
  }
 
 
  // Medic:
  //Medic:
  void CBasePlayer::SetClassMedic()
  void CBasePlayer::SetClassMedic()
  {
  {
  //Standartwerte für leben/armor:
// Standardwerte für Leben/Armor:
  m_iHealth=100;
m_iHealth = 100;
   m_iArmor=75;
m_iArmor = 75;
    
// Maximal Leben/Armor für Klasse:
m_iMaxHealth = 100;
m_iMaxArmor = 75;
   
   
  //Maximal Leben/Armor für Klasse:
// Waffen verteilen:
  m_iMaxHealth=100;
Msg("Du bist jetzt ein Medic!\n");
  m_iMaxArmor=75;
 
CBasePlayer::GiveNamedItem( "weapon_357" );
    //Waffen verteilen:
CBasePlayer::GiveNamedItem( "weapon_smg1" );
    Msg("Du bist jetzt ein Medic!\n");
   
CBasePlayer::GiveAmmo( Magazin_357*2, "357" );
CBasePlayer::GiveNamedItem( "weapon_357" );
CBasePlayer::GiveAmmo( Magazin_SMG1*2 , "SMG1");
CBasePlayer::GiveNamedItem( "weapon_smg1" );
CBasePlayer::GiveAmmo( Magazin_SMG1_Granates*3, "smg1_grenade");
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 unsere klasse fest:
  // Legt das Leben für die Klasse fest:
  int  CBasePlayer::GetClassHealth()const
  int  CBasePlayer::GetClassHealth()const
  {
  {
  return m_iHealth;
return m_iHealth;
  }
  }
 
  //Legt die Armor für die Klasse Fest:
  // Legt die Armor für die Klasse fest:
  int  CBasePlayer::GetClassArmor()const
  int  CBasePlayer::GetClassArmor()const
  {
  {
  return m_iArmor;
return m_iArmor;
  }
  }
 
  //Diese Methoden setzen für jede Klasse das Leben und die Armor
  // Diese Methoden setzen für jede Klasse das Leben und die Armor
  int CBasePlayer::GetClassMaxHealth()const
  int CBasePlayer::GetClassMaxHealth()const
  {
  {
  return m_iMaxHealth;
return m_iMaxHealth;
  }
  }
 
  //Diese Methoden holen für jede Klasse das Leben und die Armor
  // Diese Methoden holen für jede Klasse das Leben und die Armor
  int CBasePlayer::GetClassMaxArmor()const
  int CBasePlayer::GetClassMaxArmor()const
  {
  {
  return m_iMaxArmor;
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:


  //Setzt Leben und Armor für den Spieler!
  // Der Server hat für alle die selbe Standardklasse!
  void CBasePlayer::SetClassStuff()
// 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 )
  {
  {
  //Hier Leben setzen:   
m_iMaxHealth = MaxValue;
  CBasePlayer::SetHealthValue(GetClassHealth());
}
  CBasePlayer::SetMaxHealthValue(GetClassMaxHealth());
   
   
  //Variable gehört nicht zu CBasePlayer!!!
int CBasePlayer::GetHealthValue()
  //TODO: Das ist eine Private Variable die ich ändern muss!
{
  CBasePlayer::SetArmorValue(GetClassArmor());
return m_iHealth;
  //CBasePlayer::SetMaxArmorValue(GetClassMaxArmor());
  }
  }
 
  //Klasse per ConVar wechsel für Client:
  int CBasePlayer::GetMaxHealthValue()
  void CBasePlayer::ChangeClass(int NewClass)
{
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()
  {
  {
  m_iClass=NewClass;
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 );
  }
  ...
  }


Da ihr ja schon wisst wofür die ganzen Methoden sind müsst ihr nur noch gucken wie alles läuft :)
==Bonus==


Aber nun mal zum einbauen der ConVars die man braucht:
Nun kann man sich noch einen Spaß erlauben und jeder Klasse eine bestimmte Geschwindigkeit zuweisen.


//Der Server hat für alle die selbe Standartklasse!
Dazu braucht man die hl2_player.cpp und die player.h/.cpp
//Der Admin kann die Standartklasse ändern!
ConVar Default_Class("Default_Class", "3", FCVAR_ARCHIVE, "Variable für Standartklasse!");


Nun hat der Server seine Default Klasse die jeder Spieler beim spawnen bekommt.
Als erstes muss man ausfindig machen, wo der Spieler seine Laufgeschwindigkeit einstellt!
Diese könnt ihr dann über die Konsole ändern :)


Ihr könnt auch noch mehr Settings für den Admin zur verfügung stellen:
Das macht man hier:


//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):
#ifdef HL2MP
//TODO:Muss noch aktiviert werden im Code!
  #define HL2_WALK_SPEED 150
ConVar Allow_Assaulter("Allow_Assaulter", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Ground Unit!");
  #define HL2_NORM_SPEED 190
ConVar Allow_Supporter("Allow_Supporter", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Support Unit!");
  #define HL2_SPRINT_SPEED 320
ConVar Allow_Medic("Allow_Medic", "1", FCVAR_ARCHIVE, "Erlaubt Klasse Medic!");
#else
  #define HL2_WALK_SPEED hl2_walkspeed.GetFloat()
  #define HL2_NORM_SPEED hl2_normspeed.GetFloat()
  #define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
#endif


Diese müsst ihr aber selbst implementieren!
Die Erklärung ist simpel.
Wenn HL2MP definiert ist, dann werden Standardwerte gesetzt:


Nun müssen wir nur noch dem Spieler die möglichkeit zum ändern der Klasse geben!
#ifdef HL2MP
Und dann muss man noch die Prüfungen der Klassen einbauen!
  #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!


Dazu müssen wir einfach in der Methode OnClientCommand folgendes einbauen am ende:
Als erstes muss man sagen, dass die Get Methoden die Geschwindigkeit steuern.


    else if ( stricmp( cmd, "Class" ) ==0)
Das macht man wie folgt:
    {
    //int iClass=CBasePlayer::GetClass();
    if (engine->Cmd_Argc() < 2)
    return true;


    int iClass = atoi( engine->Cmd_Argv(1));
  #define HL2_WALK_SPEED CBasePlayer::GetWalkSpeed()
    ChangeClass(iClass);
  #define HL2_NORM_SPEED CBasePlayer::GetNormSpeed()
   
  #define HL2_SPRINT_SPEED CBasePlayer::GetSprintSpeed()
    return true;
    }


Hier mit wird eine ConVar Simuliert.
Das wars auch schon fast.
Aber man kann durch Class und dann die Klassennummer also Class 1 die Klasse ändern!
Nun müssen wir aber die Prüfungsmethode aufrufen und dann haben wirs schon!


Das machen wir in der Methode PostThink();
Jetzt muss man nurnoch definieren welche Geschwindigkeit eure Klasse haben soll.


Dort setzen wir am ende einfach folgendes ein:
Dazu mmuss man nur in den SetClass Methoden die neuen SetSpeed Methoden einbauen.


OnClassChange();
Dann kann man eine Klasse schnell einbauen.


Das wars :) wenn alles stimmt dann dürfte euer Klassensystem Funktionieren ;)
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) ;)


Also viel Spaß und schreibt mir falls es Probleme oder Kritik gibt ;)
Also viel Spaß und schreibt mir falls es Probleme oder Kritik gibt ;)
 
{{ACategory|Programming}}{{ACategory|Tutorials}}
[[Category:German]][[Category:Programming:de]]

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