I swear I'm going to end up just delaying the entire UI mod
[QuestHelper.git] / main.lua
blob91347b46e99e64f8f81fa3fb4a2856ac25566431
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 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 tooltip=true,
36 share = true,
37 scale = 1,
38 solo = false,
39 comm = false,
40 show_ants = true,
41 level = 3,
42 hide = false,
43 cart_wp_new = false,
44 tomtom_wp_new = false,
45 arrow = true,
46 arrow_locked = false,
47 arrow_arrowsize = 1,
48 arrow_textsize = 1,
49 metric = (QuestHelper_Locale ~= "enUS" and QuestHelper_Locale ~= "esMX"),
50 flight_time = true,
51 locale = GetLocale(), -- This variable is used for display purposes, and has nothing to do with the collected data.
52 perf_scale_2 = 1, -- How much background processing can the current machine handle? Higher means more load, lower means better performance.
53 perfload_scale = 1, -- Performance scale to use on startup
54 map_button = true,
57 -- 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.
58 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
60 QuestHelper_FlightInstructors = {}
61 QuestHelper_FlightLinks = {}
62 QuestHelper_FlightRoutes = {}
63 QuestHelper_KnownFlightRoutes = {}
64 QuestHelper_SeenRealms = {}
66 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
67 QuestHelper.objective_objects = {}
68 QuestHelper.user_objectives = {}
69 QuestHelper.quest_objects = {}
70 QuestHelper.player_level = 1
71 QuestHelper.locale = QuestHelper_Locale
73 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
74 (UnitFactionGroup("player") == "Horde" and 2)
76 assert(QuestHelper.faction)
78 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
80 function QuestHelper:GetFontPath(list_string, font)
81 if list_string then
82 for name in string.gmatch(list_string, "[^;]+") do
83 if font:SetFont(name, 10) then
84 return name
85 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
86 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
87 end
88 end
89 end
90 end
92 function QuestHelper:SetLocaleFonts()
93 self.font.sans = nil
94 self.font.serif = nil
95 self.font.fancy = nil
97 local font = self:CreateText(self)
99 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
100 -- Only use alternate fonts if using a language the client wasn't intended for.
101 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
102 if replacements then
103 self.font.sans = self:GetFontPath(replacements.sans, font)
104 self.font.serif = self:GetFontPath(replacements.serif, font)
105 self.font.fancy = self:GetFontPath(replacements.fancy, font)
109 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
110 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
111 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
113 self:ReleaseText(font)
115 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
116 self.font.serif = self.font.serif or GameFontNormal:GetFont()
117 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
119 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
120 -- This should do nothing if not using an alternate font.
121 DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
124 QuestHelper.route = {}
125 QuestHelper.to_add = {}
126 QuestHelper.to_remove = {}
127 QuestHelper.quest_log = {}
128 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
129 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
131 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
132 local last = self:NumLines()
133 local name = self:GetName()
134 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
137 function QuestHelper:SetTargetLocation(i, x, y, toffset)
138 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
139 local c, z = unpack(QuestHelper_ZoneLookup[i])
141 self.target = self:CreateTable()
142 self.target[2] = self:CreateTable()
144 self.target_time = time()+(toffset or 0)
146 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
147 self.target[1] = self.zone_nodes[i]
148 self.target[3] = x * self.continent_scales_x[c]
149 self.target[4] = y * self.continent_scales_y[c]
151 self:SetTargetLocationRecalculate()
154 function QuestHelper:SetTargetLocationRecalculate()
155 if self.target then
156 for i, n in ipairs(self.target[1]) do
157 local a, b = n.x-self.target[3], n.y-self.target[4]
158 self.target[2][i] = math.sqrt(a*a+b*b)
163 function QuestHelper:UnsetTargetLocation()
164 -- Unsets the target set above.
165 if self.target then
166 self:ReleaseTable(self.target[2])
167 self:ReleaseTable(self.target)
168 self.target = nil
169 self.target_time = nil
173 local interruptcount = 0 -- counts how many "played gained control" messages we recieve, used for flight paths
174 local init_cartographer_later = false
176 QH_Event("ADDON_LOADED", function (addonid)
177 if addonid ~= "QuestHelper" then return end
178 local self = QuestHelper -- whee hack hack hack
180 QuestHelper_Loadtime["init_start"] = GetTime()
182 -- Use DefaultPref as fallback for unset preference keys.
183 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
185 local file_problem = false
186 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
188 local expected_files =
190 ["bst_pre.lua"] = true,
191 ["bst_post.lua"] = true,
192 ["bst_astrolabe.lua"] = true,
193 ["bst_ctl.lua"] = true,
194 ["bst_libaboutpanel.lua"] = true,
196 ["manager_event.lua"] = true,
198 ["upgrade.lua"] = true,
199 ["main.lua"] = true,
200 ["recycle.lua"] = true,
201 ["objective.lua"] = true,
202 ["quest.lua"] = true,
203 ["utility.lua"] = true,
204 ["dodads.lua"] = true,
205 ["dodads_triangles.lua"] = true,
206 ["teleport.lua"] = true,
207 ["pathfinding.lua"] = true,
208 ["routing.lua"] = true,
209 ["custom.lua"] = true,
210 ["menu.lua"] = true,
211 ["nag.lua"] = true,
212 ["comm.lua"] = true,
213 ["mapbutton.lua"] = true,
214 ["help.lua"] = true,
215 ["pattern.lua"] = true,
216 ["flightpath.lua"] = true,
217 ["tracker.lua"] = true,
218 ["objtips.lua"] = true,
219 ["cartographer.lua"] = true,
220 ["cartographer_is_terrible.lua"] = true,
221 ["tomtom.lua"] = true,
222 ["textviewer.lua"] = true,
223 ["error.lua"] = true,
224 ["timeslice.lua"] = true,
225 ["lang.lua"] = true,
226 ["core.lua"] = true,
227 ["tooltip.lua"] = true,
228 ["arrow.lua"] = true,
230 ["static.lua"] = true,
231 ["static_1.lua"] = true,
232 ["static_2.lua"] = true,
233 ["static_deDE.lua"] = true,
234 ["static_deDE_1.lua"] = true,
235 ["static_deDE_2.lua"] = true,
236 ["static_enUS.lua"] = true,
237 ["static_enUS_1.lua"] = true,
238 ["static_enUS_2.lua"] = true,
239 ["static_esES.lua"] = true,
240 ["static_esES_1.lua"] = true,
241 ["static_esES_2.lua"] = true,
242 ["static_esMX.lua"] = true,
243 ["static_esMX_1.lua"] = true,
244 ["static_esMX_2.lua"] = true,
245 ["static_frFR.lua"] = true,
246 ["static_frFR_1.lua"] = true,
247 ["static_frFR_2.lua"] = true,
248 ["static_koKR.lua"] = true,
249 ["static_koKR_1.lua"] = true,
250 ["static_koKR_2.lua"] = true,
251 ["static_ruRU.lua"] = true,
252 ["static_ruRU_1.lua"] = true,
253 ["static_ruRU_2.lua"] = true,
254 ["static_zhTW.lua"] = true,
255 ["static_zhTW_1.lua"] = true,
256 ["static_zhTW_2.lua"] = true,
258 ["collect.lua"] = true,
259 ["collect_achievement.lua"] = true,
260 ["collect_lzw.lua"] = true,
261 ["collect_traveled.lua"] = true,
262 ["collect_zone.lua"] = true,
263 ["collect_location.lua"] = true,
264 ["collect_merger.lua"] = true,
265 ["collect_monster.lua"] = true,
266 ["collect_item.lua"] = true,
267 ["collect_object.lua"] = true,
268 ["collect_loot.lua"] = true,
269 ["collect_patterns.lua"] = true,
270 ["collect_flight.lua"] = true,
271 ["collect_util.lua"] = true,
272 ["collect_quest.lua"] = true,
273 ["collect_equip.lua"] = true,
274 ["collect_notifier.lua"] = true,
275 ["collect_bitstream.lua"] = true,
276 ["collect_spec.lua"] = true,
277 ["collect_upgrade.lua"] = true,
278 ["collect_merchant.lua"] = true,
279 ["collect_warp.lua"] = true,
281 ["filter_core.lua"] = true,
282 ["filter_base.lua"] = true,
284 ["routing_debug.lua"] = true,
285 ["routing_loc.lua"] = true,
286 ["routing_route.lua"] = true,
287 ["routing_core.lua"] = true,
288 ["routing_controller.lua"] = true,
289 ["routing_hidden.lua"] = true,
291 ["director_quest.lua"] = true,
292 ["director_achievement.lua"] = true,
294 ["db_get.lua"] = true,
296 ["graph_core.lua"] = true,
297 ["graph_flightpath.lua"] = true,
300 local uninstallederr = ""
302 for file, version in pairs(QuestHelper_File) do
303 if not expected_files[file] then
304 local errmsg = "Unexpected QuestHelper file: "..file
305 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
306 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
307 file_problem = true
308 elseif version ~= expected_version then
309 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
310 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
311 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
312 if version ~= "Development Version" and expected_version ~= "Development Version" then
313 -- Developers are allowed to mix dev versions with release versions
314 file_problem = true
319 for file in pairs(expected_files) do
320 if not QuestHelper_File[file] then
321 local errmsg = "Missing QuestHelper file: "..file
322 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
323 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
324 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem = true end
328 -- Don't need this table anymore.
329 QuestHelper_File = nil
331 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
332 local errmsg = "Static data does not seem to exist"
333 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
335 -- TODO: Are you sure this should be an error? Shouldn't we let people we don't have data for collect their own?
336 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
337 file_problem = true
340 if file_problem then
341 message(QHText("PLEASE_RESTART"))
342 QuestHelper_ErrorCatcher_ExplicitError(true, "not-installed-properly" .. "\n" .. uninstallederr)
343 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
344 return
347 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
348 message(QHText("PRIVATE_SERVER"))
349 QuestHelper_ErrorCatcher_ExplicitError(true, "error id cakbep ten T")
350 QuestHelper = nil
351 return
354 if not DongleStub then
355 message(QHText("NOT_UNZIPPED_CORRECTLY"))
356 QuestHelper_ErrorCatcher_ExplicitError(true, "not-unzipped-properly")
357 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
358 return
361 QuestHelper_ErrorCatcher_CompletelyStarted()
363 if not QuestHelper_StaticData then
364 -- If there is no static data for some mysterious reason, create an empty table so that
365 -- other parts of the code can carry on as usual, using locally collected data if it exists.
366 QuestHelper_StaticData = {}
369 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
371 if not QuestHelper_UID then
372 QuestHelper_UID = self:CreateUID()
374 QuestHelper_SaveDate = time()
376 QuestHelper_BuildZoneLookup()
377 QH_Graph_Init()
378 load_graph_links()
380 if QuestHelper_Locale ~= GetLocale() then
381 self:TextOut(QHText("LOCALE_ERROR"))
382 return
385 if not self:ZoneSanity() then
386 self:TextOut(QHText("ZONE_LAYOUT_ERROR"))
387 message("QuestHelper: "..QHText("ZONE_LAYOUT_ERROR"))
388 return
391 QuestHelper_UpgradeDatabase(_G)
392 QuestHelper_UpgradeComplete()
394 if QuestHelper_SaveVersion ~= 10 then
395 self:TextOut(QHText("DOWNGRADE_ERROR"))
396 return
399 if QuestHelper_IsPolluted(_G) then
400 self:TextOut(QHFormat("NAG_POLLUTED"))
401 self:Purge(nil, true, true)
404 local signature = expected_version .. " on " .. GetBuildInfo()
405 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
406 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
407 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
408 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
410 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
411 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
412 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
413 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
415 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
417 QH_Collector_Init()
418 DB_Init()
420 self.player_level = UnitLevel("player")
422 self:SetLocaleFonts()
424 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
425 self:EnableSharing()
428 if QuestHelper_Pref.hide then
429 self.map_overlay:Hide()
432 self:HandlePartyChange()
434 self:Nag("all")
436 for locale in pairs(QuestHelper_StaticData) do
437 if locale ~= self.locale then
438 -- Will delete references to locales you don't use.
439 QuestHelper_StaticData[locale] = nil
440 _G["QuestHelper_StaticData_" .. locale] = nil
444 local static = QuestHelper_StaticData[self.locale]
446 if static then
447 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
448 if faction ~= self.faction then
449 -- Will delete references to flight instructors that don't belong to your faction.
450 static.flight_instructors[faction] = nil
452 end end
454 if static.quest then for faction in pairs(static.quest) do
455 if faction ~= self.faction then
456 -- Will delete references to quests that don't belong to your faction.
457 static.quest[faction] = nil
459 end end
462 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
463 if not QuestHelper_CharVersion then
464 -- Changing per-character flight routes, now only storing the flight points they have,
465 -- will attempt to guess the routes from this.
466 local routes = {}
468 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
469 for key in pairs(l) do
470 routes[key] = true
474 QuestHelper_KnownFlightRoutes = routes
476 -- Deleting the player's home again.
477 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
478 QuestHelper_Home = nil
479 QuestHelper_CharVersion = 1
482 if not QuestHelper_Home then
483 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
484 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
487 if QuestHelper_Pref.map_button then
488 QuestHelper:InitMapButton()
491 if QuestHelper_Pref.cart_wp_new then
492 init_cartographer_later = true
495 if QuestHelper_Pref.tomtom_wp_new then
496 self:EnableTomTom()
499 self.tracker:SetScale(QuestHelper_Pref.track_scale)
501 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
502 self:ShowTracker()
505 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
507 local major, minor = (version_string or ""):match("^(%d+)%.(%d+)")
508 major, minor = tonumber(major), tonumber(minor)
510 -- For versions before 0.82, we're changing the default level offset to 3.
511 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
512 QuestHelper_Pref.level = nil
515 -- For versions before 0.84...
516 if major == 0 and minor and minor < 84 then
517 -- remove all keys that match their default setting.
518 for key, val in pairs(QuestHelper_DefaultPref) do
519 if QuestHelper_Pref[key] == val then
520 QuestHelper_Pref[key] = nil
525 QH_Hook(self, "OnUpdate", self.OnUpdate)
527 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
528 --collectgarbage("collect") -- Free everything we aren't using.
530 --[[
531 if self.debug_objectives then
532 for name, data in pairs(self.debug_objectives) do
533 self:LoadDebugObjective(name, data)
535 end]]
537 -- wellllp
538 QH_Arrow_SetScale()
539 QH_Arrow_SetTextScale()
541 --[[
542 QH_Timeslice_Add(function ()
543 self:ResetPathing()
544 self.Routing:Initialize() -- Set up the routing task
545 end, "init")]] -- FUCK YOU BOXBOT
547 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
548 function C()
551 function A()
552 q = 0
553 for x = 0, 130000000, 1 do
557 function B()
558 q = 0
559 for x = 0, 12000000, 1 do
564 function B2()
565 q = 0
566 for x = 0, 1200000, 1 do
567 --q = q + x
572 debugprofilestart()
574 local ta = debugprofilestop()
576 local tb = debugprofilestop()
578 local tc = debugprofilestop()
580 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
581 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
583 --/script SetCVar("scriptProfile", value)]]
585 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
587 QuestHelper_Loadtime["init_end"] = GetTime()
589 QuestHelper.loading_main = QuestHelper.CreateLoadingCounter()
591 QuestHelper.loading_flightpath = QuestHelper.loading_main:MakeSubcategory(1)
592 QuestHelper.loading_preroll = QuestHelper.loading_main:MakeSubcategory(1)
594 QH_Event("CHAT_MSG_ADDON", function (...)
595 if arg1 == "QHpr" and arg4 ~= UnitName("player") then
596 QH_Questcomm_Msg(arg2, arg4)
598 end)
600 QH_Event({"PARTY_MEMBERS_CHANGED", "UNIT_LEVEL", "RAID_ROSTER_UPDATE"}, function ()
601 QH_Filter_Group_Sync()
602 QH_Route_Filter_Rescan("filter_quest_level")
603 QH_Route_Filter_Rescan("filter_quest_group")
604 end)
606 QH_Event({"PARTY_MEMBERS_CHANGED", "RAID_ROSTER_UPDATE"}, function ()
607 QH_Questcomm_Sync()
608 end)
610 QH_Event("PLAYER_LEVEL_UP", function ()
611 self.player_level = arg1
612 QH_Route_Filter_Rescan("filter_quest_level")
613 end)
615 QH_Event("TAXIMAP_OPENED", function ()
616 self:taxiMapOpened()
617 end)
619 QH_Event({"ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, function()
620 QH_Route_Filter_Rescan()
621 end)
623 end)
625 local startup_time
626 local please_donate_enabled = false
627 local please_donate_initted = false
629 --[==[
630 function QuestHelper:OnEvent(event)
631 local tstart = GetTime()
633 --[[
634 if event == "GOSSIP_SHOW" then
635 local name, id = UnitName("npc"), self:GetUnitID("npc")
636 if name and id then
637 self:GetObjective("monster", name).o.id = id
638 --self:TextOut("NPC: "..name.." = "..id)
640 end]]
642 --[[if event == "PLAYER_TARGET_CHANGED" then
643 local name, id = UnitName("target"), self:GetUnitID("target")
644 if name and id then
645 self:GetObjective("monster", name).o.id = id
646 --self:TextOut("Target: "..name.." = "..id)
649 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
650 local index, x, y = self:UnitPosition("target")
652 if index then -- Might not have a position if inside an instance.
653 local w = 0.1
655 -- Modify the weight based on how far they are from us.
656 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
657 if CheckInteractDistance("target", 3) then w = 1
658 elseif CheckInteractDistance("target", 2) then w = 0.89
659 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
661 local monster_objective = self:GetObjective("monster", UnitName("target"))
662 self:AppendObjectivePosition(monster_objective, index, x, y, w)
664 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
665 (UnitFactionGroup("target") == "Horde" and 2) or nil
667 local level = UnitLevel("target")
668 if level and level >= 1 then
669 local w = monster_objective.o.levelw or 0
670 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
671 monster_objective.o.levelw = w+1
675 end]]
677 --[[if event == "LOOT_OPENED" then
678 local target = UnitName("target")
679 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
680 local index, x, y = self:UnitPosition("target")
682 local monster_objective = self:GetObjective("monster", target)
683 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
685 if index then -- Might not have a position if inside an instance.
686 self:AppendObjectivePosition(monster_objective, index, x, y)
689 for i = 1, GetNumLootItems() do
690 local icon, name, number, rarity = GetLootSlotInfo(i)
691 if name then
692 if number and number >= 1 then
693 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
694 else
695 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
696 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
697 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
699 if total > 0 then
700 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
705 else
706 local container = nil
708 -- Go through the players inventory and look for a locked item, we're probably looting it.
709 for bag = 0,NUM_BAG_SLOTS do
710 for slot = 1,GetContainerNumSlots(bag) do
711 local link = GetContainerItemLink(bag, slot)
712 if link and select(3, GetContainerItemInfo(bag, slot)) then
713 if container == nil then
714 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
715 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
716 else
717 -- Already tried to assign to a container. If there are multiple locked items, we give up.
718 container = false
724 if container then
725 local container_objective = self:GetObjective("item", container)
726 container_objective.o.opened = (container_objective.o.opened or 0) + 1
728 for i = 1, GetNumLootItems() do
729 local icon, name, number, rarity = GetLootSlotInfo(i)
730 if name and number >= 1 then
731 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
734 else
735 -- No idea where the items came from.
736 local index, x, y = self:PlayerPosition()
738 if index then
739 for i = 1, GetNumLootItems() do
740 local icon, name, number, rarity = GetLootSlotInfo(i)
741 if name and number >= 1 then
742 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
748 end]]
750 --[[if event == "CHAT_MSG_SYSTEM" then
751 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
752 if home_name then
753 if self.i then
754 self:TextOut(QHText("HOME_CHANGED"))
755 self:TextOut(QHText("WILL_RESET_PATH"))
757 local home = QuestHelper_Home
758 if not home then
759 home = {}
760 QuestHelper_Home = home
763 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
764 self.defered_graph_reset = true
767 end]]
772 --[[if event == "QUEST_DETAIL" then
773 if not self.quest_giver then self.quest_giver = {} end
774 local npc = UnitName("npc")
775 if npc then
776 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
777 local index, x, y = self:UnitPosition("npc")
779 if index then -- Might not have a position if inside an instance.
780 local npc_objective = self:GetObjective("monster", npc)
781 self:AppendObjectivePosition(npc_objective, index, x, y)
782 self.quest_giver[GetTitleText()] = npc
785 end]]
787 --[[if event == "QUEST_COMPLETE" or event == "QUEST_PROGRESS" then
788 local quest = GetTitleText()
789 if quest then
790 local level, hash = self:GetQuestLevel(quest)
791 if not level or level < 1 then
792 --self:TextOut("Don't know quest level for ".. quest.."!")
793 return
795 local q = self:GetQuest(quest, level, hash)
797 if q.need_hash then
798 q.o.hash = hash
801 local unit = UnitName("npc")
802 if unit then
803 q.o.finish = unit
804 q.o.pos = nil
806 -- Some NPCs aren't actually creatures, and so their positions might not be marked by PLAYER_TARGET_CHANGED.
807 local index, x, y = self:UnitPosition("npc")
808 if index then -- Might not have a position if inside an instance.
809 local npc_objective = self:GetObjective("monster", unit)
810 self:AppendObjectivePosition(npc_objective, index, x, y)
812 elseif not q.o.finish then
813 local index, x, y = self:PlayerPosition()
814 if index then -- Might not have a position if inside an instance.
815 self:AppendObjectivePosition(q, index, x, y)
819 end]]
821 --[[if event == "MERCHANT_SHOW" then
822 local npc_name = UnitName("npc")
823 if npc_name then
824 local npc_objective = self:GetObjective("monster", npc_name)
825 local index = 1
826 while true do
827 local item_name = GetMerchantItemInfo(index)
828 if item_name then
829 index = index + 1
830 local item_objective = self:GetObjective("item", item_name)
831 if not item_objective.o.vendor then
832 item_objective.o.vendor = {npc_name}
833 else
834 local known = false
835 for i, vendor in ipairs(item_objective.o.vendor) do
836 if npc_name == vendor then
837 known = true
838 break
841 if not known then
842 table.insert(item_objective.o.vendor, npc_name)
845 else
846 break
850 end]]
852 if event == "TAXIMAP_OPENED" then
853 self:taxiMapOpened()
856 --[[if event == "PLAYER_CONTROL_GAINED" then
857 interruptcount = interruptcount + 1
858 end]]
860 --[[if event == "BAG_UPDATE" then
861 for slot = 1,GetContainerNumSlots(arg1) do
862 local link = GetContainerItemLink(arg1, slot)
863 if link then
864 local id, name = select(3, string.find(link, "|Hitem:(%d+):.-|h%[(.-)%]|h"))
865 if name then
866 self:GetObjective("item", name).o.id = tonumber(id)
870 end]]
872 if event == "CHAT_MSG_CHANNEL_NOTICE" and please_donate_enabled and not please_donate_initted then
873 please_donate_enabled = QHNagInit()
874 startup_time = GetTime()
875 please_donate_initted = true
877 QHUpdateNagInit()
880 if event == "ZONE_CHANGED" or event == "ZONE_CHANGED_INDOORS" or event == "ZONE_CHANGED_NEW_AREA" then
881 QH_Route_Filter_Rescan()
884 QH_Timeslice_Increment(GetTime() - tstart, "event")
885 end]==]
887 local map_shown_decay = 0
888 local delayed_action = 100
889 --local update_count = 0
890 local ontaxi = false
891 local frams = 0
893 QH_OnUpdate_High(function ()
894 local self = QuestHelper -- hoorj
895 local tstart = GetTime()
896 frams = frams + 1
898 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
900 if false and frams == 60 then
901 self:ShowText([[
902 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
904 Known bugs and issues include:
906 |cff40bbffNo support for "/qh find"|r
908 |cff40bbffNo support for in-party quest synchronization|r
910 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.
912 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.
914 Thanks for testing!]], "QuestHelper " .. version_string, 500, 20, 10)
917 if frams == 250 then please_donate_enabled = false end -- TOOK TOO LONG >:(
918 if please_donate_enabled and startup_time and startup_time + 1 < GetTime() then
919 QuestHelper:TextOut(QHText("PLEASE_DONATE"))
920 startup_time = nil
921 please_donate_enabled = false
923 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
925 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
926 init_cartographer_later = false
927 if QuestHelper_Pref.cart_wp_new then
928 self:EnableCartographer()
932 if not ontaxi and UnitOnTaxi("player") then
933 self:flightBegan()
934 interruptcount = 0
935 elseif ontaxi and not UnitOnTaxi("player") then
936 self:flightEnded(interruptcount > 1)
938 ontaxi = UnitOnTaxi("player")
940 -- For now I'm ripping out the update_count code
941 --update_count = update_count - 1
942 --if update_count <= 0 then
944 -- Reset the update count for next time around; this will make sure the body executes every time
945 -- when perf_scale_2 >= 1, and down to 1 in 10 iterations when perf_scale_2 < 1, or when hidden.
946 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale_2)
948 --if update_count < 0 then
949 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
950 --update_count = 0
951 --end
953 if self.Astrolabe.WorldMapVisible then
954 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
955 map_shown_decay = 2
956 elseif map_shown_decay > 0 then
957 map_shown_decay = map_shown_decay - 1
958 else
959 --SetMapToCurrentZone() -- not sure why this existed
962 --[[delayed_action = delayed_action - 1
963 if delayed_action <= 0 then
964 delayed_action = 100
965 self:HandlePartyChange()
966 end]]
968 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
969 local tc, tx, ty
971 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
972 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
973 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
976 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
977 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
978 -- be seen in that zone but aren't in it.
979 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
980 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
981 nz, nx, ny = self.z, nnx, nny
985 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
986 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
987 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
988 nz = self.z
989 else
990 nc, nz, nx, ny = nil, nil, nil, nil
994 if nc and nz > 0 then
995 self.c, self.z, self.x, self.y = nc, nz, nx, ny
996 local upd_zone = false
997 if self.i ~= QuestHelper_IndexLookup[nc][nz] then upd_zone = true end
998 self.i = QuestHelper_IndexLookup[nc][nz]
999 if upd_zone then QH_Route_Filter_Rescan("filter_zone") end
1002 if nc and nz and nx and ny and tc and tx and ty then
1003 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
1004 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
1005 self.collect_delayed = false
1007 local ibi = self.InBrokenInstance
1008 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
1010 if ibi and not self.InBrokenInstance then self.minimap_marker:OnUpdate(0) end -- poke
1011 else
1012 self.collect_delayed = true
1013 self.InBrokenInstance = true
1016 if not UnitOnTaxi("player") and not UnitIsDeadOrGhost("player") then
1017 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
1020 QH_Timeslice_Toggle("routing", not not self.c)
1022 self:PumpCommMessages()
1023 --end
1024 end)
1026 -- 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.
1027 -- 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?
1028 function QuestHelper:Location_RawRetrieve()
1029 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
1031 function QuestHelper:Location_AbsoluteRetrieve()
1032 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
1035 --QH_Hook(QuestHelper, "OnEvent", QuestHelper.OnEvent)