Scriptspieler

Einleitung

Es ist 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.
Beispiel:
/* Script of a game rule named "Create AI" */

func Activate(int player)
{
  // The player selected the rule. Create a AI enemy.
  return CreateScriptPlayer("Computer", 0x7f7f7f);
}
  
func InitializePlayer(int player)
{
  // A player has joined. This call is issed to both the scenario script and all game rules, goals and 
  // environment objects
  // Is it a script player?
  if (GetPlayerType(player) == C4PT_Script)
  {
    // Then take over the controls for all clonks!
    var crew_counter, crew;
    while (crew = GetCrew(player, crew_counter++))
      AddEffect("Int_EAI", crew, 1, 100, this);
  }
}
  
func FxInt_EAITimer(object crew, effect, int time)
{
  // attack next enemy
  var enemy = FindObject(Find_Hostile(GetOwner(crew)), Find_OCF(OCF_Alive), Sort_Distance());
  if (enemy) crew->SetCommand("Attack", enemy);
  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 of a rule named "Create aliens" */

func Activate(int player)
{
  // The player selected the game rule. Create an alien enemy!
  return CreateScriptPlayer("Aliens", 0x00ff00, 0, CSPF_FixedAttributes | CSPF_NoScenarioInit, GetID());
}
  
func InitializeScriptPlayer(int player, int idTeam)
{
  // An alien player has joined
  // since no scenario initialization has been executed, a crew for this player has to be created in this callback
  // create a green clonk. A real scenario should of course have it's own aliens :-)
  var alien = CreateObject(Clonk, LandscapeWidth()/2, -1, player);
  MakeCrewMember(alien, player);
  SetClrModulation(0x7fff7f, alien);
  // and attack
  AddEffect("Int_EAI", alien, 1, 100, 0, GetID());
}
  
func FxInt_EAITimer(object crew, effect, int time)
{
  // Attack next enemy
  var enemy = FindObject(Find_Hostile(GetOwner(crew)), Find_OCF(OCF_Alive), Sort_Distance());
  if (enemy) crew->SetCommand("Attack", enemy);
  return FX_OK;
}
Sven2, 2007-12