--[[
Copyright 2008-∞ Daniel Ceregatti (daniel@ceregatti.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
Special thanks to:
Shefki
Adys
kagaro
And everyone else in #wowace on irc.freenode.org
]]--
-- Locale variable
local L = LibStub("AceLocale-3.0"):GetLocale("DirectionArrow", false)
-- LibMapData-1.0 for zone sizes
local mapfiles = LibStub("LibMapData-1.0")
local width = 100
local height = 100
local current_unit = nil
local current_unit_guid = nil
local DistanceMetric = L["yards"]
local s2 = sqrt(2)
local cos, sin, rad, atan2, sqrt = math.cos, math.sin, math.rad, math.atan2, math.sqrt
local strfind = string.find
local GetPlayerFacing, GetPlayerMapPosition, GetMouseFocus, SecureButton_GetModifiedUnit, UnitIsUnit =
GetPlayerFacing, GetPlayerMapPosition, GetMouseFocus, SecureButton_GetModifiedUnit, UnitIsUnit
--@debug@
local function pp (arg)
if DirectionArrow.db.profile.debug then
print (arg)
end
end
--@end-debug@
local function CalculateCorner(r)
return 0.5 + cos(r) / s2, 0.5 + sin(r) / s2;
end
local function RotateTexture(texture, angle)
local LRx, LRy = CalculateCorner(angle + 0.785398163);
local LLx, LLy = CalculateCorner(angle + 2.35619449);
local ULx, ULy = CalculateCorner(angle + 3.92699082);
local URx, URy = CalculateCorner(angle - 0.785398163);
texture:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy);
end
local function GetBearing (unit)
tx, ty = GetPlayerMapPosition(unit)
if tx == 0 and ty == 0 then
--@debug@
pp (format ("Unable to obtain the position for unit %s", unit))
--@end-debug@
return 999
end
px, py = GetPlayerMapPosition("player")
return -GetPlayerFacing() - atan2 (tx - px, py - ty)
end
function DistanceCalculator ()
return sqrt ((((tx - px) * width) ^ 2) + (((ty - py) * height) ^ 2))
end
local function CanShow (unit)
local bearing = GetBearing (unit)
if bearing == 999 then
return false
end
return true
end
local function OnUpdate (self, elapsed)
self.elapsed = self.elapsed + elapsed
if (self.elapsed > DirectionArrow.db.profile.update_frequency) then
self.elapsed = 0
local bearing = GetBearing (current_unit)
if bearing == 999 then
DirectionArrow.f:Hide()
return
end
RotateTexture (DirectionArrow.t, bearing)
DirectionArrow.x:SetText(format ("%d ", DistanceCalculator()))
end
end
local function GetRaidIndex (GUID)
for i = 1, GetNumRaidMembers() do
if GUID == UnitGUID("raid"..i) then
return i
end
end
return 0
end
local function GetPartyIndex (GUID)
for i = 1, GetNumPartyMembers() do
if GUID == UnitGUID("party"..i) then
return i
end
end
return 0
end
local function GetUnitIndex (GUID)
local RaidIndex = GetRaidIndex(GUID)
local TargetFrom = ''
if RaidIndex > 0 then
TargetFrom = 'raid'
else
RaidIndex = GetPartyIndex (GUID)
if RaidIndex > 0 then
TargetFrom = 'party'
end
end
--@debug@
pp (format ("GetUnitIndex() returns: %d, %s", RaidIndex, TargetFrom))
--@end-debug@
return RaidIndex, TargetFrom
end
-- Keybindings
BINDING_HEADER_DIRECTIONARROW = "DirectionArrow"
BINDING_NAME_DIRECTIONARROW_TOGGLE = L["Sticky Tracking"]
DirectionArrow = LibStub("AceAddon-3.0"):NewAddon("DirectionArrow", "AceConsole-3.0", "AceEvent-3.0")
function DirectionArrow:OnInitialize()
self.db = LibStub("AceDB-3.0"):New("DirectionArrowDB", DirectionArrowDefaultConfigDB, "Default")
self.sticky = false
self.f = CreateFrame("Button", nil, UIParent)
self.f:SetScale(self.db.profile.size)
self.f:SetPoint("CENTER")
self.f:SetFrameLevel(20)
self.f:EnableMouse(false)
self.f:SetAlpha(self.db.profile.opacity)
self.f:SetWidth(32)
self.f:SetHeight(32)
self.f:Hide()
self.f.elapsed = 0
self.f:SetScript("OnUpdate", OnUpdate)
self.t = self.f:CreateTexture("OVERLAY")
self.f.texture = t
self.t:SetTexture([[Interface\Minimap\MinimapArrow]])
self.t:SetBlendMode("BLEND")
self.t:SetAlpha(1)
self.t:SetAllPoints(self.f)
self.x = self.f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
if self.db.profile.show_distance then
self.x:Show()
else
self.x:Hide()
end
self.x:SetWidth(32)
self.x:SetHeight(32)
self.x:SetPoint("CENTER", self.f, "CENTER", self.db.profile.text_location_x, self.db.profile.text_location_y)
self.x:SetTextColor(unpack (self.db.profile.distance_color))
self.x:SetShadowOffset(.5,-.5)
self.x:SetShadowColor(unpack (self.db.profile.distance_ds_color))
self.x:SetAlpha(1)
self.x:SetFont(self.x:GetFont(), self.db.profile.distance_size)
LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("DirectionArrow", DirectionArrowConfig)
self.optionsFrames = {}
local ACD3 = LibStub("AceConfigDialog-3.0")
self.optionsFrames.DirectionArrow = LibStub("LibAboutPanel").new (nil, "DirectionArrow")
-- Thanks, Omen!
self.optionsFrames.General = ACD3:AddToBlizOptions("DirectionArrow", L["General"], "DirectionArrow", "General")
self:RegisterChatCommand("directionarrow", function()
InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.General)
InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.DirectionArrow)
end)
self:RegisterChatCommand("da", function()
InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.General)
InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.DirectionArrow)
end)
self.f:SetScript("OnEvent", function(frame,event,...) self[event](self,...) end)
self.f:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
self.f:RegisterEvent("RAID_ROSTER_UPDATE")
mapfiles:RegisterCallback("MapChanged", function (event, map, floor, w, h)
--@debug@
pp (format ("Map changed. Map: %s, floor: %s, w: %s, h: %s", map, floor, w, h))
--@end-debug@
width = w
height = h
end)
end
function DirectionArrow:RAID_ROSTER_UPDATE()
--@debug@
pp ("RAID_ROSTER_UPDATE fired")
--@end-debug@
-- Get the unit for the saved GUID
current_unit, _ = GetUnitIndex (current_unit_guid)
end
function DirectionArrow:ToggleSticky()
DirectionArrow.sticky = not DirectionArrow.sticky
--@debug@
pp ("Sticky toggled. Set to: " .. (DirectionArrow.sticky and "true" or "false"))
--@end-debug@
end
function DirectionArrow:UPDATE_MOUSEOVER_UNIT()
if DirectionArrow.sticky then
return
end
local frame = GetMouseFocus()
if frame then
local unit = SecureButton_GetModifiedUnit (frame)
if
unit
and (
strfind (unit, "^raid%d+$")
or strfind (unit, "^raidpet%d+$")
or strfind (unit, "^party%d+$")
or strfind (unit, "^partypet%d+$")
or strfind (unit, "^target.*$")
or strfind (unit, "^focus.*$")
)
then
if UnitIsUnit (unit, "player") then
--@debug@
pp ("The unit is the player")
--@end-debug@
return
end
--@debug@
pp ("Setting unit: " .. unit)
--@end-debug@
current_unit = unit
current_unit_guid = UnitGUID (unit)
DirectionArrow.current_frame = frame
if CanShow (current_unit) then
DirectionArrow:SetArrow ()
DirectionArrow.f:SetFrameLevel(frame:GetFrameLevel() + 50)
DirectionArrow.f:Show()
return
end
--@debug@
elseif unit then
pp ("The unit didn't match: " .. unit)
--@end-debug@
end
end
end
function DirectionArrow:SetArrow ()
DirectionArrow.f:ClearAllPoints ()
DirectionArrow.f:SetPoint (
"CENTER",
DirectionArrow.current_frame,
DirectionArrow.db.profile.relative_point,
DirectionArrow.db.profile.relative_location_x,
DirectionArrow.db.profile.relative_location_y
)
end