Merge branch 'lua_magic' into 'master'

Lua bindings for magic

See merge request OpenMW/openmw!2928
7344-support-launching-the-example-suite
psi29a 2 years ago
commit a6584557e5

@ -63,7 +63,7 @@ add_openmw_dir (mwlua
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings context globalscripts localscripts playerscripts luabindings objectbindings cellbindings
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings
types/types types/door types/actor types/container types/weapon types/npc types/creature types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/types types/door types/actor types/container types/weapon types/npc types/creature types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing
worker worker magicbindings
) )
add_openmw_dir (mwsound add_openmw_dir (mwsound

@ -2,7 +2,10 @@
#include <chrono> #include <chrono>
#include <components/esm/attr.hpp>
#include <components/esm3/activespells.hpp>
#include <components/esm3/loadalch.hpp> #include <components/esm3/loadalch.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/lua/l10n.hpp> #include <components/lua/l10n.hpp>
#include <components/lua/luastate.hpp> #include <components/lua/luastate.hpp>
@ -19,6 +22,8 @@
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "worldview.hpp" #include "worldview.hpp"
#include "magicbindings.hpp"
namespace MWLua namespace MWLua
{ {
@ -53,7 +58,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"] = 35; api["API_REVISION"] = 36;
api["quit"] = [lua]() { api["quit"] = [lua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback(); Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit(); MWBase::Environment::get().getStateManager()->requestQuit();
@ -63,6 +68,7 @@ namespace MWLua
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) }); { std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
}; };
addTimeBindings(api, context, false); addTimeBindings(api, context, false);
api["magic"] = initCoreMagicBindings(context);
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
const MWWorld::Store<ESM::GameSetting>* gmst const MWWorld::Store<ESM::GameSetting>* gmst
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); = &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -75,6 +81,18 @@ namespace MWLua
else else
return sol::make_object<float>(lua->sol(), value.getFloat()); return sol::make_object<float>(lua->sol(), value.getFloat());
}; };
sol::table skill(context.mLua->sol(), sol::create);
api["SKILL"] = LuaUtil::makeStrictReadOnly(skill);
for (int id = 0; id < ESM::Skill::Length; ++id)
skill[ESM::Skill::sSkillNames[id]] = Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id]);
sol::table attribute(context.mLua->sol(), sol::create);
api["ATTRIBUTE"] = LuaUtil::makeStrictReadOnly(attribute);
for (int id = 0; id < ESM::Attribute::Length; ++id)
attribute[ESM::Attribute::sAttributeNames[id]]
= Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[id]);
return LuaUtil::makeReadOnly(api); return LuaUtil::makeReadOnly(api);
} }
@ -97,7 +115,7 @@ namespace MWLua
const MWWorld::Ptr& ptr = mref.getPtr(); const MWWorld::Ptr& ptr = mref.getPtr();
ptr.getRefData().disable(); ptr.getRefData().disable();
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cell, count.value_or(1)); MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cell, count.value_or(1));
return GObject(getId(newPtr)); return GObject(newPtr);
}; };
// Creates a new record in the world database. // Creates a new record in the world database.

