Heroes 5 Wiki

Overview[]

Lua is a lightweight, high-level, multi-paradigm programming language designed primarily for embedded use in applications. Lua is cross-platform, since the interpreter of compiled bytecode is written in ANSI C, and Lua has a relatively simple C API to embed it into applications[1].

Heroes Might and Magic 5 editor framework uses LUA 4.0 for manipulating and customizing adventure map, combat and town environments.

Variables[]

As LUA is an embedded language it is similar to a main () function. The concept of a variable in LUA is the same as in other algorithmic languages - a sequence of Latin letters, underscores, and numbers, starting with a letter. Variable names are case sensitive so - dummy1 and Dummy1 are considered two different variables.

Variable types are assigned dynamically. The type of variable is determined depending on the context of its use. This means that sometimes we have to be careful what to expect from a variable, for example:

interval = GetGameVar ("interval")
interval = interval + 0             --converting string to integer
sleep(interval)

The GetGameVar function returns a string while sleep takes a number as a parameter. To adjust that an additional line is added that sums the variable with zero. This is needed only to show the interpreter that in the future we will use the variable interval as a number. If this line is removed, when executing sleep, interpreter will give error about type mismatch. This approach has a lot of advantages, for example, you can write something like

local currentSign = 2
...
local lastSign = "sign" .. (CurrentSign-1)       -- lastSign value is "sign1
local newSign = "sign" .. (CurrentSign + 1)      -- newSign  value is "sign3
local sign = "sign" .. CurrentSign               -- Sign     value is "sign2
SetObjectEnabled (newSign, false)
SetObjectEnabled (lastSign, true)
ChangeHeroStat (HERO_NAME, STAT_EXPERIENCE, 500 * currentSign)

In this example, the concatenation operation - ".." is used to form the name of objects.

Simple data types[]

There are four simple data types that a variable can be assigned

  • a number (a regular double)
  • a string (a sequence of octets in single or double quotes)
  • a nil
  • a logical operator (true \ false).

True and False do not exist as real logical operators in this interpreter. They are defined by constants in the file /scripts/advmap-startup.lua. Here is a snippet of the definition.

-- Logical constants
--
true = not nil
false = nil


Definitions inside this file are taken into account only on the adventure map. For combat scripts, these constants are invalid and should not be considered.

Complex data types[]

Tables[]

Table can be considered in between structures and arrays. In general, tables are very similar to familiar arrays, except:

  1. they can include heterogeneous elements
  2. they can be indexed not only by numbers. 

For example, the following entry is valid:

cam_switch = {
  {cam = 1, from = 0, to = 3000},
  {cam = 2, from = 3000, to = 5000},    
  {cam = 1, from = 5000, to = 10000},  
  {cam = 3, from = 10000, to = 16000},
  {cam = 1, from = 16000, to = 25000},
}

Syntax that can be used to call element from the table:

cam_switch[2].cam    -- invokes the second array element and gets cam value which is 2.

Another syntax variation to request the same data:

cam_switch[2]["cam"]

LUA table indexing begins from 1. This is valid for all user created tables. Though it should be noted that certain Heroes 5 specific LUA commands (for example GetPlayerHeroes) return data as table structure but unlike default standards indexing begin from 0. There is no reasonable explanation regarding this distinction but it has to be remembered.

Function pointers[]

Or pointer to a function is a variable that changes its pointer to the value of another variable. For example:

x, y = y, x

What will happen is that X will get the value of Y and vice versa. It is advisable to use easier to read code expressions such as:

local x, y = GetGameVar(some_var), -1

or even better:

local x = GetGameVar(some_var)
local y = -1

Execution control statements[]

There is a standard set of code execution control statements:  

If[]

Conditional operator

if exp then 
  block elseif exp then  block 
else 
  block 
end

Example:

if sID == "n1male" then
nUnitObjectID = 1
elseif sID == "n1female" then
nUnitObjectID = 2
else
nUnitObjectID = 3
end

The expression in the above examples can be of any type. But only false and nil are treated as false. Everything else will be considered true. Therefore, practitioners of C and C ++ should be careful with expressions like:

if (0) then ... end

The expression in the above example will always be taken as true, and the code block will be executed.

While loop []

Executes a block while expression condition is true.

Syntax:

while exp do 
   block 
end

Example:

while IsTutorialMessageBoxOpen() do
   sleep(1)
end

Repeat loop[]

Executes block while exp condition is false.

Syntax:

repeat
   block 
until exp

Example:

repeat
   Sleep(1)
until (GetCurrentPlayer() ~ = PLAYER_2)

For loop []

There are two syntax formats. First form syntax:

for var = value, limit, step do
  block
end

Example first form:

for i = 1, 11, 1 do
RemoveObject('inferno' .. i)
end

Iteration begins with var = value and the block is run until var = limit. On each iteration var is increased by amount of step, unless step is absent, then var is increased by default value of 1. It is impossible to change var by any means when inside the loop. In connection with the step remark, it is possible to write the above example more simply:

for i = 1, 11 do
RemoveObject ('inferno' .. i) end

The second form of the operator is designed to work with tables.

Second form syntax:

for index, var in explist1 do
  block
end

Example second form:

local heroes = GetPlayerHeroes (PLAYER_1);
for i, hero in heroes do
if GetTownHero ('Bobruisk') ~ = hero then
print (i, hero);
end end

Iteration begins with i = counter, not necessary a numeric one; hero = first element in table heroes.

At each iteration the counter i goes up by one value while hero becomes the next element in the table. The cycle rotates once for each element of the table.

Inside loops, you can use the break and return statements. These operators should be the last in the block. If you want to get around this condition (usually such a need arises during debugging), you must use the do break end and do return end constructs.

Note: Unlike other languages there is no goto operator nor there is a concept of labels.

Operators[]

Comparison operators[]

Comparison operations are no different from those in other languages. Unless the usual form of recording an operation is "not equal".

  • equal - ==
  • not equal - ~=
  • less than - <
  • greater than - >
  • less or equal - <=
  • greater or equal - >=

The result from these operators is either true or false. If the type of the compared variables does not match, then the result of the comparison will always be false. The following block is always false.

 If ("0" == 0) then
   ...
end

Logical operators[]

There are three type of logical operators:

  • AND - returns true only if both statements are true
  • OR - returns return true if either of the two statements is true
  • NOT - reverses statement output /if true, returns false and vice versa/.

Important note is that the order of calculation of operators cannot be relied upon (which is not consistent with the heroes 5 LUA manual). The best practice is to put statements in brackets in the order you want them executed. Otherwise, at one point, you may find that the results of the expression below are not the one that are expected because the interpreter mixes OR and AND statements not in the proper order.

if objectname == "HIREPERS1" or objectname == "HIREPERS2" and NextBlockOrderID == 2 or NextBlockOrderID == 3 then
...
end

Functions[]

Defining function in LUA is as follows:

function funcname ([list_of_parameters]) 
  block
end

Functions can return one or more values at once. For example, the following construction is valid:

function f()
return 1,2,3
end a, b, c = f()

A special property related to functions is that returning a function within a function will always return nil. It is best illustrated by the following example. Consider these two examples:

Example 1 - Will always return nil.

function GetCreatures (side)
return (GetUnits(side, CREATURE))
end

Example 2 - Will return the value returned from GetUnits function

function GetCreatures (side)
local temp = GetUnits (side, CREATURE)
return(temp)
end

Even though the functions are identical the result of the first one is always nil. This is caused by an issue in LUA interpreter implementation, therefore the general rule is that any function should return result through a local variable.[2]