Difference between revisions of "Programming robots"

From Bitfighter
(Flags)
(missing ))
 
(108 intermediate revisions by 14 users not shown)
Line 1: Line 1:
Starting with release 011, server admins will be able to program their own robots in addition to designing their own levels. Robots will be coded in Lua, and will will have a number of special functions available to them.   
+
TODO: FlagItem:getCaptureZone & FlagItem:getShip functions (verify w/ sam); make sure all items have getLoc, getRad, getVel, getTeamIndx, and isItem; hasFlag(); engineer-deploy-object (??), ship/bot:getMountedItems();  document libopt (or at least or inclusion of such with summary and pointer to docs) -- need to do require("getopt") to activate itmention inspect.lua under debugging section.
  
Robot coding is still fluid, and everything is subject to change, but here is a list of commands being implemented for the next alpha release (see forums for the URL):
+
Server admins can program their own robots in addition to designing their own levels.  Robots are coded in Lua, and have a number of special functions available to them. 
 +
 
 +
For examples of fully functional bots, see the [[Robot_gallery|Robot Gallery]]. 
 +
 
 +
Robot coding is still fluid, and the information here is subject to change.  You might want to read about the [[Script structure|general structure of Bitfighter scripts]] before proceeding.
  
  
 
How to read the function notation:
 
How to read the function notation:
  
returnType <b>funtionName( argType1, argType2 )</b> - Description of function<br>
+
{{funcdoc|returnType|functionName|argType1, argType2}} - Description of function<br>
  
  
 
Return types:
 
Return types:
Unlike most languages, Lua does not differentiate between integers and floating point numbers.  Instead, everything is simply a number.
+
Unlike most languages, Lua does not differentiate between integers and floating point numbers.  Instead, everything is simply a number.  In fact, Lua often will let you interchange strings and numbers, converting dynamically on an as-needed basis.  However, relying on this automatic type coercion is bad practice and can get you into trouble.
  
  
 
Points to remember in Lua:
 
Points to remember in Lua:
 
<ul>
 
<ul>
<li>Lua arrays are 1-based: the first index is 1 rather than 0 as is the case in most programming languages</li>
+
<li>Lua arrays are 1-based: the first index is 1 rather than 0 as is the case in programming languages such as C++ and Java</li>
 +
<li>Lua is case sensitive.  That means that the variable foo is different than Foo and FOO</li>
 +
<li>Any variable not declared as local will be global.  Accessing global variables is slower than local ones, and being sloppy about variable scope can lead to unexpected problems.  Use the local keyword whenever appropriate.</li>
 
</ul>
 
</ul>
  
 +
We will follow the Lua formatting conventions used in the Programming in Lua book.
 +
 +
== Events ==
 +
Lua robots are designed to respond to many in-game events using event handlers.  To respond to an event your bot must have an appropriately named function (described below), and must subscribe to any events it is interested in.  You can also unsubscribe to an event at any time to stop receiving event notifications.
 +
 +
==== Currently defined events ====
 +
<table border=1px cellpadding=3px>
 +
<tr><th>Event ID</th><th>Associated function</th><th>Description</th></tr>
 +
<tr><td>TickEvent</td><td>onTick(timeSinceLastTickInMs)</td><td>Called every frame of the game.  Unlike other events, robots are subscribed to this event by default.</td>
 +
<tr><td>ShipSpawnedEvent</td><td>onShipSpawned(ship)</td><td>Called when any ship or robot spawns (not including your robot).  Spawning ship is passed as the first argument.</td></tr>
 +
<tr><td>ShipKilledEvent</td><td>onShipKilled(ship)</td><td>Called when any ship or robot is killed (not including your robot).  Dead ship is passed as the first argument.</td></tr>
 +
<tr><td>MsgReceivedEvent</td><td>onMsgReceived(message, senderPlayerInfo, global)</td><td>Called when a player or robot sends a message.  Message itself is first argument, a PlayerInfo for the sender is the second argument, and the third argument is a boolean that will be true if the message was global, false if sent to teammates only.  Note that senderPlayerInfo can be nil if message is sent by a Controller script.</td></tr>
 +
<tr><td>PlayerJoinedEvent</td><td>onPlayerJoined(playerInfo)</td><td>Called when a player or robot joins the game (not when they spawn).  Note that when hosting a game locally, local player does not necessarily trigger this event due to the uncertain order of creation of game objects.  First argument is playerInfo for the joining player.</td></tr>
 +
<tr><td>PlayerLeftEvent</td><td>onPlayerLeft(playerInfo)</td><td>Called when a player or robot leaves the game.  Note that event may not be fired when robots or local player leaves game due to server shutting down.  First argument is playerInfo for the leaving player.</td></tr>
 +
<tr><td>NexusOpenedEvent</td><td>onNexusOpened()</td><td>Called when Nexus opens in a Nexus game.  Never fires in other game types.</td></tr>
 +
<tr><td>NexusClosedEvent</td><td>onNexusClosed()</td><td>Called when Nexus closes in a Nexus game.  Never fires in other game types.</td></tr>
 +
</table>
 +
 +
Example:
 +
<source lang="lua">
 +
function main()
 +
  ...
 +
  subscribe(ShipSpawnedEvent)    -- Subscribe so onShipSpawned() will be called
 +
  ...
 +
end
 +
 +
-- Since we are subscribed to the ShipSpawned event, the onShipSpawned() function
 +
-- will be called whever a ship or robot spawns.
 +
function onShipSpawned(ship)
 +
  logprint("Ship spawned: ".. tostring(ship:getLoc()))
 +
  angle = bot:getLoc():angleTo(ship:getLoc())
 +
  logprint("angle to spawned ship = "..angle)
 +
end
 +
</source>
 +
 +
 +
Example:
 +
<source lang="lua">
 +
-- Keep track of all players/robots in the game.  This may be more efficient than
 +
-- reading the list every time it is needed if it is used frequently.
 +
 +
function main()
 +
  subscribe(PlayerJoinedEvent)
 +
  subscribe(PlayerLeftEvent)
 +
  players = GameInfo():getPlayers()    -- Vars defined in main() will be global
 +
end
 +
 +
 +
function onPlayerJoined()
 +
  players = GameInfo():getPlayers()    -- Player joined: get updated player list
 +
end
 +
 +
 +
function onPlayerLeft()
 +
  players = GameInfo():getPlayers()    -- Player left: get updated player list
 +
end
 +
 +
 +
</source>
 +
 +
 +
You can also use timers and events to create a basic sleep function:
 +
<source lang="lua">
 +
function sleep(time)                                  -- Sleep time in ms
 +
  unsubscribe(TickEvent)                            -- Stop getting tick events until...
 +
  Timer:scheduleOnce("subscribe(TickEvent)", time)  -- ...we resubscribe!
 +
end
 +
</source>
  
 
== Navigation, configuration, and combat ==
 
== Navigation, configuration, and combat ==
  
<b>getZoneCenterXY( x, y )</b> - Return point representing center of zone containing point x,y<br>
+
There are two general methods for getting the current game time:<br>  
<b>getGatewayFromZoneToZone( a, b )</b> - Return point representing fastest way from zone a to zone b.  If zones a & b are not neighbors, returns nil<br>
+
{{funcdoc|number|getTime|}} -Returns time for this frame, in ms.<br>
number <b>getZoneCount()</b> - Return number of zones<br>
+
{{funcdoc|number|getCPUTime|}} - Returns number representing the CPU time in ms.  Can be used to time events<br>
number <b>getCurrentZone()</b> - Return current zone that robot is in<br>
+
 +
 
 +
These functions will return slightly different results.  Generally speaking, if you are timing things in the "real world", i.e. you want to pause for exactly 1 second, use getCPUTime().  If you want to use a timer to make the robot move smoothly around a mathematically derived course, use getTime(), as that is what is used internally when calculating ship movement.
 +
 
 +
Example:
 +
<source lang="lua">
 +
lastTime = thisTime
 +
thisTime = bot:getCPUTime()
 +
local delta = thisTime - lastTime
 +
logprint(delta .. " ms have elapsed in the real world since last frame.")
 +
</source>
 +
 
 +
Example:
 +
<source lang="lua">
 +
-- Will fly robot in a circular orbit around an item.
 +
-- Assume we've already found one and stored it in "item"
 +
 
 +
local botLoc = bot:getLoc()
 +
local itemLoc = item:getLoc()
 +
 
 +
local dist = botLoc:distanceTo(itemLoc)
 +
 
 +
-- For this example, we'll define orbitRadius as a local variable.  In a
 +
-- real robot, this might be better declared a global variable in the header.
 +
local orbitRadius = 300   
 +
 
 +
-- Here we use the getTime() function to make the motion look smooth.
 +
-- If we just advanced orbitAng by a fixed amount each frame, it would
 +
-- appear jerky, as each frame represents a slightly different length in time.
 +
 
 +
-- .0015 determined experimentally
 +
 
 +
orbitAng = orbitAng + .0015 * bot:getTime()  -- orbitAng is a global variable
 +
 
 +
local dest = nil
 +
if(dist <= orbitRadius * 1.1) then
 +
    dest = itemLoc
 +
    dest:setxy(itemLoc:x() + orbitRadius * math.cos(orbitAng),
 +
              itemLoc:y() + orbitRadius * math.sin(orbitAng) )
 +
else
 +
    dest = itemLoc
 +
end
 +
 
 +
bot:setThrustToPt(dest)        -- Travel towards calculated point
 +
</source>
 +
 
 +
 
 +
{{funcdoc|Point|getZoneCenter|x, y}} - Return point representing center of zone containing point x,y<br>
 +
{{funcdoc|Point|getGatewayFromZoneToZone|a, b}} - Return point representing fastest way from zone a to zone b.  If zones a & b are not neighbors, returns nil<br>
 +
{{funcdoc|number|getZoneCount|}} - Return number of zones<br>
 +
{{funcdoc|number|getCurrentZone|}} - Return current zone that robot is in<br>
 
<br>
 
<br>
number <b>getAngle()</b> - Return angle robot is currently facing<br>
+
{{funcdoc|number|getAngle|}} - Return angle robot is currently facing<br>
Point <b>getPosXY()</b> - Return x, y of robot<br>
+
{{funcdoc|Point|getLoc|}} - Return point representing location of robot<br>
<br>
+
{{funcdoc|number|getTeamIndx|}} - Return index of robot's team<br>
void <b>setAngle( ang )</b> - Point robot at angle<br>
+
{{funcdoc|void|setAngle|ang}} - Point robot at angle<br>
void <b>setAngleXY( x, y )</b> - Point robot towards point x,y<br>
+
{{funcdoc|void|setAngle|pt}} - Point robot toward point pt<br>
number <b>getAngleXY( x, y )</b> - Compute angle to point x, y<br>
+
 
boolean <b>hasLosXY( x, y )</b> - Return whether or not robot can see point x, y<br>
+
Example:
 +
<source lang="lua">
 +
 
 +
-- This code in onTick()
 +
 
 +
    local items = bot:findItems(AsteroidType)  -- Get list of all asteroids
 +
    local asteroid = findClosest(items)    -- Finds closest
 +
 
 +
    if(asteroid == nil) then                -- Make sure we found one
 +
        return
 +
    end
 +
 
 +
    local loc = bot:getLoc()                -- Our location (Point object)
 +
    local ang = loc:angleTo( asteroid:getLoc() )
 +
    bot:setAngle(ang)                      -- Aim ship toward asteroid
 +
    bot:fire()
 +
</source>
 +
 
 +
 
 +
{{funcdoc|void|setAnglePt|Point}} - Point robot towards Point<br>
 +
{{funcdoc|number|getAnglePt|Point}} - Compute angle to Point<br>
 +
{{funcdoc|boolean|hasLosPt|Point}} - Return whether or not robot can see Point<br>
 +
{{funcdoc|boolean|hasLosPt|x, y}} - Return whether or not robot can see point x,y<br>
 +
{{funcdoc|number|getFiringSolution|item}} - Calculate angle at which robot needs to fire to hit moving or stationary object.  Will return nil if the item is out of range, behind an obstacle, or firing will hit a friendly ship.  Always check the return value for nil!<br>
 +
 
 +
