Script reference

Program!

Any ship in Drovoid can be equipped with a Program AI module. After launching the ship, select it, and the programming panel will appear. Write code and click on 'Update firmware' (or hit CTRL+S) to save the firmware in the ship blueprint. The firmware of the selected ship is immediately updated and the ship will reset to execute the new version.
If multiple Program modules exist on the ship they all execute the same firmware in parallel (this is not recommended if you begin the game!).
Several functions return distances or coordinates in space units. It is therefore important to understand the scale of things. Fortunately that is simple: a 1x1 module is exactly 1x1 space units (or, to say it otherwise, each cell in the shipyard grid is 1x1 space unit).

Lua 101

Drovoid scripting language is based on a subset of Lua with a few twists to simplify scripting of spaceships. Here are a few simple example of the main language features:

    s = 'hello world' a = 1 + math.max(2,3) tbl={} tbl[1]='hello' tbl[2]='world' s = 'Welcome to ' .. 'Drovoid ' .. 101 if condition then do_something else do_something_else end while condition do something end foreach m in all do something_with_m end function(parameter1,parameter2) return parameter1 + parameter2 end a = nil if not a then always_goes_here else never_goes_here end

Ships and modules

The Program module has potentially access to all other ships and modules (with limited access to ships and modules belonging to other players). The modules of the ship executing the firmware can be accessed directly. For instance, if a ship has two laser modules they will be named Laser_0 and Laser_1. If the ship has a thruster, it will be named Thruster_0, and so on. Here is a quick example:

Thruster_0.ThrustPower = 1
Shield_0.SwitchedOff = 1

This code applies full thrust power on Thruster_0 and switches off Shield_0 to save power.

Modules in other ships have to be discovered using either the scan or the find functions described below. The return value is stored in a variable containing the module (or ship) which can then be similarly accessed:

-- m is a module variable
if m.Type == 'Thruster' then
  m.ThrustPower = 0
end

This code tests the type of the module, and if it is a thruster it turns it off.

A program can access properties and states of all modules. Properties never change and are read-only (e.g. module MaxIntegrity) while states evolve (e.g. module current Integrity will decrease upon damage). Some states are read only (Integrity) while others can be written to to control the module directly (e.g. Laser_0.Firing = 1). For each module, the full list of states and properties can be easily found by selecting a module in-game and opening the module status panel (right most), as shown in the inset. Here is a simple example of using modules states and properties:

Laser_0.Firing = 1
Laser_0.Angle = 0.3

This will turn on the laser beam of laser module Laser_0, and will give it an angle of 0.3 radians (all angles are in radians in Drovoid). Here is another example:

if PowerCell_0.RemainingPower > PowerCell_0.PowerCapacity / 2 then
  Shield_0.SwitchedOff = 0
end

This turns shield Shield_0 back on when the power cell PowerCell_0 is at more than half its full charge.

Ship states and properties

    Ships also have a few states and properties: ship.NameThe ship name. ship.GroupThe ship group as a string (a player name, or Asteroids, or NPC). ship.RecyclableEquals 1 if the ship can be recycled, 0 otherwise. ship.SelectedEquals 1 if the ship is currently selected by the player, 0 otherwise. This state is very useful to implement direct user control, for instance:
    if ship.Selected then
      Laser_0.Firing = 1
    else
      Laser_0.Firing = 0
    end
    
    This will turn on Laser_0 when the ship is selected.

Ship and module methods

Ships and modules have a number of convenience methods that can be called. For instance:

c = m:center()
Laser_0:orientToward(c)

This gets the center of module m and orients the laser beam of Laser_0 towards it. This is how you shoot at enemy ships! Please note the use of : (and not .) to call methods in Lua. This is a very common source of syntax errors.

Here is the list of module methods (note that they may have no effect on some modules, e.g. shields cannot be oriented).

    m:velocity()Returns current velocity at module center. m:center()Returns the coordinates in space units of the module center. m:ship()Returns the ship owning the module. m:isDestroyed()Returns true if the module is destroyed. m:orientation()Returns a vector pointing in the direction of the module turret (lasers, thrusters, etc.). m:orientToward(p)Orients the turret towards point p. m:orientAway(p)Orients the turret away from point p. m:orientAlong(v)Orients the turret to align with direction v. m:orientAgainst(v)Orients the turret to oppose direction v. m:distanceTo(m)Returns the distance from the calling AI to module m. m:spawn(blueprint) (Gate module only). Spawns a new ship. See automatically spawning ships. m:respawn() (Gate module only). Spawns again the same ship. Spawn must be called once before. See automatically spawning ships. m:recycle(s) (Gate module only). Recycles ship s (s has to be within gate radius).

