-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.lua
776 lines (735 loc) · 33.6 KB
/
Main.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
local _, CGM = ...
-- Variables.
local GetStepIndexFromQuestID = {}
local ON_UPDATE_INTERVAL = 0.2
local updateTime = 0
local flightNode = 0
local flightName = ""
local flightStepIndex = 0
local isFlightDialogOpen = false
local timer
-- Localized globals.
local IsQuestFlaggedCompleted, IsOnQuest, GetQuestObjectives, GetQuestInfo = C_QuestLog.IsQuestFlaggedCompleted, C_QuestLog.IsOnQuest,
C_QuestLog.GetQuestObjectives, C_QuestLog.GetQuestInfo
local UnitXP, UnitLevel = UnitXP, UnitLevel
local GetItemCount, GetItemInfo = GetItemCount, C_Item.GetItemInfo
local GetContainerNumSlots, GetContainerItemInfo, UseContainerItem = C_Container.GetContainerNumSlots, C_Container.GetContainerItemInfo,
C_Container.UseContainerItem
-- Processes all tags in the given guide and replaces them with the proper strings.
local function ProcessTags(guide)
local tagsFound = false
for i = 1, #guide do
local step = guide[i]
if step.text then
for tag in step.text:gmatch("{(%w+)}") do
local tagLower = tag:lower()
if tagLower:find("questname") then
tagsFound = true
if step.isMultiStep then
local n = tonumber(tag:match("%a+(%d+)"))
if n then
local questID = step.questIDs[n]
if questID then
local questName = GetQuestInfo(questID)
if questName then
step.text = step.text:gsub("{" .. tag .. "}", questName)
end
end
end
elseif step.questID then
local questName = GetQuestInfo(step.questID)
if questName then
step.text = step.text:gsub("{" .. tag .. "}", questName)
end
end
elseif tagLower:find("itemname") then
tagsFound = true
local n = tonumber(tag:match("%a+(%d+)"))
if n then
local itemID = step.items[n]
if itemID then
local item = Item:CreateFromItemID(itemID)
item:ContinueOnItemLoad(function()
local itemName = item:GetItemName()
if itemName then
step.text = step.text:gsub("{" .. tag .. "}", itemName)
end
end)
end
end
elseif tagLower == "x" then
tagsFound = true
if step.x then
step.text = step.text:gsub("{" .. tag .. "}", step.x)
end
elseif tagLower == "y" then
tagsFound = true
if step.y then
step.text = step.text:gsub("{" .. tag .. "}", step.y)
end
elseif tagLower == "cost" then
tagsFound = true
local cost = step.cost
step.text = step.text:gsub("{" .. tag .. "}",
cost < 100 and cost .. "c" or (cost >= 100 and cost < 10000 and cost / 100 .. "s") or cost / 10000 ..
"g")
elseif tagLower == "spells" then
tagsFound = true
if step.spells then
local str = ""
for _, info in pairs(step.spells) do
str = str .. info.name .. ", "
end
step.text = step.text:gsub("{" .. tag .. "}", str:gsub(", $", ""))
end
else
CGM:Debug("unknown tag in " .. guide.name)
end
end
end
end
if tagsFound then
CGM:Message("found tags in " .. guide.name .. ". Tags are unreliable and thus deprecated - consider using the built-in guide maker. " ..
"If you did not make this guide then disregard this message.")
end
end
-- Attemps to buy items specified by the current step.
local function BuyFromMerchant()
local currentStep = CGM.currentStep
if currentStep.cost and GetMoney() < currentStep.cost then
CGM:Message("Cannot buy specified item(s), not enough money")
return
end
-- Assume player can afford it.
local currentStep = CGM.currentStep
for i = 1, GetMerchantNumItems() do
local itemID = GetMerchantItemID(i)
if currentStep.items[itemID] then
-- [itemID] = quantity
for _, quantity in pairs(currentStep.items) do
local _, itemLink = GetItemInfo(itemID)
CGM:Message("buying " .. (itemLink and itemLink or itemID) .. (quantity and "x" .. quantity) .. ".")
if currentStep.exploit then
local _, _, itemPrice, itemQuantity = GetMerchantItemInfo(i)
-- If the price per quantityToBuy is <= 0.5, the game simply gives it to you for free.
local quantityToBuy = 0.5 / (itemPrice / itemQuantity)
if quantityToBuy < 1 then
CGM:Message("Could not exploit-buy, not buying")
return
end
local bought = 0
-- This delay seems to be the sweetspot.
timer = C_Timer.NewTicker(0.11, function()
if bought >= quantity then
CGM:Fire("BUY_DONE")
return
end
BuyMerchantItem(i, quantityToBuy)
bought = bought + quantityToBuy
end)
else
BuyMerchantItem(i, quantity)
CGM:Fire("BUY_DONE")
end
end
end
end
end
-- Shows the GameTooltip on the given frame with the given lines and anchor.
function CGM:ShowGameTooltip(frame, lines, anchor)
GameTooltip:SetOwner(frame, anchor or "ANCHOR_RIGHT")
GameTooltip:AddLine("|cFFFFFFFFClassicGuideMaker|r")
for _, line in ipairs(lines) do
GameTooltip:AddLine(line)
end
GameTooltip:Show()
end
-- Hides the GameTooltip.
function CGM:HideGameTooltip()
GameTooltip:Hide()
end
-- Sets the current step to the given index.
function CGM:SetCurrentStep(index, shouldScroll)
if CGM:IsStepAvailable(index) and not CGM:IsStepCompleted(index) then
CGM.currentStepIndex = index
CGMOptions.savedStepIndex[CGM.currentGuideName] = index
local step = CGM.currentGuide[index]
CGM.currentStep = step
CGM:SetGoal(step.x / 100, step.y / 100, step.mapID)
if shouldScroll then
CGM.CGMFrame.bodyFrame.slider:SetValue(index - 1)
end
CGM.CGMFrame:SetStepCounterText(index .. "/" .. #CGM.currentGuide)
CGM:Debug("set step to " .. index)
CGM:AutoAcceptOnGossipShow()
end
end
-- Attempts to mark the step with the given index as completed.
function CGM:MarkStepCompleted(index, completed, isManual)
-- TODO: check that it can be marked incomplete here (i.e. if its been handed in already etc) -- temp
-- if marking a step incomplete here makes the current step unavailable, should go back to step index #currentStep.requiredSteps (the last step in that
-- table) or if that is unvailable then go to #currentStep.requiredSteps - 1 etc. (see OnItemUpdate)
CGM:Debug("marked " .. index .. " as " .. (completed and "completed" or "not completed"))
CGMOptions.completedSteps[CGM.currentGuideName][index] = completed or nil
if completed and isManual then
CGM:ScrollToNextIncomplete()
end
end
-- Checks if the step with the given index in the currently selected guide is completed. Returns true if so, false otherwise.
function CGM:IsStepCompleted(index)
-- CGM:Debug("checking if " .. index .. " is completed...")
if CGMOptions.completedSteps[CGM.currentGuideName][index] then
return true
end
local step = CGM.currentGuide[index]
local type = step.type
local questID = step.questID
-- Check if the quest is completed, if it isn't, check if it's in the quest log.
if type == CGM.Types.Accept then
if not (IsQuestFlaggedCompleted(questID) or IsOnQuest(questID)) then
return false
end
elseif type == CGM.Types.Item and not IsQuestFlaggedCompleted(questID) then
-- First check if the player has completed the associated quest, then check if the items are in the player's bags.
if IsQuestFlaggedCompleted(step.questID) then
return true
else
for itemID, itemCount in pairs(step.items) do
if GetItemCount(itemID) < itemCount then
return false
end
end
end
elseif type == CGM.Types.Do then
-- Check if quest is complete in quest log, and if not then check if the player has completed all objectives of the quest(s).
local questObjectives
if step.isMultiStep then
for i = 1, #step.questIDs do
-- Not all quests have been completed.
if not (IsQuestComplete(step.questIDs[i]) or IsQuestFlaggedCompleted(step.questIDs[i])) then
return false
else
questObjectives = GetQuestObjectives(step.questIDs[i])
-- If this is nil, can assume the quest is a simple "go talk to this guy" quest.
if questObjectives then
-- Need to explicitly check for nil AND false since if questObjectives isn't nil but empty, we can assume the same as above.
if questObjectives.finished ~= nil and not questObjectives.finished then
return false
end
end
end
end
else
questObjectives = GetQuestObjectives(questID)
if not (IsQuestComplete(questID) or IsQuestFlaggedCompleted(questID) or
(questObjectives and questObjectives.finished ~= nil and questObjectives.finished)) then
return false
end
end
elseif type == CGM.Types.Deliver then
-- Simply check if the quest has been completed.
if not IsQuestFlaggedCompleted(questID) then
return false
end
elseif type == CGM.Types.Grind then
-- Check for level/xp.
if not (UnitLevel("player") >= step.level and UnitXP("player") >= step.xp) then
return false
end
elseif type == CGM.Types.Coordinate then
-- First check if the quest has been completed, then check if the next step has been completed and return that.
if not questID or not (IsQuestFlaggedCompleted(questID) or (CGM.currentGuideName[index + 1] and CGM:IsStepCompleted(index + 1))) then
return false
end
elseif type == CGM.Types.Buy then
-- Can't check if player already has item in case the guide asks them to buy a duplicate item so just return false by default.
return false
elseif type == CGM.Types.Train then
-- If just one of the spells in the list isn't known, return false.
local spells = step.spells
for spellID in pairs(spells) do
if not IsSpellKnown(spellID) then
return false
end
end
elseif type == CGM.Types.Fly then
-- This will manually be marked completed when player has flown.
return false
elseif type == CGM.Types.Inn then
-- This will manually be marked completed
return false
end
-- If the player removes an item from bags, this should return false.
if type ~= CGM.Types.Item or (type == CGM.Types.Item and IsQuestFlaggedCompleted(questID)) then
CGM:MarkStepCompleted(index, true)
end
-- CGM:Debug(index .. " is completed")
return true
end
-- Returns true if the given step index is available to the player, false otherwise.
function CGM:IsStepAvailable(index)
-- CGM:Debug("checking if " .. index .. " is available...")
local step = CGM.currentGuide[index]
local questID = step.questID or step.questIDs
if step.requiresLevel then
if UnitLevel("player") < step.requiresLevel then
return false
end
end
if step.lockedBySteps then
for i = 1, #step.lockedBySteps do
if CGM:IsStepCompleted(step.lockedBySteps[i]) then
return false
end
end
end
local type = step.type
-- This should always be checking backward, never forward.
-- If the quest isn't marked "complete" in the quest log, return false.
if type == CGM.Types.Deliver then
return IsQuestComplete(questID)
elseif type == CGM.Types.Do then
if step.isMultiStep then
for i = 1, #questID do
if IsOnQuest(questID[i]) then
return true
end
end
return false
else
return IsOnQuest(questID)
end
elseif step.requiresSteps and (type == CGM.Types.Accept or type == CGM.Types.Item) then
for i = 1, #step.requiresSteps do
if not self:IsStepCompleted(step.requiresSteps[i]) then
return false
end
end
elseif type == CGM.Types.Buy or type == CGM.Types.Train then
if GetMoney() < step.cost then
return false
end
end
-- CGM:Debug(index .. " is available")
-- No requirements for this step.
return true
end
--[[
Returns true if we should auto accept quests.
Will be true if:
* Auto accept is on and modifier key is set to None (auto accept no matter what).
* Auto accept is NOT on and modifier key is set to SHIFT/CTRL/ALT and that key IS down (don't auto accept unless modifier key is down).
Will be false if:
* Auto accept is on and modifier key is set to SHIFT/CTRL/ALT and that key is down (auto accept except if modifier key is down).
* Auto accept is NOT on and modifier key is set to None (DON'T auto accept no matter what).
--]]
function CGM:ShouldAuto()
local mod = CGMOptions.settings.modifier
if CGMOptions.settings.autoAccept then
if mod == CGM.Modifiers.None then
return true
else
return not CGM:IsModifierDown()
end
else
-- Auto accept only if modifier key is not "None" and is down.
if mod == CGM.Modifiers.None then
return false
else
return CGM:IsModifierDown()
end
end
end
-- Called when buying is done.
function CGM:OnBuyDone()
if timer and not timer:IsCancelled() then
timer:Cancel()
end
-- @TODO: this should only mark it complete if it actually buys everything
CGM:MarkStepCompleted(CGM.currentStepIndex, true)
CGM:ScrollToNextIncomplete()
end
-- Called on ITEM_UPDATE. Checks if the item that was just added or removed was an item required by the current step.
function CGM:OnItemUpdate()
if CGM.currentStep.type == CGM.Types.Item and CGM:IsStepCompleted(CGM.currentStepIndex) then
CGM:ScrollToNextIncomplete() -- Calls UpdateStepFrames.
else
CGM:UpdateStepFrames()
end
-- If by deleting an item, it made another step unavailable.
local requiresSteps = CGM.currentStep.requiresSteps
if not CGM:IsStepAvailable(CGM.currentStepIndex) then
if requiresSteps then
-- Go backwards until the first available step in requiredSteps.
for i = #requiresSteps, 1, -1 do
if CGM:IsStepAvailable(requiresSteps[i]) then
-- Calls UpdateStepFrames.
CGM:ScrollToIndex(requiresSteps[i])
end
end
end
end
end
-- Called when a bag's inventory is changed.
function CGM:OnBagUpdate(bag)
if bag >= BACKPACK_CONTAINER then
CGM:ScanBag(bag)
CGM:Fire("ITEM_UPDATE")
end
end
-- Called on QUEST_ACCEPTED (when the player has accepted a quest).
function CGM:OnQuestAccepted(_, questID)
CGM:Debug("quest accepted: " .. GetQuestInfo(questID) .. " (id: " .. questID .. ")")
-- No need to check any steps for the quest ID since we check for step completion dynamically when scrolling. Just update current steps.
-- We should only scroll if the current step is of type Accept and has the same questID as this one.
local currentStep = CGM.currentStep
if currentStep.type == CGM.Types.Accept and currentStep.questID == questID then
CGM:ScrollToNextIncomplete() -- Calls UpdateStepFrames().
else
if CGM:IsStepAvailable(CGM.currentStepIndex) then
CGM:UpdateStepFrames()
else
-- If the current step gets locked because the player picked up another quest, should scroll to next.
CGM:ScrollToNextIncomplete()
end
end
end
-- Called on QUEST_TURNED_IN (when the player has handed in a quest).
function CGM:OnQuestTurnedIn(questID)
CGM:Debug("quest turned in: " .. GetQuestInfo(questID) .. " (id: " .. questID .. ")")
-- Quests aren't instantly marked as complete so need to manually mark them.
-- Should simply just mark all steps containing this quest ID to completed, except if its a multi-step, in which case we check all the quests in that step
-- before marking.
local stepIndeces = GetStepIndexFromQuestID(questID)
if stepIndeces then -- If the quest actually exists in the guide.
for i = 1, #stepIndeces do
local step = CGM.currentGuide[stepIndeces[i]]
if step.isMultiStep then
local isComplete = true
for j = 1, #step.questIDs do
local currQuestID = step.questIDs[j]
-- Important to not check given quest ID since it will not be completed yet.
isComplete = currQuestID ~= questID and not IsQuestFlaggedCompleted(currQuestID)
end
CGM:MarkStepCompleted(stepIndeces[i], isComplete)
else
CGM:MarkStepCompleted(stepIndeces[i], true)
end
end
-- Won't scroll if current step is incomplete.
CGM:ScrollToNextIncomplete()
end
end
-- Called on QUEST_REMOVED (when a quest has been removed from the player's quest log).
function CGM:OnQuestRemoved(questID)
CGM:Debug("quest removed: " .. GetQuestInfo(questID) .. " (id: " .. questID .. ")")
-- If the player abandonded a quest, it's assumed the player didn't want to do that part of the guide, so skip ahead to the next available quest (if the
-- current step is now unavailable).
local stepIndeces = GetStepIndexFromQuestID(questID)
-- If the quest actually exists in the guide.
if stepIndeces then
for i = 1, #stepIndeces do
local step = CGM.currentGuide[stepIndeces[i]]
if step.type == CGM.Types.Accept then
if not IsQuestFlaggedCompleted(step.questID) then
CGM:MarkStepCompleted(stepIndeces[i], false)
end
elseif step.type == CGM.Types.Do then
CGM:MarkStepCompleted(stepIndeces[i], CGM:IsStepCompleted(stepIndeces[i]))
end
end
if not CGM:IsStepAvailable(CGM.currentStepIndex) then
-- Calls UpdateStepFrames.
CGM:ScrollToNextIncomplete()
else
CGM:UpdateStepFrames()
end
end
end
-- Called on UNIT_QUESTLOG_CHANGED (when a quest's objectives are changed [and at other times]).
function CGM:OnUnitQuestLogChanged(unit)
-- This function is a special case. If the player is not on all quests of the step (if multistep) then scroll to next, except don't mark the step as
-- completed.
if unit == "player" then
local currentStep = CGM.currentStep
if currentStep.type == CGM.Types.Do then
if CGM:IsStepCompleted(CGM.currentStepIndex) then
CGM:ScrollToNextIncomplete() -- Calls UpdateStepFrames.
else
-- Updates the objective text on the step frame. Gets called for a second time here if picking up a quest while on "Do" step, but that's fine.
CGM:UpdateStepFrames()
end
end
end
end
-- Called on PLAYER_XP_UPDATE (when the player receives XP).
function CGM:OnPlayerXPUpdate()
local currentStep = CGM.currentStep
if currentStep.type == CGM.Types.Grind and CGM:IsStepCompleted(CGM.currentStepIndex) then
if CGM:IsStepCompleted(CGM.currentStepIndex) then
CGM:ScrollToNextIncomplete()
end
else
CGM:UpdateStepFrames()
end
end
-- Called on COORDINATES_REACHED (when the player has reached the current step coordinates).
function CGM:OnCoordinatesReached()
if CGM.currentStep.type == CGM.Types.Coordinate then
CGM:MarkStepCompleted(CGM.currentStepIndex, true)
CGM:ScrollToNextIncomplete()
end
end
-- Called on MERCHANT_SHOW (whenever the player visits a vendor). Sells any items in the player's bags that are specified by the guide.
function CGM:OnMerchantShow()
if CGM:ShouldAuto() then
local itemsToSell = CGM.currentGuide.itemsToSell
if itemsToSell then
for bag = BACKPACK_CONTAINER, NUM_BAG_SLOTS do
for slot = 1, GetContainerNumSlots(bag) do
local slotInfo = GetContainerItemInfo(bag, slot)
local itemID
if slotInfo then
itemID = slotInfo.itemID
end
if itemID and itemsToSell[itemID] then
CGM:Message("selling " .. slotInfo.hyperlink .. (slotInfo.stackCount > 1 and "x" .. slotInfo.stackCount .. "." or "."))
UseContainerItem(bag, slot)
end
end
end
end
local npcID = CGM:UnitID("npc")
if npcID == CGM.currentStep.unitID and CGM.currentStep.type == CGM.Types.Buy then
BuyFromMerchant()
end
end
end
-- Called on MERCHANT_UPDATE (whenever an item is sold or purchased).
function CGM:OnMerchantUpdate()
-- @TODO: this should check if we sold and if we can now afford the item
-- local currentStep = CGM.currentStep
-- if currentStep.type == CGM.Types.Buy then
-- BuyFromMerchant()
-- end
end
-- Called on TRAINER_SHOW (whenever the trainer window shows). Trains any spells specified in the current step, if any.
function CGM:OnTrainerShow()
local currentStep = CGM.currentStep
if CGM:ShouldAuto() and currentStep.type == CGM.Types.Train and currentStep.spells then
for _, info in pairs(currentStep.spells) do
for i = 1, GetNumTrainerServices() do
local name, rank = GetTrainerServiceInfo(i)
rank = rank and tonumber(rank:match("(%d+)")) or rank
if info.name == name and (info.rank == rank or rank == nil or rank == "") then
CGM:Message("Training " .. name)
BuyTrainerService(i)
break
end
end
end
-- There is a delay between game returning true for spells being known.
C_Timer.After(0.5, function()
CGM:IsStepCompleted(CGM.currentStepIndex)
end)
end
end
-- Called on QUEST_COMPLETE. Fires when the player is able to finally complete a quest (and choose a reward if there is any).
function CGM:OnGossipShow()
CGM:AutoAcceptOnGossipShow()
if CGM:ShouldAuto() and CGM.currentStep.type == CGM.Types.Fly then
local options = C_GossipInfo.GetOptions()
if options and #options > 0 then
for i = 1, #options do
-- Not ideal Blizz.
if options[i].icon == 132057 then
CGM:Debug("flight master detected, opening dialog")
C_GossipInfo.SelectOption(options[i].gossipOptionID)
end
end
end
elseif CGM:ShouldAuto() and CGM.currentStep.type == CGM.Types.Train then
local options = C_GossipInfo.GetOptions()
if options and #options > 0 then
for i = 1, #options do
if options[i].icon == 132058 then
CGM:Debug("trainer detected, opening dialog")
C_GossipInfo.SelectOption(options[i].gossipOptionID)
end
end
end
elseif CGM:ShouldAuto() and CGM.currentStep.type == CGM.Types.Inn then
local options = C_GossipInfo.GetOptions()
if options and #options > 0 then
for i = 1, #options do
if options[i].icon == 132052 then
CGM:Debug("innkeeper detected, opening dialog")
C_GossipInfo.SelectOption(options[i].gossipOptionID)
CGM:Message("Setting HS")
C_Timer.After(0.25, function()
C_PlayerInteractionManager.ConfirmationInteraction(Enum.PlayerInteractionType.Binder)
C_PlayerInteractionManager.ClearInteraction(Enum.PlayerInteractionType.Binder)
CGM:MarkStepCompleted(CGM.currentStepIndex, true)
end)
end
end
end
end
end
-- Hooked into eventFrame.OnUpdate. Checks if we are able to fly or not.
function CGM:OnUpdate(elapsed, forceRun)
updateTime = updateTime + elapsed
if forceRun or updateTime > ON_UPDATE_INTERVAL then
-- This likely means the player has manually changed step.
if flightStepIndex ~= CGM.currentStepIndex then
CGM:Debug("step indeces do not match, did step manually get changed?")
CGM.eventFrame:SetScript("OnUpdate", nil)
updateTime = 0
return
end
-- First check if we're on a taxi. If we are, we can return. Unhook OnUpdate just to be sure.
if UnitOnTaxi("player") then
CGM.eventFrame:SetScript("OnUpdate", nil)
CGM:Debug("flight to " .. flightName .. " probably successful, unhooking OnUpdate")
CGM:MarkStepCompleted(flightStepIndex, true)
CGM:ScrollToNextIncomplete()
flightNode = 0
flightName = ""
flightStepIndex = 0
elseif isFlightDialogOpen and flightNode > 0 then
TakeTaxiNode(flightNode)
end
updateTime = 0
end
end
-- Called on TAXIMAP_OPENED. Automatically flies to the node specified by the current step.
function CGM:OnTaximapOpened()
if CGM:ShouldAuto() and CGM.currentStep.type == CGM.Types.Fly then
local step = CGM.currentStep
for i = 1, NumTaxiNodes() do
if step.nodeName:lower() == TaxiNodeName(i):lower() then
isFlightDialogOpen = true
flightNode = i
flightName = TaxiNodeName(i)
flightStepIndex = CGM.currentStepIndex
CGM:Debug("found taxi node, running OnUpdate")
-- Required in order to take a connecting flight. Only has to be called once.
TaxiNodeOnButtonEnter(_G["TaxiButton" .. flightNode])
-- Hide the tooltip that shows since we technically "hover" over the taxi button.
GameTooltip:Hide()
CGM:Message("attempting to fly to " .. flightName .. "...")
CGM.eventFrame:HookScript("OnUpdate", CGM.OnUpdate)
break
end
end
end
end
-- Called on TAXIMAP_CLOSED.
function CGM:OnTaximapClosed()
isFlightDialogOpen = false
CGM:Debug("taximap dialog closed")
CGM.eventFrame:SetScript("OnUpdate", nil)
-- Call this one last time in case dialog closed before OnUpdate could check if we're on a taxi.
C_Timer.After(1, function()
-- Means we haven't detected flight yet but since we just stopped OnUpdate...
if flightStepIndex == CGM.currentStepIndex and flightNode > 0 then
CGM:OnUpdate(0, true)
end
end)
end
-- Register a new guide for the addon.
function CGM:RegisterGuide(guide)
-- TODO: This function should check each step to make sure it has legal fields (i.e. there cant be any multistep Deliver steps etc)
if guide then
if guide.name then
-- Default to first registered.
CGM.defaultGuide = CGM.defaultGuide or guide.name
if CGM.Guides[guide.name] then
CGM:Message("guide with name " .. guide.name .. " is already registered - name must be unique.")
else
CGM.Guides[guide.name] = guide
end
else
CGM:Message(guide[1] and "the guide has no name. To help you identify which guide it is, here is the first step description:\n" ..
guide[1].text or "the guide has no name!")
end
else
CGM:Message("a guide table needs to be provided when registering a guide.")
end
end
-- Sets the currently displayed guide to the given guide (has to have been registered first).
function CGM:SetGuide(guideName)
if CGM.Guides[guideName] then
CGM:Debug("setting guide to " .. guideName)
ProcessTags(CGM.Guides[guideName])
CGMOptions.settings.currentGuide = guideName
CGM.guideDropdown:SetText(guideName) -- This hurts.
CGMOptions.completedSteps[guideName] = CGMOptions.completedSteps[guideName] or {}
CGM.currentGuideName = guideName
CGM.currentGuide = CGM.Guides[guideName]
-- First unregister from all events, then re-register if guide contains the types later.
CGM:UnregisterWowEvent("MERCHANT_SHOW")
CGM:UnregisterWowEvent("MERCHANT_UPDATE")
CGM:UnregisterWowEvent("TRAINER_SHOW")
CGM:UnregisterWowEvent("TAXIMAP_OPENED")
CGM:UnregisterWowEvent("TAXIMAP_CLOSED")
CGM:UnregisterWowEvent("MERCHANT_SHOW")
CGM:UnregisterWowEvent("BAG_UPDATE")
CGM.CGMFrame:SetTitleText(CGM.currentGuideName)
CGM:UpdateSlider()
-- Call this because some may have been disabled if previous guide had fewer steps than CGMOptions.settings.nbrSteps (meaning unused frames).
CGM:EnableAllStepFrames()
CGM:DisableUnusedStepFrames()
if CGMOptions.savedStepIndex[guideName] then
CGM:SetCurrentStep(CGMOptions.savedStepIndex[guideName], true)
CGM:UpdateStepFrames()
else
CGM:ScrollToFirstIncomplete()
end
-- Register for any relevant events and map quest IDs to step indeces so we don't have to iterate all steps to find them.
if CGM.currentGuide.itemsToSell then
CGM:RegisterWowEvent("MERCHANT_SHOW", CGM.OnMerchantShow)
end
if CGM.currentGuide.itemsToDelete then
CGM:RegisterWowEvent("BAG_UPDATE", CGM.OnBagUpdate)
for bag = BACKPACK_CONTAINER, NUM_BAG_SLOTS do
CGM:ScanBag(bag)
end
end
for i = 1, #CGM.currentGuide do
local type = CGM.currentGuide[i].type
if type == CGM.Types.Buy then
CGM:RegisterWowEvent("MERCHANT_SHOW", CGM.OnMerchantShow)
CGM:RegisterWowEvent("MERCHANT_UPDATE", CGM.OnMerchantUpdate)
elseif type == CGM.Types.Train then
CGM:RegisterWowEvent("TRAINER_SHOW", CGM.OnTrainerShow)
elseif type == CGM.Types.Fly then
CGM:RegisterWowEvent("TAXIMAP_OPENED", CGM.OnTaximapOpened)
CGM:RegisterWowEvent("TAXIMAP_CLOSED", CGM.OnTaximapClosed)
elseif type == CGM.Types.Item then
CGM:RegisterWowEvent("BAG_UPDATE", CGM.OnBagUpdate)
end
local questID = CGM.currentGuide[i].questID or CGM.currentGuide[i].questIDs
if questID then
if CGM.currentGuide[i].isMultiStep then
for j = 1, #questID do
GetStepIndexFromQuestID[questID[j]] = GetStepIndexFromQuestID[questID[j]] or {}
GetStepIndexFromQuestID[questID[j]][#GetStepIndexFromQuestID[questID[j]] + 1] = i
end
else
GetStepIndexFromQuestID[questID] = GetStepIndexFromQuestID[questID] or {}
GetStepIndexFromQuestID[questID][#GetStepIndexFromQuestID[questID] + 1] = i
end
end
end
setmetatable(GetStepIndexFromQuestID, {
__call = function(self, questID)
return self[questID]
end
})
-- temp, remove this
-- CGM:Translate(CGM.currentGuide, CGM.GuideFormats.ClassicLeveler, CGM.GuideFormats.ClassicGuideMaker)
else
CGM:Debug(guideName .. " hasn't been registered yet - can't set the guide")
end
end