Difference between revisions of "Interacting with the game: Event-based programming"

From PioneerWiki
Jump to: navigation, search
m (Remove 'Outdated' banner)
(Setting a timer)
 
(15 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
__TOC__
 +
 +
==Events==
 +
 
The Lua scripts are all executed at startup. If you were to add a single file named <code>hello_world.lua</code> to the '''data/modules''' directory containing the following:
 
The Lua scripts are all executed at startup. If you were to add a single file named <code>hello_world.lua</code> to the '''data/modules''' directory containing the following:
  
Line 15: Line 19:
 
* <code>onShipDocked</code> is triggered whenever any ship docks at a starport.
 
* <code>onShipDocked</code> is triggered whenever any ship docks at a starport.
  
There are many more. All are fully documented in the [http://eatenbyagrue.org/f/pioneer/codedoc/ Pioneer Codedoc]. Of the five that I have listed, only <code>onGameStart</code> does not provide the function with any arguments. The other four provide a reference to the ship in question, and the latter two each also provide an additional argument (a reference to the attacker, and the starport, respectively).
+
There are many more. All are fully documented in the [https://codedoc.pioneerspacesim.net/ Pioneer Codedoc]. Of the five that I have listed, only <code>onGameStart</code> does not provide the function with any arguments. The other four provide a reference to the ship in question, and the latter two each also provide an additional argument (a reference to the attacker, and the starport, respectively).
  
 
==Writing a function for an event==
 
==Writing a function for an event==
Line 21: Line 25:
 
An event handling function does not have to return anything. It will be passed any arguments specified in the documentation, which it can either deal with, or ignore. It has access to any variables that are declared in the same file scope, including named functions and tables.
 
An event handling function does not have to return anything. It will be passed any arguments specified in the documentation, which it can either deal with, or ignore. It has access to any variables that are declared in the same file scope, including named functions and tables.
  
The following function sends a message to the player's ship console, welcoming them to Pioneer:
+
Here is an adaptation of the 'Hello World' message above to be event driven. It's now triggered on game start and will still turn up on the command line, but now much later in the start sequence, pretty much when the game starts and you find yourself docked at a starport.
 +
local Event = require 'Event'
 +
 +
local welcome = function ()
 +
    print("Hello, World!")
 +
end
 +
 +
Event.Register("onGameStart", welcome)
  
  local welcome = function ()
+
We move on. The same function again but now instead the message is presented on the player's ship console and is now welcoming them to Pioneer. We need to add the ''' 'Comms' ''' module to the script and the function name has changed to ''' 'onGameStart' ''', same as the event, which is common practice in Pioneer.
 +
  local Comms = require 'Comms'
 +
local Event = require 'Event'
 +
 +
local onGameStart = function ()
 +
    Comms.Message ('Welcome to Pioneer!')
 +
end
 +
 +
Event.Register("onGameStart", onGameStart)
 +
The latest code may not work as intended. The reason is that ''' 'onGameStart' ''' is not when the game starts but when it is being launched after pressing the button on the main menu to start on Mars, or whatever location you prefer. Let's see if there is an event that better suits our purpose. ''' 'onShipDocked''''?. This will make the message trigger every time we dock at a space station, on the ground or in orbit. ''' 'onShipDocked' ''' will not trigger on launching a saved game or when we start a new game, docked at a starport. Now the script works just fine and will launch the message the next time you land or dock with a space station.
 +
local Comms = require 'Comms'
 +
local Event = require 'Event'
 +
 +
local onShipDocked = function ()
 
     Comms.Message ('Welcome to Pioneer!')
 
     Comms.Message ('Welcome to Pioneer!')
 
  end
 
  end
 
+
The next one expects a ship, and if that ship is the player, it greets them again:
+
Event.Register("onShipDocked", onShipDocked)
 
+
If you look at the comms log after docking/landing, you may see the greeting posted more than once. This is because the same function is triggered for all ships in the vicinity, not only the player's. The Pioneer universe is populated by ships and characters and they follow pretty much the same rules as the player. To fix this we need to test if the ship is the player first. Modify the ''' 'onShipDocked' ''' function in the previous example like this:
  local greetShip = function (ship)
+
  local onShipDocked = function (ship)
 
     if ship:IsPlayer() then
 
     if ship:IsPlayer() then
         Comms.Message ('Hope you had an enjoyable hyperjump.')
+
         Comms.Message ('Welcome to Pioneer!')
 
     end
 
     end
 
  end
 
  end
  
All that remains is to attach these functions to events. The first, I'm going to attach to the <code>onGameStart</code> event, the second, I'm going to attach to <code>onEnterSystem</code>:
+
An alternative solution to the last code snippet would be to test for if the ship is '''not''' the player:
  
  Event.Register("onGameStart", welcome)
+
  local onShipDocked = function (ship)
Event.Register("onEnterSystem", greetShip)
+
    if not ship:IsPlayer() then return end
 +
    Comms.Message ('Welcome to Pioneer!')
 +
end
  
The Event module houses all functionality related to events. It is generally accessed by declaring it locally: <code>local Event = require 'Event'</code>.
+
==Setting a timer==
 +
Yet another way to avoid the problem with ''' 'onGameStart' ''' missing your function initially is to place a [https://codedoc.pioneerspacesim.net/#CClass:Timer Timer] to delay the event for a short while. Just a second or so to allow the game to load completely. CallAt takes two arguments, the time of action and a function to be carried out. Example form the Timer documentation:
 +
Timer:CallAt(Game.time+30, function ()
 +
    Comms.Message("Special offer expired, sorry.")
 +
end)
  
It's actually common practice in Pioneer to name the function after the event:
+
A complete example of the welcome message reworked with a timer. ''' 'Game.time' ''' gives us the game time right now so if we call the timer with ''' 'Game.time + 1' ''' we give it a second to think things through.
  
 +
local Comms = require 'Comms'
 +
local Game = require 'Game'
 +
local Event = require 'Event'
 +
local Timer = require 'Timer'
 +
 
  local onGameStart = function ()
 
  local onGameStart = function ()
     Comms.Message ('Welcome to Pioneer!')
+
     if not Game.player then return end
 +
 +
    Timer:CallAt(Game.time + 1, function ()
 +
        Comms.Message ('Welcome to Pioneer!')
 +
    end)
 
  end
 
  end
 
   
 
   
 
  Event.Register("onGameStart", onGameStart)
 
  Event.Register("onGameStart", onGameStart)
 +
 +
==Event arguments==
 +
 +
As mentioned before '''[https://codedoc.pioneerspacesim.net/#LuaClass:Event:onShipDocked onShipDocked]''' passes two arguments to the function. A reference to the ship and a reference to the spacestation.
 +
local onShipDocked = function (ship, station)
 +
Through these arguments we also get access to some of the ''' 'ship' ''' and ''' 'station' ''' methods without having to include any modules.
 +
''' 'ship:IsPlayer()' ''' is for free. Unlimited power is now at your fingertips! ''' 'Comms.Message' ''' takes a second argument for the sender of the message. ''' 'station.label' ''' gives us the name of the space station.
 +
 +
Comms.Message ("Congratulations! Your ship has been upgraded for free!", station.label)
 +
ship:SetShipType('xylophis')

Latest revision as of 23:49, 22 July 2024

Events

The Lua scripts are all executed at startup. If you were to add a single file named hello_world.lua to the data/modules directory containing the following:

print("Hello, World!")

you would literally see the words, "Hello, World!" appear in Pioneer's output (if running in a terminal) shortly before the main menu appeared. You would also see it in the Lua console, if you were to open it.

All file-scoped imperative statements in all Lua files are executed at that time. The way to get Lua code to interact with the game itself, beyond that time, is to write functions and to connect them to event handlers. Many events are triggered by Pioneer during the course of play, all of which will cause any functions which are connected to them, to run. Most will provide those functions with arguments.

Here is a quick list of some of the more commonly used events:

  • onGameStart is triggered when the player clicks on a new game button in the main menu, or when the player loads a game.
  • onEnterSystem is triggered whenever any ship arrives in the current star system after a hyperspace journey.
  • onLeaveSystem is triggered whenever any ship leaves the current star system by hyperspacing.
  • onShipDestroyed is triggered whenever any ship is destroyed.
  • onShipDocked is triggered whenever any ship docks at a starport.

There are many more. All are fully documented in the Pioneer Codedoc. Of the five that I have listed, only onGameStart does not provide the function with any arguments. The other four provide a reference to the ship in question, and the latter two each also provide an additional argument (a reference to the attacker, and the starport, respectively).

Writing a function for an event

An event handling function does not have to return anything. It will be passed any arguments specified in the documentation, which it can either deal with, or ignore. It has access to any variables that are declared in the same file scope, including named functions and tables.

Here is an adaptation of the 'Hello World' message above to be event driven. It's now triggered on game start and will still turn up on the command line, but now much later in the start sequence, pretty much when the game starts and you find yourself docked at a starport.

local Event = require 'Event'

local welcome = function ()
    print("Hello, World!")
end

Event.Register("onGameStart", welcome)

We move on. The same function again but now instead the message is presented on the player's ship console and is now welcoming them to Pioneer. We need to add the 'Comms' module to the script and the function name has changed to 'onGameStart' , same as the event, which is common practice in Pioneer.

local Comms = require 'Comms'
local Event = require 'Event'

local onGameStart = function ()
    Comms.Message ('Welcome to Pioneer!')
end

Event.Register("onGameStart", onGameStart)

The latest code may not work as intended. The reason is that 'onGameStart' is not when the game starts but when it is being launched after pressing the button on the main menu to start on Mars, or whatever location you prefer. Let's see if there is an event that better suits our purpose. 'onShipDocked'?. This will make the message trigger every time we dock at a space station, on the ground or in orbit. 'onShipDocked' will not trigger on launching a saved game or when we start a new game, docked at a starport. Now the script works just fine and will launch the message the next time you land or dock with a space station.

local Comms = require 'Comms'
local Event = require 'Event'

local onShipDocked = function ()
    Comms.Message ('Welcome to Pioneer!')
end

Event.Register("onShipDocked", onShipDocked)

If you look at the comms log after docking/landing, you may see the greeting posted more than once. This is because the same function is triggered for all ships in the vicinity, not only the player's. The Pioneer universe is populated by ships and characters and they follow pretty much the same rules as the player. To fix this we need to test if the ship is the player first. Modify the 'onShipDocked' function in the previous example like this:

local onShipDocked = function (ship)
    if ship:IsPlayer() then
        Comms.Message ('Welcome to Pioneer!')
    end
end

An alternative solution to the last code snippet would be to test for if the ship is not the player:

local onShipDocked = function (ship)
    if not ship:IsPlayer() then return end
    Comms.Message ('Welcome to Pioneer!')
end

Setting a timer

Yet another way to avoid the problem with 'onGameStart' missing your function initially is to place a Timer to delay the event for a short while. Just a second or so to allow the game to load completely. CallAt takes two arguments, the time of action and a function to be carried out. Example form the Timer documentation:

Timer:CallAt(Game.time+30, function ()
    Comms.Message("Special offer expired, sorry.")
end)

A complete example of the welcome message reworked with a timer. 'Game.time' gives us the game time right now so if we call the timer with 'Game.time + 1' we give it a second to think things through.

local Comms = require 'Comms'
local Game = require 'Game'
local Event = require 'Event'
local Timer = require 'Timer'

local onGameStart = function ()
    if not Game.player then return end

    Timer:CallAt(Game.time + 1, function ()
        Comms.Message ('Welcome to Pioneer!')
    end)
end

Event.Register("onGameStart", onGameStart)

Event arguments

As mentioned before onShipDocked passes two arguments to the function. A reference to the ship and a reference to the spacestation.

local onShipDocked = function (ship, station)

Through these arguments we also get access to some of the 'ship' and 'station' methods without having to include any modules. 'ship:IsPlayer()' is for free. Unlimited power is now at your fingertips! 'Comms.Message' takes a second argument for the sender of the message. 'station.label' gives us the name of the space station.

Comms.Message ("Congratulations! Your ship has been upgraded for free!", station.label)
ship:SetShipType('xylophis')