mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-28 12:40:06 +00:00
Expose Wander option values to the Lua API (#7916)
This commit is contained in:
parent
a57c350c08
commit
63a27bbf99
@ -81,7 +81,7 @@ message(STATUS "Configuring OpenMW...")
|
|||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 49)
|
set(OPENMW_VERSION_MINOR 49)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
set(OPENMW_LUA_API_REVISION 60)
|
set(OPENMW_LUA_API_REVISION 61)
|
||||||
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
|
@ -104,7 +104,27 @@ namespace MWLua
|
|||||||
});
|
});
|
||||||
aiPackage["sideWithTarget"] = sol::readonly_property([](const AiPackage& p) { return p.sideWithTarget(); });
|
aiPackage["sideWithTarget"] = sol::readonly_property([](const AiPackage& p) { return p.sideWithTarget(); });
|
||||||
aiPackage["destPosition"] = sol::readonly_property([](const AiPackage& p) { return p.getDestination(); });
|
aiPackage["destPosition"] = sol::readonly_property([](const AiPackage& p) { return p.getDestination(); });
|
||||||
|
aiPackage["distance"] = sol::readonly_property([](const AiPackage& p) { return p.getDistance(); });
|
||||||
|
aiPackage["duration"] = sol::readonly_property([](const AiPackage& p) { return p.getDuration(); });
|
||||||
|
aiPackage["idle"] = sol::readonly_property([context](const AiPackage& p) -> sol::optional<sol::table> {
|
||||||
|
if (p.getTypeId() == MWMechanics::AiPackageTypeId::Wander)
|
||||||
|
{
|
||||||
|
sol::table idles(context.mLua->sol(), sol::create);
|
||||||
|
const std::vector<unsigned char>& idle = static_cast<const MWMechanics::AiWander&>(p).getIdle();
|
||||||
|
if (!idle.empty())
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < idle.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string_view groupName = MWMechanics::AiWander::getIdleGroupName(i);
|
||||||
|
idles[groupName] = idle[i];
|
||||||
|
}
|
||||||
|
return idles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sol::nullopt;
|
||||||
|
});
|
||||||
|
|
||||||
|
aiPackage["isRepeat"] = sol::readonly_property([](const AiPackage& p) { return p.getRepeat(); });
|
||||||
selfAPI["_getActiveAiPackage"] = [](SelfObject& self) -> sol::optional<std::shared_ptr<AiPackage>> {
|
selfAPI["_getActiveAiPackage"] = [](SelfObject& self) -> sol::optional<std::shared_ptr<AiPackage>> {
|
||||||
const MWWorld::Ptr& ptr = self.ptr();
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
@ -132,13 +152,25 @@ namespace MWLua
|
|||||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
ai.stack(MWMechanics::AiPursue(target.ptr()), ptr, cancelOther);
|
ai.stack(MWMechanics::AiPursue(target.ptr()), ptr, cancelOther);
|
||||||
};
|
};
|
||||||
selfAPI["_startAiFollow"] = [](SelfObject& self, const LObject& target, bool cancelOther) {
|
selfAPI["_startAiFollow"] = [](SelfObject& self, const LObject& target, sol::optional<LCell> cell,
|
||||||
|
float duration, const osg::Vec3f& dest, bool repeat, bool cancelOther) {
|
||||||
const MWWorld::Ptr& ptr = self.ptr();
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
ai.stack(MWMechanics::AiFollow(target.ptr()), ptr, cancelOther);
|
if (cell)
|
||||||
|
{
|
||||||
|
ai.stack(MWMechanics::AiFollow(target.ptr().getCellRef().getRefId(),
|
||||||
|
cell->mStore->getCell()->getNameId(), duration, dest.x(), dest.y(), dest.z(), repeat),
|
||||||
|
ptr, cancelOther);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ai.stack(MWMechanics::AiFollow(
|
||||||
|
target.ptr().getCellRef().getRefId(), duration, dest.x(), dest.y(), dest.z(), repeat),
|
||||||
|
ptr, cancelOther);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
selfAPI["_startAiEscort"] = [](SelfObject& self, const LObject& target, LCell cell, float duration,
|
selfAPI["_startAiEscort"] = [](SelfObject& self, const LObject& target, LCell cell, float duration,
|
||||||
const osg::Vec3f& dest, bool cancelOther) {
|
const osg::Vec3f& dest, bool repeat, bool cancelOther) {
|
||||||
const MWWorld::Ptr& ptr = self.ptr();
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
// TODO: change AiEscort implementation to accept ptr instead of a non-unique refId.
|
// TODO: change AiEscort implementation to accept ptr instead of a non-unique refId.
|
||||||
@ -146,23 +178,27 @@ namespace MWLua
|
|||||||
int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0));
|
int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0));
|
||||||
auto* esmCell = cell.mStore->getCell();
|
auto* esmCell = cell.mStore->getCell();
|
||||||
if (esmCell->isExterior())
|
if (esmCell->isExterior())
|
||||||
ai.stack(MWMechanics::AiEscort(refId, gameHoursDuration, dest.x(), dest.y(), dest.z(), false), ptr,
|
ai.stack(MWMechanics::AiEscort(refId, gameHoursDuration, dest.x(), dest.y(), dest.z(), repeat), ptr,
|
||||||
cancelOther);
|
cancelOther);
|
||||||
else
|
else
|
||||||
ai.stack(MWMechanics::AiEscort(
|
ai.stack(MWMechanics::AiEscort(
|
||||||
refId, esmCell->getNameId(), gameHoursDuration, dest.x(), dest.y(), dest.z(), false),
|
refId, esmCell->getNameId(), gameHoursDuration, dest.x(), dest.y(), dest.z(), repeat),
|
||||||
ptr, cancelOther);
|
ptr, cancelOther);
|
||||||
};
|
};
|
||||||
selfAPI["_startAiWander"] = [](SelfObject& self, int distance, float duration, bool cancelOther) {
|
selfAPI["_startAiWander"]
|
||||||
|
= [](SelfObject& self, int distance, int duration, sol::table luaIdle, bool repeat, bool cancelOther) {
|
||||||
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
|
std::vector<unsigned char> idle;
|
||||||
|
// Lua index starts at 1
|
||||||
|
for (size_t i = 1; i <= luaIdle.size(); i++)
|
||||||
|
idle.emplace_back(luaIdle.get<unsigned char>(i));
|
||||||
|
ai.stack(MWMechanics::AiWander(distance, duration, 0, idle, repeat), ptr, cancelOther);
|
||||||
|
};
|
||||||
|
selfAPI["_startAiTravel"] = [](SelfObject& self, const osg::Vec3f& target, bool repeat, bool cancelOther) {
|
||||||
const MWWorld::Ptr& ptr = self.ptr();
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
int gameHoursDuration = static_cast<int>(std::ceil(duration / 3600.0));
|
ai.stack(MWMechanics::AiTravel(target.x(), target.y(), target.z(), repeat), ptr, cancelOther);
|
||||||
ai.stack(MWMechanics::AiWander(distance, gameHoursDuration, 0, {}, false), ptr, cancelOther);
|
|
||||||
};
|
|
||||||
selfAPI["_startAiTravel"] = [](SelfObject& self, const osg::Vec3f& target, bool cancelOther) {
|
|
||||||
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, cancelOther);
|
|
||||||
};
|
};
|
||||||
selfAPI["_enableLuaAnimations"] = [](SelfObject& self, bool enable) {
|
selfAPI["_enableLuaAnimations"] = [](SelfObject& self, bool enable) {
|
||||||
const MWWorld::Ptr& ptr = self.ptr();
|
const MWWorld::Ptr& ptr = self.ptr();
|
||||||
|
@ -51,6 +51,8 @@ namespace MWMechanics
|
|||||||
|
|
||||||
osg::Vec3f getDestination() const override { return osg::Vec3f(mX, mY, mZ); }
|
osg::Vec3f getDestination() const override { return osg::Vec3f(mX, mY, mZ); }
|
||||||
|
|
||||||
|
std::optional<float> getDuration() const override { return mDuration; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string mCellId;
|
const std::string mCellId;
|
||||||
const float mX;
|
const float mX;
|
||||||
|
@ -110,6 +110,10 @@ namespace MWMechanics
|
|||||||
|
|
||||||
virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
|
virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
|
||||||
|
|
||||||
|
virtual std::optional<int> getDistance() const { return std::nullopt; }
|
||||||
|
|
||||||
|
virtual std::optional<float> getDuration() const { return std::nullopt; }
|
||||||
|
|
||||||
/// Return true if any loaded actor with this AI package must be active.
|
/// Return true if any loaded actor with this AI package must be active.
|
||||||
bool alwaysActive() const { return mOptions.mAlwaysActive; }
|
bool alwaysActive() const { return mOptions.mAlwaysActive; }
|
||||||
|
|
||||||
|
@ -113,6 +113,14 @@ namespace MWMechanics
|
|||||||
|
|
||||||
bool isStationary() const { return mDistance == 0; }
|
bool isStationary() const { return mDistance == 0; }
|
||||||
|
|
||||||
|
std::optional<int> getDistance() const override { return mDistance; }
|
||||||
|
|
||||||
|
std::optional<float> getDuration() const override { return static_cast<float>(mDuration); }
|
||||||
|
|
||||||
|
const std::vector<unsigned char>& getIdle() const { return mIdle; }
|
||||||
|
|
||||||
|
static std::string_view getIdleGroupName(size_t index) { return sIdleSelectToGroupName[index]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void stopWalking(const MWWorld::Ptr& actor);
|
void stopWalking(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
@ -99,6 +99,18 @@ Follow another actor.
|
|||||||
* - target
|
* - target
|
||||||
- `GameObject <openmw_core.html##(GameObject)>`_ [required]
|
- `GameObject <openmw_core.html##(GameObject)>`_ [required]
|
||||||
- the actor to follow
|
- the actor to follow
|
||||||
|
* - destCell
|
||||||
|
- Cell [optional]
|
||||||
|
- the destination cell
|
||||||
|
* - duration
|
||||||
|
- number [optional]
|
||||||
|
- duration in game time (will be rounded up to the next hour)
|
||||||
|
* - destPosition
|
||||||
|
- `3d vector <openmw_util.html##(Vector3)>`_ [optional]
|
||||||
|
- the destination point
|
||||||
|
* - isRepeat
|
||||||
|
- boolean [optional]
|
||||||
|
- Will the package repeat (true or false)
|
||||||
|
|
||||||
Escort
|
Escort
|
||||||
------
|
------
|
||||||
@ -126,6 +138,9 @@ Escort another actor to the given location.
|
|||||||
* - duration
|
* - duration
|
||||||
- number [optional]
|
- number [optional]
|
||||||
- duration in game time (will be rounded up to the next hour)
|
- duration in game time (will be rounded up to the next hour)
|
||||||
|
* - isRepeat
|
||||||
|
- boolean [optional]
|
||||||
|
- Will the package repeat (true or false)
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
@ -136,6 +151,7 @@ Escort another actor to the given location.
|
|||||||
target = object.self,
|
target = object.self,
|
||||||
destPosition = util.vector3(x, y, z),
|
destPosition = util.vector3(x, y, z),
|
||||||
duration = 3 * time.hour,
|
duration = 3 * time.hour,
|
||||||
|
isRepeat = true
|
||||||
})
|
})
|
||||||
|
|
||||||
Wander
|
Wander
|
||||||
@ -158,6 +174,34 @@ Wander nearby current position.
|
|||||||
* - duration
|
* - duration
|
||||||
- number [optional]
|
- number [optional]
|
||||||
- duration in game time (will be rounded up to the next hour)
|
- duration in game time (will be rounded up to the next hour)
|
||||||
|
* - idle
|
||||||
|
- table [optional]
|
||||||
|
- Idle chance values, up to 8
|
||||||
|
* - isRepeat
|
||||||
|
- boolean [optional]
|
||||||
|
- Will the package repeat (true or false)
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
local idleTable = {
|
||||||
|
idle2 = 60,
|
||||||
|
idle3 = 50,
|
||||||
|
idle4 = 40,
|
||||||
|
idle5 = 30,
|
||||||
|
idle6 = 20,
|
||||||
|
idle7 = 10,
|
||||||
|
idle8 = 0,
|
||||||
|
idle9 = 25
|
||||||
|
}
|
||||||
|
actor:sendEvent('StartAIPackage', {
|
||||||
|
type = 'Wander',
|
||||||
|
distance = 5000,
|
||||||
|
duration = 5 * time.hour,
|
||||||
|
idle = idleTable,
|
||||||
|
isRepeat = true
|
||||||
|
})
|
||||||
|
|
||||||
Travel
|
Travel
|
||||||
------
|
------
|
||||||
@ -176,4 +220,6 @@ Go to given location.
|
|||||||
* - destPosition
|
* - destPosition
|
||||||
- `3d vector <openmw_util.html##(Vector3)>`_ [required]
|
- `3d vector <openmw_util.html##(Vector3)>`_ [required]
|
||||||
- the point to travel to
|
- the point to travel to
|
||||||
|
* - isRepeat
|
||||||
|
- boolean [optional]
|
||||||
|
- Will the package repeat (true or false)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
local self = require('openmw.self')
|
local self = require('openmw.self')
|
||||||
local interfaces = require('openmw.interfaces')
|
local interfaces = require('openmw.interfaces')
|
||||||
|
local util = require('openmw.util')
|
||||||
|
|
||||||
local function startPackage(args)
|
local function startPackage(args)
|
||||||
local cancelOther = args.cancelOther
|
local cancelOther = args.cancelOther
|
||||||
@ -12,16 +13,34 @@ local function startPackage(args)
|
|||||||
self:_startAiPursue(args.target, cancelOther)
|
self:_startAiPursue(args.target, cancelOther)
|
||||||
elseif args.type == 'Follow' then
|
elseif args.type == 'Follow' then
|
||||||
if not args.target then error("target required") end
|
if not args.target then error("target required") end
|
||||||
self:_startAiFollow(args.target, cancelOther)
|
self:_startAiFollow(args.target, args.cellId, args.duration or 0, args.destPosition or util.vector3(0, 0, 0), args.isRepeat or false, cancelOther)
|
||||||
elseif args.type == 'Escort' then
|
elseif args.type == 'Escort' then
|
||||||
if not args.target then error("target required") end
|
if not args.target then error("target required") end
|
||||||
if not args.destPosition then error("destPosition 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, cancelOther)
|
self:_startAiEscort(args.target, args.destCell or self.cell, args.duration or 0, args.destPosition, cancelOther)
|
||||||
elseif args.type == 'Wander' then
|
elseif args.type == 'Wander' then
|
||||||
self:_startAiWander(args.distance or 0, args.duration or 0, cancelOther)
|
local key = "idle"
|
||||||
|
local idle = {}
|
||||||
|
local duration = 0
|
||||||
|
for i = 2, 9 do
|
||||||
|
local val = args.idle[key .. i]
|
||||||
|
if val == nil then
|
||||||
|
idle[i-1] = 0
|
||||||
|
else
|
||||||
|
local v = tonumber(val) or 0
|
||||||
|
if v < 0 or v > 100 then
|
||||||
|
error("idle values cannot exceed 100")
|
||||||
|
end
|
||||||
|
idle[i-1] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if args.duration then
|
||||||
|
duration = args.duration / 3600
|
||||||
|
end
|
||||||
|
self:_startAiWander(args.distance or 0, duration, idle, args.isRepeat or false, cancelOther)
|
||||||
elseif args.type == 'Travel' then
|
elseif args.type == 'Travel' then
|
||||||
if not args.destPosition then error("destPosition required") end
|
if not args.destPosition then error("destPosition required") end
|
||||||
self:_startAiTravel(args.destPosition, cancelOther)
|
self:_startAiTravel(args.destPosition, args.isRepeat or false, cancelOther)
|
||||||
else
|
else
|
||||||
error('Unsupported AI Package: ' .. args.type)
|
error('Unsupported AI Package: ' .. args.type)
|
||||||
end
|
end
|
||||||
@ -47,6 +66,10 @@ return {
|
|||||||
-- @field openmw.core#GameObject target Target (usually an actor) of the AI package (can be nil).
|
-- @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 #boolean sideWithTarget Whether to help the target in combat (true or false).
|
||||||
-- @field openmw.util#Vector3 destPosition Destination point of the AI package.
|
-- @field openmw.util#Vector3 destPosition Destination point of the AI package.
|
||||||
|
-- @field #number distance Distance value (can be nil).
|
||||||
|
-- @field #number duration Duration value (can be nil).
|
||||||
|
-- @field #table idle Idle value (can be nil).
|
||||||
|
-- @field #boolean isRepeat Should this package be repeated (true or false).
|
||||||
|
|
||||||
--- Return the currently active AI package (or `nil` if there are no AI packages).
|
--- Return the currently active AI package (or `nil` if there are no AI packages).
|
||||||
-- @function [parent=#AI] getActivePackage
|
-- @function [parent=#AI] getActivePackage
|
||||||
|
Loading…
x
Reference in New Issue
Block a user