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.
302 lines
8.5 KiB
302 lines
8.5 KiB
-- This file uses models and textures taken from TomTom. The 3D arrow model was created by Guillotine (curse.guillotine@gmail.com) and 2D minimap textures by Cladhaire.
|
|
|
|
---@class DBM
|
|
local DBM = DBM
|
|
|
|
local L = DBM_CORE_L
|
|
|
|
--------------
|
|
-- Locals --
|
|
--------------
|
|
---@class DBMArrow
|
|
local arrowFrame = {}
|
|
local frame, runAwayArrow, targetType, targetPlayer, targetX, targetY, targetMapId, hideTime, hideDistance
|
|
|
|
DBM.Arrow = arrowFrame
|
|
|
|
--------------------------------------------------------
|
|
-- Cache frequently used global variables in locals --
|
|
--------------------------------------------------------
|
|
local pi, pi2 = math.pi, math.pi * 2
|
|
local floor, sin, cos, atan2, sqrt, min = math.floor, math.sin, math.cos, math.atan2, math.sqrt, math.min
|
|
local UnitPosition, GetTime = UnitPosition, GetTime
|
|
|
|
--------------------
|
|
-- Create Frame --
|
|
--------------------
|
|
---@class DBMArrowFrame: Button
|
|
frame = CreateFrame("Button", "DBMArrow", UIParent)
|
|
frame:Hide()
|
|
frame:SetFrameStrata("HIGH")
|
|
frame:SetSize(56, 42)
|
|
frame:SetMovable(true)
|
|
frame:EnableMouse(false)
|
|
frame:RegisterForDrag("LeftButton", "RightButton")
|
|
frame:SetScript("OnDragStart", function(self)
|
|
self:StartMoving()
|
|
end)
|
|
frame:SetScript("OnDragStop", function(self)
|
|
self:StopMovingOrSizing()
|
|
local point, _, _, x, y = self:GetPoint(1)
|
|
DBM.Options.ArrowPoint = point
|
|
DBM.Options.ArrowPosX = x
|
|
DBM.Options.ArrowPosY = y
|
|
end)
|
|
|
|
---@class DBMArrowTextFrame: Frame
|
|
local textframe = CreateFrame("Frame", nil, frame)
|
|
frame.distance = textframe:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
|
frame.title = textframe:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
|
frame.title:SetPoint("TOP", frame, "BOTTOM")
|
|
frame.distance:SetPoint("TOP", frame.title, "BOTTOM")
|
|
textframe:Hide()
|
|
|
|
local arrow = frame:CreateTexture(nil, "OVERLAY")
|
|
arrow:SetTexture("Interface\\AddOns\\DBM-Core\\textures\\arrows\\Arrow.blp")
|
|
arrow:SetAllPoints(frame)
|
|
|
|
---------------------
|
|
-- Map Utilities --
|
|
---------------------
|
|
local calculateDistance
|
|
do
|
|
function calculateDistance(x1, y1, x2, y2)
|
|
local dX = x1 - x2
|
|
local dY = y1 - y2
|
|
return sqrt(dX * dX + dY * dY)
|
|
end
|
|
end
|
|
|
|
-- GetPlayerFacing seems to return values between -pi and pi instead of 0 - 2pi sometimes since 3.3.3
|
|
local GetPlayerFacing = function()
|
|
local result = GetPlayerFacing() or 0
|
|
if result < 0 then
|
|
result = result + pi2
|
|
end
|
|
return result
|
|
end
|
|
|
|
------------------------
|
|
-- Update the arrow --
|
|
------------------------
|
|
local updateArrow
|
|
do
|
|
local currentCell
|
|
local formatText = "%dy"
|
|
function updateArrow(direction, distance)
|
|
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
|
|
arrow:SetVertexColor(1 - perc, perc, 0)
|
|
if distance >= hideDistance then
|
|
frame:Hide()
|
|
end
|
|
else
|
|
local perc = min(distance, 100) / 100
|
|
arrow:SetVertexColor(1, 1 - perc, 0)
|
|
if distance <= hideDistance then
|
|
frame:Hide()
|
|
else
|
|
frame.distance:SetText(formatText:format(distance))
|
|
end
|
|
end
|
|
else
|
|
if runAwayArrow then
|
|
arrow:SetVertexColor(1, 0.3, 0)
|
|
else
|
|
arrow:SetVertexColor(1, 1, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
------------------------
|
|
-- OnUpdate Handler --
|
|
------------------------
|
|
do
|
|
local rotateState = 0
|
|
|
|
frame:SetScript("OnUpdate", function(self, elapsed)
|
|
if hideTime and GetTime() > hideTime then
|
|
frame:Hide()
|
|
end
|
|
arrow:Show()
|
|
-- the static arrow type is special because it doesn't depend on the player's orientation or position
|
|
if targetType == "static" then
|
|
return updateArrow(targetX) -- targetX contains the static angle to show
|
|
end
|
|
|
|
local x, y, _, mapId = UnitPosition("player")
|
|
--New bug in 8.2.5, unit position returns nil for position in areas there aren't restrictions for first few frames in that new area
|
|
--this just has the arrow skip some onupdates during that
|
|
if not x or not y then
|
|
if IsInInstance() then--Somehow x and y returned on entering an instance, before restrictions kicked in?
|
|
frame:Hide()--Hide, if in an instance, disable arrow entirely
|
|
end
|
|
return--Not in instance, but x and y nil, just skip updates until x and y start returning
|
|
end
|
|
if targetType == "player" then
|
|
targetX, targetY, _, targetMapId = UnitPosition(targetPlayer)
|
|
if not targetX or not targetY or mapId ~= targetMapId then
|
|
self:Hide() -- hide the arrow if the target doesn't exist. TODO: just hide the texture and add a timeout
|
|
end
|
|
elseif targetType == "rotate" then
|
|
rotateState = rotateState + elapsed
|
|
targetX = x + cos(rotateState)
|
|
targetY = y + sin(rotateState)
|
|
end
|
|
if not targetX or not targetY then
|
|
return
|
|
end
|
|
local angle = atan2(targetY - y, x - targetX)
|
|
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
|
|
else
|
|
if runAwayArrow then
|
|
angle = pi2 - angle -- pi < angle < 2pi
|
|
else
|
|
angle = pi - angle -- 0 < angle < pi
|
|
end
|
|
end
|
|
updateArrow(angle - GetPlayerFacing(), calculateDistance(x, y, targetX, targetY))
|
|
end)
|
|
end
|
|
|
|
|
|
----------------------
|
|
-- Public Methods --
|
|
----------------------
|
|
|
|
--/run DBM.Arrow:ShowRunTo(50, 50, 1, nil, true, true, "Waypoint", custom local mapID)
|
|
local function show(runAway, x, y, distance, time, legacy, _, title, customAreaID)
|
|
if DBM:HasMapRestrictions() then return end
|
|
local player
|
|
if type(x) == "string" then
|
|
player, hideDistance, hideTime = x, y, hideDistance
|
|
end
|
|
frame:Show()
|
|
textframe:Show()
|
|
if title then
|
|
frame.title:Show()
|
|
frame.title:SetText(title)
|
|
else
|
|
frame.title:Hide()
|
|
end
|
|
if runAway then
|
|
frame.distance:Hide()
|
|
else
|
|
frame.distance:Show()
|
|
end
|
|
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"
|
|
if legacy and x >= 0 and x <= 100 and y >= 0 and y <= 100 then
|
|
local _, temptable = C_Map.GetWorldPosFromMapPos(tonumber(customAreaID) or C_Map.GetBestMapForUnit("player") or 0, CreateVector2D(x / 100, y / 100))
|
|
x, y = temptable.x, temptable.y
|
|
end
|
|
targetX, targetY = x, y
|
|
end
|
|
end
|
|
|
|
function arrowFrame:ShowRunTo(...)
|
|
return show(false, ...)
|
|
end
|
|
|
|
function arrowFrame:ShowRunAway(...)
|
|
return show(true, ...)
|
|
end
|
|
|
|
-- shows a static arrow
|
|
function arrowFrame:ShowStatic(angle, time)
|
|
--Static arrows do not need restrictions, and are still permitted even in 7.1
|
|
runAwayArrow = false
|
|
hideDistance = 0
|
|
targetType = "static"
|
|
targetX = angle * pi2 / 360
|
|
if time then
|
|
hideTime = time + GetTime()
|
|
else
|
|
hideTime = nil
|
|
end
|
|
frame:Show()
|
|
textframe:Hide()--just in case they call static while a non static was already showing
|
|
end
|
|
|
|
function arrowFrame:IsShown()
|
|
return frame and frame:IsShown()
|
|
end
|
|
|
|
function arrowFrame:Hide()
|
|
textframe:Hide()
|
|
frame:Hide()
|
|
end
|
|
|
|
local function endMove()
|
|
frame:EnableMouse(false)
|
|
arrowFrame:Hide()
|
|
end
|
|
|
|
function arrowFrame:Move()
|
|
targetType = "rotate"
|
|
runAwayArrow = false
|
|
hideDistance = 0
|
|
frame:EnableMouse(true)
|
|
frame:Show()
|
|
DBT:CreateBar(25, L.ARROW_MOVABLE, 237538)
|
|
DBM:Unschedule(endMove)
|
|
DBM:Schedule(25, endMove)
|
|
end
|
|
|
|
function arrowFrame:LoadPosition()
|
|
frame:SetPoint(DBM.Options.ArrowPoint, DBM.Options.ArrowPosX, DBM.Options.ArrowPosY)
|
|
end
|
|
|
|
do
|
|
SLASH_DEADLYBOSSMODSDWAY1 = "/dway"--/way not used because DBM would load before TomTom and can't check
|
|
SlashCmdList["DEADLYBOSSMODSDWAY"] = function(msg)
|
|
if DBM:HasMapRestrictions() then
|
|
DBM:AddMsg(L.NO_ARROW)
|
|
return
|
|
end
|
|
msg = msg:sub(1):trim()
|
|
local x, y = strsplit(" ", msg) -- Try splitting by space
|
|
local xNum, yNum = tonumber(x or ""), tonumber(y or "")
|
|
if not xNum or not yNum then
|
|
x, y = strsplit(",", msg) -- And then by comma
|
|
xNum, yNum = tonumber(x or ""), tonumber(y or "")
|
|
end
|
|
if xNum and yNum then
|
|
DBM.Arrow:ShowRunTo(xNum, yNum, 1, nil, true)
|
|
return
|
|
end
|
|
if DBM.Arrow:IsShown() then
|
|
DBM.Arrow:Hide()
|
|
else
|
|
DBM:AddMsg(L.ARROW_WAY_USAGE)
|
|
end
|
|
end
|
|
end
|
|
|