mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-11 12:27:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bd33288c9 | ||
|
|
bf7e476c10 | ||
|
|
8f3ed96d51 | ||
|
|
e1ffaab6b4 |
@@ -15,8 +15,6 @@
|
||||
-- - 1.12 has no concept of an item GUID and no public item-lock API,
|
||||
-- so those return nil / no-op.
|
||||
|
||||
C_Item.GetItemInfo = GetItemInfo
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Item = {}
|
||||
|
||||
@@ -41,7 +41,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
|
||||
| [Hooks](docs/API.md#hooks) | `hooksecurefunc` |
|
||||
| [Input](docs/API.md#input) | `GetMouseButtonClicked`, `IsLeftAltKeyDown`, `IsLeftControlKeyDown`, `IsLeftShiftKeyDown`, `IsModifierKeyDown`, `IsMouseButtonDown`, `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.GetEnchantInfo`, `C_Item.GetItemCount`, `C_Item.GetItemFamily`, `C_Item.GetItemGUID`, `C_Item.GetItemIcon`, `C_Item.GetItemIconByID`, `C_Item.GetItemID`, `C_Item.GetItemInfoInstant`, `C_Item.GetItemInventorySlotInfo`, `C_Item.GetItemInventorySlotKey`, `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.GetItemStatDelta`, `C_Item.GetItemStats`, `C_Item.GetItemSubClassInfo`, `C_Item.GetItemUniqueness`, `C_Item.GetItemUniquenessByID`, `C_Item.GetStackCount`, `C_Item.GetWeaponEnchantInfo`, `C_Item.IsBound`, `C_Item.IsConsumableItem`, `C_Item.IsEquippableItem`, `C_Item.IsEquippedItem`, `C_Item.IsItemDataCached`, `C_Item.IsItemDataCachedByID`, `C_Item.IsItemGUIDInInventory`, `C_Item.IsItemInRange`, `C_Item.IsItemOpenable`, `C_Item.IsLocked`, `C_Item.LockItem`, `C_Item.LockItemByGUID`, `C_Item.PickupItem`, `C_Item.UnlockAllItems`, `C_Item.UnlockItem`, `C_Item.RequestLoadItemData`, `C_Item.RequestLoadItemDataByID`, `C_Item.UseAtCursor`, `C_Item.UseAtUnit`, `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.GetEnchantInfo`, `C_Item.GetItemCount`, `C_Item.GetItemFamily`, `C_Item.GetItemGUID`, `C_Item.GetItemIcon`, `C_Item.GetItemIconByID`, `C_Item.GetItemID`, `C_Item.GetItemInfo`, `C_Item.GetItemInfoInstant`, `C_Item.GetItemInventorySlotInfo`, `C_Item.GetItemInventorySlotKey`, `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.GetItemStatDelta`, `C_Item.GetItemStats`, `C_Item.GetItemSubClassInfo`, `C_Item.GetItemUniqueness`, `C_Item.GetItemUniquenessByID`, `C_Item.GetStackCount`, `C_Item.GetWeaponEnchantInfo`, `C_Item.IsBound`, `C_Item.IsConsumableItem`, `C_Item.IsEquippableItem`, `C_Item.IsEquippedItem`, `C_Item.IsItemDataCached`, `C_Item.IsItemDataCachedByID`, `C_Item.IsItemGUIDInInventory`, `C_Item.IsItemInRange`, `C_Item.IsItemOpenable`, `C_Item.IsLocked`, `C_Item.LockItem`, `C_Item.LockItemByGUID`, `C_Item.PickupItem`, `C_Item.UnlockAllItems`, `C_Item.UnlockItem`, `C_Item.RequestLoadItemData`, `C_Item.RequestLoadItemDataByID`, `C_Item.UseAtCursor`, `C_Item.UseAtUnit`, `C_Item.UseItemByName`, `GetAuctionItemID`, `GetAuctionSellItemID`, `GetAverageItemLevel`, `GetCraftReagentItemID`, `GetInboxItemID`, `GetInventoryItemDurability`, `GetInventoryItemID`, `GetInventoryItemsForSlot`, `GetInventoryItemRepairCost`, `GetItemIcon`, `GetLootRollItemID`, `GetLootSlotItemID`, `GetMerchantItemID`, `GetQuestItemID`, `GetQuestLogItemID`, `GetTradePlayerItemID`, `GetTradeSkillItemID`, `GetTradeSkillReagentItemID`, `GetTradeTargetItemID`, `OffhandHasWeapon` |
|
||||
| [Loot](docs/API.md#loot) | `C_Loot.GetNearbyLootableUnits`, `C_Loot.GetLastScanResults`, `C_Loot.IsScanInProgress`, `C_Loot.LootUnit`, `C_Loot.LootUnitItem`, `C_Loot.ScanNearbyLoot` |
|
||||
| [LootHistory](docs/API.md#loothistory) | `C_LootHistory.GetNumItems`, `C_LootHistory.GetItem`, `C_LootHistory.GetPlayerInfo`, `C_LootHistory.Clear` |
|
||||
| [LossOfControl](docs/API.md#lossofcontrol) | `C_LossOfControl.GetActiveLossOfControlData`, `C_LossOfControl.GetActiveLossOfControlDataCount` |
|
||||
@@ -55,7 +55,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
|
||||
| [NameCache](docs/API.md#namecache) | `C_PlayerCache.GetPlayerInfoByName`, `C_PlayerCache.IsEnabled`, `C_PlayerCache.IsScanEnabled`, `C_PlayerCache.RememberPlayer`, `C_PlayerCache.SetEnabled`, `C_PlayerCache.SetScanEnabled`, `GetPlayerInfoByGUID`, `UnitNameFromGUID` |
|
||||
| [NewItems](docs/API.md#newitems) | `C_NewItems.ClearAll`, `C_NewItems.IsNewItem`, `C_NewItems.RemoveNewItem` |
|
||||
| [PlayerInfo](docs/API.md#playerinfo) | `C_PlayerInfo.CanUseItem`, `C_PlayerInfo.GetClass`, `C_PlayerInfo.GetName`, `C_PlayerInfo.GetRace`, `C_PlayerInfo.GetSex`, `C_PlayerInfo.GUIDIsCreature`, `C_PlayerInfo.GUIDIsGameObject`, `C_PlayerInfo.GUIDIsPet`, `C_PlayerInfo.GUIDIsPlayer`, `C_PlayerInfo.IsConnected` |
|
||||
| [Quest](docs/API.md#quest) | `C_QuestLog.GetNumQuestObjectives`, `C_QuestLog.GetQuestDetails`, `C_QuestLog.GetQuestIDForLogIndex`, `C_QuestLog.GetTitleForQuestID`, `C_QuestLog.IsOnQuest`, `C_QuestLog.IsQuestDataCachedByID`, `C_QuestLog.IsUnitOnQuest`, `C_QuestLog.RequestLoadQuestByID`, `GetQuestLogLeaderBoardID` |
|
||||
| [Quest](docs/API.md#quest) | `C_QuestLog.GetNumQuestObjectives`, `C_QuestLog.GetQuestDetails`, `C_QuestLog.GetHeaderIndexForQuest`, `C_QuestLog.GetLogIndexForQuestID`, `C_QuestLog.GetQuestIDForLogIndex`, `C_QuestLog.GetTitleForQuestID`, `C_QuestLog.IsOnQuest`, `C_QuestLog.IsQuestDataCachedByID`, `C_QuestLog.IsUnitOnQuest`, `C_QuestLog.RequestLoadQuestByID`, `GetQuestLogLeaderBoardID` |
|
||||
| [Screen](docs/API.md#screen) | `GetPhysicalScreenSize` |
|
||||
| [Spell](docs/API.md#spell) | `C_Spell.CancelSpellByID`, `C_Spell.CastAtCursor`, `C_Spell.CastAtUnit`, `C_Spell.CastingInfo`, `C_Spell.ChannelInfo`, `C_Spell.DoesSpellExist`, `C_Spell.GetSchoolString`, `C_Spell.GetSpellCooldown`, `C_Spell.GetSpellDescription`, `C_Spell.GetSpellInfo`, `C_Spell.GetSpellLevelInfo`, `C_Spell.GetSpellLink`, `C_Spell.GetSpellMechanicByID`, `C_Spell.GetSpellName`, `C_Spell.GetSpellPowerCost`, `C_Spell.GetSpellRadius`, `C_Spell.GetSpellReagents`, `C_Spell.GetSpellRequiredTargetLevel`, `C_Spell.GetSpellSubtext`, `C_Spell.GetSpellTexture`, `C_Spell.IsAutoAttackSpell`, `C_Spell.IsCurrentSpell`, `C_Spell.IsRangedAutoAttackSpell`, `C_Spell.IsSelfBuff`, `C_Spell.IsSpellHarmful`, `C_Spell.IsSpellHelpful`, `C_Spell.IsSpellInRange`, `C_Spell.IsSpellPassive`, `C_Spell.IsSpellUsable`, `C_Spell.SpellHasRange`, `C_Spell.UnitCastingInfo`, `C_Spell.UnitChannelInfo`, `CancelSpellByName`, `CastSpellNoToggle`, `GetCraftSpellID`, `GetSpellBonusDamage`, `GetSpellBonusHealing`, `GetSpellInfo`, `GetSpellLink`, `GetSpellRadius`, `GetSpellRequiredTargetLevel`, `GetSpellSchool`, `IsHarmfulSpell`, `IsHelpfulSpell`, `IsPassiveSpell`, `IsPlayerSpell`, `IsSpellKnown`, `IsUsableSpell`, `SpellHasRange` |
|
||||
| [SpellBook](docs/API.md#spellbook) | `C_SpellBook.GetCurrentLevelSpells`, `C_SpellBook.GetSkillLineName`, `C_SpellBook.GetSkillLineRank`, `C_SpellBook.GetSpellLevelLearned`, `C_SpellBook.GetSpellSkillLine`, `C_SpellBook.IsAutoAttackSpellBookItem`, `C_SpellBook.IsRangedAutoAttackSpellBookItem`, `FindSpellBookSlotByID` |
|
||||
|
||||
+78
@@ -214,6 +214,7 @@ build instructions.
|
||||
- [`C_Item.GetItemFamily(item)`](#c_itemgetitemfamilyitem)
|
||||
- [`C_Item.GetItemGUID(itemLocation)`](#c_itemgetitemguiditemlocation)
|
||||
- [`C_Item.GetItemID(itemLocation)`](#c_itemgetitemiditemlocation)
|
||||
- [`C_Item.GetItemInfo(itemInfo)`](#c_itemgetiteminfoiteminfo)
|
||||
- [`C_Item.GetItemInfoInstant(item)`](#c_itemgetiteminfoinstantitem)
|
||||
- [`C_Item.GetItemInventorySlotInfo(inventorySlot)`](#c_itemgetiteminventoryslotinfoinventoryslot)
|
||||
- [`C_Item.GetItemInventorySlotKey(inventorySlot)`](#c_itemgetiteminventoryslotkeyinventoryslot)
|
||||
@@ -348,6 +349,8 @@ build instructions.
|
||||
- [`C_PlayerInfo.GetName / GetClass / GetRace / GetSex / IsConnected(playerLocation)`](#c_playerinfogetname--getclass--getrace--getsex--isconnectedplayerlocation)
|
||||
- [Quest](#quest)
|
||||
- [`C_QuestLog.GetQuestIDForLogIndex(index)`](#c_questlogGetQuestIDForLogIndexindex)
|
||||
- [`C_QuestLog.GetLogIndexForQuestID(questID)`](#c_questloggetlogindexforquestidquestid)
|
||||
- [`C_QuestLog.GetHeaderIndexForQuest(questID)`](#c_questloggetheaderindexforquestquestid)
|
||||
- [`C_QuestLog.RequestLoadQuestByID(questID)`](#c_questlogrequestloadquestbyidquestid)
|
||||
- [`C_QuestLog.IsOnQuest(questID)`](#c_questlogisonquestquestid)
|
||||
- [`C_QuestLog.IsUnitOnQuest(unit, questID)`](#c_questlogisunitonquestunit-questid)
|
||||
@@ -4984,6 +4987,50 @@ if id then
|
||||
end
|
||||
```
|
||||
|
||||
### `C_Item.GetItemInfo(itemInfo)`
|
||||
|
||||
The full modern `GetItemInfo` tuple, sourced from the client-side item cache
|
||||
plus the class/subclass/inventory-type DBC name lookups. The namespaced
|
||||
counterpart to the stock global `GetItemInfo` (which returns only vanilla's
|
||||
short tuple); this returns the wide modern set backported addons expect.
|
||||
|
||||
Accepts a numeric `itemID`, an `"item:NNN"` string, or a full chat link.
|
||||
|
||||
Returns 18 values:
|
||||
|
||||
| # | Return | Notes |
|
||||
|---|--------|-------|
|
||||
| 1 | `itemName` | |
|
||||
| 2 | `itemLink` | basic `item:ID` hyperlink (colored, bracketed name) |
|
||||
| 3 | `itemQuality` | 0–5 |
|
||||
| 4 | `itemLevel` | |
|
||||
| 5 | `itemMinLevel` | required level |
|
||||
| 6 | `itemType` | localized class name (e.g. "Weapon") |
|
||||
| 7 | `itemSubType` | localized subclass name (e.g. "Sword") |
|
||||
| 8 | `itemStackCount` | max stack size |
|
||||
| 9 | `itemEquipLoc` | `INVTYPE_*` token, `""` for non-equippable |
|
||||
| 10 | `itemTexture` | icon **path** (1.12 has no fileID system) |
|
||||
| 11 | `sellPrice` | vendor sell price in copper |
|
||||
| 12 | `classID` | |
|
||||
| 13 | `subclassID` | |
|
||||
| 14 | `bindType` | 0 none / 1 BoP / 2 BoE / 3 BoU / 4 quest |
|
||||
| 15 | `expansionID` | always `0` (classic) |
|
||||
| 16 | `setID` | item-set ID, `nil` if none |
|
||||
| 17 | `isCraftingReagent` | always `false` (no such flag in 1.12 data) |
|
||||
| 18 | `itemDescription` | item flavor/description text (`""` if none) |
|
||||
|
||||
**Asynchronous on a cache miss:** if the item isn't cached yet it returns
|
||||
nothing (nil) and warms the cache; the value lands on a retry after
|
||||
`GET_ITEM_INFO_RECEIVED` fires — same contract as the stock `GetItemInfo`
|
||||
and modern clients. Fields 4–18 are the ones the vanilla global never
|
||||
returned.
|
||||
|
||||
```lua
|
||||
local name, link, quality, ilvl, minLevel, itype, isub, stack, equipLoc,
|
||||
tex, sell, classID, subID, bind, expac, setID, reagent, desc
|
||||
= C_Item.GetItemInfo(6948) -- Hearthstone
|
||||
```
|
||||
|
||||
### `C_Item.GetItemInfoInstant(item)`
|
||||
|
||||
Modern-style accessor for the always-available subset of item info — the
|
||||
@@ -8368,6 +8415,37 @@ for i = 1, GetNumQuestLogEntries() do
|
||||
end
|
||||
```
|
||||
|
||||
### `C_QuestLog.GetLogIndexForQuestID(questID)`
|
||||
|
||||
The inverse of `GetQuestIDForLogIndex`: returns the 1-based quest log index
|
||||
of `questID`, or `nil` if the quest isn't in the log. The index spans the
|
||||
full entry list (headers included), so it can be passed straight to
|
||||
`GetQuestLogTitle(index)` / `GetQuestLogLeaderBoardID(...)` and the other
|
||||
index-based quest-log accessors.
|
||||
|
||||
```lua
|
||||
local index = C_QuestLog.GetLogIndexForQuestID(questID)
|
||||
if index then
|
||||
local title = GetQuestLogTitle(index)
|
||||
-- ...
|
||||
end
|
||||
```
|
||||
|
||||
### `C_QuestLog.GetHeaderIndexForQuest(questID)`
|
||||
|
||||
Returns the 1-based log index of the collapsible **header** (zone /
|
||||
`"Dungeon"` / class-sort category, …) that `questID` sits under, or `nil` if
|
||||
the quest isn't in the log. The quest log is laid out header-then-its-quests,
|
||||
so this locates the quest and walks back to the nearest preceding header row.
|
||||
Pass the result to `GetQuestLogTitle(headerIndex)` for the category name.
|
||||
|
||||
```lua
|
||||
local headerIndex = C_QuestLog.GetHeaderIndexForQuest(questID)
|
||||
if headerIndex then
|
||||
local zone = GetQuestLogTitle(headerIndex) -- e.g. "Westfall"
|
||||
end
|
||||
```
|
||||
|
||||
### `C_QuestLog.RequestLoadQuestByID(questID)`
|
||||
|
||||
Asks the engine to fetch the static data for `questID` (title, description,
|
||||
|
||||
+11
-1
@@ -28,7 +28,17 @@ namespace {
|
||||
// any engine code runs. `RetryClaims` (triggered by the
|
||||
// `Frame::RegisterEvent` hook) walks this list and claims a NULL slot
|
||||
// for each unclaimed entry.
|
||||
constexpr int MAX_RESERVED = 32;
|
||||
//
|
||||
// MUST stay comfortably above the total number of `AutoReserve` instances
|
||||
// across the codebase (file-scope + the lazily-registered TTS ones) — the
|
||||
// constructor SILENTLY drops any reservation past the cap, and which ones
|
||||
// overflow depends on unspecified cross-TU static-init order, so exceeding
|
||||
// it makes an arbitrary subset of custom events quietly stop firing (their
|
||||
// `Lookup` returns -1 and `Fire` no-ops). We hit exactly that at 32: ~40
|
||||
// reservations meant a shifting handful (e.g. ITEM_DATA_LOAD_RESULT /
|
||||
// GET_ITEM_INFO_RECEIVED) never claimed a slot. Grep `AutoReserve` before
|
||||
// bumping the reservation count near this ceiling.
|
||||
constexpr int MAX_RESERVED = 64;
|
||||
struct ReservedName {
|
||||
const char *name;
|
||||
int slot; // -1 until claimed
|
||||
|
||||
+3
-2
@@ -37,8 +37,9 @@ namespace Event::Custom {
|
||||
// DLL operate on the table independently of the others.
|
||||
//
|
||||
// The name pointer must outlive the engine (a string literal does).
|
||||
// Same name reserved twice is deduped; reserving more than 32 names
|
||||
// total silently drops the overflow.
|
||||
// Same name reserved twice is deduped; reserving more than `MAX_RESERVED`
|
||||
// names total (see Custom.cpp) silently drops the overflow — keep that cap
|
||||
// above the codebase-wide `AutoReserve` count.
|
||||
struct AutoReserve {
|
||||
explicit AutoReserve(const char *name);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "item/Arg.h"
|
||||
#include "item/Data.h"
|
||||
#include "item/ID.h"
|
||||
#include "item/Link.h"
|
||||
#include "item/Location.h"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -245,7 +246,89 @@ static int __fastcall Script_C_Item_GetItemFamily(void *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// `C_Item.GetItemInfo(itemInfo)` — the full modern info tuple, sourced
|
||||
// entirely from the cached `ItemStats_C` record (+ the class/subclass/invtype
|
||||
// DBC name lookups this file already does for `GetItemInfoInstant`). Accepts
|
||||
// a numeric itemID, an `"item:NNN"` string, or a full chat link — same parser
|
||||
// as `GetItemInfoInstant`.
|
||||
//
|
||||
// Returns (18 values):
|
||||
// itemName, itemLink, itemQuality, itemLevel, itemMinLevel, itemType,
|
||||
// itemSubType, itemStackCount, itemEquipLoc, itemTexture, sellPrice,
|
||||
// classID, subclassID, bindType, expansionID, setID, isCraftingReagent,
|
||||
// itemDescription
|
||||
//
|
||||
// `expansionID` is always 0 (vanilla) and `isCraftingReagent` always false
|
||||
// (no such flag in 1.12's item data); `setID` is nil when the item has no
|
||||
// set. On a cache miss it warms the cache and returns nothing (nil) —
|
||||
// the value lands on a retry after `GET_ITEM_INFO_RECEIVED`,
|
||||
// matching the modern async contract.
|
||||
static int __fastcall Script_C_Item_GetItemInfo(void *L) {
|
||||
const int itemID = Item::Arg::ResolveItemID(L, 1);
|
||||
if (itemID <= 0)
|
||||
return 0;
|
||||
|
||||
const uint8_t *record = FetchItemRecord(static_cast<uint32_t>(itemID));
|
||||
if (record == nullptr) {
|
||||
Item::Data::WarmCache(static_cast<uint32_t>(itemID));
|
||||
return 0; // nil this call; GET_ITEM_INFO_RECEIVED fires when ready
|
||||
}
|
||||
|
||||
auto u32 = [record](int off) {
|
||||
return *reinterpret_cast<const uint32_t *>(record + off);
|
||||
};
|
||||
const uint32_t quality = u32(Offsets::OFF_ITEMSTATS_QUALITY);
|
||||
const uint32_t itemLevel = u32(Offsets::OFF_ITEMSTATS_ITEM_LEVEL);
|
||||
const uint32_t reqLevel = u32(Offsets::OFF_ITEMSTATS_REQUIRED_LEVEL);
|
||||
const uint32_t classID = u32(Offsets::OFF_ITEMSTATS_CLASS);
|
||||
const uint32_t subClassID = u32(Offsets::OFF_ITEMSTATS_SUBCLASS);
|
||||
const uint32_t stackCount = u32(Offsets::OFF_ITEMSTATS_STACKABLE);
|
||||
const uint32_t invType = u32(Offsets::OFF_ITEMSTATS_INVENTORY_TYPE);
|
||||
const uint32_t displayInfoID = u32(Offsets::OFF_ITEMSTATS_DISPLAY_INFO_ID);
|
||||
const uint32_t sellPrice = u32(Offsets::OFF_ITEMSTATS_SELL_PRICE);
|
||||
const uint32_t bindType = u32(Offsets::OFF_ITEMSTATS_BONDING);
|
||||
const uint32_t setID = u32(Offsets::OFF_ITEMSTATS_ITEM_SET);
|
||||
const char *name = *reinterpret_cast<const char *const *>(
|
||||
record + Offsets::OFF_ITEMSTATS_NAME);
|
||||
const char *description = *reinterpret_cast<const char *const *>(
|
||||
record + Offsets::OFF_ITEMSTATS_DESCRIPTION);
|
||||
|
||||
char iconPath[260];
|
||||
if (!BuildIconPath(displayInfoID, iconPath, sizeof(iconPath)))
|
||||
iconPath[0] = 0;
|
||||
char link[256];
|
||||
const bool haveLink = Item::Link::BasicFromItemID(static_cast<uint32_t>(itemID),
|
||||
link, sizeof(link));
|
||||
|
||||
Game::Lua::PushString(L, PushedOrEmpty(name)); // 1 itemName
|
||||
if (haveLink)
|
||||
Game::Lua::PushString(L, link); // 2 itemLink
|
||||
else
|
||||
Game::Lua::PushNil(L);
|
||||
Game::Lua::PushNumber(L, static_cast<double>(quality)); // 3 itemQuality
|
||||
Game::Lua::PushNumber(L, static_cast<double>(itemLevel)); // 4 itemLevel
|
||||
Game::Lua::PushNumber(L, static_cast<double>(reqLevel)); // 5 itemMinLevel
|
||||
Game::Lua::PushString(L, LookupItemClassName(classID)); // 6 itemType
|
||||
Game::Lua::PushString(L, LookupItemSubClassName(classID, subClassID)); // 7 itemSubType
|
||||
Game::Lua::PushNumber(L, static_cast<double>(stackCount)); // 8 itemStackCount
|
||||
Game::Lua::PushString(L, LookupInvType(invType)); // 9 itemEquipLoc
|
||||
Game::Lua::PushString(L, iconPath); // 10 itemTexture
|
||||
Game::Lua::PushNumber(L, static_cast<double>(sellPrice)); // 11 sellPrice
|
||||
Game::Lua::PushNumber(L, static_cast<double>(classID)); // 12 classID
|
||||
Game::Lua::PushNumber(L, static_cast<double>(subClassID)); // 13 subclassID
|
||||
Game::Lua::PushNumber(L, static_cast<double>(bindType)); // 14 bindType
|
||||
Game::Lua::PushNumber(L, 0.0); // 15 expansionID (classic)
|
||||
if (setID != 0)
|
||||
Game::Lua::PushNumber(L, static_cast<double>(setID)); // 16 setID
|
||||
else
|
||||
Game::Lua::PushNil(L);
|
||||
Game::Lua::PushBool(L, false); // 17 isCraftingReagent
|
||||
Game::Lua::PushString(L, PushedOrEmpty(description)); // 18 itemDescription
|
||||
return 18;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemInfo", &Script_C_Item_GetItemInfo);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemInfoInstant", &Script_GetItemInfoInstant);
|
||||
Game::Lua::RegisterGlobalFunction("GetItemIcon", &Script_GetItemIcon);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemIcon", &Script_C_Item_GetItemIcon);
|
||||
|
||||
@@ -76,6 +76,87 @@ static int __fastcall Script_GetQuestIDForLogIndex(void *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// `C_QuestLog.GetLogIndexForQuestID(questID)` — the inverse of
|
||||
// `GetQuestIDForLogIndex`: the 1-based quest-log index of `questID`, or nil
|
||||
// if the quest isn't in the log. The index spans the full entry array
|
||||
// (headers included), matching `GetQuestIDForLogIndex` /
|
||||
// `GetQuestLogTitle`. Walks `VAR_QUEST_LOG_ENTRIES`, skipping header rows via
|
||||
// the `+8` header-pointer gate, and returns the first matching entry's index.
|
||||
static int __fastcall Script_GetLogIndexForQuestID(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::Error(L, "Usage: GetLogIndexForQuestID(questID)");
|
||||
return 0;
|
||||
}
|
||||
const int target = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
if (target <= 0)
|
||||
return 0; // nil — no such quest
|
||||
|
||||
const int total = *reinterpret_cast<const int *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_QUEST_LOG_ENTRY_COUNT));
|
||||
auto *base = reinterpret_cast<const uint8_t *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_QUEST_LOG_ENTRIES));
|
||||
for (int i = 0; i < total; ++i) {
|
||||
auto *entry = base + i * Offsets::OFF_QUEST_LOG_ENTRY_STRIDE;
|
||||
if (*reinterpret_cast<const void *const *>(
|
||||
entry + Offsets::OFF_QUEST_LOG_ENTRY_HEADER_PTR) != nullptr)
|
||||
continue; // header row
|
||||
const int questID = *reinterpret_cast<const int *>(
|
||||
entry + Offsets::OFF_QUEST_LOG_ENTRY_QUEST_ID);
|
||||
if (questID == target) {
|
||||
Game::Lua::PushNumber(L, static_cast<double>(i + 1)); // 1-based
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; // nil — quest not in the log
|
||||
}
|
||||
|
||||
// `C_QuestLog.GetHeaderIndexForQuest(questID)` — the 1-based log index of the
|
||||
// collapsible category header (zone / "Dungeon" / class sort, …) the quest
|
||||
// sits under, or nil if the quest isn't in the log or has no header above it.
|
||||
// The log is laid out header-then-its-quests, so we locate the quest's entry
|
||||
// and walk backwards to the nearest preceding header row (`+8` non-NULL).
|
||||
static int __fastcall Script_GetHeaderIndexForQuest(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::Error(L, "Usage: GetHeaderIndexForQuest(questID)");
|
||||
return 0;
|
||||
}
|
||||
const int target = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
if (target <= 0)
|
||||
return 0; // nil — no such quest
|
||||
|
||||
const int total = *reinterpret_cast<const int *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_QUEST_LOG_ENTRY_COUNT));
|
||||
auto *base = reinterpret_cast<const uint8_t *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_QUEST_LOG_ENTRIES));
|
||||
|
||||
// Locate the quest's entry.
|
||||
int questIndex = -1;
|
||||
for (int i = 0; i < total; ++i) {
|
||||
auto *entry = base + i * Offsets::OFF_QUEST_LOG_ENTRY_STRIDE;
|
||||
if (*reinterpret_cast<const void *const *>(
|
||||
entry + Offsets::OFF_QUEST_LOG_ENTRY_HEADER_PTR) != nullptr)
|
||||
continue; // header row
|
||||
if (*reinterpret_cast<const int *>(
|
||||
entry + Offsets::OFF_QUEST_LOG_ENTRY_QUEST_ID) == target) {
|
||||
questIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (questIndex < 0)
|
||||
return 0; // quest not in the log
|
||||
|
||||
// Walk back to the nearest preceding header.
|
||||
for (int i = questIndex - 1; i >= 0; --i) {
|
||||
auto *entry = base + i * Offsets::OFF_QUEST_LOG_ENTRY_STRIDE;
|
||||
if (*reinterpret_cast<const void *const *>(
|
||||
entry + Offsets::OFF_QUEST_LOG_ENTRY_HEADER_PTR) != nullptr) {
|
||||
Game::Lua::PushNumber(L, static_cast<double>(i + 1)); // 1-based
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; // nil — quest sits above any header (shouldn't normally happen)
|
||||
}
|
||||
|
||||
// `C_QuestLog.IsOnQuest(questID)` — true iff `questID` is currently
|
||||
// in the player's quest log (incomplete OR ready-to-turn-in; the log
|
||||
// holds both). Walks `VAR_QUEST_LOG_ENTRIES` and matches against each
|
||||
@@ -148,6 +229,10 @@ static int __fastcall Script_IsUnitOnQuest(void *L) {
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_QuestLog", "GetQuestIDForLogIndex",
|
||||
&Script_GetQuestIDForLogIndex);
|
||||
Game::Lua::RegisterTableFunction("C_QuestLog", "GetLogIndexForQuestID",
|
||||
&Script_GetLogIndexForQuestID);
|
||||
Game::Lua::RegisterTableFunction("C_QuestLog", "GetHeaderIndexForQuest",
|
||||
&Script_GetHeaderIndexForQuest);
|
||||
Game::Lua::RegisterTableFunction("C_QuestLog", "IsOnQuest",
|
||||
&Script_IsOnQuest);
|
||||
Game::Lua::RegisterTableFunction("C_QuestLog", "IsUnitOnQuest",
|
||||
|
||||
Reference in New Issue
Block a user