<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE doc
  SYSTEM '../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../clonk.xsl"?>
<doc>
  <title>Script Player</title>
  <h>Script Player</h>
  <part>
    <h id="Intro">Introduction</h>
    <text>It is possible to create non-human players by script. These players have all the properties of normal players. They have a crew, wealth, build knowledge, home base material, hostility, teams, etc. However, script players do not have a game view port and they can not be controlled by human users.</text>
    <text>Script players can be used to create AI controlled opponent teams in special scenarios.</text>
    <h id="Runtime">Runtime join</h>
    <text>For creating an AI player at run time - e.g. a new deathmatch opponent - you can use <funclink>CreateScriptPlayer</funclink>. This call will be followed (possibly with a little delay) by a InitializePlayer callback for the new player.</text>
    <text>Example:</text>
    <code>/* Script of a game rule named &quot;Create AI&quot; */

func Activate(int player)
{
  // The player selected the rule. Create a AI enemy.
  <funclink>return</funclink> <funclink>CreateScriptPlayer</funclink>(&quot;Computer&quot;, 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?
  <funclink>if</funclink> (<funclink>GetPlayerType</funclink>(player) == C4PT_Script)
  {
    // Then take over the controls for all clonks!
    var crew_counter, crew;
    <funclink>while</funclink> (crew = <funclink>GetCrew</funclink>(player, crew_counter++))
      <funclink>AddEffect</funclink>(&quot;Int_EAI&quot;, crew, 1, 100, <funclink>this</funclink>);
  }
}
  
func FxInt_EAITimer(object crew, effect, int time)
{
  // attack next enemy
  var enemy = <funclink>FindObject</funclink>(<funclink>Find_Hostile</funclink>(<funclink>GetOwner</funclink>(crew)), <funclink>Find_OCF</funclink>(<funclink>OCF_Alive</funclink>), <funclink>Sort_Distance</funclink>());
  <funclink>if</funclink> (enemy) crew-&gt;<funclink>SetCommand</funclink>(&quot;Attack&quot;, enemy);
  <funclink>return</funclink> FX_OK;
}
</code>
    <text>This script for a sample rule object allows the user to create AI opponents at runtime. Also, it will order all clonks of the AI players to attack. Notice: this sample script only handles those clonks automatically created through the scenario settings. Additional clonks placed by specialized scripting are not handled.</text>
    <text>For internet games you can set MaxScriptPlayers in <emlink href="scenario/Teams.html">Teams.txt</emlink> to a value greater than 0. This will enable the option to have script players join in the game lobby.</text>
    <h id="Preset">Default join</h>
    <text>If scripted players should be present in the scenario right from the beginning (comparable to objects created via Objects.txt) then you should create them via the SavePlayerInfos.txt component:</text>
    <code>[PlayerInfoList]
LastPlayerID=1

  [Client]
  ID=0
  Flags=Initial

    [Player]
    Name=&quot;Aliens&quot;
    Flags=Joined
    ID=1
    Type=Script
    Team=2
    Color=65535
    GameNumber=1
    GameJoinFrame=0
</code>
    <text>This method effectively performs a player restore, similar to a regular player restore in a savegame resume. Consequently, no InitializePlayer is called for this player and no startup material or crew is created. A crew for this player should be created in the scenario script. Alternatively, a crew may be present in the Objects.txt. Otherwise, the script player will be eliminated shortly after game start.</text>
    <text>Contrary to regular players, script players are saved if you perform "Save as scenario" in developer mode. This way, you can automatically generate the correct SavePlayerInfos.txt and an according crew in the Objects.txt of the scenario. In order to do this, simply execute <funclink>CreateScriptPlayer</funclink> from the console and create some Clonks for the newly created script player. Then save the game as scenario. The script player Clonks will automatically be restored with the correct owner and in the crew of the script player.</text>
    <h id="Specialized">Specialized Players</h>
    <text>Sometimes it can be desirable to create a script player at runtime, but still execute a specialized initialization instead of the default crew and base material creation. For example, a Hazard deathmatch scenario may provide a specialized alien enemy.</text>
    <text>By passing a parameter to <funclink>CreateScriptPlayer</funclink>, scenario initialization can be prevented, i.e. no creation of start materials, no setting of player parameters such as homebase material by the according presets and no InitializePlayer-calls are done. Instead, a single CreateScriptPlayer-callback is done to the definition passed as idExtraData. Example:</text>
    <code>/* Script of a rule named &quot;Create aliens&quot; */

func Activate(int player)
{
  // The player selected the game rule. Create an alien enemy!
  <funclink>return</funclink> <funclink>CreateScriptPlayer</funclink>(&quot;Aliens&quot;, 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
  <funclink>AddEffect</funclink>(&quot;Int_EAI&quot;, alien, 1, 100, 0, GetID());
}
  
func FxInt_EAITimer(object crew, effect, int time)
{
  // Attack next enemy
  var enemy = <funclink>FindObject</funclink>(<funclink>Find_Hostile</funclink>(<funclink>GetOwner</funclink>(crew)), <funclink>Find_OCF</funclink>(<funclink>OCF_Alive</funclink>), <funclink>Sort_Distance</funclink>());
  <funclink>if</funclink> (enemy) crew-&gt;<funclink>SetCommand</funclink>(&quot;Attack&quot;, enemy);
  <funclink>return</funclink> FX_OK;
}
</code>
  </part>
  <author>Sven2</author><date>2007-12</date>
</doc>