@ -0,0 +1,266 @@
#include "magicbindings.hpp"
#include <components/esm3/activespells.hpp>
#include <components/esm3/loadmgef.hpp>
#include <components/esm3/loadspel.hpp>
#include <components/lua/luastate.hpp>
#include <components/misc/color.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/activespells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwworld/action.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "localscripts.hpp"
#include "luamanagerimp.hpp"
#include "object.hpp"
#include "objectvariant.hpp"
#include "worldview.hpp"
namespace MWLua
{
sol::table initCoreMagicBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table magicApi(lua, sol::create);
// Constants
magicApi["RANGE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, ESM::RangeType>({
{ "Self", ESM::RT_Self },
{ "Touch", ESM::RT_Touch },
{ "Target", ESM::RT_Target },
}));
magicApi["SCHOOL"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "Alteration", 0 },
{ "Conjuration", 1 },
{ "Destruction", 2 },
{ "Illusion", 3 },
{ "Mysticism", 4 },
{ "Restoration", 5 },
}));
magicApi["SPELL_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, ESM::Spell::SpellType>({
{ "Spell", ESM::Spell::ST_Spell },
{ "Ability", ESM::Spell::ST_Ability },
{ "Blight", ESM::Spell::ST_Blight },
{ "Disease", ESM::Spell::ST_Disease },
{ "Curse", ESM::Spell::ST_Curse },
{ "Power", ESM::Spell::ST_Power },
}));
// Spell store
using SpellStore = MWWorld::Store<ESM::Spell>;
const SpellStore* spellStore = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
sol::usertype<SpellStore> spellStoreT = lua.new_usertype<SpellStore>("ESM3_SpellStore");
spellStoreT[sol::meta_function::to_string]
= [](const SpellStore& store) { return "ESM3_SpellStore{" + std::to_string(store.getSize()) + " spells}"; };
spellStoreT[sol::meta_function::length] = [](const SpellStore& store) { return store.getSize(); };
spellStoreT[sol::meta_function::index] = sol::overload(
[](const SpellStore& store, size_t index) -> const ESM::Spell* { return store.at(index - 1); },
[](const SpellStore& store, std::string_view spellId) -> const ESM::Spell* {
return store.find(ESM::RefId::deserializeText(spellId));
});
spellStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
spellStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
magicApi["spells"] = spellStore;
// MagicEffect store
using MagicEffectStore = MWWorld::Store<ESM::MagicEffect>;
const MagicEffectStore* magicEffectStore
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>();
auto magicEffectStoreT = lua.new_usertype<MagicEffectStore>("ESM3_MagicEffectStore");
magicEffectStoreT[sol::meta_function::to_string] = [](const MagicEffectStore& store) {
return "ESM3_MagicEffectStore{" + std::to_string(store.getSize()) + " effects}";
};
magicEffectStoreT[sol::meta_function::index]
= [](const MagicEffectStore& store, int id) -> const ESM::MagicEffect* { return store.find(id); };
auto magicEffectsIter = [magicEffectStore](sol::this_state lua, const sol::object& /*store*/,
sol::optional<int> id) -> std::tuple<sol::object, sol::object> {
MagicEffectStore::iterator iter;
if (id.has_value())
{
iter = magicEffectStore->findIter(*id);
if (iter != magicEffectStore->end())
iter++;
}
else
iter = magicEffectStore->begin();
if (iter != magicEffectStore->end())
return std::make_tuple(sol::make_object(lua, iter->first), sol::make_object(lua, &iter->second));
else
return std::make_tuple(sol::nil, sol::nil);
};
magicEffectStoreT[sol::meta_function::pairs]
= [iter = sol::make_object(lua, magicEffectsIter)] { return iter; };
magicApi["effects"] = magicEffectStore;
// Spell record
auto spellT = lua.new_usertype<ESM::Spell>("ESM3_Spell");
spellT[sol::meta_function::to_string]
= [](const ESM::Spell& rec) -> std::string { return "ESM3_Spell[" + rec.mId.toDebugString() + "]"; };
spellT["id"] = sol::readonly_property([](const ESM::Spell& rec) { return rec.mId.serializeText(); });
spellT["name"] = sol::readonly_property([](const ESM::Spell& rec) -> std::string_view { return rec.mName; });
spellT["type"] = sol::readonly_property([](const ESM::Spell& rec) -> int { return rec.mData.mType; });
spellT["cost"] = sol::readonly_property([](const ESM::Spell& rec) -> int { return rec.mData.mCost; });
spellT["effects"] = sol::readonly_property([&lua](const ESM::Spell& rec) -> sol::table {
sol::table res(lua, sol::create);
for (size_t i = 0; i < rec.mEffects.mList.size(); ++i)
res[i + 1] = rec.mEffects.mList[i]; // ESM::ENAMstruct (effect params)
return res;
});
// Effect params
auto effectParamsT = lua.new_usertype<ESM::ENAMstruct>("ESM3_EffectParams");
effectParamsT[sol::meta_function::to_string] = [magicEffectStore](const ESM::ENAMstruct& params) {
const ESM::MagicEffect* const rec = magicEffectStore->find(params.mEffectID);
return "ESM3_EffectParams[" + ESM::MagicEffect::effectIdToString(rec->mIndex) + "]";
};
effectParamsT["effect"]
= sol::readonly_property([magicEffectStore](const ESM::ENAMstruct& params) -> const ESM::MagicEffect* {
return magicEffectStore->find(params.mEffectID);
});
effectParamsT["affectedSkill"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional<std::string> {
if (params.mSkill >= 0 && params.mSkill < ESM::Skill::Length)
return Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[params.mSkill]);
else
return sol::nullopt;
});
effectParamsT["affectedAttribute"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional<std::string> {
if (params.mAttribute >= 0 && params.mAttribute < ESM::Attribute::Length)
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[params.mAttribute]);
else
return sol::nullopt;
});
effectParamsT["range"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mRange; });
effectParamsT["area"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mArea; });
effectParamsT["magnitudeMin"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mMagnMin; });
effectParamsT["magnitudeMax"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mMagnMax; });
// MagicEffect record
auto magicEffectT = context.mLua->sol().new_usertype<ESM::MagicEffect>("ESM3_MagicEffect");
magicEffectT[sol::meta_function::to_string] = [](const ESM::MagicEffect& rec) {
return "ESM3_MagicEffect[" + ESM::MagicEffect::effectIdToString(rec.mIndex) + "]";
};
magicEffectT["id"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> int { return rec.mIndex; });
magicEffectT["name"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view {
return MWBase::Environment::get()
.getWorld()
->getStore()
.get<ESM::GameSetting>()
.find(ESM::MagicEffect::effectIdToString(rec.mIndex))
->mValue.getString();
});
magicEffectT["school"]
= sol::readonly_property([](const ESM::MagicEffect& rec) -> int { return rec.mData.mSchool; });
magicEffectT["baseCost"]
= sol::readonly_property([](const ESM::MagicEffect& rec) -> float { return rec.mData.mBaseCost; });
magicEffectT["color"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> Misc::Color {
return Misc::Color(rec.mData.mRed / 255.f, rec.mData.mGreen / 255.f, rec.mData.mBlue / 255.f, 1.f);
});
magicEffectT["harmful"] = sol::readonly_property(
[](const ESM::MagicEffect& rec) -> bool { return rec.mData.mFlags & ESM::MagicEffect::Harmful; });
// TODO: Should we expose it? What happens if a spell has several effects with different projectileSpeed?
// magicEffectT["projectileSpeed"]
// = sol::readonly_property([](const ESM::MagicEffect& rec) -> float { return rec.mData.mSpeed; });
return LuaUtil::makeReadOnly(magicApi);
}
// class returned via 'types.Actor.spells(obj)' in Lua
struct ActorSpells
{
const ObjectVariant mActor;
};
void addActorMagicBindings(sol::table& actor, const Context& context)
{
const MWWorld::Store<ESM::Spell>* spellStore
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
// types.Actor.spells(o)
actor["spells"] = [](const sol::object actor) { return ActorSpells{ ObjectVariant(actor) }; };
auto spellsT = context.mLua->sol().new_usertype<ActorSpells>("ActorSpells");
spellsT[sol::meta_function::to_string]
= [](const ActorSpells& spells) { return "ActorSpells[" + spells.mActor.object().toString(); };
// #(types.Actor.spells(o))
spellsT[sol::meta_function::length] = [](const ActorSpells& spells) -> size_t {
const MWWorld::Ptr& ptr = spells.mActor.ptr();
return ptr.getClass().getCreatureStats(ptr).getSpells().count();
};
// types.Actor.spells(o)[i]
spellsT[sol::meta_function::index] = sol::overload(
[](const ActorSpells& spells, size_t index) -> const ESM::Spell* {
const MWWorld::Ptr& ptr = spells.mActor.ptr();
return ptr.getClass().getCreatureStats(ptr).getSpells().at(index - 1);
},
[spellStore](const ActorSpells& spells, std::string_view spellId) -> sol::optional<const ESM::Spell*> {
const MWWorld::Ptr& ptr = spells.mActor.ptr();
const ESM::Spell* spell = spellStore->find(ESM::RefId::deserializeText(spellId));
if (ptr.getClass().getCreatureStats(ptr).getSpells().hasSpell(spell))
return spell;
else
return sol::nullopt;
});
// pairs(types.Actor.spells(o))
spellsT[sol::meta_function::pairs] = context.mLua->sol()["ipairsForArray"].template get<sol::function>();
// ipairs(types.Actor.spells(o))
spellsT[sol::meta_function::ipairs] = context.mLua->sol()["ipairsForArray"].template get<sol::function>();
auto toSpellId = [](const sol::object& spellOrId) -> ESM::RefId {
if (spellOrId.is<ESM::Spell>())
return spellOrId.as<const ESM::Spell*>()->mId;
else
return ESM::RefId::deserializeText(spellOrId.as<std::string_view>());
};
// types.Actor.spells(o):add(id)
spellsT["add"] = [context, toSpellId](const ActorSpells& spells, const sol::object& spellOrId) {
if (spells.mActor.isLObject())
throw std::runtime_error("Local scripts can modify only spells of the actor they are attached to.");
context.mLuaManager->addAction([obj = spells.mActor.object(), id = toSpellId(spellOrId)]() {
const MWWorld::Ptr& ptr = obj.ptr();
ptr.getClass().getCreatureStats(ptr).getSpells().add(id);
});
};
// types.Actor.spells(o):remove(id)
spellsT["remove"] = [context, toSpellId](const ActorSpells& spells, const sol::object& spellOrId) {
if (spells.mActor.isLObject())
throw std::runtime_error("Local scripts can modify only spells of the actor they are attached to.");
context.mLuaManager->addAction([obj = spells.mActor.object(), id = toSpellId(spellOrId)]() {
const MWWorld::Ptr& ptr = obj.ptr();
ptr.getClass().getCreatureStats(ptr).getSpells().remove(id);
});
};
// types.Actor.spells(o):clear()
spellsT["clear"] = [context](const ActorSpells& spells) {
if (spells.mActor.isLObject())
throw std::runtime_error("Local scripts can modify only spells of the actor they are attached to.");
context.mLuaManager->addAction([obj = spells.mActor.object()]() {
const MWWorld::Ptr& ptr = obj.ptr();
ptr.getClass().getCreatureStats(ptr).getSpells().clear();
});
};
}
}

