From 9e629aec31ea74079032b4eab464b566b3332b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Correia?= Date: Sat, 11 Apr 2026 19:38:19 +0100 Subject: [PATCH] Skills from TRP profile --- .idea/vcs.xml | 6 ++++ Data.lua | 92 ++++++++++++++++++++++++++++++++++++++++++------ UI.lua | 75 ++++++++++++++++++++++++++++++++------- docs/2-skills.md | 12 ++++++- 4 files changed, 161 insertions(+), 24 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Data.lua b/Data.lua index ab7984a..1291929 100644 --- a/Data.lua +++ b/Data.lua @@ -1,5 +1,5 @@ -- AltSystem Data Definitions --- Contains dummy skill data, item options, defense options, and modifier constants. +-- Contains skill data (fetched from TRP3 profile), item options, defense options, and modifier constants. AltSystem = AltSystem or {} AltSystem.Data = {} @@ -12,18 +12,88 @@ AltSystem.Data.SkillLevels = { ["Master"] = 4, } --- Dummy skill data: each skill has a name, icon, and level -AltSystem.Data.Skills = { - { name = "Swordsmanship", icon = "Interface\\Icons\\INV_Sword_04", level = "Expert" }, - { name = "Archery", icon = "Interface\\Icons\\INV_Weapon_Bow_05", level = "Adept" }, - { name = "Fire Magic", icon = "Interface\\Icons\\Spell_Fire_FlameBolt", level = "Master" }, - { name = "Healing", icon = "Interface\\Icons\\Spell_Holy_FlashHeal", level = "Novice" }, - { name = "Stealth", icon = "Interface\\Icons\\Ability_Stealth", level = "Adept" }, +-- The "Unskilled" entry is always the first (default) skill +local UNSKILLED_ENTRY = { name = "Unskilled", level = "Unskilled", modifier = -4 } + +-- Default/fallback skill list used when no TRP3 profile skills are found +local DEFAULT_SKILLS = { + { name = "Unskilled", level = "Unskilled", modifier = -4 }, + { name = "Novice Skill", level = "Novice", modifier = -2 }, + { name = "Adept Skill", level = "Adept", modifier = 0 }, + { name = "Expert Skill", level = "Expert", modifier = 2 }, + { name = "Master Skill", level = "Master", modifier = 4 }, } --- Pre-compute modifier for each skill based on its level -for _, skill in ipairs(AltSystem.Data.Skills) do - skill.modifier = AltSystem.Data.SkillLevels[skill.level] or 0 +-- Parse the skill level from the right-side text of a TRP3 personality trait. +-- The level keyword must appear at the start of the string; anything after it is ignored. +-- Returns the level string and modifier, or nil if not recognized. +local function ParseSkillLevel(rightText) + if not rightText or rightText == "" then return nil, nil end + + -- Trim leading/trailing whitespace + local trimmed = rightText:match("^%s*(.-)%s*$") + if not trimmed or trimmed == "" then return nil, nil end + + -- Check if the text starts with a recognized level keyword + for _, levelName in ipairs({"Master", "Expert", "Adept", "Novice"}) do + if trimmed == levelName or trimmed:match("^" .. levelName .. "[%s%p]") then + return levelName, AltSystem.Data.SkillLevels[levelName] + end + end + + return nil, nil +end + +-- Fetch skills from the current TRP3 profile's personality traits. +-- Returns an array of skill entries, always starting with "Unskilled". +-- Falls back to the default list if no profile or no valid skills are found. +function AltSystem.Data:RefreshSkills() + local skills = {} + + -- Always add Unskilled as the first entry + table.insert(skills, { name = UNSKILLED_ENTRY.name, level = UNSKILLED_ENTRY.level, modifier = UNSKILLED_ENTRY.modifier }) + + local foundAny = false + + -- Try to read personality traits from the active TRP3 profile + 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 rightText = trait.RT + + if skillName and skillName ~= "" then + local level, modifier = ParseSkillLevel(rightText) + if level and modifier then + foundAny = true + table.insert(skills, { + name = skillName, + level = level, + modifier = modifier, + }) + end + end + end + end + end + + -- If no valid skills were found, use the default fallback list (skip first "Unskilled" since we already added it) + if not foundAny then + skills = {} + for _, skill in ipairs(DEFAULT_SKILLS) do + table.insert(skills, { name = skill.name, level = skill.level, modifier = skill.modifier }) + end + end + + AltSystem.Data.Skills = skills + return skills +end + +-- Initialize with the default skill list +AltSystem.Data.Skills = {} +for _, skill in ipairs(DEFAULT_SKILLS) do + table.insert(AltSystem.Data.Skills, { name = skill.name, level = skill.level, modifier = skill.modifier }) end -- Item options: name and modifier (first entry = no item) diff --git a/UI.lua b/UI.lua index c5c4a69..5d64ea6 100644 --- a/UI.lua +++ b/UI.lua @@ -10,6 +10,22 @@ local PADDING = 12 local ROW_HEIGHT = 30 local LABEL_WIDTH = 80 +-- 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 == "Unskilled" 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, yOffset, labelText, options, defaultIndex, onSelect) local label = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal") @@ -38,7 +54,7 @@ local function CreateDropdown(parent, name, yOffset, labelText, options, default end end) - return dropdown + return dropdown, function() return selectedIndex end, function(idx) selectedIndex = idx end end function AltSystem:CreateMainFrame() @@ -66,20 +82,19 @@ function AltSystem:CreateMainFrame() ------------------------- -- Skill dropdown ------------------------- - local skillOptions = {} - for _, skill in ipairs(AltSystem.Data.Skills) do - local levelMod = skill.modifier - local sign = levelMod >= 0 and "+" or "" - table.insert(skillOptions, { - text = skill.name .. " (" .. skill.level .. " " .. sign .. levelMod .. ")", - icon = skill.icon, - }) - end + local skillOptions = BuildSkillOptions() - CreateDropdown(f, "AltSystemSkillDropdown", yPos, "Skill:", skillOptions, AltSystem.State.selectedSkillIndex, + local skillDropdown, getSkillIndex, setSkillIndex = CreateDropdown( + f, "AltSystemSkillDropdown", yPos, "Skill:", skillOptions, + AltSystem.State.selectedSkillIndex, function(index) AltSystem.State.selectedSkillIndex = index end) + + -- Store references for refreshing + AltSystem.SkillDropdown = skillDropdown + AltSystem.GetSkillIndex = getSkillIndex + AltSystem.SetSkillIndex = setSkillIndex yPos = yPos - ROW_HEIGHT - 8 ------------------------- @@ -149,7 +164,7 @@ function AltSystem:CreateMainFrame() local attackBtn = CreateFrame("Button", "AltSystemAttackRollBtn", f, "UIPanelButtonTemplate") attackBtn:SetSize(btnWidth, 28) attackBtn:SetPoint("TOPLEFT", f, "TOPLEFT", PADDING, yPos) - attackBtn:SetText("Attack Roll") + attackBtn:SetText("Attack/Skill Roll") local defenseBtn = CreateFrame("Button", "AltSystemDefenseRollBtn", f, "UIPanelButtonTemplate") defenseBtn:SetSize(btnWidth, 28) @@ -182,9 +197,45 @@ function AltSystem:CreateMainFrame() AltSystem:PerformRoll("defense") end) + -- Refresh skills from TRP3 profile each time the window is shown + f:SetScript("OnShow", function() + AltSystem:RefreshSkillDropdown() + end) + f:Hide() AltSystem.MainFrame = f end +-- Refresh the skill dropdown with current TRP3 profile data +function AltSystem:RefreshSkillDropdown() + AltSystem.Data:RefreshSkills() + + -- Reset selection to 1 (Unskilled) since the skill list may have changed + AltSystem.State.selectedSkillIndex = 1 + if AltSystem.SetSkillIndex then + AltSystem.SetSkillIndex(1) + 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 + if AltSystem.SetSkillIndex then + AltSystem.SetSkillIndex(data) + end + end, + i + ) + end + end) + end +end + -- Create the frame on file load so it's ready when Init runs AltSystem:CreateMainFrame() diff --git a/docs/2-skills.md b/docs/2-skills.md index 9eae222..1086cb5 100644 --- a/docs/2-skills.md +++ b/docs/2-skills.md @@ -1 +1,11 @@ -# Feature: Skills \ No newline at end of file +# Feature: Skills +- The selectable skills should come from the currently active TRP profile +- The skills are defined in the TRP profile as 'Personality traits' +- For each trait: + - the left field represents the skill name + - the right field represents the skill level as a string (Novice, Adept, Expert, Master) + - should the right field have anything after the level, it is to be ignored + - should a skill have no recognizable level, it should be omitted from the list +- The list should have a default selected value of "Unskilled" which corresponds to a -4 modifier +- In case no skills are found in the profile, or no profile is selected, a default list should be displayed + - Unskilled, Novice Skill, Adept Skill, Expert Skill, Master Skill \ No newline at end of file