Skip to content

Commit

Permalink
Merge branch 'lua_sgetcurrent_conditionofitem_itemdata_idea' into 'ma…
Browse files Browse the repository at this point in the history
…ster'

Lua API to get/set item condition

See merge request OpenMW/openmw!3421
  • Loading branch information
Zackhasacat committed Nov 23, 2023
2 parents 08538dd + 1bff02e commit 5623a5c
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 22 deletions.
2 changes: 1 addition & 1 deletion apps/openmw/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
134 changes: 134 additions & 0 deletions apps/openmw/mwlua/itemdata.cpp
Original file line number Diff line number Diff line change
@@ -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); });
}
}
}
13 changes: 13 additions & 0 deletions apps/openmw/mwlua/itemdata.hpp
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion apps/openmw/mwlua/localscripts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions apps/openmw/mwlua/stats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ 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(); });
}

void setCurrent(const Context& context, const sol::object& value) const
{
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
Expand Down
6 changes: 5 additions & 1 deletion apps/openmw/mwlua/types/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@

#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(); };
item["setEnchantmentCharge"]
= [](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);
}
}
8 changes: 5 additions & 3 deletions apps/openmw/mwlua/types/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 }));

Expand Down
2 changes: 1 addition & 1 deletion apps/openmw/mwlua/types/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 16 additions & 11 deletions apps/openmw/mwworld/cellref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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{
Expand Down
15 changes: 14 additions & 1 deletion apps/openmw/mwworld/cellref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion components/esm3/cellref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions files/lua_api/openmw/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5623a5c

Please sign in to comment.