You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
501 lines
12 KiB
501 lines
12 KiB
local GlobalAddonName, WQLdb = ...
|
|
|
|
----------------------------
|
|
-- Initialize variables --
|
|
----------------------------
|
|
-- globals
|
|
WQLdb.Arrow = {}
|
|
|
|
local arrowFrame = WQLdb.Arrow
|
|
local runAwayArrow
|
|
local targetType
|
|
local targetPlayer
|
|
local targetX, targetY
|
|
local hideTime, hideDistance
|
|
local dontHide
|
|
local isWorldCoord
|
|
local currWaypoint
|
|
local funcOnEveryStep
|
|
|
|
local textureArrow,textureTop = "Interface\\AddOns\\WorldQuestsList\\Arrows", "Interface\\AddOns\\WorldQuestsList\\Arrows-Down"
|
|
|
|
local pi, pi2, pi05 = math.pi, math.pi * 2, math.pi * 0.5
|
|
local floor = math.floor
|
|
local sin, cos, atan2, sqrt, min = math.sin, math.cos, math.atan2, math.sqrt, math.min
|
|
local GetTime, GGetPlayerFacing = GetTime, GetPlayerFacing
|
|
local UnitPosition = UnitPosition
|
|
|
|
local function GetPlayerFacing()
|
|
return GGetPlayerFacing() or 0
|
|
end
|
|
|
|
local points = {}
|
|
local currPoint
|
|
|
|
--------------------
|
|
-- Create Frame --
|
|
--------------------
|
|
local frame = CreateFrame("Button", nil, UIParent)
|
|
frame:Hide()
|
|
frame:SetFrameStrata("HIGH")
|
|
frame:SetWidth(56)
|
|
frame:SetHeight(42)
|
|
frame:SetMovable(true)
|
|
frame:EnableMouse(true)
|
|
frame:RegisterForDrag("LeftButton", "RightButton")
|
|
frame:SetScript("OnDragStart", function(self)
|
|
if self:IsMovable() then
|
|
self:StartMoving()
|
|
end
|
|
end)
|
|
frame:SetScript("OnDragStop", function(self)
|
|
self:StopMovingOrSizing()
|
|
local point1, _, point2, x, y = self:GetPoint(1)
|
|
|
|
VWQL.Arrow_Point1 = point1
|
|
VWQL.Arrow_Point2 = point2
|
|
VWQL.Arrow_PointX = x
|
|
VWQL.Arrow_PointY = y
|
|
end)
|
|
frame:SetScript("OnClick", function(self)
|
|
if currPoint then
|
|
for i=#points,1,-1 do
|
|
if points[i] == currPoint then
|
|
if points[i].waypoint then
|
|
WorldQuestList:WaypointRemove(points[i].waypoint)
|
|
end
|
|
tremove(points,i)
|
|
return
|
|
end
|
|
end
|
|
else
|
|
self:Hide()
|
|
end
|
|
end)
|
|
frame:SetClampedToScreen(true)
|
|
local arrow = frame:CreateTexture(nil, "OVERLAY")
|
|
arrow:SetTexture(textureArrow)
|
|
arrow:SetAllPoints(frame)
|
|
|
|
local txtrng = frame:CreateFontString(nil,"OVERLAY")
|
|
txtrng:SetSize(0,18)
|
|
txtrng:SetPoint("BOTTOM",frame,"BOTTOMRIGHT",0,5)
|
|
txtrng:SetFont("Interface\\AddOns\\WorldQuestsList\\ariblk.ttf", 14, "OUTLINE")
|
|
txtrng:SetJustifyH("RIGHT")
|
|
txtrng:SetJustifyV("BOTTOM")
|
|
txtrng:SetText("")
|
|
|
|
local txttime = frame:CreateFontString(nil,"OVERLAY")
|
|
txttime:SetPoint("TOP",txtrng,"BOTTOM",0,-1)
|
|
txttime:SetFont("Interface\\AddOns\\WorldQuestsList\\ariblk.ttf", 10, "OUTLINE")
|
|
txttime:SetText("")
|
|
|
|
------------------------
|
|
-- Update the arrow --
|
|
------------------------
|
|
local updateArrow,IsArrowDown,updateArrowPoint
|
|
do
|
|
local currentCell
|
|
local count = 0
|
|
local showDownArrow = false
|
|
function updateArrow(direction, distance)
|
|
if distance and distance <= hideDistance and dontHide then
|
|
if not showDownArrow then
|
|
frame:SetHeight(60)
|
|
frame:SetWidth(47)
|
|
arrow:SetTexture(textureTop)
|
|
arrow:SetVertexColor(0.3, 1, 0)
|
|
showDownArrow = true
|
|
end
|
|
count = count + 1
|
|
if count >= 55 then
|
|
count = 0
|
|
end
|
|
|
|
local cell = count
|
|
local column = 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)
|
|
txtrng:SetFormattedText("%d",distance)
|
|
txttime.d = distance
|
|
else
|
|
if showDownArrow then
|
|
frame:SetHeight(42)
|
|
frame:SetWidth(56)
|
|
arrow:SetTexture(textureArrow)
|
|
showDownArrow = false
|
|
currentCell = nil
|
|
end
|
|
local cell = floor(direction / pi2 * 108 + 0.5) % 108
|
|
if cell ~= currentCell then
|
|
currentCell = cell
|
|
local column = 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
|
|
if distance then
|
|
if runAwayArrow then
|
|
local perc = distance / hideDistance
|
|
local red = 1 - perc
|
|
arrow:SetVertexColor(red, perc, 0)
|
|
txtrng:SetTextColor(red, perc, 0)
|
|
if distance >= hideDistance then
|
|
if currWaypoint then
|
|
WorldQuestList:WaypointRemove(currWaypoint)
|
|
end
|
|
frame:Hide()
|
|
end
|
|
else
|
|
local perc = distance > 2000 and 2000 or distance
|
|
if perc >= 500 then
|
|
local green = 1 - ((perc-500) / 1500)
|
|
arrow:SetVertexColor(1, green, 0)
|
|
txtrng:SetTextColor(1, green, 0)
|
|
else
|
|
perc = perc < 40 and 0 or perc - 40
|
|
local red = perc / 460
|
|
arrow:SetVertexColor(red, 1, 0)
|
|
txtrng:SetTextColor(red, 1, 0)
|
|
end
|
|
if distance <= hideDistance then
|
|
if currWaypoint then
|
|
WorldQuestList:WaypointRemove(currWaypoint)
|
|
end
|
|
frame:Hide()
|
|
end
|
|
end
|
|
txtrng:SetFormattedText("%d",distance)
|
|
txttime.d = distance
|
|
elseif runAwayArrow then
|
|
arrow:SetVertexColor(1, 0.3, 0)
|
|
else
|
|
arrow:SetVertexColor(1, 1, 0)
|
|
end
|
|
end
|
|
|
|
if funcOnEveryStep then funcOnEveryStep(distance) end
|
|
end
|
|
function updateArrowPoint(direction, distance)
|
|
if showDownArrow then
|
|
frame:SetHeight(42)
|
|
frame:SetWidth(56)
|
|
arrow:SetTexture(textureArrow)
|
|
showDownArrow = false
|
|
currentCell = nil
|
|
end
|
|
local cell = floor(direction / pi2 * 108 + 0.5) % 108
|
|
if cell ~= currentCell then
|
|
currentCell = cell
|
|
local column = 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
|
|
if distance then
|
|
local perc = distance > 2000 and 2000 or distance
|
|
if perc >= 500 then
|
|
local green = 1 - ((perc-500) / 1500)
|
|
arrow:SetVertexColor(1, green, 0)
|
|
txtrng:SetTextColor(1, green, 0)
|
|
else
|
|
perc = perc < 40 and 0 or perc - 40
|
|
local red = perc / 460
|
|
arrow:SetVertexColor(red, 1, 0)
|
|
txtrng:SetTextColor(red, 1, 0)
|
|
end
|
|
txtrng:SetFormattedText("%d",distance)
|
|
txttime.d = distance
|
|
else
|
|
arrow:SetVertexColor(1, 1, 0)
|
|
end
|
|
|
|
if funcOnEveryStep then funcOnEveryStep(distance) end
|
|
end
|
|
function IsArrowDown()
|
|
return showDownArrow
|
|
end
|
|
end
|
|
|
|
------------------------
|
|
-- OnUpdate Handler --
|
|
------------------------
|
|
|
|
local functionOnUpdateWorld,functionOnUpdateMap,functionOnUpdateStatic,functionOnUpdateWorldPoint = nil
|
|
do
|
|
local rotateState = 0
|
|
|
|
function functionOnUpdateWorld(self, elapsed)
|
|
if hideTime and GetTime() > hideTime then
|
|
if currWaypoint then
|
|
WorldQuestList:WaypointRemove(currWaypoint)
|
|
end
|
|
frame:Hide()
|
|
end
|
|
|
|
local y, x = UnitPosition'player'
|
|
|
|
if not y or not x then
|
|
self:Hide()
|
|
return
|
|
end
|
|
|
|
--print(targetX,targetY,x,y)
|
|
|
|
if targetType == "player" then
|
|
targetY, targetX = UnitPosition(targetPlayer)
|
|
if not targetY or not targetX then
|
|
self:Hide() -- hide the arrow if the target doesn't exist
|
|
end
|
|
elseif targetType == "rotate" then
|
|
rotateState = rotateState + elapsed
|
|
targetX = x + cos(rotateState)
|
|
targetY = y + sin(rotateState)
|
|
end
|
|
|
|
local angle = atan2(x - targetX, targetY - y)
|
|
if angle <= 0 then -- -pi < angle < pi but we need/want a value between 0 and 2 pi
|
|
if runAwayArrow then
|
|
angle = -angle -- 0 < angle < pi
|
|
else
|
|
angle = pi - angle -- pi < angle < 2pi
|
|
end
|
|
elseif runAwayArrow then
|
|
angle = pi2 - angle -- pi < angle < 2pi
|
|
else
|
|
angle = pi - angle -- 0 < angle < pi
|
|
end
|
|
|
|
local player = GetPlayerFacing() - pi
|
|
if player < 0 then
|
|
player = pi2 + player
|
|
end
|
|
|
|
local dX = (x - targetX)
|
|
local dY = (y - targetY)
|
|
|
|
updateArrow(angle - player, sqrt(dX * dX + dY * dY))
|
|
end
|
|
|
|
function functionOnUpdateWorldPoint(self, elapsed)
|
|
currPoint = nil
|
|
local y, x = UnitPosition'player'
|
|
if not y or not x then
|
|
self:Hide()
|
|
return
|
|
end
|
|
|
|
local targetX, targetY
|
|
local currTime, point, dX, dY, dist = GetTime()
|
|
local nearest, curr
|
|
for i=#points,1,-1 do
|
|
point = points[i]
|
|
if point.hideTime and point.hideTime > currTime then
|
|
if point.waypoint then
|
|
WorldQuestList:WaypointRemove(point.waypoint)
|
|
end
|
|
tremove(points,i)
|
|
else
|
|
dX = (x - point.x)
|
|
dY = (y - point.y)
|
|
|
|
dist = sqrt(dX * dX + dY * dY)
|
|
|
|
if dist < point.hideDistance then
|
|
if point.waypoint then
|
|
WorldQuestList:WaypointRemove(point.waypoint)
|
|
end
|
|
tremove(points,i)
|
|
elseif not nearest or nearest > dist then
|
|
targetX, targetY = point.x, point.y
|
|
nearest = dist
|
|
curr = point
|
|
end
|
|
end
|
|
end
|
|
|
|
if not targetX or not targetY then
|
|
self:Hide()
|
|
return
|
|
end
|
|
|
|
currPoint = curr
|
|
|
|
local angle = atan2(x - targetX, targetY - y)
|
|
if angle <= 0 then -- -pi < angle < pi but we need/want a value between 0 and 2 pi
|
|
if runAwayArrow then
|
|
angle = -angle -- 0 < angle < pi
|
|
else
|
|
angle = pi - angle -- pi < angle < 2pi
|
|
end
|
|
elseif runAwayArrow then
|
|
angle = pi2 - angle -- pi < angle < 2pi
|
|
else
|
|
angle = pi - angle -- 0 < angle < pi
|
|
end
|
|
|
|
local player = GetPlayerFacing() - pi
|
|
if player < 0 then
|
|
player = pi2 + player
|
|
end
|
|
|
|
local dX = (x - targetX)
|
|
local dY = (y - targetY)
|
|
|
|
updateArrowPoint(angle - player, sqrt(dX * dX + dY * dY))
|
|
end
|
|
|
|
function functionOnUpdateStatic(self, elapsed)
|
|
if hideTime and GetTime() > hideTime then
|
|
frame:Hide()
|
|
end
|
|
|
|
local player = GetPlayerFacing() - pi
|
|
if player < 0 then
|
|
player = pi2 + player
|
|
end
|
|
|
|
updateArrow(targetX - player)
|
|
end
|
|
end
|
|
|
|
|
|
----------------------
|
|
-- Public Methods --
|
|
----------------------
|
|
local function show(runAway, x, y, distance, time, world, hide, waypoint, arrowDistFunc)
|
|
local player
|
|
currPoint = nil
|
|
frame:Hide()
|
|
if x == "_static" then
|
|
frame:SetScript("OnUpdate", functionOnUpdateStatic)
|
|
else
|
|
frame:SetScript("OnUpdate", functionOnUpdateWorld)
|
|
end
|
|
if x == "_static" then
|
|
targetX = math.rad(y)
|
|
if distance then
|
|
hideTime = distance + GetTime()
|
|
else
|
|
hideTime = nil
|
|
end
|
|
frame:Show()
|
|
return
|
|
elseif type(x) == "string" then
|
|
player, hideDistance, hideTime = x, y, distance
|
|
end
|
|
frame:Show()
|
|
runAwayArrow = runAway
|
|
hideDistance = distance or runAway and 100 or 3
|
|
if time then
|
|
hideTime = time + GetTime()
|
|
else
|
|
hideTime = nil
|
|
end
|
|
if player then
|
|
targetType = "player"
|
|
targetPlayer = player
|
|
else
|
|
targetType = "fixed"
|
|
targetX, targetY = x, y
|
|
end
|
|
isWorldCoord = world
|
|
dontHide = hide
|
|
currWaypoint = waypoint
|
|
funcOnEveryStep = type(arrowDistFunc)=="function" and arrowDistFunc
|
|
end
|
|
|
|
function arrowFrame:ShowRunTo(...)
|
|
return show(false, ...)
|
|
end
|
|
|
|
function arrowFrame:ShowRunAway(...)
|
|
return show(true, ...)
|
|
end
|
|
|
|
--waypoints
|
|
|
|
local function showPoint(x, y, distance, time, hide, waypoint)
|
|
currPoint = nil
|
|
frame:Hide()
|
|
frame:SetScript("OnUpdate", functionOnUpdateWorldPoint)
|
|
|
|
points[#points+1] = {
|
|
x = x,
|
|
y = y,
|
|
hideTime = time and time + GetTime() or nil,
|
|
hideDistance = distance or 3,
|
|
waypoint = waypoint,
|
|
keepShow = hide,
|
|
}
|
|
|
|
frame:Show()
|
|
end
|
|
|
|
function arrowFrame:AddPoint(...)
|
|
return showPoint(...)
|
|
end
|
|
|
|
-- shows a static arrow
|
|
function arrowFrame:ShowStatic(angle, time)
|
|
runAwayArrow = false
|
|
hideDistance = 0
|
|
targetType = "static"
|
|
targetX = angle * pi2 / 360
|
|
if time then
|
|
hideTime = time + GetTime()
|
|
else
|
|
hideTime = nil
|
|
end
|
|
frame:Show()
|
|
end
|
|
|
|
function arrowFrame:ShowToPlayer(...)
|
|
return show(false, ...)
|
|
end
|
|
|
|
function arrowFrame:IsShown()
|
|
return frame and frame:IsShown()
|
|
end
|
|
|
|
function arrowFrame:Hide(autoHide)
|
|
frame:Hide()
|
|
end
|
|
|
|
local function endMove()
|
|
frame:EnableMouse(false)
|
|
arrowFrame:Hide()
|
|
end
|
|
|
|
function arrowFrame:Move()
|
|
targetType = "rotate"
|
|
runAwayArrow = false
|
|
hideDistance = 5
|
|
frame:EnableMouse(true)
|
|
frame:Show()
|
|
end
|
|
|
|
function arrowFrame:LoadPosition(...)
|
|
frame:SetPoint(...)
|
|
end
|
|
|
|
function arrowFrame:GetPosition()
|
|
return targetX, targetY
|
|
end
|
|
|
|
function arrowFrame:Scale(...)
|
|
frame:SetScale(...)
|
|
end
|
|
|
|
arrowFrame.frame = frame
|