mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 07:53:53 +00:00
Merge branch 'lua_raycasting' into 'master'
Lua raycasting Closes #6098 See merge request OpenMW/openmw!1175
This commit is contained in:
commit
5794a3b346
11 changed files with 247 additions and 36 deletions
|
@ -58,7 +58,7 @@ add_openmw_dir (mwscript
|
||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp actions object worldview userdataserializer eventqueue query
|
luamanagerimp actions object worldview userdataserializer eventqueue query
|
||||||
luabindings localscripts objectbindings cellbindings asyncbindings settingsbindings
|
luabindings localscripts objectbindings cellbindings asyncbindings settingsbindings
|
||||||
camerabindings uibindings inputbindings
|
camerabindings uibindings inputbindings nearbybindings
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwsound
|
add_openmw_dir (mwsound
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "luabindings.hpp"
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
|
||||||
namespace sol
|
namespace sol
|
||||||
{
|
{
|
||||||
template <>
|
template <>
|
||||||
|
@ -48,11 +50,16 @@ namespace MWLua
|
||||||
asyncId.mContainer->setupUnsavableTimer(
|
asyncId.mContainer->setupUnsavableTimer(
|
||||||
TimeUnit::HOURS, world->getGameTimeInHours() + delay, asyncId.mScript, std::move(callback));
|
TimeUnit::HOURS, world->getGameTimeInHours() + delay, asyncId.mScript, std::move(callback));
|
||||||
};
|
};
|
||||||
|
api["callback"] = [](const AsyncPackageId& asyncId, sol::function fn)
|
||||||
|
{
|
||||||
|
return Callback{std::move(fn), asyncId.mHiddenData};
|
||||||
|
};
|
||||||
|
|
||||||
auto initializer = [](sol::table hiddenData)
|
auto initializer = [](sol::table hiddenData)
|
||||||
{
|
{
|
||||||
LuaUtil::ScriptsContainer::ScriptId id = hiddenData[LuaUtil::ScriptsContainer::ScriptId::KEY];
|
LuaUtil::ScriptsContainer::ScriptId id = hiddenData[LuaUtil::ScriptsContainer::ScriptId::KEY];
|
||||||
return AsyncPackageId{id.mContainer, id.mPath};
|
hiddenData[Callback::SCRIPT_NAME_KEY] = id.toString();
|
||||||
|
return AsyncPackageId{id.mContainer, id.mPath, hiddenData};
|
||||||
};
|
};
|
||||||
return sol::make_object(context.mLua->sol(), initializer);
|
return sol::make_object(context.mLua->sol(), initializer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
auto* lua = context.mLua;
|
auto* lua = context.mLua;
|
||||||
sol::table api(lua->sol(), sol::create);
|
sol::table api(lua->sol(), sol::create);
|
||||||
api["API_REVISION"] = 5;
|
api["API_REVISION"] = 6;
|
||||||
api["quit"] = [lua]()
|
api["quit"] = [lua]()
|
||||||
{
|
{
|
||||||
std::string traceback = lua->sol()["debug"]["traceback"]().get<std::string>();
|
std::string traceback = lua->sol()["debug"]["traceback"]().get<std::string>();
|
||||||
|
@ -110,36 +110,6 @@ namespace MWLua
|
||||||
return LuaUtil::makeReadOnly(api);
|
return LuaUtil::makeReadOnly(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
sol::table initNearbyPackage(const Context& context)
|
|
||||||
{
|
|
||||||
sol::table api(context.mLua->sol(), sol::create);
|
|
||||||
WorldView* worldView = context.mWorldView;
|
|
||||||
api["activators"] = LObjectList{worldView->getActivatorsInScene()};
|
|
||||||
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
|
||||||
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
|
||||||
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
|
||||||
api["items"] = LObjectList{worldView->getItemsInScene()};
|
|
||||||
api["selectObjects"] = [context](const Queries::Query& query)
|
|
||||||
{
|
|
||||||
ObjectIdList list;
|
|
||||||
WorldView* worldView = context.mWorldView;
|
|
||||||
if (query.mQueryType == "activators")
|
|
||||||
list = worldView->getActivatorsInScene();
|
|
||||||
else if (query.mQueryType == "actors")
|
|
||||||
list = worldView->getActorsInScene();
|
|
||||||
else if (query.mQueryType == "containers")
|
|
||||||
list = worldView->getContainersInScene();
|
|
||||||
else if (query.mQueryType == "doors")
|
|
||||||
list = worldView->getDoorsInScene();
|
|
||||||
else if (query.mQueryType == "items")
|
|
||||||
list = worldView->getItemsInScene();
|
|
||||||
return LObjectList{selectObjectsFromList(query, list, context)};
|
|
||||||
// TODO: Maybe use sqlite
|
|
||||||
// return LObjectList{worldView->selectObjects(query, true)};
|
|
||||||
};
|
|
||||||
return LuaUtil::makeReadOnly(api);
|
|
||||||
}
|
|
||||||
|
|
||||||
sol::table initQueryPackage(const Context& context)
|
sol::table initQueryPackage(const Context& context)
|
||||||
{
|
{
|
||||||
Queries::registerQueryBindings(context.mLua->sol());
|
Queries::registerQueryBindings(context.mLua->sol());
|
||||||
|
|
|
@ -21,11 +21,13 @@ namespace MWLua
|
||||||
|
|
||||||
sol::table initCorePackage(const Context&);
|
sol::table initCorePackage(const Context&);
|
||||||
sol::table initWorldPackage(const Context&);
|
sol::table initWorldPackage(const Context&);
|
||||||
sol::table initNearbyPackage(const Context&);
|
|
||||||
sol::table initQueryPackage(const Context&);
|
sol::table initQueryPackage(const Context&);
|
||||||
|
|
||||||
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
|
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
|
||||||
|
|
||||||
|
// Implemented in nearbybindings.cpp
|
||||||
|
sol::table initNearbyPackage(const Context&);
|
||||||
|
|
||||||
// Implemented in objectbindings.cpp
|
// Implemented in objectbindings.cpp
|
||||||
void initObjectBindingsForLocalScripts(const Context&);
|
void initObjectBindingsForLocalScripts(const Context&);
|
||||||
void initObjectBindingsForGlobalScripts(const Context&);
|
void initObjectBindingsForGlobalScripts(const Context&);
|
||||||
|
@ -45,9 +47,9 @@ namespace MWLua
|
||||||
// Implemented in asyncbindings.cpp
|
// Implemented in asyncbindings.cpp
|
||||||
struct AsyncPackageId
|
struct AsyncPackageId
|
||||||
{
|
{
|
||||||
// TODO: add ObjectId mLocalObject;
|
|
||||||
LuaUtil::ScriptsContainer* mContainer;
|
LuaUtil::ScriptsContainer* mContainer;
|
||||||
std::string mScript;
|
std::string mScript;
|
||||||
|
sol::table mHiddenData;
|
||||||
};
|
};
|
||||||
sol::function getAsyncPackageInitializer(const Context&);
|
sol::function getAsyncPackageInitializer(const Context&);
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,16 @@ namespace MWLua
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Callback::operator()(sol::object arg) const
|
||||||
|
{
|
||||||
|
if (mHiddenData[LuaUtil::ScriptsContainer::ScriptId::KEY] != sol::nil)
|
||||||
|
LuaUtil::call(mFunc, std::move(arg));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(Debug::Debug) << "Ignored callback to removed script " << mHiddenData.get<std::string>(SCRIPT_NAME_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LuaManager::update(bool paused, float dt)
|
void LuaManager::update(bool paused, float dt)
|
||||||
{
|
{
|
||||||
ObjectRegistry* objectRegistry = mWorldView.getObjectRegistry();
|
ObjectRegistry* objectRegistry = mWorldView.getObjectRegistry();
|
||||||
|
@ -126,6 +136,11 @@ namespace MWLua
|
||||||
<< ". Object not found or has no attached scripts";
|
<< ". Object not found or has no attached scripts";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run queued callbacks
|
||||||
|
for (CallbackWithData& c : mQueuedCallbacks)
|
||||||
|
c.mCallback(c.mArg);
|
||||||
|
mQueuedCallbacks.clear();
|
||||||
|
|
||||||
// Engine handlers in local scripts
|
// Engine handlers in local scripts
|
||||||
PlayerScripts* playerScripts = dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
|
PlayerScripts* playerScripts = dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
|
||||||
if (playerScripts)
|
if (playerScripts)
|
||||||
|
|
|
@ -19,6 +19,19 @@
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Wrapper for a single-argument Lua function.
|
||||||
|
// Holds information about the script the function belongs to.
|
||||||
|
// Needed to prevent callback calls if the script was removed.
|
||||||
|
struct Callback
|
||||||
|
{
|
||||||
|
static constexpr std::string_view SCRIPT_NAME_KEY = "name";
|
||||||
|
|
||||||
|
sol::function mFunc;
|
||||||
|
sol::table mHiddenData;
|
||||||
|
|
||||||
|
void operator()(sol::object arg) const;
|
||||||
|
};
|
||||||
|
|
||||||
class LuaManager : public MWBase::LuaManager
|
class LuaManager : public MWBase::LuaManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -67,6 +80,18 @@ namespace MWLua
|
||||||
// Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script.
|
// Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script.
|
||||||
void reloadAllScripts() override;
|
void reloadAllScripts() override;
|
||||||
|
|
||||||
|
// Used to call Lua callbacks from C++
|
||||||
|
void queueCallback(Callback callback, sol::object arg) { mQueuedCallbacks.push_back({std::move(callback), std::move(arg)}); }
|
||||||
|
|
||||||
|
// Wraps Lua callback into an std::function.
|
||||||
|
// NOTE: Resulted function is not thread safe. Can not be used while LuaManager::update() or
|
||||||
|
// any other Lua-related function is running.
|
||||||
|
template <class Arg>
|
||||||
|
std::function<void(Arg)> wrapLuaCallback(const Callback& c)
|
||||||
|
{
|
||||||
|
return [this, c](Arg arg) { this->queueCallback(c, sol::make_object(c.mFunc.lua_state(), arg)); };
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr);
|
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
@ -100,6 +125,13 @@ namespace MWLua
|
||||||
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
|
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
|
||||||
std::vector<ObjectId> mActorAddedEvents;
|
std::vector<ObjectId> mActorAddedEvents;
|
||||||
|
|
||||||
|
struct CallbackWithData
|
||||||
|
{
|
||||||
|
Callback mCallback;
|
||||||
|
sol::object mArg;
|
||||||
|
};
|
||||||
|
std::vector<CallbackWithData> mQueuedCallbacks;
|
||||||
|
|
||||||
struct LocalEngineEvent
|
struct LocalEngineEvent
|
||||||
{
|
{
|
||||||
ObjectId mDest;
|
ObjectId mDest;
|
||||||
|
|
122
apps/openmw/mwlua/nearbybindings.cpp
Normal file
122
apps/openmw/mwlua/nearbybindings.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/queries/luabindings.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwphysics/raycasting.hpp"
|
||||||
|
|
||||||
|
#include "worldview.hpp"
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWPhysics::RayCastingResult> : std::false_type {};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initNearbyPackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
WorldView* worldView = context.mWorldView;
|
||||||
|
|
||||||
|
sol::usertype<MWPhysics::RayCastingResult> rayResult =
|
||||||
|
context.mLua->sol().new_usertype<MWPhysics::RayCastingResult>("RayCastingResult");
|
||||||
|
rayResult["hit"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) { return r.mHit; });
|
||||||
|
rayResult["hitPos"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) -> sol::optional<osg::Vec3f>
|
||||||
|
{
|
||||||
|
if (r.mHit)
|
||||||
|
return r.mHitPos;
|
||||||
|
else
|
||||||
|
return sol::nullopt;
|
||||||
|
});
|
||||||
|
rayResult["hitNormal"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) -> sol::optional<osg::Vec3f>
|
||||||
|
{
|
||||||
|
if (r.mHit)
|
||||||
|
return r.mHitNormal;
|
||||||
|
else
|
||||||
|
return sol::nullopt;
|
||||||
|
});
|
||||||
|
rayResult["hitObject"] = sol::readonly_property([worldView](const MWPhysics::RayCastingResult& r) -> sol::optional<LObject>
|
||||||
|
{
|
||||||
|
if (r.mHitObject.isEmpty())
|
||||||
|
return sol::nullopt;
|
||||||
|
else
|
||||||
|
return LObject(getId(r.mHitObject), worldView->getObjectRegistry());
|
||||||
|
});
|
||||||
|
|
||||||
|
constexpr int defaultCollisionType = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap |
|
||||||
|
MWPhysics::CollisionType_Actor | MWPhysics::CollisionType_Door;
|
||||||
|
api["COLLISION_TYPE"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||||
|
"World", MWPhysics::CollisionType_World,
|
||||||
|
"Door", MWPhysics::CollisionType_Door,
|
||||||
|
"Actor", MWPhysics::CollisionType_Actor,
|
||||||
|
"HeightMap", MWPhysics::CollisionType_HeightMap,
|
||||||
|
"Projectile", MWPhysics::CollisionType_Projectile,
|
||||||
|
"Water", MWPhysics::CollisionType_Water,
|
||||||
|
"Default", defaultCollisionType));
|
||||||
|
|
||||||
|
api["castRay"] = [defaultCollisionType](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ignore;
|
||||||
|
int collisionType = defaultCollisionType;
|
||||||
|
float radius = 0;
|
||||||
|
if (options)
|
||||||
|
{
|
||||||
|
sol::optional<LObject> ignoreObj = options->get<sol::optional<LObject>>("ignore");
|
||||||
|
if (ignoreObj) ignore = ignoreObj->ptr();
|
||||||
|
collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType);
|
||||||
|
radius = options->get<sol::optional<float>>("radius").value_or(0);
|
||||||
|
}
|
||||||
|
const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
|
||||||
|
if (radius <= 0)
|
||||||
|
return rayCasting->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ignore.isEmpty()) throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0");
|
||||||
|
return rayCasting->castSphere(from, to, radius, collisionType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// TODO: async raycasting
|
||||||
|
/*api["asyncCastRay"] = [luaManager = context.mLuaManager, defaultCollisionType](
|
||||||
|
const Callback& luaCallback, const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options)
|
||||||
|
{
|
||||||
|
std::function<void(MWPhysics::RayCastingResult)> callback =
|
||||||
|
luaManager->wrapLuaCallback<MWPhysics::RayCastingResult>(luaCallback);
|
||||||
|
MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
|
||||||
|
|
||||||
|
// Handle options the same way as in `castRay`.
|
||||||
|
|
||||||
|
// NOTE: `callback` is not thread safe. If MWPhysics works in separate thread, it must put results to a queue
|
||||||
|
// and use this callback from the main thread at the beginning of the next frame processing.
|
||||||
|
rayCasting->asyncCastRay(callback, from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
|
||||||
|
};*/
|
||||||
|
|
||||||
|
api["activators"] = LObjectList{worldView->getActivatorsInScene()};
|
||||||
|
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||||
|
api["containers"] = LObjectList{worldView->getContainersInScene()};
|
||||||
|
api["doors"] = LObjectList{worldView->getDoorsInScene()};
|
||||||
|
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||||
|
api["selectObjects"] = [context](const Queries::Query& query)
|
||||||
|
{
|
||||||
|
ObjectIdList list;
|
||||||
|
WorldView* worldView = context.mWorldView;
|
||||||
|
if (query.mQueryType == "activators")
|
||||||
|
list = worldView->getActivatorsInScene();
|
||||||
|
else if (query.mQueryType == "actors")
|
||||||
|
list = worldView->getActorsInScene();
|
||||||
|
else if (query.mQueryType == "containers")
|
||||||
|
list = worldView->getContainersInScene();
|
||||||
|
else if (query.mQueryType == "doors")
|
||||||
|
list = worldView->getDoorsInScene();
|
||||||
|
else if (query.mQueryType == "items")
|
||||||
|
list = worldView->getItemsInScene();
|
||||||
|
return LObjectList{selectObjectsFromList(query, list, context)};
|
||||||
|
// TODO: Maybe use sqlite
|
||||||
|
// return LObjectList{worldView->selectObjects(query, true)};
|
||||||
|
};
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,15 @@ namespace LuaUtil
|
||||||
static constexpr std::string_view REGISTERED_TIMER_CALLBACKS = "_timers";
|
static constexpr std::string_view REGISTERED_TIMER_CALLBACKS = "_timers";
|
||||||
static constexpr std::string_view TEMPORARY_TIMER_CALLBACKS = "_temp_timers";
|
static constexpr std::string_view TEMPORARY_TIMER_CALLBACKS = "_temp_timers";
|
||||||
|
|
||||||
|
std::string ScriptsContainer::ScriptId::toString() const
|
||||||
|
{
|
||||||
|
std::string res = mContainer->mNamePrefix;
|
||||||
|
res.push_back('[');
|
||||||
|
res.append(mPath);
|
||||||
|
res.push_back(']');
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
ScriptsContainer::ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix) : mNamePrefix(namePrefix), mLua(*lua)
|
ScriptsContainer::ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix) : mNamePrefix(namePrefix), mLua(*lua)
|
||||||
{
|
{
|
||||||
registerEngineHandlers({&mUpdateHandlers});
|
registerEngineHandlers({&mUpdateHandlers});
|
||||||
|
@ -81,6 +90,7 @@ namespace LuaUtil
|
||||||
auto scriptIter = mScripts.find(path);
|
auto scriptIter = mScripts.find(path);
|
||||||
if (scriptIter == mScripts.end())
|
if (scriptIter == mScripts.end())
|
||||||
return false; // no such script
|
return false; // no such script
|
||||||
|
scriptIter->second.mHiddenData[ScriptId::KEY] = sol::nil;
|
||||||
sol::object& script = scriptIter->second.mInterface;
|
sol::object& script = scriptIter->second.mInterface;
|
||||||
if (getFieldOrNil(script, INTERFACE_NAME) != sol::nil)
|
if (getFieldOrNil(script, INTERFACE_NAME) != sol::nil)
|
||||||
{
|
{
|
||||||
|
@ -320,6 +330,8 @@ namespace LuaUtil
|
||||||
|
|
||||||
void ScriptsContainer::removeAllScripts()
|
void ScriptsContainer::removeAllScripts()
|
||||||
{
|
{
|
||||||
|
for (auto& [_, script] : mScripts)
|
||||||
|
script.mHiddenData[ScriptId::KEY] = sol::nil;
|
||||||
mScripts.clear();
|
mScripts.clear();
|
||||||
mScriptOrder.clear();
|
mScriptOrder.clear();
|
||||||
for (auto& [_, handlers] : mEngineHandlers)
|
for (auto& [_, handlers] : mEngineHandlers)
|
||||||
|
|
|
@ -66,6 +66,8 @@ namespace LuaUtil
|
||||||
|
|
||||||
ScriptsContainer* mContainer;
|
ScriptsContainer* mContainer;
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
};
|
};
|
||||||
using TimeUnit = ESM::LuaTimer::TimeUnit;
|
using TimeUnit = ESM::LuaTimer::TimeUnit;
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ namespace LuaUtil
|
||||||
ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix);
|
ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix);
|
||||||
ScriptsContainer(const ScriptsContainer&) = delete;
|
ScriptsContainer(const ScriptsContainer&) = delete;
|
||||||
ScriptsContainer(ScriptsContainer&&) = delete;
|
ScriptsContainer(ScriptsContainer&&) = delete;
|
||||||
virtual ~ScriptsContainer() {}
|
virtual ~ScriptsContainer() { removeAllScripts(); }
|
||||||
|
|
||||||
// Adds package that will be available (via `require`) for all scripts in the container.
|
// Adds package that will be available (via `require`) for all scripts in the container.
|
||||||
// Automatically applies LuaUtil::makeReadOnly to the package.
|
// Automatically applies LuaUtil::makeReadOnly to the package.
|
||||||
|
|
|
@ -48,5 +48,12 @@
|
||||||
-- @param #number delay
|
-- @param #number delay
|
||||||
-- @param #function func
|
-- @param #function func
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Wraps Lua function with `Callback` object that can be used in async API calls.
|
||||||
|
-- @function [parent=#async] callback
|
||||||
|
-- @param self
|
||||||
|
-- @param #function func
|
||||||
|
-- @return #Callback
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|
|
@ -32,5 +32,47 @@
|
||||||
-- @param openmw.query#Query query
|
-- @param openmw.query#Query query
|
||||||
-- @return openmw.core#ObjectList
|
-- @return openmw.core#ObjectList
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- @type COLLISION_TYPE
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number World
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number Door
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number Actor
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number HeightMap
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number Projectile
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number Water
|
||||||
|
-- @field [parent=#COLLISION_TYPE] #number Default Used by deafult: World+Door+Actor+HeightMap
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Collision types that are used in `castRay`.
|
||||||
|
-- Several types can be combined with '+'.
|
||||||
|
-- @field [parent=#nearby] #COLLISION_TYPE COLLISION_TYPE
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Result of raycasing
|
||||||
|
-- @type RayCastingResult
|
||||||
|
-- @field [parent=#RayCastingResult] #boolean hit Is there a collision? (true/false)
|
||||||
|
-- @field [parent=#RayCastingResult] openmw.util#Vector3 hitPos Position of the collision point (nil if no collision)
|
||||||
|
-- @field [parent=#RayCastingResult] openmw.util#Vector3 hitNormal Normal to the surface in the collision point (nil if no collision)
|
||||||
|
-- @field [parent=#RayCastingResult] openmw.core#GameObject hitObject The object the ray has collided with (can be nil)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Cast ray from one point to another and return the first collision.
|
||||||
|
-- @function [parent=#nearby] castRay
|
||||||
|
-- @param openmw.util#Vector3 from Start point of the ray.
|
||||||
|
-- @param openmw.util#Vector3 to End point of the ray.
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
-- `ignore` - an object to ignore (specify here the source of the ray);
|
||||||
|
-- `collisionType` - object types to work with (see @{openmw.nearby#COLLISION_TYPE}), several types can be combined with '+';
|
||||||
|
-- `radius` - the radius of the ray (zero by default). If not zero then castRay actually casts a sphere with given radius.
|
||||||
|
-- NOTE: currently `ignore` is not supported if `radius>0`.
|
||||||
|
-- @return #RayCastingResult
|
||||||
|
-- @usage if nearby.castRay(pointA, pointB).hit then print('obstacle between A and B') end
|
||||||
|
-- @usage local res = nearby.castRay(self.position, enemy.position, {ignore=self})
|
||||||
|
-- if res.hitObject and res.hitObject ~= enemy then obstacle = res.hitObject end
|
||||||
|
-- @usage local res = nearby.castRay(self.position, targetPos, {
|
||||||
|
-- collisionType=nearby.COLLISION_TYPE.HeightMap + nearby.COLLISION_TYPE.Water,
|
||||||
|
-- radius = 10,
|
||||||
|
-- })
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue