Merge branch 'master' of gitlab.com:openmw/openmw into rtdfix

macos_ci_fix
Zackhasacat 7 months ago
commit e7c9c57017

@ -109,6 +109,7 @@
Feature #7546: Start the game on Fredas
Feature #7568: Uninterruptable scripted music
Feature #7618: Show the player character's health in the save details
Feature #7634: Support NiParticleBomb
Task #5896: Do not use deprecated MyGUI properties
Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector

@ -7,6 +7,7 @@
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwscript/globalscripts.hpp"
#include "../mwworld/esmstore.hpp"
#include "object.hpp"
@ -43,6 +44,10 @@ namespace sol
struct is_automagical<MWLua::MWScriptVariables> : std::false_type
{
};
template <>
struct is_automagical<ESM::Global> : std::false_type
{
};
}
namespace MWLua
@ -76,6 +81,7 @@ namespace MWLua
// api["getGlobalScripts"] = [](std::string_view recordId) -> list of scripts
// api["getLocalScripts"] = [](const GObject& obj) -> list of scripts
sol::state_view& lua = context.mLua->sol();
sol::usertype<MWScriptRef> mwscript = context.mLua->sol().new_usertype<MWScriptRef>("MWScript");
sol::usertype<MWScriptVariables> mwscriptVars
= context.mLua->sol().new_usertype<MWScriptVariables>("MWScriptVariables");
@ -108,6 +114,51 @@ namespace MWLua
"No variable \"" + std::string(var) + "\" in mwscript " + s.mRef.mId.toDebugString());
};
using GlobalStore = MWWorld::Store<ESM::Global>;
sol::usertype<GlobalStore> globalStoreT = lua.new_usertype<GlobalStore>("ESM3_GlobalStore");
const GlobalStore* globalStore = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Global>();
globalStoreT[sol::meta_function::to_string] = [](const GlobalStore& store) {
return "ESM3_GlobalStore{" + std::to_string(store.getSize()) + " globals}";
};
globalStoreT[sol::meta_function::length] = [](const GlobalStore& store) { return store.getSize(); };
globalStoreT[sol::meta_function::index]
= sol::overload([](const GlobalStore& store, std::string_view globalId) -> sol::optional<float> {
auto g = store.search(ESM::RefId::deserializeText(globalId));
if (g == nullptr)
return sol::nullopt;
char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId);
if (varType == 's' || varType == 'l')
{
return static_cast<float>(MWBase::Environment::get().getWorld()->getGlobalInt(globalId));
}
else
{
return MWBase::Environment::get().getWorld()->getGlobalFloat(globalId);
}
});
globalStoreT[sol::meta_function::new_index]
= sol::overload([](const GlobalStore& store, std::string_view globalId, float val) {
auto g = store.search(ESM::RefId::deserializeText(globalId));
if (g == nullptr)
return;
char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId);
if (varType == 's' || varType == 'l')
{
MWBase::Environment::get().getWorld()->setGlobalInt(globalId, static_cast<int>(val));
}
else
{
MWBase::Environment::get().getWorld()->setGlobalFloat(globalId, val);
}
});
globalStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
globalStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
api["getGlobalVariables"] = [globalStore](sol::optional<GObject> player) {
if (player.has_value() && player->ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr())
throw std::runtime_error("First argument must either be a player or be missing");
return globalStore;
};
return LuaUtil::makeReadOnly(api);
}

