fffff
[QuestHelper.git] / main.lua
blob0e804c5db93b0d3834a1481badde691f0a83ed08
1 QuestHelper_File["main.lua"] = "Development Version"
2 QuestHelper_Loadtime["main.lua"] = GetTime()
4 local version_string = QuestHelper_File["main.lua"] -- we pretty much save this only so we can inform the user that they're using a beta version
6 -- Just to make sure it's always 'seen' (there's nothing that can be seen, but still...), and therefore always updating.
7 QuestHelper:SetFrameStrata("TOOLTIP")
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 local 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 filter_watched=false, -- Limits to Watched objectives
24 filter_group=true,
25 filter_group_param=2,
26 filter_wintergrasp=true,
27 filter_raid_accessible=true,
28 zones="next",
29 track=true,
30 track_minimized=false,
31 track_scale=1,
32 track_level=true,
33 track_qcolour=true,
34 track_ocolour=true,
35 track_size=10,
36 blizzmap = false,
37 tooltip=true,
38 share = true,
39 scale = 1,
40 solo = false,
41 comm = false,
42 show_ants = true,
43 level = 3,
44 hide = false,
45 cart_wp_new = false,
46 tomtom_wp_new = false,
47 arrow = true,
48 arrow_locked = false,
49 arrow_arrowsize = 1,
50 arrow_textsize = 1,
51 metric = (QuestHelper_Locale ~= "enUS" and QuestHelper_Locale ~= "esMX"),
52 flight_time = true,
53 locale = GetLocale(), -- This variable is used for display purposes, and has nothing to do with the collected data.
54 perf_scale_2 = 1, -- How much background processing can the current machine handle? Higher means more load, lower means better performance.
55 perfload_scale = 1, -- Performance scale to use on startup
56 map_button = true,
57 travel_time = false,
58 mini_opacity = 1,
61 -- 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.
62 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
64 QuestHelper_FlightInstructors = {}
65 QuestHelper_FlightLinks = {}
66 QuestHelper_FlightRoutes = {}
67 QuestHelper_KnownFlightRoutes = {}
68 QuestHelper_SeenRealms = {}
70 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
71 QuestHelper.objective_objects = {}
72 QuestHelper.user_objectives = {}
73 QuestHelper.quest_objects = {}
74 QuestHelper.player_level = 1
75 QuestHelper.locale = QuestHelper_Locale
77 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
78 (UnitFactionGroup("player") == "Horde" and 2)
80 assert(QuestHelper.faction)
82 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
84 function QuestHelper:GetFontPath(list_string, font)
85 if list_string then
86 for name in string.gmatch(list_string, "[^;]+") do
87 if font:SetFont(name, 10) then
88 return name
89 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
90 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
91 end
92 end
93 end
94 end
96 function QuestHelper:SetLocaleFonts()
97 self.font.sans = nil
98 self.font.serif = nil
99 self.font.fancy = nil
101 local font = self:CreateText(self)
103 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
104 -- Only use alternate fonts if using a language the client wasn't intended for.
105 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
106 if replacements then
107 self.font.sans = self:GetFontPath(replacements.sans, font)
108 self.font.serif = self:GetFontPath(replacements.serif, font)
109 self.font.fancy = self:GetFontPath(replacements.fancy, font)
113 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
114 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
115 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
117 self:ReleaseText(font)
119 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
120 self.font.serif = self.font.serif or GameFontNormal:GetFont()
121 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
123 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
124 -- This should do nothing if not using an alternate font.
125 --DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
126 -- what why did we ever do this
129 QuestHelper.route = {}
130 QuestHelper.to_add = {}
131 QuestHelper.to_remove = {}
132 QuestHelper.quest_log = {}
133 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
134 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
136 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
137 local last = self:NumLines()
138 local name = self:GetName()
139 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
142 function QuestHelper:SetTargetLocation(i, x, y, toffset)
143 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
144 local c, z = unpack(QuestHelper_ZoneLookup[i])
146 self.target = self:CreateTable()
147 self.target[2] = self:CreateTable()
149 self.target_time = time()+(toffset or 0)
151 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
152 self.target[1] = self.zone_nodes[i]
153 self.target[3] = x * self.continent_scales_x[c]
154 self.target[4] = y * self.continent_scales_y[c]
156 self:SetTargetLocationRecalculate()
159 function QuestHelper:SetTargetLocationRecalculate()
160 if self.target then
161 for i, n in ipairs(self.target[1]) do
162 local a, b = n.x-self.target[3], n.y-self.target[4]
163 self.target[2][i] = math.sqrt(a*a+b*b)
168 function QuestHelper:UnsetTargetLocation()
169 -- Unsets the target set above.
170 if self.target then
171 self:ReleaseTable(self.target[2])
172 self:ReleaseTable(self.target)
173 self.target = nil
174 self.target_time = nil
178 local interruptcount = 0 -- counts how many "played gained control" messages we recieve, used for flight paths
179 local init_cartographer_later = false
182 local startup_time
183 local please_submit_enabled = true
184 local please_submit_initted = false
186 local spawned = false
187 QH_Event("ADDON_LOADED", function (addonid)
188 if addonid ~= "QuestHelper" then return end
190 -- ONLY FAST STUFF ALLOWED IN HERE
192 -- Use DefaultPref as fallback for unset preference keys.
193 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
194 QuestHelper: Assert(QuestHelper_Pref.perfload_scale) -- if this fails, something is very botched
196 if not QuestHelper_Pref.track or QuestHelper_Pref.hide then
197 QuestHelper:HideTracker()
198 else
199 QuestHelper:ShowTracker() -- to respect the minimized setting
202 local self = QuestHelper -- whee hack hack hack
204 QuestHelper_Loadtime["init2_start"] = GetTime()
206 local file_problem_version = false
208 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
210 local expected_files =
212 ["bst_pre.lua"] = true,
213 ["bst_post.lua"] = true,
214 ["bst_astrolabe.lua"] = true,
215 ["bst_ctl.lua"] = true,
216 ["bst_libaboutpanel.lua"] = true,
217 ["bst_range.lua"] = true,
219 ["manager_event.lua"] = true,
220 ["manager_achievement.lua"] = true,
221 ["manager_completed.lua"] = true,
223 ["upgrade.lua"] = true,
224 ["main.lua"] = true,
225 ["recycle.lua"] = true,
226 ["objective.lua"] = true,
227 ["quest.lua"] = true,
228 ["utility.lua"] = true,
229 ["dodads.lua"] = true,
230 ["dodads_triangles.lua"] = true,
231 ["teleport.lua"] = true,
232 ["pathfinding.lua"] = true,
233 ["routing.lua"] = true,
234 ["custom.lua"] = true,
235 ["menu.lua"] = true,
236 ["nag.lua"] = true,
237 ["comm.lua"] = true,
238 ["mapbutton.lua"] = true,
239 ["help.lua"] = true,
240 ["pattern.lua"] = true,
241 ["flightpath.lua"] = true,
242 ["tracker.lua"] = true,
243 ["objtips.lua"] = true,
244 ["cartographer.lua"] = true,
245 ["cartographer_is_terrible.lua"] = true,
246 ["tomtom.lua"] = true,
247 ["textviewer.lua"] = true,
248 ["error.lua"] = true,
249 ["timeslice.lua"] = true,
250 ["lang.lua"] = true,
251 ["core.lua"] = true,
252 ["tooltip.lua"] = true,
253 ["arrow.lua"] = true,
254 ["radar.lua"] = true,
256 ["static.lua"] = true,
257 ["static_1.lua"] = true,
258 ["static_2.lua"] = true,
259 ["static_deDE.lua"] = true,
260 ["static_deDE_1.lua"] = true,
261 ["static_deDE_2.lua"] = true,
262 ["static_enUS.lua"] = true,
263 ["static_enUS_1.lua"] = true,
264 ["static_enUS_2.lua"] = true,
265 ["static_esES.lua"] = true,
266 ["static_esES_1.lua"] = true,
267 ["static_esES_2.lua"] = true,
268 ["static_esMX.lua"] = true,
269 ["static_esMX_1.lua"] = true,
270 ["static_esMX_2.lua"] = true,
271 ["static_frFR.lua"] = true,
272 ["static_frFR_1.lua"] = true,
273 ["static_frFR_2.lua"] = true,
274 ["static_koKR.lua"] = true,
275 ["static_koKR_1.lua"] = true,
276 ["static_koKR_2.lua"] = true,
277 ["static_ruRU.lua"] = true,
278 ["static_ruRU_1.lua"] = true,
279 ["static_ruRU_2.lua"] = true,
280 ["static_zhTW.lua"] = true,
281 ["static_zhTW_1.lua"] = true,
282 ["static_zhTW_2.lua"] = true,
284 ["collect.lua"] = true,
285 ["collect_achievement.lua"] = true,
286 ["collect_lzw.lua"] = true,
287 ["collect_traveled.lua"] = true,
288 ["collect_zone.lua"] = true,
289 ["collect_location.lua"] = true,
290 ["collect_merger.lua"] = true,
291 ["collect_monster.lua"] = true,
292 ["collect_item.lua"] = true,
293 ["collect_object.lua"] = true,
294 ["collect_loot.lua"] = true,
295 ["collect_patterns.lua"] = true,
296 ["collect_flight.lua"] = true,
297 ["collect_util.lua"] = true,
298 ["collect_quest.lua"] = true,
299 ["collect_equip.lua"] = true,
300 ["collect_notifier.lua"] = true,
301 ["collect_bitstream.lua"] = true,
302 ["collect_spec.lua"] = true,
303 ["collect_upgrade.lua"] = true,
304 ["collect_merchant.lua"] = true,
305 ["collect_warp.lua"] = true,
307 ["filter_core.lua"] = true,
308 ["filter_base.lua"] = true,
310 ["routing_debug.lua"] = true,
311 ["routing_loc.lua"] = true,
312 ["routing_route.lua"] = true,
313 ["routing_core.lua"] = true,
314 ["routing_controller.lua"] = true,
315 ["routing_hidden.lua"] = true,
317 ["director_quest.lua"] = true,
318 ["director_achievement.lua"] = true,
319 ["director_find.lua"] = true,
321 ["db_get.lua"] = true,
323 ["graph_core.lua"] = true,
324 ["graph_flightpath.lua"] = true,
326 ["AstrolabeQH/Astrolabe.lua"] = true,
327 ["AstrolabeQH/AstrolabeMapMonitor.lua"] = true,
330 local uninstallederr = ""
332 for file, version in pairs(QuestHelper_File) do
333 if not expected_files[file] then
334 local errmsg = "Unexpected QuestHelper file: "..file
335 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
336 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
337 file_problem_version = true
338 elseif version ~= expected_version then
339 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
340 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
341 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
342 if version ~= "Development Version" and expected_version ~= "Development Version" then
343 -- Developers are allowed to mix dev versions with release versions
344 file_problem_version = true
349 for file in pairs(expected_files) do
350 if not QuestHelper_File[file] then
351 local errmsg = "Missing QuestHelper file: "..file
352 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
353 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
354 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem_version = true end
358 -- Don't need this table anymore.
359 QuestHelper_File = nil
361 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
362 local errmsg = "Static data does not seem to exist"
363 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
365 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
366 file_problem_version = true
369 if file_problem_version then
370 QH_fixedmessage(QHText("PLEASE_RESTART"))
371 QuestHelper_ErrorCatcher_ExplicitError(false, "not-installed-properly" .. "\n" .. uninstallederr)
372 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
373 return
376 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
377 QH_fixedmessage(QHText("PRIVATE_SERVER"))
378 QuestHelper_ErrorCatcher_ExplicitError(false, "error id cakbep ten T")
379 QuestHelper = nil
380 return
383 if not DongleStub or not QH_Astrolabe_Ready then
384 QH_fixedmessage(QHText("NOT_UNZIPPED_CORRECTLY"))
385 QuestHelper_ErrorCatcher_ExplicitError(false, "not-unzipped-properly")
386 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
387 return
390 QuestHelper_ErrorCatcher_CompletelyStarted()
392 if not QuestHelper_StaticData then
393 -- If there is no static data for some mysterious reason, create an empty table so that
394 -- other parts of the code can carry on as usual, using locally collected data if it exists.
395 QuestHelper_StaticData = {}
398 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
400 if not QuestHelper_UID then
401 QuestHelper_UID = self:CreateUID()
403 QuestHelper_SaveDate = time()
406 QH_Timeslice_Add(function ()
407 QuestHelper_Loadtime["init3_start"] = GetTime()
409 QuestHelper.loading_main = QuestHelper.CreateLoadingCounter()
411 QuestHelper.loading_init3 = QuestHelper.loading_main:MakeSubcategory(0.3)
412 QuestHelper.loading_flightpath = QuestHelper.loading_main:MakeSubcategory(1)
413 QuestHelper.loading_preroll = QuestHelper.loading_main:MakeSubcategory(1)
415 local stt = 0
417 -- This is where the slow stuff goes
418 QuestHelper_BuildZoneLookup()
419 QH_Graph_Init()
420 load_graph_links()
422 if QuestHelper_Locale ~= GetLocale() then
423 self:TextOut(QHText("LOCALE_ERROR"))
424 return
427 if not self:ZoneSanity() then
428 self:TextOut(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
429 QH_fixedmessage(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
430 QuestHelper = nil
431 return
434 QuestHelper_UpgradeDatabase(_G)
435 QuestHelper_UpgradeComplete()
437 if QuestHelper_IsPolluted(_G) then
438 self:TextOut(QHFormat("NAG_POLLUTED"))
439 self:Purge(nil, true, true)
442 local signature = expected_version .. " on " .. GetBuildInfo()
443 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
444 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
445 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
446 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
448 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
449 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
450 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
451 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
453 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
455 QuestHelper.loading_init3:SetPercentage(0.1)
456 QH_Collector_Init()
457 QuestHelper.loading_init3:SetPercentage(0.5)
458 DB_Init()
459 QuestHelper.loading_init3:SetPercentage(0.9)
461 self.player_level = UnitLevel("player")
463 self:SetLocaleFonts()
465 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
466 self:EnableSharing()
469 if QuestHelper_Pref.hide then
470 self.map_overlay:Hide()
473 self:HandlePartyChange()
475 self:Nag("all")
477 for locale in pairs(QuestHelper_StaticData) do
478 if locale ~= self.locale then
479 -- Will delete references to locales you don't use.
480 QuestHelper_StaticData[locale] = nil
481 _G["QuestHelper_StaticData_" .. locale] = nil
485 local static = QuestHelper_StaticData[self.locale]
487 if static then
488 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
489 if faction ~= self.faction then
490 -- Will delete references to flight instructors that don't belong to your faction.
491 static.flight_instructors[faction] = nil
493 end end
495 if static.quest then for faction in pairs(static.quest) do
496 if faction ~= self.faction then
497 -- Will delete references to quests that don't belong to your faction.
498 static.quest[faction] = nil
500 end end
503 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
504 if not QuestHelper_CharVersion then
505 -- Changing per-character flight routes, now only storing the flight points they have,
506 -- will attempt to guess the routes from this.
507 local routes = {}
509 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
510 for key in pairs(l) do
511 routes[key] = true
515 QuestHelper_KnownFlightRoutes = routes
517 -- Deleting the player's home again.
518 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
519 QuestHelper_Home = nil
520 QuestHelper_CharVersion = 1
523 if not QuestHelper_Home then
524 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
525 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
528 if QuestHelper_Pref.map_button then
529 QuestHelper:InitMapButton()
532 if QuestHelper_Pref.cart_wp_new then
533 init_cartographer_later = true
536 if QuestHelper_Pref.tomtom_wp_new then
537 self:EnableTomTom()
540 self.tracker:SetScale(QuestHelper_Pref.track_scale)
542 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
544 local major, minor = (version_string or ""):match("^(%d+)%.(%d+)")
545 major, minor = tonumber(major), tonumber(minor)
547 -- For versions before 0.82, we're changing the default level offset to 3.
548 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
549 QuestHelper_Pref.level = nil
552 -- For versions before 0.84...
553 if major == 0 and minor and minor < 84 then
554 -- remove all keys that match their default setting.
555 for key, val in pairs(QuestHelper_DefaultPref) do
556 if QuestHelper_Pref[key] == val then
557 QuestHelper_Pref[key] = nil
562 QH_Hook(self, "OnUpdate", self.OnUpdate)
564 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
565 --collectgarbage("collect") -- Free everything we aren't using.
567 --[[
568 if self.debug_objectives then
569 for name, data in pairs(self.debug_objectives) do
570 self:LoadDebugObjective(name, data)
572 end]]
574 -- wellllp
575 QH_Arrow_SetScale()
576 QH_Arrow_SetTextScale()
578 --[[
579 QH_Timeslice_Add(function ()
580 self:ResetPathing()
581 self.Routing:Initialize() -- Set up the routing task
582 end, "init")]] -- FUCK YOU BOXBOT
584 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
585 function C()
588 function A()
589 q = 0
590 for x = 0, 130000000, 1 do
594 function B()
595 q = 0
596 for x = 0, 12000000, 1 do
601 function B2()
602 q = 0
603 for x = 0, 1200000, 1 do
604 --q = q + x
609 debugprofilestart()
611 local ta = debugprofilestop()
613 local tb = debugprofilestop()
615 local tc = debugprofilestop()
617 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
618 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
620 --/script SetCVar("scriptProfile", value)]]
622 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
625 QH_Event("CHAT_MSG_ADDON", function (...)
626 if arg1 == "QHpr" and arg4 ~= UnitName("player") then
627 QH_Questcomm_Msg(arg2, arg4)
629 end)
631 QH_Event({"PARTY_MEMBERS_CHANGED", "UNIT_LEVEL", "RAID_ROSTER_UPDATE"}, function ()
632 QH_Filter_Group_Sync()
633 QH_Route_Filter_Rescan("filter_quest_level")
634 --QH_Route_Filter_Rescan("filter_quest_group")
635 --QH_Route_Filter_Rescan("filter_quest_raid_accessible") -- These should be in right now, but for simplicity's sake we're actually scanning everything when we get a rescan request. So they're unnecessary. PUT THEM BACK should they become necessary.
636 end)
638 QH_Event({"PARTY_MEMBERS_CHANGED", "RAID_ROSTER_UPDATE"}, function ()
639 QH_Questcomm_Sync()
640 end)
642 QH_Event("PLAYER_LEVEL_UP", function ()
643 self.player_level = arg1
644 QH_Route_Filter_Rescan("filter_quest_level")
645 end)
647 QH_Event("TAXIMAP_OPENED", function ()
648 self:taxiMapOpened()
649 end)
651 QH_Event({"ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, function()
652 QH_Route_Filter_Rescan(nil, true)
653 end)
655 QH_Event("CHAT_MSG_CHANNEL_NOTICE", function()
656 if please_submit_enabled and not please_submit_initted then
657 please_submit_enabled = QHNagInit()
658 startup_time = GetTime()
659 please_submit_initted = true
661 end)
663 QuestHelper.loading_init3:SetPercentage(1.0) -- victory
665 QuestHelper_Loadtime["init3_end"] = GetTime()
666 end, "preinit")
668 QuestHelper_Loadtime["init2_end"] = GetTime()
669 end)
672 --[==[
673 function QuestHelper:OnEvent(event)
674 local tstart = GetTime()
676 --[[
677 if event == "GOSSIP_SHOW" then
678 local name, id = UnitName("npc"), self:GetUnitID("npc")
679 if name and id then
680 self:GetObjective("monster", name).o.id = id
681 --self:TextOut("NPC: "..name.." = "..id)
683 end]]
685 --[[if event == "PLAYER_TARGET_CHANGED" then
686 local name, id = UnitName("target"), self:GetUnitID("target")
687 if name and id then
688 self:GetObjective("monster", name).o.id = id
689 --self:TextOut("Target: "..name.." = "..id)
692 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
693 local index, x, y = self:UnitPosition("target")
695 if index then -- Might not have a position if inside an instance.
696 local w = 0.1
698 -- Modify the weight based on how far they are from us.
699 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
700 if CheckInteractDistance("target", 3) then w = 1
701 elseif CheckInteractDistance("target", 2) then w = 0.89
702 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
704 local monster_objective = self:GetObjective("monster", UnitName("target"))
705 self:AppendObjectivePosition(monster_objective, index, x, y, w)
707 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
708 (UnitFactionGroup("target") == "Horde" and 2) or nil
710 local level = UnitLevel("target")
711 if level and level >= 1 then
712 local w = monster_objective.o.levelw or 0
713 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
714 monster_objective.o.levelw = w+1
718 end]]
720 --[[if event == "LOOT_OPENED" then
721 local target = UnitName("target")
722 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
723 local index, x, y = self:UnitPosition("target")
725 local monster_objective = self:GetObjective("monster", target)
726 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
728 if index then -- Might not have a position if inside an instance.
729 self:AppendObjectivePosition(monster_objective, index, x, y)
732 for i = 1, GetNumLootItems() do
733 local icon, name, number, rarity = GetLootSlotInfo(i)
734 if name then
735 if number and number >= 1 then
736 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
737 else
738 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
739 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
740 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
742 if total > 0 then
743 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
748 else
749 local container = nil
751 -- Go through the players inventory and look for a locked item, we're probably looting it.
752 for bag = 0,NUM_BAG_SLOTS do
753 for slot = 1,GetContainerNumSlots(bag) do
754 local link = GetContainerItemLink(bag, slot)
755 if link and select(3, GetContainerItemInfo(bag, slot)) then
756 if container == nil then
757 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
758 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
759 else
760 -- Already tried to assign to a container. If there are multiple locked items, we give up.
761 container = false
767 if container then
768 local container_objective = self:GetObjective("item", container)
769 container_objective.o.opened = (container_objective.o.opened or 0) + 1
771 for i = 1, GetNumLootItems() do
772 local icon, name, number, rarity = GetLootSlotInfo(i)
773 if name and number >= 1 then
774 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
777 else
778 -- No idea where the items came from.
779 local index, x, y = self:PlayerPosition()
781 if index then
782 for i = 1, GetNumLootItems() do
783 local icon, name, number, rarity = GetLootSlotInfo(i)
784 if name and number >= 1 then
785 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
791 end]]
793 --[[if event == "CHAT_MSG_SYSTEM" then
794 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
795 if home_name then
796 if self.i then
797 self:TextOut(QHText("HOME_CHANGED"))
798 self:TextOut(QHText("WILL_RESET_PATH"))
800 local home = QuestHelper_Home
801 if not home then
802 home = {}
803 QuestHelper_Home = home
806 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
807 self.defered_graph_reset = true
810 end]]
815 --[[if event == "QUEST_DETAIL" then
816 if not self.quest_giver then self.quest_giver = {} end
817 local npc = UnitName("npc")
818 if npc then
819 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
820 local index, x, y = self:UnitPosition("npc")
822 if index then -- Might not have a position if inside an instance.
823 local npc_objective = self:GetObjective("monster", npc)
824 self:AppendObjectivePosition(npc_objective, index, x, y)
825 self.quest_giver[GetTitleText()] = npc
828 end]]
830 --[[if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
831 local quest = GetTitleText()
832 if quest then
833 local level, hash = self:GetQuestLevel(quest)
834 if not level or level < 1 then
835 --self:TextOut("Don't know quest level for ".. quest.."!")
836 return
838 local q = self:GetQuest(quest, level, hash)
840 if q.need_hash then
841 q.o.hash = hash
844 local unit = UnitName("npc")
845 if unit then
846 q.o.finish = unit
847 q.o.pos = nil
849 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
850 local index, x, y = self:UnitPosition("npc")
851 if index then -- Might not have a position if inside an instance.
852 local npc_objective = self:GetObjective("monster", unit)
853 self:AppendObjectivePosition(npc_objective, index, x, y)
855 elseif not q.o.finish then
856 local index, x, y = self:PlayerPosition()
857 if index then -- Might not have a position if inside an instance.
858 self:AppendObjectivePosition(q, index, x, y)
862 end]]
864 --[[if event == "MERCHANT_SHOW" then
865 local npc_name = UnitName("npc")
866 if npc_name then
867 local npc_objective = self:GetObjective("monster", npc_name)
868 local index = 1
869 while true do
870 local item_name = GetMerchantItemInfo(index)
871 if item_name then
872 index = index + 1
873 local item_objective = self:GetObjective("item", item_name)
874 if not item_objective.o.vendor then
875 item_objective.o.vendor = {npc_name}
876 else
877 local known = false
878 for i, vendor in ipairs(item_objective.o.vendor) do
879 if npc_name == vendor then
880 known = true
881 break
884 if not known then
885 table.insert(item_objective.o.vendor, npc_name)
888 else
889 break
893 end]]
895 if event == "TAXIMAP_OPENED" then
896 self:taxiMapOpened()
899 --[[if event == "PLAYER_CONTROL_GAINED" then
900 interruptcount = interruptcount + 1
901 end]]
903 --[[if event == "BAG_UPDATE" then
904 for slot = 1,GetContainerNumSlots(arg1) do
905 local link = GetContainerItemLink(arg1, slot)
906 if link then
907 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
908 if name then
909 self:GetObjective("item", name).o.id = tonumber(id)
913 end]]
917 if event == "ZONE_CHANGED" or event == "ZONE_CHANGED_INDOORS" or event == "ZONE_CHANGED_NEW_AREA" then
918 QH_Route_Filter_Rescan()
921 QH_Timeslice_Increment(GetTime() - tstart, "event")
922 end]==]
924 local map_shown_decay = 0
925 local delayed_action = 100
926 --local update_count = 0
927 local ontaxi = false
928 local frams = 0
930 QH_OnUpdate_High(function ()
931 local self = QuestHelper -- hoorj
932 local tstart = GetTime()
933 frams = frams + 1
935 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
937 if false and frams == 60 then
938 self:ShowText([[
939 This is a |cffff8000beta of QuestHelper|r. Be warned: It may crash. It may lock up. It may give bad advice. It may spew errors. It shouldn't spam people, delete your hard-won epics, or make your computer catch on fire, but technically I'm giving no guarantees. |cffff8000If you want a polished, functioning product, close WoW, download the official QH release from curse.com, and use that.|r
941 Known bugs and issues include:
943 |cff40bbffNo support for "/qh find"|r
945 |cff40bbffNo support for in-party quest synchronization|r
947 These may not be fixed before the official 1.0 release - I'm hoping to get them all finished up in time for 1.1.
949 If you encounter any issue besides the ones listed here, please please please report it, if you're reading this you know how to get in contact with me anyway.
951 Thanks for testing!]], "QuestHelper " .. version_string, 500, 20, 10)
954 --if frams == 5000 then please_submit_enabled = false end -- TOOK TOO LONG >:(
955 if please_submit_enabled and startup_time and startup_time + 10 < GetTime() then
956 QuestHelper:TextOut(QHText("PLEASE_SUBMIT"))
957 startup_time = nil
958 please_submit_enabled = false
960 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
962 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
963 init_cartographer_later = false
964 if QuestHelper_Pref.cart_wp_new then
965 self:EnableCartographer()
969 if not ontaxi and UnitOnTaxi("player") then
970 self:flightBegan()
971 interruptcount = 0
972 elseif ontaxi and not UnitOnTaxi("player") then
973 self:flightEnded(interruptcount > 1)
975 ontaxi = UnitOnTaxi("player")
977 -- For now I'm ripping out the update_count code
978 --update_count = update_count - 1
979 --if update_count <= 0 then
981 -- Reset the update count for next time around; this will make sure the body executes every time
982 -- when perf_scale_2 >= 1, and down to 1 in 10 iterations when perf_scale_2 < 1, or when hidden.
983 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale_2)
985 --if update_count < 0 then
986 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
987 --update_count = 0
988 --end
990 if self.Astrolabe.WorldMapVisible then
991 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
992 map_shown_decay = 2
993 elseif map_shown_decay > 0 then
994 map_shown_decay = map_shown_decay - 1
995 else
996 --SetMapToCurrentZone() -- not sure why this existed
999 --[[delayed_action = delayed_action - 1
1000 if delayed_action <= 0 then
1001 delayed_action = 100
1002 self:HandlePartyChange()
1003 end]]
1005 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
1006 local tc, tx, ty
1008 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
1009 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
1010 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
1013 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
1014 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
1015 -- be seen in that zone but aren't in it.
1016 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
1017 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
1018 nz, nx, ny = self.z, nnx, nny
1022 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
1023 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
1024 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
1025 nz = self.z
1026 else
1027 nc, nz, nx, ny = nil, nil, nil, nil
1031 if nc and nz > 0 and QuestHelper_IndexLookup[nc] then -- QuestHelper_IndexLookup is only initialized after we've finished the preinit step
1032 self.c, self.z, self.x, self.y = nc, nz, nx, ny
1033 local upd_zone = false
1034 if self.i ~= QuestHelper_IndexLookup[nc][nz] then upd_zone = true end
1035 self.i = QuestHelper_IndexLookup[nc][nz]
1036 if upd_zone then QH_Route_Filter_Rescan("filter_zone") end
1039 if nc and nz and nx and ny and tc and tx and ty then
1040 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
1041 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
1042 self.collect_delayed = false
1044 local ibi = self.InBrokenInstance
1045 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
1047 if ibi and not self.InBrokenInstance then self.minimap_marker:OnUpdate(0) end -- poke
1048 else
1049 self.collect_delayed = true
1050 self.InBrokenInstance = true
1053 if not UnitOnTaxi("player") and not UnitIsDeadOrGhost("player") then
1054 QuestHelper.routing_ac, QuestHelper.routing_ax, QuestHelper.routing_ay, QuestHelper.routing_c, QuestHelper.routing_z = QuestHelper.collect_ac, QuestHelper.collect_ax, QuestHelper.collect_ay, QuestHelper.c, QuestHelper.z
1057 QH_Timeslice_Toggle("routing", not not self.c)
1059 self:PumpCommMessages()
1060 --end
1061 end)
1063 -- 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, or -77 for the DK starting zone) and x,y are the coordinates within that continent.
1064 -- 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?
1065 function QuestHelper:Location_RawRetrieve()
1066 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
1068 function QuestHelper:Location_AbsoluteRetrieve()
1069 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
1072 --QH_Hook(QuestHelper, "OnEvent", QuestHelper.OnEvent)