1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-30 08:15:37 +00:00

Merge branch 'lua_sgetcurrent_conditionofitem_itemdata_idea' into 'master'

Lua API to get/set item condition

See merge request OpenMW/openmw!3421
This commit is contained in:
Zackhasacat 2023-11-23 23:10:39 +00:00
commit 5623a5cf01
12 changed files with 202 additions and 22 deletions

View file

@ -62,7 +62,7 @@ add_openmw_dir (mwscript
add_openmw_dir (mwlua
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings
camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings itemdata
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player 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/levelledlist types/terminal
worker magicbindings factionbindings classbindings
)

View file

@ -0,0 +1,134 @@
#include "itemdata.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
#include "../mwworld/class.hpp"
#include "objectvariant.hpp"
namespace
{
using SelfObject = MWLua::SelfObject;
using Index = const SelfObject::CachedStat::Index&;
constexpr std::array properties = { "condition", /*"enchantmentCharge", "soul", "owner", etc..*/ };
void invalidPropErr(std::string_view prop, const MWWorld::Ptr& ptr)
{
throw std::runtime_error("'" + std::string(prop) + "'" + " property does not exist for item "
+ std::string(ptr.getClass().getName(ptr)) + "(" + std::string(ptr.getTypeDescription()) + ")");
}
}
namespace MWLua
{
static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj)
{
if (!obj.mStatsCache.empty())
return; // was already added before
manager->addAction(
[obj = Object(obj)] {
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
if (scripts)
scripts->applyStatsCache();
},
"StatUpdateAction");
}
class ItemData
{
ObjectVariant mObject;
public:
ItemData(ObjectVariant object)
: mObject(object)
{
}
sol::object get(const Context& context, std::string_view prop) const
{
if (mObject.isSelfObject())
{
SelfObject* self = mObject.asSelfObject();
auto it = self->mStatsCache.find({ &ItemData::setValue, std::monostate{}, prop });
if (it != self->mStatsCache.end())
return it->second;
}
return sol::make_object(context.mLua->sol(), getValue(context, prop));
}
void set(const Context& context, std::string_view prop, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] = value;
}
sol::object getValue(const Context& context, std::string_view prop) const
{
if (prop == "condition")
{
MWWorld::Ptr o = mObject.ptr();
if (o.mRef->getType() == ESM::REC_LIGH)
return sol::make_object(context.mLua->sol(), o.getClass().getRemainingUsageTime(o));
else if (o.getClass().hasItemHealth(o))
return sol::make_object(
context.mLua->sol(), o.getClass().getItemHealth(o) + o.getCellRef().getChargeIntRemainder());
}
return sol::lua_nil;
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
if (prop == "condition")
{
float cond = LuaUtil::cast<float>(value);
if (ptr.mRef->getType() == ESM::REC_LIGH)
ptr.getClass().setRemainingUsageTime(ptr, cond);
else if (ptr.getClass().hasItemHealth(ptr))
{
// if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0
ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond)));
ptr.getCellRef().setCharge(std::max(0.f, cond));
}
else
invalidPropErr(prop, ptr);
}
}
};
}
namespace sol
{
template <>
struct is_automagical<MWLua::ItemData> : std::false_type
{
};
}
namespace MWLua
{
void addItemDataBindings(sol::table& item, const Context& context)
{
item["itemData"] = [](const sol::object& object) -> sol::optional<ItemData> {
ObjectVariant o(object);
if (o.ptr().getClass().isItem(o.ptr()) || o.ptr().mRef->getType() == ESM::REC_LIGH)
return ItemData(std::move(o));
return {};
};
sol::usertype<ItemData> itemData = context.mLua->sol().new_usertype<ItemData>("ItemData");
itemData[sol::meta_function::new_index] = [](const ItemData& stat, const sol::variadic_args args) {
throw std::runtime_error("Unknown ItemData property '" + args.get<std::string>() + "'");
};
for (std::string_view prop : properties)
{
itemData[prop] = sol::property([context, prop](const ItemData& stat) { return stat.get(context, prop); },
[context, prop](const ItemData& stat, const sol::object& value) { stat.set(context, prop, value); });
}
}
}

View file

@ -0,0 +1,13 @@
#ifndef MWLUA_ITEMDATA_H
#define MWLUA_ITEMDATA_H
#include <sol/forward.hpp>
namespace MWLua
{
struct Context;
void addItemDataBindings(sol::table& item, const Context& context);
}
#endif // MWLUA_ITEMDATA_H

View file

@ -22,7 +22,7 @@ namespace MWLua
class CachedStat
{
public:
using Index = std::variant<int, ESM::RefId>;
using Index = std::variant<int, ESM::RefId, std::monostate>;
using Setter = void (*)(const Index&, std::string_view, const MWWorld::Ptr&, const sol::object&);
CachedStat(Setter setter, Index index, std::string_view prop)

View file

@ -85,7 +85,7 @@ namespace MWLua
public:
sol::object getCurrent(const Context& context) const
{
return getValue(context, mObject, &LevelStat::setValue, 0, "current",
return getValue(context, mObject, &LevelStat::setValue, std::monostate{}, "current",
[](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); });
}
@ -93,7 +93,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value;
obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, std::monostate{}, "current" }] = value;
}
sol::object getProgress(const Context& context) const

View file