@ -0,0 +1,14 @@
#ifndef MWLUA_MAGICBINDINGS_H
#define MWLUA_MAGICBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initCoreMagicBindings(const Context& context);
void addActorMagicBindings(sol::table& actor, const Context& context);
}
#endif // MWLUA_MAGICBINDINGS_H

@ -29,9 +29,6 @@ namespace MWLua
{ {
public: public:
using SafePtr::SafePtr; using SafePtr::SafePtr;
virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GObject
virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell
const MWWorld::Ptr& ptr() const const MWWorld::Ptr& ptr() const
{ {
const MWWorld::Ptr& res = ptrOrNull(); const MWWorld::Ptr& res = ptrOrNull();
@ -49,11 +46,6 @@ namespace MWLua
class LObject : public Object class LObject : public Object
{ {
using Object::Object; using Object::Object;
sol::object getObject(lua_State* lua, ObjectId id) const final { return sol::make_object<LObject>(lua, id); }
sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const final
{
return sol::make_object(lua, LCell{ store });
}
}; };
// Used only in global scripts // Used only in global scripts
@ -64,11 +56,6 @@ namespace MWLua
class GObject : public Object class GObject : public Object
{ {
using Object::Object; using Object::Object;
sol::object getObject(lua_State* lua, ObjectId id) const final { return sol::make_object<GObject>(lua, id); }
sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const final
{
return sol::make_object(lua, GCell{ store });
}
}; };
using ObjectIdList = std::shared_ptr<std::vector<ObjectId>>; using ObjectIdList = std::shared_ptr<std::vector<ObjectId>>;

@ -46,6 +46,8 @@ namespace MWLua
mVariant); mVariant);
} }
Object object() const { return Object(ptr()); }
private: private:
std::variant<SelfObject*, LObject, GObject> mVariant; std::variant<SelfObject*, LObject, GObject> mVariant;
}; };