@ -92,15 +92,21 @@ namespace LuaUi
w->widget()->detachFromWidget();
}
void WidgetExtension::updateVisible()
{
// workaround for MyGUI bug
// parent visibility doesn't affect added children
MyGUI::Widget* widget = this->widget();
MyGUI::Widget* parent = widget->getParent();
bool inheritedVisible = widget->getVisible() && (parent == nullptr || parent->getInheritedVisible());
widget->setVisible(inheritedVisible);
}
void WidgetExtension::attach(WidgetExtension* ext)
{
ext->mParent = this;
ext->mTemplateChild = false;
ext->widget()->attachToWidget(mSlot->widget());
// workaround for MyGUI bug
// parent visibility doesn't affect added children
ext->widget()->setVisible(!ext->widget()->getVisible());
ext->widget()->setVisible(!ext->widget()->getVisible());
}
void WidgetExtension::attachTemplate(WidgetExtension* ext)
@ -108,10 +114,6 @@ namespace LuaUi
ext->mParent = this;
ext->mTemplateChild = true;
ext->widget()->attachToWidget(widget());
// workaround for MyGUI bug
// parent visibility doesn't affect added children
ext->widget()->setVisible(!ext->widget()->getVisible());
ext->widget()->setVisible(!ext->widget()->getVisible());
}
WidgetExtension* WidgetExtension::findDeep(std::string_view flagName)
@ -256,6 +258,8 @@ namespace LuaUi
void WidgetExtension::updateCoord()
{
updateVisible();
MyGUI::IntCoord oldCoord = mWidget->getCoord();
MyGUI::IntCoord newCoord = calculateCoord();

@ -173,6 +173,8 @@ namespace LuaUi
void focusLoss(MyGUI::Widget*, MyGUI::Widget*);
std::optional<std::function<void(WidgetExtension*, MyGUI::IntCoord)>> mOnCoordChange;
void updateVisible();
};
class LuaWidget : public MyGUI::Widget, public WidgetExtension

@ -308,6 +308,7 @@ namespace Nif
// Modifiers, 4.0.0.2
{ "NiGravity", &construct<NiGravity, RC_NiGravity> },
{ "NiParticleBomb", &construct<NiParticleBomb, RC_NiParticleBomb> },
{ "NiParticleColorModifier", &construct<NiParticleColorModifier, RC_NiParticleColorModifier> },
{ "NiParticleGrowFade", &construct<NiParticleGrowFade, RC_NiParticleGrowFade> },
{ "NiParticleRotation", &construct<NiParticleRotation, RC_NiParticleRotation> },

@ -51,6 +51,20 @@ namespace Nif
nif->read(mDirection);
}
void NiParticleBomb::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
nif->read(mRange);
nif->read(mDuration);
nif->read(mStrength);
nif->read(mStartTime);
mDecayType = static_cast<DecayType>(nif->get<uint32_t>());
mSymmetryType = static_cast<SymmetryType>(nif->get<uint32_t>());
nif->read(mPosition);
nif->read(mDirection);
}
void NiParticleCollider::read(NIFStream* nif)
{
NiParticleModifier::read(nif);
@ -294,10 +308,10 @@ namespace Nif
mBombObject.read(nif);
nif->read(mBombAxis);
nif->read(mDecay);
nif->read(mDeltaV);
nif->read(mDecayType);
nif->read(mSymmetryType);
nif->read(mRange);
nif->read(mStrength);
mDecayType = static_cast<DecayType>(nif->get<uint32_t>());
mSymmetryType = static_cast<SymmetryType>(nif->get<uint32_t>());
}
void NiPSysBombModifier::post(Reader& nif)

