vanilla-wow-addons – Rev 1
?pathlinks?
---------------------------------------------------------------------------------------------------
-- Name: QuestieNotes
-- Description: Handles all the quest map notes
---------------------------------------------------------------------------------------------------
--///////////////////////////////////////////////////////////////////////////////////////////////--
---------------------------------------------------------------------------------------------------
-- Local Vars
---------------------------------------------------------------------------------------------------
local AllFrames = {};
local FramePool = {};
local Cluster = {};
local LastContinent = nil;
local LastZone = nil;
local Dewdrop = AceLibrary("Dewdrop-2.0");
local specialSources = { ["openedby"] = 1, };
local QGet_QuestLogTitle = GetQuestLogTitle;
local QGet_NumQuestLeaderBoards = GetNumQuestLeaderBoards;
local QSelect_QuestLogEntry = SelectQuestLogEntry;
local QGet_QuestLogLeaderBoard = GetQuestLogLeaderBoard;
local QGet_QuestLogQuestText = GetQuestLogQuestText;
local QGet_TitleText = GetTitleText;
local QGet_QuestLogSelection = GetQuestLogSelection;
---------------------------------------------------------------------------------------------------
-- Global Vars
---------------------------------------------------------------------------------------------------
QUESTIE_NOTES_MAP_ICON_SCALE = 1.2;
QUESTIE_NOTES_WORLD_MAP_ICON_SCALE = 0.75;
QUESTIE_NOTES_CONTINENT_ICON_SCALE = 1;
QUESTIE_NOTES_MINIMAP_ICON_SCALE = 1.0;
QuestieMapNotes = {};
QuestieUsedNoteFrames = {};
QuestieHandledQuests = {};
QuestieAvailableMapNotes = {};
QuestieCachedMonstersAndObjects = {};
Questie_LastTooltip = GetTime();
QUESTIE_DEBUG_TOOLTIP = nil;
Questie_TooltipCache = {};
CREATED_NOTE_FRAMES = 1;
INIT_POOL_SIZE = 11;
Cluster.__index = Cluster;
__TT_LineCache = {};
UIOpen = false;
QuestieNotes = AceLibrary("AceAddon-2.0"):new("AceHook-2.1")
---------------------------------------------------------------------------------------------------
--Setup Hooks
---------------------------------------------------------------------------------------------------
function QuestieNotes:OnInitialize()
self:Hook(WorldMapFrame, "Show", "WorldMapFrame_Show", true)
self:Hook(WorldMapFrame, "SetAlpha", "WorldMapFrame_SetAlpha", true)
end
---------------------------------------------------------------------------------------------------
-- Refreshes Quest Notes
---------------------------------------------------------------------------------------------------
function Questie:RefreshQuestNotes()
QUESTIE_UPDATE_EVENT = 1;
if (GetTime() - QUESTIE_LAST_SYNCLOG > 0.1) then
Questie:AddEvent("SYNCLOG", 0);
QUESTIE_LAST_SYNCLOG = GetTime();
else
QUESTIE_LAST_SYNCLOG = GetTime();
end
if (GetTime() - QUESTIE_LAST_DRAWNOTES > 0.1) then
Questie:AddEvent("DRAWNOTES", 0.2);
QUESTIE_LAST_DRAWNOTES = GetTime();
else
QUESTIE_LAST_DRAWNOTES = GetTime();
end
if (GetTime() - QUESTIE_LAST_TRACKER > 0.1) then
Questie:AddEvent("TRACKER", 0.4);
QUESTIE_LAST_TRACKER = GetTime();
else
QUESTIE_LAST_TRACKER = GetTime();
end
end
---------------------------------------------------------------------------------------------------
-- Adds quest notes to map
---------------------------------------------------------------------------------------------------
function Questie:AddQuestToMap(questHash, redraw)
if(IsQuestieActive == false) then return; end
if questHash == -1 then return; end
--Questie:debug_Print("Notes:AddQuestToMap --> Adding Quest to Map [Hash: "..questHash.."]");
local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
Questie:RemoveQuestFromMap(questHash);
local objectives = Questie:GetQuestObjectivePaths(questHash)
--Cache code
local ques = {};
ques["noteHandles"] = {};
UsedContinents = {};
UsedZones = {};
local Quest = Questie:IsQuestFinished(questHash);
if not (Quest) then
Questie:debug_Print("Notes:AddQuestToMap --> Display Objective Icons: [Hash: "..questHash.."]");
for objectiveid, objective in pairs(objectives) do
if not objective.done then
local typeToIcon = {
["item"] = "loot",
["event"] = "event",
["monster"] = "slay",
["object"] = "object",
};
local defaultIcon = typeToIcon[objective.type];
local iconMeta = {
["defaultIcon"] = defaultIcon
};
Questie:RecursiveCreateNotes(c, z, questHash, objective.path, iconMeta, objectiveid);
end
end
else
--Questie:debug_Print("Notes:AddQuestToMap --> Display Finished Quest Icon: [Hash: "..questHash.."]");
local addedNote = false
local questInfo = QuestieHashMap[Quest.questHash];
if questInfo ~= nil then
local typeFunctions = {
['monster'] = GetMonsterLocations,
['object'] = GetObjectLocations,
['unknown'] = function() return nil; end
};
local typeFunction = typeFunctions[questInfo.finishedType];
local finishPath = typeFunction(questInfo.finishedBy);
if finishPath == nil or (not next(finishPath)) then
finishPath = typeFunction(QuestieFinishers[Quest.name]);
end
if(finishPath) then
local locations = Questie:RecursiveGetPathLocations(finishPath);
if next(locations) then
for i, location in pairs(locations) do
local c, z, x, y = location[1], location[2], location[3], location[4];
Questie:AddNoteToMap(c, z, x, y, "complete", questHash, 0);
addedNote = true
end
end
end
end
if addedNote == false then
Questie:debug_Print("AddQuestToMap: ERROR Quest broken! ", Quest["name"], questHash, "report on github!")
end
end
--Cache code
ques["objectives"] = objectives;
QuestieHandledQuests[questHash] = ques;
if (redraw) then
Questie:debug_Print("Notes:AddQuestToMap: redraw VAR true --> Questie:RefreshQuestStatus();");
Questie:RefreshQuestNotes();
end
end
---------------------------------------------------------------------------------------------------
-- Checks for a quest note in QuestieMapNotes
---------------------------------------------------------------------------------------------------
function Questie:CheckQuestNote(questHash)
for continent, zoneTable in pairs(QuestieMapNotes) do
for index, zone in pairs(zoneTable) do
for i, note in pairs(zone) do
if (note.questHash == questHash) then
return true
end
end
end
end
return false
end
---------------------------------------------------------------------------------------------------
-- Updates quest notes on map
---------------------------------------------------------------------------------------------------
function Questie:UpdateQuestNotes(questHash, redraw)
if not QuestieHandledQuests[questHash] then
--Questie:debug_Print("UpdateQuestNotes: ERROR! Tried updating a quest not handled. Hash: ", questHash);
return;
end
local prevQuestLogSelection = QGet_QuestLogSelection()
local QuestLogID = Questie:GetQuestIdFromHash(questHash);
QSelect_QuestLogEntry(QuestLogID);
local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(QuestLogID);
local count = QGet_NumQuestLeaderBoards();
local questText, objectiveText = QGet_QuestLogQuestText();
for k, noteInfo in pairs(QuestieHandledQuests[questHash]["noteHandles"]) do
for id, note in pairs(QuestieMapNotes[noteInfo.c][noteInfo.z]) do
if(note.questHash == questHash) then
local desc, typ, done = QGet_QuestLogLeaderBoard(note.objectiveid);
--Questie:debug_Print("UpdateQuestNotes: Desc: "..tostring(desc).." Type: "..tostring(typ).." Done: "..tostring(done));
end
end
end
QSelect_QuestLogEntry(prevQuestLogSelection)
if(redraw) then
Questie:debug_Print("Notes:UpdateQuestNotes: redraw VAR true --> Questie:RefreshQuestStatus();");
Questie:RefreshQuestNotes();
end
end
---------------------------------------------------------------------------------------------------
-- Remove quest note from map
---------------------------------------------------------------------------------------------------
function Questie:RemoveQuestFromMap(questHash, redraw)
local removed = false;
for continent, zoneTable in pairs(QuestieMapNotes) do
for index, zone in pairs(zoneTable) do
for i, note in pairs(zone) do
if(note.questHash == questHash) then
QuestieMapNotes[continent][index][i] = nil;
removed = true;
end
end
end
end
if(redraw) then
Questie:debug_Print("Notes:RemoveQuestFromMap: redraw VAR true --> Questie:RefreshQuestStatus();");
Questie:RefreshQuestNotes();
end
if(QuestieHandledQuests[questHash]) then
QuestieHandledQuests[questHash] = nil;
end
end
---------------------------------------------------------------------------------------------------
function Questie:GetMapInfoFromID(id)
return QuestieZoneIDLookup[id];
end
---------------------------------------------------------------------------------------------------
-- Add quest note to map
---------------------------------------------------------------------------------------------------
function Questie:AddNoteToMap(continent, zoneid, posx, posy, type, questHash, objectiveid, path)
if (not type == "complete") then
return;
end
if(QuestieMapNotes[continent] == nil) then
QuestieMapNotes[continent] = {};
end
if(QuestieMapNotes[continent][zoneid] == nil) then
QuestieMapNotes[continent][zoneid] = {};
end
Note = {};
Note.x = posx;
Note.y = posy;
Note.zoneid = zoneid;
Note.continent = continent;
Note.icontype = type;
Note.questHash = questHash;
Note.objectiveid = objectiveid;
Note.path = path
table.insert(QuestieMapNotes[continent][zoneid], Note);
end
---------------------------------------------------------------------------------------------------
-- Add available quest note to map
---------------------------------------------------------------------------------------------------
function Questie:AddAvailableNoteToMap(continent, zoneid, posx, posy, type, questHash, objectiveid, path)
--This is to set up the variables
if(QuestieAvailableMapNotes[continent] == nil) then
QuestieAvailableMapNotes[continent] = {};
end
if(QuestieAvailableMapNotes[continent][zoneid] == nil) then
QuestieAvailableMapNotes[continent][zoneid] = {};
end
--Sets values that i want to use for the notes THIS IS WIP MORE INFO MAY BE NEDED BOTH IN PARAMETERS AND NOTES!!!
Note = {};
Note.x = posx;
Note.y = posy;
Note.zoneid = zoneid;
Note.continent = continent;
Note.icontype = type;
Note.questHash = questHash;
Note.objectiveid = objectiveid;
Note.path = path
--Inserts it into the right zone and continent for later use.
table.insert(QuestieAvailableMapNotes[continent][zoneid], Note);
end
---------------------------------------------------------------------------------------------------
-- Gets a blank frame either from Pool or creates a new one!
---------------------------------------------------------------------------------------------------
function Questie:GetBlankNoteFrame(frame)
if(table.getn(FramePool)==0) then
Questie:CreateBlankFrameNote(frame);
end
f = FramePool[1];
table.remove(FramePool, 1);
return f;
end
---------------------------------------------------------------------------------------------------
-- Hook World Map Events
---------------------------------------------------------------------------------------------------
function QuestieNotes:SetAllNoteFramesAlpha()
for i,v in ipairs({WorldMapFrame:GetChildren()}) do
if v:GetName() and string.find(v:GetName(), "^QuestieNoteFrame") then
v:SetAlpha(1)
end
end
end
function QuestieNotes:WorldMapFrame_Show(this)
self.hooks[this].Show(this)
QuestieNotes:SetAllNoteFramesAlpha()
end
function QuestieNotes:WorldMapFrame_SetAlpha(this, alpha)
self.hooks[this].SetAlpha(this, alpha)
QuestieNotes:SetAllNoteFramesAlpha()
end
---------------------------------------------------------------------------------------------------
-- Hook Tooltip
---------------------------------------------------------------------------------------------------
function Questie:hookTooltip()
local f = GameTooltip:GetScript("OnShow");
--Proper tooltip hooking!
if not f then
GameTooltip:SetScript("OnShow", function(self)
Questie:Tooltip(self, true);
end)
end
local Blizz_GameTooltip_Show = GameTooltip.Show;
GameTooltip.Show = function(self)
Questie:Tooltip(self);
Blizz_GameTooltip_Show(self);
end
local Bliz_GameTooltip_SetLootItem = GameTooltip.SetLootItem;
GameTooltip.SetLootItem = function(self, slot)
Bliz_GameTooltip_SetLootItem(self, slot);
Questie:Tooltip(self, true);
end
local index = self:GetID();
local Bliz_GameTooltip_SetQuestLogItem = GameTooltip.SetQuestLogItem;
GameTooltip.SetQuestLogItem = function(self, type, index)
local link = GetQuestLogItemLink(type, index);
if link then
Bliz_GameTooltip_SetQuestLogItem(self, type, index);
end
end
end
---------------------------------------------------------------------------------------------------
-- Tooltip code for quest objects
---------------------------------------------------------------------------------------------------
function Questie:hookTooltipLineCheck()
local oh = GameTooltip:GetScript("OnHide");
GameTooltip:SetScript("OnHide", function(self, arg)
if oh then
oh(self, arg);
end
__TT_LineCache = {};
end)
GameTooltip.AddLine_orig = GameTooltip.AddLine;
GameTooltip.AddLine = function(self, line, r, g, b, wrap, lineNumber)
GameTooltip:AddLine_orig(line, r, g, b, wrap);
if (line) then
if lineNumber == nil then lineNumber = 1; end
__TT_LineCache[lineNumber] = {};
__TT_LineCache[lineNumber][line] = true;
end
end
end
---------------------------------------------------------------------------------------------------
function Questie:Tooltip(this, forceShow, bag, slot)
if (QuestieConfig.showToolTips == false) then return end
-- Don't show detailed tooltip for questie minimap icons
local owner = GameTooltip.owner
if owner and owner.type == "MiniMapNote" then return end
local monster = UnitName("mouseover")
local objective = GameTooltipTextLeft1:GetText()
local cacheKey = ""-- .. monster .. objective
local validKey = false
if(monster) then
cacheKey = cacheKey .. monster
validKey = true
end
if(objective) then
cacheKey = cacheKey .. objective
validKey = true
end
if not validKey then
return
end
local reaction = UnitReaction("mouseover", "player")
local unitColorRGB = Questie:GetReactionColor(reaction)
local unitColor = "ff"..fRGBToHex(unitColorRGB.r, unitColorRGB.g, unitColorRGB.b)
if (Questie_TooltipCache[cacheKey] == nil) or (QUESTIE_LAST_UPDATE_FINISHED - Questie_TooltipCache[cacheKey]['updateTime']) > 0 then
-- Create or Update Tooltip Cache
Questie_TooltipCache[cacheKey] = {}
Questie_TooltipCache[cacheKey]['lines'] = {}
Questie_TooltipCache[cacheKey]['lineCount'] = 1
Questie_TooltipCache[cacheKey]['updateTime'] = GetTime()
local prevQuestLogSelection = QGet_QuestLogSelection()
for questHash, quest in pairs(QuestieHandledQuests) do
local QuestLogID = Questie:GetQuestIdFromHash(questHash)
QSelect_QuestLogEntry(QuestLogID)
local drawnQuestTitle = false
for objectiveid, objectiveInfo in pairs(quest.objectives) do
local highlightInfo = {
["text"] = objective,
["color"] = unitColor
}
local sourceNames = Questie:RecursiveGetSourceNamesFromRawPath(objectiveInfo.path)
if objectiveInfo.name == objective or sourceNames[objective] then
local lineIndex = Questie_TooltipCache[cacheKey]['lineCount']
if drawnQuestTitle == false then
local questInfo = QuestieHashMap[questHash]
local colorString = "|c" .. QuestieTracker:GetDifficultyColor(questInfo.questLevel)
local title = colorString
title = title .. "[" .. questInfo.questLevel .. "] "
title = title .. questInfo.name .. "|r"
Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
['color'] = {1,1,1},
['data'] = " "
}
lineIndex = lineIndex + 1
Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
['color'] = {1,1,1},
['data'] = title,
['wrap'] = false
}
lineIndex = lineIndex + 1
drawnQuestTitle = true
end
local desc, type, done = QGet_QuestLogLeaderBoard(objectiveid)
if done then
Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
['color'] = {0.2,1,0.3},
['data'] = desc,
['wrap'] = false
}
lineIndex = lineIndex + 1
Questie_TooltipCache[cacheKey]['lineCount'] = lineIndex
else
local objectivePath = deepcopy(objectiveInfo.path)
Questie:PostProcessIconPath(objectivePath)
local lines = Questie:GetTooltipLines(objectivePath, 1, highlightInfo)
desc = string.gsub(desc, objective, "|c"..unitColor..objective.."|r")
Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
['color'] = {1,1,1},
['data'] = desc,
['wrap'] = false
}
lineIndex = lineIndex + 1
for i, line in pairs(lines) do
Questie_TooltipCache[cacheKey]['lines'][lineIndex] = {
['color'] = {1,1,1},
['data'] = line
}
lineIndex = lineIndex + 1
end
Questie_TooltipCache[cacheKey]['lineCount'] = lineIndex
end
end
end
end
QSelect_QuestLogEntry(prevQuestLogSelection)
end
for k, v in pairs(Questie_TooltipCache[cacheKey]['lines']) do
if (not __TT_LineCache[k]) or (not __TT_LineCache[k][v['data']]) then
local wrap = v['wrap']
if wrap == nil then wrap = true end
GameTooltip:AddLine(v['data'], v['color'][1], v['color'][2], v['color'][3], wrap, k)
end
end
if(QUESTIE_DEBUG_TOOLTIP) then
GameTooltip:AddLine("--Questie hook--")
end
if(forceShow) then
GameTooltip:Show()
end
GameTooltip.QuestieDone = true
Questie_LastTooltip = GetTime()
--Questie_TooltipCache = {}
mi = nil
end
---------------------------------------------------------------------------------------------------
-- Tooltip code for quest starters and finishers
---------------------------------------------------------------------------------------------------
function Questie:GetTooltipLines(path, indent, highlightInfo, lines)
if lines == nil then lines = {} end
local indentString = "";
for i=1,indent,1 do
indentString = indentString.." ";
end
if path["contained_id"] then path["contained"] = nil; end
for sourceType, sources in pairs(path) do
local prefix;
if sourceType == "drop" then
prefix = "Dropped by";
elseif sourceType == "rewardedby" then
prefix = "Awarded by";
elseif sourceType == "contained" then
prefix = "Contained in";
elseif sourceType == "contained_id" then
prefix = "Contained in";
elseif sourceType == "containedi" then
prefix = "Opened in";
elseif sourceType == "created" then
prefix = "Created by";
elseif sourceType == "openedby" then
prefix = "Opened by";
elseif sourceType == "transforms" then
prefix = "Used on";
elseif sourceType == "transformedby" then
prefix = "Created by";
end
if prefix then
for sourceName, sourcePath in pairs(sources) do
local splitNames = Questie:SplitString(sourceName, ", ");
local combinedNames = "";
local countDown = table.getn(splitNames);
for i, name in pairs(splitNames) do
if i <= 5 or (highlightInfo ~= nil and name == highlightInfo.text) then
if i > 1 then combinedNames = combinedNames..", "; end
if highlightInfo ~= nil and name == highlightInfo.text then
combinedNames = combinedNames.."|r|c"..highlightInfo.color..name.."|r|cFFa6a6a6";
else
combinedNames = combinedNames..name;
end
countDown = countDown - 1;
end
end
if countDown > 0 then
combinedNames = combinedNames.." and "..countDown.." more...";
end
table.insert(lines, indentString..prefix..": |cFFa6a6a6"..combinedNames.."|r");
Questie:GetTooltipLines(sourcePath, indent+1, highlightInfo, lines, sourceNames);
end
end
end
return lines
end
---------------------------------------------------------------------------------------------------
function Questie:AddPathToTooltip(Tooltip, path, indent)
local lines = Questie:GetTooltipLines(path, indent);
for i, line in pairs(lines) do
Tooltip:AddLine(line,1,1,1,true);
end
end
---------------------------------------------------------------------------------------------------
function Questie_Tooltip_OnEnter()
if(this.data.questHash) then
local Tooltip = GameTooltip;
if(this.type == "WorldMapNote") then
Tooltip = WorldMapTooltip;
else
Tooltip = GameTooltip;
end
Tooltip:SetOwner(this, this);
Tooltip.owner = this
local count = 0;
local canManualComplete = 0;
local orderedQuests = {};
for questHash, questMeta in pairs(this.quests) do
orderedQuests[questMeta['sortOrder']] = questMeta;
end
local prevQuestLogSelection = QGet_QuestLogSelection();
for i, questMeta in pairs(orderedQuests) do
local data = questMeta['quest'];
count = count + 1;
if (count > 1) then
Tooltip:AddLine(" ");
end
if(data.icontype ~= "available" and data.icontype ~= "availablesoon") then
local Quest = Questie:IsQuestFinished(data.questHash);
if not Quest then
local QuestLogID = Questie:GetQuestIdFromHash(data.questHash);
if QuestLogID then
QSelect_QuestLogEntry(QuestLogID);
local q, level, questTag, isHeader, isCollapsed, isComplete = QGet_QuestLogTitle(QuestLogID);
Tooltip:AddLine(q);
for objectiveid, objectivePath in pairs(questMeta['objectives']) do
local objectiveName;
if type(objectiveid) == "string" then
objectiveName = objectiveid;
else
local desc, typ, done = QGet_QuestLogLeaderBoard(objectiveid);
objectiveName = desc;
end
Tooltip:AddLine(objectiveName,1,1,1);
Questie:AddPathToTooltip(Tooltip, objectivePath, 1);
end
end
else
Tooltip:AddLine("["..QuestieHashMap[data.questHash].questLevel.."] "..Quest["name"].." |cFF33FF00(complete)|r");
Tooltip:AddLine("Finished by: |cFFa6a6a6"..QuestieHashMap[data.questHash].finishedBy.."|r",1,1,1);
end
else
questOb = nil;
local QuestName = tostring(QuestieHashMap[data.questHash].name);
if QuestName then
local index = 0;
for k,v in pairs(Questie:SanitisedQuestLookup(QuestName)) do
index = index + 1;
if (index == 1) and (v[2] == data.questHash) and (k ~= "") then
questOb = k;
elseif (index > 0) and(v[2] == data.questHash) and (k ~= "") then
questOb = k;
elseif (index == 1) and (v[2] ~= data.questHash) and (k ~= "") then
questOb = k;
end
end
end
local questLine = "["..QuestieHashMap[data.questHash].questLevel.."] "..QuestieHashMap[data.questHash].name;
if data.icontype == "available" then
questLine = questLine.." |cFF33FF00(available)|r";
elseif data.icontype == "availablesoon" then
questLine = questLine.." |cFFa6a6a6(not available)|r";
end
Tooltip:AddLine(questLine);
Tooltip:AddLine("Min Level: |cFFa6a6a6"..QuestieHashMap[data.questHash].level.."|r",1,1,1);
Tooltip:AddLine("Started by: |cFFa6a6a6"..Questie:RemoveUniqueSuffix(QuestieHashMap[data.questHash].startedBy).."|r",1,1,1);
Questie:AddPathToTooltip(Tooltip, questMeta['path'], 1);
if questOb ~= nil then
Tooltip:AddLine("Description: |cFFa6a6a6"..Questie:RemoveUniqueSuffix(questOb).."|r",1,1,1,true);
end
canManualComplete = 1;
end
end
QSelect_QuestLogEntry(prevQuestLogSelection);
if canManualComplete > 0 then
if count > 1 then
Tooltip:AddLine(" ");
end
Tooltip:AddLine("Shift+Click: |cFFa6a6a6Manually complete quest!|r",1,1,1);
end
if(NOTES_DEBUG and IsAltKeyDown()) then
Tooltip:AddLine("!DEBUG!", 1, 0, 0);
Tooltip:AddLine("QuestID: "..this.data.questHash, 1, 0, 0);
end
Tooltip:SetFrameStrata("TOOLTIP");
Tooltip:Show();
end
end
---------------------------------------------------------------------------------------------------
-- Force a quest to be finished via the Minimap or Worldmap (Shift-Click icon - NO confirmation)
---------------------------------------------------------------------------------------------------
function Questie_AvailableQuestClick()
if this.type == "WorldMapNote" then
local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
local newC, newZ = c, z;
if arg1 == "LeftButton" then
if c == 0 then
newC = this.data.continent;
else
newZ = this.data.zoneid;
end
end
if arg1 == "RightButton" or arg1 == "MiddleButton" then
if z == 0 then
newC = 0;
else
newZ = 0;
end
end
if newC ~= c or newZ ~= z then
SetMapZoom(newC, newZ);
return;
end
end
local Tooltip = GameTooltip;
if(this.type == "WorldMapNote") then
Tooltip = WorldMapTooltip;
else
Tooltip = GameTooltip;
end
if (QuestieConfig.arrowEnabled == true) and (arg1 == "LeftButton") and (not IsControlKeyDown()) and (not IsShiftKeyDown()) then
SetArrowFromIcon(this);
end
if ((this.data.icontype == "available" or this.data.icontype == "availablesoon" or this.data.icontype == "complete") and IsShiftKeyDown() and Tooltip ) then
local finishQuest = function(quest)
if (quest.icontype == "available" or quest.icontype == "availablesoon") then
local hash = quest.questHash;
local questName = "["..QuestieHashMap[hash].questLevel.."] "..QuestieHashMap[hash]['name'];
Questie:finishAndRecurse(hash);
DEFAULT_CHAT_FRAME:AddMessage("Completing quest |cFF00FF00\"" .. questName .. "\"|r ("..hash..") and parent quests.");
--Questie:debug_Print("Notes:Questie_AvailableQuestClick --> Refreshing QuestNPC Icons: [AddEvent:DRAWNOTES]");
Questie:AddEvent("DRAWNOTES", 0.1);
end
end
local count = 0;
local firstQuest;
for questHash, questMeta in pairs(this.quests) do
count = count + 1;
if not firstQuest then
firstQuest = questMeta['quest'];
end
end
if (count < 2) then
-- Finish first quest in list
finishQuest(firstQuest);
else
-- Open Dewdrop to select which quest to finish
local closeFunc = function()
Dewdrop:Close();
end
local registerDewdrop = function(frame, quests, k1, v1, k2, v2)
Dewdrop:Register(frame,
'children', function()
for questHash, questMeta in pairs(quests) do
local quest = questMeta.quest;
local hash = questHash;
local questName = "["..QuestieHashMap[hash].questLevel.."] "..QuestieHashMap[hash]['name']
local finishFunc = function(quest)
finishQuest(quest);
Dewdrop:Close();
end
Dewdrop:AddLine(
'text', questName,
'notClickable', quest.icontype ~= "available" and quest.icontype ~= "availablesoon",
'icon', QuestieIcons[quest.icontype].path,
'iconCoordLeft', 0,
'iconCoordRight', 1,
'iconCoordTop', 0,
'iconCoordBottom', 1,
'func', finishFunc,
'arg1', quest
);
end
Dewdrop:AddLine(
'text', "",
'notClickable', true
);
Dewdrop:AddLine(
'text', "Cancel",
'func', closeFunc
);
end,
'dontHook', true,
k1, v1,
k2, v2
);
Dewdrop:Open(frame);
Dewdrop:Unregister(frame);
end
if (IsAddOnLoaded("Cartographer")) or (IsAddOnLoaded("MetaMap")) or (QuestieConfig.resizeWorldmap == true) then
registerDewdrop(WorldMapFrame, this.quests, 'cursorX', true, 'cursorY', true);
elseif (not IsAddOnLoaded("Cartographer")) or (not IsAddOnLoaded("MetaMap")) and (QuestieConfig.resizeWorldmap == false) then
registerDewdrop(this, this.quests, 'point', "TOPLEFT", 'relativePoint', "BOTTOMRIGHT");
elseif (IsAddOnLoaded("Cartographer")) and (CartographerDB["disabledModules"]["Default"]["Look 'n' Feel"] == true) then
registerDewdrop(this, this.quests, 'point', "TOPLEFT", 'relativePoint', "BOTTOMRIGHT");
end
end
end
end
---------------------------------------------------------------------------------------------------
-- Creates a blank frame for use within the map system
---------------------------------------------------------------------------------------------------
function Questie:CreateBlankFrameNote(frame)
local f = CreateFrame("Button","QuestieNoteFrame"..CREATED_NOTE_FRAMES,frame);
local t = f:CreateTexture(nil,"BACKGROUND");
f.texture = t;
f:SetScript("OnEnter", Questie_Tooltip_OnEnter);
f:SetScript("OnLeave", function()
if(WorldMapTooltip) then
WorldMapTooltip:Hide();
end
if(GameTooltip) then
GameTooltip:Hide();
GameTooltip.owner = nil
end
end)
f:SetScript("OnClick", Questie_AvailableQuestClick);
f:RegisterForClicks("LeftButtonDown", "RightButtonDown", "MiddleButtonDown");
CREATED_NOTE_FRAMES = CREATED_NOTE_FRAMES+1;
table.insert(FramePool, f);
table.insert(AllFrames, f);
end
---------------------------------------------------------------------------------------------------
function Questie:GetFrameNote(data, parentFrame, frameLevel, type, scale)
if(table.getn(FramePool)==0) then
Questie:CreateFrameNote(data, parentFrame, frameLevel, type, scale);
end
f = FramePool[1];
table.remove(FramePool, 1);
return f;
end
---------------------------------------------------------------------------------------------------
function Questie:SetFrameNoteData(f, data, parentFrame, frameLevel, type, scale)
f.data = data;
f.quests = {};
Questie:AddFrameNoteData(f, data);
f:SetParent(parentFrame);
f:SetFrameLevel(frameLevel);
f:SetPoint("CENTER",0,0);
f.type = type;
f:SetWidth(16*scale);
f:SetHeight(16*scale);
f.texture:SetTexture(QuestieIcons[data.icontype].path);
f.texture:SetAllPoints(f);
end
---------------------------------------------------------------------------------------------------
function Questie:AddFrameNoteData(icon, data)
if icon then
if (icon.averageX == nil or icon.averageY == nil or icon.countForAverage == nil) then
icon.averageX = 0;
icon.averageY = 0;
icon.countForAverage = 0;
end
local numQuests = 0;
for k, v in pairs(icon.quests) do
numQuests = numQuests + 1;
end
local newAverageX = (icon.averageX * icon.countForAverage + data.x) / (icon.countForAverage + 1);
local newAverageY = (icon.averageY * icon.countForAverage + data.y) / (icon.countForAverage + 1);
icon.averageX = newAverageX;
icon.averageY = newAverageY;
icon.countForAverage = icon.countForAverage + 1;
if icon.quests[data.questHash] then
-- Add cumulative quest data
if icon.quests[data.questHash]['objectives'][data.objectiveid] == nil then
icon.quests[data.questHash]['objectives'][data.objectiveid] = {};
end
if data.path then
Questie:JoinPathTables(icon.quests[data.questHash]['path'], data.path);
end
if data.objectiveid and data.path then
Questie:JoinPathTables(icon.quests[data.questHash]['objectives'][data.objectiveid], data.path);
end
else
icon.quests[data.questHash] = {};
icon.quests[data.questHash]['quest'] = data;
icon.quests[data.questHash]['sortOrder'] = numQuests + 1;
icon.quests[data.questHash]['objectives'] = {};
icon.quests[data.questHash]['path'] = {};
if data.objectiveid then
icon.quests[data.questHash]['objectives'][data.objectiveid] = {};
if data.path then
icon.quests[data.questHash]['objectives'][data.objectiveid] = deepcopy(data.path);
end
end
if data.path then
icon.quests[data.questHash]['path'] = deepcopy(data.path);
end
end
end
end
---------------------------------------------------------------------------------------------------
function Questie:JoinPathTables(path1, path2)
for k, v in pairs(path2) do
if path1[k] then
--Questie:debug_Print("Joining values for "..k)
Questie:JoinPathTables(path1[k], path2[k]);
else
--Questie:debug_Print("Setting value for "..k)
path1[k] = path2[k];
end
end
end
---------------------------------------------------------------------------------------------------
function Questie:PathsAreIdentical(path1, path2)
if not next(path1) and not next(path2) then
return true;
end
for sourceType1, sources1 in pairs(path1) do
for sourceType2, sources2 in pairs(path2) do
if path1[sourceType2] == nil or path2[sourceType1] == nil then
return false;
end
end
for sourceName, sourcePath in pairs(path1[sourceType1]) do
for otherSourceName, otherSourcePath in pairs(path2[sourceType1]) do
if path1[sourceType1][otherSourceName] == nil or path2[sourceType1][sourceName] == nil then
return false;
end
end
end
end
return true;
end
---------------------------------------------------------------------------------------------------
function Questie:PostProcessIconPath(path)
if path["locations"] then path["locations"] = nil; end
if path["name"] then path["name"] = nil; end
for sourceType, sources in pairs(path) do
for sourceName, sourcePath in pairs(sources) do
Questie:PostProcessIconPath(sourcePath);
end
local newSources = {};
for sourceName, sourcePath in pairs(sources) do
for otherSourceName, otherSourcePath in pairs(sources) do
if sourceName ~= otherSourceName and (newSources[sourceName] == nil or newSources[otherSourceName] == nil) then
if Questie:PathsAreIdentical(sourcePath, otherSourcePath) then
local newSource = newSources[sourceName];
if newSource == nil then
newSource = newSources[otherSourceName];
end
if newSource ~= nil then
newSource.name = newSource.name..", "..otherSourceName;
table.insert(newSource.names, otherSourceName);
else
newSource = {
['name'] = sourceName..", "..otherSourceName,
['names'] = {sourceName, otherSourceName},
['sourcePath'] = sourcePath
};
end
for i, name in ipairs(newSource.names) do
newSources[name] = newSource;
end
end
end
end
end
for sourceName, sourcePath in pairs(sources) do
if newSources[sourceName] == nil then
newSources[sourceName] = {
['name'] = sourceName,
['sourcePath'] = sourcePath,
['names'] = {sourceName}
};
end
end
local processedSources = {};
for sourceName, data in pairs(newSources) do
processedSources[data.name] = data.sourcePath;
end
path[sourceType] = processedSources;
end
end
---------------------------------------------------------------------------------------------------
function Questie:RecursiveGetSourceNamesFromRawPath(path, sourceNames)
if sourceNames == nil then sourceNames = {} end
for sourceType, sources in pairs(path) do
if sourceType ~= "locations" and sourceType ~= "name" then
for sourceName, sourcePath in pairs(sources) do
sourceNames[sourceName] = true
Questie:RecursiveGetSourceNamesFromRawPath(sourcePath, sourceNames)
end
end
end
return sourceNames
end
---------------------------------------------------------------------------------------------------
function Questie:RecursiveFindAndCombineObjectiveName(pathToSearch, objectiveName, pathToAdd)
local found = false;
for sourceType, sources in pairs(pathToSearch) do
for sourceName, sourcePath in pairs(sources) do
if sourceName == objectiveName then
sources[sourceName] = pathToAdd;
found = true;
else
if Questie:RecursiveFindAndCombineObjectiveName(sourcePath, objectiveName, pathToAdd) then
found = true;
end
end
end
end
return found;
end
---------------------------------------------------------------------------------------------------
function Questie:FindAndCombineObjectiveName(objectives, objectiveName, pathToAdd)
for objectiveid, objectivePath in pairs(objectives) do
if type(objectiveid) ~= "string" then
if Questie:RecursiveFindAndCombineObjectiveName(objectivePath, objectiveName, pathToAdd) then
objectives[objectiveName] = nil;
end
end
end
end
---------------------------------------------------------------------------------------------------
function Questie:PostProcessIconPaths(icon)
for questHash, questMeta in pairs(icon.quests) do
Questie:PostProcessIconPath(questMeta.path);
for objectiveid, objectivePath in pairs(questMeta.objectives) do
if type(objectiveid) == "string" then
Questie:FindAndCombineObjectiveName(questMeta.objectives, objectiveid, objectivePath);
end
Questie:PostProcessIconPath(objectivePath);
end
end
end
---------------------------------------------------------------------------------------------------
-- Updates notes for current zone only
---------------------------------------------------------------------------------------------------
function Questie:NOTES_ON_UPDATE(elapsed)
if GameLoadingComplete == false then return; end
local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
if(c ~= LastContinent or LastZone ~= z) then
--Questie:debug_Print("Notes:NOTES_ON_UPDATE: [AddEvent:DRAWNOTES]");
Questie:SetAvailableQuests();
Questie:RedrawNotes();
LastContinent = c;
LastZone = z;
end
if(WorldMapFrame:IsVisible() and UIOpen == false) then
--Questie:debug_Print("NOTES_ON_UPDATE: Created Frames: "..CREATED_NOTE_FRAMES, "Used Frames: "..table.getn(QuestieUsedNoteFrames), "Free Frames: "..table.getn(FramePool));
UIOpen = true;
elseif(WorldMapFrame:IsVisible() == nil and UIOpen == true) then
UIOpen = false;
end
end
---------------------------------------------------------------------------------------------------
-- Inital pool size (Not tested how much you can do before it lags like shit, from experiance 11
-- is good)
---------------------------------------------------------------------------------------------------
function Questie:NOTES_LOADED()
--Questie:debug_Print("NOTES_LOADED: Loading QuestieNotes");
if(table.getn(FramePool) < 10) then
for i = 1, INIT_POOL_SIZE do
Questie:CreateBlankFrameNote();
end
end
--Questie:debug_Print("NOTES_LOADED: Done Loading QuestieNotes");
end
---------------------------------------------------------------------------------------------------
function Questie:RecursiveGetPathLocations(path, locations)
if locations == nil then locations = {}; end
for sourceType, sources in pairs(path) do
if sourceType == "locations" and next(sources) then
for i, location in pairs(sources) do
table.insert(locations, location);
end
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
for sourceName, sourcePath in pairs(sources) do
Questie:RecursiveGetPathLocations(sourcePath, locations);
end
end
end
return locations;
end
---------------------------------------------------------------------------------------------------
function Questie:RecursiveCreateNotes(c, z, v, locationMeta, iconMeta, objectiveid, path, pathKeys)
if path == nil then path = {}; end
if pathKeys == nil then pathKeys = {}; end
for sourceType, sources in pairs(locationMeta) do
if sourceType == "locations" and next(sources) then
for specialSource, b in pairs(specialSources) do
if locationMeta[specialSource] ~= nil and next(locationMeta[specialSource]) then
local pathToAppend = path;
for i, pathKey in pairs(pathKeys) do
pathToAppend = pathToAppend[pathKey];
end
pathToAppend[specialSource] = {};
for sourceName, sourcePath in pairs(locationMeta[specialSource]) do
pathToAppend[specialSource][sourceName] = {};
end
end
end
for i, location in pairs(sources) do
local MapInfo = QuestieZoneIDLookup[location[1]];
if MapInfo ~= nil then
c = MapInfo[4];
z = MapInfo[5];
local icontype = iconMeta.selectedIcon;
if icontype == nil then icontype = iconMeta.defaultIcon; end
if icontype == "available" or icontype == "availablesoon" then
Questie:AddAvailableNoteToMap(location[1],location[2],location[3],location[4],icontype,v,-1,deepcopy(path));
else
Questie:AddNoteToMap(location[1],location[2],location[3],location[4],icontype,v,objectiveid,deepcopy(path));
end
end
end
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
for sourceName, sourceLocationMeta in pairs(sources) do
local newPath = deepcopy(path);
local editPath = newPath;
for i, pathKey in pairs(pathKeys) do
editPath = editPath[pathKey];
end
editPath[sourceType] = {};
editPath[sourceType][sourceName] = {};
local newPathKeys = deepcopy(pathKeys);
table.insert(newPathKeys, sourceType);
table.insert(newPathKeys, sourceName);
local newIconMeta = deepcopy(iconMeta);
if newIconMeta.selectedIcon == nil then
local typeToIcon = {
["drop"] = "loot",
["rewardedby"] = "slay",
["contained"] = "object",
["contained_id"] = "object",
["created"] = "event",
["containedi"] = "object",
["openedby"] = "object",
["transforms"] = "event",
["transformedby"] = "loot",
};
newIconMeta.selectedIcon = typeToIcon[sourceType];
end
local newObjectiveId = objectiveid;
if specialSources[sourceType] then
newPath = {};
newPathKeys = {};
newObjectiveId = sourceName;
newIconMeta.selectedIcon = nil;
end
Questie:RecursiveCreateNotes(c, z, v, sourceLocationMeta, newIconMeta, newObjectiveId, newPath, newPathKeys);
end
end
end
end
---------------------------------------------------------------------------------------------------
-- Sets up all available quests
---------------------------------------------------------------------------------------------------
function Questie:SetAvailableQuests(customLevel)
QuestieAvailableMapNotes = {};
local saqtime = GetTime();
local level = customLevel or UnitLevel("player");
local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
local mapFileName = GetMapInfo();
local quests = nil;
local minLevel = 0;
local maxLevel = 100;
if QuestieConfig.minLevelFilter then
minLevel = level - QuestieConfig.minShowLevel;
end
if QuestieConfig.maxLevelFilter then
maxLevel = level + QuestieConfig.maxShowLevel;
end
quests = Questie:GetAvailableQuestHashes(mapFileName, minLevel, maxLevel);
if quests then
local count = 0;
for k, v in pairs(quests) do
count = count + 1;
local icontype = "available";
if QuestieHashMap[k].level > level then icontype = "availablesoon"; end
Questie:RecursiveCreateNotes(c, z, k, v, {["selectedIcon"] = icontype});
end
--Questie:debug_Print("SetAvailableQuests: Adding "..count.." available quests took "..tostring((GetTime()- saqtime)*1000).."ms");
saqtime = nil;
end
end
---------------------------------------------------------------------------------------------------
-- Reason this exists is to be able to call both clearnotes and drawnotes without doing 2 function
-- calls, and to be able to force a redraw
---------------------------------------------------------------------------------------------------
function Questie:RedrawNotes()
Questie:CLEAR_ALL_NOTES();
Questie:DRAW_NOTES();
end
---------------------------------------------------------------------------------------------------
function Questie:Clear_Note(v)
v:SetParent(nil);
v:Hide();
v:SetAlpha(1);
v:SetFrameLevel(9);
v:SetHighlightTexture(nil, "ADD");
v.questHash = nil;
v.objId = nil;
v.data = nil;
v.quests = nil;
v.averageX = nil;
v.averageY = nil;
v.countForAverage = nil;
table.insert(FramePool, v);
end
---------------------------------------------------------------------------------------------------
-- Clears the notes, goes through the usednoteframes and clears them. Then sets the
-- QuestieUsedNotesFrame to new table;
---------------------------------------------------------------------------------------------------
function Questie:CLEAR_ALL_NOTES()
--Questie:debug_Print("CLEAR_ALL_NOTES");
Astrolabe:RemoveAllMinimapIcons();
clustersByFrame = nil;
for k, v in pairs(QuestieUsedNoteFrames) do
Questie:Clear_Note(v);
end
QuestieUsedNoteFrames = {};
end
---------------------------------------------------------------------------------------------------
-- Logic for clusters
---------------------------------------------------------------------------------------------------
function Cluster.new(points)
local self = setmetatable({}, Cluster);
self.points = points;
return self;
end
---------------------------------------------------------------------------------------------------
function Cluster:CountPoints()
local count = 0;
local counted = {};
for i, q in pairs(self.points) do
if not counted[q.questHash] then
count = count + 1;
counted[q.questHash] = true;
end
end
return count;
end
---------------------------------------------------------------------------------------------------
function Cluster.CalculateDistance(x1, y1, x2, y2)
local deltaX = x1 - x2;
local deltaY = y1 - y2;
return sqrt(deltaX*deltaX + deltaY*deltaY);
end
---------------------------------------------------------------------------------------------------
function Cluster.CalculateLinkageDistance(cluster1, cluster2)
local total = 0;
for i, pi in cluster1 do
for j, pj in cluster2 do
if pi.zoneid ~= pj.zoneid then return -1; end
local distance = Cluster.CalculateDistance(pi.x, pi.y, pj.x, pj.y);
total = total + distance;
end
end
return total / (table.getn(cluster1) * table.getn(cluster2));
end
---------------------------------------------------------------------------------------------------
function Cluster:CalculateClusters(clusters, distanceThreshold, maxClusterSize)
while table.getn(clusters) > 1 do
local nearest1;
local nearest2;
local nearestDistance;
for i, cluster in pairs(clusters) do
for j, otherCluster in pairs(clusters) do
if cluster ~= otherCluster then
local distance = Cluster.CalculateLinkageDistance(cluster.points, otherCluster.points);
if distance >= 0 and (distance == 0 or ((nearestDistance == nil or distance < nearestDistance) and (cluster:CountPoints() + otherCluster:CountPoints() <= maxClusterSize))) then
nearestDistance = distance;
nearest1 = cluster;
nearest2 = otherCluster;
end
end
if nearestDistance == 0 then break; end
end
if nearestDistance == 0 then break; end
end
if nearestDistance == nil or nearestDistance > distanceThreshold then break; end
local index1 = indexOf(clusters, nearest1);
table.remove(clusters, index1);
local index2 = indexOf(clusters, nearest2);
table.remove(clusters, index2);
local points = nearest1.points;
for i, point in pairs(nearest2.points) do
table.insert(points, point);
end
local newCluster = Cluster.new(points);
table.insert(clusters, newCluster);
end
end
---------------------------------------------------------------------------------------------------
-- splits the specified text into an array on the specified separator
-- todo make a QuestieUtils.lua file for things like this
---------------------------------------------------------------------------------------------------
function Questie:SplitString( text, separator, limit )
local parts, position, length, last, jump, count = {}, 1, string.len( text ), nil, string.len( separator ), 0;
while true do
last = string.find( text, separator, position, true );
if last and ( not limit or count < limit ) then
table.insert( parts, string.sub( text, position, last - 1 ) );
position, count = last + jump, count + 1;
else
table.insert( parts, string.sub( text, position ) );
break;
end
end
return parts;
end
---------------------------------------------------------------------------------------------------
function Questie:RoundCoordinate(coord, factor)
if factor == nil then factor = 1; end
return tonumber(string.format("%.2f", coord/factor)) * factor;
end
---------------------------------------------------------------------------------------------------
function Questie:GetReactionColor(reaction)
if reaction == nil or reaction < 1 or reaction > 8 then reaction = 4; end
return FACTION_BAR_COLORS[reaction];
end
---------------------------------------------------------------------------------------------------
function Questie:AddClusterFromNote(frame, identifier, v)
if clustersByFrame == nil then
clustersByFrame = {};
end
if clustersByFrame[frame] == nil then
clustersByFrame[frame] = {};
end
if clustersByFrame[frame][identifier] == nil then
clustersByFrame[frame][identifier] = {};
end
if clustersByFrame[frame][identifier][v.continent] == nil then
clustersByFrame[frame][identifier][v.continent] = {};
end
if clustersByFrame[frame][identifier][v.continent][v.zoneid] == nil then
clustersByFrame[frame][identifier][v.continent][v.zoneid] = {};
end
local roundedX = v.x;
local roundedY = v.y;
if QuestieConfig.clusterQuests and frame == "WorldMapNote" and identifier == "Objectives" then
roundedX = Questie:RoundCoordinate(v.x, 5);
roundedY = Questie:RoundCoordinate(v.y, 5);
end
if clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX] == nil then
clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX] = {};
end
if clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX][roundedY] == nil then
local points = { v };
local cluster = Cluster.new(points);
clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX][roundedY] = cluster;
else
table.insert(clustersByFrame[frame][identifier][v.continent][v.zoneid][roundedX][roundedY].points, v);
end
end
---------------------------------------------------------------------------------------------------
function Questie:GetClustersByFrame(frame, identifier)
if clustersByFrame == nil then
clustersByFrame = {};
end
if clustersByFrame[frame] == nil then
clustersByFrame[frame] = {};
end
if clustersByFrame[frame][identifier] == nil then
clustersByFrame[frame][identifier] = {};
end
local clusters = {};
for c, v in pairs(clustersByFrame[frame][identifier]) do
for z, v in pairs(clustersByFrame[frame][identifier][c]) do
for x, v in pairs(clustersByFrame[frame][identifier][c][z]) do
for y, v in pairs(clustersByFrame[frame][identifier][c][z][x]) do
table.insert(clusters, clustersByFrame[frame][identifier][c][z][x][y]);
end
end
end
end
return clusters;
end
---------------------------------------------------------------------------------------------------
-- Finds the index of an item in a table. Not sure if a function already exists somewhere.
---------------------------------------------------------------------------------------------------
function indexOf(table, item)
for k, v in pairs(table) do
if v == item then return k; end
end
return nil;
end
---------------------------------------------------------------------------------------------------
-- Checks first if there are any notes for the current zone, then draws the desired icon
---------------------------------------------------------------------------------------------------
function Questie:DRAW_NOTES()
--Questie:debug_Print("DRAW_NOTES");
local c, z = GetCurrentMapContinent(), GetCurrentMapZone();
if (not QuestieConfig.hideMinimapIcons) then
-- Draw minimap objective markers
if (QuestieMapNotes[c] and QuestieMapNotes[c][z]) then
for k, v in pairs(QuestieMapNotes[c][z]) do
--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
local show = QuestieConfig.alwaysShowObjectives or ((MMLastX ~= 0) and (MMLastY ~= 0)) and (QuestieCachedQuests[v.questHash] ~= nil) and (QuestieCachedQuests[v.questHash]["tracked"] ~= false);
if show then
if (v.icontype == "complete") then
Questie:AddClusterFromNote("MiniMapNote", "Quests", v);
else
if QuestieConfig.hideObjectives == false then
Questie:AddClusterFromNote("MiniMapNote", "Objectives", v);
end
end
end
end
end
end
-- Draw world map objective markers
for k, Continent in pairs(QuestieMapNotes) do
for zone, noteHeap in pairs(Continent) do
for k, v in pairs(noteHeap) do
if true then
--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
if (((QuestieCachedQuests[v.questHash] ~= nil) and (QuestieCachedQuests[v.questHash]["tracked"] ~= false)) or (v.icontype == "complete")) and (QuestieConfig.alwaysShowObjectives == false) then
if (v.icontype == "complete") then
Questie:AddClusterFromNote("WorldMapNote", "Quests", v);
else
if QuestieConfig.hideObjectives == false then
Questie:AddClusterFromNote("WorldMapNote", "Objectives", v);
end
end
elseif (QuestieConfig.alwaysShowObjectives == true) then
if (v.icontype == "complete") then
Questie:AddClusterFromNote("WorldMapNote", "Quests", v);
else
if QuestieConfig.hideObjectives == false then
Questie:AddClusterFromNote("WorldMapNote", "Objectives", v);
end
end
end
end
end
end
end
-- Draw available quest markers.
if (QuestieAvailableMapNotes[c] and QuestieAvailableMapNotes[c][z]) then
if (IsQuestieActive == true) then
local con,zon,x,y = Astrolabe:GetCurrentPlayerPosition();
for k, v in pairs(QuestieAvailableMapNotes[c][z]) do
Questie:AddClusterFromNote("WorldMapNote", "Quests", v);
if (not QuestieConfig.hideMinimapIcons) then
Questie:AddClusterFromNote("MiniMapNote", "Quests", v);
end
end
end
end
local minimapObjectiveClusters = Questie:GetClustersByFrame("MiniMapNote", "Objectives");
local worldMapObjectiveClusters = Questie:GetClustersByFrame("WorldMapNote", "Objectives");
local minimapClusters = Questie:GetClustersByFrame("MiniMapNote", "Quests");
local worldMapClusters = Questie:GetClustersByFrame("WorldMapNote", "Quests");
if QuestieConfig.clusterQuests then
Cluster:CalculateClusters(worldMapClusters, 0.025, 5);
end
local scale = QUESTIE_NOTES_MAP_ICON_SCALE;
if (z == 0 and c == 0) then--Both continents
scale = QUESTIE_NOTES_WORLD_MAP_ICON_SCALE;
elseif (z == 0) then--Single continent
scale = QUESTIE_NOTES_CONTINENT_ICON_SCALE;
end
Questie:DrawClusters(worldMapObjectiveClusters, "WorldMapNote", scale, WorldMapFrame, WorldMapButton);
Questie:DrawClusters(worldMapClusters, "WorldMapNote", scale, WorldMapFrame, WorldMapButton);
Questie:DrawClusters(minimapObjectiveClusters, "MiniMapNote", QUESTIE_NOTES_MINIMAP_ICON_SCALE, Minimap);
Questie:DrawClusters(minimapClusters, "MiniMapNote", QUESTIE_NOTES_MINIMAP_ICON_SCALE, Minimap);
end
---------------------------------------------------------------------------------------------------
function Questie:DrawClusters(clusters, frameName, scale, frame, button)
local frameLevel = 9;
if frameName == "MiniMapNote" then
frameLevel = 7;
end
for i, cluster in pairs(clusters) do
table.sort(cluster.points, function(a, b)
if QuestieIcons[a.icontype].priority ~= QuestieIcons[b.icontype].priority then return QuestieIcons[a.icontype].priority < QuestieIcons[b.icontype].priority end
if a.questHash == b.questHash then return tostring(a) < tostring(b) end
local questA = QuestieHashMap[a.questHash]
local questB = QuestieHashMap[b.questHash]
if not questA or not questB then return questA ~= nil end
if questA and questB then
if questA.level ~= questB.level then return questA.level < questB.level end
local questLevelA = GetNumberFromString(questA.questLevel)
local questLevelB = GetNumberFromString(questB.questLevel)
if questLevelA ~= questLevelB then return questLevelA < questLevelB end
end
return a.questHash < b.questHash
end)
local Icon = Questie:GetBlankNoteFrame(frame);
for j, v in pairs(cluster.points) do
if j == 1 then
local finalFrameLevel = frameLevel;
if v.icontype == "complete" then finalFrameLevel = finalFrameLevel + 1; end
Questie:SetFrameNoteData(Icon, v, frame, finalFrameLevel, frameName, scale);
else
Questie:AddFrameNoteData(Icon, v);
end
end
Questie:PostProcessIconPaths(Icon);
if frameName == "MiniMapNote" then
Icon:SetHighlightTexture(QuestieIcons[Icon.data.icontype].path, "ADD");
Astrolabe:PlaceIconOnMinimap(Icon, Icon.data.continent, Icon.data.zoneid, Icon.averageX, Icon.averageY);
table.insert(QuestieUsedNoteFrames, Icon);
else
Icon:Show();
xx, yy = Astrolabe:PlaceIconOnWorldMap(button, Icon, Icon.data.continent, Icon.data.zoneid, Icon.averageX, Icon.averageY);
if(xx and yy and xx > 0 and xx < 1 and yy > 0 and yy < 1) then
table.insert(QuestieUsedNoteFrames, Icon);
else
Questie:Clear_Note(Icon);
end
end
end
end
---------------------------------------------------------------------------------------------------
-- Debug print function
---------------------------------------------------------------------------------------------------
function Questie:debug_Print(...)
local debugWin = 0;
local name, shown;
for i=1, NUM_CHAT_WINDOWS do
name,_,_,_,_,_,shown = GetChatWindowInfo(i);
if (string.lower(name) == "questiedebug") then debugWin = i; break; end
end
if (debugWin == 0) then return; end
local out = "";
for i = 1, arg.n, 1 do
if (i > 1) then out = out .. ", "; end
local t = type(arg[i]);
if (t == "string") then
out = out .. '"'..arg[i]..'"';
elseif (t == "number") then
out = out .. arg[i];
else
out = out .. dump(arg[i]);
end
end
getglobal("ChatFrame"..debugWin):AddMessage(out, 1.0, 1.0, 0.3);
end
---------------------------------------------------------------------------------------------------
-- Sets the icon type
---------------------------------------------------------------------------------------------------
QuestieIcons = {
["complete"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\complete",
priority = 1
},
["available"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\available",
priority = 2
},
["availablesoon"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\availablesoon",
priority = 2
},
["loot"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\loot",
priority = 3
},
["item"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\loot",
priority = 3
},
["event"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\event",
priority = 3
},
["object"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\object",
priority = 3
},
["slay"] = {
text = "Complete",
path = "Interface\\AddOns\\!Questie\\Icons\\slay",
priority = 3
}
};