QuestPlates

QuestPlates

161k Downloads

Updates for 11.2 ? LUA Error since PrePatch

Niyuni opened this issue · 3 comments

commented

433x QuestPlates/QuestPlates.lua:113: attempt to call field 'SurfaceArgs' (a nil value)
[string "@QuestPlates/QuestPlates.lua"]:113: in function GetQuestProgress' [string "@QuestPlates/QuestPlates.lua"]:260: in function <QuestPlates/QuestPlates.lua:247> [string "@QuestPlates/QuestPlates.lua"]:326: in function ?'
[string "@QuestPlates/semlib/eve.lua"]:15: in function <QuestPlates/semlib/eve.lua:12>
[string "@QuestPlates/semlib/SemlarPlates.lua"]:61: in function `?'
[string "@QuestPlates/semlib/eve.lua"]:15: in function <QuestPlates/semlib/eve.lua:12>

Locals:
unitID = "nameplate1"
tooltipData =

{
dataInstanceID = 5650
type = 2
guid = "Creature-0-4245-2552-212-223657-00004883F4"
lines =
{
}
healthGUID = "Creature-0-4245-2552-212-223657-00004883F4"
}
progressGlob = nil
questType = nil
objectiveCount = 0
questTexture = nil
questLogIndex = nil
questID = nil
(for index) = 3
(for limit) = 5
(for step) = 1
i = 3
line =
{
leftColor =
{
}
type = 0
leftText = "Elementar"
}
(*temporary) = nil
(*temporary) =
{
leftColor =
{
}
type = 0
leftText = "Elementar"
}
(*temporary) = "attempt to call field 'SurfaceArgs' (a nil value)"
ActiveWorldQuests =
{
}
OurName = "Niyuni"

commented

Here's your fix.

Event Handling:

The PLAYER_LOGIN, QUEST_ACCEPTED, QUEST_REMOVED, and QUEST_WATCH_LIST_CHANGED events correctly update the ActiveWorldQuests table and trigger updates to quest progress.

Tooltip Processing:

The GetQuestProgress function safely handles the tooltip data and includes a fallback if TooltipUtil.SurfaceArgs is unavailable. This ensures no errors occur during the tooltip parsing.

Quest Icon Management:

The UpdateQuestIcon function is responsible for updating the quest icons on nameplates based on the current quest progress, handling various quest states, and ensuring the correct visuals are displayed.

Re-anchoring and Loading:

The ADDON_LOADED event re-anchors the quest icons when the addon is loaded, ensuring the visual settings are applied.

-- QuestPlates.lua

-- ICON SETTINGS

QuestPlateSettings = {
    AnchorPoint = 'RIGHT', -- Point of icon to anchor to nameplate (CENTER, LEFT, RIGHT, TOP, BOTTOM)
    RelativeTo = 'LEFT',   -- Point of nameplate to anchor icon to (CENTER, LEFT, RIGHT, TOP, BOTTOM)
    OffsetX = 0,           -- Horizontal offset for icon (from anchor point)
    OffsetY = 0,           -- Vertical offset for icon
    IconScale = 1,         -- Scale for icon
}

local addonName, addon = ...
local E = addon:Eve()

local TextureAtlases = {
    ['item'] = 'Banker', -- bag icon, you have to loot something for this quest
}

local ActiveWorldQuests = {}



do
    function E:PLAYER_LOGIN()
        local uiMapID = C_Map.GetBestMapForUnit('player')
        if uiMapID then
            for _, task in pairs(C_TaskQuest.GetQuestsForPlayerByMapID(uiMapID) or {}) do
                if task.inProgress then
                    local questID = task.questId
                    local questName = C_TaskQuest.GetQuestInfoByQuestID(questID)
                    if questName then
                        ActiveWorldQuests[questName] = questID
                    end
                end
            end
        end
    end

    function E:QUEST_ACCEPTED(questLogIndex, questID, ...)
        if questID and C_QuestLog.IsQuestTask(questID) then
            local questName = C_TaskQuest.GetQuestInfoByQuestID(questID)
            if questName then
                ActiveWorldQuests[questName] = questID
            end
        end
        E:UNIT_QUEST_LOG_CHANGED()
    end
    
    function E:QUEST_REMOVED(questID)
        local questName = C_TaskQuest.GetQuestInfoByQuestID(questID)
        if questName and ActiveWorldQuests[questName] then
            ActiveWorldQuests[questName] = nil
        end
        E:UNIT_QUEST_LOG_CHANGED()
    end
    
    function E:QUEST_WATCH_LIST_CHANGED(questID, added)
        E:QUEST_ACCEPTED(nil, questID)
    end
end



local OurName = UnitName('player')
QuestLogIndex = {}

function GetQuestProgress(unitID)
	if not C_QuestLog.UnitIsRelatedToActiveQuest(unitID) then return end

	local tooltipData = C_TooltipInfo.GetUnit(unitID)
	local progressGlob
	local questType
	local objectiveCount = 0
	local questLogIndex
	local questID

	for i = 3, #tooltipData.lines do
		local line = tooltipData.lines[i]

		-- Check if TooltipUtil.SurfaceArgs exists
		if TooltipUtil and TooltipUtil.SurfaceArgs then
			TooltipUtil.SurfaceArgs(line)
		else
			-- Fallback logic if SurfaceArgs is not available
		end

		if line.type == 17 and line.id then
			local text, objectiveType, finished = GetQuestObjectiveInfo(line.id, 1, false)
			questID = questID or line.id or text and ActiveWorldQuests[text]
			local playerName = ""
			local progressText = text
			local isQuestText = not not progressText

			if playerName and playerName ~= '' and playerName ~= OurName then
				if not questType then
					questType = 2
				end
			else
				if isQuestText then
					local x, y = strmatch(progressText, '(%d+)/(%d+)')
					if x and y then
						local numLeft = y - x
						if numLeft > objectiveCount then
							objectiveCount = numLeft
						end
					else
						local progress = tonumber(strmatch(progressText, '([%d%.]+)%%'))
						if progress and progress <= 100 then
							local questType = 3
							return text, questType, ceil(100 - progress), questID
						end
					end

					if not x or (x and y and x ~= y) then
						progressGlob = progressGlob and progressGlob .. '\n' .. progressText or progressText
					end
				elseif ActiveWorldQuests[text] then
					local progress = C_TaskQuest.GetQuestProgressBarInfo(questID)
					if progress then
						local questType = 3
						return text, questType, ceil(100 - progress), questID
					end
				elseif QuestLogIndex[text] then
					questLogIndex = QuestLogIndex[text]
				end
			end
		end
	end
	
	return progressGlob, progressGlob and 1 or questType, objectiveCount, questLogIndex, questID
end


local QuestPlates = {} -- [plate] = f
function E:OnNewPlate(f, plate)
	local frame = CreateFrame('frame', nil, f)
	frame:Hide()
	frame:SetAllPoints(f)
	QuestPlates[plate] = frame
	
	local icon = frame:CreateTexture(nil, nil, nil, 0)
	icon:SetSize(28, 22)
	icon:SetTexture('Interface/QuestFrame/AutoQuest-Parts')
	icon:SetTexCoord(0.30273438, 0.41992188, 0.015625, 0.953125)
	icon:SetPoint(QuestPlateSettings.AnchorPoint or 'RIGHT', frame, QuestPlateSettings.RelativeTo or 'LEFT', (QuestPlateSettings.OffsetX or 0) / (QuestPlateSettings.IconScale or 1), (QuestPlateSettings.OffsetY or 0) / (QuestPlateSettings.IconScale or 1))
	frame:SetScale(QuestPlateSettings.IconScale or 1)
	frame.jellybean = icon
	
	local itemTexture = frame:CreateTexture(nil, nil, nil, 1)
	itemTexture:SetPoint('TOPRIGHT', icon, 'BOTTOMLEFT', 12, 12)
	itemTexture:SetSize(16, 16)
	itemTexture:SetMask('Interface/CharacterFrame/TempPortraitAlphaMask')
	itemTexture:Hide()
	frame.itemTexture = itemTexture
	
	-- Loot icon, display if mob needs to be looted for quest item
	local lootIcon = frame:CreateTexture(nil, nil, nil, 1)
	lootIcon:SetAtlas('Banker')
	lootIcon:SetSize(16, 16)
	lootIcon:SetPoint('TOPLEFT', icon, 'BOTTOMRIGHT', -12, 12)
	lootIcon:Hide()
	frame.lootIcon = lootIcon
	
	local iconText = frame:CreateFontString(nil, 'OVERLAY', 'SystemFont_Outline_Small')
	iconText:SetPoint('CENTER', icon, 0.8, 0)
	iconText:SetShadowOffset(1, -1)
	--iconText:SetText(math.random(22))
	iconText:SetTextColor(1,.82,0)
	frame.iconText = iconText
	
	-- todo: add setting for displaying quest text again
	local questText = frame:CreateFontString(nil, 'BACKGROUND', 'GameFontWhiteSmall')
	questText:SetPoint('TOP', frame, 'BOTTOM')
	questText:SetShadowOffset(1, -1)
	questText:Hide()
	frame.questText = questText
	
	local qmark = frame:CreateTexture(nil, 'OVERLAY')
	qmark:SetSize(28, 28)
	qmark:SetPoint('CENTER', icon)
	qmark:SetTexture('Interface/WorldMap/UI-WorldMap-QuestIcon')
	qmark:SetTexCoord(0, 0.56, 0.5, 1)
	qmark:SetAlpha(0)
	
	local duration = 1
	local group = qmark:CreateAnimationGroup()
	local alpha = group:CreateAnimation('Alpha')
	alpha:SetOrder(1)
	alpha:SetFromAlpha(0)
	alpha:SetToAlpha(1)
	alpha:SetDuration(0)
	
	local translation = group:CreateAnimation('Translation')
	translation:SetOrder(1)
	translation:SetOffset(0, 20)
	translation:SetDuration(duration)
	translation:SetSmoothing('OUT')
	
	local alpha2 = group:CreateAnimation('Alpha')
	alpha2:SetOrder(1)
	alpha2:SetFromAlpha(1)
	alpha2:SetToAlpha(0)
	alpha2:SetDuration(duration)
	alpha2:SetSmoothing('OUT')
	
	frame.ani = group
	
	frame:HookScript('OnShow', function(self)
		group:Play()
	end)
	
end

local function UpdateQuestIcon(plate, unitID)
	local Q = QuestPlates[plate]
	local unitID = unitID or addon:GetUnitForPlate(plate)
	if not Q then return end
	
	local scenarioName, currentStage, numStages, flags, _, _, _, xp, money, scenarioType, _, textureKitID = C_Scenario.GetInfo()
	local inChallengeMode = (scenarioType == LE_SCENARIO_TYPE_CHALLENGE_MODE)
	local guid = UnitGUID(unitID)
	if inChallengeMode and guid then -- C_MythicPlus.IsMythicPlusActive() and guid then
		Q:Hide()
		return
	end
	
	local progressGlob, questType, objectiveCount, questLogIndex, questID = GetQuestProgress(unitID)
	if progressGlob and questType ~= 2 then
		Q.questText:SetText(progressGlob or '')
		
		if questType == 3 then -- todo: progress bar
			Q.iconText:SetText(objectiveCount > 0 and objectiveCount or '?')
		else
			Q.iconText:SetText(objectiveCount > 0 and objectiveCount or '?')
		end

		if questType == 1 then
			Q.jellybean:SetDesaturated(false)
			Q.iconText:SetTextColor(1, .82, 0)
		elseif questType == 2 then
			Q.jellybean:SetDesaturated(true)
			Q.iconText:SetTextColor(1, 1, 1)
		elseif questType == 3 then
			Q.jellybean:SetDesaturated(false)
			Q.iconText:SetTextColor(0.2, 1, 1)
		end
		Q.itemTexture:Hide()
		Q.lootIcon:Hide()
		if questLogIndex or questID then
			if questID then
				for i = 1, 10 do
					local text, objectiveType, finished = GetQuestObjectiveInfo(questID, i, false)
					if not text then break end
					if not finished and (objectiveType == 'item' or objectiveType == 'object') then
						Q.lootIcon:Show()
					end
				end
			else
				local info = C_QuestLog.GetInfo(questLogIndex)
				if info then
					for i = 1, GetNumQuestLeaderBoards(questLogIndex) or 0 do
						local text, objectiveType, finished = GetQuestObjectiveInfo(info.questID, i, false)
						if not finished and (objectiveType == 'item' or objectiveType == 'object') then
							Q.lootIcon:Show()
						end
					end
				end
			end
			
			if questLogIndex then
				local link, itemTexture, charges, showItemWhenComplete = GetQuestLogSpecialItemInfo(questLogIndex)
				if link and itemTexture then
					Q.itemTexture:SetTexture(itemTexture)
					Q.itemTexture:Show()
				else
					Q.itemTexture:Hide()
				end
			end
		end
		
		if not Q:IsVisible() then
			Q.ani:Stop()
			Q:Show()
			Q.ani:Play()
		end
		--Q:Show()
	else
		Q:Hide()
	end	
end

function E:OnPlateShow(f, plate, unitID)
	UpdateQuestIcon(plate, unitID)
end

QuestObjectiveStrings = {}
local function CacheQuestIndexes()
	wipe(QuestLogIndex)
	for i = 1, C_QuestLog.GetNumQuestLogEntries() do	
		-- for i = 1, GetNumQuestLogEntries() do if not select(4,GetQuestLogTitle(i)) and select(11,GetQuestLogTitle(i)) then QuestLogPushQuest(i) end end
		local info = C_QuestLog.GetInfo(i)		
		if info and not info.isHeader then
			QuestLogIndex[info.title] = i
			for objectiveID = 1, GetNumQuestLeaderBoards(i) or 0 do
				local objectiveText, objectiveType, finished, numFulfilled, numRequired = GetQuestObjectiveInfo(info.questID, objectiveID, false)
				if objectiveText then
					QuestObjectiveStrings[ info.title .. objectiveText ] = {info.questID, objectiveID}
				end
			end
		end
	end
	
	for plate, f in pairs(addon:GetActiveNameplates()) do
		UpdateQuestIcon(plate, f._unitID)
	end
end

function E:UNIT_QUEST_LOG_CHANGED(unitID)
	if unitID == 'player' then
		CacheQuestIndexes()
	else	
		for plate in pairs(addon:GetActiveNameplates()) do
			UpdateQuestIcon(plate)
		end
	end
end

function E:QUEST_LOG_UPDATE()
	CacheQuestIndexes()
end
E:UnregisterEvent('QUEST_LOG_UPDATE')

function E:PLAYER_LEAVING_WORLD()
	E:UnregisterEvent('QUEST_LOG_UPDATE')
end

function E:PLAYER_ENTERING_WORLD()
	E:RegisterEvent('QUEST_LOG_UPDATE')
end

-- Reanchor any existing nameplate icons after settings load
function E:ADDON_LOADED(loadedAddon)
	if loadedAddon == addonName then
		for plate, f in pairs(addon:GetAllNameplates()) do
			local frame = QuestPlates[plate]
			if frame then
				frame.jellybean:ClearAllPoints()
				frame.jellybean:SetPoint(QuestPlateSettings.AnchorPoint or 'RIGHT', frame, QuestPlateSettings.RelativeTo or 'LEFT', (QuestPlateSettings.OffsetX or 0) / (QuestPlateSettings.IconScale or 1), (QuestPlateSettings.OffsetY or 0) / (QuestPlateSettings.IconScale or 1))
				frame:SetScale(QuestPlateSettings.IconScale or 1)
			end
		end	
		self:UnregisterEvent("ADDON_LOADED")
	end
end
commented

Here's a proper release tag with updated code for TWW.

https://github.com/donniedice/QuestPlates/releases/tag/v2.0.0

I've submitted pull request to merge my fork.. dude hasn't touched his github in over a year.

commented

You're a legend <3 Thank u