@ -40,6 +40,20 @@ namespace Nif
Point = 1, // Fixed origin
};
enum class DecayType : uint32_t
{
None = 0, // f(Distance) = 1.0
Linear = 1, // f(Distance) = (Range - Distance) / Range
Exponential = 2, // f(Distance) = exp(-Distance / Range)
};
enum class SymmetryType : uint32_t
{
Spherical = 0,
Cylindrical = 1, // Perpendicular to direction axis
Planar = 2, // Parallel to direction axis
};
struct NiGravity : NiParticleModifier
{
float mDecay{ 0.f };
@ -51,6 +65,20 @@ namespace Nif
void read(NIFStream* nif) override;
};
struct NiParticleBomb : NiParticleModifier
{
float mRange;
float mDuration;
float mStrength;
float mStartTime;
DecayType mDecayType;
SymmetryType mSymmetryType;
osg::Vec3f mPosition;
osg::Vec3f mDirection;
void read(NIFStream* nif);
};
struct NiParticleCollider : NiParticleModifier
{
float mBounceFactor;
@ -210,10 +238,10 @@ namespace Nif
{
NiAVObjectPtr mBombObject;
osg::Vec3f mBombAxis;
float mDecay;
float mDeltaV;
uint32_t mDecayType;
uint32_t mSymmetryType;
float mRange;
float mStrength;
DecayType mDecayType;
SymmetryType mSymmetryType;
void read(NIFStream* nif) override;
void post(Reader& nif) override;

@ -206,6 +206,7 @@ namespace Nif
RC_NiMultiTargetTransformController,
RC_NiNode,
RC_NiPalette,
RC_NiParticleBomb,
RC_NiParticleColorModifier,
RC_NiParticleGrowFade,
RC_NiParticleRotation,

@ -1092,6 +1092,18 @@ namespace NifOsg
const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(modifier.getPtr());
program->addOperator(new GravityAffector(gr));
}
else if (modifier->recType == Nif::RC_NiParticleBomb)
{
auto bomb = static_cast<const Nif::NiParticleBomb*>(modifier.getPtr());
osg::ref_ptr<osgParticle::ModularProgram> bombProgram(new osgParticle::ModularProgram);
attachTo->addChild(bombProgram);
bombProgram->setParticleSystem(partsys);
bombProgram->setReferenceFrame(rf);
bombProgram->setStartTime(bomb->mStartTime);
bombProgram->setLifeTime(bomb->mDuration);
bombProgram->setEndless(false);
bombProgram->addOperator(new ParticleBomb(bomb));
}
else if (modifier->recType == Nif::RC_NiParticleColorModifier)
{
const Nif::NiParticleColorModifier* cl

@ -346,6 +346,84 @@ namespace NifOsg
}
}
ParticleBomb::ParticleBomb(const Nif::NiParticleBomb* bomb)
: mRange(bomb->mRange)
, mStrength(bomb->mStrength)
, mDecayType(bomb->mDecayType)
, mSymmetryType(bomb->mSymmetryType)
, mPosition(bomb->mPosition)
, mDirection(bomb->mDirection)
{
}
ParticleBomb::ParticleBomb(const ParticleBomb& copy, const osg::CopyOp& copyop)
: osgParticle::Operator(copy, copyop)
{
mRange = copy.mRange;
mStrength = copy.mStrength;
mDecayType = copy.mDecayType;
mSymmetryType = copy.mSymmetryType;
mCachedWorldPosition = copy.mCachedWorldPosition;
mCachedWorldDirection = copy.mCachedWorldDirection;
}
void ParticleBomb::beginOperate(osgParticle::Program* program)
{
bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF);
mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition;
// We don't need the direction for Spherical bomb
if (mSymmetryType != Nif::SymmetryType::Spherical)
{
mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;
mCachedWorldDirection.normalize();
}
}
void ParticleBomb::operate(osgParticle::Particle* particle, double dt)
{
float decay = 1.f;
osg::Vec3f explosionDir;
osg::Vec3f particleDir = particle->getPosition() - mCachedWorldPosition;
float distance = particleDir.length();
particleDir.normalize();
switch (mDecayType)
{
case Nif::DecayType::None:
break;
case Nif::DecayType::Linear:
decay = 1.f - distance / mRange;
break;
case Nif::DecayType::Exponential:
decay = std::exp(-distance / mRange);
break;
}
if (decay <= 0.f)
return;
switch (mSymmetryType)
{
case Nif::SymmetryType::Spherical:
explosionDir = particleDir;
break;
case Nif::SymmetryType::Cylindrical:
explosionDir = particleDir - mCachedWorldDirection * (mCachedWorldDirection * particleDir);
explosionDir.normalize();
break;
case Nif::SymmetryType::Planar:
explosionDir = mCachedWorldDirection;
if (explosionDir * particleDir < 0)
explosionDir = -explosionDir;
break;
}
particle->addVelocity(explosionDir * mStrength * decay * dt);
}
Emitter::Emitter()
: osgParticle::Emitter()
, mFlags(0)

