1065 lines
38 KiB
Lua
1065 lines
38 KiB
Lua
SFrames.Raid = {}
|
|
local _A = SFrames.ActiveTheme
|
|
|
|
local RAID_FRAME_WIDTH = 60
|
|
local RAID_FRAME_HEIGHT = 40
|
|
local GROUP_PADDING = 8
|
|
local UNIT_PADDING = 2
|
|
|
|
local RAID_UNIT_LOOKUP = {}
|
|
for i = 1, 40 do RAID_UNIT_LOOKUP["raid" .. i] = true end
|
|
|
|
local function GetIncomingHeals(unit)
|
|
return SFrames:GetIncomingHeals(unit)
|
|
end
|
|
|
|
function SFrames.Raid:GetMetrics()
|
|
local db = SFramesDB or {}
|
|
|
|
local width = tonumber(db.raidFrameWidth) or RAID_FRAME_WIDTH
|
|
width = math.max(30, math.min(150, width))
|
|
|
|
local height = tonumber(db.raidFrameHeight) or RAID_FRAME_HEIGHT
|
|
height = math.max(20, math.min(80, height))
|
|
|
|
local healthHeight = tonumber(db.raidHealthHeight) or math.floor((height - 3) * 0.8)
|
|
healthHeight = math.max(10, math.min(height - 6, healthHeight))
|
|
|
|
local powerHeight = height - healthHeight - 3
|
|
if not db.raidShowPower then
|
|
powerHeight = 0
|
|
healthHeight = height - 2
|
|
end
|
|
|
|
local hgap = tonumber(db.raidHorizontalGap) or UNIT_PADDING
|
|
local vgap = tonumber(db.raidVerticalGap) or UNIT_PADDING
|
|
local groupGap = tonumber(db.raidGroupGap) or GROUP_PADDING
|
|
|
|
local nameFont = tonumber(db.raidNameFontSize) or 10
|
|
local valueFont = tonumber(db.raidValueFontSize) or 9
|
|
|
|
return {
|
|
width = width,
|
|
height = height,
|
|
healthHeight = healthHeight,
|
|
powerHeight = powerHeight,
|
|
horizontalGap = hgap,
|
|
verticalGap = vgap,
|
|
groupGap = groupGap,
|
|
nameFont = nameFont,
|
|
valueFont = valueFont,
|
|
showPower = db.raidShowPower ~= false,
|
|
}
|
|
end
|
|
|
|
function SFrames.Raid:ApplyFrameStyle(frame, metrics)
|
|
if not frame then return end
|
|
|
|
frame:SetWidth(metrics.width)
|
|
frame:SetHeight(metrics.height)
|
|
|
|
if frame.health then
|
|
frame.health:ClearAllPoints()
|
|
frame.health:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
|
|
frame.health:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -1, -1)
|
|
frame.health:SetHeight(metrics.healthHeight)
|
|
end
|
|
|
|
if frame.healthBGFrame then
|
|
frame.healthBGFrame:ClearAllPoints()
|
|
frame.healthBGFrame:SetPoint("TOPLEFT", frame.health, "TOPLEFT", -1, 1)
|
|
frame.healthBGFrame:SetPoint("BOTTOMRIGHT", frame.health, "BOTTOMRIGHT", 1, -1)
|
|
end
|
|
|
|
if frame.power then
|
|
if metrics.showPower then
|
|
frame.power:Show()
|
|
if frame.powerBGFrame then frame.powerBGFrame:Show() end
|
|
frame.power:ClearAllPoints()
|
|
frame.power:SetPoint("TOPLEFT", frame.health, "BOTTOMLEFT", 0, -1)
|
|
frame.power:SetPoint("TOPRIGHT", frame.health, "BOTTOMRIGHT", 0, 0)
|
|
frame.power:SetHeight(metrics.powerHeight)
|
|
else
|
|
frame.power:Hide()
|
|
if frame.powerBGFrame then frame.powerBGFrame:Hide() end
|
|
end
|
|
end
|
|
|
|
local outline = (SFrames and SFrames.Media and SFrames.Media.fontOutline) or "OUTLINE"
|
|
local fontPath = SFrames:GetFont()
|
|
|
|
if frame.nameText then
|
|
frame.nameText:SetFont(fontPath, metrics.nameFont, outline)
|
|
end
|
|
if frame.healthText then
|
|
frame.healthText:SetFont(fontPath, metrics.valueFont, outline)
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:ApplyLayout()
|
|
if not (self.parent and self._framesBuilt) then return end
|
|
|
|
local metrics = self:GetMetrics()
|
|
self.metrics = metrics
|
|
local mode = (SFramesDB and SFramesDB.raidLayout) or "horizontal"
|
|
local showLabel = SFramesDB and SFramesDB.raidShowGroupLabel ~= false
|
|
|
|
local LABEL_H = 16 -- px above each column (horizontal mode)
|
|
local LABEL_W = 16 -- px left of each row (vertical mode)
|
|
|
|
for g = 1, 8 do
|
|
local groupParent = self.groups[g]
|
|
groupParent:ClearAllPoints()
|
|
|
|
if mode == "horizontal" then
|
|
local extraTop = showLabel and LABEL_H or 0
|
|
if g == 1 then
|
|
groupParent:SetPoint("TOPLEFT", self.parent, "TOPLEFT", 0, 0)
|
|
else
|
|
groupParent:SetPoint("TOPLEFT", self.groups[g-1], "TOPRIGHT", metrics.groupGap, 0)
|
|
end
|
|
groupParent:SetWidth(metrics.width)
|
|
groupParent:SetHeight(extraTop + (metrics.height * 5) + (metrics.verticalGap * 4))
|
|
else
|
|
local extraLeft = showLabel and LABEL_W or 0
|
|
if g == 1 then
|
|
groupParent:SetPoint("TOPLEFT", self.parent, "TOPLEFT", 0, 0)
|
|
else
|
|
groupParent:SetPoint("TOPLEFT", self.groups[g-1], "BOTTOMLEFT", 0, -metrics.groupGap)
|
|
end
|
|
groupParent:SetWidth(extraLeft + (metrics.width * 5) + (metrics.horizontalGap * 4))
|
|
groupParent:SetHeight(metrics.height)
|
|
end
|
|
|
|
-- Position label; visibility is controlled per-group in UpdateAll
|
|
local lbl = self.groupLabels and self.groupLabels[g]
|
|
if lbl then
|
|
lbl:ClearAllPoints()
|
|
if showLabel then
|
|
if mode == "horizontal" then
|
|
-- Place label at the very top of the column, centered horizontally
|
|
-- Frames start at -LABEL_H, so the strip [0, -LABEL_H] is exclusively for the label
|
|
lbl:SetPoint("TOP", groupParent, "TOP", 0, -2)
|
|
else
|
|
-- Vertically centered on the left strip, centered horizontally within it
|
|
lbl:SetPoint("CENTER", groupParent, "LEFT", LABEL_W / 2, 0)
|
|
end
|
|
else
|
|
lbl:Hide()
|
|
end
|
|
end
|
|
|
|
for i = 1, 5 do
|
|
local index = (g - 1) * 5 + i
|
|
local f = self.frames[index].frame
|
|
f:ClearAllPoints()
|
|
|
|
if mode == "horizontal" then
|
|
local extraTop = showLabel and LABEL_H or 0
|
|
if i == 1 then
|
|
-- Frames start below the label strip
|
|
f:SetPoint("TOPLEFT", groupParent, "TOPLEFT", 0, -extraTop)
|
|
else
|
|
f:SetPoint("TOPLEFT", self.frames[index-1].frame, "BOTTOMLEFT", 0, -metrics.verticalGap)
|
|
end
|
|
else
|
|
local extraLeft = showLabel and LABEL_W or 0
|
|
if i == 1 then
|
|
f:SetPoint("TOPLEFT", groupParent, "TOPLEFT", extraLeft, 0)
|
|
else
|
|
f:SetPoint("TOPLEFT", self.frames[index-1].frame, "TOPRIGHT", metrics.horizontalGap, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local extraTop = showLabel and LABEL_H or 0
|
|
local extraLeft = showLabel and LABEL_W or 0
|
|
if mode == "horizontal" then
|
|
self.parent:SetWidth((metrics.width * 8) + (metrics.groupGap * 7))
|
|
self.parent:SetHeight(extraTop + (metrics.height * 5) + (metrics.verticalGap * 4))
|
|
else
|
|
self.parent:SetWidth(extraLeft + (metrics.width * 5) + (metrics.horizontalGap * 4))
|
|
self.parent:SetHeight((metrics.height * 8) + (metrics.groupGap * 7))
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:SavePosition()
|
|
if not (self.parent and SFramesDB) then return end
|
|
if not SFramesDB.Positions then SFramesDB.Positions = {} end
|
|
local point, _, relativePoint, xOfs, yOfs = self.parent:GetPoint()
|
|
if point and relativePoint then
|
|
SFramesDB.Positions["RaidFrame"] = {
|
|
point = point,
|
|
relativePoint = relativePoint,
|
|
xOfs = xOfs or 0,
|
|
yOfs = yOfs or 0,
|
|
}
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:ApplyPosition()
|
|
if not self.parent then return end
|
|
self.parent:ClearAllPoints()
|
|
local pos = SFramesDB and SFramesDB.Positions and SFramesDB.Positions["RaidFrame"]
|
|
if pos and pos.point and pos.relativePoint and type(pos.xOfs) == "number" and type(pos.yOfs) == "number" then
|
|
self.parent:SetPoint(pos.point, UIParent, pos.relativePoint, pos.xOfs, pos.yOfs)
|
|
else
|
|
self.parent:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 15, -200)
|
|
end
|
|
end
|
|
|
|
local function TryDropCursorOnUnit(unit)
|
|
if not unit or not UnitExists(unit) then return false end
|
|
if not CursorHasItem or not CursorHasItem() then return false end
|
|
if not DropItemOnUnit then return false end
|
|
|
|
local ok = pcall(DropItemOnUnit, unit)
|
|
return not CursorHasItem()
|
|
end
|
|
|
|
function SFrames.Raid:Initialize()
|
|
self.frames = {}
|
|
self.groups = {}
|
|
self._framesBuilt = false
|
|
|
|
if not SFramesDB then SFramesDB = {} end
|
|
|
|
local parent = CreateFrame("Frame", "SFramesRaidParent", UIParent)
|
|
local frameScale = (SFramesDB and type(SFramesDB.raidFrameScale) == "number") and SFramesDB.raidFrameScale or 1
|
|
parent:SetScale(frameScale)
|
|
self.parent = parent
|
|
self:ApplyPosition()
|
|
|
|
parent:SetMovable(true)
|
|
|
|
self.groupLabels = {}
|
|
for g = 1, 8 do
|
|
local groupParent = CreateFrame("Frame", "SFramesRaidGroup"..g, parent)
|
|
self.groups[g] = groupParent
|
|
|
|
local lbl = groupParent:CreateFontString(nil, "OVERLAY")
|
|
lbl:SetFont(SFrames:GetFont(), 9, "OUTLINE")
|
|
lbl:SetTextColor(0.85, 0.78, 0.92)
|
|
lbl:SetShadowColor(0, 0, 0, 1)
|
|
lbl:SetShadowOffset(1, -1)
|
|
lbl:SetText("小队 " .. g)
|
|
lbl:Hide()
|
|
self.groupLabels[g] = lbl
|
|
end
|
|
|
|
SFrames:RegisterEvent("RAID_ROSTER_UPDATE", function() self:UpdateAll() end)
|
|
SFrames:RegisterEvent("PARTY_LEADER_CHANGED", function() self:UpdateAll() end)
|
|
SFrames:RegisterEvent("PLAYER_TARGET_CHANGED", function() self:UpdateTargetHighlight() end)
|
|
SFrames:RegisterEvent("UNIT_HEALTH", function() if RAID_UNIT_LOOKUP[arg1] then self:UpdateHealth(arg1) end end)
|
|
SFrames:RegisterEvent("UNIT_MAXHEALTH", function() if RAID_UNIT_LOOKUP[arg1] then self:UpdateHealth(arg1) end end)
|
|
SFrames:RegisterEvent("UNIT_MANA", function() if RAID_UNIT_LOOKUP[arg1] then self:UpdatePower(arg1) end end)
|
|
SFrames:RegisterEvent("UNIT_MAXMANA", function() if RAID_UNIT_LOOKUP[arg1] then self:UpdatePower(arg1) end end)
|
|
SFrames:RegisterEvent("UNIT_AURA", function() if RAID_UNIT_LOOKUP[arg1] then self:UpdateAuras(arg1) end end)
|
|
SFrames:RegisterEvent("RAID_TARGET_UPDATE", function() self:UpdateRaidIcons() end)
|
|
|
|
self:UpdateAll()
|
|
end
|
|
|
|
function SFrames.Raid:EnsureFrames()
|
|
if self._framesBuilt then return end
|
|
self._framesBuilt = true
|
|
|
|
for i = 1, 40 do
|
|
local unit = "raid" .. i
|
|
local groupIndex = math.ceil(i / 5)
|
|
local groupParent = self.groups[groupIndex]
|
|
local f = CreateFrame("Button", "SFramesRaidFrame"..i, groupParent)
|
|
|
|
f.id = i
|
|
f:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
f:RegisterForDrag("LeftButton")
|
|
f:SetScript("OnDragStart", function()
|
|
if IsAltKeyDown() or SFrames.isUnlocked then
|
|
SFrames.Raid.parent:StartMoving()
|
|
end
|
|
end)
|
|
f:SetScript("OnDragStop", function()
|
|
SFrames.Raid.parent:StopMovingOrSizing()
|
|
SFrames.Raid:SavePosition()
|
|
end)
|
|
f:SetScript("OnClick", function()
|
|
if arg1 == "LeftButton" then
|
|
if TryDropCursorOnUnit(this.unit) then return end
|
|
if SpellIsTargeting and SpellIsTargeting() then
|
|
SpellTargetUnit(this.unit)
|
|
return
|
|
end
|
|
TargetUnit(this.unit)
|
|
elseif arg1 == "RightButton" then
|
|
if UnitExists(this.unit) then
|
|
GameTooltip:Hide()
|
|
if not SFrames.Raid.dropDown then
|
|
SFrames.Raid.dropDown = CreateFrame("Frame", "SFramesRaidDropDown", UIParent, "UIDropDownMenuTemplate")
|
|
SFrames.Raid.dropDown.displayMode = "MENU"
|
|
SFrames.Raid.dropDown.initialize = function()
|
|
local dd = SFrames.Raid.dropDown
|
|
local unit = dd.unit
|
|
local name = dd.name
|
|
if not unit or not name then return end
|
|
|
|
local info = {}
|
|
info.text = name
|
|
info.isTitle = 1
|
|
info.notCheckable = 1
|
|
UIDropDownMenu_AddButton(info)
|
|
|
|
if not UnitIsUnit(unit, "player") then
|
|
info = {}
|
|
info.text = WHISPER or "密语"
|
|
info.notCheckable = 1
|
|
info.func = function() ChatFrame_SendTell(name) end
|
|
UIDropDownMenu_AddButton(info)
|
|
|
|
info = {}
|
|
info.text = INSPECT or "检查"
|
|
info.notCheckable = 1
|
|
info.func = function() InspectUnit(unit) end
|
|
UIDropDownMenu_AddButton(info)
|
|
|
|
info = {}
|
|
info.text = TRADE or "交易"
|
|
info.notCheckable = 1
|
|
info.func = function() InitiateTrade(unit) end
|
|
UIDropDownMenu_AddButton(info)
|
|
|
|
info = {}
|
|
info.text = FOLLOW or "跟随"
|
|
info.notCheckable = 1
|
|
info.func = function() FollowUnit(unit) end
|
|
UIDropDownMenu_AddButton(info)
|
|
end
|
|
|
|
if (IsRaidLeader and IsRaidLeader()) or (IsRaidOfficer and IsRaidOfficer()) then
|
|
if not UnitIsUnit(unit, "player") then
|
|
info = {}
|
|
info.text = "提升为助手"
|
|
info.notCheckable = 1
|
|
info.func = function() PromoteToAssistant(name) end
|
|
UIDropDownMenu_AddButton(info)
|
|
|
|
info = {}
|
|
info.text = "移出团队"
|
|
info.notCheckable = 1
|
|
info.func = function() UninviteByName(name) end
|
|
UIDropDownMenu_AddButton(info)
|
|
end
|
|
end
|
|
|
|
info = {}
|
|
info.text = CANCEL or "取消"
|
|
info.notCheckable = 1
|
|
UIDropDownMenu_AddButton(info)
|
|
end
|
|
end
|
|
SFrames.Raid.dropDown.unit = this.unit
|
|
SFrames.Raid.dropDown.name = UnitName(this.unit)
|
|
ToggleDropDownMenu(1, nil, SFrames.Raid.dropDown, "cursor")
|
|
end
|
|
end
|
|
end)
|
|
f:SetScript("OnReceiveDrag", function()
|
|
if TryDropCursorOnUnit(this.unit) then return end
|
|
if SpellIsTargeting and SpellIsTargeting() then
|
|
SpellTargetUnit(this.unit)
|
|
end
|
|
end)
|
|
f:SetScript("OnEnter", function()
|
|
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
|
GameTooltip:SetUnit(this.unit)
|
|
end)
|
|
f:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
f.unit = unit
|
|
|
|
-- Health Bar
|
|
f.health = SFrames:CreateStatusBar(f, "SFramesRaidFrame"..i.."Health")
|
|
|
|
local hbg = CreateFrame("Frame", nil, f)
|
|
hbg:SetFrameLevel(f:GetFrameLevel() - 1)
|
|
SFrames:CreateUnitBackdrop(hbg)
|
|
f.healthBGFrame = hbg
|
|
|
|
f.health.bg = f.health:CreateTexture(nil, "BACKGROUND")
|
|
f.health.bg:SetAllPoints()
|
|
f.health.bg:SetTexture(SFrames:GetTexture())
|
|
f.health.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
|
|
-- Heal prediction overlay (incoming heals)
|
|
f.health.healPredMine = f.health:CreateTexture(nil, "OVERLAY")
|
|
f.health.healPredMine:SetTexture(SFrames:GetTexture())
|
|
f.health.healPredMine:SetVertexColor(0.4, 1.0, 0.55, 0.78)
|
|
f.health.healPredMine:Hide()
|
|
|
|
f.health.healPredOther = f.health:CreateTexture(nil, "OVERLAY")
|
|
f.health.healPredOther:SetTexture(SFrames:GetTexture())
|
|
f.health.healPredOther:SetVertexColor(0.2, 0.9, 0.35, 0.5)
|
|
f.health.healPredOther:Hide()
|
|
|
|
-- Power Bar
|
|
f.power = SFrames:CreateStatusBar(f, "SFramesRaidFrame"..i.."Power")
|
|
f.power:SetMinMaxValues(0, 100)
|
|
|
|
local powerbg = CreateFrame("Frame", nil, f)
|
|
powerbg:SetFrameLevel(f:GetFrameLevel() - 1)
|
|
SFrames:CreateUnitBackdrop(powerbg)
|
|
f.powerBGFrame = powerbg
|
|
|
|
f.power.bg = f.power:CreateTexture(nil, "BACKGROUND")
|
|
f.power.bg:SetAllPoints()
|
|
f.power.bg:SetTexture(SFrames:GetTexture())
|
|
f.power.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
|
|
-- Target Highlight
|
|
f.targetHighlight = f:CreateTexture(nil, "OVERLAY")
|
|
f.targetHighlight:SetPoint("TOPLEFT", f, "TOPLEFT", -2, 2)
|
|
f.targetHighlight:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 2, -2)
|
|
f.targetHighlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
f.targetHighlight:SetBlendMode("ADD")
|
|
f.targetHighlight:SetVertexColor(1, 1, 1, 0.6)
|
|
f.targetHighlight:Hide()
|
|
|
|
-- Texts
|
|
f.nameText = SFrames:CreateFontString(f.health, 10, "CENTER")
|
|
f.nameText:SetPoint("TOP", f.health, "TOP", 0, -2)
|
|
f.nameText:SetShadowColor(0, 0, 0, 1)
|
|
f.nameText:SetShadowOffset(1, -1)
|
|
|
|
f.healthText = SFrames:CreateFontString(f.health, 9, "CENTER")
|
|
f.healthText:SetPoint("BOTTOM", f.health, "BOTTOM", 0, 2)
|
|
f.healthText:SetShadowColor(0, 0, 0, 1)
|
|
f.healthText:SetShadowOffset(1, -1)
|
|
f.healthText:SetTextColor(0.8, 0.8, 0.8)
|
|
|
|
local offOvr = CreateFrame("Frame", nil, f)
|
|
offOvr:SetFrameLevel((f:GetFrameLevel() or 0) + 4)
|
|
offOvr:SetAllPoints(f)
|
|
local offIco = SFrames:CreateIcon(offOvr, "offline", 16)
|
|
offIco:SetPoint("CENTER", offOvr, "CENTER", 0, 0)
|
|
offIco:SetVertexColor(0.7, 0.7, 0.7, 0.9)
|
|
offIco:Hide()
|
|
f.offlineIcon = offIco
|
|
|
|
local roleOvr = CreateFrame("Frame", nil, f)
|
|
roleOvr:SetFrameLevel((f:GetFrameLevel() or 0) + 6)
|
|
roleOvr:SetAllPoints(f)
|
|
|
|
f.leaderIcon = roleOvr:CreateTexture(nil, "OVERLAY")
|
|
f.leaderIcon:SetWidth(12)
|
|
f.leaderIcon:SetHeight(12)
|
|
f.leaderIcon:SetPoint("TOPLEFT", f, "TOPLEFT", -3, 3)
|
|
f.leaderIcon:SetTexture("Interface\\GroupFrame\\UI-Group-LeaderIcon")
|
|
f.leaderIcon:Hide()
|
|
|
|
f.assistantIcon = roleOvr:CreateFontString(nil, "OVERLAY")
|
|
f.assistantIcon:SetFont(SFrames:GetFont(), 12, "OUTLINE")
|
|
f.assistantIcon:SetPoint("TOPLEFT", f, "TOPLEFT", -1, 1)
|
|
f.assistantIcon:SetTextColor(0.5, 0.8, 1.0)
|
|
f.assistantIcon:SetShadowColor(0, 0, 0, 1)
|
|
f.assistantIcon:SetShadowOffset(1, -1)
|
|
f.assistantIcon:SetText("+")
|
|
f.assistantIcon:Hide()
|
|
|
|
-- Raid Target Icon
|
|
local raidIconSize = 16
|
|
local raidIconOvr = CreateFrame("Frame", nil, f)
|
|
raidIconOvr:SetFrameLevel((f:GetFrameLevel() or 0) + 7)
|
|
raidIconOvr:SetWidth(raidIconSize)
|
|
raidIconOvr:SetHeight(raidIconSize)
|
|
raidIconOvr:SetPoint("CENTER", f.health, "TOP", 0, 0)
|
|
f.raidIcon = raidIconOvr:CreateTexture(nil, "OVERLAY")
|
|
f.raidIcon:SetTexture("Interface\\TargetingFrame\\UI-RaidTargetingIcons")
|
|
f.raidIcon:SetAllPoints(raidIconOvr)
|
|
f.raidIcon:Hide()
|
|
f.raidIconOverlay = raidIconOvr
|
|
|
|
f.indicators = {}
|
|
for pos = 1, 4 do
|
|
local ind = CreateFrame("Button", nil, f)
|
|
ind:SetWidth(10)
|
|
ind:SetHeight(10)
|
|
ind:SetFrameLevel(f:GetFrameLevel() + 5)
|
|
SFrames:CreateUnitBackdrop(ind)
|
|
|
|
ind.icon = ind:CreateTexture(nil, "ARTWORK")
|
|
ind.icon:SetAllPoints()
|
|
ind.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
|
|
|
|
if pos == 1 then
|
|
ind:SetPoint("TOPLEFT", f.health, "TOPLEFT", 1, -1)
|
|
elseif pos == 2 then
|
|
ind:SetPoint("TOPRIGHT", f.health, "TOPRIGHT", -1, -1)
|
|
elseif pos == 3 then
|
|
ind:SetPoint("BOTTOMLEFT", f.health, "BOTTOMLEFT", 1, 1)
|
|
elseif pos == 4 then
|
|
ind:SetPoint("BOTTOMRIGHT", f.health, "BOTTOMRIGHT", -1, 1)
|
|
end
|
|
|
|
ind:SetScript("OnEnter", function()
|
|
if this.index then
|
|
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
|
|
if this.isDebuff then
|
|
GameTooltip:SetUnitDebuff(f.unit, this.index)
|
|
else
|
|
GameTooltip:SetUnitBuff(f.unit, this.index)
|
|
end
|
|
end
|
|
end)
|
|
ind:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
|
|
ind:Hide()
|
|
f.indicators[pos] = ind
|
|
end
|
|
|
|
self.frames[i] = { frame = f, unit = unit, index = i }
|
|
|
|
self:ApplyFrameStyle(f, self:GetMetrics())
|
|
|
|
f.rangeTimer = 0
|
|
f.auraScanTimer = 0
|
|
f.healPredTimer = 0
|
|
|
|
f:SetScript("OnShow", function()
|
|
if not SFrames.Raid.testing then
|
|
SFrames.Raid:UpdateFrame(this.unit)
|
|
end
|
|
end)
|
|
|
|
f:Hide()
|
|
end
|
|
|
|
if not self._globalUpdateFrame then
|
|
self._globalUpdateFrame = CreateFrame("Frame", nil, UIParent)
|
|
self._globalUpdateFrame:SetScript("OnUpdate", function()
|
|
if SFrames.Raid.testing then return end
|
|
local dt = arg1
|
|
local frames = SFrames.Raid.frames
|
|
if not frames then return end
|
|
for i = 1, 40 do
|
|
local entry = frames[i]
|
|
if entry then
|
|
local f = entry.frame
|
|
if f:IsVisible() and f.unit and f.unit ~= "" then
|
|
f.rangeTimer = f.rangeTimer + dt
|
|
if f.rangeTimer >= 0.5 then
|
|
if UnitExists(f.unit) and not CheckInteractDistance(f.unit, 4) then
|
|
f:SetAlpha(0.6)
|
|
else
|
|
f:SetAlpha(1.0)
|
|
end
|
|
f.rangeTimer = 0
|
|
end
|
|
|
|
f.auraScanTimer = f.auraScanTimer + dt
|
|
if f.auraScanTimer >= 0.7 then
|
|
SFrames.Raid:UpdateAuras(f.unit)
|
|
f.auraScanTimer = 0
|
|
end
|
|
|
|
f.healPredTimer = f.healPredTimer + dt
|
|
if f.healPredTimer >= 0.2 then
|
|
SFrames.Raid:UpdateHealPrediction(f.unit)
|
|
f.healPredTimer = 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
self:ApplyLayout()
|
|
end
|
|
|
|
function SFrames.Raid:UpdateAll()
|
|
if self.testing then return end
|
|
|
|
if SFramesDB and SFramesDB.enableRaidFrames == false then
|
|
self.parent:Hide()
|
|
return
|
|
else
|
|
self.parent:Show()
|
|
end
|
|
|
|
if GetNumRaidMembers() > 0 then
|
|
self:EnsureFrames()
|
|
for i = 1, 40 do
|
|
self.frames[i].frame:Hide()
|
|
self.frames[i].frame.unit = ""
|
|
self.frames[i].unit = ""
|
|
end
|
|
|
|
local groupSlots = {}
|
|
for g = 1, 8 do
|
|
groupSlots[g] = 0
|
|
end
|
|
|
|
for i = 1, 40 do
|
|
local name, rank, subgroup = GetRaidRosterInfo(i)
|
|
if name and subgroup and subgroup >= 1 and subgroup <= 8 then
|
|
groupSlots[subgroup] = groupSlots[subgroup] + 1
|
|
local slot = groupSlots[subgroup]
|
|
if slot <= 5 then
|
|
local frameIndex = (subgroup - 1) * 5 + slot
|
|
local f = self.frames[frameIndex].frame
|
|
f.unit = "raid" .. i
|
|
f.raidRank = rank
|
|
self.frames[frameIndex].unit = "raid" .. i
|
|
f:Show()
|
|
self:UpdateFrame("raid" .. i)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Show/hide group labels based on whether each group has members
|
|
local showLabel = SFramesDB and SFramesDB.raidShowGroupLabel ~= false
|
|
for g = 1, 8 do
|
|
local lbl = self.groupLabels and self.groupLabels[g]
|
|
if lbl then
|
|
if showLabel and groupSlots[g] > 0 then
|
|
lbl:Show()
|
|
else
|
|
lbl:Hide()
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if not self.testing and self._framesBuilt then
|
|
for i = 1, 40 do
|
|
self.frames[i].frame:Hide()
|
|
self.frames[i].frame.unit = ""
|
|
self.frames[i].unit = ""
|
|
end
|
|
end
|
|
for g = 1, 8 do
|
|
local lbl = self.groupLabels and self.groupLabels[g]
|
|
if lbl then lbl:Hide() end
|
|
end
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdateFrame(unit)
|
|
if self.testing or not self._framesBuilt then return end
|
|
for i=1, 40 do
|
|
if self.frames[i].unit == unit then
|
|
local f = self.frames[i].frame
|
|
local name = UnitName(unit) or ""
|
|
local _, class = UnitClass(unit)
|
|
|
|
local display = string.sub(name, 1, 15)
|
|
f.nameText:SetText(display)
|
|
|
|
local rank = f.raidRank or 0
|
|
if f.leaderIcon then
|
|
if rank == 2 then f.leaderIcon:Show() else f.leaderIcon:Hide() end
|
|
end
|
|
if f.assistantIcon then
|
|
if rank == 1 then f.assistantIcon:Show() else f.assistantIcon:Hide() end
|
|
end
|
|
|
|
if not UnitIsConnected(unit) then
|
|
f.health:SetStatusBarColor(0.5, 0.5, 0.5)
|
|
f.health.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
f.nameText:SetTextColor(0.5, 0.5, 0.5)
|
|
if f.offlineIcon then f.offlineIcon:Show() end
|
|
else
|
|
if f.offlineIcon then f.offlineIcon:Hide() end
|
|
if class and SFrames.Config.colors.class[class] then
|
|
local color = SFrames.Config.colors.class[class]
|
|
f.health:SetStatusBarColor(color.r, color.g, color.b)
|
|
f.nameText:SetTextColor(1, 1, 1)
|
|
else
|
|
f.health:SetStatusBarColor(0, 1, 0)
|
|
f.nameText:SetTextColor(1, 1, 1)
|
|
end
|
|
end
|
|
|
|
self:UpdateHealth(unit)
|
|
self:UpdatePower(unit)
|
|
self:UpdateAuras(unit)
|
|
self:UpdateRaidIcon(unit)
|
|
self:UpdateTargetHighlight()
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdateTargetHighlight()
|
|
if self.testing or not self._framesBuilt then return end
|
|
for i=1, 40 do
|
|
local f = self.frames[i].frame
|
|
if UnitExists("target") and UnitIsUnit("target", self.frames[i].unit) then
|
|
f.targetHighlight:Show()
|
|
else
|
|
f.targetHighlight:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdateHealth(unit)
|
|
if not self._framesBuilt then return end
|
|
for i=1, 40 do
|
|
if self.frames[i].unit == unit then
|
|
local f = self.frames[i].frame
|
|
if not UnitIsConnected(unit) then
|
|
f.health:SetMinMaxValues(0, 100)
|
|
f.health:SetValue(0)
|
|
f.healthText:SetText("离线")
|
|
if f.offlineIcon then f.offlineIcon:Show() end
|
|
return
|
|
end
|
|
if f.offlineIcon then f.offlineIcon:Hide() end
|
|
|
|
local hp = UnitHealth(unit)
|
|
local maxHp = UnitHealthMax(unit)
|
|
|
|
if UnitIsDead(unit) then
|
|
f.health:SetMinMaxValues(0, 100)
|
|
f.health:SetValue(0)
|
|
f.healthText:SetText("死亡")
|
|
f.nameText:SetTextColor(0.5, 0.5, 0.5)
|
|
return
|
|
end
|
|
|
|
if UnitIsGhost(unit) then
|
|
f.health:SetMinMaxValues(0, 100)
|
|
f.health:SetValue(0)
|
|
f.healthText:SetText("灵魂")
|
|
f.nameText:SetTextColor(0.5, 0.5, 0.5)
|
|
return
|
|
end
|
|
|
|
f.health:SetMinMaxValues(0, maxHp)
|
|
f.health:SetValue(hp)
|
|
|
|
local percent = 0
|
|
if maxHp > 0 then
|
|
percent = math.floor((hp / maxHp) * 100)
|
|
end
|
|
|
|
local db = SFramesDB or {}
|
|
local txt = ""
|
|
if db.raidHealthFormat == "percent" then
|
|
txt = percent .. "%"
|
|
elseif db.raidHealthFormat == "deficit" then
|
|
if maxHp - hp > 0 then
|
|
txt = "-" .. (maxHp - hp)
|
|
end
|
|
else
|
|
txt = (math.floor(hp/100)/10).."k" -- default compact e.g. 4.5k
|
|
if hp < 1000 then txt = tostring(hp) end
|
|
end
|
|
|
|
f.healthText:SetText(txt)
|
|
self:UpdateHealPrediction(unit)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdateHealPrediction(unit)
|
|
if not self._framesBuilt then return end
|
|
local frameData = nil
|
|
for i=1, 40 do
|
|
if self.frames[i].unit == unit then
|
|
frameData = self.frames[i]
|
|
break
|
|
end
|
|
end
|
|
if not frameData then return end
|
|
|
|
local f = frameData.frame
|
|
if not (f.health and f.health.healPredMine and f.health.healPredOther) then return end
|
|
|
|
local predMine = f.health.healPredMine
|
|
local predOther = f.health.healPredOther
|
|
|
|
local function HidePredictions()
|
|
predMine:Hide()
|
|
predOther:Hide()
|
|
end
|
|
|
|
if not UnitExists(unit) or not UnitIsConnected(unit) then
|
|
HidePredictions()
|
|
return
|
|
end
|
|
|
|
local hp = UnitHealth(unit) or 0
|
|
local maxHp = UnitHealthMax(unit) or 0
|
|
if maxHp <= 0 or hp >= maxHp then
|
|
HidePredictions()
|
|
return
|
|
end
|
|
|
|
local _, mineIncoming, othersIncoming = GetIncomingHeals(unit)
|
|
local missing = maxHp - hp
|
|
if missing <= 0 then
|
|
HidePredictions()
|
|
return
|
|
end
|
|
|
|
local mineShown = math.min(math.max(0, mineIncoming), missing)
|
|
local remaining = missing - mineShown
|
|
local otherShown = math.min(math.max(0, othersIncoming), remaining)
|
|
if mineShown <= 0 and otherShown <= 0 then
|
|
HidePredictions()
|
|
return
|
|
end
|
|
|
|
local barWidth = f.health:GetWidth() or 0
|
|
if barWidth <= 0 then
|
|
HidePredictions()
|
|
return
|
|
end
|
|
|
|
local currentWidth = math.floor((hp / maxHp) * barWidth + 0.5)
|
|
if currentWidth < 0 then currentWidth = 0 end
|
|
if currentWidth > barWidth then currentWidth = barWidth end
|
|
|
|
local availableWidth = barWidth - currentWidth
|
|
if availableWidth <= 0 then
|
|
HidePredictions()
|
|
return
|
|
end
|
|
|
|
local mineWidth = math.floor((mineShown / maxHp) * barWidth + 0.5)
|
|
local otherWidth = math.floor((otherShown / maxHp) * barWidth + 0.5)
|
|
if mineWidth < 0 then mineWidth = 0 end
|
|
if otherWidth < 0 then otherWidth = 0 end
|
|
if mineWidth > availableWidth then mineWidth = availableWidth end
|
|
if otherWidth > (availableWidth - mineWidth) then
|
|
otherWidth = availableWidth - mineWidth
|
|
end
|
|
|
|
if mineWidth > 0 then
|
|
predMine:ClearAllPoints()
|
|
predMine:SetPoint("TOPLEFT", f.health, "TOPLEFT", currentWidth, 0)
|
|
predMine:SetPoint("BOTTOMLEFT", f.health, "BOTTOMLEFT", currentWidth, 0)
|
|
predMine:SetWidth(mineWidth)
|
|
predMine:Show()
|
|
else
|
|
predMine:Hide()
|
|
end
|
|
|
|
if otherWidth > 0 then
|
|
predOther:ClearAllPoints()
|
|
predOther:SetPoint("TOPLEFT", f.health, "TOPLEFT", currentWidth + mineWidth, 0)
|
|
predOther:SetPoint("BOTTOMLEFT", f.health, "BOTTOMLEFT", currentWidth + mineWidth, 0)
|
|
predOther:SetWidth(otherWidth)
|
|
predOther:Show()
|
|
else
|
|
predOther:Hide()
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdatePower(unit)
|
|
if not self._framesBuilt then return end
|
|
local db = SFramesDB or {}
|
|
if db.raidShowPower == false then return end
|
|
|
|
for i=1, 40 do
|
|
if self.frames[i].unit == unit then
|
|
local f = self.frames[i].frame
|
|
local power = UnitMana(unit)
|
|
local maxPower = UnitManaMax(unit)
|
|
local _, class = UnitClass(unit)
|
|
|
|
f.power:SetMinMaxValues(0, maxPower)
|
|
f.power:SetValue(power)
|
|
|
|
local pType = UnitPowerType(unit)
|
|
local color = SFrames.Config.colors.power[pType] or SFrames.Config.colors.power[0]
|
|
f.power:SetStatusBarColor(color.r, color.g, color.b)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdateRaidIcons()
|
|
if not self._framesBuilt then return end
|
|
for i = 1, 40 do
|
|
local unit = self.frames[i].unit
|
|
if unit and unit ~= "" and self.frames[i].frame:IsShown() then
|
|
self:UpdateRaidIcon(unit)
|
|
end
|
|
end
|
|
end
|
|
|
|
function SFrames.Raid:UpdateRaidIcon(unit)
|
|
if not self._framesBuilt then return end
|
|
for i = 1, 40 do
|
|
if self.frames[i].unit == unit then
|
|
local f = self.frames[i].frame
|
|
if not f.raidIcon then return end
|
|
if not GetRaidTargetIndex then
|
|
f.raidIcon:Hide()
|
|
return
|
|
end
|
|
if not UnitExists(unit) then
|
|
f.raidIcon:Hide()
|
|
return
|
|
end
|
|
local index = GetRaidTargetIndex(unit)
|
|
if index and index > 0 and index <= 8 then
|
|
local col = math.mod(index - 1, 4)
|
|
local row = math.floor((index - 1) / 4)
|
|
f.raidIcon:SetTexCoord(col * 0.25, (col + 1) * 0.25, row * 0.25, (row + 1) * 0.25)
|
|
f.raidIcon:Show()
|
|
else
|
|
f.raidIcon:Hide()
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
local PlayerClassBuffs = nil
|
|
|
|
function SFrames.Raid:GetClassBuffs()
|
|
if PlayerClassBuffs then return PlayerClassBuffs end
|
|
|
|
local _, playerClass = UnitClass("player")
|
|
PlayerClassBuffs = {}
|
|
|
|
if playerClass == "PRIEST" then
|
|
PlayerClassBuffs = {
|
|
[1] = { "真言术:韧", "坚韧祷言" },
|
|
[2] = { "神圣之灵", "精神祷言" },
|
|
[3] = { "恢复" },
|
|
[4] = { "虚弱灵魂", isDebuff = true },
|
|
[5] = { "防恐结界" }
|
|
}
|
|
elseif playerClass == "DRUID" then
|
|
PlayerClassBuffs = {
|
|
[1] = { "野性印记", "野性赐福" },
|
|
[2] = { "荆棘术" },
|
|
[3] = { "回春术" },
|
|
[4] = { "愈合" },
|
|
}
|
|
elseif playerClass == "PALADIN" then
|
|
PlayerClassBuffs = {
|
|
[1] = { "王者祝福", "强效王者祝福" },
|
|
[2] = { "拯救祝福", "强效拯救祝福" },
|
|
[3] = { "智慧祝福", "强效智慧祝福" },
|
|
[4] = { "力量祝福", "强效力量祝福" },
|
|
[5] = { "庇护祝福", "强效庇护祝福" },
|
|
[6] = { "光明祝福", "强效光明祝福" }
|
|
}
|
|
elseif playerClass == "MAGE" then
|
|
PlayerClassBuffs = {
|
|
[1] = { "奥术智慧", "奥术光辉" },
|
|
[2] = { "魔法抑制" },
|
|
[3] = { "魔法增效" }
|
|
}
|
|
end
|
|
|
|
return PlayerClassBuffs
|
|
end
|
|
|
|
function SFrames.Raid:UpdateAuras(unit)
|
|
if not self._framesBuilt then return end
|
|
local frameData = nil
|
|
for i=1, 40 do
|
|
if self.frames[i].unit == unit then
|
|
frameData = self.frames[i]
|
|
break
|
|
end
|
|
end
|
|
if not frameData then return end
|
|
|
|
local f = frameData.frame
|
|
local buffsNeeded = self:GetClassBuffs()
|
|
|
|
local foundIndicators = { [1] = false, [2] = false, [3] = false, [4] = false }
|
|
|
|
-- Hide all first
|
|
for i = 1, 4 do
|
|
f.indicators[i]:Hide()
|
|
f.indicators[i].index = nil
|
|
end
|
|
|
|
if not buffsNeeded or not UnitIsConnected(unit) or UnitIsDeadOrGhost(unit) then
|
|
f.health.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
return
|
|
end
|
|
|
|
local function MatchesList(auraName, list)
|
|
for _, name in ipairs(list) do
|
|
if string.find(auraName, name) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Check Buffs
|
|
for i = 1, 32 do
|
|
local texture, applications = UnitBuff(unit, i)
|
|
if not texture then break end
|
|
|
|
SFrames.Tooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
SFrames.Tooltip:SetUnitBuff(unit, i)
|
|
local buffName = SFramesScanTooltipTextLeft1:GetText()
|
|
|
|
if buffName then
|
|
for pos, listData in pairs(buffsNeeded) do
|
|
if pos <= 4 and not listData.isDebuff and not foundIndicators[pos] then
|
|
if MatchesList(buffName, listData) then
|
|
f.indicators[pos].icon:SetTexture(texture)
|
|
f.indicators[pos].index = i
|
|
f.indicators[pos].isDebuff = false
|
|
f.indicators[pos]:Show()
|
|
foundIndicators[pos] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local hasDebuff = false
|
|
local debuffColor = {r=_A.slotBg[1], g=_A.slotBg[2], b=_A.slotBg[3]}
|
|
|
|
-- Check Debuffs
|
|
for i = 1, 16 do
|
|
local texture, applications, dispelType = UnitDebuff(unit, i)
|
|
if not texture then break end
|
|
|
|
if dispelType then
|
|
hasDebuff = true
|
|
if dispelType == "Magic" then debuffColor = {r=0.2, g=0.6, b=1}
|
|
elseif dispelType == "Curse" then debuffColor = {r=0.6, g=0, b=1}
|
|
elseif dispelType == "Disease" then debuffColor = {r=0.6, g=0.4, b=0}
|
|
elseif dispelType == "Poison" then debuffColor = {r=0, g=0.6, b=0}
|
|
end
|
|
end
|
|
|
|
SFrames.Tooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
SFrames.Tooltip:SetUnitDebuff(unit, i)
|
|
local debuffName = SFramesScanTooltipTextLeft1:GetText()
|
|
|
|
if debuffName then
|
|
for pos, listData in pairs(buffsNeeded) do
|
|
if pos <= 4 and listData.isDebuff and not foundIndicators[pos] then
|
|
if MatchesList(debuffName, listData) then
|
|
f.indicators[pos].icon:SetTexture(texture)
|
|
f.indicators[pos].index = i
|
|
f.indicators[pos].isDebuff = true
|
|
f.indicators[pos]:Show()
|
|
foundIndicators[pos] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if hasDebuff then
|
|
f.health.bg:SetVertexColor(debuffColor.r, debuffColor.g, debuffColor.b, 1)
|
|
else
|
|
f.health.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1)
|
|
end
|
|
end
|
|
|