Repository navigation
Expand file tree
/
Copy pathUI.lua
More file actions
641 lines (559 loc) · 23.4 KB
/
Copy pathUI.lua
File metadata and controls
641 lines (559 loc) · 23.4 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
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
-- Compact run-status HUD. Auto-shows when a run is active, hides when not.
-- Click to toggle the Softcore menu. Drag to reposition. /sc hud to show/hide.
local SC = Softcore
local MINIMAP_LOGO_TEXTURE = "Interface\\AddOns\\Softcore\\Assets\\SoftcoreLogoMinimap"
local function SavePosition(key, frame)
SoftcoreDB = SoftcoreDB or {}
SoftcoreDB.ui = SoftcoreDB.ui or {}
SoftcoreDB.ui[key] = { x = frame:GetLeft(), y = frame:GetTop() }
end
local function RestorePosition(key, frame, defaultX, defaultY)
local pos = SoftcoreDB and SoftcoreDB.ui and SoftcoreDB.ui[key]
frame:ClearAllPoints()
if pos and pos.x and pos.y then
frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", pos.x, pos.y)
else
frame:SetPoint("CENTER", UIParent, "CENTER", defaultX or 0, defaultY or 0)
end
end
-- ── HUD ──────────────────────────────────────────────────────────────────────
local HUD_W = 118
local HUD_H = 26
local HUD_PAD = 8
local DOT_SIZE = 10
local NO_ADDON_GRACE_SECONDS = 30
local LAMP_COLOR = {
GREEN = {0.22, 0.95, 0.42},
BLUE = {0.26, 0.58, 1.00},
YELLOW = {1.00, 0.78, 0.16},
ORANGE = {1.00, 0.42, 0.12},
RED = {1.00, 0.18, 0.14},
GRAY = {0.48, 0.50, 0.54},
}
local TEXT_COLOR = {
GREEN = {0.62, 1.00, 0.68},
BLUE = {0.58, 0.78, 1.00},
YELLOW = {1.00, 0.86, 0.32},
ORANGE = {1.00, 0.62, 0.28},
RED = {1.00, 0.38, 0.32},
GRAY = {0.66, 0.68, 0.72},
}
local function SetLamp(frame, state)
local c = LAMP_COLOR[state] or LAMP_COLOR.GRAY
frame.lamp:SetColorTexture(c[1], c[2], c[3], 1)
frame.lampGlow:SetVertexColor(c[1], c[2], c[3], 0.55)
end
local function LocalActiveViolations()
local db = SC.db or SoftcoreDB
if not db or not db.violations then return 0 end
local playerKey = SC:GetPlayerKey()
local n = 0
for _, v in ipairs(db.violations) do
if v.playerKey == playerKey and v.status ~= "CLEARED" then n = n + 1 end
end
return n
end
local function LocalHasDeathViolation()
local db = SC.db or SoftcoreDB
if not db or not db.violations then return false end
local playerKey = SC:GetPlayerKey()
for _, v in ipairs(db.violations) do
if v.playerKey == playerKey and v.type == "death" and v.status ~= "CLEARED" then
return true
end
end
return false
end
local function HasRemoteFailure(syncRows)
local db = SC.db or SoftcoreDB
local run = db and db.run or {}
local localKey = SC:GetPlayerKey()
local ruleset = run and run.ruleset or {}
local allowsUnsyncedParty = ruleset
and ruleset.groupingMode ~= "SOLO_SELF_FOUND"
and (ruleset.unsyncedMembers == "ALLOWED" or ruleset.unsyncedMembers == "LOG_ONLY")
and (ruleset.instanceWithUnsyncedPlayers == "ALLOWED" or ruleset.instanceWithUnsyncedPlayers == "LOG_ONLY")
for playerKey, participant in pairs(db and db.run and db.run.participants or {}) do
if playerKey ~= localKey and participant.status == "FAILED" then
if not SC.IsParticipantInCurrentParty or SC:IsParticipantInCurrentParty(playerKey) then
return true
end
end
end
for _, peer in ipairs(syncRows or {}) do
if peer.participantStatus == "FAILED" or peer.failed or peer.valid == false then
local sameRunPeer = run.runId and peer.runId and run.runId == peer.runId
if sameRunPeer or not allowsUnsyncedParty then
return true
end
end
end
return false
end
local function IsCurrentPartyMember(playerKey)
if not playerKey or playerKey == "" then
return false
end
return not SC.IsParticipantInCurrentParty or SC:IsParticipantInCurrentParty(playerKey)
end
local function AllowsUnsyncedPartyMembers(run)
local ruleset = run and run.ruleset
if SC.AllowsUnsyncedPartyMembers then
return SC:AllowsUnsyncedPartyMembers(ruleset)
end
return ruleset
and ruleset.groupingMode ~= "SOLO_SELF_FOUND"
and (ruleset.unsyncedMembers == "ALLOWED" or ruleset.unsyncedMembers == "LOG_ONLY")
and (ruleset.instanceWithUnsyncedPlayers == "ALLOWED" or ruleset.instanceWithUnsyncedPlayers == "LOG_ONLY")
end
local function ConflictHUDLabel(conflictType)
if conflictType == "ADDON_VERSION_MISMATCH" then
return "Version"
elseif conflictType == "RULESET_MISMATCH" then
return "Rules"
elseif conflictType == "RUN_MISMATCH" then
return "Run ID"
end
return "Conflict"
end
local function IsActiveRunParticipant(run, playerKey)
local p = run and run.participants and run.participants[playerKey]
return p and p.status ~= "OUT_OF_PARTY" and p.status ~= "FAILED" and p.status ~= "RETIRED"
end
local function ParticipantHUDLabel(status)
if status == "ADDON_VERSION_MISMATCH" or status == "VERSION_MISMATCH" then
return "Version"
elseif status == "RULESET_MISMATCH" then
return "Rules"
elseif status == "RUN_MISMATCH" then
return "Run ID"
elseif status == "NOT_IN_RUN" then
return "Not In Run"
elseif status == "PENDING" or status == "UNSYNCED" then
return "Unsynced"
elseif status == "WARNING" then
return "Warning"
elseif status == "FAILED" then
return "Party Fail"
end
return nil
end
local function GetPartyEdgeHUDState(db, syncRows, partyStatus)
local run = db and db.run or {}
if not run.active then
return nil
end
if partyStatus == "RAID_UNSUPPORTED" or IsInRaid() then
return "YELLOW", "Raid Local", "RUN"
end
if run.levelGapBlocked then
return "YELLOW", "Level Gap", "OVERVIEW"
end
local allowsUnsyncedParty = AllowsUnsyncedPartyMembers(run)
if not allowsUnsyncedParty then
for _, conflict in pairs(run.conflicts or {}) do
if conflict.active and not conflict.dismissed and IsCurrentPartyMember(conflict.playerKey) then
return "YELLOW", ConflictHUDLabel(conflict.type), "RUN"
end
end
end
for playerKey, participant in pairs(run.participants or {}) do
if playerKey ~= SC:GetPlayerKey() and IsCurrentPartyMember(playerKey) then
local label = ParticipantHUDLabel(participant.status)
if label then
if participant.status == "FAILED" or not allowsUnsyncedParty then
local color = participant.status == "FAILED" and "ORANGE" or "YELLOW"
return color, label, label == "Party Fail" and "OVERVIEW" or "RUN"
end
end
end
end
local now = time()
for _, peer in ipairs(syncRows or {}) do
if IsCurrentPartyMember(peer.playerKey) then
local status = tostring(peer.participantStatus or "")
local hasNeverSeenAddon = not peer.lastSeen or peer.lastSeen <= 0
if peer.participantStatus == "FAILED" or peer.failed then
local sameRunPeer = run.runId and peer.runId and run.runId == peer.runId
if allowsUnsyncedParty and not sameRunPeer then
-- Failed state from a different unsynced run is diagnostic only.
status = ""
else
return "ORANGE", "Party Fail", "OVERVIEW"
end
elseif allowsUnsyncedParty then
-- Mixed-party play is allowed; keep HUD quiet unless a real failed member is reported.
status = ""
elseif hasNeverSeenAddon then
local rosterAge = now - (tonumber(peer.rosterSeen) or now)
if rosterAge >= NO_ADDON_GRACE_SECONDS then
local label = IsActiveRunParticipant(run, peer.playerKey) and "Run: No Addon" or "No Addon"
return "YELLOW", label, "RUN"
end
return "BLUE", "Syncing", "RUN"
elseif peer.unsynced then
local label = IsActiveRunParticipant(run, peer.playerKey) and "Run: Offline" or "Offline"
return "YELLOW", label, "RUN"
elseif peer.addonVersion and peer.addonVersion ~= SC.version then
return "YELLOW", "Version", "RUN"
elseif peer.active == false then
return "YELLOW", "Not In Run", "RUN"
end
local label = ParticipantHUDLabel(status)
if label then
local color = status == "FAILED" and "ORANGE" or "YELLOW"
return color, label, label == "Party Fail" and "OVERVIEW" or "RUN"
end
end
end
if partyStatus == "BLOCKED" then
return "YELLOW", "Blocked", "OVERVIEW"
elseif partyStatus == "CONFLICT" then
return "YELLOW", "Conflict", "RUN"
elseif partyStatus == "UNSYNCED" then
return "YELLOW", "Unsynced", "RUN"
end
return nil
end
local function GetPendingRuleAmendment(db)
for _, amendment in ipairs(db and db.ruleAmendments or {}) do
if amendment.status == "PENDING" or amendment.status == "ACCEPTED" then
return amendment
end
end
return nil
end
local function HasRecentRuleGovernance(db)
if not IsInGroup() or IsInRaid() then
return false
end
local now = time()
for _, amendment in ipairs(db and db.ruleAmendments or {}) do
local settledAt = amendment.appliedAt or amendment.declinedAt or amendment.expiredAt or amendment.noChangesAt
if settledAt and now - tonumber(settledAt) <= 12 then
return true
end
end
return false
end
local function GetGovernanceHUDState(db, syncRows, partyStatus)
local proposal = SC.GetPendingProposal and SC:GetPendingProposal() or nil
if proposal then
local localKey = SC:GetPlayerKey()
local isProposer = proposal.proposedBy == localKey
if proposal.detailsPending then
return "YELLOW", "Details", "RUN"
elseif proposal.status == "ACCEPTED" then
return "YELLOW", "Waiting", "RUN"
elseif isProposer then
return "YELLOW", "Waiting", "RUN"
elseif proposal.proposalType == "ADD_PARTICIPANT" then
return "YELLOW", "Invite", "RUN"
elseif proposal.proposalType == "SYNC_RUN" then
return "YELLOW", "Run Sync", "RUN"
end
return "YELLOW", "Review", "RUN"
end
local amendment = GetPendingRuleAmendment(db)
if amendment then
if amendment.detailsPending then
return "YELLOW", "Details", "RUN"
elseif amendment.status == "ACCEPTED" then
return "BLUE", "Settling", "RUN"
elseif amendment.proposedBy == SC:GetPlayerKey() then
return "YELLOW", "Waiting", "RUN"
end
return "YELLOW", "Rule Review", "RUN"
end
if HasRecentRuleGovernance(db) then
return "BLUE", "Settling", "RUN"
end
local plan = db and db.partySyncPlan
if plan and plan.active and plan.owner == SC:GetPlayerKey() then
local route = SC.GetPartySyncAction and SC:GetPartySyncAction(true) or nil
if route then
if route.action == "RESYNC" then
return "BLUE", "Syncing", "RUN"
elseif route.action == "AMEND_RULES" then
return "YELLOW", "Rules", "RUN"
elseif route.action == "SYNC_RUN" then
return "YELLOW", "Run Sync", "RUN"
elseif route.action == "INVITE" then
return "YELLOW", "Invite", "RUN"
elseif route.action == "BLOCKED" then
if route.blockedCode == "MAX_LEVEL" then
return "YELLOW", "Max Level", "RUN"
end
local edgeState, edgeText, edgeTab = GetPartyEdgeHUDState(db, syncRows, partyStatus)
if edgeState then
return edgeState, edgeText, edgeTab
end
return "YELLOW", "Blocked", "RUN"
elseif route.action == "NONE" then
return "GREEN", "Synced", "OVERVIEW"
end
end
return "BLUE", "Syncing", "RUN"
end
return nil
end
local function GetHUDState(status, violations, syncRows)
local db = SC.db or SoftcoreDB
local localStatus = status.participantStatus or "NOT_IN_RUN"
local partyStatus = status.partyStatus or localStatus
if localStatus == "FAILED" or status.failed or status.valid == false then
return "RED", "Failed", "OVERVIEW"
end
if violations > 0 or localStatus == "WARNING" or localStatus == "VIOLATION" then
if LocalHasDeathViolation() then
return "RED", "Died", "VIOLATIONS"
end
local text = violations == 1 and "1 Violation" or tostring(violations) .. " Violations"
if violations <= 0 then text = "Violation" end
return "YELLOW", text, "VIOLATIONS"
end
local governanceState, governanceText, governanceTab = GetGovernanceHUDState(db, syncRows, partyStatus)
if governanceState then
return governanceState, governanceText, governanceTab
end
local edgeState, edgeText, edgeTab = GetPartyEdgeHUDState(db, syncRows, partyStatus)
if edgeState then
return edgeState, edgeText, edgeTab
end
if HasRemoteFailure(syncRows) then
return "ORANGE", "Party Fail", "OVERVIEW"
end
if partyStatus == "VIOLATION" or partyStatus == "WARNING" then
return "YELLOW", "Warning", "OVERVIEW"
elseif localStatus == "PENDING" then
return "YELLOW", "Pending", "RUN"
elseif localStatus == "NOT_IN_RUN" then
return "GRAY", "No Run", "RUN"
end
return "GREEN", "Valid", "OVERVIEW"
end
local HUD_HINTS = {
["Valid"] = "Your run is active and all party members are in sync.",
["Synced"] = "Your run is active and all party members are in sync.",
["No Run"] = "You don't have an active run.",
["Pending"] = "Waiting to sync with the party\226\128\166",
["Failed"] = "Your run has ended.",
["Died"] = "You have died. Check the Violations tab for details.",
["Party Fail"] = "A party member's run has failed.",
["Warning"] = "A party member has active violations. Check the Run tab.",
["Blocked"] = "Party progress is blocked. Check the Run tab.",
["Conflict"] = "A run conflict exists. Use Party Sync to resolve it.",
["Syncing"] = "Waiting for a party member to respond\226\128\166",
["Unsynced"] = "A party member's state is stale. Use Party Sync to refresh.",
["No Addon"] = "A party member doesn't have the Softcore addon. Ask them to install or enable it.",
["Run: No Addon"] = "A run partner doesn't have the Softcore addon enabled. Ask them to enable it.",
["Offline"] = "A party member went quiet. Use Party Sync to request fresh state.",
["Run: Offline"] = "A run partner's addon went quiet. Use Party Sync to request fresh state.",
["Not In Run"] = "A party member isn't on a run. Use Party Sync to invite them.",
["Run ID"] = "A party member is on a different run. Use Party Sync to align run IDs.",
["Rules"] = "A party member has a different ruleset. Use Party Sync to align rules.",
["Version"] = "A party member has a different addon version. They need to update Softcore.",
["Max Level"] = "Max-level characters cannot start or join leveling runs.",
["Level Gap"] = "The party level gap is too large to continue. Check the Run tab.",
["Raid Local"] = "You're in a raid. Runs are tracked individually in raids.",
}
local function GetHUDHint(text)
if not text then return nil end
local hint = HUD_HINTS[text]
if hint then return hint end
if string.find(text, "Violation") then
return "You have active violations. Check the Violations tab."
end
return nil
end
function SC:HUD_Create()
if self.hudFrame then
self:HUD_Refresh()
return
end
local frame = CreateFrame("Button", "SoftcoreHUDFrame", UIParent, "BackdropTemplate")
frame:SetSize(HUD_W, HUD_H)
RestorePosition("hud", frame, 0, 150)
frame:SetMovable(true)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", frame.StartMoving)
frame:SetScript("OnDragStop", function(f)
f:StopMovingOrSizing()
SavePosition("hud", f)
end)
frame:RegisterForClicks("LeftButtonUp")
frame:SetScript("OnClick", function()
if SC.ToggleMasterWindow then
SC:ToggleMasterWindow(frame.focusTab or "OVERVIEW")
elseif SC.OpenMasterWindow then
SC:OpenMasterWindow(frame.focusTab or "OVERVIEW")
end
end)
frame:SetBackdrop({
bgFile = "Interface\\Buttons\\WHITE8X8",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = false, edgeSize = 12,
insets = { left = 3, right = 3, top = 3, bottom = 3 },
})
frame:SetBackdropColor(0.055, 0.032, 0.014, 0.94)
frame:SetBackdropBorderColor(0.78, 0.56, 0.24, 0.92)
frame:Hide()
local shine = frame:CreateTexture(nil, "BACKGROUND")
shine:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
shine:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 4)
shine:SetColorTexture(0.85, 0.58, 0.18, 0.08)
frame.lampGlow = frame:CreateTexture(nil, "ARTWORK")
frame.lampGlow:SetTexture("Interface\\GLUES\\Models\\UI_Draenei\\GenericGlow64")
frame.lampGlow:SetBlendMode("ADD")
frame.lampGlow:SetSize(22, 22)
frame.lampGlow:SetPoint("LEFT", frame, "LEFT", 2, 0)
frame.lamp = frame:CreateTexture(nil, "OVERLAY")
frame.lamp:SetSize(DOT_SIZE, DOT_SIZE)
frame.lamp:SetPoint("LEFT", frame, "LEFT", HUD_PAD, 0)
local mask = frame:CreateMaskTexture()
mask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMaskSmall",
"CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
mask:SetAllPoints(frame.lamp)
frame.lamp:AddMaskTexture(mask)
frame.statusText = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
frame.statusText:SetPoint("LEFT", frame.lamp, "RIGHT", 8, 0)
frame.statusText:SetSize(HUD_W - 34, 18)
frame.statusText:SetJustifyH("LEFT")
frame.statusText:SetJustifyV("MIDDLE")
frame.statusText:SetMaxLines(1)
frame:SetScript("OnEnter", function()
GameTooltip:SetOwner(frame, "ANCHOR_RIGHT")
GameTooltip:SetText("Softcore HUD", 1, 1, 1)
local hint = GetHUDHint(frame.hudStateText)
if hint then
GameTooltip:AddLine(hint, 0.74, 0.66, 0.50, true)
GameTooltip:AddLine("Click to open or close details. Drag to move.", 0.48, 0.44, 0.36)
else
GameTooltip:AddLine("Click to open or close details. Drag to move.", 0.74, 0.66, 0.50)
end
GameTooltip:Show()
end)
frame:SetScript("OnLeave", function() GameTooltip:Hide() end)
self.hudFrame = frame
self:HUD_Refresh()
end
function SC:HUD_Refresh()
local frame = self.hudFrame
if not frame then return end
local db = self.db or SoftcoreDB
local run = db and db.run or {}
local active = run.active
local ui = SoftcoreDB and SoftcoreDB.ui or {}
local status = self:GetPlayerStatus()
local syncRows = self.Sync_GetGroupRows and self:Sync_GetGroupRows() or {}
local violations = LocalActiveViolations()
local state, text, focusTab = GetHUDState(status, violations, syncRows)
local textColor = TEXT_COLOR[state] or TEXT_COLOR.GRAY
local showForGovernance = (focusTab == "RUN" and text ~= "No Run")
if ui.hudHidden or (not active and not showForGovernance) then
frame:Hide()
return
end
frame.focusTab = focusTab
frame.hudStateText = text
SetLamp(frame, state)
frame.statusText:SetText(text)
frame.statusText:SetTextColor(textColor[1], textColor[2], textColor[3])
frame:SetSize(HUD_W, HUD_H)
frame:Show()
end
function SC:HUD_Toggle()
if not self.hudFrame then
self:HUD_Create()
return
end
SoftcoreDB = SoftcoreDB or {}
SoftcoreDB.ui = SoftcoreDB.ui or {}
SoftcoreDB.ui.hudHidden = not SoftcoreDB.ui.hudHidden
self:HUD_Refresh()
if SoftcoreDB.ui.hudHidden then
DEFAULT_CHAT_FRAME:AddMessage("|cff4ade80Softcore:|r Status HUD hidden. Type /sc hud to restore.")
end
end
-- ── Minimap button ───────────────────────────────────────────────────────────
local MINIMAP_RADIUS = 98
local function GetMinimapUI()
SoftcoreDB = SoftcoreDB or {}
SoftcoreDB.ui = SoftcoreDB.ui or {}
return SoftcoreDB.ui
end
local function PlaceMinimapButton(button, angle)
local rad = math.rad(angle)
button:ClearAllPoints()
button:SetPoint("CENTER", Minimap, "CENTER",
math.cos(rad) * MINIMAP_RADIUS,
math.sin(rad) * MINIMAP_RADIUS)
end
function SC:MinimapButton_Create()
if self.minimapButton then return end
local ui = GetMinimapUI()
local angle = ui.minimapAngle or 225
local button = CreateFrame("Button", "SoftcoreMinimapButton", Minimap)
button:SetSize(31, 31)
button:SetFrameStrata("MEDIUM")
button:SetFrameLevel(8)
local icon = button:CreateTexture(nil, "BACKGROUND")
icon:SetSize(20, 20)
icon:SetPoint("TOPLEFT", button, "TOPLEFT", 7, -6)
icon:SetTexture(MINIMAP_LOGO_TEXTURE)
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
local border = button:CreateTexture(nil, "OVERLAY")
border:SetSize(53, 53)
border:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0)
border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
button:RegisterForClicks("LeftButtonUp")
button:RegisterForDrag("LeftButton")
local dragging = false
button:SetScript("OnDragStart", function()
dragging = true
button:SetScript("OnUpdate", function()
local mx, my = Minimap:GetCenter()
local scale = UIParent:GetEffectiveScale()
local cx, cy = GetCursorPosition()
cx, cy = cx / scale, cy / scale
local a = math.deg(math.atan2(cy - my, cx - mx))
PlaceMinimapButton(button, a)
GetMinimapUI().minimapAngle = a
end)
end)
button:SetScript("OnDragStop", function()
button:SetScript("OnUpdate", nil)
C_Timer.After(0.05, function() dragging = false end)
end)
button:SetScript("OnClick", function()
if dragging then return end
if SC.ToggleMasterWindow then SC:ToggleMasterWindow() end
end)
button:SetScript("OnEnter", function()
GameTooltip:SetOwner(button, "ANCHOR_LEFT")
GameTooltip:SetText("Softcore", 1, 1, 1)
GameTooltip:AddLine("/sc minimap to hide this button", 0.6, 0.6, 0.6)
GameTooltip:Show()
end)
button:SetScript("OnLeave", function() GameTooltip:Hide() end)
PlaceMinimapButton(button, angle)
if ui.minimapHidden then button:Hide() end
self.minimapButton = button
end
function SC:MinimapButton_Toggle()
if not self.minimapButton then
self:MinimapButton_Create()
return
end
local ui = GetMinimapUI()
if self.minimapButton:IsShown() then
self.minimapButton:Hide()
ui.minimapHidden = true
DEFAULT_CHAT_FRAME:AddMessage("|cff4ade80Softcore:|r Minimap button hidden. Type /sc minimap to restore it.")
else
self.minimapButton:Show()
ui.minimapHidden = false
end
end