Lua introduction for confirmed developers
Generalities about Lua
Lua, not LUA
This is a Portuguese word meaning "moon", not an acronym.
Lua design
It is useful to remind what Lua was designed for, as long with the fact that it dates from 1993:
- Easy to embed and to interface from other languages.
- Highly portable. The con is a lightweight and somewhat lacking API.
- A small set of features allowing extensible semantics to support multiple paradigms, from functional programming to object programming. For example, while inheritance is not natively supported, it can be easily set up through metatables. The same features allow events in civ5 to behave both as functions and objects with Add and Remove methods.
- A beginners-friendly syntax.
Performances
Lua is an interpreted and dynamically typed language. So as usual with those kind of languages expect a 10 to 100 order of magnitude in performances decrease when compared to compiled and statically type languages like C, C++ or C#. While it is possible for Lua-like languages to drastically improve the performances through more or less sophisticated means (parse once, Jit compiler, type inference to statically resolve code), no such solution has been used in Civ5 (aside, probably, of the caching). And while an external Jit library exists, it causes compatibility problems with most mods using Lua.
That being said, Lua is still pretty good for this family of languages, better than the Python implementation from Civ4 for example. And all in all, it is fast enough for most of what modders want to achieve without the need to compromise code elegance, readability and maintainability for performances. Anyway, remember: "premature optimization is the root of all evil [ 97% of the time ]" (Donald Knuth).
Syntax cheatsheet
Basics
-- Literals
local x = 5
local x = 5.0
local x = nil -- A nil value is the same as an undefined value
local x = true
local x = false
-- Strings
local x = "five" -- String
local x = 'five' -- String (no difference with "")
local x = "five\n" -- Escape characters with "\".
local x = [[Five is a number.
Did you know?]] -- Multi-line string: equivalent to "Five is number.\nDid you know?"
-- Globals versus locals
x = 5 -- A global variable
local x = 5 -- A local variable
-- Operators :
local x = a or b
local x = a and b
local x = "hello ".."world" .. "!" -- concatenation: two dots
local x = "five is "..5 -- concatenation with implicit cast: "five is 5".
local x = (a + b - d) * f ^ g % h -- ^ is exponentiation, % is modulo
local x = a == b -- equality
local x = a ~= b -- inequality
local x = ((a <= b) == not (a > b)) == ((a >= b) == not (a < b))
-- No increment operator (++ --), no binary-assign operator (+= *=), no ternary operator (? :), no coalescence operator (??)
Tables and multiple assignments
-- Table declaration
SomeTable = {}
SomeArray = { "abc", "def" }
-- 1 => "abc", 2 => "def"
SomeComplexTable = { name = "John", surname = "Doe", age = 25, 43 }
-- "name" => "John", "surname" => "Doe", "age" => 25, 1 => 43.
-- Member access (all equivalent)
SomeTable.SomeMember = 5
SomeTable["SomeMember"] = 5
-- Multiple assignment
local x, y = "abc", "def"
local x, y = unpack(SomeArray) -- equivalent to the former statement since SomeArray contains "abc" and "def".
Functions
-- Functions declarations (all equivalent)
function HelloWorld(a, b) print("Hello world") end
HelloWorld = function(a, b) print("Hello world") end
-- Functions are first-class objects and can be assigned like any other value!
-- Methods declarations (all equivalent)
SomeTable.HelloWorld(a, b) print("Hello world") end
SomeTable.HelloWorld = function(a, b) print("Hello world") end
-- Instanced methods declarations (all equivalent)
SomeTable.HelloWorldInstanced(self, a, b) print(self.SomeMember) end
SomeTable:HelloWorldInstanced(a, b) print(self.SomeMember) end
-- Functions and methods calls
HelloWorld(a, b)
SomeTable.HelloWorld(a, b)
-- Instanced methods calls (all equivalent)
SomeTable:HelloWorldInstanced(a, b)
SomeTable.HelloWorldInstanced(SomeTable, a, b)
-- Variable arguments
function SomeFunc(a, b, ...)
print(a)
print(b)
for i, v in ipairs(arg) do -- arg is a keyword for a table containing the variable arguments
print(v)
end
end
SomeFunc(1, 2, 3, 4, 5) -- prints 1, 2, 3, 4, 5
SomeFunc(1, 2) -- prints 1, 2
Control flow
-- If
if name == "Wu Zetian" then
print("Hello China")
elseif name == "Napoleon" then -- "elseif", not "else if". Beware C developers!
print("Hello France")
else -- no "then" after "else"!
print("Hello unknown")
end
-- For loop (with counter)
for i = 1, 8 do print(i) end -- Prints 1, 2, 3, 4, 5, 6, 7, 8, 9
for i = 1, 8, 2 do print(i) end -- Prints 1, 3, 5, 7
-- For loop (with iterator)
for playerID, pPlayer in pairs(Players) do -- Prints 1 Wu Zetian, 2 Napoleon, ...
print(playerID, pPlayer:GetName())
end
for plot in Plots() do -- Prints 0 0, 0 1, 0 2, ...
print(plot:GetX(), plot:GetY())
end
-- While (i will be 5 in the end
while i < 5 do
i = i + 1
end
--Repeat until (i will be 5 in the end)
repeat
i = i + 1
until i == 5
Specificities
Come back later!