@ -5,6 +5,7 @@
#include <apps/openmw/mwbase/mechanicsmanager.hpp> #include <apps/openmw/mwbase/mechanicsmanager.hpp>
#include <apps/openmw/mwbase/windowmanager.hpp> #include <apps/openmw/mwbase/windowmanager.hpp>
#include <apps/openmw/mwlua/luabindings.hpp>
#include <apps/openmw/mwmechanics/creaturestats.hpp> #include <apps/openmw/mwmechanics/creaturestats.hpp>
#include <apps/openmw/mwmechanics/drawstate.hpp> #include <apps/openmw/mwmechanics/drawstate.hpp>
#include <apps/openmw/mwworld/class.hpp> #include <apps/openmw/mwworld/class.hpp>
@ -12,6 +13,7 @@
#include "../localscripts.hpp" #include "../localscripts.hpp"
#include "../luamanagerimp.hpp" #include "../luamanagerimp.hpp"
#include "../magicbindings.hpp"
#include "../stats.hpp" #include "../stats.hpp"
namespace MWLua namespace MWLua
@ -218,9 +220,9 @@ namespace MWLua
actor["inventory"] = sol::overload([](const LObject& o) { return Inventory<LObject>{ o }; }, actor["inventory"] = sol::overload([](const LObject& o) { return Inventory<LObject>{ o }; },
[](const GObject& o) { return Inventory<GObject>{ o }; }); [](const GObject& o) { return Inventory<GObject>{ o }; });
auto getAllEquipment = [context](const Object& o) { auto getAllEquipment = [](sol::this_state lua, const Object& o) {
const MWWorld::Ptr& ptr = o.ptr(); const MWWorld::Ptr& ptr = o.ptr();
sol::table equipment(context.mLua->sol(), sol::create); sol::table equipment(lua, sol::create);
if (!ptr.getClass().hasInventoryStore(ptr)) if (!ptr.getClass().hasInventoryStore(ptr))
return equipment; return equipment;
@ -231,13 +233,15 @@ namespace MWLua
if (it == store.end()) if (it == store.end())
continue; continue;
MWBase::Environment::get().getWorldModel()->registerPtr(*it); MWBase::Environment::get().getWorldModel()->registerPtr(*it);
equipment[slot] = o.getObject(context.mLua->sol(), getId(*it)); if (dynamic_cast<const GObject*>(&o))
equipment[slot] = sol::make_object(lua, GObject(*it));
else
equipment[slot] = sol::make_object(lua, LObject(*it));
} }
return equipment; return equipment;
}; };
auto getEquipmentFromSlot = [context](const Object& o, int slot) -> sol::object { auto getEquipmentFromSlot = [](sol::this_state lua, const Object& o, int slot) -> sol::object {
const MWWorld::Ptr& ptr = o.ptr(); const MWWorld::Ptr& ptr = o.ptr();
sol::table equipment(context.mLua->sol(), sol::create);
if (!ptr.getClass().hasInventoryStore(ptr)) if (!ptr.getClass().hasInventoryStore(ptr))
return sol::nil; return sol::nil;
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
@ -245,7 +249,10 @@ namespace MWLua
if (it == store.end()) if (it == store.end())
return sol::nil; return sol::nil;
MWBase::Environment::get().getWorldModel()->registerPtr(*it); MWBase::Environment::get().getWorldModel()->registerPtr(*it);
return o.getObject(context.mLua->sol(), getId(*it)); if (dynamic_cast<const GObject*>(&o))
return sol::make_object(lua, GObject(*it));
else
return sol::make_object(lua, LObject(*it));
}; };
actor["equipment"] = sol::overload(getAllEquipment, getEquipmentFromSlot); actor["equipment"] = sol::overload(getAllEquipment, getEquipmentFromSlot);
actor["hasEquipped"] = [](const Object& o, const Object& item) { actor["hasEquipped"] = [](const Object& o, const Object& item) {
@ -274,16 +281,17 @@ namespace MWLua
context.mLuaManager->addAction( context.mLuaManager->addAction(
std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp))); std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
}; };
actor["getPathfindingAgentBounds"] = [context](const LObject& o) { actor["getPathfindingAgentBounds"] = [](sol::this_state lua, const LObject& o) {
const DetourNavigator::AgentBounds agentBounds const DetourNavigator::AgentBounds agentBounds
= MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(o.ptr()); = MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(o.ptr());
sol::table result = context.mLua->newTable(); sol::table result(lua, sol::create);
result["shapeType"] = agentBounds.mShapeType; result["shapeType"] = agentBounds.mShapeType;
result["halfExtents"] = agentBounds.mHalfExtents; result["halfExtents"] = agentBounds.mHalfExtents;
return result; return result;
}; };
addActorStatsBindings(actor, context); addActorStatsBindings(actor, context);
addActorMagicBindings(actor, context);
} }
} }

