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
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
worker
worker magicbindings
)
add_openmw_dir (mwsound

@ -2,7 +2,10 @@
#include <chrono>
#include <components/esm/attr.hpp>
#include <components/esm3/activespells.hpp>
#include <components/esm3/loadalch.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/lua/l10n.hpp>
#include <components/lua/luastate.hpp>
@ -19,6 +22,8 @@
#include "luamanagerimp.hpp"
#include "worldview.hpp"
#include "magicbindings.hpp"
namespace MWLua
{
@ -53,7 +58,7 @@ namespace MWLua
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 35;
api["API_REVISION"] = 36;
api["quit"] = [lua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit();
@ -63,6 +68,7 @@ namespace MWLua
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
};
addTimeBindings(api, context, false);
api["magic"] = initCoreMagicBindings(context);
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
const MWWorld::Store<ESM::GameSetting>* gmst
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -75,6 +81,18 @@ namespace MWLua
else
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);
}
@ -97,7 +115,7 @@ namespace MWLua
const MWWorld::Ptr& ptr = mref.getPtr();
ptr.getRefData().disable();
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.

@ -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:
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& res = ptrOrNull();
@ -49,11 +46,6 @@ namespace MWLua
class LObject : public 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
@ -64,11 +56,6 @@ namespace MWLua
class GObject : public 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>>;

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

@ -5,6 +5,7 @@
#include <apps/openmw/mwbase/mechanicsmanager.hpp>
#include <apps/openmw/mwbase/windowmanager.hpp>
#include <apps/openmw/mwlua/luabindings.hpp>
#include <apps/openmw/mwmechanics/creaturestats.hpp>
#include <apps/openmw/mwmechanics/drawstate.hpp>
#include <apps/openmw/mwworld/class.hpp>
@ -12,6 +13,7 @@
#include "../localscripts.hpp"
#include "../luamanagerimp.hpp"
#include "../magicbindings.hpp"
#include "../stats.hpp"
namespace MWLua
@ -218,9 +220,9 @@ namespace MWLua
actor["inventory"] = sol::overload([](const LObject& o) { return Inventory<LObject>{ 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();
sol::table equipment(context.mLua->sol(), sol::create);
sol::table equipment(lua, sol::create);
if (!ptr.getClass().hasInventoryStore(ptr))
return equipment;
@ -231,13 +233,15 @@ namespace MWLua
if (it == store.end())
continue;
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;
};
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();
sol::table equipment(context.mLua->sol(), sol::create);
if (!ptr.getClass().hasInventoryStore(ptr))
return sol::nil;
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
@ -245,7 +249,10 @@ namespace MWLua
if (it == store.end())
return sol::nil;
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["hasEquipped"] = [](const Object& o, const Object& item) {
@ -274,16 +281,17 @@ namespace MWLua
context.mLuaManager->addAction(
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
= MWBase::Environment::get().getWorld()->getPathfindingAgentBounds(o.ptr());
sol::table result = context.mLua->newTable();
sol::table result(lua, sol::create);
result["shapeType"] = agentBounds.mShapeType;
result["halfExtents"] = agentBounds.mHalfExtents;
return result;
};
addActorStatsBindings(actor, context);
addActorMagicBindings(actor, context);
}
}

@ -22,6 +22,8 @@ namespace MWLua
{
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);
book["SKILL"] = LuaUtil::makeStrictReadOnly(skill);
for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id)

@ -48,7 +48,10 @@ namespace MWLua
if (!cellRef.getTeleport())
return sol::nil;
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();
@ -84,7 +87,10 @@ namespace MWLua
if (!cellRef.getTeleport())
return sol::nil;
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();

@ -95,6 +95,10 @@ namespace MWMechanics
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 writeState(ESM::SpellState& state) const;

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

@ -292,4 +292,141 @@
-- @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

@ -149,6 +149,50 @@
-- local Actor = require('openmw.types').Actor
-- 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
-- @field #number current The actor's current level.
@ -631,7 +675,7 @@
-- @field #string speechcraft "speechcraft"
-- @field #string unarmored "unarmored"
--- @{#BookSKILL}
--- DEPRECATED, use @{openmw.core#SKILL}
-- @field [parent=#Book] #BookSKILL SKILL
---
@ -651,7 +695,7 @@
-- @field #string text The text content of the book
-- @field #number weight
-- @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 #number enchantCapacity

Loading…
Cancel
Save