version from 09.04.2017, written by AgentSmith
WSE/LUA uses a modified version of LuaJIT 2.0.4, which is based on and ABI compatible to Lua 5.1
The game table is a predefined table that allows you access to the game from lua.
To call operations, use game.operation_name(arg1, arg2, ...)
Example: game.display_message("test string")
String and pos arguments are passed to the game via the games registers (overwritting them), starting from reg[128] and counting downwards.
Notice that the argument type matters. For example, if an operation expects an integer, you cannot pass a string that contains a number.
Lhs operations ("left hand side", operations that return a value) are written as
result = game.lhs_operation(lhs_arg, arg1, arg2, ...)
While most Lhs operations don't require an actual value for lhs_arg (in those cases it's strictly a return variable, for example player_get_gold), some (for example val_add) do, so you have to specify it anyway.
Return values are:
boolDidNotFail, intError
intResult, intError
boolDidNotFail, intResult, intError
intError
= game.cf_operation(arg1, arg2, ...) for can_fail operations
= game.lhs_operation(arg1, arg2, ...) for lhs operations
= game.cf_lhs_operation(arg1, arg2, ...) for operations which are both can_fail and lhs
= game.operation(arg1, arg2, ...) for all other operations
I'm not sure what the error code signalizes, however it should be 0 if there was no error.
To access registers, use game.reg[n] , game.sreg[n] and game.preg[n]
Example:
game.reg[0] = 123
local i = game.reg[0]
game.sreg[0] = "test string"
local s = game.sreg[0]
game.preg[0] = game.pos.new()
local pos = game.preg[0]
As of the current version, registers are copied by value instead of by reference. So doing things like:
game.preg[0]:rotX(30)
Does not change the actual game register. This is likely going to be improved in a future version. What you have to do instead for now is:
local p = game.preg[0]
p:rotX(30)
game.preg[0] = p
To add triggers, use game.addTrigger(strTemplateId, numCheckTime, numDelayTime, numRearmTime, funcConditionsCallback, [funcConsequencesCallback])
These work exactly like module system triggers. Times can be float values or values from header_triggers.py (the numbers, string names are not supported yet), just like you would do it in the MS. The callbacks must return a boolean. If conditionsCallback returns false, consequencesCallback will not be executed.
This function is fairly expensive, as it involves copying the entire array of triggers and then adding the new one.
Example:
function condCB()
doStuff()
return true
end
game.addTrigger("mst_multiplayer_dm", 1, 0, 0, condCB)
addTrigger returns an integer that is the index of the added trigger.
You can also remove triggers by using game.removeTrigger(strTemplateId, intTriggerIndex). If intTriggerIndex is negative, the trigger with (numTriggers + intTriggerIndex) gets removed (e.g. to remove the last trigger, use -1).
To use iterators (ty_for_agents, try_for_players, ...), use the provided iterator functions game.partiesI, game.agentsI, game.propInstI, game.playersI
These work exactly like module system iterators and take the same arguments.
Example:
for curAgent in game.playersI(true) do --skip server
foo(curAgent)
end
There is a limit of 256 iterators that can exist at the same time. This is not an issue when using for-loops, since the iterator gets freed when the loop ends. You should however keep it in mind when initializing the iterators manually (see Other WSE LUA API functions). An iterator gets freed as soon as the call to game.IterNext returns nil.
To work with positions, the following "classes" are provided: game.rotation, game.pos and vector3 (see under miscellaneous)
Keep in mind that these don't need the fixed point multiplier that the module system requires you to use.
The MS multiplier is essentially a way of specifying the current unit of length.
MS Example:
set_fixed_point_multiplier(1),
position_get_x(":x", 0), #get pos0_x in meters
set_fixed_point_multiplier(100),
position_get_x(":x", 0), #get pos0_x in centimeters
Consider the lua counterpart to "always have a fixed point multiplier of 1".
game.rotation
game.rotation.new([obj]) - constructor. obj can be used to specify initial values.
game.rotation.prototype
s = vector3.new({x = 1}) --x axis
f = vector3.new({y = 1}) --forwards/y axis
u = vector3.new({z = 1}) --up/z axis
function getRot(self) --returns vector3.new({z = yaw, x = pitch, y = roll})
function rotX(self, angle) --rotate around x axis, angle in degrees
function rotY(self, angle) --rotate around y axis, angle in degrees
function rotZ(self, angle) --rotate around z axis, angle in degrees
function rotate(self, rotVec3) --rotate around all axis, zxy order, angle in degrees
game.pos
game.pos.new([obj]) - constructor. obj can be used to specify initial values.
game.pos.prototype
o = vector3.new() --position
rot = game.rotation.new() --rotation
--see game.rotation.prototype for these
function getRot(self)
function rotX(self, angle)
function rotY(self, angle)
function rotZ(self, angle)
function rotate(self, rotVec3)
Example:
local pos0 = game.preg[0]
local newRot = game.rotation.new(pos0.rot)
local newPos = game.pos.new({rot = newRot})
newPos:rotX(45)
newPos:rotate({x = 90, z = 180})
game.getScriptNo(script_name), use in conjunction with game.call_script(scriptNo, ...)
Example:
local no = game.getScriptNo("game_start")
game.call_script(no)
game.getCurTemplateId(), returns the id/name of the current mission template. Note that no template will be loaded at the time main.lua is executed.
Other WSE LUA API functions - these could be useful for advanced users, but you should get along fine without ever using them.
game.execOperation(strOperationName, arg1, arg2, ....) - see operations
vector3.prototype
x = 0
y = 0
z = 0
function len(self) --returns the length of the vector
vector3.new([obj]) - constructor. obj; can be used to specify initial values.
vector3 operators + - * ==
Example:
local vecA = vector3.new({x = 60, y = 30})
local vecB = vector3.new()
game.display_message(tostring(vecA:len())) --67.08...
if vecA == vecB then
--will not happen
end
vecA = vecA * vecB --{60*0, 30*0, 0*0}
if vecA == vecB then
--will happen
end
Changes include:
Disabled package.loadlib, package.cpath, io.popen. os.execute, os.getenv, os.tmpname, ffi library, loading bytecode (can be exploited)
Restrict dofile, loadfile and all IO operations to the lua directory