Files
coa-altoholic/Altoholic/Frames/Skills.lua
T
florian.berthold ec868716ed
release / release (push) Successful in 5s
coa.9: revert scale default + crash fixes + personal/realm bank + woodworking
- Revert UIScale default 1.4->1.0 (scaling only zoomed, no extra content; real
  larger layout tracked separately). Apply saved scale on open, default 1.0.
- Fix Options:Get/Set nil 'options' crash (TabOptions.lua:442).
- Guild Members: guard Level_OnClick against cleared/stale row IDs.
- Personal + Realm bank tracking ported from coa-bagnon (BANK_PERMISSIONS_PAYLOAD
  detection; personal per-char, realm per-realm; Search + BagUsage surfacing).
- Woodcutting/Woodworking columns on Skills tab (CoA custom professions).
2026-05-29 15:43:42 +02:00

436 lines
17 KiB
Lua

local addonName = ...
local addon = _G[addonName]
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local BI = LibStub("LibBabble-Inventory-3.0"):GetLookupTable()
local INFO_REALM_LINE = 0
local INFO_CHARACTER_LINE = 1
local INFO_TOTAL_LINE = 2
local WHITE = "|cFFFFFFFF"
local TEAL = "|cFF00FF9A"
local RED = "|cFFFF0000"
local ORANGE = "|cFFFF7F00"
local YELLOW = "|cFFFFFF00"
local GREEN = "|cFF00FF00"
local RECIPE_GREY = "|cFF808080"
local RECIPE_GREEN = "|cFF40C040"
local RECIPE_ORANGE = "|cFFFF8040"
local ICON_FACTION_HORDE = "Interface\\Icons\\INV_BannerPVP_01"
local ICON_FACTION_ALLIANCE = "Interface\\Icons\\INV_BannerPVP_02"
local ns = addon.TradeSkills -- ns = namespace
local Characters = addon.Characters
local size = 22
local inset = 2
function ns:Update()
local VisibleLines = 14
local frame = "AltoholicFrameSkills"
local entry = frame.."Entry"
local DS = DataStore
local offset = FauxScrollFrame_GetOffset( _G[ frame.."ScrollFrame" ] );
local DisplayedCount = 0
local VisibleCount = 0
local DrawRealm
local i=1
for _, line in pairs(Characters:GetView()) do
local lineType = Characters:GetLineType(line)
if (offset > 0) or (DisplayedCount >= VisibleLines) then -- if the line will not be visible
if lineType == INFO_REALM_LINE then -- then keep track of counters
if Characters:GetField(line, "isCollapsed") == false then
DrawRealm = true
else
DrawRealm = false
end
VisibleCount = VisibleCount + 1
offset = offset - 1 -- no further control, nevermind if it goes negative
elseif DrawRealm then
VisibleCount = VisibleCount + 1
offset = offset - 1 -- no further control, nevermind if it goes negative
end
else -- line will be displayed
if lineType == INFO_REALM_LINE then
local _, realm, account = Characters:GetInfo(line)
if Characters:GetField(line, "isCollapsed") == false then
_G[ entry..i.."Collapse" ]:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up");
DrawRealm = true
else
_G[ entry..i.."Collapse" ]:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up");
DrawRealm = false
end
_G[entry..i.."Collapse"]:Show()
_G[entry..i.."Name"]:SetWidth(300)
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 25, 0)
_G[entry..i.."NameNormalText"]:SetWidth(300)
if account == "Default" then -- saved as default, display as localized.
_G[entry..i.."NameNormalText"]:SetText(format("%s (%s".. L["Account"]..": %s%s|r)", realm, WHITE, GREEN, L["Default"]))
else
local last = addon:GetLastAccountSharingInfo(realm, account)
_G[entry..i.."NameNormalText"]:SetText(format("%s (%s".. L["Account"]..": %s%s %s%s|r)", realm, WHITE, GREEN, account, YELLOW, last or ""))
end
_G[entry..i.."Level"]:SetText("")
_G[entry..i.."Skill1NormalText"]:SetText("")
_G[entry..i.."Skill2NormalText"]:SetText("")
_G[entry..i.."CookingNormalText"]:SetText("")
_G[entry..i.."FirstAidNormalText"]:SetText("")
_G[entry..i.."FishingNormalText"]:SetText("")
_G[entry..i.."RidingNormalText"]:SetText("")
_G[entry..i.."WoodcuttingNormalText"]:SetText("")
_G[entry..i.."WoodworkingNormalText"]:SetText("")
_G[ entry..i ]:SetID(line)
_G[ entry..i ]:Show()
i = i + 1
VisibleCount = VisibleCount + 1
DisplayedCount = DisplayedCount + 1
elseif DrawRealm then
if (lineType == INFO_CHARACTER_LINE) then
local character = DS:GetCharacter( Characters:GetInfo(line) )
local icon
if DS:GetCharacterFaction(character) == "Alliance" then
-- icon = addon:TextureToFontstring(ICON_FACTION_ALLIANCE, size, size) .. " "
icon = addon:TextureToFontstring2(ICON_FACTION_ALLIANCE, size, size, inset, inset, inset, inset) .. " "
else
-- icon = addon:TextureToFontstring(ICON_FACTION_HORDE, size, size) .. " "
icon = addon:TextureToFontstring2(ICON_FACTION_HORDE, size, size, inset, inset, inset, inset) .. " "
end
_G[entry..i.."Collapse"]:Hide()
_G[entry..i.."Name"]:SetWidth(170)
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0)
_G[entry..i.."NameNormalText"]:SetWidth(170)
addon:SetCharacterRowNameLevel(entry, i, icon, character)
-- profession 1
local field = Characters:GetField(line, "spellID1")
if field then
-- icon = addon:TextureToFontstring(addon:GetSpellIcon(field), size, size) .. " "
icon = addon:TextureToFontstring2(addon:GetSpellIcon(field), size, size, inset, inset, inset, inset) .. " "
else
icon = ""
end
field = Characters:GetField(line, "skillRank1") or 0
_G[entry..i.."Skill1NormalText"]:SetText(icon .. ns:GetColor(field) .. field)
-- profession 2
field = Characters:GetField(line, "spellID2")
if field then
-- icon = addon:TextureToFontstring(addon:GetSpellIcon(field), size, size) .. " "
icon = addon:TextureToFontstring2(addon:GetSpellIcon(field), size, size, inset, inset, inset, inset) .. " "
else
icon = ""
end
field = Characters:GetField(line, "skillRank2") or 0
_G[entry..i.."Skill2NormalText"]:SetText(icon .. ns:GetColor(field) .. field)
-- cooking
-- icon = addon:TextureToFontstring(addon:GetSpellIcon(2550), size, size) .. " "
icon = addon:TextureToFontstring2(addon:GetSpellIcon(2550), size, size, inset, inset, inset, inset) .. " "
field = Characters:GetField(line, "cooking") or 0
_G[entry..i.."CookingNormalText"]:SetText(icon .. ns:GetColor(field) .. field)
-- first aid
-- icon = addon:TextureToFontstring(addon:GetSpellIcon(3273), size, size) .. " "
icon = addon:TextureToFontstring2(addon:GetSpellIcon(3273), size, size, inset, inset, inset, inset) .. " "
field = Characters:GetField(line, "firstaid") or 0
_G[entry..i.."FirstAidNormalText"]:SetText(icon .. ns:GetColor(field) .. field)
-- fishing
-- icon = addon:TextureToFontstring(addon:GetSpellIcon(7733), size, size) .. " "
icon = addon:TextureToFontstring2(addon:GetSpellIcon(7733), size, size, inset, inset, inset, inset) .. " "
field = Characters:GetField(line, "fishing") or 0
_G[entry..i.."FishingNormalText"]:SetText(icon .. ns:GetColor(field) .. field)
-- riding
field = Characters:GetField(line, "riding") or 0
if field >= 300 then
-- icon = addon:TextureToFontstring("Interface\\Icons\\Ability_Mount_Gryphon_01", size, size) .. " "
icon = addon:TextureToFontstring2("Interface\\Icons\\ability_mount_drake_bronze", size, size, inset, inset, inset, inset)
for _,spId in pairs({63956, 63963, 60024, 72808, 72807, 63796, 40192, 69395, 60021, 59976, 64927, 67336, 65439, 49193, 71810, 44317, 44744, 58615, 37015, 3363, 32345}) do
-- IsPetKnown(character, companionType, spellID)
if DS:IsPetKnown(character, "MOUNT", spId) then
icon = addon:TextureToFontstring2("Interface\\Icons\\inv_misc_enggizmos_03", size, size, inset, inset, inset, inset)
break
end
end
elseif field >= 225 then
icon = addon:TextureToFontstring2("Interface\\Icons\\ability_mount_goldengryphon", size, size, inset, inset, inset, inset)
elseif field >= 150 then
icon = addon:TextureToFontstring2("Interface\\Icons\\ability_mount_charger", size, size, inset, inset, inset, inset)
elseif field >= 75 then
icon = addon:TextureToFontstring2("Interface\\Icons\\spell_nature_swiftness", size, size, inset, inset, inset, inset)
else
icon = addon:TextureToFontstring2("Interface\\Icons\\inv_boots_03", size, size, inset, inset, inset, inset)
end
if DS:IsSpellKnown(character, 54197) then -- KNOWS [Cold Weather Flying]
-- Interface\Icons\Spell_Frost_FrostShock
-- icon = addon:TextureToFontstring(addon:GetSpellIcon(54197), size, size) .. " "
-- TextureToFontstringHalfSquare(name, side, inset, isRight)
-- icon = addon:TextureToFontstringHalfSquare(addon:GetSpellIcon(54197), size, inset, true) .. " "
-- icon = icon .. addon:TextureToFontstringHalfSquare(addon:GetSpellIcon(54197), size, inset, 0.7, 1.0) .. " "
-- TextureToFontstringCut(name, heightOrig, widthOrig, insetLeft, insetRight, insetTop, insetBottom)
-- local sTemp = size+2*inset
-- icon = icon .. addon:TextureToFontstringCut(addon:GetSpellIcon(54197), sTemp, sTemp, floor(0.7*sTemp), floor(0.1*sTemp), inset, inset) .. " "
icon = addon:TextureToFontstring(addon:GetSpellIcon(54197), size, size) .. " " .. icon .. " "
else -- DOES NOT KNOW [Cold Weather Flying]
icon = format("|T%s:%s:%s:0:0:%s:%s:%s:%s:%s:%s|t", addon:GetSpellIcon(54197), size, size, size, size, 0, 0, 0, 0) .. " " .. icon .. " "
end
_G[entry..i.."RidingNormalText"]:SetText(icon .. ns:GetColor(field, 300) .. field)
-- CoA custom professions: Woodcutting (spell 13977860) + Woodworking (spell 1005008).
-- Ranks are read live from DataStore_Skills' name-based getters (not from a
-- precomputed Characters field) so this stays self-contained. The getters return
-- 0 on non-CoA chars or chars not yet scanned; still guarded with "or 0" before
-- GetColor/concat. DataStore methods may be absent if DataStore_Skills is an older
-- build, so guard each call with an existence check.
field = (DS.GetWoodcuttingRank and (DS:GetWoodcuttingRank(character)) or 0) or 0
icon = addon:TextureToFontstring2(addon:GetSpellIcon(13977860), size, size, inset, inset, inset, inset) .. " "
_G[entry..i.."WoodcuttingNormalText"]:SetText(icon .. ns:GetColor(field) .. field)
field = (DS.GetWoodworkingRank and (DS:GetWoodworkingRank(character)) or 0) or 0
icon = addon:TextureToFontstring2(addon:GetSpellIcon(1005008), size, size, inset, inset, inset, inset) .. " "
_G[entry..i.."WoodworkingNormalText"]:SetText(icon .. ns:GetColor(field) .. field)
elseif (lineType == INFO_TOTAL_LINE) then
_G[entry..i.."Collapse"]:Hide()
_G[entry..i.."Name"]:SetWidth(200)
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 15, 0)
_G[entry..i.."NameNormalText"]:SetWidth(200)
_G[entry..i.."NameNormalText"]:SetText(L["Totals"])
_G[entry..i.."Level"]:SetText(Characters:GetField(line, "level"))
_G[entry..i.."Skill1NormalText"]:SetText("")
_G[entry..i.."Skill2NormalText"]:SetText("")
_G[entry..i.."CookingNormalText"]:SetText("")
_G[entry..i.."FirstAidNormalText"]:SetText("")
_G[entry..i.."FishingNormalText"]:SetText("")
_G[entry..i.."RidingNormalText"]:SetText("")
_G[entry..i.."WoodcuttingNormalText"]:SetText("")
_G[entry..i.."WoodworkingNormalText"]:SetText("")
end
_G[ entry..i ]:SetID(line)
_G[ entry..i ]:Show()
i = i + 1
VisibleCount = VisibleCount + 1
DisplayedCount = DisplayedCount + 1
end
end
end
while i <= VisibleLines do
_G[ entry..i ]:SetID(0)
_G[ entry..i ]:Hide()
i = i + 1
end
FauxScrollFrame_Update( _G[ frame.."ScrollFrame" ], VisibleCount, VisibleLines, 18);
end
function ns:OnEnter(frame)
local line = frame:GetParent():GetID()
local lineType = Characters:GetLineType(line)
if lineType ~= INFO_CHARACTER_LINE then
return
end
local id = frame:GetID()
local skillName, rank, suggestion
if id == 1 then
skillName = Characters:GetField(line, "skillName1")
elseif id == 2 then
skillName = Characters:GetField(line, "skillName2")
elseif id == 3 then
skillName = GetSpellInfo(2550) -- cooking
elseif id == 4 then
skillName = GetSpellInfo(3273) -- First Aid
elseif id == 5 then
skillName = GetSpellInfo(24303) -- Fishing
elseif id == 6 then
skillName = L["Riding"]
elseif id == 8 then
skillName = GetSpellInfo(13977860) or "Woodcutting" -- CoA custom: Woodcutting
elseif id == 9 then
skillName = GetSpellInfo(1005008) or "Woodworking" -- CoA custom: Woodworking
end
local DS = DataStore
local character = DS:GetCharacter(Characters:GetInfo(line))
local curRank, maxRank = DS:GetSkillInfo(character, skillName)
curRank, maxRank = curRank or 0, maxRank or 0 -- CoA: getter returns no value for skills DataStore_Skills hasn't scanned
local profession = DS:GetProfession(character, skillName)
if (id == 8) or (id == 9) then -- CoA custom professions: show rank + recipe summary like the primary professions
rank = ns:GetColor(curRank) .. curRank .. "/" .. maxRank
suggestion = addon:GetSuggestion(skillName, curRank)
elseif (id >= 1) and (id <= 6) then
if id == 6 then -- riding
rank = ns:GetColor(curRank, 300) .. curRank .. "/" .. maxRank
else
rank = ns:GetColor(curRank) .. curRank .. "/" .. maxRank
end
suggestion = addon:GetSuggestion(skillName, curRank)
elseif id == 7 then -- class
local _, class = DS:GetCharacterClass(character)
if class ~= "ROGUE" then
return
end
skillName = L["Rogue Proficiencies"]
local curLock, maxLock = DS:GetSkillInfo(character, L["Lockpicking"])
curLock, maxLock = curLock or 0, maxLock or 0 -- CoA: guard unscanned lockpicking
rank = TEAL .. L["Lockpicking"] .. " " .. curLock .. "/" .. maxLock
suggestion = addon:GetSuggestion(L["Lockpicking"], curLock)
end
AltoTooltip:ClearLines();
AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT");
AltoTooltip:AddLine(skillName,1,1,1);
AltoTooltip:AddLine(GREEN..rank,1,1,1);
if (id <= 4) or (id == 9) then -- crafting skills (incl. CoA Woodworking, id 9); skips fishing/riding/Woodcutting
if skillName ~= GetSpellInfo(13614) and skillName ~= GetSpellInfo(8613) then -- no display for herbalism & skinning
AltoTooltip:AddLine(" ");
if not profession then
AltoTooltip:AddLine(L["No data"]);
AltoTooltip:Show();
return
end
if DS:GetNumCraftLines(profession) == 0 then
AltoTooltip:AddLine(L["No data"].. ": 0 " .. TRADESKILL_SERVICE_LEARN,1,1,1);
else
local orange, yellow, green, grey = DS:GetNumRecipesByColor(profession)
AltoTooltip:AddLine(orange+yellow+green+grey .. " " .. TRADESKILL_SERVICE_LEARN,1,1,1);
AltoTooltip:AddLine(format(WHITE .. "%d " .. RECIPE_GREEN .. "Green|r /"
.. WHITE .. " %d " .. YELLOW .. "Yellow|r /"
.. WHITE .. " %d " .. RECIPE_ORANGE .. "Orange",
green, yellow, orange))
end
end
end
local skillCap = 450
if id == 6 then
skillCap = 300
end
AltoTooltip:AddLine(" ");
AltoTooltip:AddLine(RECIPE_GREY .. L["Grey"] .. "|r " .. L["up to"] .. " " .. (floor(skillCap*0.25)-1),1,1,1);
AltoTooltip:AddLine(RED .. RED_GEM .. "|r " .. L["up to"] .. " " .. (floor(skillCap*0.50)-1),1,1,1);
AltoTooltip:AddLine(ORANGE .. BI["Orange"] .. "|r " .. L["up to"] .. " " .. (floor(skillCap*0.75)-1),1,1,1);
AltoTooltip:AddLine(YELLOW .. YELLOW_GEM .. "|r " .. L["up to"] .. " " .. (skillCap-1),1,1,1);
AltoTooltip:AddLine(GREEN .. BI["Green"] .. "|r " .. L["at"] .. " "..skillCap.." " .. L["and above"],1,1,1);
if suggestion then
AltoTooltip:AddLine(" ",1,1,1);
AltoTooltip:AddLine(L["Suggestion"] .. ": ",1,1,1);
AltoTooltip:AddLine(TEAL .. suggestion,1,1,1);
end
-- parse profession cooldowns
if id ~= 7 and profession then
DS:ClearExpiredCooldowns(profession)
local numCooldows = DS:GetNumActiveCooldowns(profession)
if numCooldows == 0 then
AltoTooltip:AddLine(" ",1,1,1);
AltoTooltip:AddLine(L["All cooldowns are up"],1,1,1);
else
AltoTooltip:AddLine(" ",1,1,1);
for i = 1, numCooldows do
local craftName, expiresIn = DS:GetCraftCooldownInfo(profession, i)
AltoTooltip:AddDoubleLine(craftName, addon:GetTimeString(expiresIn));
end
end
end
AltoTooltip:Show();
end
local VIEW_MOUNTS = 8
function ns:OnClick(frame, button)
local line = frame:GetParent():GetID()
local lineType = Characters:GetLineType(line)
if lineType ~= INFO_CHARACTER_LINE then
return
end
local id = frame:GetID()
if id == 5 then return end -- fishing ? do nothing
addon:SetCurrentCharacter( Characters:GetInfo(line) )
local skillName
if id == 1 then
skillName = Characters:GetField(line, "skillName1")
elseif id == 2 then
skillName = Characters:GetField(line, "skillName2")
elseif id == 3 then
skillName = GetSpellInfo(2550) -- cooking
elseif id == 4 then
skillName = GetSpellInfo(3273) -- First Aid
elseif id == 8 then
skillName = GetSpellInfo(13977860) or "Woodcutting" -- CoA custom (gathering, no recipes)
elseif id == 9 then
skillName = GetSpellInfo(1005008) or "Woodworking" -- CoA custom (crafting)
end
local DS = DataStore
local character = DS:GetCharacter(Characters:GetInfo(line))
local profession = DS:GetProfession(character, skillName)
if skillName then
if not profession or DS:GetNumCraftLines(profession) == 0 then -- if profession hasn't been scanned (or scan failed), exit
return
end
end
local charName, realm, account = addon:GetCurrentCharacter()
local chat = ChatEdit_GetLastActiveWindow()
if chat:IsShown() and IsShiftKeyDown() and realm == GetRealmName() and id ~= 6 then
-- if shift-click, then display the profession link and exit
local link = profession.FullLink
if link and link:match("trade:") then
chat:Insert(link);
end
return
end
addon.Tabs.Characters:SetCurrent(charName, realm, account)
addon.Tabs:OnClick(2)
if id == 6 then
addon.Tabs.Characters:ViewCharInfo(VIEW_MOUNTS)
else
addon.Tabs.Characters:ViewRecipes(skillName)
end
end
local skillColors = { RECIPE_GREY, RED, ORANGE, YELLOW, GREEN }
function ns:GetColor(rank, skillCap)
rank = rank or 0 -- CoA: skill fields are nil for chars DataStore_Characters hasn't scanned
skillCap = skillCap or 450
return skillColors[ floor(rank / (skillCap/4)) + 1 ]
end