Here is the list of ship methods

    s:modules()Returns the list of all modules of ship s. s:isDestroyed()Returns true if the ship is destroyed, false otherwise. s:isFriend()Returns true if the ship is a friend, false otherwise. s:lastAttacker()Returns the ship that last attacked ship s (expires after a preset delay).

Functions

The following functions can be called directly from the ship firmware.


    Time and log

      log('message to log') Writes a log message to the in-game console.
      sleep(500) Sleeps for a number of milliseconds (500 in this example). Use whenever a ship has nothing to do, it will avoid needlessly consuming your CPU quota.
      time() Returns the current time in milliseconds.

    Math

      Most functions of the Lua math library are available, see reference here. A few examples:
      mx = math.max(10,2)
      cs = math.cos(math.pi / 3.0)
      rn = math.random()
      

    Vectors

      v = Vector(1,2) Creates a vector (1,2) and stores it in v.
      v.X Accesses component X of the vector ; same for Y.
      length(v) Returns the length of v.
      n = normalize(v) Returns the normalized version of v (unit length).
      dot(v,u) Returns the dot product of v and u (a scalar).
      v + u Returns the sum of v and u. Same for -.
      v * 2 Returns v multiplied by a scalar (here, 2).

    Tables

      insert(tbl,element) Inserts element into table tbl.
      empty(tbl) Returns true if table tbl is empty, false otherwise.
       
      Some functions of the Lua table library are also available, see reference here.

    Targeting

      scan(Enemy + Fighter, 100) Scans nearby space for ships. The first parameter tells the scanner what to look for; in this case any module from an Enemy ship having a Fighter AI. The second parameter is the scanning range, in space units. If not specified, the scanner scans as far as possible. Scan returns an array of detected modules. Here is a usage example:
      found = scan(Enemy + Fighter, 100)
      if not empty(found) do
        m = found[1]
        log('found module ' .. m.Name)
      end
      
      This will only consider the first detected module. Note that currently scan returns at most one module. This may change in future releases. Keep in mind that scan might not return the closest target. It is therefore important to carefully consider the range being used, to avoid targetting far away ships.
      accept_target(Friend + Miner,50,module) Returns true if module is an acceptable target; in this example if module belongs to a Friend ship having a Miner AI, and being within 50 space units.
      find('AI_0@Ship_e0f')Returns the module described by the string, in this case the module named AI_0 of the ship named Ship_e0f. If no such module can be found, the function returns nil. It is also possible to only give a ship name:find('Ship_e0f') In this case the ship is returned, if found. Otherwise nil is returned.

    Attackers and colliders

      colliders() Returns the list of collider ships (list cleared after the call). Usage example:
      cols = colliders()
      foreach c in cols do
        log('collided with ' .. c.Name)
      end
      

      attackers() Returns the list of attackers (list cleared after the call).
      lastAttacker() Returns the last attacker (ship).

    Ship structure

      While all Program modules can access all other ship modules, it is often useful to restrict the set of modules controlled by the current Program AI - in particular if there are multiple Program AIs on the same ship. The following functions help you do this. See also the modules global table to access all ship modules.
      neighbors() Returns the list of modules that are directly neighboring the calling AI module.
      connected() Returns the list of modules that are connected to the calling AI module through DataBus modules.
      component() Returns the list of modules that are in the same ship component as the calling AI module. A component is a set of modules that are connected together without going through Armor modules. When a Hinge module opens it disconnects the component in two new components.
      componentLastChange() Returns the last time at which a component in the ship changed (consistent with the time() function).


    String

      Main functions of the Lua string library are available, see reference here. A few examples:
      l = string.len('abcde')
      su = string.sub('hello',3)
      

    Inputs

      mouse() Returns a vector with the owner player current mouse coordinates in space units.
      key(KeySignal1) Returns true if the given owner player key is pressed (here KeySignal1), false otherwise. Key ids can be KeySignal1-4, KeyThrust, KeyFire. Typical mappings are number keys '1' to '4' for the signals, CTRL for fire and SPACE for thrust.