Example:
 +
<source lang="lua">
 +
 
 +
    local loc = bot:getLoc()                                -- Current location
 +
    local items = bot:findGlobalItems(SoccerBallItemType)  -- Find soccerball
 +
 
 +
 
 +
    -- Loop through all returned items and find the closest
 +
    -- This code here for learning purposes... could be replaced with
 +
    -- much simpler single line:
 +
    -- local ball = findClosest(items)
 +
 
 +
    local minDist = 99999999
 +
    local ball = nil
 +
   
 +
    for sb in values(items) do
 +
        local l = sb:getLoc()          -- Ball's location
 +
       
 +
        -- Distance from ship to ball (use distSquared because
 +
        -- it is easy to compute and good for comparison)
 +
        local d = loc:distSquared(l)   
 +
               
 +
        if(d < minDist) then
 +
          ball = sb
 +
          minDist = d
 +
        end
 +
    end
 +
    -- End replaceable code section
 +
 
 +
    -- Check to make sure we found a soccer ball ... could be out of view
 +
    if(ball == nil) then
 +
        return
 +
    end
 +
 
 +
   
 +
    local ang = bot:getFiringSolution(sb)  -- Get shoot angle to hit moving ball
 +
 
 +
    -- Always check for nil: could happen if ball were behind a wall or
 +
    -- friendly ship.  Running setAngle(nil) will do nothing.
 +
    if(ang == nil) then
 +
        return
 +
    end
 +
   
 +
    bot:setAngle(ang)    -- Ready... aim...
 +
    bot:fire()          --  ...fire!
 +
 
 +
</source>
 
<br>
 
<br>
<b>hasFlag()</b> - Return whether or not robot currently has the flag<br>
+
{{funcdoc|number|getTeamIndx|}} - Return index of team this robot is on<br>
 +
{{funcdoc|boolean|hasFlag|}} - Return whether or not robot currently has the flag<br>
 
<br>
 
<br>
 
<br>
 
<br>
WeaponType <b>getWeapon()</b> - Return currently selected weapon<br>
+
{{funcdoc|WeaponType|getActiveWeapon|}} - Return currently selected weapon<br>
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
weap = bot:getWeapon()           -- Get info about our currently active weapon
+
weap = bot:getActiveWeapon()   -- Get info about our currently active weapon
weapInfo = WeaponInfo( weap )    -- Get information about it
+
weapInfo = WeaponInfo(weap)    -- Get information about it  
bot:logprint( weapInfo:getName().." has a range of "..weapInfo:getRange() )
+
logprint(weapInfo:getName() .. " has a range of " .. weapInfo:getRange())
 
</source>
 
</source>
  
  
Modules are automatically disabled at the end of each game cycle.  Therefore, if you want to keep a module on for a sustained period of time, you must activate it each time getMove() is called.  To activate a module, use one of the activateModule() commands.
+
Modules are automatically disabled at the end of each game cycle.  Therefore, if you want to keep a module on for a sustained period of time, you must activate it each time onTick() is called.  To activate a module, use one of the activateModule() commands.
  
void <b>activateModule(ModuleType)</b> - Activate module, specified by ModuleType.  If specified module is not included in the current loadout, this command will have no effect.
+
{{funcdoc|void|activateModule|ModuleType}} - Activate module, specified by ModuleType.  If specified module is not included in the current loadout, this command will have no effect.
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
 
-- Note that this line will do nothing if we don't have shields
 
-- Note that this line will do nothing if we don't have shields
bot:activateModule( ModuleShield )    -- Shields up!
+
bot:activateModule(ModuleShield)    -- Shields up!
 
</source>
 
</source>
  
void <b>activateModuleIndex(indx)</b> - Activate module, specified by its index (1 or 2)<br>
+
{{funcdoc|void|activateModuleIndex|indx}} - Activate module, specified by its index (1 or 2)<br>
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
 
-- Must specify an index, currently either 1 or 2
 
-- Must specify an index, currently either 1 or 2
bot:activateModuleIndex( 1 )          -- Activate first module, whatever it is
+
bot:activateModuleIndex(1)          -- Activate first module, whatever it is
 
</source>
 
</source>
  
void <b>setReqLoadout(Loadout)</b> - Set the requested loadout to Loadout<br>
+
{{funcdoc|void|setReqLoadout|Loadout}} - Set the requested loadout to Loadout<br>
Loadout <b>getCurrLoadout()</b> - Returns current loadout<br>
+
{{funcdoc|Loadout|getCurrLoadout|}} - Returns current loadout<br>
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
Line 73: Line 269:
  
 
-- Check to see if the first weapon is a phaser
 
-- Check to see if the first weapon is a phaser
if ( weapType1 == WeaponPhaser ) then
+
if (weapType1 == WeaponPhaser) then
bot:logprint("First weapon is a phaser!")
+
  logprint("First weapon is a phaser!")
 
end
 
end
  
 
-- Print a list of our three current weapons
 
-- Print a list of our three current weapons
bot:logprint( "My three weapons are: "..WeaponInfo(weapType1):getName()..", "
+
logprint("My three weapons are: " .. WeaponInfo(weapType1):getName() .. ", "
                                      ..WeaponInfo(weapType2):getName()..", and "
+
                                  .. WeaponInfo(weapType2):getName() .. ", and "
                                      ..WeaponInfo(weapType3):getName() )
+
                                  .. WeaponInfo(weapType3):getName())
 
</source>
 
</source>
  
  
Loadout <b>getReqLoadout()</b> - Returns requested loadout<br>
+
{{funcdoc|Loadout|getReqLoadout|}} - Returns requested loadout<br>
  
  
Line 90: Line 286:
 
<u>Navigation</u><br>
 
<u>Navigation</u><br>
 
<br>
 
<br>
<b>findObjects(ObjectType)</b><br>
+
{{funcdoc|[Item list]|findItems|ObjectType,  ...}} - Return a list of items of the specified type or types that are within the robot's viewable area<br>
<b>getWaypoint(x, y)</b><br>
+
{{funcdoc|[Item list]|findGlobalItems|ObjectType,  ...}} - Return a list of items of the specified type or types that are anywhere in the game.  Note that items that would ordinarily be out of scope will <i>not</i> be included in this list!<br>
 +
 
 +
Example:
 +
 
 +
<source lang="lua">
 +
-- Find all resource items in the normal viewport
 +
local items = bot:findItems(ResourceItemType)                 
 +
 
 +
-- You can specify multiple item types if you want
 +
-- Get a list of all TestItems or Asteroids that are visible anywhere
 +
-- (note that items that are out-of-scope will be omitted)
 +
local globItems = bot:findGlobalItems(TestItemType, AsteroidType)
 +
 
 +
-- Now iterate over the list ... there are many examples in the documentation!
 +
for item in values(globItems) do
 +
    -- Do something with item ... perhaps figure out which is closest,
 +
    -- which is heading towards us, etc.
 +
end
 +
</source>
 +
 
 +
 
 +
{{funcdoc|Point|getWaypoint|Point}}<br>
 +
{{funcdoc|Point|getWaypoint|x, y}}<br>
 
<br>
 
<br>
 
<br>
 
<br>
 
<u>Ship control</u><br>
 
<u>Ship control</u><br>
void <b>setThrustAng( vel, angle )</b> - Set robot's velocity to vel (0-1), at angle<br>
+
{{funcdoc|void|setThrust|vel, angle}} - Set robot's velocity to vel (0-1), at angle<br>
void <b>setThrustXY( vel, x, y )</b>  - Set robot's velocity to vel (0-1), toward coordinates x, y<br>
+
{{funcdoc|void|setThrustPt|vel, Point}} - Set robot's velocity to vel (0-1), toward Point. <br>
 
<br>
 
<br>
void <b>fire()</b> - Fires active weapon<br>
+
{{funcdoc|void|setThrustToPt|Point}} - Thrust toward Point, but adjust speed in an attempt to land right on point, and not overshoot.  If Point is distant, setThrustToPt() will cause robot to go at full speed.<br>
void <b>setWeaponIndex( index )</b> - Activates weapon with index (1, 2, 3)<br>
+
<br>
void <b>setWeapon( WeaponType )</b> - Activates weapon type specified (does nothing if WeaponType is not in current loadout)<br>
+
{{funcdoc|void|fire|}} - Fires active weapon<br>
boolean <b>hasWeapon (WeaponType )</b> - Does current configuation have specified weapon<br>
+
{{funcdoc|void|setWeaponIndex|index}} - Activates weapon with index (1, 2, 3)<br>
 +
{{funcdoc|void|setWeapon|WeaponType}} - Activates weapon type specified (does nothing if WeaponType is not in current loadout)<br>
 +
{{funcdoc|boolean|hasWeapon |WeaponType}} - Does current configuration have specified weapon<br>
  
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
if ( bot:hasWeapon( WeaponMine ) ) then
+
if(bot:hasWeapon(WeaponMine)) then
  bot:setWeapon( WeaponMine )      -- Make mine layer active, if it's  
+
    bot:setWeapon(WeaponMine)      -- Make mine layer active if it's  
                                    --   part of our current loadout
+
                                  --     part of our current loadout
  bot:fire()                       -- Lay a mine
+
    bot:fire()                     -- Lay a mine
 
end
 
end
 
</source>
 
</source>
  
void <b>globalMsg( msg )</b> - Send a message to all players<br>
+
==Logging & Sending Messages==
void <b>teamMsg( msg )</b> - Send a message to players on the same team<br>
+
{{funcdoc|void|globalMsg|msg}} - Send a message to all players<br>
 +
{{funcdoc|void|teamMsg|msg}} - Send a message to players on the same team<br>
 
<br>
 
<br>
<b>logprint(msg)</b> - Print msg to game logfile<br>
+
{{funcdoc|void|logprint|msg}} - Print msg to game logfile and to game console<br>
 +
 
 +
When sending a team or global message, some basic variable substitutions are available.  %playerName% will be replaced with the receiving player's name.  %controlName% will be replaced with the appropriate key binding on the receiving player's machine.  A full list of controlNames can be found in the KeyBinding section of the bitfighter.ini file.  Examples include %SelWeapon1% and %ShowScoreboard%.  While this capability is also available to player manually typing chat messages, it is mostly intended for tutorial bots to help the player along.  Variable names are case insensitive.
  
 
== GameItems ==
 
== GameItems ==
  
The Lua object structure follows that used by Bitfighter.  The GameItems group conisists of RepairItems, Asteroids, ResourceItems, SoccerBallItems, Flags, NexusFlags, and TestItems.  These all share similar properties, and have similar methods.  All of these implement the getLoc, getVel, and getRad methods for querying location, velocity, and radius respectively.  Some items have additional methods that apply only to them.  See below for details on these additional methods.
+
The Lua object structure generally follows that used by Bitfighter.  The GameItems group conisists of Ships, Robots, RepairItems, Asteroids, ResourceItems, SoccerBallItems, Flags, NexusFlags, and TestItems.  These all share similar properties, and have similar methods.  All of these implement the getLoc(), getVel(), and getRad() methods for querying location, velocity, and radius respectively.  Some items have additional methods that apply only to them.  See below for details on these additional methods.
  
  
=== TestItems ===  
+
=== Ships & Robots ===
Category: GameItem<br>
+
<br>
+
Point <b>getLoc()</b> - Center of testItem<br>
+
number <b>getRad()</b> - Radius of testItem<br>
+
Point <b>getVel()</b> - Speed of testItem<br>
+
  
Example:
+
Unless otherwise noted, all of these methods will work both on a ShipItem object as well as the local bot object.
<source lang="lua">ti = bot:findTestItem() -- Function likely to change
+
vel = it:getVel()</source>
+
  
 +
{{funcdoc|boolean|isAlive|}} - Returns true if ship/bot is still alive, false otherwise.
  
