-
Notifications
You must be signed in to change notification settings - Fork 2
/
Archy.lua
1882 lines (1576 loc) · 57.6 KB
/
Archy.lua
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
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- ----------------------------------------------------------------------------
-- Upvalued Lua API.
-- ----------------------------------------------------------------------------
-- Libraries
local math = _G.math
local table = _G.table
local string = _G.string
-- Functions
local date = _G.date
local next = _G.next
local pairs = _G.pairs
local setmetatable = _G.setmetatable
local tonumber = _G.tonumber
local type = _G.type
-- ----------------------------------------------------------------------------
-- AddOn namespace.
-- ----------------------------------------------------------------------------
local LibStub = _G.LibStub
local FOLDER_NAME, private = ...
local Archy = LibStub("AceAddon-3.0"):NewAddon("Archy", "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0", "AceBucket-3.0", "AceTimer-3.0", "LibSink-2.0", "LibToast-1.0")
Archy.version = _G.GetAddOnMetadata(FOLDER_NAME, "Version")
_G["Archy"] = Archy
local Dialog = LibStub("LibDialog-1.0")
local HereBeDragons = LibStub("HereBeDragons-2.0")
local L = LibStub("AceLocale-3.0"):GetLocale("Archy", false)
local LDBI = LibStub("LibDBIcon-1.0")
local DatamineTooltip = _G.CreateFrame("GameTooltip", "ArchyScanTip", nil, "GameTooltipTemplate")
DatamineTooltip:SetOwner(_G.UIParent, "ANCHOR_NONE")
-- ----------------------------------------------------------------------------
-- Constants
-- ----------------------------------------------------------------------------
local BFA_EXPANSION_LEVEL = 7 -- BFA is the last expansion with Archeology skills
local MAX_PROFESSION_RANK = min(BFA_EXPANSION_LEVEL, _G.GetExpansionLevel()) + 4 -- Skip the 4 ranks of vanilla
local MAX_ARCHAEOLOGY_RANK = _G.PROFESSION_RANKS[MAX_PROFESSION_RANK][1]
private.MAX_ARCHAEOLOGY_RANK = MAX_ARCHAEOLOGY_RANK
local GLOBAL_COOLDOWN_TIME = 1.5
local SURVEY_SPELL_ID = 80451
local CRATE_USE_STRING -- Populate in Archy:OnEnable()
local ZONE_DATA = {}
private.ZONE_DATA = ZONE_DATA
local MAP_CONTINENTS = {} -- Popupated in Archy:OnEnable()
private.MAP_CONTINENTS = MAP_CONTINENTS
local EasySurveyButton -- Populated in Archy:OnInitialize()
local LorewalkersLodestone = {
itemID = 87548,
spellID = 126956
}
local LorewalkersMap = {
itemID = 87549,
spellID = 126957
}
-- If fishing pole detection breaks in a future patch due to indices changing, uncomment the below code to find the correct values:
--do
-- COMPILED_ITEM_CLASSES = {}
-- local classIndex = 0
-- local className = _G.GetItemClassInfo(classIndex)
--
-- while className and className ~= "" do
-- COMPILED_ITEM_CLASSES[classIndex] = {
-- name = className,
-- subClasses = {},
-- }
--
-- local subClassIndex = 0
-- local subClassName = _G.GetItemSubClassInfo(classIndex, subClassIndex)
--
-- while subClassName and subClassName ~= "" do
-- COMPILED_ITEM_CLASSES[classIndex].subClasses[subClassIndex] = subClassName
--
-- subClassIndex = subClassIndex + 1
-- subClassName = _G.GetItemSubClassInfo(classIndex, subClassIndex)
-- end
--
-- classIndex = classIndex + 1
-- className = _G.GetItemClassInfo(classIndex)
-- end
--end
local FISHING_POLE_ITEM_TYPE_NAME
do
local ITEM_CLASS_WEAPON = 2
local ITEM_SUBCLASS_FISHING_POLE = 20
FISHING_POLE_ITEM_TYPE_NAME = _G.GetItemSubClassInfo(ITEM_CLASS_WEAPON, ITEM_SUBCLASS_FISHING_POLE)
end
_G.BINDING_HEADER_ARCHY = "Archy"
_G.BINDING_NAME_OPTIONSARCHY = L["BINDING_NAME_OPTIONS"]
_G.BINDING_NAME_TOGGLEARCHY = L["BINDING_NAME_TOGGLE"]
_G.BINDING_NAME_SOLVEARCHY = L["BINDING_NAME_SOLVE"]
_G.BINDING_NAME_SOLVE_WITH_KEYSTONESARCHY = L["BINDING_NAME_SOLVESTONE"]
_G.BINDING_NAME_ARTIFACTSARCHY = L["BINDING_NAME_ARTIFACTS"]
_G.BINDING_NAME_DIGSITESARCHY = L["BINDING_NAME_DIGSITES"]
-- ----------------------------------------------------------------------------
-- Variables
-- ----------------------------------------------------------------------------
MissingDigsites = MissingDigsites or {}
local continent_digsites = {}
private.continent_digsites = continent_digsites
local lootedKeystoneRace -- this is to force a refresh after the BAG_UPDATE event
local digsitesTrackingID -- set in Archy:OnEnable()
local nearestDigsite
local playerLocation = {
UIMapID = 0,
UIMapType = 0,
x = 0,
y = 0
}
local surveyLocation = {
UIMapID = 0,
UIMapType = 0,
x = 0,
y = 0
}
local prevTheme
-- ----------------------------------------------------------------------------
-- Debugger.
-- ----------------------------------------------------------------------------
local Debug, DebugPour, GetDebugger
do
local TextDump = LibStub("LibTextDump-1.0")
local DEBUGGER_WIDTH = 750
local DEBUGGER_HEIGHT = 800
local debugger
function Debug(...)
if not debugger then
debugger = TextDump:New(("%s Debug Output"):format(FOLDER_NAME), DEBUGGER_WIDTH, DEBUGGER_HEIGHT)
end
local message = string.format(...)
debugger:AddLine(message)
return message
end
function DebugPour(...)
Archy:Pour(Debug(...), 1, 1, 1)
end
function GetDebugger()
if not debugger then
debugger = TextDump:New(("%s Debug Output"):format(FOLDER_NAME), DEBUGGER_WIDTH, DEBUGGER_HEIGHT)
end
return debugger
end
private.Debug = Debug
private.DebugPour = DebugPour
end
-- ----------------------------------------------------------------------------
-- Function upvalues
-- ----------------------------------------------------------------------------
local Blizzard_SolveArtifact
local UpdateAllSites
-- ----------------------------------------------------------------------------
-- External objects. Assigned in Archy:OnEnable()
-- ----------------------------------------------------------------------------
local ArtifactFrame
local DigSiteFrame
local DistanceIndicatorFrame
local TomTomHandler
-- ----------------------------------------------------------------------------
-- Initialization.
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- Local helper functions
-- ----------------------------------------------------------------------------
local function UpdateMinimapIcons()
if not private.hasArchaeology or not playerLocation.x and not playerLocation.y then
return
end
local continentDigsites = continent_digsites[private.CurrentContinentID]
if not continentDigsites then
return
end
local minimapSettings = private.ProfileSettings.minimap
local canShow = private.ProfileSettings.general.show and minimapSettings.show
for _, digsite in pairs(continentDigsites) do
if canShow then
if nearestDigsite == digsite or not minimapSettings.nearest then
digsite:EnableMapIcon()
else
digsite:DisableMapIcon()
end
if nearestDigsite == digsite and minimapSettings.fragmentNodes then
digsite:EnableSurveyNodes()
else
digsite:DisableSurveyNodes()
end
else
digsite:DisableMapIcon()
digsite:DisableSurveyNodes()
end
end
end
local function HideFrames()
DigSiteFrame:Hide()
ArtifactFrame:Hide()
end
local function ShowFrames()
if private.in_combat or private.FramesShouldBeHidden() then
return
end
if private.ProfileSettings.digsite.show then
DigSiteFrame:Show()
end
if private.ProfileSettings.artifact.show then
ArtifactFrame:Show()
end
Archy:ConfigUpdated()
end
local SuspendClickToMove
do
local click_to_move
function SuspendClickToMove()
-- we're not using easy cast, no need to mess with click to move
if not private.ProfileSettings.general.easyCast or _G.IsEquippedItemType(FISHING_POLE_ITEM_TYPE_NAME) or not _G.CanScanResearchSite() then
return
end
if private.ProfileSettings.general.show then
if _G.GetCVarBool("autointeract") then
_G.SetCVar("autointeract", "0")
click_to_move = "1"
end
else
if click_to_move and click_to_move == "1" then
_G.SetCVar("autointeract", "1")
click_to_move = nil
end
end
end
end -- do-block
local function AnnounceNearestDigsite()
if not nearestDigsite or not nearestDigsite.distance then
return
end
local digsiteName = ("%s%s|r"):format(_G.GREEN_FONT_COLOR_CODE, nearestDigsite.name)
local digsiteZoneName = ("%s%s|r"):format(_G.GREEN_FONT_COLOR_CODE, nearestDigsite.zoneName)
Archy:Pour(L["Nearest Dig Site is: %s in %s (%.1f yards away)"]:format(digsiteName, digsiteZoneName, nearestDigsite.distance), 1, 1, 1)
end
-- returns the rank and max rank for the players archaeology skill
local function GetArchaeologyRank()
local _, _, archaeologyIndex = _G.GetProfessions()
if not archaeologyIndex then
return 0, 0
end
local _, _, rank, maxRank = _G.GetProfessionInfo(archaeologyIndex)
return rank, maxRank
end
private.GetArchaeologyRank = GetArchaeologyRank
local function IsTaintable()
return (private.in_combat or _G.InCombatLockdown() or (_G.UnitAffectingCombat("player") or _G.UnitAffectingCombat("pet")))
end
private.IsTaintable = IsTaintable
local function SolveRaceArtifact(race, useKeystones)
-- The check for race exists because its absence means we're calling this function from the default UI and should NOT perform any of the actions within the block.
if race then
local artifact = race.currentProject
if artifact then
_G.SetSelectedArtifact(race.ID)
lootedKeystoneRace = race
-- Override keystones that have already been added if true or false were passed.
if type(useKeystones) == "boolean" then
artifact.keystones_added = useKeystones and math.min(race.keystonesInInventory, artifact.sockets) or 0
end
if artifact.keystones_added > 0 then
for index = 1, artifact.keystones_added do
_G.SocketItemToArtifact()
if not _G.ItemAddedToArtifact(index) then
break
end
end
elseif artifact.sockets > 0 then
for index = 1, artifact.sockets do
_G.RemoveItemFromArtifact()
end
end
end
end
Blizzard_SolveArtifact()
end
Dialog:Register("ArchyConfirmSolve", {
text = "",
on_show = function(self, data)
self.text:SetFormattedText(L["Your Archaeology skill is at %d of %d. Are you sure you would like to solve this artifact before visiting a trainer?"], data.rank, data.maxRank)
end,
buttons = {
{
text = _G.YES,
on_click = function(self, data)
if data.race then
SolveRaceArtifact(data.race, data.useKeystones)
else
Blizzard_SolveArtifact()
end
end,
},
{
text = _G.NO,
},
},
show_while_dead = false,
hide_on_escape = true,
})
-- ----------------------------------------------------------------------------
-- AddOn methods
-- ----------------------------------------------------------------------------
function Archy:ShowArchaeology()
if _G.IsAddOnLoaded("Blizzard_ArchaeologyUI") then
if _G.ArchaeologyFrame:IsShown() then
_G.HideUIPanel(_G.ArchaeologyFrame)
else
_G.ShowUIPanel(_G.ArchaeologyFrame)
end
return true
end
local loaded, reason = _G.LoadAddOn("Blizzard_ArchaeologyUI")
if loaded then
if _G.ArchaeologyFrame:IsShown() then
_G.HideUIPanel(_G.ArchaeologyFrame)
else
_G.ShowUIPanel(_G.ArchaeologyFrame)
end
return true
else
Archy:Print(L["ArchaeologyUI not loaded: %s Try opening manually."]:format(_G["ADDON_" .. reason]))
return false
end
end
local CONFIG_UPDATE_FUNCTIONS = {
artifact = function(option)
if option == "autofill" then
for raceID, race in pairs(private.Races) do
race:UpdateCurrentProject()
end
elseif option == "color" then
ArtifactFrame:RefreshDisplay()
else
ArtifactFrame:UpdateChrome()
ArtifactFrame:RefreshDisplay()
Archy:SetFramePosition(ArtifactFrame)
end
end,
digsite = function(option)
if option == "tooltip" then
UpdateAllSites()
end
Archy:UpdateSiteDistances()
DigSiteFrame:UpdateChrome()
if option == "font" then
Archy:ResizeDigSiteDisplay()
else
Archy:RefreshDigSiteDisplay()
end
Archy:SetFramePosition(DigSiteFrame)
Archy:SetFramePosition(DistanceIndicatorFrame)
DistanceIndicatorFrame:Toggle()
end,
minimap = function(option)
UpdateMinimapIcons()
end,
waypoint = function(option)
local digsiteSettings = private.ProfileSettings.digsite
if not digsiteSettings.waypointNearest then
WaypoingHandler:ClearWaypoint(true)
else
WaypoingHandler:Refresh(nearestDigsite, true)
end
end,
tomtom = function(option)
local tomtomSettings = private.ProfileSettings.tomtom
TomTomHandler.hasTomTom = IsAddOnLoaded("TomTom")
if TomTomHandler.hasTomTom and tomtomSettings.enabled and _G.TomTom.profile then
_G.TomTom.profile.arrow.arrival = tomtomSettings.distance
_G.TomTom.profile.arrow.enablePing = tomtomSettings.ping
end
TomTomHandler:Refresh(nearestDigsite)
end,
}
function Archy:ConfigUpdated(namespace, option)
if namespace then
CONFIG_UPDATE_FUNCTIONS[namespace](option)
else
ArtifactFrame:UpdateChrome()
ArtifactFrame:RefreshDisplay()
DigSiteFrame:UpdateChrome()
self:RefreshDigSiteDisplay()
self:UpdateTracking()
DistanceIndicatorFrame:Toggle()
UpdateMinimapIcons()
SuspendClickToMove()
TomTomHandler:Refresh(nearestDigsite)
WaypoingHandler:Refresh(nearestDigsite, false)
end
end
function Archy:SolveAnyArtifact(useKeystones)
local found = false
for raceID, race in pairs(private.Races) do
local artifact = race.currentProject
if artifact and not race:IsOnArtifactBlacklist() and (artifact.canSolve or (useKeystones and artifact.canSolveInventory)) then
SolveRaceArtifact(race, useKeystones)
found = true
break
end
end
if not found then
self:Print(L["No artifacts were solvable"])
end
end
function Archy:SocketClicked(keystone_button, mouseButtonName, down)
local raceID = keystone_button:GetParent():GetParent():GetID()
private.Races[raceID]:KeystoneSocketOnClick(mouseButtonName)
ArtifactFrame:RefreshDisplay()
end
--[[ Dig Site List Functions ]] --
local function CompareAndResetDigCounters(digsiteListA, digsiteListB)
if not digsiteListA or not digsiteListB or (#digsiteListA == 0) or (#digsiteListB == 0) then
return
end
for _, siteA in pairs(digsiteListA) do
local exists = false
for _, siteB in pairs(digsiteListB) do
if siteA == siteB then
exists = true
break
end
end
if not exists then
siteA.stats.counter = 0
siteA:DisableMapIcon()
siteA:DisableSurveyNodes()
end
end
end
local SourceCount = 0
function UpdateAllSites()
for continentID, continentData in pairs(MAP_CONTINENTS) do
local sites = {}
local continentSites = {}
for _, continentSite in ipairs(C_ResearchInfo.GetDigSitesForMap(continentID)) do
continentSites[continentSite.researchSiteID] = continentSite
end
for continentZoneIndex = 1, #continentData.zones do
local zone = ZONE_DATA[continentData.zones[continentZoneIndex]]
for key, zoneSite in pairs(C_ResearchInfo.GetDigSitesForMap(zone.UIMapID)) do
if not continentSites[zoneSite.researchSiteID] then
return
end
local mapPositionX = continentSites[zoneSite.researchSiteID].position.x
local mapPositionY = continentSites[zoneSite.researchSiteID].position.y
local templateKey = ("%d:%.6f:%.6f"):format(continentID, mapPositionX, mapPositionY)
local digsiteTemplate = Archy:SearchDigsiteTemplate(continentID, zone, zoneSite, mapPositionX, mapPositionY)
if digsiteTemplate then
if digsiteTemplate.mapID == zone.UIMapID then
local digsite = private.Digsites[zoneSite.researchSiteID]
if not digsite then
digsite = private.AddDigsite(digsiteTemplate, templateKey, zoneSite.researchSiteID, zoneSite.name, zoneSite.position.x, zoneSite.position.y)
end
table.insert(sites, digsite)
end
end
end
end
if #sites > 0 then
if continent_digsites[continentID] then
CompareAndResetDigCounters(continent_digsites[continentID], sites)
CompareAndResetDigCounters(sites, continent_digsites[continentID])
end
end
continent_digsites[continentID] = sites
end
if MissingDigsites and MissingDigsites.Count and MissingDigsites.Count > SourceCount then
Archy:DebugMissingDigsites()
end
SourceCount = MissingDigsites.Count or 0
end
local function SortSitesByDistance(digsiteA, digsiteB)
if digsiteA:IsBlacklisted() and not digsiteB:IsBlacklisted() then
return 1 < 0
elseif not digsiteA:IsBlacklisted() and digsiteB:IsBlacklisted() then
return 0 < 1
end
if (digsiteA.distance == -1 and digsiteB.distance == -1) or (not digsiteA.distance and not digsiteB.distance) then
return digsiteA.zoneName .. ":" .. digsiteA.name < digsiteB.zoneName .. ":" .. digsiteB.name
else
return (digsiteA.distance or 0) < (digsiteB.distance or 0)
end
end
local function SortSitesByZoneNameAndName(a, b)
return a.zoneName .. ":" .. a.name < b.zoneName .. ":" .. b.name
end
function Archy:AddMissingDigSite(siteKey, id, name, continentID, mapID, zonename, raceID)
if MissingDigsites and MissingDigsites.Sites and MissingDigsites.Sites[siteKey] then
return
end
MissingDigsites = MissingDigsites or {}
MissingDigsites.Count = MissingDigsites.Count or 0
MissingDigsites.Sites = MissingDigsites.Sites or {}
local RaceID = private.RaceID
if raceID == RaceID.Unknown and continentID == 875 then --Zandalar
raceID = RaceID.ArchRaceZandalari
end
if raceID == RaceID.Unknown and continentID == 876 then --Kul Tiras
raceID = RaceID.ArchRaceDrust
end
MissingDigsites.Sites[siteKey] = {
id = id,
name = name,
mapID = mapID,
zonename = zonename,
raceID = raceID,
continentID = continentID
}
MissingDigsites.Count = MissingDigsites.Count + 1
end
function Archy:DebugMissingDigsites()
if MissingDigsites and MissingDigsites.Sites then
MissingDigsites.Count = 0
for siteKey, site in pairs(MissingDigsites.Sites) do
if private.DIGSITE_TEMPLATES[siteKey] then
MissingDigsites.Sites[siteKey] = nil
elseif private.DIGSITE_TEMPLATES_BY_ID[site.id] then
MissingDigsites.Sites[siteKey] = nil
else
local raceID = site.raceID
local raceName = private.RaceIDToRaceLabel[raceID];
Debug("\n\t\t[\""..siteKey.."\"] = {\n\t\t\t\siteID = "..site.id..", -- "..site.name.."\n\t\t\tmapID = "..site.mapID..", -- "..site.zonename.."\n\t\t\traceID = RaceID."..raceName..",\n\t\t},")
MissingDigsites.Count = MissingDigsites.Count + 1
end
end
end
if MissingDigsites and MissingDigsites.Count > 0 then
print(MissingDigsites.Count .. " missing digsites found. Please run /archy debug and report the list")
end
end
function Archy:SearchDigsiteTemplate(continentID, zone, zoneSite, mapPositionX, mapPositionY)
local siteKey = ("%d:%.6f:%.6f"):format(continentID, mapPositionX, mapPositionY)
local digsiteTemplate = private.DIGSITE_TEMPLATES[siteKey]
if not digsiteTemplate then
local oldsiteKey = private.DIGSITE_TEMPLATES_BY_ID[zoneSite.researchSiteID]
if oldsiteKey then
digsiteTemplate = private.DIGSITE_TEMPLATES[oldsiteKey]
private.DIGSITE_TEMPLATES[oldsiteKey] = nil
private.DIGSITE_TEMPLATES[siteKey] = digsiteTemplate
end
end
if not digsiteTemplate then
digsiteTemplate = private.DIGSITE_TEMPLATES[zoneSite.researchSiteID]
end
if not digsiteTemplate and MissingDigsites and MissingDigsites.Sites and MissingDigsites.Sites[siteKey] then
digsiteTemplate = MissingDigsites.Sites[siteKey]
end
if not digsiteTemplate then
Archy:AddMissingDigSite(siteKey, zoneSite.researchSiteID, zoneSite.name, continentID, zone.UIMapID, zone.name, 0)
digsiteTemplate = MissingDigsites.Sites[siteKey]
end
if digsiteTemplate then
if not digsiteTemplate.siteID then
Archy:AddMissingDigSite(siteKey, zoneSite.researchSiteID, zoneSite.name, continentID, zone.UIMapID, zone.name, digsiteTemplate.raceID)
end
end
return digsiteTemplate
end
function Archy:UpdateSiteDistances(force)
local continentDigsites = continent_digsites[private.CurrentContinentID]
if not continentDigsites or #continentDigsites == 0 then
nearestDigsite = nil
return
end
local closestDistance, closestDigsite
for index = 1, #continentDigsites do
local digsite = continentDigsites[index]
if digsite.mapIconFrame:IsShown() then
digsite.distance = digsite.mapIconFrame:GetDistance()
else
digsite.distance = HereBeDragons:GetZoneDistance(playerLocation.UIMapID, playerLocation.x, playerLocation.y, digsite.UIMapID, digsite.coordX, digsite.coordY)
end
if digsite.coordX and digsite.distance and not digsite:IsBlacklisted() and (not closestDistance or digsite.distance < closestDistance) then
closestDistance = digsite.distance
closestDigsite = digsite
end
end
if closestDigsite and (nearestDigsite ~= closestDigsite or force) then
nearestDigsite = closestDigsite
TomTomHandler.isActive = true
TomTomHandler:Refresh(nearestDigsite)
WaypoingHandler.isActive = true
WaypoingHandler:Refresh(nearestDigsite, false)
UpdateMinimapIcons()
if private.ProfileSettings.digsite.announceNearest and private.ProfileSettings.general.show then
AnnounceNearestDigsite()
end
end
table.sort(continentDigsites, private.ProfileSettings.digsite.sortByDistance and SortSitesByDistance or SortSitesByZoneNameAndName)
end
function Archy:OnInitialize()
private.isLoading = true
self.db = LibStub("AceDB-3.0"):New("ArchyDB", private.DEFAULT_SETTINGS, 'Default')
self.db.RegisterCallback(self, "OnNewProfile", "OnProfileUpdate")
self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileUpdate")
self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileUpdate")
self.db.RegisterCallback(self, "OnProfileReset", "OnProfileUpdate")
local about_panel = LibStub:GetLibrary("LibAboutPanel-2.0", true)
if about_panel then
self.optionsFrame = about_panel:CreateAboutPanel("Archy")
end
self:DefineSinkToast(FOLDER_NAME, [[Interface\Archeology\Arch-Icon-Marker]])
self:SetSinkStorage(self.db.profile.general.sinkOptions)
self:SetupOptions()
self.db.global.surveyNodes = self.db.global.surveyNodes or {}
self.db.char.waypoint = self.db.char.waypoint or {}
self.db.char.digsites = self.db.char.digsites or {
stats = {},
blacklist = {}
}
setmetatable(self.db.char.digsites.stats, {
__index = function(t, k)
if k then
t[k] = {
surveys = 0,
fragments = 0,
looted = 0,
keystones = 0,
counter = 0
}
return t[k]
end
end
})
self.db.char.digsites.blacklist = self.db.char.digsites.blacklist or {}
local profileSettings = self.db.profile
private.ProfileSettings = profileSettings
prevTheme = profileSettings.general and profileSettings.general.theme or private.DEFAULT_SETTINGS.profile.general.theme
LDBI:Register("Archy", private.LDB_object, profileSettings.general.icon)
local survey_spell_name = GetSpellInfo(SURVEY_SPELL_ID);
do
local surveyButtonName = "Archy_EasySurveyButton"
local surveyButton = _G.CreateFrame("Button", surveyButtonName, _G.UIParent, "SecureActionButtonTemplate")
surveyButton:SetPoint("LEFT", _G.UIParent, "RIGHT", 10000, 0)
surveyButton:Hide()
surveyButton:SetFrameStrata("LOW")
surveyButton:EnableMouse(true)
surveyButton:RegisterForClicks("RightButtonDown")
surveyButton:SetAttribute("type", "macro")
surveyButton:SetAttribute("macrotext", "/use [noflying] " .. survey_spell_name)
surveyButton:SetAttribute("action", nil)
surveyButton:SetScript("PostClick", function(self, mouse_button, is_down)
if private.override_binding_on and not IsTaintable() then
_G.ClearOverrideBindings(self)
private.override_binding_on = nil
else
private.regen_clear_override = true
end
end)
EasySurveyButton = surveyButton
local DOUBLECLICK_MAX_SECONDS = 0.2
local DOUBLECLICK_MIN_SECONDS = 0.04
local previousClickTime
_G.WorldFrame:HookScript("OnMouseDown", function(frame, button, down)
uiMapID = C_Map.GetBestMapForUnit("player")
if button == "RightButton" and profileSettings.general.easyCast and uiMapID and ArchaeologyMapUpdateAll(uiMapID) > 0 and not IsTaintable() and not _G.IsEquippedItemType(FISHING_POLE_ITEM_TYPE_NAME) and _G.CanScanResearchSite() and _G.GetSpellCooldown(SURVEY_SPELL_ID) == 0 and not _G.IsFlying() then
-- Ensure the LootFrame contains no items; we don't care if it's simply visible.
if _G.GetNumLootItems() == 0 and previousClickTime then
local doubleClickTime = _G.GetTime() - previousClickTime
if doubleClickTime < DOUBLECLICK_MAX_SECONDS and doubleClickTime > DOUBLECLICK_MIN_SECONDS then
previousClickTime = nil
if not IsTaintable() then
_G.SetOverrideBindingClick(surveyButton, true, "BUTTON2", surveyButtonName)
private.override_binding_on = true
end
end
end
previousClickTime = _G.GetTime()
end
end)
end
private.InitializeFrames()
ArtifactFrame = private.ArtifactFrame
DigSiteFrame = private.DigSiteFrame
DistanceIndicatorFrame = private.DistanceIndicatorFrame
DistanceIndicatorFrame.surveyButton:SetAttribute("type", "macro")
DistanceIndicatorFrame.surveyButton:SetAttribute("macrotext", "/use [noflying] " .. survey_spell_name)
-- ----------------------------------------------------------------------------
-- DB cleanups.
-- ----------------------------------------------------------------------------
for siteID, value in pairs(self.db.char.digsites.blacklist) do
if value == false then
self.db.char.digsites.blacklist[siteID] = nil
end
end
profileSettings.data = nil
end
function Archy:UpdateFramePositions()
self:SetFramePosition(DistanceIndicatorFrame)
self:SetFramePosition(DigSiteFrame)
self:SetFramePosition(ArtifactFrame)
end
local PositionUpdateTimerHandle
function Archy:LoadWorldData(worldID)
local info = C_Map.GetMapInfo(worldID)
if not info then
return
end
if info.mapType == Enum.UIMapType.Cosmic or info.mapType == Enum.UIMapType.World then
local continents = C_Map.GetMapChildrenInfo(worldID, Enum.UIMapType.Continent, true)
for i = 1, #continents do
Archy:LoadContinentData(continents[i].mapID)
end
end
end
function Archy:LoadContinentData(continentID)
local info = C_Map.GetMapInfo(continentID)
if not info then
return
end
if info.mapType == Enum.UIMapType.Continent then
local continentName = HereBeDragons:GetLocalizedMap(continentID)
local continentZones = {}
local zoneData = C_Map.GetMapChildrenInfo(continentID, Enum.UIMapType.Zone, true)
for zoneDataIndex = 1, #zoneData do
local zoneID = zoneData[zoneDataIndex].mapID
local zoneName = HereBeDragons:GetLocalizedMap(zoneID)
continentZones[zoneDataIndex] = zoneID
ZONE_DATA[zoneID] = {
continentID = continentID,
ID = zoneID,
UIMapID = zoneID,
name = zoneName
}
end
MAP_CONTINENTS[continentID] =
{
continentID = continentID,
ID = continentID,
UIMapID = continentID,
name = continentName,
zones = continentZones
}
end
end
function Archy:OnEnable()
-- Ignore this event for now as it's can break other Archaeology UIs
-- Would have been nice if Blizzard passed the race index or artifact name with the event
-- self:RegisterEvent("ARTIFACT_UPDATE")
self:RegisterEvent("ARCHAEOLOGY_FIND_COMPLETE")
self:RegisterEvent("ARCHAEOLOGY_SURVEY_CAST")
self:RegisterEvent("BAG_UPDATE_DELAYED")
self:RegisterEvent("CHAT_MSG_LOOT")
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:RegisterEvent("CURRENCY_DISPLAY_UPDATE")
self:RegisterEvent("LOOT_OPENED")
self:RegisterEvent("PET_BATTLE_CLOSE")
self:RegisterEvent("PET_BATTLE_OPENING_START")
self:RegisterEvent("PLAYER_ENTERING_WORLD")
self:RegisterEvent("PLAYER_LEAVING_WORLD")
self:RegisterEvent("PLAYER_CONTROL_GAINED")
self:RegisterEvent("PLAYER_CONTROL_LOST")
self:RegisterEvent("PLAYER_REGEN_DISABLED")
self:RegisterEvent("PLAYER_REGEN_ENABLED")
self:RegisterEvent("PLAYER_STARTED_MOVING")
self:RegisterEvent("PLAYER_STOPPED_MOVING")
self:RegisterEvent("QUEST_LOG_UPDATE")
self:RegisterEvent("RESEARCH_ARTIFACT_COMPLETE")
self:RegisterEvent("RESEARCH_ARTIFACT_DIG_SITE_UPDATED")
self:RegisterEvent("SKILL_LINES_CHANGED")
self:RegisterEvent("TAXIMAP_CLOSED")
self:RegisterEvent("TAXIMAP_OPENED")
self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
self:RegisterEvent("UNIT_SPELLCAST_FAILED", "UNIT_SPELLCAST_SUCCEEDED")
self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED", "UNIT_SPELLCAST_SUCCEEDED")
self:RegisterEvent("UNIT_SPELLCAST_STOP", "UNIT_SPELLCAST_SUCCEEDED")
self:RegisterEvent("UNIT_SPELLCAST_SENT")
self:RESEARCH_ARTIFACT_HISTORY_READY()
self:SKILL_LINES_CHANGED()
Archy:UpdateFramePositions()
DigSiteFrame:UpdateChrome()
ArtifactFrame:UpdateChrome()
DatamineTooltip:ClearLines()
DatamineTooltip:SetSpellByID(private.CRATE_SPELL_ID)
CRATE_USE_STRING = ("%s %s"):format(_G.ITEM_SPELL_TRIGGER_ONUSE, _G["ArchyScanTipTextLeft" .. DatamineTooltip:NumLines()]:GetText())
for trackingTypeIndex = 1, C_Minimap.GetNumTrackingTypes() do
if (C_Minimap.GetTrackingInfo(trackingTypeIndex)) == _G.MINIMAP_TRACKING_DIGSITES then
digsitesTrackingID = trackingTypeIndex
break
end
end
self:UpdateTracking()
TomTomHandler = private.TomTomHandler
TomTomHandler.isActive = true
TomTomHandler.hasTomTom = IsAddOnLoaded("TomTom")
TomTomHandler.hasPOIIntegration = TomTomHandler.hasTomTom and (_G.TomTom.profile and _G.TomTom.profile.poi and _G.TomTom.EnableDisablePOIIntegration) and true or false
WaypoingHandler = private.WaypoingHandler
WaypoingHandler.isActive = true
private.InitializeRaces()
private.InitializeDigsiteTemplates()
private.InitializeArtifactTemplates()
-- ----------------------------------------------------------------------------
-- Map stuff.
-- ----------------------------------------------------------------------------
local CosmicUIMapID = 946
Archy:LoadWorldData(CosmicUIMapID)
private.PlayerGUID = _G.UnitGUID("player")
self:ScheduleTimer("UpdatePlayerPosition", 2, true)
private.isLoading = false
end
function Archy:OnProfileUpdate(event, database, ProfileKey)
local newTheme
if database then
if event == "OnProfileChanged" or event == "OnProfileCopied" then
newTheme = database.profile and database.profile.general and database.profile.general.theme or private.DEFAULT_SETTINGS.profile.general.theme
elseif event == "OnProfileReset" or event == "OnNewProfile" then
newTheme = database.defaults and database.defaults.profile and database.defaults.profile.general and database.defaults.profile.general.theme
end
end
private.ProfileSettings = database and database.profile or self.db.profile
if newTheme and prevTheme and (newTheme ~= prevTheme) then
_G.ReloadUI()
end
self:ConfigUpdated()
self:UpdateFramePositions()
end
-- ----------------------------------------------------------------------------
-- Slash command handler
-- ----------------------------------------------------------------------------
local SUBCOMMAND_FUNCS = {
[L["config"]:lower()] = function()
_G.InterfaceOptionsFrame_OpenToCategory(Archy.optionsFrame)
end,
[L["stealth"]:lower()] = function()
private.ProfileSettings.general.stealthMode = not private.ProfileSettings.general.stealthMode
Archy:ConfigUpdated()
end,
[L["dig sites"]:lower()] = function()
private.ProfileSettings.digsite.show = not private.ProfileSettings.digsite.show
Archy:ConfigUpdated('digsite')
end,
[L["artifacts"]:lower()] = function()
private.ProfileSettings.artifact.show = not private.ProfileSettings.artifact.show
Archy:ConfigUpdated('artifact')
end,
[_G.SOLVE:lower()] = function()
Archy:SolveAnyArtifact()
end,
[L["solve stone"]:lower()] = function()
Archy:SolveAnyArtifact(true)
end,
[L["nearest"]:lower()] = AnnounceNearestDigsite,
[L["closest"]:lower()] = AnnounceNearestDigsite,
[L["reset"]:lower()] = function()
private:ResetFramePositions()