diff --git a/Altoholic/Altoholic.lua b/Altoholic/Altoholic.lua index 24aeb14..5f6a3ff 100644 --- a/Altoholic/Altoholic.lua +++ b/Altoholic/Altoholic.lua @@ -17,7 +17,6 @@ local TEAL = "|cFF00FF9A" local GOLD = "|cFFFFD700" local THIS_ACCOUNT = "Default" -local VERSION_STRING = "1.04a" Altoholic.ClassInfo = { ["MAGE"] = "|cFF69CCF0", @@ -249,10 +248,12 @@ end -- *** Event Handlers *** +local hasScannedThisSession local function OnPlayerAlive() - -- print("Altoholic.lua") -- DEBUG 2025 07 21 - if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard - + -- CoA: scan once at login (see DataStore_Inventory / commit fdcb25a). FRIENDLIST_UPDATE also + -- keeps the friends list fresh; this just guarantees an initial scan without rescanning on res. + if hasScannedThisSession then return end + hasScannedThisSession = true ScanFriends() end @@ -332,7 +333,12 @@ function addon:OnEnable() addon:RegisterEvent("AUCTION_HOUSE_SHOW", addon.AuctionHouse.OnShow) addon:RegisterEvent("PLAYER_TALENT_UPDATE", addon.Talents.OnUpdate); - AltoholicFrameName:SetText("Altoholic |cFFFFFFFF".. addon.Version .. "|r by |cFF69CCF0Thaoky|r" .. " (Edited by |cFF69CCF0Telkar-RG|r ".."|cFFFFFFFF".. VERSION_STRING .."|r)") + -- CoA: just "Altoholic " in the title bar (Exiles branding + author credit live in the .toc). + -- Read the live .toc Version so it tracks each -coa.N release without editing this string. + -- CoA: use a Lua constant, not GetAddOnMetadata — TOC metadata is cached at game launch + -- and does NOT refresh on /reload, so the .toc version looked stale ("still .18"). A Lua + -- constant re-evaluates on every /reload, giving a truthful loaded-code version. Bump with the .toc. + AltoholicFrameName:SetText("Altoholic |cFFFFFFFF3.3.002b-coa.30|r") local realm = GetRealmName() local player = UnitName("player") @@ -398,7 +404,15 @@ function addon:ToggleUI() end function addon:OnShow() - SetPortraitTexture(AltoholicFramePortrait, "player"); + SetPortraitTexture(AltoholicFramePortrait, "player"); + + -- CoA: apply the saved UI scale on open (upstream only applied it after the Options + -- tab was visited, so the window opened un-scaled). Default is 1.0 — scaling is opt-in + -- via Options; a true larger layout is tracked separately. + local O = addon.db.global.options + if O and O.UIScale then + AltoholicFrame:SetScale(O.UIScale) + end addon.Characters:BuildList() addon.Characters:BuildView() @@ -483,11 +497,12 @@ function addon:SetItemButtonTexture(button, texture, width, height) height = height or 36 local itemTexture = _G[button.."IconTexture"] - + if not itemTexture then return end -- CoA: guard buttons that don't exist / lack an IconTexture region (e.g. iterating more professions than there are _ProfN buttons) + itemTexture:SetWidth(width); itemTexture:SetHeight(height); itemTexture:SetAllPoints(_G[button]); - + SetItemButtonTexture(_G[button], texture) end @@ -539,6 +554,7 @@ function addon:GetSpellIDFromRecipeLink(link) end function addon:GetMoneyString(copper, color, noTexture) + copper = copper or 0 -- CoA: callers may pass a no-value DS getter result color = color or "|cFFFFD700" local gold = floor( copper / 10000 ); @@ -559,6 +575,7 @@ function addon:GetMoneyString(copper, color, noTexture) end function addon:GetTimeString(seconds) + seconds = seconds or 0 -- CoA: callers may pass a no-value DS getter result local days = floor(seconds / 86400); -- TotalTime is expressed in seconds seconds = mod(seconds, 86400) local hours = floor(seconds / 3600); @@ -585,6 +602,26 @@ function addon:GetDelayInDays(delay) return floor((time() - delay) / 86400) end +-- CoA: shared, nil-safe character display helpers. +-- DataStore char-based getters return *no value* for any module that hasn't +-- scanned a given character (DataStore.lua: "if not arg1.lastUpdate then return end"). +-- Fresh alts have partial per-module data, so every field is guarded here once +-- instead of being copy-pasted (and missed) across the frames. +function Altoholic:AddCharacterTooltipHeader(character) + local locClass, engClass = DS:GetCharacterClass(character) + local className = Altoholic:GetCoAClassName(engClass) or locClass or "" -- CoA: current class name (PROPHET->Venomancer, …) + AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character) or "?", DS:GetColoredCharacterFaction(character) or "") + AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"], + GREEN..(DS:GetCharacterLevel(character) or 0), DS:GetCharacterRace(character) or "", className), 1, 1, 1) +end + +function Altoholic:SetCharacterRowNameLevel(entry, i, icon, character) + local locClass, engClass = DS:GetCharacterClass(character) + local className = Altoholic:GetCoAClassName(engClass) or locClass or "" -- CoA: current class name + _G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character) or "?", className)) + _G[entry..i.."Level"]:SetText(GREEN .. (DS:GetCharacterLevel(character) or 0)) +end + function Altoholic:FormatDelay(timeStamp) -- timeStamp = value when time() was last called for a given variable (ex: last time the mailbox was checked) if not timeStamp then @@ -619,7 +656,7 @@ function Altoholic:FormatDelay(timeStamp) end function addon:GetRestedXP(character) - local rate = DS:GetRestXPRate(character) + local rate = DS:GetRestXPRate(character) or 0 -- CoA: getter returns no value for unscanned/partial chars local coeff = 1 if addon.Options:Get("RestXPMode") == 1 then @@ -679,21 +716,18 @@ function Altoholic:ShowClassIcons() local realm, account = Altoholic:GetCurrentRealm() - -- #################### + -- Sort characters by level first, then average item level. The getters yield no value + -- for alts whose Characters/Inventory module hasn't scanned them, so default [3]/[4] to 0. local CharNameList = DS:GetCharacters(realm, account) local CharNameList_sort = {} - + for k,v in pairs(CharNameList) do - table.insert(CharNameList_sort,{k,v, DS:GetAverageItemLevel(v), DS:GetCharacterLevel(v)}) + table.insert(CharNameList_sort, {k, v, DS:GetAverageItemLevel(v) or 0, DS:GetCharacterLevel(v) or 0}) end - - -- sort for level first, avg iLevel secondly + table.sort(CharNameList_sort, function(a,b) return b[3]+b[4]*10000 < a[3]+a[4]*10000 end) - -- DEBUG_CHARLIST = CharNameList - -- print("-- altoholic DEBUG READY") - -- #################### - - + + -- for characterName, character in pairs(DS:GetCharacters(realm, account)) do for _,charTbl in ipairs(CharNameList_sort) do local characterName, character = charTbl[1], charTbl[2] @@ -707,13 +741,22 @@ function Altoholic:ShowClassIcons() end) local _, class = DS:GetCharacterClass(character) - -- CoA: CLASS_ICON_TCOORDS only carries the vanilla 10 + DK on Voljin. - -- For the 21 CoA custom classes the lookup is nil; fall back to - -- WARRIOR's coords so we render *something* rather than crashing. - local tc = CLASS_ICON_TCOORDS[class] or CLASS_ICON_TCOORDS["WARRIOR"] local itemTexture = _G[itemName .. "IconTexture"] - itemTexture:SetTexture("Interface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes"); - itemTexture:SetTexCoord(tc[1], tc[2], tc[3], tc[4]); + -- CoA: CLASS_ICON_TCOORDS only carries the vanilla 10 + DK on Voljin, + -- so the 21 CoA custom classes have no entry. GetCoAClassIcon (defined + -- in CoAClassColors.lua) returns the realm-authoritative atlas + coords + -- for any CoA-playable class (incl. vanilla 10 + DK); it returns nil for + -- an unknown/unscanned (no-value) token, in which case we keep the stock + -- CLASS_ICON_TCOORDS path, defaulting to WARRIOR rather than crashing. + local coaTex, l, r, t, b = Altoholic:GetCoAClassIcon(class) + if coaTex then + itemTexture:SetTexture(coaTex); + itemTexture:SetTexCoord(l, r, t, b); + else + local tc = CLASS_ICON_TCOORDS[class] or CLASS_ICON_TCOORDS["WARRIOR"] + itemTexture:SetTexture("Interface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes"); + itemTexture:SetTexCoord(tc[1], tc[2], tc[3], tc[4]); + end itemTexture:SetWidth(36); itemTexture:SetHeight(36); itemTexture:SetAllPoints(itemButton); @@ -763,25 +806,22 @@ function Altoholic:DrawCharacterTooltip(self, charName) AltoTooltip:SetOwner(self, "ANCHOR_LEFT"); AltoTooltip:ClearLines(); - AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character)) - - AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"], - GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1) + Altoholic:AddCharacterTooltipHeader(character) local zone, subZone = DS:GetLocation(character) - AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..zone, GOLD..subZone),1,1,1) - + AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..(zone or "?"), GOLD..(subZone or "")),1,1,1) + local restXP = DS:GetRestXP(character) if restXP and restXP > 0 then AltoTooltip:AddLine(format("%s: %s", L["Rest XP"], GREEN..restXP),1,1,1) end - - AltoTooltip:AddLine("Average iLevel: " .. GREEN .. format("%.1f", DS:GetAverageItemLevel(character)),1,1,1); + + AltoTooltip:AddLine("Average iLevel: " .. GREEN .. format("%.1f", DS:GetAverageItemLevel(character) or 0),1,1,1); if IsAddOnLoaded("DataStore_Achievements") then - if DS:GetNumCompletedAchievements(character) > 0 then - AltoTooltip:AddLine(ACHIEVEMENTS_COMPLETED ..": " .. GREEN .. DS:GetNumCompletedAchievements(character) .. "/"..DS:GetNumAchievements(character)) - AltoTooltip:AddLine(ACHIEVEMENT_TITLE ..": " .. GREEN .. DS:GetNumAchievementPoints(character)) + if (DS:GetNumCompletedAchievements(character) or 0) > 0 then + AltoTooltip:AddLine(ACHIEVEMENTS_COMPLETED ..": " .. GREEN .. DS:GetNumCompletedAchievements(character) .. "/"..(DS:GetNumAchievements(character) or 0)) + AltoTooltip:AddLine(ACHIEVEMENT_TITLE ..": " .. GREEN .. (DS:GetNumAchievementPoints(character) or 0)) end end diff --git a/Altoholic/Altoholic.toc b/Altoholic/Altoholic.toc index 0279010..7e30c77 100644 --- a/Altoholic/Altoholic.toc +++ b/Altoholic/Altoholic.toc @@ -1,5 +1,5 @@ ## Interface: 30300 -## Title: Altoholic (|cFF69CCF0Telkar-RG|cFFFFFFFF 1.04a|r |cFFFFD100CoA|r) +## Title: Altoholic (|cFFFFD100Exiles|r) ## X-Curse-Packaged-Version: r90 ## X-Curse-Project-Name: Altoholic @@ -13,7 +13,7 @@ ## Author: Thaoky, Telkar-RG ## X-Edited-By: Exiles (Sub-Net) — florian.berthold@sub-net.at -## Version: 3.3.002b-coa.3 +## Version: 3.3.002b-coa.30 ## X-Category: Inventory, Tradeskill, Mail ## X-Localizations: enUS, frFR, zhCN, zhTW, deDE, koKR, esES, esMX, ruRU ## X-Website: http://wow.curse.com/downloads/wow-addons/details/altoholic.aspx diff --git a/Altoholic/Altoholic.xml b/Altoholic/Altoholic.xml index ac97f6f..696cf24 100644 --- a/Altoholic/Altoholic.xml +++ b/Altoholic/Altoholic.xml @@ -92,7 +92,7 @@ - + @@ -172,6 +172,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -179,7 +222,7 @@ - + @@ -191,7 +234,7 @@ - + diff --git a/Altoholic/Characters.lua b/Altoholic/Characters.lua index 9c5634a..c2699b4 100644 --- a/Altoholic/Characters.lua +++ b/Altoholic/Characters.lua @@ -76,8 +76,6 @@ local function AddRealm(AccountName, RealmName) local realmBankSlots = 0 local realmFreeBankSlots = 0 - local SkillsCache = { {name = "", rank = 0}, {name = "", rank = 0} } - -- 1) Add the realm name table.insert(characterList, { linetype = INFO_REALM_LINE + (realmCount*3), isCollapsed = false, @@ -87,36 +85,28 @@ local function AddRealm(AccountName, RealmName) -- 2) Add the characters for characterName, character in pairs(DataStore:GetCharacters(RealmName, AccountName)) do - SkillsCache[1].name = "" - SkillsCache[1].rank = 0 - SkillsCache[1].spellID = nil - SkillsCache[2].name = "" - SkillsCache[2].rank = 0 - SkillsCache[2].spellID = nil - - local i = 1 - local professions = DataStore:GetPrimaryProfessions(character) - if professions then - for SkillName, s in pairs(professions) do - SkillsCache[i].name = SkillName - SkillsCache[i].rank = DataStore:GetSkillInfo(character, SkillName) - SkillsCache[i].spellID = DataStore:GetProfessionSpellID(SkillName) - i = i + 1 - - if i > 2 then -- it seems that under certain conditions, the loop continues after 2 professions.., so break - break - end + -- CoA: characters can know ALL professions at once (no retail 2-primary + -- limit) plus the customs Woodcutting/Woodworking. Build a dynamic list of + -- every known primary profession instead of the old fixed 2 slots. Each + -- entry carries its own name/rank/spellID(icon) so Skills.lua can render an + -- arbitrary number of professions. GetPrimaryProfessionList never returns + -- nil (returns {} for unscanned chars), but guard anyway. + local professions = {} + if DataStore.GetPrimaryProfessionList then + local list = DataStore:GetPrimaryProfessionList(character) or {} + for _, p in ipairs(list) do + professions[#professions + 1] = { + name = p.name, + rank = p.rank or 0, + maxRank = p.maxRank or 0, -- CoA: needed for the "rank/max" display in the vertical Skills list + spellID = DataStore:GetProfessionSpellID(p.name), + } end end - + table.insert(characterList, { linetype = INFO_CHARACTER_LINE + (realmCount*3), key = character, - skillName1 = SkillsCache[1].name, - skillRank1 = SkillsCache[1].rank, - spellID1 = SkillsCache[1].spellID, - skillName2 = SkillsCache[2].name, - skillRank2 = SkillsCache[2].rank, - spellID2 = SkillsCache[2].spellID, + professions = professions, -- CoA: dynamic list of all primary professions cooking = DataStore:GetCookingRank(character), firstaid = DataStore:GetFirstAidRank(character), fishing = DataStore:GetFishingRank(character), diff --git a/Altoholic/CoAClassColors.lua b/Altoholic/CoAClassColors.lua index f4f1e3d..799b096 100644 --- a/Altoholic/CoAClassColors.lua +++ b/Altoholic/CoAClassColors.lua @@ -24,7 +24,8 @@ -- Source of truth: db.exil.es /coa/dev for the full palette; -- _G.RAID_CLASS_COLORS at FrameXML load time for the running client. -local AC = _G.Altoholic and _G.Altoholic.ClassInfo +local Alto = _G.Altoholic +local AC = Alto and Alto.ClassInfo if type(AC) ~= "table" then return end local source = _G.RAID_CLASS_COLORS @@ -45,3 +46,92 @@ for token, color in pairs(source) do end end end + +-- Class ICONS +-- ----------- +-- WoW's _G.CLASS_ICON_TCOORDS only carries texcoords for the playable +-- classes the *client* shipped with — on the CoA Voljin client that is +-- the vanilla 10 + DEATHKNIGHT only. The 21 CoA custom classes +-- (BARBARIAN, WITCHDOCTOR, CHRONOMANCER, …) have no entry, so any draw +-- site that does `CLASS_ICON_TCOORDS[class]` falls back to a wrong or +-- blank icon (Altoholic.lua:ShowClassIcons hit this). +-- +-- The realm-authoritative class-icon atlas is the 512x512 (8x8 grid of +-- 64px cells) BLP that the CoA Details! fork bundles and renders for +-- all 32 classes. We ship a copy of that atlas as +-- Interface\AddOns\Altoholic\images\coa-classes.blp and reproduce its +-- per-token texcoords below (source: Details/functions/profiles.lua +-- class_coords). Keyed by the UPPERCASE englishClass token — the same +-- key DataStore stores (DataStore_Characters: UnitClass()'s 2nd return) +-- and CLASS_ICON_TCOORDS uses, so it is a drop-in for both. +-- +-- Includes the vanilla 10 + DK too, so a single lookup covers every +-- CoA-playable class uniformly out of one texture. + +local COA_CLASS_ICON_TEXTURE = [[Interface\AddOns\Altoholic\images\coa-classes]] + +-- left, right, top, bottom (verbatim from the CoA Details atlas) +local COA_CLASS_ICON_TCOORDS = { + WITCHHUNTER = { 0.875, 1, 0.375, 0.5 }, + WITCHDOCTOR = { 0.75, 0.875, 0.375, 0.5 }, + WILDWALKER = { 0.625, 0.75, 0.375, 0.5 }, + WARRIOR = { 0.5, 0.625, 0.375, 0.5 }, + WARLOCK = { 0.375, 0.5, 0.375, 0.5 }, + TINKER = { 0.25, 0.375, 0.375, 0.5 }, + SUNCLERIC = { 0.125, 0.25, 0.375, 0.5 }, + STORMBRINGER = { 0, 0.125, 0.375, 0.5 }, + STARCALLER = { 0.875, 1, 0.25, 0.375 }, + SPIRITMAGE = { 0.75, 0.875, 0.25, 0.375 }, + SONOFARUGAL = { 0.625, 0.75, 0.25, 0.375 }, + SHAMAN = { 0.5, 0.625, 0.25, 0.375 }, + ROGUE = { 0.375, 0.5, 0.25, 0.375 }, + REAPER = { 0.25, 0.375, 0.25, 0.375 }, + RANGER = { 0.125, 0.25, 0.25, 0.375 }, + PYROMANCER = { 0, 0.125, 0.25, 0.375 }, + PROPHET = { 0.875, 1, 0.125, 0.25 }, + PRIEST = { 0.75, 0.875, 0.125, 0.25 }, + PALADIN = { 0.625, 0.75, 0.125, 0.25 }, + NECROMANCER = { 0.5, 0.625, 0.125, 0.25 }, + MONK = { 0.375, 0.5, 0.125, 0.25 }, + MAGE = { 0.25, 0.375, 0.125, 0.25 }, + HUNTER = { 0.125, 0.25, 0.125, 0.25 }, + HERO = { 0, 0.125, 0.125, 0.25 }, + GUARDIAN = { 0.875, 1, 0, 0.125 }, + FLESHWARDEN = { 0.75, 0.875, 0, 0.125 }, + DRUID = { 0.625, 0.75, 0, 0.125 }, + DEMONHUNTER = { 0.5, 0.625, 0, 0.125 }, + DEATHKNIGHT = { 0.375, 0.5, 0, 0.125 }, + CULTIST = { 0.25, 0.375, 0, 0.125 }, + CHRONOMANCER = { 0.125, 0.25, 0, 0.125 }, + BARBARIAN = { 0, 0.125, 0, 0.125 }, +} + +-- Returns texture, left, right, top, bottom for a CoA-playable class +-- token, or nil if the token is unknown (caller should fall back to the +-- stock CLASS_ICON_TCOORDS path). Tolerant of a nil/missing token. +function Alto:GetCoAClassIcon(token) + if type(token) ~= "string" then return end + local tc = COA_CLASS_ICON_TCOORDS[token] + if not tc then return end + return COA_CLASS_ICON_TEXTURE, tc[1], tc[2], tc[3], tc[4] +end + +-- CoA renamed its classes, but UnitClass()/DataStore still return the OLD tokens +-- (PROPHET, MONK, …). Map them to the current display names. Source: coa-omen/README-CoA.md. +-- Tokens not listed here keep their normal localized name (returns nil). +local COA_CLASS_NAMES = { + HERO = "Hero", BARBARIAN = "Barbarian", WITCHDOCTOR = "Witch Doctor", + DEMONHUNTER = "Felsworn", WITCHHUNTER = "Witch Hunter", STORMBRINGER = "Stormbringer", + FLESHWARDEN = "Knight of Xoroth", GUARDIAN = "Guardian", MONK = "Templar", + SONOFARUGAL = "Bloodmage", RANGER = "Ranger", CHRONOMANCER = "Chronomancer", + NECROMANCER = "Necromancer", PYROMANCER = "Pyromancer", CULTIST = "Cultist", + STARCALLER = "Starcaller", SUNCLERIC = "Sun Cleric", TINKER = "Tinker", + PROPHET = "Venomancer", REAPER = "Reaper", WILDWALKER = "Primalist", + SPIRITMAGE = "Runemaster", +} + +-- Current CoA display name for a class token, or nil if unmapped (caller falls back). +function Alto:GetCoAClassName(token) + if type(token) ~= "string" then return end + return COA_CLASS_NAMES[token] +end diff --git a/Altoholic/Frames/AccountSharing.lua b/Altoholic/Frames/AccountSharing.lua index 56fba3c..ab40622 100644 --- a/Altoholic/Frames/AccountSharing.lua +++ b/Altoholic/Frames/AccountSharing.lua @@ -279,7 +279,7 @@ local ContentScrollFrame_Desc = { [CHARACTER_HEADER_LINE] = { GetText = function(self, line) local _, realm, name = strsplit(".", line.key) - return format("%s|r / %s", WHITE..realm, DataStore:GetColoredCharacterName(line.key)) + return format("%s|r / %s", WHITE..realm, DataStore:GetColoredCharacterName(line.key) or "?") end, GetOffset = function(self, line) return 20 diff --git a/Altoholic/Frames/AccountSummary.lua b/Altoholic/Frames/AccountSummary.lua index 9815fc0..2a90d21 100644 --- a/Altoholic/Frames/AccountSummary.lua +++ b/Altoholic/Frames/AccountSummary.lua @@ -168,7 +168,7 @@ end function ns:Update() - local VisibleLines = 14 + local VisibleLines = 18 local frame = "AltoholicFrameSummary" local entry = frame.."Entry" @@ -249,12 +249,11 @@ function ns:Update() _G[entry..i.."Name"]:SetWidth(170) _G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0) _G[entry..i.."NameNormalText"]:SetWidth(170) - _G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character))) - _G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(character)) + addon:SetCharacterRowNameLevel(entry, i, icon, character) - _G[entry..i.."Money"]:SetText(addon:GetMoneyString(DS:GetMoney(character))) - _G[entry..i.."Played"]:SetText(addon:GetTimeString(DS:GetPlayTime(character))) - _G[entry..i.."XP"]:SetText(GREEN .. DS:GetXPRate(character) .. "%") + _G[entry..i.."Money"]:SetText(addon:GetMoneyString(DS:GetMoney(character) or 0)) + _G[entry..i.."Played"]:SetText(addon:GetTimeString(DS:GetPlayTime(character) or 0)) + _G[entry..i.."XP"]:SetText(GREEN .. (DS:GetXPRate(character) or 0) .. "%") if DS:GetCharacterLevel(character) == MAX_PLAYER_LEVEL then _G[entry..i.."Rested"]:SetText(WHITE .. "0%") @@ -262,7 +261,7 @@ function ns:Update() _G[entry..i.."Rested"]:SetText( addon:GetRestedXP(character) ) end - _G[entry..i.."AvgILevelNormalText"]:SetText(YELLOW..format("%.1f", DS:GetAverageItemLevel(character))) + _G[entry..i.."AvgILevelNormalText"]:SetText(YELLOW..format("%.1f", DS:GetAverageItemLevel(character) or 0)) elseif (lineType == INFO_TOTAL_LINE) then _G[entry..i.."Collapse"]:Hide() @@ -342,23 +341,21 @@ function ns:Level_OnEnter(frame) AltoTooltip:ClearLines(); AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT"); - AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character)) - AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"], - GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1) + addon:AddCharacterTooltipHeader(character) local zone, subZone = DS:GetLocation(character) - AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..zone, GOLD..subZone),1,1,1) - + AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..(zone or "?"), GOLD..(subZone or "")),1,1,1) + local guildName = DS:GetGuildInfo(character) if guildName then AltoTooltip:AddLine(format("%s: %s", GUILD, GREEN..guildName),1,1,1) end - - AltoTooltip:AddLine(EXPERIENCE_COLON .. " " - .. GREEN .. DS:GetXP(character) .. WHITE .. "/" - .. GREEN .. DS:GetXPMax(character) .. WHITE .. " (" - .. GREEN .. DS:GetXPRate(character) .. "%" - .. WHITE .. ")",1,1,1); + + AltoTooltip:AddLine(EXPERIENCE_COLON .. " " + .. GREEN .. (DS:GetXP(character) or 0) .. WHITE .. "/" + .. GREEN .. (DS:GetXPMax(character) or 0) .. WHITE .. " (" + .. GREEN .. (DS:GetXPRate(character) or 0) .. "%" + .. WHITE .. ")",1,1,1); local restXP = DS:GetRestXP(character) if restXP and restXP > 0 then @@ -448,7 +445,7 @@ function ns:AIL_OnEnter(frame) AltoTooltip:ClearLines(); AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT"); AltoTooltip:AddLine(DS:GetColoredCharacterName(character),1,1,1); - AltoTooltip:AddLine(WHITE .. L["Average Item Level"] ..": " .. GREEN.. format("%.1f", DS:GetAverageItemLevel(character)),1,1,1); + AltoTooltip:AddLine(WHITE .. L["Average Item Level"] ..": " .. GREEN.. format("%.1f", DS:GetAverageItemLevel(character) or 0),1,1,1); addon:AiLTooltip() AltoTooltip:Show(); diff --git a/Altoholic/Frames/AccountSummary.xml b/Altoholic/Frames/AccountSummary.xml index e65f1c0..a4a7bfc 100644 --- a/Altoholic/Frames/AccountSummary.xml +++ b/Altoholic/Frames/AccountSummary.xml @@ -168,7 +168,7 @@ - + @@ -294,6 +294,36 @@ + + + + + + diff --git a/Altoholic/Frames/Activity.lua b/Altoholic/Frames/Activity.lua index f4c13cf..6525114 100644 --- a/Altoholic/Frames/Activity.lua +++ b/Altoholic/Frames/Activity.lua @@ -23,7 +23,7 @@ local ns = addon.Activity -- ns = namespace local Characters = addon.Characters function ns:Update() - local VisibleLines = 14 + local VisibleLines = 18 local frame = "AltoholicFrameActivity" local entry = frame.."Entry" @@ -100,8 +100,7 @@ function ns:Update() _G[entry..i.."Name"]:SetWidth(170) _G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0) _G[entry..i.."NameNormalText"]:SetWidth(170) - _G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character))) - _G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(character)) + addon:SetCharacterRowNameLevel(entry, i, icon, character) local color local num = DS:GetNumMails(character) or 0 @@ -112,7 +111,7 @@ function ns:Update() color = GREEN -- green by default, red if at least one mail is about to expire local threshold = DataStore:GetOption("DataStore_Mails", "MailWarningThreshold") - if DS:GetNumExpiredMails(character, threshold) > 0 then + if (DS:GetNumExpiredMails(character, threshold) or 0) > 0 then color = RED end end @@ -181,18 +180,16 @@ function ns:OnEnter(self) AltoTooltip:ClearLines(); AltoTooltip:SetOwner(self, "ANCHOR_RIGHT"); - AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character)) - AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"], - GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1) + addon:AddCharacterTooltipHeader(character) local zone, subZone = DS:GetLocation(character) - AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..zone, GOLD..subZone),1,1,1) - - AltoTooltip:AddLine(EXPERIENCE_COLON .. " " - .. GREEN .. DS:GetXP(character) .. WHITE .. "/" - .. GREEN .. DS:GetXPMax(character) .. WHITE .. " (" - .. GREEN .. DS:GetXPRate(character) .. "%" - .. WHITE .. ")",1,1,1); + AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..(zone or "?"), GOLD..(subZone or "")),1,1,1) + + AltoTooltip:AddLine(EXPERIENCE_COLON .. " " + .. GREEN .. (DS:GetXP(character) or 0) .. WHITE .. "/" + .. GREEN .. (DS:GetXPMax(character) or 0) .. WHITE .. " (" + .. GREEN .. (DS:GetXPRate(character) or 0) .. "%" + .. WHITE .. ")",1,1,1); local restXP = DS:GetRestXP(character) if restXP and restXP > 0 then diff --git a/Altoholic/Frames/Activity.xml b/Altoholic/Frames/Activity.xml index 763049c..a1e9f44 100644 --- a/Altoholic/Frames/Activity.xml +++ b/Altoholic/Frames/Activity.xml @@ -176,7 +176,7 @@ - + @@ -302,6 +302,36 @@ + + + + + + diff --git a/Altoholic/Frames/AuctionHouse.lua b/Altoholic/Frames/AuctionHouse.lua index 2991518..772931c 100644 --- a/Altoholic/Frames/AuctionHouse.lua +++ b/Altoholic/Frames/AuctionHouse.lua @@ -136,7 +136,7 @@ function ns:InvalidateView() end function ns:UpdateAuctions() - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameAuctions" local entry = frame.."Entry" @@ -217,7 +217,7 @@ function ns:UpdateAuctions() end function ns:UpdateBids() - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameAuctions" local entry = frame.."Entry" diff --git a/Altoholic/Frames/AuctionHouse.xml b/Altoholic/Frames/AuctionHouse.xml index f2cf05b..5d343bc 100644 --- a/Altoholic/Frames/AuctionHouse.xml +++ b/Altoholic/Frames/AuctionHouse.xml @@ -106,7 +106,7 @@ - + @@ -222,6 +222,21 @@ + + + diff --git a/Altoholic/Frames/BagUsage.lua b/Altoholic/Frames/BagUsage.lua index e2ef491..a478b50 100644 --- a/Altoholic/Frames/BagUsage.lua +++ b/Altoholic/Frames/BagUsage.lua @@ -22,7 +22,7 @@ local ns = addon.BagUsage -- ns = namespace local Characters = addon.Characters function ns:Update() - local VisibleLines = 14 + local VisibleLines = 18 local frame = "AltoholicFrameBagUsage" local entry = frame.."Entry" @@ -96,38 +96,37 @@ function ns:Update() _G[entry..i.."Name"]:SetWidth(170) _G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0) _G[entry..i.."NameNormalText"]:SetWidth(170) - _G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character))) - _G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(character)) - - _G[entry..i.."FreeBags"]:SetText(GREEN .. DS:GetNumFreeBagSlots(character)) - _G[entry..i.."FreeBank"]:SetText(GREEN .. DS:GetNumFreeBankSlots(character)) + addon:SetCharacterRowNameLevel(entry, i, icon, character) + + _G[entry..i.."FreeBags"]:SetText(GREEN .. (DS:GetNumFreeBagSlots(character) or 0)) + _G[entry..i.."FreeBank"]:SetText(GREEN .. (DS:GetNumFreeBankSlots(character) or 0)) _G[entry..i.."BagSlotsNormalText"]:SetJustifyH("LEFT") _G[entry..i.."BankSlotsNormalText"]:SetJustifyH("LEFT") -- Normal bags _G[entry..i.."BagSlotsNormalText"]:SetText(format("%s/%s|r/%s|r/%s|r/%s |r(%s|r)", - DS:GetContainerSize(character, 0), - WHITE .. DS:GetContainerSize(character, 1), - WHITE .. DS:GetContainerSize(character, 2), - WHITE .. DS:GetContainerSize(character, 3), - WHITE .. DS:GetContainerSize(character, 4), - CYAN .. DS:GetNumBagSlots(character))) - + DS:GetContainerSize(character, 0) or 0, + WHITE .. (DS:GetContainerSize(character, 1) or 0), + WHITE .. (DS:GetContainerSize(character, 2) or 0), + WHITE .. (DS:GetContainerSize(character, 3) or 0), + WHITE .. (DS:GetContainerSize(character, 4) or 0), + CYAN .. (DS:GetNumBagSlots(character) or 0))) + -- Bank bags - if DS:GetNumBankSlots(character) < 28 then + if (DS:GetNumBankSlots(character) or 0) < 28 then _G[entry..i.."BankSlotsNormalText"]:SetText(L["Bank not visited yet"]) else _G[entry..i.."BankSlotsNormalText"]:SetText(format("%s/%s|r/%s|r/%s|r/%s|r/%s|r/%s|r/%s |r(%s|r)", - DS:GetContainerSize(character, 100), - WHITE .. DS:GetContainerSize(character, 5), - WHITE .. DS:GetContainerSize(character, 6), - WHITE .. DS:GetContainerSize(character, 7), - WHITE .. DS:GetContainerSize(character, 8), - WHITE .. DS:GetContainerSize(character, 9), - WHITE .. DS:GetContainerSize(character, 10), - WHITE .. DS:GetContainerSize(character, 11), - CYAN .. DS:GetNumBankSlots(character))) + DS:GetContainerSize(character, 100) or 0, -- CoA: empty/unscanned bank bags return nil size + WHITE .. (DS:GetContainerSize(character, 5) or 0), + WHITE .. (DS:GetContainerSize(character, 6) or 0), + WHITE .. (DS:GetContainerSize(character, 7) or 0), + WHITE .. (DS:GetContainerSize(character, 8) or 0), + WHITE .. (DS:GetContainerSize(character, 9) or 0), + WHITE .. (DS:GetContainerSize(character, 10) or 0), + WHITE .. (DS:GetContainerSize(character, 11) or 0), + CYAN .. (DS:GetNumBankSlots(character) or 0))) end elseif (lineType == INFO_TOTAL_LINE) then _G[entry..i.."Collapse"]:Hide() @@ -182,9 +181,7 @@ function ns:OnEnter(self) AltoTooltip:ClearLines(); AltoTooltip:SetOwner(self, "ANCHOR_RIGHT"); - AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character)) - AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"], - GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1) + addon:AddCharacterTooltipHeader(character) AltoTooltip:AddLine(" ",1,1,1); local id = self:GetID() @@ -203,7 +200,7 @@ function ns:OnEnter(self) end numSlots = DS:GetNumBagSlots(character) numFree = DS:GetNumFreeBagSlots(character) - elseif DS:GetNumBankSlots(character) < 28 then + elseif (DS:GetNumBankSlots(character) or 0) < 28 then AltoTooltip:AddLine(L["Bank not visited yet"],1,1,1); AltoTooltip:Show(); return @@ -221,5 +218,15 @@ function ns:OnEnter(self) AltoTooltip:AddLine(" ",1,1,1); AltoTooltip:AddLine(CYAN .. numSlots .. " |r" .. L["slots"] .. " (" .. GREEN .. numFree .. "|r " ..L["free"] .. ") ",1,1,1); - AltoTooltip:Show(); + + -- CoA personal bank (per-character). Realm bank is per-realm, not per + -- character, so it is intentionally not shown on this character tooltip. + local personalTabs = DS:GetPersonalBankTabCount(character) or 0 + if personalTabs > 0 then + AltoTooltip:AddLine(" ",1,1,1); + AltoTooltip:AddLine(format("%sPersonal Bank|r: %s%d|r %s", + CYAN, WHITE, personalTabs, (personalTabs == 1) and "tab" or "tabs"), 1, 1, 1); + end + + AltoTooltip:Show(); end diff --git a/Altoholic/Frames/BagUsage.xml b/Altoholic/Frames/BagUsage.xml index 9714cb5..a9ac8cc 100644 --- a/Altoholic/Frames/BagUsage.xml +++ b/Altoholic/Frames/BagUsage.xml @@ -177,7 +177,7 @@ - + @@ -303,6 +303,36 @@ + + + + + + diff --git a/Altoholic/Frames/Calendar.lua b/Altoholic/Frames/Calendar.lua index 65f796d..5561a84 100644 --- a/Altoholic/Frames/Calendar.lua +++ b/Altoholic/Frames/Calendar.lua @@ -764,7 +764,7 @@ function Altoholic.Calendar.Events:BuildList() self:BuildView() end -local NUM_EVENTLINES = 14 +local NUM_EVENTLINES = 20 function Altoholic.Calendar.Events:Update() local self = Altoholic.Calendar.Events diff --git a/Altoholic/Frames/Calendar.xml b/Altoholic/Frames/Calendar.xml index fa3ee8f..fdc0fea 100644 --- a/Altoholic/Frames/Calendar.xml +++ b/Altoholic/Frames/Calendar.xml @@ -131,7 +131,7 @@ - + @@ -286,7 +286,7 @@ - + @@ -406,6 +406,36 @@ + + + + + + diff --git a/Altoholic/Frames/Containers.lua b/Altoholic/Frames/Containers.lua index 0da83e7..afc46be 100644 --- a/Altoholic/Frames/Containers.lua +++ b/Altoholic/Frames/Containers.lua @@ -96,7 +96,7 @@ end local function UpdateSpread() local mode = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectContainerView) local rarity = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectRarity) - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameContainers" local entry = frame.."Entry" @@ -177,8 +177,8 @@ local function UpdateSpread() local slotID = bagIndices[line].from - 3 + j local itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID) - - if (slotID <= containerSize) then + + if (slotID <= (containerSize or 0)) then -- CoA: containerSize nil for unscanned bag on partial-data alt if itemID then Altoholic:SetItemButtonTexture(itemName, GetItemIcon(itemID)); @@ -242,7 +242,7 @@ end local function UpdateAllInOne() local mode = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectContainerView) local rarity = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectRarity) - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameContainers" local entry = frame.."Entry" @@ -278,7 +278,7 @@ local function UpdateAllInOne() local container = DS:GetContainer(character, containerID) local _, _, containerSize = DS:GetContainerInfo(character, containerID) - for slotID = 1, containerSize do + for slotID = 1, (containerSize or 0) do -- CoA: containerSize nil for unscanned bag on partial-data alt local itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID) if itemID then currentSlotIndex = currentSlotIndex + 1 diff --git a/Altoholic/Frames/Containers.xml b/Altoholic/Frames/Containers.xml index 85c2d4b..4ff8fca 100644 --- a/Altoholic/Frames/Containers.xml +++ b/Altoholic/Frames/Containers.xml @@ -137,7 +137,7 @@ - + @@ -282,6 +282,21 @@ + + + diff --git a/Altoholic/Frames/Currencies.lua b/Altoholic/Frames/Currencies.lua index 3d94c38..1d7637c 100644 --- a/Altoholic/Frames/Currencies.lua +++ b/Altoholic/Frames/Currencies.lua @@ -177,7 +177,7 @@ local function Currencies_UpdateEx(self, offset, entry, desc) end local CurrenciesScrollFrame_Desc = { - NumLines = 8, + NumLines = 10, LineHeight = 41, Frame = "AltoholicFrameCurrencies", GetSize = function() return #usedTokens end, diff --git a/Altoholic/Frames/Currencies.xml b/Altoholic/Frames/Currencies.xml index 823221a..b33792a 100644 --- a/Altoholic/Frames/Currencies.xml +++ b/Altoholic/Frames/Currencies.xml @@ -175,7 +175,7 @@ - + @@ -302,6 +302,16 @@ + + diff --git a/Altoholic/Frames/Equipment.lua b/Altoholic/Frames/Equipment.lua index 2d684c5..6587eb7 100644 --- a/Altoholic/Frames/Equipment.lua +++ b/Altoholic/Frames/Equipment.lua @@ -415,7 +415,7 @@ function ns:GetInventoryTypeName(inv) end function ns:Update() - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameEquipment" local entry = frame.."Entry" diff --git a/Altoholic/Frames/Equipment.xml b/Altoholic/Frames/Equipment.xml index 9c02118..e8aa3fe 100644 --- a/Altoholic/Frames/Equipment.xml +++ b/Altoholic/Frames/Equipment.xml @@ -117,7 +117,7 @@ - + @@ -213,6 +213,21 @@ + + + diff --git a/Altoholic/Frames/GuildBankTabs.lua b/Altoholic/Frames/GuildBankTabs.lua index 165693c..ea44eef 100644 --- a/Altoholic/Frames/GuildBankTabs.lua +++ b/Altoholic/Frames/GuildBankTabs.lua @@ -72,7 +72,7 @@ function ns:Update() BuildView() end - local VisibleLines = 14 + local VisibleLines = 18 local frame = "AltoholicFrameGuildBankTabs" local entry = frame.."Entry" diff --git a/Altoholic/Frames/GuildBankTabs.xml b/Altoholic/Frames/GuildBankTabs.xml index 2dd2070..16736cb 100644 --- a/Altoholic/Frames/GuildBankTabs.xml +++ b/Altoholic/Frames/GuildBankTabs.xml @@ -107,7 +107,7 @@ - + @@ -233,6 +233,36 @@ + + + + + + diff --git a/Altoholic/Frames/GuildMembers.lua b/Altoholic/Frames/GuildMembers.lua index 082e5df..d9d4dd7 100644 --- a/Altoholic/Frames/GuildMembers.lua +++ b/Altoholic/Frames/GuildMembers.lua @@ -81,9 +81,9 @@ local SecondaryLevelSort = {-- sort functions for the alts end end, ["level"] = function(a, b) - local levelA = select(4, DataStore:GetGuildMemberInfo(a)) - local levelB = select(4, DataStore:GetGuildMemberInfo(b)) - + local levelA = select(4, DataStore:GetGuildMemberInfo(a)) or 0 -- CoA: nil level on partial guild data crashed table.sort + local levelB = select(4, DataStore:GetGuildMemberInfo(b)) or 0 + if viewSortOrder then return levelA < levelB else @@ -285,7 +285,7 @@ function ns:Update() BuildView() end - local VisibleLines = 14 + local VisibleLines = 17 local frame = "AltoholicFrameGuildMembers" local entry = frame.."Entry" @@ -460,7 +460,9 @@ function ns:Level_OnClick(self, button) if button ~= "LeftButton" then return end local id = self:GetParent():GetID() + if id == 0 then return end -- CoA: cleared/hidden rows keep SetID(0); view[0] is nil and indexing line.lineType below errors. Level_OnEnter & Collapse_OnClick already guard this; Level_OnClick (the AiL click that opens equipment) was the only handler missing it. local line = view[id] + if not line then return end -- CoA: stale button ID after a GUILD_ROSTER_UPDATE view rebuild can point past #view if line.lineType == NORMALPLAYER_LINE then return end local member = self:GetParent().CharName diff --git a/Altoholic/Frames/GuildMembers.xml b/Altoholic/Frames/GuildMembers.xml index d416eb3..73877a6 100644 --- a/Altoholic/Frames/GuildMembers.xml +++ b/Altoholic/Frames/GuildMembers.xml @@ -159,7 +159,7 @@ - + @@ -307,6 +307,36 @@ + + + + + + + + + + + + diff --git a/Altoholic/Frames/Keys.lua b/Altoholic/Frames/Keys.lua index f2609af..3aabe6d 100644 --- a/Altoholic/Frames/Keys.lua +++ b/Altoholic/Frames/Keys.lua @@ -461,7 +461,7 @@ function ns:Update() AltoTooltip:Hide(); GameTooltip:Hide(); - local VisibleLines = 8 + local VisibleLines = 10 local NumLines = VisibleLines local frame = "AltoholicFrameKeys" local entry = frame.."Entry" @@ -789,7 +789,7 @@ function ns:Item_OnEnter(frame) AltoTooltip:SetOwner(frame, "ANCHOR_LEFT") AltoTooltip:ClearLines() - AltoTooltip:AddLine( format("%s|r: %s", DS:GetColoredCharacterName(character), nameKey) ) + AltoTooltip:AddLine( format("%s|r: %s", DS:GetColoredCharacterName(character) or "?", nameKey) ) AltoTooltip:AddLine(" ") local questDone, cr,cg,cb @@ -848,7 +848,7 @@ function ns:Item_OnEnter(frame) else AltoTooltip:SetOwner(frame, "ANCHOR_LEFT") AltoTooltip:ClearLines() - AltoTooltip:AddLine( format("%s|r: %s", DS:GetColoredCharacterName(character), nameKey) ) + AltoTooltip:AddLine( format("%s|r: %s", DS:GetColoredCharacterName(character) or "?", nameKey) ) AltoTooltip:AddLine(" ") local questDone, cr,cg,cb @@ -912,7 +912,7 @@ function ns:Item_OnEnter(frame) else AltoTooltip:SetOwner(frame, "ANCHOR_LEFT") AltoTooltip:ClearLines() - AltoTooltip:AddLine( format("%s|r: %s", DS:GetColoredCharacterName(character), nameKey) ) + AltoTooltip:AddLine( format("%s|r: %s", DS:GetColoredCharacterName(character) or "?", nameKey) ) AltoTooltip:AddLine(" ") AltoTooltip:AddLine(L["Required reputation"] .. ":",1,1,1) diff --git a/Altoholic/Frames/Keys.xml b/Altoholic/Frames/Keys.xml index db9922a..9687994 100644 --- a/Altoholic/Frames/Keys.xml +++ b/Altoholic/Frames/Keys.xml @@ -221,7 +221,7 @@ - + @@ -317,6 +317,16 @@ + + diff --git a/Altoholic/Frames/Mails.lua b/Altoholic/Frames/Mails.lua index ab9401d..9578c64 100644 --- a/Altoholic/Frames/Mails.lua +++ b/Altoholic/Frames/Mails.lua @@ -81,7 +81,7 @@ function ns:BuildView(field, ascending) end function ns:Update() - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameMail" local entry = frame.."Entry" local player = addon:GetCurrentCharacter() diff --git a/Altoholic/Frames/Mails.xml b/Altoholic/Frames/Mails.xml index 2ba22ce..f72d190 100644 --- a/Altoholic/Frames/Mails.xml +++ b/Altoholic/Frames/Mails.xml @@ -84,7 +84,7 @@ - + @@ -190,6 +190,21 @@ + + + diff --git a/Altoholic/Frames/Pets.lua b/Altoholic/Frames/Pets.lua index 89f9b50..e48daf7 100644 --- a/Altoholic/Frames/Pets.lua +++ b/Altoholic/Frames/Pets.lua @@ -212,7 +212,7 @@ function ns:UpdatePets() end function ns:UpdatePetsAllInOne() - local VisibleLines = 8 + local VisibleLines = 10 local frame = "AltoholicFramePetsAllInOne" local entry = frame.."Entry" diff --git a/Altoholic/Frames/Pets.xml b/Altoholic/Frames/Pets.xml index ae8c97b..115f927 100644 --- a/Altoholic/Frames/Pets.xml +++ b/Altoholic/Frames/Pets.xml @@ -25,7 +25,7 @@ - + @@ -333,7 +333,7 @@ - + @@ -429,6 +429,16 @@ + + diff --git a/Altoholic/Frames/Quests.lua b/Altoholic/Frames/Quests.lua index 4c3f02f..77f24df 100644 --- a/Altoholic/Frames/Quests.lua +++ b/Altoholic/Frames/Quests.lua @@ -34,13 +34,13 @@ function ns:Update() local character = addon.Tabs.Characters:GetCurrent() - local VisibleLines = 14 + local VisibleLines = 18 local frame = "AltoholicFrameQuests" local entry = frame.."Entry" local DS = DataStore - if DS:GetQuestLogSize(character) == 0 then + if (DS:GetQuestLogSize(character) or 0) == 0 then AltoholicTabCharactersStatus:SetText(L["No quest found for "] .. addon:GetCurrentCharacter()) addon:ClearScrollFrame( _G[ frame.."ScrollFrame" ], entry, VisibleLines, 18) return diff --git a/Altoholic/Frames/Quests.xml b/Altoholic/Frames/Quests.xml index 707715a..d73145c 100644 --- a/Altoholic/Frames/Quests.xml +++ b/Altoholic/Frames/Quests.xml @@ -106,7 +106,7 @@ - + @@ -232,6 +232,36 @@ + + + + + + diff --git a/Altoholic/Frames/Recipes.lua b/Altoholic/Frames/Recipes.lua index 493b075..cb1cb49 100644 --- a/Altoholic/Frames/Recipes.lua +++ b/Altoholic/Frames/Recipes.lua @@ -218,7 +218,7 @@ end function ns:Update() local currentProfession = addon.TradeSkills.CurrentProfession - local VisibleLines = 14 + local VisibleLines = 18 local frame = "AltoholicFrameRecipes" local entry = frame.."Entry" diff --git a/Altoholic/Frames/Recipes.xml b/Altoholic/Frames/Recipes.xml index 2f1c0f4..466478d 100644 --- a/Altoholic/Frames/Recipes.xml +++ b/Altoholic/Frames/Recipes.xml @@ -196,7 +196,7 @@ - + @@ -459,6 +459,36 @@ + + + + + + diff --git a/Altoholic/Frames/Reputations.lua b/Altoholic/Frames/Reputations.lua index 71bac7c..e421b7b 100644 --- a/Altoholic/Frames/Reputations.lua +++ b/Altoholic/Frames/Reputations.lua @@ -14,6 +14,13 @@ local DARK_RED = "|cFFF00000" local ICON_UNKNOWN = "\124TInterface\\RaidFrame\\ReadyCheck-NotReady:14\124t" local ICON_EXALTED = "\124TInterface\\RaidFrame\\ReadyCheck-Ready:14\124t" +-- NOTE (Exiles/CoA): The Reputations view is DATA-DRIVEN. +-- The hardcoded `Factions` table below is used ONLY as an icon lookup (faction name -> icon), +-- so well-known Blizzard factions keep their nice icons. The list of factions actually shown, +-- and their grouping, is built at runtime from what DataStore_Reputations scanned on each +-- character (faction name + in-game category header). This means CoA's custom factions +-- (and any new ones added over time) appear automatically, with no code edits required. +-- Factions not present in the icon lookup fall back to a generic faction icon. local Factions = { -- Factions reference table, based on http://www.wowwiki.com/Factions { -- [1] @@ -151,8 +158,96 @@ local VertexColors = { [FACTION_STANDING_LABEL8] = { r = 1.0, g = 1.0, b = 1.0 }, -- exalted } -local currentXPack = 1 -- default to wow classic -local currentFactionGroup = (UnitFactionGroup("player") == "Alliance") and 1 or 2 -- default to alliance or horde +local GENERIC_FACTION_ICON = "Achievement_Reputation_01" -- fallback icon for factions not in the lookup (ex: CoA custom factions) + +-- Flat icon lookup built once from the hardcoded reference table above: faction name -> icon name. +local FactionIcons = {} +for _, xpack in ipairs(Factions) do + for _, factionGroup in ipairs(xpack) do + for _, faction in ipairs(factionGroup) do + if faction.name and faction.icon then + FactionIcons[faction.name] = faction.icon + end + end + end +end + +local function GetFactionIcon(name) + return FactionIcons[name] or GENERIC_FACTION_ICON +end + +-- *** Dynamic, data-driven group/faction model *** +-- currentGroup = "" means "All factions" (every scanned faction, flat). Otherwise it's an in-game +-- category header name (ex: "Wrath of the Lich King", or a CoA custom category). +local ALL_GROUPS = "" +local currentGroup = ALL_GROUPS + +-- Rebuilt on each Update from the union of all characters' scanned reputations on the current realm. +local displayedGroups = {} -- ordered list of { name = headerName } for the dropdown +local displayedFactions = {} -- ordered list of faction names currently shown (filtered by currentGroup) + +local function BuildModel() + local DS = DataStore + local realm, account = addon:GetCurrentRealm() + + -- header (group) name -> { set of faction names }, plus first-seen order for stable display + local groupSet = {} + local factionHeader = {} -- faction name -> its header (last writer wins; headers are consistent across chars) + local factionOrder = {} -- faction name -> first-seen index (stable ordering) + local orderCounter = 0 + local groupOrder = {} -- header name -> first-seen index + + for _, characterKey in pairs(DS:GetCharacters(realm, account)) do + local reputations = DS:GetReputations(characterKey) or {} + local headers = DS:GetReputationHeaders(characterKey) or {} + for factionName in pairs(reputations) do + local header = headers[factionName] or "" + if factionOrder[factionName] == nil then + orderCounter = orderCounter + 1 + factionOrder[factionName] = orderCounter + end + factionHeader[factionName] = header + if not groupSet[header] then + groupSet[header] = true + groupOrder[header] = orderCounter + end + end + end + + -- Build the ordered group list for the dropdown. + wipe(displayedGroups) + local groupNames = {} + for header in pairs(groupSet) do + tinsert(groupNames, header) + end + table.sort(groupNames, function(a, b) return (groupOrder[a] or 0) < (groupOrder[b] or 0) end) + for _, header in ipairs(groupNames) do + tinsert(displayedGroups, header) + end + + -- If the previously selected group no longer exists, fall back to "All factions". + if currentGroup ~= ALL_GROUPS and not groupSet[currentGroup] then + currentGroup = ALL_GROUPS + end + + -- Build the ordered faction list for the currently selected group. + wipe(displayedFactions) + local names = {} + for factionName in pairs(factionHeader) do + if currentGroup == ALL_GROUPS or factionHeader[factionName] == currentGroup then + tinsert(names, factionName) + end + end + table.sort(names, function(a, b) return (factionOrder[a] or 0) < (factionOrder[b] or 0) end) + for _, factionName in ipairs(names) do + tinsert(displayedFactions, factionName) + end +end + +local function GroupLabel(header) + if header == ALL_GROUPS or header == "" then return ALL end -- "All", a Blizzard global string + return header +end addon.Reputations = {} @@ -194,46 +289,43 @@ local function DDM_AddCloseMenu() UIDropDownMenu_AddButton(info, 1) end -local function DDM_OnClick(self, xpackIndex, factionGroupIndex) - currentXPack = xpackIndex - currentFactionGroup = factionGroupIndex - - local factionGroup = Factions[currentXPack][currentFactionGroup] - UIDropDownMenu_SetText(AltoholicFrameReputations_SelectFaction, factionGroup.name) - +local function DDM_OnClick(self, header) + currentGroup = header or ALL_GROUPS + + UIDropDownMenu_SetText(AltoholicFrameReputations_SelectFaction, GroupLabel(currentGroup)) + ns:Update() end local function Reputations_UpdateEx(self, offset, entry, desc) local line local size = desc:GetSize() - + local DS = DataStore local realm, account = addon:GetCurrentRealm() local character - local factionGroup = Factions[currentXPack][currentFactionGroup] - + for i=1, desc.NumLines do line = i + offset if line <= size then - local faction = factionGroup[line] - - _G[entry..i.."Name"]:SetText(WHITE .. faction.name) + local factionName = displayedFactions[line] + + _G[entry..i.."Name"]:SetText(WHITE .. (factionName or "")) _G[entry..i.."Name"]:SetJustifyH("LEFT") _G[entry..i.."Name"]:SetPoint("TOPLEFT", 15, 0) - + for j = 1, 10 do -- loop through the 10 alts local itemName = entry.. i .. "Item" .. j; local itemButton = _G[itemName] local classButton = _G["AltoholicFrameClassesItem" .. j] - + local itemTexture = _G[itemName .. "_Background"] - itemTexture:SetTexture("Interface\\Icons\\"..faction.icon) + itemTexture:SetTexture("Interface\\Icons\\"..GetFactionIcon(factionName)) local status, rate if classButton.CharName then -- if there's an alt in this column.. character = DS:GetCharacter(classButton.CharName, realm, account) - status, _, _, rate = DS:GetReputationInfo(character, faction.name) + status, _, _, rate = DS:GetReputationInfo(character, factionName) if status and rate then local vc = VertexColors[status] @@ -273,25 +365,27 @@ local function Reputations_UpdateEx(self, offset, entry, desc) end local ReputationsScrollFrame_Desc = { - NumLines = 8, + NumLines = 10, LineHeight = 41, Frame = "AltoholicFrameReputations", - GetSize = function() return #Factions[currentXPack][currentFactionGroup] end, + GetSize = function() return #displayedFactions end, Update = Reputations_UpdateEx, } function ns:DropDownFaction_Initialize() - for xpackIndex, xpack in ipairs(Factions) do - DDM_AddTitle(xpack.name) - - for factionGroupIndex, factionGroup in ipairs(Factions[xpackIndex]) do - DDM_Add(factionGroup.name, DDM_OnClick, xpackIndex, factionGroupIndex) - end + -- Dropdown is built dynamically from the categories actually scanned across all characters. + BuildModel() + + DDM_Add(GroupLabel(ALL_GROUPS), DDM_OnClick, ALL_GROUPS) -- "All factions" pseudo-group + for _, header in ipairs(displayedGroups) do + DDM_Add(GroupLabel(header), DDM_OnClick, header) end DDM_AddCloseMenu() end function ns:Update() + BuildModel() + UIDropDownMenu_SetText(AltoholicFrameReputations_SelectFaction, GroupLabel(currentGroup)) addon:ScrollFrameUpdate(ReputationsScrollFrame_Desc) end @@ -302,15 +396,15 @@ function ns:OnEnter(frame) local DS = DataStore local realm, account = addon:GetCurrentRealm() local character = DS:GetCharacter(charName, realm, account) - local factionGroup = Factions[currentXPack][currentFactionGroup] - local faction = factionGroup[ frame:GetParent():GetID() ].name - + local faction = displayedFactions[ frame:GetParent():GetID() ] + if not faction then return end + local status, currentLevel, maxLevel, rate = DS:GetReputationInfo(character, faction) if not status then return end - + AltoTooltip:SetOwner(frame, "ANCHOR_LEFT"); AltoTooltip:ClearLines(); - AltoTooltip:AddLine(DS:GetColoredCharacterName(character) .. WHITE .. " @ " .. TEAL .. faction,1,1,1); + AltoTooltip:AddLine((DS:GetColoredCharacterName(character) or "?") .. WHITE .. " @ " .. TEAL .. faction,1,1,1); rate = format("%d", floor(rate)) .. "%" AltoTooltip:AddLine(format("%s: %d/%d (%s)", status, currentLevel, maxLevel, rate),1,1,1 ) @@ -346,12 +440,12 @@ function ns:OnClick(frame, button) local DS = DataStore local realm, account = addon:GetCurrentRealm() local character = DS:GetCharacter(charName, realm, account) - local factionGroup = Factions[currentXPack][currentFactionGroup] - local faction = factionGroup[ frame:GetParent():GetID() ].name - + local faction = displayedFactions[ frame:GetParent():GetID() ] + if not faction then return end + local status, currentLevel, maxLevel, rate = DS:GetReputationInfo(character, faction) if not status then return end - + if ( button == "LeftButton" ) and ( IsShiftKeyDown() ) then local chat = ChatEdit_GetLastActiveWindow() if chat:IsShown() then diff --git a/Altoholic/Frames/Reputations.xml b/Altoholic/Frames/Reputations.xml index 88647e3..4e7bd9a 100644 --- a/Altoholic/Frames/Reputations.xml +++ b/Altoholic/Frames/Reputations.xml @@ -178,7 +178,7 @@ - + @@ -213,11 +213,11 @@ - local faction = (UnitFactionGroup("player") == "Alliance") and FACTION_ALLIANCE or FACTION_HORDE - - UIDropDownMenu_SetWidth(self, 100) + -- Default to "All factions"; the dropdown contents are built dynamically + -- from the categories actually scanned (see Reputations.lua / BuildModel). + UIDropDownMenu_SetWidth(self, 140) UIDropDownMenu_SetButtonWidth(self, 20) - UIDropDownMenu_SetText(self, faction) + UIDropDownMenu_SetText(self, ALL) UIDropDownMenu_Initialize(self, Altoholic.Reputations.DropDownFaction_Initialize) @@ -309,6 +309,16 @@ + + diff --git a/Altoholic/Frames/Search.lua b/Altoholic/Frames/Search.lua index 0e390c5..78afba2 100644 --- a/Altoholic/Frames/Search.lua +++ b/Altoholic/Frames/Search.lua @@ -239,7 +239,7 @@ function ns:Realm_Update() end function ns:Loots_Update() - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameSearch" local entry = frame.."Entry" @@ -311,7 +311,7 @@ function ns:Loots_Update() end function ns:Upgrade_Update() - local VisibleLines = 7 + local VisibleLines = 10 local frame = "AltoholicFrameSearch" local entry = frame.."Entry" @@ -603,15 +603,17 @@ local function BrowseCharacter(character) currentResultLocation = L["Bank"] elseif (containerName == "Bag-2") then currentResultLocation = KEYRING + elseif string.sub(containerName, 1, 5) == "PBank" then -- CoA personal bank tab + currentResultLocation = "Personal Bank" else local bagNum = tonumber(string.sub(containerName, 4)) if (bagNum >= 0) and (bagNum <= 4) then currentResultLocation = L["Bags"] else currentResultLocation = L["Bank"] - end + end end - + for slotID = 1, container.size do itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID) @@ -710,7 +712,39 @@ local function BrowseRealm(realm, account, bothFactions) currentResultType = nil currentResultLocation = nil end - + + -- CoA realm bank: shared per-realm storage. Scanned once per realm (not per + -- character). Reported on the player-item line, attributed to the realm. + if addon.Options:Get("IncludeGuildBank") == 1 then + currentResultType = PLAYER_ITEM_LINE + -- Use the first scanned character of this realm as the row "source" so + -- the result lists the correct realm/account; if none, skip. + local anyChar + for _, character in pairs(DS:GetCharacters(realm, account)) do + anyChar = character + break + end + + if anyChar then + for tabID = 1, 6 do + local tab = DS:GetRealmBankTab(realm, account, tabID) + if tab and tab.name then + currentResultKey = anyChar + for slotID = 1, (tab.size or 0) do + currentResultLocation = format("Realm Bank (%s)", tab.name) + local id = tab.ids[slotID] + if id then + VerifyItem((tab.links or {})[slotID] or id, (tab.counts or {})[slotID] or 1) + end + end + end + end + currentResultKey = nil + end + currentResultType = nil + currentResultLocation = nil + end + if addon.Options:Get("IncludeGuildSkills") == 1 and string.len(currentValue) > 1 then -- Check guild professions ? local guild = addon:GetGuild() if guild and LTL then -- LTL won't be valid if there's a version mismatch (see :Init() ) diff --git a/Altoholic/Frames/Search.xml b/Altoholic/Frames/Search.xml index f4c4ca8..1889133 100644 --- a/Altoholic/Frames/Search.xml +++ b/Altoholic/Frames/Search.xml @@ -240,7 +240,7 @@ - + @@ -331,6 +331,21 @@ + + + diff --git a/Altoholic/Frames/Skills.lua b/Altoholic/Frames/Skills.lua index 776863e..164ede4 100644 --- a/Altoholic/Frames/Skills.lua +++ b/Altoholic/Frames/Skills.lua @@ -28,377 +28,141 @@ local Characters = addon.Characters local size = 22 local inset = 2 + function ns:Update() - local VisibleLines = 14 + local VisibleLines = 18 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 + + -- CoA: vertical list. For each visible character a header row, then one row per known + -- profession / secondary skill (icon + name + rank/max), top to bottom (like the other detail views). + local SECONDARY = { 2550, 3273, 7733 } -- Cooking, First Aid, Fishing (spell id -> icon + GetSpellInfo name) + local items = {} + for _, viewLine in pairs(Characters:GetView()) do + if Characters:GetLineType(viewLine) == INFO_CHARACTER_LINE then + local character = DS:GetCharacter( Characters:GetInfo(viewLine) ) + items[#items + 1] = { kind = "header", viewLine = viewLine, character = character } + + local profs = Characters:GetField(viewLine, "professions") + if profs then + for _, p in ipairs(profs) do + items[#items + 1] = { kind = "skill", viewLine = viewLine, character = character, + spellID = p.spellID, name = p.name, rank = p.rank or 0, maxRank = p.maxRank or 0 } 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 + for _, sid in ipairs(SECONDARY) do + local sName = GetSpellInfo(sid) + if sName then + local cur, max = DS:GetSkillInfo(character, sName) + if cur and cur > 0 then + items[#items + 1] = { kind = "skill", viewLine = viewLine, character = character, + spellID = sid, name = sName, rank = cur, maxRank = max or 0 } + end 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 ]: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) - _G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character))) - _G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(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") - _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") - _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") - _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") - _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") - _G[entry..i.."FishingNormalText"]:SetText(icon .. ns:GetColor(field) .. field) - - -- riding - field = Characters:GetField(line, "riding") - 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) - 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("") - end - _G[ entry..i ]:SetID(line) - _G[ entry..i ]:Show() - i = i + 1 - VisibleCount = VisibleCount + 1 - DisplayedCount = DisplayedCount + 1 end + local riding = Characters:GetField(viewLine, "riding") or 0 + if riding > 0 then + items[#items + 1] = { kind = "skill", viewLine = viewLine, character = character, + spellID = 33388, name = (L and L["Riding"]) or "Riding", rank = riding, maxRank = 300 } + end + items[#items + 1] = { kind = "spacer" } -- blank row between characters 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 + local offset = FauxScrollFrame_GetOffset( _G[ frame.."ScrollFrame" ] ) + for i = 1, VisibleLines do + local e = entry..i + local btn = _G[e] + local item = items[i + offset] + if item then + _G[e.."Collapse"]:Hide() + _G[e.."Skill1NormalText"]:SetText("") + _G[e.."CookingNormalText"]:SetText("") + _G[e.."FirstAidNormalText"]:SetText("") + _G[e.."FishingNormalText"]:SetText("") + _G[e.."RidingNormalText"]:SetText("") + + if item.kind == "spacer" then + -- blank separator row between characters + _G[e.."Name"]:SetPoint("TOPLEFT", 15, 0) + _G[e.."NameNormalText"]:SetText("") + _G[e.."Level"]:SetText("") + elseif item.kind == "header" then + -- character header: gold "Name (Class)" across the row, no rank column + _G[e.."Name"]:SetPoint("TOPLEFT", 12, 0) + _G[e.."Name"]:SetWidth(420) + _G[e.."NameNormalText"]:SetWidth(420) + local cname = Characters:GetInfo(item.viewLine) or "?" -- name from the key, always present (scanned char.name can be nil) + local locClass, engClass = DS:GetCharacterClass(item.character) + local className = Altoholic:GetCoAClassName(engClass) or locClass or "" -- CoA: PROPHET->Venomancer, MONK->Templar, … + local classColor = DS:GetClassColor(item.character) or WHITE + _G[e.."NameNormalText"]:SetText( classColor .. cname .. "|r " .. WHITE .. "(" .. className .. ")" ) + _G[e.."Level"]:SetText("") + else + -- profession row: [icon] name in the Name cell (indented), rank/max in its own column + local iconEsc = "" + if item.spellID then + iconEsc = addon:TextureToFontstring2(addon:GetSpellIcon(item.spellID), size, size, inset, inset, inset, inset) .. " " + end + _G[e.."Name"]:SetPoint("TOPLEFT", 34, 0) + _G[e.."Name"]:SetWidth(170) + _G[e.."NameNormalText"]:SetWidth(170) + _G[e.."NameNormalText"]:SetText( iconEsc .. WHITE .. item.name ) + + local cap = (item.maxRank > 0) and item.maxRank or 450 + _G[e.."Level"]:SetPoint("TOPLEFT", 195, 0) + _G[e.."Level"]:SetWidth(120) + _G[e.."Level"]:SetJustifyH("LEFT") + _G[e.."Level"]:SetText( ns:GetColor(item.rank, cap) .. item.rank .. " / " .. item.maxRank ) + end + btn.coaItem = item + btn:SetID(item.viewLine or 0) + btn:Show() + else + btn.coaItem = nil + btn:SetID(0) + btn:Hide() + end + end + FauxScrollFrame_Update( _G[ frame.."ScrollFrame" ], #items, 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"] - end - - local DS = DataStore - local character = DS:GetCharacter(Characters:GetInfo(line)) - local curRank, maxRank = DS:GetSkillInfo(character, skillName) - local profession = DS:GetProfession(character, skillName) - - if (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"]) - 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 then -- all skills except fishing & riding - 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(); + local item = frame:GetParent() and frame:GetParent().coaItem + if not item or item.kind ~= "skill" then return end + AltoTooltip:ClearLines() + AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT") + AltoTooltip:AddLine(WHITE .. item.name, 1, 1, 1) + local cap = (item.maxRank > 0) and item.maxRank or 450 + AltoTooltip:AddLine( ns:GetColor(item.rank, cap) .. item.rank .. " / " .. item.maxRank, 1, 1, 1 ) + 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 - 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) + -- CoA: clicking a profession row opens that character's profession recipe list, if available. + local item = frame:GetParent() and frame:GetParent().coaItem + if not item or item.kind ~= "skill" or not item.character then return end + if addon.Tabs and addon.Tabs.Characters and addon.Tabs.Characters.ViewCharInfo then + local name, realm, account = Characters:GetInfo(item.viewLine) + addon:SetCurrentCharacter(name, realm, account) + addon.Tabs.Characters:SetCurrent(name, realm, account) + addon.Tabs:OnClick(2) 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 ] + -- CoA: custom professions can exceed skillCap (e.g. ranks > 450), which would + -- push the index past the 5-colour table and return nil -> crash on concat. + -- Clamp into [1, #skillColors]. + local index = floor(rank / (skillCap / 4)) + 1 + if index < 1 then index = 1 end + if index > #skillColors then index = #skillColors end + return skillColors[index] end + diff --git a/Altoholic/Frames/Skills.xml b/Altoholic/Frames/Skills.xml index 96a4a35..d275ec0 100644 --- a/Altoholic/Frames/Skills.xml +++ b/Altoholic/Frames/Skills.xml @@ -108,7 +108,18 @@ - - + + + + + + diff --git a/Altoholic/Frames/TabCharacters.lua b/Altoholic/Frames/TabCharacters.lua index 83bf46a..8cbd989 100644 --- a/Altoholic/Frames/TabCharacters.lua +++ b/Altoholic/Frames/TabCharacters.lua @@ -227,22 +227,32 @@ function ns:UpdateViewIcons() AltoholicTabCharacters_FirstAid.text = professionName AltoholicTabCharacters_FirstAid:Show() + -- CoA: characters can know far more than the retail 2 primary professions, so the + -- _ProfN button row may run out before the profession list does. Stop at the last + -- existing button instead of indexing a nil frame (which crashed the character view). local i = 1 - for skillName, skill in pairs(DS:GetPrimaryProfessions(character)) do + for skillName, skill in pairs(DS:GetPrimaryProfessions(character) or {}) do local itemName = "AltoholicTabCharacters_Prof" .. i local item = _G[itemName] + if not item then break end -- no more profession buttons available local spellID = DataStore:GetProfessionSpellID(skillName) - + if spellID then addon:SetItemButtonTexture(itemName, addon:GetSpellIcon(spellID), size, size) item.text = skillName item:Show() else item.text = nil - item:Hide() + item:Hide() end i = i + 1 end + -- Hide any leftover profession buttons this character doesn't fill. + while _G["AltoholicTabCharacters_Prof" .. i] do + _G["AltoholicTabCharacters_Prof" .. i].text = nil + _G["AltoholicTabCharacters_Prof" .. i]:Hide() + i = i + 1 + end end function ns:MenuItem_OnClick(frame, button) diff --git a/Altoholic/Frames/TabCharacters.xml b/Altoholic/Frames/TabCharacters.xml index ad06c7c..4303347 100644 --- a/Altoholic/Frames/TabCharacters.xml +++ b/Altoholic/Frames/TabCharacters.xml @@ -55,7 +55,7 @@ - + @@ -243,19 +243,44 @@ + + + + + + + + + + +