314 lines
9.8 KiB
Lua
314 lines
9.8 KiB
Lua
local _G = ShaguTweaks.GetGlobalEnv()
|
|
|
|
-- Central capability layer for ClassicAPI / SuperWoW integration.
|
|
-- Keep feature checks here so individual modules don't scatter DLL-specific
|
|
-- detection logic all over the codebase.
|
|
ShaguTweaks.API = ShaguTweaks.API or {}
|
|
local API = ShaguTweaks.API
|
|
|
|
API.classicapi_version = tonumber(_G.CLASSIC_API_VERSION) or 0
|
|
API.classicapi = type(_G.C_NamePlate) == "table"
|
|
or type(_G.C_Spell) == "table"
|
|
or type(_G.C_UnitAuras) == "table"
|
|
or type(_G.UnitGUID) == "function"
|
|
|
|
API.nameplates = type(_G.C_NamePlate) == "table"
|
|
and type(_G.C_NamePlate.GetNamePlateForUnit) == "function"
|
|
|
|
API.casts = type(_G.C_Spell) == "table"
|
|
and type(_G.C_Spell.UnitCastingInfo) == "function"
|
|
and type(_G.C_Spell.UnitChannelInfo) == "function"
|
|
|
|
API.auras = type(_G.C_UnitAuras) == "table"
|
|
and type(_G.C_UnitAuras.GetAuraDataByIndex) == "function"
|
|
and type(_G.C_UnitAuras.GetDebuffDataByIndex) == "function"
|
|
|
|
API.spellinfo = type(_G.GetSpellInfo) == "function"
|
|
|
|
API.inventory = type(_G.C_Container) == "table"
|
|
and type(_G.C_Container.GetContainerNumFreeSlots) == "function"
|
|
|
|
API.itemprice = type(_G.C_Item) == "table"
|
|
and type(_G.C_Item.GetItemSellPriceByID) == "function"
|
|
|
|
API.merchant = type(_G.C_MerchantFrame) == "table"
|
|
and type(_G.C_MerchantFrame.GetNumJunkItems) == "function"
|
|
and type(_G.C_MerchantFrame.SellAllJunkItems) == "function"
|
|
|
|
API.playerstate = type(_G.IsMounted) == "function"
|
|
and type(_G.Dismount) == "function"
|
|
and type(_G.GetShapeshiftFormID) == "function"
|
|
and type(_G.CancelShapeshiftForm) == "function"
|
|
|
|
API.unitguid = type(_G.UnitGUID) == "function"
|
|
|
|
API.chatidentity = type(_G.GetCurrentChatGUID) == "function"
|
|
and type(_G.GetPlayerInfoByGUID) == "function"
|
|
|
|
API.playercache = type(_G.C_PlayerCache) == "table"
|
|
and type(_G.C_PlayerCache.GetPlayerInfoByName) == "function"
|
|
|
|
-- SuperWoW remains optional. It can still provide useful extra cast/GUID
|
|
-- information, but ClassicAPI is the primary compatibility layer.
|
|
API.superwow = type(_G.SpellInfo) == "function"
|
|
and type(_G.CombatLogAdd) == "function"
|
|
|
|
API.GetNamePlateForUnit = function(unit)
|
|
if API.nameplates then
|
|
return _G.C_NamePlate.GetNamePlateForUnit(unit)
|
|
end
|
|
end
|
|
|
|
API.UnitCastingInfo = function(unit)
|
|
if API.casts then
|
|
return _G.C_Spell.UnitCastingInfo(unit)
|
|
end
|
|
end
|
|
|
|
API.UnitChannelInfo = function(unit)
|
|
if API.casts then
|
|
return _G.C_Spell.UnitChannelInfo(unit)
|
|
end
|
|
end
|
|
|
|
-- ShaguTweaks' castbar modules use the old 1.12-compatible seven-value shape.
|
|
-- Keep that public shape while sourcing the data from ClassicAPI first.
|
|
API.GetCastInfo = function(unit)
|
|
if not API.casts then return end
|
|
|
|
local name, displayName, texture, startTime, endTime, isTradeSkill,
|
|
castID, notInterruptible, spellID = _G.C_Spell.UnitCastingInfo(unit)
|
|
|
|
-- Remote channels seen after they started can have metadata but no timing.
|
|
-- Castbars require start/end times, so only expose complete timing data here.
|
|
if not name or not startTime or not endTime then return end
|
|
|
|
return name, "", displayName or "", texture, startTime, endTime,
|
|
isTradeSkill, castID, notInterruptible, spellID
|
|
end
|
|
|
|
API.GetChannelInfo = function(unit)
|
|
if not API.casts then return end
|
|
|
|
local name, displayName, texture, startTime, endTime, isTradeSkill,
|
|
notInterruptible, spellID = _G.C_Spell.UnitChannelInfo(unit)
|
|
|
|
if not name or not startTime or not endTime then return end
|
|
|
|
return name, "", displayName or "", texture, startTime, endTime,
|
|
isTradeSkill, notInterruptible, spellID
|
|
end
|
|
|
|
API.GetAuraDataByIndex = function(unit, index, filter)
|
|
if API.auras then
|
|
return _G.C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
|
|
end
|
|
end
|
|
|
|
API.GetDebuffDataByIndex = function(unit, index)
|
|
if API.auras then
|
|
return _G.C_UnitAuras.GetDebuffDataByIndex(unit, index)
|
|
end
|
|
end
|
|
|
|
API.GetCurrentChatGUID = function()
|
|
if API.chatidentity then
|
|
return _G.GetCurrentChatGUID()
|
|
end
|
|
end
|
|
|
|
API.GetPlayerInfoByGUID = function(guid)
|
|
if API.chatidentity and guid then
|
|
return _G.GetPlayerInfoByGUID(guid)
|
|
end
|
|
end
|
|
|
|
-- Read-only bridge to ClassicAPI's optional persistent player cache.
|
|
-- ShaguTweaks never enables that cache or its visible-object scanner itself.
|
|
API.GetCachedPlayerInfoByName = function(name)
|
|
if API.playercache and name then
|
|
return _G.C_PlayerCache.GetPlayerInfoByName(name)
|
|
end
|
|
end
|
|
|
|
API.GetSpellInfo = function(spell, bookType)
|
|
if API.spellinfo then
|
|
return _G.GetSpellInfo(spell, bookType)
|
|
end
|
|
end
|
|
|
|
API.GetContainerNumFreeSlots = function(bag)
|
|
if API.inventory then
|
|
return _G.C_Container.GetContainerNumFreeSlots(bag)
|
|
end
|
|
return 0, 0
|
|
end
|
|
|
|
-- Returns the live vendor sell price in copper when the client has the item
|
|
-- cached. ClassicAPI warms uncached item data itself and returns nil until the
|
|
-- cache fill completes.
|
|
API.GetItemSellPriceByID = function(itemID)
|
|
if API.itemprice and itemID then
|
|
return _G.C_Item.GetItemSellPriceByID(itemID)
|
|
end
|
|
end
|
|
|
|
local function GetVendorPriceCache()
|
|
ShaguTweaks_cache = ShaguTweaks_cache or {}
|
|
ShaguTweaks_cache["vendor_prices"] = ShaguTweaks_cache["vendor_prices"] or {}
|
|
return ShaguTweaks_cache["vendor_prices"]
|
|
end
|
|
|
|
-- Store only prices that add something to the original static database:
|
|
-- new Turtle/custom items or prices changed by Turtle WoW. This keeps the
|
|
-- per-character SavedVariables cache very small.
|
|
local function StoreVendorPrice(itemID, price)
|
|
if type(itemID) ~= "number" or type(price) ~= "number" or price <= 0 then return end
|
|
|
|
local learned = GetVendorPriceCache()
|
|
local legacy = ShaguTweaks.SellValueLegacyDB or ShaguTweaks.SellValueDB
|
|
local legacyPrice = legacy and legacy[itemID]
|
|
|
|
if not legacyPrice or legacyPrice ~= price then
|
|
learned[itemID] = price
|
|
else
|
|
-- Drop a now-redundant learned value if the bundled database caught up.
|
|
learned[itemID] = nil
|
|
end
|
|
|
|
-- If the ClassicAPI proxy is already active, make this confirmed value
|
|
-- immediately available without another lookup during the current session.
|
|
if ShaguTweaks.SellValueLegacyDB and ShaguTweaks.SellValueDB then
|
|
rawset(ShaguTweaks.SellValueDB, itemID, price)
|
|
end
|
|
|
|
return price
|
|
end
|
|
|
|
API.StoreVendorPrice = StoreVendorPrice
|
|
|
|
API.RememberVendorPrice = function(itemID)
|
|
if not API.itemprice or type(itemID) ~= "number" then return end
|
|
|
|
local price = API.GetItemSellPriceByID(itemID)
|
|
if price and price > 0 then
|
|
return StoreVendorPrice(itemID, price)
|
|
end
|
|
end
|
|
|
|
-- Vendor Values keeps its original static database as the final fallback.
|
|
-- Resolution order is: current ClassicAPI value -> learned persistent value ->
|
|
-- bundled ShaguTweaks database.
|
|
API.PrepareVendorValues = function()
|
|
if not API.itemprice or not ShaguTweaks.SellValueDB or ShaguTweaks.SellValueLegacyDB then
|
|
return
|
|
end
|
|
|
|
local legacy = ShaguTweaks.SellValueDB
|
|
local learned = GetVendorPriceCache()
|
|
local live = {}
|
|
|
|
setmetatable(live, {
|
|
__index = function(tab, itemID)
|
|
if type(itemID) ~= "number" then
|
|
return legacy[itemID]
|
|
end
|
|
|
|
local price = API.GetItemSellPriceByID(itemID)
|
|
if price and price > 0 then
|
|
if not legacy[itemID] or legacy[itemID] ~= price then
|
|
learned[itemID] = price
|
|
else
|
|
learned[itemID] = nil
|
|
end
|
|
|
|
rawset(tab, itemID, price)
|
|
return price
|
|
end
|
|
|
|
local remembered = learned[itemID]
|
|
if remembered and remembered > 0 then
|
|
return remembered
|
|
end
|
|
|
|
return legacy[itemID]
|
|
end
|
|
})
|
|
|
|
ShaguTweaks.SellValueLegacyDB = legacy
|
|
ShaguTweaks.SellValueDB = live
|
|
|
|
-- Vendor Values intentionally suppresses its own price while a merchant is
|
|
-- open because vanilla already displays the real sell value there. If
|
|
-- ClassicAPI's item record still lacks a new Turtle item's sell price, read
|
|
-- the native money frame *after* SetBagItem built the merchant tooltip and
|
|
-- persist that value. The merchant tooltip reports the current stack value,
|
|
-- so convert it back to the per-item price used by SellValueDB.
|
|
if not API.vendorprice_tooltip_hooks then
|
|
API.vendorprice_tooltip_hooks = true
|
|
|
|
ShaguTweaks.hooksecurefunc(GameTooltip, "SetBagItem", function(tooltip, container, slot)
|
|
if not MerchantFrame or not MerchantFrame:IsShown() then return end
|
|
|
|
local link = GetContainerItemLink(container, slot)
|
|
local itemID = link and ShaguTweaks.GetItemIDFromLink(link)
|
|
if not itemID then return end
|
|
|
|
-- Prefer ClassicAPI whenever it has a direct item-record price.
|
|
if API.RememberVendorPrice(itemID) then return end
|
|
|
|
local _, count = GetContainerItemInfo(container, slot)
|
|
count = tonumber(count) or 1
|
|
if count < 1 then count = 1 end
|
|
|
|
local tooltipName = tooltip and tooltip:GetName()
|
|
local moneyFrame = tooltipName and _G[tooltipName .. "MoneyFrame1"]
|
|
local stackPrice = moneyFrame and moneyFrame:IsShown() and tonumber(moneyFrame.staticMoney)
|
|
if not stackPrice or stackPrice <= 0 then return end
|
|
|
|
local unitPrice = floor(stackPrice / count)
|
|
-- A vendor stack value must be an exact multiple of its per-item value.
|
|
-- Refuse ambiguous values rather than persist a potentially wrong price.
|
|
if unitPrice > 0 and unitPrice * count == stackPrice then
|
|
StoreVendorPrice(itemID, unitPrice)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
API.GetNumJunkItems = function()
|
|
if API.merchant then
|
|
return _G.C_MerchantFrame.GetNumJunkItems()
|
|
end
|
|
return 0
|
|
end
|
|
|
|
API.SellAllJunkItems = function()
|
|
if API.merchant then
|
|
return _G.C_MerchantFrame.SellAllJunkItems()
|
|
end
|
|
end
|
|
|
|
API.IsMounted = function()
|
|
return API.playerstate and _G.IsMounted() or false
|
|
end
|
|
|
|
API.Dismount = function()
|
|
if API.playerstate then return _G.Dismount() end
|
|
end
|
|
|
|
API.GetShapeshiftFormID = function()
|
|
if API.playerstate then return _G.GetShapeshiftFormID() end
|
|
return 0
|
|
end
|
|
|
|
API.CancelShapeshiftForm = function()
|
|
if API.playerstate then return _G.CancelShapeshiftForm() end
|
|
end
|
|
|
|
API.UnitGUID = function(unit)
|
|
if API.unitguid then
|
|
return _G.UnitGUID(unit)
|
|
end
|
|
end
|