Added a small filter for removing field repair bots from the static data (English...
[QuestHelper.git] / main.lua
blobfd4652959f2966acee322fec8c606fcb85fa1c01
1 QuestHelper_File["main.lua"] = "Development Version"
3 QuestHelper = CreateFrame("Frame", "QuestHelper", nil)
5 -- Just to make sure it's always 'seen' (there's nothing that can be seen, but still...), and therefore always updating.
6 QuestHelper:SetFrameStrata("TOOLTIP")
8 QuestHelper_SaveVersion = 7
9 QuestHelper_CharVersion = 1
10 QuestHelper_Locale = GetLocale() -- This variable is used only for the collected data, and has nothing to do with displayed text.
11 QuestHelper_Quests = {}
12 QuestHelper_Objectives = {}
14 QuestHelper_Pref =
17 QuestHelper_DefaultPref =
19 filter_level=true,
20 filter_zone=false,
21 filter_done=false,
22 filter_blocked=false, -- Hides blocked objectives, such as quest turn-ins for incomplete quests
23 track=true,
24 track_minimized=false,
25 track_scale=1,
26 track_level=true,
27 track_qcolour=true,
28 track_ocolour=true,
29 track_size=8,
30 tooltip=true,
31 share = true,
32 scale = 1,
33 solo = false,
34 comm = false,
35 show_ants = true,
36 level = 2,
37 hide = false,
38 cart_wp = true,
39 tomtom_wp = true,
40 flight_time = true,
41 locale = GetLocale(), -- This variable is used for display purposes, and has nothing to do with the collected data.
42 perf_scale = 1, -- How much background processing can the current machine handle? Higher means more load, lower means better performance.
43 map_button = true
46 QuestHelper_FlightInstructors = {}
47 QuestHelper_FlightLinks = {}
48 QuestHelper_FlightRoutes = {}
49 QuestHelper_KnownFlightRoutes = {}
51 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
52 QuestHelper.objective_objects = {}
53 QuestHelper.user_objectives = {}
54 QuestHelper.quest_objects = {}
55 QuestHelper.player_level = 1
56 QuestHelper.locale = QuestHelper_Locale
58 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
59 (UnitFactionGroup("player") == "Horde" and 2)
61 assert(QuestHelper.faction)
63 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
65 function QuestHelper:GetFontPath(list_string, font)
66 if list_string then
67 for name in string.gmatch(list_string, "[^;]+") do
68 if font:SetFont(name, 10) then
69 return name
70 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
71 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
72 end
73 end
74 end
75 end
77 function QuestHelper:SetLocaleFonts()
78 self.font.sans = nil
79 self.font.serif = nil
80 self.font.fancy = nil
82 local font = self:CreateText(self)
84 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
85 -- Only use alternate fonts if using a language the client wasn't intended for.
86 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
87 if replacements then
88 self.font.sans = self:GetFontPath(replacements.sans, font)
89 self.font.serif = self:GetFontPath(replacements.serif, font)
90 self.font.fancy = self:GetFontPath(replacements.fancy, font)
91 end
92 end
94 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
95 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
96 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
98 self:ReleaseText(font)
100 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
101 self.font.serif = self.font.serif or GameFontNormal:GetFont()
102 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
104 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
105 -- This should do nothing if not using an alternate font.
106 DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
108 if QuestHelperWorldMapButton then
109 QuestHelperWorldMapButton:SetFont(self.font.serif, select(2, QuestHelperWorldMapButton:GetFont()))
113 QuestHelper.route = {}
114 QuestHelper.to_add = {}
115 QuestHelper.to_remove = {}
116 QuestHelper.quest_log = {}
117 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
118 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
120 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
121 local last = self:NumLines()
122 local name = self:GetName()
123 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
126 function QuestHelper:SetTargetLocation(i, x, y, toffset)
127 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
128 local c, z = unpack(QuestHelper_ZoneLookup[i])
130 self.target = self:CreateTable()
131 self.target[2] = self:CreateTable()
133 self.target_time = time()+(toffset or 0)
135 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
136 self.target[1] = self.zone_nodes[i]
137 self.target[3] = x * self.continent_scales_x[c]
138 self.target[4] = y * self.continent_scales_y[c]
140 for i, n in ipairs(self.target[1]) do
141 local a, b = n.x-self.target[3], n.y-self.target[4]
142 self.target[2][i] = math.sqrt(a*a+b*b)
146 function QuestHelper:UnsetTargetLocation()
147 -- Unsets the target set above.
148 if self.target then
149 self:ReleaseTable(self.target[2])
150 self:ReleaseTable(self.target)
151 self.target = nil
152 self.target_time = nil
156 function QuestHelper:OnEvent(event)
157 if event == "VARIABLES_LOADED" then
158 local file_problem = false
159 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
161 local expected_files =
163 ["upgrade.lua"] = true,
164 ["main.lua"] = true,
165 ["recycle.lua"] = true,
166 ["objective.lua"] = true,
167 ["quest.lua"] = true,
168 ["questlog.lua"] = true,
169 ["utility.lua"] = true,
170 ["dodads.lua"] = true,
171 ["graph.lua"] = true,
172 ["teleport.lua"] = true,
173 ["pathfinding.lua"] = true,
174 ["routing.lua"] = true,
175 ["custom.lua"] = true,
176 ["menu.lua"] = true,
177 ["hidden.lua"] = true,
178 ["nag.lua"] = true,
179 ["comm.lua"] = true,
180 ["mapbutton.lua"] = true,
181 ["help.lua"] = true,
182 ["pattern.lua"] = true,
183 ["flightpath.lua"] = true,
184 ["tracker.lua"] = true,
185 ["objtips.lua"] = true,
186 ["cartographer.lua"] = true,
187 ["tomtom.lua"] = true,
188 ["textviewer.lua"] = true
191 for file, version in pairs(QuestHelper_File) do
192 if not expected_files[file] then
193 DEFAULT_CHAT_FRAME:AddMessage("Unexpected QuestHelper file: "..file)
194 file_problem = true
195 elseif version ~= expected_version then
196 DEFAULT_CHAT_FRAME:AddMessage("Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')")
197 if version ~= "Development Version" then
198 -- Developers are allowed to mix dev versions with release versions
199 file_problem = true
204 for file in pairs(expected_files) do
205 if not QuestHelper_File[file] then
206 DEFAULT_CHAT_FRAME:AddMessage("Missing QuestHelper file: "..file)
207 file_problem = true
211 -- Don't need this table anymore.
212 QuestHelper_File = nil
214 if file_problem then
215 DEFAULT_CHAT_FRAME:AddMessage("QuestHelper hasn't been installed properly.")
216 message("QuestHelper hasn't been installed properly.")
217 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
218 return
221 if not QuestHelper_StaticData then
222 -- If there is no static data for some mysterious reason, create an empty table so that
223 -- other parts of the code can carry on as usual, using locally collected data if it exists.
224 QuestHelper_StaticData = {}
227 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
229 if not QuestHelper_UID then
230 QuestHelper_UID = self:CreateUID()
232 QuestHelper_SaveDate = time()
234 QuestHelper_BuildZoneLookup()
236 if QuestHelper_Locale ~= GetLocale() then
237 self:TextOut(QHText("LOCALE_ERROR"))
238 return
241 self.Astrolabe = DongleStub("Astrolabe-0.4")
243 if not self:ZoneSanity() then
244 self:TextOut(QHText("ZONE_LAYOUT_ERROR"))
245 message("QuestHelper: "..QHText("ZONE_LAYOUT_ERROR"))
246 return
249 QuestHelper_UpgradeDatabase(_G)
250 QuestHelper_UpgradeComplete()
252 if QuestHelper_SaveVersion ~= 8 then
253 self:TextOut(QHText("DOWNGRADE_ERROR"))
254 return
257 self.player_level = UnitLevel("player")
259 self:ResetPathing()
261 self:UnregisterEvent("VARIABLES_LOADED")
262 self:RegisterEvent("PLAYER_TARGET_CHANGED")
263 self:RegisterEvent("LOOT_OPENED")
264 self:RegisterEvent("QUEST_COMPLETE")
265 self:RegisterEvent("QUEST_LOG_UPDATE")
266 self:RegisterEvent("QUEST_PROGRESS")
267 self:RegisterEvent("MERCHANT_SHOW")
268 self:RegisterEvent("QUEST_DETAIL")
269 self:RegisterEvent("TAXIMAP_OPENED")
270 self:RegisterEvent("PLAYER_CONTROL_GAINED")
271 self:RegisterEvent("PLAYER_CONTROL_LOST")
272 self:RegisterEvent("PLAYER_LEVEL_UP")
273 self:RegisterEvent("PARTY_MEMBERS_CHANGED")
274 self:RegisterEvent("CHAT_MSG_ADDON")
275 self:RegisterEvent("CHAT_MSG_SYSTEM")
276 self:RegisterEvent("BAG_UPDATE")
277 self:RegisterEvent("GOSSIP_SHOW")
279 for key, def in pairs(QuestHelper_DefaultPref) do
280 if QuestHelper_Pref[key] == nil then
281 QuestHelper_Pref[key] = def
285 self:SetLocaleFonts()
287 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
288 self:EnableSharing()
291 if QuestHelper_Pref.hide then
292 self.map_overlay:Hide()
295 self:HandlePartyChange()
296 self:Nag("all")
298 for locale in pairs(QuestHelper_StaticData) do
299 if locale ~= self.locale then
300 -- Will delete references to locales you don't use.
301 QuestHelper_StaticData[locale] = nil
305 local static = QuestHelper_StaticData[self.locale]
307 if static then
308 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
309 if faction ~= self.faction then
310 -- Will delete references to flight instructors that don't belong to your faction.
311 static.flight_instructors[faction] = nil
313 end end
315 if static.quest then for faction in pairs(static.quest) do
316 if faction ~= self.faction then
317 -- Will delete references to quests that don't belong to your faction.
318 static.quest[faction] = nil
320 end end
323 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
324 if not QuestHelper_CharVersion then
325 -- Changing per-character flight routes, now only storing the flight points they have,
326 -- will attempt to guess the routes from this.
327 local routes = {}
329 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
330 for key in pairs(l) do
331 routes[key] = true
335 QuestHelper_KnownFlightRoutes = routes
337 -- Deleting the player's home again.
338 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
339 QuestHelper_Home = nil
340 QuestHelper_CharVersion = 1
343 if not QuestHelper_Home then
344 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
345 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
348 self.minimap_dodad = self:CreateMipmapDodad()
350 if QuestHelper_Pref.map_button then
351 QuestHelper:InitMapButton()
354 if QuestHelper_Pref.cart_wp then
355 self:EnableCartographer()
358 if QuestHelper_Pref.tomtom_wp then
359 self:EnableTomTom()
362 self.tracker:SetScale(QuestHelper_Pref.track_scale)
364 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
365 self:ShowTracker()
368 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
369 if QuestHelper_Version ~= version then
370 QuestHelper_Version = version
371 self:ChangeLog()
374 self:SetScript("OnUpdate", self.OnUpdate)
376 collectgarbage("collect") -- Free everything we aren't using.
378 if self.debug_objectives then
379 for name, data in pairs(self.debug_objectives) do
380 self:LoadDebugObjective(name, data)
385 if event == "GOSSIP_SHOW" then
386 local name, id = UnitName("npc"), self:GetUnitID("npc")
387 if name and id then
388 self:GetObjective("monster", name).o.id = id
389 --self:TextOut("NPC: "..name.." = "..id)
393 if event == "PLAYER_TARGET_CHANGED" then
394 local name, id = UnitName("target"), self:GetUnitID("target")
395 if name and id then
396 self:GetObjective("monster", name).o.id = id
397 --self:TextOut("Target: "..name.." = "..id)
400 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
401 local index, x, y = self:UnitPosition("target")
403 if index then -- Might not have a position if inside an instance.
404 local w = 0.1
406 -- Modify the weight based on how far they are from us.
407 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
408 if CheckInteractDistance("target", 3) then w = 1
409 elseif CheckInteractDistance("target", 2) then w = 0.89
410 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
412 local monster_objective = self:GetObjective("monster", UnitName("target"))
413 self:AppendObjectivePosition(monster_objective, index, x, y, w)
415 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
416 (UnitFactionGroup("target") == "Horde" and 2) or nil
418 local level = UnitLevel("target")
419 if level and level >= 1 then
420 local w = monster_objective.o.levelw or 0
421 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
422 monster_objective.o.levelw = w+1
428 if event == "LOOT_OPENED" then
429 local target = UnitName("target")
430 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
431 local index, x, y = self:UnitPosition("target")
433 local monster_objective = self:GetObjective("monster", target)
434 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
436 if index then -- Might not have a position if inside an instance.
437 self:AppendObjectivePosition(monster_objective, index, x, y)
440 for i = 1, GetNumLootItems() do
441 local icon, name, number, rarity = GetLootSlotInfo(i)
442 if name then
443 if number and number >= 1 then
444 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
445 else
446 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
447 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
448 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
450 if total > 0 then
451 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
456 else
457 local container = nil
459 -- Go through the players inventory and look for a locked item, we're probably looting it.
460 for bag = 0,NUM_BAG_SLOTS do
461 for slot = 1,GetContainerNumSlots(bag) do
462 local link = GetContainerItemLink(bag, slot)
463 if link and select(3, GetContainerItemInfo(bag, slot)) then
464 if container == nil then
465 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
466 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
467 else
468 -- Already tried to assign to a container. If there are multiple locked items, we give up.
469 container = false
475 if container then
476 local container_objective = self:GetObjective("item", container)
477 container_objective.o.opened = (container_objective.o.opened or 0) + 1
479 for i = 1, GetNumLootItems() do
480 local icon, name, number, rarity = GetLootSlotInfo(i)
481 if name and number >= 1 then
482 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
485 else
486 -- No idea where the items came from.
487 local index, x, y = self:PlayerPosition()
489 if index then
490 for i = 1, GetNumLootItems() do
491 local icon, name, number, rarity = GetLootSlotInfo(i)
492 if name and number >= 1 then
493 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
501 if event == "CHAT_MSG_SYSTEM" then
502 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
503 if home_name then
504 if self.i then
505 self:TextOut(QHText("HOME_CHANGED"))
506 self:TextOut(QHText("WILL_RESET_PATH"))
508 local home = QuestHelper_Home
509 if not home then
510 home = {}
511 QuestHelper_Home = home
514 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
515 self.defered_graph_reset = true
520 if event == "CHAT_MSG_ADDON" then
521 if arg1 == "QHpr" and (arg3 == "PARTY" or arg3 == "WHISPER") and arg4 ~= UnitName("player") then
522 self:HandleRemoteData(arg2, arg4)
526 if event == "PARTY_MEMBERS_CHANGED" then
527 self:HandlePartyChange()
530 if event == "QUEST_LOG_UPDATE" or
531 event == "PLAYER_LEVEL_UP" or
532 event == "PARTY_MEMBERS_CHANGED" then
533 self.defered_quest_scan = true
536 if event == "QUEST_DETAIL" then
537 if not self.quest_giver then self.quest_giver = {} end
538 local npc = UnitName("npc")
539 if npc then
540 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
541 local index, x, y = self:UnitPosition("npc")
543 if index then -- Might not have a position if inside an instance.
544 local npc_objective = self:GetObjective("monster", npc)
545 self:AppendObjectivePosition(npc_objective, index, x, y)
546 self.quest_giver[GetTitleText()] = npc
551 if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
552 local quest = GetTitleText()
553 if quest then
554 local level, hash = self:GetQuestLevel(quest)
555 if not level or level < 1 then
556 --self:TextOut("Don't know quest level for ".. quest.."!")
557 return
559 local q = self:GetQuest(quest, level, hash)
561 if q.need_hash then
562 q.o.hash = hash
565 local unit = UnitName("npc")
566 if unit then
567 q.o.finish = unit
568 q.o.pos = nil
570 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
571 local index, x, y = self:UnitPosition("npc")
572 if index then -- Might not have a position if inside an instance.
573 local npc_objective = self:GetObjective("monster", unit)
574 self:AppendObjectivePosition(npc_objective, index, x, y)
576 elseif not q.o.finish then
577 local index, x, y = self:PlayerPosition()
578 if index then -- Might not have a position if inside an instance.
579 self:AppendObjectivePosition(q, index, x, y)
585 if event == "MERCHANT_SHOW" then
586 local npc_name = UnitName("npc")
587 if npc_name then
588 local npc_objective = self:GetObjective("monster", npc_name)
589 local index = 1
590 while true do
591 local item_name = GetMerchantItemInfo(index)
592 if item_name then
593 index = index + 1
594 local item_objective = self:GetObjective("item", item_name)
595 if not item_objective.o.vendor then
596 item_objective.o.vendor = {npc_name}
597 else
598 local known = false
599 for i, vendor in ipairs(item_objective.o.vendor) do
600 if npc_name == vendor then
601 known = true
602 break
605 if not known then
606 table.insert(item_objective.o.vendor, npc_name)
609 else
610 break
616 if event == "TAXIMAP_OPENED" then
617 self:taxiMapOpened()
620 if event == "PLAYER_CONTROL_GAINED" then
621 self:flightEnded()
624 if event == "PLAYER_CONTROL_LOST" then
625 self:flightBegan()
628 if event == "BAG_UPDATE" then
629 for slot = 1,GetContainerNumSlots(arg1) do
630 local link = GetContainerItemLink(arg1, slot)
631 if link then
632 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
633 if name then
634 self:GetObjective("item", name).o.id = tonumber(id)
641 local map_shown_decay = 0
642 local delayed_action = 100
643 local update_count = 0
645 function QuestHelper:OnUpdate()
647 update_count = update_count - 1
649 if update_count <= 0 then
651 -- Reset the update count for next time around; this will make sure the body executes every time
652 -- when perf_scale >= 1, and down to 1 in 10 iterations when perf_scale < 1, or when hidden.
653 update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale)
655 if update_count < 0 then
656 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
657 update_count = 0
660 if self.Astrolabe.WorldMapVisible then
661 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
662 map_shown_decay = 2
663 elseif map_shown_decay > 0 then
664 map_shown_decay = map_shown_decay - 1
665 else
666 SetMapToCurrentZone()
669 delayed_action = delayed_action - 1
670 if delayed_action <= 0 then
671 delayed_action = 100
672 self:HandlePartyChange()
675 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
677 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
678 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
679 -- be seen in that zone but aren't in it.
680 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
681 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
682 nz, nx, ny = self.z, nnx, nny
686 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
687 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
688 if nx and ny and nx > -0.1 and ny > -0.1 and nx < 1.1 and ny < 1.1 then
689 nz = self.z
690 else
691 nc, nz, nx, ny = nil, nil, nil, nil
695 if nc and nz > 0 then
696 self.c, self.z, self.x, self.y = nc, nz, nx, ny
697 self.i = QuestHelper_IndexLookup[nc][nz]
700 if self.defered_quest_scan and not self.graph_in_limbo then
701 self.defered_quest_scan = false
702 self:ScanQuestLog()
705 if self.c then
706 self:RunCoroutine()
709 local level = UnitLevel("player")
710 if level >= 58 and self.player_level < 58 then
711 self.defered_graph_reset = true
713 self.player_level = level
715 self:PumpCommMessages()
719 QuestHelper:RegisterEvent("VARIABLES_LOADED")
720 QuestHelper:SetScript("OnEvent", QuestHelper.OnEvent)