acherus support
[QuestHelper.git] / main.lua
blob1ab60e8ca163ed6521538241c76bccba4391f7fc
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_SaveVersion = 10
10 QuestHelper_CharVersion = 1
11 QuestHelper_Locale = GetLocale() -- This variable is used only for the collected data, and has nothing to do with displayed text.
12 QuestHelper_Quests = {}
13 QuestHelper_Objectives = {}
15 QuestHelper_Pref =
18 QuestHelper_DefaultPref =
20 filter_level=true,
21 filter_zone=false,
22 filter_done=false,
23 filter_blocked=false, -- Hides blocked objectives, such as quest turn-ins for incomplete quests
24 filter_watched=false, -- Limits to Watched objectives
25 filter_group=true,
26 filter_group_param=2,
27 filter_wintergrasp=true,
28 zones="next",
29 track=true,
30 track_minimized=false,
31 track_scale=1,
32 track_level=true,
33 track_qcolour=true,
34 track_ocolour=true,
35 track_size=10,
36 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 = false
193 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
195 local expected_files =
197 ["bst_pre.lua"] = true,
198 ["bst_post.lua"] = true,
199 ["bst_astrolabe.lua"] = true,
200 ["bst_ctl.lua"] = true,
201 ["bst_libaboutpanel.lua"] = true,
203 ["manager_event.lua"] = true,
205 ["upgrade.lua"] = true,
206 ["main.lua"] = true,
207 ["recycle.lua"] = true,
208 ["objective.lua"] = true,
209 ["quest.lua"] = true,
210 ["utility.lua"] = true,
211 ["dodads.lua"] = true,
212 ["dodads_triangles.lua"] = true,
213 ["teleport.lua"] = true,
214 ["pathfinding.lua"] = true,
215 ["routing.lua"] = true,
216 ["custom.lua"] = true,
217 ["menu.lua"] = true,
218 ["nag.lua"] = true,
219 ["comm.lua"] = true,
220 ["mapbutton.lua"] = true,
221 ["help.lua"] = true,
222 ["pattern.lua"] = true,
223 ["flightpath.lua"] = true,
224 ["tracker.lua"] = true,
225 ["objtips.lua"] = true,
226 ["cartographer.lua"] = true,
227 ["cartographer_is_terrible.lua"] = true,
228 ["tomtom.lua"] = true,
229 ["textviewer.lua"] = true,
230 ["error.lua"] = true,
231 ["timeslice.lua"] = true,
232 ["lang.lua"] = true,
233 ["core.lua"] = true,
234 ["tooltip.lua"] = true,
235 ["arrow.lua"] = true,
237 ["static.lua"] = true,
238 ["static_1.lua"] = true,
239 ["static_2.lua"] = true,
240 ["static_deDE.lua"] = true,
241 ["static_deDE_1.lua"] = true,
242 ["static_deDE_2.lua"] = true,
243 ["static_enUS.lua"] = true,
244 ["static_enUS_1.lua"] = true,
245 ["static_enUS_2.lua"] = true,
246 ["static_esES.lua"] = true,
247 ["static_esES_1.lua"] = true,
248 ["static_esES_2.lua"] = true,
249 ["static_esMX.lua"] = true,
250 ["static_esMX_1.lua"] = true,
251 ["static_esMX_2.lua"] = true,
252 ["static_frFR.lua"] = true,
253 ["static_frFR_1.lua"] = true,
254 ["static_frFR_2.lua"] = true,
255 ["static_koKR.lua"] = true,
256 ["static_koKR_1.lua"] = true,
257 ["static_koKR_2.lua"] = true,
258 ["static_ruRU.lua"] = true,
259 ["static_ruRU_1.lua"] = true,
260 ["static_ruRU_2.lua"] = true,
261 ["static_zhTW.lua"] = true,
262 ["static_zhTW_1.lua"] = true,
263 ["static_zhTW_2.lua"] = true,
265 ["collect.lua"] = true,
266 ["collect_achievement.lua"] = true,
267 ["collect_lzw.lua"] = true,
268 ["collect_traveled.lua"] = true,
269 ["collect_zone.lua"] = true,
270 ["collect_location.lua"] = true,
271 ["collect_merger.lua"] = true,
272 ["collect_monster.lua"] = true,
273 ["collect_item.lua"] = true,
274 ["collect_object.lua"] = true,
275 ["collect_loot.lua"] = true,
276 ["collect_patterns.lua"] = true,
277 ["collect_flight.lua"] = true,
278 ["collect_util.lua"] = true,
279 ["collect_quest.lua"] = true,
280 ["collect_equip.lua"] = true,
281 ["collect_notifier.lua"] = true,
282 ["collect_bitstream.lua"] = true,
283 ["collect_spec.lua"] = true,
284 ["collect_upgrade.lua"] = true,
285 ["collect_merchant.lua"] = true,
286 ["collect_warp.lua"] = true,
288 ["filter_core.lua"] = true,
289 ["filter_base.lua"] = true,
291 ["routing_debug.lua"] = true,
292 ["routing_loc.lua"] = true,
293 ["routing_route.lua"] = true,
294 ["routing_core.lua"] = true,
295 ["routing_controller.lua"] = true,
296 ["routing_hidden.lua"] = true,
298 ["director_quest.lua"] = true,
299 ["director_achievement.lua"] = true,
301 ["db_get.lua"] = true,
303 ["graph_core.lua"] = true,
304 ["graph_flightpath.lua"] = true,
307 local uninstallederr = ""
309 for file, version in pairs(QuestHelper_File) do
310 if not expected_files[file] then
311 local errmsg = "Unexpected QuestHelper file: "..file
312 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
313 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
314 file_problem = true
315 elseif version ~= expected_version then
316 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
317 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
318 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
319 if version ~= "Development Version" and expected_version ~= "Development Version" then
320 -- Developers are allowed to mix dev versions with release versions
321 file_problem = true
326 for file in pairs(expected_files) do
327 if not QuestHelper_File[file] then
328 local errmsg = "Missing QuestHelper file: "..file
329 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
330 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
331 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem = true end
335 -- Don't need this table anymore.
336 QuestHelper_File = nil
338 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
339 local errmsg = "Static data does not seem to exist"
340 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
342 -- TODO: Are you sure this should be an error? Shouldn't we let people we don't have data for collect their own?
343 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
344 file_problem = true
347 if file_problem then
348 message(QHText("PLEASE_RESTART"))
349 QuestHelper_ErrorCatcher_ExplicitError(true, "not-installed-properly" .. "\n" .. uninstallederr)
350 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
351 return
354 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
355 message(QHText("PRIVATE_SERVER"))
356 QuestHelper_ErrorCatcher_ExplicitError(true, "error id cakbep ten T")
357 QuestHelper = nil
358 return
361 if not DongleStub then
362 message(QHText("NOT_UNZIPPED_CORRECTLY"))
363 QuestHelper_ErrorCatcher_ExplicitError(true, "not-unzipped-properly")
364 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
365 return
368 QuestHelper_ErrorCatcher_CompletelyStarted()
370 if not QuestHelper_StaticData then
371 -- If there is no static data for some mysterious reason, create an empty table so that
372 -- other parts of the code can carry on as usual, using locally collected data if it exists.
373 QuestHelper_StaticData = {}
376 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
378 if not QuestHelper_UID then
379 QuestHelper_UID = self:CreateUID()
381 QuestHelper_SaveDate = time()
383 QuestHelper_BuildZoneLookup()
384 QH_Graph_Init()
385 load_graph_links()
387 if QuestHelper_Locale ~= GetLocale() then
388 self:TextOut(QHText("LOCALE_ERROR"))
389 return
392 if not self:ZoneSanity() then
393 self:TextOut(QHText("ZONE_LAYOUT_ERROR"))
394 message("QuestHelper: "..QHText("ZONE_LAYOUT_ERROR"))
395 return
398 QuestHelper_UpgradeDatabase(_G)
399 QuestHelper_UpgradeComplete()
401 if QuestHelper_SaveVersion ~= 10 then
402 self:TextOut(QHText("DOWNGRADE_ERROR"))
403 return
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)