v9.7.2: Ebonhold Database integration and core logic refinements
This commit is contained in:
@@ -0,0 +1,689 @@
|
||||
---@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")
|
||||
local function _Questie_SilentGetQuestLogIndexByID(questId)
|
||||
questId = tonumber(questId)
|
||||
if not questId then return 0 end
|
||||
|
||||
if _G.GetQuestLogIndexByID then
|
||||
return _G.GetQuestLogIndexByID(questId) or 0
|
||||
end
|
||||
|
||||
local n = (GetNumQuestLogEntries and select(1, GetNumQuestLogEntries())) or 0
|
||||
if n > 0 and GetQuestLogTitle then
|
||||
for i = 1, n do
|
||||
local _, _, _, _, isHeader, _, _, _, qid = GetQuestLogTitle(i)
|
||||
qid = tonumber(qid)
|
||||
if (not isHeader) and qid and qid == questId then
|
||||
return i
|
||||
end
|
||||
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()
|
||||
|
||||
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 pairs(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
|
||||
if iconData.Type == "available" or iconData.Type == "complete" then
|
||||
if not npcAndObjectOrder[iconData.Name] then
|
||||
npcAndObjectOrder[iconData.Name] = {};
|
||||
end
|
||||
|
||||
local tip = _MapIconTooltip:GetAvailableOrCompleteTooltip(icon)
|
||||
npcAndObjectOrder[iconData.Name][tip.title] = tip
|
||||
elseif iconData.ObjectiveData and iconData.ObjectiveData.Description then
|
||||
local key = iconData.Id
|
||||
if not questOrder[key] then
|
||||
questOrder[key] = {};
|
||||
end
|
||||
|
||||
local orderedTooltips = {}
|
||||
local _qid = tonumber(iconData.Id)
|
||||
if _qid and _Questie_SilentGetQuestLogIndexByID(_qid) <= 0 then
|
||||
return
|
||||
end
|
||||
local _ok = pcall(iconData.ObjectiveData.Update, iconData.ObjectiveData)
|
||||
if not _ok then
|
||||
return
|
||||
end
|
||||
if iconData.Type == "event" then
|
||||
local tip = _MapIconTooltip:GetEventObjectiveTooltip(icon.data)
|
||||
|
||||
-- We need to check for duplicates.
|
||||
local add = true;
|
||||
for _, data in pairs(questOrder[key]) do
|
||||
for text, _ in pairs(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 pairs(tooltips) do
|
||||
tinsert(orderedTooltips, 1, tip);
|
||||
end
|
||||
for _, tip in pairs(orderedTooltips) do
|
||||
local quest = questOrder[key]
|
||||
_MapIconTooltip:AddTooltipsForQuest(icon, tip, quest, usedText)
|
||||
end
|
||||
end
|
||||
elseif iconData.CustomTooltipData then
|
||||
questOrder[iconData.CustomTooltipData.Title] = {}
|
||||
tinsert(questOrder[iconData.CustomTooltipData.Title], iconData.CustomTooltipData.Body);
|
||||
elseif iconData.ManualTooltipData then
|
||||
manualOrder[iconData.ManualTooltipData.Title] = iconData.ManualTooltipData
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.miniMapIcon then
|
||||
for icon, _ in pairs(HBDPins.activeMinimapPins) do
|
||||
handleMapIcon(icon)
|
||||
end
|
||||
else
|
||||
for pin in HBDPins.worldmapProvider:GetMap():EnumeratePinsByTemplate("HereBeDragonsPinsTemplateQuestie") do
|
||||
handleMapIcon(pin.icon)
|
||||
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
|
||||
for npcOrObjectName, quests in pairs(self.npcAndObjectOrder) do -- this logic really needs to be improved
|
||||
haveGiver = true
|
||||
if shift and (not firstLine) then
|
||||
-- Spacer between NPCs
|
||||
self:AddLine(" ")
|
||||
end
|
||||
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
|
||||
|
||||
for _, questData in pairs(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 pairs(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 pairs(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 pairs(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);
|
||||
nextQuest = QuestieDB.GetQuest(nextQuest.nextQuestInChain)
|
||||
end
|
||||
end
|
||||
|
||||
if shift and reputationReward and next(reputationReward) then
|
||||
local rewardTable = {}
|
||||
local factionId, factionName
|
||||
local rewardValue
|
||||
local aldorPenalty, scryersPenalty
|
||||
for _, rewardPair in pairs(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, 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
|
||||
|
||||
rewardTable[#rewardTable + 1] = (rewardValue > 0 and "+" or "") .. rewardValue .. " " .. factionName
|
||||
end
|
||||
end
|
||||
|
||||
if aldorPenalty then
|
||||
factionName = select(1, GetFactionInfoByID(932))
|
||||
rewardTable[#rewardTable + 1] = aldorPenalty .. " " .. factionName
|
||||
elseif scryersPenalty then
|
||||
factionName = select(1, GetFactionInfoByID(934))
|
||||
rewardTable[#rewardTable + 1] = 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 pairs(self.questOrder) do -- this logic really needs to be improved
|
||||
---@type Quest
|
||||
local quest = QuestieDB.GetQuest(questId);
|
||||
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);
|
||||
if haveGiver then
|
||||
if shift and xpReward then
|
||||
self:AddLine(" ");
|
||||
self:AddDoubleLine(questTitle, "(" .. 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(questTitle, "(" .. 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(questTitle, "(" .. FormatLargeNumber(xpReward) .. xpString .. ")", 0.2, 1, 0.2, r, g, b);
|
||||
firstLine = false;
|
||||
elseif (firstLine and not shift) then
|
||||
self:AddDoubleLine(questTitle, "(" .. l10n('Hold Shift') .. ")", 0.2, 1, 0.2, 0.43, 0.43, 0.43); --"(Shift+click)"
|
||||
firstLine = false;
|
||||
else
|
||||
self:AddLine(questTitle);
|
||||
end
|
||||
end
|
||||
|
||||
local function _GetLevelString(creatureLevels, name)
|
||||
local levelString = name
|
||||
if creatureLevels[name] then
|
||||
local minLevel = creatureLevels[name][1]
|
||||
local maxLevel = creatureLevels[name][2]
|
||||
local rank = creatureLevels[name][3]
|
||||
if minLevel == maxLevel then
|
||||
levelString = name .. " (" .. minLevel
|
||||
else
|
||||
levelString = name .. " (" .. minLevel .. "-" .. maxLevel
|
||||
end
|
||||
|
||||
if rank and rank == 1 then
|
||||
levelString = levelString .. "+"
|
||||
end
|
||||
|
||||
levelString = levelString .. ")"
|
||||
end
|
||||
return levelString
|
||||
end
|
||||
|
||||
-- Used to get the white color for the quests which don't have anything to collect
|
||||
local defaultQuestColor = QuestieLib:GetRGBForObjective({})
|
||||
if shift then
|
||||
local creatureLevels = QuestieDB:GetCreatureLevels(quest) -- Data for min and max level
|
||||
local addedCreatureNames = {}
|
||||
for _, textData in pairs(textList) do
|
||||
for textLine, nameData in pairs(textData) do
|
||||
local dataType = type(nameData)
|
||||
if dataType == "table" then
|
||||
for name in pairs(nameData) do
|
||||
if (not addedCreatureNames[name]) then
|
||||
addedCreatureNames[name] = true
|
||||
name = _GetLevelString(creatureLevels, name)
|
||||
self:AddLine(" |cFFDDDDDD" .. name);
|
||||
end
|
||||
end
|
||||
elseif dataType == "string" and (not addedCreatureNames[nameData]) then
|
||||
addedCreatureNames[nameData] = true
|
||||
nameData = _GetLevelString(creatureLevels, nameData)
|
||||
self:AddLine(" |cFFDDDDDD" .. nameData);
|
||||
end
|
||||
self:AddLine(" " .. defaultQuestColor .. textLine);
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, textData in pairs(textList) do
|
||||
for textLine, _ in pairs(textData) do
|
||||
self:AddLine(" " .. defaultQuestColor .. textLine);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if next(self.npcAndObjectOrder) and next(self.manualOrder) then
|
||||
-- Spacer before townsfolk
|
||||
self:AddLine(" ")
|
||||
end
|
||||
|
||||
for title, data in pairs(self.manualOrder) do
|
||||
local body = data.Body
|
||||
self:AddLine(title)
|
||||
for _, stringOrTable in ipairs(body) do
|
||||
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
|
||||
end
|
||||
if self.miniMapIcon == false and not data.disableShiftToRemove then
|
||||
self:AddLine('|cFFa6a6a6Shift-click to hide|r') -- grey
|
||||
end
|
||||
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") .. ")";
|
||||
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
|
||||
@@ -0,0 +1,406 @@
|
||||
---@class QuestieTooltips
|
||||
local QuestieTooltips = QuestieLoader:CreateModule("QuestieTooltips");
|
||||
local _QuestieTooltips = QuestieTooltips.private
|
||||
-------------------------
|
||||
--Import modules.
|
||||
-------------------------
|
||||
---@type QuestieComms
|
||||
local QuestieComms = QuestieLoader:ImportModule("QuestieComms");
|
||||
---@type QuestieLib
|
||||
local QuestieLib = QuestieLoader:ImportModule("QuestieLib");
|
||||
---@type QuestiePlayer
|
||||
local QuestiePlayer = QuestieLoader:ImportModule("QuestiePlayer");
|
||||
---@type QuestieDB
|
||||
local QuestieDB = QuestieLoader:ImportModule("QuestieDB");
|
||||
---@type l10n
|
||||
local l10n = QuestieLoader:ImportModule("l10n")
|
||||
|
||||
--- COMPATIBILITY ---
|
||||
local UnitInParty = QuestieCompat.UnitInParty
|
||||
local IsInGroup = QuestieCompat.IsInGroup
|
||||
local GetClassColor = QuestieCompat.GetClassColor
|
||||
|
||||
local tinsert = table.insert
|
||||
QuestieTooltips.lastGametooltip = ""
|
||||
QuestieTooltips.lastGametooltipCount = -1;
|
||||
QuestieTooltips.lastGametooltipType = "";
|
||||
QuestieTooltips.lastFrameName = "";
|
||||
|
||||
QuestieTooltips.lookupByKey = {
|
||||
--["u_Grell"] = {questid, {"Line 1", "Line 2"}}
|
||||
}
|
||||
QuestieTooltips.lookupKeysByQuestId = {
|
||||
--["questId"] = {"u_Grell", ... }
|
||||
}
|
||||
|
||||
local MAX_GROUP_MEMBER_COUNT = 6
|
||||
|
||||
local _InitObjectiveTexts
|
||||
|
||||
---@param questId number
|
||||
---@param key string monster: m_, items: i_, objects: o_ + string name of the objective
|
||||
---@param objective table
|
||||
function QuestieTooltips:RegisterObjectiveTooltip(questId, key, objective)
|
||||
if not QuestieTooltips.lookupByKey[key] then
|
||||
QuestieTooltips.lookupByKey[key] = {};
|
||||
end
|
||||
if not QuestieTooltips.lookupKeysByQuestId[questId] then
|
||||
QuestieTooltips.lookupKeysByQuestId[questId] = {}
|
||||
end
|
||||
local tooltip = {
|
||||
questId = questId,
|
||||
objective = objective,
|
||||
};
|
||||
QuestieTooltips.lookupByKey[key][tostring(questId) .. " " .. objective.Index] = tooltip
|
||||
tinsert(QuestieTooltips.lookupKeysByQuestId[questId], key)
|
||||
end
|
||||
|
||||
---@param questId number
|
||||
---@param name string The name of the object or NPC the tooltip should show on
|
||||
---@param starterId number The ID of the object or NPC the tooltip should show on
|
||||
---@param key string @Either m_<npcId> or o_<objectId>
|
||||
function QuestieTooltips:RegisterQuestStartTooltip(questId, name, starterId, key)
|
||||
if not QuestieTooltips.lookupByKey[key] then
|
||||
QuestieTooltips.lookupByKey[key] = {};
|
||||
end
|
||||
if not QuestieTooltips.lookupKeysByQuestId[questId] then
|
||||
QuestieTooltips.lookupKeysByQuestId[questId] = {}
|
||||
end
|
||||
local tooltip = {
|
||||
questId = questId,
|
||||
name = name,
|
||||
starterId = starterId,
|
||||
};
|
||||
QuestieTooltips.lookupByKey[key][tostring(questId) .. " " .. name .. " " .. starterId] = tooltip
|
||||
tinsert(QuestieTooltips.lookupKeysByQuestId[questId], key)
|
||||
end
|
||||
|
||||
---@param questId number
|
||||
function QuestieTooltips:RemoveQuest(questId)
|
||||
if (not QuestieTooltips.lookupKeysByQuestId[questId]) then
|
||||
-- Tooltip has already been removed
|
||||
return
|
||||
end
|
||||
|
||||
-- Remove tooltip related keys from quest table so that
|
||||
-- it can be readded/registered by other quest functions.
|
||||
local quest = QuestieDB.GetQuest(questId)
|
||||
|
||||
if quest then
|
||||
for _, objective in pairs(quest.Objectives) do
|
||||
objective.AlreadySpawned = {}
|
||||
objective.hasRegisteredTooltips = false
|
||||
objective.registeredItemTooltips = false
|
||||
end
|
||||
|
||||
for _, objective in pairs(quest.SpecialObjectives) do
|
||||
objective.AlreadySpawned = {}
|
||||
objective.hasRegisteredTooltips = false
|
||||
objective.registeredItemTooltips = false
|
||||
end
|
||||
end
|
||||
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieTooltips:RemoveQuest]", questId)
|
||||
|
||||
for _, key in pairs(QuestieTooltips.lookupKeysByQuestId[questId] or {}) do
|
||||
--Count to see if we should remove the main object
|
||||
local totalCount = 0
|
||||
local totalRemoved = 0
|
||||
for _, tooltipData in pairs(QuestieTooltips.lookupByKey[key] or {}) do
|
||||
--Remove specific quest
|
||||
if (tooltipData.questId == questId and tooltipData.objective) then
|
||||
QuestieTooltips.lookupByKey[key][tostring(tooltipData.questId) .. " " .. tooltipData.objective.Index] = nil
|
||||
totalRemoved = totalRemoved + 1
|
||||
elseif (tooltipData.questId == questId and tooltipData.name) then
|
||||
QuestieTooltips.lookupByKey[key][tostring(tooltipData.questId) .. " " .. tooltipData.name .. " " .. tooltipData.starterId] = nil
|
||||
totalRemoved = totalRemoved + 1
|
||||
end
|
||||
totalCount = totalCount + 1
|
||||
end
|
||||
if (totalCount == totalRemoved) then
|
||||
QuestieTooltips.lookupByKey[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
QuestieTooltips.lookupKeysByQuestId[questId] = nil
|
||||
end
|
||||
|
||||
-- This code is related to QuestieComms, here we fetch all the tooltip data that exist in QuestieCommsData
|
||||
-- It uses a similar system like here with i_ID etc as keys.
|
||||
local function _FetchTooltipsForGroupMembers(key, tooltipData)
|
||||
local anotherPlayer = false;
|
||||
if QuestieComms and QuestieComms.data:KeyExists(key) then
|
||||
---@tooltipData @tooltipData[questId][playerName][objectiveIndex].text
|
||||
local tooltipDataExternal = QuestieComms.data:GetTooltip(key);
|
||||
for questId, playerList in pairs(tooltipDataExternal) do
|
||||
if (not tooltipData[questId]) then
|
||||
tooltipData[questId] = {
|
||||
title = QuestieLib:GetColoredQuestName(questId, Questie.db.profile.enableTooltipsQuestLevel, true, true)
|
||||
}
|
||||
end
|
||||
for playerName, _ in pairs(playerList) do
|
||||
local playerInfo = QuestiePlayer:GetPartyMemberByName(playerName);
|
||||
if playerInfo or QuestieComms.remotePlayerEnabled[playerName] then
|
||||
anotherPlayer = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if anotherPlayer then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if QuestieComms.data:KeyExists(key) and anotherPlayer then
|
||||
---@tooltipData @tooltipData[questId][playerName][objectiveIndex].text
|
||||
local tooltipDataExternal = QuestieComms.data:GetTooltip(key);
|
||||
for questId, playerList in pairs(tooltipDataExternal) do
|
||||
if (not tooltipData[questId]) then
|
||||
tooltipData[questId] = {
|
||||
title = QuestieLib:GetColoredQuestName(questId, Questie.db.profile.enableTooltipsQuestLevel, true, true)
|
||||
}
|
||||
end
|
||||
for playerName, objectives in pairs(playerList) do
|
||||
local playerInfo = QuestiePlayer:GetPartyMemberByName(playerName);
|
||||
if playerInfo or QuestieComms.remotePlayerEnabled[playerName] then
|
||||
anotherPlayer = true;
|
||||
for objectiveIndex, objective in pairs(objectives) do
|
||||
if (not objective) then
|
||||
objective = {}
|
||||
end
|
||||
|
||||
tooltipData[questId].objectivesText = _InitObjectiveTexts(tooltipData[questId].objectivesText, objectiveIndex, playerName)
|
||||
|
||||
local text;
|
||||
local color = QuestieLib:GetRGBForObjective(objective)
|
||||
|
||||
if objective.required then
|
||||
text = " " .. color .. tostring(objective.fulfilled) .. "/" .. tostring(objective.required) .. " " .. objective.text;
|
||||
else
|
||||
text = " " .. color .. objective.text;
|
||||
end
|
||||
|
||||
tooltipData[questId].objectivesText[objectiveIndex][playerName] = { ["color"] = color, ["text"] = text };
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return anotherPlayer
|
||||
end
|
||||
|
||||
---@param key string
|
||||
function QuestieTooltips:GetTooltip(key)
|
||||
Questie:Debug(Questie.DEBUG_SPAM, "[QuestieTooltips:GetTooltip]", key)
|
||||
if (not key) then
|
||||
return nil
|
||||
end
|
||||
|
||||
if QuestiePlayer.numberOfGroupMembers > MAX_GROUP_MEMBER_COUNT then
|
||||
return nil -- temporary disable tooltips in raids, we should make a proper fix
|
||||
end
|
||||
|
||||
--Do not remove! This is the datastrucutre for tooltipData!
|
||||
--[[tooltipdata[questId] = {
|
||||
title = coloredTitle,
|
||||
objectivesText = {
|
||||
[objectiveIndex] = {
|
||||
[playerName] = {
|
||||
[color] = color,
|
||||
[text] = text
|
||||
}
|
||||
}
|
||||
}
|
||||
}]]
|
||||
--
|
||||
local tooltipData = {}
|
||||
local tooltipLines = {}
|
||||
|
||||
if QuestieTooltips.lookupByKey[key] then
|
||||
local playerName = UnitName("player")
|
||||
for k, tooltip in pairs(QuestieTooltips.lookupByKey[key]) do
|
||||
if tooltip.name then
|
||||
if Questie.db.profile.showQuestsInNpcTooltip then
|
||||
local questString = QuestieLib:GetColoredQuestName(tooltip.questId, Questie.db.profile.enableTooltipsQuestLevel, true, true)
|
||||
tinsert(tooltipLines, questString)
|
||||
end
|
||||
else
|
||||
local objective = tooltip.objective
|
||||
if not (objective.IsSourceItem or objective.IsRequiredSourceItem) then
|
||||
-- Tooltip was registered for a sourceItem or requiredSourceItem and not a real "objective"
|
||||
objective:Update()
|
||||
end
|
||||
|
||||
local questId = tooltip.questId
|
||||
local objectiveIndex = objective.Index;
|
||||
if (not tooltipData[questId]) then
|
||||
tooltipData[questId] = {
|
||||
title = QuestieLib:GetColoredQuestName(questId, Questie.db.profile.enableTooltipsQuestLevel, true, true)
|
||||
}
|
||||
end
|
||||
if not QuestiePlayer.currentQuestlog[questId] then
|
||||
-- TODO: Is this still required?
|
||||
QuestieTooltips.lookupByKey[key][k] = nil
|
||||
else
|
||||
tooltipData[questId].objectivesText = _InitObjectiveTexts(tooltipData[questId].objectivesText, objectiveIndex, playerName)
|
||||
local text;
|
||||
local color = QuestieLib:GetRGBForObjective(objective)
|
||||
|
||||
if objective.Type == "spell" and objective.spawnList[tonumber(key:sub(3))].ItemId then
|
||||
text = " " .. color .. tostring(QuestieDB.QueryItemSingle(objective.spawnList[tonumber(key:sub(3))].ItemId, "name"));
|
||||
tooltipData[questId].objectivesText[objectiveIndex][playerName] = { ["color"] = color, ["text"] = text };
|
||||
elseif objective.Needed then
|
||||
text = " " .. color .. tostring(objective.Collected) .. "/" .. tostring(objective.Needed) .. " " .. tostring(objective.Description);
|
||||
tooltipData[questId].objectivesText[objectiveIndex][playerName] = { ["color"] = color, ["text"] = text };
|
||||
else
|
||||
text = " " .. color .. tostring(objective.Description);
|
||||
tooltipData[questId].objectivesText[objectiveIndex][playerName] = { ["color"] = color, ["text"] = text };
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local anotherPlayer = false
|
||||
if IsInGroup() then
|
||||
anotherPlayer = _FetchTooltipsForGroupMembers(key, tooltipData)
|
||||
end
|
||||
|
||||
local playerName = UnitName("player")
|
||||
|
||||
for questId, questData in pairs(tooltipData) do
|
||||
local hasObjective = false
|
||||
local tempObjectives = {}
|
||||
for _, playerList in pairs(questData.objectivesText or {}) do
|
||||
for objectivePlayerName, objectiveInfo in pairs(playerList) do
|
||||
local playerInfo = QuestiePlayer:GetPartyMemberByName(objectivePlayerName)
|
||||
local playerColor
|
||||
local playerType = ""
|
||||
if playerInfo then
|
||||
playerColor = "|c" .. playerInfo.colorHex
|
||||
elseif QuestieComms.remotePlayerEnabled[objectivePlayerName] and QuestieComms.remoteQuestLogs[questId] and QuestieComms.remoteQuestLogs[questId][objectivePlayerName] and (not Questie.db.profile.onlyPartyShared or UnitInParty(objectivePlayerName)) then
|
||||
playerColor = QuestieComms.remotePlayerClasses[playerName]
|
||||
if playerColor then
|
||||
playerColor = Questie:GetClassColor(playerColor)
|
||||
playerType = " (" .. l10n("Nearby") .. ")"
|
||||
end
|
||||
end
|
||||
if objectivePlayerName == playerName and anotherPlayer then -- why did we have this case
|
||||
local _, classFilename = UnitClass("player");
|
||||
local _, _, _, argbHex = GetClassColor(classFilename)
|
||||
objectiveInfo.text = objectiveInfo.text .. " (|c" .. argbHex .. objectivePlayerName .. "|r" .. objectiveInfo.color .. ")|r"
|
||||
elseif playerColor and objectivePlayerName ~= playerName then
|
||||
objectiveInfo.text = objectiveInfo.text .. " (" .. playerColor .. objectivePlayerName .. "|r" .. objectiveInfo.color .. ")|r" .. playerType
|
||||
end
|
||||
-- We want the player to be on top.
|
||||
if objectivePlayerName == playerName then
|
||||
tinsert(tempObjectives, 1, objectiveInfo.text);
|
||||
hasObjective = true
|
||||
elseif playerColor then
|
||||
tinsert(tempObjectives, objectiveInfo.text);
|
||||
hasObjective = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if hasObjective then
|
||||
tinsert(tooltipLines, questData.title);
|
||||
for _, text in pairs(tempObjectives) do
|
||||
tinsert(tooltipLines, text);
|
||||
end
|
||||
end
|
||||
end
|
||||
return tooltipLines
|
||||
end
|
||||
|
||||
_InitObjectiveTexts = function(objectivesText, objectiveIndex, playerName)
|
||||
if (not objectivesText) then
|
||||
objectivesText = {}
|
||||
end
|
||||
if (not objectivesText[objectiveIndex]) then
|
||||
objectivesText[objectiveIndex] = {}
|
||||
end
|
||||
if (not objectivesText[objectiveIndex][playerName]) then
|
||||
objectivesText[objectiveIndex][playerName] = {}
|
||||
end
|
||||
return objectivesText
|
||||
end
|
||||
|
||||
function QuestieTooltips:Initialize()
|
||||
-- For the clicked item frame.
|
||||
ItemRefTooltip:HookScript("OnTooltipSetItem", _QuestieTooltips.AddItemDataToTooltip)
|
||||
ItemRefTooltip:HookScript("OnHide", function(self)
|
||||
if (not self.IsForbidden) or (not self:IsForbidden()) then -- do we need this here also
|
||||
QuestieTooltips.lastGametooltip = ""
|
||||
QuestieTooltips.lastItemRefTooltip = ""
|
||||
QuestieTooltips.lastGametooltipItem = nil
|
||||
QuestieTooltips.lastGametooltipUnit = nil
|
||||
QuestieTooltips.lastGametooltipCount = 0
|
||||
QuestieTooltips.lastFrameName = "";
|
||||
end
|
||||
end)
|
||||
|
||||
-- For the hover frame.
|
||||
GameTooltip:HookScript("OnTooltipSetUnit", function(self)
|
||||
if QuestiePlayer.numberOfGroupMembers > MAX_GROUP_MEMBER_COUNT then
|
||||
-- When in a raid, we want as little code running as possible
|
||||
return
|
||||
end
|
||||
|
||||
_QuestieTooltips.AddUnitDataToTooltip(self)
|
||||
end)
|
||||
GameTooltip:HookScript("OnTooltipSetItem", _QuestieTooltips.AddItemDataToTooltip)
|
||||
GameTooltip:HookScript("OnShow", function(self)
|
||||
if QuestiePlayer.numberOfGroupMembers > MAX_GROUP_MEMBER_COUNT then
|
||||
-- When in a raid, we want as little code running as possible
|
||||
return
|
||||
end
|
||||
|
||||
if (not self.IsForbidden) or (not self:IsForbidden()) then -- do we need this here also
|
||||
QuestieTooltips.lastGametooltipItem = nil
|
||||
QuestieTooltips.lastGametooltipUnit = nil
|
||||
QuestieTooltips.lastGametooltipCount = 0
|
||||
QuestieTooltips.lastFrameName = "";
|
||||
end
|
||||
end)
|
||||
GameTooltip:HookScript("OnHide", function(self)
|
||||
if QuestiePlayer.numberOfGroupMembers > MAX_GROUP_MEMBER_COUNT then
|
||||
-- When in a raid, we want as little code running as possible
|
||||
return
|
||||
end
|
||||
|
||||
if (not self.IsForbidden) or (not self:IsForbidden()) then -- do we need this here also
|
||||
QuestieTooltips.lastGametooltip = ""
|
||||
QuestieTooltips.lastItemRefTooltip = ""
|
||||
QuestieTooltips.lastGametooltipItem = nil
|
||||
QuestieTooltips.lastGametooltipUnit = nil
|
||||
QuestieTooltips.lastGametooltipCount = 0
|
||||
end
|
||||
end)
|
||||
|
||||
-- Fired whenever the cursor hovers something with a tooltip. And then on every frame
|
||||
GameTooltip:HookScript("OnUpdate", function(self)
|
||||
if QuestiePlayer.numberOfGroupMembers > MAX_GROUP_MEMBER_COUNT then
|
||||
-- When in a raid, we want as little code running as possible
|
||||
return
|
||||
end
|
||||
|
||||
if (not self.IsForbidden) or (not self:IsForbidden()) then
|
||||
--Because this is an OnUpdate we need to check that it is actually not a Unit or Item to think its a
|
||||
local uName, unit = self:GetUnit()
|
||||
local iName, link = self:GetItem()
|
||||
local sName, spell = self:GetSpell()
|
||||
if (uName == nil and unit == nil and iName == nil and link == nil and sName == nil and spell == nil) and (
|
||||
QuestieTooltips.lastGametooltip ~= GameTooltipTextLeft1:GetText() or
|
||||
(not QuestieTooltips.lastGametooltipCount) or
|
||||
_QuestieTooltips:CountTooltip() < QuestieTooltips.lastGametooltipCount
|
||||
or QuestieTooltips.lastGametooltipType ~= "object"
|
||||
) and (not self.ShownAsMapIcon) then -- We are hovering over a Questie map icon which adds it's own tooltip
|
||||
_QuestieTooltips:AddObjectDataToTooltip(GameTooltipTextLeft1:GetText())
|
||||
QuestieTooltips.lastGametooltipCount = _QuestieTooltips:CountTooltip()
|
||||
end
|
||||
QuestieTooltips.lastGametooltip = GameTooltipTextLeft1:GetText()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return QuestieTooltips
|
||||
@@ -0,0 +1,397 @@
|
||||
---@type QuestieTooltips
|
||||
local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips");
|
||||
local _QuestieTooltips = QuestieTooltips.private
|
||||
|
||||
---@type l10n
|
||||
local l10n = QuestieLoader:ImportModule("l10n")
|
||||
|
||||
---@type QuestieDB
|
||||
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
|
||||
|
||||
--- COMPATIBILITY ---
|
||||
local UnitGUID = QuestieCompat.UnitGUID
|
||||
|
||||
local lastGuid
|
||||
|
||||
-- ============================================================
|
||||
-- NPCs that DROP an item which STARTS a quest (quest-starter drops)
|
||||
-- Shows in tooltip like:
|
||||
-- (yellow quest icon) Drops quest item !
|
||||
-- (item icon) [ItemLink (quality colored)] [ID: <QuestId> (light blue)]
|
||||
-- Works with Questie-335 compiled databases (binary + pointers).
|
||||
-- ============================================================
|
||||
|
||||
local QUEST_START_LINE = "|TInterface\\GossipFrame\\AvailableQuestIcon:18:18:0:0|t |cFFFFD200Drops a quest !|r"
|
||||
local QUEST_ID_COLOR = "|cFF80C8FF" -- light blue
|
||||
local RESET_COLOR = "|r"
|
||||
|
||||
local _npcQuestStarterDrops = nil
|
||||
local _npcQuestStarterDropsBuilt = false
|
||||
|
||||
-- ============================================================
|
||||
-- Quest state cache (so we don't scan the log for every tooltip)
|
||||
-- Hide lines if:
|
||||
-- - quest is in quest log
|
||||
-- - quest is flagged completed (turned in)
|
||||
-- ============================================================
|
||||
|
||||
local _questInLogCache = {}
|
||||
local _questInLogCacheBuilt = false
|
||||
|
||||
local function _WipeTable(t)
|
||||
if wipe then
|
||||
wipe(t)
|
||||
else
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _RebuildQuestInLogCache()
|
||||
_WipeTable(_questInLogCache)
|
||||
_questInLogCacheBuilt = true
|
||||
|
||||
if not GetNumQuestLogEntries or not GetQuestLogTitle then
|
||||
return
|
||||
end
|
||||
|
||||
local n = GetNumQuestLogEntries()
|
||||
for i = 1, n do
|
||||
-- WotLK: title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID
|
||||
local _, _, _, isHeader, _, _, _, questID = GetQuestLogTitle(i)
|
||||
if not isHeader and questID then
|
||||
questID = tonumber(questID)
|
||||
if questID then
|
||||
_questInLogCache[questID] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _QuestInLog(questId)
|
||||
if not questId then return false end
|
||||
if not _questInLogCacheBuilt then
|
||||
_RebuildQuestInLogCache()
|
||||
end
|
||||
return _questInLogCache[questId] == true
|
||||
end
|
||||
|
||||
-- Invalidate cache on quest log changes
|
||||
do
|
||||
local f = CreateFrame("Frame")
|
||||
f:RegisterEvent("QUEST_LOG_UPDATE")
|
||||
f:RegisterEvent("QUEST_ACCEPTED")
|
||||
f:RegisterEvent("QUEST_REMOVED")
|
||||
f:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
f:SetScript("OnEvent", function()
|
||||
_questInLogCacheBuilt = false
|
||||
end)
|
||||
end
|
||||
|
||||
local function _GetItemPointers()
|
||||
-- QuestieDB exposes these after DB init:
|
||||
-- QuestieDB.ItemPointers = QuestieDB.QueryItem.pointers
|
||||
if QuestieDB and type(QuestieDB.ItemPointers) == "table" then
|
||||
return QuestieDB.ItemPointers
|
||||
end
|
||||
if QuestieDB and QuestieDB.QueryItem and type(QuestieDB.QueryItem.pointers) == "table" then
|
||||
return QuestieDB.QueryItem.pointers
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Check if player already has the quest (in log or turned in)
|
||||
local function _PlayerHasQuest(questId)
|
||||
questId = tonumber(questId)
|
||||
if not questId then return false end
|
||||
|
||||
-- 1) Active in log
|
||||
if _QuestInLog(questId) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- 2) Turned in / completed
|
||||
if IsQuestFlaggedCompleted and IsQuestFlaggedCompleted(questId) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Optional fallback (some cores implement this reliably)
|
||||
if GetQuestLogIndexByID then
|
||||
local idx = GetQuestLogIndexByID(questId)
|
||||
if idx and idx > 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function _TryBuildNpcQuestStarterDrops()
|
||||
if _npcQuestStarterDropsBuilt and _npcQuestStarterDrops then
|
||||
return true
|
||||
end
|
||||
|
||||
-- DB not ready yet? Try again later.
|
||||
if not QuestieDB or type(QuestieDB.QueryItemSingle) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
local pointers = _GetItemPointers()
|
||||
if type(pointers) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
_npcQuestStarterDrops = {}
|
||||
|
||||
-- Helper function to process an item and add it to the lookup table
|
||||
local function processItem(itemId)
|
||||
local questId = QuestieDB.QueryItemSingle(itemId, "startQuest")
|
||||
if questId and questId ~= 0 then
|
||||
local npcDrops = QuestieDB.QueryItemSingle(itemId, "npcDrops")
|
||||
if npcDrops and type(npcDrops) == "table" then
|
||||
local itemName = QuestieDB.QueryItemSingle(itemId, "name")
|
||||
for _, npcId in pairs(npcDrops) do
|
||||
local list = _npcQuestStarterDrops[npcId]
|
||||
if not list then
|
||||
list = {}
|
||||
_npcQuestStarterDrops[npcId] = list
|
||||
end
|
||||
list[#list + 1] = { itemId = itemId, questId = tonumber(questId), name = itemName }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Iterate all items from compiled database
|
||||
for itemId, _ in pairs(pointers) do
|
||||
processItem(itemId)
|
||||
end
|
||||
|
||||
-- Also iterate Ascension override items (they don't have pointers)
|
||||
if QuestieDB.itemDataOverrides and type(QuestieDB.itemDataOverrides) == "table" then
|
||||
for itemId, _ in pairs(QuestieDB.itemDataOverrides) do
|
||||
processItem(itemId)
|
||||
end
|
||||
end
|
||||
|
||||
_npcQuestStarterDropsBuilt = true
|
||||
return true
|
||||
end
|
||||
|
||||
local function _TooltipHasQuestStarterLine(tooltip)
|
||||
local n = tooltip:NumLines()
|
||||
local base = tooltip:GetName() .. "TextLeft"
|
||||
for i = 1, n do
|
||||
local left = _G[base .. i]
|
||||
if left then
|
||||
local t = left:GetText()
|
||||
if t and t:find("Drops quest item", 1, true) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function _AddQuestStarterDropsToTooltip(npcId)
|
||||
if not _TryBuildNpcQuestStarterDrops() then return end
|
||||
if not _npcQuestStarterDrops then return end
|
||||
|
||||
local drops = _npcQuestStarterDrops[npcId]
|
||||
if not drops or #drops == 0 then return end
|
||||
|
||||
if _TooltipHasQuestStarterLine(GameTooltip) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Filter drops to only show items where player doesn't have the quest yet
|
||||
local filteredDrops = {}
|
||||
for _, info in ipairs(drops) do
|
||||
if info.questId and (not _PlayerHasQuest(info.questId)) then
|
||||
filteredDrops[#filteredDrops + 1] = info
|
||||
end
|
||||
end
|
||||
|
||||
if #filteredDrops == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
GameTooltip:AddLine(QUEST_START_LINE)
|
||||
|
||||
for _, info in ipairs(filteredDrops) do
|
||||
local itemId = info.itemId
|
||||
local questId = info.questId
|
||||
|
||||
-- Item link with correct quality color when cached by client
|
||||
local itemLink = select(2, GetItemInfo(itemId))
|
||||
if not itemLink then
|
||||
local itemName = info.name
|
||||
if not itemName or itemName == "" then
|
||||
itemName = "Item " .. tostring(itemId)
|
||||
end
|
||||
itemLink = ("|Hitem:%d:::::::::|h[%s]|h"):format(itemId, itemName)
|
||||
end
|
||||
|
||||
local icon = GetItemIcon and GetItemIcon(itemId)
|
||||
local qid = ("%s[ID: %d]%s"):format(QUEST_ID_COLOR, questId, RESET_COLOR)
|
||||
|
||||
if icon then
|
||||
GameTooltip:AddLine(("|T%s:14:14:0:0|t %s %s"):format(icon, itemLink, qid))
|
||||
else
|
||||
GameTooltip:AddLine(("%s %s"):format(itemLink, qid))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _QuestieTooltips:AddUnitDataToTooltip()
|
||||
if (self.IsForbidden and self:IsForbidden()) or (not Questie.db.profile.enableTooltips) then
|
||||
return
|
||||
end
|
||||
|
||||
local name, unitToken = self:GetUnit();
|
||||
if not unitToken then return end
|
||||
local guid = UnitGUID(unitToken);
|
||||
if (not guid) then
|
||||
guid = UnitGUID("mouseover");
|
||||
end
|
||||
|
||||
local type, _, _, _, _, npcId, _ = strsplit("-", guid or "");
|
||||
|
||||
if name and (type == "Creature" or type == "Vehicle") and (
|
||||
name ~= QuestieTooltips.lastGametooltipUnit or
|
||||
(not QuestieTooltips.lastGametooltipCount) or
|
||||
_QuestieTooltips:CountTooltip() < QuestieTooltips.lastGametooltipCount or
|
||||
QuestieTooltips.lastGametooltipType ~= "monster" or
|
||||
lastGuid ~= guid
|
||||
) then
|
||||
QuestieTooltips.lastGametooltipUnit = name
|
||||
|
||||
local tooltipData = QuestieTooltips:GetTooltip("m_" .. npcId);
|
||||
|
||||
if tooltipData then
|
||||
if Questie.db.profile.enableTooltipsNPCID == true then
|
||||
GameTooltip:AddDoubleLine("NPC ID", "|cFFFFFFFF" .. npcId .. "|r")
|
||||
end
|
||||
for _, v in pairs (tooltipData) do
|
||||
GameTooltip:AddLine(v)
|
||||
end
|
||||
else
|
||||
-- Even if Questie has no objective tooltip for this NPC, we still want to show quest-starter drops.
|
||||
if Questie.db.profile.enableTooltipsNPCID == true then
|
||||
GameTooltip:AddDoubleLine("NPC ID", "|cFFFFFFFF" .. npcId .. "|r")
|
||||
end
|
||||
end
|
||||
|
||||
local npcNum = tonumber(npcId)
|
||||
if npcNum then
|
||||
_AddQuestStarterDropsToTooltip(npcNum)
|
||||
end
|
||||
|
||||
QuestieTooltips.lastGametooltipCount = _QuestieTooltips:CountTooltip()
|
||||
end
|
||||
lastGuid = guid;
|
||||
QuestieTooltips.lastGametooltipType = "monster";
|
||||
end
|
||||
|
||||
-- =======================
|
||||
-- Rest of original file
|
||||
-- =======================
|
||||
|
||||
local lastItemId = 0;
|
||||
function _QuestieTooltips:AddItemDataToTooltip()
|
||||
if (self.IsForbidden and self:IsForbidden()) or (not Questie.db.profile.enableTooltips) then
|
||||
return
|
||||
end
|
||||
|
||||
local name, link = self:GetItem()
|
||||
local itemId
|
||||
if link then
|
||||
itemId = select(3, string.match(link, "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*):?(%d*):?(%-?%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?"))
|
||||
end
|
||||
if name and itemId and (
|
||||
name ~= QuestieTooltips.lastGametooltipItem or
|
||||
(not QuestieTooltips.lastGametooltipCount) or
|
||||
_QuestieTooltips:CountTooltip() < QuestieTooltips.lastGametooltipCount or
|
||||
QuestieTooltips.lastGametooltipType ~= "item" or
|
||||
lastItemId ~= itemId or
|
||||
QuestieTooltips.lastFrameName ~= self:GetName()
|
||||
) then
|
||||
QuestieTooltips.lastGametooltipItem = name
|
||||
local tooltipData = QuestieTooltips:GetTooltip("i_" .. (itemId or 0));
|
||||
if tooltipData then
|
||||
if Questie.db.profile.enableTooltipsItemID == true then
|
||||
GameTooltip:AddDoubleLine("Item ID", "|cFFFFFFFF" .. itemId .. "|r")
|
||||
end
|
||||
for _, v in pairs (tooltipData) do
|
||||
self:AddLine(v)
|
||||
end
|
||||
end
|
||||
QuestieTooltips.lastGametooltipCount = _QuestieTooltips:CountTooltip()
|
||||
end
|
||||
lastItemId = itemId;
|
||||
QuestieTooltips.lastGametooltipType = "item";
|
||||
QuestieTooltips.lastFrameName = self:GetName();
|
||||
end
|
||||
|
||||
function _QuestieTooltips:AddObjectDataToTooltip(name)
|
||||
if (not Questie.db.profile.enableTooltips) then
|
||||
return
|
||||
end
|
||||
if name then
|
||||
local titleAdded = false
|
||||
local lookup = l10n.objectNameLookup[name] or {}
|
||||
local count = table.getn(lookup)
|
||||
|
||||
if Questie.db.profile.enableTooltipsObjectID == true and count ~= 0 then
|
||||
if count == 1 then
|
||||
GameTooltip:AddDoubleLine("Object ID", "|cFFFFFFFF" .. lookup[1] .. "|r")
|
||||
else
|
||||
GameTooltip:AddDoubleLine("Object ID", "|cFFFFFFFF" .. lookup[1] .. " (" .. count .. ")|r")
|
||||
end
|
||||
end
|
||||
|
||||
local alreadyAddedObjectiveLines = {}
|
||||
for _, gameObjectId in pairs(lookup) do
|
||||
local tooltipData = QuestieTooltips:GetTooltip("o_" .. gameObjectId);
|
||||
|
||||
if type(gameObjectId) == "number" and tooltipData then
|
||||
if (not titleAdded) then
|
||||
GameTooltip:AddLine(tooltipData[1])
|
||||
titleAdded = true
|
||||
end
|
||||
|
||||
if tooltipData[2] then
|
||||
-- Quest has objectives
|
||||
for index, line in pairs (tooltipData) do
|
||||
if index > 1 and (not alreadyAddedObjectiveLines[line]) then -- skip the first entry, it's the title
|
||||
local _, _, acquired, needed = string.find(line, "(%d+)/(%d+)")
|
||||
-- We need "tonumber", because acquired can contain parts of the color string
|
||||
if acquired and tonumber(acquired) == tonumber(needed) then
|
||||
-- We don't want to show completed objectives on game objects
|
||||
break;
|
||||
end
|
||||
alreadyAddedObjectiveLines[line] = true
|
||||
GameTooltip:AddLine(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end
|
||||
QuestieTooltips.lastGametooltipType = "object";
|
||||
end
|
||||
|
||||
function _QuestieTooltips:CountTooltip()
|
||||
local tooltipCount = 0
|
||||
for i = 1, GameTooltip:NumLines() do
|
||||
local frame = _G["GameTooltipTextLeft"..i]
|
||||
if frame and frame:GetText() then
|
||||
tooltipCount = tooltipCount + 1
|
||||
else
|
||||
return tooltipCount
|
||||
end
|
||||
end
|
||||
return tooltipCount
|
||||
end
|
||||
Reference in New Issue
Block a user