Add files via upload
This commit is contained in:
@@ -0,0 +1,650 @@
|
||||
-- RelationshipsAttune: Core.lua
|
||||
-- Event handling, scanning, throttled auto-scan, faction-aware evaluation.
|
||||
-- Lua 5.0 / WoW 1.12 safe (Turtle/Octo compatible).
|
||||
|
||||
RelationshipsAttune = RelationshipsAttune or {}
|
||||
local RA = RelationshipsAttune
|
||||
local L = RelationshipsAttune_L
|
||||
|
||||
RA.VERSION = "1.2.7"
|
||||
RA.COMM_PREFIX = "RATTUNE"
|
||||
|
||||
RA.playerName = nil
|
||||
RA.playerRealm = nil
|
||||
RA.playerKey = nil
|
||||
RA.playerFaction = nil
|
||||
RA.initialized = false
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Utilities
|
||||
-- ---------------------------------------------------------------
|
||||
local function tsize(t)
|
||||
if not t then return 0 end
|
||||
local n = 0
|
||||
for _ in pairs(t) do n = n + 1 end
|
||||
return n
|
||||
end
|
||||
RA.tsize = tsize
|
||||
|
||||
local function print(msg)
|
||||
if DEFAULT_CHAT_FRAME then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff66ccff[RAttune]|r " .. tostring(msg))
|
||||
end
|
||||
end
|
||||
RA.print = print
|
||||
|
||||
local STANDING = {
|
||||
Hated = 1, Hostile = 2, Unfriendly = 3, Neutral = 4,
|
||||
Friendly = 5, Honored = 6, Revered = 7, Exalted = 8,
|
||||
}
|
||||
local DATA_STANDING_MAP = { [1]=2, [2]=3, [3]=4, [4]=5, [5]=6, [6]=7, [7]=8 }
|
||||
|
||||
-- Return true if this step is applicable to the current player faction.
|
||||
function RA:StepApplies(step)
|
||||
if step.faction and step.faction ~= "Both" and step.faction ~= RA.playerFaction then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Saved-variables init
|
||||
-- ---------------------------------------------------------------
|
||||
function RA:InitDB()
|
||||
if not RelationshipsAttuneDB then RelationshipsAttuneDB = {} end
|
||||
if not RelationshipsAttuneDB.players then RelationshipsAttuneDB.players = {} end
|
||||
if not RelationshipsAttuneDB.options then
|
||||
RelationshipsAttuneDB.options = {}
|
||||
end
|
||||
local o = RelationshipsAttuneDB.options
|
||||
if o.autoShare == nil then o.autoShare = true end
|
||||
if o.autoScanSeconds == nil then o.autoScanSeconds = 20 end
|
||||
if type(o.minimapAngle) ~= "number" then o.minimapAngle = 200 end
|
||||
if type(o.minimapRadius) ~= "number" then o.minimapRadius = 80 end
|
||||
if o.minimapHide == nil then o.minimapHide = false end
|
||||
|
||||
if not RelationshipsAttuneCharDB then RelationshipsAttuneCharDB = {} end
|
||||
if not RelationshipsAttuneCharDB.quests then RelationshipsAttuneCharDB.quests = {} end
|
||||
if not RelationshipsAttuneCharDB.items then RelationshipsAttuneCharDB.items = {} end
|
||||
if not RelationshipsAttuneCharDB.completedAttunements then RelationshipsAttuneCharDB.completedAttunements = {} end
|
||||
if not RelationshipsAttuneCharDB.progress then RelationshipsAttuneCharDB.progress = {} end
|
||||
if not RelationshipsAttuneCharDB.expanded then RelationshipsAttuneCharDB.expanded = {} end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Quest log scan (vanilla API)
|
||||
-- ---------------------------------------------------------------
|
||||
function RA:ScanQuestLog()
|
||||
if not GetNumQuestLogEntries then return end
|
||||
local numEntries = GetNumQuestLogEntries()
|
||||
for i = 1, numEntries do
|
||||
local title, level, tag, isHeader, _, isComplete, _, questId =
|
||||
GetQuestLogTitle(i)
|
||||
if (not isHeader) and questId then
|
||||
if isComplete and isComplete == 1 then
|
||||
RelationshipsAttuneCharDB.quests[questId] = "complete"
|
||||
else
|
||||
if not RelationshipsAttuneCharDB.quests[questId] then
|
||||
RelationshipsAttuneCharDB.quests[questId] = "inlog"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RA:MarkQuestTurnedIn(questId)
|
||||
if not questId then return end
|
||||
RelationshipsAttuneCharDB.quests[questId] = "turnedin"
|
||||
self:RecomputeAll()
|
||||
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Item scan: bags, equipped slots, and keyring
|
||||
-- ---------------------------------------------------------------
|
||||
local function rememberItemLink(link)
|
||||
if not link then return end
|
||||
local _, _, idStr = string.find(link, "item:(%d+)")
|
||||
local linkId
|
||||
if idStr then
|
||||
linkId = tonumber(idStr)
|
||||
if linkId then RelationshipsAttuneCharDB.items[linkId] = true end
|
||||
end
|
||||
-- Resolve by name so a custom Turtle/Octo item whose live client id
|
||||
-- differs from the id declared in Data.lua still gets credited.
|
||||
-- Try GetItemInfo first (works when the item is cached), then fall back
|
||||
-- to parsing the bracketed display name straight out of the link. On
|
||||
-- 1.12 / Turtle / Octo, keyring items very often return a link but
|
||||
-- GetItemInfo returns nil because the item is not cached client-side,
|
||||
-- so the bracketed-name parse is what actually credits the item.
|
||||
local resolvedName
|
||||
if GetItemInfo then
|
||||
local ok, n = pcall(GetItemInfo, link)
|
||||
if ok and n then resolvedName = n end
|
||||
end
|
||||
if not resolvedName then
|
||||
local _, _, bracket = string.find(link, "%[(.-)%]")
|
||||
if bracket and bracket ~= "" then resolvedName = bracket end
|
||||
end
|
||||
if resolvedName then
|
||||
RA:BuildNameIndex()
|
||||
if RA._nameToId then
|
||||
local mapped = RA._nameToId[string.lower(resolvedName)]
|
||||
if mapped and mapped ~= linkId then
|
||||
RelationshipsAttuneCharDB.items[mapped] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function rememberItemId(id)
|
||||
if not id then return end
|
||||
id = tonumber(id)
|
||||
if id then RelationshipsAttuneCharDB.items[id] = true end
|
||||
end
|
||||
|
||||
-- Build (and cache) a name -> item id map from RelationshipsAttune_Data so we
|
||||
-- can identify keyring items even when the client refuses to return a link
|
||||
-- for them (a known 1.12 / Turtle / Octo quirk for keyring slots).
|
||||
RA._nameToId = nil
|
||||
function RA:BuildNameIndex()
|
||||
if RA._nameToId then return RA._nameToId end
|
||||
local map = {}
|
||||
if RelationshipsAttune_Data then
|
||||
for i = 1, table.getn(RelationshipsAttune_Data) do
|
||||
local att = RelationshipsAttune_Data[i]
|
||||
if att and att.steps then
|
||||
for j = 1, table.getn(att.steps) do
|
||||
local s = att.steps[j]
|
||||
if s and s.type == "item" and s.name and s.id then
|
||||
map[string.lower(s.name)] = s.id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
RA._nameToId = map
|
||||
return map
|
||||
end
|
||||
|
||||
-- Hidden tooltip used to force item info to resolve for keyring slots.
|
||||
local _raTip
|
||||
local _raTipName = "RelationshipsAttuneScanTip"
|
||||
local function ensureTip()
|
||||
if _raTip then return _raTip end
|
||||
_raTip = CreateFrame("GameTooltip", _raTipName, nil, "GameTooltipTemplate")
|
||||
_raTip:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
return _raTip
|
||||
end
|
||||
|
||||
-- Read the first tooltip line (item name) after a SetBagItem call.
|
||||
-- 1.12 exposes frame globals via getglobal(), not _G.
|
||||
local function tipItemName()
|
||||
local fs
|
||||
if getglobal then fs = getglobal(_raTipName .. "TextLeft1") end
|
||||
if (not fs) and _G then fs = _G[_raTipName .. "TextLeft1"] end
|
||||
if fs and fs.GetText then
|
||||
local t = fs:GetText()
|
||||
if t and t ~= "" then return t end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function scanContainer(containerId)
|
||||
if not GetContainerNumSlots then return end
|
||||
local ok, slots = pcall(GetContainerNumSlots, containerId)
|
||||
if not ok or not slots or slots < 1 then return end
|
||||
for slot = 1, slots do
|
||||
local ok2, link = pcall(GetContainerItemLink, containerId, slot)
|
||||
if ok2 then rememberItemLink(link) end
|
||||
end
|
||||
end
|
||||
|
||||
-- Scan a keyring-like container. Uses every fallback we know of so that
|
||||
-- keys register on vanilla 1.12, Turtle, and Octo regardless of which
|
||||
-- bag id / API path actually works on that client build:
|
||||
-- 1) GetContainerItemLink -> parse item id + bracketed name from link.
|
||||
-- 2) SetBagItem tooltip -> read visible line as name.
|
||||
-- 3) GetContainerItemInfo -> at minimum proves a slot is occupied so
|
||||
-- we keep probing even when NumSlots lies.
|
||||
local function scanKeyringContainer(bag)
|
||||
if not GetContainerNumSlots then return end
|
||||
local ok, slots = pcall(GetContainerNumSlots, bag)
|
||||
if not ok or not slots then slots = 0 end
|
||||
-- Some 1.12 clients report 0 slots for the keyring until it has been
|
||||
-- opened at least once. Probe a reasonable upper bound in that case.
|
||||
local probe = slots
|
||||
if probe < 1 then probe = 32 end
|
||||
|
||||
local tip = ensureTip()
|
||||
local nameMap = RA:BuildNameIndex()
|
||||
|
||||
for slot = 1, probe do
|
||||
pcall(function() tip:ClearLines() end)
|
||||
local setOk = false
|
||||
pcall(function() tip:SetBagItem(bag, slot); setOk = true end)
|
||||
|
||||
-- Path 1: link
|
||||
local link
|
||||
local okL, l = pcall(GetContainerItemLink, bag, slot)
|
||||
if okL then link = l end
|
||||
if link then rememberItemLink(link) end
|
||||
|
||||
-- Path 2: tooltip visible name (works even when link is nil).
|
||||
if setOk then
|
||||
local name = tipItemName()
|
||||
if name and nameMap then
|
||||
local id = nameMap[string.lower(name)]
|
||||
if id then rememberItemId(id) end
|
||||
end
|
||||
end
|
||||
|
||||
-- Path 3: GetContainerItemInfo. We can't get a name from it, but
|
||||
-- if it returns a texture the slot is definitely occupied; that
|
||||
-- lets us know it's worth continuing to probe even when the
|
||||
-- earlier paths returned nothing on this build.
|
||||
-- (No-op for identification, kept for future diagnostics.)
|
||||
if GetContainerItemInfo then
|
||||
pcall(GetContainerItemInfo, bag, slot)
|
||||
end
|
||||
end
|
||||
pcall(function() tip:Hide() end)
|
||||
end
|
||||
|
||||
-- Warm the client-side item cache for every item id we care about. On
|
||||
-- vanilla 1.12 the first GetItemInfo(id) call for an uncached item kicks
|
||||
-- off a server fetch and returns nil; a later call returns real data.
|
||||
-- Doing this at load time (and again after a short delay) means that by
|
||||
-- the time we parse a keyring link, GetItemInfo can actually resolve it.
|
||||
function RA:WarmItemCache()
|
||||
if not GetItemInfo or not RelationshipsAttune_Data then return end
|
||||
for i = 1, table.getn(RelationshipsAttune_Data) do
|
||||
local att = RelationshipsAttune_Data[i]
|
||||
if att and att.steps then
|
||||
for j = 1, table.getn(att.steps) do
|
||||
local s = att.steps[j]
|
||||
if s and s.type == "item" and s.id then
|
||||
pcall(GetItemInfo, s.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function RA:ScanBags()
|
||||
if not GetContainerNumSlots then return end
|
||||
RA:BuildNameIndex()
|
||||
for bag = 0, 4 do
|
||||
scanContainer(bag)
|
||||
end
|
||||
|
||||
-- Keyring: try every index the various 1.12 forks use. Vanilla/Turtle/
|
||||
-- Octo typically expose it as -2; a few builds use -1, -3, or define
|
||||
-- KEYRING_CONTAINER to something else entirely. Scanning a bogus id
|
||||
-- is harmless because scanKeyringContainer pcall-guards every call.
|
||||
local seen = {}
|
||||
local function tryBag(b)
|
||||
if b == nil or seen[b] then return end
|
||||
seen[b] = true
|
||||
scanKeyringContainer(b)
|
||||
end
|
||||
tryBag(-2)
|
||||
tryBag(-1)
|
||||
tryBag(-3)
|
||||
tryBag(KEYRING_CONTAINER)
|
||||
|
||||
if GetInventoryItemLink then
|
||||
for slot = 1, 19 do
|
||||
local ok, link = pcall(GetInventoryItemLink, "player", slot)
|
||||
if ok then rememberItemLink(link) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Back-compat shim: older code paths may still call this.
|
||||
function RA:ForceKeyringResolve()
|
||||
scanKeyringContainer(-2)
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Reputation lookup
|
||||
-- ---------------------------------------------------------------
|
||||
function RA:GetReputationStanding(factionName)
|
||||
if not GetNumFactions then return 0 end
|
||||
local numFactions = GetNumFactions()
|
||||
for i = 1, numFactions do
|
||||
local name, _, standingId, _, _, _, _, _, isHeader = GetFactionInfo(i)
|
||||
if name == factionName and not isHeader then
|
||||
return standingId or 0
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Compute attunement progress
|
||||
-- ---------------------------------------------------------------
|
||||
function RA:EvaluateStep(step)
|
||||
if step.type == "level" then
|
||||
return (UnitLevel("player") or 0) >= (step.level or 0)
|
||||
elseif step.type == "quest" then
|
||||
local st = RelationshipsAttuneCharDB.quests[step.id]
|
||||
return st == "complete" or st == "turnedin"
|
||||
elseif step.type == "item" then
|
||||
return RelationshipsAttuneCharDB.items[step.id] == true
|
||||
elseif step.type == "rep" then
|
||||
local need = DATA_STANDING_MAP[step.standing] or step.standing or 5
|
||||
local have = self:GetReputationStanding(step.faction)
|
||||
return have >= need
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function RA:EvaluateAttunement(att)
|
||||
if att.faction and att.faction ~= "Both" and att.faction ~= RA.playerFaction then
|
||||
return { applicable = false, done = false, steps = {}, doneCount = 0, total = 0 }
|
||||
end
|
||||
local steps, doneCount, total = {}, 0, 0
|
||||
local numSteps = table.getn(att.steps)
|
||||
for i = 1, numSteps do
|
||||
local step = att.steps[i]
|
||||
if self:StepApplies(step) then
|
||||
local ok = self:EvaluateStep(step) and true or false
|
||||
steps[i] = ok
|
||||
total = total + 1
|
||||
if ok then doneCount = doneCount + 1 end
|
||||
else
|
||||
steps[i] = nil
|
||||
end
|
||||
end
|
||||
-- Short-circuit: if the player already owns the final key/item (or a step
|
||||
-- explicitly flagged final=true) then the entire attunement is done, even
|
||||
-- if earlier bookkeeping steps (prereq quests, reputation) can no longer
|
||||
-- be detected on this character. This handles the case where the key was
|
||||
-- earned before the addon was installed.
|
||||
local finalIdx = nil
|
||||
for i = numSteps, 1, -1 do
|
||||
local step = att.steps[i]
|
||||
if step.final and self:StepApplies(step) then
|
||||
finalIdx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if not finalIdx then
|
||||
-- Fall back to the last applicable item step (typically the key).
|
||||
for i = numSteps, 1, -1 do
|
||||
local step = att.steps[i]
|
||||
if step.type == "item" and self:StepApplies(step) then
|
||||
finalIdx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not finalIdx then
|
||||
-- If there is no key/item step, the last applicable step is the final
|
||||
-- proof we can see. This covers quest-only or reputation attunements.
|
||||
for i = numSteps, 1, -1 do
|
||||
local step = att.steps[i]
|
||||
if self:StepApplies(step) then
|
||||
finalIdx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
local persistedDone = RelationshipsAttuneCharDB.completedAttunements and RelationshipsAttuneCharDB.completedAttunements[att.id]
|
||||
if persistedDone or (finalIdx and steps[finalIdx] == true) then
|
||||
for i = 1, numSteps do
|
||||
if steps[i] == false then
|
||||
steps[i] = true
|
||||
doneCount = doneCount + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if total > 0 and doneCount == total then
|
||||
RelationshipsAttuneCharDB.completedAttunements[att.id] = true
|
||||
end
|
||||
return {
|
||||
applicable = true,
|
||||
done = (total > 0 and doneCount == total),
|
||||
steps = steps,
|
||||
doneCount = doneCount,
|
||||
total = total,
|
||||
}
|
||||
end
|
||||
|
||||
function RA:RecomputeAll()
|
||||
for i = 1, table.getn(RelationshipsAttune_Data) do
|
||||
local att = RelationshipsAttune_Data[i]
|
||||
RelationshipsAttuneCharDB.progress[att.id] = self:EvaluateAttunement(att)
|
||||
end
|
||||
RelationshipsAttuneDB.players[RA.playerKey] = {
|
||||
name = RA.playerName,
|
||||
realm = RA.playerRealm,
|
||||
faction = RA.playerFaction,
|
||||
level = UnitLevel("player") or 0,
|
||||
class = UnitClass("player"),
|
||||
version = RA.VERSION,
|
||||
updated = time and time() or 0,
|
||||
progress = RelationshipsAttuneCharDB.progress,
|
||||
}
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Throttled auto-scan
|
||||
-- ---------------------------------------------------------------
|
||||
RA._throttle = 0
|
||||
RA._dirty = false
|
||||
RA._minRescan = 2.0
|
||||
|
||||
function RA:MarkDirty() RA._dirty = true end
|
||||
|
||||
function RA:_OnUpdate(elapsed)
|
||||
RA._throttle = RA._throttle + (elapsed or 0)
|
||||
local interval = (RelationshipsAttuneDB and RelationshipsAttuneDB.options
|
||||
and RelationshipsAttuneDB.options.autoScanSeconds) or 20
|
||||
if interval < 5 then interval = 5 end
|
||||
|
||||
-- Delayed post-login retry passes. GetItemInfo populates async on
|
||||
-- 1.12, and the keyring itself may not be populated at PLAYER_LOGIN,
|
||||
-- so we re-warm the cache and rescan a couple of times shortly after
|
||||
-- login to catch keys that weren't resolvable on the first pass.
|
||||
if RA._retryAt and GetTime then
|
||||
local now = GetTime()
|
||||
local pending = {}
|
||||
local fired = false
|
||||
for i = 1, table.getn(RA._retryAt) do
|
||||
local t = RA._retryAt[i]
|
||||
if now >= t then
|
||||
fired = true
|
||||
else
|
||||
table.insert(pending, t)
|
||||
end
|
||||
end
|
||||
if fired then
|
||||
RA:WarmItemCache()
|
||||
RA:ScanBags()
|
||||
RA:RecomputeAll()
|
||||
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
|
||||
end
|
||||
if table.getn(pending) == 0 then
|
||||
RA._retryAt = nil
|
||||
else
|
||||
RA._retryAt = pending
|
||||
end
|
||||
end
|
||||
|
||||
if RA._dirty and RA._throttle >= RA._minRescan then
|
||||
RA._dirty = false
|
||||
RA._throttle = 0
|
||||
RA:ScanQuestLog()
|
||||
RA:ScanBags()
|
||||
RA:RecomputeAll()
|
||||
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
|
||||
return
|
||||
end
|
||||
|
||||
if RA._throttle >= interval then
|
||||
RA._throttle = 0
|
||||
RA:ScanQuestLog()
|
||||
RA:ScanBags()
|
||||
RA:RecomputeAll()
|
||||
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Slash commands
|
||||
-- ---------------------------------------------------------------
|
||||
local function slashHandler(msg)
|
||||
msg = string.lower(msg or "")
|
||||
-- trim
|
||||
msg = string.gsub(msg, "^%s+", "")
|
||||
msg = string.gsub(msg, "%s+$", "")
|
||||
|
||||
if msg == "show" then
|
||||
if RA.UI then RA.UI:Show() end
|
||||
elseif msg == "hide" then
|
||||
-- If the window is open, /attune hide closes it; otherwise it hides the minimap button.
|
||||
if RA.UI and RA.UI:IsShown() then
|
||||
RA.UI:Hide()
|
||||
elseif RA.Minimap then
|
||||
RA.Minimap:Hide()
|
||||
print("Minimap button hidden. Type /attune minimap to show it again.")
|
||||
end
|
||||
elseif msg == "minimap" or msg == "mm" then
|
||||
if RA.Minimap then
|
||||
RA.Minimap:Toggle()
|
||||
local o = RelationshipsAttuneDB.options
|
||||
if o.minimapHide then
|
||||
print("Minimap button hidden. /attune minimap to show it.")
|
||||
else
|
||||
print("Minimap button shown. Drag to reposition around the minimap.")
|
||||
end
|
||||
end
|
||||
elseif msg == "sync" then
|
||||
if RA.Comm then RA.Comm:RequestSync() end
|
||||
print(L.MSG_SYNC_SENT)
|
||||
elseif msg == "scan" then
|
||||
RA:ScanQuestLog(); RA:ScanBags(); RA:RecomputeAll()
|
||||
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
|
||||
print(L.MSG_SCANNED)
|
||||
elseif msg == "reset" then
|
||||
RelationshipsAttuneCharDB = nil
|
||||
RA:InitDB()
|
||||
RA:ScanQuestLog(); RA:ScanBags(); RA:RecomputeAll()
|
||||
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
|
||||
print(L.MSG_RESET)
|
||||
elseif msg == "debug" or string.find(msg, "^find") then
|
||||
local _, _, arg = string.find(msg, "find%s+(%d+)")
|
||||
RA:ScanBags()
|
||||
if arg then
|
||||
local id = tonumber(arg)
|
||||
print("Item " .. id .. " owned: " .. tostring(RelationshipsAttuneCharDB.items[id] == true))
|
||||
end
|
||||
print("Keyring dump:")
|
||||
local probeBags = { -2, -1 }
|
||||
if KEYRING_CONTAINER and KEYRING_CONTAINER ~= -2 and KEYRING_CONTAINER ~= -1 then
|
||||
table.insert(probeBags, KEYRING_CONTAINER)
|
||||
end
|
||||
local tip = ensureTip()
|
||||
for _, bag in ipairs(probeBags) do
|
||||
local ok, slots = pcall(GetContainerNumSlots, bag)
|
||||
if not ok or not slots then slots = 0 end
|
||||
local probe = slots > 0 and slots or 32
|
||||
print(" bag " .. bag .. " GetContainerNumSlots=" .. tostring(slots) .. " probing=" .. probe)
|
||||
for s = 1, probe do
|
||||
pcall(function() tip:ClearLines() end)
|
||||
pcall(function() tip:SetBagItem(bag, s) end)
|
||||
local ok2, link = pcall(GetContainerItemLink, bag, s)
|
||||
local name = tipItemName()
|
||||
if (ok2 and link) or name then
|
||||
print(" [" .. s .. "] link=" .. tostring(link) .. " name=" .. tostring(name))
|
||||
end
|
||||
end
|
||||
end
|
||||
pcall(function() tip:Hide() end)
|
||||
print("Scarlet Key (7146) owned: " .. tostring(RelationshipsAttuneCharDB.items[7146] == true))
|
||||
elseif msg == "help" or msg == "?" then
|
||||
print("Relationships Attune - commands:")
|
||||
print(" /attune - toggle the window")
|
||||
print(" /attune show - open the window")
|
||||
print(" /attune hide - close the window (or hide the minimap button)")
|
||||
print(" /attune minimap - toggle the minimap button")
|
||||
print(" /attune scan - force a rescan of quests + bags")
|
||||
print(" /attune sync - request sync from your group")
|
||||
print(" /attune debug - dump keyring + item detection")
|
||||
print(" /attune find <id>- check if a specific item is detected")
|
||||
print(" /attune reset - wipe this character's data and rescan")
|
||||
else
|
||||
if RA.UI then RA.UI:Toggle() end
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_RELATIONSHIPSATTUNE1 = "/rattune"
|
||||
SLASH_RELATIONSHIPSATTUNE2 = "/relationshipsattune"
|
||||
SLASH_RELATIONSHIPSATTUNE3 = "/attune"
|
||||
SlashCmdList["RELATIONSHIPSATTUNE"] = slashHandler
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Event frame
|
||||
-- ---------------------------------------------------------------
|
||||
local ef = CreateFrame("Frame", "RelationshipsAttuneEventFrame")
|
||||
ef:RegisterEvent("PLAYER_LOGIN")
|
||||
ef:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
ef:RegisterEvent("QUEST_LOG_UPDATE")
|
||||
ef:RegisterEvent("BAG_UPDATE")
|
||||
ef:RegisterEvent("PLAYER_LEVEL_UP")
|
||||
ef:RegisterEvent("UPDATE_FACTION")
|
||||
ef:RegisterEvent("CHAT_MSG_ADDON")
|
||||
pcall(function() ef:RegisterEvent("QUEST_TURNED_IN") end)
|
||||
pcall(function() ef:RegisterEvent("KEYRING_UPDATE") end)
|
||||
|
||||
ef:SetScript("OnEvent", function()
|
||||
local e = event
|
||||
if e == "PLAYER_LOGIN" then
|
||||
RA:InitDB()
|
||||
RA.playerName = UnitName("player")
|
||||
RA.playerRealm = GetRealmName() or "Unknown"
|
||||
RA.playerKey = RA.playerName .. "-" .. RA.playerRealm
|
||||
RA.playerFaction = UnitFactionGroup("player") or "Neutral"
|
||||
RA:BuildNameIndex()
|
||||
RA:WarmItemCache()
|
||||
RA:ScanQuestLog(); RA:ScanBags(); RA:RecomputeAll()
|
||||
-- Retry pass: on 1.12 GetItemInfo populates async, and the
|
||||
-- keyring is not always populated at PLAYER_LOGIN. Schedule two
|
||||
-- delayed rescans so keys still get credited without user action.
|
||||
RA._retryAt = { GetTime() + 3, GetTime() + 10 }
|
||||
if RA.Comm and RA.Comm.Init then RA.Comm:Init() end
|
||||
if RA.UI and RA.UI.Init then RA.UI:Init() end
|
||||
if RA.Minimap and RA.Minimap.Init then RA.Minimap:Init() end
|
||||
RA.initialized = true
|
||||
print(L.ADDON_NAME .. " v" .. RA.VERSION .. " " .. L.MSG_LOADED
|
||||
.. " (" .. tostring(RA.playerFaction) .. "). Type /attune for options.")
|
||||
|
||||
elseif e == "PLAYER_ENTERING_WORLD" then
|
||||
if RA.initialized then
|
||||
RA.playerFaction = UnitFactionGroup("player") or RA.playerFaction or "Neutral"
|
||||
RA:MarkDirty()
|
||||
end
|
||||
|
||||
elseif e == "QUEST_LOG_UPDATE"
|
||||
or e == "BAG_UPDATE"
|
||||
or e == "KEYRING_UPDATE"
|
||||
or e == "PLAYER_LEVEL_UP"
|
||||
or e == "UPDATE_FACTION" then
|
||||
if RA.initialized then RA:MarkDirty() end
|
||||
|
||||
elseif e == "QUEST_TURNED_IN" then
|
||||
if arg1 then RA:MarkQuestTurnedIn(arg1) end
|
||||
|
||||
elseif e == "CHAT_MSG_ADDON" then
|
||||
if arg1 == RA.COMM_PREFIX and RA.Comm and RA.Comm.OnMessage then
|
||||
RA.Comm:OnMessage(arg2, arg3, arg4)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ef:SetScript("OnUpdate", function()
|
||||
if not RA.initialized then return end
|
||||
RA:_OnUpdate(arg1)
|
||||
end)
|
||||
Reference in New Issue
Block a user