Lua in ten minutes
Just enough Lua to read and write Underlives content, for people who have never programmed.
Scenes, actions, stats and most data files are written in Lua, a small language designed to be embedded in programs like games. Underlives uses Lua 5.3. You need very little of it to write content.
Values
lualocal name = "Mia" -- text (a "string"), in double or single quotes
local age = 27 -- a number; 27 and 27.5 are both numbers
local married = false -- true or false
local nothing = nil -- "no value"
local creates a variable that exists only in the block where it is written. Use it for everything inside a scene.
-- starts a comment: the rest of the line is ignored.
Text
lualocal line = "Hello, " .. name .. "!" -- .. joins text
local n = "You have " .. tostring(3) .. " messages"
Tables
A table is Lua's only data structure. It is both a list and a record.
lualocal places = { "cafe", "bar", "park" } -- a list; places[1] is "cafe" (lists start at 1)
local stat = { STAT_ID = "mood", STAT_MAX = 100 } -- a record; stat.STAT_ID is "mood"
Most Underlives files are one big table: the file says return { ... } and the game reads the fields.
Decisions
luaif age < 18 then
Game:Narrate("Too young.")
elseif married then
Game:Narrate("Already taken.")
else
Game:Narrate("Why not?")
end
Comparisons: == equal, ~= not equal, <, >, <=, >=. Combine them with and, or, not.
In Lua, only false and nil are false. 0 and "" count as true. if Player:getVar("met") then is true even when the value is 0; compare explicitly: if Player:getVar("met") == 1 then.
Loops
luafor i = 1, 3 do
Game:Narrate("Knock " .. i)
end
for index, place in ipairs(places) do
Game:Log(place)
end
while Player:getStat("energy") > 20 do
Player:modifyStat("energy", -10)
end
Functions
lualocal function greet(person)
return "Hi, " .. person:name()
end
Game:Narrate(greet(Actor))
Calling the game: the colon
Game objects are called with a colon, which passes the object itself along:
luaGame:Narrate("text") -- right
Player:modifyStat("mood", 5)
Game.Narrate("text") -- wrong: the function never learns which object it was called on
The objects you will use most:
| Object | What it is |
|---|---|
Game |
The scene and the world: dialogue, choices, time, finding people, story state |
Player |
The player, as a person |
Actor, and any person you look up |
Other people |
ModGame |
The same as Game, available in libraries and UI scripts |
Function names are not case-sensitive: Game:SceneStart(), Game:sceneStart() and Game:scenestart() are the same call. The legacy content mixes spellings; pick one and stick to it.
What a mod cannot do
Players install mods with one click, so mod code runs in a sandbox. It cannot touch the player's computer, and one mod cannot break the game for the others.
- Available: the Lua basics (
pairs,ipairs,pcall,tostring,setmetatableon your own tables,loadof text), and thestring,table,math,coroutineandutf8libraries. - Not available:
io,os,debug,package,require,dofile,loadfile,collectgarbage,rawset,string.dump, andloadof compiled (binary) chunks. To share code between files, put it in your mod'sLua/folder: every file there is loaded before any scene runs. - The game's names are the game's:
People.find = ...,People = nil,string.rep = nilorActorMethods = {}raise an error ("belongs to the game"). Add your own globals and your ownActorMethods.myMethodfreely. - Data files are only tables: a
.ulstat,.ulaction,.ulroomand the like can uselocalvalues,..and its own local functions, but no game function and no library at all. - Limits: a scene that runs too long without pausing for the player (an endless loop), or a script that allocates too much memory, stops with an error instead of freezing the game.
Where to learn more
- Programming in Lua, the official book (the first edition is free online).
- The base game itself: every scene in
Content/Modules/ul_Baseis readable Lua.