loud in debug mode
[QuestHelper.git] / main.lua
blob2e2de4bce25b655aba79cf434b33b3082f0f163b
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 track=true,
26 track_minimized=false,
27 track_scale=1,
28 track_size=10,
29 tooltip=true,
30 share = true,
31 scale = 1,
32 solo = false,
33 comm = false,
34 show_ants = true,
35 level = 3,
36 hide = false,
37 cart_wp_new = false,
38 tomtom_wp_new = false,
39 arrow = true,
40 arrow_locked = false,
41 arrow_arrowsize = 1,
42 arrow_textsize = 1,
43 flight_time = true,
44 locale = GetLocale(), -- This variable is used for display purposes, and has nothing to do with the collected data.
45 perf_scale = 1, -- How much background processing can the current machine handle? Higher means more load, lower means better performance.
46 perfload_scale = 1, -- Performance scale to use on startup
47 map_button = true,
50 -- 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.
51 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
53 QuestHelper_FlightInstructors = {}
54 QuestHelper_FlightLinks = {}
55 QuestHelper_FlightRoutes = {}
56 QuestHelper_KnownFlightRoutes = {}
57 QuestHelper_SeenRealms = {}
59 QuestHelper.tooltip = CreateFrame("GameTooltip", "QuestHelperTooltip", nil, "GameTooltipTemplate")
60 QuestHelper.objective_objects = {}
61 QuestHelper.user_objectives = {}
62 QuestHelper.quest_objects = {}
63 QuestHelper.player_level = 1
64 QuestHelper.locale = QuestHelper_Locale
66 QuestHelper.faction = (UnitFactionGroup("player") == "Alliance" and 1) or
67 (UnitFactionGroup("player") == "Horde" and 2)
69 assert(QuestHelper.faction)
71 QuestHelper.font = {serif=GameFontNormal:GetFont(), sans=ChatFontNormal:GetFont(), fancy=QuestTitleFont:GetFont()}
73 function QuestHelper:GetFontPath(list_string, font)
74 if list_string then
75 for name in string.gmatch(list_string, "[^;]+") do
76 if font:SetFont(name, 10) then
77 return name
78 elseif font:SetFont("Interface\\AddOns\\QuestHelper\\Fonts\\"..name, 10) then
79 return "Interface\\AddOns\\QuestHelper\\Fonts\\"..name
80 end
81 end
82 end
83 end
85 function QuestHelper:SetLocaleFonts()
86 self.font.sans = nil
87 self.font.serif = nil
88 self.font.fancy = nil
90 local font = self:CreateText(self)
92 if QuestHelper_Locale ~= QuestHelper_Pref.locale then
93 -- Only use alternate fonts if using a language the client wasn't intended for.
94 local replacements = QuestHelper_SubstituteFonts[QuestHelper_Pref.locale]
95 if replacements then
96 self.font.sans = self:GetFontPath(replacements.sans, font)
97 self.font.serif = self:GetFontPath(replacements.serif, font)
98 self.font.fancy = self:GetFontPath(replacements.fancy, font)
99 end
102 self.font.sans = self.font.sans or self:GetFontPath(QuestHelper_Pref.locale.."_sans.ttf", font)
103 self.font.serif = self.font.serif or self:GetFontPath(QuestHelper_Pref.locale.."_serif.ttf", font) or self.font.sans
104 self.font.fancy = self.font.fancy or self:GetFontPath(QuestHelper_Pref.locale.."_fancy.ttf", font) or self.font.serif
106 self:ReleaseText(font)
108 self.font.sans = self.font.sans or ChatFontNormal:GetFont()
109 self.font.serif = self.font.serif or GameFontNormal:GetFont()
110 self.font.fancy = self.font.fancy or QuestTitleFont:GetFont()
112 -- Need to change the font of the chat frame, for any messages that QuestHelper displays.
113 -- This should do nothing if not using an alternate font.
114 DEFAULT_CHAT_FRAME:SetFont(self.font.sans, select(2, DEFAULT_CHAT_FRAME:GetFont()))
117 QuestHelper.route = {}
118 QuestHelper.to_add = {}
119 QuestHelper.to_remove = {}
120 QuestHelper.quest_log = {}
121 QuestHelper.pos = {nil, {}, 0, 0, 1, "You are here.", 0}
122 QuestHelper.sharing = false -- Will be set to true when sharing with at least one user.
124 function QuestHelper.tooltip:GetPrevLines() -- Just a helper to make life easier.
125 local last = self:NumLines()
126 local name = self:GetName()
127 return _G[name.."TextLeft"..last], _G[name.."TextRight"..last]
130 function QuestHelper:SetTargetLocation(i, x, y, toffset)
131 -- Informs QuestHelper that you're going to be at some location in toffset seconds.
132 local c, z = unpack(QuestHelper_ZoneLookup[i])
134 self.target = self:CreateTable()
135 self.target[2] = self:CreateTable()
137 self.target_time = time()+(toffset or 0)
139 x, y = self.Astrolabe:TranslateWorldMapPosition(c, z, x, y, c, 0)
140 self.target[1] = self.zone_nodes[i]
141 self.target[3] = x * self.continent_scales_x[c]
142 self.target[4] = y * self.continent_scales_y[c]
144 self:SetTargetLocationRecalculate()
147 function QuestHelper:SetTargetLocationRecalculate()
148 if self.target then
149 for i, n in ipairs(self.target[1]) do
150 local a, b = n.x-self.target[3], n.y-self.target[4]
151 self.target[2][i] = math.sqrt(a*a+b*b)
156 function QuestHelper:UnsetTargetLocation()
157 -- Unsets the target set above.
158 if self.target then
159 self:ReleaseTable(self.target[2])
160 self:ReleaseTable(self.target)
161 self.target = nil
162 self.target_time = nil
166 local interruptcount = 0 -- counts how many "played gained control" messages we recieve, used for flight paths
167 local init_cartographer_later = false
169 function QuestHelper:Initialize()
170 QuestHelper_Loadtime["init_start"] = GetTime()
172 -- Use DefaultPref as fallback for unset preference keys.
173 setmetatable(QuestHelper_Pref, {__index=QuestHelper_DefaultPref})
175 local file_problem = false
176 local expected_version = GetAddOnMetadata("QuestHelper", "Version")
178 local expected_files =
180 ["bst_pre.lua"] = true,
181 ["bst_post.lua"] = true,
182 ["bst_astrolabe.lua"] = true,
183 ["bst_ctl.lua"] = true,
184 ["bst_libaboutpanel.lua"] = true,
186 ["manager_event.lua"] = true,
188 ["upgrade.lua"] = true,
189 ["main.lua"] = true,
190 ["recycle.lua"] = true,
191 ["objective.lua"] = true,
192 ["quest.lua"] = true,
193 ["questlog.lua"] = true,
194 ["utility.lua"] = true,
195 ["dodads.lua"] = true,
196 ["graph.lua"] = true,
197 ["teleport.lua"] = true,
198 ["pathfinding.lua"] = true,
199 ["routing.lua"] = true,
200 ["custom.lua"] = true,
201 ["menu.lua"] = true,
202 ["hidden.lua"] = true,
203 ["nag.lua"] = true,
204 ["comm.lua"] = true,
205 ["mapbutton.lua"] = true,
206 ["help.lua"] = true,
207 ["pattern.lua"] = true,
208 ["flightpath.lua"] = true,
209 ["tracker.lua"] = true,
210 ["objtips.lua"] = true,
211 ["cartographer.lua"] = true,
212 ["tomtom.lua"] = true,
213 ["textviewer.lua"] = true,
214 ["error.lua"] = true,
215 ["timeslice.lua"] = true,
216 ["lang.lua"] = true,
217 ["core.lua"] = true,
218 ["tooltip.lua"] = true,
219 ["arrow.lua"] = true,
221 ["static.lua"] = true,
222 ["static_1.lua"] = true,
223 ["static_2.lua"] = true,
224 ["static_deDE.lua"] = true,
225 ["static_deDE_1.lua"] = true,
226 ["static_deDE_2.lua"] = true,
227 ["static_enUS.lua"] = true,
228 ["static_enUS_1.lua"] = true,
229 ["static_enUS_2.lua"] = true,
230 ["static_esES.lua"] = true,
231 ["static_esES_1.lua"] = true,
232 ["static_esES_2.lua"] = true,
233 ["static_frFR.lua"] = true,
234 ["static_frFR_1.lua"] = true,
235 ["static_frFR_2.lua"] = true,
236 ["static_koKR.lua"] = true,
237 ["static_koKR_1.lua"] = true,
238 ["static_koKR_2.lua"] = true,
239 ["static_ruRU.lua"] = true,
240 ["static_ruRU_1.lua"] = true,
241 ["static_ruRU_2.lua"] = true,
242 ["static_zhTW.lua"] = true,
243 ["static_zhTW_1.lua"] = true,
244 ["static_zhTW_2.lua"] = true,
246 ["collect.lua"] = true,
247 ["collect_achievement.lua"] = true,
248 ["collect_lzw.lua"] = true,
249 ["collect_traveled.lua"] = true,
250 ["collect_zone.lua"] = true,
251 ["collect_location.lua"] = true,
252 ["collect_merger.lua"] = true,
253 ["collect_monster.lua"] = true,
254 ["collect_item.lua"] = true,
255 ["collect_object.lua"] = true,
256 ["collect_loot.lua"] = true,
257 ["collect_patterns.lua"] = true,
258 ["collect_flight.lua"] = true,
259 ["collect_util.lua"] = true,
260 ["collect_quest.lua"] = true,
261 ["collect_equip.lua"] = true,
262 ["collect_notifier.lua"] = true,
263 ["collect_bitstream.lua"] = true,
264 ["collect_spec.lua"] = true,
265 ["collect_upgrade.lua"] = true,
266 ["collect_merchant.lua"] = true,
267 ["collect_warp.lua"] = true,
269 ["filter_core.lua"] = true,
270 ["filter_base.lua"] = true,
272 ["routing_debug.lua"] = true,
273 ["routing_loc.lua"] = true,
274 ["routing_route.lua"] = true,
275 ["routing_core.lua"] = true,
276 ["routing_controller.lua"] = true,
277 ["routing_hidden.lua"] = true,
279 ["director_quest.lua"] = true,
281 ["db_get.lua"] = true,
283 ["graph_core.lua"] = true,
284 ["graph_flightpath.lua"] = true,
287 local uninstallederr = ""
289 for file, version in pairs(QuestHelper_File) do
290 if not expected_files[file] then
291 local errmsg = "Unexpected QuestHelper file: "..file
292 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
293 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
294 file_problem = true
295 elseif version ~= expected_version then
296 local errmsg = "Wrong version of QuestHelper file: "..file.." (found '"..version.."', should be '"..expected_version.."')"
297 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
298 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
299 if version ~= "Development Version" and expected_version ~= "Development Version" then
300 -- Developers are allowed to mix dev versions with release versions
301 file_problem = true
306 for file in pairs(expected_files) do
307 if not QuestHelper_File[file] then
308 local errmsg = "Missing QuestHelper file: "..file
309 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
310 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
311 if not (expected_version == "Development Version" and file:match("static.*")) then file_problem = true end
315 -- Don't need this table anymore.
316 QuestHelper_File = nil
318 if QuestHelper_StaticData and not QuestHelper_StaticData[GetLocale()] then
319 local errmsg = "Static data does not seem to exist"
320 DEFAULT_CHAT_FRAME:AddMessage(errmsg)
322 -- TODO: Are you sure this should be an error? Shouldn't we let people we don't have data for collect their own?
323 uninstallederr = uninstallederr .. " " .. errmsg .. "\n"
324 file_problem = true
327 if file_problem then
328 message(QHText("PLEASE_RESTART"))
329 QuestHelper_ErrorCatcher_ExplicitError(true, "not-installed-properly" .. "\n" .. uninstallederr)
330 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
331 return
334 if not GetCategoryList or not GetQuestLogSpecialItemInfo or not WatchFrame_RemoveObjectiveHandler then
335 message(QHText("PRIVATE_SERVER"))
336 QuestHelper_ErrorCatcher_ExplicitError(true, "error id cakbep ten T")
337 QuestHelper = nil
338 return
341 if not DongleStub then
342 message(QHText("NOT_UNZIPPED_CORRECTLY"))
343 QuestHelper_ErrorCatcher_ExplicitError(true, "not-unzipped-properly")
344 QuestHelper = nil -- Just in case anybody else is checking for us, we're not home
345 return
348 QuestHelper_ErrorCatcher_CompletelyStarted()
350 if not QuestHelper_StaticData then
351 -- If there is no static data for some mysterious reason, create an empty table so that
352 -- other parts of the code can carry on as usual, using locally collected data if it exists.
353 QuestHelper_StaticData = {}
356 QHFormatSetLocale(QuestHelper_Pref.locale or GetLocale())
358 if not QuestHelper_UID then
359 QuestHelper_UID = self:CreateUID()
361 QuestHelper_SaveDate = time()
363 QuestHelper_BuildZoneLookup()
364 QH_Graph_Init()
365 load_graph_links()
367 if QuestHelper_Locale ~= GetLocale() then
368 self:TextOut(QHText("LOCALE_ERROR"))
369 return
372 if not self:ZoneSanity() then
373 self:TextOut(QHText("ZONE_LAYOUT_ERROR"))
374 message("QuestHelper: "..QHText("ZONE_LAYOUT_ERROR"))
375 return
378 QuestHelper_UpgradeDatabase(_G)
379 QuestHelper_UpgradeComplete()
381 if QuestHelper_SaveVersion ~= 10 then
382 self:TextOut(QHText("DOWNGRADE_ERROR"))
383 return
386 if QuestHelper_IsPolluted(_G) then
387 self:TextOut(QHFormat("NAG_POLLUTED"))
388 self:Purge(nil, true, true)
391 local signature = expected_version .. " on " .. GetBuildInfo()
392 QuestHelper_Quests[signature] = QuestHelper_Quests[signature] or {}
393 QuestHelper_Objectives[signature] = QuestHelper_Objectives[signature] or {}
394 QuestHelper_FlightInstructors[signature] = QuestHelper_FlightInstructors[signature] or {}
395 QuestHelper_FlightRoutes[signature] = QuestHelper_FlightRoutes[signature] or {}
397 QuestHelper_Quests_Local = QuestHelper_Quests[signature]
398 QuestHelper_Objectives_Local = QuestHelper_Objectives[signature]
399 QuestHelper_FlightInstructors_Local = QuestHelper_FlightInstructors[signature]
400 QuestHelper_FlightRoutes_Local = QuestHelper_FlightRoutes[signature]
402 QuestHelper_SeenRealms[GetRealmName()] = true -- some attempt at tracking private servers
404 QH_Collector_Init()
405 DB_Init()
407 self.player_level = UnitLevel("player")
409 self:UnregisterEvent("VARIABLES_LOADED")
410 self:RegisterEvent("PLAYER_TARGET_CHANGED")
411 self:RegisterEvent("LOOT_OPENED")
412 self:RegisterEvent("QUEST_COMPLETE")
413 self:RegisterEvent("QUEST_LOG_UPDATE")
414 self:RegisterEvent("QUEST_PROGRESS")
415 self:RegisterEvent("MERCHANT_SHOW")
416 self:RegisterEvent("QUEST_DETAIL")
417 self:RegisterEvent("TAXIMAP_OPENED")
418 self:RegisterEvent("PLAYER_CONTROL_GAINED")
419 self:RegisterEvent("PLAYER_LEVEL_UP")
420 self:RegisterEvent("PARTY_MEMBERS_CHANGED")
421 self:RegisterEvent("CHAT_MSG_ADDON")
422 self:RegisterEvent("CHAT_MSG_SYSTEM")
423 self:RegisterEvent("BAG_UPDATE")
424 self:RegisterEvent("GOSSIP_SHOW")
425 self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE")
426 self:RegisterEvent("UNIT_LEVEL")
427 self:RegisterEvent("ZONE_CHANGED")
428 self:RegisterEvent("ZONE_CHANGED_INDOORS")
429 self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
431 self:SetLocaleFonts()
433 if QuestHelper_Pref.share and not QuestHelper_Pref.solo then
434 self:EnableSharing()
437 if QuestHelper_Pref.hide then
438 self.map_overlay:Hide()
441 self:HandlePartyChange()
443 self:Nag("all")
445 for locale in pairs(QuestHelper_StaticData) do
446 if locale ~= self.locale then
447 -- Will delete references to locales you don't use.
448 QuestHelper_StaticData[locale] = nil
449 _G["QuestHelper_StaticData_" .. locale] = nil
453 local static = QuestHelper_StaticData[self.locale]
455 if static then
456 if static.flight_instructors then for faction in pairs(static.flight_instructors) do
457 if faction ~= self.faction then
458 -- Will delete references to flight instructors that don't belong to your faction.
459 static.flight_instructors[faction] = nil
461 end end
463 if static.quest then for faction in pairs(static.quest) do
464 if faction ~= self.faction then
465 -- Will delete references to quests that don't belong to your faction.
466 static.quest[faction] = nil
468 end end
471 -- Adding QuestHelper_CharVersion, so I know if I've already converted this characters saved data.
472 if not QuestHelper_CharVersion then
473 -- Changing per-character flight routes, now only storing the flight points they have,
474 -- will attempt to guess the routes from this.
475 local routes = {}
477 for i, l in pairs(QuestHelper_KnownFlightRoutes) do
478 for key in pairs(l) do
479 routes[key] = true
483 QuestHelper_KnownFlightRoutes = routes
485 -- Deleting the player's home again.
486 -- But using the new CharVersion variable I'm adding is cleaner that what I was doing, so I'll go with it.
487 QuestHelper_Home = nil
488 QuestHelper_CharVersion = 1
491 if not QuestHelper_Home then
492 -- Not going to bother complaining about the player's home not being set, uncomment this when the home is used in routing.
493 -- self:TextOut(QHText("HOME_NOT_KNOWN"))
496 if QuestHelper_Pref.map_button then
497 QuestHelper:InitMapButton()
500 if QuestHelper_Pref.cart_wp_new then
501 init_cartographer_later = true
504 if QuestHelper_Pref.tomtom_wp_new then
505 self:EnableTomTom()
508 self.tracker:SetScale(QuestHelper_Pref.track_scale)
510 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
511 self:ShowTracker()
514 local version = GetAddOnMetadata("QuestHelper", "Version") or "Unknown"
516 local major, minor = (version_string or ""):match("^(%d+)%.(%d+)")
517 major, minor = tonumber(major), tonumber(minor)
519 -- For versions before 0.82, we're changing the default level offset to 3.
520 if major == 0 and minor and minor < 82 and QuestHelper_Pref.level == 2 then
521 QuestHelper_Pref.level = nil
524 -- For versions before 0.84...
525 if major == 0 and minor and minor < 84 then
526 -- remove all keys that match their default setting.
527 for key, val in pairs(QuestHelper_DefaultPref) do
528 if QuestHelper_Pref[key] == val then
529 QuestHelper_Pref[key] = nil
534 self:SetScript("OnUpdate", self.OnUpdate)
536 -- Seems to do its own garbage collection pass before fully loading, so I'll just rely on that
537 --collectgarbage("collect") -- Free everything we aren't using.
539 --[[
540 if self.debug_objectives then
541 for name, data in pairs(self.debug_objectives) do
542 self:LoadDebugObjective(name, data)
544 end]]
546 -- wellllp
547 QH_Arrow_SetScale()
548 QH_Arrow_SetTextScale()
550 --[[
551 QH_Timeslice_Add(function ()
552 self:ResetPathing()
553 self.Routing:Initialize() -- Set up the routing task
554 end, "init")]] -- FUCK YOU BOXBOT
556 --[[ -- This is just an example of how the WoW profiler biases its profiles heavily.
557 function C()
560 function A()
561 q = 0
562 for x = 0, 130000000, 1 do
566 function B()
567 q = 0
568 for x = 0, 12000000, 1 do
573 function B2()
574 q = 0
575 for x = 0, 1200000, 1 do
576 --q = q + x
581 debugprofilestart()
583 local ta = debugprofilestop()
585 local tb = debugprofilestop()
587 local tc = debugprofilestop()
589 QuestHelper:TextOut(string.format("%d %d %d", ta, tb - ta, tc - tb))
590 QuestHelper:TextOut(string.format("%d %d", GetFunctionCPUUsage(A), GetFunctionCPUUsage(B)))
592 --/script SetCVar("scriptProfile", value)]]
594 LibStub("LibAboutPanelQH").new(nil, "QuestHelper")
596 QuestHelper_Loadtime["init_end"] = GetTime()
598 QuestHelper.loading_main = QuestHelper.CreateLoadingCounter()
600 QuestHelper.loading_flightpath = QuestHelper.loading_main:MakeSubcategory(1)
601 QuestHelper.loading_preroll = QuestHelper.loading_main:MakeSubcategory(1)
604 local startup_time
605 local please_donate_enabled = false
606 local please_donate_initted = false
608 function QuestHelper:OnEvent(event)
609 if event == "VARIABLES_LOADED" then
610 local tstart = GetTime()
611 self:Initialize()
612 QH_Timeslice_Increment(GetTime() - tstart, "init")
615 local tstart = GetTime()
617 --[[
618 if event == "GOSSIP_SHOW" then
619 local name, id = UnitName("npc"), self:GetUnitID("npc")
620 if name and id then
621 self:GetObjective("monster", name).o.id = id
622 --self:TextOut("NPC: "..name.." = "..id)
624 end]]
626 --[[if event == "PLAYER_TARGET_CHANGED" then
627 local name, id = UnitName("target"), self:GetUnitID("target")
628 if name and id then
629 self:GetObjective("monster", name).o.id = id
630 --self:TextOut("Target: "..name.." = "..id)
633 if UnitExists("target") and UnitIsVisible("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
634 local index, x, y = self:UnitPosition("target")
636 if index then -- Might not have a position if inside an instance.
637 local w = 0.1
639 -- Modify the weight based on how far they are from us.
640 -- We don't know the exact location (using our own location), so the farther, the less sure we are that it's correct.
641 if CheckInteractDistance("target", 3) then w = 1
642 elseif CheckInteractDistance("target", 2) then w = 0.89
643 elseif CheckInteractDistance("target", 1) or CheckInteractDistance("target", 4) then w = 0.33 end
645 local monster_objective = self:GetObjective("monster", UnitName("target"))
646 self:AppendObjectivePosition(monster_objective, index, x, y, w)
648 monster_objective.o.faction = (UnitFactionGroup("target") == "Alliance" and 1) or
649 (UnitFactionGroup("target") == "Horde" and 2) or nil
651 local level = UnitLevel("target")
652 if level and level >= 1 then
653 local w = monster_objective.o.levelw or 0
654 monster_objective.o.level = ((monster_objective.o.level or 0)*w+level)/(w+1)
655 monster_objective.o.levelw = w+1
659 end]]
661 --[[if event == "LOOT_OPENED" then
662 local target = UnitName("target")
663 if target and UnitIsDead("target") and UnitCreatureType("target") ~= "Critter" and not UnitIsPlayer("target") and not UnitPlayerControlled("target") then
664 local index, x, y = self:UnitPosition("target")
666 local monster_objective = self:GetObjective("monster", target)
667 monster_objective.o.looted = (monster_objective.o.looted or 0) + 1
669 if index then -- Might not have a position if inside an instance.
670 self:AppendObjectivePosition(monster_objective, index, x, y)
673 for i = 1, GetNumLootItems() do
674 local icon, name, number, rarity = GetLootSlotInfo(i)
675 if name then
676 if number and number >= 1 then
677 self:AppendItemObjectiveDrop(self:GetObjective("item", name), name, target, number)
678 else
679 local total = (name:match(COPPER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) +
680 (name:match(SILVER_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 100 +
681 (name:match(GOLD_AMOUNT:gsub("%%d", "%(%%d+%)")) or 0) * 10000
683 if total > 0 then
684 self:AppendObjectiveDrop(self:GetObjective("item", "money"), target, total)
689 else
690 local container = nil
692 -- Go through the players inventory and look for a locked item, we're probably looting it.
693 for bag = 0,NUM_BAG_SLOTS do
694 for slot = 1,GetContainerNumSlots(bag) do
695 local link = GetContainerItemLink(bag, slot)
696 if link and select(3, GetContainerItemInfo(bag, slot)) then
697 if container == nil then
698 -- Found a locked item and haven't previously assigned to container, assign its name, or false if we fail to parse it.
699 container = select(3, string.find(link, "|h%[(.+)%]|h|r")) or false
700 else
701 -- Already tried to assign to a container. If there are multiple locked items, we give up.
702 container = false
708 if container then
709 local container_objective = self:GetObjective("item", container)
710 container_objective.o.opened = (container_objective.o.opened or 0) + 1
712 for i = 1, GetNumLootItems() do
713 local icon, name, number, rarity = GetLootSlotInfo(i)
714 if name and number >= 1 then
715 self:AppendItemObjectiveContainer(self:GetObjective("item", name), container, number)
718 else
719 -- No idea where the items came from.
720 local index, x, y = self:PlayerPosition()
722 if index then
723 for i = 1, GetNumLootItems() do
724 local icon, name, number, rarity = GetLootSlotInfo(i)
725 if name and number >= 1 then
726 self:AppendItemObjectivePosition(self:GetObjective("item", name), name, index, x, y)
732 end]]
734 --[[if event == "CHAT_MSG_SYSTEM" then
735 local home_name = self:convertPattern(ERR_DEATHBIND_SUCCESS_S)(arg1)
736 if home_name then
737 if self.i then
738 self:TextOut(QHText("HOME_CHANGED"))
739 self:TextOut(QHText("WILL_RESET_PATH"))
741 local home = QuestHelper_Home
742 if not home then
743 home = {}
744 QuestHelper_Home = home
747 home[1], home[2], home[3], home[4] = self.i, self.x, self.y, home_name
748 self.defered_graph_reset = true
751 end]]
753 --[[if event == "CHAT_MSG_ADDON" then
754 if arg1 == "QHpr" and (arg3 == "PARTY" or arg3 == "WHISPER") and arg4 ~= UnitName("player") then
755 self:HandleRemoteData(arg2, arg4)
757 end]]
759 if event == "PARTY_MEMBERS_CHANGED" or
760 event == "UNIT_LEVEL" or
761 event == "RAID_ROSTER_UPDATE" then
762 QH_Filter_Group_Sync()
763 QH_Route_Filter_Rescan("filter_quest_level")
766 if event == "PLAYER_LEVEL_UP" then
767 self.player_level = arg1
768 QH_Route_Filter_Rescan("filter_quest_level")
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")
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 function QuestHelper:OnUpdate()
894 local tstart = GetTime()
895 frams = frams + 1
897 if not QuestHelper_Loadtime["onupdate"] then QuestHelper_Loadtime["onupdate"] = GetTime() end
899 if false and frams == 60 then
900 self:ShowText([[
901 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
903 Known bugs and issues include:
905 |cff40bbffNo support for "/qh find"|r
907 |cff40bbffNo support for in-party quest synchronization|r
909 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.
911 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.
913 Thanks for testing!]], "QuestHelper " .. version_string, 500, 20, 10)
916 if frams == 250 then please_donate_enabled = false end -- TOOK TOO LONG >:(
917 if please_donate_enabled and startup_time and startup_time + 1 < GetTime() then
918 QuestHelper:TextOut(QHText("PLEASE_DONATE"))
919 startup_time = nil
920 please_donate_enabled = false
922 QHUpdateNagTick() -- These probably shouldn't be in OnUpdate. Eventually I'll move them somewhere cleaner.
924 if init_cartographer_later and Cartographer_Waypoints then -- there has to be a better way to do this
925 init_cartographer_later = false
926 if QuestHelper_Pref.cart_wp_new then
927 self:EnableCartographer()
931 if not ontaxi and UnitOnTaxi("player") then
932 self:flightBegan()
933 interruptcount = 0
934 elseif ontaxi and not UnitOnTaxi("player") then
935 self:flightEnded(interruptcount > 1)
937 ontaxi = UnitOnTaxi("player")
939 -- For now I'm ripping out the update_count code
940 --update_count = update_count - 1
941 --if update_count <= 0 then
943 -- Reset the update count for next time around; this will make sure the body executes every time
944 -- when perf_scale >= 1, and down to 1 in 10 iterations when perf_scale < 1, or when hidden.
945 --update_count = update_count + (QuestHelper_Pref.hide and 10 or 1/QuestHelper_Pref.perf_scale)
947 --if update_count < 0 then
948 -- Make sure the count doesn't go perpetually negative; don't know what will happen if it underflows.
949 --update_count = 0
950 --end
952 if self.Astrolabe.WorldMapVisible then
953 -- We won't trust that the zone returned by Astrolabe is correct until map_shown_decay is 0.
954 map_shown_decay = 2
955 elseif map_shown_decay > 0 then
956 map_shown_decay = map_shown_decay - 1
957 else
958 --SetMapToCurrentZone() -- not sure why this existed
961 --[[delayed_action = delayed_action - 1
962 if delayed_action <= 0 then
963 delayed_action = 100
964 self:HandlePartyChange()
965 end]]
967 local nc, nz, nx, ny = self.Astrolabe:GetCurrentPlayerPosition()
968 local tc, tx, ty
970 if nc and nc ~= -1 then -- We just want the raw data here, before we've done anything clever.
971 tc, tx, ty = self.Astrolabe:GetAbsoluteContinentPosition(nc, nz, nx, ny)
972 QuestHelper: Assert(tc and tx and ty) -- is it true? nobody knows! :D
975 if nc and nc == self.c and map_shown_decay > 0 and self.z > 0 and self.z ~= nz then
976 -- There's a chance Astrolable will return the wrong zone if you're messing with the world map, if you can
977 -- be seen in that zone but aren't in it.
978 local nnx, nny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
979 if nnx > 0 and nny > 0 and nnx < 1 and nny < 1 then
980 nz, nx, ny = self.z, nnx, nny
984 if nc and nc > 0 and nz == 0 and nc == self.c and self.z > 0 then
985 nx, ny = self.Astrolabe:TranslateWorldMapPosition(nc, nz, nx, ny, nc, self.z)
986 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
987 nz = self.z
988 else
989 nc, nz, nx, ny = nil, nil, nil, nil
993 if nc and nz > 0 then
994 self.c, self.z, self.x, self.y = nc, nz, nx, ny
995 local upd_zone = false
996 if self.i ~= QuestHelper_IndexLookup[nc][nz] then upd_zone = true end
997 self.i = QuestHelper_IndexLookup[nc][nz]
998 if upd_zone then QH_Route_Filter_Rescan("filter_zone") end
1001 if nc and nz and nx and ny and tc and tx and ty then
1002 self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry = nc, nz, nx, ny
1003 self.collect_ac, self.collect_ax, self.collect_ay = tc, tx, ty
1004 self.collect_delayed = false
1006 local ibi = self.InBrokenInstance
1007 if nc < -77 then self.InBrokenInstance = true else self.InBrokenInstance = false end
1009 if ibi and not self.InBrokenInstance then self.minimap_marker:OnUpdate(0) end -- poke
1010 else
1011 self.collect_delayed = true
1012 self.InBrokenInstance = true
1015 QH_Timeslice_Toggle("routing", not not self.c)
1017 self:PumpCommMessages()
1018 --end
1020 QH_Collector_OnUpdate()
1022 QH_Timeslice_Increment(GetTime() - tstart, "onupdate")
1024 QH_Timeslice_Work()
1027 -- 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.
1028 -- 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?
1029 function QuestHelper:Location_RawRetrieve()
1030 return self.collect_delayed, self.collect_rc, self.collect_rz, self.collect_rx, self.collect_ry
1032 function QuestHelper:Location_AbsoluteRetrieve()
1033 return self.collect_delayed, self.collect_ac, self.collect_ax, self.collect_ay
1036 QuestHelper:RegisterEvent("VARIABLES_LOADED")
1037 QuestHelper:SetScript("OnEvent", QuestHelper.OnEvent)