@ -22,6 +22,8 @@ namespace MWLua
{ {
void addBookBindings(sol::table book, const Context& context) void addBookBindings(sol::table book, const Context& context)
{ {
// types.book.SKILL is deprecated (core.SKILL should be used instead)
// TODO: Remove book.SKILL after branching 0.49
sol::table skill(context.mLua->sol(), sol::create); sol::table skill(context.mLua->sol(), sol::create);
book["SKILL"] = LuaUtil::makeStrictReadOnly(skill); book["SKILL"] = LuaUtil::makeStrictReadOnly(skill);
for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id) for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id)

@ -48,7 +48,10 @@ namespace MWLua
if (!cellRef.getTeleport()) if (!cellRef.getTeleport())
return sol::nil; return sol::nil;
MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell()); MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell());
return o.getCell(lua, &cell); if (dynamic_cast<const GObject*>(&o))
return sol::make_object(lua, GCell{ &cell });
else
return sol::make_object(lua, LCell{ &cell });
}; };
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
@ -84,7 +87,10 @@ namespace MWLua
if (!cellRef.getTeleport()) if (!cellRef.getTeleport())
return sol::nil; return sol::nil;
MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell()); MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell());
return o.getCell(lua, &cell); if (dynamic_cast<const GObject*>(&o))
return sol::make_object(lua, GCell{ &cell });
else
return sol::make_object(lua, LCell{ &cell });
}; };
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();

