Adds main tank unit frames with paired target display for raid tank monitoring.
[Tank1 Health] [Tank1's Target]
[Tank2 Health] [Tank2's Target]
[Tank3 Health] [Tank3's Target]
File: utils/quicore_main.lua (add after boss settings, around line 1350)
-- Main Tank frames (for raid tank monitoring)
maintank = {
enabled = false, -- Disabled by default
maxFrames = 4, -- How many MT frame pairs to create (1-8)
spacing = 4, -- Vertical spacing between rows
-- Tank frame settings
width = 120,
height = 28,
offsetX = -600,
offsetY = 200,
texture = "Quazii v5",
useClassColor = true,
customHealthColor = { 0.2, 0.6, 0.2, 1 },
-- Name text
showName = true,
nameTextUseClassColor = false,
nameTextColor = { 1, 1, 1, 1 },
nameFontSize = 11,
nameAnchor = "LEFT",
nameOffsetX = 4,
nameOffsetY = 0,
maxNameLength = 8,
-- Health text
showHealth = true,
healthDisplayStyle = "percent", -- "percent", "absolute", "both"
healthFontSize = 11,
healthAnchor = "RIGHT",
healthOffsetX = -4,
healthOffsetY = 0,
healthTextUseClassColor = false,
healthTextColor = { 1, 1, 1, 1 },
-- Power bar (usually disabled for compact MT frames)
showPowerBar = false,
powerBarHeight = 3,
-- Target frame settings
showTarget = true,
targetWidth = 120,
targetHeight = 28,
targetSpacing = 2, -- Horizontal gap between tank and target frames
targetTexture = "Quazii v5 Inverse",
targetUseClassColor = false,
targetUseHostilityColor = true, -- Red/yellow/green based on mob hostility
targetCustomHealthColor = { 0.8, 0.2, 0.2, 1 },
-- Target name text
targetShowName = true,
targetNameFontSize = 11,
targetNameAnchor = "LEFT",
targetNameOffsetX = 4,
targetMaxNameLength = 10,
-- Target health text
targetShowHealth = true,
targetHealthDisplayStyle = "percent",
targetHealthFontSize = 11,
targetHealthAnchor = "RIGHT",
targetHealthOffsetX = -4,
},File: utils/qui_unitframes.lua (add after CreateBossFrame function, around line 1150)
---------------------------------------------------------------------------
-- CREATE: Main Tank Frame
---------------------------------------------------------------------------
local function CreateMainTankFrame(unit, frameKey, mtIndex)
local settings = GetUnitSettings("maintank")
local general = GetGeneralSettings()
if not settings then return nil end
local frameName = "QUI_MainTank" .. mtIndex
local frame = CreateFrame("Button", frameName, UIParent, "SecureUnitButtonTemplate, BackdropTemplate")
frame.unit = unit -- "maintank1", "maintank2", etc.
frame.unitKey = "maintank"
frame.mtIndex = mtIndex
-- Size and position
local width = Scale(settings.width or 120)
local height = Scale(settings.height or 28)
frame:SetSize(width, height)
-- Position (first frame uses offsetX/Y, others stack below)
if mtIndex == 1 then
local offsetX = Scale(settings.offsetX or -600)
local offsetY = Scale(settings.offsetY or 200)
frame:SetPoint("CENTER", UIParent, "CENTER", offsetX, offsetY)
end
frame:SetMovable(true)
frame:SetClampedToScreen(true)
-- Secure unit attributes
frame:SetAttribute("unit", unit)
frame:SetAttribute("*type1", "target")
frame:SetAttribute("*type2", "togglemenu")
frame:RegisterForClicks("AnyUp")
-- Tooltip
frame:HookScript("OnEnter", function(self) ShowUnitTooltip(self) end)
frame:HookScript("OnLeave", HideUnitTooltip)
-- Visibility: show when this maintank exists
RegisterStateDriver(frame, "visibility", "[@" .. unit .. ",exists] show; hide")
-- Background
local bgColor = { 0.1, 0.1, 0.1, 0.9 }
if general and general.darkMode then
bgColor = general.darkModeBgColor or { 0.25, 0.25, 0.25, 1 }
end
local borderSize = Scale(1)
frame:SetBackdrop({
bgFile = "Interface\\Buttons\\WHITE8x8",
edgeFile = "Interface\\Buttons\\WHITE8x8",
edgeSize = borderSize,
})
frame:SetBackdropColor(bgColor[1], bgColor[2], bgColor[3], bgColor[4] or 1)
frame:SetBackdropBorderColor(0, 0, 0, 1)
-- Health bar
local powerHeight = settings.showPowerBar and Scale(settings.powerBarHeight or 3) or 0
local separatorHeight = (settings.showPowerBar and settings.powerBarBorder ~= false) and 1 or 0
local healthBar = CreateFrame("StatusBar", nil, frame)
healthBar:SetPoint("TOPLEFT", frame, "TOPLEFT", borderSize, -borderSize)
healthBar:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -borderSize, borderSize + powerHeight + separatorHeight)
healthBar:SetStatusBarTexture(GetTexturePath(settings.texture))
healthBar:SetMinMaxValues(0, 100)
healthBar:SetValue(100)
healthBar:EnableMouse(false)
frame.healthBar = healthBar
-- Name text
local nameText = healthBar:CreateFontString(nil, "OVERLAY")
nameText:SetFont(GetFontPath(), settings.nameFontSize or 11, GetFontOutline())
nameText:SetTextColor(unpack(settings.nameTextColor or {1,1,1,1}))
nameText:SetShadowOffset(1, -1)
local nameAnchorInfo = GetTextAnchorInfo(settings.nameAnchor or "LEFT")
nameText:SetPoint(nameAnchorInfo.point, healthBar, nameAnchorInfo.point,
Scale(settings.nameOffsetX or 4), Scale(settings.nameOffsetY or 0))
nameText:SetJustifyH(nameAnchorInfo.justify)
frame.nameText = nameText
-- Health text
local healthText = healthBar:CreateFontString(nil, "OVERLAY")
healthText:SetFont(GetFontPath(), settings.healthFontSize or 11, GetFontOutline())
healthText:SetTextColor(unpack(settings.healthTextColor or {1,1,1,1}))
healthText:SetShadowOffset(1, -1)
local healthAnchorInfo = GetTextAnchorInfo(settings.healthAnchor or "RIGHT")
healthText:SetPoint(healthAnchorInfo.point, healthBar, healthAnchorInfo.point,
Scale(settings.healthOffsetX or -4), Scale(settings.healthOffsetY or 0))
healthText:SetJustifyH(healthAnchorInfo.justify)
frame.healthText = healthText
-- Power bar (optional)
if settings.showPowerBar then
local powerBar = CreateFrame("StatusBar", nil, frame)
powerBar:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", borderSize, borderSize)
powerBar:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -borderSize, borderSize)
powerBar:SetHeight(powerHeight)
powerBar:SetStatusBarTexture(GetTexturePath(settings.texture))
powerBar:SetMinMaxValues(0, 100)
powerBar:SetValue(100)
frame.powerBar = powerBar
end
-- Register events
frame:RegisterEvent("UNIT_HEALTH")
frame:RegisterEvent("UNIT_MAXHEALTH")
frame:RegisterEvent("UNIT_NAME_UPDATE")
frame:RegisterEvent("GROUP_ROSTER_UPDATE")
if settings.showPowerBar then
frame:RegisterEvent("UNIT_POWER_UPDATE")
frame:RegisterEvent("UNIT_MAXPOWER")
end
frame:SetScript("OnEvent", function(self, event, arg1)
if arg1 == self.unit or event == "GROUP_ROSTER_UPDATE" then
UpdateMainTankFrame(self)
end
end)
return frame
end
---------------------------------------------------------------------------
-- CREATE: Main Tank Target Frame
---------------------------------------------------------------------------
local function CreateMainTankTargetFrame(unit, frameKey, mtIndex, parentFrame)
local settings = GetUnitSettings("maintank")
local general = GetGeneralSettings()
if not settings or not settings.showTarget then return nil end
local frameName = "QUI_MainTank" .. mtIndex .. "Target"
local frame = CreateFrame("Button", frameName, UIParent, "SecureUnitButtonTemplate, BackdropTemplate")
frame.unit = unit -- "maintank1target", "maintank2target", etc.
frame.unitKey = "maintanktarget"
frame.mtIndex = mtIndex
-- Size
local width = Scale(settings.targetWidth or 120)
local height = Scale(settings.targetHeight or 28)
frame:SetSize(width, height)
-- Position: anchored to right of parent MT frame
local targetSpacing = Scale(settings.targetSpacing or 2)
frame:SetPoint("LEFT", parentFrame, "RIGHT", targetSpacing, 0)
frame:SetMovable(false) -- Moves with parent
-- Secure unit attributes
frame:SetAttribute("unit", unit)
frame:SetAttribute("*type1", "target")
frame:SetAttribute("*type2", "togglemenu")
frame:RegisterForClicks("AnyUp")
-- Tooltip
frame:HookScript("OnEnter", function(self) ShowUnitTooltip(self) end)
frame:HookScript("OnLeave", HideUnitTooltip)
-- Visibility: show when maintankN's target exists
RegisterStateDriver(frame, "visibility", "[@" .. unit .. ",exists] show; hide")
-- Background
local bgColor = { 0.1, 0.1, 0.1, 0.9 }
if general and general.darkMode then
bgColor = general.darkModeBgColor or { 0.25, 0.25, 0.25, 1 }
end
local borderSize = Scale(1)
frame:SetBackdrop({
bgFile = "Interface\\Buttons\\WHITE8x8",
edgeFile = "Interface\\Buttons\\WHITE8x8",
edgeSize = borderSize,
})
frame:SetBackdropColor(bgColor[1], bgColor[2], bgColor[3], bgColor[4] or 1)
frame:SetBackdropBorderColor(0, 0, 0, 1)
-- Health bar
local healthBar = CreateFrame("StatusBar", nil, frame)
healthBar:SetPoint("TOPLEFT", frame, "TOPLEFT", borderSize, -borderSize)
healthBar:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -borderSize, borderSize)
healthBar:SetStatusBarTexture(GetTexturePath(settings.targetTexture or settings.texture))
healthBar:SetMinMaxValues(0, 100)
healthBar:SetValue(100)
healthBar:EnableMouse(false)
frame.healthBar = healthBar
-- Name text
local nameText = healthBar:CreateFontString(nil, "OVERLAY")
nameText:SetFont(GetFontPath(), settings.targetNameFontSize or 11, GetFontOutline())
nameText:SetTextColor(1, 1, 1, 1)
nameText:SetShadowOffset(1, -1)
nameText:SetPoint("LEFT", healthBar, "LEFT", Scale(settings.targetNameOffsetX or 4), 0)
nameText:SetJustifyH("LEFT")
frame.nameText = nameText
-- Health text
local healthText = healthBar:CreateFontString(nil, "OVERLAY")
healthText:SetFont(GetFontPath(), settings.targetHealthFontSize or 11, GetFontOutline())
healthText:SetTextColor(1, 1, 1, 1)
healthText:SetShadowOffset(1, -1)
healthText:SetPoint("RIGHT", healthBar, "RIGHT", Scale(settings.targetHealthOffsetX or -4), 0)
healthText:SetJustifyH("RIGHT")
frame.healthText = healthText
-- Register events
frame:RegisterEvent("UNIT_HEALTH")
frame:RegisterEvent("UNIT_MAXHEALTH")
frame:RegisterEvent("UNIT_NAME_UPDATE")
frame:RegisterEvent("UNIT_TARGET") -- For when tank changes target
frame:SetScript("OnEvent", function(self, event, arg1)
-- Update when our unit changes or when the parent tank changes target
local parentUnit = "maintank" .. self.mtIndex
if arg1 == self.unit or (event == "UNIT_TARGET" and arg1 == parentUnit) then
UpdateMainTankTargetFrame(self)
end
end)
return frame
end
---------------------------------------------------------------------------
-- UPDATE: Main Tank Frame
---------------------------------------------------------------------------
local function UpdateMainTankFrame(frame)
if not frame then return end
local unit = frame.unit
local settings = GetUnitSettings("maintank")
if not settings then return end
if not UnitExists(unit) then
return
end
-- Update health bar
local healthPct = GetHealthPct(unit) or 100
frame.healthBar:SetValue(healthPct)
-- Health bar color (class color or custom)
if settings.useClassColor then
local _, class = UnitClass(unit)
if class and RAID_CLASS_COLORS[class] then
local c = RAID_CLASS_COLORS[class]
frame.healthBar:SetStatusBarColor(c.r, c.g, c.b, 1)
end
else
local c = settings.customHealthColor or {0.2, 0.6, 0.2, 1}
frame.healthBar:SetStatusBarColor(c[1], c[2], c[3], c[4] or 1)
end
-- Update name
if settings.showName and frame.nameText then
local name = UnitName(unit) or ""
local maxLen = settings.maxNameLength or 0
if maxLen > 0 and #name > maxLen then
name = name:sub(1, maxLen)
end
frame.nameText:SetText(name)
if settings.nameTextUseClassColor then
local _, class = UnitClass(unit)
if class and RAID_CLASS_COLORS[class] then
local c = RAID_CLASS_COLORS[class]
frame.nameText:SetTextColor(c.r, c.g, c.b, 1)
end
end
end
-- Update health text
if settings.showHealth and frame.healthText then
local text = ""
if settings.healthDisplayStyle == "percent" then
text = string.format("%.0f%%", healthPct)
elseif settings.healthDisplayStyle == "absolute" then
text = AbbreviateNumber(UnitHealth(unit))
else
text = string.format("%.0f%%", healthPct)
end
frame.healthText:SetText(text)
end
-- Update power bar if enabled
if settings.showPowerBar and frame.powerBar then
local powerPct = GetPowerPct(unit) or 100
frame.powerBar:SetValue(powerPct)
end
end
---------------------------------------------------------------------------
-- UPDATE: Main Tank Target Frame
---------------------------------------------------------------------------
local function UpdateMainTankTargetFrame(frame)
if not frame then return end
local unit = frame.unit
local settings = GetUnitSettings("maintank")
if not settings then return end
if not UnitExists(unit) then
return
end
-- Update health bar
local healthPct = GetHealthPct(unit) or 100
frame.healthBar:SetValue(healthPct)
-- Health bar color (hostility or class color)
if settings.targetUseHostilityColor then
local reaction = UnitReaction(unit, "player")
if UnitIsPlayer(unit) then
local _, class = UnitClass(unit)
if class and RAID_CLASS_COLORS[class] then
local c = RAID_CLASS_COLORS[class]
frame.healthBar:SetStatusBarColor(c.r, c.g, c.b, 1)
end
elseif reaction then
if reaction <= 2 then
frame.healthBar:SetStatusBarColor(0.8, 0.2, 0.2, 1) -- Hostile (red)
elseif reaction <= 4 then
frame.healthBar:SetStatusBarColor(0.9, 0.7, 0.2, 1) -- Neutral (yellow)
else
frame.healthBar:SetStatusBarColor(0.2, 0.8, 0.2, 1) -- Friendly (green)
end
end
elseif settings.targetUseClassColor and UnitIsPlayer(unit) then
local _, class = UnitClass(unit)
if class and RAID_CLASS_COLORS[class] then
local c = RAID_CLASS_COLORS[class]
frame.healthBar:SetStatusBarColor(c.r, c.g, c.b, 1)
end
else
local c = settings.targetCustomHealthColor or {0.8, 0.2, 0.2, 1}
frame.healthBar:SetStatusBarColor(c[1], c[2], c[3], c[4] or 1)
end
-- Update name
if settings.targetShowName and frame.nameText then
local name = UnitName(unit) or ""
local maxLen = settings.targetMaxNameLength or 0
if maxLen > 0 and #name > maxLen then
name = name:sub(1, maxLen)
end
frame.nameText:SetText(name)
end
-- Update health text
if settings.targetShowHealth and frame.healthText then
local text = ""
if settings.targetHealthDisplayStyle == "percent" then
text = string.format("%.0f%%", healthPct)
elseif settings.targetHealthDisplayStyle == "absolute" then
text = AbbreviateNumber(UnitHealth(unit))
else
text = string.format("%.0f%%", healthPct)
end
frame.healthText:SetText(text)
end
endFile: utils/qui_unitframes.lua (add in Initialize() function, after boss frames around line 4230)
-- Create main tank frames (maintank1 through maintankN with targets)
if db.maintank and db.maintank.enabled then
local maxFrames = db.maintank.maxFrames or 4
local spacing = db.maintank.spacing or 4
for i = 1, maxFrames do
local mtUnit = "maintank" .. i
local mtKey = "maintank" .. i
-- Create tank frame
self.frames[mtKey] = CreateMainTankFrame(mtUnit, mtKey, i)
-- Position: stack vertically (first frame uses offsetX/Y, others stack below)
if self.frames[mtKey] and i > 1 then
local prevFrame = self.frames["maintank" .. (i - 1)]
if prevFrame then
self.frames[mtKey]:ClearAllPoints()
self.frames[mtKey]:SetPoint("TOPLEFT", prevFrame, "BOTTOMLEFT", 0, -spacing)
end
end
-- Create tank target frame (anchored to right of tank frame)
if self.frames[mtKey] and db.maintank.showTarget then
local mtTargetKey = "maintank" .. i .. "target"
self.frames[mtTargetKey] = CreateMainTankTargetFrame(
mtUnit .. "target",
mtTargetKey,
i,
self.frames[mtKey]
)
end
end
endFile: utils/qui_unitframes.lua (add to RefreshFrame() and RefreshAll())
In RefreshFrame():
-- Handle maintank frames specially - refresh all pairs
if unitKey == "maintank" then
local settings = GetUnitSettings("maintank")
local maxFrames = settings and settings.maxFrames or 4
local spacing = settings and settings.spacing or 4
for i = 1, maxFrames do
local mtKey = "maintank" .. i
local mtTargetKey = "maintank" .. i .. "target"
local frame = self.frames[mtKey]
local targetFrame = self.frames[mtTargetKey]
if frame then
-- Update size
frame:SetSize(Scale(settings.width or 120), Scale(settings.height or 28))
-- Reposition (first frame uses saved position, others stack)
if i > 1 then
local prevFrame = self.frames["maintank" .. (i - 1)]
if prevFrame then
frame:ClearAllPoints()
frame:SetPoint("TOPLEFT", prevFrame, "BOTTOMLEFT", 0, -spacing)
end
end
UpdateMainTankFrame(frame)
end
if targetFrame and settings.showTarget then
-- Update size and reanchor
targetFrame:SetSize(Scale(settings.targetWidth or 120), Scale(settings.targetHeight or 28))
targetFrame:ClearAllPoints()
targetFrame:SetPoint("LEFT", frame, "RIGHT", Scale(settings.targetSpacing or 2), 0)
UpdateMainTankTargetFrame(targetFrame)
end
end
return
endIn RefreshAll():
-- Track if we've refreshed maintank frames
local maintankRefreshed = false
-- ... existing code ...
-- In the loop:
if unitKey:match("^maintank%d+") then
if not maintankRefreshed then
self:RefreshFrame("maintank")
maintankRefreshed = true
end
-- ... rest of existing logicFile: utils/qui_options.lua (add new tab in Unit Frames section)
-- Main Tank tab (add after Boss tab)
do
local mtTab = GUI:CreateTab(tabContainer, "Main Tank")
local mtContent = CreateFrame("Frame", nil, mtTab)
mtContent:SetAllPoints()
local ufdb = GetUFDB()
local mt = ufdb and ufdb.maintank
local y = -PADDING
-- Enable checkbox
local enableCheck = GUI:CreateFormCheckbox(mtContent, "Enable Main Tank Frames",
"enabled", mt, RefreshNewUF)
enableCheck:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Max frames slider (1-8)
local maxFramesSlider = GUI:CreateFormSlider(mtContent, "Number of Frames",
"maxFrames", mt, 1, 8, 1, RefreshNewUF)
maxFramesSlider:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Show target checkbox
local showTargetCheck = GUI:CreateFormCheckbox(mtContent, "Show Tank Target",
"showTarget", mt, RefreshNewUF)
showTargetCheck:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Spacing slider
local spacingSlider = GUI:CreateFormSlider(mtContent, "Vertical Spacing",
"spacing", mt, 0, 20, 1, RefreshNewUF)
spacingSlider:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Tank Frame section header
local tankHeader = GUI:CreateSectionHeader(mtContent, "Tank Frame")
tankHeader:SetPoint("TOPLEFT", PADDING, y)
y = y - 25
-- Width/Height sliders
local widthSlider = GUI:CreateFormSlider(mtContent, "Width",
"width", mt, 80, 200, 1, RefreshNewUF)
widthSlider:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
local heightSlider = GUI:CreateFormSlider(mtContent, "Height",
"height", mt, 20, 50, 1, RefreshNewUF)
heightSlider:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Class color checkbox
local classColorCheck = GUI:CreateFormCheckbox(mtContent, "Use Class Color",
"useClassColor", mt, RefreshNewUF)
classColorCheck:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Target Frame section header
local targetHeader = GUI:CreateSectionHeader(mtContent, "Target Frame")
targetHeader:SetPoint("TOPLEFT", PADDING, y)
y = y - 25
-- Target width slider
local targetWidthSlider = GUI:CreateFormSlider(mtContent, "Target Width",
"targetWidth", mt, 80, 200, 1, RefreshNewUF)
targetWidthSlider:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Target spacing slider
local targetSpacingSlider = GUI:CreateFormSlider(mtContent, "Target Spacing",
"targetSpacing", mt, 0, 20, 1, RefreshNewUF)
targetSpacingSlider:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
-- Hostility color checkbox
local hostilityCheck = GUI:CreateFormCheckbox(mtContent, "Color by Hostility",
"targetUseHostilityColor", mt, RefreshNewUF)
hostilityCheck:SetPoint("TOPLEFT", PADDING, y)
y = y - FORM_ROW
end| Aspect | Implementation |
|---|---|
| Unit IDs | maintank1 through maintankN |
| Target Units | maintank1target, maintank2target, etc. |
| Settings Location | db.maintank in quicore_main.lua |
| Frame Functions | CreateMainTankFrame, CreateMainTankTargetFrame |
| Files Changed | 3 (quicore_main.lua, qui_unitframes.lua, qui_options.lua) |
Notes:
- Requires raid group (main tank assignments are raid-only)
- Frames auto-show/hide based on unit existence
- Target frames anchor to tank frames and update on
UNIT_TARGET - Follows boss frames pattern with paired target display