mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-18 13:12:50 +00:00
Merge branch 'lua_ai' into 'master'
Control AI packages from Lua See merge request OpenMW/openmw!1604
This commit is contained in:
commit
643c1d6aeb
@ -1,9 +1,17 @@
|
||||
#include "localscripts.hpp"
|
||||
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwmechanics/aisequence.hpp"
|
||||
#include "../mwmechanics/aicombat.hpp"
|
||||
#include "../mwmechanics/aiescort.hpp"
|
||||
#include "../mwmechanics/aifollow.hpp"
|
||||
#include "../mwmechanics/aipursue.hpp"
|
||||
#include "../mwmechanics/aitravel.hpp"
|
||||
#include "../mwmechanics/aiwander.hpp"
|
||||
#include "../mwmechanics/aipackage.hpp"
|
||||
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
@ -60,28 +68,106 @@ namespace MWLua
|
||||
}
|
||||
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
|
||||
};
|
||||
selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional<LObject>
|
||||
|
||||
using AiPackage = MWMechanics::AiPackage;
|
||||
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");
|
||||
aiPackage["type"] = sol::readonly_property([](const AiPackage& p) -> std::string_view
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
MWWorld::Ptr target;
|
||||
if (ai.getCombatTarget(target))
|
||||
return LObject(getId(target), worldView->getObjectRegistry());
|
||||
switch (p.getTypeId())
|
||||
{
|
||||
case MWMechanics::AiPackageTypeId::Wander: return "Wander";
|
||||
case MWMechanics::AiPackageTypeId::Travel: return "Travel";
|
||||
case MWMechanics::AiPackageTypeId::Escort: return "Escort";
|
||||
case MWMechanics::AiPackageTypeId::Follow: return "Follow";
|
||||
case MWMechanics::AiPackageTypeId::Activate: return "Activate";
|
||||
case MWMechanics::AiPackageTypeId::Combat: return "Combat";
|
||||
case MWMechanics::AiPackageTypeId::Pursue: return "Pursue";
|
||||
case MWMechanics::AiPackageTypeId::AvoidDoor: return "AvoidDoor";
|
||||
case MWMechanics::AiPackageTypeId::Face: return "Face";
|
||||
case MWMechanics::AiPackageTypeId::Breathe: return "Breathe";
|
||||
case MWMechanics::AiPackageTypeId::Cast: return "Cast";
|
||||
default: return "Unknown";
|
||||
}
|
||||
});
|
||||
aiPackage["target"] = sol::readonly_property([worldView=context.mWorldView](const AiPackage& p) -> sol::optional<LObject>
|
||||
{
|
||||
MWWorld::Ptr target = p.getTarget();
|
||||
if (target.isEmpty())
|
||||
return sol::nullopt;
|
||||
else
|
||||
return {};
|
||||
};
|
||||
selfAPI["stopCombat"] = [](SelfObject& self)
|
||||
return LObject(getId(target), worldView->getObjectRegistry());
|
||||
});
|
||||
aiPackage["sideWithTarget"] = sol::readonly_property([](const AiPackage& p) { return p.sideWithTarget(); });
|
||||
aiPackage["destination"] = sol::readonly_property([](const AiPackage& p) { return p.getDestination(); });
|
||||
|
||||
selfAPI["_getActiveAiPackage"] = [](SelfObject& self) -> sol::optional<std::shared_ptr<AiPackage>>
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stopCombat();
|
||||
if (ai.isEmpty())
|
||||
return sol::nullopt;
|
||||
else
|
||||
return *ai.begin();
|
||||
};
|
||||
selfAPI["startCombat"] = [](SelfObject& self, const LObject& target)
|
||||
selfAPI["_iterateAndFilterAiSequence"] = [](SelfObject& self, sol::function callback)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
std::list<std::shared_ptr<AiPackage>>& list = ai.getUnderlyingList();
|
||||
for (auto it = list.begin(); it != list.end();)
|
||||
{
|
||||
bool keep = LuaUtil::call(callback, *it).get<bool>();
|
||||
if (keep)
|
||||
++it;
|
||||
else
|
||||
it = list.erase(it);
|
||||
}
|
||||
};
|
||||
selfAPI["_startAiCombat"] = [](SelfObject& self, const LObject& target)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stack(MWMechanics::AiCombat(target.ptr()), ptr);
|
||||
};
|
||||
selfAPI["_startAiPursue"] = [](SelfObject& self, const LObject& target)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stack(MWMechanics::AiPursue(target.ptr()), ptr);
|
||||
};
|
||||
selfAPI["_startAiFollow"] = [](SelfObject& self, const LObject& target)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stack(MWMechanics::AiFollow(target.ptr()), ptr);
|
||||
};
|
||||
selfAPI["_startAiEscort"] = [](SelfObject& self, const LObject& target, LCell cell,
|
||||
float duration, const osg::Vec3f& dest)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
// TODO: change AiEscort implementation to accept ptr instead of a non-unique refId.
|
||||
const std::string& refId = target.ptr().getCellRef().getRefId();
|
||||
int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0));
|
||||
const ESM::Cell* esmCell = cell.mStore->getCell();
|
||||
if (esmCell->isExterior())
|
||||
ai.stack(MWMechanics::AiEscort(refId, gameHoursDuration, dest.x(), dest.y(), dest.z(), false), ptr);
|
||||
else
|
||||
ai.stack(MWMechanics::AiEscort(refId, esmCell->mName, gameHoursDuration, dest.x(), dest.y(), dest.z(), false), ptr);
|
||||
};
|
||||
selfAPI["_startAiWander"] = [](SelfObject& self, int distance, float duration)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0));
|
||||
ai.stack(MWMechanics::AiWander(distance, gameHoursDuration, 0, {}, false), ptr);
|
||||
};
|
||||
selfAPI["_startAiTravel"] = [](SelfObject& self, const osg::Vec3f& target)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = self.ptr();
|
||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
ai.stack(MWMechanics::AiTravel(target.x(), target.y(), target.z(), false), ptr);
|
||||
};
|
||||
}
|
||||
|
||||
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj, ESM::LuaScriptCfg::Flags autoStartMode)
|
||||
|
@ -2013,7 +2013,7 @@ namespace MWMechanics
|
||||
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
|
||||
{
|
||||
std::list<MWWorld::Ptr> list;
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::shared_ptr<AiPackage>& package)
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
list.push_back(iter.first);
|
||||
@ -2064,7 +2064,7 @@ namespace MWMechanics
|
||||
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
|
||||
{
|
||||
std::list<int> list;
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::shared_ptr<AiPackage>& package)
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
{
|
||||
@ -2081,7 +2081,7 @@ namespace MWMechanics
|
||||
std::map<int, MWWorld::Ptr> Actors::getActorsFollowingByIndex(const MWWorld::Ptr &actor)
|
||||
{
|
||||
std::map<int, MWWorld::Ptr> map;
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
|
||||
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::shared_ptr<AiPackage>& package)
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
{
|
||||
|
@ -87,17 +87,7 @@ bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const
|
||||
return !targetActors.empty();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::begin() const
|
||||
{
|
||||
return mPackages.begin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::end() const
|
||||
{
|
||||
return mPackages.end();
|
||||
}
|
||||
|
||||
void AiSequence::erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package)
|
||||
void AiSequence::erase(std::list<std::shared_ptr<AiPackage>>::const_iterator package)
|
||||
{
|
||||
// Not sure if manually terminated packages should trigger mDone, probably not?
|
||||
for(auto it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
|
@ -38,7 +38,7 @@ namespace MWMechanics
|
||||
class AiSequence
|
||||
{
|
||||
///AiPackages to run though
|
||||
std::list<std::unique_ptr<AiPackage>> mPackages;
|
||||
std::list<std::shared_ptr<AiPackage>> mPackages;
|
||||
|
||||
///Finished with top AIPackage, set for one frame
|
||||
bool mDone;
|
||||
@ -63,10 +63,12 @@ namespace MWMechanics
|
||||
virtual ~AiSequence();
|
||||
|
||||
/// Iterator may be invalidated by any function calls other than begin() or end().
|
||||
std::list<std::unique_ptr<AiPackage>>::const_iterator begin() const;
|
||||
std::list<std::unique_ptr<AiPackage>>::const_iterator end() const;
|
||||
std::list<std::shared_ptr<AiPackage>>::const_iterator begin() const { return mPackages.begin(); }
|
||||
std::list<std::shared_ptr<AiPackage>>::const_iterator end() const { return mPackages.end(); }
|
||||
|
||||
void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package);
|
||||
void erase(std::list<std::shared_ptr<AiPackage>>::const_iterator package);
|
||||
|
||||
std::list<std::shared_ptr<AiPackage>>& getUnderlyingList() { return mPackages; }
|
||||
|
||||
/// Returns currently executing AiPackage type
|
||||
/** \see enum class AiPackageTypeId **/
|
||||
|
@ -65,5 +65,6 @@ $DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw/*lua
|
||||
|
||||
cd $FILES_DIR/builtin_scripts
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw_aux/*lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/ai.lua
|
||||
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera.lua
|
||||
|
||||
|
145
docs/source/reference/lua-scripting/aipackages.rst
Normal file
145
docs/source/reference/lua-scripting/aipackages.rst
Normal file
@ -0,0 +1,145 @@
|
||||
Built-in AI packages
|
||||
====================
|
||||
|
||||
Combat
|
||||
------
|
||||
|
||||
Attack another actor.
|
||||
|
||||
**Arguments**
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20 60
|
||||
|
||||
* - name
|
||||
- type
|
||||
- description
|
||||
* - target
|
||||
- `GameObject <openmw_core.html##(GameObject)>`_ [required]
|
||||
- the actor to attack
|
||||
|
||||
**Examples**
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
-- from local script add package to self
|
||||
local AI = require('openmw.interfaces').AI
|
||||
AI.startPackage({type='Combat', target=anotherActor})
|
||||
|
||||
-- via event to any actor
|
||||
actor:sendEvent('StartAIPackage', {type='Combat', target=anotherActor})
|
||||
|
||||
Pursue
|
||||
------
|
||||
|
||||
Pursue another actor.
|
||||
|
||||
**Arguments**
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20 60
|
||||
|
||||
* - name
|
||||
- type
|
||||
- description
|
||||
* - target
|
||||
- `GameObject <openmw_core.html##(GameObject)>`_ [required]
|
||||
- the actor to pursue
|
||||
|
||||
Follow
|
||||
------
|
||||
|
||||
Follow another actor.
|
||||
|
||||
**Arguments**
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20 60
|
||||
|
||||
* - name
|
||||
- type
|
||||
- description
|
||||
* - target
|
||||
- `GameObject <openmw_core.html##(GameObject)>`_ [required]
|
||||
- the actor to follow
|
||||
|
||||
Escort
|
||||
------
|
||||
|
||||
Escort another actor to the given location.
|
||||
|
||||
**Arguments**
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20 60
|
||||
|
||||
* - name
|
||||
- type
|
||||
- description
|
||||
* - target
|
||||
- `GameObject <openmw_core.html##(GameObject)>`_ [required]
|
||||
- the actor to follow
|
||||
* - destPosition
|
||||
- `3d vector <openmw_util.html##(Vector3)>`_ [required]
|
||||
- the destination point
|
||||
* - destCell
|
||||
- Cell [optional]
|
||||
- the destination cell
|
||||
* - duration
|
||||
- number [optional]
|
||||
- duration in game time (will be rounded up to the next hour)
|
||||
|
||||
**Example**
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
actor:sendEvent('StartAIPackage', {
|
||||
type = 'Escort',
|
||||
target = object.self,
|
||||
destPosition = util.vector3(x, y, z),
|
||||
duration = 3 * time.hour,
|
||||
})
|
||||
|
||||
Wander
|
||||
------
|
||||
|
||||
Wander nearby current position.
|
||||
|
||||
**Arguments**
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20 60
|
||||
|
||||
* - name
|
||||
- type
|
||||
- description
|
||||
* - distance
|
||||
- float [default=0]
|
||||
- the actor to follow
|
||||
* - duration
|
||||
- number [optional]
|
||||
- duration in game time (will be rounded up to the next hour)
|
||||
|
||||
Travel
|
||||
------
|
||||
|
||||
Go to given location.
|
||||
|
||||
**Arguments**
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 20 60
|
||||
|
||||
* - name
|
||||
- type
|
||||
- description
|
||||
* - destPosition
|
||||
- `3d vector <openmw_util.html##(Vector3)>`_ [required]
|
||||
- the point to travel to
|
||||
|
@ -7,6 +7,8 @@ Lua API reference
|
||||
|
||||
engine_handlers
|
||||
user_interface
|
||||
aipackages
|
||||
events
|
||||
openmw_util
|
||||
openmw_storage
|
||||
openmw_core
|
||||
@ -21,6 +23,7 @@ Lua API reference
|
||||
openmw_aux_calendar
|
||||
openmw_aux_util
|
||||
openmw_aux_time
|
||||
interface_ai
|
||||
interface_camera
|
||||
|
||||
|
||||
@ -28,6 +31,8 @@ Lua API reference
|
||||
- :ref:`User interface reference <User interface reference>`
|
||||
- `Game object reference <openmw_core.html##(GameObject)>`_
|
||||
- `Cell reference <openmw_core.html##(Cell)>`_
|
||||
- :ref:`Built-in AI packages`
|
||||
- :ref:`Built-in events`
|
||||
|
||||
**API packages**
|
||||
|
||||
@ -87,6 +92,8 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Interface | Can be used | Description |
|
||||
+=========================================================+====================+===============================================================+
|
||||
|:ref:`AI <Interface AI>` | by local scripts | | Control basic AI of NPCs and creatures. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`Camera <Interface Camera>` | by player scripts | | Allows to alter behavior of the built-in camera script |
|
||||
| | | | without overriding the script completely. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|
13
docs/source/reference/lua-scripting/events.rst
Normal file
13
docs/source/reference/lua-scripting/events.rst
Normal file
@ -0,0 +1,13 @@
|
||||
Built-in events
|
||||
===============
|
||||
|
||||
Any script can send to any actor (except player, for player will be ignored) events ``StartAIPackage`` and ``RemoveAIPackages``.
|
||||
The effect is equivalent to calling ``interfaces.AI.startPackage`` or ``interfaces.AI.removePackages`` in a local script on this actor.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
actor:sendEvent('StartAIPackage', {type='Combat', target=self.object})
|
||||
actor:sendEvent('RemoveAIPackages', 'Pursue')
|
||||
|
6
docs/source/reference/lua-scripting/interface_ai.rst
Normal file
6
docs/source/reference/lua-scripting/interface_ai.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Interface AI
|
||||
============
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/scripts_omw_ai.html
|
||||
|
@ -463,6 +463,8 @@ The order in which the scripts are started is important. So if one mod should ov
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
| Interface | Can be used | Description |
|
||||
+=========================================================+====================+===============================================================+
|
||||
|:ref:`AI <Interface AI>` | by local scripts | | Control basic AI of NPCs and creatures. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
|:ref:`Camera <Interface Camera>` | by player scripts | | Allows to alter behavior of the built-in camera script |
|
||||
| | | | without overriding the script completely. |
|
||||
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||
@ -495,7 +497,7 @@ At some moment it will send the 'DamagedByDarkPower' event to all nearby actors:
|
||||
local self = require('openmw.self')
|
||||
local nearby = require('openmw.nearby')
|
||||
|
||||
local function onActivate()
|
||||
local function onActivated()
|
||||
for i, actor in nearby.actors:ipairs() do
|
||||
local dist = (self.position - actor.position):length()
|
||||
if dist < 500 then
|
||||
@ -505,7 +507,7 @@ At some moment it will send the 'DamagedByDarkPower' event to all nearby actors:
|
||||
end
|
||||
end
|
||||
|
||||
return { engineHandlers = { ... } }
|
||||
return { engineHandlers = { onActivated = onActivated } }
|
||||
|
||||
And every actor should have a local script that processes this event:
|
||||
|
||||
@ -537,6 +539,8 @@ The protection mod attaches an additional local script to every actor. The scrip
|
||||
|
||||
In order to be able to intercept the event, the protection script should be placed in the load order below the original script.
|
||||
|
||||
See :ref:`the list of events <Built-in events>` that are used by built-in scripts.
|
||||
|
||||
|
||||
Timers
|
||||
======
|
||||
|
@ -1 +1,2 @@
|
||||
PLAYER: scripts/omw/camera.lua
|
||||
NPC,CREATURE: scripts/omw/ai.lua
|
||||
|
116
files/builtin_scripts/scripts/omw/ai.lua
Normal file
116
files/builtin_scripts/scripts/omw/ai.lua
Normal file
@ -0,0 +1,116 @@
|
||||
local self = require('openmw.self')
|
||||
local interfaces = require('openmw.interfaces')
|
||||
|
||||
local function startPackage(args)
|
||||
if args.type == 'Combat' then
|
||||
if not args.target then error("target required") end
|
||||
self:_startAiCombat(args.target)
|
||||
elseif args.type == 'Pursue' then
|
||||
if not args.target then error("target required") end
|
||||
self:_startAiPursue(args.target)
|
||||
elseif args.type == 'Follow' then
|
||||
if not args.target then error("target required") end
|
||||
self:_startAiFollow(args.target)
|
||||
elseif args.type == 'Escort' then
|
||||
if not args.target then error("target required") end
|
||||
if not args.destPosition then error("destPosition required") end
|
||||
self:_startAiEscort(args.target, args.destCell or self.cell, args.duration or 0, args.destPosition)
|
||||
elseif args.type == 'Wander' then
|
||||
self:_startAiWander(args.distance or 0, args.duration or 0)
|
||||
elseif args.type == 'Travel' then
|
||||
if not args.destPosition then error("destPosition required") end
|
||||
self:_startAiTravel(args.destPosition)
|
||||
else
|
||||
error('Unsupported AI Package: '..args.type)
|
||||
end
|
||||
end
|
||||
|
||||
local function filterPackages(filter)
|
||||
self:_iterateAndFilterAiSequence(filter)
|
||||
end
|
||||
|
||||
return {
|
||||
interfaceName = 'AI',
|
||||
--- Basic AI interface
|
||||
-- @module AI
|
||||
-- @usage require('openmw.interfaces').AI
|
||||
interface = {
|
||||
--- Interface version
|
||||
-- @field [parent=#AI] #number version
|
||||
version = 0,
|
||||
|
||||
--- AI Package
|
||||
-- @type Package
|
||||
-- @field #string type Type of the AI package.
|
||||
-- @field openmw.core#GameObject target Target (usually an actor) of the AI package (can be nil).
|
||||
-- @field #boolean sideWithTarget Whether to help the target in combat (true or false).
|
||||
-- @field openmw.util#Vector3 position Destination point of the AI package (can be nil).
|
||||
|
||||
--- Return the currently active AI package (or `nil` if there are no AI packages).
|
||||
-- @function [parent=#AI] getActivePackage
|
||||
-- @return #Package
|
||||
getActivePackage = function() return self:_getActiveAiPackage() end,
|
||||
|
||||
--- Start new AI package.
|
||||
-- @function [parent=#AI] startPackage
|
||||
-- @param #table options See the "Built-in AI packages" page.
|
||||
startPackage = startPackage,
|
||||
|
||||
--- Iterate over all packages starting from the active one and remove those where `filterCallback` returns false.
|
||||
-- @function [parent=#AI] filterPackages
|
||||
-- @param #function filterCallback
|
||||
filterPackages = filterPackages,
|
||||
|
||||
--- Iterate over all packages and run `callback` for each starting from the active one.
|
||||
-- The same as `filterPackage`, but without removal.
|
||||
-- @function [parent=#AI] forEachPackage
|
||||
-- @param #function callback
|
||||
forEachPackage = function(callback)
|
||||
local filter = function(p)
|
||||
callback(p)
|
||||
return true
|
||||
end
|
||||
filterPackages(filter)
|
||||
end,
|
||||
|
||||
--- Remove packages of given type (remove all packages if the type is not specified).
|
||||
-- @function [parent=#AI] removePackages
|
||||
-- @param #string packageType (optional) The type of packages to remove.
|
||||
removePackages = function(packageType)
|
||||
filterPackages(function(p) return packageType and p.type ~= packageType end)
|
||||
end,
|
||||
|
||||
--- Return the target of the active package if the package has given type
|
||||
-- @function [parent=#AI] getActiveTarget
|
||||
-- @param #string packageType The expected type of the active package
|
||||
-- @return openmw.core#GameObject The target (can be nil if the package has no target or has another type)
|
||||
getActiveTarget = function(packageType)
|
||||
local p = self:_getActiveAiPackage()
|
||||
if p and p.type == packageType then
|
||||
return p.target
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
|
||||
--- Get list of targets of all packages of the given type.
|
||||
-- @function [parent=#AI] getTargets
|
||||
-- @param #string packageType
|
||||
-- @return #list<openmw.core#GameObject>
|
||||
getTargets = function(packageType)
|
||||
local res = {}
|
||||
filterPackages(function(p)
|
||||
if p.type == packageType and p.target then
|
||||
res[#res + 1] = p.target
|
||||
end
|
||||
return true
|
||||
end)
|
||||
return res
|
||||
end,
|
||||
},
|
||||
eventHandlers = {
|
||||
StartAIPackage = function(options) interfaces.AI.startPackage(options) end,
|
||||
RemoveAIPackages = function(packageType) interfaces.AI.removePackages(packageType) end,
|
||||
},
|
||||
}
|
||||
|
@ -37,27 +37,10 @@
|
||||
-- @field [parent=#ActorControls] #number use if 1 - activates the readied weapon/spell. For weapons, keeping at 1 will charge the attack until set to 0.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Enables or disables standart AI (enabled by default).
|
||||
-- Enables or disables standard AI (enabled by default).
|
||||
-- @function [parent=#self] enableAI
|
||||
-- @param self
|
||||
-- @param #boolean v
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Returns current target or nil if not in combat
|
||||
-- @function [parent=#self] getCombatTarget
|
||||
-- @param self
|
||||
-- @return openmw.core#GameObject
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Remove all combat packages from the actor.
|
||||
-- @function [parent=#self] stopCombat
|
||||
-- @param self
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Attack `target`.
|
||||
-- @function [parent=#self] startCombat
|
||||
-- @param self
|
||||
-- @param openmw.core#GameObject target
|
||||
|
||||
return nil
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user