-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWeeklies.lua
More file actions
598 lines (519 loc) · 26.5 KB
/
Copy pathWeeklies.lua
File metadata and controls
598 lines (519 loc) · 26.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
setfenv(1, _G.SlackHacks)
local module = Self:NewModule("Weeklies", "AceEvent-3.0")
Self.Weeklies = module
-- Widget backing the "Gilded Stash" counter shown on the Delver's Journey (rank 4 reward). Widget ID and
-- required count confirmed against MidnightRoutine's Delves.lua, which reads the same live tooltip text.
-- Widget 7591 is the in-game Delver's Journey counter; its tooltip is the authoritative weekly progress source.
local GILDED_STASH_WIDGET_ID = 7591
local GILDED_STASH_REQUIRED = 4
local TROVEHUNTERS_BOUNTY_QUEST_ID = 86371
local TROVEHUNTERS_BOUNTY_ICON_ID = 1064187
local TROVEHUNTERS_BOUNTY_ITEM_ID = 274374
local TROVEHUNTERS_BOUNTY_ENGAGED_SPELL_ID = 1254631
local BOUNTY_STATUS_AVAILABLE = "Available"
local BOUNTY_STATUS_ACQUIRED = "Acquired"
local BOUNTY_STATUS_ENGAGED = "Engaged"
local BOUNTY_STATUS_COMPLETED = "Completed"
local DELVE_RENOWN_ICON_ID = 6025441
local VALEERA_ICON_ID = 236439
-- Midnight currency IDs let us query live amounts and caps even when a currency is not currently tracked.
local DELVE_CURRENCIES = {
{ name = "Restored Coffer Keys", id = 3028 },
{ name = "Coffer Key Shards", id = 3310 },
{ name = "Untainted Mana-Crystals", id = 3356 },
{ name = "Undercoin", id = 2803 },
}
-- Same door icons used for delve entrances on the world map (glowing = bountiful, plain = regular).
local DELVES_ICON_ATLAS_PENDING = "delves-bountiful"
local DELVES_ICON_ATLAS_DONE = "delves-regular"
local BUTTON_SIZE = 18
local TROVEHUNTERS_BOUNTY_ICON = "Interface\\Icons\\INV_Misc_Map_01"
-- The Gilded Stash reward icon already has its ornate gold border baked into the art; a circular mask
-- just crops the square texture down to that coin shape, used to render the 4 weekly stash slots.
local GILDED_STASH_ICON_ID = 5872049
local STASH_ICON_MASK_ATLAS = "CircleMaskScalable"
local STASH_ICON_SIZE = 14
local STASH_ICON_GAP = 2
local STASH_ICON_COUNT = 4
local button
local contextMenu
local contextMenuRows = {}
--- Returns the Delver's Journey faction ID for the current season, if available.
---@return number|nil factionID Seasonal major-faction ID, or nil when Delves data is unavailable.
local function delvesSeasonFactionID()
return C_DelvesUI and C_DelvesUI.GetDelvesFactionForSeason and C_DelvesUI.GetDelvesFactionForSeason()
end
--- Reads weekly Gilded Stash progress from the Delver's Journey widget.
--- Caches remaining stashes because Blizzard blanks the widget while the player is inside a Delve.
---@return table|nil info Contains `current`, `max`, and `completed`; nil when progress cannot be determined.
local function gildedStashInfo()
if C_DelvesUI and C_DelvesUI.HasActiveDelve and C_DelvesUI.HasActiveDelve() then
-- The widget goes blank while inside a delve, so fall back to the last value we saw outside one.
local remaining = db.char.cache.lastKnownGildedStashesRemaining
if remaining == nil then return nil end
local current = GILDED_STASH_REQUIRED - remaining
return { current = current, max = GILDED_STASH_REQUIRED, completed = remaining <= 0 }
end
if not C_UIWidgetManager or not C_UIWidgetManager.GetSpellDisplayVisualizationInfo then return nil end
local ok, widgetInfo = pcall(C_UIWidgetManager.GetSpellDisplayVisualizationInfo, GILDED_STASH_WIDGET_ID)
if not ok or not widgetInfo or not widgetInfo.spellInfo then return nil end
local tooltip = widgetInfo.spellInfo.tooltip
if type(tooltip) ~= "string" or tooltip == "" then return nil end
-- Prefer the number pair matching the known weekly cap, in case the tooltip mentions other counts too.
local current, max
for foundCurrent, foundMax in tooltip:gmatch("(%d+)%s*/%s*(%d+)") do
foundCurrent, foundMax = tonumber(foundCurrent), tonumber(foundMax)
if foundCurrent and foundMax == GILDED_STASH_REQUIRED then
current, max = foundCurrent, foundMax
break
elseif not current and foundCurrent and foundMax then
current, max = foundCurrent, foundMax
end
end
max = max or GILDED_STASH_REQUIRED
if current then
db.char.cache.lastKnownGildedStashesRemaining = max - current
end
return {
current = current,
max = max,
completed = current and current >= max
}
end
--- Checks whether this week's Trovehunter's Bounty quest reward has been claimed.
---@return boolean completed True when the weekly quest is flagged complete.
local function trovehuntersBountyCompleted()
return C_QuestLog.IsQuestFlaggedCompleted(TROVEHUNTERS_BOUNTY_QUEST_ID)
end
--- Determines this week's Trovehunter's Bounty progress.
--- Checked in priority order: a finished weekly quest always wins; the Engaged buff is looked up by
--- spell ID directly (a single hash-map read via GetPlayerAuraBySpellID, not a full aura-list scan),
--- so this stays cheap even though it can run every time the tracker refreshes; bag possession is the
--- cheapest remaining fallback.
---@return string status One of `BOUNTY_STATUS_AVAILABLE`, `BOUNTY_STATUS_ACQUIRED`, `BOUNTY_STATUS_ENGAGED`, or `BOUNTY_STATUS_COMPLETED`.
local function trovehuntersBountyStatus()
if trovehuntersBountyCompleted() then return BOUNTY_STATUS_COMPLETED end
if C_UnitAuras and C_UnitAuras.GetPlayerAuraBySpellID and C_UnitAuras.GetPlayerAuraBySpellID(TROVEHUNTERS_BOUNTY_ENGAGED_SPELL_ID) then
return BOUNTY_STATUS_ENGAGED
end
if C_Item and C_Item.GetItemCount and C_Item.GetItemCount(TROVEHUNTERS_BOUNTY_ITEM_ID) > 0 then
return BOUNTY_STATUS_ACQUIRED
end
return BOUNTY_STATUS_AVAILABLE
end
--- Looks up a currency by ID, falling back to the visible currency list for currencies without an ID.
--- Returns the full record as well as amount and icon so callers can display weekly and total caps.
---@param currencyName string Localized display name used by the currency-list fallback.
---@param currencyID number|nil Stable currency ID when known.
---@return number|nil quantity Current balance, if the currency is known.
---@return number|nil iconFileID Currency icon texture ID.
---@return table|nil info Full `CurrencyInfo` record, including cap fields.
local function currencyAmount(currencyName, currencyID)
if currencyID and C_CurrencyInfo and C_CurrencyInfo.GetCurrencyInfo then
local currency = C_CurrencyInfo.GetCurrencyInfo(currencyID)
if currency then return currency.quantity, currency.iconFileID, currency end
end
if not C_CurrencyInfo or not C_CurrencyInfo.GetCurrencyListSize or not C_CurrencyInfo.GetCurrencyListInfo then return nil end
for index = 1, C_CurrencyInfo.GetCurrencyListSize() do
local currency = C_CurrencyInfo.GetCurrencyListInfo(index)
if currency and currency.name == currencyName then return currency.quantity, currency.iconFileID, currency end
end
end
--- Adds a fixed-size inline texture before a tooltip label.
---@param label string Text displayed after the icon.
---@param icon number|string|nil Texture ID, atlas, or nil for a plain label.
---@return string text WoW tooltip markup and label text.
local function tooltipIconLabel(label, icon)
if not icon then return label end
return ("|T%s:14:14:0:0|t %s"):format(icon, label)
end
--- Reads the active Delve companion's Friendship-style reputation rank.
--- This is Valeera/Brann's personal level, distinct from seasonal Delver's Journey renown. Passing no
--- companion ID follows Blizzard's own configuration-frame behavior and resolves the active companion.
---@return table|nil info Contains `name`, `level`, and `maxLevel`; nil when reputation data is unavailable.
local function companionReputation()
if not C_DelvesUI or not C_DelvesUI.GetFactionForCompanion then return nil end
local ok, companionFactionID = pcall(C_DelvesUI.GetFactionForCompanion)
if not ok or not companionFactionID or companionFactionID == 0 then return nil end
if not C_GossipInfo or not C_GossipInfo.GetFriendshipReputationRanks then return nil end
local ok3, rankInfo = pcall(C_GossipInfo.GetFriendshipReputationRanks, companionFactionID)
if not ok3 or not rankInfo or not rankInfo.maxLevel then return nil end
local name
if C_Reputation and C_Reputation.GetFactionDataByID then
local ok4, factionData = pcall(C_Reputation.GetFactionDataByID, companionFactionID)
name = ok4 and factionData and factionData.name
end
return { name = name or "Valeera", level = rankInfo.currentLevel, maxLevel = rankInfo.maxLevel }
end
--- Reads the current season's Delver's Journey renown level and, when available, its cap.
---@return table|nil info Contains `level` and optional `maxLevel`; nil when seasonal renown is unavailable.
local function delveRenownLevel()
local seasonFactionID = delvesSeasonFactionID()
if not seasonFactionID or not C_MajorFactions then return nil end
local ok, info = pcall(C_MajorFactions.GetMajorFactionRenownInfo, seasonFactionID)
if not ok or not info or not info.renownLevel then return nil end
local maxLevel
if C_MajorFactions.GetRenownLevels then
local ok2, levels = pcall(C_MajorFactions.GetRenownLevels, seasonFactionID)
if ok2 and type(levels) == "table" and #levels > 0 then
maxLevel = #levels
end
end
return { level = info.renownLevel, maxLevel = maxLevel }
end
--- Creates one circular Gilded Stash texture used by the tooltip progress row.
---@param parent Frame Parent frame for the icon.
---@return Frame frame Icon frame exposing its masked texture as `frame.icon`.
local function createStashIcon(parent)
local frame = CreateFrame("Frame", nil, parent)
frame:SetSize(STASH_ICON_SIZE, STASH_ICON_SIZE)
local icon = frame:CreateTexture(nil, "ARTWORK")
icon:SetAllPoints()
icon:SetTexture(GILDED_STASH_ICON_ID)
local mask = frame:CreateMaskTexture(nil, "ARTWORK")
mask:SetAllPoints(icon)
mask:SetAtlas(STASH_ICON_MASK_ATLAS, false)
icon:AddMaskTexture(mask)
frame.icon = icon
return frame
end
--- Opens the Delver's Journey page in the Encounter Journal for the current seasonal faction.
local function openDelveRenownJourney()
local factionID = delvesSeasonFactionID()
if not factionID then return end
if C_AddOns and C_AddOns.LoadAddOn then C_AddOns.LoadAddOn("Blizzard_EncounterJournal") end
if EncounterJournal_OpenToJourney then EncounterJournal_OpenToJourney(factionID) end
end
--- Loads and displays Blizzard's companion configuration panel for the active companion.
local function openDelveCompanionPanel()
if C_AddOns and C_AddOns.LoadAddOn then C_AddOns.LoadAddOn("Blizzard_DelvesCompanionConfiguration") end
if DelvesCompanionConfigurationFrame then ShowUIPanel(DelvesCompanionConfigurationFrame) end
end
-- Toy actions use collection item IDs and the secure `toy` action type; direct Lua casts are protected.
-- L00T RAID-R Mini is a consumable item, so it uses the secure `item` action type instead.
local CONTEXT_MENU_OPTIONS = {
{ name = "Open Delve Renown Journey", icon = DELVE_RENOWN_ICON_ID, action = openDelveRenownJourney },
{ name = "See Valeera Sanguinar's Loadout", icon = VALEERA_ICON_ID, action = openDelveCompanionPanel },
{ name = "Use Coffer Key Glue", toyID = 267291 },
{ name = "Use Delve-O-Bot 7001", toyID = 230850 },
{ name = "Use L00T RAID-R Mini", itemID = 244193 },
}
--- Resolves a menu option's texture from its explicit icon, Toy Box entry, spell, or item.
---@param option table Context-menu option containing an icon or toy/spell/item ID.
---@return number|string|nil icon Texture ID or atlas, if available.
local function contextMenuIcon(option)
if option.icon then return option.icon end
if option.toyID and C_ToyBox and C_ToyBox.GetToyInfo then
local _, _, icon = C_ToyBox.GetToyInfo(option.toyID)
if icon then return icon end
end
if option.spellID and C_Spell and C_Spell.GetSpellTexture then return C_Spell.GetSpellTexture(option.spellID) end
if option.itemID and C_Item and C_Item.GetItemIconByID then return C_Item.GetItemIconByID(option.itemID) end
end
--- Hides the custom context menu if it has been created.
local function closeContextMenu()
if contextMenu then contextMenu:Hide() end
end
--- Displays the matching item, toy, or spell tooltip for a hovered action row.
---@param row Button Context-menu row carrying itemID, toyID, or spellID.
local function onContextMenuRowEnter(row)
GameTooltip:SetOwner(row, "ANCHOR_RIGHT")
if row.toyID then
GameTooltip:SetToyByItemID(row.toyID)
elseif row.itemID then
GameTooltip:SetItemByID(row.itemID)
elseif row.spellID then
GameTooltip:SetSpellByID(row.spellID)
else
GameTooltip_Hide()
return
end
GameTooltip:Show()
end
--- Closes the action menu after a secure row completes its click action.
---@param row Button Clicked row; `action` is an optional non-secure menu action.
local function onContextMenuRowPostClick(row)
closeContextMenu()
if row.action then row.action() end
end
--- Handles Blizzard's global mouse-down event by dismissing clicks outside the menu and its owner.
local function onGlobalMouseDown()
if contextMenu:IsShown() and not contextMenu:IsMouseOver() and not (contextMenu.anchor and contextMenu.anchor:IsMouseOver()) then
closeContextMenu()
end
end
--- Creates one reusable secure menu row that can activate a toy or item on hardware click.
--- Unlike a Blizzard Menu compositor row, this row itself owns the secure action attributes, matching
--- the working secure-button pattern used by Buffs.lua.
---@param index number Stable row index used to name the frame.
---@return Button row Secure action button with icon, label, and tooltip handlers.
local function createContextMenuRow(index)
local row = CreateFrame("Button", "SlackHacksDelvesContextMenuRow" .. index, contextMenu, "BackdropTemplate, SecureActionButtonTemplate")
row:SetHeight(24)
row:RegisterForClicks("AnyUp", "AnyDown")
local highlight = row:CreateTexture(nil, "HIGHLIGHT")
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetPoint("TOPLEFT", row, "TOPLEFT", 2, -1)
highlight:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -2, 1)
highlight:SetVertexColor(1, 1, 1, 0.35)
highlight:SetBlendMode("ADD")
local icon = row:CreateTexture(nil, "ARTWORK")
icon:SetSize(20, 20)
icon:SetPoint("LEFT", row, "LEFT", 4, 0)
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
row.icon = icon
local name = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
name:SetPoint("LEFT", icon, "RIGHT", 6, 0)
name:SetPoint("RIGHT", row, "RIGHT", -6, 0)
name:SetJustifyH("LEFT")
row.name = name
-- The secure row itself receives the click; tooltip callbacks never execute protected actions.
row:SetScript("OnEnter", onContextMenuRowEnter)
row:SetScript("OnLeave", GameTooltip_Hide)
row:SetScript("PostClick", onContextMenuRowPostClick)
return row
end
--- Toggles the Delves action menu below the tracker icon.
--- Uses reusable secure rows because protected toy/item actions must receive the actual hardware click.
---@param anchor Button Tracker button used to position the menu and exempt it from outside-click dismissal.
local function openContextMenu(anchor)
if InCombatLockdown() then return end
GameTooltip_Hide()
if not contextMenu then
contextMenu = CreateFrame("Frame", "SlackHacksDelvesContextMenu", UIParent, "BackdropTemplate")
contextMenu:SetFrameStrata("DIALOG")
contextMenu:SetClampedToScreen(true)
contextMenu:EnableMouse(true)
local background = contextMenu:CreateTexture(nil, "BACKGROUND")
background:SetAtlas("common-dropdown-c-bg")
background:SetPoint("TOPLEFT", -17, 12)
background:SetPoint("BOTTOMRIGHT", 17, -22)
-- Blizzard's global mouse event restores normal outside-click dismissal without polling mouse position.
EventRegistry:RegisterFrameEventAndCallback("GLOBAL_MOUSE_DOWN", onGlobalMouseDown)
end
if contextMenu:IsShown() then
closeContextMenu()
return
end
local rowHeight = 24
local rowGap = 2
local padding = 6
local menuWidth = 240
for index, option in ipairs(CONTEXT_MENU_OPTIONS) do
local row = contextMenuRows[index] or createContextMenuRow(index)
contextMenuRows[index] = row
row:SetSize(menuWidth - (padding * 2), rowHeight)
row:ClearAllPoints()
row:SetPoint("TOPLEFT", contextMenu, "TOPLEFT", padding, -padding - (index - 1) * (rowHeight + rowGap))
row.itemID = option.itemID
row.spellID = option.spellID
row.toyID = option.toyID
row.action = option.action
row:SetAttribute("type", option.toyID and "toy" or option.spellID and "spell" or option.itemID and "item" or nil)
row:SetAttribute("toy", option.toyID)
row:SetAttribute("spell", option.spellID)
row:SetAttribute("item", option.itemID and "item:" .. option.itemID or nil)
row.icon:SetTexture(contextMenuIcon(option))
row.name:SetText(option.name)
row:Show()
end
contextMenu:SetSize(menuWidth, (rowHeight + rowGap) * #CONTEXT_MENU_OPTIONS + padding * 2 - rowGap)
contextMenu:ClearAllPoints()
contextMenu.anchor = anchor
contextMenu:SetPoint("TOPRIGHT", anchor, "BOTTOMRIGHT", 0, -2)
contextMenu:Show()
end
--- Lazily creates the Objective Tracker button and wires its tooltip and context-menu interactions.
local function createButton()
if button then return end
if InCombatLockdown() then return end
local header = ObjectiveTrackerFrame and ObjectiveTrackerFrame.Header
local anchor = header and header.MinimizeButton
if not anchor then return end
button = CreateFrame("Button", "SlackHacksDelvesTrackerButton", header)
button:SetSize(BUTTON_SIZE, BUTTON_SIZE)
button:SetPoint("RIGHT", anchor, "LEFT", -4, 0)
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
local icon = button:CreateTexture(nil, "ARTWORK")
icon:SetAllPoints()
icon:SetAtlas(DELVES_ICON_ATLAS_PENDING, false)
button.icon = icon
local count = button:CreateFontString(nil, "OVERLAY", "NumberFontNormalSmall")
count:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 2, -2)
button.count = count
local bounty = button:CreateTexture(nil, "OVERLAY")
bounty:SetSize(10, 10)
bounty:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", -2, -2)
bounty:SetTexture(TROVEHUNTERS_BOUNTY_ICON)
bounty:Hide()
button.bounty = bounty
button:SetScript("OnEnter", module.ShowTooltip)
button:SetScript("OnLeave", GameTooltip_Hide)
button:SetScript("OnClick", openContextMenu)
button:Hide()
end
-- Created lazily and parented directly to GameTooltip so it hides along with it; anchored over the
-- right column of the "Gilded Stashes Remaining" double line since GameTooltip has no native slot for
-- custom textures within a line.
local tooltipStashIcons
--- Lazily creates the four stash-slot icons over the tooltip's right-aligned progress value.
--- The tooltip-cleared hook prevents these custom textures leaking onto unrelated GameTooltip content.
---@return Frame container Container with four child stash-icon frames.
local function getTooltipStashIcons()
if tooltipStashIcons then return tooltipStashIcons end
local container = CreateFrame("Frame", nil, GameTooltip)
container:SetSize(STASH_ICON_COUNT * STASH_ICON_SIZE + (STASH_ICON_COUNT - 1) * STASH_ICON_GAP, STASH_ICON_SIZE)
local icons = {}
local previous
for index = 1, STASH_ICON_COUNT do
local stashIcon = createStashIcon(container)
if previous then
stashIcon:SetPoint("LEFT", previous, "RIGHT", STASH_ICON_GAP, 0)
else
stashIcon:SetPoint("LEFT", container, "LEFT", 0, 0)
end
icons[index] = stashIcon
previous = stashIcon
end
container.icons = icons
-- GameTooltip is shared by every tooltip in the game; without this the icons would keep showing
-- (still parented/anchored from our last use) whenever the tooltip is reused for something else.
--- Hides the stash-slot overlay when the shared tooltip is cleared or reused.
local function onTooltipCleared()
container:Hide()
end
GameTooltip:HookScript("OnTooltipCleared", onTooltipCleared)
tooltipStashIcons = container
return container
end
--- Populates the tracker tooltip with weekly objectives, currencies, companion rank, and seasonal renown.
--- Stash icons are positioned after `GameTooltip:Show()` because custom textures cannot be inserted into a line.
---@param self Button Tracker button owning the tooltip.
function module.ShowTooltip(self)
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
GameTooltip:SetText("Delves", 1, 1, 1)
-- Leading spaces on the right column widen the tooltip without shifting the right-justified text,
-- which opens up a gap between the two columns.
local COLUMN_GAP = " "
local stash = gildedStashInfo()
local iconLineIndex
if stash and stash.current and stash.max then
GameTooltip:AddDoubleLine(tooltipIconLabel("Gilded Stashes Remaining", GILDED_STASH_ICON_ID), COLUMN_GAP .. " ", 1, 0.82, 0, 1, 0.82, 0)
iconLineIndex = GameTooltip:NumLines()
else
GameTooltip:AddDoubleLine(tooltipIconLabel("Gilded Stashes Remaining", GILDED_STASH_ICON_ID), COLUMN_GAP .. "unavailable", 1, 0.82, 0, 0.6, 0.6, 0.6)
end
GameTooltip:AddDoubleLine(tooltipIconLabel("Trovehunter's Bounty", TROVEHUNTERS_BOUNTY_ICON_ID), COLUMN_GAP .. trovehuntersBountyStatus(), 1, 0.82, 0, 1, 0.82, 0)
for _, currency in ipairs(DELVE_CURRENCIES) do
local amount, icon, info = currencyAmount(currency.name, currency.id)
local displayAmount = amount or "Unknown"
-- These currencies expose explicit weekly and total caps in CurrencyInfo; other currencies show balance only.
if currency.id == 3356 and info then
displayAmount = info.quantityEarnedThisWeek .. " / " .. info.maxWeeklyQuantity .. " (" .. info.quantity .. " / " .. info.maxQuantity .. ")"
elseif currency.id == 3310 and info then
displayAmount = info.quantity .. " / " .. info.maxWeeklyQuantity
end
GameTooltip:AddDoubleLine(tooltipIconLabel(currency.name, icon), COLUMN_GAP .. displayAmount, 1, 0.82, 0, 1, 0.82, 0)
end
local companion = companionReputation()
if companion then
GameTooltip:AddDoubleLine(tooltipIconLabel(companion.name or "Valeera", VALEERA_ICON_ID), COLUMN_GAP .. companion.level .. " / " .. companion.maxLevel, 1, 0.82, 0, 1, 0.82, 0)
else
GameTooltip:AddDoubleLine(tooltipIconLabel("Valeera", VALEERA_ICON_ID), COLUMN_GAP .. "Unknown", 1, 0.82, 0, 1, 0.82, 0)
end
local renown = delveRenownLevel()
if renown and renown.maxLevel then
GameTooltip:AddDoubleLine(tooltipIconLabel("Delve Renown", DELVE_RENOWN_ICON_ID), COLUMN_GAP .. renown.level .. " / " .. renown.maxLevel, 1, 0.82, 0, 1, 0.82, 0)
elseif renown then
GameTooltip:AddDoubleLine(tooltipIconLabel("Delve Renown", DELVE_RENOWN_ICON_ID), COLUMN_GAP .. "Level " .. renown.level, 1, 0.82, 0, 1, 0.82, 0)
else
GameTooltip:AddDoubleLine(tooltipIconLabel("Delve Renown", DELVE_RENOWN_ICON_ID), COLUMN_GAP .. "Unknown", 1, 0.82, 0, 1, 0.82, 0)
end
GameTooltip:Show()
if iconLineIndex then
local container = getTooltipStashIcons()
local lineFrame = _G["GameTooltipTextRight" .. iconLineIndex]
container:ClearAllPoints()
container:SetPoint("RIGHT", lineFrame, "RIGHT", 0, 0)
container:Show()
for index, stashIcon in ipairs(container.icons) do
local isClaimed = index <= stash.current
stashIcon.icon:SetDesaturated(isClaimed)
stashIcon.icon:SetVertexColor(isClaimed and 0.4 or 1, isClaimed and 0.4 or 1, isClaimed and 0.4 or 1)
end
elseif tooltipStashIcons then
tooltipStashIcons:Hide()
end
end
--- Creates/updates the tracker button and hides it when weekly tracking is disabled.
--- The door atlas only goes dark (the plain, non-bountiful world-map door) once BOTH weekly Delve
--- tasks are done; the map marker independently reflects Trovehunter's Bounty status.
function module:Refresh()
if InCombatLockdown() then return end
createButton()
if not button then return end
local shouldShow = db.profile.weeklies.enabled and db.profile.weeklies.trackDelves
if not shouldShow then
button:Hide()
return
end
local stash = gildedStashInfo()
local bountyStatus = trovehuntersBountyStatus()
local allTasksDone = stash and stash.completed and bountyStatus == BOUNTY_STATUS_COMPLETED
button.icon:SetAtlas(allTasksDone and DELVES_ICON_ATLAS_DONE or DELVES_ICON_ATLAS_PENDING, false)
if bountyStatus == BOUNTY_STATUS_COMPLETED then button.bounty:Hide() else button.bounty:Show() end
if stash and stash.completed then
button.count:Hide()
else
if stash and stash.current and stash.max then
button.count:SetText(stash.max - stash.current)
button.count:Show()
else
button.count:Hide()
end
end
button:Show()
end
--- Updates the global Weeklies module setting and enables or disables its event handlers.
---@param enabled boolean Whether weekly tracking is enabled.
function module:SetEnabled(enabled)
db.profile.weeklies.enabled = enabled
if enabled then self:Enable() else self:Disable() end
end
--- Updates the Delves tracker preference and immediately refreshes its button.
---@param enabled boolean Whether the Delves tracker should be shown.
function module:SetTrackDelves(enabled)
db.profile.weeklies.trackDelves = enabled
self:Refresh()
end
--- AceAddon initialization hook; creates the button when its Objective Tracker anchor exists.
function module:OnInitialize()
createButton()
if not db.profile.weeklies.enabled then self:Disable() end
end
--- Registers events that can change stash progress or bounty status, then performs an initial refresh.
--- BAG_UPDATE_DELAYED (fired once per batch of bag changes, not per slot) catches the Acquired status
--- picking up/losing the Trovehunter's Bounty item without registering a noisier per-slot bag event.
function module:OnEnable()
self:RegisterEvent("PLAYER_ENTERING_WORLD", "Refresh")
self:RegisterEvent("UPDATE_UI_WIDGET", "Refresh")
self:RegisterEvent("QUEST_LOG_UPDATE", "Refresh")
self:RegisterEvent("BAG_UPDATE_DELAYED", "Refresh")
self:RegisterEvent("PLAYER_REGEN_ENABLED", "Refresh")
self:Refresh()
end
--- Hides the tracker button after combat when the module was disabled during lockdown.
function module:HideButtonAfterCombat()
self:UnregisterEvent("PLAYER_REGEN_ENABLED")
if button then button:Hide() end
end
--- Unregisters module events and hides the tracker button when the module is disabled.
function module:OnDisable()
self:UnregisterAllEvents()
if not button then return end
if InCombatLockdown() then
self:RegisterEvent("PLAYER_REGEN_ENABLED", "HideButtonAfterCombat")
else
button:Hide()
end
end