Scriptspieler

Einleitung

Ab CR Build 265 ist es möglich, Spieler auch per Script beitreten zu lassen. Solche Spieler verhalten sich wie normale Spieler. Sie besitzen eine Crew, ein Konto, Heimatbasismaterial, ein Team, Baupläne, etc. Einziger Unterschied ist, dass sie von keinem Spieler gesteuert werden und auf keinem Rechner ein Sichtfenster für diese Spieler geöffnet wird.
Scriptspieler sind nützlich, um zum Beispiel KI-Gegner zu realisieren.

Beitritt zur Laufzeit

Fürs Erstellen einer KI zur Laufzeit - zum Beispiel als Gegner im Deathmatch - dient CreateScriptPlayer. Daraufhin erfolgt (unter Umständen verzögert, weil es sich um einen Spielerbeitritt handelt!) ein InitializePlayer-Aufruf für diesen Scriptspieler. Da der Aufruf verzögert ist, sollte die eigentliche KI-Initialisierung in diesem Aufruf passieren.
Dazu ein Beispiel:
/* Script einer aktivierten Spielregel namens "KI Erzeugen" */

#strict

public func Activate(int iPlr)
  {
  // Der Spieler iPlr hat die Spielregel ausgewählt. Erzeuge einen KI-Gegner!
  return(CreateScriptPlayer("Computer", 0x7f7f7f));
  }
  
protected func InitializePlayer(int iPlr)
  {
  // Ein Spieler ist beigetreten. Dieser Aufruf erfolgt an das Szenarienscript, sowie an alle Spielregeln,
  // Spielziele und Umweltobjekte
  // Ist es ein Scriptspieler?
  if (GetPlayerType(iPlr) == C4PT_Script)
    {
    // Dann übernimm die Steuerung für alle Clonks!
    var iCrew, pCrew;
    while (pCrew = GetCrew(iPlr, iCrew++))
      AddEffect("Int_EAI", pCrew, 1, 100, this());
    }
  }
  
protected func FxInt_EAITimer(object pCrew, int iEffNum, int iEffTime)
  {
  // Nächsten Gegner angreifen
  var pEnemy = FindObject2(Find_Hostile(GetOwner(pCrew)), Find_OCF(OCF_Alive), Sort_Distance());
  if (pEnemy) SetCommand(pCrew, "Attack", pEnemy);
  return FX_OK;
  }
Dieses Beispielscript für ein Regelobjekt erlaubt dem Spieler, zur Laufzeit KI-Gegner zu erstellen. Außerdem sorgt es dafür, dass alle Clonks dieses KI-Gegners angreifen. Achtung: Das Beispiel übernimmt die Kontrolle nur für alle Clonks, die der Spieler zu Spielbeginn nach Szenarienvorgaben erhalten hat. Wenn ein Szenarienscript zum Beispiel noch andere Clonks erstellen würde, würden diese nicht gesteuert.
Für Internetspiele kann man auch MaxScriptPlayers in der Teams.txt auf einen Wert >0 setzen. Dann bekommt man in der Lobby die Option, Scriptspieler zu aktivieren. Diese Spieler treten auch wie gewöhnliche Spieler bei, und man sollte auch hier in InitializePlayer entsprechend das KI-Kontrollobjekt erstellen. Das obige Beispiel würde also auch sofort mit in der Lobby aktivierten KI-Spielern funktionieren.

Beitritt als Vorgabe

Wenn ein Szenario schon von Anfang an einen Scriptspieler beinhalten soll - zum Beispiel weil Objekte in der Objects.txt in dessen Besitz sein sollen, oder weil in Initialize Objekte für diesen Spieler erzeugt werden, dann sollte man diesen wie in einem Savegame definieren. Also eine SavePlayerInfos.txt wie diese anlegen:
[PlayerInfoList]
LastPlayerID=1

  [Client]
  ID=0
  Flags=Initial

    [Player]
    Name="Aliens"
    Flags=Joined
    ID=1
    Type=Script
    Team=2
    Color=65535
    GameNumber=1
    GameJoinFrame=0
Dies führt eine Spieler-Wiederherstellung durch, analog zur Wiederherstellung nach einem Savegame. Es wird also kein InitializePlayer für diesen Spieler aufgerufen. Das Szenarienscript sollte in der Initialisierung die Crew für diesen Spieler erstellen, oder es sollte eine entsprechende Crew in der Objects.txt vorhanden sein. Ansonsten wird der Scriptspieler sofort zum Spielbeginn eliminiert.
Scriptspieler werden im Gegensatz zu regulären Spielern ebenfalls gespeichert, wenn man in der Konsole "Speichern als Szenario" wählt. Auf diese Weise kann man sich die richtige SavePlayerInfos.txt automatisch anlegen lassen. Dazu sollte einfach im Entwicklermodus manuell CreateScriptPlayer aufgerufen und dann Clonks für diesen Scriptspieler verteilt werden. Speichert man dann als Szenario, wird der Scriptspieler mitgespeichert und steht beim Starten wieder zur Verfügung.

Spezialisierte Spieler

Manchmal kann es sinnvoll sein, einen Scriptspieler erst zur Laufzeit zu erstellen aber trotzdem eine spezielle Initialisierung durchzuführen. Zum Beispiel sollte ein spezieller Alien-Gegner in einem Hazard-Deathmatch keine Hazardclonks erhalten.
Mit einem Parameter an CreateScriptPlayer lassen sich die szenarienspezifische Initialisierung, das heißt das Erzeugen des Startmaterials, das Setzen der Startparameter nach Vorgaben und auch alle InitializePlayer-Aufrufe unterbinden. Stattdessen erfolgt nur ein InitializeScriptPlayer-Definitionsaufruf in der angegebenen Definition. Dazu ein Beispiel:
/* Script einer aktivierten Spielregel namens "Aliens erzeugen" */

#strict

public func Activate(int iPlr)
  {
  // Der Spieler iPlr hat die Spielregel ausgewählt. Erzeuge einen Alien-Gegner!
  return(CreateScriptPlayer("Aliens", 0x00ff00, 0, CSPF_FixedAttributes | CSPF_NoScenarioInit, GetID()));
  }
  
protected func InitializeScriptPlayer(int iPlr, int idTeam)
  {
  // Ein Alienspieler ist beigetreten
  // Da keine Szenarieninitialisierung durchgeführt wurde, muss in diesem Callback eine Crew für den Spieler erstellt werden
  // Erstelle einen grünen Clonk - ein richtiges Szenario sollte natürlich echte Aliens mitbringen :)
  var pAlien = CreateObject(CLNK, LandscapeWidth()/2, -1, iPlr);
  MakeCrewMember(pAlien, iPlr);
  SetClrModulation(0x7fff7f, pAlien);
  // Und angreifen lassen
  AddEffect("Int_EAI", pAlien, 1, 100, 0, GetID());
  }
  
protected func FxInt_EAITimer(object pCrew, int iEffNum, int iEffTime)
  {
  // Nächsten Gegner angreifen
  var pEnemy = FindObject2(Find_Hostile(GetOwner(pCrew)), Find_OCF(OCF_Alive), Sort_Distance());
  if (pEnemy) SetCommand(pCrew, "Attack", pEnemy);
  return FX_OK;
  }
Sven2, Dezember 2007