Difference between revisions of "Programming robots"

From Bitfighter
(missing ))
 
(24 intermediate revisions by 2 users not shown)
Line 1: Line 1:
TODO: Update timer documentation, 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();
+
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.   
 
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.   
Line 32: Line 32:
 
<table border=1px cellpadding=3px>
 
<table border=1px cellpadding=3px>
 
<tr><th>Event ID</th><th>Associated function</th><th>Description</th></tr>
 
<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>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>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(ship)</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.</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>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>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>
 
</table>
  
Line 44: Line 46:
 
function main()
 
function main()
 
   ...
 
   ...
   subscribe(ShipSpawnedEvent)    -- Subscribe so OnShipSpawned() will be called
+
   subscribe(ShipSpawnedEvent)    -- Subscribe so onShipSpawned() will be called
 
   ...
 
   ...
 
end
 
end
Line 64: Line 66:
  
 
function main()
 
function main()
  ...
 
 
   subscribe(PlayerJoinedEvent)
 
   subscribe(PlayerJoinedEvent)
 
   subscribe(PlayerLeftEvent)
 
   subscribe(PlayerLeftEvent)
  ...
 
 
 
 
   players = GameInfo():getPlayers()    -- Vars defined in main() will be global
 
   players = GameInfo():getPlayers()    -- Vars defined in main() will be global
 
end
 
end
Line 83: Line 82:
  
  
 +
</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>
 
</source>
  
Line 329: Line 337:
 
<br>
 
<br>
 
{{funcdoc|void|logprint|msg}} - Print msg to game logfile and to game console<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 ==
Line 377: Line 387:
 
{{funcdoc|WeaponType|getActiveWeapon|}} - Returns the active weapon constant (see WeaponType constants)<br>
 
{{funcdoc|WeaponType|getActiveWeapon|}} - Returns the active weapon constant (see WeaponType constants)<br>
  
=== TestItems, ResourceItems, and SoccerBallItems ===  
+
=== CoreItems, ResourceItems, SoccerBallItems, and TestItems ===  
 
These items all have the same methods and behave in a very similar manner.  
 
These items all have the same methods and behave in a very similar manner.  
 
Category: GameItem<br>
 
Category: GameItem<br>
Line 385: Line 395:
 
{{funcdoc|Point|getVel|}} - Return velocity of item<br>
 
{{funcdoc|Point|getVel|}} - Return velocity of item<br>
 
{{funcdoc|number|getTeamIndx|}} - Returns index of items's team (will always be NeutralTeamIndx)<br>
 
{{funcdoc|number|getTeamIndx|}} - Returns index of items's team (will always be NeutralTeamIndx)<br>
 +
 +
CoreItems have one additional method:<br>
 +
{{funcdoc|number|getHealth|}} -  Return item's health<br>
  
 
Example:  
 
Example:  
Line 551: Line 564:
 
Commands that return a WeaponType will return one of the following values:
 
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>
  
Line 574: Line 587:
 
<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>
  
Line 647: Line 661:
 
</source>
 
</source>
  
{{funcdoc|GameType |getGameType|}} - Return current game typet) <br>
+
{{funcdoc|GameType |getGameType|}} - Return current game type <br>
 
{{funcdoc|string|getGameTypeName|}} - Return current game type <br>
 
{{funcdoc|string|getGameTypeName|}} - Return current game type <br>
 
{{funcdoc|number|getFlagCount|}} - Return the number of flags in the game<br>
 
{{funcdoc|number|getFlagCount|}} - Return the number of flags in the game<br>
Line 689: Line 703:
 
{{funcdoc|string|getLevelName|}} - Gets the level's name<br>
 
{{funcdoc|string|getLevelName|}} - Gets the level's name<br>
 
{{funcdoc|number|getGridSize|}} - Gets the level's gridSize parameter<br>
 
{{funcdoc|number|getGridSize|}} - Gets the level's gridSize parameter<br>
{{funcdoc|boolean|getIsTeamGame|}} - Is this a team game?<br>
+
{{funcdoc|boolean|IsTeamGame|}} - Is this a team game?<br>
 
<br>
 
<br>
 
{{funcdoc|void|getEventScore|ScoringEvent}} - Gets score awarded for ScoringEvent<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:
 
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>
  
Line 769: Line 783:
  
 
== Timers ==
 
== Timers ==
Timer is a utility class that makes it easier to manage timing periodic or delayed events.  
+
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
  
{{funcdoc|void|reset|}} - Reset the timer<br>
+
A timer object that can be used to schedule arbitrary events to be executed
{{funcdoc|number|getTime|}} - Returns time left on the timer (in ms)<br>
+
at some time in the futureAll times are given in milliseconds.
{{funcdoc|number|getFraction|}} - Returns the fraction of the timer left (1 = all time left, 0 = no time left)<br>
+
{{funcdoc|void|setPeriod|time}} - Set the time (in ms) on the timer, and reset it<br>
+
{{funcdoc|boolean|update|time}} - Update the timer by the specified amount of time (in ms), and return true if timer has gone offUse bot:getTime() as the time parameter.<br>
+
  
Example:
+
=== Usage ===
 +
 
 +
There are four basic timer functions:
 
<source lang="lua">
 
<source lang="lua">
-- In main()
+
-- Execute the event once in 'delay' ms
timer = Timer(1000)     -- Set timer that will go off every second
+
Timer:scheduleOnce(event, delay)
  ...
+
 
-- Then later, in onTick()
+
-- Execute the event repeatedly, every 'delay' ms
...
+
Timer:scheduleRepeating(event, delay)
if(timer:update(bot:getTime())) then   -- Always update with bot:getTime()
+
     
   logprint("1 sec has elapsed!")      -- Msg will get printed every second
+
-- Execute the event every 'delay' ms while event returns true
   timer:reset()                       -- Start timer over
+
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
 
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>
 
</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 ==
 
== Points ==
Line 843: Line 904:
  
 
<b>ShipType</b><br>
 
<b>ShipType</b><br>
<b>RobotType</b><br>
 
 
<b>BulletType</b><br>
 
<b>BulletType</b><br>
 
<b>MineType</b><br>
 
<b>MineType</b><br>
Line 851: Line 911:
 
<b>SpeedZoneType</b> (not yet fully implemented)<br>
 
<b>SpeedZoneType</b> (not yet fully implemented)<br>
 
<b>AsteroidType</b><br>
 
<b>AsteroidType</b><br>
 +
<b>CoreType</b><br>
 
<b>TurretType</b><br>
 
<b>TurretType</b><br>
 
<b>ForceFieldProjectorType</b><br>
 
<b>ForceFieldProjectorType</b><br>

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.