=== Asteroids ===
+
Rather than checking isAlive() every tick, it may be more efficient to keep track of dying ships by subscribing to ShipKilledEvent.
 +
 
 +
Example:
 +
<source lang="lua">
 +
-- Assuming we've subscribed to ShipKilledEvent, and that target is a global
 +
function onShipKilled(ship)
 +
  if target == ship then
 +
      target = nil  -- check for target == nil elsewhere and reacquire a target
 +
  end
 +
end
 +
</source >
 +
 
 +
 
 +
Note: All of the following functions will return nil if they are called on a dead ship.  When in doubt, check for nil!
 +
 
 +
{{funcdoc|Point|getLoc|}} -  Return center of item<br>
 +
{{funcdoc|number|getRad|}} -  Return radius of item<br>
 +
{{funcdoc|Point|getVel|}} -  Return speed of item<br>
 +
{{funcdoc|number|getAngle|}} - Returns angle ship is currently facing<br>
 +
{{funcdoc|number|getTeamIndx|}} - Index of the team this ship/robot is on<br>
 +
 
 +
{{funcdoc|PlayerInfo|getPlayerInfo|}} - Return info about player or robot controlling the ship<br>
 +
 
 +
The following two methods return a number between 0 and 1 where 1 means full health or energy, 0 means no health or energy.
 +
 
 +
{{funcdoc|number|getHealth|}} - Return health of the ship or robot<br>
 +
{{funcdoc|number|getEnergy|}} - Return energy of ship or robot<br>
 +
 
 +
{{funcdoc|boolean|isModuleActive|ModuleType}} - Returns true if specified module is active<br>
 +
{{funcdoc|boolean|hasFlag|}} - Returns true if ship is carrying a flag, false otherwise<br>
 +
{{funcdoc|number|getFlagCount|}} - Returns number of flags a ship is carrying.  Currently, ships can only have more than one flag in Nexus games<br>
 +
{{funcdoc|void|dropItem|}} - Drops any items the bot may be holding<br>
 +
 
 +
 
 +
{{funcdoc|WeaponType|getActiveWeapon|}} - Returns the active weapon constant (see WeaponType constants)<br>
 +
 
 +
=== CoreItems, ResourceItems, SoccerBallItems, and TestItems ===
 +
These items all have the same methods and behave in a very similar manner.
 
Category: GameItem<br>
 
Category: GameItem<br>
 
<br>
 
<br>
Point <b>getLoc()</b> - Center of asteroid<br>
+
{{funcdoc|Point|getLoc|}} - Return center of item<br>
number <b>getRad()</b> - Radius of asteroid<br>
+
{{funcdoc|number|getRad|}} - Return radius of item<br>
Point <b>getVel()</b> - Speed of asteroid<br>
+
{{funcdoc|Point|getVel|}} - Return velocity of item<br>
number <b>getSize()</b> - Index of current asteroid size (0 = initial size, 1 = next smaller, 2 = ...)<br>
+
{{funcdoc|number|getTeamIndx|}} - Returns index of items's team (will always be NeutralTeamIndx)<br>
number <b>getSizeCount()</b> - Number of indexes of size we can have<br>
+
 
 +
CoreItems have one additional method:<br>
 +
{{funcdoc|number|getHealth|}} - Return item's health<br>
  
 
Example:  
 
Example:  
<source lang="lua">asteroid = bot:findAsteroid() -- Function likely to change
+
<source lang="lua">
target = asteroid:getLoc()</source>
+
local items = bot:findItems(TestItemType) -- Find all TestItems in view
  
 +
-- Now cycle through all returned items and find the fastest one
 +
local maxSpeed = -1
 +
local fastest = nil
  
 +
for indx, ti in ipairs(items) do
 +
    -- getVel() returns a point representing the x and y components of
 +
    -- the velocity.  Need to use len() to get actual speed.  Or, in this
 +
    -- case, since actual speed isn't important, we can use lenSquared()
 +
    -- which is less computationally intensive, and will yield perfectly
 +
    -- good results for comparison purposes (such as finding the fastest).
 +
    spd = ti:getVel():lenSquared() 
  
=== ResourceItems ===
+
    if(spd > maxSpeed) then
 +
        fastest = ti
 +
        maxSpeed = spd
 +
    end
 +
end
 +
 
 +
-- Better check to make sure fastest isn't nil before using it!
 +
-- Could be nil if there are no TestItems in view
 +
 
 +
if(fastest != nil) then
 +
  -- Do something
 +
end
 +
 
 +
</source>
 +
 
 +
=== Asteroids ===
 
Category: GameItem<br>
 
Category: GameItem<br>
 
<br>
 
<br>
Point <b>getLoc()</b> - Center of ResourceItem<br>
+
{{funcdoc|Point|getLoc|}} - Return center of asteroid<br>
number <b>getRad()</b> - Radius of ResourceItem<br>
+
{{funcdoc|number|getRad|}} - Return radius of asteroid<br>
Point <b>getVel()</b> - Speed of ResourceItem<br>
+
{{funcdoc|Point|getVel|}} - Return velocity of asteroid<br>
 
+
{{funcdoc|number|getTeamIndx|}} - Returns asteroid's team index (will always be NeutralTeamIndx)<br>
Example:
+
{{funcdoc|number|getSize|}} - Index of current asteroid size (0 = initial size, 1 = next smaller, 2 =  ...)<br>
<source lang="lua">ri = bot:ResourceItem() -- Function likely to change
+
{{funcdoc|number|getSizeCount|}} - Number of indexes of size we can have<br>
vel = ri:getVel()</source>
+
  
 +
Example:
 +
<source lang="lua">
 +
local asteroids = bot:findItems(AsteroidType)
 +
local target = asteroids[1]:getLoc()  -- Pick first asteroid
 +
</source>
  
 
=== RepairItems ===
 
=== RepairItems ===
 
Category: GameItem<br>
 
Category: GameItem<br>
 
<br>
 
<br>
Point <b>getLoc()</b> - Center of RepairItem<br>
+
{{funcdoc|Point|getLoc|}} - Return center of repairItem<br>
number <b>getRad()</b> - Radius of RepairItem<br>
+
{{funcdoc|number|getRad|}} - Return radius of repairItem<br>
Point <b>getVel()</b> - Speed of RepairItem (usually 0,0)<br>
+
{{funcdoc|Point|getVel|}} - Return velocity of repairItem (usually 0,0)<br>
boolean <b>isVis()</b> - Is repair item currently visible?<br>
+
{{funcdoc|number|getTeamIndx|}} - Returns repairItems's team index (will always be NeutralTeamIndx)<br>
 +
{{funcdoc|boolean|isVis|}} - Is repairItem currently visible?<br>
  
 
Example:
 
Example:
<source lang="lua">ri = bot:findRepairItem() -- Function likely to change
+
<source lang="lua">
vel = ri:getVel()</source>
+
local ri = bot:findItems(RepairItemType)   -- Get list of all known RepairItems 
 +
local pos = ri[1]:getLoc()                 -- Get position of first
 +
</source>
  
=== SoccerBallItems ===
+
=== Flags ===
 
Category: GameItem<br>
 
Category: GameItem<br>
<i>Note: Only appears in Soccer games.</i>
 
 
<br>
 
<br>
Point <b>getLoc()</b> - Center of SoccerBallItem<br>
+
{{funcdoc|Point|getLoc|}} - Return location of flag<br>
number <b>getRad()</b> - Radius of SoccerBallItem<br>
+
{{funcdoc|number|getRad|}} - Return radius of flag<br>
Point <b>getVel()</b> - Speed of SoccerBallItemusually 0,0)<br>
+
{{funcdoc|Point|getVel|}} - Return velocity of flag<br>
 +
{{funcdoc|number|getTeamIndx|}} - Get index of owning team (-1 for neutral flag)<br>
 +
{{funcdoc|boolean|isInInitLoc|}} - Is flag in it's initial location?<br>
 +
{{funcdoc|boolean|inCaptureZone|}} - Is flag in a team's capture zone?<br>
 +
{{funcdoc|boolean|isOnShip|}} - Is flag being carried by a ship?<br>
  
=== Flags ===
+
=== Turrets and ForceFieldProjectors ===
 
Category: GameItem<br>
 
Category: GameItem<br>
<br>
 
Point <b>getLoc()</b> - Center of Flag<br>
 
number <b>getRad()</b> - Radius of Flag<br>
 
Point <b>getVel()</b> - Speed of Flag (usually 0,0)<br>
 
number <b>getTeamIndx()</b> - Get index of owning team (-1 for neutral flag)<br>
 
boolean <b>inInitLoc()</b> - Is flag in it's initial location?<br>
 
boolean <b>inCaptureZone()</b> - Is flag in a team's capture zone?<br>
 
boolean <b>isOnShip()</b> - Is flag being carried by a ship?<br>
 
  
=== NexusFlags ===
+
{{funcdoc|Point|getLoc|}} - Return location of NexusFlag<br>
 +
{{funcdoc|number|getRad|}} - Return radius of NexusFlag<br>
 +
{{funcdoc|Point|getVel|}} - Return velocity of NexusFlag<br>
 +
{{funcdoc|number|getTeamIndx|}} - Get index of owning team (see Special Team Constants)<br>
 +
{{funcdoc|number|getHealth|}} - Get health of item (1 = full heath, 0 = totally dead)<br>
 +
{{funcdoc|boolean|isActive|}} - True if item is active, false otherwise<br>
 +
 
 +
=== GoalZones and LoadoutZones ===
 +
{{funcdoc|Point|getLoc|}} - Get the centroid of the zone<br>
 +
{{funcdoc|number|getRad|}} - Returns 0<br>
 +
{{funcdoc|Point|getVel|}} - Get the velocity of the item (will usually be (0,0)!)<br>
 +
{{funcdoc|number|getTeamIndx|}} - Get index of owning team (see Special Team Constants)<br>
 +
 
 +
=== Nexus Related ===
 +
The following functions only have meaning in a Nexus game.
 +
 
 +
{{funcdoc|number|getNexusTimeLeft|}} - Return time left until next change of the nexus open/closed status (in ms)<br>
 +
{{funcdoc|boolean|isNexusOpen|}} - Returns true if nexus is open, false if closed<br>
 +
 
 +
Example:
 +
<source lang="lua">
 +
if isNexusOpen() then
 +
  local timeLeft = getNexusTimeLeft() / 1000
 +
  if timeLeft < 10 then
 +
      teamMsg("Hurry! Nexus closes in "  ..  timeLeft .. " seconds!")
 +
  end
 +
end
 +
</source>
 +
 
 +
=== Bullets, Mines, and SpyBugs ===
 
Category: GameItem<br>
 
Category: GameItem<br>
<i>Note: Only appears in Nexus games.</i>
+
There are three GameItem types that have to do with bullets, mines, and spybugs: BulletType, MineType, and SpyBugType respectively.  They all have the same methods, and can be used somewhat interchangeably, as shown in the example below. However, as shown, they can also be found independently.
<br>
+
<p>
Point <b>getLoc()</b> - Center of NexusFlag<br>
+
BulletType encompases Phaseser, Bounce, Triple, Burst, and Turret shots, the properties of which can be retrieved with the WeaponInfo object.
number <b>getRad()</b> - Radius of NexusFlag<br>
+
<br><br>
Point <b>getVel()</b> - Speed of NexusFlag<br>
+
 
 +
{{funcdoc|Point|getLoc|}} - Return location of item<br>
 +
{{funcdoc|number|getRad|}} - Return radius of item<br>
 +
{{funcdoc|Point|getVel|}} - Return velocity of item<br>
 +
{{funcdoc|number|getTeamIndx|}} - Get the team of the shooter/layer of the item<br>
 +
{{funcdoc|WeaponType|getWeapon|}} - Return WeaponType for this item<br>
 +
 
 +
