vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 ---------------------------------------------------------------------------------------------------
2 --Name: QuestieQuest
3 --Description: Handles all the quest related functions
4 ---------------------------------------------------------------------------------------------------
5 --///////////////////////////////////////////////////////////////////////////////////////////////--
6 ---------------------------------------------------------------------------------------------------
7 --Local Vars
8 ---------------------------------------------------------------------------------------------------
9 local QuestieHashCache = {};
10 local LastNrOfEntries = 0;
11 local CachedIds = {};
12 local QuestieQuestHashCache = {};
13 local QGet_TitleText = GetTitleText;
14 local QGet_QuestLogTitle = GetQuestLogTitle;
15 local QGet_NumQuestLeaderBoards = GetNumQuestLeaderBoards;
16 local QGet_QuestLogLeaderBoard = GetQuestLogLeaderBoard;
17 local QGet_QuestLogQuestText = GetQuestLogQuestText;
18 local QGet_NumQuestLogEntries = GetNumQuestLogEntries;
19 local QGet_QuestLogSelection = GetQuestLogSelection;
20 local QSelect_QuestLogEntry = SelectQuestLogEntry;
21 ---------------------------------------------------------------------------------------------------
22 --Global Vars
23 ---------------------------------------------------------------------------------------------------
24 LastQuestLogHashes = nil;
25 LastQuestLogCount = 0;
26 lastObjectives = nil;
27 QuestAbandonOnAccept = nil;
28 QuestAbandonWithItemsOnAccept = nil;
29 QuestRewardCompleteButton = nil;
30 QuestProgressCompleteButton = nil;
31 QuestDetailAcceptButton = nil;
32 Questie.lastCollapsedCount = 0;
33 Questie.collapsedThisRun = false;
34 QUESTIE_LAST_UPDATECACHE = GetTime();
35 ---------------------------------------------------------------------------------------------------
36 --Blizzard Hook: Quest Abandon On Accept
37 ---------------------------------------------------------------------------------------------------
38 QuestAbandonOnAccept = StaticPopupDialogs["ABANDON_QUEST"].OnAccept;
39 StaticPopupDialogs["ABANDON_QUEST"].OnAccept = function()
40 local qName = GetAbandonQuestName();
41 for hash,v in pairs(QuestieCachedQuests) do
42 if v["questName"] == qName then
43 QuestieSeenQuests[hash] = -1;
44 QuestieCachedQuests[hash] = nil;
45 QuestieHandledQuests[hash] = nil;
46 --Questie:debug_Print("Quest:QuestAbandonOnAccept: [questTitle: "..qName.."] | [Hash: "..hash.."]");
47 RemoveCrazyArrow(hash);
48 end
49 end
50 QuestAbandonOnAccept();
51 end
52 ---------------------------------------------------------------------------------------------------
53 --Blizzard Hook: Quest Abandon With Items On Accept
54 ---------------------------------------------------------------------------------------------------
55 QuestAbandonWithItemsOnAccept = StaticPopupDialogs["ABANDON_QUEST_WITH_ITEMS"].OnAccept;
56 StaticPopupDialogs["ABANDON_QUEST_WITH_ITEMS"].OnAccept = function()
57 local qName = GetAbandonQuestName();
58 for hash,v in pairs(QuestieCachedQuests) do
59 if v["questName"] == qName then
60 QuestieSeenQuests[hash] = -1;
61 QuestieCachedQuests[hash] = nil;
62 QuestieHandledQuests[hash] = nil;
63 --Questie:debug_Print("Quest:QuestAbandonWithItemsOnAccept: [questTitle: "..qName.."] | [Hash: "..hash.."]");
64 RemoveCrazyArrow(hash);
65 end
66 end
67 QuestAbandonWithItemsOnAccept();
68 end
69 ---------------------------------------------------------------------------------------------------
70 --This function saves the number of slots that get freed when turning in a quest
71 --to the QuestieCachedQuests table, so it can be read in the next function.
72 --It is called upon the QUEST_PROGRESS event.
73 ---------------------------------------------------------------------------------------------------
74 function Questie:OnQuestProgress()
75 local questTitle = QGet_TitleText();
76 local _, _, qlevel, qName = string.find(questTitle, "%[(.+)%] (.+)");
77 if qName == nil then
78 qName = QGet_TitleText();
79 end
80 for hash,v in pairs(QuestieCachedQuests) do
81 if v["questName"] == qName then
82 v["numQuestItems"] = GetNumQuestItems();
83 end
84 end
85 end
86 ---------------------------------------------------------------------------------------------------
87 --This function is used by the next two hooks.
88 --It checks if the conditions for completing a quest are met:
89 -- 1. If there is a choice available, the player must have chosen.
90 -- 2. There must be (numRewards-numItems) free slots in the invetory.
91 --If both conditions are met, the quest is marked as finished, otherwise
92 --false is returned (return value is currently unused).
93 ---------------------------------------------------------------------------------------------------
94 function Questie:MarkQuestAsFinished()
95 local rewards = GetNumQuestRewards();
96 local choices = GetNumQuestChoices();
97 -- Filter condition: choices available but no choice made.
98 if ( QuestFrameRewardPanel.itemChoice == 0 and choices > 0 ) then
99 return false;
100 end
101 -- If there are rewards and choices, we need 1 more space.
102 if choices > 0 then
103 rewards = rewards + 1;
104 end
105 -- Get quest name and compare it against values in cache
106 local questTitle = QGet_TitleText();
107 local _, _, qlevel, qName = string.find(questTitle, "%[(.+)%] (.+)");
108 if qName == nil then
109 qName = QGet_TitleText();
110 end
111 for hash,v in pairs(QuestieCachedQuests) do
112 if v["questName"] == qName then
113 if (not v["numQuestItems"]) then
114 Questie:debug_Print("ERROR: QuestRewardCompleteButton: [questTitle: "..qName.."] | [Hash: "..hash.."]:\n Failed to read numQuestItems from SavedVariables")
115 end
116 -- Filter condition: not enough space in inventory.
117 if (rewards > 0) and (Questie:CheckPlayerInventory() < (rewards - (v["numQuestItems"] or 0))) then
118 return false;
119 end
120 -- All checks passed, mark quest as finished and remove it from cache
121 QuestieSeenQuests[hash] = 1;
122 QuestieCompletedQuestMessages[qName] = 1;
123 QuestieCachedQuests[hash] = nil;
124 QuestieHandledQuests[hash] = nil;
125 Questie:debug_Print("Quest:QuestRewardCompleteButton: [questTitle: "..qName.."] | [Hash: "..hash.."]");
126 RemoveCrazyArrow(hash);
127 return true;
128 end
129 end
130 end
131 ---------------------------------------------------------------------------------------------------
132 --Blizzard Hook: GetQuestReward function
133 --This hook marks a quest as finished in Questies DB if it can be finished.
134 --The call to the hooked function then finishes the quest.
135 --It is needed in addition to the next hook, because it is used by EQL3
136 --when its "Auto Complete Quests"-option is enabled.
137 ---------------------------------------------------------------------------------------------------
138 QGetQuestReward = GetQuestReward;
139 GetQuestReward = function(choice)
140 Questie:MarkQuestAsFinished();
141 QGetQuestReward(choice);
142 end
143 ---------------------------------------------------------------------------------------------------
144 --Blizzard Hook: Quest Reward Complete Button
145 --This hook marks a quest as finished in Questies DB if it can be finished.
146 --The call to the hooked function then finishes the quest.
147 ---------------------------------------------------------------------------------------------------
148 QuestRewardCompleteButton = QuestRewardCompleteButton_OnClick;
149 QuestRewardCompleteButton_OnClick = function()
150 Questie:MarkQuestAsFinished();
151 QuestRewardCompleteButton();
152 end
153 ---------------------------------------------------------------------------------------------------
154 --Blizzard Hook: Quest Progress Accept Button
155 ---------------------------------------------------------------------------------------------------
156 QuestDetailAcceptButton = QuestDetailAcceptButton_OnClick;
157 function QuestDetailAcceptButton_OnClick()
158 Questie:CheckQuestLogStatus();
159 QuestDetailAcceptButton();
160 end
161 ---------------------------------------------------------------------------------------------------
162 --Matches a looted item to quest items that are contained in the QuestieCachedQuests table
163 ---------------------------------------------------------------------------------------------------
164 function Questie:DetectQuestItem(itemName)
165 for k, v in pairs(QuestieCachedQuests) do
166 local num = v["leaderboards"]
167 for i=1,num do
168 desc = v["objective"..i]["desc"]
169 if (desc) then
170 local _, _, questItem, itemHave, itemNeed = string.find(desc, "(.+)%: (%d+)/(%d+)");
171 if itemName == questItem and itemHave ~= itemNeed then
172 --Questie:debug_Print("Quest:DetectQuestItem: TRUE");
173 --Questie:debug_Print("Quest:DetectQuestItem: [itemName: "..itemName.."] | [questItem: "..questItem.."] | [itemHave: "..itemHave.."] | [itemNeed: "..itemNeed.."]");
174 return true
175 else
176 --Questie:debug_Print("Quest:DetectQuestItem: FALSE");
177 return false
178 end
179 end
180 end
181 end
182 end
183 ---------------------------------------------------------------------------------------------------
184 --Parses loot messages then passes item to DetectQuestItem for verification
185 ---------------------------------------------------------------------------------------------------
186 function Questie:ParseQuestLoot(arg1)
187 local msg, item, loot
188 if string.find(arg1, "(You receive loot%:) (.+)") then
189 _, _, msg, item = string.find(arg1, "(You receive loot%:) (.+)");
190 elseif string.find(arg1, "(Received item%:) (.+)") then
191 _, _, msg, item = string.find(arg1, "(Received item%:) (.+)");
192 elseif string.find(arg1, "(You receive item%:) (.+)") then
193 _, _, msg, item = string.find(arg1, "(You receive item%:) (.+)");
194 end
195 if item then
196 _, _, loot = string.find(item, "%[(.+)%].+");
197 if Questie:DetectQuestItem(loot) then
198 --Questie:debug_Print("Quest:ParseQuestLoot --> [POST] Quest Loot: [ "..loot.." ] was found.");
199 Questie:CheckQuestLogStatus();
200 else
201 --Questie:debug_Print("Quest:ParseQuestLoot --> [POST] Quest Loot: [ "..loot.." ] is not a quest item.");
202 end
203 end
204 end
205 ---------------------------------------------------------------------------------------------------
206 --Used to make sure the players inventory isn't full before auto-completing quest.
207 ---------------------------------------------------------------------------------------------------
208 function Questie:CheckPlayerInventory()
209 local totalSlots, usedSlosts, availableSlots;
210 local totalSlots = 0;
211 local usedSlots = 0;
212 for bag = 0, 4 do
213 local size = GetContainerNumSlots(bag);
214 if (size and size > 0) then
215 totalSlots = totalSlots + size;
216 for slot = 1, size do
217 if (GetContainerItemInfo(bag, slot)) then
218 usedSlots = usedSlots + 1;
219 end
220 end
221 end
222 end
223 availableSlots = totalSlots - usedSlots;
224 return availableSlots
225 end
226 ---------------------------------------------------------------------------------------------------
227 --Finishes a quest and performs a recrusive check to make sure all the required quests that come
228 --before it are also finsihed and recorded in the players QuestieSeenQuests. It will also clear
229 --any redundant quest tracking data and make sure a quest that is in a players log isn't
230 --accidently marked finished. When ever this function is run it will also remove invalid tracker
231 --data when it doesn't find a matching hash in the QuestieSeenQuests table. This sometimes
232 --happens when a player starts a quest chain.
233 ---------------------------------------------------------------------------------------------------
234 function Questie:finishAndRecurse(questhash)
235 local QSQ = QuestieSeenQuests;
236 local QCQ = QuestieCachedQuests;
237 local QHM = QuestieHashMap;
238 --If it finds a completed quest with left over cached data, then the cached
239 --data gets cleared.
240 if (QSQ[questhash] == 1) then
241 if (QCQ[questhash]) then
242 QCQ[questhash] = nil;
243 end
244 end
245 --This loop checks to make sure a quest is finished before marking it complete. It then
246 --recursively checks all required quests before it and marks those as complete as well. It
247 --also checks each one to make sure we aren't marking a seen quest finished.
248 if (QSQ[questhash] == 0) and (QCQ[questhash]) then
249 if ((QCQ[questhash]["leaderboards"] == 0 or QCQ[questhash]["leaderboards"] == 1) or (QCQ[questhash]["isComplete"] == 1)) then
250 QSQ[questhash] = 1;
251 QCQ[questhash] = nil;
252 RemoveCrazyArrow(questhash);
253 else
254 local req = nil;
255 if QHM[questhash] then
256 req = QHM[questhash]['rq'];
257 end
258 if req and QSQ[req] ~= 1 then
259 Questie:finishAndRecurse(req);
260 end
261 return;
262 end
263 --This loop allows a player to recursively finish a quest and all required quests that comes
264 --before it by shift+clicking an icon from one of the maps. It also checks each one to make
265 --sure we aren't marking a seen quest finished.
266 elseif ((QSQ[questhash] == nil) and (QCQ[questhash] == nil)) then
267 QSQ[questhash] = 1;
268 local req = nil;
269 if QHM[questhash] then
270 req = QHM[questhash]['rq'];
271 end
272 if req and QSQ[req] ~= 1 then
273 Questie:finishAndRecurse(req);
274 else
275 return;
276 end
277 end
278 --This trolls through all cached data to make sure it stays cleaned up.
279 local index = 0;
280 for i,v in pairs(QCQ) do
281 if QSQ[i] == 1 then
282 QCQ[i] = nil;
283 index = index + 1;
284 end
285 end
286 end
287 ---------------------------------------------------------------------------------------------------
288 --Checks the players quest log upon login or ReloadUI to make sure QuestieMapNotes and
289 --QuestieCachedQuests get pre-populated with cache data before normal CheckLog functions are run.
290 --This is especially important if this data isn't already in the WoW game clients local cache.
291 ---------------------------------------------------------------------------------------------------
292 function Questie:UpdateGameClientCache(force)
293 if (IsQuestieActive == false) then return; end
294 Questie:debug_Print();
295 Questie:debug_Print("****************| Running Quest:UpdateGameClientCache |****************");
296 local prevQuestLogSelection = QGet_QuestLogSelection();
297 local id = 1;
298 local qc = 0;
299 local nEntry, nQuests = QGet_NumQuestLogEntries();
300 while qc < nQuests do
301 local questName, level, _, isHeader, isCollapsed, _ = QGet_QuestLogTitle(id);
302 if not isHeader and not isCollapsed then
303 QSelect_QuestLogEntry(id);
304 local questText, objectiveText = QGet_QuestLogQuestText();
305 local hash = Questie:getQuestHash(questName, level, objectiveText);
306 if (force) then
307 Questie:AddQuestToMap(hash, true);
308 Questie:debug_Print("Quest:UpdateGameClientCache --> Questie:AddQuestToMap(forced): [Name: "..questName.."]");
309 QuestieTracker:addQuestToTrackerCache(hash, id, level);
310 Questie:debug_Print("Quest:UpdateGameClientCache --> Questie:addQuestToTrackerCache(forced): [Hash: "..hash.."]");
311 end
312 for index=1, QGet_NumQuestLeaderBoards(id) do
313 local desc = QGet_QuestLogLeaderBoard(index, id);
314 local objectiveName = desc;
315 local splitIndex = findLast(objectiveName, ":");
316 if splitIndex ~= nil then
317 objectiveName = string.sub(objectiveName, 1, splitIndex-1);
318 if (string.find(objectiveName, " slain")) then
319 objectiveName = string.sub(objectiveName, 1, string.len(objectiveName)-6);
320 end
321 end
322 if (not LastQuestLogHashes and not force) or (QuestieHandledQuests[hash] and QuestieHandledQuests[hash]["objectives"] and QuestieHandledQuests[hash]["objectives"][index]["name"] ~= objectiveName) then
323 Questie:AddQuestToMap(hash);
324 Questie:debug_Print("Quest:UpdateGameClientCache --> Questie:AddQuestToMap(): [Name: "..QuestieHandledQuests[hash]["objectives"][index]["name"].."]");
325 QuestieTracker:addQuestToTrackerCache(hash, id, level);
326 Questie:debug_Print("Quest:UpdateGameClientCache --> Questie:addQuestToTrackerCache(): [Hash: "..hash.."]");
327 end
328 end
329 end
330 if not isHeader then
331 qc = qc + 1;
332 end
333 id = id + 1;
334 end
335 QSelect_QuestLogEntry(prevQuestLogSelection);
336 end
337 ---------------------------------------------------------------------------------------------------
338 --Checks the players quest log
339 ---------------------------------------------------------------------------------------------------
340 function Questie:CheckQuestLog()
341 --LastQuestLogHashes should always be nil upon Login or a ReloadUI - do these checks
342 if (not LastQuestLogHashes) then
343 Questie:debug_Print();
344 Questie:debug_Print("****************| Running [PRE] Quest:CheckQuestLog |****************");
345 --Clears abandoned quests
346 for k, v in pairs(QuestieSeenQuests) do
347 if (QuestieSeenQuests[k] == -1) then
348 Questie:RemoveQuestFromMap(k);
349 QuestieCachedQuests[k] = nil;
350 QuestieSeenQuests[k] = nil;
351 QUEST_WATCH_LIST[k] = nil;
352 Questie:debug_Print("Quest:CheckQuestLog: Found abandoned quest in QuestDB - Removed: [Hash: "..k.."]");
353 end
354 end
355 --Clears cached data
356 for k, v in pairs(QuestieCachedQuests) do
357 if QuestieSeenQuests[k] == 1 then
358 Questie:RemoveQuestFromMap(k);
359 QuestieCachedQuests[k] = nil;
360 Questie:debug_Print("Quest:CheckQuestLog: Found cached data for a finished quest - Removed: [Hash: "..k.."]");
361 end
362 end
363 LastQuestLogHashes, LastQuestLogCount = Questie:AstroGetAllCurrentQuestHashesAsMeta();
364 for k, v in pairs(LastQuestLogHashes) do
365 --If a quest is found in the log and for some reason it's set as finished (1), or
366 --missing all together (nil), reset its status back to active (0).
367 if QuestieSeenQuests[k] == 1 or QuestieSeenQuests[k] == nil then
368 QuestieSeenQuests[k] = 0;
369 Questie:debug_Print("Quest:CheckQuestLog: --> Quest found in QuestLog marked complete - Fixed: [Hash: "..v["hash"].."]");
370 end
371 --This "double-tap" ensures quest data is inserted into the cache
372 if (QuestieCachedQuests[v["hash"]] == nil) or (QuestieHandledQuests[v["hash"]] == nil) then
373 QuestieTracker:addQuestToTrackerCache(v["hash"], v["logId"], v["level"]);
374 Questie:AddQuestToMap(v["hash"]);
375 Questie:debug_Print("Quest:CheckQuestLog: --> Add quest to Tracker and MapNotes caches: [Hash: "..v["hash"].."]");
376 end
377 end
378 --Removes active quests from QuestDB if it's not active in the QuestLog
379 for k, v in pairs(QuestieSeenQuests) do
380 if QuestieSeenQuests[k] == 0 and LastQuestLogHashes[k] == nil then
381 QuestieCachedQuests[k] = nil;
382 QuestieSeenQuests[k] = nil;
383 QUEST_WATCH_LIST[k] = nil;
384 Questie:debug_Print("Quest:CheckQuestLog: --> Quest found in QuestDB not in QuestLog - Removed: [Hash: "..k.."]");
385 end
386 end
387 QUESTIE_LAST_UPDATE_FINISHED = GetTime();
388 return;
389 end
390 local CheckLogTime = GetTime();
391 local Quests, QuestsCount = Questie:AstroGetAllCurrentQuestHashesAsMeta();
392 MapChanged = false;
393 delta = {};
394 if (QuestsCount > LastQuestLogCount) then
395 for k, v in pairs(Quests) do
396 if (Quests[k] and LastQuestLogHashes[k]) then
397 else
398 if (Quests[k]) then
399 v["deltaType"] = 1;
400 table.insert(delta, v);
401 else
402 v["deltaType"] = 0;
403 table.insert(delta, v);
404 end
405 end
406 end
407 else
408 for k, v in pairs(LastQuestLogHashes) do
409 if (Quests[k] and LastQuestLogHashes[k]) then
410 else
411 if (Quests[k]) then
412 v["deltaType"] = 1;
413 table.insert(delta, v);
414 else
415 v["deltaType"] = 0;
416 table.insert(delta, v);
417 end
418 end
419 end
420 end
421 for k, v in pairs(delta) do
422 Questie:debug_Print();
423 Questie:debug_Print("****************| Running [POST] Quest:CheckQuestLog |**************** ");
424 Questie:debug_Print("Quest:CheckQuestLog: UPON ENTER: [QuestsCount: "..QuestsCount.."] | [LastCount: "..LastQuestLogCount.."]");
425 if (v["deltaType"] == 1) then
426 Questie:AddQuestToMap(v["hash"]);
427 --This adds a quest to the cache
428 if (QuestieSeenQuests[v["hash"]] == nil) then
429 QuestieSeenQuests[v["hash"]] = 0;
430 QuestieTracker:addQuestToTrackerCache(v["hash"], v["logId"], v["level"]);
431 Questie:debug_Print("Quest:CheckQuestLog: --> Add quest to Tracker and MapNotes caches: [Hash: "..v["hash"].."]");
432 RemoveCrazyArrow(v["hash"]);
433 if (AUTO_QUEST_WATCH == "1") then
434 AddQuestWatch(v["logId"]);
435 end
436 end
437 MapChanged = true;
438 elseif not Questie.collapsedThisRun then
439 Questie:RemoveQuestFromMap(v["hash"]);
440 --This clears cache of finished quests
441 if (QuestieSeenQuests[v["hash"]] == 1) then
442 QuestieTracker:removeQuestFromTracker(v["hash"]);
443 QUEST_WATCH_LIST[v["hash"]] = nil;
444 Questie:finishAndRecurse(v["hash"]);
445 Questie:debug_Print("Quest:CheckQuestLog: --> Quest:finishAndRecurse() [Hash: "..v["hash"].."]");
446 if (not QuestieCompletedQuestMessages[v["name"]]) then
447 QuestieCompletedQuestMessages[v["name"]] = 0;
448 end
449 --This clears cache of abandoned quests
450 elseif (QuestieSeenQuests[v["hash"]] == -1) then
451 QuestieTracker:removeQuestFromTracker(v["hash"]);
452 QuestieCachedQuests[v["hash"]] = nil;
453 QuestieSeenQuests[v["hash"]] = nil;
454 QUEST_WATCH_LIST[v["hash"]] = nil;
455 Questie:debug_Print("Quest:CheckQuestLog: clear abandoned quest: [Hash: "..v["hash"].."]");
456 end
457 --Cleans cached data
458 for k, v in pairs(QuestieCachedQuests) do
459 if QuestieSeenQuests[k] == 1 then
460 QuestieCachedQuests[k] = nil;
461 Questie:debug_Print("Quest:CheckQuestLog: Cleaned Quest Cache: [Hash: "..k.."]");
462 end
463 end
464 if lastObjectives and lastObjectives[v["hash"]] then
465 Questie:debug_Print("Quest:CheckQuestLog: lastObjectives update [Hash: "..v["hash"].."]");
466 lastObjectives = {};
467 end
468 MapChanged = true;
469 end
470 end
471 delta = nil;
472 LastQuestLogHashes = Quests;
473 LastQuestLogCount = QuestsCount;
474 if (MapChanged == true) then
475 Questie:debug_Print("Quest:CheckQuestLog: QuestLog Changed --> Questie:RefreshQuestStatus()");
476 Questie:RefreshQuestStatus();
477 QUESTIE_LAST_UPDATE_FINISHED = GetTime();
478 Questie:debug_Print("Quest:CheckQuestLog: UPON EXIT: [QuestsCount: "..QuestsCount.."] | [LastCount: "..LastQuestLogCount.."]");
479 return true;
480 else
481 Questie:debug_Print("Quest:CheckQuestLog: NO CHANGE --> Refresh Notes and Tracker");
482 Questie:AddEvent("SYNCLOG", 0.2);
483 Questie:AddEvent("DRAWNOTES", 0.4);
484 Questie:AddEvent("TRACKER", 0.6);
485 QUESTIE_LAST_UPDATE_FINISHED = GetTime();
486 return nil;
487 end
488 end
489 ---------------------------------------------------------------------------------------------------
490 --Adds or updates all active objectives in the questlog to the lastObjectives table
491 ---------------------------------------------------------------------------------------------------
492 function Questie:UpdateQuests(force)
493 if (not lastObjectives) then
494 lastObjectives = {};
495 Questie:UpdateQuestsInit();
496 return;
497 end
498 local UpdateQuestsTime = GetTime();
499 local ZonesChecked = 0;
500 local CurrentZone = GetZoneText();
501 local numEntries, numQuests = QGet_NumQuestLogEntries();
502 local change = Questie:UpdateQuestInZone(CurrentZone);
503 local i = 1;
504 local qc = 0;
505 ZonesChecked = ZonesChecked + 1;
506 if (not change) then
507 change = Questie:UpdateQuestInZone(GetMinimapZoneText());
508 ZonesChecked = ZonesChecked + 1;
509 end
510 if (not change or force) then
511 while qc < numQuests do
512 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
513 if (isHeader and q ~= CurrentZone) then
514 local c = Questie:UpdateQuestInZone(q, force);
515 ZonesChecked = ZonesChecked + 1;
516 change = c;
517 if (c and not force)then
518 break;
519 end
520 end
521 if not isHeader then
522 qc = qc + 1;
523 end
524 i = i + 1;
525 end
526 else
527 end
528 return change;
529 end
530 ---------------------------------------------------------------------------------------------------
531 --Updates all active objectives in a zone then updates the lastObjectives table
532 ---------------------------------------------------------------------------------------------------
533 function Questie:UpdateQuestInZone(Zone, force)
534 local numEntries, numQuests = QGet_NumQuestLogEntries();
535 local foundChange = nil;
536 local ZoneFound = nil;
537 local QuestsChecked = 0;
538 local i = 1;
539 local qc = 0;
540 local prevQuestLogSelection = QGet_QuestLogSelection();
541 while qc < numQuests do
542 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
543 if (ZoneFound and isHeader) then
544 break;
545 end
546 if (isHeader and q == Zone) then
547 ZoneFound = true;
548 end
549 if not isHeader and ZoneFound then
550 QuestsChecked = QuestsChecked + 1;
551 QSelect_QuestLogEntry(i);
552 local count = QGet_NumQuestLeaderBoards();
553 local questText, objectiveText = QGet_QuestLogQuestText();
554 local hash = Questie:getQuestHash(q, level, objectiveText);
555 if QuestieHashCache[q] == nil then QuestieHashCache[q] = {}; end
556 QuestieHashCache[q][hash] = GetTime();
557 if not lastObjectives[hash] then
558 lastObjectives[hash] = {};
559 end
560 local Refresh = nil;
561 for obj = 1, count do
562 if (not lastObjectives[hash][obj]) then
563 lastObjectives[hash][obj] = {};
564 end
565 local desc, typ, done = QGet_QuestLogLeaderBoard(obj);
566 if(lastObjectives[hash][obj].desc == desc and lastObjectives[hash][obj].typ == typ and lastObjectives[hash][obj].done == done) then
567 elseif(lastObjectives[hash][obj].done ~= done) then
568 Refresh = true;
569 foundChange = true;
570 else
571 foundChange = true;
572 end
573 lastObjectives[hash][obj].desc = desc;
574 lastObjectives[hash][obj].typ = typ;
575 lastObjectives[hash][obj].done = done;
576 end
577 if (Refresh) then
578 Questie:AddQuestToMap(hash, true);
579 end
580 if (foundChange and QuestieConfig.trackerEnabled == true) then
581 if (QuestieCachedQuests[hash]) then
582 QuestieTracker:updateTrackerCache(hash, i, level);
583 end
584 end
585 end
586 if (foundChange and not force) then
587 break;
588 end
589 if not isHeader then
590 qc = qc + 1;
591 end
592 i = i + 1;
593 end
594 QSelect_QuestLogEntry(prevQuestLogSelection);
595 return foundChange;
596 end
597 ---------------------------------------------------------------------------------------------------
598 --Adds all active objectives from all quests in the questlog to the lastObjectives table
599 ---------------------------------------------------------------------------------------------------
600 function Questie:UpdateQuestsInit()
601 local numEntries, numQuests = QGet_NumQuestLogEntries();
602 local i = 1;
603 local qc = 0;
604 local prevQuestLogSelection = QGet_QuestLogSelection();
605 while qc < numQuests do
606 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
607 if not isHeader then
608 QSelect_QuestLogEntry(i);
609 local count = QGet_NumQuestLeaderBoards();
610 local questText, objectiveText = QGet_QuestLogQuestText();
611 local hash = Questie:getQuestHash(q, level, objectiveText);
612 if not lastObjectives[hash] then
613 lastObjectives[hash] = {};
614 end
615 for obj = 1, count do
616 if (not lastObjectives[hash][obj]) then
617 lastObjectives[hash][obj] = {};
618 end
619 lastObjectives[hash][obj].desc = desc;
620 lastObjectives[hash][obj].typ = typ;
621 lastObjectives[hash][obj].done = done;
622 end
623 qc = qc + 1;
624 end
625 i = i + 1;
626 end
627 QSelect_QuestLogEntry(prevQuestLogSelection);
628 end
629 ---------------------------------------------------------------------------------------------------
630 --Astrolabe functions
631 ---------------------------------------------------------------------------------------------------
632 function Questie:AstroGetAllCurrentQuestHashes(print)
633 local hashes = {};
634 local numEntries, numQuests = QGet_NumQuestLogEntries();
635 local i = 1;
636 local qc = 0;
637 if (print) then
638 --Questie:debug_Print("Quest:AstroGetAllCurrentQuestHashes: Listing all current quests");
639 end
640 local prevQuestLogSelection = QGet_QuestLogSelection();
641 while qc < numQuests do
642 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
643 if not isHeader then
644 QSelect_QuestLogEntry(i);
645 local count = QGet_NumQuestLeaderBoards();
646 local questText, objectiveText = QGet_QuestLogQuestText();
647 local quest = {};
648 quest["name"] = q;
649 quest["level"] = level;
650 local hash = Questie:getQuestHash(q, level, objectiveText);
651 quest["hash"] = hash;
652 if(IsAddOnLoaded("URLCopy") and print) then
653 Questie:debug_Print(" "..q,URLCopy_Link(quest["hash"]));
654 elseif(print) then
655 Questie:debug_Print(" "..q,quest["hash"]);
656 end
657 table.insert(hashes, quest);
658 qc = qc + 1;
659 else
660 if (print) then
661 Questie:debug_Print(" Zone:", q);
662 end
663 end
664 i = i + 1;
665 end
666 QSelect_QuestLogEntry(prevQuestLogSelection);
667 if (print) then
668 --Questie:debug_Print("Quest:AstroGetAllCurrentQuestHashes: End of all current quests");
669 end
670 return hashes;
671 end
672 ---------------------------------------------------------------------------------------------------
673 function Questie:AstroGetAllCurrentQuestHashesAsMeta(print)
674 local agacqhamtime = GetTime();
675 local hashes = {};
676 local Count = 0;
677 local numEntries, numQuests = QGet_NumQuestLogEntries();
678 local collapsedCount = 0;
679 local i = 1;
680 local qc = 0;
681 Questie.collapsedThisRun = false;
682 local prevQuestLogSelection = QGet_QuestLogSelection();
683 while qc < numQuests do
684 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
685 if isCollapsed then collapsedCount = collapsedCount + 1; end
686 if not isHeader then
687 QSelect_QuestLogEntry(i);
688 local count = QGet_NumQuestLeaderBoards();
689 local questText, objectiveText = QGet_QuestLogQuestText();
690 local hash = Questie:getQuestHash(q, level, objectiveText);
691 if hash >= 0 then
692 hashes[hash] = {};
693 hashes[hash]["hash"] = hash;
694 hashes[hash]["name"] = q;
695 hashes[hash]["level"] = level;
696 hashes[hash]["logId"] = i;
697 if(IsAddOnLoaded("URLCopy") and print)then
698 Questie:debug_Print(" "..q,URLCopy_Link(quest["hash"]));
699 elseif(print) then
700 Questie:debug_Print(" "..q,quest["hash"]);
701 end
702 end
703 qc = qc + 1;
704 else
705 if (print) then
706 Questie:debug_Print(" Zone:", q);
707 end
708 end
709 i=i+1
710 end
711 QSelect_QuestLogEntry(prevQuestLogSelection);
712 if (print) then
713 --Questie:debug_Print("Quest:AstroGetAllCurrentQuestHashesAsMeta: End of all current quests");
714 end
715 if not (collapsedCount == Questie.lastCollapsedCount) then
716 Questie.lastCollapsedCount = collapsedCount;
717 Questie.collapsedThisRun = true;
718 end
719 --Questie:debug_Print("Quest:AstroGetAllCurrentQuestHashesAsMeta --> Getting all hashes took: ["..tostring((GetTime()- agacqhamtime)*1000).."ms]");
720 return hashes, numQuests;
721 end
722 ---------------------------------------------------------------------------------------------------
723 function Questie:AstroGetFinishedQuests()
724 numEntries, numQuests = QGet_NumQuestLogEntries();
725 local FinishedQuests = {};
726 local i = 1;
727 local qc = 0;
728 local prevQuestLogSelection = QGet_QuestLogSelection();
729 while qc < numQuests do
730 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
731 if not isHeader then
732 QSelect_QuestLogEntry(i);
733 local count = QGet_NumQuestLeaderBoards();
734 local questText, objectiveText = QGet_QuestLogQuestText();
735 Done = true;
736 for obj = 1, count do
737 local desc, typ, done = QGet_QuestLogLeaderBoard(obj);
738 if not done then
739 Done = nil;
740 end
741 end
742 if(Done) then
743 local hash = Questie:getQuestHash(q, level, objectiveText);
744 --Questie:debug_Print("AstroGetFinishedQuests: [Hash: "..hash.."] | [Quest: "..q.."] | [Level: "..level.."]");
745 table.insert(FinishedQuests, hash);
746 end
747 qc = qc + 1;
748 end
749 i = i + 1;
750 end
751 QSelect_QuestLogEntry(prevQuestLogSelection);
752 return FinishedQuests;
753 end
754 ---------------------------------------------------------------------------------------------------
755 function Questie:GetQuestObjectivePaths(questHash)
756 local prevQuestLogSelection = QGet_QuestLogSelection();
757 local questLogID = Questie:GetQuestIdFromHash(questHash);
758 QSelect_QuestLogEntry(questLogID);
759 local count = QGet_NumQuestLeaderBoards();
760 local objectivePaths = {};
761 for i = 1, count do
762 local desc, type, done = QGet_QuestLogLeaderBoard(i);
763 local typeFunctions = {
764 ['item'] = GetItemLocations,
765 ['event'] = GetEventLocations,
766 ['monster'] = GetMonsterLocations,
767 ['object'] = GetObjectLocations,
768 ['reputation'] = GetReputationLocations
769 };
770 local typeFunction = typeFunctions[type];
771 if typeFunction ~= nil then
772 local objectiveName = desc;
773 local splitIndex = findLast(objectiveName, ":");
774 if splitIndex ~= nil then
775 objectiveName = string.sub(objectiveName, 1, splitIndex-1);
776 if (string.find(objectiveName, " slain")) then
777 objectiveName = string.sub(objectiveName, 1, string.len(objectiveName)-6);
778 end
779 end
780 locations = typeFunction(objectiveName);
781 objectivePaths[i] = {};
782 objectivePaths[i]['path'] = locations;
783 objectivePaths[i]['done'] = done;
784 objectivePaths[i]['type'] = type;
785 objectivePaths[i]['name'] = objectiveName;
786 objectivePaths[i]['desc'] = desc
787 end
788 end
789 QSelect_QuestLogEntry(prevQuestLogSelection);
790 return objectivePaths;
791 end
792 ---------------------------------------------------------------------------------------------------
793 --Perhaps we should consider removing this function from Questie
794 ---------------------------------------------------------------------------------------------------
795 function Questie:AstroGetQuestObjectives(questHash)
796 local prevQuestLogSelection = QGet_QuestLogSelection();
797 local QuestLogID = Questie:GetQuestIdFromHash(questHash);
798 local mapid = GetCurrentMapID();
799 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(QuestLogID);
800 QSelect_QuestLogEntry(QuestLogID);
801 local count = QGet_NumQuestLeaderBoards();
802 local questText, objectiveText = QGet_QuestLogQuestText();
803 local AllObjectives = {};
804 AllObjectives["QuestName"] = q;
805 AllObjectives["objectives"] = {};
806 for i = 1, count do
807 local desc, typ, done = QGet_QuestLogLeaderBoard(i);
808 local typeFunction = AstroobjectiveProcessors[typ];
809 if typ == "item" or typ == "monster" or not (typeFunction == nil) then
810 local indx = findLast(desc, ":");
811 local countless = indx == nil;
812 local countstr = "";
813 local namestr = desc;
814 if not countless then
815 countstr = string.sub(desc, indx + 2);
816 namestr = string.sub(desc, 1, indx - 1);
817 end
818 local objectives = typeFunction(q, namestr, countstr, selected, mapid);
819 Objective = {};
820 local hash = Questie:getQuestHash(q, level, objectiveText);
821 for k, v in pairs(objectives) do
822 if (AllObjectives["objectives"][v["name"]] == nil) then
823 AllObjectives["objectives"][v["name"]] = {};
824 end
825 if (not QuestieCachedMonstersAndObjects[hash]) then
826 QuestieCachedMonstersAndObjects[hash] = {};
827 end
828 if (not QuestieCachedMonstersAndObjects[hash][v["name"]]) then
829 QuestieCachedMonstersAndObjects[hash][v["name"]] = {};
830 end
831 QuestieCachedMonstersAndObjects[hash][v["name"]].name = v["name"];
832 for monster, info in pairs(v['locations']) do
833 local obj = {};
834 obj["mapid"] = info[1];
835 obj["x"] = info[2];
836 obj["y"] = info[3];
837 obj["lootname"] = v["lootname"];
838 obj["type"] = v["type"];
839 obj["done"] = done;
840 obj['objectiveid'] = i;
841 table.insert(AllObjectives["objectives"][v["name"]], obj);
842 end
843 end
844 else
845 end
846 end
847 QSelect_QuestLogEntry(prevQuestLogSelection);
848 return AllObjectives;
849 end
850 ---------------------------------------------------------------------------------------------------
851 AstroobjectiveProcessors = {
852 ['item'] = function(quest, name, amount, selected, mapid)
853 local list = {};
854 local itemdata = QuestieItems[name];
855 --Questie:debug_Print(name);
856 if itemdata == nil then
857 Questie:debug_Print("Quest:AstroobjectiveProcessors --> ERROR1 PROCESSING: [Quest: "..quest.."] | [Objective: "..name.."] | No [itemdata] found | ID:0");
858 itemdata = QuestieItems[name];
859 end
860 if itemdata then
861 for k,v in pairs(itemdata) do
862 if k == "locationCount" then
863 local monster = {};
864 monster["name"] = name;
865 monster["locations"] = {};
866 monster["type"] = "loot";
867 for b=1,itemdata['locationCount'] do
868 local loc = itemdata['locations'][b];
869 table.insert(monster["locations"], loc);
870 end
871 table.insert(list, monster);
872 elseif k == "drop" then
873 for e,r in pairs(v) do
874 local monster = {};
875 monster["name"] = name;
876 monster["lootname"] = e;
877 monster["locations"] = {};
878 monster["type"] = "loot";
879 for k, pos in pairs(QuestieMonsters[e]['locations']) do
880 table.insert(monster["locations"], pos);
881 end
882 table.insert(list, monster);
883 end
884 elseif k == "contained" then
885 for objectName, someNumber in pairs(v) do
886 local monster = {};
887 monster["name"] = name;
888 monster["lootname"] = objectName;
889 monster["locations"] = {};
890 monster["type"] = "object";
891 if QuestieObjects[objectName] then
892 --TODO: handle objects that appear when a mob is killed
893 for k, pos in pairs(QuestieObjects[objectName]['locations']) do
894 table.insert(monster["locations"], pos);
895 end
896 table.insert(list, monster);
897 end
898 end
899 elseif k =="locations" then
900 else
901 Questie:debug_Print("Quest:AstroobjectiveProcessors --> ERROR2: [Quest: "..quest.."] | [Objective: "..name.."] | ID:1");
902 for s, r in pairs(itemdata) do
903 Questie:debug_Print(s,tostring(r));
904 end
905 end
906 end
907 end
908 return list;
909 end,
910 ['event'] = function(quest, name, amount, selected, mapid)
911 local evtdata = QuestieEvents[name];
912 local list = {};
913 if evtdata == nil then
914 Questie:debug_Print("Quest:AstroobjectiveProcessors --> ERROR3 UNKNOWN EVENT: [Quest: "..quest.."] | [Objective: "..name.."] | ID:2");
915 else
916 for b=1,evtdata['locationCount'] do
917 local monster = {};
918 monster["name"] = name;
919 monster["locations"] = {};
920 monster["type"] = "event";
921 for b=1,evtdata['locationCount'] do
922 local loc = evtdata['locations'][b];
923 table.insert(monster["locations"], loc);
924 end
925 table.insert(list, monster);
926 end
927 end
928 return list;
929 end,
930 ['monster'] = function(quest, name, amount, selected, mapid)
931 local list = {};
932 local monster = {};
933 if (string.find(name, " slain")) then
934 name = string.sub(name, 1, string.len(name)-6);
935 end
936 monster["name"] = name;
937 monster["type"] = "slay";
938 monster["locations"] = {};
939 if (QuestieMonsters[name] and QuestieMonsters[name]['locations']) then
940 for k, pos in pairs(QuestieMonsters[name]['locations']) do
941 table.insert(monster["locations"], pos);
942 end
943 end
944 table.insert(list, monster);
945 return list;
946 end,
947 ['object'] = function(quest, name, amount, selected, mapid)
948 local list = {};
949 local objdata = QuestieObjects[name];
950 if objdata == nil then
951 Questie:debug_Print("Quest:AstroobjectiveProcessors: ERROR4 UNKNOWN OBJECT: [Quest: "..quest.."] | [Objective: "..name.."]");
952 else
953 for b=1,objdata['locationCount'] do
954 local monster = {};
955 monster["name"] = name;
956 monster["locations"] = {};
957 monster["type"] = "object";
958 for b=1,objdata['locationCount'] do
959 local loc = objdata['locations'][b];
960 table.insert(monster["locations"], loc);
961 end
962 table.insert(list, monster);
963 end
964 end
965 return list;
966 end
967 }
968 ---------------------------------------------------------------------------------------------------
969 --End of Astrolabe functions
970 ---------------------------------------------------------------------------------------------------
971 --///////////////////////////////////////////////////////////////////////////////////////////////--
972 ---------------------------------------------------------------------------------------------------
973 --Get quest ID from quest hash
974 ---------------------------------------------------------------------------------------------------
975 function Questie:GetQuestIdFromHash(questHash)
976 local numEntries, numQuests = QGet_NumQuestLogEntries();
977 if (QUESTIE_UPDATE_EVENT or numEntries ~= LastNrOfEntries or not CachedIds[questHash]) then
978 CachedIds[questHash] = {};
979 QUESTIE_UPDATE_EVENT = 0;
980 LastNrOfEntries = numEntries;
981 Questie:UpdateQuestIds();
982 if CachedIds[questHash] then
983 return CachedIds[questHash];
984 end
985 else
986 local prevQuestLogSelection = QGet_QuestLogSelection();
987 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(CachedIds[questHash]);
988 QSelect_QuestLogEntry(CachedIds[questHash]);
989 local questText, objectiveText = QGet_QuestLogQuestText();
990 if (q and level and objectiveText) then
991 if(Questie:getQuestHash(q, level, objectiveText) == questHash) then
992 QSelect_QuestLogEntry(prevQuestLogSelection)
993 return CachedIds[questHash];
994 else
995 Questie:debug_Print("Quest:GetQuestIdFromHash --> Error: [Hash: "..tostring(CachedIds[questHash]).."]1");
996 end
997 else
998 Questie:debug_Print("Quest:GetQuestIdFromHash --> Error2: [Hash: "..tostring(CachedIds[questHash]).."] | [Quest: "..tostring(q).."] | [Level: "..tostring(level).."]");
999 end
1000 QSelect_QuestLogEntry(prevQuestLogSelection);
1001 end
1002 end
1003 ---------------------------------------------------------------------------------------------------
1004 --Update quest ID's
1005 ---------------------------------------------------------------------------------------------------
1006 function Questie:UpdateQuestIds()
1007 local uqidtime = GetTime()
1008 local numEntries, numQuests = QGet_NumQuestLogEntries();
1009 local i = 1;
1010 local qc = 0;
1011 local prevQuestLogSelection = QGet_QuestLogSelection()
1012 while qc < numQuests do
1013 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(i);
1014 if not isHeader then
1015 QSelect_QuestLogEntry(i);
1016 local questText, objectiveText = QGet_QuestLogQuestText();
1017 local hash = Questie:getQuestHash(q, level, objectiveText);
1018 if (not q or not level or not objective) then
1019 --commented out the error because it was really annoying. -ZoeyZolotova
1020 --Questie:debug_Print("Quest:UpdateQuestIds --> Error1: [Name: "..tostring(name).."] | [Level: "..tostring(level).."] | [Id: "..tostring(i).."] | [Hash: "..tostring(hash).."]")
1021 end
1022 CachedIds[hash] = i;
1023 qc = qc + 1;
1024 end
1025 i = i + 1;
1026 end
1027 QSelect_QuestLogEntry(prevQuestLogSelection);
1028 --Questie:debug_Print("Quest:UpdateQuestID: --> Updating QuestIds took: ["..tostring((GetTime()- uqidtime)*1000).."ms]")
1029 end
1030 ---------------------------------------------------------------------------------------------------
1031 --Some outdated server databases still use names like "Tower of Althalaxx part x".
1032 --which were used to turn quest names into a unique key. This function removes
1033 --those suffixes, so that they don't harm the quest lookup.
1034 ---------------------------------------------------------------------------------------------------
1035 function Questie:SanitisedQuestLookup(name)
1036 local realName, matched = string.gsub(name, " [(]?[Pp]art %d+[)]?", "");
1037 return QuestieLevLookup[realName] or false;
1038 end
1039 ---------------------------------------------------------------------------------------------------
1040 --Remove unique suffix from text.
1041 ---------------------------------------------------------------------------------------------------
1042 function Questie:RemoveUniqueSuffix(text)
1043 if string.sub(text, -1) == "]" then
1044 local strlen = string.len(text)
1045 text = string.sub(text, 1, strlen-4)
1046 end
1047 return text
1048 end
1049 ---------------------------------------------------------------------------------------------------
1050 --Lookup quest hash from name, level or objective text
1051 ---------------------------------------------------------------------------------------------------
1052 function Questie:getQuestHash(name, level, objectiveText)
1053 local hashLevel = level or "hashLevel";
1054 local hashText = objectiveText or "hashText";
1055 if QuestieQuestHashCache[name..hashLevel..hashText] then
1056 return QuestieQuestHashCache[name..hashLevel..hashText];
1057 end
1058 local questLookup = Questie:SanitisedQuestLookup(name);
1059 local hasOthers = false;
1060 if questLookup then
1061 local count = 0;
1062 local retval = 0;
1063 local bestDistance = 4294967295; --some high number (0xFFFFFFFF)
1064 local race = UnitRace("Player");
1065 for k,v in pairs(questLookup) do
1066 if QuestieHashMap[v[2]] ~= nil then
1067 local rr = v[1];
1068 local adjustedDescription = Questie:RemoveUniqueSuffix(k)
1069 if count == 1 then
1070 hasOthers = true;
1071 end
1072 local requiredQuest = QuestieHashMap[v[2]]['rq']
1073 if adjustedDescription == objectiveText and tonumber(QuestieHashMap[v[2]]['questLevel']) == hashLevel and checkRequirements(null, race, null, rr) and (not requiredQuest or QuestieSeenQuests[requiredQuest]) and not QuestieSeenQuests[v[2]] then
1074 QuestieQuestHashCache[name..hashLevel..hashText] = v[2];
1075 return v[2],hasOthers; --exact match
1076 end
1077 local dist = 4294967294;
1078 if not (objectiveText == nil) then
1079 dist = Questie:Levenshtein(objectiveText, adjustedDescription);
1080 end
1081 if dist < bestDistance then
1082 bestDistance = dist;
1083 retval = v[2];
1084 end
1085 count = count + 1;
1086 else
1087 Questie:debug_Print("ERROR: Quest '"..name.."' was found but data is missing for hash "..v[2].." Please report this on Github!")
1088 end
1089 end
1090 if not (retval == 0) then
1091 QuestieQuestHashCache[name..hashLevel..hashText] = retval;
1092 return retval, hasOthers; --nearest match
1093 end
1094 end
1095 if name == nil then
1096 return -1;
1097 end
1098 local hash = Questie:MixString(0, name);
1099 if not (level == nil) then
1100 hash = Questie:MixInt(hash, level);
1101 QuestieQuestHashCache[name..hashLevel..hashText] = hash;
1102 end
1103 if not (objectiveText == nil) then
1104 hash = Questie:MixString(hash, objectiveText);
1105 QuestieQuestHashCache[name..hashLevel..hashText] = hash;
1106 end
1107 QuestieQuestHashCache[name..hashLevel..hashText] = hash;
1108 return hash, false;
1109 end
1110 ---------------------------------------------------------------------------------------------------
1111 --Checks to see if a quest is finished by quest hash
1112 ---------------------------------------------------------------------------------------------------
1113 function Questie:IsQuestFinished(questHash)
1114 local id = Questie:GetQuestIdFromHash(questHash);
1115 if (not id) then
1116 return false;
1117 end
1118 local prevQuestLogSelection = QGet_QuestLogSelection()
1119 local FinishedQuests = {};
1120 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(id);
1121 QSelect_QuestLogEntry(id);
1122 local count = QGet_NumQuestLeaderBoards();
1123 local questText, objectiveText = QGet_QuestLogQuestText();
1124 local Done = true;
1125 for obj = 1, count do
1126 local desc, typ, done = QGet_QuestLogLeaderBoard(obj);
1127 if not done then
1128 Done = nil;
1129 end
1130 end
1131 QSelect_QuestLogEntry(prevQuestLogSelection);
1132 if (Done and Questie:getQuestHash(q, level, objectiveText) == questHash) then
1133 local ret = {};
1134 ret["questHash"] = questHash;
1135 ret["name"] = q;
1136 ret["level"] = level;
1137 return ret;
1138 end
1139 return nil;
1140 end
1141 ---------------------------------------------------------------------------------------------------
1142 --Race, Class and Profession filter functions
1143 ---------------------------------------------------------------------------------------------------
1144 RaceBitIndexTable = {
1145 ['human'] = 1,
1146 ['orc'] = 2,
1147 ['dwarf'] = 3,
1148 ['nightelf'] = 4,
1149 ['night elf'] = 4,
1150 ['scourge'] = 5,
1151 ['undead'] = 5,
1152 ['tauren'] = 6,
1153 ['gnome'] = 7,
1154 ['troll'] = 8,
1155 ['goblin'] = 9
1156 };
1157 ClassBitIndexTable = {
1158 ['warrior'] = 1,
1159 ['paladin'] = 2,
1160 ['hunter'] = 3,
1161 ['rogue'] = 4,
1162 ['priest'] = 5,
1163 ['shaman'] = 7,
1164 ['mage'] = 8,
1165 ['warlock'] = 9,
1166 ['druid'] = 11
1167 };
1168 ---------------------------------------------------------------------------------------------------
1169 function unpackBinary(val)
1170 ret = {};
1171 for q=0,16 do
1172 if bit.band(bit.rshift(val,q), 1) == 1 then
1173 table.insert(ret, true);
1174 else
1175 table.insert(ret, false);
1176 end
1177 end
1178 return ret;
1179 end
1180 ---------------------------------------------------------------------------------------------------
1181 function checkRequirements(class, race, dbClass, dbRace)
1182 local valid = true;
1183 if race and dbRace and not (dbRace == 0) then
1184 local racemap = unpackBinary(dbRace);
1185 valid = racemap[RaceBitIndexTable[strlower(race)]];
1186 end
1187 if class and dbClass and valid and not (dbRace == 0)then
1188 local classmap = unpackBinary(dbClass);
1189 valid = classmap[ClassBitIndexTable[strlower(class)]];
1190 end
1191 return valid;
1192 end
1193 ---------------------------------------------------------------------------------------------------
1194 function Questie:GetAvailableQuestHashes(mapFileName, levelFrom, levelTo)
1195 local mapid = -1;
1196 if(QuestieZones[mapFileName]) then
1197 c = QuestieZones[mapFileName][4];
1198 z = QuestieZones[mapFileName][5];
1199 end
1200 local class = UnitClass("Player");
1201 local race = UnitRace("Player");
1202 local hashes = {};
1203 for l = 0,100 do
1204 if QuestieZoneLevelMap[c] and QuestieZoneLevelMap[c][z] then
1205 local content = QuestieZoneLevelMap[c][z][l];
1206 if content then
1207 for v, locationMeta in pairs(content) do
1208 local qdata = QuestieHashMap[v];
1209 if (qdata) then
1210 local stop = false;
1211 local questLevel = qdata.questLevel;
1212 for x in string.gfind(questLevel, "%d+") do questLevel = x; end
1213 questLevel = tonumber(questLevel);
1214 if QuestieConfig.minLevelFilter and questLevel < levelFrom then
1215 stop = true;
1216 end
1217 if QuestieConfig.maxLevelFilter and qdata.level > levelTo then
1218 stop = true;
1219 end
1220 if (not stop) then
1221 local requiredQuest = qdata['rq'];
1222 local requiredRaces = qdata['rr'];
1223 local requiredClasses = qdata['rc'];
1224 local requiredSkill = qdata['rs'];
1225 local valid = not QuestieSeenQuests[requiredQuest];
1226 if(requiredQuest) then valid = QuestieSeenQuests[requiredQuest]; end
1227 valid = valid and (requiredSkill == nil or QuestieConfig.showProfessionQuests);
1228 if valid then valid = valid and checkRequirements(class, race, requiredClasses,requiredRaces); end
1229 if valid and not QuestieHandledQuests[requiredQuest] and not QuestieSeenQuests[v] then
1230 hashes[v] = locationMeta;
1231 end
1232 end
1233 end
1234 end
1235 end
1236 end
1237 end
1238 return hashes;
1239 end
1240 ---------------------------------------------------------------------------------------------------
1241 --End of filter functions
1242 ---------------------------------------------------------------------------------------------------