@ -2,11 +2,13 @@
#include "../../mwworld/class.hpp"
#include "../itemdata.hpp"
#include "types.hpp"
namespace MWLua
{
void addItemBindings(sol::table item)
void addItemBindings(sol::table item, const Context& context)
{
item["getEnchantmentCharge"]
= [](const Object& object) { return object.ptr().getCellRef().getEnchantmentCharge(); };
@ -14,5 +16,7 @@ namespace MWLua
= [](const GObject& object, float charge) { object.ptr().getCellRef().setEnchantmentCharge(charge); };
item["isRestocking"]
= [](const Object& object) -> bool { return object.ptr().getRefData().getCount(false) < 0; };
addItemDataBindings(item, context);
}
}

View file

@ -191,9 +191,11 @@ namespace MWLua
addActorBindings(
addType(ObjectTypeName::Actor, { ESM::REC_INTERNAL_PLAYER, ESM::REC_CREA, ESM::REC_NPC_ }), context);
addItemBindings(addType(ObjectTypeName::Item,
{ ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR, ESM::REC_LIGH, ESM::REC_MISC, ESM::REC_ALCH,
ESM::REC_WEAP, ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA }));
addItemBindings(
addType(ObjectTypeName::Item,
{ ESM::REC_ARMO, ESM::REC_BOOK, ESM::REC_CLOT, ESM::REC_INGR, ESM::REC_LIGH, ESM::REC_MISC,
ESM::REC_ALCH, ESM::REC_WEAP, ESM::REC_APPA, ESM::REC_LOCK, ESM::REC_PROB, ESM::REC_REPA }),
context);
addLockableBindings(
addType(ObjectTypeName::Lockable, { ESM::REC_CONT, ESM::REC_DOOR, ESM::REC_CONT4, ESM::REC_DOOR4 }));

View file

@ -47,7 +47,7 @@ namespace MWLua
void addBookBindings(sol::table book, const Context& context);
void addContainerBindings(sol::table container, const Context& context);
void addDoorBindings(sol::table door, const Context& context);
void addItemBindings(sol::table item);
void addItemBindings(sol::table item, const Context& context);
void addActorBindings(sol::table actor, const Context& context);
void addWeaponBindings(sol::table weapon, const Context& context);
void addNpcBindings(sol::table npc, const Context& context);

View file

@ -188,19 +188,14 @@ namespace MWWorld
void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder)
{
auto esm3Visit = [&](ESM::CellRef& cellRef3) {
cellRef3.mChargeIntRemainder += std::abs(chargeRemainder);
if (cellRef3.mChargeIntRemainder > 1.0f)
cellRef3.mChargeIntRemainder -= std::abs(chargeRemainder);
if (cellRef3.mChargeIntRemainder <= -1.0f)
{
float newChargeRemainder = (cellRef3.mChargeIntRemainder - std::floor(cellRef3.mChargeIntRemainder));
if (cellRef3.mChargeInt <= static_cast<int>(cellRef3.mChargeIntRemainder))
{
cellRef3.mChargeInt = 0;
}
else
{
cellRef3.mChargeInt -= static_cast<int>(cellRef3.mChargeIntRemainder);
}
float newChargeRemainder = std::modf(cellRef3.mChargeIntRemainder, &cellRef3.mChargeIntRemainder);
cellRef3.mChargeInt += static_cast<int>(cellRef3.mChargeIntRemainder);
cellRef3.mChargeIntRemainder = newChargeRemainder;
if (cellRef3.mChargeInt < 0)
cellRef3.mChargeInt = 0;
}
};
std::visit(ESM::VisitOverload{
@ -211,6 +206,16 @@ namespace MWWorld
mCellRef.mVariant);
}
void CellRef::setChargeIntRemainder(float chargeRemainder)
{
std::visit(ESM::VisitOverload{
[&](ESM4::Reference& /*ref*/) {},
[&](ESM4::ActorCharacter&) {},
[&](ESM::CellRef& ref) { ref.mChargeIntRemainder = chargeRemainder; },
},
mCellRef.mVariant);
}
void CellRef::setChargeFloat(float charge)
{
std::visit(ESM::VisitOverload{

View file

@ -118,9 +118,22 @@ namespace MWWorld
};
return std::visit(Visitor(), mCellRef.mVariant);
} // Implemented as union with int charge
float getChargeIntRemainder() const
{
struct Visitor
{
float operator()(const ESM::CellRef& ref) { return ref.mChargeIntRemainder; }
float operator()(const ESM4::Reference& /*ref*/) { return 0; }
float operator()(const ESM4::ActorCharacter&) { throw std::logic_error("Not applicable"); }
};
return std::visit(Visitor(), mCellRef.mVariant);
}
void setCharge(int charge);
void setChargeFloat(float charge);
void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1
void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if <= -1
// Stores fractional part of mChargeInt
void setChargeIntRemainder(float chargeRemainder);
// The NPC that owns this object (and will get angry if you steal it)
ESM::RefId getOwner() const

View file

@ -60,7 +60,7 @@ namespace ESM
int32_t mChargeInt; // Used by everything except lights
float mChargeFloat; // Used only by lights
};
float mChargeIntRemainder; // Stores amount of charge not subtracted from mChargeInt
float mChargeIntRemainder; // Fractional part of mChargeInt
// Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full).
float mEnchantmentCharge;

View file

@ -675,6 +675,15 @@
-- @param openmw.core#GameObject object
-- @return #boolean
---
-- Set of properties that differentiates one item from another of the same record type.
-- @function [parent=#Item] itemData
-- @param openmw.core#GameObject item
-- @return #ItemData
---
-- @type ItemData
-- @field #number condition The item's current condition. Time remaining for lights. Uses left for lockpicks and probes. Current health for weapons and armor.
--------------------------------------------------------------------------------
-- @{#Creature} functions