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.

304 lines
8.5 KiB

local myname, ns = ...
local core = LibStub("AceAddon-3.0"):GetAddon("SilverDragon")
local module = core:NewModule("ClickTarget", "AceEvent-3.0")
local Debug = core.Debug
local HBD = LibStub("HereBeDragons-2.0")
local LibWindow = LibStub("LibWindow-1.1")
local AceConfigRegistry = LibStub("AceConfigRegistry-3.0")
module.Looks = {}
module.LookConfig = {}
module.LookReset = {}
module.defaults = {
profile = {
show = true,
loot = true,
locked = true,
style = "SilverDragon",
closeAfter = 30,
closeDead = true,
stacksize = 4,
announce = "IMMEDIATELY", -- or "OPENLAST"
announceChannel = "CHANNEL",
sources = {
target = false,
grouptarget = true,
mouseover = true,
nameplate = true,
vignette = true,
['point-of-interest'] = true,
chat = true,
groupsync = true,
guildsync = false,
fake = true,
},
anchor = {
point = "BOTTOMRIGHT",
x = -260,
y = 270,
scale = 1,
},
style_options = {
['*'] = {},
},
},
}
function module:OnInitialize()
self.db = core.db:RegisterNamespace("ClickTarget", self.defaults)
core.RegisterCallback(self, "Announce")
core.RegisterCallback(self, "AnnounceLoot")
core.RegisterCallback(self, "Marked")
core.RegisterCallback(self, "SeenVignette")
core.RegisterCallback(self, "SeenLoot")
self:RegisterEvent("PLAYER_REGEN_ENABLED", "ProcessQueue")
self.anchor = self:CreateAnchor()
self:RegisterConfig()
end
local pending
function module:Announce(callback, id, zone, x, y, dead, source, unit, _, vignetteGUID)
if not self.db.profile.show then return end
if source:match("^sync") then
local channel, player = source:match("sync:(.+):(.+)")
if channel == "GUILD" then
source = "guildsync"
else
source = "groupsync"
end
end
if not self.db.profile.sources[source] then
Debug("Not showing popup, source disabled", source)
return
end
local data = {
type = "mob",
id = id,
unit = unit,
source = source,
dead = dead,
zone = zone,
x = x or 0,
y = y or 0,
vignetteGUID = vignetteGUID,
}
if vignetteGUID then
local vignetteInfo = C_VignetteInfo.GetVignetteInfo(vignetteGUID)
if vignetteInfo then
data.vignetteID = vignetteInfo.vignetteID
end
end
self:Enqueue(data)
FlashClientIcon() -- If you're tabbed out, bounce the WoW icon if we're in a context that supports that
data.unit = nil -- can't be trusted to remain the same
end
function module:AnnounceLoot(_, name, id, zone, x, y, vignetteGUID)
if not self.db.profile.loot then return end
local data = {
type = "loot",
id = id,
name = name,
zone = zone,
x = x or 0,
y = y or 0,
vignetteGUID = vignetteGUID,
vignetteID = id,
}
self:Enqueue(data)
FlashClientIcon() -- If you're tabbed out, bounce the WoW icon if we're in a context that supports that
end
function module:SeenVignette(_, name, vignetteID, atlasName, uiMapID, x, y, vignetteGUID, mobid)
if not mobid then return end
self:UpdateWithData({
type = "mob",
id = mobid,
vignetteGUID = vignetteGUID,
vignetteID = vignetteID,
zone = uiMapID,
x = x,
y = y,
source = "vignette",
})
end
function module:SeenLoot(_, name, vignetteID, uiMapID, x, y, vignetteGUID)
self:UpdateWithData({
type = "loot",
id = vignetteID,
vignetteGUID = vignetteGUID,
vignetteID = vignetteID,
zone = uiMapID,
x = x,
y = y,
source = "vignette",
})
end
function module:CanPoint(uiMapID)
return core:GetModule("TomTom"):CanPointTo(uiMapID)
end
function module:Point(data)
local uiMapID, x, y = self:GetPositionFromData(data)
if uiMapID and x and y then
-- point to it, without a timeout, and ignoring whether it'll be replacing an existing waypoint
core:GetModule("TomTom"):PointTo(data.type == "mob" and data.id or data.name, uiMapID, x, y, 0, true)
end
end
function module:Marked(callback, id, marker, unit)
for popup in self:EnumerateActive() do
if popup.data and popup.data.id == id then
popup:SetRaidIcon(marker)
end
end
end
function module:GetGeneralID()
local channelFormat = GetLocale() == "ruRU" and "%s: %s" or "%s - %s"
local zoneText = GetZoneText()
local general = EnumerateServerChannels()
if zoneText == nil or general == nil then return false end
local id = GetChannelName(channelFormat:format(general, zoneText))
return (id and id > 0) and id
end
function module:SendLink(prefix, uiMapID, x, y)
local message
if MAP_PIN_HYPERLINK then
message = ("%s|cffffff00|Hworldmap:%d:%d:%d|h[%s]|h|r"):format(
prefix and (prefix .. " ") or "",
uiMapID,
x * 10000,
y * 10000,
-- Can't do this:
-- core:GetMobLabel(self.data.id) or UNKNOWN
-- WoW seems to filter out anything which isn't the standard MAP_PIN_HYPERLINK
MAP_PIN_HYPERLINK
)
PlaySound(SOUNDKIT.UI_MAP_WAYPOINT_CHAT_SHARE)
else
-- classic
message = prefix
end
-- if you have an open editbox, just paste to it
if not ChatEdit_InsertLink(message) then
-- then do whatever's configured
if self.db.profile.announce == "OPENLAST" then
ChatFrame_OpenChat(message)
elseif self.db.profile.announce == "IMMEDIATELY" then
local generalID
if self.db.profile.announceChannel == "CHANNEL" then
generalID = module:GetGeneralID()
if not generalID then
ChatFrame_OpenChat(message)
return
end
end
Debug("SendChatMessage", message, self.db.profile.announceChannel, generalID)
SendChatMessage(
message,
self.db.profile.announceChannel,
nil, -- use default language
self.db.profile.announceChannel == "CHANNEL" and generalID or nil
)
end
end
end
function module:SendLinkToMob(id, uiMapID, x, y)
local unit = core:FindUnitWithID(id)
local prefix = core:NameForMob(id, unit)
if unit then
prefix = ("%s %s"):format(prefix, ('(' .. math.ceil(UnitHealth(unit) / UnitHealthMax(unit) * 100) .. '%)'))
end
self:SendLink(prefix, uiMapID, x, y)
end
function module:SendLinkToLoot(name, uiMapID, x, y)
self:SendLink(name, uiMapID, x, y)
end
function module:SendLinkFromData(data)
-- worldmap:uiMapId:x:y
local uiMapID, x, y = module:GetPositionFromData(data)
if data.type == "mob" then
self:SendLinkToMob(data.id, uiMapID, x, y)
elseif data.type == "loot" then
self:SendLinkToLoot(data.name, uiMapID, x, y)
end
end
function module:GetPositionFromData(data, allowFallback)
local x, y, uiMapID = data.x, data.y, data.zone
if uiMapID and data.vignetteGUID then
local position = C_VignetteInfo.GetVignettePosition(data.vignetteGUID, uiMapID)
if position then
x, y = position:GetXY()
end
end
if not (x and y and x > 0 and y > 0) and data.type == "mob" then
uiMapID, x, y = core:GetClosestLocationForMob(data.id)
end
if allowFallback and not (x and y and x > 0 and y > 0) then
-- fall back to sending a link to the current position
x, y, uiMapID = HBD:GetPlayerZonePosition()
end
return uiMapID, x, y
end
function module:CreateAnchor()
local anchor = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
anchor:SetSize(250, 100)
anchor:SetFrameStrata("DIALOG")
anchor:SetBackdrop({
bgFile = [[Interface\FriendsFrame\UI-Toast-Background]],
edgeFile = [[Interface\FriendsFrame\UI-Toast-Border]],
edgeSize = 12,
tile = true,
tileSize = 12,
insets = { left = 5, right = 5, top = 5, bottom = 5, },
})
anchor:EnableMouse(true)
anchor:RegisterForDrag("LeftButton")
anchor:SetClampedToScreen(true)
anchor:Hide()
local title = anchor:CreateFontString(nil, "BORDER", "FriendsFont_Normal")
title:SetJustifyH("CENTER")
title:SetJustifyV("MIDDLE")
title:SetWordWrap(true)
title:SetPoint("TOPLEFT", anchor, "TOPLEFT", 15, -10)
title:SetPoint("RIGHT", anchor, "RIGHT", -20, 10)
title:SetText(myname)
title:SetWidth(anchor:GetWidth())
local text = anchor:CreateFontString(nil, "BORDER", "FriendsFont_Normal")
text:SetSize(anchor:GetWidth() - 20, 24)
text:SetJustifyH("MIDDLE")
text:SetJustifyV("TOP")
text:SetWordWrap(true)
text:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4)
text:SetText("Target buttons will appear here")
local close = CreateFrame("Button", nil, anchor, "UIPanelCloseButton")
close:SetSize(24, 24)
close:SetPoint("TOPRIGHT", anchor, "TOPRIGHT", -2, -2)
anchor:SetHeight(text:GetStringHeight() + title:GetStringHeight() + 25)
LibWindow.RegisterConfig(anchor, self.db.profile.anchor)
LibWindow.RestorePosition(anchor)
LibWindow.MakeDraggable(anchor)
anchor:HookScript("OnDragStop", function()
AceConfigRegistry:NotifyChange(myname)
end)
return anchor
end