AltSystem/UI.lua

600 lines
21 KiB
Lua

-- AltSystem UI
-- Creates the main dialog window with tabbed layout.
-- The "Use Skills" tab contains controls on the left and a Log panel on the right.
-- Uses the modern DropdownButton API (WoW 10.2.5+ / 12.0+).
AltSystem = AltSystem or {}
local WINDOW_WIDTH = 700
local WINDOW_HEIGHT = 500
local CONTROLS_WIDTH = 350
local LOG_WIDTH = 350
local PADDING = 12
local ROW_HEIGHT = 26
local SECTION_GAP = 10
-- Helper: Build the skill option list from current AltSystem.Data.Skills
local function BuildSkillOptions()
local options = {}
for _, skill in ipairs(AltSystem.Data.Skills) do
local sign = skill.modifier >= 0 and "+" or ""
local text
if skill.level == "Base" or skill.level == "Inept" then
text = skill.name .. " (" .. sign .. skill.modifier .. ")"
else
text = skill.name .. " (" .. skill.level .. " " .. sign .. skill.modifier .. ")"
end
table.insert(options, { text = text })
end
return options
end
-- Helper: Create a modern dropdown (WowStyle1DropdownTemplate)
local function CreateDropdown(parent, name, labelText, options, defaultIndex, onSelect, labelFont)
local container = CreateFrame("Frame", nil, parent)
container:SetHeight(ROW_HEIGHT)
local label
if labelText and labelText ~= "" then
label = container:CreateFontString(nil, "OVERLAY", labelFont or "GameFontNormal")
label:SetPoint("LEFT", container, "LEFT", 0, 0)
label:SetText(labelText)
label:SetJustifyH("LEFT")
end
local selectedIndex = defaultIndex or 1
local dropdown = CreateFrame("DropdownButton", name, container, "WowStyle1DropdownTemplate")
if label then
dropdown:SetPoint("RIGHT", container, "RIGHT", 0, 0)
else
dropdown:SetPoint("LEFT", container, "LEFT", 0, 0)
end
dropdown:SetWidth(190)
dropdown:SetupMenu(function(dropdown, rootDescription)
for i, option in ipairs(options) do
rootDescription:CreateRadio(
option.text,
function(data)
return data == selectedIndex
end,
function(data)
selectedIndex = data
if onSelect then
onSelect(data, options[data])
end
end,
i
)
end
end)
return container, dropdown, function()
return selectedIndex
end, function(idx)
selectedIndex = idx
end
end
-- Helper: Create a radio button (CheckButton with radio texture)
local function CreateRadioButton(parent, name, text, x, y, isChecked, onClick)
local radio = CreateFrame("CheckButton", name, parent, "UIRadioButtonTemplate")
radio:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
radio:SetChecked(isChecked)
local radioText = radio:GetFontString()
if radioText then
radioText:SetText(text)
radioText:SetFontObject("GameFontHighlight")
else
local t = radio:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
t:SetPoint("LEFT", radio, "RIGHT", 4, 0)
t:SetText(text)
end
radio:SetScript("OnClick", function(self)
onClick(self)
end)
return radio
end
-- Helper: Create a section header (golden text)
local function CreateSectionHeader(parent, text, x, y)
local header = parent:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
header:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
header:SetText(text)
header:SetTextColor(0.9, 0.75, 0.2)
return header
end
-- Helper: Create a sub-label (smaller golden text)
local function CreateSubLabel(parent, text, x, y)
local label = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
label:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
label:SetText(text)
label:SetTextColor(0.9, 0.75, 0.2)
return label
end
function AltSystem:CreateMainFrame()
if AltSystem.MainFrame then
return
end
-- Main frame
local f = CreateFrame("Frame", "AltSystemMainFrame", UIParent, "BasicFrameTemplateWithInset")
f:SetSize(WINDOW_WIDTH, WINDOW_HEIGHT)
f:SetPoint("CENTER")
f:SetMovable(true)
f:EnableMouse(true)
f:RegisterForDrag("LeftButton")
f:SetScript("OnDragStart", f.StartMoving)
f:SetScript("OnDragStop", f.StopMovingOrSizing)
f:SetClampedToScreen(true)
f.TitleBg:SetHeight(30)
f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
f.title:SetPoint("TOPLEFT", f.TitleBg, "TOPLEFT", 5, -3)
f.title:SetText("AltSystem")
---------------------
-- TAB BUTTONS (span full window width)
---------------------
local contentTop = -24
local tabHeight = 28
local contentWidth = WINDOW_WIDTH - 8 -- 4px inset on each side
local tabWidth = contentWidth / 2
local tabUseSkills = CreateFrame("Button", "AltSystemTabUseSkills", f)
tabUseSkills:SetSize(tabWidth, tabHeight)
tabUseSkills:SetPoint("TOPLEFT", f, "TOPLEFT", 4, contentTop)
tabUseSkills:SetNormalFontObject("GameFontHighlight")
tabUseSkills:SetHighlightFontObject("GameFontHighlight")
tabUseSkills:SetText("Use Skills")
local tabUseSkillsBg = tabUseSkills:CreateTexture(nil, "BACKGROUND")
tabUseSkillsBg:SetAllPoints()
tabUseSkillsBg:SetColorTexture(0.15, 0.15, 0.15, 1)
local tabBuildSkills = CreateFrame("Button", "AltSystemTabBuildSkills", f)
tabBuildSkills:SetSize(tabWidth, tabHeight)
tabBuildSkills:SetPoint("TOPLEFT", tabUseSkills, "TOPRIGHT", 0, 0)
tabBuildSkills:SetNormalFontObject("GameFontHighlight")
tabBuildSkills:SetHighlightFontObject("GameFontHighlight")
tabBuildSkills:SetText("Build Skills")
local tabBuildSkillsBg = tabBuildSkills:CreateTexture(nil, "BACKGROUND")
tabBuildSkillsBg:SetAllPoints()
tabBuildSkillsBg:SetColorTexture(0.3, 0.3, 0.3, 1)
---------------------
-- TAB CONTENT FRAMES
---------------------
local tabContentTop = contentTop - tabHeight
local tabContentHeight = WINDOW_HEIGHT - 28 - tabHeight
local useSkillsContent = CreateFrame("Frame", "AltSystemUseSkillsContent", f)
useSkillsContent:SetPoint("TOPLEFT", f, "TOPLEFT", 4, tabContentTop)
useSkillsContent:SetSize(contentWidth, tabContentHeight)
local buildSkillsContent = CreateFrame("Frame", "AltSystemBuildSkillsContent", f)
buildSkillsContent:SetPoint("TOPLEFT", f, "TOPLEFT", 4, tabContentTop)
buildSkillsContent:SetSize(contentWidth, tabContentHeight)
buildSkillsContent:Hide()
local buildPlaceholder = buildSkillsContent:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
buildPlaceholder:SetPoint("CENTER")
buildPlaceholder:SetText("Coming soon")
-- Tab switching logic
local function SelectTab(tabIndex)
if tabIndex == 1 then
useSkillsContent:Show()
buildSkillsContent:Hide()
tabUseSkillsBg:SetColorTexture(0.15, 0.15, 0.15, 1)
tabBuildSkillsBg:SetColorTexture(0.3, 0.3, 0.3, 1)
else
useSkillsContent:Hide()
buildSkillsContent:Show()
tabUseSkillsBg:SetColorTexture(0.3, 0.3, 0.3, 1)
tabBuildSkillsBg:SetColorTexture(0.15, 0.15, 0.15, 1)
end
end
tabUseSkills:SetScript("OnClick", function()
SelectTab(1)
end)
tabBuildSkills:SetScript("OnClick", function()
SelectTab(2)
end)
---------------------
-- USE SKILLS TAB CONTENT (left: controls, right: log)
---------------------
local controlsPanel = CreateFrame("Frame", nil, useSkillsContent)
controlsPanel:SetPoint("TOPLEFT", useSkillsContent, "TOPLEFT", 0, 0)
controlsPanel:SetSize(CONTROLS_WIDTH, tabContentHeight)
local content = controlsPanel
local yPos = -PADDING
-- Section: Define Your Base Roll
CreateSectionHeader(content, "Define Your Base Roll", PADDING, yPos)
yPos = yPos - 20
-- Roll Type label
CreateSubLabel(content, "Roll Type", PADDING, yPos)
yPos = yPos - 20
-- Roll Type radio buttons
local attackRadio, defenseRadio
local function UpdateRollTypeSelection(rollType)
AltSystem.State.rollType = rollType
attackRadio:SetChecked(rollType == "attack")
defenseRadio:SetChecked(rollType == "defense")
-- Update roll button text
if AltSystem.RollButton then
local label = rollType == "attack" and "Roll Attack" or "Roll Defense"
AltSystem.RollButton:SetText(label)
end
end
attackRadio = CreateRadioButton(content, "AltSystemAttackRadio", "Attack Roll", PADDING, yPos,
AltSystem.State.rollType == "attack",
function()
UpdateRollTypeSelection("attack")
end)
defenseRadio = CreateRadioButton(content, "AltSystemDefenseRadio", "Defense Roll", CONTROLS_WIDTH / 2, yPos,
AltSystem.State.rollType == "defense",
function()
UpdateRollTypeSelection("defense")
end)
yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Skill dropdown
local skillOptions = BuildSkillOptions()
local UpdateSkillWarning -- forward declaration
local skillContainer, skillDropdown, getSkillIndex, setSkillIndex = CreateDropdown(
content, "AltSystemSkillDropdown", "Skill", skillOptions,
AltSystem.State.selectedSkillIndex,
function(index)
AltSystem.State.selectedSkillIndex = index
AltSystem.State.selectedSkillName = AltSystem.Data.Skills[index] and AltSystem.Data.Skills[index].name or nil
UpdateSkillWarning(index)
end,
"GameFontNormal"
)
skillContainer:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
skillContainer:SetWidth(CONTROLS_WIDTH - PADDING * 2)
-- Warning icon for skill mismatch
local skillWarning = CreateFrame("Frame", nil, skillContainer)
skillWarning:SetSize(20, 20)
skillWarning:SetPoint("LEFT", skillDropdown, "RIGHT", 4, 0)
local skillWarningIcon = skillWarning:CreateTexture(nil, "ARTWORK")
skillWarningIcon:SetAllPoints()
skillWarningIcon:SetAtlas("services-icon-warning")
skillWarning:SetScript("OnEnter", function(self)
if self.tooltipText then
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(self.tooltipText, 1, 0.82, 0)
GameTooltip:Show()
end
end)
skillWarning:SetScript("OnLeave", function()
GameTooltip:Hide()
end)
skillWarning:Hide()
UpdateSkillWarning = function(index)
local skill = AltSystem.Data.Skills[index]
if skill and skill.warning then
skillWarning.tooltipText = skill.warning
skillWarning:Show()
else
skillWarning:Hide()
end
end
-- Store references for refreshing
AltSystem.SkillDropdown = skillDropdown
AltSystem.GetSkillIndex = getSkillIndex
AltSystem.SetSkillIndex = setSkillIndex
AltSystem.UpdateSkillWarning = UpdateSkillWarning
yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Armor label
CreateSubLabel(content, "Extra Armor", PADDING, yPos)
yPos = yPos - 20
-- Armor radio buttons
local armorRadios = {}
local function UpdateArmorSelection(index)
AltSystem.State.selectedDefenseIndex = index
for i, radio in ipairs(armorRadios) do
radio:SetChecked(i == index)
end
end
local armorX = PADDING
for i, def in ipairs(AltSystem.Data.Defenses) do
local text = def.name
if def.modifier > 0 then
text = text .. " (+" .. def.modifier .. ")"
end
local radio = CreateRadioButton(content, "AltSystemArmorRadio" .. i, text, armorX, yPos,
AltSystem.State.selectedDefenseIndex == i,
function()
UpdateArmorSelection(i)
end)
table.insert(armorRadios, radio)
armorX = armorX + 110
end
yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Item label
CreateSubLabel(content, "Item", PADDING, yPos)
yPos = yPos - 20
-- Item radio buttons
local itemRadios = {}
local function UpdateItemSelection(index)
AltSystem.State.selectedItemIndex = index
for i, radio in ipairs(itemRadios) do
radio:SetChecked(i == index)
end
end
local itemX = PADDING
for i, item in ipairs(AltSystem.Data.Items) do
local text = item.name
if item.modifier > 0 then
text = text .. " (+" .. item.modifier .. ")"
end
local radio = CreateRadioButton(content, "AltSystemItemRadio" .. i, text, itemX, yPos,
AltSystem.State.selectedItemIndex == i,
function()
UpdateItemSelection(i)
end)
table.insert(itemRadios, radio)
itemX = itemX + 110
end
yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Section: Modifiers (optional)
CreateSectionHeader(content, "Modifiers (optional)", PADDING, yPos)
yPos = yPos - 18
--CreateSubLabel(content, "Label", PADDING, yPos)
--yPos = yPos - 22
-- Shield checkbox
local shieldCheck = CreateFrame("CheckButton", "AltSystemShieldCheck", content, "UICheckButtonTemplate")
shieldCheck:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
shieldCheck:SetChecked(AltSystem.State.shieldEnabled)
local shieldText = shieldCheck:GetFontString()
if shieldText then
shieldText:SetText("Shield (+ 1)")
else
shieldText = shieldCheck:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
shieldText:SetPoint("LEFT", shieldCheck, "RIGHT", 2, 0)
shieldText:SetText("Shield (+ 1)")
end
shieldCheck:SetScript("OnClick", function(self)
AltSystem.State.shieldEnabled = self:GetChecked()
end)
-- Pet checkbox
local petCheck = CreateFrame("CheckButton", "AltSystemPetSummonCheck", content, "UICheckButtonTemplate")
petCheck:SetPoint("LEFT", shieldCheck, "RIGHT", 80, 0)
petCheck:SetChecked(AltSystem.State.petSummonEnabled)
local petText = petCheck:GetFontString()
if petText then
petText:SetText("Pet (+d5)")
else
petText = petCheck:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
petText:SetPoint("LEFT", petCheck, "RIGHT", 2, 0)
petText:SetText("Pet (+d5)")
end
petCheck:SetScript("OnClick", function(self)
AltSystem.State.petSummonEnabled = self:GetChecked()
end)
yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Section: Roll Dice
CreateSectionHeader(content, "Roll Dice", PADDING, yPos)
yPos = yPos - 22
-- Announce Roll dropdown (Self Roll + channels)
local announceOptions = { { text = "Self Roll" } }
for _, ch in ipairs(AltSystem.AnnounceChannels) do
table.insert(announceOptions, { text = ch.name })
end
local announceContainer, announceDropdown = CreateDropdown(
content, "AltSystemAnnounceDropdown", "Announce Roll", announceOptions,
AltSystem.State.announceOptionIndex,
function(index)
AltSystem.State.announceOptionIndex = index
if index > 1 then
AltSystem.State.announceEnabled = true
AltSystem.State.announceChannelIndex = index - 1
else
AltSystem.State.announceEnabled = false
AltSystem.State.announceChannelIndex = 1
end
end,
"GameFontNormal")
announceContainer:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
announceContainer:SetWidth(CONTROLS_WIDTH - PADDING * 2)
yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Roll button
local rollBtn = CreateFrame("Button", "AltSystemRollBtn", content, "UIPanelButtonTemplate")
rollBtn:SetSize(CONTROLS_WIDTH - PADDING * 2, 32)
rollBtn:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
local rollLabel = AltSystem.State.rollType == "attack" and "Roll Attack" or "Roll Defense"
rollBtn:SetText(rollLabel)
rollBtn:SetScript("OnClick", function()
AltSystem:PerformRoll(AltSystem.State.rollType)
end)
AltSystem.RollButton = rollBtn
---------------------
-- LOG PANEL (right side of Use Skills tab)
---------------------
local logPanel = CreateFrame("Frame", nil, useSkillsContent)
logPanel:SetPoint("TOPLEFT", useSkillsContent, "TOPLEFT", CONTROLS_WIDTH, 0)
logPanel:SetSize(LOG_WIDTH, tabContentHeight)
-- Log header
local logHeader = logPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
logHeader:SetPoint("TOPLEFT", logPanel, "TOPLEFT", PADDING, -4)
logHeader:SetText("Log")
logHeader:SetTextColor(0.9, 0.75, 0.2)
-- Log scroll area background
local logBg = CreateFrame("Frame", nil, logPanel, "InsetFrameTemplate")
logBg:SetPoint("TOPLEFT", logPanel, "TOPLEFT", 4, -22)
logBg:SetPoint("BOTTOMRIGHT", logPanel, "BOTTOMRIGHT", -4, 4)
-- Scroll frame for log entries
local scrollFrame = CreateFrame("ScrollFrame", "AltSystemLogScrollFrame", logBg, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", logBg, "TOPLEFT", 6, -6)
scrollFrame:SetPoint("BOTTOMRIGHT", logBg, "BOTTOMRIGHT", -28, 6)
local scrollChild = CreateFrame("Frame", "AltSystemLogScrollChild", scrollFrame)
scrollChild:SetWidth(scrollFrame:GetWidth() or (LOG_WIDTH - 50))
scrollChild:SetHeight(1) -- Will be updated dynamically
scrollFrame:SetScrollChild(scrollChild)
AltSystem.LogScrollFrame = scrollFrame
AltSystem.LogScrollChild = scrollChild
-- Refresh skills from TRP3 profile each time the window is shown
f:SetScript("OnShow", function()
AltSystem:RefreshSkillDropdown()
AltSystem:RefreshLogPanel()
end)
f:Hide()
AltSystem.MainFrame = f
end
-- Refresh the log panel UI from AltSystem.State.rollLog
function AltSystem:RefreshLogPanel()
local scrollChild = AltSystem.LogScrollChild
if not scrollChild then
return
end
-- Remove existing log entry fontstrings
if scrollChild.entries then
for _, entry in ipairs(scrollChild.entries) do
entry:Hide()
entry:SetText("")
end
end
scrollChild.entries = scrollChild.entries or {}
local rollLog = AltSystem.State.rollLog
local entryHeight = 16
local spacing = 4
local yPos = 0
local childWidth = AltSystem.LogScrollFrame:GetWidth() or 250
-- Entries are newest-first
for i = #rollLog, 1, -1 do
local idx = #rollLog - i + 1
local logEntry = rollLog[i]
local fontStr = scrollChild.entries[idx]
if not fontStr then
fontStr = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
scrollChild.entries[idx] = fontStr
end
fontStr:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 2, -yPos)
fontStr:SetWidth(childWidth - 4)
fontStr:SetJustifyH("LEFT")
fontStr:SetText(logEntry.text)
fontStr:Show()
yPos = yPos + entryHeight + spacing
end
scrollChild:SetHeight(math.max(yPos, 1))
end
-- Refresh the skill dropdown with current TRP3 profile data
function AltSystem:RefreshSkillDropdown()
AltSystem.Data:RefreshSkills()
-- Try to restore the previously selected skill by name; fall back to 1 (Base roll)
local newIndex = 1
local savedName = AltSystem.State.selectedSkillName
if savedName then
for i, skill in ipairs(AltSystem.Data.Skills) do
if skill.name == savedName then
newIndex = i
break
end
end
end
AltSystem.State.selectedSkillIndex = newIndex
AltSystem.State.selectedSkillName = AltSystem.Data.Skills[newIndex] and AltSystem.Data.Skills[newIndex].name or nil
if AltSystem.SetSkillIndex then
AltSystem.SetSkillIndex(newIndex)
end
if AltSystem.UpdateSkillWarning then
AltSystem.UpdateSkillWarning(newIndex)
end
-- Rebuild the dropdown menu with the new skill list
if AltSystem.SkillDropdown then
local skillOptions = BuildSkillOptions()
AltSystem.SkillDropdown:SetupMenu(function(dropdown, rootDescription)
for i, option in ipairs(skillOptions) do
rootDescription:CreateRadio(
option.text,
function(data)
return data == AltSystem.State.selectedSkillIndex
end,
function(data)
AltSystem.State.selectedSkillIndex = data
AltSystem.State.selectedSkillName = AltSystem.Data.Skills[data] and AltSystem.Data.Skills[data].name or nil
if AltSystem.SetSkillIndex then
AltSystem.SetSkillIndex(data)
end
if AltSystem.UpdateSkillWarning then
AltSystem.UpdateSkillWarning(data)
end
end,
i
)
end
end)
end
end
-- Frame is created by AltSystem:Init() in Core.lua, after saved variables are loaded