@ -95,6 +95,10 @@ namespace MWMechanics
bool hasBlightDisease() const; bool hasBlightDisease() const;
/// Iteration methods for lua
size_t count() const { return mSpells.size(); }
const ESM::Spell* at(size_t index) const { return mSpells.at(index); }
void readState(const ESM::SpellState& state, CreatureStats* creatureStats); void readState(const ESM::SpellState& state, CreatureStats* creatureStats);
void writeState(ESM::SpellState& state) const; void writeState(ESM::SpellState& state) const;

@ -94,6 +94,7 @@ namespace MWWorld
iterator begin() const; iterator begin() const;
iterator end() const; iterator end() const;
iterator findIter(int index) const { return mStatic.find(index); }
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);

@ -292,4 +292,141 @@
-- @usage for _, item in ipairs(inventory:findAll('common_shirt_01')) do ... end -- @usage for _, item in ipairs(inventory:findAll('common_shirt_01')) do ... end
--- Possible @{#ATTRIBUTE} values
-- @field [parent=#core] #ATTRIBUTE ATTRIBUTE
--- `core.ATTRIBUTE`
-- @type ATTRIBUTE
-- @field #string Strength "strength"
-- @field #string Intelligence "intelligence"
-- @field #string Willpower "willpower"
-- @field #string Agility "agility"
-- @field #string Speed "speed"
-- @field #string Endurance "endurance"
-- @field #string Personality "personality"
-- @field #string Luck "luck"
--- Possible @{#SKILL} values
-- @field [parent=#core] #SKILL SKILL
--- `core.SKILL`
-- @type SKILL
-- @field #string Acrobatics "acrobatics"
-- @field #string Alchemy "alchemy"
-- @field #string Alteration "alteration"
-- @field #string Armorer "armorer"
-- @field #string Athletics "athletics"
-- @field #string Axe "axe"
-- @field #string Block "block"
-- @field #string BluntWeapon "bluntweapon"
-- @field #string Conjuration "conjuration"
-- @field #string Destruction "destruction"
-- @field #string Enchant "enchant"
-- @field #string HandToHand "handtohand"
-- @field #string HeavyArmor "heavyarmor"
-- @field #string Illusion "illusion"
-- @field #string LightArmor "lightarmor"
-- @field #string LongBlade "longblade"
-- @field #string Marksman "marksman"
-- @field #string MediumArmor "mediumarmor"
-- @field #string Mercantile "mercantile"
-- @field #string Mysticism "mysticism"
-- @field #string Restoration "restoration"
-- @field #string Security "security"
-- @field #string ShortBlade "shortblade"
-- @field #string Sneak "sneak"
-- @field #string Spear "spear"
-- @field #string Speechcraft "speechcraft"
-- @field #string Unarmored "unarmored"
--- @{#Magic}: spells and spell effects
-- @field [parent=#core] #Magic magic
--- Possible @{#SpellRange} values
-- @field [parent=#Magic] #SpellRange RANGE
--- `core.magic.RANGE`
-- @type SpellRange
-- @field #number Self Applied on self
-- @field #number Touch On touch
-- @field #number Target Ranged spell
--- Possible @{#MagicSchool} values
-- @field [parent=#Magic] #MagicSchool SCHOOL
--- `core.magic.SCHOOL`
-- @type MagicSchool
-- @field #number Alteration Alteration
-- @field #number Conjuration Conjuration
-- @field #number Destruction Destruction
-- @field #number Illusion Illusion
-- @field #number Mysticism Mysticism
-- @field #number Restoration Restoration
--- Possible @{#SpellType} values
-- @field [parent=#Magic] #SpellType SPELL_TYPE
--- `core.magic.SPELL_TYPE`
-- @type SpellType
-- @field #number Spell Normal spell, must be cast and costs mana
-- @field #number Ability Inert ability, always in effect
-- @field #number Blight Blight disease
-- @field #number Disease Common disease
-- @field #number Curse Curse
-- @field #number Power Power, can be used once a day
--- List of all @{#Spell}s.
-- @field [parent=#Magic] #list<#Spell> spells
-- @usage local spell = core.magic.spells['thunder fist'] -- get by id
-- @usage local spell = core.magic.spells[1] -- get by index
-- @usage -- Print all powers
-- for _, spell in pairs(core.magic.spells) do
-- if spell.types == core.magic.SPELL_TYPE.Power then
-- print(spell.name)
-- end
-- end
--- Map from effectId to @{#SpellEffect}
-- @field [parent=#Magic] #map<#number, #MagicEffect> effects
-- @usage -- Print all harmful effects
-- for _, effect in pairs(core.magic.effects) do
-- if effect.harmful then
-- print(effect.name)
-- end
-- end
---
-- @type Spell
-- @field #string id Spell id
-- @field #string name Spell name
-- @field #number type @{#SpellType}
-- @field #number cost
-- @field #list<#MagicEffectWithParams> effects The effects (@{#MagicEffectWithParams}) of the spell
---
-- @type MagicEffect
-- @field #number id
-- @field #string name
-- @field #number school @{#MagicSchool}
-- @field #number baseCost
-- @field openmw.util#Color color
-- @field #boolean harmful
---
-- @type MagicEffectWithParams
-- @field #MagicEffect effect @{#MagicEffect}
-- @field #any affectedSkill @{#SKILL} or nil
-- @field #any affectedAttribute @{#ATTRIBUTE} or nil
-- @field #number range
-- @field #number area
-- @field #number magnitudeMin
-- @field #number magnitudeMax
return nil return nil

@ -149,6 +149,50 @@
-- local Actor = require('openmw.types').Actor -- local Actor = require('openmw.types').Actor
-- Actor.setEquipment(self, {}) -- unequip all -- Actor.setEquipment(self, {}) -- unequip all
---
-- Return the spells (@{ActorSpells}) of the given actor.
-- @function [parent=#Actor] spells
-- @param openmw.core#GameObject actor
-- @return #ActorSpells
--- List of spells with additional functions add/remove/clear (modification are allowed only in global scripts or on self).
-- @type ActorSpells
-- @usage -- print available spells
-- local mySpells = types.Actor.spells(self)
-- for _, spell in pairs(mySpells) do print(spell.id) end
-- @usage -- print available spells (equivalent)
-- local mySpells = types.Actor.spells(self)
-- for i = 1, #mySpells do print(mySpells[i].id) end
-- @usage -- add ALL spells that exist in the world
-- local mySpells = types.Actor.spells(self)
-- for _, spell in pairs(core.magic.spells) do
-- if spell.type == core.magic.SPELL_TYPE.Spell then
-- mySpells:add(spell)
-- end
-- end
-- @usage -- add specific spell
-- types.Actor.spells(self):add('thunder fist')
-- @usage -- check specific spell
-- local mySpells = types.Actor.spells(self)
-- if mySpells['thunder fist'] then print('I have thunder fist') end
---
-- Add spell (only in global scripts or on self).
-- @function [parent=#ActorSpells] add
-- @param self
-- @param #any spellOrId @{openmw.core#Spell} or string spell id
---
-- Remove spell (only in global scripts or on self).
-- @function [parent=#ActorSpells] remove
-- @param self
-- @param #any spellOrId @{openmw.core#Spell} or string spell id
---
-- Remove all spells (only in global scripts or on self).
-- @function [parent=#ActorSpells] clear
-- @param self
--- ---
-- @type LevelStat -- @type LevelStat
-- @field #number current The actor's current level. -- @field #number current The actor's current level.
@ -631,7 +675,7 @@
-- @field #string speechcraft "speechcraft" -- @field #string speechcraft "speechcraft"
-- @field #string unarmored "unarmored" -- @field #string unarmored "unarmored"
--- @{#BookSKILL} --- DEPRECATED, use @{openmw.core#SKILL}
-- @field [parent=#Book] #BookSKILL SKILL -- @field [parent=#Book] #BookSKILL SKILL
--- ---
@ -651,7 +695,7 @@
-- @field #string text The text content of the book -- @field #string text The text content of the book
-- @field #number weight -- @field #number weight
-- @field #number value -- @field #number value
-- @field #string skill The skill that this book teaches. See @{#Book.SKILL} -- @field #string skill The skill that this book teaches. See @{openmw.core#SKILL}
-- @field #boolean isScroll -- @field #boolean isScroll
-- @field #number enchantCapacity -- @field #number enchantCapacity

Loading…
Cancel
Save