vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 ---------------------------------------------------------------------------------------------------
2 -- Name: QuestieNotes
3 -- Description: Handles all the quest map notes
4 ---------------------------------------------------------------------------------------------------
5 --///////////////////////////////////////////////////////////////////////////////////////////////--
6 ---------------------------------------------------------------------------------------------------
7 -- Local Vars
8 ---------------------------------------------------------------------------------------------------
9 local AllFrames = {};
10 local FramePool = {};
11 local Cluster = {};
12 local LastContinent = nil;
13 local LastZone = nil;
14 local Dewdrop = AceLibrary("Dewdrop-2.0");
15 local specialSources = { ["openedby"] = 1, };
16 local QGet_QuestLogTitle = GetQuestLogTitle;
17 local QGet_NumQuestLeaderBoards = GetNumQuestLeaderBoards;
18 local QSelect_QuestLogEntry = SelectQuestLogEntry;
19 local QGet_QuestLogLeaderBoard = GetQuestLogLeaderBoard;
20 local QGet_QuestLogQuestText = GetQuestLogQuestText;
21 local QGet_TitleText = GetTitleText;
22 local QGet_QuestLogSelection = GetQuestLogSelection;
23 ---------------------------------------------------------------------------------------------------
24 -- Global Vars
25 ---------------------------------------------------------------------------------------------------
26 QUESTIE_NOTES_MAP_ICON_SCALE = 1.2;
27 QUESTIE_NOTES_WORLD_MAP_ICON_SCALE = 0.75;
28 QUESTIE_NOTES_CONTINENT_ICON_SCALE = 1;
29 QUESTIE_NOTES_MINIMAP_ICON_SCALE = 1.0;
30 QuestieMapNotes = {};
31 QuestieUsedNoteFrames = {};
32 QuestieHandledQuests = {};
33 QuestieAvailableMapNotes = {};
34 QuestieCachedMonstersAndObjects = {};
35 Questie_LastTooltip = GetTime();
36 QUESTIE_DEBUG_TOOLTIP = nil;
37 Questie_TooltipCache = {};
38 CREATED_NOTE_FRAMES = 1;
39 INIT_POOL_SIZE = 11;
40 Cluster.__index = Cluster;
41 __TT_LineCache = {};
42 UIOpen = false;
43 QuestieNotes = AceLibrary("AceAddon-2.0"):new("AceHook-2.1")
44 ---------------------------------------------------------------------------------------------------
45 --Setup Hooks
46 ---------------------------------------------------------------------------------------------------
47 function QuestieNotes:OnInitialize()
48 self:Hook(WorldMapFrame, "Show", "WorldMapFrame_Show", true)
49 self:Hook(WorldMapFrame, "SetAlpha", "WorldMapFrame_SetAlpha", true)
50 end
51 ---------------------------------------------------------------------------------------------------
52 -- Refreshes Quest Notes
53 ---------------------------------------------------------------------------------------------------
54 function Questie:RefreshQuestNotes()
55 QUESTIE_UPDATE_EVENT = 1;
56 if (GetTime() - QUESTIE_LAST_SYNCLOG > 0.1) then
57 Questie:AddEvent("SYNCLOG", 0);
58 QUESTIE_LAST_SYNCLOG = GetTime();
59 else
60 QUESTIE_LAST_SYNCLOG = GetTime();
61 end
62 if (GetTime() - QUESTIE_LAST_DRAWNOTES > 0.1) then
63 Questie:AddEvent("DRAWNOTES", 0.2);
64 QUESTIE_LAST_DRAWNOTES = GetTime();
65 else
66 QUESTIE_LAST_DRAWNOTES = GetTime();
67 end
68 if (GetTime() - QUESTIE_LAST_TRACKER > 0.1) then
69 Questie:AddEvent("TRACKER", 0.4);
70 QUESTIE_LAST_TRACKER = GetTime();
71 else
72 QUESTIE_LAST_TRACKER = GetTime();
73 end
74 end
75 ---------------------------------------------------------------------------------------------------
76 -- Adds quest notes to map
77 ---------------------------------------------------------------------------------------------------
78 function Questie:AddQuestToMap(questHash, redraw)
79 if(IsQuestieActive == false) then return; end
80 if questHash == -1 then return; end
81 --Questie:debug_Print("Notes:AddQuestToMap --> Adding Quest to Map [Hash: "..questHash.."]");
82 local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
83 Questie:RemoveQuestFromMap(questHash);
84 local objectives = Questie:GetQuestObjectivePaths(questHash)
85 --Cache code
86 local ques = {};
87 ques["noteHandles"] = {};
88 UsedContinents = {};
89 UsedZones = {};
90 local Quest = Questie:IsQuestFinished(questHash);
91 if not (Quest) then
92 Questie:debug_Print("Notes:AddQuestToMap --> Display Objective Icons: [Hash: "..questHash.."]");
93 for objectiveid, objective in pairs(objectives) do
94 if not objective.done then
95 local typeToIcon = {
96 ["item"] = "loot",
97 ["event"] = "event",
98 ["monster"] = "slay",
99 ["object"] = "object",
100 };
101 local defaultIcon = typeToIcon[objective.type];
102 local iconMeta = {
103 ["defaultIcon"] = defaultIcon
104 };
105 Questie:RecursiveCreateNotes(c, z, questHash, objective.path, iconMeta, objectiveid);
106 end
107 end
108 else
109 --Questie:debug_Print("Notes:AddQuestToMap --> Display Finished Quest Icon: [Hash: "..questHash.."]");
110 local addedNote = false
111 local questInfo = QuestieHashMap[Quest.questHash];
112 if questInfo ~= nil then
113 local typeFunctions = {
114 ['monster'] = GetMonsterLocations,
115 ['object'] = GetObjectLocations,
116 ['unknown'] = function() return nil; end
117 };
118 local typeFunction = typeFunctions[questInfo.finishedType];
119 local finishPath = typeFunction(questInfo.finishedBy);
120 if finishPath == nil or (not next(finishPath)) then
121 finishPath = typeFunction(QuestieFinishers[Quest.name]);
122 end
123 if(finishPath) then
124 local locations = Questie:RecursiveGetPathLocations(finishPath);
125 if next(locations) then
126 for i, location in pairs(locations) do
127 local c, z, x, y = location[1], location[2], location[3], location[4];
128 Questie:AddNoteToMap(c, z, x, y, "complete", questHash, 0);
129 addedNote = true
130 end
131 end
132 end
133 end
134 if addedNote == false then
135 Questie:debug_Print("AddQuestToMap: ERROR Quest broken! ", Quest["name"], questHash, "report on github!")
136 end
137 end
138 --Cache code
139 ques["objectives"] = objectives;
140 QuestieHandledQuests[questHash] = ques;
141 if (redraw) then
142 Questie:debug_Print("Notes:AddQuestToMap: redraw VAR true --> Questie:RefreshQuestStatus();");
143 Questie:RefreshQuestNotes();
144 end
145 end
146 ---------------------------------------------------------------------------------------------------
147 -- Checks for a quest note in QuestieMapNotes
148 ---------------------------------------------------------------------------------------------------
149 function Questie:CheckQuestNote(questHash)
150 for continent, zoneTable in pairs(QuestieMapNotes) do
151 for index, zone in pairs(zoneTable) do
152 for i, note in pairs(zone) do
153 if (note.questHash == questHash) then
154 return true
155 end
156 end
157 end
158 end
159 return false
160 end
161 ---------------------------------------------------------------------------------------------------
162 -- Updates quest notes on map
163 ---------------------------------------------------------------------------------------------------
164 function Questie:UpdateQuestNotes(questHash, redraw)
165 if not QuestieHandledQuests[questHash] then
166 --Questie:debug_Print("UpdateQuestNotes: ERROR! Tried updating a quest not handled. Hash: ", questHash);
167 return;
168 end
169 local prevQuestLogSelection = QGet_QuestLogSelection()
170 local QuestLogID = Questie:GetQuestIdFromHash(questHash);
171 QSelect_QuestLogEntry(QuestLogID);
172 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(QuestLogID);
173 local count = QGet_NumQuestLeaderBoards();
174 local questText, objectiveText = QGet_QuestLogQuestText();
175 for k, noteInfo in pairs(QuestieHandledQuests[questHash]["noteHandles"]) do
176 for id, note in pairs(QuestieMapNotes[noteInfo.c][noteInfo.z]) do
177 if(note.questHash == questHash) then
178 local desc, typ, done = QGet_QuestLogLeaderBoard(note.objectiveid);
179 --Questie:debug_Print("UpdateQuestNotes: Desc: "..tostring(desc).." Type: "..tostring(typ).." Done: "..tostring(done));
180 end
181 end
182 end
183 QSelect_QuestLogEntry(prevQuestLogSelection)
184 if(redraw) then
185 Questie:debug_Print("Notes:UpdateQuestNotes: redraw VAR true --> Questie:RefreshQuestStatus();");
186 Questie:RefreshQuestNotes();
187 end
188 end
189 ---------------------------------------------------------------------------------------------------
190 -- Remove quest note from map
191 ---------------------------------------------------------------------------------------------------
192 function Questie:RemoveQuestFromMap(questHash, redraw)
193 local removed = false;
194 for continent, zoneTable in pairs(QuestieMapNotes) do
195 for index, zone in pairs(zoneTable) do
196 for i, note in pairs(zone) do
197 if(note.questHash == questHash) then
198 QuestieMapNotes[continent][index][i] = nil;
199 removed = true;
200 end
201 end
202 end
203 end
204 if(redraw) then
205 Questie:debug_Print("Notes:RemoveQuestFromMap: redraw VAR true --> Questie:RefreshQuestStatus();");
206 Questie:RefreshQuestNotes();
207 end
208 if(QuestieHandledQuests[questHash]) then
209 QuestieHandledQuests[questHash] = nil;
210 end
211 end
212 ---------------------------------------------------------------------------------------------------
213 function Questie:GetMapInfoFromID(id)
214 return QuestieZoneIDLookup[id];
215 end
216 ---------------------------------------------------------------------------------------------------
217 -- Add quest note to map
218 ---------------------------------------------------------------------------------------------------
219 function Questie:AddNoteToMap(continent, zoneid, posx, posy, type, questHash, objectiveid, path)
220 if (not type == "complete") then
221 return;
222 end
223 if(QuestieMapNotes[continent] == nil) then
224 QuestieMapNotes[continent] = {};
225 end
226 if(QuestieMapNotes[continent][zoneid] == nil) then
227 QuestieMapNotes[continent][zoneid] = {};
228 end
229 Note = {};
230 Note.x = posx;
231 Note.y = posy;
232 Note.zoneid = zoneid;
233 Note.continent = continent;
234 Note.icontype = type;
235 Note.questHash = questHash;
236 Note.objectiveid = objectiveid;
237 Note.path = path
238 table.insert(QuestieMapNotes[continent][zoneid], Note);
239 end
240 ---------------------------------------------------------------------------------------------------
241 -- Add available quest note to map
242 ---------------------------------------------------------------------------------------------------
243 function Questie:AddAvailableNoteToMap(continent, zoneid, posx, posy, type, questHash, objectiveid, path)
244 --This is to set up the variables
245 if(QuestieAvailableMapNotes[continent] == nil) then
246 QuestieAvailableMapNotes[continent] = {};
247 end
248 if(QuestieAvailableMapNotes[continent][zoneid] == nil) then
249 QuestieAvailableMapNotes[continent][zoneid] = {};
250 end
251 --Sets values that i want to use for the notes THIS IS WIP MORE INFO MAY BE NEDED BOTH IN PARAMETERS AND NOTES!!!
252 Note = {};
253 Note.x = posx;
254 Note.y = posy;
255 Note.zoneid = zoneid;
256 Note.continent = continent;
257 Note.icontype = type;
258 Note.questHash = questHash;
259 Note.objectiveid = objectiveid;
260 Note.path = path
261 --Inserts it into the right zone and continent for later use.
262 table.insert(QuestieAvailableMapNotes[continent][zoneid], Note);
263 end
264 ---------------------------------------------------------------------------------------------------
265 -- Gets a blank frame either from Pool or creates a new one!
266 ---------------------------------------------------------------------------------------------------
267 function Questie:GetBlankNoteFrame(frame)
268 if(table.getn(FramePool)==0) then
269 Questie:CreateBlankFrameNote(frame);
270 end
271 f = FramePool[1];
272 table.remove(FramePool, 1);
273 return f;
274 end
275 ---------------------------------------------------------------------------------------------------
276 -- Hook World Map Events
277 ---------------------------------------------------------------------------------------------------
278 function QuestieNotes:SetAllNoteFramesAlpha()
279 for i,v in ipairs({WorldMapFrame:GetChildren()}) do
280 if v:GetName() and string.find(v:GetName(), "^QuestieNoteFrame") then
281 v:SetAlpha(1)
282 end
283 end
284 end
285  
286 function QuestieNotes:WorldMapFrame_Show(this)
287 self.hooks[this].Show(this)
288 QuestieNotes:SetAllNoteFramesAlpha()
289 end
290  
291 function QuestieNotes:WorldMapFrame_SetAlpha(this, alpha)
292 self.hooks[this].SetAlpha(this, alpha)
293 QuestieNotes:SetAllNoteFramesAlpha()
294 end
295 ---------------------------------------------------------------------------------------------------
296 -- Hook Tooltip
297 ---------------------------------------------------------------------------------------------------
298 function Questie:hookTooltip()
299 local f = GameTooltip:GetScript("OnShow");
300 --Proper tooltip hooking!
301 if not f then
302 GameTooltip:SetScript("OnShow", function(self)
303 Questie:Tooltip(self, true);
304 end)
305 end
306 local Blizz_GameTooltip_Show = GameTooltip.Show;
307 GameTooltip.Show = function(self)
308 Questie:Tooltip(self);
309 Blizz_GameTooltip_Show(self);
310 end
311 local Bliz_GameTooltip_SetLootItem = GameTooltip.SetLootItem;
312 GameTooltip.SetLootItem = function(self, slot)
313 Bliz_GameTooltip_SetLootItem(self, slot);
314 Questie:Tooltip(self, true);
315 end
316 local index = self:GetID();
317 local Bliz_GameTooltip_SetQuestLogItem = GameTooltip.SetQuestLogItem;
318 GameTooltip.SetQuestLogItem = function(self, type, index)
319 local link = GetQuestLogItemLink(type, index);
320 if link then
321 Bliz_GameTooltip_SetQuestLogItem(self, type, index);
322 end
323 end
324 end
325 ---------------------------------------------------------------------------------------------------
326 -- Tooltip code for quest objects
327 ---------------------------------------------------------------------------------------------------
328 function Questie:hookTooltipLineCheck()
329 local oh = GameTooltip:GetScript("OnHide");
330 GameTooltip:SetScript("OnHide", function(self, arg)
331 if oh then
332 oh(self, arg);
333 end
334 __TT_LineCache = {};
335 end)
336 GameTooltip.AddLine_orig = GameTooltip.AddLine;
337 GameTooltip.AddLine = function(self, line, r, g, b, wrap, lineNumber)
338 GameTooltip:AddLine_orig(line, r, g, b, wrap);
339 if (line) then
340 if lineNumber == nil then lineNumber = 1; end
341 __TT_LineCache[lineNumber] = {};
342 __TT_LineCache[lineNumber][line] = true;
343 end
344 end
345 end
346 ---------------------------------------------------------------------------------------------------
347 function Questie:Tooltip(this, forceShow, bag, slot)
348 if (QuestieConfig.showToolTips == false) then return end
349  
350 -- Don't show detailed tooltip for questie minimap icons
351 local owner = GameTooltip.owner
352 if owner and owner.type == "MiniMapNote" then return end
353  
354 local monster = UnitName("mouseover")
355 local objective = GameTooltipTextLeft1:GetText()
356 local cacheKey = ""-- .. monster .. objective
357 local validKey = false
358 if(monster) then
359 cacheKey = cacheKey .. monster
360 validKey = true
361 end
362 if(objective) then
363 cacheKey = cacheKey .. objective
364 validKey = true
365 end
366 if not validKey then
367 return
368 end
369 local reaction = UnitReaction("mouseover", "player")
370 local unitColorRGB = Questie:GetReactionColor(reaction)
371 local unitColor = "ff"..fRGBToHex(unitColorRGB.r, unitColorRGB.g, unitColorRGB.b)
372 if (Questie_TooltipCache[cacheKey] == nil) or (QUESTIE_LAST_UPDATE_FINISHED - Questie_TooltipCache[cacheKey]['updateTime']) > 0 then
373 -- Create or Update Tooltip Cache
374 Questie_TooltipCache[cacheKey] = {}
375 Questie_TooltipCache[cacheKey]['lines'] = {}
376 Questie_TooltipCache[cacheKey]['lineCount'] = 1
377 Questie_TooltipCache[cacheKey]['updateTime'] = GetTime()
378 local prevQuestLogSelection = QGet_QuestLogSelection()
379 for questHash, quest in pairs(QuestieHandledQuests) do
380 local QuestLogID = Questie:GetQuestIdFromHash(questHash)
381 QSelect_QuestLogEntry(QuestLogID)
382 local drawnQuestTitle = false
383 for objectiveid, objectiveInfo in pairs(quest.objectives) do
384 local highlightInfo = {
385 ["text"] = objective,
386 ["color"] = unitColor
387 }
388 local sourceNames = Questie:RecursiveGetSourceNamesFromRawPath(objectiveInfo.path)
389 if objectiveInfo.name == objective or sourceNames[objective] then
390 local lineIndex = Questie_TooltipCache[cacheKey]['lineCount']
391 if drawnQuestTitle == false then
392 local questInfo = QuestieHashMap[questHash]
393 local colorString = "|c" .. QuestieTracker:GetDifficultyColor(questInfo.questLevel)
394 local title = colorString
395 title = title .. "[" .. questInfo.questLevel .. "] "
396 title = title .. questInfo.name .. "|r"
397 Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
398 ['color'] = {1,1,1},
399 ['data'] = " "
400 }
401 lineIndex = lineIndex + 1
402 Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
403 ['color'] = {1,1,1},
404 ['data'] = title,
405 ['wrap'] = false
406 }
407 lineIndex = lineIndex + 1
408 drawnQuestTitle = true
409 end
410 local desc, type, done = QGet_QuestLogLeaderBoard(objectiveid)
411 if done then
412 Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
413 ['color'] = {0.2,1,0.3},
414 ['data'] = desc,
415 ['wrap'] = false
416 }
417 lineIndex = lineIndex + 1
418 Questie_TooltipCache[cacheKey]['lineCount'] = lineIndex
419 else
420 local objectivePath = deepcopy(objectiveInfo.path)
421 Questie:PostProcessIconPath(objectivePath)
422 local lines = Questie:GetTooltipLines(objectivePath, 1, highlightInfo)
423 desc = string.gsub(desc, objective, "|c"..unitColor..objective.."|r")
424 Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
425 ['color'] = {1,1,1},
426 ['data'] = desc,
427 ['wrap'] = false
428 }
429 lineIndex = lineIndex + 1
430 for i, line in pairs(lines) do
431 Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
432 ['color'] = {1,1,1},
433 ['data'] = line
434 }
435 lineIndex = lineIndex + 1
436 end
437 Questie_TooltipCache[cacheKey]['lineCount'] = lineIndex
438 end
439 end
440 end
441 end
442 QSelect_QuestLogEntry(prevQuestLogSelection)
443 end
444 for k, v in pairs(Questie_TooltipCache[cacheKey]['lines']) do
445 if (not __TT_LineCache[k]) or (not __TT_LineCache[k][v['data']]) then
446 local wrap = v['wrap']
447 if wrap == nil then wrap = true end
448 GameTooltip:AddLine(v['data'], v['color'][1], v['color'][2], v['color'][3], wrap, k)
449 end
450 end
451 if(QUESTIE_DEBUG_TOOLTIP) then
452 GameTooltip:AddLine("--Questie hook--")
453 end
454 if(forceShow) then
455 GameTooltip:Show()
456 end
457 GameTooltip.QuestieDone = true
458 Questie_LastTooltip = GetTime()
459 --Questie_TooltipCache = {}
460 mi = nil
461 end
462 ---------------------------------------------------------------------------------------------------
463 -- Tooltip code for quest starters and finishers
464 ---------------------------------------------------------------------------------------------------
465 function Questie:GetTooltipLines(path, indent, highlightInfo, lines)
466 if lines == nil then lines = {} end
467 local indentString = "";
468 for i=1,indent,1 do
469 indentString = indentString.." ";
470 end
471 if path["contained_id"] then path["contained"] = nil; end
472 for sourceType, sources in pairs(path) do
473 local prefix;
474 if sourceType == "drop" then
475 prefix = "Dropped by";
476 elseif sourceType == "rewardedby" then
477 prefix = "Awarded by";
478 elseif sourceType == "contained" then
479 prefix = "Contained in";
480 elseif sourceType == "contained_id" then
481 prefix = "Contained in";
482 elseif sourceType == "containedi" then
483 prefix = "Opened in";
484 elseif sourceType == "created" then
485 prefix = "Created by";
486 elseif sourceType == "openedby" then
487 prefix = "Opened by";
488 elseif sourceType == "transforms" then
489 prefix = "Used on";
490 elseif sourceType == "transformedby" then
491 prefix = "Created by";
492 end
493 if prefix then
494 for sourceName, sourcePath in pairs(sources) do
495 local splitNames = Questie:SplitString(sourceName, ", ");
496 local combinedNames = "";
497 local countDown = table.getn(splitNames);
498 for i, name in pairs(splitNames) do
499 if i <= 5 or (highlightInfo ~= nil and name == highlightInfo.text) then
500 if i > 1 then combinedNames = combinedNames..", "; end
501 if highlightInfo ~= nil and name == highlightInfo.text then
502 combinedNames = combinedNames.."|r|c"..highlightInfo.color..name.."|r|cFFa6a6a6";
503 else
504 combinedNames = combinedNames..name;
505 end
506 countDown = countDown - 1;
507 end
508 end
509 if countDown > 0 then
510 combinedNames = combinedNames.." and "..countDown.." more...";
511 end
512 table.insert(lines, indentString..prefix..": |cFFa6a6a6"..combinedNames.."|r");
513 Questie:GetTooltipLines(sourcePath, indent+1, highlightInfo, lines, sourceNames);
514 end
515 end
516 end
517 return lines
518 end
519 ---------------------------------------------------------------------------------------------------
520 function Questie:AddPathToTooltip(Tooltip, path, indent)
521 local lines = Questie:GetTooltipLines(path, indent);
522 for i, line in pairs(lines) do
523 Tooltip:AddLine(line,1,1,1,true);
524 end
525 end
526 ---------------------------------------------------------------------------------------------------
527 function Questie_Tooltip_OnEnter()
528 if(this.data.questHash) then
529 local Tooltip = GameTooltip;
530 if(this.type == "WorldMapNote") then
531 Tooltip = WorldMapTooltip;
532 else
533 Tooltip = GameTooltip;
534 end
535 Tooltip:SetOwner(this, this);
536 Tooltip.owner = this
537 local count = 0;
538 local canManualComplete = 0;
539 local orderedQuests = {};
540 for questHash, questMeta in pairs(this.quests) do
541 orderedQuests[questMeta['sortOrder']] = questMeta;
542 end
543 local prevQuestLogSelection = QGet_QuestLogSelection();
544 for i, questMeta in pairs(orderedQuests) do
545 local data = questMeta['quest'];
546 count = count + 1;
547 if (count > 1) then
548 Tooltip:AddLine(" ");
549 end
550 if(data.icontype ~= "available" and data.icontype ~= "availablesoon") then
551 local Quest = Questie:IsQuestFinished(data.questHash);
552 if not Quest then
553 local QuestLogID = Questie:GetQuestIdFromHash(data.questHash);
554 if QuestLogID then
555 QSelect_QuestLogEntry(QuestLogID);
556 local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(QuestLogID);
557 Tooltip:AddLine(q);
558 for objectiveid, objectivePath in pairs(questMeta['objectives']) do
559 local objectiveName;
560 if type(objectiveid) == "string" then
561 objectiveName = objectiveid;
562 else
563 local desc, typ, done = QGet_QuestLogLeaderBoard(objectiveid);
564 objectiveName = desc;
565 end
566 Tooltip:AddLine(objectiveName,1,1,1);
567 Questie:AddPathToTooltip(Tooltip, objectivePath, 1);
568 end
569 end
570 else
571 Tooltip:AddLine("["..QuestieHashMap[data.questHash].questLevel.."] "..Quest["name"].." |cFF33FF00(complete)|r");
572 Tooltip:AddLine("Finished by: |cFFa6a6a6"..QuestieHashMap[data.questHash].finishedBy.."|r",1,1,1);
573 end
574 else
575 questOb = nil;
576 local QuestName = tostring(QuestieHashMap[data.questHash].name);
577 if QuestName then
578 local index = 0;
579 for k,v in pairs(Questie:SanitisedQuestLookup(QuestName)) do
580 index = index + 1;
581 if (index == 1) and (v[2] == data.questHash) and (k ~= "") then
582 questOb = k;
583 elseif (index > 0) and(v[2] == data.questHash) and (k ~= "") then
584 questOb = k;
585 elseif (index == 1) and (v[2] ~= data.questHash) and (k ~= "") then
586 questOb = k;
587 end
588 end
589 end
590 local questLine = "["..QuestieHashMap[data.questHash].questLevel.."] "..QuestieHashMap[data.questHash].name;
591 if data.icontype == "available" then
592 questLine = questLine.." |cFF33FF00(available)|r";
593 elseif data.icontype == "availablesoon" then
594 questLine = questLine.." |cFFa6a6a6(not available)|r";
595 end
596 Tooltip:AddLine(questLine);
597 Tooltip:AddLine("Min Level: |cFFa6a6a6"..QuestieHashMap[data.questHash].level.."|r",1,1,1);
598 Tooltip:AddLine("Started by: |cFFa6a6a6"..Questie:RemoveUniqueSuffix(QuestieHashMap[data.questHash].startedBy).."|r",1,1,1);
599 Questie:AddPathToTooltip(Tooltip, questMeta['path'], 1);
600 if questOb ~= nil then
601 Tooltip:AddLine("Description: |cFFa6a6a6"..Questie:RemoveUniqueSuffix(questOb).."|r",1,1,1,true);
602 end
603 canManualComplete = 1;
604 end
605 end
606 QSelect_QuestLogEntry(prevQuestLogSelection);
607 if canManualComplete > 0 then
608 if count > 1 then
609 Tooltip:AddLine(" ");
610 end
611 Tooltip:AddLine("Shift+Click: |cFFa6a6a6Manually complete quest!|r",1,1,1);
612 end
613 if(NOTES_DEBUG and IsAltKeyDown()) then
614 Tooltip:AddLine("!DEBUG!", 1, 0, 0);
615 Tooltip:AddLine("QuestID: "..this.data.questHash, 1, 0, 0);
616 end
617 Tooltip:SetFrameStrata("TOOLTIP");
618 Tooltip:Show();
619 end
620 end
621 ---------------------------------------------------------------------------------------------------
622 -- Force a quest to be finished via the Minimap or Worldmap (Shift-Click icon - NO confirmation)
623 ---------------------------------------------------------------------------------------------------
624 function Questie_AvailableQuestClick()
625 if this.type == "WorldMapNote" then
626 local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
627 local newC, newZ = c, z;
628 if arg1 == "LeftButton" then
629 if c == 0 then
630 newC = this.data.continent;
631 else
632 newZ = this.data.zoneid;
633 end
634 end
635 if arg1 == "RightButton" or arg1 == "MiddleButton" then
636 if z == 0 then
637 newC = 0;
638 else
639 newZ = 0;
640 end
641 end
642 if newC ~= c or newZ ~= z then
643 SetMapZoom(newC, newZ);
644 return;
645 end
646 end
647 local Tooltip = GameTooltip;
648 if(this.type == "WorldMapNote") then
649 Tooltip = WorldMapTooltip;
650 else
651 Tooltip = GameTooltip;
652 end
653 if (QuestieConfig.arrowEnabled == true) and (arg1 == "LeftButton") and (not IsControlKeyDown()) and (not IsShiftKeyDown()) then
654 SetArrowFromIcon(this);
655 end
656 if ((this.data.icontype == "available" or this.data.icontype == "availablesoon" or this.data.icontype == "complete") and IsShiftKeyDown() and Tooltip ) then
657 local finishQuest = function(quest)
658 if (quest.icontype == "available" or quest.icontype == "availablesoon") then
659 local hash = quest.questHash;
660 local questName = "["..QuestieHashMap[hash].questLevel.."] "..QuestieHashMap[hash]['name'];
661 Questie:finishAndRecurse(hash);
662 DEFAULT_CHAT_FRAME:AddMessage("Completing quest |cFF00FF00\"" .. questName .. "\"|r ("..hash..") and parent quests.");
663 --Questie:debug_Print("Notes:Questie_AvailableQuestClick --> Refreshing QuestNPC Icons: [AddEvent:DRAWNOTES]");
664 Questie:AddEvent("DRAWNOTES", 0.1);
665 end
666 end
667 local count = 0;
668 local firstQuest;
669 for questHash, questMeta in pairs(this.quests) do
670 count = count + 1;
671 if not firstQuest then
672 firstQuest = questMeta['quest'];
673 end
674 end
675 if (count < 2) then
676 -- Finish first quest in list
677 finishQuest(firstQuest);
678 else
679 -- Open Dewdrop to select which quest to finish
680 local closeFunc = function()
681 Dewdrop:Close();
682 end
683 local registerDewdrop = function(frame, quests, k1, v1, k2, v2)
684 Dewdrop:Register(frame,
685 'children', function()
686 for questHash, questMeta in pairs(quests) do
687 local quest = questMeta.quest;
688 local hash = questHash;
689 local questName = "["..QuestieHashMap[hash].questLevel.."] "..QuestieHashMap[hash]['name']
690 local finishFunc = function(quest)
691 finishQuest(quest);
692 Dewdrop:Close();
693 end
694 Dewdrop:AddLine(
695 'text', questName,
696 'notClickable', quest.icontype ~= "available" and quest.icontype ~= "availablesoon",
697 'icon', QuestieIcons[quest.icontype].path,
698 'iconCoordLeft', 0,
699 'iconCoordRight', 1,
700 'iconCoordTop', 0,
701 'iconCoordBottom', 1,
702 'func', finishFunc,
703 'arg1', quest
704 );
705 end
706 Dewdrop:AddLine(
707 'text', "",
708 'notClickable', true
709 );
710 Dewdrop:AddLine(
711 'text', "Cancel",
712 'func', closeFunc
713 );
714 end,
715 'dontHook', true,
716 k1, v1,
717 k2, v2
718 );
719 Dewdrop:Open(frame);
720 Dewdrop:Unregister(frame);
721 end
722 if (IsAddOnLoaded("Cartographer")) or (IsAddOnLoaded("MetaMap")) or (QuestieConfig.resizeWorldmap == true) then
723 registerDewdrop(WorldMapFrame, this.quests, 'cursorX', true, 'cursorY', true);
724 elseif (not IsAddOnLoaded("Cartographer")) or (not IsAddOnLoaded("MetaMap")) and (QuestieConfig.resizeWorldmap == false) then
725 registerDewdrop(this, this.quests, 'point', "TOPLEFT", 'relativePoint', "BOTTOMRIGHT");
726 elseif (IsAddOnLoaded("Cartographer")) and (CartographerDB["disabledModules"]["Default"]["Look 'n' Feel"] == true) then
727 registerDewdrop(this, this.quests, 'point', "TOPLEFT", 'relativePoint', "BOTTOMRIGHT");
728 end
729 end
730 end
731 end
732 ---------------------------------------------------------------------------------------------------
733 -- Creates a blank frame for use within the map system
734 ---------------------------------------------------------------------------------------------------
735 function Questie:CreateBlankFrameNote(frame)
736 local f = CreateFrame("Button","QuestieNoteFrame"..CREATED_NOTE_FRAMES,frame);
737 local t = f:CreateTexture(nil,"BACKGROUND");
738 f.texture = t;
739 f:SetScript("OnEnter", Questie_Tooltip_OnEnter);
740 f:SetScript("OnLeave", function()
741 if(WorldMapTooltip) then
742 WorldMapTooltip:Hide();
743 end
744 if(GameTooltip) then
745 GameTooltip:Hide();
746 GameTooltip.owner = nil
747 end
748 end)
749 f:SetScript("OnClick", Questie_AvailableQuestClick);
750 f:RegisterForClicks("LeftButtonDown", "RightButtonDown", "MiddleButtonDown");
751 CREATED_NOTE_FRAMES = CREATED_NOTE_FRAMES+1;
752 table.insert(FramePool, f);
753 table.insert(AllFrames, f);
754 end
755 ---------------------------------------------------------------------------------------------------
756 function Questie:GetFrameNote(data, parentFrame, frameLevel, type, scale)
757 if(table.getn(FramePool)==0) then
758 Questie:CreateFrameNote(data, parentFrame, frameLevel, type, scale);
759 end
760 f = FramePool[1];
761 table.remove(FramePool, 1);
762 return f;
763 end
764 ---------------------------------------------------------------------------------------------------
765 function Questie:SetFrameNoteData(f, data, parentFrame, frameLevel, type, scale)
766 f.data = data;
767 f.quests = {};
768 Questie:AddFrameNoteData(f, data);
769 f:SetParent(parentFrame);
770 f:SetFrameLevel(frameLevel);
771 f:SetPoint("CENTER",0,0);
772 f.type = type;
773 f:SetWidth(16*scale);
774 f:SetHeight(16*scale);
775 f.texture:SetTexture(QuestieIcons[data.icontype].path);
776 f.texture:SetAllPoints(f);
777 end
778 ---------------------------------------------------------------------------------------------------
779 function Questie:AddFrameNoteData(icon, data)
780 if icon then
781 if (icon.averageX == nil or icon.averageY == nil or icon.countForAverage == nil) then
782 icon.averageX = 0;
783 icon.averageY = 0;
784 icon.countForAverage = 0;
785 end
786 local numQuests = 0;
787 for k, v in pairs(icon.quests) do
788 numQuests = numQuests + 1;
789 end
790 local newAverageX = (icon.averageX * icon.countForAverage + data.x) / (icon.countForAverage + 1);
791 local newAverageY = (icon.averageY * icon.countForAverage + data.y) / (icon.countForAverage + 1);
792 icon.averageX = newAverageX;
793 icon.averageY = newAverageY;
794 icon.countForAverage = icon.countForAverage + 1;
795 if icon.quests[data.questHash] then
796 -- Add cumulative quest data
797 if icon.quests[data.questHash]['objectives'][data.objectiveid] == nil then
798 icon.quests[data.questHash]['objectives'][data.objectiveid] = {};
799 end
800 if data.path then
801 Questie:JoinPathTables(icon.quests[data.questHash]['path'], data.path);
802 end
803 if data.objectiveid and data.path then
804 Questie:JoinPathTables(icon.quests[data.questHash]['objectives'][data.objectiveid], data.path);
805 end
806 else
807 icon.quests[data.questHash] = {};
808 icon.quests[data.questHash]['quest'] = data;
809 icon.quests[data.questHash]['sortOrder'] = numQuests + 1;
810 icon.quests[data.questHash]['objectives'] = {};
811 icon.quests[data.questHash]['path'] = {};
812 if data.objectiveid then
813 icon.quests[data.questHash]['objectives'][data.objectiveid] = {};
814 if data.path then
815 icon.quests[data.questHash]['objectives'][data.objectiveid] = deepcopy(data.path);
816 end
817 end
818 if data.path then
819 icon.quests[data.questHash]['path'] = deepcopy(data.path);
820 end
821 end
822 end
823 end
824 ---------------------------------------------------------------------------------------------------
825 function Questie:JoinPathTables(path1, path2)
826 for k, v in pairs(path2) do
827 if path1[k] then
828 --Questie:debug_Print("Joining values for "..k)
829 Questie:JoinPathTables(path1[k], path2[k]);
830 else
831 --Questie:debug_Print("Setting value for "..k)
832 path1[k] = path2[k];
833 end
834 end
835 end
836 ---------------------------------------------------------------------------------------------------
837 function Questie:PathsAreIdentical(path1, path2)
838 if not next(path1) and not next(path2) then
839 return true;
840 end
841 for sourceType1, sources1 in pairs(path1) do
842 for sourceType2, sources2 in pairs(path2) do
843 if path1[sourceType2] == nil or path2[sourceType1] == nil then
844 return false;
845 end
846 end
847 for sourceName, sourcePath in pairs(path1[sourceType1]) do
848 for otherSourceName, otherSourcePath in pairs(path2[sourceType1]) do
849 if path1[sourceType1][otherSourceName] == nil or path2[sourceType1][sourceName] == nil then
850 return false;
851 end
852 end
853 end
854 end
855 return true;
856 end
857 ---------------------------------------------------------------------------------------------------
858 function Questie:PostProcessIconPath(path)
859 if path["locations"] then path["locations"] = nil; end
860 if path["name"] then path["name"] = nil; end
861 for sourceType, sources in pairs(path) do
862 for sourceName, sourcePath in pairs(sources) do
863 Questie:PostProcessIconPath(sourcePath);
864 end
865 local newSources = {};
866 for sourceName, sourcePath in pairs(sources) do
867 for otherSourceName, otherSourcePath in pairs(sources) do
868 if sourceName ~= otherSourceName and (newSources[sourceName] == nil or newSources[otherSourceName] == nil) then
869 if Questie:PathsAreIdentical(sourcePath, otherSourcePath) then
870 local newSource = newSources[sourceName];
871 if newSource == nil then
872 newSource = newSources[otherSourceName];
873 end
874 if newSource ~= nil then
875 newSource.name = newSource.name..", "..otherSourceName;
876 table.insert(newSource.names, otherSourceName);
877 else
878 newSource = {
879 ['name'] = sourceName..", "..otherSourceName,
880 ['names'] = {sourceName, otherSourceName},
881 ['sourcePath'] = sourcePath
882 };
883 end
884 for i, name in ipairs(newSource.names) do
885 newSources[name] = newSource;
886 end
887 end
888 end
889 end
890 end
891 for sourceName, sourcePath in pairs(sources) do
892 if newSources[sourceName] == nil then
893 newSources[sourceName] = {
894 ['name'] = sourceName,
895 ['sourcePath'] = sourcePath,
896 ['names'] = {sourceName}
897 };
898 end
899 end
900 local processedSources = {};
901 for sourceName, data in pairs(newSources) do
902 processedSources[data.name] = data.sourcePath;
903 end
904 path[sourceType] = processedSources;
905 end
906 end
907 ---------------------------------------------------------------------------------------------------
908 function Questie:RecursiveGetSourceNamesFromRawPath(path, sourceNames)
909 if sourceNames == nil then sourceNames = {} end
910 for sourceType, sources in pairs(path) do
911 if sourceType ~= "locations" and sourceType ~= "name" then
912 for sourceName, sourcePath in pairs(sources) do
913 sourceNames[sourceName] = true
914 Questie:RecursiveGetSourceNamesFromRawPath(sourcePath, sourceNames)
915 end
916 end
917 end
918 return sourceNames
919 end
920 ---------------------------------------------------------------------------------------------------
921 function Questie:RecursiveFindAndCombineObjectiveName(pathToSearch, objectiveName, pathToAdd)
922 local found = false;
923 for sourceType, sources in pairs(pathToSearch) do
924 for sourceName, sourcePath in pairs(sources) do
925 if sourceName == objectiveName then
926 sources[sourceName] = pathToAdd;
927 found = true;
928 else
929 if Questie:RecursiveFindAndCombineObjectiveName(sourcePath, objectiveName, pathToAdd) then
930 found = true;
931 end
932 end
933 end
934 end
935 return found;
936 end
937 ---------------------------------------------------------------------------------------------------
938 function Questie:FindAndCombineObjectiveName(objectives, objectiveName, pathToAdd)
939 for objectiveid, objectivePath in pairs(objectives) do
940 if type(objectiveid) ~= "string" then
941 if Questie:RecursiveFindAndCombineObjectiveName(objectivePath, objectiveName, pathToAdd) then
942 objectives[objectiveName] = nil;
943 end
944 end
945 end
946 end
947 ---------------------------------------------------------------------------------------------------
948 function Questie:PostProcessIconPaths(icon)
949 for questHash, questMeta in pairs(icon.quests) do
950 Questie:PostProcessIconPath(questMeta.path);
951 for objectiveid, objectivePath in pairs(questMeta.objectives) do
952 if type(objectiveid) == "string" then
953 Questie:FindAndCombineObjectiveName(questMeta.objectives, objectiveid, objectivePath);
954 end
955 Questie:PostProcessIconPath(objectivePath);
956 end
957 end
958 end
959 ---------------------------------------------------------------------------------------------------
960 -- Updates notes for current zone only
961 ---------------------------------------------------------------------------------------------------
962 function Questie:NOTES_ON_UPDATE(elapsed)
963 if GameLoadingComplete == false then return; end
964 local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
965 if(c ~= LastContinent or LastZone ~= z) then
966 --Questie:debug_Print("Notes:NOTES_ON_UPDATE: [AddEvent:DRAWNOTES]");
967 Questie:SetAvailableQuests();
968 Questie:RedrawNotes();
969 LastContinent = c;
970 LastZone = z;
971 end
972 if(WorldMapFrame:IsVisible() and UIOpen == false) then
973 --Questie:debug_Print("NOTES_ON_UPDATE: Created Frames: "..CREATED_NOTE_FRAMES, "Used Frames: "..table.getn(QuestieUsedNoteFrames), "Free Frames: "..table.getn(FramePool));
974 UIOpen = true;
975 elseif(WorldMapFrame:IsVisible() == nil and UIOpen == true) then
976 UIOpen = false;
977 end
978 end
979 ---------------------------------------------------------------------------------------------------
980 -- Inital pool size (Not tested how much you can do before it lags like shit, from experiance 11
981 -- is good)
982 ---------------------------------------------------------------------------------------------------
983 function Questie:NOTES_LOADED()
984 --Questie:debug_Print("NOTES_LOADED: Loading QuestieNotes");
985 if(table.getn(FramePool) < 10) then
986 for i = 1, INIT_POOL_SIZE do
987 Questie:CreateBlankFrameNote();
988 end
989 end
990 --Questie:debug_Print("NOTES_LOADED: Done Loading QuestieNotes");
991 end
992 ---------------------------------------------------------------------------------------------------
993 function Questie:RecursiveGetPathLocations(path, locations)
994 if locations == nil then locations = {}; end
995 for sourceType, sources in pairs(path) do
996 if sourceType == "locations" and next(sources) then
997 for i, location in pairs(sources) do
998 table.insert(locations, location);
999 end
1000 elseif sourceType == "drop" or sourceType == "rewardedby" or sourceType == "contained" or sourceType == "contained_id" or sourceType == "created" or sourceType == "containedi" or sourceType == "transforms" or sourceType == "transformedby" then
1001 for sourceName, sourcePath in pairs(sources) do
1002 Questie:RecursiveGetPathLocations(sourcePath, locations);
1003 end
1004 end
1005 end
1006 return locations;
1007 end
1008 ---------------------------------------------------------------------------------------------------
1009 function Questie:RecursiveCreateNotes(c, z, v, locationMeta, iconMeta, objectiveid, path, pathKeys)
1010 if path == nil then path = {}; end
1011 if pathKeys == nil then pathKeys = {}; end
1012 for sourceType, sources in pairs(locationMeta) do
1013 if sourceType == "locations" and next(sources) then
1014 for specialSource, b in pairs(specialSources) do
1015 if locationMeta[specialSource] ~= nil and next(locationMeta[specialSource]) then
1016 local pathToAppend = path;
1017 for i, pathKey in pairs(pathKeys) do
1018 pathToAppend = pathToAppend[pathKey];
1019 end
1020 pathToAppend[specialSource] = {};
1021 for sourceName, sourcePath in pairs(locationMeta[specialSource]) do
1022 pathToAppend[specialSource][sourceName] = {};
1023 end
1024 end
1025 end
1026 for i, location in pairs(sources) do
1027 local MapInfo = QuestieZoneIDLookup[location[1]];
1028 if MapInfo ~= nil then
1029 c = MapInfo[4];
1030 z = MapInfo[5];
1031 local icontype = iconMeta.selectedIcon;
1032 if icontype == nil then icontype = iconMeta.defaultIcon; end
1033 if icontype == "available" or icontype == "availablesoon" then
1034 Questie:AddAvailableNoteToMap(location[1],location[2],location[3],location[4],icontype,v,-1,deepcopy(path));
1035 else
1036 Questie:AddNoteToMap(location[1],location[2],location[3],location[4],icontype,v,objectiveid,deepcopy(path));
1037 end
1038 end
1039 end
1040 elseif sourceType == "drop" or sourceType == "rewardedby" or sourceType == "contained" or sourceType == "contained_id" or sourceType == "created" or sourceType == "containedi" or sourceType == "openedby" or sourceType == "transforms" or sourceType == "transformedby" then
1041 for sourceName, sourceLocationMeta in pairs(sources) do
1042 local newPath = deepcopy(path);
1043 local editPath = newPath;
1044 for i, pathKey in pairs(pathKeys) do
1045 editPath = editPath[pathKey];
1046 end
1047 editPath[sourceType] = {};
1048 editPath[sourceType][sourceName] = {};
1049 local newPathKeys = deepcopy(pathKeys);
1050 table.insert(newPathKeys, sourceType);
1051 table.insert(newPathKeys, sourceName);
1052 local newIconMeta = deepcopy(iconMeta);
1053 if newIconMeta.selectedIcon == nil then
1054 local typeToIcon = {
1055 ["drop"] = "loot",
1056 ["rewardedby"] = "slay",
1057 ["contained"] = "object",
1058 ["contained_id"] = "object",
1059 ["created"] = "event",
1060 ["containedi"] = "object",
1061 ["openedby"] = "object",
1062 ["transforms"] = "event",
1063 ["transformedby"] = "loot",
1064 };
1065 newIconMeta.selectedIcon = typeToIcon[sourceType];
1066 end
1067 local newObjectiveId = objectiveid;
1068 if specialSources[sourceType] then
1069 newPath = {};
1070 newPathKeys = {};
1071 newObjectiveId = sourceName;
1072 newIconMeta.selectedIcon = nil;
1073 end
1074 Questie:RecursiveCreateNotes(c, z, v, sourceLocationMeta, newIconMeta, newObjectiveId, newPath, newPathKeys);
1075 end
1076 end
1077 end
1078 end
1079 ---------------------------------------------------------------------------------------------------
1080 -- Sets up all available quests
1081 ---------------------------------------------------------------------------------------------------
1082 function Questie:SetAvailableQuests(customLevel)
1083 QuestieAvailableMapNotes = {};
1084 local saqtime = GetTime();
1085 local level = customLevel or UnitLevel("player");
1086 local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
1087 local mapFileName = GetMapInfo();
1088 local quests = nil;
1089 local minLevel = 0;
1090 local maxLevel = 100;
1091 if QuestieConfig.minLevelFilter then
1092 minLevel = level - QuestieConfig.minShowLevel;
1093 end
1094 if QuestieConfig.maxLevelFilter then
1095 maxLevel = level + QuestieConfig.maxShowLevel;
1096 end
1097 quests = Questie:GetAvailableQuestHashes(mapFileName, minLevel, maxLevel);
1098 if quests then
1099 local count = 0;
1100 for k, v in pairs(quests) do
1101 count = count + 1;
1102 local icontype = "available";
1103 if QuestieHashMap[k].level > level then icontype = "availablesoon"; end
1104 Questie:RecursiveCreateNotes(c, z, k, v, {["selectedIcon"] = icontype});
1105 end
1106 --Questie:debug_Print("SetAvailableQuests: Adding "..count.." available quests took "..tostring((GetTime()- saqtime)*1000).."ms");
1107 saqtime = nil;
1108 end
1109 end
1110 ---------------------------------------------------------------------------------------------------
1111 -- Reason this exists is to be able to call both clearnotes and drawnotes without doing 2 function
1112 -- calls, and to be able to force a redraw
1113 ---------------------------------------------------------------------------------------------------
1114 function Questie:RedrawNotes()
1115 Questie:CLEAR_ALL_NOTES();
1116 Questie:DRAW_NOTES();
1117 end
1118 ---------------------------------------------------------------------------------------------------
1119 function Questie:Clear_Note(v)
1120 v:SetParent(nil);
1121 v:Hide();
1122 v:SetAlpha(1);
1123 v:SetFrameLevel(9);
1124 v:SetHighlightTexture(nil, "ADD");
1125 v.questHash = nil;
1126 v.objId = nil;
1127 v.data = nil;
1128 v.quests = nil;
1129 v.averageX = nil;
1130 v.averageY = nil;
1131 v.countForAverage = nil;
1132 table.insert(FramePool, v);
1133 end
1134 ---------------------------------------------------------------------------------------------------
1135 -- Clears the notes, goes through the usednoteframes and clears them. Then sets the
1136 -- QuestieUsedNotesFrame to new table;
1137 ---------------------------------------------------------------------------------------------------
1138 function Questie:CLEAR_ALL_NOTES()
1139 --Questie:debug_Print("CLEAR_ALL_NOTES");
1140 Astrolabe:RemoveAllMinimapIcons();
1141 clustersByFrame = nil;
1142 for k, v in pairs(QuestieUsedNoteFrames) do
1143 Questie:Clear_Note(v);
1144 end
1145 QuestieUsedNoteFrames = {};
1146 end
1147 ---------------------------------------------------------------------------------------------------
1148 -- Logic for clusters
1149 ---------------------------------------------------------------------------------------------------
1150 function Cluster.new(points)
1151 local self = setmetatable({}, Cluster);
1152 self.points = points;
1153 return self;
1154 end
1155 ---------------------------------------------------------------------------------------------------
1156 function Cluster:CountPoints()
1157 local count = 0;
1158 local counted = {};
1159 for i, q in pairs(self.points) do
1160 if not counted[q.questHash] then
1161 count = count + 1;
1162 counted[q.questHash] = true;
1163 end
1164 end
1165 return count;
1166 end
1167 ---------------------------------------------------------------------------------------------------
1168 function Cluster.CalculateDistance(x1, y1, x2, y2)
1169 local deltaX = x1 - x2;
1170 local deltaY = y1 - y2;
1171 return sqrt(deltaX*deltaX + deltaY*deltaY);
1172 end
1173 ---------------------------------------------------------------------------------------------------
1174 function Cluster.CalculateLinkageDistance(cluster1, cluster2)
1175 local total = 0;
1176 for i, pi in cluster1 do
1177 for j, pj in cluster2 do
1178 if pi.zoneid ~= pj.zoneid then return -1; end
1179 local distance = Cluster.CalculateDistance(pi.x, pi.y, pj.x, pj.y);
1180 total = total + distance;
1181 end
1182 end
1183 return total / (table.getn(cluster1) * table.getn(cluster2));
1184 end
1185 ---------------------------------------------------------------------------------------------------
1186 function Cluster:CalculateClusters(clusters, distanceThreshold, maxClusterSize)
1187 while table.getn(clusters) > 1 do
1188 local nearest1;
1189 local nearest2;
1190 local nearestDistance;
1191 for i, cluster in pairs(clusters) do
1192 for j, otherCluster in pairs(clusters) do
1193 if cluster ~= otherCluster then
1194 local distance = Cluster.CalculateLinkageDistance(cluster.points, otherCluster.points);
1195 if distance >= 0 and (distance == 0 or ((nearestDistance == nil or distance < nearestDistance) and (cluster:CountPoints() + otherCluster:CountPoints() <= maxClusterSize))) then
1196 nearestDistance = distance;
1197 nearest1 = cluster;
1198 nearest2 = otherCluster;
1199 end
1200 end
1201 if nearestDistance == 0 then break; end
1202 end
1203 if nearestDistance == 0 then break; end
1204 end
1205 if nearestDistance == nil or nearestDistance > distanceThreshold then break; end
1206 local index1 = indexOf(clusters, nearest1);
1207 table.remove(clusters, index1);
1208 local index2 = indexOf(clusters, nearest2);
1209 table.remove(clusters, index2);
1210 local points = nearest1.points;
1211 for i, point in pairs(nearest2.points) do
1212 table.insert(points, point);
1213 end
1214 local newCluster = Cluster.new(points);
1215 table.insert(clusters, newCluster);
1216 end
1217 end
1218 ---------------------------------------------------------------------------------------------------
1219 -- splits the specified text into an array on the specified separator
1220 -- todo make a QuestieUtils.lua file for things like this
1221 ---------------------------------------------------------------------------------------------------
1222 function Questie:SplitString( text, separator, limit )
1223 local parts, position, length, last, jump, count = {}, 1, string.len( text ), nil, string.len( separator ), 0;
1224 while true do
1225 last = string.find( text, separator, position, true );
1226 if last and ( not limit or count < limit ) then
1227 table.insert( parts, string.sub( text, position, last - 1 ) );
1228 position, count = last + jump, count + 1;
1229 else
1230 table.insert( parts, string.sub( text, position ) );
1231 break;
1232 end
1233 end
1234 return parts;
1235 end
1236 ---------------------------------------------------------------------------------------------------
1237 function Questie:RoundCoordinate(coord, factor)
1238 if factor == nil then factor = 1; end
1239 return tonumber(string.format("%.2f", coord/factor)) * factor;
1240 end
1241 ---------------------------------------------------------------------------------------------------
1242 function Questie:GetReactionColor(reaction)
1243 if reaction == nil or reaction < 1 or reaction > 8 then reaction = 4; end
1244 return FACTION_BAR_COLORS[reaction];
1245 end
1246 ---------------------------------------------------------------------------------------------------
1247 function Questie:AddClusterFromNote(frame, identifier, v)
1248 if clustersByFrame == nil then
1249 clustersByFrame = {};
1250 end
1251 if clustersByFrame[frame] == nil then
1252 clustersByFrame[frame] = {};
1253 end
1254 if clustersByFrame[frame][identifier] == nil then
1255 clustersByFrame[frame][identifier] = {};
1256 end
1257 if clustersByFrame[frame][identifier][v.continent] == nil then
1258 clustersByFrame[frame][identifier][v.continent] = {};
1259 end
1260 if clustersByFrame[frame][identifier][v.continent][v.zoneid] == nil then
1261 clustersByFrame[frame][identifier][v.continent][v.zoneid] = {};
1262 end
1263 local roundedX = v.x;
1264 local roundedY = v.y;
1265 if QuestieConfig.clusterQuests and frame == "WorldMapNote" and identifier == "Objectives" then
1266 roundedX = Questie:RoundCoordinate(v.x, 5);
1267 roundedY = Questie:RoundCoordinate(v.y, 5);
1268 end
1269 if clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX] == nil then
1270 clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX] = {};
1271 end
1272 if clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX][roundedY] == nil then
1273 local points = { v };
1274 local cluster = Cluster.new(points);
1275 clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX][roundedY] = cluster;
1276 else
1277 table.insert(clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX][roundedY].points, v);
1278 end
1279 end
1280 ---------------------------------------------------------------------------------------------------
1281 function Questie:GetClustersByFrame(frame, identifier)
1282 if clustersByFrame == nil then
1283 clustersByFrame = {};
1284 end
1285 if clustersByFrame[frame] == nil then
1286 clustersByFrame[frame] = {};
1287 end
1288 if clustersByFrame[frame][identifier] == nil then
1289 clustersByFrame[frame][identifier] = {};
1290 end
1291 local clusters = {};
1292 for c, v in pairs(clustersByFrame[frame][identifier]) do
1293 for z, v in pairs(clustersByFrame[frame][identifier][c]) do
1294 for x, v in pairs(clustersByFrame[frame][identifier][c][z]) do
1295 for y, v in pairs(clustersByFrame[frame][identifier][c][z][x]) do
1296 table.insert(clusters, clustersByFrame[frame][identifier][c][z][x][y]);
1297 end
1298 end
1299 end
1300 end
1301 return clusters;
1302 end
1303 ---------------------------------------------------------------------------------------------------
1304 -- Finds the index of an item in a table. Not sure if a function already exists somewhere.
1305 ---------------------------------------------------------------------------------------------------
1306 function indexOf(table, item)
1307 for k, v in pairs(table) do
1308 if v == item then return k; end
1309 end
1310 return nil;
1311 end
1312 ---------------------------------------------------------------------------------------------------
1313 -- Checks first if there are any notes for the current zone, then draws the desired icon
1314 ---------------------------------------------------------------------------------------------------
1315 function Questie:DRAW_NOTES()
1316 --Questie:debug_Print("DRAW_NOTES");
1317 local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
1318 if (not QuestieConfig.hideMinimapIcons) then
1319 -- Draw minimap objective markers
1320 if (QuestieMapNotes[c] and QuestieMapNotes[c][z]) then
1321 for k, v in pairs(QuestieMapNotes[c][z]) do
1322 --If an available quest isn't in the zone or we aren't tracking a quest on the QuestTracker or the user wants to hide all objectives then hide the objectives from the minimap
1323 local show = QuestieConfig.alwaysShowObjectives or ((MMLastX ~= 0) and (MMLastY ~= 0)) and (QuestieCachedQuests[v.questHash] ~= nil) and (QuestieCachedQuests[v.questHash]["tracked"] ~= false);
1324 if show then
1325 if (v.icontype == "complete") then
1326 Questie:AddClusterFromNote("MiniMapNote", "Quests", v);
1327 else
1328 if QuestieConfig.hideObjectives == false then
1329 Questie:AddClusterFromNote("MiniMapNote", "Objectives", v);
1330 end
1331 end
1332 end
1333 end
1334 end
1335 end
1336 -- Draw world map objective markers
1337 for k, Continent in pairs(QuestieMapNotes) do
1338 for zone, noteHeap in pairs(Continent) do
1339 for k, v in pairs(noteHeap) do
1340 if true then
1341 --If we aren't tracking a quest on the QuestTracker or the user wants to hide all objectives then hide the objectives from the worldmap
1342 if (((QuestieCachedQuests[v.questHash] ~= nil) and (QuestieCachedQuests[v.questHash]["tracked"] ~= false)) or (v.icontype == "complete")) and (QuestieConfig.alwaysShowObjectives == false) then
1343 if (v.icontype == "complete") then
1344 Questie:AddClusterFromNote("WorldMapNote", "Quests", v);
1345 else
1346 if QuestieConfig.hideObjectives == false then
1347 Questie:AddClusterFromNote("WorldMapNote", "Objectives", v);
1348 end
1349 end
1350 elseif (QuestieConfig.alwaysShowObjectives == true) then
1351 if (v.icontype == "complete") then
1352 Questie:AddClusterFromNote("WorldMapNote", "Quests", v);
1353 else
1354 if QuestieConfig.hideObjectives == false then
1355 Questie:AddClusterFromNote("WorldMapNote", "Objectives", v);
1356 end
1357 end
1358 end
1359 end
1360 end
1361 end
1362 end
1363 -- Draw available quest markers.
1364 if (QuestieAvailableMapNotes[c] and QuestieAvailableMapNotes[c][z]) then
1365 if (IsQuestieActive == true) then
1366 local con,zon,x,y = Astrolabe:GetCurrentPlayerPosition();
1367 for k, v in pairs(QuestieAvailableMapNotes[c][z]) do
1368 Questie:AddClusterFromNote("WorldMapNote", "Quests", v);
1369 if (not QuestieConfig.hideMinimapIcons) then
1370 Questie:AddClusterFromNote("MiniMapNote", "Quests", v);
1371 end
1372 end
1373 end
1374 end
1375 local minimapObjectiveClusters = Questie:GetClustersByFrame("MiniMapNote", "Objectives");
1376 local worldMapObjectiveClusters = Questie:GetClustersByFrame("WorldMapNote", "Objectives");
1377 local minimapClusters = Questie:GetClustersByFrame("MiniMapNote", "Quests");
1378 local worldMapClusters = Questie:GetClustersByFrame("WorldMapNote", "Quests");
1379 if QuestieConfig.clusterQuests then
1380 Cluster:CalculateClusters(worldMapClusters, 0.025, 5);
1381 end
1382 local scale = QUESTIE_NOTES_MAP_ICON_SCALE;
1383 if (z == 0 and c == 0) then--Both continents
1384 scale = QUESTIE_NOTES_WORLD_MAP_ICON_SCALE;
1385 elseif (z == 0) then--Single continent
1386 scale = QUESTIE_NOTES_CONTINENT_ICON_SCALE;
1387 end
1388 Questie:DrawClusters(worldMapObjectiveClusters, "WorldMapNote", scale, WorldMapFrame, WorldMapButton);
1389 Questie:DrawClusters(worldMapClusters, "WorldMapNote", scale, WorldMapFrame, WorldMapButton);
1390 Questie:DrawClusters(minimapObjectiveClusters, "MiniMapNote", QUESTIE_NOTES_MINIMAP_ICON_SCALE, Minimap);
1391 Questie:DrawClusters(minimapClusters, "MiniMapNote", QUESTIE_NOTES_MINIMAP_ICON_SCALE, Minimap);
1392 end
1393 ---------------------------------------------------------------------------------------------------
1394 function Questie:DrawClusters(clusters, frameName, scale, frame, button)
1395 local frameLevel = 9;
1396 if frameName == "MiniMapNote" then
1397 frameLevel = 7;
1398 end
1399 for i, cluster in pairs(clusters) do
1400 table.sort(cluster.points, function(a, b)
1401 if QuestieIcons[a.icontype].priority ~= QuestieIcons[b.icontype].priority then return QuestieIcons[a.icontype].priority < QuestieIcons[b.icontype].priority end
1402 if a.questHash == b.questHash then return tostring(a) < tostring(b) end
1403 local questA = QuestieHashMap[a.questHash]
1404 local questB = QuestieHashMap[b.questHash]
1405 if not questA or not questB then return questA ~= nil end
1406 if questA and questB then
1407 if questA.level ~= questB.level then return questA.level < questB.level end
1408 local questLevelA = GetNumberFromString(questA.questLevel)
1409 local questLevelB = GetNumberFromString(questB.questLevel)
1410 if questLevelA ~= questLevelB then return questLevelA < questLevelB end
1411 end
1412 return a.questHash < b.questHash
1413 end)
1414 local Icon = Questie:GetBlankNoteFrame(frame);
1415 for j, v in pairs(cluster.points) do
1416 if j == 1 then
1417 local finalFrameLevel = frameLevel;
1418 if v.icontype == "complete" then finalFrameLevel = finalFrameLevel + 1; end
1419 Questie:SetFrameNoteData(Icon, v, frame, finalFrameLevel, frameName, scale);
1420 else
1421 Questie:AddFrameNoteData(Icon, v);
1422 end
1423 end
1424 Questie:PostProcessIconPaths(Icon);
1425 if frameName == "MiniMapNote" then
1426 Icon:SetHighlightTexture(QuestieIcons[Icon.data.icontype].path, "ADD");
1427 Astrolabe:PlaceIconOnMinimap(Icon, Icon.data.continent, Icon.data.zoneid, Icon.averageX, Icon.averageY);
1428 table.insert(QuestieUsedNoteFrames, Icon);
1429 else
1430 Icon:Show();
1431 xx, yy = Astrolabe:PlaceIconOnWorldMap(button, Icon, Icon.data.continent, Icon.data.zoneid, Icon.averageX, Icon.averageY);
1432 if(xx and yy and xx > 0 and xx < 1 and yy > 0 and yy < 1) then
1433 table.insert(QuestieUsedNoteFrames, Icon);
1434 else
1435 Questie:Clear_Note(Icon);
1436 end
1437 end
1438 end
1439 end
1440 ---------------------------------------------------------------------------------------------------
1441 -- Debug print function
1442 ---------------------------------------------------------------------------------------------------
1443 function Questie:debug_Print(...)
1444 local debugWin = 0;
1445 local name, shown;
1446 for i=1, NUM_CHAT_WINDOWS do
1447 name,_,_,_,_,_,shown = GetChatWindowInfo(i);
1448 if (string.lower(name) == "questiedebug") then debugWin = i; break; end
1449 end
1450 if (debugWin == 0) then return; end
1451 local out = "";
1452 for i = 1, arg.n, 1 do
1453 if (i > 1) then out = out .. ", "; end
1454 local t = type(arg[i]);
1455 if (t == "string") then
1456 out = out .. '"'..arg[i]..'"';
1457 elseif (t == "number") then
1458 out = out .. arg[i];
1459 else
1460 out = out .. dump(arg[i]);
1461 end
1462 end
1463 getglobal("ChatFrame"..debugWin):AddMessage(out, 1.0, 1.0, 0.3);
1464 end
1465 ---------------------------------------------------------------------------------------------------
1466 -- Sets the icon type
1467 ---------------------------------------------------------------------------------------------------
1468 QuestieIcons = {
1469 ["complete"] = {
1470 text = "Complete",
1471 path = "Interface\\AddOns\\!Questie\\Icons\\complete",
1472 priority = 1
1473 },
1474 ["available"] = {
1475 text = "Complete",
1476 path = "Interface\\AddOns\\!Questie\\Icons\\available",
1477 priority = 2
1478 },
1479 ["availablesoon"] = {
1480 text = "Complete",
1481 path = "Interface\\AddOns\\!Questie\\Icons\\availablesoon",
1482 priority = 2
1483 },
1484 ["loot"] = {
1485 text = "Complete",
1486 path = "Interface\\AddOns\\!Questie\\Icons\\loot",
1487 priority = 3
1488 },
1489 ["item"] = {
1490 text = "Complete",
1491 path = "Interface\\AddOns\\!Questie\\Icons\\loot",
1492 priority = 3
1493 },
1494 ["event"] = {
1495 text = "Complete",
1496 path = "Interface\\AddOns\\!Questie\\Icons\\event",
1497 priority = 3
1498 },
1499 ["object"] = {
1500 text = "Complete",
1501 path = "Interface\\AddOns\\!Questie\\Icons\\object",
1502 priority = 3
1503 },
1504 ["slay"] = {
1505 text = "Complete",
1506 path = "Interface\\AddOns\\!Questie\\Icons\\slay",
1507 priority = 3
1508 }
1509 };