Difference between revisions of "Programming robots"
Watusimoto (Talk | contribs) (→Currently defined events) |
(missing )) |
||
(16 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | 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. | + | 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 30: | Line 30: | ||
==== Currently defined events ==== | ==== Currently defined events ==== | ||
− | |||
<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> | ||
Line 36: | Line 35: | ||
<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( | + | <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> | ||
Line 47: | Line 46: | ||
function main() | function main() | ||
... | ... | ||
− | subscribe(ShipSpawnedEvent) -- Subscribe so | + | subscribe(ShipSpawnedEvent) -- Subscribe so onShipSpawned() will be called |
... | ... | ||
end | end | ||
Line 67: | 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 341: | 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 389: | 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> | ||
− | === | + | === 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 397: | 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 563: | 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> | + | <tr><td width=50%><b>Weapon.Phaser</b></td><td><b>Weapon.Bounce</b></td></tr> |
− | <tr><td><b> | + | <tr><td><b>Weapon.Triple</b></td><td><b>Weapon.Burst</b></td></tr> |
− | <tr><td><b> | + | <tr><td><b>Weapon.Mine</b></td><td><b>Weapon.SpyBug</b></td></tr> |
− | <tr><td><b> | + | <tr><td><b>Weapon.Turret</b></td><td></td></tr> |
</table> | </table> | ||
Line 586: | 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> | + | <tr><td><b>ModuleCloak</b></td><td><b>ModuleEngineer</b></td></tr> |
+ | <tr><td><b>ModuleArmor</b></td></tr> | ||
</table> | </table> | ||
Line 659: | Line 661: | ||
</source> | </source> | ||
− | {{funcdoc|GameType |getGameType|}} - Return current game | + | {{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 701: | 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| | + | {{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> | + | <tr><td width=50%><b>BitmatchGame</b></td><td><b>CoreGame</b></td></tr> |
− | <tr><td><b> | + | <tr><td><b>CTFGame</b></td><td><b>HTFGame</b></td></tr> |
− | <tr><td><b> | + | <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 791: | Line 793: | ||
<source lang="lua"> | <source lang="lua"> | ||
-- Execute the event once in 'delay' ms | -- Execute the event once in 'delay' ms | ||
− | Timer:scheduleOnce(event, delay | + | Timer:scheduleOnce(event, delay) |
-- Execute the event repeatedly, every 'delay' ms | -- Execute the event repeatedly, every 'delay' ms | ||
Line 902: | Line 904: | ||
<b>ShipType</b><br> | <b>ShipType</b><br> | ||
− | |||
<b>BulletType</b><br> | <b>BulletType</b><br> | ||
<b>MineType</b><br> | <b>MineType</b><br> | ||
Line 910: | 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)
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.
Contents
- 1 Events
- 2 Navigation, configuration, and combat
- 3 Logging & Sending Messages
- 4 GameItems
- 5 Weapon Information
- 6 WeaponType constants
- 7 Module Information
- 8 ModuleType constants
- 9 Loadouts
- 10 GameInfo
- 11 GameType constants
- 12 ScoringEvent constants
- 13 TeamInfo
- 14 PlayerInfo
- 15 Timers
- 16 Points
- 17 ItemTypes
- 18 Things that are not really items
- 19 Helper functions
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 ID | Associated function | Description |
---|---|---|
TickEvent | onTick(timeSinceLastTickInMs) | Called every frame of the game. Unlike other events, robots are subscribed to this event by default. |
ShipSpawnedEvent | onShipSpawned(ship) | Called when any ship or robot spawns (not including your robot). Spawning ship is passed as the first argument. |
ShipKilledEvent | onShipKilled(ship) | Called when any ship or robot is killed (not including your robot). Dead ship is passed as the first argument. |
MsgReceivedEvent | onMsgReceived(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. |
PlayerJoinedEvent | onPlayerJoined(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. |
PlayerLeftEvent | onPlayerLeft(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. |
NexusOpenedEvent | onNexusOpened() | Called when Nexus opens in a Nexus game. Never fires in other game types. |
NexusClosedEvent | onNexusClosed() | 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
There are two general methods for getting the current game time:
- number getTime()
- number getCPUTime()
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)
- Point getGatewayFromZoneToZone(a, b)
- number getZoneCount()
- number getCurrentZone()
- number getAngle()
- Point getLoc()
- number getTeamIndx()
- void setAngle(ang)
- void setAngle(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)
- number getAnglePt(Point)
- boolean hasLosPt(Point)
- boolean hasLosPt(x, y)
- number getFiringSolution(item)
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()
- boolean hasFlag()
- WeaponType getActiveWeapon()
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)
Example:
-- Note that this line will do nothing if we don't have shields bot:activateModule(ModuleShield) -- Shields up!
- void activateModuleIndex(indx)
Example:
-- Must specify an index, currently either 1 or 2 bot:activateModuleIndex(1) -- Activate first module, whatever it is
- void setReqLoadout(Loadout)
- Loadout getCurrLoadout()
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()
Navigation
- [Item list] findItems(ObjectType, ...)
- [Item list] findGlobalItems(ObjectType, ...)
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)
- void setThrustPt(vel, Point)
- void setThrustToPt(Point)
- void fire()
- void setWeaponIndex(index)
- void setWeapon(WeaponType)
- boolean hasWeapon (WeaponType)
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)
- void teamMsg(msg)
- void logprint(msg)
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()
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()
- number getRad()
- Point getVel()
- number getAngle()
- number getTeamIndx()
- PlayerInfo getPlayerInfo()
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()
- number getEnergy()
- boolean isModuleActive(ModuleType)
- boolean hasFlag()
- number getFlagCount()
- void dropItem()
- WeaponType getActiveWeapon()
CoreItems, ResourceItems, SoccerBallItems, and TestItems
These items all have the same methods and behave in a very similar manner.
Category: GameItem
- Point getLoc()
- number getRad()
- Point getVel()
- number getTeamIndx()
CoreItems have one additional method:
- number getHealth()
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()
- number getRad()
- Point getVel()
- number getTeamIndx()
- number getSize()
- number getSizeCount()
Example:
local asteroids = bot:findItems(AsteroidType) local target = asteroids[1]:getLoc() -- Pick first asteroid
RepairItems
Category: GameItem
- Point getLoc()
- number getRad()
- Point getVel()
- number getTeamIndx()
- boolean isVis()
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()
- number getRad()
- Point getVel()
- number getTeamIndx()
- boolean isInInitLoc()
- boolean inCaptureZone()
- boolean isOnShip()
Turrets and ForceFieldProjectors
Category: GameItem
- Point getLoc()
- number getRad()
- Point getVel()
- number getTeamIndx()
- number getHealth()
- boolean isActive()
GoalZones and LoadoutZones
- Point getLoc()
- number getRad()
- Point getVel()
- number getTeamIndx()
Nexus Related
The following functions only have meaning in a Nexus game.
- number getNexusTimeLeft()
- boolean isNexusOpen()
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()
- number getRad()
- Point getVel()
- number getTeamIndx()
- WeaponType getWeapon()
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()
- WeaponType getID()
- number getRange()
- number getFireDelay()
- number getMinEnergy()
- number getEnergyDrain()
- number getProjVel()
- number getProjLife()
- number getDamage()
- number getDamageSelf()
- boolean getCanDamageTeammate()
WeaponType constants
Commands that return a WeaponType will return one of the following values:
Weapon.Phaser | Weapon.Bounce |
Weapon.Triple | Weapon.Burst |
Weapon.Mine | Weapon.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()
- ModuleType getID()
ModuleType constants
Functions that return a ModuleType will return one of the following values:
ModuleShield | ModuleBoost |
ModuleSensor | ModuleRepair |
ModuleCloak | ModuleEngineer |
ModuleArmor |
Loadouts
- void setWeapon(index, WeaponType)
- void setModule(index, ModuleType)
- WeaponType getWeapon(index)
- ModuleType getModule(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()
- boolean equals(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()
- string getGameTypeName()
- number getFlagCount()
- number getWinningScore()
- number getGameTimeTotal()
- number getGameTimeRemaining()
- number getLeadingScore()
- number getLeadingTeam()
- number getTeamCount()
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()
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()
- number getGridSize()
- boolean IsTeamGame()
- void getEventScore(ScoringEvent)
GameType constants
Functions that return a GameType will return one of the following values:
BitmatchGame | CoreGame |
CTFGame | HTFGame |
NexusGame | RabbitGame |
RetrieveGame | SoccerGame |
ZoneControlGame |
ScoringEvent constants
Functions that return a ScoringEvent will return one of the following values:
KillEnemy | KillSelf |
KillTeammate | KillEnemyTurret |
KillOwnTurret | CaptureFlag |
CaptureZone | UncaptureZone |
HoldFlagInZone | RemoveFlagFromEnemyZone |
RabbitHoldsFlag | RabbitKilled |
RabbitKills | ReturnFlagsToNexus |
ReturnFlagToZone | LostFlag |
ReturnTeamFlag | ScoreGoalEnemyTeam |
ScoreGoalHostileTeam | ScoreGoalOwnTeam |
TeamInfo
- string getName()
- number getIndex()
- number getPlayerCount()
- number getScore()
- PlayerInfo list getPlayers()
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)
- number x()
- number y()
- void setxy(x, y)
- void setx(x)
- void sety(y)
- void setAngle(ang)
- void setPolar(r, ang)
- number len()
- number lenSquared()
- boolean equals(Point)
- void normalize(length)
- number distanceTo(Point)
- number distSquared(Point)
- number angleTo(Point)
- number angleTo(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])