6 Commits
v .. AA

Author SHA1 Message Date
Relationship ccfb78c3a6 Add files via upload 2026-07-18 18:50:56 +01:00
Relationship 9cdcc1814f Update README.md 2026-07-18 18:37:07 +01:00
Relationship 0c9af0e9d2 Update README.md 2026-07-16 17:57:27 +01:00
Relationship 8f4480bf14 Add files via upload 2026-07-16 17:56:42 +01:00
Relationship a276cca444 Add files via upload 2026-07-16 17:34:22 +01:00
Relationship 4502a1db7e Add files via upload 2026-07-16 17:03:20 +01:00
6 changed files with 256 additions and 82 deletions
+135 -7
View File
@@ -6,7 +6,7 @@ RelationshipsAttune = RelationshipsAttune or {}
local RA = RelationshipsAttune local RA = RelationshipsAttune
local L = RelationshipsAttune_L local L = RelationshipsAttune_L
RA.VERSION = "1.4.2" RA.VERSION = "1.4.3"
RA.COMM_PREFIX = "RATTUNE" RA.COMM_PREFIX = "RATTUNE"
RA.playerName = nil RA.playerName = nil
@@ -70,6 +70,56 @@ function RA:InitDB()
if not RelationshipsAttuneCharDB.announced then RelationshipsAttuneCharDB.announced = {} end if not RelationshipsAttuneCharDB.announced then RelationshipsAttuneCharDB.announced = {} end
if not RelationshipsAttuneCharDB.progress then RelationshipsAttuneCharDB.progress = {} end if not RelationshipsAttuneCharDB.progress then RelationshipsAttuneCharDB.progress = {} end
if not RelationshipsAttuneCharDB.expanded then RelationshipsAttuneCharDB.expanded = {} end if not RelationshipsAttuneCharDB.expanded then RelationshipsAttuneCharDB.expanded = {} end
if not RelationshipsAttuneCharDB.forcedSteps then RelationshipsAttuneCharDB.forcedSteps = {} end
if not RelationshipsAttuneCharDB.forcedAtt then RelationshipsAttuneCharDB.forcedAtt = {} end
end
-- ---------------------------------------------------------------
-- Force-complete (manual override) helpers
-- ---------------------------------------------------------------
function RA:IsStepForced(attId, i)
local t = RelationshipsAttuneCharDB.forcedSteps
return t and t[attId] and t[attId][i] == true
end
function RA:SetStepForced(attId, i, v)
if not attId or not i then return end
local t = RelationshipsAttuneCharDB.forcedSteps
if not t then RelationshipsAttuneCharDB.forcedSteps = {}; t = RelationshipsAttuneCharDB.forcedSteps end
if not t[attId] then t[attId] = {} end
if v then t[attId][i] = true else t[attId][i] = nil end
self:RecomputeAll()
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
end
function RA:IsAttForced(attId)
local t = RelationshipsAttuneCharDB.forcedAtt
return t and t[attId] == true
end
function RA:SetAttForced(attId, v)
if not attId then return end
if not RelationshipsAttuneCharDB.forcedAtt then
RelationshipsAttuneCharDB.forcedAtt = {}
end
if not RelationshipsAttuneCharDB.completedAttunements then
RelationshipsAttuneCharDB.completedAttunements = {}
end
if v then
RelationshipsAttuneCharDB.forcedAtt[attId] = true
RelationshipsAttuneCharDB.completedAttunements[attId] = true
else
RelationshipsAttuneCharDB.forcedAtt[attId] = nil
RelationshipsAttuneCharDB.completedAttunements[attId] = nil
if RelationshipsAttuneCharDB.forcedSteps then
RelationshipsAttuneCharDB.forcedSteps[attId] = nil
end
if RelationshipsAttuneCharDB.announced then
RelationshipsAttuneCharDB.announced[attId] = nil
end
end
self:RecomputeAll()
if RA.UI and RA.UI.Refresh then RA.UI:Refresh() end
end end
-- --------------------------------------------------------------- -- ---------------------------------------------------------------
@@ -81,10 +131,16 @@ end
-- --------------------------------------------------------------- -- ---------------------------------------------------------------
function RA:ScanQuestLog() function RA:ScanQuestLog()
if not GetNumQuestLogEntries then return end if not GetNumQuestLogEntries then return end
-- Rebuild transient quest states from the live log so that abandoning a -- Snapshot previous transient states so we can detect a turn-in on clients
-- quest reverts its step colour. "turnedin" is authoritative and preserved. -- that don't fire QUEST_TURNED_IN (stock 1.12). A quest that was
-- "complete" (ready to hand in) and is now missing from the log is
-- almost certainly turned in -- abandoning a quest requires it to be
-- accepted-but-not-complete. This is what actually credits MC's
-- "Attunement to the Core" (7487), BWL, Naxx, and any other
-- quest-only final step.
local prev = RelationshipsAttuneCharDB.quests or {}
local kept = {} local kept = {}
for qid, st in pairs(RelationshipsAttuneCharDB.quests) do for qid, st in pairs(prev) do
if st == "turnedin" then kept[qid] = "turnedin" end if st == "turnedin" then kept[qid] = "turnedin" end
end end
RelationshipsAttuneCharDB.quests = kept RelationshipsAttuneCharDB.quests = kept
@@ -92,6 +148,7 @@ function RA:ScanQuestLog()
RA:BuildNameIndex() RA:BuildNameIndex()
local qNameMap = RA._questNameToId or {} local qNameMap = RA._questNameToId or {}
local seenThisScan = {}
local numEntries = GetNumQuestLogEntries() local numEntries = GetNumQuestLogEntries()
for i = 1, numEntries do for i = 1, numEntries do
local ok, title, level, tag, isHeader, _, isComplete, _, questId = local ok, title, level, tag, isHeader, _, isComplete, _, questId =
@@ -104,7 +161,9 @@ function RA:ScanQuestLog()
if (not qid) and title then if (not qid) and title then
qid = qNameMap[string.lower(title)] qid = qNameMap[string.lower(title)]
end end
if qid and RelationshipsAttuneCharDB.quests[qid] ~= "turnedin" then if qid then
seenThisScan[qid] = true
if RelationshipsAttuneCharDB.quests[qid] ~= "turnedin" then
if isComplete and isComplete == 1 then if isComplete and isComplete == 1 then
RelationshipsAttuneCharDB.quests[qid] = "complete" RelationshipsAttuneCharDB.quests[qid] = "complete"
else else
@@ -115,6 +174,16 @@ function RA:ScanQuestLog()
end end
end end
-- Retroactive turn-in detection: any quest we tracked as "complete" last
-- scan that isn't in the log any more got handed in.
for qid, st in pairs(prev) do
if st == "complete" and not seenThisScan[qid] then
RelationshipsAttuneCharDB.quests[qid] = "turnedin"
end
end
end
function RA:MarkQuestTurnedIn(questId) function RA:MarkQuestTurnedIn(questId)
if not questId then return end if not questId then return end
RelationshipsAttuneCharDB.quests[questId] = "turnedin" RelationshipsAttuneCharDB.quests[questId] = "turnedin"
@@ -175,10 +244,24 @@ function RA:BuildNameIndex()
for j = 1, table.getn(att.steps) do for j = 1, table.getn(att.steps) do
local s = att.steps[j] local s = att.steps[j]
if s and s.name and s.id then if s and s.name and s.id then
local nlow = string.lower(s.name)
if s.type == "item" then if s.type == "item" then
map[string.lower(s.name)] = s.id map[nlow] = s.id
elseif s.type == "quest" then elseif s.type == "quest" then
qmap[string.lower(s.name)] = s.id qmap[nlow] = s.id
-- Data.lua disambiguates faction-specific quests
-- with a "(Alliance)" / "(Horde)" suffix that
-- the in-game quest log does NOT include. Index
-- the stripped variant so the name fallback in
-- ScanQuestLog matches on stock 1.12 clients
-- that don't return a questId.
local _, _, stripped = string.find(nlow, "^(.-)%s*%(alliance%)%s*$")
if not stripped then
_, _, stripped = string.find(nlow, "^(.-)%s*%(horde%)%s*$")
end
if stripped and stripped ~= "" and not qmap[stripped] then
qmap[stripped] = s.id
end
end end
end end
end end
@@ -373,6 +456,20 @@ function RA:EvaluateAttunement(att)
stepsInProgress[i] = nil stepsInProgress[i] = nil
end end
end end
-- Apply per-step forced completions (manual overrides from the UI).
-- These are treated exactly like a real completion for tracking purposes.
local forced = RelationshipsAttuneCharDB.forcedSteps and RelationshipsAttuneCharDB.forcedSteps[att.id]
if forced then
for i = 1, numSteps do
local step = att.steps[i]
if step and self:StepApplies(step) and forced[i] and steps[i] == false then
steps[i] = true
stepsInProgress[i] = false
doneCount = doneCount + 1
end
end
end
-- Short-circuit: if the "final" step is complete, treat the whole -- Short-circuit: if the "final" step is complete, treat the whole
-- attunement as done. Prefer an explicit final=true step; fall back to -- attunement as done. Prefer an explicit final=true step; fall back to
-- the last item step (key), then the last applicable step. -- the last item step (key), then the last applicable step.
@@ -597,6 +694,34 @@ local function slashHandler(msg)
print("Minimap button shown. Drag to reposition around the minimap.") print("Minimap button shown. Drag to reposition around the minimap.")
end end
end end
elseif string.find(msg, "^pos") then
-- /attune pos <x> <y> -- set exact offset from UIParent CENTER.
-- Values may be off-screen (e.g. -9999) to park the button out of
-- the way while still letting MinimapButtonBag collect it.
local _, _, sx, sy = string.find(msg, "pos%s+(%-?%d+%.?%d*)%s+(%-?%d+%.?%d*)")
local x, y = tonumber(sx), tonumber(sy)
if x and y and RA.Minimap then
local o = RelationshipsAttuneDB.options
o.minimapX, o.minimapY = x, y
RA.Minimap:Refresh()
print("Minimap button moved to " .. x .. ", " .. y .. ".")
else
print("Usage: /attune pos <x> <y> (offsets from screen center; can be off-screen)")
end
elseif msg == "offscreen" or msg == "park" then
if RA.Minimap then
local o = RelationshipsAttuneDB.options
o.minimapX, o.minimapY = -10000, -10000
RA.Minimap:Refresh()
print("Minimap button parked off-screen. MinimapButtonBag (if installed) can still collect it. Use /attune resetpos to bring it back.")
end
elseif msg == "resetpos" then
if RA.Minimap then
local o = RelationshipsAttuneDB.options
o.minimapX, o.minimapY = 0, -140
RA.Minimap:Refresh()
print("Minimap button position reset.")
end
elseif msg == "sync" then elseif msg == "sync" then
if RA.Comm then RA.Comm:RequestSync() end if RA.Comm then RA.Comm:RequestSync() end
print(L.MSG_SYNC_SENT) print(L.MSG_SYNC_SENT)
@@ -645,6 +770,9 @@ local function slashHandler(msg)
print(" /attune show - open the window") print(" /attune show - open the window")
print(" /attune hide - close the window (or hide the minimap button)") print(" /attune hide - close the window (or hide the minimap button)")
print(" /attune minimap - toggle the minimap button") print(" /attune minimap - toggle the minimap button")
print(" /attune pos x y - place the minimap button at exact offset (can be off-screen)")
print(" /attune offscreen- park the minimap button off-screen (MBB can still collect it)")
print(" /attune resetpos - restore the minimap button to a visible default position")
print(" /attune scan - force a rescan of quests + bags") print(" /attune scan - force a rescan of quests + bags")
print(" /attune sync - request sync from your group") print(" /attune sync - request sync from your group")
print(" /attune debug - dump keyring + item detection") print(" /attune debug - dump keyring + item detection")
-61
View File
@@ -537,17 +537,6 @@ RelationshipsAttune_Data = {
start = "Discs of Norgannon, Chamber of Khaz'mul, Uldaman, Badlands (42, 20)." }, start = "Discs of Norgannon, Chamber of Khaz'mul, Uldaman, Badlands (42, 20)." },
}, },
}, },
{
id = "hydraxian", name = "Hydraxian Waterlords (MC Runes)", category = "Keys & Access", faction = "Both",
icon = "Interface\\Icons\\Spell_Frost_SummonWaterElemental",
steps = {
{ type = "level", level = 60, name = "Reach Level 60", icon = ICON_LEVEL },
{ type = "rep", faction = "Hydraxian Waterlords", standing = 5,
name = "Honored with the Hydraxian Waterlords", icon = ICON_REP,
desc = "Needed to purchase Aqual Quintessence to douse the Molten Core runes for Majordomo.",
start = "Duke Hydraxis, small island off south-east Azshara (79, 76). Turn in water elemental drops (Azshara/Silithus) and MC boss items." },
},
},
---------------------------------------------------------------- ----------------------------------------------------------------
-- TURTLE / OCTO WOW CUSTOM CONTENT -- TURTLE / OCTO WOW CUSTOM CONTENT
-- Placeholder ids where the live client id isn't confirmed; the -- Placeholder ids where the live client id isn't confirmed; the
@@ -579,56 +568,6 @@ RelationshipsAttune_Data = {
start = "Awarded from Archmage Alturus (Morgan's Plot, Deadwind Pass 40, 75) at the end of the LKH attunement." }, start = "Awarded from Archmage Alturus (Morgan's Plot, Deadwind Pass 40, 75) at the end of the LKH attunement." },
}, },
}, },
{
id = "es", name = "Emerald Sanctum", category = "Turtle", faction = "Both",
icon = "Interface\\Icons\\INV_Misc_Head_Dragon_Green",
steps = {
{ type = "level", level = 60, name = "Reach Level 60", icon = ICON_LEVEL },
{ type = "quest", id = 40020, name = "Emerald Sanctum attunement", icon = ICON_QUEST, final = true,
desc = "Emerald Sanctum attunement chain (Hinterlands portal). Speak with Keeper Remulos and collect the Sanctum Sigil from the four green dragons around Azeroth (Ysondre, Emeriss, Lethon, Taerar).",
start = "Keeper Remulos avatar, eastern Hinterlands near Seradane (55, 22)." },
},
},
{
id = "cgrove", name = "Crescent Grove (Turtle 20-man)", category = "Turtle", faction = "Both",
icon = "Interface\\Icons\\INV_Misc_Herb_02",
steps = {
{ type = "level", level = 55, name = "Reach Level 55", icon = ICON_LEVEL },
{ type = "quest", id = 40100, name = "Crescent Grove attunement", icon = ICON_QUEST, final = true,
desc = "Turtle-WoW 20-man Emerald-themed raid in Ashenvale. Speak to the druid representative at the grove entrance to start the chain (collect corrupted samples inside the outer ring).",
start = "Ashenvale, near the Crescent Grove portal south of Warsong Lumber Camp (~40, 63)." },
},
},
{
id = "gilneas", name = "Gilneas City", category = "Turtle", faction = "Both",
icon = "Interface\\Icons\\Ability_Mount_WhiteDireWolf",
steps = {
{ type = "level", level = 55, name = "Reach Level 55", icon = ICON_LEVEL },
{ type = "quest", id = 40030, name = "Gilneas City attunement", icon = ICON_QUEST, final = true,
desc = "Attunement for the Gilneas City dungeon. Speak with Lord Darius Crowley at the Greymane Wall and complete the intro quest chain in Silverpine.",
start = "Lord Darius Crowley, Greymane Wall entrance, southern Silverpine Forest (43, 66)." },
},
},
{
id = "hateforge", name = "Hateforge Quarry", category = "Turtle", faction = "Both",
icon = "Interface\\Icons\\INV_Hammer_16",
steps = {
{ type = "level", level = 55, name = "Reach Level 55", icon = ICON_LEVEL },
{ type = "quest", id = 40040, name = "Hateforge Quarry attunement", icon = ICON_QUEST, final = true,
desc = "Attunement for the Hateforge Quarry dungeon. Turtle Dark Iron chain out of Burning Steppes.",
start = "Overseer Maltorius, Flame Crest camp, northern Burning Steppes (62, 24)." },
},
},
{
id = "stormwind_vault", name = "Stormwind Vault", category = "Turtle", faction = "Both",
icon = "Interface\\Icons\\INV_Misc_Coin_01",
steps = {
{ type = "level", level = 55, name = "Reach Level 55", icon = ICON_LEVEL },
{ type = "quest", id = 40050, name = "Stormwind Vault attunement", icon = ICON_QUEST, final = true,
desc = "Attunement for the Stormwind Vault dungeon (Turtle). Alliance intro from the Stockade warden; Horde variant from a SI:7 defector in Booty Bay.",
start = "Warden Thelwater, Stormwind Stockade entrance, Mage Quarter, Stormwind City (50, 68). Horde variant: Booty Bay, Stranglethorn (27, 77)." },
},
},
---------------------------------------------------------------- ----------------------------------------------------------------
-- DRAGONMAW RETREAT (Turtle/Octo custom, Wetlands, patch 1.18) -- DRAGONMAW RETREAT (Turtle/Octo custom, Wetlands, patch 1.18)
---------------------------------------------------------------- ----------------------------------------------------------------
+26 -5
View File
@@ -52,22 +52,35 @@ end
MM._updatePosition = applyPosition MM._updatePosition = applyPosition
local function onDragUpdate() local function onDragUpdate()
-- Do all math in the button's own effective scale. The button is
-- parented to Minimap, whose effective scale can differ from
-- UIParent's; mixing scales makes the icon drift away from the cursor.
local btn = this
local bscale = btn:GetEffectiveScale() or 1
local uscale = UIParent:GetEffectiveScale() or 1
local mx, my = GetCursorPosition() local mx, my = GetCursorPosition()
local scale = UIParent:GetEffectiveScale() or 1 mx = mx / bscale
mx = mx / scale; my = my / scale my = my / bscale
local ucx, ucy = UIParent:GetCenter() local ucx, ucy = UIParent:GetCenter()
if not ucx then return end if not ucx then return end
-- Convert UIParent center from UIParent scale into btn scale.
ucx = ucx * uscale / bscale
ucy = ucy * uscale / bscale
local o = opts() local o = opts()
o.minimapX = mx - ucx o.minimapX = mx - ucx
o.minimapY = my - ucy o.minimapY = my - ucy
applyPosition(this) applyPosition(btn)
end end
function MM:Create() function MM:Create()
if getglobal(BTN_NAME) then return getglobal(BTN_NAME) end if getglobal(BTN_NAME) then return getglobal(BTN_NAME) end
-- Parent to UIParent so we're not clipped to the minimap area. -- Parent to Minimap so button-collector addons (MinimapButtonBag /
local btn = CreateFrame("Button", BTN_NAME, UIParent) -- MBB, and similar) can discover and manage this button the same way
-- they discover Blizzard's built-in minimap buttons. SetPoint still
-- anchors to UIParent CENTER so the button can be dragged anywhere
-- on screen -- the anchor frame is independent of the parent frame.
local btn = CreateFrame("Button", BTN_NAME, Minimap)
btn:SetWidth(31); btn:SetHeight(31) btn:SetWidth(31); btn:SetHeight(31)
btn:SetFrameStrata("MEDIUM") btn:SetFrameStrata("MEDIUM")
btn:SetFrameLevel(8) btn:SetFrameLevel(8)
@@ -75,6 +88,12 @@ function MM:Create()
btn:RegisterForClicks("LeftButtonUp", "RightButtonUp") btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
btn:RegisterForDrag("LeftButton", "RightButton") btn:RegisterForDrag("LeftButton", "RightButton")
btn:SetMovable(true) btn:SetMovable(true)
-- Allow the button to sit off-screen (via /attune pos or /attune offscreen)
-- while remaining discoverable by MinimapButtonBag.
if btn.SetClampedToScreen then btn:SetClampedToScreen(false) end
-- MBB-friendly highlight so the button visually matches other
-- minimap buttons once it has been swept into the bag.
btn:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
-- Round border ring (standard minimap-button look) -- Round border ring (standard minimap-button look)
local overlay = btn:CreateTexture(nil, "OVERLAY") local overlay = btn:CreateTexture(nil, "OVERLAY")
@@ -89,6 +108,7 @@ function MM:Create()
icon:SetPoint("TOPLEFT", btn, "TOPLEFT", 7, -6) icon:SetPoint("TOPLEFT", btn, "TOPLEFT", 7, -6)
btn.icon = icon btn.icon = icon
btn:SetScript("OnClick", function() btn:SetScript("OnClick", function()
if RA.UI and RA.UI.Toggle then RA.UI:Toggle() end if RA.UI and RA.UI.Toggle then RA.UI:Toggle() end
end) end)
@@ -107,6 +127,7 @@ function MM:Create()
GameTooltip:AddLine("Relationships Attune", 1, 0.82, 0) GameTooltip:AddLine("Relationships Attune", 1, 0.82, 0)
GameTooltip:AddLine("Left-click: open / close window", 0.9, 0.9, 0.9) GameTooltip:AddLine("Left-click: open / close window", 0.9, 0.9, 0.9)
GameTooltip:AddLine("Drag: move anywhere on screen", 0.9, 0.9, 0.9) GameTooltip:AddLine("Drag: move anywhere on screen", 0.9, 0.9, 0.9)
GameTooltip:AddLine("/attune offscreen - park off-screen (MBB safe)", 0.6, 0.8, 1)
GameTooltip:AddLine("/attune hide - hide this button", 0.6, 0.8, 1) GameTooltip:AddLine("/attune hide - hide this button", 0.6, 0.8, 1)
GameTooltip:Show() GameTooltip:Show()
end) end)
+1 -1
View File
@@ -1,4 +1,4 @@
<img src="https://cdn.discordapp.com/attachments/1158418666883395656/1526959667941474304/v.png?ex=6a58eb82&is=6a579a02&hm=dbc8525f4040f4cc7d276f22c3a8ecce16a4930bdf168676d7075e116097b8e2&"/> <img src="https://i.postimg.cc/rwVg8mmF/v.png"/>
# RelationshipsAttune # RelationshipsAttune
+1 -1
View File
@@ -2,7 +2,7 @@
## Title: Relationships Attune ## Title: Relationships Attune
## Notes: Track Vanilla and Turtle/Octo WoW attunement progress ## Notes: Track Vanilla and Turtle/Octo WoW attunement progress
## Author: Relationship ## Author: Relationship
## Version: 1.5 ## Version: 1.8
## X-Category: Quest ## X-Category: Quest
## SavedVariables: RelationshipsAttuneDB ## SavedVariables: RelationshipsAttuneDB
## SavedVariablesPerCharacter: RelationshipsAttuneCharDB ## SavedVariablesPerCharacter: RelationshipsAttuneCharDB
+86
View File
@@ -26,6 +26,55 @@ local C_TURTLE = { 0.55, 0.85, 1.00 }
local C_KEYS = { 0.95, 0.75, 0.35 } local C_KEYS = { 0.95, 0.75, 0.35 }
local C_INPROGRESS = { 1.00, 0.60, 0.15 } -- orange: quest accepted / step in progress local C_INPROGRESS = { 1.00, 0.60, 0.15 } -- orange: quest accepted / step in progress
-- ---------------------------------------------------------------
-- Force-complete confirmation dialogs (StaticPopup)
-- In vanilla 1.12, StaticPopup OnAccept is called as OnAccept(dialog.data),
-- so the data arrives as arg1 (Lua 5.0 vararg). `this` inside OnAccept is
-- NOT the dialog frame, so this.data is nil. We stash the data on the
-- dialog itself via OnShow (which does run with this = dialog) and read
-- it back from arg1 with a dialog fallback for safety.
-- ---------------------------------------------------------------
StaticPopupDialogs = StaticPopupDialogs or {}
local function RATTUNE_StashData()
-- OnShow runs with `this` = the dialog frame. StaticPopup_Show already
-- assigns dialog.data, but we snapshot it into an upvalue table so the
-- OnAccept closures can find it even on clients that don't forward arg1.
if this then RATTUNE_LastPopupData = this.data end
end
-- Attunement id awaiting the user's confirmation for a force / unforce.
-- StaticPopup OnAccept in vanilla 1.12 does not reliably receive dialog.data
-- as arg1, so we snapshot the id here at click time and read it back on accept.
RATTUNE_PendingForceAtt = nil
StaticPopupDialogs["RATTUNE_FORCE_ATT"] = {
text = "Force complete the entire \"%s\" attunement?\n\nEvery step will be marked as done for tracking. You can undo this later.",
button1 = "Force Complete",
button2 = "Cancel",
OnAccept = function()
local id = RATTUNE_PendingForceAtt
RATTUNE_PendingForceAtt = nil
if id then RA:SetAttForced(id, true) end
end,
OnCancel = function() RATTUNE_PendingForceAtt = nil end,
timeout = 0, whileDead = 1, hideOnEscape = 1, showAlert = 1,
}
StaticPopupDialogs["RATTUNE_UNFORCE_ATT"] = {
text = "Undo forced completion for \"%s\"?\n\nProgress will revert to real, scanned state.",
button1 = "Undo",
button2 = "Cancel",
OnAccept = function()
local id = RATTUNE_PendingForceAtt
RATTUNE_PendingForceAtt = nil
if id then RA:SetAttForced(id, false) end
end,
OnCancel = function() RATTUNE_PendingForceAtt = nil end,
timeout = 0, whileDead = 1, hideOnEscape = 1, showAlert = 1,
}
local selectedPlayer = nil local selectedPlayer = nil
local selectedRaid = nil local selectedRaid = nil
@@ -196,6 +245,29 @@ function UI:Build()
detail.category = fs(frame.rightPane, "", 10, 0.75, 0.75, 0.75) detail.category = fs(frame.rightPane, "", 10, 0.75, 0.75, 0.75)
detail.category:SetPoint("TOPLEFT", detail.name, "BOTTOMLEFT", 0, -2) detail.category:SetPoint("TOPLEFT", detail.name, "BOTTOMLEFT", 0, -2)
-- Force Complete (attunement-level) button. Sits in the top-right of the
-- right pane, hidden when viewing another player's data.
detail.forceBtn = CreateFrame("Button", nil, frame.rightPane, "UIPanelButtonTemplate")
detail.forceBtn:SetWidth(150); detail.forceBtn:SetHeight(20)
detail.forceBtn:SetPoint("TOPRIGHT", frame.rightPane, "TOPRIGHT", -4, -4)
detail.forceBtn:SetText("Force Complete")
detail.forceBtn:SetScript("OnClick", function()
local attId = selectedRaid
if not attId then return end
local att
local data = RelationshipsAttune_Data
for i = 1, table.getn(data) do
if data[i].id == attId then att = data[i]; break end
end
if not att then return end
RATTUNE_PendingForceAtt = attId
if RA:IsAttForced(attId) then
StaticPopup_Show("RATTUNE_UNFORCE_ATT", att.name)
else
StaticPopup_Show("RATTUNE_FORCE_ATT", att.name)
end
end)
-- Progress bar: give it explicit left+right anchors AND a border so it -- Progress bar: give it explicit left+right anchors AND a border so it
-- always has a resolvable width, then size the fill from that width. -- always has a resolvable width, then size the fill from that width.
detail.barBG = solid(frame.rightPane, "BORDER", 0, 0, 0, 0.90) detail.barBG = solid(frame.rightPane, "BORDER", 0, 0, 0, 0.90)
@@ -566,6 +638,20 @@ function UI:RenderDetail()
detail.barFill:SetVertexColor(c[1], c[2], c[3], 0.95) detail.barFill:SetVertexColor(c[1], c[2], c[3], 0.95)
detail.barText:SetText(done .. " / " .. total .. " (" .. math.floor(pct * 100 + 0.5) .. "%)") detail.barText:SetText(done .. " / " .. total .. " (" .. math.floor(pct * 100 + 0.5) .. "%)")
-- Update attunement-level Force button (only for your own data)
if detail.forceBtn then
if selectedPlayer ~= nil then
detail.forceBtn:Hide()
else
if RA:IsAttForced(att.id) then
detail.forceBtn:SetText("Undo Force Complete")
else
detail.forceBtn:SetText("Force Complete")
end
detail.forceBtn:Show()
end
end
-- Build applicable step list -- Build applicable step list
local applicable = {} local applicable = {}
for i = 1, table.getn(att.steps) do for i = 1, table.getn(att.steps) do