This commit is contained in:
Gonçalo Correia 2026-05-12 16:20:52 +01:00
parent a4b2e69cc8
commit ffc693a63c
4 changed files with 487 additions and 241 deletions

View file

@ -6,11 +6,14 @@ AltSystem.State = {
selectedSkillIndex = 1, selectedSkillIndex = 1,
selectedSkillName = nil, -- skill name used to restore selection across sessions selectedSkillName = nil, -- skill name used to restore selection across sessions
selectedItemIndex = 1, -- 1 = No item selectedItemIndex = 1, -- 1 = No item
selectedDefenseIndex = 1, -- 1 = Base armor selectedDefenseIndex = 1, -- 1 = No Armor
shieldEnabled = false, shieldEnabled = false,
petSummonEnabled = false, petSummonEnabled = false,
announceEnabled = false, announceEnabled = false,
announceChannelIndex = 1, -- 1 = Emote, 2 = Party, 3 = Raid, 4 = Guild announceChannelIndex = 1, -- 1 = Emote, 2 = Party, 3 = Raid, 4 = Guild
rollType = "attack", -- "attack" or "defense"
announceOptionIndex = 1, -- 1 = Self Roll, 2+ = channel from AnnounceChannels
rollLog = {}, -- array of { text = "...", timestamp = time() }, max 100, not persisted
} }
-- Channel definitions for announcing rolls -- Channel definitions for announcing rolls
@ -35,12 +38,6 @@ end)
function AltSystem:Init() function AltSystem:Init()
-- Load saved settings -- Load saved settings
AltSystemDB = AltSystemDB or {} AltSystemDB = AltSystemDB or {}
if AltSystemDB.announceEnabled ~= nil then
AltSystem.State.announceEnabled = AltSystemDB.announceEnabled
end
if AltSystemDB.announceChannelIndex then
AltSystem.State.announceChannelIndex = AltSystemDB.announceChannelIndex
end
if AltSystemDB.petSummonEnabled ~= nil then if AltSystemDB.petSummonEnabled ~= nil then
AltSystem.State.petSummonEnabled = AltSystemDB.petSummonEnabled AltSystem.State.petSummonEnabled = AltSystemDB.petSummonEnabled
end end
@ -56,6 +53,28 @@ function AltSystem:Init()
if AltSystemDB.selectedSkillName then if AltSystemDB.selectedSkillName then
AltSystem.State.selectedSkillName = AltSystemDB.selectedSkillName AltSystem.State.selectedSkillName = AltSystemDB.selectedSkillName
end end
if AltSystemDB.rollType then
AltSystem.State.rollType = AltSystemDB.rollType
end
-- Migrate from old announce settings or load new announceOptionIndex
if AltSystemDB.announceOptionIndex then
AltSystem.State.announceOptionIndex = AltSystemDB.announceOptionIndex
elseif AltSystemDB.announceEnabled ~= nil then
-- Backwards compat: derive from old announceEnabled + announceChannelIndex
if AltSystemDB.announceEnabled and AltSystemDB.announceChannelIndex then
AltSystem.State.announceOptionIndex = AltSystemDB.announceChannelIndex + 1
else
AltSystem.State.announceOptionIndex = 1
end
end
-- Derive announceEnabled and announceChannelIndex from announceOptionIndex
if AltSystem.State.announceOptionIndex > 1 then
AltSystem.State.announceEnabled = true
AltSystem.State.announceChannelIndex = AltSystem.State.announceOptionIndex - 1
else
AltSystem.State.announceEnabled = false
AltSystem.State.announceChannelIndex = 1
end
-- Register slash command /altsystem -- Register slash command /altsystem
SLASH_ALTSYSTEM1 = "/altsystem" SLASH_ALTSYSTEM1 = "/altsystem"
@ -68,13 +87,13 @@ function AltSystem:Init()
saveFrame:RegisterEvent("PLAYER_LOGOUT") saveFrame:RegisterEvent("PLAYER_LOGOUT")
saveFrame:SetScript("OnEvent", function() saveFrame:SetScript("OnEvent", function()
AltSystemDB = AltSystemDB or {} AltSystemDB = AltSystemDB or {}
AltSystemDB.announceEnabled = AltSystem.State.announceEnabled AltSystemDB.announceOptionIndex = AltSystem.State.announceOptionIndex
AltSystemDB.announceChannelIndex = AltSystem.State.announceChannelIndex
AltSystemDB.petSummonEnabled = AltSystem.State.petSummonEnabled AltSystemDB.petSummonEnabled = AltSystem.State.petSummonEnabled
AltSystemDB.shieldEnabled = AltSystem.State.shieldEnabled AltSystemDB.shieldEnabled = AltSystem.State.shieldEnabled
AltSystemDB.selectedItemIndex = AltSystem.State.selectedItemIndex AltSystemDB.selectedItemIndex = AltSystem.State.selectedItemIndex
AltSystemDB.selectedDefenseIndex = AltSystem.State.selectedDefenseIndex AltSystemDB.selectedDefenseIndex = AltSystem.State.selectedDefenseIndex
AltSystemDB.selectedSkillName = AltSystem.State.selectedSkillName AltSystemDB.selectedSkillName = AltSystem.State.selectedSkillName
AltSystemDB.rollType = AltSystem.State.rollType
end) end)
-- Build the main frame now that saved variables are loaded -- Build the main frame now that saved variables are loaded

View file

@ -147,11 +147,11 @@ AltSystem.Data.Items = {
{ name = "Epic item", modifier = 5 }, { name = "Epic item", modifier = 5 },
} }
-- Defense options: name and modifier -- Defense / Armor options: name and modifier
AltSystem.Data.Defenses = { AltSystem.Data.Defenses = {
{ name = "Base armor", modifier = 0 }, { name = "None", modifier = 0 },
{ name = "Extra small armor", modifier = 1 }, { name = "Partial", modifier = 1 },
{ name = "Extra large armor", modifier = 2 }, { name = "Full", modifier = 2 },
} }
-- Shield modifier -- Shield modifier

105
Roll.lua
View file

@ -27,10 +27,42 @@ local function BuildModifierString(modifiers)
return table.concat(parts, " ") return table.concat(parts, " ")
end end
-- Build the log message string (always uses the non-emote format with character name)
local function BuildLogMessage(rollValue, modifiers, total)
local name = GetCharacterName()
local modStr = BuildModifierString(modifiers)
if modStr ~= "" then
return name .. " rolled " .. rollValue .. " " .. modStr .. " = " .. total
else
return name .. " rolled " .. rollValue .. " = " .. total
end
end
-- Build the critical roll log message
local function BuildCriticalLogMessage(isCriticalSuccess)
local name = GetCharacterName()
if isCriticalSuccess then
return name .. " rolled a Critical Success!"
else
return name .. " rolled a Critical Failure!"
end
end
-- Add an entry to the roll log and refresh the UI
local function AddLogEntry(text)
local rollLog = AltSystem.State.rollLog
table.insert(rollLog, { text = text, timestamp = time() })
-- Cap at 100 entries
if #rollLog > 100 then
table.remove(rollLog, 1)
end
-- Refresh the log panel if it exists
if AltSystem.RefreshLogPanel then
AltSystem:RefreshLogPanel()
end
end
-- Send a message to the given channel. -- Send a message to the given channel.
-- Note: EMOTE channel uses SendChatMessage like all others. The WoW API
-- SendChatMessage(msg, "EMOTE") sends a proper /e emote from any context,
-- unlike RunMacroText which is a protected function requiring a hardware event.
local function SendToChannel(msg, channel) local function SendToChannel(msg, channel)
SendChatMessage(msg, channel) SendChatMessage(msg, channel)
end end
@ -64,6 +96,24 @@ local function AnnounceRoll(rollValue, modifiers, total)
SendToChannel(msg, channelDef.channel) SendToChannel(msg, channelDef.channel)
end end
-- Announce a critical roll result
local function AnnounceCritical(isCriticalSuccess)
if not AltSystem.State.announceEnabled then return end
local channelDef = AltSystem.AnnounceChannels[AltSystem.State.announceChannelIndex]
if not channelDef then return end
local critText = isCriticalSuccess and "rolled a Critical Success!" or "rolled a Critical Failure!"
local msg
if channelDef.channel == "EMOTE" then
msg = critText
else
msg = GetCharacterName() .. " " .. critText
end
SendToChannel(msg, channelDef.channel)
end
-- Perform a roll: triggers the WoW native /roll 20 command -- Perform a roll: triggers the WoW native /roll 20 command
function AltSystem:PerformRoll(rollType) function AltSystem:PerformRoll(rollType)
pendingPetRoll = nil pendingPetRoll = nil
@ -116,32 +166,12 @@ end)
function AltSystem:CalculateAndDisplayResult(rollType, rollValue, petRollValue) function AltSystem:CalculateAndDisplayResult(rollType, rollValue, petRollValue)
-- Critical rolls bypass normal calculation -- Critical rolls bypass normal calculation
if rollValue == 1 then if rollValue == 1 then
if AltSystem.ResultText then AddLogEntry(BuildCriticalLogMessage(false))
AltSystem.ResultText:SetText("|cffff0000Critical Failure|r") AnnounceCritical(false)
end
if AltSystem.State.announceEnabled then
local channelDef = AltSystem.AnnounceChannels[AltSystem.State.announceChannelIndex]
if channelDef then
local critMsg = channelDef.channel == "EMOTE"
and "rolled a Critical Failure!"
or (GetCharacterName() .. " rolled a Critical Failure!")
SendToChannel(critMsg, channelDef.channel)
end
end
return return
elseif rollValue == 20 then elseif rollValue == 20 then
if AltSystem.ResultText then AddLogEntry(BuildCriticalLogMessage(true))
AltSystem.ResultText:SetText("|cff00ff00Critical Success|r") AnnounceCritical(true)
end
if AltSystem.State.announceEnabled then
local channelDef = AltSystem.AnnounceChannels[AltSystem.State.announceChannelIndex]
if channelDef then
local critMsg = channelDef.channel == "EMOTE"
and "rolled a Critical Success!"
or (GetCharacterName() .. " rolled a Critical Success!")
SendToChannel(critMsg, channelDef.channel)
end
end
return return
end end
@ -153,7 +183,6 @@ function AltSystem:CalculateAndDisplayResult(rollType, rollValue, petRollValue)
local itemMod = item and item.modifier or 0 local itemMod = item and item.modifier or 0
local total = rollValue local total = rollValue
local breakdown = "Roll: " .. rollValue
local modifiers = {} local modifiers = {}
@ -166,18 +195,14 @@ function AltSystem:CalculateAndDisplayResult(rollType, rollValue, petRollValue)
total = rollValue + skillMod + itemMod + petMod total = rollValue + skillMod + itemMod + petMod
if not isBaseRoll then if not isBaseRoll then
breakdown = breakdown .. "\nSkill: " .. FormatModifier(skillMod)
table.insert(modifiers, { name = skill and skill.name or "Skill", value = skillMod }) table.insert(modifiers, { name = skill and skill.name or "Skill", value = skillMod })
end end
if itemMod ~= 0 then if itemMod ~= 0 then
breakdown = breakdown .. " | Item: " .. FormatModifier(itemMod)
table.insert(modifiers, { name = item and item.name or "Item", value = itemMod }) table.insert(modifiers, { name = item and item.name or "Item", value = itemMod })
end end
if petMod ~= 0 then if petMod ~= 0 then
breakdown = breakdown .. " | Pet: +" .. petMod
table.insert(modifiers, { name = "Pet", value = petMod }) table.insert(modifiers, { name = "Pet", value = petMod })
end end
breakdown = breakdown .. "\n|cffffd100Attack Total: " .. total .. "|r"
elseif rollType == "defense" then elseif rollType == "defense" then
-- Defense Roll = roll + skill modifier + item modifier + defense modifier + shield modifier -- Defense Roll = roll + skill modifier + item modifier + defense modifier + shield modifier
@ -188,30 +213,26 @@ function AltSystem:CalculateAndDisplayResult(rollType, rollValue, petRollValue)
total = rollValue + skillMod + itemMod + defenseMod + shieldMod + petMod total = rollValue + skillMod + itemMod + defenseMod + shieldMod + petMod
if not isBaseRoll then if not isBaseRoll then
breakdown = breakdown .. "\nSkill: " .. FormatModifier(skillMod)
table.insert(modifiers, { name = skill and skill.name or "Skill", value = skillMod }) table.insert(modifiers, { name = skill and skill.name or "Skill", value = skillMod })
end end
if itemMod ~= 0 then if itemMod ~= 0 then
breakdown = breakdown .. " | Item: " .. FormatModifier(itemMod)
table.insert(modifiers, { name = item and item.name or "Item", value = itemMod }) table.insert(modifiers, { name = item and item.name or "Item", value = itemMod })
end end
breakdown = breakdown .. "\nDefense: " .. FormatModifier(defenseMod) if defenseMod ~= 0 then
table.insert(modifiers, { name = defense and defense.name or "Defense", value = defenseMod }) table.insert(modifiers, { name = defense and defense.name or "Armor", value = defenseMod })
end
if shieldMod ~= 0 then if shieldMod ~= 0 then
breakdown = breakdown .. " | Shield: " .. FormatModifier(shieldMod)
table.insert(modifiers, { name = "Shield", value = shieldMod }) table.insert(modifiers, { name = "Shield", value = shieldMod })
end end
if petMod ~= 0 then if petMod ~= 0 then
breakdown = breakdown .. " | Pet: +" .. petMod
table.insert(modifiers, { name = "Pet", value = petMod }) table.insert(modifiers, { name = "Pet", value = petMod })
end end
breakdown = breakdown .. "\n|cff00ccffDefense Total: " .. total .. "|r"
end end
if AltSystem.ResultText then -- Add to log (always, regardless of announce setting)
AltSystem.ResultText:SetText(breakdown) AddLogEntry(BuildLogMessage(rollValue, modifiers, total))
end
-- Announce to chat (if enabled)
AnnounceRoll(rollValue, modifiers, total) AnnounceRoll(rollValue, modifiers, total)
end end

578
UI.lua
View file

@ -1,14 +1,17 @@
-- AltSystem UI -- AltSystem UI
-- Creates the main dialog window with all interface elements. -- 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+). -- Uses the modern DropdownButton API (WoW 10.2.5+ / 12.0+).
AltSystem = AltSystem or {} AltSystem = AltSystem or {}
local WINDOW_WIDTH = 300 local WINDOW_WIDTH = 726
local WINDOW_HEIGHT = 478 local WINDOW_HEIGHT = 500
local PADDING = 12 local CONTROLS_WIDTH = 363
local ROW_HEIGHT = 30 local LOG_WIDTH = 363
local LABEL_WIDTH = 80 local PADDING = 12
local ROW_HEIGHT = 26
local SECTION_GAP = 10
-- Helper: Build the skill option list from current AltSystem.Data.Skills -- Helper: Build the skill option list from current AltSystem.Data.Skills
local function BuildSkillOptions() local function BuildSkillOptions()
@ -27,38 +30,98 @@ local function BuildSkillOptions()
end end
-- Helper: Create a modern dropdown (WowStyle1DropdownTemplate) -- Helper: Create a modern dropdown (WowStyle1DropdownTemplate)
local function CreateDropdown(parent, name, yOffset, labelText, options, defaultIndex, onSelect) local function CreateDropdown(parent, name, labelText, options, defaultIndex, onSelect, labelFont)
local label = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal") local container = CreateFrame("Frame", nil, parent)
label:SetPoint("TOPLEFT", parent, "TOPLEFT", PADDING, yOffset) container:SetHeight(ROW_HEIGHT)
label:SetText(labelText)
label:SetWidth(LABEL_WIDTH) local label
label:SetJustifyH("LEFT") 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 selectedIndex = defaultIndex or 1
local dropdown = CreateFrame("DropdownButton", name, parent, "WowStyle1DropdownTemplate") local dropdown = CreateFrame("DropdownButton", name, container, "WowStyle1DropdownTemplate")
dropdown:SetPoint("LEFT", label, "RIGHT", 4, 0) if label then
dropdown:SetWidth(160) dropdown:SetPoint("LEFT", label, "RIGHT", 8, 0)
else
dropdown:SetPoint("LEFT", container, "LEFT", 0, 0)
end
dropdown:SetWidth(180)
dropdown:SetupMenu(function(dropdown, rootDescription) dropdown:SetupMenu(function(dropdown, rootDescription)
for i, option in ipairs(options) do for i, option in ipairs(options) do
rootDescription:CreateRadio( rootDescription:CreateRadio(
option.text, option.text,
function(data) return data == selectedIndex end, function(data)
function(data) return data == selectedIndex
selectedIndex = data end,
if onSelect then onSelect(data, options[data]) end function(data)
end, selectedIndex = data
i if onSelect then
onSelect(data, options[data])
end
end,
i
) )
end end
end) end)
return dropdown, function() return selectedIndex end, function(idx) selectedIndex = idx 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", "GameFontNormalSmall")
label:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
label:SetText(text)
label:SetTextColor(0.9, 0.75, 0.2)
return label
end end
function AltSystem:CreateMainFrame() function AltSystem:CreateMainFrame()
if AltSystem.MainFrame then return end if AltSystem.MainFrame then
return
end
-- Main frame -- Main frame
local f = CreateFrame("Frame", "AltSystemMainFrame", UIParent, "BasicFrameTemplateWithInset") local f = CreateFrame("Frame", "AltSystemMainFrame", UIParent, "BasicFrameTemplateWithInset")
@ -76,26 +139,142 @@ function AltSystem:CreateMainFrame()
f.title:SetPoint("TOPLEFT", f.TitleBg, "TOPLEFT", 5, -3) f.title:SetPoint("TOPLEFT", f.TitleBg, "TOPLEFT", 5, -3)
f.title:SetText("AltSystem") f.title:SetText("AltSystem")
-- Track current Y offset for layout ---------------------
local yPos = -40 -- 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 -- Skill dropdown
-------------------------
local skillOptions = BuildSkillOptions() local skillOptions = BuildSkillOptions()
local UpdateSkillWarning -- forward declaration local UpdateSkillWarning -- forward declaration
local skillDropdown, getSkillIndex, setSkillIndex = CreateDropdown( local skillContainer, skillDropdown, getSkillIndex, setSkillIndex = CreateDropdown(
f, "AltSystemSkillDropdown", yPos, "Skill:", skillOptions, content, "AltSystemSkillDropdown", "Skill", skillOptions,
AltSystem.State.selectedSkillIndex, AltSystem.State.selectedSkillIndex,
function(index) function(index)
AltSystem.State.selectedSkillIndex = index AltSystem.State.selectedSkillIndex = index
AltSystem.State.selectedSkillName = AltSystem.Data.Skills[index] and AltSystem.Data.Skills[index].name or nil AltSystem.State.selectedSkillName = AltSystem.Data.Skills[index] and AltSystem.Data.Skills[index].name or nil
UpdateSkillWarning(index) UpdateSkillWarning(index)
end) end,
"GameFontNormalSmall"
)
skillContainer:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
skillContainer:SetWidth(CONTROLS_WIDTH - PADDING * 2)
-- Warning icon for skill mismatch (shown next to dropdown when value doesn't match keyword) -- Warning icon for skill mismatch
local skillWarning = CreateFrame("Frame", nil, f) local skillWarning = CreateFrame("Frame", nil, skillContainer)
skillWarning:SetSize(20, 20) skillWarning:SetSize(20, 20)
skillWarning:SetPoint("LEFT", skillDropdown, "RIGHT", 4, 0) skillWarning:SetPoint("LEFT", skillDropdown, "RIGHT", 4, 0)
@ -115,7 +294,6 @@ function AltSystem:CreateMainFrame()
end) end)
skillWarning:Hide() skillWarning:Hide()
-- Update the warning icon visibility based on the currently selected skill
UpdateSkillWarning = function(index) UpdateSkillWarning = function(index)
local skill = AltSystem.Data.Skills[index] local skill = AltSystem.Data.Skills[index]
if skill and skill.warning then if skill and skill.warning then
@ -131,186 +309,212 @@ function AltSystem:CreateMainFrame()
AltSystem.GetSkillIndex = getSkillIndex AltSystem.GetSkillIndex = getSkillIndex
AltSystem.SetSkillIndex = setSkillIndex AltSystem.SetSkillIndex = setSkillIndex
AltSystem.UpdateSkillWarning = UpdateSkillWarning AltSystem.UpdateSkillWarning = UpdateSkillWarning
yPos = yPos - ROW_HEIGHT - 8
------------------------- yPos = yPos - ROW_HEIGHT - SECTION_GAP
-- Item dropdown
------------------------- -- Armor label
local itemOptions = {} CreateSubLabel(content, "Extra Armor", PADDING, yPos)
for _, item in ipairs(AltSystem.Data.Items) do yPos = yPos - 20
local sign = item.modifier >= 0 and "+" or ""
local modText = item.modifier ~= 0 and (" (" .. sign .. item.modifier .. ")") or "" -- Armor radio buttons
table.insert(itemOptions, { local armorRadios = {}
text = item.name .. modText,
}) local function UpdateArmorSelection(index)
AltSystem.State.selectedDefenseIndex = index
for i, radio in ipairs(armorRadios) do
radio:SetChecked(i == index)
end
end end
CreateDropdown(f, "AltSystemItemDropdown", yPos, "Item:", itemOptions, AltSystem.State.selectedItemIndex, local armorX = PADDING
function(index) for i, def in ipairs(AltSystem.Data.Defenses) do
AltSystem.State.selectedItemIndex = index local text = def.name
end) if def.modifier > 0 then
yPos = yPos - ROW_HEIGHT - 8 text = text .. " (+" .. def.modifier .. ")"
end
------------------------- local radio = CreateRadioButton(content, "AltSystemArmorRadio" .. i, text, armorX, yPos,
-- Defense dropdown AltSystem.State.selectedDefenseIndex == i,
------------------------- function()
local defenseOptions = {} UpdateArmorSelection(i)
for _, def in ipairs(AltSystem.Data.Defenses) do end)
local sign = def.modifier >= 0 and "+" or "" table.insert(armorRadios, radio)
local modText = " (" .. sign .. def.modifier .. ")" armorX = armorX + 110
table.insert(defenseOptions, {
text = def.name .. modText,
})
end end
CreateDropdown(f, "AltSystemDefenseDropdown", yPos, "Defense:", defenseOptions, AltSystem.State.selectedDefenseIndex, yPos = yPos - ROW_HEIGHT - SECTION_GAP
function(index)
AltSystem.State.selectedDefenseIndex = index -- Section: Modifiers (optional)
end) CreateSectionHeader(content, "Modifiers (optional)", PADDING, yPos)
yPos = yPos - ROW_HEIGHT - 8 yPos = yPos - 18
--CreateSubLabel(content, "Label", PADDING, yPos)
--yPos = yPos - 22
-------------------------
-- Shield checkbox -- Shield checkbox
------------------------- local shieldCheck = CreateFrame("CheckButton", "AltSystemShieldCheck", content, "UICheckButtonTemplate")
local shieldLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormal") shieldCheck:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
shieldLabel:SetPoint("TOPLEFT", f, "TOPLEFT", PADDING, yPos)
shieldLabel:SetText("Shield:")
shieldLabel:SetWidth(LABEL_WIDTH)
shieldLabel:SetJustifyH("LEFT")
local shieldCheck = CreateFrame("CheckButton", "AltSystemShieldCheck", f, "UICheckButtonTemplate")
shieldCheck:SetPoint("LEFT", shieldLabel, "RIGHT", -6, 0)
shieldCheck:SetChecked(AltSystem.State.shieldEnabled) shieldCheck:SetChecked(AltSystem.State.shieldEnabled)
local shieldText = shieldCheck:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") local shieldText = shieldCheck:GetFontString()
shieldText:SetPoint("LEFT", shieldCheck, "RIGHT", 2, 0) if shieldText then
shieldText:SetText("+1 modifier") 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) shieldCheck:SetScript("OnClick", function(self)
AltSystem.State.shieldEnabled = self:GetChecked() AltSystem.State.shieldEnabled = self:GetChecked()
end) end)
yPos = yPos - ROW_HEIGHT - 8 -- Pet checkbox
local petCheck = CreateFrame("CheckButton", "AltSystemPetSummonCheck", content, "UICheckButtonTemplate")
------------------------- petCheck:SetPoint("LEFT", shieldCheck, "RIGHT", 80, 0)
-- Pet/Summon checkbox
-------------------------
local petLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
petLabel:SetPoint("TOPLEFT", f, "TOPLEFT", PADDING, yPos)
petLabel:SetText("Pet:")
petLabel:SetWidth(LABEL_WIDTH)
petLabel:SetJustifyH("LEFT")
local petCheck = CreateFrame("CheckButton", "AltSystemPetSummonCheck", f, "UICheckButtonTemplate")
petCheck:SetPoint("LEFT", petLabel, "RIGHT", -6, 0)
petCheck:SetChecked(AltSystem.State.petSummonEnabled) petCheck:SetChecked(AltSystem.State.petSummonEnabled)
local petText = petCheck:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") local petText = petCheck:GetFontString()
petText:SetPoint("LEFT", petCheck, "RIGHT", 2, 0) if petText then
petText:SetText("+d5 modifier") 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) petCheck:SetScript("OnClick", function(self)
AltSystem.State.petSummonEnabled = self:GetChecked() AltSystem.State.petSummonEnabled = self:GetChecked()
end) end)
yPos = yPos - ROW_HEIGHT - 8 yPos = yPos - ROW_HEIGHT - SECTION_GAP
------------------------- -- Section: Roll Dice
-- Announce checkbox + channel dropdown CreateSectionHeader(content, "Roll Dice", PADDING, yPos)
------------------------- yPos = yPos - 22
local announceLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
announceLabel:SetPoint("TOPLEFT", f, "TOPLEFT", PADDING, yPos)
announceLabel:SetText("Announce:")
announceLabel:SetWidth(LABEL_WIDTH)
announceLabel:SetJustifyH("LEFT")
local announceCheck = CreateFrame("CheckButton", "AltSystemAnnounceCheck", f, "UICheckButtonTemplate") -- Announce Roll dropdown (Self Roll + channels)
announceCheck:SetPoint("LEFT", announceLabel, "RIGHT", -6, 0) local announceOptions = { { text = "Self Roll" } }
announceCheck:SetChecked(AltSystem.State.announceEnabled)
-- Channel dropdown (shown only when announce is enabled)
local channelOptions = {}
for _, ch in ipairs(AltSystem.AnnounceChannels) do for _, ch in ipairs(AltSystem.AnnounceChannels) do
table.insert(channelOptions, { text = ch.name }) table.insert(announceOptions, { text = ch.name })
end end
local channelDropdown, getChannelIndex, setChannelIndex = CreateDropdown( local announceContainer, announceDropdown = CreateDropdown(
f, "AltSystemChannelDropdown", yPos, "", channelOptions, content, "AltSystemAnnounceDropdown", "Announce Roll", announceOptions,
AltSystem.State.announceChannelIndex, AltSystem.State.announceOptionIndex,
function(index) function(index)
AltSystem.State.announceChannelIndex = index AltSystem.State.announceOptionIndex = index
end) if index > 1 then
channelDropdown:SetPoint("LEFT", announceCheck, "RIGHT", 2, 0) AltSystem.State.announceEnabled = true
channelDropdown:SetWidth(130) AltSystem.State.announceChannelIndex = index - 1
else
AltSystem.State.announceEnabled = false
AltSystem.State.announceChannelIndex = 1
end
end,
"GameFontNormalSmall")
announceContainer:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos)
announceContainer:SetWidth(CONTROLS_WIDTH - PADDING * 2)
-- Show/hide channel dropdown based on checkbox state yPos = yPos - ROW_HEIGHT - SECTION_GAP
local function UpdateChannelDropdownVisibility()
if AltSystem.State.announceEnabled then
channelDropdown:Show()
else
channelDropdown:Hide()
end
end
announceCheck:SetScript("OnClick", function(self) -- Roll button
AltSystem.State.announceEnabled = self:GetChecked() local rollBtn = CreateFrame("Button", "AltSystemRollBtn", content, "UIPanelButtonTemplate")
UpdateChannelDropdownVisibility() 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) end)
UpdateChannelDropdownVisibility() AltSystem.RollButton = rollBtn
yPos = yPos - ROW_HEIGHT - 12 ---------------------
-- 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
-- Roll buttons local logHeader = logPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
------------------------- logHeader:SetPoint("TOPLEFT", logPanel, "TOPLEFT", PADDING, -4)
local btnWidth = (WINDOW_WIDTH - PADDING * 3) / 2 logHeader:SetText("Log")
logHeader:SetTextColor(0.9, 0.75, 0.2)
local attackBtn = CreateFrame("Button", "AltSystemAttackRollBtn", f, "UIPanelButtonTemplate") -- Log scroll area background
attackBtn:SetSize(btnWidth, 28) local logBg = CreateFrame("Frame", nil, logPanel, "InsetFrameTemplate")
attackBtn:SetPoint("TOPLEFT", f, "TOPLEFT", PADDING, yPos) logBg:SetPoint("TOPLEFT", logPanel, "TOPLEFT", 4, -22)
attackBtn:SetText("Attack/Skill Roll") logBg:SetPoint("BOTTOMRIGHT", logPanel, "BOTTOMRIGHT", -4, 4)
local defenseBtn = CreateFrame("Button", "AltSystemDefenseRollBtn", f, "UIPanelButtonTemplate") -- Scroll frame for log entries
defenseBtn:SetSize(btnWidth, 28) local scrollFrame = CreateFrame("ScrollFrame", "AltSystemLogScrollFrame", logBg, "UIPanelScrollFrameTemplate")
defenseBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -PADDING, yPos) scrollFrame:SetPoint("TOPLEFT", logBg, "TOPLEFT", 6, -6)
defenseBtn:SetText("Defense Roll") scrollFrame:SetPoint("BOTTOMRIGHT", logBg, "BOTTOMRIGHT", -28, 6)
yPos = yPos - 40 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
-- Roll result area AltSystem.LogScrollChild = scrollChild
-------------------------
local resultBg = CreateFrame("Frame", nil, f, "InsetFrameTemplate")
resultBg:SetPoint("TOPLEFT", f, "TOPLEFT", PADDING, yPos)
resultBg:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -PADDING, PADDING)
local resultText = resultBg:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
resultText:SetPoint("CENTER")
resultText:SetText("Roll result will appear here")
resultText:SetJustifyH("CENTER")
resultText:SetWidth(resultBg:GetWidth() - 10)
AltSystem.ResultText = resultText
-- Button click handlers
attackBtn:SetScript("OnClick", function()
AltSystem:PerformRoll("attack")
end)
defenseBtn:SetScript("OnClick", function()
AltSystem:PerformRoll("defense")
end)
-- Refresh skills from TRP3 profile each time the window is shown -- Refresh skills from TRP3 profile each time the window is shown
f:SetScript("OnShow", function() f:SetScript("OnShow", function()
AltSystem:RefreshSkillDropdown() AltSystem:RefreshSkillDropdown()
AltSystem:RefreshLogPanel()
end) end)
f:Hide() f:Hide()
AltSystem.MainFrame = f AltSystem.MainFrame = f
end 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 -- Refresh the skill dropdown with current TRP3 profile data
function AltSystem:RefreshSkillDropdown() function AltSystem:RefreshSkillDropdown()
AltSystem.Data:RefreshSkills() AltSystem.Data:RefreshSkills()
@ -341,19 +545,21 @@ function AltSystem:RefreshSkillDropdown()
AltSystem.SkillDropdown:SetupMenu(function(dropdown, rootDescription) AltSystem.SkillDropdown:SetupMenu(function(dropdown, rootDescription)
for i, option in ipairs(skillOptions) do for i, option in ipairs(skillOptions) do
rootDescription:CreateRadio( rootDescription:CreateRadio(
option.text, option.text,
function(data) return data == AltSystem.State.selectedSkillIndex end, function(data)
function(data) return data == AltSystem.State.selectedSkillIndex
AltSystem.State.selectedSkillIndex = data end,
AltSystem.State.selectedSkillName = AltSystem.Data.Skills[data] and AltSystem.Data.Skills[data].name or nil function(data)
if AltSystem.SetSkillIndex then AltSystem.State.selectedSkillIndex = data
AltSystem.SetSkillIndex(data) AltSystem.State.selectedSkillName = AltSystem.Data.Skills[data] and AltSystem.Data.Skills[data].name or nil
end if AltSystem.SetSkillIndex then
if AltSystem.UpdateSkillWarning then AltSystem.SetSkillIndex(data)
AltSystem.UpdateSkillWarning(data) end
end if AltSystem.UpdateSkillWarning then
end, AltSystem.UpdateSkillWarning(data)
i end
end,
i
) )
end end
end) end)