update changes
[QuestHelper.git] / main.lua
blob10617452f7023583e441c9aa6c091c0c83de68b2
1 QuestHelper_File["main.lua"] = "Development Version"
2 QuestHelper_Loadtime["main.lua"] = GetTime()
4 local QuestHelper_Version = QuestHelper_File["main.lua"]
6 QuestHelper = CreateFrame("Frame", "QuestHelper", nil)
8 -- Just to make sure it's always 'seen' (there's nothing that can be seen, but still...), and therefore always updating.
9 QuestHelper:SetFrameStrata("TOOLTIP")
11 QuestHelper_SaveVersion = 10
12 QuestHelper_CharVersion = 1
13 QuestHelper_Locale = GetLocale() -- This variable is used only for the collected data, and has nothing to do with displayed text.
14 QuestHelper_Quests = {}
15 QuestHelper_Objectives = {}
17 QuestHelper_Pref =
20 QuestHelper_DefaultPref =
22 filter_level=true,
23 filter_zone=false,
24 filter_done=false,
25 filter_blocked=false, -- Hides blocked objectives, such as quest turn-ins for incomplete quests
26 filter_watched=false, -- Limits to Watched objectives
27 track=true,
28 track_minimized=false,
29 track_scale=1,
30 track_level=true,
31 track_qcolour=true,
32 track_ocolour=true,
33 track_size=8,
34 tooltip=true,
35 share = true,
36 scale = 1,
37 solo = false,
38 comm = false,
39 show_ants = true,
40 level = 3,
41 hide = false,
42 cart_wp_new = false,
43 tomtom_wp_new = false,
44 arrow = true,
45 arrow_locked = false,
46 arrow_arrowsize = 1,
47 arrow_textsize = 1,
48 flight_time = true,
49 locale = GetLocale(), -- This variable is used for display purposes, and has nothing to do with the collected data.
50 perf_scale = 1, -- How much background processing can the current machine handle? Higher means more load, lower means better performance.
51 perfload_scale = 1, -- Performance scale to use on startup
52 map_button = true
55 -- We do it here also in case things decide they care about preferences before the init function is called. Shouldn't happen, but maybe does.
56 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
58 QuestHelper_FlightInstructors = {}
59 QuestHelper_FlightLinks = {}
60 QuestHelper_FlightRoutes = {}
61 QuestHelper_KnownFlightRoutes = {}
62 QuestHelper_SeenRealms = {}
64 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
65 QuestHelper.objective_objects = {}
66 QuestHelper.user_objectives = {}
67 QuestHelper.quest_objects = {}
68 QuestHelper.player_level = 1
69 QuestHelper.locale = QuestHelper_Locale
71 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
72 (UnitFactionGroup("player") == "Horde" and 2)
74 assert(QuestHelper.faction)
76 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
78 function QuestHelper:GetFontPath(list_string, font)
79 if list_string then
80 for name in string.gmatch(list_string, "[^;]+") do
81 if font:SetFont(name, 10) then
82 return name
83 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
84 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
85 end
86 end
87 end
88 end
90 function QuestHelper:SetLocaleFonts()
91 self.font.sans = nil
92 self.font.serif = nil
93 self.font.fancy = nil
95 local font = self:CreateText(self)
97 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
98 -- Only use alternate fonts if using a language the client wasn't intended for.
99 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
100 if replacements then
101 self.font.sans = self:GetFontPath(replacements.sans, font)
102 self.font.serif = self:GetFontPath(replacements.serif, font)
103 self.font.fancy = self:GetFontPath(replacements.fancy, font)
107 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
108 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
109 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
111 self:ReleaseText(font)
113 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
114 self.font.serif = self.font.serif or GameFontNormal:GetFont()
115 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
117 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
118 -- This should do nothing if not using an alternate font.
119 DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
122 QuestHelper.route = {}
123 QuestHelper.to_add = {}
124 QuestHelper.to_remove = {}
125 QuestHelper.quest_log = {}
126 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
127 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
129 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
130 local last = self:NumLines()
131 local name = self:GetName()
132 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
135 function QuestHelper:SetTargetLocation(i, x, y, toffset)
136 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
137 local c, z = unpack(QuestHelper_ZoneLookup[i])
139 self.target = self:CreateTable()
140 self.target[2] = self:CreateTable()
142 self.target_time = time()+(toffset or 0)
144 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
145 self.target[1] = self.zone_nodes[i]
146 self.target[3] = x * self.continent_scales_x[c]
147 self.target[4] = y * self.continent_scales_y[c]
149 self:SetTargetLocationRecalculate()
152 function QuestHelper:SetTargetLocationRecalculate()
153 if self.target then
154 for i, n in ipairs(self.target[1]) do
155 local a, b = n.x-self.target[3], n.y-self.target[4]
156 self.target[2][i] = math.sqrt(a*a+b*b)
161 function QuestHelper:UnsetTargetLocation()
162 -- Unsets the target set above.
163 if self.target then
164 self:ReleaseTable(self.target[2])
165 self:ReleaseTable(self.target)
166 self.target = nil
167 self.target_time = nil
171 local interruptcount = 0 -- counts how many "played gained control" messages we recieve, used for flight paths
172 local init_cartographer_later = false
174 function QuestHelper:Initialize()
175 QuestHelper_Loadtime["init_start"] = GetTime()
177 -- Use DefaultPref as fallback for unset preference keys.
178 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
180 local file_problem = false
181 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
183 local expected_files =
185 ["bst_pre.lua"] = true,
186 ["bst_post.lua"] = true,
187 ["bst_astrolabe.lua"] = true,
188 ["bst_ctl.lua"] = true,
189 ["bst_libaboutpanel.lua"] = true,
191 ["upgrade.lua"] = true,
192 ["main.lua"] = true,
193 ["recycle.lua"] = true,
194 ["objective.lua"] = true,
195 ["quest.lua"] = true,
196 ["questlog.lua"] = true,
197 ["utility.lua"] = true,
198 ["dodads.lua"] = true,
199 ["graph.lua"] = true,
200 ["teleport.lua"] = true,
201 ["pathfinding.lua"] = true,
202 ["routing.lua"] = true,
203 ["custom.lua"] = true,
204 ["menu.lua"] = true,
205 ["hidden.lua"] = true,
206 ["nag.lua"] = true,
207 ["comm.lua"] = true,
208 ["mapbutton.lua"] = true,
209 ["help.lua"] = true,
210 ["pattern.lua"] = true,
211 ["flightpath.lua"] = true,
212 ["tracker.lua"] = true,
213 ["objtips.lua"] = true,
214 ["cartographer.lua"] = true,
215 ["tomtom.lua"] = true,
216 ["textviewer.lua"] = true,
217 ["error.lua"] = true,
218 ["timeslice.lua"] = true,
219 ["lang.lua"] = true,
220 ["arrow.lua"] = true,
222 ["static.lua"] = true,
223 ["static_deDE.lua"] = true,
224 ["static_enUS.lua"] = true,
225 ["static_esES.lua"] = true,
226 ["static_esMX.lua"] = true,
227 ["static_frFR.lua"] = true,
228 ["static_koKR.lua"] = true,
229 ["static_ruRU.lua"] = true,
230 ["static_zhCN.lua"] = true,
231 ["static_zhTW.lua"] = true,
233 ["collect.lua"] = true,
234 ["collect_achievement.lua"] = true,
235 ["collect_lzw.lua"] = true,
236 ["collect_traveled.lua"] = true,
237 ["collect_zone.lua"] = true,
238 ["collect_location.lua"] = true,
239 ["collect_merger.lua"] = true,
240 ["collect_monster.lua"] = true,
241 ["collect_item.lua"] = true,
242 ["collect_object.lua"] = true,
243 ["collect_loot.lua"] = true,
244 ["collect_patterns.lua"] = true,
245 ["collect_flight.lua"] = true,
246 ["collect_util.lua"] = true,
247 ["collect_quest.lua"] = true,
248 ["collect_equip.lua"] = true,
249 ["collect_notifier.lua"] = true,
250 ["collect_bitstream.lua"] = true,
251 ["collect_spec.lua"] = true,
252 ["collect_upgrade.lua"] = true,
253 ["collect_merchant.lua"] = true,
254 ["collect_warp.lua"] = true,
257 local uninstallederr = ""
259 for file, version in pairs(QuestHelper_File) do
260 if not expected_files[file] then
261 local errmsg = "Unexpected QuestHelper file: "..file
262 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
263 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
264 file_problem = true
265 elseif version ~= expected_version then
266 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
267 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
268 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
269 if version ~= "Development Version" and expected_version ~= "Development Version" then
270 -- Developers are allowed to mix dev versions with release versions
271 file_problem = true
276 for file in pairs(expected_files) do
277 if not QuestHelper_File[file] then
278 local errmsg = "Missing QuestHelper file: "..file
279 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
280 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
281 file_problem = true
285 -- Don't need this table anymore.
286 QuestHelper_File = nil
288 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
289 local errmsg = "Static data does not seem to exist"
290 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
292 -- TODO: Are you sure this should be an error? Shouldn't we let people we don't have data for collect their own?
293 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
294 file_problem = true
297 if file_problem then
298 message(QHText("PLEASE_RESTART"))
299 QuestHelper_ErrorCatcher_ExplicitError(true, "not-installed-properly" .. "\n" .. uninstallederr)
300 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
301 return
304 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
305 message(QHText("PRIVATE_SERVER"))
306 QuestHelper_ErrorCatcher_ExplicitError(true, "error id cakbep ten T")
307 QuestHelper = nil
308 return
311 if not DongleStub then
312 message(QHText("NOT_UNZIPPED_CORRECTLY"))
313 QuestHelper_ErrorCatcher_ExplicitError(true, "not-unzipped-properly")
314 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
315 return
318 QuestHelper_ErrorCatcher_CompletelyStarted()
320 if not QuestHelper_StaticData then
321 -- If there is no static data for some mysterious reason, create an empty table so that
322 -- other parts of the code can carry on as usual, using locally collected data if it exists.
323 QuestHelper_StaticData = {}
326 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
328 if not QuestHelper_UID then
329 QuestHelper_UID = self:CreateUID()
331 QuestHelper_SaveDate = time()
333 self.Astrolabe = DongleStub("Astrolabe-0.4-QuestHelper")
334 QuestHelper_BuildZoneLookup()
336 if QuestHelper_Locale ~= GetLocale() then
337 self:TextOut(QHText("LOCALE_ERROR"))
338 return
341 if not self:ZoneSanity() then
342 self:TextOut(QHText("ZONE_LAYOUT_ERROR"))
343 message("QuestHelper: "..QHText("ZONE_LAYOUT_ERROR"))
344 return
347 QuestHelper_UpgradeDatabase(_G)
348 QuestHelper_UpgradeComplete()
350 if QuestHelper_SaveVersion ~= 10 then
351 self:TextOut(QHText("DOWNGRADE_ERROR"))
352 return
355 if QuestHelper_IsPolluted(_G) then
356 self:TextOut(QHFormat("NAG_POLLUTED"))
357 self:Purge(nil, true, true)
360 local signature = expected_version .. " on " .. GetBuildInfo()
361 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
362 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
363 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
364 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
366 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
367 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
368 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
369 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
371 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
373 QH_Collector_Init()
375 self.player_level = UnitLevel("player")
377 self:UnregisterEvent("VARIABLES_LOADED")
378 self:RegisterEvent("PLAYER_TARGET_CHANGED")
379 self:RegisterEvent("LOOT_OPENED")
380 self:RegisterEvent("QUEST_COMPLETE")
381 self:RegisterEvent("QUEST_LOG_UPDATE")
382 self:RegisterEvent("QUEST_PROGRESS")
383 self:RegisterEvent("MERCHANT_SHOW")
384 self:RegisterEvent("QUEST_DETAIL")
385 self:RegisterEvent("TAXIMAP_OPENED")
386 self:RegisterEvent("PLAYER_CONTROL_GAINED")
387 self:RegisterEvent("PLAYER_LEVEL_UP")
388 self:RegisterEvent("PARTY_MEMBERS_CHANGED")
389 self:RegisterEvent("CHAT_MSG_ADDON")
390 self:RegisterEvent("CHAT_MSG_SYSTEM")
391 self:RegisterEvent("BAG_UPDATE")
392 self:RegisterEvent("GOSSIP_SHOW")
393 self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE")
395 self:SetLocaleFonts()
397 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
398 self:EnableSharing()
401 if QuestHelper_Pref.hide then
402 self.map_overlay:Hide()
405 self:HandlePartyChange()
407 self:Nag("all")
409 for locale in pairs(QuestHelper_StaticData) do
410 if locale ~= self.locale then
411 -- Will delete references to locales you don't use.
412 QuestHelper_StaticData[locale] = nil
413 _G["QuestHelper_StaticData_" .. locale] = nil
417 local static = QuestHelper_StaticData[self.locale]
419 if static then
420 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
421 if faction ~= self.faction then
422 -- Will delete references to flight instructors that don't belong to your faction.
423 static.flight_instructors[faction] = nil
425 end end
427 if static.quest then for faction in pairs(static.quest) do
428 if faction ~= self.faction then
429 -- Will delete references to quests that don't belong to your faction.
430 static.quest[faction] = nil
432 end end
435 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
436 if not QuestHelper_CharVersion then
437 -- Changing per-character flight routes, now only storing the flight points they have,
438 -- will attempt to guess the routes from this.
439 local routes = {}
441 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
442 for key in pairs(l) do
443 routes[key] = true
447 QuestHelper_KnownFlightRoutes = routes
449 -- Deleting the player's home again.
450 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
451 QuestHelper_Home = nil
452 QuestHelper_CharVersion = 1
455 if not QuestHelper_Home then
456 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
457 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
460 self.minimap_dodad = self:CreateMipmapDodad()
461 QuestHelper: Assert(self.minimap_dodad)
463 if QuestHelper_Pref.map_button then
464 QuestHelper:InitMapButton()
467 if QuestHelper_Pref.cart_wp_new then
468 init_cartographer_later = true
471 if QuestHelper_Pref.tomtom_wp_new then
472 self:EnableTomTom()
475 self.tracker:SetScale(QuestHelper_Pref.track_scale)
477 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
478 self:ShowTracker()
481 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
483 local major, minor = (QuestHelper_Version or ""):match("^(%d+)%.(%d+)")
484 major, minor = tonumber(major), tonumber(minor)
486 -- For versions before 0.82, we're changing the default level offset to 3.
487 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
488 QuestHelper_Pref.level = nil
491 -- For versions before 0.84...
492 if major == 0 and minor and minor < 84 then
493 -- remove all keys that match their default setting.
494 for key, val in pairs(QuestHelper_DefaultPref) do
495 if QuestHelper_Pref[key] == val then
496 QuestHelper_Pref[key] = nil
501 self:SetScript("OnUpdate", self.OnUpdate)
503 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
504 --collectgarbage("collect") -- Free everything we aren't using.
506 if self.debug_objectives then
507 for name, data in pairs(self.debug_objectives) do
508 self:LoadDebugObjective(name, data)
512 QH_Timeslice_Add(function ()
513 self:ResetPathing()
514 self.Routing:Initialize() -- Set up the routing task
515 end, "init")
517 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
518 function C()
521 function A()
522 q = 0
523 for x = 0, 130000000, 1 do
527 function B()
528 q = 0
529 for x = 0, 12000000, 1 do
534 function B2()
535 q = 0
536 for x = 0, 1200000, 1 do
537 --q = q + x
542 debugprofilestart()
544 local ta = debugprofilestop()
546 local tb = debugprofilestop()
548 local tc = debugprofilestop()
550 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
551 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
553 --/script SetCVar("scriptProfile", value)]]
555 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
557 QuestHelper_Loadtime["init_end"] = GetTime()
560 local startup_time
561 local please_donate_enabled = false
562 local please_donate_initted = false
564 function QuestHelper:OnEvent(event)
565 if event == "VARIABLES_LOADED" then
566 local tstart = GetTime()
567 self:Initialize()
568 QH_Timeslice_Increment(GetTime() - tstart, "init")
571 local tstart = GetTime()
573 if event == "GOSSIP_SHOW" then
574 local name, id = UnitName("npc"), self:GetUnitID("npc")
575 if name and id then
576 self:GetObjective("monster", name).o.id = id
577 --self:TextOut("NPC: "..name.." = "..id)
581 if event == "PLAYER_TARGET_CHANGED" then
582 local name, id = UnitName("target"), self:GetUnitID("target")
583 if name and id then
584 self:GetObjective("monster", name).o.id = id
585 --self:TextOut("Target: "..name.." = "..id)
588 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
589 local index, x, y = self:UnitPosition("target")
591 if index then -- Might not have a position if inside an instance.
592 local w = 0.1
594 -- Modify the weight based on how far they are from us.
595 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
596 if CheckInteractDistance("target", 3) then w = 1
597 elseif CheckInteractDistance("target", 2) then w = 0.89
598 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
600 local monster_objective = self:GetObjective("monster", UnitName("target"))
601 self:AppendObjectivePosition(monster_objective, index, x, y, w)
603 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
604 (UnitFactionGroup("target") == "Horde" and 2) or nil
606 local level = UnitLevel("target")
607 if level and level >= 1 then
608 local w = monster_objective.o.levelw or 0
609 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
610 monster_objective.o.levelw = w+1
616 if event == "LOOT_OPENED" then
617 local target = UnitName("target")
618 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
619 local index, x, y = self:UnitPosition("target")
621 local monster_objective = self:GetObjective("monster", target)
622 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
624 if index then -- Might not have a position if inside an instance.
625 self:AppendObjectivePosition(monster_objective, index, x, y)
628 for i = 1, GetNumLootItems() do
629 local icon, name, number, rarity = GetLootSlotInfo(i)
630 if name then
631 if number and number >= 1 then
632 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
633 else
634 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
635 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
636 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
638 if total > 0 then
639 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
644 else
645 local container = nil
647 -- Go through the players inventory and look for a locked item, we're probably looting it.
648 for bag = 0,NUM_BAG_SLOTS do
649 for slot = 1,GetContainerNumSlots(bag) do
650 local link = GetContainerItemLink(bag, slot)
651 if link and select(3, GetContainerItemInfo(bag, slot)) then
652 if container == nil then
653 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
654 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
655 else
656 -- Already tried to assign to a container. If there are multiple locked items, we give up.
657 container = false
663 if container then
664 local container_objective = self:GetObjective("item", container)
665 container_objective.o.opened = (container_objective.o.opened or 0) + 1
667 for i = 1, GetNumLootItems() do
668 local icon, name, number, rarity = GetLootSlotInfo(i)
669 if name and number >= 1 then
670 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
673 else
674 -- No idea where the items came from.
675 local index, x, y = self:PlayerPosition()
677 if index then
678 for i = 1, GetNumLootItems() do
679 local icon, name, number, rarity = GetLootSlotInfo(i)
680 if name and number >= 1 then
681 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
689 if event == "CHAT_MSG_SYSTEM" then
690 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
691 if home_name then
692 if self.i then
693 self:TextOut(QHText("HOME_CHANGED"))
694 self:TextOut(QHText("WILL_RESET_PATH"))
696 local home = QuestHelper_Home
697 if not home then
698 home = {}
699 QuestHelper_Home = home
702 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
703 self.defered_graph_reset = true
708 if event == "CHAT_MSG_ADDON" then
709 if arg1 == "QHpr" and (arg3 == "PARTY" or arg3 == "WHISPER") and arg4 ~= UnitName("player") then
710 self:HandleRemoteData(arg2, arg4)
714 if event == "PARTY_MEMBERS_CHANGED" then
715 self:HandlePartyChange()
718 if event == "QUEST_LOG_UPDATE" or
719 event == "PLAYER_LEVEL_UP" or
720 event == "PARTY_MEMBERS_CHANGED" then
721 self.defered_quest_scan = true
724 if event == "QUEST_DETAIL" then
725 if not self.quest_giver then self.quest_giver = {} end
726 local npc = UnitName("npc")
727 if npc then
728 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
729 local index, x, y = self:UnitPosition("npc")
731 if index then -- Might not have a position if inside an instance.
732 local npc_objective = self:GetObjective("monster", npc)
733 self:AppendObjectivePosition(npc_objective, index, x, y)
734 self.quest_giver[GetTitleText()] = npc
739 if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
740 local quest = GetTitleText()
741 if quest then
742 local level, hash = self:GetQuestLevel(quest)
743 if not level or level < 1 then
744 --self:TextOut("Don't know quest level for ".. quest.."!")
745 return
747 local q = self:GetQuest(quest, level, hash)
749 if q.need_hash then
750 q.o.hash = hash
753 local unit = UnitName("npc")
754 if unit then
755 q.o.finish = unit
756 q.o.pos = nil
758 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
759 local index, x, y = self:UnitPosition("npc")
760 if index then -- Might not have a position if inside an instance.
761 local npc_objective = self:GetObjective("monster", unit)
762 self:AppendObjectivePosition(npc_objective, index, x, y)
764 elseif not q.o.finish then
765 local index, x, y = self:PlayerPosition()
766 if index then -- Might not have a position if inside an instance.
767 self:AppendObjectivePosition(q, index, x, y)
773 if event == "MERCHANT_SHOW" then
774 local npc_name = UnitName("npc")
775 if npc_name then
776 local npc_objective = self:GetObjective("monster", npc_name)
777 local index = 1
778 while true do
779 local item_name = GetMerchantItemInfo(index)
780 if item_name then
781 index = index + 1
782 local item_objective = self:GetObjective("item", item_name)
783 if not item_objective.o.vendor then
784 item_objective.o.vendor = {npc_name}
785 else
786 local known = false
787 for i, vendor in ipairs(item_objective.o.vendor) do
788 if npc_name == vendor then
789 known = true
790 break
793 if not known then
794 table.insert(item_objective.o.vendor, npc_name)
797 else
798 break
804 if event == "TAXIMAP_OPENED" then
805 self:taxiMapOpened()
808 if event == "PLAYER_CONTROL_GAINED" then
809 interruptcount = interruptcount + 1
812 if event == "BAG_UPDATE" then
813 for slot = 1,GetContainerNumSlots(arg1) do
814 local link = GetContainerItemLink(arg1, slot)
815 if link then
816 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
817 if name then
818 self:GetObjective("item", name).o.id = tonumber(id)
824 if event == "CHAT_MSG_CHANNEL_NOTICE" and please_donate_enabled and not please_donate_initted then
825 please_donate_enabled = QHNagInit()
826 startup_time = GetTime()
827 please_donate_initted = true
829 QHUpdateNagInit()
832 QH_Timeslice_Increment(GetTime() - tstart, "event")
835 local map_shown_decay = 0
836 local delayed_action = 100
837 --local update_count = 0
838 local ontaxi = false
839 local frams = 0
841 function QuestHelper:OnUpdate()
842 local tstart = GetTime()
843 frams = frams + 1
845 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
847 if frams == 250 then please_donate_enabled = false end -- TOOK TOO LONG >:(
848 if please_donate_enabled and startup_time and startup_time + 1 < GetTime() then
849 QuestHelper:TextOut(QHText("PLEASE_DONATE"))
850 startup_time = nil
851 please_donate_enabled = false
853 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
855 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
856 init_cartographer_later = false
857 if QuestHelper_Pref.cart_wp_new then
858 self:EnableCartographer()
862 if not ontaxi and UnitOnTaxi("player") then
863 self:flightBegan()
864 interruptcount = 0
865 elseif ontaxi and not UnitOnTaxi("player") then
866 self:flightEnded(interruptcount > 1)
868 ontaxi = UnitOnTaxi("player")
870 -- For now I'm ripping out the update_count code
871 --update_count = update_count - 1
872 --if update_count <= 0 then
874 -- Reset the update count for next time around; this will make sure the body executes every time
875 -- when perf_scale >= 1, and down to 1 in 10 iterations when perf_scale < 1, or when hidden.
876 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale)
878 --if update_count < 0 then
879 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
880 --update_count = 0
881 --end
883 if self.Astrolabe.WorldMapVisible then
884 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
885 map_shown_decay = 2
886 elseif map_shown_decay > 0 then
887 map_shown_decay = map_shown_decay - 1
888 else
889 --SetMapToCurrentZone() -- not sure why this existed
892 delayed_action = delayed_action - 1
893 if delayed_action <= 0 then
894 delayed_action = 100
895 self:HandlePartyChange()
898 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
899 local tc, tx, ty
901 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
902 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
903 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
906 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
907 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
908 -- be seen in that zone but aren't in it.
909 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
910 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
911 nz, nx, ny = self.z, nnx, nny
915 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
916 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
917 if nx and ny --[[and nx > -0.1 and ny > -0.1 and nx < 1.1 and ny < 1.1]] then -- removing the conditional because I think I can use the data even when it's a little wonky
918 nz = self.z
919 else
920 nc, nz, nx, ny = nil, nil, nil, nil
924 if nc and nz > 0 then
925 self.c, self.z, self.x, self.y = nc, nz, nx, ny
926 self.i = QuestHelper_IndexLookup[nc][nz]
929 if nc and nz and nx and ny and tc and tx and ty then
930 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
931 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
932 self.collect_delayed = false
934 local ibi = self.InBrokenInstance
935 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
937 if ibi and not self.InBrokenInstance then self.minimap_dodad:OnUpdate(0) end -- poke
938 else
939 self.collect_delayed = true
940 self.InBrokenInstance = true
943 local level = UnitLevel("player")
944 if level >= 58 and self.player_level < 58 then
945 self.defered_graph_reset = true
947 if level ~= self.player_level then
948 self.defered_quest_scan = true
950 self.player_level = level
952 if self.defered_quest_scan and not self.graph_in_limbo then
953 self.defered_quest_scan = false
954 self:ScanQuestLog()
957 QH_Timeslice_Toggle("routing", not not self.c)
959 self:PumpCommMessages()
960 --end
962 QH_Collector_OnUpdate()
964 QH_Timeslice_Increment(GetTime() - tstart, "onupdate")
966 QH_Timeslice_Work()
969 -- Some or all of these may be nil. c,x,y should be enough for a location - c is the pure continent (currently either 0 or 3 for Azeroth or Outland) and x,y are the coordinates within that continent.
970 -- rc and rz are the continent and zone that Questhelper thinks it's within. For various reasons, this isn't perfect. TODO: Base it off the map zone name identifiers instead of the map itself?
971 function QuestHelper:Location_RawRetrieve()
972 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
974 function QuestHelper:Location_AbsoluteRetrieve()
975 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
978 QuestHelper:RegisterEvent("VARIABLES_LOADED")
979 QuestHelper:SetScript("OnEvent", QuestHelper.OnEvent)