mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-06 13:13:51 +00:00
chore(lua): refactor addon Lua engine API + progress docs
- Refactor Lua addon integration: - Update CMakeLists.txt for addon build paths - Enhance addons API headers and Lua engine interface - Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`) - Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
This commit is contained in:
parent
6e02b4451c
commit
a916270a13
21 changed files with 6183 additions and 6700 deletions
892
src/addons/lua_inventory_api.cpp
Normal file
892
src/addons/lua_inventory_api.cpp
Normal file
|
|
@ -0,0 +1,892 @@
|
|||
// lua_inventory_api.cpp — Items, containers, merchant, loot, equipment, trading, auction, and mail Lua API bindings.
|
||||
// Extracted from lua_engine.cpp as part of §5.1 (Tame LuaEngine).
|
||||
#include "addons/lua_api_helpers.hpp"
|
||||
|
||||
namespace wowee::addons {
|
||||
|
||||
static int lua_GetMoney(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
lua_pushnumber(L, gh ? static_cast<double>(gh->getMoneyCopper()) : 0.0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// --- Merchant/Vendor API ---
|
||||
|
||||
static int lua_GetMerchantNumItems(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { return luaReturnZero(L); }
|
||||
lua_pushnumber(L, gh->getVendorItems().items.size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GetMerchantItemInfo(index) → name, texture, price, stackCount, numAvailable, isUsable
|
||||
static int lua_GetMerchantItemInfo(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || index < 1) { return luaReturnNil(L); }
|
||||
const auto& items = gh->getVendorItems().items;
|
||||
if (index > static_cast<int>(items.size())) { return luaReturnNil(L); }
|
||||
const auto& vi = items[index - 1];
|
||||
const auto* info = gh->getItemInfo(vi.itemId);
|
||||
std::string name = info ? info->name : ("Item #" + std::to_string(vi.itemId));
|
||||
lua_pushstring(L, name.c_str()); // name
|
||||
// texture
|
||||
std::string iconPath;
|
||||
if (info && info->displayInfoId != 0)
|
||||
iconPath = gh->getItemIconPath(info->displayInfoId);
|
||||
if (!iconPath.empty()) lua_pushstring(L, iconPath.c_str());
|
||||
else lua_pushnil(L);
|
||||
lua_pushnumber(L, vi.buyPrice); // price (copper)
|
||||
lua_pushnumber(L, vi.stackCount > 0 ? vi.stackCount : 1); // stackCount
|
||||
lua_pushnumber(L, vi.maxCount == -1 ? -1 : vi.maxCount); // numAvailable (-1=unlimited)
|
||||
lua_pushboolean(L, 1); // isUsable
|
||||
return 6;
|
||||
}
|
||||
|
||||
// GetMerchantItemLink(index) → item link
|
||||
static int lua_GetMerchantItemLink(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || index < 1) { return luaReturnNil(L); }
|
||||
const auto& items = gh->getVendorItems().items;
|
||||
if (index > static_cast<int>(items.size())) { return luaReturnNil(L); }
|
||||
const auto& vi = items[index - 1];
|
||||
const auto* info = gh->getItemInfo(vi.itemId);
|
||||
if (!info) { return luaReturnNil(L); }
|
||||
|
||||
const char* ch = (info->quality < 8) ? kQualHexAlpha[info->quality] : "ffffffff";
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|c%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r", ch, vi.itemId, info->name.c_str());
|
||||
lua_pushstring(L, link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_CanMerchantRepair(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
lua_pushboolean(L, gh && gh->getVendorItems().canRepair ? 1 : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// UnitStat(unit, statIndex) → base, effective, posBuff, negBuff
|
||||
|
||||
static int lua_GetItemInfo(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { return luaReturnNil(L); }
|
||||
|
||||
uint32_t itemId = 0;
|
||||
if (lua_isnumber(L, 1)) {
|
||||
itemId = static_cast<uint32_t>(lua_tonumber(L, 1));
|
||||
} else if (lua_isstring(L, 1)) {
|
||||
// Try to parse "item:12345" link format
|
||||
const char* s = lua_tostring(L, 1);
|
||||
std::string str(s ? s : "");
|
||||
auto pos = str.find("item:");
|
||||
if (pos != std::string::npos) {
|
||||
try { itemId = static_cast<uint32_t>(std::stoul(str.substr(pos + 5))); } catch (...) {}
|
||||
}
|
||||
}
|
||||
if (itemId == 0) { return luaReturnNil(L); }
|
||||
|
||||
const auto* info = gh->getItemInfo(itemId);
|
||||
if (!info) { return luaReturnNil(L); }
|
||||
|
||||
lua_pushstring(L, info->name.c_str()); // 1: name
|
||||
// Build item link with quality-colored text
|
||||
const char* colorHex = (info->quality < 8) ? kQualHexAlpha[info->quality] : "ffffffff";
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|c%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r",
|
||||
colorHex, itemId, info->name.c_str());
|
||||
lua_pushstring(L, link); // 2: link
|
||||
lua_pushnumber(L, info->quality); // 3: quality
|
||||
lua_pushnumber(L, info->itemLevel); // 4: iLevel
|
||||
lua_pushnumber(L, info->requiredLevel); // 5: requiredLevel
|
||||
// 6: class (type string) — map itemClass to display name
|
||||
{
|
||||
static constexpr const char* kItemClasses[] = {
|
||||
"Consumable", "Bag", "Weapon", "Gem", "Armor", "Reagent", "Projectile",
|
||||
"Trade Goods", "Generic", "Recipe", "Money", "Quiver", "Quest", "Key",
|
||||
"Permanent", "Miscellaneous", "Glyph"
|
||||
};
|
||||
if (info->itemClass < 17)
|
||||
lua_pushstring(L, kItemClasses[info->itemClass]);
|
||||
else
|
||||
lua_pushstring(L, "Miscellaneous");
|
||||
}
|
||||
// 7: subclass — use subclassName from ItemDef if available, else generic
|
||||
lua_pushstring(L, info->subclassName.empty() ? "" : info->subclassName.c_str());
|
||||
lua_pushnumber(L, info->maxStack > 0 ? info->maxStack : 1); // 8: maxStack
|
||||
// 9: equipSlot — WoW inventoryType to INVTYPE string
|
||||
{
|
||||
static constexpr const char* kInvTypes[] = {
|
||||
"", "INVTYPE_HEAD", "INVTYPE_NECK", "INVTYPE_SHOULDER",
|
||||
"INVTYPE_BODY", "INVTYPE_CHEST", "INVTYPE_WAIST", "INVTYPE_LEGS",
|
||||
"INVTYPE_FEET", "INVTYPE_WRIST", "INVTYPE_HAND", "INVTYPE_FINGER",
|
||||
"INVTYPE_TRINKET", "INVTYPE_WEAPON", "INVTYPE_SHIELD",
|
||||
"INVTYPE_RANGED", "INVTYPE_CLOAK", "INVTYPE_2HWEAPON",
|
||||
"INVTYPE_BAG", "INVTYPE_TABARD", "INVTYPE_ROBE",
|
||||
"INVTYPE_WEAPONMAINHAND", "INVTYPE_WEAPONOFFHAND", "INVTYPE_HOLDABLE",
|
||||
"INVTYPE_AMMO", "INVTYPE_THROWN", "INVTYPE_RANGEDRIGHT",
|
||||
"INVTYPE_QUIVER", "INVTYPE_RELIC"
|
||||
};
|
||||
uint32_t invType = info->inventoryType;
|
||||
lua_pushstring(L, invType < 29 ? kInvTypes[invType] : "");
|
||||
}
|
||||
// 10: texture (icon path from ItemDisplayInfo.dbc)
|
||||
if (info->displayInfoId != 0) {
|
||||
std::string iconPath = gh->getItemIconPath(info->displayInfoId);
|
||||
if (!iconPath.empty()) lua_pushstring(L, iconPath.c_str());
|
||||
else lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
lua_pushnumber(L, info->sellPrice); // 11: vendorPrice
|
||||
return 11;
|
||||
}
|
||||
|
||||
// GetItemQualityColor(quality) → r, g, b, hex
|
||||
// Quality: 0=Poor(gray), 1=Common(white), 2=Uncommon(green), 3=Rare(blue),
|
||||
// 4=Epic(purple), 5=Legendary(orange), 6=Artifact(gold), 7=Heirloom(gold)
|
||||
static int lua_GetItemQualityColor(lua_State* L) {
|
||||
int q = static_cast<int>(luaL_checknumber(L, 1));
|
||||
struct QC { float r, g, b; const char* hex; };
|
||||
static const QC colors[] = {
|
||||
{0.62f, 0.62f, 0.62f, "ff9d9d9d"}, // 0 Poor
|
||||
{1.00f, 1.00f, 1.00f, "ffffffff"}, // 1 Common
|
||||
{0.12f, 1.00f, 0.00f, "ff1eff00"}, // 2 Uncommon
|
||||
{0.00f, 0.44f, 0.87f, "ff0070dd"}, // 3 Rare
|
||||
{0.64f, 0.21f, 0.93f, "ffa335ee"}, // 4 Epic
|
||||
{1.00f, 0.50f, 0.00f, "ffff8000"}, // 5 Legendary
|
||||
{0.90f, 0.80f, 0.50f, "ffe6cc80"}, // 6 Artifact
|
||||
{0.00f, 0.80f, 1.00f, "ff00ccff"}, // 7 Heirloom
|
||||
};
|
||||
if (q < 0 || q > 7) q = 1;
|
||||
lua_pushnumber(L, colors[q].r);
|
||||
lua_pushnumber(L, colors[q].g);
|
||||
lua_pushnumber(L, colors[q].b);
|
||||
lua_pushstring(L, colors[q].hex);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// GetItemCount(itemId [, includeBank]) → count
|
||||
static int lua_GetItemCount(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { return luaReturnZero(L); }
|
||||
uint32_t itemId = static_cast<uint32_t>(luaL_checknumber(L, 1));
|
||||
const auto& inv = gh->getInventory();
|
||||
uint32_t count = 0;
|
||||
// Backpack
|
||||
for (int i = 0; i < inv.getBackpackSize(); ++i) {
|
||||
const auto& s = inv.getBackpackSlot(i);
|
||||
if (!s.empty() && s.item.itemId == itemId)
|
||||
count += (s.item.stackCount > 0 ? s.item.stackCount : 1);
|
||||
}
|
||||
// Bags 1-4
|
||||
for (int b = 0; b < game::Inventory::NUM_BAG_SLOTS; ++b) {
|
||||
int sz = inv.getBagSize(b);
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
const auto& s = inv.getBagSlot(b, i);
|
||||
if (!s.empty() && s.item.itemId == itemId)
|
||||
count += (s.item.stackCount > 0 ? s.item.stackCount : 1);
|
||||
}
|
||||
}
|
||||
lua_pushnumber(L, count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// UseContainerItem(bag, slot) — use/equip an item from a bag
|
||||
static int lua_UseContainerItem(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) return 0;
|
||||
int bag = static_cast<int>(luaL_checknumber(L, 1));
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 2));
|
||||
const auto& inv = gh->getInventory();
|
||||
const game::ItemSlot* itemSlot = nullptr;
|
||||
if (bag == 0 && slot >= 1 && slot <= inv.getBackpackSize())
|
||||
itemSlot = &inv.getBackpackSlot(slot - 1);
|
||||
else if (bag >= 1 && bag <= 4) {
|
||||
int sz = inv.getBagSize(bag - 1);
|
||||
if (slot >= 1 && slot <= sz)
|
||||
itemSlot = &inv.getBagSlot(bag - 1, slot - 1);
|
||||
}
|
||||
if (itemSlot && !itemSlot->empty())
|
||||
gh->useItemById(itemSlot->item.itemId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _GetItemTooltipData(itemId) → table with armor, bind, stats, damage, description
|
||||
// Returns a Lua table with detailed item info for tooltip building
|
||||
static int lua_GetItemTooltipData(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
uint32_t itemId = static_cast<uint32_t>(luaL_checknumber(L, 1));
|
||||
if (!gh || itemId == 0) { return luaReturnNil(L); }
|
||||
const auto* info = gh->getItemInfo(itemId);
|
||||
if (!info) { return luaReturnNil(L); }
|
||||
|
||||
lua_newtable(L);
|
||||
// Unique / Heroic flags
|
||||
if (info->maxCount == 1) { lua_pushboolean(L, 1); lua_setfield(L, -2, "isUnique"); }
|
||||
if (info->itemFlags & 0x8) { lua_pushboolean(L, 1); lua_setfield(L, -2, "isHeroic"); }
|
||||
if (info->itemFlags & 0x1000000) { lua_pushboolean(L, 1); lua_setfield(L, -2, "isUniqueEquipped"); }
|
||||
// Bind type
|
||||
lua_pushnumber(L, info->bindType);
|
||||
lua_setfield(L, -2, "bindType");
|
||||
// Armor
|
||||
lua_pushnumber(L, info->armor);
|
||||
lua_setfield(L, -2, "armor");
|
||||
// Damage
|
||||
lua_pushnumber(L, info->damageMin);
|
||||
lua_setfield(L, -2, "damageMin");
|
||||
lua_pushnumber(L, info->damageMax);
|
||||
lua_setfield(L, -2, "damageMax");
|
||||
lua_pushnumber(L, info->delayMs);
|
||||
lua_setfield(L, -2, "speed");
|
||||
// Primary stats
|
||||
if (info->stamina != 0) { lua_pushnumber(L, info->stamina); lua_setfield(L, -2, "stamina"); }
|
||||
if (info->strength != 0) { lua_pushnumber(L, info->strength); lua_setfield(L, -2, "strength"); }
|
||||
if (info->agility != 0) { lua_pushnumber(L, info->agility); lua_setfield(L, -2, "agility"); }
|
||||
if (info->intellect != 0) { lua_pushnumber(L, info->intellect); lua_setfield(L, -2, "intellect"); }
|
||||
if (info->spirit != 0) { lua_pushnumber(L, info->spirit); lua_setfield(L, -2, "spirit"); }
|
||||
// Description
|
||||
if (!info->description.empty()) {
|
||||
lua_pushstring(L, info->description.c_str());
|
||||
lua_setfield(L, -2, "description");
|
||||
}
|
||||
// Required level
|
||||
lua_pushnumber(L, info->requiredLevel);
|
||||
lua_setfield(L, -2, "requiredLevel");
|
||||
// Extra stats (hit, crit, haste, AP, SP, etc.) as array of {type, value} pairs
|
||||
if (!info->extraStats.empty()) {
|
||||
lua_newtable(L);
|
||||
for (size_t i = 0; i < info->extraStats.size(); ++i) {
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, info->extraStats[i].statType);
|
||||
lua_setfield(L, -2, "type");
|
||||
lua_pushnumber(L, info->extraStats[i].statValue);
|
||||
lua_setfield(L, -2, "value");
|
||||
lua_rawseti(L, -2, static_cast<int>(i) + 1);
|
||||
}
|
||||
lua_setfield(L, -2, "extraStats");
|
||||
}
|
||||
// Resistances
|
||||
if (info->fireRes != 0) { lua_pushnumber(L, info->fireRes); lua_setfield(L, -2, "fireRes"); }
|
||||
if (info->natureRes != 0) { lua_pushnumber(L, info->natureRes); lua_setfield(L, -2, "natureRes"); }
|
||||
if (info->frostRes != 0) { lua_pushnumber(L, info->frostRes); lua_setfield(L, -2, "frostRes"); }
|
||||
if (info->shadowRes != 0) { lua_pushnumber(L, info->shadowRes); lua_setfield(L, -2, "shadowRes"); }
|
||||
if (info->arcaneRes != 0) { lua_pushnumber(L, info->arcaneRes); lua_setfield(L, -2, "arcaneRes"); }
|
||||
// Item spell effects (Use: / Equip: / Chance on Hit:)
|
||||
{
|
||||
lua_newtable(L);
|
||||
int spellCount = 0;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (info->spells[i].spellId == 0) continue;
|
||||
++spellCount;
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, info->spells[i].spellId);
|
||||
lua_setfield(L, -2, "spellId");
|
||||
lua_pushnumber(L, info->spells[i].spellTrigger);
|
||||
lua_setfield(L, -2, "trigger");
|
||||
// Get spell name for display
|
||||
const std::string& sName = gh->getSpellName(info->spells[i].spellId);
|
||||
if (!sName.empty()) { lua_pushstring(L, sName.c_str()); lua_setfield(L, -2, "name"); }
|
||||
// Get description
|
||||
const std::string& sDesc = gh->getSpellDescription(info->spells[i].spellId);
|
||||
if (!sDesc.empty()) { lua_pushstring(L, sDesc.c_str()); lua_setfield(L, -2, "description"); }
|
||||
lua_rawseti(L, -2, spellCount);
|
||||
}
|
||||
if (spellCount > 0) lua_setfield(L, -2, "itemSpells");
|
||||
else lua_pop(L, 1);
|
||||
}
|
||||
// Gem sockets (WotLK/TBC)
|
||||
int numSockets = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (info->socketColor[i] != 0) ++numSockets;
|
||||
}
|
||||
if (numSockets > 0) {
|
||||
lua_newtable(L);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (info->socketColor[i] != 0) {
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, info->socketColor[i]);
|
||||
lua_setfield(L, -2, "color");
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
}
|
||||
lua_setfield(L, -2, "sockets");
|
||||
}
|
||||
// Item set
|
||||
if (info->itemSetId != 0) {
|
||||
lua_pushnumber(L, info->itemSetId);
|
||||
lua_setfield(L, -2, "itemSetId");
|
||||
}
|
||||
// Quest-starting item
|
||||
if (info->startQuestId != 0) {
|
||||
lua_pushboolean(L, 1);
|
||||
lua_setfield(L, -2, "startsQuest");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// --- Locale/Build/Realm info ---
|
||||
|
||||
|
||||
static int lua_GetContainerNumSlots(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int container = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh) { return luaReturnZero(L); }
|
||||
const auto& inv = gh->getInventory();
|
||||
if (container == 0) {
|
||||
lua_pushnumber(L, inv.getBackpackSize());
|
||||
} else if (container >= 1 && container <= 4) {
|
||||
lua_pushnumber(L, inv.getBagSize(container - 1));
|
||||
} else {
|
||||
lua_pushnumber(L, 0);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GetContainerItemInfo(container, slot) → texture, count, locked, quality, readable, lootable, link
|
||||
static int lua_GetContainerItemInfo(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int container = static_cast<int>(luaL_checknumber(L, 1));
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh) { return luaReturnNil(L); }
|
||||
|
||||
const auto& inv = gh->getInventory();
|
||||
const game::ItemSlot* itemSlot = nullptr;
|
||||
|
||||
if (container == 0 && slot >= 1 && slot <= inv.getBackpackSize()) {
|
||||
itemSlot = &inv.getBackpackSlot(slot - 1); // WoW uses 1-based
|
||||
} else if (container >= 1 && container <= 4) {
|
||||
int bagIdx = container - 1;
|
||||
int bagSize = inv.getBagSize(bagIdx);
|
||||
if (slot >= 1 && slot <= bagSize)
|
||||
itemSlot = &inv.getBagSlot(bagIdx, slot - 1);
|
||||
}
|
||||
|
||||
if (!itemSlot || itemSlot->empty()) { return luaReturnNil(L); }
|
||||
|
||||
// Get item info for quality/icon
|
||||
const auto* info = gh->getItemInfo(itemSlot->item.itemId);
|
||||
|
||||
lua_pushnil(L); // texture (icon path — would need ItemDisplayInfo icon resolver)
|
||||
lua_pushnumber(L, itemSlot->item.stackCount); // count
|
||||
lua_pushboolean(L, 0); // locked
|
||||
lua_pushnumber(L, info ? info->quality : 0); // quality
|
||||
lua_pushboolean(L, 0); // readable
|
||||
lua_pushboolean(L, 0); // lootable
|
||||
// Build item link with quality color
|
||||
std::string name = info ? info->name : ("Item #" + std::to_string(itemSlot->item.itemId));
|
||||
uint32_t q = info ? info->quality : 0;
|
||||
|
||||
uint32_t qi = q < 8 ? q : 1u;
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r",
|
||||
kQualHexNoAlpha[qi], itemSlot->item.itemId, name.c_str());
|
||||
lua_pushstring(L, link); // link
|
||||
return 7;
|
||||
}
|
||||
|
||||
// GetContainerItemLink(container, slot) → item link string
|
||||
static int lua_GetContainerItemLink(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int container = static_cast<int>(luaL_checknumber(L, 1));
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh) { return luaReturnNil(L); }
|
||||
|
||||
const auto& inv = gh->getInventory();
|
||||
const game::ItemSlot* itemSlot = nullptr;
|
||||
|
||||
if (container == 0 && slot >= 1 && slot <= inv.getBackpackSize()) {
|
||||
itemSlot = &inv.getBackpackSlot(slot - 1);
|
||||
} else if (container >= 1 && container <= 4) {
|
||||
int bagIdx = container - 1;
|
||||
int bagSize = inv.getBagSize(bagIdx);
|
||||
if (slot >= 1 && slot <= bagSize)
|
||||
itemSlot = &inv.getBagSlot(bagIdx, slot - 1);
|
||||
}
|
||||
|
||||
if (!itemSlot || itemSlot->empty()) { return luaReturnNil(L); }
|
||||
const auto* info = gh->getItemInfo(itemSlot->item.itemId);
|
||||
std::string name = info ? info->name : ("Item #" + std::to_string(itemSlot->item.itemId));
|
||||
uint32_t q = info ? info->quality : 0;
|
||||
char link[256];
|
||||
|
||||
uint32_t qi = q < 8 ? q : 1u;
|
||||
snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r",
|
||||
kQualHexNoAlpha[qi], itemSlot->item.itemId, name.c_str());
|
||||
lua_pushstring(L, link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GetContainerNumFreeSlots(container) → numFreeSlots, bagType
|
||||
static int lua_GetContainerNumFreeSlots(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int container = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh) { lua_pushnumber(L, 0); lua_pushnumber(L, 0); return 2; }
|
||||
|
||||
const auto& inv = gh->getInventory();
|
||||
int freeSlots = 0;
|
||||
int totalSlots = 0;
|
||||
|
||||
if (container == 0) {
|
||||
totalSlots = inv.getBackpackSize();
|
||||
for (int i = 0; i < totalSlots; ++i)
|
||||
if (inv.getBackpackSlot(i).empty()) ++freeSlots;
|
||||
} else if (container >= 1 && container <= 4) {
|
||||
totalSlots = inv.getBagSize(container - 1);
|
||||
for (int i = 0; i < totalSlots; ++i)
|
||||
if (inv.getBagSlot(container - 1, i).empty()) ++freeSlots;
|
||||
}
|
||||
|
||||
lua_pushnumber(L, freeSlots);
|
||||
lua_pushnumber(L, 0); // bagType (0 = normal)
|
||||
return 2;
|
||||
}
|
||||
|
||||
// --- Equipment Slot API ---
|
||||
// WoW inventory slot IDs: 1=Head,2=Neck,3=Shoulders,4=Shirt,5=Chest,
|
||||
// 6=Waist,7=Legs,8=Feet,9=Wrists,10=Hands,11=Ring1,12=Ring2,
|
||||
// 13=Trinket1,14=Trinket2,15=Back,16=MainHand,17=OffHand,18=Ranged,19=Tabard
|
||||
|
||||
// GetInventorySlotInfo("slotName") → slotId, textureName, checkRelic
|
||||
// Maps WoW slot names (e.g. "HeadSlot", "HEADSLOT") to inventory slot IDs
|
||||
static int lua_GetInventorySlotInfo(lua_State* L) {
|
||||
const char* name = luaL_checkstring(L, 1);
|
||||
std::string slot(name);
|
||||
// Normalize: uppercase, strip trailing "SLOT" if present
|
||||
for (char& c : slot) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
||||
if (slot.size() > 4 && slot.substr(slot.size() - 4) == "SLOT")
|
||||
slot = slot.substr(0, slot.size() - 4);
|
||||
|
||||
// WoW inventory slots are 1-indexed
|
||||
struct SlotMap { const char* name; int id; const char* texture; };
|
||||
static const SlotMap mapping[] = {
|
||||
{"HEAD", 1, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Head"},
|
||||
{"NECK", 2, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Neck"},
|
||||
{"SHOULDER", 3, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Shoulder"},
|
||||
{"SHIRT", 4, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Shirt"},
|
||||
{"CHEST", 5, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Chest"},
|
||||
{"WAIST", 6, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Waist"},
|
||||
{"LEGS", 7, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Legs"},
|
||||
{"FEET", 8, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Feet"},
|
||||
{"WRIST", 9, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Wrists"},
|
||||
{"HANDS", 10, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Hands"},
|
||||
{"FINGER0", 11, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Finger"},
|
||||
{"FINGER1", 12, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Finger"},
|
||||
{"TRINKET0", 13, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Trinket"},
|
||||
{"TRINKET1", 14, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Trinket"},
|
||||
{"BACK", 15, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Chest"},
|
||||
{"MAINHAND", 16, "Interface\\PaperDoll\\UI-PaperDoll-Slot-MainHand"},
|
||||
{"SECONDARYHAND",17, "Interface\\PaperDoll\\UI-PaperDoll-Slot-SecondaryHand"},
|
||||
{"RANGED", 18, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Ranged"},
|
||||
{"TABARD", 19, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Tabard"},
|
||||
};
|
||||
for (const auto& m : mapping) {
|
||||
if (slot == m.name) {
|
||||
lua_pushnumber(L, m.id);
|
||||
lua_pushstring(L, m.texture);
|
||||
lua_pushboolean(L, m.id == 18 ? 1 : 0); // checkRelic: only ranged slot
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
luaL_error(L, "Unknown inventory slot: %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lua_GetInventoryItemLink(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* uid = luaL_optstring(L, 1, "player");
|
||||
int slotId = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh || slotId < 1 || slotId > 19) { return luaReturnNil(L); }
|
||||
std::string uidStr(uid);
|
||||
toLowerInPlace(uidStr);
|
||||
if (uidStr != "player") { return luaReturnNil(L); }
|
||||
|
||||
const auto& inv = gh->getInventory();
|
||||
const auto& slot = inv.getEquipSlot(static_cast<game::EquipSlot>(slotId - 1));
|
||||
if (slot.empty()) { return luaReturnNil(L); }
|
||||
|
||||
const auto* info = gh->getItemInfo(slot.item.itemId);
|
||||
std::string name = info ? info->name : slot.item.name;
|
||||
uint32_t q = info ? info->quality : static_cast<uint32_t>(slot.item.quality);
|
||||
|
||||
uint32_t qi = q < 8 ? q : 1u;
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r",
|
||||
kQualHexNoAlpha[qi], slot.item.itemId, name.c_str());
|
||||
lua_pushstring(L, link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_GetInventoryItemID(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* uid = luaL_optstring(L, 1, "player");
|
||||
int slotId = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh || slotId < 1 || slotId > 19) { return luaReturnNil(L); }
|
||||
std::string uidStr(uid);
|
||||
toLowerInPlace(uidStr);
|
||||
if (uidStr != "player") { return luaReturnNil(L); }
|
||||
|
||||
const auto& inv = gh->getInventory();
|
||||
const auto& slot = inv.getEquipSlot(static_cast<game::EquipSlot>(slotId - 1));
|
||||
if (slot.empty()) { return luaReturnNil(L); }
|
||||
lua_pushnumber(L, slot.item.itemId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_GetInventoryItemTexture(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* uid = luaL_optstring(L, 1, "player");
|
||||
int slotId = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh || slotId < 1 || slotId > 19) { return luaReturnNil(L); }
|
||||
std::string uidStr(uid);
|
||||
toLowerInPlace(uidStr);
|
||||
if (uidStr != "player") { return luaReturnNil(L); }
|
||||
|
||||
const auto& inv = gh->getInventory();
|
||||
const auto& slot = inv.getEquipSlot(static_cast<game::EquipSlot>(slotId - 1));
|
||||
if (slot.empty()) { return luaReturnNil(L); }
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_GetNumLootItems(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh || !gh->isLootWindowOpen()) { return luaReturnZero(L); }
|
||||
lua_pushnumber(L, gh->getCurrentLoot().items.size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GetLootSlotInfo(slot) → texture, name, quantity, quality, locked
|
||||
static int lua_GetLootSlotInfo(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 1)); // 1-indexed
|
||||
if (!gh || !gh->isLootWindowOpen()) {
|
||||
return luaReturnNil(L);
|
||||
}
|
||||
const auto& loot = gh->getCurrentLoot();
|
||||
if (slot < 1 || slot > static_cast<int>(loot.items.size())) {
|
||||
return luaReturnNil(L);
|
||||
}
|
||||
const auto& item = loot.items[slot - 1];
|
||||
const auto* info = gh->getItemInfo(item.itemId);
|
||||
|
||||
// texture (icon path from ItemDisplayInfo.dbc)
|
||||
std::string icon;
|
||||
if (info && info->displayInfoId != 0) {
|
||||
icon = gh->getItemIconPath(info->displayInfoId);
|
||||
}
|
||||
if (!icon.empty()) lua_pushstring(L, icon.c_str());
|
||||
else lua_pushnil(L);
|
||||
|
||||
// name
|
||||
if (info && !info->name.empty()) lua_pushstring(L, info->name.c_str());
|
||||
else lua_pushstring(L, ("Item #" + std::to_string(item.itemId)).c_str());
|
||||
|
||||
lua_pushnumber(L, item.count); // quantity
|
||||
lua_pushnumber(L, info ? info->quality : 1); // quality
|
||||
lua_pushboolean(L, 0); // locked (not tracked)
|
||||
return 5;
|
||||
}
|
||||
|
||||
// GetLootSlotLink(slot) → itemLink
|
||||
static int lua_GetLootSlotLink(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || !gh->isLootWindowOpen()) { return luaReturnNil(L); }
|
||||
const auto& loot = gh->getCurrentLoot();
|
||||
if (slot < 1 || slot > static_cast<int>(loot.items.size())) {
|
||||
return luaReturnNil(L);
|
||||
}
|
||||
const auto& item = loot.items[slot - 1];
|
||||
const auto* info = gh->getItemInfo(item.itemId);
|
||||
if (!info || info->name.empty()) { return luaReturnNil(L); }
|
||||
|
||||
uint32_t qi = info->quality < 8 ? info->quality : 1u;
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r",
|
||||
kQualHexNoAlpha[qi], item.itemId, info->name.c_str());
|
||||
lua_pushstring(L, link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// LootSlot(slot) — take item from loot
|
||||
static int lua_LootSlot(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || !gh->isLootWindowOpen()) return 0;
|
||||
const auto& loot = gh->getCurrentLoot();
|
||||
if (slot < 1 || slot > static_cast<int>(loot.items.size())) return 0;
|
||||
gh->lootItem(loot.items[slot - 1].slotIndex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CloseLoot() — close loot window
|
||||
static int lua_CloseLoot(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (gh) gh->closeLoot();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// GetLootMethod() → "freeforall"|"roundrobin"|"master"|"group"|"needbeforegreed", partyLoot, raidLoot
|
||||
static int lua_GetLootMethod(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { lua_pushstring(L, "freeforall"); lua_pushnumber(L, 0); lua_pushnumber(L, 0); return 3; }
|
||||
const auto& pd = gh->getPartyData();
|
||||
const char* method = "freeforall";
|
||||
switch (pd.lootMethod) {
|
||||
case 0: method = "freeforall"; break;
|
||||
case 1: method = "roundrobin"; break;
|
||||
case 2: method = "master"; break;
|
||||
case 3: method = "group"; break;
|
||||
case 4: method = "needbeforegreed"; break;
|
||||
}
|
||||
lua_pushstring(L, method);
|
||||
lua_pushnumber(L, 0); // partyLootMaster (index)
|
||||
lua_pushnumber(L, 0); // raidLootMaster (index)
|
||||
return 3;
|
||||
}
|
||||
|
||||
// --- Additional WoW API ---
|
||||
|
||||
static int lua_GetItemLink(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { return luaReturnNil(L); }
|
||||
uint32_t itemId = static_cast<uint32_t>(luaL_checknumber(L, 1));
|
||||
if (itemId == 0) { return luaReturnNil(L); }
|
||||
const auto* info = gh->getItemInfo(itemId);
|
||||
if (!info || info->name.empty()) { return luaReturnNil(L); }
|
||||
|
||||
uint32_t qi = info->quality < 8 ? info->quality : 1u;
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r",
|
||||
kQualHexNoAlpha[qi], itemId, info->name.c_str());
|
||||
lua_pushstring(L, link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GetSpellLink(spellIdOrName) → "|cFFxxxxxx|Hspell:ID|h[Name]|h|r"
|
||||
|
||||
void registerInventoryLuaAPI(lua_State* L) {
|
||||
static const struct { const char* name; lua_CFunction func; } api[] = {
|
||||
{"GetMoney", lua_GetMoney},
|
||||
{"GetMerchantNumItems", lua_GetMerchantNumItems},
|
||||
{"GetMerchantItemInfo", lua_GetMerchantItemInfo},
|
||||
{"GetMerchantItemLink", lua_GetMerchantItemLink},
|
||||
{"CanMerchantRepair", lua_CanMerchantRepair},
|
||||
{"GetItemInfo", lua_GetItemInfo},
|
||||
{"GetItemQualityColor", lua_GetItemQualityColor},
|
||||
{"_GetItemTooltipData", lua_GetItemTooltipData},
|
||||
{"GetItemCount", lua_GetItemCount},
|
||||
{"UseContainerItem", lua_UseContainerItem},
|
||||
{"GetContainerNumSlots", lua_GetContainerNumSlots},
|
||||
{"GetContainerItemInfo", lua_GetContainerItemInfo},
|
||||
{"GetContainerItemLink", lua_GetContainerItemLink},
|
||||
{"GetContainerNumFreeSlots", lua_GetContainerNumFreeSlots},
|
||||
{"GetInventorySlotInfo", lua_GetInventorySlotInfo},
|
||||
{"GetInventoryItemLink", lua_GetInventoryItemLink},
|
||||
{"GetInventoryItemID", lua_GetInventoryItemID},
|
||||
{"GetInventoryItemTexture", lua_GetInventoryItemTexture},
|
||||
{"GetItemLink", lua_GetItemLink},
|
||||
{"GetNumLootItems", lua_GetNumLootItems},
|
||||
{"GetLootSlotInfo", lua_GetLootSlotInfo},
|
||||
{"GetLootSlotLink", lua_GetLootSlotLink},
|
||||
{"LootSlot", lua_LootSlot},
|
||||
{"CloseLoot", lua_CloseLoot},
|
||||
{"GetLootMethod", lua_GetLootMethod},
|
||||
{"BuyMerchantItem", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 1));
|
||||
int count = static_cast<int>(luaL_optnumber(L, 2, 1));
|
||||
if (!gh || index < 1) return 0;
|
||||
const auto& items = gh->getVendorItems().items;
|
||||
if (index > static_cast<int>(items.size())) return 0;
|
||||
const auto& vi = items[index - 1];
|
||||
gh->buyItem(gh->getVendorGuid(), vi.itemId, vi.slot, count);
|
||||
return 0;
|
||||
}},
|
||||
{"SellContainerItem", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
int bag = static_cast<int>(luaL_checknumber(L, 1));
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh) return 0;
|
||||
if (bag == 0) gh->sellItemBySlot(slot - 1);
|
||||
else if (bag >= 1 && bag <= 4) gh->sellItemInBag(bag - 1, slot - 1);
|
||||
return 0;
|
||||
}},
|
||||
{"RepairAllItems", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (gh && gh->getVendorItems().canRepair) {
|
||||
bool useGuildBank = lua_toboolean(L, 1) != 0;
|
||||
gh->repairAll(gh->getVendorGuid(), useGuildBank);
|
||||
}
|
||||
return 0;
|
||||
}},
|
||||
{"UnequipItemSlot", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (gh && slot >= 1 && slot <= 19)
|
||||
gh->unequipToBackpack(static_cast<game::EquipSlot>(slot - 1));
|
||||
return 0;
|
||||
}},
|
||||
{"AcceptTrade", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (gh) gh->acceptTrade();
|
||||
return 0;
|
||||
}},
|
||||
{"CancelTrade", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (gh && gh->isTradeOpen()) gh->cancelTrade();
|
||||
return 0;
|
||||
}},
|
||||
{"InitiateTrade", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* uid = luaL_checkstring(L, 1);
|
||||
if (gh) {
|
||||
uint64_t guid = resolveUnitGuid(gh, std::string(uid));
|
||||
if (guid != 0) gh->initiateTrade(guid);
|
||||
}
|
||||
return 0;
|
||||
}},
|
||||
{"GetNumAuctionItems", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* listType = luaL_optstring(L, 1, "list");
|
||||
if (!gh) { lua_pushnumber(L, 0); lua_pushnumber(L, 0); return 2; }
|
||||
std::string t(listType);
|
||||
const game::AuctionListResult* r = nullptr;
|
||||
if (t == "list" || t == "browse") r = &gh->getAuctionBrowseResults();
|
||||
else if (t == "owner") r = &gh->getAuctionOwnerResults();
|
||||
else if (t == "bidder") r = &gh->getAuctionBidderResults();
|
||||
lua_pushnumber(L, r ? r->auctions.size() : 0);
|
||||
lua_pushnumber(L, r ? r->totalCount : 0);
|
||||
return 2;
|
||||
}},
|
||||
{"GetAuctionItemInfo", [](lua_State* L) -> int {
|
||||
// GetAuctionItemInfo(type, index) → name, texture, count, quality, canUse, level, levelColHeader, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, bidderFullName, owner, ownerFullName, saleStatus, itemId
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* listType = luaL_checkstring(L, 1);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh || index < 1) { return luaReturnNil(L); }
|
||||
std::string t(listType);
|
||||
const game::AuctionListResult* r = nullptr;
|
||||
if (t == "list") r = &gh->getAuctionBrowseResults();
|
||||
else if (t == "owner") r = &gh->getAuctionOwnerResults();
|
||||
else if (t == "bidder") r = &gh->getAuctionBidderResults();
|
||||
if (!r || index > static_cast<int>(r->auctions.size())) { return luaReturnNil(L); }
|
||||
const auto& a = r->auctions[index - 1];
|
||||
const auto* info = gh->getItemInfo(a.itemEntry);
|
||||
std::string name = info ? info->name : "Item #" + std::to_string(a.itemEntry);
|
||||
std::string icon = (info && info->displayInfoId != 0) ? gh->getItemIconPath(info->displayInfoId) : "";
|
||||
uint32_t quality = info ? info->quality : 1;
|
||||
lua_pushstring(L, name.c_str()); // name
|
||||
lua_pushstring(L, icon.empty() ? "Interface\\Icons\\INV_Misc_QuestionMark" : icon.c_str()); // texture
|
||||
lua_pushnumber(L, a.stackCount); // count
|
||||
lua_pushnumber(L, quality); // quality
|
||||
lua_pushboolean(L, 1); // canUse
|
||||
lua_pushnumber(L, info ? info->requiredLevel : 0); // level
|
||||
lua_pushstring(L, ""); // levelColHeader
|
||||
lua_pushnumber(L, a.startBid); // minBid
|
||||
lua_pushnumber(L, a.minBidIncrement); // minIncrement
|
||||
lua_pushnumber(L, a.buyoutPrice); // buyoutPrice
|
||||
lua_pushnumber(L, a.currentBid); // bidAmount
|
||||
lua_pushboolean(L, a.bidderGuid != 0 ? 1 : 0); // highBidder
|
||||
lua_pushstring(L, ""); // bidderFullName
|
||||
lua_pushstring(L, ""); // owner
|
||||
lua_pushstring(L, ""); // ownerFullName
|
||||
lua_pushnumber(L, 0); // saleStatus
|
||||
lua_pushnumber(L, a.itemEntry); // itemId
|
||||
return 17;
|
||||
}},
|
||||
{"GetAuctionItemTimeLeft", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* listType = luaL_checkstring(L, 1);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh || index < 1) { lua_pushnumber(L, 4); return 1; }
|
||||
std::string t(listType);
|
||||
const game::AuctionListResult* r = nullptr;
|
||||
if (t == "list") r = &gh->getAuctionBrowseResults();
|
||||
else if (t == "owner") r = &gh->getAuctionOwnerResults();
|
||||
else if (t == "bidder") r = &gh->getAuctionBidderResults();
|
||||
if (!r || index > static_cast<int>(r->auctions.size())) { lua_pushnumber(L, 4); return 1; }
|
||||
// Return 1=short(<30m), 2=medium(<2h), 3=long(<12h), 4=very long(>12h)
|
||||
uint32_t ms = r->auctions[index - 1].timeLeftMs;
|
||||
int cat = (ms < 1800000) ? 1 : (ms < 7200000) ? 2 : (ms < 43200000) ? 3 : 4;
|
||||
lua_pushnumber(L, cat);
|
||||
return 1;
|
||||
}},
|
||||
{"GetAuctionItemLink", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
const char* listType = luaL_checkstring(L, 1);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 2));
|
||||
if (!gh || index < 1) { return luaReturnNil(L); }
|
||||
std::string t(listType);
|
||||
const game::AuctionListResult* r = nullptr;
|
||||
if (t == "list") r = &gh->getAuctionBrowseResults();
|
||||
else if (t == "owner") r = &gh->getAuctionOwnerResults();
|
||||
else if (t == "bidder") r = &gh->getAuctionBidderResults();
|
||||
if (!r || index > static_cast<int>(r->auctions.size())) { return luaReturnNil(L); }
|
||||
uint32_t itemId = r->auctions[index - 1].itemEntry;
|
||||
const auto* info = gh->getItemInfo(itemId);
|
||||
if (!info) { return luaReturnNil(L); }
|
||||
|
||||
const char* ch = (info->quality < 8) ? kQualHexAlpha[info->quality] : "ffffffff";
|
||||
char link[256];
|
||||
snprintf(link, sizeof(link), "|c%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r", ch, itemId, info->name.c_str());
|
||||
lua_pushstring(L, link);
|
||||
return 1;
|
||||
}},
|
||||
{"GetInboxNumItems", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
lua_pushnumber(L, gh ? gh->getMailInbox().size() : 0);
|
||||
return 1;
|
||||
}},
|
||||
{"GetInboxHeaderInfo", [](lua_State* L) -> int {
|
||||
// GetInboxHeaderInfo(index) → packageIcon, stationeryIcon, sender, subject, money, COD, daysLeft, hasItem, wasRead, wasReturned, textCreated, canReply, isGM
|
||||
auto* gh = getGameHandler(L);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || index < 1) { return luaReturnNil(L); }
|
||||
const auto& inbox = gh->getMailInbox();
|
||||
if (index > static_cast<int>(inbox.size())) { return luaReturnNil(L); }
|
||||
const auto& mail = inbox[index - 1];
|
||||
lua_pushstring(L, "Interface\\Icons\\INV_Letter_15"); // packageIcon
|
||||
lua_pushstring(L, "Interface\\Icons\\INV_Letter_15"); // stationeryIcon
|
||||
lua_pushstring(L, mail.senderName.c_str()); // sender
|
||||
lua_pushstring(L, mail.subject.c_str()); // subject
|
||||
lua_pushnumber(L, mail.money); // money (copper)
|
||||
lua_pushnumber(L, mail.cod); // COD
|
||||
lua_pushnumber(L, mail.expirationTime / 86400.0f); // daysLeft
|
||||
lua_pushboolean(L, mail.attachments.empty() ? 0 : 1); // hasItem
|
||||
lua_pushboolean(L, mail.read ? 1 : 0); // wasRead
|
||||
lua_pushboolean(L, 0); // wasReturned
|
||||
lua_pushboolean(L, !mail.body.empty() ? 1 : 0); // textCreated
|
||||
lua_pushboolean(L, mail.messageType == 0 ? 1 : 0); // canReply (player mail only)
|
||||
lua_pushboolean(L, 0); // isGM
|
||||
return 13;
|
||||
}},
|
||||
{"GetInboxText", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
int index = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || index < 1) { return luaReturnNil(L); }
|
||||
const auto& inbox = gh->getMailInbox();
|
||||
if (index > static_cast<int>(inbox.size())) { return luaReturnNil(L); }
|
||||
lua_pushstring(L, inbox[index - 1].body.c_str());
|
||||
return 1;
|
||||
}},
|
||||
{"HasNewMail", [](lua_State* L) -> int {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { return luaReturnFalse(L); }
|
||||
bool hasNew = false;
|
||||
for (const auto& m : gh->getMailInbox()) {
|
||||
if (!m.read) { hasNew = true; break; }
|
||||
}
|
||||
lua_pushboolean(L, hasNew ? 1 : 0);
|
||||
return 1;
|
||||
}},
|
||||
};
|
||||
for (const auto& [name, func] : api) {
|
||||
lua_pushcfunction(L, func);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wowee::addons
|
||||
Loading…
Add table
Add a link
Reference in a new issue