---------------------------------------------------------------------------- -- TomTom: A navigational assistant for World of Warcraft -- CrazyTaxi: A crazy-taxi style arrow used for waypoint navigation. -- concept taken from MapNotes2 (Thanks to Mery for the idea, along -- with the artwork.) ---------------------------------------------------------------------------- local function log(msg) DEFAULT_CHAT_FRAME:AddMessage(msg) end -- alias for convenience --------------------------------------------------------------------------------------------------- math.modf = function(number) local fractional = Questie:Modulo(number, 1) local integral = number - fractional return integral, fractional end --------------------------------------------------------------------------------------------------- function GetPlayerFacing() local p = Minimap local m = ({p:GetChildren()})[9] return m:GetFacing() end --------------------------------------------------------------------------------------------------- local sformat = string.format local function ColorGradient(perc, tablee) local num = table.getn(tablee) local hexes = tablee[1] == "string" if perc == 1 then return tablee[num-2], tablee[num-1], tablee[num] end num = num / 3 local segment, relperc = math.modf(perc*(num-1)) local r1, g1, b1, r2, g2, b2 r1, g1, b1 = tablee[(segment*3)+1], tablee[(segment*3)+2], tablee[(segment*3)+3] r2, g2, b2 = tablee[(segment*3)+4], tablee[(segment*3)+5], tablee[(segment*3)+6] if not r1 then return end if not r2 or not g2 or not b2 then return r1, g1, b1 else return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc end end --------------------------------------------------------------------------------------------------- local twopi = math.pi * 2 function WayFrame_OnClick() if (MouseIsOver(TomTomCrazyArrow) ~= nil) and (arg1 == "RightButton") then TomTomCrazyArrow:Hide() end end --------------------------------------------------------------------------------------------------- local wayframe = CreateFrame("Button", "TomTomCrazyArrow", UIParent) wayframe:SetHeight(42) wayframe:SetWidth(56) wayframe:SetPoint("CENTER", 0, 0) wayframe:EnableMouse(true) wayframe:SetMovable(true) wayframe:Hide() -- Frame used to control the scaling of the title and friends local titleframe = CreateFrame("Frame", nil, wayframe) wayframe.title = titleframe:CreateFontString("OVERLAY", nil, "GameFontHighlightSmall") wayframe.status = titleframe:CreateFontString("OVERLAY", nil, "GameFontNormalSmall") wayframe.tta = titleframe:CreateFontString("OVERLAY", nil, "GameFontNormalSmall") wayframe.title:SetPoint("TOP", wayframe, "BOTTOM", 0, 0) wayframe.status:SetPoint("TOP", wayframe.title, "BOTTOM", 0, 0) wayframe.tta:SetPoint("TOP", wayframe.status, "BOTTOM", 0, 0) --------------------------------------------------------------------------------------------------- local function OnDragStart(self, button) if IsControlKeyDown() and IsShiftKeyDown() then this:StartMoving() wayframe:SetClampedToScreen(true); end end --------------------------------------------------------------------------------------------------- local function OnDragStop(self, button) this:StopMovingOrSizing() end --------------------------------------------------------------------------------------------------- local function OnEvent(self, event, ...) if event == "ZONE_CHANGED_NEW_AREA" then self:Show() end end --------------------------------------------------------------------------------------------------- wayframe:SetScript("OnDragStart", OnDragStart) wayframe:SetScript("OnDragStop", OnDragStop) wayframe:RegisterForDrag("LeftButton") wayframe:RegisterEvent("ZONE_CHANGED_NEW_AREA") wayframe:SetScript("OnEvent", OnEvent) wayframe.arrow = wayframe:CreateTexture("OVERLAY") wayframe.arrow:SetTexture("Interface\\AddOns\\!Questie\\Images\\Arrow") wayframe.arrow:SetAllPoints() --------------------------------------------------------------------------------------------------- local active_point, arrive_distance, showDownArrow, point_title, arrow_objective, isHide function SetCrazyArrow(point, dist, title) active_point = point arrive_distance = dist point_title = title if active_point and not isHide then wayframe.title:SetText(point_title or "Unknown waypoint") wayframe:Show() else wayframe:Hide() end end --------------------------------------------------------------------------------------------------- function SetArrowFromIcon(icon) local targetPoint = { ["c"] = icon.data.continent, ["z"] = icon.data.zoneid, ["x"] = icon.averageX, ["y"] = icon.averageY } local quest = QuestieHashMap[icon.data.questHash] local data = { ["point"] = targetPoint, ["questName"] = quest.name, ["questLevel"] = quest.questLevel } SetArrowFromData(data) end --------------------------------------------------------------------------------------------------- function SetArrowFromData(data) local colorString = "|c" .. QuestieTracker:GetDifficultyColor(data.questLevel) local title = colorString title = title .. "[" .. data.questLevel .. "] " if QuestieConfig.boldColors then title = title .. "|r|cFFFFFFFF" .. data.questName .. "|r" else title = title .. data.questName .. "|r" end SetCrazyArrow(data.point, 0, title) arrow_objective = nil arrow_data = data end --------------------------------------------------------------------------------------------------- function SetArrowObjective(hash) if arrow_objective == hash then wayframe:Hide(); arrow_objective = 0; return; end arrow_objective = hash arrow_data = nil if not QuestieCachedQuests[hash] or not QuestieCachedQuests[hash]["arrowPoint"] then return end local objective = QuestieCachedQuests[hash]["arrowPoint"] SetCrazyArrow(objective, objective.dist, objective.title) end --------------------------------------------------------------------------------------------------- function RemoveCrazyArrow(hash) if hash then if (TomTomCrazyArrow:IsVisible() ~= nil) and (arrow_objective == hash or arrow_data) then TomTomCrazyArrow:Hide() end end if (QuestieConfig.arrowEnabled == false) then TomTomCrazyArrow:Hide() end end --------------------------------------------------------------------------------------------------- local status = wayframe.status; local tta = wayframe.tta; local arrow = wayframe.arrow; local arrowLastUpdate = 0; local LastPlayerPosition = {}; local count = 0; local last_distance = 0; local tta_throttle = 0; local speed = 0; local speed_count = 0; local function OnUpdate(self, elapsed) local self = this local elapsed = 1/GetFramerate() local dist,x,y if not UnitIsDeadOrGhost("player") then if arrow_objective then if QuestieCachedQuests[arrow_objective] then local objective = QuestieCachedQuests[arrow_objective]["arrowPoint"] if objective then SetCrazyArrow(objective, objective.dist, objective.title) end else self:Hide() end elseif arrow_data then SetArrowFromData(arrow_data) end if not active_point then self:Hide() return end end --While dead this maintains the corpseArrow if UnitIsDeadOrGhost("player") and (UnitIsDead("player") ~= 1) and (bgactive == false) then Questie:OnUpdate(elapsed) end local dist,x,y = GetDistanceToIcon(active_point) if GetTime() - arrowLastUpdate >= .04 then if (TomTomCrazyArrow:IsVisible() ~= nil) then if not dist or IsInInstance() then if not active_point.x and not active_point.y then active_point = nil end self:Hide() return end status:SetText(sformat("%d yards", dist)) local cell -- Showing the arrival arrow? if dist <= 5 then if not showDownArrow then arrow:SetHeight(70) arrow:SetWidth(53) arrow:SetTexture("Interface\\AddOns\\!Questie\\Images\\Arrow-UP") arrow:SetVertexColor(0, 1, 0) showDownArrow = true end count = count + 1 if count >= 55 then count = 0 end cell = count local column = Questie:Modulo(cell, 9) local row = floor(cell / 9) local xstart = (column * 53) / 512 local ystart = (row * 70) / 512 local xend = ((column + 1) * 53) / 512 local yend = ((row + 1) * 70) / 512 arrow:SetTexCoord(xstart,xend,ystart,yend) else if showDownArrow then arrow:SetHeight(56) arrow:SetWidth(42) arrow:SetTexture("Interface\\AddOns\\!Questie\\Images\\Arrow") showDownArrow = false end local degtemp = GetDirectionToIcon(active_point); if degtemp < 0 then degtemp = degtemp + 360; end local angle = math.rad(degtemp) local player = GetPlayerFacing() + 0.1 angle = angle - player local perc = 1- math.abs(((math.pi - math.abs(angle)) / math.pi)) local gr,gg,gb = 1, 1, 1 local mr,mg,mb = 0.75, 0.75, 0.75 local br,bg,bb = 0.5, 0.5, 0.5 local tablee = {}; table.insert(tablee, gr) table.insert(tablee, gg) table.insert(tablee, gb) table.insert(tablee, mr) table.insert(tablee, mg) table.insert(tablee, mb) table.insert(tablee, br) table.insert(tablee, bg) table.insert(tablee, bb) local r,g,b = ColorGradient(perc,tablee) if not g then g = 0; end arrow:SetVertexColor(1-g,-1+g*2,0) cell = Questie:Modulo(floor(angle / twopi * 108 + 0.5), 108); local column = Questie:Modulo(cell, 9) local row = floor(cell / 9) local xstart = (column * 56) / 512 local ystart = (row * 42) / 512 local xend = ((column + 1) * 56) / 512 local yend = ((row + 1) * 42) / 512 arrow:SetTexCoord(xstart,xend,ystart,yend) end arrowLastUpdate = GetTime() end end if (QuestieConfig.arrowTime == true) then -- Calculate the TTA every second (%01d:%02d) tta_throttle = tta_throttle + elapsed if tta_throttle >= 1 then -- Calculate the speed in yards per sec at which we're moving local current_speed = (last_distance - dist) / tta_throttle if last_distance == 0 then current_speed = 0 end if speed_count < 2 then speed = (speed + current_speed) / 2 speed_count = speed_count + 1 else speed_count = 0 speed = current_speed end if speed > 0 then local eta = math.abs(dist / speed) local text = string.format("%01d:%02d", eta / 60, Questie:Modulo(eta, 60)) tta:SetText(text) else tta:SetText("***") end last_distance = dist tta_throttle = 0 end end end --------------------------------------------------------------------------------------------------- function ShowHideCrazyArrow() if wayframe:IsShown() then wayframe:Show() if true then wayframe:EnableMouse(false) else wayframe:EnableMouse(true) end -- Set the scale and alpha wayframe:SetScale(1) wayframe:SetAlpha(1) local width = 80 local height = 80 local scale = 1 wayframe.title:SetWidth(width) wayframe.title:SetHeight(height) titleframe:SetScale(scale) titleframe:SetAlpha(1) if true then tta:Show() else tta:Hide() end else wayframe:Hide() end end --------------------------------------------------------------------------------------------------- function ArrowHidden() isHide = true end --------------------------------------------------------------------------------------------------- function ArrowShown() isHide = false end --------------------------------------------------------------------------------------------------- wayframe:SetScript("OnUpdate", OnUpdate) wayframe:RegisterForClicks("RightButtonUp") wayframe:SetScript("OnClick", WayFrame_OnClick) local function getCoords(column, row) local xstart = (column * 56) / 512 local ystart = (row * 42) / 512 local xend = ((column + 1) * 56) / 512 local yend = ((row + 1) * 42) / 512 return xstart, xend, ystart, yend end --------------------------------------------------------------------------------------------------- --this is where texcoords are extracted incorrectly (I think), leading the arrow to not point in the correct direction local texcoords = setmetatable({}, {__index = function(t, k) -- this was k:match("(%d+):(%d+)") - so we need string.match, but that's not in Lua 5.0 local fIndex, lIndex = string.find(k, "(%d+)") local col = string.sub(k, fIndex, lIndex) fIndex2, lIndex2 = string.find(k, ":(%d+)") local row = string.sub(k, fIndex2+1, lIndex2) col,row = tonumber(col), tonumber(row) local obj = {getCoords(col, row)} rawset(t, k, obj) return obj end}) --------------------------------------------------------------------------------------------------- wayframe:RegisterEvent("ADDON_LOADED") local runonce = true wayframe:SetScript("OnEvent", function(self, event, arg1, ...) if runonce == true then local feed_crazy = CreateFrame("Frame") local crazyFeedFrame = CreateFrame("Frame") local throttle = 1 local counter = 0 crazyFeedFrame:SetScript("OnUpdate", function(self, elapsed) elapsed = 1/GetFramerate() counter = counter + elapsed if counter < throttle then return end counter = 0 local angle = GetDirectionToIcon(active_point) local player = GetPlayerFacing() if not angle or not player then feed_crazy.iconCoords = texcoords["1:1"] feed_crazy.iconR = 0.2 feed_crazy.iconG = 1.0 feed_crazy.iconB = 0.2 feed_crazy.text = "No waypoint" return end angle = angle - player local perc = math.abs((math.pi - math.abs(angle)) / math.pi) local gr,gg,gb = 1, 1, 1 local mr,mg,mb = 0.75, 0.75, 0.75 local br,bg,bb = 0.5, 0.5, 0.5 local tablee = {}; table.insert(tablee, gr) table.insert(tablee, gg) table.insert(tablee, gb) table.insert(tablee, mr) table.insert(tablee, mg) table.insert(tablee, mb) table.insert(tablee, br) table.insert(tablee, bg) table.insert(tablee, bb) local r,g,b = ColorGradient(perc, tablee) feed_crazy.iconR = r feed_crazy.iconG = g feed_crazy.iconB = b cell = Questie:Modulo(floor(angle / twopi * 108 + 0.5) ,108) local column = Questie:Modulo(cell, 9) local row = floor(cell / 9) local key = column .. ":" .. row feed_crazy.iconCoords = texcoords[key] feed_crazy.text = point_title or "Unknown waypoint" end) end runonce = false end) --------------------------------------------------------------------------------------------------- -- calculations have to be redone - we are NOT actually working with Astrolabe "icons" here as TomTom did and want the arrow API -- to be accessible to everyone function GetDirectionToIcon( point ) if not point then return end local C,Z,X,Y = Astrolabe:GetCurrentPlayerPosition() -- continent, zone, x, y local dist, xDelta, yDelta = Astrolabe:ComputeDistance( C, Z, X, Y, point.c, point.z, point.x, point.y ) if not xDelta or not yDelta then return end local dir = atan2(xDelta, -(yDelta)) if ( dir > 0 ) then return twopi - dir; else return -dir; end end --------------------------------------------------------------------------------------------------- function GetDistanceToIcon( point ) local C,Z,X,Y = Astrolabe:GetCurrentPlayerPosition() -- continent, zone, x, y local dist, xDelta, yDelta = Astrolabe:ComputeDistance( C, Z, X, Y, point.c, point.z, point.x, point.y ) return dist, xDelta, yDelta end