Introduction

Pragma uses the programming language Lua 5.1 (with LuaJIT) for its scripting engine. If you're new to Lua, the PIL is a good starting point.

Any text editor suited for programming (Notepad++, Sublime, Visual Studio Code, etc.) can be used to write Lua scripts, however using the ZeroBrane IDE is highly recommended due to its powerful debugging capabilities specialized for Lua development.

Pragma has one Lua instance per game state. The game state is split into a serverside portion and a clientside portion. The client generally handles things like rendering and device inputs, while the server handles AI, game logic, etc. Some components (like physics) are simulated on both sides and may or may not be synchronized.

The game states (along with the Lua instances) are initialized once you're loading a map, which means Lua commands and scripts are not available in the main menu. Which game states are available depends on the situation:

  • Starting a local game/server: Both game states are initialized.
  • Connecting to a server: Only the clientside game state is initialized.
  • Starting a dedicated server: Only the serverside game state is initialized.

 

There are many ways to execute Lua code, but the easiest one is via console commands:

  • lua_run <code> / lua_run_cl <code>: Executes the specified Lua code serverside / clientside (if the game state is available)
  • lua_exec <code> / lua_exec_cl <code>: Executes the specified Lua file serverside / clientside (if the game state is available)

These Lua instances are completely independent and cannot communicate directly with each other (except via net messages). Any variables or functions you define in the serverside state will not be accessible in the clientside state and vice versa. Here are a few examples using the lua_run and lua_run_cl console commands:

lua_run var = "This is a serverside variable" -- Defines the global serverside variable 'var'
lua_run_cl print(var) -- Prints 'nil' (indicating an undefined variable) to the console, because 'var' was not defined clientside
lua_run print(var) -- Prints 'This is a serverside variable' to the console, because we're back in the serverside instance

 

You can also place Lua-files in one of the following autorun directories, in which case they will automatically be executed when the game is started:

  • "lua/autorun/<fileName>.lua": The Lua-file will be executed for both the serverside and clientside Lua instances.
  • "lua/autorun/server/<fileName>.lua": The Lua-file will be executed serverside.
  • "lua/autorun/client/<fileName>.lua": The Lua-file will be executed clientside.

API Documentation

You can find a list of all available Lua classes, libraries and functions in the API documentation. Some functions are only available serverside, some only clientside, which is indicated by the colored bars in front of the function names in the navigation:

api_color_codes.png

Anything with a blue bar is available serverside, and anything with a purple bar clientside. For instance, the client cannot trigger a map change, so game.change_map() can only be executed in the serverside Lua instance. If you want to know how a particular function is implemented, you can click the github-logo.png-logo, which will direct you to the function implementation on the Pragma GitHub repository.

If you have configured ZeroBrane, all of the functions should also appear in the auto-complete list when typing the name of a library or class:

Zerobrane_autocomplete.gif

In-Engine Documentation

In addition to the online API documentation, you can also use the lua_help console command to retrieve the information ingame:

lua_help.png

 

Lua errors will also use this documentation to provide you with suggestions on how to correct the error:

lua_error_help.png

 

Hello World

Creating a Lua script is very straight forward. Simply create a new text-file and add the following line:

print("Hello World")

Then save that file as "hello_world.lua" to "Pragma/lua/". To execute the script, simply start a new game and run the console command lua_exec_cl hello_world.lua and you should see the message "Hello World" printed in the console.

Any Lua files that have been executed at least once are cached by the Engine and automatically reloaded when changed in the future (until you close the game). This means if you make any changes to the file, all you have to do is save it, and it will automatically be executed again (i.e. you don't have to run lua_exec_cl anymore). You can test this by changing the print message in your Lua-script and then saving it, and it should automatically print your new message to the console.

Addons

In general, putting Lua scripts in the main Lua directory ("Pragma/lua/") should be avoided to avoid clutter and naming conflicts and an addon should be used instead:

  • Create a new directory called "hello_world" in "Pragma/addons/"
  • Create a new "lua" directory inside it
  • Move your "hello_world.lua" to "Pragma/addons/hello_world/lua/hello_world.lua"

Pragma will automatically mount the addon, and will treat all asset files within the addon as if they were in the main Pragma directory. This means that despite the file being in a different location, you can still use lua_exec_cl hello_world.lua to execute the file.

 

Examples

 

Creating a timer

Lua state: Any

-- Print "Hello" every 2 seconds
local numberOfRepetitions = -1 -- -1 = infinite
local timer = time.create_timer(2.0,numberOfRepetitions,function()
    print("Current game time:",time.cur_time())
end)
timer:Start()

-- Remove the timer after 15 seconds using a second timer
time.create_simple_timer(15,function()
    util.remove(timer)
end)
 
Registering a custom console command

Lua state: Any

console.register_command("custom_command",function(pl,...)
    if(pl ~= nil) then
		print("Command was initiated by player ",pl:GetEntity():GetName())
    end
    print("Command arguments: ",...)
    
    local arg1,arg2,arg3 = ...
    -- Do something with arguments
end)
 
Creating a GUI element

Lua state: Client

local rect = gui.create("WIRect")
rect:SetColor(Color.Red)
rect:SetPos(Vector(32,32))
rect:SetSize(256,65)

local text = gui.create("WIText",rect)
text:SetColor(Color(0,255,64,255))
text:SetPos(5,5)
text:SetText("Hello World")
text:SizeToContents()
 
Classes and class inheritance

Lua state: Any

-- Define class 'Car' in 'util' library
util.register_class("util.Car")
-- The constructor for the 'Car' class, which allows us to create an instance of the class like so:
-- local carInstance = util.Car("Name of the car")
function util.Car:__init(name)
	self.m_name = name
	self.m_color = Color.Red
end

function util.Car:SetName(name) self.m_name = name end
function util.Car:GetName() return self.m_name end

function util.Car:SetColor(color)
	self.m_color = color
	self:OnColorChanged(color)
end
function util.Car:GetColor() return self.m_color end

-- These can be overwritten by derived classes
function util.Car:GetWheelCount() return 4 end
function util.Car:OnColorChanged(newColor)
	print("Color of car '" .. self:GetName() .. "' has been changed to " .. tostring(newColor) .. "!")
end

-------

-- Define class 'Truck' and derive from base class 'Car'
util.register_class("util.Truck",util.Car)
function util.Truck:__init(name)
	util.Car.__init(self,name) -- Base constructor has to be called, note the use of '.' instead of ':' for calling base functions
end

function util.Truck:GetWheelCount() return 6 end -- Overwrites the base function when called on a truck instance

function util.Truck:OnColorChanged(color)
	util.Car.OnColorChanged(self,color) -- Call base function
	debug.beep() -- Play a beep sound after the message has been printed
end

-------

-- Create car object
local car = util.Car("Car Name")
car:SetColor(Color.Lime)
print("Number of car wheels: " .. car:GetWheelCount())

-- Create truck object
local truck = util.Truck("Truck Name")
truck:SetColor(Color(0,255,128,255))
print("Number of truck wheels: " .. truck:GetWheelCount())
 
Respawn all players

Lua state: Server

for ent in ents.iterator({ents.IteratorFilterComponent(ents.COMPONENT_PLAYER)}) do
	local playerC = ent:GetComponent(ents.COMPONENT_PLAYER)
	playerC:Respawn()
end

 

Pragma Filmmaker

The Pragma Filmmaker is implemented as a Pragma addon written in Lua, you can find the source code on GitHub: https://github.com/Silverlan/pfm