Globals


    Ship
      modules Table of all ship modules. Usage example:
      foreach m in modules do
        log('type ' .. m.Type .. ' name ' .. m.Name)
      end
      

      thisThe calling AI module. Usage example:
      log('I am ' .. this.Name)

      shipThe ship owning the calling AI.

    Targeting
      Friend Enemy Fighter Miner Defender Ore

    Inputs
      KeySignal1 KeySignal2 KeySignal3 KeySignal4 KeyThrust KeyFire

Automatically spawning ships

Gate modules have the ability to spawn ships. It is as simple as this:

--blueprint NameOfAShip
Gate_0:spawn(NameOfAShip)

In this example NameOfAShip is the name of a previously created ship that appears in your hangar. If multiple ships have the same name, the first one is taken. If the ship is not found there will be a runtime error. The first line --blueprint NameOfAShip tells the compiler to embed the ship blueprint inside the firmware. This line is mandatory, a runtime error will occur without it. Of course multiple ships can be embedded. Spawn even returns the ship variable, so you can manipulate it:

--blueprint NameOfAShip
s = Gate_0:spawn(NameOfAShip)
while not s:isDestroyed() do 
  sleep(500) -- waiting
end
log('oops, my ship has been destroyed!!')

There are two constraints when spawning ships: 1) each spawned ship will decrease your M.U. (as when manually launching ships) 2) each player is limited to a maximum number of ships. After this point you cannot spawn ships anymore. Therefore, it is important to manage your fleet and clean up. It is possible to automatically clean-up using the recycle method of the gate module:

--blueprint NameOfAShip
s = Gate_0:spawn(NameOfAShip)
sleep(500) -- waiting
recycle(s) -- works only if ship still in gate radius

Manual clean up is done as usual, selecting a ship and using the Recycle/Kill button.

Performance tips

Drovoid is powered by the A.S.T.R.E. operating system, which distributes computations between ship and players. Avoid saturating your CPU quota by using sleep wisely.

It is perfectly ok to create infinite loops. Most ships use programs like this:

init()
while true do
  s = scan(...)
  do_something(s)
  sleep(200)
end

Keep in mind that your ship AIs can only execute a limited number of instructions per seconds, and therefore expensive computations, long loops, etc. will make them unresponsive.

Errors

Errors are reported in the in-game console. Two types of errors can occur: 1) syntax errors and 2) runtime errors. These will be tagged accordingly in the console. Drovoid does not feature a debugger yet, so use log to display variables and track bugs.

Custom AI states

It is possible to declare additional states on AI modules. This is very useful as several AI modules, across different ships, can use these to communicate. Custom states can store anything: values, vectors, modules or ships. They are visible in the module status panel. Here is an example:

--var Active
this.Active = 1
foreach m in modules do
  if m.HasActive then
    if this.Name < m.Name then 
      m.Active = 0
    end
  end
end

In this example, each Program module of the ship gets an Active state. Each module looks at other modules, checks if the module has the variable (m.HasActive). If it is there it sets its value to 0 if the current module has higher priority (lower Name). The goal of this program is to have a single program activated.

Whenever a variable Foo is declared, a state HasFoo is automatically created as well (accessing Foo on an AI which does not have it otherwise triggers an error).

The standard AIs (Fighter, Miner, Defender) have two special custom states: Target and Follow which are set by the mouse clicks. By default, any ship firmware also receives these two custom states (so you can refer to this.Target and this.Follow in your code). Standard AIs also have a Class state which contains the AI class name (e.g. 'Fighter').

For convenience you can set all AI states of a ship by setting the value directly on the ship, that is:

s = Gate_0:spawn(AShip)
s.Follow = this

This code sets the 'Follow' variable of all AI modules of s to be the spawning ship.

Known issues

While it is perfectly fine to spawn ships that also spawn ships, beware of recursive definitions (a ship that spawns itself). This is one of the few things that can crash the compiler.

When referring to AI custom states use simple expressions on a single line without comments such as this.State = .... Avoid expressions such as ais[1].State = ... and comments on the same line which might lead to syntax errors (this will be fixed in future releases).