@ -199,6 +199,31 @@ namespace NifOsg
osg::Vec3f mCachedWorldDirection;
};
class ParticleBomb : public osgParticle::Operator
{
public:
ParticleBomb(const Nif::NiParticleBomb* bomb);
ParticleBomb() = default;
ParticleBomb(const ParticleBomb& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
ParticleBomb& operator=(const ParticleBomb&) = delete;
META_Object(NifOsg, ParticleBomb)
void operate(osgParticle::Particle* particle, double dt) override;
void beginOperate(osgParticle::Program*) override;
private:
float mRange{ 0.f };
float mStrength{ 0.f };
Nif::DecayType mDecayType{ Nif::DecayType::None };
Nif::SymmetryType mSymmetryType{ Nif::SymmetryType::Spherical };
osg::Vec3f mPosition;
osg::Vec3f mDirection;
osg::Vec3f mCachedWorldPosition;
osg::Vec3f mCachedWorldDirection;
};
// NodeVisitor to find a Group node with the given record index, stored in the node's user data container.
// Alternatively, returns the node's parent Group if that node is not a Group (i.e. a leaf node).
class FindGroupByRecIndex : public osg::NodeVisitor

@ -1,5 +1,7 @@
#include "riggeometry.hpp"
#include <unordered_map>
#include <osg/MatrixTransform>
#include <osgUtil/CullVisitor>
@ -10,39 +12,10 @@
#include "skeleton.hpp"
#include "util.hpp"
namespace
{
inline void accumulateMatrix(
const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result)
{
osg::Matrixf m = invBindMatrix * matrix;
float* ptr = m.ptr();
float* ptrresult = result.ptr();
ptrresult[0] += ptr[0] * weight;
ptrresult[1] += ptr[1] * weight;
ptrresult[2] += ptr[2] * weight;
ptrresult[4] += ptr[4] * weight;
ptrresult[5] += ptr[5] * weight;
ptrresult[6] += ptr[6] * weight;
ptrresult[8] += ptr[8] * weight;
ptrresult[9] += ptr[9] * weight;
ptrresult[10] += ptr[10] * weight;
ptrresult[12] += ptr[12] * weight;
ptrresult[13] += ptr[13] * weight;
ptrresult[14] += ptr[14] * weight;
}
}
namespace SceneUtil
{
RigGeometry::RigGeometry()
: mSkeleton(nullptr)
, mLastFrameNumber(0)
, mBoundsFirstFrame(true)
{
setNumChildrenRequiringUpdateTraversal(1);
// update done in accept(NodeVisitor&)
@ -50,12 +23,7 @@ namespace SceneUtil
RigGeometry::RigGeometry(const RigGeometry& copy, const osg::CopyOp& copyop)
: Drawable(copy, copyop)
, mSkeleton(nullptr)
, mInfluenceMap(copy.mInfluenceMap)
, mBone2VertexVector(copy.mBone2VertexVector)
, mBoneSphereVector(copy.mBoneSphereVector)
, mLastFrameNumber(0)
, mBoundsFirstFrame(true)
, mData(copy.mData)
{
setSourceGeometry(copy.mSourceGeometry);
setNumChildrenRequiringUpdateTraversal(1);
@ -151,42 +119,18 @@ namespace SceneUtil
return false;
}
if (!mInfluenceMap)
if (!mData)
{
Log(Debug::Error) << "Error: No InfluenceMap set on RigGeometry";
Log(Debug::Error) << "Error: No influence data set on RigGeometry";
return false;
}
mBoneNodesVector.clear();
for (auto& bonePair : mBoneSphereVector->mData)
mNodes.clear();
for (const BoneInfo& info : mData->mBones)
{
const std::string& boneName = bonePair.first;
Bone* bone = mSkeleton->getBone(boneName);
if (!bone)
{
mBoneNodesVector.push_back(nullptr);
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName;
continue;
}
mBoneNodesVector.push_back(bone);
}
for (auto& pair : mBone2VertexVector->mData)
{
for (auto& weight : pair.first)
{
const std::string& boneName = weight.first.first;
Bone* bone = mSkeleton->getBone(boneName);
if (!bone)
{
mBoneNodesVector.push_back(nullptr);
Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName;
continue;
}
mBoneNodesVector.push_back(bone);
}
mNodes.push_back(mSkeleton->getBone(info.mName));
if (!mNodes.back())
Log(Debug::Error) << "Error: RigGeometry did not find bone " << info.mName;
}
return true;
@ -226,25 +170,28 @@ namespace SceneUtil
osg::Vec3Array* normalDst = static_cast<osg::Vec3Array*>(geom.getNormalArray());
osg::Vec4Array* tangentDst = static_cast<osg::Vec4Array*>(geom.getTexCoordArray(7));
int index = mBoneSphereVector->mData.size();
for (auto& pair : mBone2VertexVector->mData)
for (const auto& [influences, vertices] : mData->mInfluences)
{
osg::Matrixf resultMat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
for (auto& weight : pair.first)
for (const auto& [index, weight] : influences)
{
Bone* bone = mBoneNodesVector[index];
const Bone* bone = mNodes[index];
if (bone == nullptr)
continue;
accumulateMatrix(weight.first.second, bone->mMatrixInSkeletonSpace, weight.second, resultMat);
index++;
osg::Matrixf boneMat = mData->mBones[index].mInvBindMatrix * bone->mMatrixInSkeletonSpace;
float* boneMatPtr = boneMat.ptr();
float* resultMatPtr = resultMat.ptr();
for (int i = 0; i < 16; ++i, ++resultMatPtr, ++boneMatPtr)
if (i % 4 != 3)
*resultMatPtr += *boneMatPtr * weight;
}
if (mGeomToSkelMatrix)
resultMat *= (*mGeomToSkelMatrix);
for (auto& vertex : pair.second)
for (unsigned short vertex : vertices)
{
(*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]);
if (normalDst)
@ -291,15 +238,14 @@ namespace SceneUtil
osg::BoundingBox box;
int index = 0;
for (auto& boundPair : mBoneSphereVector->mData)
size_t index = 0;
for (const BoneInfo& info : mData->mBones)
{
Bone* bone = mBoneNodesVector[index];
const Bone* bone = mNodes[index++];
if (bone == nullptr)
continue;
index++;
osg::BoundingSpheref bs = boundPair.second;
osg::BoundingSpheref bs = info.mBoundSphere;
if (mGeomToSkelMatrix)
transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs);
else
@ -357,35 +303,26 @@ namespace SceneUtil
void RigGeometry::setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap)
{
mInfluenceMap = influenceMap;
typedef std::map<unsigned short, std::vector<BoneWeight>> Vertex2BoneMap;
Vertex2BoneMap vertex2BoneMap;
mBoneSphereVector = new BoneSphereVector;
mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size());
mBone2VertexVector = new Bone2VertexVector;
for (auto& influencePair : mInfluenceMap->mData)
{
const std::string& boneName = influencePair.first;
const BoneInfluence& bi = influencePair.second;
mBoneSphereVector->mData.emplace_back(boneName, bi.mBoundSphere);
mData = new InfluenceData;
mData->mBones.reserve(influenceMap->mData.size());
for (auto& weightPair : bi.mWeights)
{
std::vector<BoneWeight>& vec = vertex2BoneMap[weightPair.first];
std::unordered_map<unsigned short, std::vector<BoneWeight>> vertexToInfluences;
size_t index = 0;
for (const auto& [boneName, bi] : influenceMap->mData)
{
mData->mBones.push_back({ boneName, bi.mBoundSphere, bi.mInvBindMatrix });
vec.emplace_back(std::make_pair(boneName, bi.mInvBindMatrix), weightPair.second);
}
for (const auto& [vertex, weight] : bi.mWeights)
vertexToInfluences[vertex].emplace_back(index, weight);
index++;
}
Bone2VertexMap bone2VertexMap;
for (auto& vertexPair : vertex2BoneMap)
{
bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first);
}
std::map<std::vector<BoneWeight>, VertexList> influencesToVertices;
for (const auto& [vertex, weights] : vertexToInfluences)
influencesToVertices[weights].emplace_back(vertex);
mBone2VertexVector->mData.reserve(bone2VertexMap.size());
mBone2VertexVector->mData.assign(bone2VertexMap.begin(), bone2VertexMap.end());
mData->mInfluences.reserve(influencesToVertices.size());
mData->mInfluences.assign(influencesToVertices.begin(), influencesToVertices.end());
}
void RigGeometry::accept(osg::NodeVisitor& nv)

@ -36,6 +36,7 @@ namespace SceneUtil
// static parts of the model.
void compileGLObjects(osg::RenderInfo& renderInfo) const override {}
// TODO: Make InfluenceMap more similar to InfluenceData
struct BoneInfluence
{
osg::Matrixf mInvBindMatrix;
@ -84,35 +85,29 @@ namespace SceneUtil
osg::ref_ptr<osg::Geometry> mSourceGeometry;
osg::ref_ptr<const osg::Vec4Array> mSourceTangents;
Skeleton* mSkeleton;
Skeleton* mSkeleton{ nullptr };
osg::ref_ptr<osg::RefMatrix> mGeomToSkelMatrix;
osg::ref_ptr<InfluenceMap> mInfluenceMap;
typedef std::pair<std::string, osg::Matrixf> BoneBindMatrixPair;
typedef std::pair<BoneBindMatrixPair, float> BoneWeight;
typedef std::vector<unsigned short> VertexList;
typedef std::map<std::vector<BoneWeight>, VertexList> Bone2VertexMap;
struct Bone2VertexVector : public osg::Referenced
struct BoneInfo
{
std::vector<std::pair<std::vector<BoneWeight>, VertexList>> mData;
std::string mName;
osg::BoundingSpheref mBoundSphere;
osg::Matrixf mInvBindMatrix;
};
osg::ref_ptr<Bone2VertexVector> mBone2VertexVector;
struct BoneSphereVector : public osg::Referenced
using BoneWeight = std::pair<size_t, float>;
using VertexList = std::vector<unsigned short>;
struct InfluenceData : public osg::Referenced
{
std::vector<std::pair<std::string, osg::BoundingSpheref>> mData;
std::vector<BoneInfo> mBones;
std::vector<std::pair<std::vector<BoneWeight>, VertexList>> mInfluences;
};
osg::ref_ptr<BoneSphereVector> mBoneSphereVector;
std::vector<Bone*> mBoneNodesVector;
osg::ref_ptr<InfluenceData> mData;
std::vector<Bone*> mNodes;
unsigned int mLastFrameNumber;
bool mBoundsFirstFrame;
unsigned int mLastFrameNumber{ 0 };
bool mBoundsFirstFrame{ true };
bool initFromParentSkeleton(osg::NodeVisitor* nv);

@ -167,7 +167,7 @@ namespace SceneUtil
"SceneUtil::DisableLight", "SceneUtil::MWShadowTechnique", "SceneUtil::TextKeyMapHolder",
"Shader::AddedState", "Shader::RemovedAlphaFunc", "NifOsg::FlipController",
"NifOsg::KeyframeController", "NifOsg::Emitter", "NifOsg::ParticleColorAffector",
"NifOsg::ParticleSystem", "NifOsg::GravityAffector", "NifOsg::GrowFadeAffector",
"NifOsg::ParticleSystem", "NifOsg::GravityAffector", "NifOsg::ParticleBomb", "NifOsg::GrowFadeAffector",
"NifOsg::InverseWorldMatrix", "NifOsg::StaticBoundingBoxCallback", "NifOsg::GeomMorpherController",
"NifOsg::UpdateMorphGeometry", "NifOsg::UVController", "NifOsg::VisController", "osgMyGUI::Drawable",
"osg::DrawCallback", "osg::UniformBufferObject", "osgOQ::ClearQueriesCallback",

@ -29,6 +29,12 @@
-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the script if there is a separate instance for each player. Currently has no effect.
-- @return #MWScript, #nil
---
-- Returns mutable global variables. In multiplayer, these may be specific to the provided player.
-- @function [parent=#MWScriptFunctions] getGlobalVariables
-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the globals if there is a separate instance for each player. Currently has no effect.
-- @return #list<#number>
---
-- Returns global mwscript with given recordId. Returns `nil` if the script doesn't exist or is not started.
-- Currently there can be only one instance of each mwscript, but in multiplayer it will be possible to have a separate instance per player.

Loading…
Cancel
Save