Merge branch 'master' into translations
[QuestHelper.git] / main.lua
blobd87887357900664a19911e6b071d811c917da456
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 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,
57 mini_opacity = 1,
60 -- 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.
61 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
63 QuestHelper_FlightInstructors = {}
64 QuestHelper_FlightLinks = {}
65 QuestHelper_FlightRoutes = {}
66 QuestHelper_KnownFlightRoutes = {}
67 QuestHelper_SeenRealms = {}
69 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
70 QuestHelper.objective_objects = {}
71 QuestHelper.user_objectives = {}
72 QuestHelper.quest_objects = {}
73 QuestHelper.player_level = 1
74 QuestHelper.locale = QuestHelper_Locale
76 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
77 (UnitFactionGroup("player") == "Horde" and 2)
79 assert(QuestHelper.faction)
81 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
83 function QuestHelper:GetFontPath(list_string, font)
84 if list_string then
85 for name in string.gmatch(list_string, "[^;]+") do
86 if font:SetFont(name, 10) then
87 return name
88 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
89 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
90 end
91 end
92 end
93 end
95 function QuestHelper:SetLocaleFonts()
96 self.font.sans = nil
97 self.font.serif = nil
98 self.font.fancy = nil
100 local font = self:CreateText(self)
102 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
103 -- Only use alternate fonts if using a language the client wasn't intended for.
104 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
105 if replacements then
106 self.font.sans = self:GetFontPath(replacements.sans, font)
107 self.font.serif = self:GetFontPath(replacements.serif, font)
108 self.font.fancy = self:GetFontPath(replacements.fancy, font)
112 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
113 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
114 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
116 self:ReleaseText(font)
118 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
119 self.font.serif = self.font.serif or GameFontNormal:GetFont()
120 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
122 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
123 -- This should do nothing if not using an alternate font.
124 DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
127 QuestHelper.route = {}
128 QuestHelper.to_add = {}
129 QuestHelper.to_remove = {}
130 QuestHelper.quest_log = {}
131 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
132 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
134 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
135 local last = self:NumLines()
136 local name = self:GetName()
137 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
140 function QuestHelper:SetTargetLocation(i, x, y, toffset)
141 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
142 local c, z = unpack(QuestHelper_ZoneLookup[i])
144 self.target = self:CreateTable()
145 self.target[2] = self:CreateTable()
147 self.target_time = time()+(toffset or 0)
149 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
150 self.target[1] = self.zone_nodes[i]
151 self.target[3] = x * self.continent_scales_x[c]
152 self.target[4] = y * self.continent_scales_y[c]
154 self:SetTargetLocationRecalculate()
157 function QuestHelper:SetTargetLocationRecalculate()
158 if self.target then
159 for i, n in ipairs(self.target[1]) do
160 local a, b = n.x-self.target[3], n.y-self.target[4]
161 self.target[2][i] = math.sqrt(a*a+b*b)
166 function QuestHelper:UnsetTargetLocation()
167 -- Unsets the target set above.
168 if self.target then
169 self:ReleaseTable(self.target[2])
170 self:ReleaseTable(self.target)
171 self.target = nil
172 self.target_time = nil
176 local interruptcount = 0 -- counts how many "played gained control" messages we recieve, used for flight paths
177 local init_cartographer_later = false
180 local startup_time
181 local please_submit_enabled = true
182 local please_submit_initted = false
184 local spawned = false
185 QH_Event("ADDON_LOADED", function (addonid)
186 if addonid ~= "QuestHelper" then return end
188 -- ONLY FAST STUFF ALLOWED IN HERE
190 -- Use DefaultPref as fallback for unset preference keys.
191 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
193 local self = QuestHelper -- whee hack hack hack
195 QuestHelper_Loadtime["init2_start"] = GetTime()
197 local file_problem_version = false
199 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
201 local expected_files =
203 ["bst_pre.lua"] = true,
204 ["bst_post.lua"] = true,
205 ["bst_astrolabe.lua"] = true,
206 ["bst_ctl.lua"] = true,
207 ["bst_libaboutpanel.lua"] = true,
209 ["manager_event.lua"] = true,
211 ["upgrade.lua"] = true,
212 ["main.lua"] = true,
213 ["recycle.lua"] = true,
214 ["objective.lua"] = true,
215 ["quest.lua"] = true,
216 ["utility.lua"] = true,
217 ["dodads.lua"] = true,
218 ["dodads_triangles.lua"] = true,
219 ["teleport.lua"] = true,
220 ["pathfinding.lua"] = true,
221 ["routing.lua"] = true,
222 ["custom.lua"] = true,
223 ["menu.lua"] = true,
224 ["nag.lua"] = true,
225 ["comm.lua"] = true,
226 ["mapbutton.lua"] = true,
227 ["help.lua"] = true,
228 ["pattern.lua"] = true,
229 ["flightpath.lua"] = true,
230 ["tracker.lua"] = true,
231 ["objtips.lua"] = true,
232 ["cartographer.lua"] = true,
233 ["cartographer_is_terrible.lua"] = true,
234 ["tomtom.lua"] = true,
235 ["textviewer.lua"] = true,
236 ["error.lua"] = true,
237 ["timeslice.lua"] = true,
238 ["lang.lua"] = true,
239 ["core.lua"] = true,
240 ["tooltip.lua"] = true,
241 ["arrow.lua"] = true,
243 ["static.lua"] = true,
244 ["static_1.lua"] = true,
245 ["static_2.lua"] = true,
246 ["static_deDE.lua"] = true,
247 ["static_deDE_1.lua"] = true,
248 ["static_deDE_2.lua"] = true,
249 ["static_enUS.lua"] = true,
250 ["static_enUS_1.lua"] = true,
251 ["static_enUS_2.lua"] = true,
252 ["static_esES.lua"] = true,
253 ["static_esES_1.lua"] = true,
254 ["static_esES_2.lua"] = true,
255 ["static_esMX.lua"] = true,
256 ["static_esMX_1.lua"] = true,
257 ["static_esMX_2.lua"] = true,
258 ["static_frFR.lua"] = true,
259 ["static_frFR_1.lua"] = true,
260 ["static_frFR_2.lua"] = true,
261 ["static_koKR.lua"] = true,
262 ["static_koKR_1.lua"] = true,
263 ["static_koKR_2.lua"] = true,
264 ["static_ruRU.lua"] = true,
265 ["static_ruRU_1.lua"] = true,
266 ["static_ruRU_2.lua"] = true,
267 ["static_zhTW.lua"] = true,
268 ["static_zhTW_1.lua"] = true,
269 ["static_zhTW_2.lua"] = true,
271 ["collect.lua"] = true,
272 ["collect_achievement.lua"] = true,
273 ["collect_lzw.lua"] = true,
274 ["collect_traveled.lua"] = true,
275 ["collect_zone.lua"] = true,
276 ["collect_location.lua"] = true,
277 ["collect_merger.lua"] = true,
278 ["collect_monster.lua"] = true,
279 ["collect_item.lua"] = true,
280 ["collect_object.lua"] = true,
281 ["collect_loot.lua"] = true,
282 ["collect_patterns.lua"] = true,
283 ["collect_flight.lua"] = true,
284 ["collect_util.lua"] = true,
285 ["collect_quest.lua"] = true,
286 ["collect_equip.lua"] = true,
287 ["collect_notifier.lua"] = true,
288 ["collect_bitstream.lua"] = true,
289 ["collect_spec.lua"] = true,
290 ["collect_upgrade.lua"] = true,
291 ["collect_merchant.lua"] = true,
292 ["collect_warp.lua"] = true,
294 ["filter_core.lua"] = true,
295 ["filter_base.lua"] = true,
297 ["routing_debug.lua"] = true,
298 ["routing_loc.lua"] = true,
299 ["routing_route.lua"] = true,
300 ["routing_core.lua"] = true,
301 ["routing_controller.lua"] = true,
302 ["routing_hidden.lua"] = true,
304 ["director_quest.lua"] = true,
305 ["director_achievement.lua"] = true,
307 ["db_get.lua"] = true,
309 ["graph_core.lua"] = true,
310 ["graph_flightpath.lua"] = true,
312 ["AstrolabeQH/Astrolabe.lua"] = true,
313 ["AstrolabeQH/AstrolabeMapMonitor.lua"] = true,
316 local uninstallederr = ""
318 for file, version in pairs(QuestHelper_File) do
319 if not expected_files[file] then
320 local errmsg = "Unexpected QuestHelper file: "..file
321 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
322 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
323 file_problem_version = true
324 elseif version ~= expected_version then
325 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
326 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
327 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
328 if version ~= "Development Version" and expected_version ~= "Development Version" then
329 -- Developers are allowed to mix dev versions with release versions
330 file_problem_version = true
335 for file in pairs(expected_files) do
336 if not QuestHelper_File[file] then
337 local errmsg = "Missing QuestHelper file: "..file
338 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
339 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
340 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem_version = true end
344 -- Don't need this table anymore.
345 QuestHelper_File = nil
347 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
348 local errmsg = "Static data does not seem to exist"
349 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
351 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
352 file_problem_version = true
355 if file_problem_version then
356 QH_fixedmessage(QHText("PLEASE_RESTART"))
357 QuestHelper_ErrorCatcher_ExplicitError(false, "not-installed-properly" .. "\n" .. uninstallederr)
358 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
359 return
362 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
363 QH_fixedmessage(QHText("PRIVATE_SERVER"))
364 QuestHelper_ErrorCatcher_ExplicitError(false, "error id cakbep ten T")
365 QuestHelper = nil
366 return
369 if not DongleStub or not QH_Astrolabe_Ready then
370 QH_fixedmessage(QHText("NOT_UNZIPPED_CORRECTLY"))
371 QuestHelper_ErrorCatcher_ExplicitError(false, "not-unzipped-properly")
372 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
373 return
376 QuestHelper_ErrorCatcher_CompletelyStarted()
378 if not QuestHelper_StaticData then
379 -- If there is no static data for some mysterious reason, create an empty table so that
380 -- other parts of the code can carry on as usual, using locally collected data if it exists.
381 QuestHelper_StaticData = {}
384 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
386 if not QuestHelper_UID then
387 QuestHelper_UID = self:CreateUID()
389 QuestHelper_SaveDate = time()
392 QH_Timeslice_Add(function ()
393 QuestHelper_Loadtime["init3_start"] = GetTime()
395 QuestHelper.loading_main = QuestHelper.CreateLoadingCounter()
397 QuestHelper.loading_init3 = QuestHelper.loading_main:MakeSubcategory(0.3)
398 QuestHelper.loading_flightpath = QuestHelper.loading_main:MakeSubcategory(1)
399 QuestHelper.loading_preroll = QuestHelper.loading_main:MakeSubcategory(1)
401 local stt = 0
403 -- This is where the slow stuff goes
404 QuestHelper_BuildZoneLookup()
405 QH_Graph_Init()
406 load_graph_links()
408 if QuestHelper_Locale ~= GetLocale() then
409 self:TextOut(QHText("LOCALE_ERROR"))
410 return
413 if not self:ZoneSanity() then
414 self:TextOut(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
415 QH_fixedmessage(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
416 QuestHelper = nil
417 return
420 QuestHelper_UpgradeDatabase(_G)
421 QuestHelper_UpgradeComplete()
423 if QuestHelper_IsPolluted(_G) then
424 self:TextOut(QHFormat("NAG_POLLUTED"))
425 self:Purge(nil, true, true)
428 local signature = expected_version .. " on " .. GetBuildInfo()
429 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
430 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
431 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
432 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
434 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
435 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
436 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
437 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
439 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
441 QuestHelper.loading_init3:SetPercentage(0.1)
442 QH_Collector_Init()
443 QuestHelper.loading_init3:SetPercentage(0.5)
444 DB_Init()
445 QuestHelper.loading_init3:SetPercentage(0.9)
447 self.player_level = UnitLevel("player")
449 self:SetLocaleFonts()
451 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
452 self:EnableSharing()
455 if QuestHelper_Pref.hide then
456 self.map_overlay:Hide()
459 self:HandlePartyChange()
461 self:Nag("all")
463 for locale in pairs(QuestHelper_StaticData) do
464 if locale ~= self.locale then
465 -- Will delete references to locales you don't use.
466 QuestHelper_StaticData[locale] = nil
467 _G["QuestHelper_StaticData_" .. locale] = nil
471 local static = QuestHelper_StaticData[self.locale]
473 if static then
474 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
475 if faction ~= self.faction then
476 -- Will delete references to flight instructors that don't belong to your faction.
477 static.flight_instructors[faction] = nil
479 end end
481 if static.quest then for faction in pairs(static.quest) do
482 if faction ~= self.faction then
483 -- Will delete references to quests that don't belong to your faction.
484 static.quest[faction] = nil
486 end end
489 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
490 if not QuestHelper_CharVersion then
491 -- Changing per-character flight routes, now only storing the flight points they have,
492 -- will attempt to guess the routes from this.
493 local routes = {}
495 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
496 for key in pairs(l) do
497 routes[key] = true
501 QuestHelper_KnownFlightRoutes = routes
503 -- Deleting the player's home again.
504 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
505 QuestHelper_Home = nil
506 QuestHelper_CharVersion = 1
509 if not QuestHelper_Home then
510 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
511 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
514 if QuestHelper_Pref.map_button then
515 QuestHelper:InitMapButton()
518 if QuestHelper_Pref.cart_wp_new then
519 init_cartographer_later = true
522 if QuestHelper_Pref.tomtom_wp_new then
523 self:EnableTomTom()
526 self.tracker:SetScale(QuestHelper_Pref.track_scale)
528 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
530 local major, minor = (version_string or ""):match("^(%d+)%.(%d+)")
531 major, minor = tonumber(major), tonumber(minor)
533 -- For versions before 0.82, we're changing the default level offset to 3.
534 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
535 QuestHelper_Pref.level = nil
538 -- For versions before 0.84...
539 if major == 0 and minor and minor < 84 then
540 -- remove all keys that match their default setting.
541 for key, val in pairs(QuestHelper_DefaultPref) do
542 if QuestHelper_Pref[key] == val then
543 QuestHelper_Pref[key] = nil
548 QH_Hook(self, "OnUpdate", self.OnUpdate)
550 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
551 --collectgarbage("collect") -- Free everything we aren't using.
553 --[[
554 if self.debug_objectives then
555 for name, data in pairs(self.debug_objectives) do
556 self:LoadDebugObjective(name, data)
558 end]]
560 -- wellllp
561 QH_Arrow_SetScale()
562 QH_Arrow_SetTextScale()
564 --[[
565 QH_Timeslice_Add(function ()
566 self:ResetPathing()
567 self.Routing:Initialize() -- Set up the routing task
568 end, "init")]] -- FUCK YOU BOXBOT
570 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
571 function C()
574 function A()
575 q = 0
576 for x = 0, 130000000, 1 do
580 function B()
581 q = 0
582 for x = 0, 12000000, 1 do
587 function B2()
588 q = 0
589 for x = 0, 1200000, 1 do
590 --q = q + x
595 debugprofilestart()
597 local ta = debugprofilestop()
599 local tb = debugprofilestop()
601 local tc = debugprofilestop()
603 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
604 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
606 --/script SetCVar("scriptProfile", value)]]
608 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
611 QH_Event("CHAT_MSG_ADDON", function (...)
612 if arg1 == "QHpr" and arg4 ~= UnitName("player") then
613 QH_Questcomm_Msg(arg2, arg4)
615 end)
617 QH_Event({"PARTY_MEMBERS_CHANGED", "UNIT_LEVEL", "RAID_ROSTER_UPDATE"}, function ()
618 QH_Filter_Group_Sync()
619 QH_Route_Filter_Rescan("filter_quest_level")
620 QH_Route_Filter_Rescan("filter_quest_group")
621 end)
623 QH_Event({"PARTY_MEMBERS_CHANGED", "RAID_ROSTER_UPDATE"}, function ()
624 QH_Questcomm_Sync()
625 end)
627 QH_Event("PLAYER_LEVEL_UP", function ()
628 self.player_level = arg1
629 QH_Route_Filter_Rescan("filter_quest_level")
630 end)
632 QH_Event("TAXIMAP_OPENED", function ()
633 self:taxiMapOpened()
634 end)
636 QH_Event({"ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, function()
637 QH_Route_Filter_Rescan()
638 end)
640 QH_Event("CHAT_MSG_CHANNEL_NOTICE", function()
641 if please_submit_enabled and not please_submit_initted then
642 please_submit_enabled = QHNagInit()
643 startup_time = GetTime()
644 please_submit_initted = true
646 end)
648 QuestHelper.loading_init3:SetPercentage(1.0) -- victory
650 QuestHelper_Loadtime["init3_end"] = GetTime()
651 end, "preinit")
653 QuestHelper_Loadtime["init2_end"] = GetTime()
654 end)
657 --[==[
658 function QuestHelper:OnEvent(event)
659 local tstart = GetTime()
661 --[[
662 if event == "GOSSIP_SHOW" then
663 local name, id = UnitName("npc"), self:GetUnitID("npc")
664 if name and id then
665 self:GetObjective("monster", name).o.id = id
666 --self:TextOut("NPC: "..name.." = "..id)
668 end]]
670 --[[if event == "PLAYER_TARGET_CHANGED" then
671 local name, id = UnitName("target"), self:GetUnitID("target")
672 if name and id then
673 self:GetObjective("monster", name).o.id = id
674 --self:TextOut("Target: "..name.." = "..id)
677 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
678 local index, x, y = self:UnitPosition("target")
680 if index then -- Might not have a position if inside an instance.
681 local w = 0.1
683 -- Modify the weight based on how far they are from us.
684 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
685 if CheckInteractDistance("target", 3) then w = 1
686 elseif CheckInteractDistance("target", 2) then w = 0.89
687 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
689 local monster_objective = self:GetObjective("monster", UnitName("target"))
690 self:AppendObjectivePosition(monster_objective, index, x, y, w)
692 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
693 (UnitFactionGroup("target") == "Horde" and 2) or nil
695 local level = UnitLevel("target")
696 if level and level >= 1 then
697 local w = monster_objective.o.levelw or 0
698 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
699 monster_objective.o.levelw = w+1
703 end]]
705 --[[if event == "LOOT_OPENED" then
706 local target = UnitName("target")
707 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
708 local index, x, y = self:UnitPosition("target")
710 local monster_objective = self:GetObjective("monster", target)
711 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
713 if index then -- Might not have a position if inside an instance.
714 self:AppendObjectivePosition(monster_objective, index, x, y)
717 for i = 1, GetNumLootItems() do
718 local icon, name, number, rarity = GetLootSlotInfo(i)
719 if name then
720 if number and number >= 1 then
721 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
722 else
723 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
724 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
725 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
727 if total > 0 then
728 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
733 else
734 local container = nil
736 -- Go through the players inventory and look for a locked item, we're probably looting it.
737 for bag = 0,NUM_BAG_SLOTS do
738 for slot = 1,GetContainerNumSlots(bag) do
739 local link = GetContainerItemLink(bag, slot)
740 if link and select(3, GetContainerItemInfo(bag, slot)) then
741 if container == nil then
742 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
743 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
744 else
745 -- Already tried to assign to a container. If there are multiple locked items, we give up.
746 container = false
752 if container then
753 local container_objective = self:GetObjective("item", container)
754 container_objective.o.opened = (container_objective.o.opened or 0) + 1
756 for i = 1, GetNumLootItems() do
757 local icon, name, number, rarity = GetLootSlotInfo(i)
758 if name and number >= 1 then
759 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
762 else
763 -- No idea where the items came from.
764 local index, x, y = self:PlayerPosition()
766 if index then
767 for i = 1, GetNumLootItems() do
768 local icon, name, number, rarity = GetLootSlotInfo(i)
769 if name and number >= 1 then
770 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
776 end]]
778 --[[if event == "CHAT_MSG_SYSTEM" then
779 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
780 if home_name then
781 if self.i then
782 self:TextOut(QHText("HOME_CHANGED"))
783 self:TextOut(QHText("WILL_RESET_PATH"))
785 local home = QuestHelper_Home
786 if not home then
787 home = {}
788 QuestHelper_Home = home
791 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
792 self.defered_graph_reset = true
795 end]]
800 --[[if event == "QUEST_DETAIL" then
801 if not self.quest_giver then self.quest_giver = {} end
802 local npc = UnitName("npc")
803 if npc then
804 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
805 local index, x, y = self:UnitPosition("npc")
807 if index then -- Might not have a position if inside an instance.
808 local npc_objective = self:GetObjective("monster", npc)
809 self:AppendObjectivePosition(npc_objective, index, x, y)
810 self.quest_giver[GetTitleText()] = npc
813 end]]
815 --[[if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
816 local quest = GetTitleText()
817 if quest then
818 local level, hash = self:GetQuestLevel(quest)
819 if not level or level < 1 then
820 --self:TextOut("Don't know quest level for ".. quest.."!")
821 return
823 local q = self:GetQuest(quest, level, hash)
825 if q.need_hash then
826 q.o.hash = hash
829 local unit = UnitName("npc")
830 if unit then
831 q.o.finish = unit
832 q.o.pos = nil
834 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
835 local index, x, y = self:UnitPosition("npc")
836 if index then -- Might not have a position if inside an instance.
837 local npc_objective = self:GetObjective("monster", unit)
838 self:AppendObjectivePosition(npc_objective, index, x, y)
840 elseif not q.o.finish then
841 local index, x, y = self:PlayerPosition()
842 if index then -- Might not have a position if inside an instance.
843 self:AppendObjectivePosition(q, index, x, y)
847 end]]
849 --[[if event == "MERCHANT_SHOW" then
850 local npc_name = UnitName("npc")
851 if npc_name then
852 local npc_objective = self:GetObjective("monster", npc_name)
853 local index = 1
854 while true do
855 local item_name = GetMerchantItemInfo(index)
856 if item_name then
857 index = index + 1
858 local item_objective = self:GetObjective("item", item_name)
859 if not item_objective.o.vendor then
860 item_objective.o.vendor = {npc_name}
861 else
862 local known = false
863 for i, vendor in ipairs(item_objective.o.vendor) do
864 if npc_name == vendor then
865 known = true
866 break
869 if not known then
870 table.insert(item_objective.o.vendor, npc_name)
873 else
874 break
878 end]]
880 if event == "TAXIMAP_OPENED" then
881 self:taxiMapOpened()
884 --[[if event == "PLAYER_CONTROL_GAINED" then
885 interruptcount = interruptcount + 1
886 end]]
888 --[[if event == "BAG_UPDATE" then
889 for slot = 1,GetContainerNumSlots(arg1) do
890 local link = GetContainerItemLink(arg1, slot)
891 if link then
892 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
893 if name then
894 self:GetObjective("item", name).o.id = tonumber(id)
898 end]]
902 if event == "ZONE_CHANGED" or event == "ZONE_CHANGED_INDOORS" or event == "ZONE_CHANGED_NEW_AREA" then
903 QH_Route_Filter_Rescan()
906 QH_Timeslice_Increment(GetTime() - tstart, "event")
907 end]==]
909 local map_shown_decay = 0
910 local delayed_action = 100
911 --local update_count = 0
912 local ontaxi = false
913 local frams = 0
915 QH_OnUpdate_High(function ()
916 local self = QuestHelper -- hoorj
917 local tstart = GetTime()
918 frams = frams + 1
920 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
922 if false and frams == 60 then
923 self:ShowText([[
924 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
926 Known bugs and issues include:
928 |cff40bbffNo support for "/qh find"|r
930 |cff40bbffNo support for in-party quest synchronization|r
932 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.
934 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.
936 Thanks for testing!]], "QuestHelper " .. version_string, 500, 20, 10)
939 --if frams == 5000 then please_submit_enabled = false end -- TOOK TOO LONG >:(
940 if please_submit_enabled and startup_time and startup_time + 10 < GetTime() then
941 QuestHelper:TextOut(QHText("PLEASE_SUBMIT"))
942 startup_time = nil
943 please_submit_enabled = false
945 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
947 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
948 init_cartographer_later = false
949 if QuestHelper_Pref.cart_wp_new then
950 self:EnableCartographer()
954 if not ontaxi and UnitOnTaxi("player") then
955 self:flightBegan()
956 interruptcount = 0
957 elseif ontaxi and not UnitOnTaxi("player") then
958 self:flightEnded(interruptcount > 1)
960 ontaxi = UnitOnTaxi("player")
962 -- For now I'm ripping out the update_count code
963 --update_count = update_count - 1
964 --if update_count <= 0 then
966 -- Reset the update count for next time around; this will make sure the body executes every time
967 -- when perf_scale_2 >= 1, and down to 1 in 10 iterations when perf_scale_2 < 1, or when hidden.
968 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale_2)
970 --if update_count < 0 then
971 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
972 --update_count = 0
973 --end
975 if self.Astrolabe.WorldMapVisible then
976 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
977 map_shown_decay = 2
978 elseif map_shown_decay > 0 then
979 map_shown_decay = map_shown_decay - 1
980 else
981 --SetMapToCurrentZone() -- not sure why this existed
984 --[[delayed_action = delayed_action - 1
985 if delayed_action <= 0 then
986 delayed_action = 100
987 self:HandlePartyChange()
988 end]]
990 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
991 local tc, tx, ty
993 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
994 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
995 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
998 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
999 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
1000 -- be seen in that zone but aren't in it.
1001 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
1002 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
1003 nz, nx, ny = self.z, nnx, nny
1007 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
1008 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
1009 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
1010 nz = self.z
1011 else
1012 nc, nz, nx, ny = nil, nil, nil, nil
1016 if nc and nz > 0 and QuestHelper_IndexLookup[nc] then -- QuestHelper_IndexLookup is only initialized after we've finished the preinit step
1017 self.c, self.z, self.x, self.y = nc, nz, nx, ny
1018 local upd_zone = false
1019 if self.i ~= QuestHelper_IndexLookup[nc][nz] then upd_zone = true end
1020 self.i = QuestHelper_IndexLookup[nc][nz]
1021 if upd_zone then QH_Route_Filter_Rescan("filter_zone") end
1024 if nc and nz and nx and ny and tc and tx and ty then
1025 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
1026 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
1027 self.collect_delayed = false
1029 local ibi = self.InBrokenInstance
1030 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
1032 if ibi and not self.InBrokenInstance then self.minimap_marker:OnUpdate(0) end -- poke
1033 else
1034 self.collect_delayed = true
1035 self.InBrokenInstance = true
1038 if not UnitOnTaxi("player") and not UnitIsDeadOrGhost("player") then
1039 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
1042 QH_Timeslice_Toggle("routing", not not self.c)
1044 self:PumpCommMessages()
1045 --end
1046 end)
1048 -- 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.
1049 -- 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?
1050 function QuestHelper:Location_RawRetrieve()
1051 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
1053 function QuestHelper:Location_AbsoluteRetrieve()
1054 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
1057 --QH_Hook(QuestHelper, "OnEvent", QuestHelper.OnEvent)