Merge branch 'master' of zorba@192.168.100.11:questhelper
[QuestHelper.git] / main.lua
blob0c6cef440a1684d34504bedd7a8c1e38f2b6c3aa
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 QH_Event("ADDON_LOADED", function (addonid)
185 if addonid ~= "QuestHelper" then return end
186 local self = QuestHelper -- whee hack hack hack
188 QuestHelper_Loadtime["init_start"] = GetTime()
190 -- Use DefaultPref as fallback for unset preference keys.
191 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
193 local file_problem_version = false
195 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
197 local expected_files =
199 ["bst_pre.lua"] = true,
200 ["bst_post.lua"] = true,
201 ["bst_astrolabe.lua"] = true,
202 ["bst_ctl.lua"] = true,
203 ["bst_libaboutpanel.lua"] = true,
205 ["manager_event.lua"] = true,
207 ["upgrade.lua"] = true,
208 ["main.lua"] = true,
209 ["recycle.lua"] = true,
210 ["objective.lua"] = true,
211 ["quest.lua"] = true,
212 ["utility.lua"] = true,
213 ["dodads.lua"] = true,
214 ["dodads_triangles.lua"] = true,
215 ["teleport.lua"] = true,
216 ["pathfinding.lua"] = true,
217 ["routing.lua"] = true,
218 ["custom.lua"] = true,
219 ["menu.lua"] = true,
220 ["nag.lua"] = true,
221 ["comm.lua"] = true,
222 ["mapbutton.lua"] = true,
223 ["help.lua"] = true,
224 ["pattern.lua"] = true,
225 ["flightpath.lua"] = true,
226 ["tracker.lua"] = true,
227 ["objtips.lua"] = true,
228 ["cartographer.lua"] = true,
229 ["cartographer_is_terrible.lua"] = true,
230 ["tomtom.lua"] = true,
231 ["textviewer.lua"] = true,
232 ["error.lua"] = true,
233 ["timeslice.lua"] = true,
234 ["lang.lua"] = true,
235 ["core.lua"] = true,
236 ["tooltip.lua"] = true,
237 ["arrow.lua"] = true,
239 ["static.lua"] = true,
240 ["static_1.lua"] = true,
241 ["static_2.lua"] = true,
242 ["static_deDE.lua"] = true,
243 ["static_deDE_1.lua"] = true,
244 ["static_deDE_2.lua"] = true,
245 ["static_enUS.lua"] = true,
246 ["static_enUS_1.lua"] = true,
247 ["static_enUS_2.lua"] = true,
248 ["static_esES.lua"] = true,
249 ["static_esES_1.lua"] = true,
250 ["static_esES_2.lua"] = true,
251 ["static_esMX.lua"] = true,
252 ["static_esMX_1.lua"] = true,
253 ["static_esMX_2.lua"] = true,
254 ["static_frFR.lua"] = true,
255 ["static_frFR_1.lua"] = true,
256 ["static_frFR_2.lua"] = true,
257 ["static_koKR.lua"] = true,
258 ["static_koKR_1.lua"] = true,
259 ["static_koKR_2.lua"] = true,
260 ["static_ruRU.lua"] = true,
261 ["static_ruRU_1.lua"] = true,
262 ["static_ruRU_2.lua"] = true,
263 ["static_zhTW.lua"] = true,
264 ["static_zhTW_1.lua"] = true,
265 ["static_zhTW_2.lua"] = true,
267 ["collect.lua"] = true,
268 ["collect_achievement.lua"] = true,
269 ["collect_lzw.lua"] = true,
270 ["collect_traveled.lua"] = true,
271 ["collect_zone.lua"] = true,
272 ["collect_location.lua"] = true,
273 ["collect_merger.lua"] = true,
274 ["collect_monster.lua"] = true,
275 ["collect_item.lua"] = true,
276 ["collect_object.lua"] = true,
277 ["collect_loot.lua"] = true,
278 ["collect_patterns.lua"] = true,
279 ["collect_flight.lua"] = true,
280 ["collect_util.lua"] = true,
281 ["collect_quest.lua"] = true,
282 ["collect_equip.lua"] = true,
283 ["collect_notifier.lua"] = true,
284 ["collect_bitstream.lua"] = true,
285 ["collect_spec.lua"] = true,
286 ["collect_upgrade.lua"] = true,
287 ["collect_merchant.lua"] = true,
288 ["collect_warp.lua"] = true,
290 ["filter_core.lua"] = true,
291 ["filter_base.lua"] = true,
293 ["routing_debug.lua"] = true,
294 ["routing_loc.lua"] = true,
295 ["routing_route.lua"] = true,
296 ["routing_core.lua"] = true,
297 ["routing_controller.lua"] = true,
298 ["routing_hidden.lua"] = true,
300 ["director_quest.lua"] = true,
301 ["director_achievement.lua"] = true,
303 ["db_get.lua"] = true,
305 ["graph_core.lua"] = true,
306 ["graph_flightpath.lua"] = true,
308 ["AstrolabeQH/Astrolabe.lua"] = true,
309 ["AstrolabeQH/AstrolabeMapMonitor.lua"] = true,
312 local uninstallederr = ""
314 for file, version in pairs(QuestHelper_File) do
315 if not expected_files[file] then
316 local errmsg = "Unexpected QuestHelper file: "..file
317 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
318 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
319 file_problem_version = true
320 elseif version ~= expected_version then
321 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
322 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
323 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
324 if version ~= "Development Version" and expected_version ~= "Development Version" then
325 -- Developers are allowed to mix dev versions with release versions
326 file_problem_version = true
331 for file in pairs(expected_files) do
332 if not QuestHelper_File[file] then
333 local errmsg = "Missing QuestHelper file: "..file
334 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
335 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
336 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem_version = true end
340 -- Don't need this table anymore.
341 QuestHelper_File = nil
343 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
344 local errmsg = "Static data does not seem to exist"
345 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
347 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
348 file_problem_version = true
351 if file_problem_version then
352 QH_fixedmessage(QHText("PLEASE_RESTART"))
353 QuestHelper_ErrorCatcher_ExplicitError(false, "not-installed-properly" .. "\n" .. uninstallederr)
354 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
355 return
358 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
359 QH_fixedmessage(QHText("PRIVATE_SERVER"))
360 QuestHelper_ErrorCatcher_ExplicitError(false, "error id cakbep ten T")
361 QuestHelper = nil
362 return
365 if not DongleStub or not QH_Astrolabe_Ready then
366 QH_fixedmessage(QHText("NOT_UNZIPPED_CORRECTLY"))
367 QuestHelper_ErrorCatcher_ExplicitError(false, "not-unzipped-properly")
368 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
369 return
372 QuestHelper_ErrorCatcher_CompletelyStarted()
374 if not QuestHelper_StaticData then
375 -- If there is no static data for some mysterious reason, create an empty table so that
376 -- other parts of the code can carry on as usual, using locally collected data if it exists.
377 QuestHelper_StaticData = {}
380 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
382 if not QuestHelper_UID then
383 QuestHelper_UID = self:CreateUID()
385 QuestHelper_SaveDate = time()
387 QuestHelper_BuildZoneLookup()
388 QH_Graph_Init()
389 load_graph_links()
391 if QuestHelper_Locale ~= GetLocale() then
392 self:TextOut(QHText("LOCALE_ERROR"))
393 return
396 if not self:ZoneSanity() then
397 self:TextOut(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
398 QH_fixedmessage(QHFormat("ZONE_LAYOUT_ERROR", expected_version))
399 QuestHelper = nil
400 return
403 QuestHelper_UpgradeDatabase(_G)
404 QuestHelper_UpgradeComplete()
406 if QuestHelper_IsPolluted(_G) then
407 self:TextOut(QHFormat("NAG_POLLUTED"))
408 self:Purge(nil, true, true)
411 local signature = expected_version .. " on " .. GetBuildInfo()
412 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
413 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
414 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
415 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
417 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
418 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
419 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
420 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
422 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
424 QH_Collector_Init()
425 DB_Init()
427 self.player_level = UnitLevel("player")
429 self:SetLocaleFonts()
431 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
432 self:EnableSharing()
435 if QuestHelper_Pref.hide then
436 self.map_overlay:Hide()
439 self:HandlePartyChange()
441 self:Nag("all")
443 for locale in pairs(QuestHelper_StaticData) do
444 if locale ~= self.locale then
445 -- Will delete references to locales you don't use.
446 QuestHelper_StaticData[locale] = nil
447 _G["QuestHelper_StaticData_" .. locale] = nil
451 local static = QuestHelper_StaticData[self.locale]
453 if static then
454 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
455 if faction ~= self.faction then
456 -- Will delete references to flight instructors that don't belong to your faction.
457 static.flight_instructors[faction] = nil
459 end end
461 if static.quest then for faction in pairs(static.quest) do
462 if faction ~= self.faction then
463 -- Will delete references to quests that don't belong to your faction.
464 static.quest[faction] = nil
466 end end
469 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
470 if not QuestHelper_CharVersion then
471 -- Changing per-character flight routes, now only storing the flight points they have,
472 -- will attempt to guess the routes from this.
473 local routes = {}
475 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
476 for key in pairs(l) do
477 routes[key] = true
481 QuestHelper_KnownFlightRoutes = routes
483 -- Deleting the player's home again.
484 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
485 QuestHelper_Home = nil
486 QuestHelper_CharVersion = 1
489 if not QuestHelper_Home then
490 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
491 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
494 if QuestHelper_Pref.map_button then
495 QuestHelper:InitMapButton()
498 if QuestHelper_Pref.cart_wp_new then
499 init_cartographer_later = true
502 if QuestHelper_Pref.tomtom_wp_new then
503 self:EnableTomTom()
506 self.tracker:SetScale(QuestHelper_Pref.track_scale)
508 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
509 self:ShowTracker()
512 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
514 local major, minor = (version_string or ""):match("^(%d+)%.(%d+)")
515 major, minor = tonumber(major), tonumber(minor)
517 -- For versions before 0.82, we're changing the default level offset to 3.
518 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
519 QuestHelper_Pref.level = nil
522 -- For versions before 0.84...
523 if major == 0 and minor and minor < 84 then
524 -- remove all keys that match their default setting.
525 for key, val in pairs(QuestHelper_DefaultPref) do
526 if QuestHelper_Pref[key] == val then
527 QuestHelper_Pref[key] = nil
532 QH_Hook(self, "OnUpdate", self.OnUpdate)
534 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
535 --collectgarbage("collect") -- Free everything we aren't using.
537 --[[
538 if self.debug_objectives then
539 for name, data in pairs(self.debug_objectives) do
540 self:LoadDebugObjective(name, data)
542 end]]
544 -- wellllp
545 QH_Arrow_SetScale()
546 QH_Arrow_SetTextScale()
548 --[[
549 QH_Timeslice_Add(function ()
550 self:ResetPathing()
551 self.Routing:Initialize() -- Set up the routing task
552 end, "init")]] -- FUCK YOU BOXBOT
554 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
555 function C()
558 function A()
559 q = 0
560 for x = 0, 130000000, 1 do
564 function B()
565 q = 0
566 for x = 0, 12000000, 1 do
571 function B2()
572 q = 0
573 for x = 0, 1200000, 1 do
574 --q = q + x
579 debugprofilestart()
581 local ta = debugprofilestop()
583 local tb = debugprofilestop()
585 local tc = debugprofilestop()
587 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
588 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
590 --/script SetCVar("scriptProfile", value)]]
592 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
594 QuestHelper_Loadtime["init_end"] = GetTime()
596 QuestHelper.loading_main = QuestHelper.CreateLoadingCounter()
598 QuestHelper.loading_flightpath = QuestHelper.loading_main:MakeSubcategory(1)
599 QuestHelper.loading_preroll = QuestHelper.loading_main:MakeSubcategory(1)
601 QH_Event("CHAT_MSG_ADDON", function (...)
602 if arg1 == "QHpr" and arg4 ~= UnitName("player") then
603 QH_Questcomm_Msg(arg2, arg4)
605 end)
607 QH_Event({"PARTY_MEMBERS_CHANGED", "UNIT_LEVEL", "RAID_ROSTER_UPDATE"}, function ()
608 QH_Filter_Group_Sync()
609 QH_Route_Filter_Rescan("filter_quest_level")
610 QH_Route_Filter_Rescan("filter_quest_group")
611 end)
613 QH_Event({"PARTY_MEMBERS_CHANGED", "RAID_ROSTER_UPDATE"}, function ()
614 QH_Questcomm_Sync()
615 end)
617 QH_Event("PLAYER_LEVEL_UP", function ()
618 self.player_level = arg1
619 QH_Route_Filter_Rescan("filter_quest_level")
620 end)
622 QH_Event("TAXIMAP_OPENED", function ()
623 self:taxiMapOpened()
624 end)
626 QH_Event({"ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, function()
627 QH_Route_Filter_Rescan()
628 end)
630 QH_Event("CHAT_MSG_CHANNEL_NOTICE", function()
631 if please_submit_enabled and not please_submit_initted then
632 please_submit_enabled = QHNagInit()
633 startup_time = GetTime()
634 please_submit_initted = true
636 end)
638 end)
641 --[==[
642 function QuestHelper:OnEvent(event)
643 local tstart = GetTime()
645 --[[
646 if event == "GOSSIP_SHOW" then
647 local name, id = UnitName("npc"), self:GetUnitID("npc")
648 if name and id then
649 self:GetObjective("monster", name).o.id = id
650 --self:TextOut("NPC: "..name.." = "..id)
652 end]]
654 --[[if event == "PLAYER_TARGET_CHANGED" then
655 local name, id = UnitName("target"), self:GetUnitID("target")
656 if name and id then
657 self:GetObjective("monster", name).o.id = id
658 --self:TextOut("Target: "..name.." = "..id)
661 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
662 local index, x, y = self:UnitPosition("target")
664 if index then -- Might not have a position if inside an instance.
665 local w = 0.1
667 -- Modify the weight based on how far they are from us.
668 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
669 if CheckInteractDistance("target", 3) then w = 1
670 elseif CheckInteractDistance("target", 2) then w = 0.89
671 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
673 local monster_objective = self:GetObjective("monster", UnitName("target"))
674 self:AppendObjectivePosition(monster_objective, index, x, y, w)
676 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
677 (UnitFactionGroup("target") == "Horde" and 2) or nil
679 local level = UnitLevel("target")
680 if level and level >= 1 then
681 local w = monster_objective.o.levelw or 0
682 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
683 monster_objective.o.levelw = w+1
687 end]]
689 --[[if event == "LOOT_OPENED" then
690 local target = UnitName("target")
691 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
692 local index, x, y = self:UnitPosition("target")
694 local monster_objective = self:GetObjective("monster", target)
695 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
697 if index then -- Might not have a position if inside an instance.
698 self:AppendObjectivePosition(monster_objective, index, x, y)
701 for i = 1, GetNumLootItems() do
702 local icon, name, number, rarity = GetLootSlotInfo(i)
703 if name then
704 if number and number >= 1 then
705 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
706 else
707 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
708 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
709 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
711 if total > 0 then
712 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
717 else
718 local container = nil
720 -- Go through the players inventory and look for a locked item, we're probably looting it.
721 for bag = 0,NUM_BAG_SLOTS do
722 for slot = 1,GetContainerNumSlots(bag) do
723 local link = GetContainerItemLink(bag, slot)
724 if link and select(3, GetContainerItemInfo(bag, slot)) then
725 if container == nil then
726 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
727 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
728 else
729 -- Already tried to assign to a container. If there are multiple locked items, we give up.
730 container = false
736 if container then
737 local container_objective = self:GetObjective("item", container)
738 container_objective.o.opened = (container_objective.o.opened or 0) + 1
740 for i = 1, GetNumLootItems() do
741 local icon, name, number, rarity = GetLootSlotInfo(i)
742 if name and number >= 1 then
743 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
746 else
747 -- No idea where the items came from.
748 local index, x, y = self:PlayerPosition()
750 if index then
751 for i = 1, GetNumLootItems() do
752 local icon, name, number, rarity = GetLootSlotInfo(i)
753 if name and number >= 1 then
754 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
760 end]]
762 --[[if event == "CHAT_MSG_SYSTEM" then
763 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
764 if home_name then
765 if self.i then
766 self:TextOut(QHText("HOME_CHANGED"))
767 self:TextOut(QHText("WILL_RESET_PATH"))
769 local home = QuestHelper_Home
770 if not home then
771 home = {}
772 QuestHelper_Home = home
775 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
776 self.defered_graph_reset = true
779 end]]
784 --[[if event == "QUEST_DETAIL" then
785 if not self.quest_giver then self.quest_giver = {} end
786 local npc = UnitName("npc")
787 if npc then
788 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
789 local index, x, y = self:UnitPosition("npc")
791 if index then -- Might not have a position if inside an instance.
792 local npc_objective = self:GetObjective("monster", npc)
793 self:AppendObjectivePosition(npc_objective, index, x, y)
794 self.quest_giver[GetTitleText()] = npc
797 end]]
799 --[[if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
800 local quest = GetTitleText()
801 if quest then
802 local level, hash = self:GetQuestLevel(quest)
803 if not level or level < 1 then
804 --self:TextOut("Don't know quest level for ".. quest.."!")
805 return
807 local q = self:GetQuest(quest, level, hash)
809 if q.need_hash then
810 q.o.hash = hash
813 local unit = UnitName("npc")
814 if unit then
815 q.o.finish = unit
816 q.o.pos = nil
818 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
819 local index, x, y = self:UnitPosition("npc")
820 if index then -- Might not have a position if inside an instance.
821 local npc_objective = self:GetObjective("monster", unit)
822 self:AppendObjectivePosition(npc_objective, index, x, y)
824 elseif not q.o.finish then
825 local index, x, y = self:PlayerPosition()
826 if index then -- Might not have a position if inside an instance.
827 self:AppendObjectivePosition(q, index, x, y)
831 end]]
833 --[[if event == "MERCHANT_SHOW" then
834 local npc_name = UnitName("npc")
835 if npc_name then
836 local npc_objective = self:GetObjective("monster", npc_name)
837 local index = 1
838 while true do
839 local item_name = GetMerchantItemInfo(index)
840 if item_name then
841 index = index + 1
842 local item_objective = self:GetObjective("item", item_name)
843 if not item_objective.o.vendor then
844 item_objective.o.vendor = {npc_name}
845 else
846 local known = false
847 for i, vendor in ipairs(item_objective.o.vendor) do
848 if npc_name == vendor then
849 known = true
850 break
853 if not known then
854 table.insert(item_objective.o.vendor, npc_name)
857 else
858 break
862 end]]
864 if event == "TAXIMAP_OPENED" then
865 self:taxiMapOpened()
868 --[[if event == "PLAYER_CONTROL_GAINED" then
869 interruptcount = interruptcount + 1
870 end]]
872 --[[if event == "BAG_UPDATE" then
873 for slot = 1,GetContainerNumSlots(arg1) do
874 local link = GetContainerItemLink(arg1, slot)
875 if link then
876 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
877 if name then
878 self:GetObjective("item", name).o.id = tonumber(id)
882 end]]
886 if event == "ZONE_CHANGED" or event == "ZONE_CHANGED_INDOORS" or event == "ZONE_CHANGED_NEW_AREA" then
887 QH_Route_Filter_Rescan()
890 QH_Timeslice_Increment(GetTime() - tstart, "event")
891 end]==]
893 local map_shown_decay = 0
894 local delayed_action = 100
895 --local update_count = 0
896 local ontaxi = false
897 local frams = 0
899 QH_OnUpdate_High(function ()
900 local self = QuestHelper -- hoorj
901 local tstart = GetTime()
902 frams = frams + 1
904 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
906 if false and frams == 60 then
907 self:ShowText([[
908 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
910 Known bugs and issues include:
912 |cff40bbffNo support for "/qh find"|r
914 |cff40bbffNo support for in-party quest synchronization|r
916 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.
918 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.
920 Thanks for testing!]], "QuestHelper " .. version_string, 500, 20, 10)
923 --if frams == 5000 then please_submit_enabled = false end -- TOOK TOO LONG >:(
924 if please_submit_enabled and startup_time and startup_time + 10 < GetTime() then
925 QuestHelper:TextOut(QHText("PLEASE_SUBMIT"))
926 startup_time = nil
927 please_submit_enabled = false
929 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
931 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
932 init_cartographer_later = false
933 if QuestHelper_Pref.cart_wp_new then
934 self:EnableCartographer()
938 if not ontaxi and UnitOnTaxi("player") then
939 self:flightBegan()
940 interruptcount = 0
941 elseif ontaxi and not UnitOnTaxi("player") then
942 self:flightEnded(interruptcount > 1)
944 ontaxi = UnitOnTaxi("player")
946 -- For now I'm ripping out the update_count code
947 --update_count = update_count - 1
948 --if update_count <= 0 then
950 -- Reset the update count for next time around; this will make sure the body executes every time
951 -- when perf_scale_2 >= 1, and down to 1 in 10 iterations when perf_scale_2 < 1, or when hidden.
952 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale_2)
954 --if update_count < 0 then
955 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
956 --update_count = 0
957 --end
959 if self.Astrolabe.WorldMapVisible then
960 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
961 map_shown_decay = 2
962 elseif map_shown_decay > 0 then
963 map_shown_decay = map_shown_decay - 1
964 else
965 --SetMapToCurrentZone() -- not sure why this existed
968 --[[delayed_action = delayed_action - 1
969 if delayed_action <= 0 then
970 delayed_action = 100
971 self:HandlePartyChange()
972 end]]
974 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
975 local tc, tx, ty
977 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
978 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
979 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
982 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
983 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
984 -- be seen in that zone but aren't in it.
985 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
986 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
987 nz, nx, ny = self.z, nnx, nny
991 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
992 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
993 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
994 nz = self.z
995 else
996 nc, nz, nx, ny = nil, nil, nil, nil
1000 if nc and nz > 0 then
1001 self.c, self.z, self.x, self.y = nc, nz, nx, ny
1002 local upd_zone = false
1003 if self.i ~= QuestHelper_IndexLookup[nc][nz] then upd_zone = true end
1004 self.i = QuestHelper_IndexLookup[nc][nz]
1005 if upd_zone then QH_Route_Filter_Rescan("filter_zone") end
1008 if nc and nz and nx and ny and tc and tx and ty then
1009 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
1010 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
1011 self.collect_delayed = false
1013 local ibi = self.InBrokenInstance
1014 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
1016 if ibi and not self.InBrokenInstance then self.minimap_marker:OnUpdate(0) end -- poke
1017 else
1018 self.collect_delayed = true
1019 self.InBrokenInstance = true
1022 if not UnitOnTaxi("player") and not UnitIsDeadOrGhost("player") then
1023 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
1026 QH_Timeslice_Toggle("routing", not not self.c)
1028 self:PumpCommMessages()
1029 --end
1030 end)
1032 -- 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.
1033 -- 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?
1034 function QuestHelper:Location_RawRetrieve()
1035 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
1037 function QuestHelper:Location_AbsoluteRetrieve()
1038 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
1041 --QH_Hook(QuestHelper, "OnEvent", QuestHelper.OnEvent)