try to instrument more proper-installation stuff
[QuestHelper.git] / main.lua
bloba3726632033a3328aefd4ac5bdc9b03bddf30983
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 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 zones="next",
28 track=true,
29 track_minimized=false,
30 track_scale=1,
31 track_level=true,
32 track_qcolour=true,
33 track_ocolour=true,
34 track_size=10,
35 blizzmap = false,
36 tooltip=true,
37 share = true,
38 scale = 1,
39 solo = false,
40 comm = false,
41 show_ants = true,
42 level = 3,
43 hide = false,
44 cart_wp_new = false,
45 tomtom_wp_new = false,
46 arrow = true,
47 arrow_locked = false,
48 arrow_arrowsize = 1,
49 arrow_textsize = 1,
50 metric = (QuestHelper_Locale ~= "enUS" and QuestHelper_Locale ~= "esMX"),
51 flight_time = true,
52 locale = GetLocale(), -- This variable is used for display purposes, and has nothing to do with the collected data.
53 perf_scale_2 = 1, -- How much background processing can the current machine handle? Higher means more load, lower means better performance.
54 perfload_scale = 1, -- Performance scale to use on startup
55 map_button = true,
56 travel_time = false,
59 -- 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.
60 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
62 QuestHelper_FlightInstructors = {}
63 QuestHelper_FlightLinks = {}
64 QuestHelper_FlightRoutes = {}
65 QuestHelper_KnownFlightRoutes = {}
66 QuestHelper_SeenRealms = {}
68 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
69 QuestHelper.objective_objects = {}
70 QuestHelper.user_objectives = {}
71 QuestHelper.quest_objects = {}
72 QuestHelper.player_level = 1
73 QuestHelper.locale = QuestHelper_Locale
75 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
76 (UnitFactionGroup("player") == "Horde" and 2)
78 assert(QuestHelper.faction)
80 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
82 function QuestHelper:GetFontPath(list_string, font)
83 if list_string then
84 for name in string.gmatch(list_string, "[^;]+") do
85 if font:SetFont(name, 10) then
86 return name
87 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
88 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
89 end
90 end
91 end
92 end
94 function QuestHelper:SetLocaleFonts()
95 self.font.sans = nil
96 self.font.serif = nil
97 self.font.fancy = nil
99 local font = self:CreateText(self)
101 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
102 -- Only use alternate fonts if using a language the client wasn't intended for.
103 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
104 if replacements then
105 self.font.sans = self:GetFontPath(replacements.sans, font)
106 self.font.serif = self:GetFontPath(replacements.serif, font)
107 self.font.fancy = self:GetFontPath(replacements.fancy, font)
111 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
112 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
113 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
115 self:ReleaseText(font)
117 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
118 self.font.serif = self.font.serif or GameFontNormal:GetFont()
119 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
121 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
122 -- This should do nothing if not using an alternate font.
123 DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
126 QuestHelper.route = {}
127 QuestHelper.to_add = {}
128 QuestHelper.to_remove = {}
129 QuestHelper.quest_log = {}
130 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
131 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
133 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
134 local last = self:NumLines()
135 local name = self:GetName()
136 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
139 function QuestHelper:SetTargetLocation(i, x, y, toffset)
140 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
141 local c, z = unpack(QuestHelper_ZoneLookup[i])
143 self.target = self:CreateTable()
144 self.target[2] = self:CreateTable()
146 self.target_time = time()+(toffset or 0)
148 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
149 self.target[1] = self.zone_nodes[i]
150 self.target[3] = x * self.continent_scales_x[c]
151 self.target[4] = y * self.continent_scales_y[c]
153 self:SetTargetLocationRecalculate()
156 function QuestHelper:SetTargetLocationRecalculate()
157 if self.target then
158 for i, n in ipairs(self.target[1]) do
159 local a, b = n.x-self.target[3], n.y-self.target[4]
160 self.target[2][i] = math.sqrt(a*a+b*b)
165 function QuestHelper:UnsetTargetLocation()
166 -- Unsets the target set above.
167 if self.target then
168 self:ReleaseTable(self.target[2])
169 self:ReleaseTable(self.target)
170 self.target = nil
171 self.target_time = nil
175 local interruptcount = 0 -- counts how many "played gained control" messages we recieve, used for flight paths
176 local init_cartographer_later = false
179 local startup_time
180 local please_submit_enabled = true
181 local please_submit_initted = false
183 QH_Event("ADDON_LOADED", function (addonid)
184 if addonid ~= "QuestHelper" then return end
185 local self = QuestHelper -- whee hack hack hack
187 QuestHelper_Loadtime["init_start"] = GetTime()
189 -- Use DefaultPref as fallback for unset preference keys.
190 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
192 local file_problem_version = false
194 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
196 local expected_files =
198 ["bst_pre.lua"] = true,
199 ["bst_post.lua"] = true,
200 ["bst_astrolabe.lua"] = true,
201 ["bst_ctl.lua"] = true,
202 ["bst_libaboutpanel.lua"] = true,
204 ["manager_event.lua"] = true,
206 ["upgrade.lua"] = true,
207 ["main.lua"] = true,
208 ["recycle.lua"] = true,
209 ["objective.lua"] = true,
210 ["quest.lua"] = true,
211 ["utility.lua"] = true,
212 ["dodads.lua"] = true,
213 ["dodads_triangles.lua"] = true,
214 ["teleport.lua"] = true,
215 ["pathfinding.lua"] = true,
216 ["routing.lua"] = true,
217 ["custom.lua"] = true,
218 ["menu.lua"] = true,
219 ["nag.lua"] = true,
220 ["comm.lua"] = true,
221 ["mapbutton.lua"] = true,
222 ["help.lua"] = true,
223 ["pattern.lua"] = true,
224 ["flightpath.lua"] = true,
225 ["tracker.lua"] = true,
226 ["objtips.lua"] = true,
227 ["cartographer.lua"] = true,
228 ["cartographer_is_terrible.lua"] = true,
229 ["tomtom.lua"] = true,
230 ["textviewer.lua"] = true,
231 ["error.lua"] = true,
232 ["timeslice.lua"] = true,
233 ["lang.lua"] = true,
234 ["core.lua"] = true,
235 ["tooltip.lua"] = true,
236 ["arrow.lua"] = true,
238 ["static.lua"] = true,
239 ["static_1.lua"] = true,
240 ["static_2.lua"] = true,
241 ["static_deDE.lua"] = true,
242 ["static_deDE_1.lua"] = true,
243 ["static_deDE_2.lua"] = true,
244 ["static_enUS.lua"] = true,
245 ["static_enUS_1.lua"] = true,
246 ["static_enUS_2.lua"] = true,
247 ["static_esES.lua"] = true,
248 ["static_esES_1.lua"] = true,
249 ["static_esES_2.lua"] = true,
250 ["static_esMX.lua"] = true,
251 ["static_esMX_1.lua"] = true,
252 ["static_esMX_2.lua"] = true,
253 ["static_frFR.lua"] = true,
254 ["static_frFR_1.lua"] = true,
255 ["static_frFR_2.lua"] = true,
256 ["static_koKR.lua"] = true,
257 ["static_koKR_1.lua"] = true,
258 ["static_koKR_2.lua"] = true,
259 ["static_ruRU.lua"] = true,
260 ["static_ruRU_1.lua"] = true,
261 ["static_ruRU_2.lua"] = true,
262 ["static_zhTW.lua"] = true,
263 ["static_zhTW_1.lua"] = true,
264 ["static_zhTW_2.lua"] = true,
266 ["collect.lua"] = true,
267 ["collect_achievement.lua"] = true,
268 ["collect_lzw.lua"] = true,
269 ["collect_traveled.lua"] = true,
270 ["collect_zone.lua"] = true,
271 ["collect_location.lua"] = true,
272 ["collect_merger.lua"] = true,
273 ["collect_monster.lua"] = true,
274 ["collect_item.lua"] = true,
275 ["collect_object.lua"] = true,
276 ["collect_loot.lua"] = true,
277 ["collect_patterns.lua"] = true,
278 ["collect_flight.lua"] = true,
279 ["collect_util.lua"] = true,
280 ["collect_quest.lua"] = true,
281 ["collect_equip.lua"] = true,
282 ["collect_notifier.lua"] = true,
283 ["collect_bitstream.lua"] = true,
284 ["collect_spec.lua"] = true,
285 ["collect_upgrade.lua"] = true,
286 ["collect_merchant.lua"] = true,
287 ["collect_warp.lua"] = true,
289 ["filter_core.lua"] = true,
290 ["filter_base.lua"] = true,
292 ["routing_debug.lua"] = true,
293 ["routing_loc.lua"] = true,
294 ["routing_route.lua"] = true,
295 ["routing_core.lua"] = true,
296 ["routing_controller.lua"] = true,
297 ["routing_hidden.lua"] = true,
299 ["director_quest.lua"] = true,
300 ["director_achievement.lua"] = true,
302 ["db_get.lua"] = true,
304 ["graph_core.lua"] = true,
305 ["graph_flightpath.lua"] = true,
307 ["AstrolabeQH/Astrolabe.lua"] = true,
308 ["AstrolabeQH/AstrolabeMapMonitor.lua"] = true,
311 local uninstallederr = ""
313 for file, version in pairs(QuestHelper_File) do
314 if not expected_files[file] then
315 local errmsg = "Unexpected QuestHelper file: "..file
316 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
317 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
318 file_problem_version = true
319 elseif version ~= expected_version then
320 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
321 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
322 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
323 if version ~= "Development Version" and expected_version ~= "Development Version" then
324 -- Developers are allowed to mix dev versions with release versions
325 file_problem_version = true
330 for file in pairs(expected_files) do
331 if not QuestHelper_File[file] then
332 local errmsg = "Missing QuestHelper file: "..file
333 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
334 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
335 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem_version = true end
339 -- Don't need this table anymore.
340 QuestHelper_File = nil
342 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
343 local errmsg = "Static data does not seem to exist"
344 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
346 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
347 file_problem_version = true
350 if file_problem_version then
351 QH_fixedmessage(QHText("PLEASE_RESTART"))
352 QuestHelper_ErrorCatcher_ExplicitError(false, "not-installed-properly" .. "\n" .. uninstallederr)
353 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
354 return
357 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
358 QH_fixedmessage(QHText("PRIVATE_SERVER"))
359 QuestHelper_ErrorCatcher_ExplicitError(false, "error id cakbep ten T")
360 QuestHelper = nil
361 return
364 if not DongleStub or not QH_Astrolabe_Ready then
365 QH_fixedmessage(QHText("NOT_UNZIPPED_CORRECTLY"))
366 QuestHelper_ErrorCatcher_ExplicitError(false, "not-unzipped-properly")
367 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
368 return
371 QuestHelper_ErrorCatcher_CompletelyStarted()
373 if not QuestHelper_StaticData then
374 -- If there is no static data for some mysterious reason, create an empty table so that
375 -- other parts of the code can carry on as usual, using locally collected data if it exists.
376 QuestHelper_StaticData = {}
379 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
381 if not QuestHelper_UID then
382 QuestHelper_UID = self:CreateUID()
384 QuestHelper_SaveDate = time()
386 QuestHelper_BuildZoneLookup()
387 QH_Graph_Init()
388 load_graph_links()
390 if QuestHelper_Locale ~= GetLocale() then
391 self:TextOut(QHText("LOCALE_ERROR"))
392 return
395 if not self:ZoneSanity() then
396 self:TextOut(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
397 QH_fixedmessage(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
398 QuestHelper = nil
399 return
402 QuestHelper_UpgradeDatabase(_G)
403 QuestHelper_UpgradeComplete()
405 if QuestHelper_IsPolluted(_G) then
406 self:TextOut(QHFormat("NAG_POLLUTED"))
407 self:Purge(nil, true, true)
410 local signature = expected_version .. " on " .. GetBuildInfo()
411 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
412 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
413 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
414 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
416 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
417 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
418 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
419 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
421 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
423 QH_Collector_Init()
424 DB_Init()
426 self.player_level = UnitLevel("player")
428 self:SetLocaleFonts()
430 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
431 self:EnableSharing()
434 if QuestHelper_Pref.hide then
435 self.map_overlay:Hide()
438 self:HandlePartyChange()
440 self:Nag("all")
442 for locale in pairs(QuestHelper_StaticData) do
443 if locale ~= self.locale then
444 -- Will delete references to locales you don't use.
445 QuestHelper_StaticData[locale] = nil
446 _G["QuestHelper_StaticData_" .. locale] = nil
450 local static = QuestHelper_StaticData[self.locale]
452 if static then
453 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
454 if faction ~= self.faction then
455 -- Will delete references to flight instructors that don't belong to your faction.
456 static.flight_instructors[faction] = nil
458 end end
460 if static.quest then for faction in pairs(static.quest) do
461 if faction ~= self.faction then
462 -- Will delete references to quests that don't belong to your faction.
463 static.quest[faction] = nil
465 end end
468 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
469 if not QuestHelper_CharVersion then
470 -- Changing per-character flight routes, now only storing the flight points they have,
471 -- will attempt to guess the routes from this.
472 local routes = {}
474 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
475 for key in pairs(l) do
476 routes[key] = true
480 QuestHelper_KnownFlightRoutes = routes
482 -- Deleting the player's home again.
483 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
484 QuestHelper_Home = nil
485 QuestHelper_CharVersion = 1
488 if not QuestHelper_Home then
489 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
490 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
493 if QuestHelper_Pref.map_button then
494 QuestHelper:InitMapButton()
497 if QuestHelper_Pref.cart_wp_new then
498 init_cartographer_later = true
501 if QuestHelper_Pref.tomtom_wp_new then
502 self:EnableTomTom()
505 self.tracker:SetScale(QuestHelper_Pref.track_scale)
507 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
508 self:ShowTracker()
511 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
513 local major, minor = (version_string or ""):match("^(%d+)%.(%d+)")
514 major, minor = tonumber(major), tonumber(minor)
516 -- For versions before 0.82, we're changing the default level offset to 3.
517 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
518 QuestHelper_Pref.level = nil
521 -- For versions before 0.84...
522 if major == 0 and minor and minor < 84 then
523 -- remove all keys that match their default setting.
524 for key, val in pairs(QuestHelper_DefaultPref) do
525 if QuestHelper_Pref[key] == val then
526 QuestHelper_Pref[key] = nil
531 QH_Hook(self, "OnUpdate", self.OnUpdate)
533 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
534 --collectgarbage("collect") -- Free everything we aren't using.
536 --[[
537 if self.debug_objectives then
538 for name, data in pairs(self.debug_objectives) do
539 self:LoadDebugObjective(name, data)
541 end]]
543 -- wellllp
544 QH_Arrow_SetScale()
545 QH_Arrow_SetTextScale()
547 --[[
548 QH_Timeslice_Add(function ()
549 self:ResetPathing()
550 self.Routing:Initialize() -- Set up the routing task
551 end, "init")]] -- FUCK YOU BOXBOT
553 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
554 function C()
557 function A()
558 q = 0
559 for x = 0, 130000000, 1 do
563 function B()
564 q = 0
565 for x = 0, 12000000, 1 do
570 function B2()
571 q = 0
572 for x = 0, 1200000, 1 do
573 --q = q + x
578 debugprofilestart()
580 local ta = debugprofilestop()
582 local tb = debugprofilestop()
584 local tc = debugprofilestop()
586 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
587 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
589 --/script SetCVar("scriptProfile", value)]]
591 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
593 QuestHelper_Loadtime["init_end"] = GetTime()
595 QuestHelper.loading_main = QuestHelper.CreateLoadingCounter()
597 QuestHelper.loading_flightpath = QuestHelper.loading_main:MakeSubcategory(1)
598 QuestHelper.loading_preroll = QuestHelper.loading_main:MakeSubcategory(1)
600 QH_Event("CHAT_MSG_ADDON", function (...)
601 if arg1 == "QHpr" and arg4 ~= UnitName("player") then
602 QH_Questcomm_Msg(arg2, arg4)
604 end)
606 QH_Event({"PARTY_MEMBERS_CHANGED", "UNIT_LEVEL", "RAID_ROSTER_UPDATE"}, function ()
607 QH_Filter_Group_Sync()
608 QH_Route_Filter_Rescan("filter_quest_level")
609 QH_Route_Filter_Rescan("filter_quest_group")
610 end)
612 QH_Event({"PARTY_MEMBERS_CHANGED", "RAID_ROSTER_UPDATE"}, function ()
613 QH_Questcomm_Sync()
614 end)
616 QH_Event("PLAYER_LEVEL_UP", function ()
617 self.player_level = arg1
618 QH_Route_Filter_Rescan("filter_quest_level")
619 end)
621 QH_Event("TAXIMAP_OPENED", function ()
622 self:taxiMapOpened()
623 end)
625 QH_Event({"ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, function()
626 QH_Route_Filter_Rescan()
627 end)
629 QH_Event("CHAT_MSG_CHANNEL_NOTICE", function()
630 if please_submit_enabled and not please_submit_initted then
631 please_submit_enabled = QHNagInit()
632 startup_time = GetTime()
633 please_submit_initted = true
635 end)
637 end)
640 --[==[
641 function QuestHelper:OnEvent(event)
642 local tstart = GetTime()
644 --[[
645 if event == "GOSSIP_SHOW" then
646 local name, id = UnitName("npc"), self:GetUnitID("npc")
647 if name and id then
648 self:GetObjective("monster", name).o.id = id
649 --self:TextOut("NPC: "..name.." = "..id)
651 end]]
653 --[[if event == "PLAYER_TARGET_CHANGED" then
654 local name, id = UnitName("target"), self:GetUnitID("target")
655 if name and id then
656 self:GetObjective("monster", name).o.id = id
657 --self:TextOut("Target: "..name.." = "..id)
660 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
661 local index, x, y = self:UnitPosition("target")
663 if index then -- Might not have a position if inside an instance.
664 local w = 0.1
666 -- Modify the weight based on how far they are from us.
667 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
668 if CheckInteractDistance("target", 3) then w = 1
669 elseif CheckInteractDistance("target", 2) then w = 0.89
670 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
672 local monster_objective = self:GetObjective("monster", UnitName("target"))
673 self:AppendObjectivePosition(monster_objective, index, x, y, w)
675 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
676 (UnitFactionGroup("target") == "Horde" and 2) or nil
678 local level = UnitLevel("target")
679 if level and level >= 1 then
680 local w = monster_objective.o.levelw or 0
681 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
682 monster_objective.o.levelw = w+1
686 end]]
688 --[[if event == "LOOT_OPENED" then
689 local target = UnitName("target")
690 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
691 local index, x, y = self:UnitPosition("target")
693 local monster_objective = self:GetObjective("monster", target)
694 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
696 if index then -- Might not have a position if inside an instance.
697 self:AppendObjectivePosition(monster_objective, index, x, y)
700 for i = 1, GetNumLootItems() do
701 local icon, name, number, rarity = GetLootSlotInfo(i)
702 if name then
703 if number and number >= 1 then
704 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
705 else
706 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
707 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
708 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
710 if total > 0 then
711 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
716 else
717 local container = nil
719 -- Go through the players inventory and look for a locked item, we're probably looting it.
720 for bag = 0,NUM_BAG_SLOTS do
721 for slot = 1,GetContainerNumSlots(bag) do
722 local link = GetContainerItemLink(bag, slot)
723 if link and select(3, GetContainerItemInfo(bag, slot)) then
724 if container == nil then
725 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
726 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
727 else
728 -- Already tried to assign to a container. If there are multiple locked items, we give up.
729 container = false
735 if container then
736 local container_objective = self:GetObjective("item", container)
737 container_objective.o.opened = (container_objective.o.opened or 0) + 1
739 for i = 1, GetNumLootItems() do
740 local icon, name, number, rarity = GetLootSlotInfo(i)
741 if name and number >= 1 then
742 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
745 else
746 -- No idea where the items came from.
747 local index, x, y = self:PlayerPosition()
749 if index then
750 for i = 1, GetNumLootItems() do
751 local icon, name, number, rarity = GetLootSlotInfo(i)
752 if name and number >= 1 then
753 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
759 end]]
761 --[[if event == "CHAT_MSG_SYSTEM" then
762 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
763 if home_name then
764 if self.i then
765 self:TextOut(QHText("HOME_CHANGED"))
766 self:TextOut(QHText("WILL_RESET_PATH"))
768 local home = QuestHelper_Home
769 if not home then
770 home = {}
771 QuestHelper_Home = home
774 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
775 self.defered_graph_reset = true
778 end]]
783 --[[if event == "QUEST_DETAIL" then
784 if not self.quest_giver then self.quest_giver = {} end
785 local npc = UnitName("npc")
786 if npc then
787 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
788 local index, x, y = self:UnitPosition("npc")
790 if index then -- Might not have a position if inside an instance.
791 local npc_objective = self:GetObjective("monster", npc)
792 self:AppendObjectivePosition(npc_objective, index, x, y)
793 self.quest_giver[GetTitleText()] = npc
796 end]]
798 --[[if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
799 local quest = GetTitleText()
800 if quest then
801 local level, hash = self:GetQuestLevel(quest)
802 if not level or level < 1 then
803 --self:TextOut("Don't know quest level for ".. quest.."!")
804 return
806 local q = self:GetQuest(quest, level, hash)
808 if q.need_hash then
809 q.o.hash = hash
812 local unit = UnitName("npc")
813 if unit then
814 q.o.finish = unit
815 q.o.pos = nil
817 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
818 local index, x, y = self:UnitPosition("npc")
819 if index then -- Might not have a position if inside an instance.
820 local npc_objective = self:GetObjective("monster", unit)
821 self:AppendObjectivePosition(npc_objective, index, x, y)
823 elseif not q.o.finish then
824 local index, x, y = self:PlayerPosition()
825 if index then -- Might not have a position if inside an instance.
826 self:AppendObjectivePosition(q, index, x, y)
830 end]]
832 --[[if event == "MERCHANT_SHOW" then
833 local npc_name = UnitName("npc")
834 if npc_name then
835 local npc_objective = self:GetObjective("monster", npc_name)
836 local index = 1
837 while true do
838 local item_name = GetMerchantItemInfo(index)
839 if item_name then
840 index = index + 1
841 local item_objective = self:GetObjective("item", item_name)
842 if not item_objective.o.vendor then
843 item_objective.o.vendor = {npc_name}
844 else
845 local known = false
846 for i, vendor in ipairs(item_objective.o.vendor) do
847 if npc_name == vendor then
848 known = true
849 break
852 if not known then
853 table.insert(item_objective.o.vendor, npc_name)
856 else
857 break
861 end]]
863 if event == "TAXIMAP_OPENED" then
864 self:taxiMapOpened()
867 --[[if event == "PLAYER_CONTROL_GAINED" then
868 interruptcount = interruptcount + 1
869 end]]
871 --[[if event == "BAG_UPDATE" then
872 for slot = 1,GetContainerNumSlots(arg1) do
873 local link = GetContainerItemLink(arg1, slot)
874 if link then
875 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
876 if name then
877 self:GetObjective("item", name).o.id = tonumber(id)
881 end]]
885 if event == "ZONE_CHANGED" or event == "ZONE_CHANGED_INDOORS" or event == "ZONE_CHANGED_NEW_AREA" then
886 QH_Route_Filter_Rescan()
889 QH_Timeslice_Increment(GetTime() - tstart, "event")
890 end]==]
892 local map_shown_decay = 0
893 local delayed_action = 100
894 --local update_count = 0
895 local ontaxi = false
896 local frams = 0
898 QH_OnUpdate_High(function ()
899 local self = QuestHelper -- hoorj
900 local tstart = GetTime()
901 frams = frams + 1
903 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
905 if false and frams == 60 then
906 self:ShowText([[
907 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
909 Known bugs and issues include:
911 |cff40bbffNo support for "/qh find"|r
913 |cff40bbffNo support for in-party quest synchronization|r
915 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.
917 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.
919 Thanks for testing!]], "QuestHelper " .. version_string, 500, 20, 10)
922 --if frams == 5000 then please_submit_enabled = false end -- TOOK TOO LONG >:(
923 if please_submit_enabled and startup_time and startup_time + 10 < GetTime() then
924 QuestHelper:TextOut(QHText("PLEASE_SUBMIT"))
925 startup_time = nil
926 please_submit_enabled = false
928 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
930 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
931 init_cartographer_later = false
932 if QuestHelper_Pref.cart_wp_new then
933 self:EnableCartographer()
937 if not ontaxi and UnitOnTaxi("player") then
938 self:flightBegan()
939 interruptcount = 0
940 elseif ontaxi and not UnitOnTaxi("player") then
941 self:flightEnded(interruptcount > 1)
943 ontaxi = UnitOnTaxi("player")
945 -- For now I'm ripping out the update_count code
946 --update_count = update_count - 1
947 --if update_count <= 0 then
949 -- Reset the update count for next time around; this will make sure the body executes every time
950 -- when perf_scale_2 >= 1, and down to 1 in 10 iterations when perf_scale_2 < 1, or when hidden.
951 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale_2)
953 --if update_count < 0 then
954 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
955 --update_count = 0
956 --end
958 if self.Astrolabe.WorldMapVisible then
959 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
960 map_shown_decay = 2
961 elseif map_shown_decay > 0 then
962 map_shown_decay = map_shown_decay - 1
963 else
964 --SetMapToCurrentZone() -- not sure why this existed
967 --[[delayed_action = delayed_action - 1
968 if delayed_action <= 0 then
969 delayed_action = 100
970 self:HandlePartyChange()
971 end]]
973 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
974 local tc, tx, ty
976 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
977 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
978 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
981 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
982 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
983 -- be seen in that zone but aren't in it.
984 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
985 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
986 nz, nx, ny = self.z, nnx, nny
990 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
991 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
992 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
993 nz = self.z
994 else
995 nc, nz, nx, ny = nil, nil, nil, nil
999 if nc and nz > 0 then
1000 self.c, self.z, self.x, self.y = nc, nz, nx, ny
1001 local upd_zone = false
1002 if self.i ~= QuestHelper_IndexLookup[nc][nz] then upd_zone = true end
1003 self.i = QuestHelper_IndexLookup[nc][nz]
1004 if upd_zone then QH_Route_Filter_Rescan("filter_zone") end
1007 if nc and nz and nx and ny and tc and tx and ty then
1008 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
1009 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
1010 self.collect_delayed = false
1012 local ibi = self.InBrokenInstance
1013 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
1015 if ibi and not self.InBrokenInstance then self.minimap_marker:OnUpdate(0) end -- poke
1016 else
1017 self.collect_delayed = true
1018 self.InBrokenInstance = true
1021 if not UnitOnTaxi("player") and not UnitIsDeadOrGhost("player") then
1022 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
1025 QH_Timeslice_Toggle("routing", not not self.c)
1027 self:PumpCommMessages()
1028 --end
1029 end)
1031 -- 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.
1032 -- 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?
1033 function QuestHelper:Location_RawRetrieve()
1034 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
1036 function QuestHelper:Location_AbsoluteRetrieve()
1037 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
1040 --QH_Hook(QuestHelper, "OnEvent", QuestHelper.OnEvent)