mirror of
https://github.com/fuffc/WeakestAuras.git
synced 2026-11-10 20:07:35 +00:00
ClassicAPI 1.18 is now the only requirement Requirements - Needs ClassicAPI 1.18.0 or newer. SuperWoW is no longer used, and Nampower is optional. - On an older ClassicAPI the addon stays off, and a popup links the download. - Custom triggers on SuperWoW or Nampower events only fire while that mod is installed. Triggers - New Combat Log trigger, with lists of spell IDs, spell names and NPC IDs. WeakAuras2 auras that use one now import. - Custom triggers can listen to the combat log with CLEU or CLEU:SUBEVENT. - Crowd Control matches a specific type (stun, fear, root, silence, disarm and more) or a school lockout, with the effect's real timer. - Saved Crowd Control auras: Root, Silence and Disarm filters carry over; CC and Snare become "any type", and snares are no longer detected. - Cast bars follow pushback and channel updates. Displays - Icon cooldown swipes are drawn by the client's own cooldown frame. - Sound actions play on a chosen channel, and a new Stop sound type ends a looping sound. Fixes - Cooldown auras no longer sweep on every wand shot. - The Global Cooldown trigger ignores wand and Auto Shot shots.
396 lines
14 KiB
Lua
396 lines
14 KiB
Lua
-- WeakestAuras -- radial wedge textures: the circular-progress primitive behind
|
|
-- the circular progress texture orientations. Mirrors
|
|
-- WA2's BaseRegions/TextureCoords.lua + BaseRegions/CircularProgressTexture.lua.
|
|
--
|
|
-- A wedge is drawn by moving the quad's four corners onto the region center
|
|
-- plus points on the perimeter: Texture:SetVertexOffset (the ClassicAPI
|
|
-- corner-transform backport) places the corner, and the 8-argument corner form
|
|
-- of SetTexCoord says what the art samples there. Upstream's paired write,
|
|
-- ported.
|
|
--
|
|
-- The pair only composes because both sides refuse to crop. The engine can
|
|
-- shrink a region's drawn rect by its texcoord span before storing the drawn
|
|
-- corners (FUN_REGION_TEXCOORD_CROP), which would make the corner texcoords a
|
|
-- second writer of the geometry the offsets are measured against; it gates that
|
|
-- on SetTexCoordModifiesRect, off by default, so a partial or corner-form
|
|
-- texcoord leaves the quad at full size (/wa texprobe corners). ClassicAPI's
|
|
-- transform used to call the crop directly, bypassing the gate, and a wedge
|
|
-- then collapsed onto the shrunken rect's edge -- diagnosed off /wa progtex
|
|
-- corner dumps and fixed on the fork. **This file requires that fix**: without
|
|
-- it every wedge whose texcoords span less than the full art is destroyed.
|
|
|
|
if WeakestAuras.disabled then return end
|
|
local WA = WeakestAuras
|
|
|
|
-- ClassicAPI's embedded addon publishes these ("!!!" loads it first); the
|
|
-- literals cover the headless harness, where it is absent.
|
|
local UPPER_LEFT = UPPER_LEFT_VERTEX or 1
|
|
local LOWER_LEFT = LOWER_LEFT_VERTEX or 2
|
|
local UPPER_RIGHT = UPPER_RIGHT_VERTEX or 3
|
|
local LOWER_RIGHT = LOWER_RIGHT_VERTEX or 4
|
|
|
|
WA.TextureCoords = {}
|
|
WA.Spinner = {}
|
|
|
|
local defaultTexCoord = {
|
|
ULx = 0, ULy = 0,
|
|
LLx = 0, LLy = 1,
|
|
URx = 1, URy = 0,
|
|
LRx = 1, LRy = 1,
|
|
}
|
|
|
|
-- Perimeter points of the eight exact 45-degree angles, clockwise from twelve
|
|
-- o'clock, in texcoord space.
|
|
local exactAngles = {
|
|
{ 0.5, 0 }, { 1, 0 }, { 1, 0.5 }, { 1, 1 },
|
|
{ 0.5, 1 }, { 0, 1 }, { 0, 0.5 }, { 0, 0 },
|
|
}
|
|
|
|
local function tanDeg(angle)
|
|
return math.tan(math.rad(angle))
|
|
end
|
|
|
|
-- Angle (degrees, 0 at twelve o'clock, increasing clockwise) -> the point on
|
|
-- the unit-square perimeter where a ray from the center at that angle exits.
|
|
local function angleToCoord(angle)
|
|
angle = angle % 360
|
|
if math.fmod(angle, 45) == 0 then
|
|
local index = math.floor(angle / 45) + 1
|
|
return exactAngles[index][1], exactAngles[index][2]
|
|
end
|
|
if angle < 45 then
|
|
return 0.5 + tanDeg(angle) / 2, 0
|
|
elseif angle < 135 then
|
|
return 1, 0.5 + tanDeg(angle - 90) / 2
|
|
elseif angle < 225 then
|
|
return 0.5 - tanDeg(angle) / 2, 1
|
|
elseif angle < 315 then
|
|
return 0, 0.5 - tanDeg(angle - 90) / 2
|
|
end
|
|
return 0.5 + tanDeg(angle) / 2, 0
|
|
end
|
|
|
|
-- Corner names in clockwise perimeter order, repeated so any start index reads
|
|
-- four consecutive corners. This order is what keeps every wedge's quad
|
|
-- consistently wound -- the engine culls a quad whose winding inverts, so a
|
|
-- reordering here shows up as a vanished wedge, not a distorted one.
|
|
local pointOrder = { "LL", "UL", "UR", "LR", "LL", "UL", "UR", "LR", "LL", "UL", "UR", "LR" }
|
|
|
|
-- Crop/mirror/rotate one texcoord around the texture center. scalex/scaley are
|
|
-- crop factors (1 = none, larger samples a wider area); texRotation is degrees.
|
|
local function TransformPoint(x, y, scalex, scaley, texRotation, mirror_h, mirror_v, user_x, user_y)
|
|
x = x - 0.5
|
|
y = y - 0.5
|
|
-- Grow the sampled area by sqrt(2) so a rotated square still covers its
|
|
-- frame (upstream's "shrink texture" step, expressed on the coords).
|
|
x = x * 1.4142
|
|
y = y * 1.4142
|
|
x = x / scalex
|
|
y = y / scaley
|
|
if mirror_h then x = -x end
|
|
if mirror_v then y = -y end
|
|
local cos_rotation = math.cos(math.rad(texRotation))
|
|
local sin_rotation = math.sin(math.rad(texRotation))
|
|
x, y = cos_rotation * x - sin_rotation * y, sin_rotation * x + cos_rotation * y
|
|
x = x + 0.5
|
|
y = y + 0.5
|
|
return x + (user_x or 0), y + (user_y or 0)
|
|
end
|
|
|
|
-- Published because the progresstexture region's linear orientations crop,
|
|
-- mirror and rotate through the same formula: one meaning for `crop_x` and
|
|
-- friends across both orientation families is what lets a WeakAuras2 aura's
|
|
-- values travel here untranslated.
|
|
WA.TextureCoords.TransformPoint = TransformPoint
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- TextureCoords: one texture's paired texcoord + vertex-offset bookkeeping.
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
local coordFuncs = {}
|
|
|
|
-- Move one corner to texcoord-space point (x, y), writing both halves: the
|
|
-- texcoord the corner samples, and the vertex displacement from the corner's
|
|
-- default position, in pixels of the drawn quad. Texcoord y grows downward
|
|
-- while vertex y grows upward, hence the one flipped sign.
|
|
function coordFuncs.MoveCorner(self, width, height, corner, x, y)
|
|
local rx = defaultTexCoord[corner .. "x"] - x
|
|
local ry = defaultTexCoord[corner .. "y"] - y
|
|
self[corner .. "vx"] = -rx * width
|
|
self[corner .. "vy"] = ry * height
|
|
self[corner .. "x"] = x
|
|
self[corner .. "y"] = y
|
|
end
|
|
|
|
function coordFuncs.Hide(self)
|
|
self.texture:Hide()
|
|
end
|
|
|
|
function coordFuncs.Show(self)
|
|
self:Apply()
|
|
self.texture:Show()
|
|
end
|
|
|
|
function coordFuncs.SetFull(self)
|
|
self.ULx, self.ULy = 0, 0
|
|
self.LLx, self.LLy = 0, 1
|
|
self.URx, self.URy = 1, 0
|
|
self.LRx, self.LRy = 1, 1
|
|
self.ULvx, self.ULvy = 0, 0
|
|
self.LLvx, self.LLvy = 0, 0
|
|
self.URvx, self.URvy = 0, 0
|
|
self.LRvx, self.LRvy = 0, 0
|
|
end
|
|
|
|
-- The one boundary where the wedge reaches the client. Offsets before
|
|
-- texcoords, upstream's order.
|
|
function coordFuncs.Apply(self)
|
|
local texture = self.texture
|
|
texture:SetVertexOffset(UPPER_RIGHT, self.URvx, self.URvy)
|
|
texture:SetVertexOffset(UPPER_LEFT, self.ULvx, self.ULvy)
|
|
texture:SetVertexOffset(LOWER_RIGHT, self.LRvx, self.LRvy)
|
|
texture:SetVertexOffset(LOWER_LEFT, self.LLvx, self.LLvy)
|
|
texture:SetTexCoord(self.ULx, self.ULy, self.LLx, self.LLy,
|
|
self.URx, self.URy, self.LRx, self.LRy)
|
|
end
|
|
|
|
-- Shape this texture into the wedge from angle1 to angle2, a span that may not
|
|
-- exceed two quadrants (the spinner splits wider spans across its textures).
|
|
function coordFuncs.SetAngle(self, width, height, angle1, angle2)
|
|
local index = math.floor((angle1 + 45) / 90)
|
|
|
|
local middleCorner = pointOrder[index + 1]
|
|
local startCorner = pointOrder[index + 2]
|
|
local endCorner1 = pointOrder[index + 3]
|
|
local endCorner2 = pointOrder[index + 4]
|
|
|
|
self:MoveCorner(width, height, middleCorner, 0.5, 0.5)
|
|
self:MoveCorner(width, height, startCorner, angleToCoord(angle1))
|
|
|
|
local edge1 = math.floor((angle1 - 45) / 90)
|
|
local edge2 = math.floor((angle2 - 45) / 90)
|
|
|
|
if edge1 == edge2 then
|
|
self:MoveCorner(width, height, endCorner1, angleToCoord(angle2))
|
|
else
|
|
self:MoveCorner(width, height, endCorner1,
|
|
defaultTexCoord[endCorner1 .. "x"], defaultTexCoord[endCorner1 .. "y"])
|
|
end
|
|
|
|
self:MoveCorner(width, height, endCorner2, angleToCoord(angle2))
|
|
end
|
|
|
|
function coordFuncs.Transform(self, scalex, scaley, texRotation, mirror_h, mirror_v, user_x, user_y)
|
|
self.ULx, self.ULy = TransformPoint(self.ULx, self.ULy, scalex, scaley, texRotation, mirror_h, mirror_v, user_x, user_y)
|
|
self.LLx, self.LLy = TransformPoint(self.LLx, self.LLy, scalex, scaley, texRotation, mirror_h, mirror_v, user_x, user_y)
|
|
self.URx, self.URy = TransformPoint(self.URx, self.URy, scalex, scaley, texRotation, mirror_h, mirror_v, user_x, user_y)
|
|
self.LRx, self.LRy = TransformPoint(self.LRx, self.LRy, scalex, scaley, texRotation, mirror_h, mirror_v, user_x, user_y)
|
|
end
|
|
|
|
function WA.TextureCoords.Create(texture)
|
|
local coord = {
|
|
ULx = 0, ULy = 0,
|
|
LLx = 0, LLy = 1,
|
|
URx = 1, URy = 0,
|
|
LRx = 1, LRy = 1,
|
|
ULvx = 0, ULvy = 0,
|
|
LLvx = 0, LLvy = 0,
|
|
URvx = 0, URvy = 0,
|
|
LRvx = 0, LRvy = 0,
|
|
texture = texture,
|
|
}
|
|
for name, func in pairs(coordFuncs) do coord[name] = func end
|
|
return coord
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Spinner: three wedge textures covering an arbitrary angle span.
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
local spinnerFuncs = {}
|
|
|
|
-- Changing what a wedge samples reaches the engine's own corner store, which
|
|
-- writes back the plain axis-aligned quad, so the wedge shape has to be
|
|
-- rewritten after it. The art setter owns that rather than each caller, so a
|
|
-- caller that never reaches UpdateTextures on its own path cannot leave the
|
|
-- wedges collapsed.
|
|
function spinnerFuncs.SetTexture(self, path)
|
|
for i = 1, 3 do self.textures[i]:SetTexture(path) end
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function spinnerFuncs.SetDesaturated(self, desaturate)
|
|
for i = 1, 3 do self.textures[i]:SetDesaturated(desaturate and true or false) end
|
|
end
|
|
|
|
function spinnerFuncs.SetBlendMode(self, blendMode)
|
|
for i = 1, 3 do self.textures[i]:SetBlendMode(blendMode or "BLEND") end
|
|
end
|
|
|
|
-- Whole-spinner rotation (radians). Safe alongside the wedge offsets: rotation
|
|
-- and vertex offsets live in one ClassicAPI transform that composes them --
|
|
-- one writer, unlike SetTexCoord.
|
|
function spinnerFuncs.SetAuraRotation(self, radians)
|
|
for i = 1, 3 do self.textures[i]:SetRotation(radians or 0) end
|
|
end
|
|
|
|
function spinnerFuncs.SetColor(self, r, g, b, a)
|
|
for i = 1, 3 do self.textures[i]:SetVertexColor(r, g, b, a or 1) end
|
|
end
|
|
|
|
function spinnerFuncs.Show(self)
|
|
self.visible = true
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function spinnerFuncs.Hide(self)
|
|
self.visible = false
|
|
for i = 1, 3 do self.textures[i]:Hide() end
|
|
end
|
|
|
|
function spinnerFuncs.SetCropX(self, crop_x)
|
|
self.crop_x = crop_x
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function spinnerFuncs.SetCropY(self, crop_y)
|
|
self.crop_y = crop_y
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function spinnerFuncs.SetTexRotation(self, texRotation)
|
|
self.texRotation = texRotation
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function spinnerFuncs.SetMirrorHV(self, mirror_h, mirror_v)
|
|
self.mirror_h = mirror_h
|
|
self.mirror_v = mirror_v
|
|
end
|
|
|
|
function spinnerFuncs.SetMirror(self, mirror)
|
|
self.mirror = mirror
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function spinnerFuncs.SetWidth(self, width)
|
|
self.width = width
|
|
end
|
|
|
|
function spinnerFuncs.SetHeight(self, height)
|
|
self.height = height
|
|
end
|
|
|
|
function spinnerFuncs.SetScale(self, scalex, scaley)
|
|
self.scalex, self.scaley = scalex, scaley
|
|
end
|
|
|
|
-- A reused spinner must not carry its last shape into the next owner: the
|
|
-- client keys vertex offsets and rotation on the raw texture and holds them
|
|
-- until explicitly cleared, so releasing without this hands the next user a
|
|
-- pre-warped quad.
|
|
function spinnerFuncs.Reset(self)
|
|
for i = 1, 3 do
|
|
self.coords[i]:SetFull()
|
|
self.coords[i]:Apply()
|
|
end
|
|
self:SetAuraRotation(0)
|
|
self.angle1, self.angle2 = nil, nil
|
|
end
|
|
|
|
function spinnerFuncs.UpdateTextures(self)
|
|
if not self.visible then return end
|
|
local crop_x = self.crop_x or 1
|
|
local crop_y = self.crop_y or 1
|
|
local texRotation = self.texRotation or 0
|
|
local mirror_h = self.mirror_h or false
|
|
if self.mirror then
|
|
mirror_h = not mirror_h
|
|
end
|
|
local mirror_v = self.mirror_v or false
|
|
|
|
local width = (self.width or 0) * (self.scalex or 1) + 2 * self.offset
|
|
local height = (self.height or 0) * (self.scaley or 1) + 2 * self.offset
|
|
if width == 0 or height == 0 then return end
|
|
|
|
local angle1 = self.angle1
|
|
local angle2 = self.angle2
|
|
if angle1 == nil or angle2 == nil then return end
|
|
|
|
if angle2 - angle1 >= 360 then
|
|
self.coords[1]:SetFull()
|
|
self.coords[1]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[1]:Show()
|
|
self.coords[2]:Hide()
|
|
self.coords[3]:Hide()
|
|
return
|
|
end
|
|
if angle1 == angle2 then
|
|
self.coords[1]:Hide()
|
|
self.coords[2]:Hide()
|
|
self.coords[3]:Hide()
|
|
return
|
|
end
|
|
|
|
local index1 = math.floor((angle1 + 45) / 90)
|
|
local index2 = math.floor((angle2 + 45) / 90)
|
|
|
|
if index1 + 1 >= index2 then
|
|
self.coords[1]:SetAngle(width, height, angle1, angle2)
|
|
self.coords[1]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[1]:Show()
|
|
self.coords[2]:Hide()
|
|
self.coords[3]:Hide()
|
|
elseif index1 + 3 >= index2 then
|
|
local firstEndAngle = (index1 + 1) * 90 + 45
|
|
self.coords[1]:SetAngle(width, height, angle1, firstEndAngle)
|
|
self.coords[1]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[1]:Show()
|
|
self.coords[2]:SetAngle(width, height, firstEndAngle, angle2)
|
|
self.coords[2]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[2]:Show()
|
|
self.coords[3]:Hide()
|
|
else
|
|
local firstEndAngle = (index1 + 1) * 90 + 45
|
|
local secondEndAngle = firstEndAngle + 180
|
|
self.coords[1]:SetAngle(width, height, angle1, firstEndAngle)
|
|
self.coords[1]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[1]:Show()
|
|
self.coords[2]:SetAngle(width, height, firstEndAngle, secondEndAngle)
|
|
self.coords[2]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[2]:Show()
|
|
self.coords[3]:SetAngle(width, height, secondEndAngle, angle2)
|
|
self.coords[3]:Transform(crop_x, crop_y, texRotation, mirror_h, mirror_v)
|
|
self.coords[3]:Show()
|
|
end
|
|
end
|
|
|
|
-- The wedge from angle1 to angle2 (degrees, clockwise from twelve o'clock).
|
|
-- angle2 may exceed 360 to express a span crossing the top; a span of 360 or
|
|
-- more shows everything, a zero span nothing.
|
|
function spinnerFuncs.SetProgress(self, angle1, angle2)
|
|
self.angle1 = angle1
|
|
self.angle2 = angle2
|
|
self:UpdateTextures()
|
|
end
|
|
|
|
function WA.Spinner.Create(frame, layer)
|
|
local spinner = {
|
|
textures = {},
|
|
coords = {},
|
|
offset = 0,
|
|
visible = true,
|
|
parentFrame = frame,
|
|
}
|
|
for i = 1, 3 do
|
|
local texture = frame:CreateTexture(nil, layer or "ARTWORK")
|
|
if texture.SetSnapToPixelGrid then texture:SetSnapToPixelGrid(false) end
|
|
if texture.SetTexelSnappingBias then texture:SetTexelSnappingBias(0) end
|
|
texture:SetAllPoints(frame)
|
|
spinner.textures[i] = texture
|
|
spinner.coords[i] = WA.TextureCoords.Create(texture)
|
|
end
|
|
for name, func in pairs(spinnerFuncs) do spinner[name] = func end
|
|
return spinner
|
|
end
|