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() if SFrames.Movers and SFrames.Movers.RegisterMover and self.parent then SFrames.Movers:RegisterMover("RaidFrame", self.parent, "团队", "TOPLEFT", "UIParent", "TOPLEFT", 15, -200) end 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() SFrames.Tooltip:Hide() 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() SFrames.Tooltip:Hide() 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