---@class MapIconTooltip local MapIconTooltip = QuestieLoader:CreateModule("MapIconTooltip"); local _MapIconTooltip = {} local tinsert = table.insert; ---@type QuestieMap local QuestieMap = QuestieLoader:ImportModule("QuestieMap") ---@type QuestieReputation local QuestieReputation = QuestieLoader:ImportModule("QuestieReputation") ---@type QuestiePlayer local QuestiePlayer = QuestieLoader:ImportModule("QuestiePlayer") ---@type QuestieEvent local QuestieEvent = QuestieLoader:ImportModule("QuestieEvent") ---@type QuestieDB local QuestieDB = QuestieLoader:ImportModule("QuestieDB") ---@type QuestieLib local QuestieLib = QuestieLoader:ImportModule("QuestieLib") ---@type QuestieComms local QuestieComms = QuestieLoader:ImportModule("QuestieComms") ---@type l10n local l10n = QuestieLoader:ImportModule("l10n") ---@type QuestXP local QuestXP = QuestieLoader:ImportModule("QuestXP") --- COMPATIBILITY --- local C_Map = QuestieCompat.C_Map local WorldMapFrame = QuestieCompat.WorldMapFrame local FormatLargeNumber = QuestieCompat.FormatLargeNumber local GetQuestLogRewardMoney = QuestieCompat.GetQuestLogRewardMoney local GetClassColor = QuestieCompat.GetClassColor -- Silent quest log index lookup (prevents warning spam for auto-complete/achievement "quests") -- Uses QuestieCompat wrapper which normalizes return values across API versions. local function _Questie_SilentGetQuestLogIndexByID(questId) questId = tonumber(questId) if not questId then return 0 end -- QuestieCompat.GetQuestLogIndexByID returns nil when not found, -- coerce to 0 for callers that check > 0. local idx = QuestieCompat.GetQuestLogIndexByID(questId) if idx then return idx end -- Fallback: use the compat-normalized GetQuestLogTitle (always 8 returns) local GetQuestLogTitleCompat = QuestieCompat.GetQuestLogTitle local n = select(1, GetNumQuestLogEntries()) or 0 for i = 1, n do local _, _, _, isHeader, _, _, _, qid = GetQuestLogTitleCompat(i) qid = tonumber(qid) if (not isHeader) and qid and qid == questId then return i end end return 0 end local HBDPins = QuestieCompat.HBDPins or LibStub("HereBeDragonsQuestie-Pins-2.0") local REPUTATION_ICON_PATH = QuestieLib.AddonPath .. "Icons\\reputation.blp" local REPUTATION_ICON_TEXTURE = "|T" .. REPUTATION_ICON_PATH .. ":14:14:2:0|t" local TRANSPARENT_ICON_PATH = "Interface\\Minimap\\UI-bonusobjectiveblob-inside.blp" local TRANSPARENT_ICON_TEXTURE = QuestieCompat.Is335 and "" or "|T" .. TRANSPARENT_ICON_PATH .. ":14:14:2:0|t" local DEFAULT_WAYPOINT_HOVER_COLOR = { 0.93, 0.46, 0.13, 0.8 } local lastTooltipShowTimestamp = GetTime() local function _GetWorldMapTooltipSourceLine(pinData) -- Show learner spawn data for the hovered map icon when available. local id = pinData and pinData.Id if not id then return nil end local npcId = math.abs(id) local QuestieLearner = QuestieLoader:ImportModule("QuestieLearner") if not QuestieLearner or not QuestieLearner.IsEnabled or not QuestieLearner:IsEnabled() then return nil end local learnedNpc = QuestieLearner.data and QuestieLearner.data.npcs and QuestieLearner.data.npcs[npcId] if not learnedNpc then return nil end local guidSpawns = learnedNpc[8] local bestX, bestY, bestCount = nil, nil, 0 if guidSpawns then for uid, entry in pairs(guidSpawns) do local c = tonumber(entry.count) or 1 if c > bestCount then bestCount = c bestX = entry.x bestY = entry.y end end end if bestX and bestY then return string.format("|cFF808080Learned spawn (%.1f, %.1f) from %d kills|r", bestX, bestY, bestCount) end return nil end --- Per-pin data-source attribution line for the hovered world-map icon. --- Reads the DataSource tag set at pin creation (objective/finisher/available/manual) so --- the label is accurate, plus a Comms overlay when comms holds data for this id. local function _GetWorldMapDataSourceLine(pinData) if not Questie.db.profile.enableTooltipsSource then return nil end -- Source attribution is tied to the secondary learner tooltip: only show it when that -- option is enabled, and never otherwise (matches the unit-hover behaviour). if Questie.db.profile.learnerTooltipUseSecondary ~= true then return nil end local src = pinData and pinData.DataSource local parts = {} if src then tinsert(parts, src) end local id = pinData and pinData.Id if id and QuestieComms and QuestieComms.data and QuestieComms.data.KeyExists and QuestieComms.data:KeyExists("m_" .. tostring(id)) then tinsert(parts, "Comms") end if table.getn(parts) == 0 then return nil end return "|cFF808080Source: " .. table.concat(parts, " + ") .. "|r" end function MapIconTooltip:Show() local _, _, _, alpha = self.texture:GetVertexColor(); if alpha == 0 then Questie:Debug(Questie.DEBUG_DEVELOP, "[MapIconTooltip:Show] Alpha of texture is 0, nothing to show") return end if GetTime() - lastTooltipShowTimestamp < 0.05 and GameTooltip:IsShown() then Questie:Debug(Questie.DEBUG_DEVELOP, "[MapIconTooltip:Show] Call has been too fast, not showing again") return end lastTooltipShowTimestamp = GetTime() local Tooltip = QuestieCompat.Is335 and QuestieCompat.SetupTooltip(self) or GameTooltip; Tooltip._owner = self; Tooltip:SetOwner(self, "ANCHOR_CURSOR"); --"ANCHOR_CURSOR" or (self, self) local maxDistCluster = 1 local mapId = WorldMapFrame:GetMapID(); if C_Map and C_Map.GetMapInfo then local mapInfo = C_Map.GetMapInfo(mapId) if mapInfo then if (mapInfo.mapType == 0 or mapInfo.mapType == 1) then -- Cosmic or World maxDistCluster = 6 elseif mapInfo.mapType == 2 then -- Continent maxDistCluster = 4 end end end if self.miniMapIcon then if _MapIconTooltip:IsMinimapInside() then maxDistCluster = 0.3 / (1 + Minimap:GetZoom()) else maxDistCluster = 0.5 / (1 + Minimap:GetZoom()) end end local r, g, b, a = unpack(QuestieMap.zoneWaypointHoverColorOverrides[self.AreaID] or DEFAULT_WAYPOINT_HOVER_COLOR) --Highlight waypoints if they exist. for _, lineFrame in next, self.data.lineFrames or {} do lineFrame.line:SetColorTexture(r, g, b, a) end -- FIXME: `data` can be nil here which leads to an error, will have to debug: -- https://discordapp.com/channels/263036731165638656/263040777658171392/627808795715960842 -- happens when a note doesn't get removed after a quest has been finished, see #1170 -- TODO: change how the logic works, so this [ObjectiveIndex?] can be nil -- it is nil on some notes like starters/finishers, because its for objectives. However, it needs to be an number here for duplicate checks if not self.data.ObjectiveIndex then self.data.ObjectiveIndex = 0 end --for k,v in pairs(self.data.tooltip) do --Tooltip:AddLine(v); --end local usedText = {} local npcAndObjectOrder = {}; local questOrder = {}; local manualOrder = {} self.data.touchedPins = {} ---@param icon IconFrame local function handleMapIcon(icon) local iconData = icon.data if not iconData then Questie:Error("[MapIconTooltip:Show] handleMapIcon - iconData is nil! self.data.Id =", self.data.Id, "- Aborting!") return end -- Do not recolor MiniMap, Available and Completed Quest Icons. if (not icon.miniMapIcon) and not (iconData.Type == "available" or iconData.Type == "complete") and self.data.Id == iconData.Id then -- Recolor hovered icons local entry = {} entry.color = { icon.texture.r, icon.texture.g, icon.texture.b, icon.texture.a }; entry.icon = icon; if Questie.db.profile.questObjectiveColors then icon.texture:SetVertexColor(1, 1, 1, 1); -- If different colors are active simply change it to the regular icon color else icon.texture:SetVertexColor(0.6, 1, 1, 1); -- Without colors make it blueish end tinsert(self.data.touchedPins, entry); end if icon.x and icon.AreaID == self.AreaID then local dist = QuestieLib:Maxdist(icon.x, icon.y, self.x, self.y); if dist < maxDistCluster then local _qid = tonumber(iconData.Id) local key = iconData.Id if iconData.ObjectiveData and iconData.ObjectiveData.Description and _qid and _Questie_SilentGetQuestLogIndexByID(_qid) > 0 then if not questOrder[key] then questOrder[key] = {}; end local orderedTooltips = {} if iconData.Type == "event" then local tip = _MapIconTooltip:GetEventObjectiveTooltip(icon.data) -- We need to check for duplicates. local add = true; for _, data in next, questOrder[key] do for text, _ in next, data do if (text == iconData.ObjectiveData.Description) then add = false; break; end end end if add then questOrder[key] = tip end else local tooltips = _MapIconTooltip:GetObjectiveTooltip(icon) for _, tip in next, tooltips do tinsert(orderedTooltips, 1, tip); end for _, tip in next, orderedTooltips do local questData = questOrder[key] _MapIconTooltip:AddTooltipsForQuest(icon, tip, questData, usedText) end end elseif iconData.Type == "available" or iconData.Type == "complete" then local tip = _MapIconTooltip:GetAvailableOrCompleteTooltip(icon) if not npcAndObjectOrder["default"] then npcAndObjectOrder["default"] = {npcNames = {}, quests = {}}; end if iconData.Name then npcAndObjectOrder["default"].npcNames[iconData.Name] = true end if tip and tip.title then npcAndObjectOrder["default"].quests[tip.title] = tip end elseif iconData.Type == "monster" or iconData.Type == "killcredit" or iconData.Type == "spell" or iconData.Type == "object" or iconData.Type == "event" or iconData.Type == "item" then local tip = _MapIconTooltip:GetAvailableOrCompleteTooltip(icon) if not npcAndObjectOrder["default"] then npcAndObjectOrder["default"] = {npcNames = {}, quests = {}}; end if iconData.Name then npcAndObjectOrder["default"].npcNames[iconData.Name] = true end if tip and tip.title then npcAndObjectOrder["default"].quests[tip.title] = tip end elseif iconData.CustomTooltipData then manualOrder[iconData.CustomTooltipData.Title] = { Body = { iconData.CustomTooltipData.Body or "" } } elseif iconData.ManualTooltipData then manualOrder[iconData.ManualTooltipData.Title] = iconData.ManualTooltipData end end end end if self.miniMapIcon then for icon, _ in next, HBDPins.activeMinimapPins do handleMapIcon(icon) end else if QuestieCompat.Is335 then for icon, _ in next, HBDPins.worldmapPins do handleMapIcon(icon) end else for pin in HBDPins.worldmapProvider:GetMap():EnumeratePinsByTemplate("HereBeDragonsPinsTemplateQuestie") do handleMapIcon(pin.icon) end end end Tooltip.npcAndObjectOrder = npcAndObjectOrder Tooltip.questOrder = questOrder Tooltip.manualOrder = manualOrder Tooltip.miniMapIcon = self.miniMapIcon Tooltip._Rebuild = function(self) -- generate the tooltips local xpString = l10n('xp'); local shift = IsShiftKeyDown() local haveGiver = false -- hack local firstLine = true; local playerIsHuman = QuestiePlayer:GetRaceId() == 1 local playerIsHonoredWithShaTar = (not QuestieReputation:HasReputation(nil, { 935, 8999 })) -- tooltips for quest icons on the map -- tooltips for quest icons on the map for questTitleKey, data in next, self.npcAndObjectOrder do -- this logic really needs to be improved haveGiver = true if shift and (not firstLine) then -- Spacer between NPCs self:AddLine(" ") end -- Display all NPC names for this quest group local names = {} for name, _ in next, data.npcNames do tinsert(names, name) end table.sort(names) local npcOrObjectName = table.concat(names, ", ") if (firstLine and not shift) then self:AddDoubleLine(npcOrObjectName, "(" .. l10n('Hold Shift') .. ")", 0.2, 1, 0.2, 0.43, 0.43, 0.43); firstLine = false; elseif (firstLine and shift) then self:AddLine(npcOrObjectName, 0.2, 1, 0.2); firstLine = false; else self:AddLine(npcOrObjectName, 0.2, 1, 0.2); end local quests = data.quests for _, questData in next, quests do local reputationReward = QuestieDB.QueryQuestSingle(questData.questId, "reputationReward") if questData.title ~= nil then local quest = QuestieDB.GetQuest(questData.questId) local rewardString = "" if (quest and shift) then local xpReward = QuestXP:GetQuestLogRewardXP(questData.questId, Questie.db.profile.showQuestXpAtMaxLevel) if xpReward > 0 then rewardString = QuestieLib:PrintDifficultyColor(quest.level, "(" .. FormatLargeNumber(xpReward) .. xpString .. ") ", QuestieDB.IsRepeatable(questData.questId), QuestieDB.IsActiveEventQuest(questData.questId), QuestieDB.IsPvPQuest(questData.questId)) end local moneyReward = QuestXP.GetQuestRewardMoney(questData.questId) if moneyReward > 0 then rewardString = rewardString .. Questie:Colorize("(" .. GetCoinTextureString(moneyReward) .. ") ", "white") end end rewardString = rewardString .. questData.type if (not shift) and reputationReward and next(reputationReward) then self:AddDoubleLine(REPUTATION_ICON_TEXTURE .. " " .. questData.title, rewardString, 1, 1, 1, 1, 1, 0); else if shift then self:AddDoubleLine(questData.title, rewardString, 1, 1, 1, 1, 1, 0); else -- We use a transparent icon because this eases setting the correct margin self:AddDoubleLine(TRANSPARENT_ICON_TEXTURE .. " " .. questData.title, rewardString, 1, 1, 1, 1, 1, 0); end end end if questData.subData and shift then local dataType = type(questData.subData) if dataType == "table" then for _, rawLine in next, questData.subData do local lines = QuestieLib:TextWrap(rawLine, " ", false, math.max(375, Tooltip:GetWidth()), questData.questId) --275 is the default questlog width for _, line in next, lines do self:AddLine(line, 0.86, 0.86, 0.86); end end elseif dataType == "string" then local lines = QuestieLib:TextWrap(questData.subData, " ", false, math.max(375, Tooltip:GetWidth())) --275 is the default questlog width for _, line in next, lines do self:AddLine(line, 0.86, 0.86, 0.86); end end end local nextQuestInChain = QuestieDB.QueryQuestSingle(questData.questId, "nextQuestInChain") or 0 if shift and nextQuestInChain > 0 and Questie.db.profile.enableTooltipsNextInChain then -- add quest chain info local nextQuest = QuestieDB.GetQuest(nextQuestInChain) local firstInChain = true; while nextQuest ~= nil do local nextQuestTitleString; local nextQuestXpRewardString = ""; local nextQuestMoneyRewardString = ""; local nextQuestIdString = ""; local nextQuestTagString = ""; if firstInChain then self:AddLine( " |T" .. QuestieLib.AddonPath .. "Icons\\nextquest.blp:16|t " .. l10n("Next in chain:"), 0.86, 0.86, 0.86) firstInChain = false; end if Questie.db.profile.enableTooltipsQuestLevel then nextQuestTitleString = string.format("%s", QuestieLib:GetLevelString(nextQuest.Id, "", nextQuest.level, true) .. nextQuest.name) else nextQuestTitleString = string.format("%s", nextQuest.name) end if Questie.db.profile.enableTooltipsQuestID then nextQuestIdString = string.format(" (%d)", nextQuest.Id) end local nextQuestXpReward = QuestXP:GetQuestLogRewardXP(nextQuest.Id, Questie.db.profile.showQuestXpAtMaxLevel); if nextQuestXpReward > 0 then nextQuestXpRewardString = string.format(" (%s%s)", FormatLargeNumber(nextQuestXpReward), xpString); end local nextQuestMoneyReward = QuestXP:GetQuestRewardMoney(nextQuest.Id); if nextQuestMoneyReward > 0 then nextQuestMoneyRewardString = Questie:Colorize( string.format(" (%s)", GetCoinTextureString(nextQuestMoneyReward)), "white"); end if (QuestieDB.IsGroupQuest(nextQuest.Id) or QuestieDB.IsDungeonQuest(nextQuest.Id) or QuestieDB.IsRaidQuest(nextQuest.Id)) then local _, nextQuestTag = QuestieDB.GetQuestTagInfo(nextQuest.Id) nextQuestTagString = Questie:Colorize(string.format(" (%s)", nextQuestTag), "yellow") end local nextQuestString = string.format(" %s%s%s%s%s", nextQuestTitleString, nextQuestIdString, nextQuestXpRewardString, nextQuestMoneyRewardString, nextQuestTagString); -- we need an offset to align with description self:AddLine( QuestieLib:PrintDifficultyColor(nextQuest.level, nextQuestString, QuestieDB.IsRepeatable(nextQuest.Id), QuestieDB.IsActiveEventQuest(nextQuest.Id), QuestieDB.IsPvPQuest(nextQuest.Id)), 1, 1, 1); if nextQuest.nextQuestInChain and nextQuest.nextQuestInChain > 0 then nextQuest = QuestieDB.GetQuest(nextQuest.nextQuestInChain) else nextQuest = nil end end end if shift and reputationReward and next(reputationReward) then local rewardTable = {} local factionId, factionName local rewardValue local aldorPenalty, scryersPenalty for _, rewardPair in next, reputationReward do factionId = rewardPair[1] if factionId == 935 and playerIsHonoredWithShaTar and (scryersPenalty or aldorPenalty) then -- Quests for Aldor and Scryers gives reputation to the Sha'tar but only before being Honored -- with the Sha'tar break end factionName = select(1, QuestieCompat.GetFactionInfoByID(factionId)) if factionName then rewardValue = rewardPair[2] if playerIsHuman and rewardValue > 0 then -- Humans get 10% more reputation rewardValue = math.floor(rewardValue * 1.1) end if factionId == 932 then -- Aldor scryersPenalty = 0 - math.floor(rewardValue * 1.1) elseif factionId == 934 then -- Scryers aldorPenalty = 0 - math.floor(rewardValue * 1.1) end tinsert(rewardTable, (rewardValue > 0 and "+" or "") .. rewardValue .. " " .. factionName) end end if aldorPenalty then factionName = select(1, QuestieCompat.GetFactionInfoByID(932)) tinsert(rewardTable, aldorPenalty .. " " .. factionName) elseif scryersPenalty then factionName = select(1, QuestieCompat.GetFactionInfoByID(934)) tinsert(rewardTable, scryersPenalty .. " " .. factionName) end self:AddLine( REPUTATION_ICON_TEXTURE .. " " .. Questie:Colorize(table.concat(rewardTable, " / "), "reputationBlue"), 1, 1, 1, 1, 1, 0) end end end -- tooltips for objectives of active quests ---@param questId number for questId, textList in next, self.questOrder do -- this logic really needs to be improved if type(questId) ~= "number" then -- Skip non-quest keys if any somehow ended up here break end ---@type Quest local quest = QuestieDB.GetQuest(questId); if not quest then break end local questTitle = QuestieLib:GetColoredQuestName(questId, Questie.db.profile.enableTooltipsQuestLevel, true, true); local xpReward = QuestXP:GetQuestLogRewardXP(questId, Questie.db.profile.showQuestXpAtMaxLevel); r, g, b = QuestieLib:GetDifficultyColorPercent(quest.level); local reputationReward = QuestieDB.QueryQuestSingle(questId, "reputationReward") local objectiveTitle = questTitle if (not shift) and reputationReward and next(reputationReward) then objectiveTitle = REPUTATION_ICON_TEXTURE .. " " .. questTitle end if haveGiver then if shift and xpReward then self:AddLine(" "); self:AddDoubleLine(objectiveTitle, "(" .. FormatLargeNumber(xpReward) .. xpString .. ") (" .. l10n("Active") .. ")", 0.2, 1, 0.2, 1, 1, 0); haveGiver = false -- looks better when only the first one shows (active) else self:AddLine(" "); self:AddDoubleLine(objectiveTitle, "(" .. l10n("Active") .. ")", 1, 1, 1, 1, 1, 0); haveGiver = false -- looks better when only the first one shows (active) end else if (quest and shift and xpReward > 0) then self:AddDoubleLine(objectiveTitle, "(" .. FormatLargeNumber(xpReward) .. xpString .. ")", 0.2, 1, 0.2, r, g, b); firstLine = false; elseif (firstLine and not shift) then self:AddDoubleLine(objectiveTitle, "(" .. l10n('Hold Shift') .. ")", 0.2, 1, 0.2, 0.43, 0.43, 0.43); --"(Shift+click)" firstLine = false; else self:AddLine(objectiveTitle); end end local function _GetLevelString(creatureLevels, name) if not creatureLevels[name] then return name end local levelString = name if creatureLevels[name] then local minLevel = creatureLevels[name][1] local maxLevel = creatureLevels[name][2] local rank = creatureLevels[name][3] if minLevel and maxLevel then if minLevel == maxLevel then levelString = name .. " (" .. minLevel else levelString = name .. " (" .. minLevel .. "-" .. maxLevel end if rank and rank == 1 then levelString = levelString .. "+" elseif rank and rank == 2 then levelString = levelString .. " " .. l10n("Rare Elite") elseif rank and rank == 3 then levelString = levelString .. " Boss" elseif rank and rank == 4 then levelString = levelString .. " " .. l10n("Rare") end levelString = levelString .. ")" end end return levelString end -- Used to get the white color for the quests which don't have anything to collect local defaultQuestColor = QuestieLib:GetRGBForObjective({}) local creatureLevels = QuestieDB:GetCreatureLevels(quest) -- Data for min and max level local hasRare = false if not shift then for _, textData in next, textList do for textLine, nameData in next, textData do local dataType = type(nameData) if dataType == "table" then for name in next, nameData do if creatureLevels[name] and (creatureLevels[name][3] == 2 or creatureLevels[name][3] == 4) then hasRare = true break end end elseif dataType == "string" then if creatureLevels[nameData] and (creatureLevels[nameData][3] == 2 or creatureLevels[nameData][3] == 4) then hasRare = true end end if hasRare then break end end if hasRare then break end end end local addedCreatureNames = {} for _, textData in next, textList do for textLine, nameData in next, textData do local dataType = type(nameData) local hasName = false if dataType == "table" then for name in next, nameData do if (not addedCreatureNames[name]) then addedCreatureNames[name] = true name = _GetLevelString(creatureLevels, name) self:AddLine(" |cFFDDDDDD" .. name); end hasName = true end elseif dataType == "string" and (not addedCreatureNames[nameData]) then addedCreatureNames[nameData] = true nameData = _GetLevelString(creatureLevels, nameData) self:AddLine(" |cFFDDDDDD" .. nameData); hasName = true end if hasName or shift or hasRare then self:AddLine(" " .. defaultQuestColor .. textLine); else -- No name found, but we should still show the objective text with normal indent self:AddLine(" " .. defaultQuestColor .. textLine); end end end local reputationReward = QuestieDB.QueryQuestSingle(questId, "reputationReward") if shift and reputationReward and next(reputationReward) then local rewardTable = {} local factionId, factionName local rewardValue local aldorPenalty, scryersPenalty for _, rewardPair in next, reputationReward do factionId = rewardPair[1] if factionId == 935 and playerIsHonoredWithShaTar and (scryersPenalty or aldorPenalty) then -- Quests for Aldor and Scryers gives reputation to the Sha'tar but only before being Honored -- with the Sha'tar break end factionName = select(1, QuestieCompat.GetFactionInfoByID(factionId)) if factionName then rewardValue = rewardPair[2] if playerIsHuman and rewardValue > 0 then -- Humans get 10% more reputation rewardValue = math.floor(rewardValue * 1.1) end if factionId == 932 then -- Aldor scryersPenalty = 0 - math.floor(rewardValue * 1.1) elseif factionId == 934 then -- Scryers aldorPenalty = 0 - math.floor(rewardValue * 1.1) end tinsert(rewardTable, (rewardValue > 0 and "+" or "") .. rewardValue .. " " .. factionName) end end if aldorPenalty then factionName = select(1, QuestieCompat.GetFactionInfoByID(932)) tinsert(rewardTable, aldorPenalty .. " " .. factionName) elseif scryersPenalty then factionName = select(1, QuestieCompat.GetFactionInfoByID(934)) tinsert(rewardTable, scryersPenalty .. " " .. factionName) end self:AddLine( REPUTATION_ICON_TEXTURE .. " " .. Questie:Colorize(table.concat(rewardTable, " / "), "reputationBlue"), 1, 1, 1, 1, 1, 0) end local nextQuestInChain = QuestieDB.QueryQuestSingle(questId, "nextQuestInChain") or 0 if shift and nextQuestInChain > 0 and Questie.db.profile.enableTooltipsNextInChain then -- add quest chain info local nextQuest = QuestieDB.GetQuest(nextQuestInChain) local firstInChain = true; while nextQuest ~= nil do local nextQuestTitleString; local nextQuestXpRewardString = ""; local nextQuestMoneyRewardString = ""; local nextQuestIdString = ""; local nextQuestTagString = ""; if firstInChain then self:AddLine( " |T" .. QuestieLib.AddonPath .. "Icons\\nextquest.blp:16|t " .. l10n("Next in chain:"), 0.86, 0.86, 0.86) firstInChain = false; end if Questie.db.profile.enableTooltipsQuestLevel then nextQuestTitleString = string.format("%s", QuestieLib:GetLevelString(nextQuest.Id, "", nextQuest.level, true) .. nextQuest.name) else nextQuestTitleString = string.format("%s", nextQuest.name) end if Questie.db.profile.enableTooltipsQuestID then nextQuestIdString = string.format(" (%d)", nextQuest.Id) end local nextQuestXpReward = QuestXP:GetQuestLogRewardXP(nextQuest.Id, Questie.db.profile.showQuestXpAtMaxLevel); if nextQuestXpReward > 0 then nextQuestXpRewardString = string.format(" (%s%s)", FormatLargeNumber(nextQuestXpReward), xpString); end local nextQuestMoneyReward = QuestXP.GetQuestRewardMoney(nextQuest.Id); if nextQuestMoneyReward > 0 then nextQuestMoneyRewardString = Questie:Colorize( string.format(" (%s)", GetCoinTextureString(nextQuestMoneyReward)), "white"); end if (QuestieDB.IsGroupQuest(nextQuest.Id) or QuestieDB.IsDungeonQuest(nextQuest.Id) or QuestieDB.IsRaidQuest(nextQuest.Id)) then local _, nextQuestTag = QuestieDB.GetQuestTagInfo(nextQuest.Id) nextQuestTagString = Questie:Colorize(string.format(" (%s)", nextQuestTag), "yellow") end local nextQuestString = string.format(" %s%s%s%s%s", nextQuestTitleString, nextQuestIdString, nextQuestXpRewardString, nextQuestMoneyRewardString, nextQuestTagString); -- we need an offset to align with description self:AddLine( QuestieLib:PrintDifficultyColor(nextQuest.level, nextQuestString, QuestieDB.IsRepeatable(nextQuest.Id), QuestieDB.IsActiveEventQuest(nextQuest.Id), QuestieDB.IsPvPQuest(nextQuest.Id)), 1, 1, 1); if nextQuest.nextQuestInChain and nextQuest.nextQuestInChain > 0 then nextQuest = QuestieDB.GetQuest(nextQuest.nextQuestInChain) else nextQuest = nil end end end end if next(self.npcAndObjectOrder) and next(self.manualOrder) then -- Spacer before townsfolk self:AddLine(" ") end for title, data in next, self.manualOrder do local body = data.Body self:AddLine(title) local bIndex = 1 while body[bIndex] do local stringOrTable = body[bIndex] local dataType = type(stringOrTable) if dataType == "string" then self:AddLine(stringOrTable) elseif dataType == "table" then self:AddDoubleLine(stringOrTable[1], '|cFFffffff' .. stringOrTable[2] .. '|r') --normal, white end bIndex = bIndex + 1 end if self.miniMapIcon == false and not data.disableShiftToRemove then self:AddLine('|cFFa6a6a6Shift-click to hide|r') -- grey end end self:AddLine(_GetWorldMapTooltipSourceLine(self.data), 0.55, 0.55, 0.55) local dataSourceLine = _GetWorldMapDataSourceLine(self.data) if dataSourceLine then self:AddLine(dataSourceLine, 0.55, 0.55, 0.55) end end Tooltip:_Rebuild() -- we separate this so things like MODIFIER_STATE_CHANGED can redraw the tooltip Tooltip:SetFrameStrata("TOOLTIP"); Tooltip.ShownAsMapIcon = true Tooltip:Show(); end local isLastMinimapInside, lastMinimapInsideCheckTimestamp function _MapIconTooltip:IsMinimapInside() if lastMinimapInsideCheckTimestamp and GetTime() - lastMinimapInsideCheckTimestamp < 1 then return isLastMinimapInside end local tempzoom = 0; if (GetCVar("minimapZoom") == GetCVar("minimapInsideZoom")) then if (GetCVar("minimapInsideZoom") + 0 >= 3) then Minimap:SetZoom(Minimap:GetZoom() - 1); tempzoom = 1; else Minimap:SetZoom(Minimap:GetZoom() + 1); tempzoom = -1; end end if (GetCVar("minimapInsideZoom") + 0 == Minimap:GetZoom()) then Minimap:SetZoom(Minimap:GetZoom() + tempzoom); isLastMinimapInside = true lastMinimapInsideCheckTimestamp = GetTime() return true else isLastMinimapInside = false lastMinimapInsideCheckTimestamp = GetTime() Minimap:SetZoom(Minimap:GetZoom() + tempzoom); return false end end --- Get the quest tag to display in the tooltip ---@param quest Quest ---@return string tag local function _GetQuestTag(quest) if quest.Type == "complete" then return "(" .. l10n("Complete") .. ")"; elseif _Questie_SilentGetQuestLogIndexByID(quest.Id) > 0 then return "(" .. l10n("Active") .. ")"; else local questType, questTag = QuestieDB.GetQuestTagInfo(quest.Id) if (QuestieEvent and QuestieEvent.activeQuests[quest.Id]) then return "(" .. l10n("Event") .. ")"; elseif (questType == 41) then return "(" .. l10n("PvP") .. ")"; elseif (QuestieDB.IsRepeatable(quest.Id)) then return "(" .. l10n("Repeatable") .. ")"; elseif (questType == 81 or questType == 83 or questType == 62 or questType == 1) then -- Dungeon or Legendary or Raid or Group(Elite) return "(" .. questTag .. ")"; elseif (Questie.IsSoD and QuestieDB.IsSoDRuneQuest(quest.Id)) then return "(" .. l10n("Rune") .. ")"; else return "(" .. l10n("Available") .. ")"; end end end function _MapIconTooltip:GetAvailableOrCompleteTooltip(icon) local tip = {}; tip.type = _GetQuestTag(icon.data) tip.title = QuestieLib:GetColoredQuestName(icon.data.Id, Questie.db.profile.enableTooltipsQuestLevel, false, true) tip.subData = icon.data.QuestData.Description tip.questId = icon.data.Id; return tip end function _MapIconTooltip:GetEventObjectiveTooltip(iconData) if iconData.Name then return { [iconData.ObjectiveData.Index] = { [iconData.ObjectiveData.Description] = { [iconData.Name] = true } } } else return { [iconData.ObjectiveData.Index] = { [iconData.ObjectiveData.Description] = true } } end end function _MapIconTooltip:GetObjectiveTooltip(icon) local tooltips = {} local iconData = icon.data local text = iconData.ObjectiveData.Description local color = QuestieLib:GetRGBForObjective(iconData.ObjectiveData) if iconData.ObjectiveData.Needed then if iconData.ObjectiveData.Type == "spell" and iconData.ObjectiveData.spawnList[iconData.ObjectiveTargetId].ItemId then text = color .. tostring(QuestieDB.QueryItemSingle(iconData.ObjectiveData.spawnList[iconData.ObjectiveTargetId].ItemId, "name")) else text = color .. tostring(iconData.ObjectiveData.Collected) .. "/" .. tostring(iconData.ObjectiveData.Needed) .. " " .. text end end if QuestieComms then local anotherPlayer = false; local quest = QuestieComms:GetQuest(iconData.Id) if quest then for playerName, objectiveData in pairs(quest) do local playerInfo = QuestiePlayer:GetPartyMemberByName(playerName) local playerColor local playerType = "" if playerInfo then playerColor = "|c" .. playerInfo.colorHex else playerColor = QuestieComms.remotePlayerClasses[playerName] if playerColor then playerColor = Questie:GetClassColor(playerColor) playerType = " (" .. l10n("Nearby") .. ")" end end if playerColor then local objectiveEntry = objectiveData[iconData.ObjectiveIndex] if not objectiveEntry then Questie:Debug(Questie.DEBUG_DEVELOP, "[_MapIconTooltip:GetObjectiveTooltip] No objective data for quest", quest.Id) objectiveEntry = {} -- This will make "GetRGBForObjective" return default color end local remoteColor = QuestieLib:GetRGBForObjective(objectiveEntry) local colorizedPlayerName = " (" .. playerColor .. playerName .. "|r" .. remoteColor .. ")|r" .. playerType local remoteText = iconData.ObjectiveData.Description if objectiveEntry and objectiveEntry.fulfilled and objectiveEntry.required then local fulfilled = objectiveEntry.fulfilled; local required = objectiveEntry.required; remoteText = remoteColor .. tostring(fulfilled) .. "/" .. tostring(required) .. " " .. remoteText .. colorizedPlayerName; else remoteText = remoteColor .. remoteText .. colorizedPlayerName; end local partyMemberTip = { [remoteText] = {}, } if iconData.Name then partyMemberTip[remoteText][iconData.Name] = true; end tinsert(tooltips, partyMemberTip); anotherPlayer = true; end end if anotherPlayer then local name = UnitName("player"); local _, classFilename = UnitClass("player"); local _, _, _, argbHex = GetClassColor(classFilename) name = " (|c" .. argbHex .. name .. "|r" .. color .. ")|r"; text = text .. name; end end end local t = { [text] = {}, } if iconData.Name then t[text][iconData.Name] = true; end tinsert(tooltips, 1, t); return tooltips end function _MapIconTooltip:AddTooltipsForQuest(icon, tip, quest, usedText) for text, nameTable in pairs(tip) do local data = {} data[text] = nameTable -- Add the data for the first time if not usedText[icon.data.Id] then usedText[icon.data.Id] = { [text] = true } tinsert(quest, data) -- add another line to an existing entry elseif not usedText[icon.data.Id][text] then tinsert(quest, data) usedText[icon.data.Id][text] = true else --We want to add more NPCs as possible candidates when shift is pressed. if icon.data.Name then for dataIndex, _ in pairs(quest) do if quest[dataIndex][text] then quest[dataIndex][text][icon.data.Name] = true; end end end end end end