Example:
 +
<source lang="lua">
 +
local bullets = bot:findItems(BulletType, MineType, SpyBugType)  
 +
 
 +
<some code>   -- Code here to select one of the found items
 +
 
 +
local weap = selectedBullet:getWeapon()
 +
local weapInfo = WeaponInfo(weap)
 +
logprint("Selected "  ..  weapInfo:getName())
 +
</source>
  
 
== Weapon Information ==
 
== Weapon Information ==
All the WeaponInfo data will remain constant throughout the game.  Therefore, if you need some information about a weapon, it might make sense to retrieve it in the bot's header and store it in a local variable rather than instantiating a new WeaponInfo object during every loop of the robot's getMove() method.
+
All WeaponInfo data will remain constant throughout the game.  Therefore, if you need some information about a weapon (mines, say, for a minesweeping bot), it might make sense to retrieve it in the bot's main() function, and store it in a global variable rather than instantiating a new WeaponInfo object during every iteration of the robot's onTick() method.
  
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
weap = bot:getWeapon()             -- Get bot's currently active weapon
+
local weap = bot:getActiveWeapon()     -- Get bot's currently active weapon
bot:logprint( weap:getName().." has a range of "..weap:getRange() )
+
logprint(weap:getName() .. " shoots with a speed of " .. weap:getProjVel())
 
</source>
 
</source>
  
 
<source lang="lua">
 
<source lang="lua">
weap = WeaponInfo( WeaponTurret ) -- Get info about those infernal turrets
+
-- In main()... we'd better not declare these variables as local!
bot:logprint( weap:getName().." shoots with a speed of "..weap:getProjVel() )
+
function main()
 +
  ...
 +
  turretInfo = WeaponInfo(WeaponTurret)   -- Get info about the infernal turrets
 +
  safeDist = turretInfo:getRange()       -- Be safe and stay this far away
 +
  ...
 +
end
 
</source>
 
</source>
  
  
  
string <b>getName()</b> - Name of weapon ("Phaser", "Triple", etc.)<br>
+
{{funcdoc|string|getName|}} - Name of weapon ("Phaser", "Triple", etc.)<br>
WeaponType <b>getID()</b> - ID of module (WeaponPhaser, WeaponTriple, etc.)<br>
+
{{funcdoc|WeaponType|getID|}} - ID of module (WeaponPhaser, WeaponTriple, etc.)<br>
number <b>getRange()</b> - Get range of weapon (units)<br>
+
{{funcdoc|number|getRange|}} - Get range of weapon (units)<br>
number <b>getFireDelay()</b> - Delay between shots in ms<br>
+
{{funcdoc|number|getFireDelay|}} - Delay between shots in ms<br>
number <b>getMinEnergy()</b> - Minimum energy needed to use<br>
+
{{funcdoc|number|getMinEnergy|}} - Minimum energy needed to use<br>
number <b>getEnergyDrain()</b> - Amount of energy weapon consumes<br>
+
{{funcdoc|number|getEnergyDrain|}} - Amount of energy weapon consumes<br>
number <b>getProjVel()</b> - Speed of projectile (units/sec)<br>
+
{{funcdoc|number|getProjVel|}} - Speed of projectile (units/sec)<br>
number <b>getProjLife()</b> - Time projectile will live (ms) -1 == live forever)<br>
+
{{funcdoc|number|getProjLife|}} - Time projectile will live (ms) -1 == live forever)<br>
number <b>getDamage()</b> - Damage projectile does (0-1, where 1 = total destruction)<br>
+
{{funcdoc|number|getDamage|}} - Damage projectile does (0-1, where 1 = total destruction)<br>
boolean <b>getCanDamageSelf()</b> - Will weapon damage self?<br>
+
{{funcdoc|number|getDamageSelf|}} - Damage if shooter shoots self (0-1, where 1 = total destruction)<br>
boolean <b>getCanDamageTeammate()</b> - Will weapon damage teammates?<br>
+
{{funcdoc|boolean|getCanDamageTeammate|}} - Will weapon damage teammates?<br>
  
 
== WeaponType constants ==
 
== WeaponType constants ==
 +
Commands that return a WeaponType will return one of the following values:
 
<table width=100%>
 
<table width=100%>
<tr><td width=50%><b>WeaponPhaser</b></td><td><b>WeaponBounce</b></td></tr>
+
<tr><td width=50%><b>Weapon.Phaser</b></td><td><b>Weapon.Bounce</b></td></tr>
<tr><td><b>WeaponTriple</b></td><td><b>WeaponBurst</b></td></tr>
+
<tr><td><b>Weapon.Triple</b></td><td><b>Weapon.Burst</b></td></tr>
<tr><td><b>WeaponMine</b></td><td><b>WeaponSpyBug</b></td></tr>
+
<tr><td><b>Weapon.Mine</b></td><td><b>Weapon.SpyBug</b></td></tr>
<tr><td><b>WeaponTurret</b></td><td></td></tr>
+
<tr><td><b>Weapon.Turret</b></td><td></td></tr>
 
</table>
 
</table>
  
 
== Module Information ==
 
== Module Information ==
 +
All ModuleInfo data will remain constant throughout the game. Therefore, if you need some information about a module (for example, the energy consmption of shields), it might make sense to retrieve it in the bot's main() function, and store it in a global variable rather than instantiating a new ModuleInfo object during every iteration of the robot's onTick() method.
 +
 
Example:
 
Example:
<source lang="lua">mod = ModuleInfo( ModuleBoost )
 
bot:logprint( "This is a lame example!" )</source>
 
  
string <b>getName()</b> - Name of module ("Shield", "Turbo", etc.)<br>
+
<source lang="lua">
ModuleType <b>getID()</b> - ID of module (ModuleShield, ModuleBoost, etc.)<br>
+
local mod = ModuleInfo(ModuleBoost)
 +
logprint("This is a lame example!")</source>
 +
 
 +
{{funcdoc|string|getName|}} - Name of module ("Shield", "Turbo", etc.)<br>
 +
{{funcdoc|ModuleType|getID|}} - ID of module (ModuleShield, ModuleBoost, etc.)<br>
  
 
== ModuleType constants ==
 
== ModuleType constants ==
 +
Functions that return a ModuleType will return one of the following values:
 
<table width = 100%>
 
<table width = 100%>
 
<tr><td width=50%><b>ModuleShield</b></td><td><b>ModuleBoost</b></td></tr>
 
<tr><td width=50%><b>ModuleShield</b></td><td><b>ModuleBoost</b></td></tr>
 
<tr><td><b>ModuleSensor</b></td><td><b>ModuleRepair</b></td></tr>
 
<tr><td><b>ModuleSensor</b></td><td><b>ModuleRepair</b></td></tr>
<tr><td><b>ModuleCloak</b></td><td><b>ModuleEngineer</b> (maybe someday)</td></tr>
+
<tr><td><b>ModuleCloak</b></td><td><b>ModuleEngineer</b></td></tr>
 +
<tr><td><b>ModuleArmor</b></td></tr>
 
</table>
 
</table>
  
 
== Loadouts ==
 
== Loadouts ==
void <b>setWeapon(index, WeaponType)</b> - Set weapon at index<br>
+
{{funcdoc|void|setWeapon|index, WeaponType}} - Set weapon at index<br>
void <b>setModule(index, ModuleType)</b> - Set module at index<br>
+
{{funcdoc|void|setModule|index, ModuleType}} - Set module at index<br>
WeaponType <b>getWeapon(index)</b> - return weapon at index<br>
+
{{funcdoc|WeaponType|getWeapon|index}} - Return weapon at index<br>
ModuleType <b>getModule(index)</b> - return module at index<br>
+
{{funcdoc|ModuleType|getModule|index}} - Return module at index<br>
  
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
 
-- Get a new loadout (comes pre-filled with default values)
 
-- Get a new loadout (comes pre-filled with default values)
loadout = Loadout()
+
local loadout = Loadout()
  
 
-- Configure the loadout to suit our needs
 
-- Configure the loadout to suit our needs
loadout:setWeapon( 1, WeaponPhaser )
+
loadout:setWeapon(1, WeaponPhaser)
loadout:setWeapon( 2, WeaponBurst )
+
loadout:setWeapon(2, WeaponBurst)
loadout:setWeapon( 3, WeaponMine )
+
loadout:setWeapon(3, WeaponMine)
loadout:setModule( 1, ModuleShield )
+
loadout:setModule(1, ModuleShield)
loadout:setModule( 2, ModuleCloak )
+
loadout:setModule(2, ModuleCloak)
  
 
-- Set the loadout, will become active when bot hits loadout zone
 
-- Set the loadout, will become active when bot hits loadout zone
 
-- or spawns, depending on game
 
-- or spawns, depending on game
bot:setReqLoadout( loadout )     
+
bot:setReqLoadout(loadout)     
  
if( loadout:getWeapon(1) == WeaponPhaser) then
+
if(loadout:getWeapon(1) == WeaponPhaser) then
   bot:logprintf( "This line always gets printed!" )
+
   logprintf("This line always gets printed!")
 
end
 
end
 
</source>
 
</source>
Line 280: Line 620:
  
  
boolean <b>isValid()</b> - Is loadout config valid?<br>
+
{{funcdoc|boolean|isValid|}} - Is loadout config valid?<br>
boolean <b>equals(Loadout)</b> - is loadout the same as Loadout?<br>
+
{{funcdoc|boolean|equals|Loadout}} - is loadout the same as Loadout?<br>
  
 
== GameInfo ==
 
== GameInfo ==
Line 289: Line 629:
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
game = GameInfo()         -- Create the GameInfo object  
+
game = GameInfo()           -- Create the GameInfo object  
 
gameType = game:getGameType()
 
gameType = game:getGameType()
  
if( gameType == SoccerGame ) then
+
if(gameType == SoccerGame) then
   bot:logprint("This bot is not very good at soccer!")
+
   logprint("This bot is not very good at soccer!")
 
else
 
else
 
   gameTypeName = game:getGameTypeName()
 
   gameTypeName = game:getGameTypeName()
   bot:logprintf("I just love playing "..gameTypeName.."!")   
+
   logprintf("I just love playing " .. gameTypeName .. "!")   
 
end
 
end
 
</source>
 
