These libraries are loaded automatically and are always available (except the function `math.randomseed` -- it is called by the engine on startup and not available from scripts).
Loading libraries with ``require('library_name')`` is allowed, but limited. It works this way:
1. If `library_name` is one of the standard libraries, then return the library.
2. If `library_name` is one of the built-in `API packages`_, then return the package.
3. Otherwise search for a Lua source file with such name in :ref:`data folders <Multiple data folders>`. For example ``require('my_lua_library.something')`` will try to open the file ``my_lua_library/something.lua``.
Any object that exists in the game world and has a specific location. Player, actors, items, and statics are game objects.
Record
Persistent information about an object. Includes starting stats and links to assets, but doesn't have a location. Game objects are instances of records. Some records (e.g. a unique NPC) have a single instance, some (e.g. a specific potion) may correspond to multiple objects.
..note::
Don't be confused with MWSE terminology. In MWSE game objects are "references" and records are "objects".
An area of the game world. A position in the world is a link to a cell and X, Y, Z coordinates in the cell. At a specific moment in time each cell can be active or inactive. Inactive cells don't perform physics updates.
Lua scripts that are not attached to any game object and are always active. Global scripts can not be started or stopped during a game session. Lists of global scripts are defined by `omwscripts` files, which should be :ref:`registered <Lua scripting>` in `openmw.cfg`.
Lua scripts that are attached to some game object. A local script is active only if the object it is attached to is in an active cell. There are no limitations to the number of local scripts on one object. Local scripts can be attached to (or detached from) any object at any moment by a global script. In some cases inactive local scripts still can run code (for example during saving and loading), but while inactive they can not see nearby objects.
A specific kind of local scripts; *player script* is a local script that is attached to a player. It can do everything that a normal local script can do, plus some player-specific functionality (e.g. control UI and camera).
This scripting API was developed to be conceptually compatible with `multiplayer <https://github.com/TES3MP/openmw-tes3mp>`__. In multiplayer the server is lightweight and delegates most of the work to clients. Each client processes some part of the game world. Global scripts are server-side and local scripts are client-side. Because of this, there are several rules for the Lua scripting API:
1. A local script can see only some area of the game world (cells that are active on a specific client). Any data from inactive cells can't be used, as they are not synchronized and could be already changed on another client.
2. A local script can only modify the object it is attached to. Other objects can theoretically be processed by another client. To prevent synchronization problems the access to them is read-only.
3. Global scripts can access and modify the whole game world including unloaded areas, but the global scripts API is different from the local scripts API and in some aspects limited, because it is not always possible to have all game assets in memory at the same time.
4. Though the scripting system doesn't actually work with multiplayer yet, the API assumes that there can be several players. That's why any command related to UI, camera, and everything else that is player-specific can be used only by player scripts.
How to run a script
===================
Let's write a simple example of a `Player script`:
It works only with existing ``*.lua`` files that are not packed to any archives. Adding new scripts or modifying ``*.omwscripts`` files always requires restarting the game.
`Starting a script` means that the engine runs the file, parses the table it returns, and registers its interface, event handlers, and engine handlers. The handlers are permanent and exist until the script is stopped (if it is a local script, because global scripts can not be stopped).
`Serializable` value means that OpenMW is able to convert it to a sequence of bytes and then (probably on a different computer and with different OpenMW version) restore it back to the same form.
``openmw_aux.*`` are built-in libraries that are themselves implemented in Lua. They can not do anything that is not possible with the basic API, they only make it more convenient.
Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can override them, but it is not recommended.
orig.doSomething(x, y) -- calls the original `doSomething`
-- WRONG! Would lead to an infinite recursion.
-- interfaces.SomeUtils.doSomething(x, y)
end,
}
}
A general recomendation about overriding is that the new interface should be fully compatible with the old one.
So it is fine to change the behaviour of `SomeUtils.doSomething`, but if you want to add a completely new function, it would be
better to create a new interface for it. For example `SomeUtilsExtended` with an additional function `doSomethingElse`.
Using the interface:
..code-block:: Lua
local interfaces = require('openmw.interfaces')
local function onUpdate()
interfaces.SomeUtils.doSomething(2, 3)
end
return { engineHandlers = {onUpdate = onUpdate} }
The order in which the scripts are started is important. So if one mod should override an interface provided by another mod, make sure that load order (i.e. the sequence of `lua-scripts=...` in `openmw.cfg`) is correct.
Timers are in the :ref:`openmw.async <Package openmw.async>` package.
They can be set either in game seconds or in game hours.
-`Game seconds`: the number of seconds in the game world (i.e. seconds when the game is not paused), passed from starting a new game.
-`Game hours`: current time of the game world in hours. The number of seconds in a game hour is not guaranteed to be fixed.
When the game is paused, all timers are paused as well.
When an object becomes inactive, timers on this object are not paused, but callbacks are called only when the object becomes active again.
For example if there were 3 timers with delays 30, 50, 90 seconds, and from the 15-th to the 65-th second the object was inactive, then the first two callbacks are both evaluated on the 65-th second and the third one -- on the 90-th second.
There are two types: *reliable* and *unsavable* timers.
Reliable timer
--------------
Reliable timers are automatically saved and restored when the game is saved or loaded.
When the game is saved each timer record contains only name of a callback, the time when the callback should be called, and an argument that should be passed to the callback.
The callback itself is not stored. That's why callbacks must be registered when the script is initialized with a function ``async:registerTimerCallback(name, func)``.
`Name` is an arbitrary string.
An example:
..code-block:: Lua
local async = require('openmw.async')
local teleportWithDelayCallback = async:registerTimerCallback('teleport',
`openmw.query` contains base queries of each type (e.g. `query.doors`, `query.containers`...), which return all of the objects of given type in no particular order. You can then modify that query to filter the results, sort them, group them, etc. Queries are immutable, so any operations on them return a new copy, leaving the original unchanged.
`openmw.world.selectObjects` and `openmw.nearby.selectObjects` both accept a query and return objects that match it. However, `nearby.selectObjects` is only available in local scripts, and returns only objects from currently active cells, while `world.selectObjects` is only available in global scripts, and returns objects regardless of them being in active cells.
**TODO:** describe how to filter out inactive objects from world queries
-`:orderBy(field)` and `:orderByDesc(field)` sort the result by the `field` argument. Sorts in descending order in case of `:orderByDesc`. Multiple calls can be chained, with the first call having priority. (i. e. if the first field is equal, objects are sorted by the second one...) **(not implemented yet)**
-`:groupBy(field)` returns only one result for each value of the `field` argument. The choice of the result is arbitrary. Useful for counting only unique objects, or checking if certain objects exist. **(not implemented yet)**