mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec73652b67 | ||
|
|
4c798ba58a | ||
|
|
fbe81b7f01 | ||
|
|
1c0e20cca8 |
@@ -17,12 +17,6 @@
|
||||
-- Secure-Templates system, so the protective wrappers in 3.3.5
|
||||
-- reduce to a plain pcall.
|
||||
--
|
||||
-- Skipping if print is already defined keeps this file safe to load
|
||||
-- alongside any other DLL or addon that might inject its own print.
|
||||
|
||||
if type(print) == "function" then
|
||||
return
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- tostringall: convert every vararg to a string, preserving count.
|
||||
|
||||
@@ -36,7 +36,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
|
||||
| [Hooks](docs/API.md#hooks) | `hooksecurefunc` |
|
||||
| [Input](docs/API.md#input) | `IsLeftAltKeyDown`, `IsLeftControlKeyDown`, `IsLeftShiftKeyDown`, `IsModifierKeyDown`, `IsRightAltKeyDown`, `IsRightControlKeyDown`, `IsRightShiftKeyDown` |
|
||||
| [Instance](docs/API.md#instance) | `GetInstanceInfo` |
|
||||
| [Item](docs/API.md#item) | `C_Item.DoesItemExist`, `C_Item.DoesItemExistByID`, `C_Item.EquipItemByName`, `C_Item.GetCurrentItemLevel`, `C_Item.GetDetailedItemLevelInfo`, `C_Item.GetItemCount`, `C_Item.GetItemFamily`, `C_Item.GetItemGUID`, `C_Item.GetItemIcon`, `C_Item.GetItemIconByID`, `C_Item.GetItemID`, `C_Item.GetItemInfoInstant`, `C_Item.GetItemInventoryType`, `C_Item.GetItemInventoryTypeByID`, `C_Item.GetItemLink`, `C_Item.GetItemLocation`, `C_Item.GetItemMaxStackSize`, `C_Item.GetItemMaxStackSizeByID`, `C_Item.GetItemName`, `C_Item.GetItemNameByID`, `C_Item.GetItemQuality`, `C_Item.GetItemQualityByID`, `C_Item.GetItemSellPrice`, `C_Item.GetItemSellPriceByID`, `C_Item.GetItemSetID`, `C_Item.GetItemSetIDByID`, `C_Item.GetItemSetInfo`, `C_Item.GetItemSpell`, `C_Item.GetItemUniqueness`, `C_Item.GetItemUniquenessByID`, `C_Item.GetStackCount`, `C_Item.GetWeaponEnchantInfo`, `C_Item.IsBound`, `C_Item.IsEquippableItem`, `C_Item.IsEquippedItem`, `C_Item.IsItemDataCached`, `C_Item.IsItemDataCachedByID`, `C_Item.IsItemOpenable`, `C_Item.IsItemOpenableByID`, `C_Item.IsLocked`, `C_Item.LockItem`, `C_Item.LockItemByGUID`, `C_Item.UnlockAllItems`, `C_Item.UnlockItem`, `C_Item.RequestLoadItemData`, `C_Item.RequestLoadItemDataByID`, `C_Item.UseItemByName`, `GetAuctionItemID`, `GetAuctionSellItemID`, `GetAverageItemLevel`, `GetCraftReagentItemID`, `GetInboxItemID`, `GetInventoryItemDurability`, `GetInventoryItemID`, `GetInventoryItemsForSlot`, `GetInventoryItemRepairCost`, `GetItemIcon`, `GetLootRollItemID`, `GetLootSlotItemID`, `GetMerchantItemID`, `GetQuestItemID`, `GetQuestLogItemID`, `GetTradePlayerItemID`, `GetTradeSkillItemID`, `GetTradeSkillReagentItemID`, `GetTradeTargetItemID`, `OffhandHasWeapon` |
|
||||
| [Item](docs/API.md#item) | `C_Item.DoesItemExist`, `C_Item.DoesItemExistByID`, `C_Item.EquipItemByName`, `C_Item.GetCurrentItemLevel`, `C_Item.GetDetailedItemLevelInfo`, `C_Item.GetItemCount`, `C_Item.GetItemFamily`, `C_Item.GetItemGUID`, `C_Item.GetItemIcon`, `C_Item.GetItemIconByID`, `C_Item.GetItemID`, `C_Item.GetItemInfoInstant`, `C_Item.GetItemInventoryType`, `C_Item.GetItemInventoryTypeByID`, `C_Item.GetItemLink`, `C_Item.GetItemLocation`, `C_Item.GetItemMaxStackSize`, `C_Item.GetItemMaxStackSizeByID`, `C_Item.GetItemName`, `C_Item.GetItemNameByID`, `C_Item.GetItemQuality`, `C_Item.GetItemQualityByID`, `C_Item.GetItemSellPrice`, `C_Item.GetItemSellPriceByID`, `C_Item.GetItemSetID`, `C_Item.GetItemSetIDByID`, `C_Item.GetItemSetInfo`, `C_Item.GetItemSpell`, `C_Item.GetItemUniqueness`, `C_Item.GetItemUniquenessByID`, `C_Item.GetStackCount`, `C_Item.GetWeaponEnchantInfo`, `C_Item.IsBound`, `C_Item.IsEquippableItem`, `C_Item.IsEquippedItem`, `C_Item.IsItemDataCached`, `C_Item.IsItemDataCachedByID`, `C_Item.IsItemOpenable`, `C_Item.IsLocked`, `C_Item.LockItem`, `C_Item.LockItemByGUID`, `C_Item.UnlockAllItems`, `C_Item.UnlockItem`, `C_Item.RequestLoadItemData`, `C_Item.RequestLoadItemDataByID`, `C_Item.UseItemByName`, `GetAuctionItemID`, `GetAuctionSellItemID`, `GetAverageItemLevel`, `GetCraftReagentItemID`, `GetInboxItemID`, `GetInventoryItemDurability`, `GetInventoryItemID`, `GetInventoryItemsForSlot`, `GetInventoryItemRepairCost`, `GetItemIcon`, `GetLootRollItemID`, `GetLootSlotItemID`, `GetMerchantItemID`, `GetQuestItemID`, `GetQuestLogItemID`, `GetTradePlayerItemID`, `GetTradeSkillItemID`, `GetTradeSkillReagentItemID`, `GetTradeTargetItemID`, `OffhandHasWeapon` |
|
||||
| [Macros](docs/API.md#macros) | `GetMacroSpell` |
|
||||
| [Map](docs/API.md#map) | `C_Map.GetBestMapForUnit` |
|
||||
| [MerchantFrame](docs/API.md#merchantframe) | `C_MerchantFrame.GetBuybackItemID`, `C_MerchantFrame.GetItemInfo`, `C_MerchantFrame.GetNumJunkItems`, `C_MerchantFrame.IsMerchantItemRefundable`, `C_MerchantFrame.IsSellAllJunkEnabled`, `C_MerchantFrame.SellAllJunkItems` |
|
||||
@@ -66,7 +66,7 @@ the glue state because GlueXML had no way to reach them otherwise.
|
||||
|
||||
| Group | Calls |
|
||||
|-------|-------|
|
||||
| CharacterOrder | `GetSavedCharacterOrder`, `SetSavedCharacterOrder` (persist to `WTF\Account\...\ClassicAPI.txt`) |
|
||||
| [CharacterList](docs/API.md#characterlist) | `GetSavedCharacterOrder`, `SetSavedCharacterOrder` (persist to `WTF\Account\...\ClassicAPI.txt`) |
|
||||
| CVar | `GetCVar`, `SetCVar`, `RegisterCVar`, `GetCVarDefault`, `C_CVar.GetCVarBool` (storage is process-global — writes from glue are visible in-world and vice versa) |
|
||||
| Script | `RunScript` (compile and run a Lua chunk in the glue state's globals — useful for slash-command-style helpers in GlueXML) |
|
||||
|
||||
|
||||
+42
-33
@@ -179,7 +179,7 @@ build instructions.
|
||||
- [`C_Item.GetItemUniqueness(itemLocation)` / `C_Item.GetItemUniquenessByID(item)`](#c_itemgetitemuniquenessitemlocation--c_itemgetitemuniquenessbyiditem)
|
||||
- [`C_Item.GetItemLink(itemLocation)`](#c_itemgetitemlinkitemlocation)
|
||||
- [`C_Item.GetItemInventoryType(itemLocation)` / `C_Item.GetItemInventoryTypeByID(item)`](#c_itemgetiteminventorytypeitemlocation--c_itemgetiteminventorytypebyiditem)
|
||||
- [`C_Item.IsItemOpenable(itemLocation)` / `C_Item.IsItemOpenableByID(item)`](#c_itemisitemopenableitemlocation--c_itemisitemopenablebyiditem)
|
||||
- [`C_Item.IsItemOpenable(itemLocation)`](#c_itemisitemopenableitemlocation)
|
||||
- [`C_Item.IsLocked(itemLocation)`](#c_itemislockeditemlocation)
|
||||
- [`C_Item.LockItem(itemLocation)`](#c_itemlockitemitemlocation)
|
||||
- [`C_Item.LockItemByGUID(itemGUID)`](#c_itemlockitembyguiditemguid)
|
||||
@@ -650,10 +650,6 @@ if UnitAffectingCombat("player") then
|
||||
end
|
||||
```
|
||||
|
||||
Equivalent to `UnitAffectingCombat("player")` but faster — reads the
|
||||
`UNIT_FLAG_IN_COMBAT` bit directly off the player CGUnit's
|
||||
m_objectFields, no token-resolution roundtrip.
|
||||
|
||||
## Container
|
||||
|
||||
### `C_Container.GetContainerItemID(bagIndex, slotIndex)`
|
||||
@@ -885,10 +881,9 @@ empty). Cross-checked in-game against a manual
|
||||
### `C_Container.IsContainerItemOpenable(containerIndex, slotIndex)`
|
||||
|
||||
Positional-arg wrapper for [`C_Item.IsItemOpenable`](#c_itemisitemopenableitemlocation--c_itemisitemopenablebyiditem)
|
||||
against a bag/slot pair. Returns `true` if the slot holds a right-
|
||||
click-openable item (sack, clam, lockbox, etc.), `false` for non-
|
||||
openable items, and `nil` for empty slots or items whose data hasn't
|
||||
been cached yet.
|
||||
against a bag/slot pair. Same `(isOpenable, canOpen)` tuple — see
|
||||
the linked section for full semantics. Both returns are `nil` for
|
||||
empty slots or items whose data hasn't been cached yet.
|
||||
|
||||
```lua
|
||||
-- Sweep main backpack for openables
|
||||
@@ -4094,40 +4089,54 @@ if t == 1 then -- head
|
||||
end
|
||||
```
|
||||
|
||||
### `C_Item.IsItemOpenable(itemLocation)` / `C_Item.IsItemOpenableByID(item)`
|
||||
### `C_Item.IsItemOpenable(itemLocation)`
|
||||
|
||||
Returns `true` if the item is right-click-openable — a sack, clam,
|
||||
simple chest, quest box, lockbox, etc. (anything whose tooltip would
|
||||
show `<Right Click to Open>`). Intrinsic to the item type, not
|
||||
contextual: a locked lockbox you can't currently open still reports
|
||||
`true` here.
|
||||
Returns two values: `(isOpenable, canOpen)`.
|
||||
|
||||
- **`isOpenable`** — `true` if the item type is right-click-openable
|
||||
(sack, clam, simple chest, quest box, lockbox, etc. — anything
|
||||
whose tooltip *could* show `<Right Click to Open>`). Intrinsic to
|
||||
the item type. From `ItemStats.Flags & 0x4`.
|
||||
- **`canOpen`** — `true` if the player can right-click *this specific
|
||||
instance* and trigger the open action right now. For unlocked-by-
|
||||
default items (clams, sacks) this matches `isOpenable`. For items
|
||||
with a lock (lockboxes), it's only true after the lock has been
|
||||
removed (rogue Pick Lock, key item, etc.) — so a priest looking at
|
||||
a fresh lockbox sees `(true, false)`, while a rogue who just
|
||||
picked it sees `(true, true)`.
|
||||
|
||||
Both values are `nil` when the item data isn't cached yet (background
|
||||
`SMSG_ITEM_QUERY_SINGLE` fired) or when the location is empty /
|
||||
invalid. Lets callers distinguish "data unknown" from "definitely
|
||||
not openable".
|
||||
|
||||
There's no `ByID` variant — the `canOpen` check depends on a specific
|
||||
instance's `ITEM_FIELD_FLAGS`, which you can only have via an
|
||||
itemLocation. If you only have an itemID, use
|
||||
`C_Item.GetItemInfoInstant` to inspect the item type instead.
|
||||
|
||||
```lua
|
||||
-- Loop a bag and gather every openable
|
||||
-- Loop a bag and gather every openable the player can act on now
|
||||
for slot = 1, GetContainerNumSlots(0) do
|
||||
local itemID = C_Container.GetContainerItemID(0, slot)
|
||||
if itemID and C_Item.IsItemOpenableByID(itemID) then
|
||||
-- pickup + use
|
||||
local _, canOpen = C_Container.IsContainerItemOpenable(0, slot)
|
||||
if canOpen then
|
||||
-- pickup + use; this slot is good to open
|
||||
end
|
||||
end
|
||||
|
||||
-- Single-slot check via itemLocation
|
||||
if C_Item.IsItemOpenable({bagID = 0, slotIndex = 1}) then
|
||||
-- ...
|
||||
-- Show a different hint depending on the lock state
|
||||
local isOpenable, canOpen = C_Item.IsItemOpenable({bagID = 0, slotIndex = 1})
|
||||
if isOpenable and not canOpen then
|
||||
print("This item is locked.")
|
||||
end
|
||||
```
|
||||
|
||||
Reads the openable bit (`Flags & 0x4`) from the client-side ItemSparse
|
||||
cache. Same bit the engine's tooltip builder uses to gate the
|
||||
`ITEM_OPENABLE` string. Returns `nil` for uncached items (so callers
|
||||
can distinguish "data unknown" from "definitely not openable") and
|
||||
fires a background `SMSG_ITEM_QUERY_SINGLE` so a follow-up call after
|
||||
`GET_ITEM_INFO_RECEIVED` resolves to a real `true` / `false`; in
|
||||
practice bag items are cached on bag-update and resolve instantly.
|
||||
|
||||
The "ByID" variant accepts a numeric itemID, an item link, or an
|
||||
`"item:NNN..."` string — same input shape as
|
||||
`C_Item.GetItemInfoInstant`.
|
||||
Implementation: `isOpenable` reads `ItemStats.Flags & 0x4` from the
|
||||
client-side ItemSparse cache. `canOpen` adds a check that either
|
||||
`ItemStats.LockID == 0` (no lock) or the CGItem instance's
|
||||
`ITEM_FIELD_FLAGS & 0x4` (UNLOCKED) bit is set. Same conditions the
|
||||
engine's tooltip builder uses at `0x0052E323` to gate the
|
||||
`ITEM_OPENABLE` line.
|
||||
|
||||
### `C_Item.IsLocked(itemLocation)`
|
||||
|
||||
|
||||
@@ -893,6 +893,14 @@ enum Offsets {
|
||||
// descriptor bytes for a worn-and-bound item.
|
||||
OFF_DESCRIPTOR_FLAGS = 0x3C,
|
||||
ITEM_FLAG_SOULBOUND = 0x01,
|
||||
// Bit 2 — instance is "unlocked", i.e. the lock referenced by
|
||||
// `ItemStats.LockID` has been opened (rogue Pick Lock, key item,
|
||||
// etc.). For unlockable items, the engine gates the
|
||||
// `<Right Click to Open>` tooltip on this bit: a fresh lockbox
|
||||
// has it clear (priest sees no openable affordance), a picked
|
||||
// lockbox has it set. Items with `LockID == 0` (clams, simple
|
||||
// sacks) never need the bit.
|
||||
ITEM_FLAG_UNLOCKED = 0x04,
|
||||
|
||||
// Client-side "in-transaction" lock — set when the engine puts an
|
||||
// item on the cursor / mail-attaches it / trade-attaches it, cleared
|
||||
@@ -1079,6 +1087,14 @@ enum Offsets {
|
||||
OFF_ITEMSTATS_FLAGS = 0x20,
|
||||
ITEMSTATS_FLAG_OPENABLE = 0x4,
|
||||
OFF_ITEMSTATS_SELL_PRICE = 0x28,
|
||||
// `m_lockID` — references `Lock.dbc`. Zero means "no lock"; any
|
||||
// nonzero value gates the openable interaction on the player
|
||||
// successfully unlocking the item (instance flag bit 2 in
|
||||
// `ITEM_FIELD_FLAGS`). Same field the tooltip builder reads as
|
||||
// `ItemStats[+0x1AC]` (puVar4[0x6b]) when deciding whether the
|
||||
// `<Right Click to Open>` line should require the lock to be
|
||||
// picked first.
|
||||
OFF_ITEMSTATS_LOCK_ID = 0x1AC,
|
||||
OFF_ITEMSTATS_INVENTORY_TYPE = 0x2C,
|
||||
OFF_ITEMSTATS_ITEM_LEVEL = 0x38, // u32 — base ilvl from ItemSparse
|
||||
// `m_stackable` — max stack size for this item type (1 for non-
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Container.IsContainerItemOpenable(bag, slot)` — positional-arg
|
||||
// wrapper around `Item::Openable::IsItemIDOpenable`. Same chain
|
||||
// wrapper around `Item::Openable::PushIsItemOpenable`. Same chain
|
||||
// `C_Container.GetContainerItemDurability` and friends use:
|
||||
// `ResolveBag` → `GetItemBySlot` → instance-block itemID → cache flag
|
||||
// read.
|
||||
// `ResolveBag` → `GetItemBySlot`. Returns the
|
||||
// `(isOpenable, canOpen)` tuple — see [[item/Openable.h]].
|
||||
|
||||
#include "Game.h"
|
||||
#include "item/ID.h"
|
||||
#include "item/Location.h"
|
||||
#include "item/Openable.h"
|
||||
|
||||
@@ -34,9 +33,8 @@ int __fastcall Script_C_Container_IsContainerItemOpenable(void *L) {
|
||||
}
|
||||
const int bagID = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
const int slotIndex = static_cast<int>(Game::Lua::ToNumber(L, 2));
|
||||
const int itemID = Item::ID::FromCGItem(
|
||||
Item::Location::ResolveBag(L, bagID, slotIndex));
|
||||
return Item::Openable::PushIsItemOpenable(L, static_cast<uint32_t>(itemID));
|
||||
return Item::Openable::PushIsItemOpenable(
|
||||
L, Item::Location::ResolveBag(L, bagID, slotIndex));
|
||||
}
|
||||
|
||||
void RegisterLuaFunctions() {
|
||||
|
||||
+45
-26
@@ -11,19 +11,22 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Item.IsItemOpenable` / `C_Item.IsItemOpenableByID` — backport of
|
||||
// the modern WoW accessor that says "right-clicking this item triggers
|
||||
// an open/loot interaction" (clams, sacks, simple chests, quest
|
||||
// boxes). The flag is intrinsic to the item type, read from the
|
||||
// client-side ItemSparse cache.
|
||||
// `C_Item.IsItemOpenable(itemLocation)` — backport of the modern WoW
|
||||
// accessor that says "right-clicking this item triggers an open/loot
|
||||
// interaction" (clams, sacks, simple chests, quest boxes, lockboxes).
|
||||
// Returns two values: `(isOpenable, canOpen)` — the first is the
|
||||
// item-type-intrinsic openable flag, the second is whether the
|
||||
// player can right-click *this specific instance* right now (lock
|
||||
// is gone or never existed).
|
||||
//
|
||||
// Vanilla 1.12's tooltip builder gates the `<Right Click to Open>`
|
||||
// string (`ITEM_OPENABLE`) on `ItemStats_C.Flags & 0x4` (see
|
||||
// `Offsets::ITEMSTATS_FLAG_OPENABLE`). We surface the same bit.
|
||||
// string (`ITEM_OPENABLE`) on `ItemStats.Flags & 0x4` AND
|
||||
// (`LockID == 0` OR `ITEM_FIELD_FLAGS & 0x4`). We mirror both checks
|
||||
// — see `Offsets::ITEMSTATS_FLAG_OPENABLE`, `ITEMSTATS_LOCK_ID`, and
|
||||
// `ITEM_FLAG_UNLOCKED`.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "item/Arg.h"
|
||||
#include "item/Data.h"
|
||||
#include "item/ID.h"
|
||||
#include "item/Location.h"
|
||||
@@ -48,20 +51,44 @@ const uint8_t *PeekItemRecord(uint32_t itemID) {
|
||||
|
||||
} // namespace
|
||||
|
||||
int PushIsItemOpenable(void *L, uint32_t itemID) {
|
||||
if (itemID == 0)
|
||||
namespace {
|
||||
|
||||
// Reads `ITEM_FIELD_FLAGS` from the instance descriptor at +0x114.
|
||||
// Defaults to 0 if `cgItem` is null or the descriptor pointer hasn't
|
||||
// been populated yet.
|
||||
uint32_t InstanceFlags(const uint8_t *cgItem) {
|
||||
if (cgItem == nullptr)
|
||||
return 0;
|
||||
const uint8_t *record = PeekItemRecord(itemID);
|
||||
auto *descriptor = *reinterpret_cast<const uint8_t *const *>(
|
||||
cgItem + Offsets::OFF_ITEM_DESCRIPTOR);
|
||||
if (descriptor == nullptr)
|
||||
return 0;
|
||||
return *reinterpret_cast<const uint32_t *>(
|
||||
descriptor + Offsets::OFF_DESCRIPTOR_FLAGS);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int PushIsItemOpenable(void *L, const uint8_t *cgItem) {
|
||||
const int itemID = Item::ID::FromCGItem(cgItem);
|
||||
if (itemID <= 0)
|
||||
return 0;
|
||||
const uint8_t *record = PeekItemRecord(static_cast<uint32_t>(itemID));
|
||||
if (record == nullptr) {
|
||||
// Background fill so a follow-up call (after the cache warms)
|
||||
// returns the real value. Same pattern Quality / Info use.
|
||||
Item::Data::WarmCache(itemID);
|
||||
Item::Data::WarmCache(static_cast<uint32_t>(itemID));
|
||||
return 0;
|
||||
}
|
||||
const uint32_t flags = *reinterpret_cast<const uint32_t *>(
|
||||
const uint32_t typeFlags = *reinterpret_cast<const uint32_t *>(
|
||||
record + Offsets::OFF_ITEMSTATS_FLAGS);
|
||||
Game::Lua::PushBool(L, (flags & Offsets::ITEMSTATS_FLAG_OPENABLE) != 0);
|
||||
return 1;
|
||||
const uint32_t lockID = *reinterpret_cast<const uint32_t *>(
|
||||
record + Offsets::OFF_ITEMSTATS_LOCK_ID);
|
||||
const bool isOpenable = (typeFlags & Offsets::ITEMSTATS_FLAG_OPENABLE) != 0;
|
||||
const uint32_t instFlags = InstanceFlags(cgItem);
|
||||
const bool canOpen = isOpenable && (lockID == 0 ||
|
||||
(instFlags & Offsets::ITEM_FLAG_UNLOCKED) != 0);
|
||||
Game::Lua::PushBool(L, isOpenable);
|
||||
Game::Lua::PushBool(L, canOpen);
|
||||
return 2;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -71,20 +98,12 @@ int __fastcall Script_C_Item_IsItemOpenable(void *L) {
|
||||
Game::Lua::Error(L, "Usage: C_Item.IsItemOpenable(itemLocation)");
|
||||
return 0;
|
||||
}
|
||||
const int itemID = Item::ID::FromCGItem(Item::Location::Resolve(L, 1));
|
||||
return PushIsItemOpenable(L, static_cast<uint32_t>(itemID));
|
||||
}
|
||||
|
||||
int __fastcall Script_C_Item_IsItemOpenableByID(void *L) {
|
||||
const int itemID = Item::Arg::ResolveItemID(L, 1);
|
||||
return PushIsItemOpenable(L, static_cast<uint32_t>(itemID));
|
||||
return PushIsItemOpenable(L, Item::Location::Resolve(L, 1));
|
||||
}
|
||||
|
||||
void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Item", "IsItemOpenable",
|
||||
&Script_C_Item_IsItemOpenable);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "IsItemOpenableByID",
|
||||
&Script_C_Item_IsItemOpenableByID);
|
||||
}
|
||||
|
||||
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
+15
-9
@@ -17,15 +17,21 @@
|
||||
|
||||
namespace Item::Openable {
|
||||
|
||||
// Pushes the openable bool for `itemID` and returns 1, OR pushes
|
||||
// nothing and returns 0 when the item is uncached / itemID is
|
||||
// invalid. Caller can `return PushIsItemOpenable(...)` from a Lua
|
||||
// C function; pushing 0 surfaces as `nil` to the Lua caller, which
|
||||
// distinguishes "data unknown" from "definitely not openable".
|
||||
// Pushes two values for the openable check on a player-owned item
|
||||
// (`isOpenable, canOpen`), returning 2. The two semantics:
|
||||
//
|
||||
// Reads `ItemStats_C.Flags & 0x4` from the client-side item cache.
|
||||
// Fires a background `SMSG_ITEM_QUERY_SINGLE` for uncached items so
|
||||
// a follow-up call after `GET_ITEM_INFO_RECEIVED` resolves correctly.
|
||||
int PushIsItemOpenable(void *L, uint32_t itemID);
|
||||
// `isOpenable` — bool. The item type carries the openable flag
|
||||
// (`ItemStats.Flags & 0x4` — clams, sacks, lockboxes, etc.).
|
||||
// `canOpen` — bool. The player can right-click this specific
|
||||
// instance and trigger the open action right now. True for
|
||||
// openable items with no lock (`LockID == 0`) or whose instance
|
||||
// has been unlocked (`ITEM_FIELD_FLAGS & 0x4` — set after a
|
||||
// rogue picks the lock, a key is used, etc.). False for fresh
|
||||
// lockboxes a non-rogue is looking at.
|
||||
//
|
||||
// `cgItem` may be null (empty slot), in which case nothing is pushed
|
||||
// and 0 is returned (Lua sees nil for both returns). Uncached itemID
|
||||
// also returns 0 plus a background cache warm.
|
||||
int PushIsItemOpenable(void *L, const uint8_t *cgItem);
|
||||
|
||||
} // namespace Item::Openable
|
||||
|
||||
Reference in New Issue
Block a user