</source>
Line 302: Line 642:
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
-- Create the GameInfo object in header:
+
-- Create the GameInfo object in main() (don't declare it local!!)
game = GameInfo()        
+
function main()
 
+
  ...
...
+
  game = GameInfo()   -- In main non-local variables become globals
 +
  ...
 +
end
 +
...
  
 
-- Even though we only create our GameInfo once, it is always current
 
-- Even though we only create our GameInfo once, it is always current
  
...
+
...
  
-- Later, in getMove():
+
-- Later, in onTick():
remTime = game:getGameTimeReamaining()
+
local remTime = game:getGameTimeReamaining()
totTime = game:getGameTimeTotal()
+
local totTime = game:getGameTimeTotal()
percent = ( totTime - remTime ) / remTime
+
local percent = (totTime - remTime) / remTime
bot:logprint( "Game is "..percent.."% over )
+
logprint("Game is " .. percent .. "% over)
 
</source>
 
</source>
  
GameType <b>getGameType()</b> - Return current game typet) <br>
+
{{funcdoc|GameType |getGameType|}} - Return current game type <br>
string <b>getGameTypeName()</b> - Return current game type <br>
+
{{funcdoc|string|getGameTypeName|}} - Return current game type <br>
number <b>getFlagCount()</b> - Return the number of flags in the game<br>
+
{{funcdoc|number|getFlagCount|}} - Return the number of flags in the game<br>
number <b>getWinningScore()</b> - Returns the score required to win the level<br>
+
{{funcdoc|number|getWinningScore|}} - Returns the score required to win the level<br>
number <b>getGameTimeTotal()</b> - Returns the time (in seconds) that the level will be played for<br>
+
{{funcdoc|number|getGameTimeTotal|}} - Returns the time (in seconds) that the level will be played for<br>
number <b>getGameTimeRemaining()</b> - Returns the time remaining (in seconds) for this level<br>
+
{{funcdoc|number|getGameTimeRemaining|}} - Returns the time remaining (in seconds) for this level<br>
number <b>getLeadingScore()</b> - Gets score of the leading team<br>
+
{{funcdoc|number|getLeadingScore|}} - Gets score of the leading team<br>
number <b>getLeadingTeam()</b> - Gets index of leading team<br>
+
{{funcdoc|number|getLeadingTeam|}} - Gets index of leading team<br>
number <b>getTeamCount()</b> - Return number of teams<br>
+
{{funcdoc|number|getTeamCount|}} - Return number of teams<br>
  
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
-- Create the GameInfo object in header:
+
-- Create the GameInfo object main() (don't declare it local!!)
game = GameInfo()        
+
function main()
 +
  ...
 +
  game = GameInfo()   -- In main non-local variables become globals
 +
  ...
 +
end
 +
 
 +
-- Then later, in onTick() or other function
 
...
 
...
-- Then later...
+
local leadingTeam = game:getLeadingTeam()
leadingTeam = game:getLeadingTeam()
+
local team = TeamInfo(leadingTeam)
team = TeamInfo( leadingTeam )
+
bot:teamMsg("Hurry up! Team " .. team:getName() .. " is winning!" )
bot:teamMsg( "Hurry up! Team "..team:getName().." is winning!" )
+
 
</source>
 
</source>
  
string <b>getLevelName()</b> - Gets the level's name<br>
+
{{funcdoc|PlayerInfo list|getPlayers|}} - Return a list of PlayerInfo objects representing all players/bots in the game<br>
number <b>getGridSize()</b> - Gets the level's gridSize parameter<br>
+
boolean <b>getIsTeamGame()</b> - Is this a team game?<br>
+
<br>
+
<b>getEventScore(ScoringEvent)</b><br>
+
  
 +
Example:
 +
<source lang="lua">
 +
players = GameInfo():getPlayers()
 +
for player in values(players) do
 +
  if player:isRobot() then
 +
      logprint(player:getName().." is a bot!")
 +
  else
 +
      logprint("Hello "..player:getName())
 +
  end
 +
end
 +
</source>
 +
 +
{{funcdoc|string|getLevelName|}} - Gets the level's name<br>
 +
{{funcdoc|number|getGridSize|}} - Gets the level's gridSize parameter<br>
 +
{{funcdoc|boolean|IsTeamGame|}} - Is this a team game?<br>
 +
<br>
 +
{{funcdoc|void|getEventScore|ScoringEvent}} - Gets score awarded for ScoringEvent<br>
  
 
== GameType constants ==
 
== GameType constants ==
 +
Functions that return a GameType will return one of the following values:
 
<table width=100%>
 
<table width=100%>
<tr><td width=50%><b>BitmatchGame</b></td><td><b>CTFGame</b></td></tr>
+
<tr><td width=50%><b>BitmatchGame</b></td><td><b>CoreGame</b></td></tr>
<tr><td><b>HTFGame</b></td><td><b>NexusGame</b></td></tr>
+
<tr><td><b>CTFGame</b></td><td><b>HTFGame</b></td></tr>
<tr><td><b>RabbitGame</b></td><td><b>RetrieveGame</b></td></tr>
+
<tr><td><b>NexusGame</b></td><td><b>RabbitGame</b></td></tr>
<tr><td><b>SoccerGame</b></td><td><b>ZoneControlGame</b></td></tr>
+
<tr><td><b>RetrieveGame</b></td><td><b>SoccerGame</b></td></tr>
 +
<tr><td><b>ZoneControlGame</b></td></tr>
 
</table>
 
</table>
  
 
== ScoringEvent constants ==
 
== ScoringEvent constants ==
 +
Functions that return a ScoringEvent will return one of the following values:
 
<table width=100%>
 
<table width=100%>
 
<tr><td width=50%><b>KillEnemy</b></td><td><b>KillSelf</b></td></tr>
 
<tr><td width=50%><b>KillEnemy</b></td><td><b>KillSelf</b></td></tr>
Line 369: Line 733:
  
 
== TeamInfo ==
 
== TeamInfo ==
string <b>getName()</b> - return team name<br>
+
{{funcdoc|string|getName|}} - return team name<br>
number <b>getIndex()</b> - return team's index, index of first team is 1<br>
+
{{funcdoc|number|getIndex|}} - return team's index, index of first team is 1<br>
number <b>getPlayerCount()</b> - return player count<br>
+
{{funcdoc|number|getPlayerCount|}} - return player count<br>
number <b>getScore()</b> - return team score<br>
+
{{funcdoc|number|getScore|}} - return team score<br>
 +
{{funcdoc|PlayerInfo list|getPlayers|}} - Return a list of PlayerInfo objects representing all players/bots on the team<br>
  
 
Example:
 
Example:
 
<source lang="lua">
 
<source lang="lua">
gameInfo = GameInfo()
+
local gameInfo = GameInfo()
teams = gameInfo:getTeamCount()
+
local teams = gameInfo:getTeamCount()
  
 
-- Note that while the number of teams will not change throughout the game,
 
-- Note that while the number of teams will not change throughout the game,
Line 385: Line 750:
  
 
for i = 1, teams do        -- First team has an index of 1
 
for i = 1, teams do        -- First team has an index of 1
   team = TeamInfo( i )
+
   team = TeamInfo(i)
   bot:logprint( "Team "..i.." is called "..team:getName()..
+
   logprint("Team " .. i .. " is called " .. team:getName() ..  
       " and has "..team:getPlayerCount().." players" )
+
       " and has " .. team:getPlayerCount() .. " players")
 
end
 
end
 
</source>
 
</source>
  
== Misc ==
+
=== Special team constants ===
 +
Two special team constants are available to make your programs simpler and easier to read.  In addition to the normal player teams, Bitfighter has two pseudo teams, "Neutral" and "Hostile".  These constants can be used to identify those teams:
 +
 
 +
<b>NeutralTeamIndx</b> - Neutral team<br>
 +
<b>HostileTeamIndx</b> - Hostile team<br>
 +
 
 +
Example:
 +
<source lang="lua">
 +
team = turret:getTeamIndx()
 +
if team == NeutralTeamIndx then        -- Neutral - can be repaired
 +
  mode = repair
 +
else if team == HostileTeamIndx then  -- Hostile - must be destroyed!
 +
  mode = attack
 +
end
 +
</source>
 +
 
 +
== PlayerInfo ==
 +
Provides information about a player or robot.  The PlayerInfo object will remain valid and will contain current information as long as the player is in the game.<br><br>
 +
string <b>getName()</b> - return player's name<br>
 +
Ship <b>getShip()</b> - return player's ship (note: can be nil)<br>
 +
string <b>getScriptName()</b> - for robots, returns name of script; for players, returns nil<br>
 +
number <b>getTeamIndx()</b> - return team's index, index of first team is 1<br>
 +
number <b>getRating()</b> - return player's rating<br>
 +
number <b>getScore()</b> - return player's score<br>
 +
boolean <b>isRobot()</b> - returns true if the PlayerInfo is a robot, false if it is a human<br>
 +
 
 +
== Timers ==
 +
Timer is a utility class that makes it easier to manage timing periodic or delayed events.  The timer code is based on a very nice library written by Haywood Slap
 +
 
 +
A timer object that can be used to schedule arbitrary events to be executed
 +
at some time in the future.  All times are given in milliseconds.
 +
 
 +
=== Usage ===
 +
 
 +
There are four basic timer functions:
 +
<source lang="lua">
 +
-- Execute the event once in 'delay' ms
 +
Timer:scheduleOnce(event, delay)
 +
 
 +
-- Execute the event repeatedly, every 'delay' ms
 +
Timer:scheduleRepeating(event, delay) 
 +
     
 +
-- Execute the event every 'delay' ms while event returns true
 +
Timer:scheduleRepeatWhileTrue(event, delay) 
 +
 
 +
-- Remove all pending events from the timer's queue
 +
Timer:clear()                               
 +
</source>
 +
 
 +
=== Timer Events ===
 +
 
 +
The 'event' used can either be the name a function, a table that contains
 +
a function named 'run', or a bit of arbitrary Lua code.
 +
 
 +
Any Lua function can be called by the Timer.
 +
 
 +
Examples:
 +
<source lang="lua">
 +
function onLevelStart()
 +
  -- Compiles and runs string in five seconds.
 +
  -- Note that the string here is in quotes.
 +
  Timer:scheduleOnce("logprint(\"Once!\")", 5000) 
 +
 
 +
  -- Runs the always function every 30 seconds
 +
  Timer:scheduleRepeating(always, 30000)           
 +
 
 +
  -- Runs "run" method of maybe every two seconds
 +
  -- until method returns false
 +
  Timer:scheduleRepeatWhileTrue(maybe, 2000)       
 +
end                                                 
 +
 
 +
-- Standard function
 +
function always()
 +
  logprint("Timer called always")
 +
end
 +
 
 +
-- Table: run method will be called
 +
maybe = {
 +
  count = 3
 +
  run = function(self)
 +
      logprint("Count down " .. self.count)
 +
      self.count = self.count - 1
 +
      return self.count > 0
 +
  end
 +
}
 +
</source>
 +
 
 +
When using a table as an 'event' the first parameter to the run
 +
function should always be a parameter named "self" (or "this"
 +
if you prefer).  The "self" parameter can be used to access the
 +
other fields in the table, that is, the "self" parameter will refer
 +
to the table that contains the run function.  If you do not need to
 +
refer to any of the other fields in the table then you do not need
 +
to include the self parameter.
 +
 
 +
== Points ==
 +
 
 +
Point is a utility class to make handling coordinates or vectors simpler.  You can create your own point objects using the constructor, or you can get them as return values from many functions.
 +
 
 +
{{funcdoc|Point|Point|x, y}} - Create a new Point object with coordinates x, y.  You do not need to use setxy() unless you want to change the coords later.<br>
 +
 
 +
{{funcdoc|number|x|}} - Return x value<br>
 +
{{funcdoc|number|y|}} - Return y value<br>
 +
 
 +
{{funcdoc|void|setxy|x, y}} - Update coordinates of point<br>
 +
{{funcdoc|void|setx|x}} - Update x coordinate of point<br>
 +
{{funcdoc|void|sety|y}} - Update y coordinate of point<br>
 +
{{funcdoc|void|setAngle|ang}} - Set angle of point to <i>ang</i>.  This will preserve the point's current length, and can can be used in conjunction with normalize to set a point via polar coordinates<br>
 +
{{funcdoc|void|setPolar|r, ang}} - Set point using polar coordinates, r = distance from origin, ang = angle<br>
 +
 
 +
{{funcdoc|number|len|}} - Returns the length of the point, useful if point represents a velocity or other vector<br>
 +
{{funcdoc|number|lenSquared|}} - Returns the length squared of the point, useful if point represents a velocity or other vector, and you want a less computationally expensive representation of length that will work for comparative purposes<br>
 +
{{funcdoc|boolean|equals|Point}} - Returns true if the point is the same as Point<br>
 +
{{funcdoc|void|normalize|length}} - If Point represents a vector, set its length to <i>length</i>
 +
{{funcdoc|number|distanceTo|Point}} - Returns distance from point to Point<br>
 +
{{funcdoc|number|distSquared|Point}} - Returns distance squared from point to Point (less computationally intensive, good for comparison)<br>
 +
{{funcdoc|number|angleTo|Point}} - Returns angle from point to Point<br>
 +
{{funcdoc|number|angleTo|x, y}} - Returns angle from point to point(x, y)<br>
 +
 
 +
Example:
 +
<source lang="lua">
 +
local point = Point(0, 0)      -- Create a new point
 +
 
 +
if point:equals(Point(1, 1)) then     
 +
  logprint("This never gets printed")     
 +
end
 +
 
 +
if point:distanceTo(Point(1, 1)) < 5 then     
 +
  logprint("This always gets printed")     
 +
end
 +
 
 +
-- Same thing, but with a little less overhead
 +
if point:distanceTo(1, 1) < 5 then     
 +
  logprint("So does this, but more efficiently")     
 +
end
 +
 
 +
 
 +
-- Here we'll use a point to represent a velocity, which has x & y components
 +
local xspeed = 10
 +
local yspeed = 15
 +
local velocity = Point(xspeed, yspeed)
 +
logprint("Speed = " .. velocity:len())
 +
</source>
 +
 
 +
== ItemTypes ==
 +
Functions that return an ItemType will return one of the following values:
  
Some constants available in game:<br><br>
 
<u>Game Objects</u><br>
 
 
<b>ShipType</b><br>
 
<b>ShipType</b><br>
<b>BarrierType</b><br>
 
<b>MoveableType</b><br>
 
 
<b>BulletType</b><br>
 
<b>BulletType</b><br>
<b>ItemType</b><br>
 
<b>ResourceItemType</b><br>
 
<b>EngineeredType</b><br>
 
<b>ForceFieldType</b><br>
 
<b>LoadoutZoneType</b><br>
 
 
<b>MineType</b><br>
 
<b>MineType</b><br>
<b>TestItemType</b><br>
 
<b>FlagType</b><br>
 
<b>NexusFlagType</b><br>
 
<b>TurretTargetType</b><br>
 
<b>SlipZoneType</b><br>
 
<b>HeatSeekerType</b><br>
 
 
<b>SpyBugType</b><br>
 
<b>SpyBugType</b><br>
<b>NexusType</b><br>
 
<b>BotNavMeshZoneType</b><br>
 
 
<b>RobotType</b><br>
 
<b>RobotType</b><br>
 +
<b>TeleportType</b> (not yet fully implemented)<br>
 +
<b>SpeedZoneType</b> (not yet fully implemented)<br>
 +
<b>AsteroidType</b><br>
 +
<b>CoreType</b><br>
 +
<b>TurretType</b><br>
 +
<b>ForceFieldProjectorType</b><br>
 +
<b>TestItemType</b><br>
 +
<b>FlagType</b><br>
 
<b>SoccerBallItemType</b><br>
 
<b>SoccerBallItemType</b><br>
<b>TeleportType</b><br>
+
<b>ResourceItemType</b><br>
 +
 
 +
== Things that are not really items ==
 +
<u>Polygonal-like</u><br>
 +
<b>BotNavMeshZoneType</b><br>
 +
<b>NexusType</b><br>
 
<b>GoalZoneType</b><br>
 
<b>GoalZoneType</b><br>
<b>AsteroidType</b><br>
+
<b>LoadoutZoneType</b><br>
 +
 
 +
<u>Wall-like</u><br>
 +
<b>BarrierType</b>
 +
<b>ForceFieldType</b><br>
 +
 
 +
== Helper functions ==
 +
{{funcdoc|item|findClosest|(list of items [, teamIndx]}} - Finds item closest to bot.  If teamIndx is specified, only considers items on team.  If list is empty (or there are no team items in the list), funtion returns nil.

Latest revision as of 23:23, 4 April 2013

TODO: FlagItem:getCaptureZone & FlagItem:getShip functions (verify w/ sam); make sure all items have getLoc, getRad, getVel, getTeamIndx, and isItem; hasFlag(); engineer-deploy-object (??), ship/bot:getMountedItems(); document libopt (or at least or inclusion of such with summary and pointer to docs) -- need to do require("getopt") to activate it. mention inspect.lua under debugging section.

Server admins can program their own robots in addition to designing their own levels. Robots are coded in Lua, and have a number of special functions available to them.

For examples of fully functional bots, see the Robot Gallery.

Robot coding is still fluid, and the information here is subject to change. You might want to read about the general structure of Bitfighter scripts before proceeding.


How to read the function notation:

returnType functionName(argType1, argType2)
- Description of function


Return types: Unlike most languages, Lua does not differentiate between integers and floating point numbers. Instead, everything is simply a number. In fact, Lua often will let you interchange strings and numbers, converting dynamically on an as-needed basis. However, relying on this automatic type coercion is bad practice and can get you into trouble.


Points to remember in Lua:

  • Lua arrays are 1-based: the first index is 1 rather than 0 as is the case in programming languages such as C++ and Java
  • Lua is case sensitive. That means that the variable foo is different than Foo and FOO
  • Any variable not declared as local will be global. Accessing global variables is slower than local ones, and being sloppy about variable scope can lead to unexpected problems. Use the local keyword whenever appropriate.

We will follow the Lua formatting conventions used in the Programming in Lua book.

Events

Lua robots are designed to respond to many in-game events using event handlers. To respond to an event your bot must have an appropriately named function (described below), and must subscribe to any events it is interested in. You can also unsubscribe to an event at any time to stop receiving event notifications.

Currently defined events

Event IDAssociated functionDescription
TickEventonTick(timeSinceLastTickInMs)Called every frame of the game. Unlike other events, robots are subscribed to this event by default.
ShipSpawnedEventonShipSpawned(ship)Called when any ship or robot spawns (not including your robot). Spawning ship is passed as the first argument.
ShipKilledEventonShipKilled(ship)Called when any ship or robot is killed (not including your robot). Dead ship is passed as the first argument.
MsgReceivedEventonMsgReceived(message, senderPlayerInfo, global)Called when a player or robot sends a message. Message itself is first argument, a PlayerInfo for the sender is the second argument, and the third argument is a boolean that will be true if the message was global, false if sent to teammates only. Note that senderPlayerInfo can be nil if message is sent by a Controller script.
PlayerJoinedEventonPlayerJoined(playerInfo)Called when a player or robot joins the game (not when they spawn). Note that when hosting a game locally, local player does not necessarily trigger this event due to the uncertain order of creation of game objects. First argument is playerInfo for the joining player.
PlayerLeftEventonPlayerLeft(playerInfo)Called when a player or robot leaves the game. Note that event may not be fired when robots or local player leaves game due to server shutting down. First argument is playerInfo for the leaving player.
NexusOpenedEventonNexusOpened()Called when Nexus opens in a Nexus game. Never fires in other game types.
NexusClosedEventonNexusClosed()Called when Nexus closes in a Nexus game. Never fires in other game types.

Example:

function main()
   ...
   subscribe(ShipSpawnedEvent)    -- Subscribe so onShipSpawned() will be called
   ...
end
 
-- Since we are subscribed to the ShipSpawned event, the onShipSpawned() function 
-- will be called whever a ship or robot spawns.
function onShipSpawned(ship)
   logprint("Ship spawned: ".. tostring(ship:getLoc()))
   angle = bot:getLoc():angleTo(ship:getLoc())
   logprint("angle to spawned ship = "..angle)
end


Example:

-- Keep track of all players/robots in the game.  This may be more efficient than
-- reading the list every time it is needed if it is used frequently.
 
function main()
   subscribe(PlayerJoinedEvent)
   subscribe(PlayerLeftEvent)
   players = GameInfo():getPlayers()    -- Vars defined in main() will be global
end
 
 
function onPlayerJoined()
   players = GameInfo():getPlayers()    -- Player joined: get updated player list
end
 
 
function onPlayerLeft()
   players = GameInfo():getPlayers()    -- Player left: get updated player list
end


You can also use timers and events to create a basic sleep function:

function sleep(time)                                  -- Sleep time in ms
   unsubscribe(TickEvent)                             -- Stop getting tick events until...
   Timer:scheduleOnce("subscribe(TickEvent)", time)   -- ...we resubscribe!
end

Navigation, configuration, and combat

There are two general methods for getting the current game time:

number getTime()
-Returns time for this frame, in ms.
number getCPUTime()
- Returns number representing the CPU time in ms. Can be used to time events


These functions will return slightly different results. Generally speaking, if you are timing things in the "real world", i.e. you want to pause for exactly 1 second, use getCPUTime(). If you want to use a timer to make the robot move smoothly around a mathematically derived course, use getTime(), as that is what is used internally when calculating ship movement.

Example:

lastTime = thisTime
thisTime = bot:getCPUTime()
local delta = thisTime - lastTime
logprint(delta .. " ms have elapsed in the real world since last frame.")

Example:

-- Will fly robot in a circular orbit around an item.
-- Assume we've already found one and stored it in "item"
 
local botLoc = bot:getLoc()
local itemLoc = item:getLoc()
 
local dist = botLoc:distanceTo(itemLoc)
 
-- For this example, we'll define orbitRadius as a local variable.  In a
-- real robot, this might be better declared a global variable in the header. 
local orbitRadius = 300    
 
-- Here we use the getTime() function to make the motion look smooth.
-- If we just advanced orbitAng by a fixed amount each frame, it would
-- appear jerky, as each frame represents a slightly different length in time.
 
-- .0015 determined experimentally
 
orbitAng = orbitAng + .0015 * bot:getTime()  -- orbitAng is a global variable
 
local dest = nil
if(dist <= orbitRadius * 1.1) then 
    dest = itemLoc
    dest:setxy(itemLoc:x() + orbitRadius * math.cos(orbitAng),
               itemLoc:y() + orbitRadius * math.sin(orbitAng) )
else
    dest = itemLoc
end
 
bot:setThrustToPt(dest)         -- Travel towards calculated point


Point getZoneCenter(x, y)
- Return point representing center of zone containing point x,y
Point getGatewayFromZoneToZone(a, b)
- Return point representing fastest way from zone a to zone b. If zones a & b are not neighbors, returns nil
number getZoneCount()
- Return number of zones
number getCurrentZone()
- Return current zone that robot is in


number getAngle()
- Return angle robot is currently facing
Point getLoc()
- Return point representing location of robot
number getTeamIndx()
- Return index of robot's team
void setAngle(ang)
- Point robot at angle
void setAngle(pt)
- Point robot toward point pt

Example:

-- This code in onTick()
 
    local items = bot:findItems(AsteroidType)  -- Get list of all asteroids
    local asteroid = findClosest(items)     -- Finds closest
 
    if(asteroid == nil) then                -- Make sure we found one
        return
    end
 
    local loc = bot:getLoc()                -- Our location (Point object)
    local ang = loc:angleTo( asteroid:getLoc() )
    bot:setAngle(ang)                       -- Aim ship toward asteroid 
    bot:fire()


void setAnglePt(Point)
- Point robot towards Point
number getAnglePt(Point)
- Compute angle to Point
boolean hasLosPt(Point)
- Return whether or not robot can see Point
boolean hasLosPt(x, y)
- Return whether or not robot can see point x,y
number getFiringSolution(item)
- Calculate angle at which robot needs to fire to hit moving or stationary object. Will return nil if the item is out of range, behind an obstacle, or firing will hit a friendly ship. Always check the return value for nil!

Example:

    local loc = bot:getLoc()                                -- Current location
    local items = bot:findGlobalItems(SoccerBallItemType)   -- Find soccerball
 
 
    -- Loop through all returned items and find the closest
    -- This code here for learning purposes... could be replaced with
    -- much simpler single line:
    -- local ball = findClosest(items)
 
    local minDist = 99999999
    local ball = nil
 
    for sb in values(items) do
        local l = sb:getLoc()           -- Ball's location
 
        -- Distance from ship to ball (use distSquared because
        -- it is easy to compute and good for comparison)
        local d = loc:distSquared(l)    
 
        if(d < minDist) then
           ball = sb
           minDist = d
        end
    end
    -- End replaceable code section
 
    -- Check to make sure we found a soccer ball ... could be out of view
    if(ball == nil) then
        return
    end
 
 
    local ang = bot:getFiringSolution(sb)   -- Get shoot angle to hit moving ball
 
    -- Always check for nil: could happen if ball were behind a wall or 
    -- friendly ship.  Running setAngle(nil) will do nothing.
    if(ang == nil) then
        return
    end
 
    bot:setAngle(ang)    -- Ready... aim...
    bot:fire()           --  ...fire!


number getTeamIndx()
- Return index of team this robot is on
boolean hasFlag()
- Return whether or not robot currently has the flag



WeaponType getActiveWeapon()
- Return currently selected weapon

Example:

weap = bot:getActiveWeapon()    -- Get info about our currently active weapon
weapInfo = WeaponInfo(weap)     -- Get information about it   
logprint(weapInfo:getName() .. " has a range of " .. weapInfo:getRange())


Modules are automatically disabled at the end of each game cycle. Therefore, if you want to keep a module on for a sustained period of time, you must activate it each time onTick() is called. To activate a module, use one of the activateModule() commands.

void activateModule(ModuleType)
- Activate module, specified by ModuleType. If specified module is not included in the current loadout, this command will have no effect.

Example:

-- Note that this line will do nothing if we don't have shields
bot:activateModule(ModuleShield)    -- Shields up!
void activateModuleIndex(indx)
- Activate module, specified by its index (1 or 2)

Example:

-- Must specify an index, currently either 1 or 2
bot:activateModuleIndex(1)          -- Activate first module, whatever it is
void setReqLoadout(Loadout)
- Set the requested loadout to Loadout
Loadout getCurrLoadout()
- Returns current loadout

Example:

loadout = bot:getCurrLoadout()       -- Retrieve current bot configuration
 
weapType1 = loadout:getWeapon(1)     -- Get the first weapon
weapType2 = loadout:getWeapon(2)     -- Get the second weapon
weapType3 = loadout:getWeapon(3)     -- Get the third weapon
 
-- Check to see if the first weapon is a phaser
if (weapType1 == WeaponPhaser) then
   logprint("First weapon is a phaser!")
end
 
-- Print a list of our three current weapons
logprint("My three weapons are: " .. WeaponInfo(weapType1):getName() .. ", "
                                  .. WeaponInfo(weapType2):getName() .. ", and "
                                  .. WeaponInfo(weapType3):getName())


Loadout getReqLoadout()
- Returns requested loadout



Navigation

[Item list] findItems(ObjectType, ...)
- Return a list of items of the specified type or types that are within the robot's viewable area
[Item list] findGlobalItems(ObjectType, ...)
- Return a list of items of the specified type or types that are anywhere in the game. Note that items that would ordinarily be out of scope will not be included in this list!

Example:

-- Find all resource items in the normal viewport
local items = bot:findItems(ResourceItemType)                   
 
-- You can specify multiple item types if you want
-- Get a list of all TestItems or Asteroids that are visible anywhere
-- (note that items that are out-of-scope will be omitted)
local globItems = bot:findGlobalItems(TestItemType, AsteroidType)  
 
-- Now iterate over the list ... there are many examples in the documentation!
for item in values(globItems) do
    -- Do something with item ... perhaps figure out which is closest, 
    -- which is heading towards us, etc.
end


Point getWaypoint(Point)

Point getWaypoint(x, y)



Ship control

void setThrust(vel, angle)
- Set robot's velocity to vel (0-1), at angle
void setThrustPt(vel, Point)
- Set robot's velocity to vel (0-1), toward Point.


void setThrustToPt(Point)
- Thrust toward Point, but adjust speed in an attempt to land right on point, and not overshoot. If Point is distant, setThrustToPt() will cause robot to go at full speed.


void fire()
- Fires active weapon
void setWeaponIndex(index)
- Activates weapon with index (1, 2, 3)
void setWeapon(WeaponType)
- Activates weapon type specified (does nothing if WeaponType is not in current loadout)
boolean hasWeapon (WeaponType)
- Does current configuration have specified weapon

Example:

if(bot:hasWeapon(WeaponMine)) then
    bot:setWeapon(WeaponMine)      -- Make mine layer active if it's 
                                   --     part of our current loadout
    bot:fire()                     -- Lay a mine
end

Logging & Sending Messages

void globalMsg(msg)
- Send a message to all players
void teamMsg(msg)
- Send a message to players on the same team


void logprint(msg)
- Print msg to game logfile and to game console

When sending a team or global message, some basic variable substitutions are available.  %playerName% will be replaced with the receiving player's name.  %controlName% will be replaced with the appropriate key binding on the receiving player's machine. A full list of controlNames can be found in the KeyBinding section of the bitfighter.ini file. Examples include %SelWeapon1% and %ShowScoreboard%. While this capability is also available to player manually typing chat messages, it is mostly intended for tutorial bots to help the player along. Variable names are case insensitive.

GameItems

The Lua object structure generally follows that used by Bitfighter. The GameItems group conisists of Ships, Robots, RepairItems, Asteroids, ResourceItems, SoccerBallItems, Flags, NexusFlags, and TestItems. These all share similar properties, and have similar methods. All of these implement the getLoc(), getVel(), and getRad() methods for querying location, velocity, and radius respectively. Some items have additional methods that apply only to them. See below for details on these additional methods.


Ships & Robots

Unless otherwise noted, all of these methods will work both on a ShipItem object as well as the local bot object.

boolean isAlive()
- Returns true if ship/bot is still alive, false otherwise.

Rather than checking isAlive() every tick, it may be more efficient to keep track of dying ships by subscribing to ShipKilledEvent.

Example:

-- Assuming we've subscribed to ShipKilledEvent, and that target is a global
function onShipKilled(ship)
   if target == ship then
      target = nil   -- check for target == nil elsewhere and reacquire a target
   end
end


Note: All of the following functions will return nil if they are called on a dead ship. When in doubt, check for nil!

Point getLoc()
- Return center of item
number getRad()
- Return radius of item
Point getVel()
- Return speed of item
number getAngle()
- Returns angle ship is currently facing
number getTeamIndx()
- Index of the team this ship/robot is on
PlayerInfo getPlayerInfo()
- Return info about player or robot controlling the ship

The following two methods return a number between 0 and 1 where 1 means full health or energy, 0 means no health or energy.

number getHealth()
- Return health of the ship or robot
number getEnergy()
- Return energy of ship or robot
boolean isModuleActive(ModuleType)
- Returns true if specified module is active
boolean hasFlag()
- Returns true if ship is carrying a flag, false otherwise
number getFlagCount()
- Returns number of flags a ship is carrying. Currently, ships can only have more than one flag in Nexus games
void dropItem()
- Drops any items the bot may be holding


WeaponType getActiveWeapon()
- Returns the active weapon constant (see WeaponType constants)

CoreItems, ResourceItems, SoccerBallItems, and TestItems

These items all have the same methods and behave in a very similar manner. Category: GameItem

Point getLoc()
- Return center of item
number getRad()
- Return radius of item
Point getVel()
- Return velocity of item
number getTeamIndx()
- Returns index of items's team (will always be NeutralTeamIndx)

CoreItems have one additional method:

number getHealth()
- Return item's health

Example:

local items = bot:findItems(TestItemType)  -- Find all TestItems in view
 
-- Now cycle through all returned items and find the fastest one
local maxSpeed = -1
local fastest = nil
 
for indx, ti in ipairs(items) do
    -- getVel() returns a point representing the x and y components of
    -- the velocity.  Need to use len() to get actual speed.  Or, in this
    -- case, since actual speed isn't important, we can use lenSquared()
    -- which is less computationally intensive, and will yield perfectly
    -- good results for comparison purposes (such as finding the fastest).
    spd = ti:getVel():lenSquared()  
 
    if(spd > maxSpeed) then
        fastest = ti
        maxSpeed = spd 
    end
end
 
-- Better check to make sure fastest isn't nil before using it!
-- Could be nil if there are no TestItems in view
 
if(fastest != nil) then
   -- Do something
end

Asteroids

Category: GameItem

Point getLoc()
- Return center of asteroid
number getRad()
- Return radius of asteroid
Point getVel()
- Return velocity of asteroid
number getTeamIndx()
- Returns asteroid's team index (will always be NeutralTeamIndx)
number getSize()
- Index of current asteroid size (0 = initial size, 1 = next smaller, 2 = ...)
number getSizeCount()
- Number of indexes of size we can have

Example:

local asteroids = bot:findItems(AsteroidType) 
local target = asteroids[1]:getLoc()  -- Pick first asteroid

RepairItems

Category: GameItem

Point getLoc()
- Return center of repairItem
number getRad()
- Return radius of repairItem
Point getVel()
- Return velocity of repairItem (usually 0,0)
number getTeamIndx()
- Returns repairItems's team index (will always be NeutralTeamIndx)
boolean isVis()
- Is repairItem currently visible?

Example:

local ri = bot:findItems(RepairItemType)   -- Get list of all known RepairItems  
local pos = ri[1]:getLoc()                 -- Get position of first

Flags

Category: GameItem

Point getLoc()
- Return location of flag
number getRad()
- Return radius of flag
Point getVel()
- Return velocity of flag
number getTeamIndx()
- Get index of owning team (-1 for neutral flag)
boolean isInInitLoc()
- Is flag in it's initial location?
boolean inCaptureZone()
- Is flag in a team's capture zone?
boolean isOnShip()
- Is flag being carried by a ship?

Turrets and ForceFieldProjectors

Category: GameItem

Point getLoc()
- Return location of NexusFlag
number getRad()
- Return radius of NexusFlag
Point getVel()
- Return velocity of NexusFlag
number getTeamIndx()
- Get index of owning team (see Special Team Constants)
number getHealth()
- Get health of item (1 = full heath, 0 = totally dead)
boolean isActive()
- True if item is active, false otherwise

GoalZones and LoadoutZones

Point getLoc()
- Get the centroid of the zone
number getRad()
- Returns 0
Point getVel()
- Get the velocity of the item (will usually be (0,0)!)
number getTeamIndx()
- Get index of owning team (see Special Team Constants)

Nexus Related

The following functions only have meaning in a Nexus game.

number getNexusTimeLeft()
- Return time left until next change of the nexus open/closed status (in ms)
boolean isNexusOpen()
- Returns true if nexus is open, false if closed

Example:

if isNexusOpen() then
   local timeLeft = getNexusTimeLeft() / 1000
   if timeLeft < 10 then
      teamMsg("Hurry! Nexus closes in "  ..  timeLeft .. " seconds!")
   end
end

Bullets, Mines, and SpyBugs

Category: GameItem
There are three GameItem types that have to do with bullets, mines, and spybugs: BulletType, MineType, and SpyBugType respectively. They all have the same methods, and can be used somewhat interchangeably, as shown in the example below. However, as shown, they can also be found independently.

BulletType encompases Phaseser, Bounce, Triple, Burst, and Turret shots, the properties of which can be retrieved with the WeaponInfo object.

Point getLoc()
- Return location of item
number getRad()
- Return radius of item
Point getVel()
- Return velocity of item
number getTeamIndx()
- Get the team of the shooter/layer of the item
WeaponType getWeapon()
- Return WeaponType for this item

Example:

local bullets = bot:findItems(BulletType, MineType, SpyBugType) 
 
<some code>    -- Code here to select one of the found items
 
local weap = selectedBullet:getWeapon()
local weapInfo = WeaponInfo(weap)
logprint("Selected "  ..  weapInfo:getName())

Weapon Information

All WeaponInfo data will remain constant throughout the game. Therefore, if you need some information about a weapon (mines, say, for a minesweeping bot), it might make sense to retrieve it in the bot's main() function, and store it in a global variable rather than instantiating a new WeaponInfo object during every iteration of the robot's onTick() method.

Example:

local weap = bot:getActiveWeapon()      -- Get bot's currently active weapon
logprint(weap:getName() .. " shoots with a speed of " .. weap:getProjVel())
-- In main()... we'd better not declare these variables as local!
function main()
   ...
   turretInfo = WeaponInfo(WeaponTurret)   -- Get info about the infernal turrets
   safeDist = turretInfo:getRange()        -- Be safe and stay this far away 
   ...
end


string getName()
- Name of weapon ("Phaser", "Triple", etc.)
WeaponType getID()
- ID of module (WeaponPhaser, WeaponTriple, etc.)
number getRange()
- Get range of weapon (units)
number getFireDelay()
- Delay between shots in ms
number getMinEnergy()
- Minimum energy needed to use
number getEnergyDrain()
- Amount of energy weapon consumes
number getProjVel()
- Speed of projectile (units/sec)
number getProjLife()
- Time projectile will live (ms) -1 == live forever)
number getDamage()
- Damage projectile does (0-1, where 1 = total destruction)
number getDamageSelf()
- Damage if shooter shoots self (0-1, where 1 = total destruction)
boolean getCanDamageTeammate()
- Will weapon damage teammates?

WeaponType constants

Commands that return a WeaponType will return one of the following values:

Weapon.PhaserWeapon.Bounce
Weapon.TripleWeapon.Burst
Weapon.MineWeapon.SpyBug
Weapon.Turret

Module Information

All ModuleInfo data will remain constant throughout the game. Therefore, if you need some information about a module (for example, the energy consmption of shields), it might make sense to retrieve it in the bot's main() function, and store it in a global variable rather than instantiating a new ModuleInfo object during every iteration of the robot's onTick() method.

Example:

local mod = ModuleInfo(ModuleBoost)
logprint("This is a lame example!")
string getName()
- Name of module ("Shield", "Turbo", etc.)
ModuleType getID()
- ID of module (ModuleShield, ModuleBoost, etc.)

ModuleType constants

Functions that return a ModuleType will return one of the following values:

ModuleShieldModuleBoost
ModuleSensorModuleRepair
ModuleCloakModuleEngineer
ModuleArmor

Loadouts

void setWeapon(index, WeaponType)
- Set weapon at index
void setModule(index, ModuleType)
- Set module at index
WeaponType getWeapon(index)
- Return weapon at index
ModuleType getModule(index)
- Return module at index

Example:

-- Get a new loadout (comes pre-filled with default values)
local loadout = Loadout()
 
-- Configure the loadout to suit our needs
loadout:setWeapon(1, WeaponPhaser)
loadout:setWeapon(2, WeaponBurst)
loadout:setWeapon(3, WeaponMine)
loadout:setModule(1, ModuleShield)
loadout:setModule(2, ModuleCloak)
 
-- Set the loadout, will become active when bot hits loadout zone
-- or spawns, depending on game
bot:setReqLoadout(loadout)     
 
if(loadout:getWeapon(1) == WeaponPhaser) then
   logprintf("This line always gets printed!")
end


boolean isValid()
- Is loadout config valid?
boolean equals(Loadout)
- is loadout the same as Loadout?

GameInfo

You can get information about the current game with the GameInfo object. You only need get this object once, then you can use it as often as you like. It will always reflect the latest data.
Example:

game = GameInfo()           -- Create the GameInfo object 
gameType = game:getGameType()
 
if(gameType == SoccerGame) then
   logprint("This bot is not very good at soccer!")
else
   gameTypeName = game:getGameTypeName()
   logprintf("I just love playing " .. gameTypeName .. "!")   
end

Example:

-- Create the GameInfo object in main() (don't declare it local!!)
function main()
   ...
   game = GameInfo()    -- In main non-local variables become globals
   ...
end
 ...
 
-- Even though we only create our GameInfo once, it is always current
 
 ...
 
-- Later, in onTick():
local remTime = game:getGameTimeReamaining()
local totTime = game:getGameTimeTotal()
local percent = (totTime - remTime) / remTime
logprint("Game is " .. percent .. "% over)
GameType getGameType()
- Return current game type
string getGameTypeName()
- Return current game type
number getFlagCount()
- Return the number of flags in the game
number getWinningScore()
- Returns the score required to win the level
number getGameTimeTotal()
- Returns the time (in seconds) that the level will be played for
number getGameTimeRemaining()
- Returns the time remaining (in seconds) for this level
number getLeadingScore()
- Gets score of the leading team
number getLeadingTeam()
- Gets index of leading team
number getTeamCount()
- Return number of teams

Example:

-- Create the GameInfo object main() (don't declare it local!!)
function main()
   ...
   game = GameInfo()    -- In main non-local variables become globals
   ...
end
 
-- Then later, in onTick() or other function
...
local leadingTeam = game:getLeadingTeam()
local team = TeamInfo(leadingTeam)
bot:teamMsg("Hurry up! Team " .. team:getName() .. " is winning!" )
PlayerInfo list getPlayers()
- Return a list of PlayerInfo objects representing all players/bots in the game

Example:

players = GameInfo():getPlayers()
for player in values(players) do
   if player:isRobot() then
      logprint(player:getName().." is a bot!")
   else
      logprint("Hello "..player:getName())
   end
end
string getLevelName()
- Gets the level's name
number getGridSize()
- Gets the level's gridSize parameter
boolean IsTeamGame()
- Is this a team game?


void getEventScore(ScoringEvent)
- Gets score awarded for ScoringEvent

GameType constants

Functions that return a GameType will return one of the following values:

BitmatchGameCoreGame
CTFGameHTFGame
NexusGameRabbitGame
RetrieveGameSoccerGame
ZoneControlGame

ScoringEvent constants

Functions that return a ScoringEvent will return one of the following values:

KillEnemyKillSelf
KillTeammateKillEnemyTurret
KillOwnTurretCaptureFlag
CaptureZoneUncaptureZone
HoldFlagInZoneRemoveFlagFromEnemyZone
RabbitHoldsFlagRabbitKilled
RabbitKillsReturnFlagsToNexus
ReturnFlagToZoneLostFlag
ReturnTeamFlagScoreGoalEnemyTeam
ScoreGoalHostileTeamScoreGoalOwnTeam

TeamInfo

string getName()
- return team name
number getIndex()
- return team's index, index of first team is 1
number getPlayerCount()
- return player count
number getScore()
- return team score
PlayerInfo list getPlayers()
- Return a list of PlayerInfo objects representing all players/bots on the team

Example:

local gameInfo = GameInfo()
local teams = gameInfo:getTeamCount()
 
-- Note that while the number of teams will not change throughout the game,
-- the number of players on each team might.  Also, the robot may be initialized
-- before the players have been added, so if you run this code in the bot header, 
-- it may report some teams having 0 players.
 
for i = 1, teams do        -- First team has an index of 1
   team = TeamInfo(i)
   logprint("Team " .. i .. " is called " .. team:getName() .. 
      " and has " .. team:getPlayerCount() .. " players")
end

Special team constants

Two special team constants are available to make your programs simpler and easier to read. In addition to the normal player teams, Bitfighter has two pseudo teams, "Neutral" and "Hostile". These constants can be used to identify those teams:

NeutralTeamIndx - Neutral team
HostileTeamIndx - Hostile team

Example:

team = turret:getTeamIndx()
if team == NeutralTeamIndx then        -- Neutral - can be repaired
   mode = repair
else if team == HostileTeamIndx then   -- Hostile - must be destroyed!
   mode = attack 
end

PlayerInfo

Provides information about a player or robot. The PlayerInfo object will remain valid and will contain current information as long as the player is in the game.

string getName() - return player's name
Ship getShip() - return player's ship (note: can be nil)
string getScriptName() - for robots, returns name of script; for players, returns nil
number getTeamIndx() - return team's index, index of first team is 1
number getRating() - return player's rating
number getScore() - return player's score
boolean isRobot() - returns true if the PlayerInfo is a robot, false if it is a human

Timers

Timer is a utility class that makes it easier to manage timing periodic or delayed events. The timer code is based on a very nice library written by Haywood Slap

A timer object that can be used to schedule arbitrary events to be executed at some time in the future. All times are given in milliseconds.

Usage

There are four basic timer functions:

-- Execute the event once in 'delay' ms
Timer:scheduleOnce(event, delay)
 
-- Execute the event repeatedly, every 'delay' ms
Timer:scheduleRepeating(event, delay)  
 
-- Execute the event every 'delay' ms while event returns true
Timer:scheduleRepeatWhileTrue(event, delay)  
 
-- Remove all pending events from the timer's queue
Timer:clear()

Timer Events

The 'event' used can either be the name a function, a table that contains a function named 'run', or a bit of arbitrary Lua code.

Any Lua function can be called by the Timer.

Examples:

function onLevelStart()
   -- Compiles and runs string in five seconds. 
   -- Note that the string here is in quotes.
   Timer:scheduleOnce("logprint(\"Once!\")", 5000)   
 
   -- Runs the always function every 30 seconds
   Timer:scheduleRepeating(always, 30000)            
 
   -- Runs "run" method of maybe every two seconds
   -- until method returns false
   Timer:scheduleRepeatWhileTrue(maybe, 2000)        
end                                                  
 
-- Standard function
function always()
   logprint("Timer called always")
end
 
-- Table: run method will be called
maybe = {
   count = 3
   run = function(self)
      logprint("Count down " .. self.count)
      self.count = self.count - 1
      return self.count > 0
   end
}

When using a table as an 'event' the first parameter to the run function should always be a parameter named "self" (or "this" if you prefer). The "self" parameter can be used to access the other fields in the table, that is, the "self" parameter will refer to the table that contains the run function. If you do not need to refer to any of the other fields in the table then you do not need to include the self parameter.

Points

Point is a utility class to make handling coordinates or vectors simpler. You can create your own point objects using the constructor, or you can get them as return values from many functions.

Point Point(x, y)
- Create a new Point object with coordinates x, y. You do not need to use setxy() unless you want to change the coords later.
number x()
- Return x value
number y()
- Return y value
void setxy(x, y)
- Update coordinates of point
void setx(x)
- Update x coordinate of point
void sety(y)
- Update y coordinate of point
void setAngle(ang)
- Set angle of point to ang. This will preserve the point's current length, and can can be used in conjunction with normalize to set a point via polar coordinates
void setPolar(r, ang)
- Set point using polar coordinates, r = distance from origin, ang = angle
number len()
- Returns the length of the point, useful if point represents a velocity or other vector
number lenSquared()
- Returns the length squared of the point, useful if point represents a velocity or other vector, and you want a less computationally expensive representation of length that will work for comparative purposes
boolean equals(Point)
- Returns true if the point is the same as Point
void normalize(length)
- If Point represents a vector, set its length to length
number distanceTo(Point)
- Returns distance from point to Point
number distSquared(Point)
- Returns distance squared from point to Point (less computationally intensive, good for comparison)
number angleTo(Point)
- Returns angle from point to Point
number angleTo(x, y)
- Returns angle from point to point(x, y)

Example:

local point = Point(0, 0)      -- Create a new point
 
if point:equals(Point(1, 1)) then       
   logprint("This never gets printed")      
end
 
if point:distanceTo(Point(1, 1)) < 5 then       
   logprint("This always gets printed")      
end
 
-- Same thing, but with a little less overhead
if point:distanceTo(1, 1) < 5 then       
   logprint("So does this, but more efficiently")      
end
 
 
-- Here we'll use a point to represent a velocity, which has x & y components
local xspeed = 10
local yspeed = 15
local velocity = Point(xspeed, yspeed)
logprint("Speed = " .. velocity:len())

ItemTypes

Functions that return an ItemType will return one of the following values:

ShipType
BulletType
MineType
SpyBugType
RobotType
TeleportType (not yet fully implemented)
SpeedZoneType (not yet fully implemented)
AsteroidType
CoreType
TurretType
ForceFieldProjectorType
TestItemType
FlagType
SoccerBallItemType
ResourceItemType

Things that are not really items

Polygonal-like
BotNavMeshZoneType
NexusType
GoalZoneType
LoadoutZoneType

Wall-like
BarrierType ForceFieldType

Helper functions

item findClosest((list of items [, teamIndx])
- Finds item closest to bot. If teamIndx is specified, only considers items on team. If list is empty (or there are no team items in the list), funtion returns nil.