mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-29 05:17:58 +03:00
Update OpenMW Lua documentation
This commit is contained in:
parent
cc7dbabd19
commit
4eb5841c60
21 changed files with 2111 additions and 481 deletions
|
@ -17,426 +17,69 @@ Engine handler is a function defined by a script, that can be called by the engi
|
|||
| onUpdate(dt) | | Called every frame if game not paused. `dt` is the time |
|
||||
| | | from the last update in seconds. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onSave() -> data | Called when the game is saving. |
|
||||
| onSave() -> data | | Called when the game is saving. May be called in inactive |
|
||||
| | | state, so it shouldn't use `openmw.nearby`. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onLoad(data) | Called on loading with the data previosly returned by onSave|
|
||||
| onLoad(data) | | Called on loading with the data previosly returned by |
|
||||
| | | onSave. During loading the object is always in inactive. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| **Only for global scripts** |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onNewGame() | New game is started |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onPlayerAdded(player) |Player added to game world. The argument is a `Game object`_.|
|
||||
| onPlayerAdded(player) |Player added to game world. The argument is a `Game object`. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onActorActive(actor) | Actor (NPC or Creature) becomes active. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| **Only for local scripts** |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onActive() | | Called when the object becomes active (either a player |
|
||||
| | | came to this cell again, or a save was loaded). |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onInactive() | | Object became inactive. Since it is inactive the handler |
|
||||
| | | can not access anything nearby, but it is possible to send|
|
||||
| | | an event to global scripts. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onConsume(recordId) | | Called if `recordId` (e.g. a potion) is consumed. |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| **Only for local scripts attached to a player** |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
| onKeyPress(symbol, modifiers) | | Key pressed. `Symbol` is an ASCII code, `modifiers` is |
|
||||
| | | a binary OR of flags of special keys (ctrl, shift, alt). |
|
||||
+----------------------------------+-------------------------------------------------------------+
|
||||
|
||||
.. _Game object:
|
||||
|
||||
Game object reference
|
||||
=====================
|
||||
Packages reference
|
||||
==================
|
||||
|
||||
API packages provide functions that can be called by scripts. I.e. it is a script-to-engine interaction.
|
||||
A package can be loaded with ``require('<package name>')``.
|
||||
It can not be overloaded even if there is a lua file with the same name.
|
||||
The list of available packages is different for global and for local scripts.
|
||||
Player scripts are local scripts that are attached to a player.
|
||||
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Package | Can be used | Description |
|
||||
+=========================================================+====================+===============================================================+
|
||||
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.util <../../lua-api-reference/util.html>`_ | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||
| | | | that don't depend on the game world. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.core <../../lua-api-reference/core.html>`_ | everywhere | | Functions that are common for both global and local scripts |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.async <../../lua-api-reference/async.html>`_ | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.query <../../lua-api-reference/query.html>`_ | everywhere | | Tools for constructing queries: base queries and fields. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.world <../../lua-api-reference/world.html>`_ | by global scripts | | Read-write access to the game world. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.self <../../lua-api-reference/self.html>`_ | by local scripts | | Full access to the object the script is attached to. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.nearby <../../lua-api-reference/nearby.html>`_ | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|`openmw.ui <../../lua-api-reference/ui.html>`_ | by player scripts | | Controls user interface |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|openmw.camera | by player scripts | | Controls camera (not implemented) |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
||||
Game object is a universal reference to an object in the game world. Anything that has a reference number.
|
||||
|
||||
**Can be used on any object:**
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| object:isValid() | | Returns true if the object exists and loaded, |
|
||||
| | | and false otherwise. If false, then every |
|
||||
| | | access to the object will raise an error. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| object:sendEvent(eventName, eventData) | Sends local event to the object. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| object:isEquipped(item) | Returns true if `item` is equipped on `object`. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| object:getEquipment() -> table | | Returns a table `slot` -> `object` of currently|
|
||||
| | | equipped items. See `core.EQUIPMENT_SLOT`. |
|
||||
| | | Returns empty table if `object` doesn't have |
|
||||
| | | equipment slots. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| object:setEquipment(table) | | Sets equipment. Keys in the table are equipment|
|
||||
| | | slots (see `core.EQUIPMENT_SLOT`). Each value |
|
||||
| | | can be either an object or `recordId`. Raises |
|
||||
| | | an error if `object` doesn't have equipment |
|
||||
| | | slots and `table` is not empty. Can be called |
|
||||
| | | only on `self` or from a global script. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| object:addScript(scriptPath) | | Adds new script to the object. |
|
||||
| | | Can be called only from a global script. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| object:teleport(cell, pos, [rot]) | | Moves object to given cell and position. |
|
||||
| | | The effect is not immediate: the position will |
|
||||
| | | be updated only in the next frame. |
|
||||
| | | Can be called only from a global script. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+==================================================+
|
||||
| object.position | vector3_ | Position |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.rotation | vector3_ | Rotation |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.cell | string | Cell |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.type | string | :ref:`Type <Object type>` of the object |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.count | integer | Count (makes sense if holded in a container) |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.recordId | string | Record ID |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.inventory | Inventory | Inventory of an actor or content of a container |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
|
||||
**Can be used if object.type == 'Door':**
|
||||
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+==================================================+
|
||||
| object.isTeleport | boolean | True if it is a teleport door |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.destPosition | vector3_ | Destination (only if a teleport door) |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.destRotation | vector3_ | Destination rotation (only if a teleport door) |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| object.destCell | string | Destination cell (only if a teleport door) |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
|
||||
Object type
|
||||
-----------
|
||||
|
||||
Type is represented as a string. Can be one of:
|
||||
|
||||
- "Activator"
|
||||
- "Armor"
|
||||
- "Book"
|
||||
- "Clothing"
|
||||
- "Container"
|
||||
- "Creature"
|
||||
- "Door"
|
||||
- "Ingredient"
|
||||
- "Light"
|
||||
- "Miscellaneous"
|
||||
- "NPC"
|
||||
- "Player"
|
||||
- "Potion"
|
||||
- "Static"
|
||||
- "Weapon"
|
||||
|
||||
ObjectList
|
||||
----------
|
||||
|
||||
List of game objects. Can't be created or modified by a script.
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
-- Iteration by index
|
||||
for i = 1, #someList do
|
||||
doSomething(someList[i])
|
||||
end
|
||||
|
||||
-- Generic for (equivalent to iteration by index)
|
||||
for i, item in someList:ipairs() do
|
||||
doSomething(item)
|
||||
end
|
||||
|
||||
-- WRONG: for i, item in ipairs(someList) do
|
||||
-- It doesn't work because Lua 5.1 doesn't allow to overload ipairs for userdata.
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| list:ipairs() | Returns an iterator |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| list:select(query) -> ObjectList | Returns a filtered list |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
Object inventory
|
||||
----------------
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| inv:countOf(recordId) -> int | The number of items with given recordId |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getAll(recordId) -> ObjectList_ | All contained items |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getPotions() -> ObjectList_ | All potions from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getApparatuses() -> ObjectList_ | All apparatuses from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getArmors() -> ObjectList_ | All armors from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getBooks() -> ObjectList_ | All books from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getClothing() -> ObjectList_ | All clothing from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getIngredients() -> ObjectList_ | All ingredients from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getLights() -> ObjectList_ | All lights from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getLockpicks() -> ObjectList_ | All lockpicks from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getMiscellaneous() -> ObjectList_ | All miscellaneous items from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getProbes() -> ObjectList_ | All probes from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getRepairKits() -> ObjectList_ | All repair kits from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| inv:getWeapons() -> ObjectList_ | All weapon from the inventory |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
openmw.util
|
||||
===========
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| vector2(x, y) -> vector2_ | Creates 2D vector |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| vector3(x, y, z) -> vector3_ | Creates 3D vector |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| clamp(value, from, to) -> number | Limits given value to the interval [from, to] |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| normalizeAngle(radians) -> number | Adds 2pi*k and puts the angle in range [-pi, pi] |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
vector2
|
||||
-------
|
||||
|
||||
Immutable 2D vector.
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
v = vector2(3, 4)
|
||||
v.x, v.y -- 3.0, 4.0
|
||||
str(v) -- "(3.0, 4.0)"
|
||||
v:length() -- 5.0 length
|
||||
v:length2() -- 25.0 square of the length
|
||||
v:normalize() -- vector2(3/5, 4/5)
|
||||
v:rotate(radians) -- rotate clockwise (returns rotated vector)
|
||||
v1:dot(v2) -- dot product (returns a number)
|
||||
v1 * v2 -- dot product
|
||||
v1 + v2 -- vector addition
|
||||
v1 - v2 -- vector subtraction
|
||||
v1 * x -- multiplication by a number
|
||||
v1 / x -- division by a number
|
||||
|
||||
vector3
|
||||
-------
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
v = vector3(3, 4, 5)
|
||||
v.x, v.y, v.z -- 3.0, 4.0, 5.0
|
||||
str(v) -- "(3.0, 4.0, 4.5)"
|
||||
v:length() -- length
|
||||
v:length2() -- square of the length
|
||||
v:normalize() -- normalized vector
|
||||
v1:dot(v2) -- dot product (returns a number)
|
||||
v1 * v2 -- dot product (returns a number)
|
||||
v1:cross(v2) -- cross product (returns a vector)
|
||||
v1 ^ v2 -- cross product (returns a vector)
|
||||
v1 + v2 -- vector addition
|
||||
v1 - v2 -- vector subtraction
|
||||
v1 * x -- multiplication by a number
|
||||
v1 / x -- division by a number
|
||||
|
||||
openmw.core
|
||||
===========
|
||||
|
||||
+-----------------------+---------------------+---------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+===================================================+
|
||||
| OBJECT_TYPE | Readonly table | Possible :ref:`object type <Object type>` values |
|
||||
+-----------------------+---------------------+---------------------------------------------------+
|
||||
| EQUIPMENT_SLOTS | Readonly table | | Contains keys that can be used in |
|
||||
| | | | `object:getEquipment` and `object:setEquipment`.|
|
||||
+-----------------------+---------------------+---------------------------------------------------+
|
||||
|
||||
``EQUIPMENT_SLOTS`` contains fields: "Helmet", "Cuirass", "Greaves", "LeftPauldron", "RightPauldron",
|
||||
"LeftGauntlet", "RightGauntlet", "Boots", "Shirt", "Pants", "Skirt", "Robe", "LeftRing", "RightRing",
|
||||
"Amulet", "Belt", "CarriedRight", "CarriedLeft", "Ammunition".
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| sendGlobalEvent(eventName, eventData) | Sends an event to global scripts |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| getRealTime() | | The number of seconds (with floating point) |
|
||||
| | | passed from 00:00 UTC 1 Januar 1970 (unix time)|
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| getGameTimeInSeconds() | | The number of seconds in the game world, passed|
|
||||
| | | from starting a new game. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| getGameTimeInHours() | | Current time of the game world in hours. |
|
||||
| | | Note that the number of game seconds in a game |
|
||||
| | | hour is not guaranteed to be fixed. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
openmw.async
|
||||
============
|
||||
|
||||
Timers and coroutine utils.
|
||||
All functions require the package itself as a first argument.
|
||||
I.e. functions should be called with ``:`` rather than with ``.``.
|
||||
|
||||
+-----------------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=====================================================+==================================================+
|
||||
| async:registerTimerCallback(name, func) -> Callback | Registers a function as a timer callback |
|
||||
+-----------------------------------------------------+--------------------------------------------------+
|
||||
| async:newTimerInSeconds(delay, Callback, arg) | | Calls `Callback(arg)` in `delay` seconds. |
|
||||
| | | `Callback` must be registered in advance. |
|
||||
+-----------------------------------------------------+--------------------------------------------------+
|
||||
| async:newTimerInHours(delay, Callback, arg) | | Calls `Callback(arg)` in `delay` game hours. |
|
||||
| | | `Callback` must be registered in advance. |
|
||||
+-----------------------------------------------------+--------------------------------------------------+
|
||||
| async:newUnsavableTimerInSeconds(delay, func) | | Call `func()` in `delay` seconds. The timer |
|
||||
| | | will be lost if the game is saved and loaded. |
|
||||
+-----------------------------------------------------+--------------------------------------------------+
|
||||
| async:newUnsavableTimerInHours(delay, func) | | Call `func()` in `delay` game hours. The timer |
|
||||
| | | will be lost if the game is saved and loaded. |
|
||||
+-----------------------------------------------------+--------------------------------------------------+
|
||||
|
||||
openmw.query
|
||||
============
|
||||
|
||||
**TODO**
|
||||
|
||||
openmw.world
|
||||
============
|
||||
|
||||
Interface to the game world. Can be used only by global scripts.
|
||||
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+==================================================+
|
||||
| activeActors | ObjectList_ | List of currently active actors |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| selectObjects(query) -> ObjectList_ | Evaluates a query |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
openmw.nearby
|
||||
=============
|
||||
|
||||
Can be used only by local scripts.
|
||||
Read-only access to the nearest area of the game world.
|
||||
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+==================================================+
|
||||
| activators | ObjectList_ | List of nearby activators |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| actors | ObjectList_ | List of nearby actors |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| containers | ObjectList_ | List of nearby containers |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| doors | ObjectList_ | List of nearby doors |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| items | ObjectList_ | Everything that can be picked up in the nearby |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| selectObjects(query) -> ObjectList_ | Evaluates a query |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
openmw.self
|
||||
===========
|
||||
|
||||
Can be used only by local scripts. Full access to the object the script is attached to.
|
||||
All fields and function of `Game object`_ are also available for `openmw.self`. For example:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local self = require('openmw.self')
|
||||
if self.type == 'Player' then
|
||||
self:sendEvent("something", self.position)
|
||||
end
|
||||
|
||||
Note that `self` is not a Game Object. If you need an actual object, use `self.object`:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
if self == someObject then ... -- Incorrect, this condition is always false
|
||||
core.sendGlobalEvent('something', self) -- Incorrect, will raise an error
|
||||
|
||||
if self.object == someObject then ... -- Correct
|
||||
core.sendGlobalEvent('something', self.object) -- Correct
|
||||
|
||||
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+==================================================+
|
||||
| self.object | `Game object`_ | The object the script is attached to (readonly) |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| self.controls | `Actor controls`_ | Movement controls (only for actors) |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| self:setDirectControl(bool) | Enables or disables direct movement control |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| self:setEquipment(table) | | Sets equipment. Keys in the table are equipment|
|
||||
| | | slots (see `core.EQUIPMENT_SLOT`). Each value |
|
||||
| | | can be either an object or `recordId`. Raises |
|
||||
| | | an error if the object has no equipment |
|
||||
| | | slots and `table` is not empty. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| self:getCombatTarget() -> `Game object`_ | Returns current target or nil if not in combat |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| self:stopCombat() | Removes all combat packages from the actor |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| self:startCombat(target) | Attack `target` |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
|
||||
Actor controls
|
||||
--------------
|
||||
|
||||
Allows to control movements of an actor. Makes an effect only if `setDirectControl(true)` was called.
|
||||
All fields are mutable.
|
||||
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| Field | Type | Description |
|
||||
+=======================+=====================+==================================================+
|
||||
| movement | float number | +1 - move forward, -1 - move backward |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| sideMovement | float number | +1 - move right, -1 - move left |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| turn | float number | turn right (radians); if negative - turn left |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| run | boolean | true - run, false - walk |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
| jump | boolean | if true - initiate a jump |
|
||||
+-----------------------+---------------------+--------------------------------------------------+
|
||||
|
||||
openmw.ui
|
||||
=========
|
||||
|
||||
Can be used only by local scripts, that are attached to a player.
|
||||
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
| Function | Description |
|
||||
+=============================================+==================================================+
|
||||
| showMessage(string) | Shows given message at the bottom of the screen. |
|
||||
+---------------------------------------------+--------------------------------------------------+
|
||||
|
||||
openmw.camera
|
||||
=============
|
||||
|
||||
.. warning::
|
||||
Not implemented yet.
|
||||
|
|
|
@ -12,8 +12,8 @@ Here are starting points for learning Lua:
|
|||
- `Programing in Lua <https://www.lua.org/pil/contents.html>`__ (first edition, aimed at Lua 5.0)
|
||||
- `Lua 5.1 Reference Manual <https://www.lua.org/manual/5.1/>`__
|
||||
|
||||
Each script works in a separate sandbox and doesn't have any access to operation system.
|
||||
Only limited list of allowed standard libraries can be used:
|
||||
Each script works in a separate sandbox and doesn't have any access to the underlying operating system.
|
||||
Only a limited list of allowed standard libraries can be used:
|
||||
`coroutine <https://www.lua.org/manual/5.1/manual.html#5.2>`__,
|
||||
`math <https://www.lua.org/manual/5.1/manual.html#5.6>`__,
|
||||
`string <https://www.lua.org/manual/5.1/manual.html#5.4>`__,
|
||||
|
@ -21,7 +21,7 @@ Only limited list of allowed standard libraries can be used:
|
|||
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).
|
||||
|
||||
Allowed `basic functions <https://www.lua.org/manual/5.1/manual.html#5.1>`__:
|
||||
``assert``, ``error``, ``ipairs``, ``next``, ``pairs``, ``pcall``, ``print``, ``tonumber``, ``tostring``, ``type``, ``unpack``, ``xpcall``, ``rawequal``, ``rawget``, ``rawset``, ``setmetatable``.
|
||||
``assert``, ``error``, ``ipairs``, ``next``, ``pairs``, ``pcall``, ``print``, ``select``, ``tonumber``, ``tostring``, ``type``, ``unpack``, ``xpcall``, ``rawequal``, ``rawget``, ``rawset``, ``getmetatable``, ``setmetatable``.
|
||||
|
||||
Loading libraries with ``require('library_name')`` is allowed, but limited. It works this way:
|
||||
|
||||
|
@ -29,7 +29,7 @@ Loading libraries with ``require('library_name')`` is allowed, but limited. It w
|
|||
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``.
|
||||
|
||||
Loading DLLs and precompiled Lua files is intentionally prohibited for reasons of safety and compatibility between different platforms.
|
||||
Loading DLLs and precompiled Lua files is intentionally prohibited for compatibility and security reasons.
|
||||
|
||||
Basic concepts
|
||||
==============
|
||||
|
@ -44,21 +44,21 @@ Record
|
|||
Don't be confused with MWSE terminology. In MWSE game objects are "references" and records are "objects".
|
||||
|
||||
Cell
|
||||
An area of the game world. A position in the world is a link to a cell and coordinates X, Y, Z in the cell. At a specific moment in time each cell can be active or inactive. Inactive cells don't perform physics updates.
|
||||
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.
|
||||
|
||||
Global scripts
|
||||
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. List of global scripts is defined by `omwscripts` files, that should be :ref:`registered <Lua scripting>` in `openmw.cfg`.
|
||||
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`.
|
||||
|
||||
Local scripts
|
||||
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.
|
||||
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.
|
||||
|
||||
Player scripts
|
||||
It is a specific case 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).
|
||||
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).
|
||||
|
||||
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. It leads to several rules of Lua scripting API:
|
||||
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 modify only 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.
|
||||
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.
|
||||
|
||||
|
@ -76,8 +76,8 @@ Let's write a simple example of a `Player script`:
|
|||
|
||||
return {
|
||||
engineHandlers = {
|
||||
onKeyPress = function(code, modifiers)
|
||||
if code == string.byte('x') then
|
||||
onKeyPress = function(key)
|
||||
if key.symbol == 'x' then
|
||||
ui.showMessage('You have pressed "X"')
|
||||
end
|
||||
end
|
||||
|
@ -117,14 +117,14 @@ Now every time the player presses "X" on a keyboard, a message is shown.
|
|||
Hot reloading
|
||||
=============
|
||||
|
||||
It is possible to modify a script without restarting OpenMW. To apply changes open in-game console and run the command ``reloadlua``.
|
||||
It will restart all Lua scripts using `onSave and onLoad`_ handlers the same way as if the game was saved and loaded.
|
||||
It is possible to modify a script without restarting OpenMW. To apply changes, open the in-game console and run the command: ``reloadlua``.
|
||||
This will restart all Lua scripts using the `onSave and onLoad`_ handlers the same way as if the game was saved or loaded.
|
||||
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.
|
||||
|
||||
Script structure
|
||||
================
|
||||
|
||||
Each script is a separate file in game assets.
|
||||
Each script is a separate file in the game assets.
|
||||
`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).
|
||||
|
||||
Here is an example of a basic script structure:
|
||||
|
@ -155,7 +155,7 @@ Here is an example of a basic script structure:
|
|||
end
|
||||
|
||||
return {
|
||||
name = 'MyScriptInterface',
|
||||
interfaceName = 'MyScriptInterface',
|
||||
interface = {
|
||||
somePublicFunction = somePublicFunction,
|
||||
},
|
||||
|
@ -192,7 +192,7 @@ Engine handlers
|
|||
===============
|
||||
|
||||
An engine handler is a function defined by a script, that can be called by the engine. I.e. it is an engine-to-script interaction.
|
||||
Not visible for other scripts. If several scripts register an engine handler with the same name,
|
||||
Not visible to other scripts. If several scripts register an engine handler with the same name,
|
||||
the engine calls all of them in the same order as the scripts were started.
|
||||
|
||||
Some engine handlers are allowed only for global, or only for local/player scripts. Some are universal.
|
||||
|
@ -202,17 +202,61 @@ See :ref:`Engine handlers reference`.
|
|||
onSave and onLoad
|
||||
=================
|
||||
|
||||
When game is saved or loaded, the engine calls engine handlers `onSave` or `onLoad` for every script.
|
||||
When a game is saved or loaded, the engine calls the engine handlers `onSave` or `onLoad` for every script.
|
||||
The value that `onSave` returns will be passed to `onLoad` when the game is loaded.
|
||||
It is the only way to save internal state of a script. All other script vatiables will be lost after closing the game.
|
||||
It is the only way to save the internal state of a script. All other script variables will be lost after closing the game.
|
||||
The saved state must be :ref:`serializable <Serializable data>`.
|
||||
|
||||
`onSave` and `onLoad` are special:
|
||||
The list of active global scripts is controlled by ``*.omwscripts`` files. Loading a save doesn't synchronize
|
||||
the list of global scripts with those that were active previously, it only calls `onLoad` for those currently active.
|
||||
|
||||
- Unlike all other engine handlers it is called even for objects in inactive cells.
|
||||
- During saving and loading the environment may be not fully initialized, so these handlers shouldn't use any API calls.
|
||||
For local scripts the situation is different. When a save is loading, it tries to run all local scripts that were saved.
|
||||
So if ``lua-scripts=`` entries of some mod are removed, but ``data=`` entries are still enabled, then local scripts from the mod may still run.
|
||||
|
||||
TODO: example, explain how global scripts are loading
|
||||
`onSave` and `onLoad` can be called even for objects in inactive state, so it shouldn't use `openmw.nearby`.
|
||||
|
||||
An example:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
...
|
||||
|
||||
local scriptVersion = 3 -- increase it every time when `onSave` is changed
|
||||
|
||||
local function onSave()
|
||||
return {
|
||||
version = scriptVersion
|
||||
some = someVariable,
|
||||
someOther = someOtherVariable
|
||||
}
|
||||
end
|
||||
|
||||
local function onLoad(data)
|
||||
if not data or not data.version or data.version < 2 then
|
||||
print('Was saved with an old version of the script, initializing to default')
|
||||
someVariable = 'some value'
|
||||
someOtherVariable = 42
|
||||
return
|
||||
end
|
||||
if data.version > scriptVersion then
|
||||
error('Required update to a new version of the script')
|
||||
end
|
||||
someVariable = data.some
|
||||
if data.version == scriptVersion then
|
||||
someOtherVariable = data.someOther
|
||||
else
|
||||
print(string.format('Updating from version %d to %d', data.version, scriptVersion))
|
||||
someOtherVariable = 42
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
engineHandlers = {
|
||||
onUpdate = update,
|
||||
onSave = onSave,
|
||||
onLoad = onLoad,
|
||||
}
|
||||
}
|
||||
|
||||
Serializable data
|
||||
-----------------
|
||||
|
@ -225,7 +269,7 @@ Serializable value is one of:
|
|||
- a number
|
||||
- a string
|
||||
- a game object
|
||||
- a value of a type, defined by :ref:`openmw.util`
|
||||
- a value of a type, defined by :ref:`openmw.util <Package openmw.util>`
|
||||
- a table whith serializable keys and values
|
||||
|
||||
Serializable data can not contain:
|
||||
|
@ -244,70 +288,277 @@ It can not be overloaded even if there is a lua file with the same name.
|
|||
The list of available packages is different for global and for local scripts.
|
||||
Player scripts are local scripts that are attached to a player.
|
||||
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
| Package | Can be used | Description |
|
||||
+======================+====================+===============================================================+
|
||||
|:ref:`openmw.util` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||
| | | | that don't depend on the game world. |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.core` | everywhere | | Functions that are common for both global and local scripts |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.async` | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.query` | everywhere | | Tools for constructing queries: base queries and fields. |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.world` | by global scripts | | Read-write access to the game world. |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.self` | by local scripts | | Full access to the object the script is attached to. |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.nearby` | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.ui` | by player scripts | | Controls user interface |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.camera` | by player scripts | | Controls camera (not implemented) |
|
||||
+----------------------+--------------------+---------------------------------------------------------------+
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Package | Can be used | Description |
|
||||
+=========================================================+====================+===============================================================+
|
||||
|:ref:`openmw.interfaces <Script interfaces>` | everywhere | | Public interfaces of other scripts. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
|
||||
| | | | that don't depend on the game world. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.async <Package openmw.async>` | everywhere | | Timers (implemented) and coroutine utils (not implemented) |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.query <Package openmw.query>` | everywhere | | Tools for constructing queries: base queries and fields. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.world <Package openmw.world>` | by global scripts | | Read-write access to the game world. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.self <Package openmw.self>` | by local scripts | | Full access to the object the script is attached to. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls user interface |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|openmw.camera | by player scripts | | Controls camera (not implemented) |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
||||
openmw_aux
|
||||
----------
|
||||
|
||||
``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.
|
||||
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Built-in library | Can be used | Description |
|
||||
+=========================================================+====================+===============================================================+
|
||||
|:ref:`openmw_aux.util <Package openmw_aux.util>` | everywhere | | Miscellaneous utils |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
||||
They can be loaded with ``require`` the same as API packages. For example:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local aux_util = require('openmw_aux.util')
|
||||
aux_util.runEveryNSeconds(15, doSomething) -- run `doSomething()` every 15 seconds
|
||||
|
||||
|
||||
Script interfaces
|
||||
=================
|
||||
|
||||
.. warning::
|
||||
Not implemented yet.
|
||||
|
||||
Each script can provide a named interface for other scripts.
|
||||
It is a script-to-script interaction. This mechanism is not used by the engine itself.
|
||||
|
||||
A script can use an interface of another script only if either both a global scripts, or both are local scripts on the same object.
|
||||
A script can use an interface of another script either if both are global scripts, or both are local scripts on the same object.
|
||||
In other cases events should be used.
|
||||
|
||||
TODO: example, overloading
|
||||
Defining an interface:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
return {
|
||||
interfaceName = "SomeUtils"
|
||||
interface = {
|
||||
version = 1,
|
||||
doSomething = function(x, y) ... end,
|
||||
}
|
||||
}
|
||||
|
||||
Overriding the interface and adding a debug output:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local interfaces = require('openmw.interfaces')
|
||||
|
||||
-- it is important to save it before returning the new interface
|
||||
local orig = interfaces.SomeUtils
|
||||
|
||||
return {
|
||||
interfaceName = "SomeUtils"
|
||||
interface = {
|
||||
version = orig.version,
|
||||
doSomething = function(x, y)
|
||||
print(string.format('SomeUtils.doSomething(%d, %d)', x, y))
|
||||
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.
|
||||
|
||||
|
||||
Event system
|
||||
============
|
||||
|
||||
It is another way of script-to-script interactions. The differences:
|
||||
This is another kind of script-to-script interactions. The differences:
|
||||
|
||||
- Any script can send an event to any object or a global event to global scripts.
|
||||
- Events are always delivered with a delay.
|
||||
- Event handlers can not return any data to a sender.
|
||||
- Events are delivered with a small delay (in single player the delay is always one frame).
|
||||
- Event handlers can not return any data to the sender.
|
||||
- Event handlers have a single argument `eventData` (must be :ref:`serializable <Serializable data>`)
|
||||
|
||||
Events are the main way of interactions between local and global scripts.
|
||||
It is not recommended to use for interactions between two global scripts, because in this case interfaces are more convenient.
|
||||
Events are the main way of interacting between local and global scripts.
|
||||
They are not recommended for interactions between two global scripts, because in this case interfaces are more convenient.
|
||||
|
||||
If several scripts register handlers for the same event, it will be called in the reversed order (opposite to engine handlers).
|
||||
I.e. handler from the last attached script will be called first.
|
||||
Return value 'false' in a handler means "skip all other handlers for this event".
|
||||
If several scripts register handlers for the same event, the handlers will be called in reverse order (opposite to engine handlers).
|
||||
I.e. the handler from the last attached script will be called first.
|
||||
Return value 'false' means "skip all other handlers for this event".
|
||||
Any other return value (including nil) means nothing.
|
||||
|
||||
TODO: example
|
||||
An example. Imagine we are working on a mod that adds some "dark power" with special effects.
|
||||
We attach a local script to an item that can explode.
|
||||
At some moment it will send the 'DamagedByDarkPower' event to all nearby actors:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local self = require('openmw.self')
|
||||
local nearby = require('openmw.nearby')
|
||||
|
||||
local function onActivate()
|
||||
for i, actor in nearby.actors:ipairs() do
|
||||
local dist = (self.position - actor.position):length()
|
||||
if dist < 500 then
|
||||
local damage = (1 - dist / 500) * 200
|
||||
actor:sendEvent('DamagedByDarkPower', {source=self.object, damage=damage})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return { engineHandlers = { ... } }
|
||||
|
||||
And every actor should have a local script that processes this event:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local function damagedByDarkPower(data)
|
||||
... -- apply `data.damage` to stats / run custom animation / etc
|
||||
end
|
||||
|
||||
return {
|
||||
eventHandlers = { DamagedByDarkPower = damagedByDarkPower },
|
||||
}
|
||||
|
||||
Someone may create an additional mod that adds a protection from the dark power.
|
||||
The protection mod attaches an additional local script to every actor. The script intercepts and modifies the event:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local protectionLevel = ...
|
||||
|
||||
local function reduceDarkDamage(data)
|
||||
data.damage = data.damage - protectionLevel -- reduce the damage
|
||||
return data.damage > 0 -- it skips the original handler if the damage becomes <= 0
|
||||
end
|
||||
|
||||
return {
|
||||
eventHandlers = { DamagedByDarkPower = reduceDarkDamage },
|
||||
}
|
||||
|
||||
In order to be able to intercept the event, the protection script should be attached after the original script (i.e. below in the load order).
|
||||
|
||||
|
||||
Timers
|
||||
======
|
||||
|
||||
**TODO**
|
||||
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',
|
||||
function(data)
|
||||
data.actor:teleport(data.destCellName, data.destPos)
|
||||
end)
|
||||
|
||||
local function teleportWithDelay(delay, actor, cellName, pos)
|
||||
async:newTimerInSeconds(delay, teleportWithDelayCallback, {
|
||||
actor = actor,
|
||||
destCellName = cellName,
|
||||
destPos = pos,
|
||||
})
|
||||
end
|
||||
|
||||
Unsavable timer
|
||||
---------------
|
||||
|
||||
Unsavable timers can be created from any function without registering a callback in advance, but they can not be saved.
|
||||
If the player saves the game when an unsavable timer is running, then the timer will be lost after reloading.
|
||||
So be careful with unsavable timers and don't use them if there is a risk of leaving the game world in an inconsistent state.
|
||||
|
||||
An example:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local async = require('openmw.async')
|
||||
local ui = require('openmw.ui')
|
||||
|
||||
return {
|
||||
engineHandlers = {
|
||||
onKeyPress = function(key)
|
||||
if key.symbol == 'x' then
|
||||
async:newUnsavableTimerInSeconds(
|
||||
10,
|
||||
function()
|
||||
ui.showMessage('You have pressed "X" 10 seconds ago')
|
||||
end)
|
||||
end
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
Also in `openmw_aux`_ are the helper functions ``runEveryNSeconds`` and ``runEveryNHours``, they are implemented on top of unsavable timers:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
local async = require('openmw.async')
|
||||
local core = require('openmw.core')
|
||||
|
||||
-- call `doSomething()` at the end of every game day.
|
||||
-- `timeBeforeMidnight` is a delay before the first call. `24` is an interval.
|
||||
-- the periodical evaluation can be stopped at any moment by calling `stopFn()`
|
||||
local timeBeforeMidnight = 24 - math.fmod(core.getGameTimeInHours(), 24)
|
||||
local stopFn = aux_util.runEveryNHours(24, doSomething, timeBeforeMidnight)
|
||||
|
||||
return {
|
||||
engineHandlers = {
|
||||
onLoad = function()
|
||||
-- the timer is unsavable, so we need to restart it in `onLoad`.
|
||||
timeBeforeMidnight = 24 - math.fmod(core.getGameTimeInHours(), 24)
|
||||
stopFn = aux_util.runEveryNHours(24, doSomething, timeBeforeMidnight)
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Queries
|
||||
|
@ -318,7 +569,7 @@ Queries
|
|||
`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
|
||||
|
||||
A minimal example of an object query:
|
||||
An example of an object query:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
|
@ -326,10 +577,8 @@ A minimal example of an object query:
|
|||
local nearby = require('openmw.nearby')
|
||||
local ui = require('openmw.ui')
|
||||
|
||||
local doorQuery = query.doors:orderBy(query.DOOR.destPosition.x)
|
||||
|
||||
local function selectDoors(namePattern)
|
||||
local query = doorQuery:where(query.DOOR.destCell:like(namePattern))
|
||||
local query = query.doors:where(query.DOOR.destCell.name:like(namePattern))
|
||||
return nearby.selectObjects(query)
|
||||
end
|
||||
|
||||
|
@ -344,8 +593,8 @@ A minimal example of an object query:
|
|||
|
||||
return {
|
||||
engineHandlers = {
|
||||
onKeyPress = function(code, modifiers)
|
||||
if code == string.byte('e') then
|
||||
onKeyPress = function(key)
|
||||
if key.symbol == 'e' then
|
||||
showGuildDoors()
|
||||
end
|
||||
end
|
||||
|
@ -360,10 +609,10 @@ A minimal example of an object query:
|
|||
Queries are constructed through the following method calls: (if you've used SQL before, you will find them familiar)
|
||||
|
||||
- `:where(filter)` - filters the results to match the combination of conditions passed as the argument
|
||||
- `: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...)
|
||||
- `: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.
|
||||
- `: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)**
|
||||
- `:limit(number)` will only return `number` of results (or fewer)
|
||||
- `:offset(number)` skips the first `number` results. Particularly useful in combination with `:limit`
|
||||
- `:offset(number)` skips the first `number` results. Particularly useful in combination with `:limit` **(not implemented yet)**
|
||||
|
||||
Filters consist of conditions, which are combined with "and" (operator `*`), "or" (operator `+`), "not" (operator `-`) and braces `()`.
|
||||
|
||||
|
@ -375,7 +624,7 @@ To make a condition, take a field from the `openmw.query` package and call any o
|
|||
- `:gte` greater or equal to
|
||||
- `:lt` less than
|
||||
- `:lte` less or equal to
|
||||
- `:like` matches a pattern. Only applicable to text (strings)
|
||||
- `:like` matches a pattern. Only applicable to text (strings) **(not implemented yet)**
|
||||
|
||||
**TODO:** describe the pattern format
|
||||
|
||||
|
@ -401,12 +650,61 @@ A few examples of filters:
|
|||
|
||||
local DOOR = query.DOOR
|
||||
|
||||
local interestingDoors = -DOOR.name:eq("") * DOOR.isTeleport:eq(true) * Door.destCell:neq("")
|
||||
local interestingDoors = -DOOR.name:eq("") * DOOR.isTeleport:eq(true) * Door.destCell.isExterior:eq(false)
|
||||
|
||||
|
||||
Using IDE for Lua scripting
|
||||
===========================
|
||||
|
||||
.. warning::
|
||||
This section is not written yet. Later it will explain how to setup Lua Development Tools (eclipse-based IDE) with code autocompletion and integrated OpenMW API reference.
|
||||
Find the directory ``resources/lua_api`` in your installation of OpenMW.
|
||||
It describes OpenMW LuaAPI in
|
||||
`LDT Documentation Language <https://wiki.eclipse.org/LDT/User_Area/Documentation_Language>`__.
|
||||
It is the source from which the :ref:`API reference <Lua API reference>` is generated.
|
||||
|
||||
If you write scripts using `Lua Development Tools <https://www.eclipse.org/ldt/>`__ (eclipse-based IDE),
|
||||
you can import these files to get code autocompletion and integrated OpenMW API reference. Here are the steps:
|
||||
|
||||
- Install and run `LDT <https://www.eclipse.org/ldt/#installation>`__.
|
||||
- Press `File` / `New` / `Lua Project` in menu.
|
||||
|
||||
.. image:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/reference/lua-scripting/_static/lua-ide-create-project.png
|
||||
|
||||
- Specify project name (for example the title of your omwaddon)
|
||||
- Set `Targeted Execution Environment` to `No Execution Environment`, and `Target Grammar` to `lua-5.1`.
|
||||
|
||||
.. image:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/reference/lua-scripting/_static/lua-ide-project-settings.png
|
||||
|
||||
- Press `Next`, choose the `Libraries` tab, and click `Add External Source Folder`.
|
||||
- Specify there the path to ``resources/lua_api`` in your OpenMW installation.
|
||||
- If you use `openmw_aux`_, add ``resources/vfs`` as an additional external source folder.
|
||||
|
||||
.. image:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/reference/lua-scripting/_static/lua-ide-import-api.png
|
||||
|
||||
- Press `Finish`. Create a new Lua file.
|
||||
- Now you have code completion! Press ``Ctrl+Space`` in any place to see the variants.
|
||||
|
||||
.. image:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/reference/lua-scripting/_static/lua-ide-code-completion1.png
|
||||
|
||||
In some cases LDT can deduce types automatically, but it is not always possible.
|
||||
You can add special hints to give LDT more information:
|
||||
|
||||
- Before function definition: ``--- @param TYPE argName``
|
||||
- Before variable definition: ``--- @field TYPE variableName``
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
--- @param openmw.core#GameObject obj
|
||||
local function doSomething(obj)
|
||||
-- autocompletion now works with `obj`
|
||||
end
|
||||
|
||||
--- @field openmw.util#Vector3 c
|
||||
local c
|
||||
|
||||
-- autocompletion now works with `c`
|
||||
|
||||
.. image:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/reference/lua-scripting/_static/lua-ide-code-completion2.png
|
||||
|
||||
See `LDT Documentation Language <https://wiki.eclipse.org/LDT/User_Area/Documentation_Language>`__ for more details.
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue