diff --git a/.idea/.gitignore b/.idea/.gitignore old mode 100755 new mode 100644 diff --git a/.idea/misc.xml b/.idea/misc.xml old mode 100755 new mode 100644 diff --git a/.idea/modules.xml b/.idea/modules.xml old mode 100755 new mode 100644 diff --git a/.idea/vcs.xml b/.idea/vcs.xml old mode 100755 new mode 100644 diff --git a/AltSystem.iml b/AltSystem.iml old mode 100755 new mode 100644 diff --git a/AltSystem.toc b/AltSystem.toc old mode 100755 new mode 100644 index 3044c78..a085d34 --- a/AltSystem.toc +++ b/AltSystem.toc @@ -8,6 +8,5 @@ Data.lua Core.lua -BuildSkillsUI.lua UI.lua Roll.lua diff --git a/BuildSkillsUI.lua b/BuildSkillsUI.lua deleted file mode 100755 index c102705..0000000 --- a/BuildSkillsUI.lua +++ /dev/null @@ -1,430 +0,0 @@ --- AltSystem Build Skills UI --- Creates the Build Skills tab content with editable skill rows, add/delete functionality, and save to TRP. - -AltSystem = AltSystem or {} - -local PADDING = 12 -local ROW_HEIGHT = 36 -local ROW_SPACING = 8 -local NAME_WIDTH = 340 -local LEVEL_WIDTH = 140 -local VALUE_WIDTH = 80 -local DELETE_WIDTH = 30 - --- Working copy of skills being edited (not saved until user clicks Save) -local editableSkills = {} - --- Pool of created row frames for reuse -local skillRowFrames = {} - --- References set during creation -local scrollFrame, scrollChild, addRowButton - --- ─── Helper: Create a flat dark input box matching the blocky design ──────── - -local function CreateFlatEditBox(name, parent, width) - local container = CreateFrame("Frame", name .. "Container", parent, "BackdropTemplate") - container:SetSize(width, 28) - container:SetBackdrop({ - bgFile = "Interface\\ChatFrame\\ChatFrameBackground", - edgeFile = "Interface\\ChatFrame\\ChatFrameBackground", - edgeSize = 1, - }) - container:SetBackdropColor(0.12, 0.12, 0.12, 1) - container:SetBackdropBorderColor(0.25, 0.25, 0.25, 1) - - local editBox = CreateFrame("EditBox", name, container) - editBox:SetPoint("LEFT", 8, 0) - editBox:SetPoint("RIGHT", -8, 0) - editBox:SetHeight(28) - editBox:SetAutoFocus(false) - editBox:SetFontObject("GameFontHighlight") - - return container, editBox -end - --- ─── Helper: Create a flat dark dropdown button matching the blocky design ── --- Shared globally so other files (UI.lua) can reuse it. - -function AltSystem.CreateFlatDropdown(name, parent, width) - local btn = CreateFrame("Button", name, parent, "BackdropTemplate") - btn:SetSize(width, 28) - btn:SetBackdrop({ - bgFile = "Interface\\ChatFrame\\ChatFrameBackground", - edgeFile = "Interface\\ChatFrame\\ChatFrameBackground", - edgeSize = 1, - }) - btn:SetBackdropColor(0.12, 0.12, 0.12, 1) - btn:SetBackdropBorderColor(0.25, 0.25, 0.25, 1) - - -- Text label (left-aligned) - local text = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlight") - text:SetPoint("LEFT", 8, 0) - text:SetPoint("RIGHT", -24, 0) - text:SetJustifyH("LEFT") - btn.label = text - - -- Gold arrow icon (right side) - local arrow = btn:CreateTexture(nil, "OVERLAY") - arrow:SetSize(12, 12) - arrow:SetPoint("RIGHT", -6, 0) - arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow") - arrow:SetVertexColor(0.9, 0.75, 0.2, 1) - btn.arrow = arrow - - -- Hover highlight - btn:SetScript("OnEnter", function(self) - self:SetBackdropColor(0.18, 0.18, 0.18, 1) - end) - btn:SetScript("OnLeave", function(self) - self:SetBackdropColor(0.12, 0.12, 0.12, 1) - end) - - -- Menu storage - btn.menuSetup = nil - btn:SetScript("OnClick", function(self) - if self.menuSetup then - MenuUtil.CreateContextMenu(self, self.menuSetup) - end - end) - - function btn:SetupMenu(setupFunc) - self.menuSetup = setupFunc - end - - return btn -end - --- ─── Helper: Create a flat dark button matching the blocky design ──────────── --- Shared globally so other files (UI.lua) can reuse it. - -function AltSystem.CreateFlatButton(name, parent, width, height, text) - local btn = CreateFrame("Button", name, parent, "BackdropTemplate") - btn:SetSize(width, height) - btn:SetBackdrop({ - bgFile = "Interface\\ChatFrame\\ChatFrameBackground", - edgeFile = "Interface\\ChatFrame\\ChatFrameBackground", - edgeSize = 1, - }) - btn:SetBackdropColor(0.4, 0.08, 0.08, 0.9) - btn:SetBackdropBorderColor(0.25, 0.25, 0.25, 1) - - local label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal") - label:SetPoint("CENTER") - label:SetText(text or "") - btn.label = label - - btn:SetScript("OnEnter", function(self) - self:SetBackdropColor(0.55, 0.12, 0.12, 0.9) - end) - btn:SetScript("OnLeave", function(self) - self:SetBackdropColor(0.4, 0.08, 0.08, 0.9) - end) - - function btn:SetText(newText) - self.label:SetText(newText) - end - - function btn:GetText() - return self.label:GetText() - end - - return btn -end - --- ─── Create a single skill row frame ──────────────────────────────────────── - -local function CreateSkillRowFrame(index) - local row = CreateFrame("Frame", "AltSystemSkillRow" .. index, scrollChild) - row:SetHeight(ROW_HEIGHT) - - -- Name EditBox (flat dark style) - local nameContainer, nameBox = CreateFlatEditBox("AltSystemSkillName" .. index, row, NAME_WIDTH) - nameContainer:SetPoint("LEFT", row, "LEFT", 8, 0) - row.nameBox = nameBox - row.nameContainer = nameContainer - - -- Level Dropdown (flat dark style) - local levelDropdown = AltSystem.CreateFlatDropdown("AltSystemSkillLevel" .. index, row, LEVEL_WIDTH) - levelDropdown:SetPoint("LEFT", nameContainer, "RIGHT", 12, 0) - row.levelDropdown = levelDropdown - - -- Value Dropdown (flat dark style) - local valueDropdown = AltSystem.CreateFlatDropdown("AltSystemSkillValue" .. index, row, VALUE_WIDTH) - valueDropdown:SetPoint("LEFT", levelDropdown, "RIGHT", 8, 0) - row.valueDropdown = valueDropdown - - -- Delete Button (trash can icon with dark red background, matching design) - local deleteBtn = CreateFrame("Button", "AltSystemSkillDelete" .. index, row) - deleteBtn:SetSize(DELETE_WIDTH, DELETE_WIDTH) - deleteBtn:SetPoint("LEFT", valueDropdown, "RIGHT", 8, 0) - - - local deleteIcon = deleteBtn:CreateTexture(nil, "ARTWORK") - deleteIcon:SetSize(DELETE_WIDTH - 4, DELETE_WIDTH - 4) - deleteIcon:SetPoint("CENTER") - deleteIcon:SetTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up") - deleteBtn.icon = deleteIcon - - -- Shimmer animation on hover - local shimmer = deleteBtn:CreateTexture(nil, "OVERLAY") - shimmer:SetSize(DELETE_WIDTH - 4, DELETE_WIDTH - 4) - shimmer:SetPoint("CENTER") - shimmer:SetTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up") - shimmer:SetBlendMode("ADD") - shimmer:SetAlpha(0) - deleteBtn.shimmer = shimmer - - local shimmerAnim = shimmer:CreateAnimationGroup() - shimmerAnim:SetLooping("REPEAT") - local fadeIn = shimmerAnim:CreateAnimation("Alpha") - fadeIn:SetFromAlpha(0) - fadeIn:SetToAlpha(0.5) - fadeIn:SetDuration(0.5) - fadeIn:SetOrder(1) - local fadeOut = shimmerAnim:CreateAnimation("Alpha") - fadeOut:SetFromAlpha(0.5) - fadeOut:SetToAlpha(0) - fadeOut:SetDuration(0.5) - fadeOut:SetOrder(2) - deleteBtn.shimmerAnim = shimmerAnim - - deleteBtn:SetScript("OnEnter", function(self) - self.shimmer:SetAlpha(0) - self.shimmerAnim:Play() - end) - deleteBtn:SetScript("OnLeave", function(self) - self.shimmerAnim:Stop() - self.shimmer:SetAlpha(0) - end) - - row.deleteBtn = deleteBtn - - return row -end - --- ─── Refresh all skill rows ───────────────────────────────────────────────── - -local function RefreshSkillRows() - -- Hide all existing row frames - for _, row in ipairs(skillRowFrames) do - row:Hide() - end - - local yPos = 0 - - for i, skillData in ipairs(editableSkills) do - local row = skillRowFrames[i] - if not row then - row = CreateSkillRowFrame(i) - skillRowFrames[i] = row - end - - -- Clear previous anchor points before repositioning - row:ClearAllPoints() - row:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -yPos) - row:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", 0, -yPos) - - -- Populate name - row.nameBox:SetText(skillData.name or "") - row.nameBox:SetScript("OnTextChanged", function(self) - local idx = nil - for j, s in ipairs(editableSkills) do - if skillRowFrames[j] == row then - idx = j - break - end - end - if idx then - editableSkills[idx].name = self:GetText() - end - end) - - -- Setup level dropdown - local currentRowIndex = i - row.levelDropdown.label:SetText(skillData.level or "Novice") - row.levelDropdown:SetupMenu(function(owner, rootDescription) - for _, levelName in ipairs(AltSystem.Data.SkillLevelOrder) do - rootDescription:CreateRadio( - levelName, - function() - return editableSkills[currentRowIndex] and editableSkills[currentRowIndex].level == levelName - end, - function() - if editableSkills[currentRowIndex] then - editableSkills[currentRowIndex].level = levelName - editableSkills[currentRowIndex].value = AltSystem.Data:GetDefaultValueForLevel(levelName) - RefreshSkillRows() - end - end - ) - end - end) - - -- Setup value dropdown based on current level - row.valueDropdown.label:SetText(tostring(skillData.value or 1)) - local range = AltSystem.Data.SkillValueRanges[skillData.level] - if range then - row.valueDropdown:SetupMenu(function(owner, rootDescription) - for v = range.min, range.max do - rootDescription:CreateRadio( - tostring(v), - function() - return editableSkills[currentRowIndex] and editableSkills[currentRowIndex].value == v - end, - function() - if editableSkills[currentRowIndex] then - editableSkills[currentRowIndex].value = v - row.valueDropdown.label:SetText(tostring(v)) - end - end - ) - end - end) - end - - -- Bind delete button for current index - row.deleteBtn:SetScript("OnClick", function() - table.remove(editableSkills, currentRowIndex) - RefreshSkillRows() - end) - - row:Show() - yPos = yPos + ROW_HEIGHT + ROW_SPACING - end - - -- Reposition Add A Row button - if addRowButton then - addRowButton:ClearAllPoints() - addRowButton:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", -PADDING, -(yPos + 4)) - end - - -- Update scroll child height - local totalHeight = yPos + ROW_HEIGHT + 20 -- extra space for Add A Row button - if scrollChild then - scrollChild:SetHeight(math.max(totalHeight, 1)) - end -end - --- ─── Create Build Skills Content ──────────────────────────────────────────── - -function AltSystem:CreateBuildSkillsContent(parentFrame) - local contentWidth = parentFrame:GetWidth() or 692 - local contentHeight = parentFrame:GetHeight() or 444 - - local yPos = -PADDING - - -- Info text paragraph 1 - local info1 = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - info1:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos) - info1:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -PADDING, yPos) - info1:SetJustifyH("LEFT") - info1:SetText("All skills below are directly extracted from your TRP's characteristics sheet. You can view and edit them there at any time.") - info1:SetTextColor(0.9, 0.75, 0.2) - info1:SetWordWrap(true) - - yPos = yPos - (info1:GetStringHeight() or 16) - 10 - - -- Info text paragraph 2 - local info2 = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - info2:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos) - info2:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -PADDING, yPos) - info2:SetJustifyH("LEFT") - info2:SetText("This menu serves as an easy alternative for if you want to use this system fast without diving deep into understanding it and/or styling your TRP sheet at this point in time. All your changes made will not be saved to TRP up until you hit the specific button to do so.") - info2:SetTextColor(0.9, 0.75, 0.2) - info2:SetWordWrap(true) - - yPos = yPos - (info2:GetStringHeight() or 32) - 16 - - -- "Skill list" section header - local sectionHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") - sectionHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos) - sectionHeader:SetText("Skill list") - sectionHeader:SetTextColor(1, 1, 1) - - yPos = yPos - 24 - - -- Column headers - local nameHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - nameHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING + 8, yPos) - nameHeader:SetText("Name") - nameHeader:SetTextColor(0.9, 0.75, 0.2) - - local levelHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - levelHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING + 8 + NAME_WIDTH + 12, yPos) - levelHeader:SetText("Level") - levelHeader:SetTextColor(0.9, 0.75, 0.2) - - local valueHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - valueHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING + 8 + NAME_WIDTH + 12 + LEVEL_WIDTH + 8, yPos) - valueHeader:SetText("Value") - valueHeader:SetTextColor(0.9, 0.75, 0.2) - - yPos = yPos - 20 - - -- Save button (pinned to bottom, outside scroll frame) - local saveButton = AltSystem.CreateFlatButton("AltSystemSaveSkillsButton", parentFrame, 180, 30, "Save Skills to TRP") - saveButton:SetPoint("BOTTOM", parentFrame, "BOTTOM", 0, PADDING) - - saveButton:SetScript("OnClick", function() - local success = AltSystem.Data:SaveSkills(editableSkills) - if success and AltSystem.RefreshSkillDropdown then - AltSystem:RefreshSkillDropdown() - end - end) - - -- Scrollable skill list area (between column headers and save button) - scrollFrame = CreateFrame("ScrollFrame", "AltSystemBuildSkillsScrollFrame", parentFrame, "UIPanelScrollFrameTemplate") - scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos) - scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -PADDING - 22, saveButton:GetHeight() + PADDING + 8) - - scrollChild = CreateFrame("Frame", "AltSystemBuildSkillsScrollChild", scrollFrame) - scrollChild:SetWidth(scrollFrame:GetWidth() or (contentWidth - PADDING * 2 - 22)) - scrollChild:SetHeight(1) - scrollFrame:SetScrollChild(scrollChild) - - -- Update scroll child width when frame resizes - scrollFrame:SetScript("OnSizeChanged", function(self) - scrollChild:SetWidth(self:GetWidth()) - end) - - -- "Add A Row" button (inside scroll child, below last row) - addRowButton = CreateFrame("Button", "AltSystemAddRowButton", scrollChild, "UIPanelButtonTemplate") - addRowButton:SetSize(130, 26) - addRowButton:SetText("+ Add A Row") - addRowButton:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", -PADDING, 0) - - -- Style add row button - local addBg = addRowButton:CreateTexture(nil, "BACKGROUND") - addBg:SetAllPoints() - addBg:SetColorTexture(0.4, 0.08, 0.08, 0.9) - - addRowButton:SetScript("OnClick", function() - table.insert(editableSkills, { - name = "Skillname", - level = "Novice", - value = 1, - icon = "inv_misc_questionmark", - isNew = true, - }) - RefreshSkillRows() - - -- Auto-scroll to bottom to show the new row - C_Timer.After(0.05, function() - if scrollFrame then - scrollFrame:SetVerticalScroll(scrollFrame:GetVerticalScrollRange()) - end - end) - end) - - AltSystem.BuildSkillsScrollFrame = scrollFrame - AltSystem.BuildSkillsScrollChild = scrollChild -end - --- ─── Refresh Build Skills list (called on tab switch) ─────────────────────── - -function AltSystem:RefreshBuildSkillsList() - -- Reload skills from TRP3 into working copy, discarding any unsaved edits - editableSkills = AltSystem.Data:GetEditableSkills() - RefreshSkillRows() -end diff --git a/Core.lua b/Core.lua old mode 100755 new mode 100644 diff --git a/Data.lua b/Data.lua old mode 100755 new mode 100644 index 6706c49..d1e5f56 --- a/Data.lua +++ b/Data.lua @@ -39,27 +39,6 @@ local SKILL_KEYWORD_RANGES = { ["Master"] = { min = 20, max = 20 }, } --- Shared lookup tables for the Build Skills tab -AltSystem.Data.SkillValueRanges = { - ["Inept"] = { min = 0, max = 0 }, - ["Novice"] = { min = 1, max = 5 }, - ["Adept"] = { min = 6, max = 10 }, - ["Expert"] = { min = 11, max = 19 }, - ["Master"] = { min = 20, max = 20 }, -} - --- Ordered list of skill levels for dropdown display -AltSystem.Data.SkillLevelOrder = { "Inept", "Novice", "Adept", "Expert", "Master" } - --- Returns the minimum value for a given skill level -function AltSystem.Data:GetDefaultValueForLevel(level) - local range = AltSystem.Data.SkillValueRanges[level] - if range then - return range.min - end - return 1 -end - -- Check if the trait's right text field contains a valid skill keyword. -- Returns the matched keyword if found, nil otherwise. local function FindSkillKeyword(rightText) @@ -155,94 +134,6 @@ function AltSystem.Data:RefreshSkills() return skills end --- Returns an array of {name, level, value, icon} for each valid skill trait in the TRP3 profile. --- Excludes Base Roll and Unskilled (system-generated). Sorted by level order (Inept first, Master last). -function AltSystem.Data:GetEditableSkills() - local skills = {} - - if TRP3_API and TRP3_API.profile and TRP3_API.profile.getData then - local ok, characteristics = pcall(TRP3_API.profile.getData, "player/characteristics") - if ok and characteristics and characteristics.PS then - for _, trait in ipairs(characteristics.PS) do - local skillName = trait.LT - local numericValue = trait.V2 or 0 - local keyword = FindSkillKeyword(trait.RT) - if skillName and skillName ~= "" and keyword then - table.insert(skills, { - name = skillName, - level = keyword, - value = numericValue, - icon = trait.IC or "inv_misc_questionmark", - }) - end - end - end - end - - -- Sort by level order descending (Master first, Inept last) - local levelOrderMap = {} - for i, lvl in ipairs(AltSystem.Data.SkillLevelOrder) do - levelOrderMap[lvl] = i - end - table.sort(skills, function(a, b) - return (levelOrderMap[a.level] or 99) > (levelOrderMap[b.level] or 99) - end) - - return skills -end - --- Saves the edited skills back to the TRP3 profile. --- editedSkills is an array of {name, level, value, icon}. -function AltSystem.Data:SaveSkills(editedSkills) - if not TRP3_API or not TRP3_API.profile or not TRP3_API.profile.getData then - print("|cFFFF0000AltSystem:|r TRP3 API is unavailable. Cannot save skills.") - return false - end - - local ok, characteristics = pcall(TRP3_API.profile.getData, "player/characteristics") - if not ok or not characteristics then - print("|cFFFF0000AltSystem:|r Could not access TRP3 profile characteristics. Cannot save skills.") - return false - end - - -- Ensure PS array exists - if not characteristics.PS then - characteristics.PS = {} - end - - -- Separate non-skill traits from skill traits in the existing PS - local nonSkillTraits = {} - for _, trait in ipairs(characteristics.PS) do - local keyword = FindSkillKeyword(trait.RT) - if not keyword then - -- This is not a skill trait, preserve it - table.insert(nonSkillTraits, trait) - end - end - - -- Rebuild PS: non-skill traits first, then edited skills - local newPS = {} - for _, trait in ipairs(nonSkillTraits) do - table.insert(newPS, trait) - end - for _, skill in ipairs(editedSkills) do - table.insert(newPS, { - LT = skill.name, - RT = skill.level, - V2 = skill.value, - IC = skill.icon or "inv_misc_questionmark", - }) - end - - characteristics.PS = newPS - - -- Refresh the Use Skills tab data - AltSystem.Data:RefreshSkills() - - print("|cFF00FF00AltSystem:|r Skills saved to TRP profile successfully.") - return true -end - -- Initialize with the default skill list AltSystem.Data.Skills = {} for _, skill in ipairs(DEFAULT_SKILLS) do @@ -252,8 +143,8 @@ end -- Item options: name and modifier (first entry = no item) AltSystem.Data.Items = { { name = "No item", modifier = 0 }, - { name = "Rare", modifier = 3 }, - { name = "Epic", modifier = 5 }, + { name = "Rare item", modifier = 3 }, + { name = "Epic item", modifier = 5 }, } -- Defense / Armor options: name and modifier diff --git a/README.md b/README.md old mode 100755 new mode 100644 diff --git a/Roll.lua b/Roll.lua old mode 100755 new mode 100644 diff --git a/UI.lua b/UI.lua old mode 100755 new mode 100644 index bfc7013..0b989bb --- a/UI.lua +++ b/UI.lua @@ -29,7 +29,7 @@ local function BuildSkillOptions() return options end --- Helper: Create a flat dark dropdown with optional label (reuses shared CreateFlatDropdown) +-- 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) @@ -44,32 +44,28 @@ local function CreateDropdown(parent, name, labelText, options, defaultIndex, on local selectedIndex = defaultIndex or 1 - local dropdown = AltSystem.CreateFlatDropdown(name, container, 190) + 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) - -- Set initial label text - if options[selectedIndex] then - dropdown.label:SetText(options[selectedIndex].text) - end - - dropdown:SetupMenu(function(owner, rootDescription) + dropdown:SetupMenu(function(dropdown, rootDescription) for i, option in ipairs(options) do rootDescription:CreateRadio( option.text, - function() - return i == selectedIndex + function(data) + return data == selectedIndex end, - function() - selectedIndex = i - dropdown.label:SetText(option.text) + function(data) + selectedIndex = data if onSelect then - onSelect(i, option) + onSelect(data, options[data]) end - end + end, + i ) end end) @@ -78,52 +74,30 @@ local function CreateDropdown(parent, name, labelText, options, defaultIndex, on return selectedIndex end, function(idx) selectedIndex = idx - if options[idx] then - dropdown.label:SetText(options[idx].text) - end end end --- Helper: Create a custom flat radio button (gold circle when selected, grey when not) +-- Helper: Create a radio button (CheckButton with radio texture) local function CreateRadioButton(parent, name, text, x, y, isChecked, onClick) - local size = 20 - local btn = CreateFrame("CheckButton", name, parent) - btn:SetSize(size, size) - btn:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) + local radio = CreateFrame("CheckButton", name, parent, "UIRadioButtonTemplate") + radio:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) + radio:SetChecked(isChecked) - -- Background (grey when unselected, yellow when selected) — circular via mask - local bg = btn:CreateTexture(nil, "BACKGROUND") - bg:SetAllPoints() - bg:SetColorTexture(0.3, 0.3, 0.3, 1) - local mask = btn:CreateMaskTexture() - mask:SetAllPoints() - mask:SetTexture("Interface\\CharacterFrame\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE") - bg:AddMaskTexture(mask) - - btn.checkTex = nil -- unused, kept for compatibility - - local function UpdateVisual() - if btn:GetChecked() then - bg:SetColorTexture(0.9, 0.75, 0.2, 1) - else - bg:SetColorTexture(0.3, 0.3, 0.3, 1) - end + 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 - btn:SetChecked(isChecked) - UpdateVisual() - - local label = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlight") - label:SetPoint("LEFT", btn, "RIGHT", 6, 0) - label:SetText(text) - - btn:SetScript("OnClick", function(self) + radio:SetScript("OnClick", function(self) onClick(self) - UpdateVisual() end) - btn.UpdateVisual = UpdateVisual - return btn + return radio end -- Helper: Create a section header (golden text) @@ -210,8 +184,9 @@ function AltSystem:CreateMainFrame() buildSkillsContent:SetSize(contentWidth, tabContentHeight) buildSkillsContent:Hide() - -- Build Skills tab content (created in BuildSkillsUI.lua) - AltSystem:CreateBuildSkillsContent(buildSkillsContent) + local buildPlaceholder = buildSkillsContent:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + buildPlaceholder:SetPoint("CENTER") + buildPlaceholder:SetText("Coming soon") -- Tab switching logic local function SelectTab(tabIndex) @@ -225,7 +200,6 @@ function AltSystem:CreateMainFrame() buildSkillsContent:Show() tabUseSkillsBg:SetColorTexture(0.3, 0.3, 0.3, 1) tabBuildSkillsBg:SetColorTexture(0.15, 0.15, 0.15, 1) - AltSystem:RefreshBuildSkillsList() end end @@ -261,8 +235,6 @@ function AltSystem:CreateMainFrame() AltSystem.State.rollType = rollType attackRadio:SetChecked(rollType == "attack") defenseRadio:SetChecked(rollType == "defense") - attackRadio.UpdateVisual() - defenseRadio.UpdateVisual() -- Update roll button text if AltSystem.RollButton then local label = rollType == "attack" and "Roll Attack" or "Roll Defense" @@ -351,7 +323,6 @@ function AltSystem:CreateMainFrame() AltSystem.State.selectedDefenseIndex = index for i, radio in ipairs(armorRadios) do radio:SetChecked(i == index) - radio.UpdateVisual() end end @@ -372,86 +343,6 @@ function AltSystem:CreateMainFrame() yPos = yPos - ROW_HEIGHT - SECTION_GAP - -- Section: Modifiers (optional) - CreateSectionHeader(content, "Modifiers (optional)", PADDING, yPos) - yPos = yPos - 28 - --CreateSubLabel(content, "Label", PADDING, yPos) - --yPos = yPos - 22 - - -- Shield checkbox (flat square toggle) - local shieldCheck = CreateFrame("CheckButton", "AltSystemShieldCheck", content) - shieldCheck:SetSize(20, 20) - shieldCheck:SetPoint("TOPLEFT", content, "TOPLEFT", PADDING, yPos) - shieldCheck:SetChecked(AltSystem.State.shieldEnabled) - - local shieldBg = shieldCheck:CreateTexture(nil, "BACKGROUND") - shieldBg:SetAllPoints() - shieldBg:SetColorTexture(0.3, 0.3, 0.3, 1) - - local shieldCheckMark = shieldCheck:CreateTexture(nil, "ARTWORK") - shieldCheckMark:SetSize(14, 14) - shieldCheckMark:SetPoint("CENTER") - shieldCheckMark:SetTexture("Interface\\RAIDFRAME\\ReadyCheck-Ready") - shieldCheckMark:SetVertexColor(0.9, 0.75, 0.2, 1) - - local function UpdateShieldVisual() - if shieldCheck:GetChecked() then - shieldCheckMark:Show() - shieldBg:SetColorTexture(0.25, 0.25, 0.25, 1) - else - shieldCheckMark:Hide() - shieldBg:SetColorTexture(0.3, 0.3, 0.3, 1) - end - end - UpdateShieldVisual() - - local shieldLabel = shieldCheck:CreateFontString(nil, "OVERLAY", "GameFontHighlight") - shieldLabel:SetPoint("LEFT", shieldCheck, "RIGHT", 6, 0) - shieldLabel:SetText("Shield (+ 1)") - - shieldCheck:SetScript("OnClick", function(self) - AltSystem.State.shieldEnabled = self:GetChecked() - UpdateShieldVisual() - end) - - -- Pet checkbox (flat square toggle) - local petCheck = CreateFrame("CheckButton", "AltSystemPetSummonCheck", content) - petCheck:SetSize(20, 20) - petCheck:SetPoint("LEFT", shieldCheck, "RIGHT", 80, 0) - petCheck:SetChecked(AltSystem.State.petSummonEnabled) - - local petBg = petCheck:CreateTexture(nil, "BACKGROUND") - petBg:SetAllPoints() - petBg:SetColorTexture(0.3, 0.3, 0.3, 1) - - local petCheckMark = petCheck:CreateTexture(nil, "ARTWORK") - petCheckMark:SetSize(14, 14) - petCheckMark:SetPoint("CENTER") - petCheckMark:SetTexture("Interface\\RAIDFRAME\\ReadyCheck-Ready") - petCheckMark:SetVertexColor(0.9, 0.75, 0.2, 1) - - local function UpdatePetVisual() - if petCheck:GetChecked() then - petCheckMark:Show() - petBg:SetColorTexture(0.25, 0.25, 0.25, 1) - else - petCheckMark:Hide() - petBg:SetColorTexture(0.3, 0.3, 0.3, 1) - end - end - UpdatePetVisual() - - local petLabel = petCheck:CreateFontString(nil, "OVERLAY", "GameFontHighlight") - petLabel:SetPoint("LEFT", petCheck, "RIGHT", 6, 0) - petLabel:SetText("Pet (+d5)") - - petCheck:SetScript("OnClick", function(self) - AltSystem.State.petSummonEnabled = self:GetChecked() - UpdatePetVisual() - end) - - yPos = yPos - ROW_HEIGHT - SECTION_GAP - -- Item label CreateSubLabel(content, "Item", PADDING, yPos) yPos = yPos - 20 @@ -463,7 +354,6 @@ function AltSystem:CreateMainFrame() AltSystem.State.selectedItemIndex = index for i, radio in ipairs(itemRadios) do radio:SetChecked(i == index) - radio.UpdateVisual() end end @@ -484,6 +374,50 @@ function AltSystem:CreateMainFrame() 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 @@ -514,9 +448,11 @@ function AltSystem:CreateMainFrame() yPos = yPos - ROW_HEIGHT - SECTION_GAP -- Roll button - local rollLabel = AltSystem.State.rollType == "attack" and "Roll Attack" or "Roll Defense" - local rollBtn = AltSystem.CreateFlatButton("AltSystemRollBtn", content, CONTROLS_WIDTH - PADDING * 2, 32, rollLabel) + 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) @@ -637,28 +573,24 @@ function AltSystem:RefreshSkillDropdown() -- Rebuild the dropdown menu with the new skill list if AltSystem.SkillDropdown then local skillOptions = BuildSkillOptions() - -- Update the displayed label text - if skillOptions[newIndex] then - AltSystem.SkillDropdown.label:SetText(skillOptions[newIndex].text) - end - AltSystem.SkillDropdown:SetupMenu(function(owner, rootDescription) + AltSystem.SkillDropdown:SetupMenu(function(dropdown, rootDescription) for i, option in ipairs(skillOptions) do rootDescription:CreateRadio( option.text, - function() - return i == AltSystem.State.selectedSkillIndex + function(data) + return data == AltSystem.State.selectedSkillIndex end, - function() - AltSystem.State.selectedSkillIndex = i - AltSystem.State.selectedSkillName = AltSystem.Data.Skills[i] and AltSystem.Data.Skills[i].name or nil - AltSystem.SkillDropdown.label:SetText(option.text) + 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(i) + AltSystem.SetSkillIndex(data) end if AltSystem.UpdateSkillWarning then - AltSystem.UpdateSkillWarning(i) + AltSystem.UpdateSkillWarning(data) end - end + end, + i ) end end) diff --git a/docs/1-interface.md b/docs/1-interface.md old mode 100755 new mode 100644 diff --git a/docs/2-skills.md b/docs/2-skills.md old mode 100755 new mode 100644 diff --git a/docs/3-announce.md b/docs/3-announce.md old mode 100755 new mode 100644 diff --git a/docs/4-redesign.md b/docs/4-redesign.md old mode 100755 new mode 100644 diff --git a/docs/5-build_skills.md b/docs/5-build_skills.md deleted file mode 100755 index 9b76326..0000000 --- a/docs/5-build_skills.md +++ /dev/null @@ -1,132 +0,0 @@ -# Feature: Build Skills tab -The second tab of the addon will follow these [designs](./build_skills_tab_design.png). - -## Acceptance Criteria -- This screen should show the same skills we use in the main screen, which come from the TRP profile -- The skills should be sorted by level -- The skill list should be scrollable, with the "Save" button pinned/sticky to the bottom -## Editing skills -- The user should be able to edit the name, level, and numerical score of each skill -- Edits should not be saved until the user explicitly clicks the "Save" button -### Skill Level and Value -- When a skill level is selected, the numerical score dropdown should update to only allow values within the skill level - - Inept: 0 - - Novice: 1-5 - - Adept: 6-10 - - Expert: 11-19 - - Master: 20 -### Deleting skills -- Clicking the "Delete" button should remove the skill from the list -### Adding Skills -- Clicking the "Add a Row" button should add a new skill row to the list -- Default values for the new skill should be: - - Name: Skillname - - Level: Novice - - Value: 1 -### Saving Skills -- When clicking the "Save" button: - - Newly added skills should be added to the TRP profile - - Existing skills should be updated in the TRP profile - - Skills that were deleted should also be removed from the TRP profile -- Icons should not be changed -## References -- Refer to [Data.lua](../Data.lua) for details on how we currently fetch the skills from TRP -- Refer to the TRP3 source code in case it's necessary [here](https://github.com/Total-RP/Total-RP-3) - ---- - -## Implementation Plan - -### 1. Add a `SaveSkills` function to Data.lua -- Create `AltSystem.Data:SaveSkills(editedSkills)` that writes skills back to the TRP3 profile -- Access the TRP3 profile via `TRP3_API.profile.getData("player/characteristics")` to get the `characteristics.PS` (personality traits) array -- For each edited skill, update or insert entries in `PS`: - - `LT` = skill name - - `RT` = level keyword (e.g. "Novice", "Adept", etc.) - - `V2` = numeric value (0–20) - - `IC` = preserve existing icon (do not change); for new skills, use a sensible default icon (e.g. `"inv_misc_questionmark"`) -- Remove any PS entries that were deleted by the user -- After writing, call `TRP3_API.dashboard.showCharacteristics()` or fire the appropriate TRP3 event if needed to refresh TRP3's own UI -- **Edge case:** If the TRP3 API is unavailable, show a warning message and abort save - -### 2. Extract skill-level constants into shared lookup tables in Data.lua -- The acceptance criteria defines value ranges per level (Inept: 0, Novice: 1–5, Adept: 6–10, Expert: 11–19, Master: 20) -- `SKILL_KEYWORD_RANGES` already exists but excludes Inept/Master min-max correctly for the Build tab's needs; extend or create a new table `AltSystem.Data.SkillValueRanges` that is accessible from UI.lua: - ``` - { Inept = {min=0, max=0}, Novice = {min=1, max=5}, Adept = {min=6, max=10}, Expert = {min=11, max=19}, Master = {min=20, max=20} } - ``` -- Create `AltSystem.Data.SkillLevelOrder` — an ordered array `{"Inept", "Novice", "Adept", "Expert", "Master"}` for populating the level dropdown in display order -- Create a helper `AltSystem.Data:GetDefaultValueForLevel(level)` that returns the minimum value for that level (used when the user changes level to auto-set the value) - -### 3. Add a function to read raw skills (with numeric values) from TRP3 in Data.lua -- Currently `RefreshSkills()` converts TRP3 traits into `{name, level, modifier}` — the Build tab needs the raw **numeric value** and the **icon** as well -- Create `AltSystem.Data:GetEditableSkills()` that returns an array of `{name, level, value, icon}` for each valid skill trait in the TRP3 profile (excluding Base Roll and Unskilled, which are system-generated entries) -- Sort the returned skills by level using `AltSystem.Data.SkillLevelOrder` ordering (Inept first, Master last) — matching the acceptance criteria "sorted by level" -- Reuse existing helpers `FindSkillKeyword` and `ParseSkillLevel` (promote them from local to module-level if needed, or call internally) - -### 4. Build the Build Skills tab UI (new file: BuildSkillsUI.lua) -- Create a new file to keep UI.lua manageable; register it in `AltSystem.toc` between `UI.lua` and `Roll.lua` -- Create `AltSystem:CreateBuildSkillsContent(parentFrame)` called from `CreateMainFrame` in UI.lua (replacing the placeholder) -- **Layout structure:** - - **Info text** at top — two golden/yellow paragraphs explaining that skills come from TRP (matches mockup) - - **"Skill list" section header** - - **Column headers**: Name, Level, Value (bold golden text) - - **Scrollable skill list** — a `ScrollFrame` containing dynamically created skill rows - - **"Add A Row" button** — anchored below the last skill row, inside the scroll child - - **"Save Skills to TRP" button** — pinned/sticky at the bottom of the tab, outside the scroll frame - -### 5. Implement editable skill rows -- Each skill row is a frame containing: - - **Name**: `EditBox` (text input) — pre-filled with current skill name - - **Level**: `DropdownButton` (WowStyle1DropdownTemplate) — options: Inept, Novice, Adept, Expert, Master - - **Value**: `DropdownButton` — options dynamically generated based on selected level (e.g. Novice → 1,2,3,4,5) - - **Delete button**: A button with a trash-can icon/red texture that removes the row -- Store all row data in a local working copy array (`editableSkills`), not directly in `AltSystem.Data.Skills` -- When the **level dropdown** changes: - - Update the value dropdown options to only show valid values for the new level - - Auto-set the value to the minimum for that level (e.g. switching to Adept → value becomes 6) -- **Row management:** - - `CreateSkillRow(parent, index, skillData)` — creates or recycles a row frame - - `RefreshSkillRows()` — rebuilds/repositions all rows and updates scroll child height - - Deleting a row removes it from `editableSkills` and calls `RefreshSkillRows()` - -### 6. Implement "Add A Row" functionality -- Clicking "Add A Row" inserts a new entry into `editableSkills`: - - `{ name = "Skillname", level = "Novice", value = 1, icon = "inv_misc_questionmark", isNew = true }` -- Calls `RefreshSkillRows()` to render the new row -- The scroll frame should auto-scroll to show the new row - -### 7. Implement "Save Skills to TRP" functionality -- On click, call `AltSystem.Data:SaveSkills(editableSkills)` which: - 1. Reads current `characteristics.PS` from TRP3 - 2. Rebuilds the PS array: keeps non-skill traits untouched, updates/adds/removes skill traits based on `editableSkills` - 3. Writes the updated PS back to the TRP3 profile data - 4. Calls `RefreshSkills()` so the Use Skills tab dropdown reflects the changes immediately -- Show a confirmation message (print to chat or a brief on-screen text) on successful save -- **Edge cases:** - - Empty skill list: allowed — just remove all skill traits from PS - - Duplicate skill names: allowed (TRP3 doesn't enforce uniqueness) - - Unsaved changes + tab switch: no confirmation dialog required (per spec, changes are just lost) - -### 8. Wire up tab switching to populate Build Skills tab -- In `SelectTab(2)` (UI.lua), call `AltSystem:RefreshBuildSkillsList()` to reload skills from TRP3 into the working copy -- This ensures the Build tab always shows the latest TRP3 data when opened, and any unsaved edits are discarded on tab switch - -### 9. Update AltSystem.toc -- Add `BuildSkillsUI.lua` to the file list (after `UI.lua`, before `Roll.lua`) - -### 10. Testing checklist -- [ ] Build Skills tab shows skills from TRP3 profile, sorted by level -- [ ] Skill name is editable via text input -- [ ] Level dropdown shows all 5 levels; changing level updates value dropdown options and auto-selects minimum value -- [ ] Value dropdown only shows values valid for the current level -- [ ] Delete button removes the row immediately -- [ ] "Add A Row" adds a row with defaults (Skillname, Novice, 1) -- [ ] Skill list scrolls when rows exceed visible area -- [ ] "Save" button is always visible (pinned to bottom) -- [ ] Save writes correct data to TRP3 profile (LT, RT, V2, IC preserved) -- [ ] Save does not modify icons of existing skills -- [ ] After save, Use Skills tab dropdown reflects the updated skills -- [ ] Switching tabs discards unsaved changes and reloads from TRP3 -- [ ] Works correctly with 0 skills (empty profile) -- [ ] Works correctly with many skills (20+) — scroll behavior \ No newline at end of file diff --git a/docs/Changelog.md b/docs/Changelog.md old mode 100755 new mode 100644 diff --git a/docs/build_skills_tab_design.png b/docs/build_skills_tab_design.png deleted file mode 100755 index ddbfdd3..0000000 Binary files a/docs/build_skills_tab_design.png and /dev/null differ diff --git a/docs/roll_tab_design.png b/docs/roll_tab_design.png old mode 100755 new mode 100644 index 3ebb626..5754915 Binary files a/docs/roll_tab_design.png and b/docs/roll_tab_design.png differ