-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathRXPGuides.lua
2005 lines (1706 loc) · 65.6 KB
/
RXPGuides.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
local addonName, addon = ...
local _G = _G
local UnitInRaid = UnitInRaid
local fmt = string.format
addon = LibStub("AceAddon-3.0"):NewAddon(addon, addonName, "AceEvent-3.0")
local RegisterMessage_OLD = addon.RegisterMessage
local rand, tinsert, select = math.random, table.insert, _G.select
local IsAddOnLoadOnDemand = C_AddOns and C_AddOns.IsAddOnLoadOnDemand or _G.IsAddOnLoadOnDemand
local GetSpellInfo
if C_Spell and C_Spell.GetSpellInfo then
addon.GetSpellInfo = function(...)
local id = ...
if not id then return end
local t = C_Spell.GetSpellInfo(...)
--local rank = C_Spell.GetSpellSubtext(...)
if t then
return t.name, t.rank, t.iconID, t.castTime, t.minRange, t.maxRange, t.spellID, t.originalIconID
end
end
GetSpellInfo = addon.GetSpellInfo
else
GetSpellInfo = _G.GetSpellInfo
end
local GetSpellTexture = C_Spell and C_Spell.GetSpellTexture or _G.GetSpellTexture
local GetSpellSubtext = C_Spell and C_Spell.GetSpellSubtext or _G.GetSpellSubtext
local IsCurrentSpell = C_Spell and C_Spell.IsCurrentSpell or _G.IsCurrentSpell
local IsSpellKnown = C_Spell and C_Spell.IsSpellKnown or _G.IsSpellKnown
local IsPlayerSpell = C_Spell and C_Spell.IsPlayerSpell or _G.IsPlayerSpell
local NewTicker = C_Timer.NewTicker
local messageList = {}
local function MessageHandler(message,...)
for func in pairs(messageList[message]) do
func(message,...)
end
end
addon.RegisterMessage = function(self,message,callback,...)
if not messageList[message] then
messageList[message] = {}
RegisterMessage_OLD(self,message,MessageHandler)
end
messageList[message][callback] = true
end
function addon:UnregisterMessage(message,callback)
if not messageList[message] then
return
elseif callback then
messageList[message][callback] = nil
else
table.wipe(messageList[message])
end
end
addon.HookMessage = function(self,message,callback,...)
if not (messageList[message] and messageList[message][callback]) then
addon.RegisterMessage(self,message,callback,...)
else
local callback_old = MessageHandler
local callback_new
if type(callback_old) == "function" then
callback_new = function(...)
callback_old(...)
callback(...)
end
else
callback_new = callback
end
RegisterMessage_OLD(self,message,callback_new,...)
end
end
function addon.SendEvent(self,...)
if _G.WeakAuras and _G.WeakAuras.ScanEvents then
_G.WeakAuras.ScanEvents(...)
end
return addon.SendMessage(self,...)
end
local messageQueue = {}
function addon:QueueMessage(...)
tinsert(messageQueue,{...})
end
function addon.ProcessMessageQueue()
local processed
local removedIndexes = {}
for i = 1,#messageQueue do
addon:SendEvent(unpack(messageQueue[i]))
tinsert(removedIndexes,i)
if i >= 10 then
break
end
end
for i = #removedIndexes,1,-1 do
processed = true
table.remove(messageQueue,removedIndexes[i])
end
return processed
end
local GetAddOnMetadata = C_AddOns and C_AddOns.GetAddOnMetadata or _G.GetAddOnMetadata
addon.release = GetAddOnMetadata(addonName, "Version")
addon.title = GetAddOnMetadata(addonName, "Title")
local cacheVersion = 26
local L = addon.locale.Get
if string.match(addon.release, 'project') then
addon.release = L('Development')
addon.versionText = L('Development')
else
addon.versionText = string.format("%s %s", _G.GAME_VERSION_LABEL,
addon.release)
end
addon.version = 40000
local gameVersion = select(4, GetBuildInfo())
addon.gameVersion = gameVersion
local maxLevel
if gameVersion > 50000 then
addon.game = "RETAIL"
maxLevel = 70
if gameVersion > 120000 then
maxLevel = 80
end
elseif gameVersion > 40000 then
addon.game = "CATA"
maxLevel = 85
elseif gameVersion > 30000 then
addon.game = "WOTLK"
maxLevel = 80
elseif gameVersion > 20000 then
addon.game = "TBC"
maxLevel = 70
else
addon.game = "CLASSIC"
maxLevel = 60
end
function addon.GetSeason()
local season = C_Seasons and C_Seasons.HasActiveSeason() and (not(C_GameRules and C_GameRules.IsHardcoreActive and C_GameRules.IsHardcoreActive()) and C_Seasons.GetActiveSeason()) or 0
if season > 2 then
return 0
end
return season
end
local RXPGuides = {}
addon.RXPGuides = RXPGuides
_G.RXPGuides = RXPGuides
addon.guideCache = {}
addon.questQueryList = {}
addon.itemQueryList = {}
addon.questAccept = {}
addon.questTurnIn = {}
addon.disabledQuests = {}
addon.activeItems = {}
addon.activeSpells = {}
addon.functions = {}
addon.enabledFrames = {} -- Hold all enabled frame/features for Hide/Show
addon.player = {
localeClass = select(1, UnitClass("player")),
class = select(2, UnitClass("player")),
race = select(2, UnitRace("player")),
faction = select(1,UnitFactionGroup("player")),
guid = UnitGUID("player"),
name = UnitName("player"),
maxlevel = maxLevel,
season = addon.GetSeason(),
}
addon.player.neutral = addon.player.faction == "Neutral"
addon.generatedSteps = {}
--local class = addon.player.class
--local race = addon.player.race
BINDING_HEADER_RXPGuides = addon.title
BINDING_HEADER_RXPTargeting = addon.title
local errorTimer = 0
addon.errors = {}
function addon.Call(label,func,...)
--if true then return true end
label = label or ""
addon.lastCall = label
local pass, r1, r2, r3, r4 = pcall(func,...)
if not pass then
local msg = r1
addon.errors[label] = addon.errors[label] or {}
local count = addon.errors[label][msg] or 0
addon.errors[label][msg] = count + 1
if GetTime() - errorTimer > 30 then
errorTimer = GetTime()
error(msg)
end
return
end
return r1, r2, r3, r4
end
local questFrame = CreateFrame("Frame");
local startTime = GetTime()
function addon.QuestAutoAccept(titleOrId)
if not titleOrId then return end
-- questAccept contains quest and title lookups
-- addon.questAccept[747] == addon.questAccept["The Hunt Begins"]
local element = addon.questAccept[titleOrId]
if not element or (element.questId and addon.disabledQuests[element.questId]) then return end
local step = element.step
if step.active or step.index > 1 and addon.currentGuide.steps[step.index - 1].active then
addon:SendEvent("RXP_QUEST_ACCEPT",element.questId)
return true
end
end
function addon.GetStepQuestReward(titleOrId)
-- enableQuestRewardAutomation is setting for hard-coded .turnin step data
if not titleOrId then return 0 end
-- questTurnIn contains quest and title lookups
-- addon.questTurnIn[747] == addon.questTurnIn["The Hunt Begins"]
local element = addon.questTurnIn[titleOrId]
if not element then return 0 end
if not addon.settings.profile.enableQuestRewardAutomation then return 0,element end
return (element.reward >= 0 and element.reward or 0), element
end
function addon.IsPlayerSpell(id)
if IsPlayerSpell(id) or IsSpellKnown(id, true) or IsSpellKnown(id) then
return true
end
if addon.player.season == 2 then
for _,slot in pairs (C_Engraving.GetRuneCategories(false,true)) do
local runes = C_Engraving.GetRunesForCategory(slot,true)
for _,rune in pairs(runes) do
if rune.skillLineAbilityID == id then
return true
elseif type(rune.learnedAbilitySpellIDs) == "table" then
for _,spell in pairs(rune.learnedAbilitySpellIDs) do
if spell == id then
return true
end
end
end
end
end
end
return false
end
local currrentSkillLevel = {}
local maxSkillLevel = {}
local professionNames
function addon.GetProfessionNames()
if not professionNames then professionNames = {} end
for profession, ids in pairs(addon.professionID) do
for i, id in ipairs(ids) do
if IsSpellKnown(id) then
if id == 2656 then
professionNames[profession] = GetSpellInfo(2575)
elseif id == 2383 then
professionNames[profession] = GetSpellInfo(9134)
elseif id == 1804 then
professionNames[profession] = GetSpellInfo(1809)
else
professionNames[profession] = GetSpellInfo(id)
end
break
end
end
end
professionNames.riding = GetSpellInfo(33388)
return professionNames
end
function addon.GetProfessionLevel()
local names
if not (professionNames and professionNames.riding) then
addon.GetProfessionNames()
end
names = professionNames
if IsPlayerSpell(33388) then
currrentSkillLevel["riding"] = 75
elseif IsPlayerSpell(33391) then
currrentSkillLevel["riding"] = 150
elseif IsPlayerSpell(34090) then
currrentSkillLevel["riding"] = 225
elseif IsPlayerSpell(34091) then
currrentSkillLevel["riding"] = 300
elseif IsPlayerSpell(90265) then
currrentSkillLevel["riding"] = 375
end
if addon.IsPlayerSpell(54197) then currrentSkillLevel["coldweatherflying"] = 1 end
if not _G.GetSkillLineInfo then return end
if not names.riding then names.riding = GetSpellInfo(33388) end
for i = 1, _G.GetNumSkillLines() do
local skillName, _, _, skillRank, _, _, skillMaxRank =
_G.GetSkillLineInfo(i)
if skillRank then
for profession, name in pairs(names) do
-- print(name,skillName,name == skillName)
if name == skillName then
currrentSkillLevel[profession] = skillRank
maxSkillLevel[profession] = skillMaxRank
end
end
end
end
end
function addon.UpdateSkillData()
addon.GetProfessionNames()
addon.GetProfessionLevel()
end
local GetContainerNumSlots = C_Container and C_Container.GetContainerNumSlots or _G.GetContainerNumSlots
local GetContainerItemID = C_Container and C_Container.GetContainerItemID or _G.GetContainerItemID
local GetItemSpell = C_Item and C_Item.GetItemSpell or _G.GetItemSpell
function addon.GetSkillLevel(skill, useMaxValue)
addon.UpdateSkillData()
local function finditem(id)
if type(id) == "number" then
for level,t in pairs(addon.mountIDs) do
if t[id] then
return level
end
end
end
return -1
end
if skill == "riding" and gameVersion < 20000 and addon.mountIDs then
local level = -1
for bag = BACKPACK_CONTAINER, NUM_BAG_FRAMES do
for slot = 1,GetContainerNumSlots(bag) do
local id = GetContainerItemID(bag, slot)
local _,spellId = GetItemSpell(id or 0)
level = math.max(level,finditem(spellId))
end
end
return level
elseif skill then
if useMaxValue then
return maxSkillLevel[skill] or -1
else
return currrentSkillLevel[skill] or -1
end
else
if useMaxValue then
return maxSkillLevel
else
return currrentSkillLevel
end
end
end
local function ChangeStep(srcGuide,srcStep,destGuide,destStep,func)
local function stepindex(guide,refresh)
if type(guide) ~= "table" then
return false
elseif not guide.stepIds or refresh then
guide.stepIds = {}
for i,step in ipairs(guide.steps) do
if step.stepId then
guide.stepIds[step.stepId] = i
end
end
end
return true
end
srcGuide = addon:FetchGuide(addon.guideIds[srcGuide])
destGuide = addon:FetchGuide(addon.guideIds[destGuide])
if not (stepindex(srcGuide) and (not destGuide or stepindex(destGuide))) then
return
end
srcStep = srcGuide.stepIds[srcStep]
destStep = srcGuide.stepIds[destStep]
if srcStep and (not destGuide or destStep) then
func(srcGuide,srcStep,destGuide,destStep)
stepindex(srcGuide,true)
stepindex(destGuide,true)
addon:ScheduleTask(addon.ReloadGuide)
--print(srcGuide.name,destGuide.name,srcStep,destStep)
return true
end
end
function addon.ReplaceStep(arg1,arg2,arg3,arg4)
local function replace(srcGuide,srcStep,destGuide,destStep)
--local oldStep = destGuide.steps[destStep]
destGuide.steps[destStep] = srcGuide.steps[srcStep]
--srcGuide.steps[srcStep] = oldStep
end
return ChangeStep(arg1,arg2,arg3,arg4,replace)
end
function addon.RemoveStep(arg1,arg2)
local function remove(srcGuide,srcStep)
--print('remove',srcGuide.name,srcStep)
table.remove(srcGuide.steps,srcStep)
end
return ChangeStep(arg1,arg2,"","",remove)
end
function addon.InsertStep(arg1,arg2,arg3,arg4)
local function insert(srcGuide,srcStep,destGuide,destStep)
table.insert(destGuide.steps,destStep,srcGuide.steps[srcStep])
end
return ChangeStep(arg1,arg2,arg3,arg4,insert)
end
addon.skillList = {}
local spellRequest = {}
local trainerUpdate = 0
local function ProcessSpells(names, rank)
if gameVersion > 90000 then return end
local _, race = UnitRace("player")
local level = UnitLevel("player")
local entries = {race, addon.player.class}
for _, entry in pairs(entries) do
if addon.defaultSpellList[entry] then
for spellLvl, spells in pairs(addon.defaultSpellList[entry]) do
if spellLvl <= level then
for i, spellId in ipairs(spells) do
if not (spellRequest[spellId] or
C_Spell.IsSpellDataCached(spellId)) then
C_Spell.RequestLoadSpellData(spellId)
spellRequest[spellId] = true
end
if names and rank and
not (addon.settings.profile.hardcore and
addon.HCSpellList and addon.HCSpellList[spellId]) then
spellRequest[spellId] = nil
local sName = GetSpellInfo(spellId)
local sRank = GetSpellSubtext(spellId)
for id, name in pairs(names) do
if sName == name and sRank == rank[id] then
BuyTrainerService(id)
end
end
end
end
end
end
end
end
end
local function OnTrainer()
if not addon.settings.profile.enableTrainerAutomation then return end
local i = GetNumTrainerServices()
if not i or i == 0 or GetTime() - trainerUpdate > 15 then return end
local names = {}
local rank = {}
for id = 1, i do
local n, r, cat = GetTrainerServiceInfo(id)
if cat == "available" then
names[id] = n
rank[id] = r
end
end
ProcessSpells(names, rank)
for spellName, spellRank in pairs(addon.skillList) do
for id, name in pairs(names) do
if name == spellName then
local r = rank[id]
r = r and tonumber(r:match("(%d+)")) or 0
if (r <= spellRank or spellRank == 0) then
BuyTrainerService(id)
return
end
end
end
end
end
local tTimer = 0
local function trainerFrameUpdate(self, t)
tTimer = tTimer + t
if tTimer >= 0.2 then
tTimer = 0
if GetTime() - trainerUpdate > 15 then
self:SetScript("OnUpdate", nil)
end
OnTrainer()
end
end
local function GossipGetNumOptions()
if C_GossipInfo.GetNumOptions then
return C_GossipInfo.GetNumOptions()
elseif C_GossipInfo.GetOptions then
return #C_GossipInfo.GetOptions()
else
return _G.GetNumGossipOptions()
end
end
addon.GossipGetNumOptions = GossipGetNumOptions
local GossipGetNumActiveQuests = C_GossipInfo.GetNumActiveQuests or
_G.GetNumGossipActiveQuests
local GossipGetNumAvailableQuests = C_GossipInfo.GetNumAvailableQuests or
_G.GetNumGossipAvailableQuests
local GossipSelectAvailableQuest = C_GossipInfo.SelectAvailableQuest or
_G.SelectGossipAvailableQuest
local GossipGetActiveQuests = C_GossipInfo.GetActiveQuests or
_G.GetGossipActiveQuests
local GossipSelectActiveQuest = C_GossipInfo.SelectActiveQuest or
_G.SelectGossipActiveQuest
local GossipGetAvailableQuests = C_GossipInfo.GetAvailableQuests or
_G.GetGossipAvailableQuests
-- TODO handle Pawn compatibility
local questRewardChoiceIcons = {}
local questLogRewardChoiceIcons = {}
local function hideRewardChoiceIcons()
for _, f in pairs(questRewardChoiceIcons) do
if not f:IsForbidden() then f:Hide() end
end
for _, f in pairs(questLogRewardChoiceIcons) do
if not f:IsForbidden() then f:Hide() end
end
end
local function createRewardChoiceIcons()
if not _G.QuestInfoRewardsFrame then return end
if not questRewardChoiceIcons["ratio"] then
questRewardChoiceIcons["ratio"] = _G.QuestInfoRewardsFrame:CreateTexture()
questRewardChoiceIcons["ratio"]:SetTexture("Interface/AddOns/" .. addonName .. "/Textures/rxp_logo-64")
questRewardChoiceIcons["ratio"]:SetSize(20, 20)
end
if questRewardChoiceIcons["ratio"].isHooked then return end
if not questRewardChoiceIcons["value"] then
questRewardChoiceIcons["value"] = _G.QuestInfoRewardsFrame:CreateTexture()
questRewardChoiceIcons["value"]:SetTexture("Interface/GossipFrame/VendorGossipIcon.blp")
questRewardChoiceIcons["value"]:SetSize(20, 20)
end
_G.QuestInfoRewardsFrame:HookScript("OnHide", hideRewardChoiceIcons)
-- "OnShow" equivalent is handled by QuestAutomation function
questRewardChoiceIcons["ratio"].isHooked = true
end
local function createLogRewardChoiceIcons()
if not _G.QuestLogDetailScrollFrame then return end
if not questLogRewardChoiceIcons["ratio"] then
questLogRewardChoiceIcons["ratio"] = _G.QuestLogDetailScrollFrame:CreateTexture()
questLogRewardChoiceIcons["ratio"]:SetTexture("Interface/AddOns/" .. addonName .. "/Textures/rxp_logo-64")
questLogRewardChoiceIcons["ratio"]:SetSize(20, 20)
end
if questLogRewardChoiceIcons["ratio"].isHooked then return end
if not questLogRewardChoiceIcons["value"] then
questLogRewardChoiceIcons["value"] = _G.QuestLogDetailScrollFrame:CreateTexture()
questLogRewardChoiceIcons["value"]:SetTexture("Interface/GossipFrame/VendorGossipIcon.blp")
questLogRewardChoiceIcons["value"]:SetSize(20, 20)
end
-- Triggers on open and selection in Classic
-- Only triggers on selection in Wrath
hooksecurefunc("SelectQuestLogEntry", function(questLogIndex)
hideRewardChoiceIcons()
addon.DisplayQuestLogRewards(questLogIndex)
end)
-- Hide icons on quest log close to avoid mislabeled rewards
_G.QuestLogDetailScrollFrame:HookScript("OnHide", hideRewardChoiceIcons)
if addon.gameVersion > 20000 then
-- Inefficient, but bypasses load order issues between helper functions
_G.QuestLogDetailScrollFrame:HookScript("OnShow", function ()
addon.DisplayQuestLogRewards()
end)
end
questLogRewardChoiceIcons["ratio"].isHooked = true
end
-- Retail has enough helpers and massive UI differences
if addon.gameVersion < 40000 then
createRewardChoiceIcons()
createLogRewardChoiceIcons()
end
local GetItemInfo = C_Item and C_Item.GetItemInfo or _G.GetItemInfo
local GetQuestLogSelection, GetNumQuestLogChoices = _G.GetQuestLogSelection,
_G.GetNumQuestLogChoices
local GetQuestLogChoiceInfo, GetQuestLogItemLink, GetQuestLogTitle =
_G.GetQuestLogChoiceInfo, _G.GetQuestLogItemLink, _G.GetQuestLogTitle
-- bestSellOption, bestRatioOption, options
local function evaluateQuestChoices(questID, numChoices, GetQuestItemInfo, GetQuestItemLink, GetQuestLogChoiceInfo)
local hardCodedReward = addon.GetStepQuestReward(questID)
-- If explicitly hard-coded .turnin reward choice, use that and exit
if addon.settings.profile.enableQuestRewardAutomation
and hardCodedReward > 0 then -- Quest has an explicit reward ID for .turnin step
return -1, hardCodedReward, {}
end
-- Only support hard-coded turnin values on Retail
if addon.gameVersion > 40000 then return -1, -1, {} end
local options = {}
local itemLink, isUsable, itemData
-- Load choices data
-- TODO retry or handle query failures
for i = 1, numChoices do
if GetQuestItemInfo then
isUsable = select(5, GetQuestItemInfo("choice", i))
else
isUsable = select(5, GetQuestLogChoiceInfo(i))
end
itemLink = GetQuestItemLink("choice", i)
if addon.itemUpgrades and addon.settings:IsEnabled('enableTips', 'enableItemUpgrades') then
itemData = addon.itemUpgrades:GetItemData(itemLink)
if itemData then
-- Returns nil if item not applicable
itemData.comparisons = addon.itemUpgrades:CompareItemWeight(itemLink) or {}
itemData.isUsable = isUsable
options[i] = itemData
end
else
local _, _, _, _, itemMinLevel, _, _, _, itemEquipLoc, _, sellPrice, _,
itemSubTypeID = GetItemInfo(itemLink)
-- Build ItemUpgrades object without comparisons
options[i] = {
itemLink = itemLink,
itemSubTypeID = itemSubTypeID,
itemEquipLoc = itemEquipLoc,
sellPrice = sellPrice,
itemMinLevel = itemMinLevel,
comparisons = {},
isUsable = isUsable
}
end
end
local bestSellOption, bestSellValue = -1, -1
local bestRatioOption, bestRatioValue = -1, 0
for choice, data in ipairs(options) do
if data.sellPrice > bestSellValue then
bestSellValue = data.sellPrice
bestSellOption = choice
end
-- Check for best compared upgrade
for _, compareData in ipairs(data.comparisons) do
if not compareData.Ratio then
if compareData.ItemLink == _G.EMPTY then
-- An item needs to be 10x better to beat an empty slot fill
bestRatioValue = 10.0
bestRatioOption = choice
end
elseif compareData.Ratio > bestRatioValue then
bestRatioValue = compareData.Ratio
bestRatioOption = choice
end
end
end
return bestSellOption, bestRatioOption, options
end
local function handleQuestComplete()
local id = GetQuestID()
if not id or id < 0 or addon.questTurnIn[id] == false or addon.disabledQuests[id] then return end
local numChoices = GetNumQuestChoices()
-- Automatically complete quests with no user choice
if numChoices <= 1 then
GetQuestReward(1)
addon:SendEvent("RXP_QUEST_TURNIN", id, numChoices, 1)
return
end
-- Pull quest handling out for .turnin legacy/hard-coded choices
local hardCodedReward = addon.GetStepQuestReward(id)
-- If explicitly hard-coded .turnin reward choice, use that and exit
-- Preserve simplest path for existing functionality
if hardCodedReward > 0 and
addon.settings.profile.enableQuestRewardAutomation then
GetQuestReward(hardCodedReward)
addon:SendEvent("RXP_QUEST_TURNIN", id, numChoices, hardCodedReward)
-- Hard-coded, so exit early to keep recommendations and QuestLog portions simpler
return
end
if not addon.settings.profile.enableTips or not addon.settings.profile.enableItemUpgrades then return end
local bestSellOption, bestRatioOption, options = evaluateQuestChoices(id, numChoices, GetQuestItemInfo, GetQuestItemLink)
if addon.gameVersion < 40000 and addon.settings.profile.enableQuestChoiceRecommendation then
if bestRatioOption > 0 then
local bestRatioFrame = QuestInfo_GetRewardButton(QuestInfoFrame.rewardsFrame, bestRatioOption)
if bestRatioFrame then
questRewardChoiceIcons["ratio"]:SetPoint("TOPRIGHT", bestRatioFrame , -1, 1)
questRewardChoiceIcons["ratio"]:SetParent(bestRatioFrame)
questRewardChoiceIcons["ratio"]:Show()
end
end
end
if addon.gameVersion < 40000 and addon.settings.profile.enableQuestChoiceGoldRecommendation then
local bestSellFrame = QuestInfo_GetRewardButton(QuestInfoFrame.rewardsFrame, bestSellOption)
if bestSellFrame then
if bestSellOption > 0 then
questRewardChoiceIcons["value"]:SetPoint("BOTTOMRIGHT", bestSellFrame , -1, 1)
questRewardChoiceIcons["value"]:SetParent(bestSellFrame)
questRewardChoiceIcons["value"]:Show()
end
-- No calculated best upgrade, so add recommendation to value as well, only if weights added
if addon.itemUpgrades and bestRatioOption < 1 then
questRewardChoiceIcons["ratio"]:SetPoint("TOPRIGHT", bestSellFrame , -1, 1)
questRewardChoiceIcons["ratio"]:SetParent(bestSellFrame)
questRewardChoiceIcons["ratio"]:Show()
end
end
end
-- If auto rewards disabled, abort because not doing anything further
-- also disables the auto picker if the quest is not in the guide
if not (addon.settings.profile.enableQuestChoiceAutomation and addon.questTurnIn[id]) then return end
-- upgrade is more useful than selling
if bestRatioOption > 0 then
-- if isUsable, then automatically pick
-- If not usable but recommended then leave the window open for user decision
if options and options[bestRatioOption].isUsable then
GetQuestReward(bestRatioOption)
addon:SendEvent("RXP_QUEST_TURNIN", id, numChoices, bestRatioOption)
end
elseif bestSellOption > 0 then
GetQuestReward(bestSellOption)
addon:SendEvent("RXP_QUEST_TURNIN", id, numChoices, bestSellOption)
end
end
-- Not hooked by createLogRewardChoiceIcons so never called on Retail
function addon.DisplayQuestLogRewards(questLogIndex)
if not questLogIndex or type(questLogIndex) == "table" then
questLogIndex = GetQuestLogSelection()
end
if questLogIndex < 1 then return end
local numChoices = GetNumQuestLogChoices()
if numChoices <= 1 then
return
end
local questID = select(8, GetQuestLogTitle(questLogIndex))
-- options third return only used for handleQuestComplete
local bestSellOption, bestRatioOption, _ = evaluateQuestChoices(questID, numChoices, nil, GetQuestLogItemLink, GetQuestLogChoiceInfo)
if addon.settings.profile.enableQuestChoiceRecommendation then
-- Classic is QuestLogItem, Wrath+ is QuestInfoRewardsFrameQuestInfoItem
local bestRatioFrame = _G['QuestLogItem' .. bestRatioOption] or
QuestInfo_GetRewardButton(QuestInfoFrame.rewardsFrame, bestRatioOption)
if bestRatioFrame then
questLogRewardChoiceIcons["ratio"]:SetPoint("TOPRIGHT", bestRatioFrame , -1, 1)
questLogRewardChoiceIcons["ratio"]:SetParent(bestRatioFrame)
questLogRewardChoiceIcons["ratio"]:Show()
end
end
if addon.settings.profile.enableQuestChoiceGoldRecommendation then
local bestSellFrame = _G['QuestLogItem' .. bestRatioOption] or
QuestInfo_GetRewardButton(QuestInfoFrame.rewardsFrame, bestSellOption)
if bestSellFrame then
questLogRewardChoiceIcons["value"]:SetPoint("BOTTOMRIGHT", bestSellFrame , -1, 1)
questLogRewardChoiceIcons["value"]:SetParent(bestSellFrame)
questLogRewardChoiceIcons["value"]:Show()
-- No calculated best upgrade, so add recommendation to value as well, only if weights added
if addon.itemUpgrades and bestRatioOption < 1 then
questLogRewardChoiceIcons["ratio"]:SetParent(bestSellFrame)
questLogRewardChoiceIcons["ratio"]:SetPoint("TOPRIGHT", bestSellFrame , -1, 1)
questLogRewardChoiceIcons["ratio"]:Show()
end
end
end
end
local turnInTimer = 0
function addon:QuestAutomation(event, arg1, arg2, arg3)
if not addon.settings.profile.enableQuestAutomation or IsControlKeyDown() or addon.isHidden then
return
end
if not event then
if _G.GossipFrame and _G.GossipFrame:IsShown() then
event = "GOSSIP_SHOW"
elseif _G.QuestFrameGreetingPanel and
_G.QuestFrameGreetingPanel:IsShown() then
event = "QUEST_GREETING"
elseif _G.QuestFrameProgressPanel and
_G.QuestFrameProgressPanel:IsShown() then
event = "QUEST_PROGRESS"
elseif _G.QuestFrameDetailPanel and _G.QuestFrameDetailPanel:IsShown() then
event = "QUEST_DETAIL"
elseif _G.QuestFrameRewardPanel and _G.QuestFrameRewardPanel:IsShown() or
_G.QuestFrameCompleteButton and
_G.QuestFrameCompleteButton:IsShown() then
event = "QUEST_COMPLETE"
else
return
end
end
--print(event)
if event == "QUEST_ACCEPT_CONFIRM" and addon.QuestAutoAccept(arg2) then
ConfirmAcceptQuest()
elseif event == "QUEST_COMPLETE" then
handleQuestComplete()
elseif event == "QUEST_PROGRESS" then
local id = GetQuestID()
if id and addon.disabledQuests[id] then
return
elseif IsQuestCompletable() then
CompleteQuest()
elseif addon.QuestAutoAccept(id) then
HideUIPanel(_G.QuestFrame)
elseif GetTime()-turnInTimer < 0.5 then
HideUIPanel(_G.QuestFrame)
turnInTimer = 0
end
-- questProgressTimer = GetTime()
elseif event == "QUEST_DETAIL" then
local id = GetQuestID()
if id and addon.disabledQuests[id] then
return
elseif addon.QuestAutoAccept(id) then
AcceptQuest()
HideUIPanel(_G.QuestFrame)
elseif GetTime()-turnInTimer < 0.5 then
HideUIPanel(_G.QuestFrame)
turnInTimer = 0
end
elseif event == "QUEST_ACCEPTED" then
local id = arg1 and arg2 or arg1
if (id == GetQuestID() or addon.QuestAutoAccept(id)) and not addon.disabledQuests[id] then
HideUIPanel(_G.QuestFrame)
end
elseif event == "QUEST_GREETING" then
local nActive = GetNumActiveQuests()
local nAvailable = GetNumAvailableQuests()
local title, isComplete
for i = 1, nActive do
title, isComplete = GetActiveTitle(i)
local reward,exists = addon.GetStepQuestReward(title)
if exists and isComplete then
return SelectActiveQuest(i)
end
end
if GossipGetNumOptions() == 0 and nAvailable == 1 and nActive == 0 then
SelectAvailableQuest(1)
else
for i = 1, nAvailable do
title, isComplete = GetAvailableTitle(i)
if addon.QuestAutoAccept(title) then
return SelectAvailableQuest(i)
end
end
end
elseif event == "GOSSIP_SHOW" then
local nActive = GossipGetNumActiveQuests()
local nAvailable = GossipGetNumAvailableQuests()
local quests, selectAvailableByQuestID, selectActiveByQuestID,
missingTurnIn
if C_GossipInfo.GetActiveQuests then
quests = C_GossipInfo.GetActiveQuests()
selectActiveByQuestID = true
end
for i = 1, nActive do
local title, isComplete
local reward,isAutoTurnIn
if type(quests) == "table" then
title = quests[i].questID
isComplete = quests[i].isComplete
reward,isAutoTurnIn = addon.GetStepQuestReward(title)
if not (isComplete or missingTurnIn) and isAutoTurnIn then
local objectives = addon.GetQuestObjectives(title)
missingTurnIn = objectives and objectives[1].generated and
(selectActiveByQuestID and title or i)
end
else
title, _, _, isComplete = select(i * 6 - 5,
GossipGetActiveQuests())
reward,isAutoTurnIn = addon.GetStepQuestReward(title)
end
if isComplete and isAutoTurnIn then
return GossipSelectActiveQuest(
selectActiveByQuestID and title or i)
end
end
local availableQuests
if C_GossipInfo.GetAvailableQuests then
availableQuests = C_GossipInfo.GetAvailableQuests()
selectAvailableByQuestID = true
end
if GossipGetNumOptions() == 0 and nAvailable == 1 and nActive == 0 and
not selectAvailableByQuestID then
return GossipSelectAvailableQuest(
selectAvailableByQuestID and availableQuests[1] and
availableQuests[1].questID or 1)
else
for i = 1, nAvailable do
local quest
if type(availableQuests) == "table" then
quest = availableQuests[i].questID
else
quest = select(i * 7 - 6, GossipGetAvailableQuests())
end
if addon.QuestAutoAccept(quest) then
return GossipSelectAvailableQuest(
selectAvailableByQuestID and quest or i)
end
end
end
if missingTurnIn then
return GossipSelectActiveQuest(missingTurnIn)
end
elseif event == "QUEST_TURNED_IN" and addon.questTurnIn[arg1] then
turnInTimer = GetTime()
elseif event == "QUEST_AUTOCOMPLETE" then
if arg1 and addon.disabledQuests[arg1] then
return
elseif (addon.gameVersion < 50000 and UnitLevel('player') ~= 85) then
for i = 1, GetNumAutoQuestPopUps() do
local id,status = GetAutoQuestPopUp(i)