Compare commits

...

24 Commits

Author SHA1 Message Date
florian.berthold 5f39ea21fa coa.30: show all professions in the character view (4x3 wrapped grid)
release / release (push) Successful in 5s
CoA characters can learn every profession, but the char-view panel only had 2 profession
quick-icons. Added Prof1-12 laid out as a wrapped 4-per-row grid in the open middle-left
area (room exists now that the window is 555 tall), clear of the dropdowns and the bottom
view-groups. UpdateViewIcons already fills/hides them dynamically (coa.13).
2026-05-30 01:33:20 +02:00
florian.berthold 57c603fa8e coa.29: right-size VisibleLines for the taller window (rows were overflowing)
release / release (push) Successful in 5s
All list rows are 22px; the resize had set VisibleLines=20 uniformly (20x22=440 > the 414px
content frame), so the bottom rows spilled past the frame - visible as the guild list
running into the 'Click a character's AiL' footer. Set 18 rows for all list tabs (18x22=396),
17 for GuildMembers (it has the equipment footer).
2026-05-30 01:09:12 +02:00
florian.berthold f4f3de929b coa.28: fix login scan in 9 DataStore modules (the 'data not saved' root cause)
release / release (push) Successful in 5s
Quests, Achievements, Reputations, Pets, Stats, Skills, Crafts, Spells, Talents all had
the ghost-gated PLAYER_ALIVE scan (DEBUG 2025-07-21 leftover): they only scanned when the
player died and released spirit, so their data never populated on a normal login. Now
scan once per session at login (addon.coaScannedThisSession guard), matching the earlier
DataStore_Characters/_Inventory fix. This is why reputations/recipes/quests/pets/etc were
'not saved'.
2026-05-29 23:55:29 +02:00
florian.berthold ee3fec624d coa.27: Skills polish - tighter rank column, gold Name(Class) headers, spacer rows
release / release (push) Successful in 5s
Rank column pulled left (250->195), name cell narrowed. Headers now show class-colored
character name from the key (reliable) + (Class), with a blank spacer row between characters.
2026-05-29 23:52:35 +02:00
florian.berthold 863709e450 coa.26: fix Skills frame overlapping the menu (745->615 width)
release / release (push) Successful in 5s
Root cause of the 'all over the place' Skills layout: the Skills content frame was 745
wide (widened at coa.9 for extra columns) vs 615 for every other Summary view. Both
anchor TOPRIGHT, so the extra 130px pushed the Skills frame's LEFT edge over the nav menu
-> profession names rendered on top of the menu. Restored to 615 so the left edge clears
the menu like AccountSummary; the two-column name/rank list now sits in the content area.
2026-05-29 23:37:56 +02:00
florian.berthold 78e50e9f5c coa.25: clean two-column Skills layout
release / release (push) Successful in 5s
Skills vertical list now uses a proper two-column row: indented [icon] profession name
in the Name cell, rank/max color-coded in its own Level column to the right (was all
crammed into one text cell). Character headers span the row with name + (class).
2026-05-29 22:58:45 +02:00
florian.berthold 14dd30e9a9 coa.24: revert char-view profession grid to clean 2-slot original
release / release (push) Successful in 4s
The relocated Prof1-8 block (coa.20) was jumbled below the dropdowns in the narrow
char-view panel. Restored Prof1/Prof2 in the original bottom row (Cooking/First Aid/Prof1/Prof2);
removed Prof3-8. Full profession list stays on the Skills tab. No crash (loop stops at Prof2).
2026-05-29 22:50:04 +02:00
florian.berthold 996a11dd0a coa.23: correct CoA class names (PROPHET->Venomancer etc.) + fix Skills rank/max
release / release (push) Successful in 5s
- CoA renamed classes but UnitClass returns old tokens; added a token->name map
  (CoAClassColors.lua, from coa-omen) applied in the Skills header + the shared
  AddCharacterTooltipHeader/SetCharacterRowNameLevel helpers (fixes class names everywhere).
- Skills vertical list now shows rank/max (precompute carries maxRank; was showing /0).
2026-05-29 22:27:53 +02:00
florian.berthold d1616b4354 coa.22: Skills view as a vertical list (character header + one row per profession)
release / release (push) Successful in 6s
Rewrote the Skills tab from the cramped multi-column per-character grid to a vertical
list: each character is a header row, followed by one row per known primary profession
(incl Woodcutting/Woodworking) + secondary skills (Cooking/First Aid/Fishing) + Riding,
icon + name + rank/max, top to bottom. Simplified the column headers to match.
2026-05-29 22:04:19 +02:00
florian.berthold 3663d44cd4 coa.21: title from Lua constant (updates on /reload, unlike cached TOC metadata)
release / release (push) Successful in 5s
GetAddOnMetadata Version is cached at game launch and not refreshed by /reload, so the
title showed a stale version after reloads ('still .18'). Use a Lua constant that
re-evaluates each /reload as a truthful loaded-code indicator.
2026-05-29 20:42:03 +02:00
florian.berthold d915f6b844 coa.20: restore bigger window + relocate char-view professions
release / release (push) Successful in 5s
- Re-applied the window enlargement (832x555, more rows) - user accepts the AuctionFrame
  art seam in exchange for the bigger frame.
- Char-view professions (Prof1-8) moved out of the cramped bottom Cooking/FirstAid row
  into the open middle-left gap (2 rows of 4) so they stop overlapping/overflowing.
2026-05-29 20:30:29 +02:00
florian.berthold d440c62a73 coa.19: revert the window enlargement (restore original 832x447 frame)
release / release (push) Successful in 5s
The coa.12 enlargement couldn't extend WoW's fixed AuctionFrame art cleanly -> fragmented
background + broken scrollbar. Reverted to the original clean frame (14 rows, intact art,
working scrollbar). All crash fixes, Skills professions cap, login-scan, class icons, and
the char-view profession wrap are preserved (they live in different lines/files).
2026-05-29 20:23:40 +02:00
florian.berthold 219e749046 coa.18: guard DataStore_Achievements nil criteria quantity
release / release (push) Successful in 5s
GetAchievementCriteriaInfo returns nil reqQuantity/quantity for some CoA achievements;
'reqQuantity > 1' crashed. Guarded both.
2026-05-29 20:05:03 +02:00
florian.berthold 0a56cbe560 coa.17: comprehensive partial-data hardening + DataStore_Characters login scan + Skills strip cap
release / release (push) Successful in 5s
- Hardening sweep across DataStore_* (softened crash-asserts in Talents/Containers/Quests
  to graceful nil) + Altoholic frames (guarded remaining getter results).
- DataStore_Characters: scan on login (was ghost-gated -> name/level/class never populated;
  the core 'no character data' cause).
- Skills tab: cap inline professions at 6 (+N) so the strip stops overflowing into Cooking.
2026-05-29 19:53:03 +02:00
florian.berthold b8d619c3bb coa.16: Talents view no longer crashes on CoA custom classes
release / release (push) Successful in 5s
DataStore_Talents._GetTreeReference degrades to nil (was assert) when a custom class
(MONK, etc.) has no/partial talent reference data; Talents.lua skips background render
when GetTreeInfo returns no bg.
2026-05-29 19:36:11 +02:00
florian.berthold f305f0a226 coa.15: guard GetRestedXP + GetMoneyString/GetTimeString against no-value getters
release / release (push) Successful in 4s
GetRestedXP did 'rate * coeff' on a nil DS:GetRestXPRate result (crashed Account
Summary). Guarded it, plus defensive nil->0 on the two common string helpers.
2026-05-29 19:34:25 +02:00
florian.berthold f64d2c9250 coa.14: wrap character-view professions at 4 per row
release / release (push) Successful in 5s
_Prof5 starts a 2nd row under _Prof1 (Prof6-8 follow) so chars with many professions
don't run the icon row off the right edge of the screen.
2026-05-29 18:34:21 +02:00
florian.berthold 1faf213f17 coa.13: fix character-view crash on chars with >2 professions
release / release (push) Successful in 5s
SetItemButtonTexture now guards a nil IconTexture (was crashing whenever a button
didn't exist). UpdateViewIcons profession loop stops at the last existing _ProfN
button instead of indexing a nil frame; added _Prof3..8 so CoA chars (which can know
many professions) show more than 2. This was blocking the character detail view from
rendering when you clicked a char in the Account Summary.
2026-05-29 18:17:53 +02:00
florian.berthold ee7770baed coa.12: bigger main window with more visible rows (real size, not scale)
release / release (push) Successful in 4s
- AltoholicFrame 832x447 -> 832x555; AuctionFrame art bottom row re-anchored down
  + middle filler textures cover the gap; tab containers grown to match.
- Every scrolling tab shows more rows: 18px tabs VisibleLines 14->20, 41px tabs
  7/8->10; added matching $parentEntryN frames + extended scrollframes.
- Verified VisibleLines <= max entry per tab (no nil-row crash); luac + xmllint clean.
- Flagged for visual check: art seam at filler, decorative scrollbar track length.
2026-05-29 18:09:16 +02:00
florian.berthold e555a22f36 coa.11: fix Skills tab crash (Skill1 ButtonText name collision)
release / release (push) Successful in 5s
$parentSkill1 inherited AltoSkillButtonTemplate AND redefined a $parentNormalText
ButtonText -> duplicate global name left Skill1NormalText nil and crashed the Skills
tab on open. Made $parentSkill1 self-contained (own 325px ButtonText + scripts).
2026-05-29 17:59:05 +02:00
florian.berthold 0565051302 coa.10: CoA reputation factions (data-driven) + custom-class icons + all-professions Skills
release / release (push) Successful in 5s
- Reputation view rebuilt data-driven from each char's scanned factions grouped
  by in-game category; CoA custom factions (and future ones) appear automatically.
  Old hardcoded tree kept only as an icon lookup.
- CoA custom-class icons (classes 12-32) render from bundled atlas
  Altoholic/images/coa-classes.blp (texcoords from coa-details) instead of the
  Warrior glue-icon fallback in ShowClassIcons.
- Skills tab shows ALL known professions (dynamic list incl Woodcutting/Woodworking),
  not 2 fixed slots; DataStore_Skills scans on PLAYER_ENTERING_WORLD/SKILL_LINES_CHANGED
  (fixes 'no profession data' that only scanned on ghost-release).
2026-05-29 15:55:25 +02:00
florian.berthold ec868716ed coa.9: revert scale default + crash fixes + personal/realm bank + woodworking
release / release (push) Successful in 5s
- 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
florian.berthold ad54312ad9 coa.8: simplify title bar + default to AtlasLoot-size window
release / release (push) Successful in 6s
- Title bar: 'Altoholic <toc version>' (was 'Altoholic v.. by Thaoky (Edited by
  Telkar-RG 1.04a)'); reads GetAddOnMetadata Version so it tracks releases. Drop
  dead VERSION_STRING.
- Default UIScale 1.0 -> 1.4 (~AtlasLoot 1105x640). Apply scale in OnShow (was only
  applied after visiting Options); one-time migration bumps old-default profiles.
  Options reset/default slider -> 1.4.
2026-05-29 15:29:55 +02:00
florian.berthold 11c47a86db coa.7: guard Skills tab summary against unscanned skill ranks
release / release (push) Successful in 5s
GetColor() did floor(rank/..) with no nil-guard and riding did 'field >= 300';
skillRank1/2/cooking/firstaid/fishing/riding are nil for chars DataStore_Characters
hasn't scanned. GetColor now defaults rank to 0 and each field falls back to 0.
2026-05-29 15:22:21 +02:00
68 changed files with 1525 additions and 609 deletions
+42 -14
View File
@@ -17,7 +17,6 @@ local TEAL = "|cFF00FF9A"
local GOLD = "|cFFFFD700" local GOLD = "|cFFFFD700"
local THIS_ACCOUNT = "Default" local THIS_ACCOUNT = "Default"
local VERSION_STRING = "1.04a"
Altoholic.ClassInfo = { Altoholic.ClassInfo = {
["MAGE"] = "|cFF69CCF0", ["MAGE"] = "|cFF69CCF0",
@@ -334,7 +333,12 @@ function addon:OnEnable()
addon:RegisterEvent("AUCTION_HOUSE_SHOW", addon.AuctionHouse.OnShow) addon:RegisterEvent("AUCTION_HOUSE_SHOW", addon.AuctionHouse.OnShow)
addon:RegisterEvent("PLAYER_TALENT_UPDATE", addon.Talents.OnUpdate); 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 <version>" 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 realm = GetRealmName()
local player = UnitName("player") local player = UnitName("player")
@@ -400,7 +404,15 @@ function addon:ToggleUI()
end end
function addon:OnShow() 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:BuildList()
addon.Characters:BuildView() addon.Characters:BuildView()
@@ -485,11 +497,12 @@ function addon:SetItemButtonTexture(button, texture, width, height)
height = height or 36 height = height or 36
local itemTexture = _G[button.."IconTexture"] 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:SetWidth(width);
itemTexture:SetHeight(height); itemTexture:SetHeight(height);
itemTexture:SetAllPoints(_G[button]); itemTexture:SetAllPoints(_G[button]);
SetItemButtonTexture(_G[button], texture) SetItemButtonTexture(_G[button], texture)
end end
@@ -541,6 +554,7 @@ function addon:GetSpellIDFromRecipeLink(link)
end end
function addon:GetMoneyString(copper, color, noTexture) function addon:GetMoneyString(copper, color, noTexture)
copper = copper or 0 -- CoA: callers may pass a no-value DS getter result
color = color or "|cFFFFD700" color = color or "|cFFFFD700"
local gold = floor( copper / 10000 ); local gold = floor( copper / 10000 );
@@ -561,6 +575,7 @@ function addon:GetMoneyString(copper, color, noTexture)
end end
function addon:GetTimeString(seconds) 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 local days = floor(seconds / 86400); -- TotalTime is expressed in seconds
seconds = mod(seconds, 86400) seconds = mod(seconds, 86400)
local hours = floor(seconds / 3600); local hours = floor(seconds / 3600);
@@ -593,13 +608,17 @@ end
-- Fresh alts have partial per-module data, so every field is guarded here once -- Fresh alts have partial per-module data, so every field is guarded here once
-- instead of being copy-pasted (and missed) across the frames. -- instead of being copy-pasted (and missed) across the frames.
function Altoholic:AddCharacterTooltipHeader(character) 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:AddDoubleLine(DS:GetColoredCharacterName(character) or "?", DS:GetColoredCharacterFaction(character) or "")
AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"], AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"],
GREEN..(DS:GetCharacterLevel(character) or 0), DS:GetCharacterRace(character) or "", DS:GetCharacterClass(character) or ""), 1, 1, 1) GREEN..(DS:GetCharacterLevel(character) or 0), DS:GetCharacterRace(character) or "", className), 1, 1, 1)
end end
function Altoholic:SetCharacterRowNameLevel(entry, i, icon, character) function Altoholic:SetCharacterRowNameLevel(entry, i, icon, character)
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character) or "?", DS:GetCharacterClass(character) or "")) 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)) _G[entry..i.."Level"]:SetText(GREEN .. (DS:GetCharacterLevel(character) or 0))
end end
@@ -637,7 +656,7 @@ function Altoholic:FormatDelay(timeStamp)
end end
function addon:GetRestedXP(character) 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 local coeff = 1
if addon.Options:Get("RestXPMode") == 1 then if addon.Options:Get("RestXPMode") == 1 then
@@ -722,13 +741,22 @@ function Altoholic:ShowClassIcons()
end) end)
local _, class = DS:GetCharacterClass(character) 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"] local itemTexture = _G[itemName .. "IconTexture"]
itemTexture:SetTexture("Interface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes"); -- CoA: CLASS_ICON_TCOORDS only carries the vanilla 10 + DK on Voljin,
itemTexture:SetTexCoord(tc[1], tc[2], tc[3], tc[4]); -- 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:SetWidth(36);
itemTexture:SetHeight(36); itemTexture:SetHeight(36);
itemTexture:SetAllPoints(itemButton); itemTexture:SetAllPoints(itemButton);
+1 -1
View File
@@ -13,7 +13,7 @@
## Author: Thaoky, Telkar-RG ## Author: Thaoky, Telkar-RG
## X-Edited-By: Exiles (Sub-Net) — florian.berthold@sub-net.at ## X-Edited-By: Exiles (Sub-Net) — florian.berthold@sub-net.at
## Version: 3.3.002b-coa.6 ## Version: 3.3.002b-coa.30
## X-Category: Inventory, Tradeskill, Mail ## X-Category: Inventory, Tradeskill, Mail
## X-Localizations: enUS, frFR, zhCN, zhTW, deDE, koKR, esES, esMX, ruRU ## X-Localizations: enUS, frFR, zhCN, zhTW, deDE, koKR, esES, esMX, ruRU
## X-Website: http://wow.curse.com/downloads/wow-addons/details/altoholic.aspx ## X-Website: http://wow.curse.com/downloads/wow-addons/details/altoholic.aspx
+46 -3
View File
@@ -92,7 +92,7 @@
<Frame name="AltoholicFrame" toplevel="true" parent="UIParent" movable="true" enableMouse="true" hidden="true"> <Frame name="AltoholicFrame" toplevel="true" parent="UIParent" movable="true" enableMouse="true" hidden="true">
<Size> <Size>
<AbsDimension x="832" y="447"/> <AbsDimension x="832" y="555"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
@@ -172,6 +172,49 @@
</Anchor> </Anchor>
</Anchors> </Anchors>
</Texture> </Texture>
<!-- Filler strip covering the gap opened between the top row (ends at y=-256) and the
re-anchored bottom row (top now at y=-364). 108px tall = +6 content rows.
Uses a thin horizontal slice from the vertical middle (flat parchment / side border)
of the same AuctionFrame-Browse tiles, stretched over the 108px gap. -->
<Texture name="$parentMidLeft" file="Interface\AuctionFrame\UI-AuctionFrame-Browse-BotLeft">
<Size>
<AbsDimension x="256" y="108"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="0" y="-256"/>
</Offset>
</Anchor>
</Anchors>
<TexCoords left="0" right="1.0" top="0.46" bottom="0.54"/>
</Texture>
<Texture name="$parentMid" file="Interface\AuctionFrame\UI-AuctionFrame-Browse-Bot">
<Size>
<AbsDimension x="320" y="108"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="256" y="-256"/>
</Offset>
</Anchor>
</Anchors>
<TexCoords left="0" right="1.0" top="0.46" bottom="0.54"/>
</Texture>
<Texture name="$parentMidRight" file="Interface\AuctionFrame\UI-AuctionFrame-Browse-BotRight">
<Size>
<AbsDimension x="256" y="108"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentMid" relativePoint="TOPRIGHT">
<Offset>
<AbsDimension x="0" y="0"/>
</Offset>
</Anchor>
</Anchors>
<TexCoords left="0" right="1.0" top="0.46" bottom="0.54"/>
</Texture>
<Texture name="$parentBotLeft" file="Interface\AuctionFrame\UI-AuctionFrame-Browse-BotLeft"> <Texture name="$parentBotLeft" file="Interface\AuctionFrame\UI-AuctionFrame-Browse-BotLeft">
<Size> <Size>
<AbsDimension x="256" y="256"/> <AbsDimension x="256" y="256"/>
@@ -179,7 +222,7 @@
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
<Offset> <Offset>
<AbsDimension x="0" y="-256"/> <AbsDimension x="0" y="-364"/>
</Offset> </Offset>
</Anchor> </Anchor>
</Anchors> </Anchors>
@@ -191,7 +234,7 @@
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
<Offset> <Offset>
<AbsDimension x="256" y="-256"/> <AbsDimension x="256" y="-364"/>
</Offset> </Offset>
</Anchor> </Anchor>
</Anchors> </Anchors>
+18 -28
View File
@@ -76,8 +76,6 @@ local function AddRealm(AccountName, RealmName)
local realmBankSlots = 0 local realmBankSlots = 0
local realmFreeBankSlots = 0 local realmFreeBankSlots = 0
local SkillsCache = { {name = "", rank = 0}, {name = "", rank = 0} }
-- 1) Add the realm name -- 1) Add the realm name
table.insert(characterList, { linetype = INFO_REALM_LINE + (realmCount*3), table.insert(characterList, { linetype = INFO_REALM_LINE + (realmCount*3),
isCollapsed = false, isCollapsed = false,
@@ -87,36 +85,28 @@ local function AddRealm(AccountName, RealmName)
-- 2) Add the characters -- 2) Add the characters
for characterName, character in pairs(DataStore:GetCharacters(RealmName, AccountName)) do for characterName, character in pairs(DataStore:GetCharacters(RealmName, AccountName)) do
SkillsCache[1].name = "" -- CoA: characters can know ALL professions at once (no retail 2-primary
SkillsCache[1].rank = 0 -- limit) plus the customs Woodcutting/Woodworking. Build a dynamic list of
SkillsCache[1].spellID = nil -- every known primary profession instead of the old fixed 2 slots. Each
SkillsCache[2].name = "" -- entry carries its own name/rank/spellID(icon) so Skills.lua can render an
SkillsCache[2].rank = 0 -- arbitrary number of professions. GetPrimaryProfessionList never returns
SkillsCache[2].spellID = nil -- nil (returns {} for unscanned chars), but guard anyway.
local professions = {}
local i = 1 if DataStore.GetPrimaryProfessionList then
local professions = DataStore:GetPrimaryProfessions(character) local list = DataStore:GetPrimaryProfessionList(character) or {}
if professions then for _, p in ipairs(list) do
for SkillName, s in pairs(professions) do professions[#professions + 1] = {
SkillsCache[i].name = SkillName name = p.name,
SkillsCache[i].rank = DataStore:GetSkillInfo(character, SkillName) rank = p.rank or 0,
SkillsCache[i].spellID = DataStore:GetProfessionSpellID(SkillName) maxRank = p.maxRank or 0, -- CoA: needed for the "rank/max" display in the vertical Skills list
i = i + 1 spellID = DataStore:GetProfessionSpellID(p.name),
}
if i > 2 then -- it seems that under certain conditions, the loop continues after 2 professions.., so break
break
end
end end
end end
table.insert(characterList, { linetype = INFO_CHARACTER_LINE + (realmCount*3), table.insert(characterList, { linetype = INFO_CHARACTER_LINE + (realmCount*3),
key = character, key = character,
skillName1 = SkillsCache[1].name, professions = professions, -- CoA: dynamic list of all primary professions
skillRank1 = SkillsCache[1].rank,
spellID1 = SkillsCache[1].spellID,
skillName2 = SkillsCache[2].name,
skillRank2 = SkillsCache[2].rank,
spellID2 = SkillsCache[2].spellID,
cooking = DataStore:GetCookingRank(character), cooking = DataStore:GetCookingRank(character),
firstaid = DataStore:GetFirstAidRank(character), firstaid = DataStore:GetFirstAidRank(character),
fishing = DataStore:GetFishingRank(character), fishing = DataStore:GetFishingRank(character),
+91 -1
View File
@@ -24,7 +24,8 @@
-- Source of truth: db.exil.es /coa/dev for the full palette; -- Source of truth: db.exil.es /coa/dev for the full palette;
-- _G.RAID_CLASS_COLORS at FrameXML load time for the running client. -- _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 if type(AC) ~= "table" then return end
local source = _G.RAID_CLASS_COLORS local source = _G.RAID_CLASS_COLORS
@@ -45,3 +46,92 @@ for token, color in pairs(source) do
end end
end 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
+1 -1
View File
@@ -168,7 +168,7 @@ end
function ns:Update() function ns:Update()
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameSummary" local frame = "AltoholicFrameSummary"
local entry = frame.."Entry" local entry = frame.."Entry"
+31 -1
View File
@@ -168,7 +168,7 @@
<Frame name="AltoholicFrameSummary" parent="AltoholicTabSummary"> <Frame name="AltoholicFrameSummary" parent="AltoholicTabSummary">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -294,6 +294,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoCharacterSummaryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoCharacterSummaryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoCharacterSummaryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoCharacterSummaryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoCharacterSummaryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoCharacterSummaryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Frame name="$parentRightClickMenu" inherits="UIDropDownMenuTemplate" id="1" hidden="true"> <Frame name="$parentRightClickMenu" inherits="UIDropDownMenuTemplate" id="1" hidden="true">
<Size> <Size>
+1 -1
View File
@@ -23,7 +23,7 @@ local ns = addon.Activity -- ns = namespace
local Characters = addon.Characters local Characters = addon.Characters
function ns:Update() function ns:Update()
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameActivity" local frame = "AltoholicFrameActivity"
local entry = frame.."Entry" local entry = frame.."Entry"
+31 -1
View File
@@ -176,7 +176,7 @@
<Frame name="AltoholicFrameActivity" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameActivity" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -302,6 +302,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoActivityTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoActivityTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoActivityTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoActivityTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoActivityTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoActivityTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+2 -2
View File
@@ -136,7 +136,7 @@ function ns:InvalidateView()
end end
function ns:UpdateAuctions() function ns:UpdateAuctions()
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameAuctions" local frame = "AltoholicFrameAuctions"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -217,7 +217,7 @@ function ns:UpdateAuctions()
end end
function ns:UpdateBids() function ns:UpdateBids()
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameAuctions" local frame = "AltoholicFrameAuctions"
local entry = frame.."Entry" local entry = frame.."Entry"
+16 -1
View File
@@ -106,7 +106,7 @@
<Frame name="AltoholicFrameAuctions" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameAuctions" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -222,6 +222,21 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry8" inherits="AltoAuctionEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry9" inherits="AltoAuctionEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoAuctionEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+21 -11
View File
@@ -22,7 +22,7 @@ local ns = addon.BagUsage -- ns = namespace
local Characters = addon.Characters local Characters = addon.Characters
function ns:Update() function ns:Update()
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameBagUsage" local frame = "AltoholicFrameBagUsage"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -118,15 +118,15 @@ function ns:Update()
_G[entry..i.."BankSlotsNormalText"]:SetText(L["Bank not visited yet"]) _G[entry..i.."BankSlotsNormalText"]:SetText(L["Bank not visited yet"])
else else
_G[entry..i.."BankSlotsNormalText"]:SetText(format("%s/%s|r/%s|r/%s|r/%s|r/%s|r/%s|r/%s |r(%s|r)", _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), DS:GetContainerSize(character, 100) or 0, -- CoA: empty/unscanned bank bags return nil size
WHITE .. DS:GetContainerSize(character, 5), WHITE .. (DS:GetContainerSize(character, 5) or 0),
WHITE .. DS:GetContainerSize(character, 6), WHITE .. (DS:GetContainerSize(character, 6) or 0),
WHITE .. DS:GetContainerSize(character, 7), WHITE .. (DS:GetContainerSize(character, 7) or 0),
WHITE .. DS:GetContainerSize(character, 8), WHITE .. (DS:GetContainerSize(character, 8) or 0),
WHITE .. DS:GetContainerSize(character, 9), WHITE .. (DS:GetContainerSize(character, 9) or 0),
WHITE .. DS:GetContainerSize(character, 10), WHITE .. (DS:GetContainerSize(character, 10) or 0),
WHITE .. DS:GetContainerSize(character, 11), WHITE .. (DS:GetContainerSize(character, 11) or 0),
CYAN .. DS:GetNumBankSlots(character))) CYAN .. (DS:GetNumBankSlots(character) or 0)))
end end
elseif (lineType == INFO_TOTAL_LINE) then elseif (lineType == INFO_TOTAL_LINE) then
_G[entry..i.."Collapse"]:Hide() _G[entry..i.."Collapse"]:Hide()
@@ -218,5 +218,15 @@ function ns:OnEnter(self)
AltoTooltip:AddLine(" ",1,1,1); AltoTooltip:AddLine(" ",1,1,1);
AltoTooltip:AddLine(CYAN .. numSlots .. " |r" .. L["slots"] .. " (" .. GREEN .. numFree .. "|r " ..L["free"] .. ") ",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 end
+31 -1
View File
@@ -177,7 +177,7 @@
<Frame name="AltoholicFrameBagUsage" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameBagUsage" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -303,6 +303,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoBagUsageTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoBagUsageTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoBagUsageTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoBagUsageTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoBagUsageTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoBagUsageTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -764,7 +764,7 @@ function Altoholic.Calendar.Events:BuildList()
self:BuildView() self:BuildView()
end end
local NUM_EVENTLINES = 14 local NUM_EVENTLINES = 20
function Altoholic.Calendar.Events:Update() function Altoholic.Calendar.Events:Update()
local self = Altoholic.Calendar.Events local self = Altoholic.Calendar.Events
+32 -2
View File
@@ -131,7 +131,7 @@
<Frame name="AltoholicFrameCalendar" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameCalendar" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -286,7 +286,7 @@
<ScrollFrame name="$parentScrollFrame" inherits="FauxScrollFrameTemplate"> <ScrollFrame name="$parentScrollFrame" inherits="FauxScrollFrameTemplate">
<Size> <Size>
<AbsDimension x="250" y="306"/> <AbsDimension x="250" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
@@ -406,6 +406,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoCalendarEventTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoCalendarEventTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoCalendarEventTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoCalendarEventTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoCalendarEventTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoCalendarEventTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+5 -5
View File
@@ -96,7 +96,7 @@ end
local function UpdateSpread() local function UpdateSpread()
local mode = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectContainerView) local mode = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectContainerView)
local rarity = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectRarity) local rarity = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectRarity)
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameContainers" local frame = "AltoholicFrameContainers"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -177,8 +177,8 @@ local function UpdateSpread()
local slotID = bagIndices[line].from - 3 + j local slotID = bagIndices[line].from - 3 + j
local itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID) 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 if itemID then
Altoholic:SetItemButtonTexture(itemName, GetItemIcon(itemID)); Altoholic:SetItemButtonTexture(itemName, GetItemIcon(itemID));
@@ -242,7 +242,7 @@ end
local function UpdateAllInOne() local function UpdateAllInOne()
local mode = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectContainerView) local mode = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectContainerView)
local rarity = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectRarity) local rarity = UIDropDownMenu_GetSelectedValue(AltoholicFrameContainers_SelectRarity)
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameContainers" local frame = "AltoholicFrameContainers"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -278,7 +278,7 @@ local function UpdateAllInOne()
local container = DS:GetContainer(character, containerID) local container = DS:GetContainer(character, containerID)
local _, _, containerSize = DS:GetContainerInfo(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) local itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID)
if itemID then if itemID then
currentSlotIndex = currentSlotIndex + 1 currentSlotIndex = currentSlotIndex + 1
+16 -1
View File
@@ -137,7 +137,7 @@
<Frame name="AltoholicFrameContainers" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameContainers" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -282,6 +282,21 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry8" inherits="AltoContainerTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry9" inherits="AltoContainerTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoContainerTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -177,7 +177,7 @@ local function Currencies_UpdateEx(self, offset, entry, desc)
end end
local CurrenciesScrollFrame_Desc = { local CurrenciesScrollFrame_Desc = {
NumLines = 8, NumLines = 10,
LineHeight = 41, LineHeight = 41,
Frame = "AltoholicFrameCurrencies", Frame = "AltoholicFrameCurrencies",
GetSize = function() return #usedTokens end, GetSize = function() return #usedTokens end,
+11 -1
View File
@@ -175,7 +175,7 @@
<Frame name="AltoholicFrameCurrencies" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameCurrencies" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -302,6 +302,16 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry9" inherits="AltoCurrenciesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoCurrenciesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -415,7 +415,7 @@ function ns:GetInventoryTypeName(inv)
end end
function ns:Update() function ns:Update()
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameEquipment" local frame = "AltoholicFrameEquipment"
local entry = frame.."Entry" local entry = frame.."Entry"
+16 -1
View File
@@ -117,7 +117,7 @@
<Frame name="AltoholicFrameEquipment" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameEquipment" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -213,6 +213,21 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry8" inherits="AltoEquipmentEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry9" inherits="AltoEquipmentEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoEquipmentEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -72,7 +72,7 @@ function ns:Update()
BuildView() BuildView()
end end
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameGuildBankTabs" local frame = "AltoholicFrameGuildBankTabs"
local entry = frame.."Entry" local entry = frame.."Entry"
+31 -1
View File
@@ -107,7 +107,7 @@
<Frame name="AltoholicFrameGuildBankTabs" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameGuildBankTabs" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -233,6 +233,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoGuildBankTabsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoGuildBankTabsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoGuildBankTabsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoGuildBankTabsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoGuildBankTabsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoGuildBankTabsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+6 -4
View File
@@ -81,9 +81,9 @@ local SecondaryLevelSort = {-- sort functions for the alts
end end
end, end,
["level"] = function(a, b) ["level"] = function(a, b)
local levelA = select(4, DataStore:GetGuildMemberInfo(a)) 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)) local levelB = select(4, DataStore:GetGuildMemberInfo(b)) or 0
if viewSortOrder then if viewSortOrder then
return levelA < levelB return levelA < levelB
else else
@@ -285,7 +285,7 @@ function ns:Update()
BuildView() BuildView()
end end
local VisibleLines = 14 local VisibleLines = 17
local frame = "AltoholicFrameGuildMembers" local frame = "AltoholicFrameGuildMembers"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -460,7 +460,9 @@ function ns:Level_OnClick(self, button)
if button ~= "LeftButton" then return end if button ~= "LeftButton" then return end
local id = self:GetParent():GetID() 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] 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 if line.lineType == NORMALPLAYER_LINE then return end
local member = self:GetParent().CharName local member = self:GetParent().CharName
+31 -1
View File
@@ -159,7 +159,7 @@
<Frame name="AltoholicFrameGuildMembers" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameGuildMembers" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -307,6 +307,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoGuildMembersTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoGuildMembersTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoGuildMembersTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoGuildMembersTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoGuildMembersTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoGuildMembersTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<!-- Equipment Icons on the right of the frame --> <!-- Equipment Icons on the right of the frame -->
<Button name="$parentItem1" inherits="AltoAltEquipmentTemplate"> <Button name="$parentItem1" inherits="AltoAltEquipmentTemplate">
+10 -8
View File
@@ -25,9 +25,9 @@ local PrimaryLevelSort = { -- sort functions for the mains
end end
end, end,
["level"] = function(a, b) ["level"] = function(a, b)
local levelA = select(4, DataStore:GetGuildMemberInfo(a.name)) local levelA = select(4, DataStore:GetGuildMemberInfo(a.name)) or 0 -- CoA: nil level on partial guild data crashed table.sort
local levelB = select(4, DataStore:GetGuildMemberInfo(b.name)) local levelB = select(4, DataStore:GetGuildMemberInfo(b.name)) or 0
if viewSortOrder then if viewSortOrder then
return levelA < levelB return levelA < levelB
else else
@@ -76,9 +76,9 @@ local SecondaryLevelSort = {-- sort functions for the alts
end end
end, end,
["level"] = function(a, b) ["level"] = function(a, b)
local levelA = select(4, DataStore:GetGuildMemberInfo(a)) 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)) local levelB = select(4, DataStore:GetGuildMemberInfo(b)) or 0
if viewSortOrder then if viewSortOrder then
return levelA < levelB return levelA < levelB
else else
@@ -199,6 +199,7 @@ local function DisplayProfessionLink(frameName, member, index)
local icon = addon:TextureToFontstring(addon:GetSpellIcon(tonumber(spellID)), 18, 18) .. " " local icon = addon:TextureToFontstring(addon:GetSpellIcon(tonumber(spellID)), 18, 18) .. " "
if link then if link then
local curRank, maxRank = DataStore:GetProfessionInfo(link) local curRank, maxRank = DataStore:GetProfessionInfo(link)
curRank, maxRank = curRank or 0, maxRank or 0 -- CoA: GetProfessionInfo returns nil if the link doesn't match the trade pattern
local ts = addon.TradeSkills local ts = addon.TradeSkills
text:SetText(icon .. ts:GetColor(curRank) .. curRank .. "/" .. maxRank) text:SetText(icon .. ts:GetColor(curRank) .. curRank .. "/" .. maxRank)
else else
@@ -220,7 +221,7 @@ function ns:Update()
BuildView() BuildView()
end end
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameGuildProfessions" local frame = "AltoholicFrameGuildProfessions"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -350,7 +351,8 @@ function ns:OnEnter(self)
if not spellID or not link then return end if not spellID or not link then return end
local curRank, maxRank = DataStore:GetProfessionInfo(link) local curRank, maxRank = DataStore:GetProfessionInfo(link)
curRank, maxRank = curRank or 0, maxRank or 0 -- CoA: nil ranks when link doesn't match trade pattern; guard concat below
AltoTooltip:ClearLines(); AltoTooltip:ClearLines();
AltoTooltip:SetOwner(self, "ANCHOR_RIGHT"); AltoTooltip:SetOwner(self, "ANCHOR_RIGHT");
+31 -1
View File
@@ -143,7 +143,7 @@
<Frame name="AltoholicFrameGuildProfessions" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameGuildProfessions" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -269,6 +269,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoGuildProfessionsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoGuildProfessionsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoGuildProfessionsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoGuildProfessionsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoGuildProfessionsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoGuildProfessionsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -461,7 +461,7 @@ function ns:Update()
AltoTooltip:Hide(); AltoTooltip:Hide();
GameTooltip:Hide(); GameTooltip:Hide();
local VisibleLines = 8 local VisibleLines = 10
local NumLines = VisibleLines local NumLines = VisibleLines
local frame = "AltoholicFrameKeys" local frame = "AltoholicFrameKeys"
local entry = frame.."Entry" local entry = frame.."Entry"
+11 -1
View File
@@ -221,7 +221,7 @@
<Frame name="AltoholicFrameKeys" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameKeys" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -317,6 +317,16 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry9" inherits="AltoKeyEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoKeyEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -81,7 +81,7 @@ function ns:BuildView(field, ascending)
end end
function ns:Update() function ns:Update()
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameMail" local frame = "AltoholicFrameMail"
local entry = frame.."Entry" local entry = frame.."Entry"
local player = addon:GetCurrentCharacter() local player = addon:GetCurrentCharacter()
+16 -1
View File
@@ -84,7 +84,7 @@
<Frame name="AltoholicFrameMail" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameMail" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -190,6 +190,21 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry8" inherits="AltoMailEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry9" inherits="AltoMailEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoMailEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -212,7 +212,7 @@ function ns:UpdatePets()
end end
function ns:UpdatePetsAllInOne() function ns:UpdatePetsAllInOne()
local VisibleLines = 8 local VisibleLines = 10
local frame = "AltoholicFramePetsAllInOne" local frame = "AltoholicFramePetsAllInOne"
local entry = frame.."Entry" local entry = frame.."Entry"
+12 -2
View File
@@ -25,7 +25,7 @@
<Frame name="AltoholicFramePets" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFramePets" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -333,7 +333,7 @@
<Frame name="$parentAllInOne" hidden="true"> <Frame name="$parentAllInOne" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
@@ -429,6 +429,16 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry9" inherits="AltoIconListtEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoIconListtEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -34,7 +34,7 @@ function ns:Update()
local character = addon.Tabs.Characters:GetCurrent() local character = addon.Tabs.Characters:GetCurrent()
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameQuests" local frame = "AltoholicFrameQuests"
local entry = frame.."Entry" local entry = frame.."Entry"
+31 -1
View File
@@ -106,7 +106,7 @@
<Frame name="AltoholicFrameQuests" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameQuests" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -232,6 +232,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoQuestEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoQuestEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoQuestEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoQuestEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoQuestEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoQuestEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+1 -1
View File
@@ -218,7 +218,7 @@ end
function ns:Update() function ns:Update()
local currentProfession = addon.TradeSkills.CurrentProfession local currentProfession = addon.TradeSkills.CurrentProfession
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameRecipes" local frame = "AltoholicFrameRecipes"
local entry = frame.."Entry" local entry = frame.."Entry"
+31 -1
View File
@@ -196,7 +196,7 @@
<Frame name="AltoholicFrameRecipes" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameRecipes" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -459,6 +459,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoRecipesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoRecipesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoRecipesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoRecipesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoRecipesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoRecipesEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+129 -35
View File
@@ -14,6 +14,13 @@ local DARK_RED = "|cFFF00000"
local ICON_UNKNOWN = "\124TInterface\\RaidFrame\\ReadyCheck-NotReady:14\124t" local ICON_UNKNOWN = "\124TInterface\\RaidFrame\\ReadyCheck-NotReady:14\124t"
local ICON_EXALTED = "\124TInterface\\RaidFrame\\ReadyCheck-Ready: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 = { local Factions = {
-- Factions reference table, based on http://www.wowwiki.com/Factions -- Factions reference table, based on http://www.wowwiki.com/Factions
{ -- [1] { -- [1]
@@ -151,8 +158,96 @@ local VertexColors = {
[FACTION_STANDING_LABEL8] = { r = 1.0, g = 1.0, b = 1.0 }, -- exalted [FACTION_STANDING_LABEL8] = { r = 1.0, g = 1.0, b = 1.0 }, -- exalted
} }
local currentXPack = 1 -- default to wow classic local GENERIC_FACTION_ICON = "Achievement_Reputation_01" -- fallback icon for factions not in the lookup (ex: CoA custom factions)
local currentFactionGroup = (UnitFactionGroup("player") == "Alliance") and 1 or 2 -- default to alliance or horde
-- 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 = {} addon.Reputations = {}
@@ -194,46 +289,43 @@ local function DDM_AddCloseMenu()
UIDropDownMenu_AddButton(info, 1) UIDropDownMenu_AddButton(info, 1)
end end
local function DDM_OnClick(self, xpackIndex, factionGroupIndex) local function DDM_OnClick(self, header)
currentXPack = xpackIndex currentGroup = header or ALL_GROUPS
currentFactionGroup = factionGroupIndex
UIDropDownMenu_SetText(AltoholicFrameReputations_SelectFaction, GroupLabel(currentGroup))
local factionGroup = Factions[currentXPack][currentFactionGroup]
UIDropDownMenu_SetText(AltoholicFrameReputations_SelectFaction, factionGroup.name)
ns:Update() ns:Update()
end end
local function Reputations_UpdateEx(self, offset, entry, desc) local function Reputations_UpdateEx(self, offset, entry, desc)
local line local line
local size = desc:GetSize() local size = desc:GetSize()
local DS = DataStore local DS = DataStore
local realm, account = addon:GetCurrentRealm() local realm, account = addon:GetCurrentRealm()
local character local character
local factionGroup = Factions[currentXPack][currentFactionGroup]
for i=1, desc.NumLines do for i=1, desc.NumLines do
line = i + offset line = i + offset
if line <= size then if line <= size then
local faction = factionGroup[line] local factionName = displayedFactions[line]
_G[entry..i.."Name"]:SetText(WHITE .. faction.name) _G[entry..i.."Name"]:SetText(WHITE .. (factionName or ""))
_G[entry..i.."Name"]:SetJustifyH("LEFT") _G[entry..i.."Name"]:SetJustifyH("LEFT")
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 15, 0) _G[entry..i.."Name"]:SetPoint("TOPLEFT", 15, 0)
for j = 1, 10 do -- loop through the 10 alts for j = 1, 10 do -- loop through the 10 alts
local itemName = entry.. i .. "Item" .. j; local itemName = entry.. i .. "Item" .. j;
local itemButton = _G[itemName] local itemButton = _G[itemName]
local classButton = _G["AltoholicFrameClassesItem" .. j] local classButton = _G["AltoholicFrameClassesItem" .. j]
local itemTexture = _G[itemName .. "_Background"] local itemTexture = _G[itemName .. "_Background"]
itemTexture:SetTexture("Interface\\Icons\\"..faction.icon) itemTexture:SetTexture("Interface\\Icons\\"..GetFactionIcon(factionName))
local status, rate local status, rate
if classButton.CharName then -- if there's an alt in this column.. if classButton.CharName then -- if there's an alt in this column..
character = DS:GetCharacter(classButton.CharName, realm, account) 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 if status and rate then
local vc = VertexColors[status] local vc = VertexColors[status]
@@ -273,25 +365,27 @@ local function Reputations_UpdateEx(self, offset, entry, desc)
end end
local ReputationsScrollFrame_Desc = { local ReputationsScrollFrame_Desc = {
NumLines = 8, NumLines = 10,
LineHeight = 41, LineHeight = 41,
Frame = "AltoholicFrameReputations", Frame = "AltoholicFrameReputations",
GetSize = function() return #Factions[currentXPack][currentFactionGroup] end, GetSize = function() return #displayedFactions end,
Update = Reputations_UpdateEx, Update = Reputations_UpdateEx,
} }
function ns:DropDownFaction_Initialize() function ns:DropDownFaction_Initialize()
for xpackIndex, xpack in ipairs(Factions) do -- Dropdown is built dynamically from the categories actually scanned across all characters.
DDM_AddTitle(xpack.name) BuildModel()
for factionGroupIndex, factionGroup in ipairs(Factions[xpackIndex]) do DDM_Add(GroupLabel(ALL_GROUPS), DDM_OnClick, ALL_GROUPS) -- "All factions" pseudo-group
DDM_Add(factionGroup.name, DDM_OnClick, xpackIndex, factionGroupIndex) for _, header in ipairs(displayedGroups) do
end DDM_Add(GroupLabel(header), DDM_OnClick, header)
end end
DDM_AddCloseMenu() DDM_AddCloseMenu()
end end
function ns:Update() function ns:Update()
BuildModel()
UIDropDownMenu_SetText(AltoholicFrameReputations_SelectFaction, GroupLabel(currentGroup))
addon:ScrollFrameUpdate(ReputationsScrollFrame_Desc) addon:ScrollFrameUpdate(ReputationsScrollFrame_Desc)
end end
@@ -302,12 +396,12 @@ function ns:OnEnter(frame)
local DS = DataStore local DS = DataStore
local realm, account = addon:GetCurrentRealm() local realm, account = addon:GetCurrentRealm()
local character = DS:GetCharacter(charName, realm, account) local character = DS:GetCharacter(charName, realm, account)
local factionGroup = Factions[currentXPack][currentFactionGroup] local faction = displayedFactions[ frame:GetParent():GetID() ]
local faction = factionGroup[ frame:GetParent():GetID() ].name if not faction then return end
local status, currentLevel, maxLevel, rate = DS:GetReputationInfo(character, faction) local status, currentLevel, maxLevel, rate = DS:GetReputationInfo(character, faction)
if not status then return end if not status then return end
AltoTooltip:SetOwner(frame, "ANCHOR_LEFT"); AltoTooltip:SetOwner(frame, "ANCHOR_LEFT");
AltoTooltip:ClearLines(); AltoTooltip:ClearLines();
AltoTooltip:AddLine((DS:GetColoredCharacterName(character) or "?") .. WHITE .. " @ " .. TEAL .. faction,1,1,1); AltoTooltip:AddLine((DS:GetColoredCharacterName(character) or "?") .. WHITE .. " @ " .. TEAL .. faction,1,1,1);
@@ -346,12 +440,12 @@ function ns:OnClick(frame, button)
local DS = DataStore local DS = DataStore
local realm, account = addon:GetCurrentRealm() local realm, account = addon:GetCurrentRealm()
local character = DS:GetCharacter(charName, realm, account) local character = DS:GetCharacter(charName, realm, account)
local factionGroup = Factions[currentXPack][currentFactionGroup] local faction = displayedFactions[ frame:GetParent():GetID() ]
local faction = factionGroup[ frame:GetParent():GetID() ].name if not faction then return end
local status, currentLevel, maxLevel, rate = DS:GetReputationInfo(character, faction) local status, currentLevel, maxLevel, rate = DS:GetReputationInfo(character, faction)
if not status then return end if not status then return end
if ( button == "LeftButton" ) and ( IsShiftKeyDown() ) then if ( button == "LeftButton" ) and ( IsShiftKeyDown() ) then
local chat = ChatEdit_GetLastActiveWindow() local chat = ChatEdit_GetLastActiveWindow()
if chat:IsShown() then if chat:IsShown() then
+15 -5
View File
@@ -178,7 +178,7 @@
<Frame name="AltoholicFrameReputations" parent="AltoholicTabCharacters" hidden="true"> <Frame name="AltoholicFrameReputations" parent="AltoholicTabCharacters" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -213,11 +213,11 @@
</Anchors> </Anchors>
<Scripts> <Scripts>
<OnLoad> <OnLoad>
local faction = (UnitFactionGroup("player") == "Alliance") and FACTION_ALLIANCE or FACTION_HORDE -- Default to "All factions"; the dropdown contents are built dynamically
-- from the categories actually scanned (see Reputations.lua / BuildModel).
UIDropDownMenu_SetWidth(self, 100) UIDropDownMenu_SetWidth(self, 140)
UIDropDownMenu_SetButtonWidth(self, 20) UIDropDownMenu_SetButtonWidth(self, 20)
UIDropDownMenu_SetText(self, faction) UIDropDownMenu_SetText(self, ALL)
UIDropDownMenu_Initialize(self, Altoholic.Reputations.DropDownFaction_Initialize) UIDropDownMenu_Initialize(self, Altoholic.Reputations.DropDownFaction_Initialize)
</OnLoad> </OnLoad>
</Scripts> </Scripts>
@@ -309,6 +309,16 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry9" inherits="AltoReputationEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoReputationEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+39 -5
View File
@@ -239,7 +239,7 @@ function ns:Realm_Update()
end end
function ns:Loots_Update() function ns:Loots_Update()
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameSearch" local frame = "AltoholicFrameSearch"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -311,7 +311,7 @@ function ns:Loots_Update()
end end
function ns:Upgrade_Update() function ns:Upgrade_Update()
local VisibleLines = 7 local VisibleLines = 10
local frame = "AltoholicFrameSearch" local frame = "AltoholicFrameSearch"
local entry = frame.."Entry" local entry = frame.."Entry"
@@ -603,15 +603,17 @@ local function BrowseCharacter(character)
currentResultLocation = L["Bank"] currentResultLocation = L["Bank"]
elseif (containerName == "Bag-2") then elseif (containerName == "Bag-2") then
currentResultLocation = KEYRING currentResultLocation = KEYRING
elseif string.sub(containerName, 1, 5) == "PBank" then -- CoA personal bank tab
currentResultLocation = "Personal Bank"
else else
local bagNum = tonumber(string.sub(containerName, 4)) local bagNum = tonumber(string.sub(containerName, 4))
if (bagNum >= 0) and (bagNum <= 4) then if (bagNum >= 0) and (bagNum <= 4) then
currentResultLocation = L["Bags"] currentResultLocation = L["Bags"]
else else
currentResultLocation = L["Bank"] currentResultLocation = L["Bank"]
end end
end end
for slotID = 1, container.size do for slotID = 1, container.size do
itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID) itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID)
@@ -710,7 +712,39 @@ local function BrowseRealm(realm, account, bothFactions)
currentResultType = nil currentResultType = nil
currentResultLocation = nil currentResultLocation = nil
end 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 ? if addon.Options:Get("IncludeGuildSkills") == 1 and string.len(currentValue) > 1 then -- Check guild professions ?
local guild = addon:GetGuild() local guild = addon:GetGuild()
if guild and LTL then -- LTL won't be valid if there's a version mismatch (see :Init() ) if guild and LTL then -- LTL won't be valid if there's a version mismatch (see :Init() )
+16 -1
View File
@@ -240,7 +240,7 @@
<Frame name="AltoholicFrameSearch" parent="AltoholicTabSearch" hidden="true"> <Frame name="AltoholicFrameSearch" parent="AltoholicTabSearch" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -331,6 +331,21 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry6" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry8" inherits="AltoSearchEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry7" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry9" inherits="AltoSearchEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry8" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry10" inherits="AltoSearchEntryTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry9" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+114 -351
View File
@@ -28,378 +28,141 @@ local Characters = addon.Characters
local size = 22 local size = 22
local inset = 2 local inset = 2
function ns:Update() function ns:Update()
local VisibleLines = 14 local VisibleLines = 18
local frame = "AltoholicFrameSkills" local frame = "AltoholicFrameSkills"
local entry = frame.."Entry" local entry = frame.."Entry"
local DS = DataStore local DS = DataStore
local offset = FauxScrollFrame_GetOffset( _G[ frame.."ScrollFrame" ] ); -- CoA: vertical list. For each visible character a header row, then one row per known
local DisplayedCount = 0 -- profession / secondary skill (icon + name + rank/max), top to bottom (like the other detail views).
local VisibleCount = 0 local SECONDARY = { 2550, 3273, 7733 } -- Cooking, First Aid, Fishing (spell id -> icon + GetSpellInfo name)
local DrawRealm local items = {}
local i=1 for _, viewLine in pairs(Characters:GetView()) do
if Characters:GetLineType(viewLine) == INFO_CHARACTER_LINE then
for _, line in pairs(Characters:GetView()) do local character = DS:GetCharacter( Characters:GetInfo(viewLine) )
local lineType = Characters:GetLineType(line) items[#items + 1] = { kind = "header", viewLine = viewLine, character = character }
if (offset > 0) or (DisplayedCount >= VisibleLines) then -- if the line will not be visible local profs = Characters:GetField(viewLine, "professions")
if lineType == INFO_REALM_LINE then -- then keep track of counters if profs then
if Characters:GetField(line, "isCollapsed") == false then for _, p in ipairs(profs) do
DrawRealm = true items[#items + 1] = { kind = "skill", viewLine = viewLine, character = character,
else spellID = p.spellID, name = p.name, rank = p.rank or 0, maxRank = p.maxRank or 0 }
DrawRealm = false
end 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 end
else -- line will be displayed for _, sid in ipairs(SECONDARY) do
if lineType == INFO_REALM_LINE then local sName = GetSpellInfo(sid)
local _, realm, account = Characters:GetInfo(line) if sName then
local cur, max = DS:GetSkillInfo(character, sName)
if Characters:GetField(line, "isCollapsed") == false then if cur and cur > 0 then
_G[ entry..i.."Collapse" ]:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up"); items[#items + 1] = { kind = "skill", viewLine = viewLine, character = character,
DrawRealm = true spellID = sid, name = sName, rank = cur, maxRank = max or 0 }
else end
_G[ entry..i.."Collapse" ]:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up");
DrawRealm = false
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)
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")
_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 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
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); local offset = FauxScrollFrame_GetOffset( _G[ frame.."ScrollFrame" ] )
end 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) function ns:OnEnter(frame)
local line = frame:GetParent():GetID() local item = frame:GetParent() and frame:GetParent().coaItem
local lineType = Characters:GetLineType(line) if not item or item.kind ~= "skill" then return end
if lineType ~= INFO_CHARACTER_LINE then AltoTooltip:ClearLines()
return AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT")
end AltoTooltip:AddLine(WHITE .. item.name, 1, 1, 1)
local cap = (item.maxRank > 0) and item.maxRank or 450
local id = frame:GetID() AltoTooltip:AddLine( ns:GetColor(item.rank, cap) .. item.rank .. " / " .. item.maxRank, 1, 1, 1 )
local skillName, rank, suggestion AltoTooltip:Show()
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)
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 >= 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 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();
end end
local VIEW_MOUNTS = 8
function ns:OnClick(frame, button) function ns:OnClick(frame, button)
local line = frame:GetParent():GetID() -- CoA: clicking a profession row opens that character's profession recipe list, if available.
local lineType = Characters:GetLineType(line) local item = frame:GetParent() and frame:GetParent().coaItem
if lineType ~= INFO_CHARACTER_LINE then if not item or item.kind ~= "skill" or not item.character then return end
return if addon.Tabs and addon.Tabs.Characters and addon.Tabs.Characters.ViewCharInfo then
end local name, realm, account = Characters:GetInfo(item.viewLine)
addon:SetCurrentCharacter(name, realm, account)
local id = frame:GetID() addon.Tabs.Characters:SetCurrent(name, realm, account)
if id == 5 then return end -- fishing ? do nothing addon.Tabs:OnClick(2)
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)
end end
end end
local skillColors = { RECIPE_GREY, RED, ORANGE, YELLOW, GREEN } local skillColors = { RECIPE_GREY, RED, ORANGE, YELLOW, GREEN }
function ns:GetColor(rank, skillCap) 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 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 end
+69 -12
View File
@@ -108,7 +108,18 @@
<NormalFont style="GameFontNormalSmallLeft"/> <NormalFont style="GameFontNormalSmallLeft"/>
<HighlightFont style="GameFontHighlightSmallLeft"/> <HighlightFont style="GameFontHighlightSmallLeft"/>
</Button> </Button>
<Button name="$parentSkill1" inherits="AltoSkillButtonTemplate" id="1"> <!-- CoA: single wide "Professions" cell. Lists every known primary
profession (incl. customs Woodcutting/Woodworking) as icon+rank
segments, replacing the old fixed Prof.1/Prof.2 columns. Width
(325) must match the "Professions" header in TabSummary.lua. -->
<!-- CoA: self-contained (NOT inheriting AltoSkillButtonTemplate) so the wide
325px professions-strip ButtonText is the ONLY $parentNormalText on this
button. Inheriting + redefining ButtonText collided on the global name and
left Skill1NormalText nil (crashed the Skills tab). -->
<Button name="$parentSkill1" id="1">
<Size>
<AbsDimension x="325" y="18"/>
</Size>
<Anchors> <Anchors>
<Anchor point="BOTTOMLEFT" relativeTo="$parentLevel" relativePoint="BOTTOMRIGHT"> <Anchor point="BOTTOMLEFT" relativeTo="$parentLevel" relativePoint="BOTTOMRIGHT">
<Offset> <Offset>
@@ -116,19 +127,35 @@
</Offset> </Offset>
</Anchor> </Anchor>
</Anchors> </Anchors>
</Button> <ButtonText name="$parentNormalText" justifyH="LEFT">
<Button name="$parentSkill2" inherits="AltoSkillButtonTemplate" id="2"> <Size>
<Anchors> <AbsDimension x="325" y="18"/>
<Anchor point="BOTTOMLEFT" relativeTo="$parentSkill1" relativePoint="BOTTOMRIGHT"> </Size>
<Offset> <Anchors>
<AbsDimension x="0" y="0"/> <Anchor point="LEFT">
</Offset> <Offset>
</Anchor> <AbsDimension x="0" y="0"/>
</Anchors> </Offset>
</Anchor>
</Anchors>
</ButtonText>
<Scripts>
<OnEnter>
Altoholic.TradeSkills:OnEnter(self)
</OnEnter>
<OnClick>
Altoholic.TradeSkills:OnClick(self, button)
</OnClick>
<OnLeave>
AltoTooltip:Hide();
</OnLeave>
</Scripts>
<NormalFont style="GameFontNormalSmallLeft"/>
<HighlightFont style="GameFontHighlightSmallLeft"/>
</Button> </Button>
<Button name="$parentCooking" inherits="AltoSkillButtonTemplate" id="3"> <Button name="$parentCooking" inherits="AltoSkillButtonTemplate" id="3">
<Anchors> <Anchors>
<Anchor point="BOTTOMLEFT" relativeTo="$parentSkill2" relativePoint="BOTTOMRIGHT"> <Anchor point="BOTTOMLEFT" relativeTo="$parentSkill1" relativePoint="BOTTOMRIGHT">
<Offset> <Offset>
<AbsDimension x="0" y="0"/> <AbsDimension x="0" y="0"/>
</Offset> </Offset>
@@ -167,7 +194,7 @@
<Frame name="AltoholicFrameSkills" parent="AltoholicTabSummary" hidden="true"> <Frame name="AltoholicFrameSkills" parent="AltoholicTabSummary" hidden="true">
<Size> <Size>
<AbsDimension x="615" y="306"/> <AbsDimension x="615" y="414"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPRIGHT"> <Anchor point="TOPRIGHT">
@@ -293,6 +320,36 @@
<Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" /> <Anchor point="TOPLEFT" relativeTo="$parentEntry13" relativePoint="BOTTOMLEFT" />
</Anchors> </Anchors>
</Button> </Button>
<Button name="$parentEntry15" inherits="AltoSkillsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry14" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry16" inherits="AltoSkillsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry15" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry17" inherits="AltoSkillsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry16" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry18" inherits="AltoSkillsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry17" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry19" inherits="AltoSkillsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry18" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
<Button name="$parentEntry20" inherits="AltoSkillsTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentEntry19" relativePoint="BOTTOMLEFT" />
</Anchors>
</Button>
</Frames> </Frames>
</Frame> </Frame>
+13 -3
View File
@@ -227,22 +227,32 @@ function ns:UpdateViewIcons()
AltoholicTabCharacters_FirstAid.text = professionName AltoholicTabCharacters_FirstAid.text = professionName
AltoholicTabCharacters_FirstAid:Show() 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 local i = 1
for skillName, skill in pairs(DS:GetPrimaryProfessions(character) or {}) do -- CoA: getter returns no value for chars DataStore_Crafts hasn't scanned for skillName, skill in pairs(DS:GetPrimaryProfessions(character) or {}) do
local itemName = "AltoholicTabCharacters_Prof" .. i local itemName = "AltoholicTabCharacters_Prof" .. i
local item = _G[itemName] local item = _G[itemName]
if not item then break end -- no more profession buttons available
local spellID = DataStore:GetProfessionSpellID(skillName) local spellID = DataStore:GetProfessionSpellID(skillName)
if spellID then if spellID then
addon:SetItemButtonTexture(itemName, addon:GetSpellIcon(spellID), size, size) addon:SetItemButtonTexture(itemName, addon:GetSpellIcon(spellID), size, size)
item.text = skillName item.text = skillName
item:Show() item:Show()
else else
item.text = nil item.text = nil
item:Hide() item:Hide()
end end
i = i + 1 i = i + 1
end 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 end
function ns:MenuItem_OnClick(frame, button) function ns:MenuItem_OnClick(frame, button)
+36 -11
View File
@@ -55,7 +55,7 @@
<Frame name="AltoholicTabCharacters" parent="AltoholicFrame" hidden="true"> <Frame name="AltoholicTabCharacters" parent="AltoholicFrame" hidden="true">
<Size> <Size>
<AbsDimension x="758" y="447"/> <AbsDimension x="758" y="555"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
@@ -243,19 +243,44 @@
</Anchor> </Anchor>
</Anchors> </Anchors>
</Button> </Button>
<!-- CoA: a character can learn every profession. Lay the profession quick-icons out
as a wrapped 4-per-row grid in the open middle-left area (the taller window has room
below the Character dropdown). UpdateViewIcons fills/hides Prof1..12 dynamically. -->
<Button name="$parent_Prof1" inherits="AltoViewIconTemplate"> <Button name="$parent_Prof1" inherits="AltoViewIconTemplate">
<Anchors> <Anchors><Anchor point="TOPLEFT" relativeTo="AltoholicTabCharacters" relativePoint="TOPLEFT"><Offset x="30" y="-205" /></Anchor></Anchors>
<Anchor point="BOTTOMLEFT" relativeTo="$parent_FirstAid" relativePoint="BOTTOMRIGHT" >
<Offset x="5" y="0" />
</Anchor>
</Anchors>
</Button> </Button>
<Button name="$parent_Prof2" inherits="AltoViewIconTemplate"> <Button name="$parent_Prof2" inherits="AltoViewIconTemplate">
<Anchors> <Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof1" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
<Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof1" relativePoint="BOTTOMRIGHT" > </Button>
<Offset x="5" y="0" /> <Button name="$parent_Prof3" inherits="AltoViewIconTemplate">
</Anchor> <Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof2" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Anchors> </Button>
<Button name="$parent_Prof4" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof3" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof5" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="TOPLEFT" relativeTo="$parent_Prof1" relativePoint="BOTTOMLEFT"><Offset x="0" y="-5" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof6" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof5" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof7" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof6" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof8" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof7" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof9" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="TOPLEFT" relativeTo="$parent_Prof5" relativePoint="BOTTOMLEFT"><Offset x="0" y="-5" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof10" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof9" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof11" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof10" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button>
<Button name="$parent_Prof12" inherits="AltoViewIconTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof11" relativePoint="BOTTOMRIGHT"><Offset x="5" y="0" /></Anchor></Anchors>
</Button> </Button>
<Button name="$parent_Sort1" inherits="AltoSortButtonTemplate" id="1"> <Button name="$parent_Sort1" inherits="AltoSortButtonTemplate" id="1">
+1 -1
View File
@@ -18,7 +18,7 @@
<Frame name="AltoholicTabGuildBank" parent="AltoholicFrame" hidden="true"> <Frame name="AltoholicTabGuildBank" parent="AltoholicFrame" hidden="true">
<Size> <Size>
<AbsDimension x="758" y="447"/> <AbsDimension x="758" y="555"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"> <Anchor point="TOPLEFT">
+2 -2
View File
@@ -432,13 +432,13 @@ function Altoholic:MoveMinimapIcon()
end end
function Altoholic.Options:Get(name) function Altoholic.Options:Get(name)
if addon.db and addon.db.global then if addon.db and addon.db.global and addon.db.global.options then
return addon.db.global.options[name] return addon.db.global.options[name]
end end
end end
function Altoholic.Options:Set(name, value) function Altoholic.Options:Set(name, value)
if addon.db and addon.db.global then if addon.db and addon.db.global and addon.db.global.options then
addon.db.global.options[name] = value addon.db.global.options[name] = value
end end
end end
+1 -1
View File
@@ -3,7 +3,7 @@
<Frame name="AltoholicTabSearch" parent="AltoholicFrame" hidden="true"> <Frame name="AltoholicTabSearch" parent="AltoholicFrame" hidden="true">
<Size> <Size>
<AbsDimension x="758" y="447"/> <AbsDimension x="758" y="555"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"/> <Anchor point="TOPLEFT"/>
+5 -11
View File
@@ -158,17 +158,11 @@ function ns:SetMode(mode)
Columns:Add(L["free"], 50, function(self) addon.Characters:Sort(self, "GetNumFreeBankSlots") end) Columns:Add(L["free"], 50, function(self) addon.Characters:Sort(self, "GetNumFreeBankSlots") end)
elseif currentMode == 3 then elseif currentMode == 3 then
Columns:Add(NAME, 100, function(self) addon.Characters:Sort(self, "GetCharacterName") end) -- CoA: the Skills view is now a vertical list (character header, then one row per
Columns:Add(LEVEL, 60, function(self) addon.Characters:Sort(self, "GetCharacterLevel") end) -- known profession/secondary skill, top to bottom). A single wide column header
Columns:Add(L["Prof. 1"], 65, function(self) addon.Characters:Sort(self, "skillName1") end) -- replaces the old per-skill columns, which no longer match the layout.
Columns:Add(L["Prof. 2"], 65, function(self) addon.Characters:Sort(self, "skillName2") end) Columns:Add((L["Character"] or "Character") .. " / " .. (L["Professions"] or "Professions"), 460, function(self) addon.Characters:Sort(self, "GetCharacterName") end)
title = GetSpellInfo(2550) -- cooking Columns:Add((SKILL or "Skill"), 220, function(self) addon.Characters:Sort(self, "GetCharacterName") end)
Columns:Add(title, 65, function(self) addon.Characters:Sort(self, "GetCookingRank") end)
title = GetSpellInfo(3273) -- First Aid
Columns:Add(title, 65, function(self) addon.Characters:Sort(self, "GetFirstAidRank") end)
title = GetSpellInfo(24303) -- Fishing
Columns:Add(title, 65, function(self) addon.Characters:Sort(self, "GetFishingRank") end)
Columns:Add(L["Riding"], 65, function(self) addon.Characters:Sort(self, "GetRidingRank") end)
elseif currentMode == 4 then elseif currentMode == 4 then
Columns:Add(NAME, 100, function(self) addon.Characters:Sort(self, "GetCharacterName") end) Columns:Add(NAME, 100, function(self) addon.Characters:Sort(self, "GetCharacterName") end)
+1 -1
View File
@@ -14,7 +14,7 @@
<Frame name="AltoholicTabSummary" parent="AltoholicFrame"> <Frame name="AltoholicTabSummary" parent="AltoholicFrame">
<Size> <Size>
<AbsDimension x="758" y="447"/> <AbsDimension x="758" y="555"/>
</Size> </Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"/> <Anchor point="TOPLEFT"/>
+1
View File
@@ -337,6 +337,7 @@ function tns:Update(treeIndex)
-- textures are 90.625% of the original size -- textures are 90.625% of the original size
local _, bg = DS:GetTreeInfo(class, treeName) local _, bg = DS:GetTreeInfo(class, treeName)
if not bg then return end -- CoA: no talent-tree background for this class (e.g. custom class with no reference data)
AltoholicFrameTalents_bgTopLeft:SetTexture(bg.."-TopLeft") AltoholicFrameTalents_bgTopLeft:SetTexture(bg.."-TopLeft")
AltoholicFrameTalents_bgTopRight:SetTexture(bg.."-TopRight") AltoholicFrameTalents_bgTopRight:SetTexture(bg.."-TopRight")
AltoholicFrameTalents_bgBottomLeft:SetTexture(bg.."-BottomLeft") AltoholicFrameTalents_bgBottomLeft:SetTexture(bg.."-BottomLeft")
+1 -1
View File
@@ -380,7 +380,7 @@ local function GetItemCount(searchedID)
for tabID = 1, 6 do for tabID = 1, 6 do
local tabCount = DataStore:GetGuildBankTabItemCount(guildKey, tabID, searchedID) local tabCount = DataStore:GetGuildBankTabItemCount(guildKey, tabID, searchedID)
if tabCount > 0 then if tabCount > 0 then
table.insert(tabCounters, format("%s: %s", WHITE .. DataStore:GetGuildBankTabName(guildKey, tabID), TEAL..tabCount)) table.insert(tabCounters, format("%s: %s", WHITE .. (DataStore:GetGuildBankTabName(guildKey, tabID) or ""), TEAL..tabCount)) -- CoA: tab name nil on partial guild-bank data
end end
end end
Binary file not shown.
@@ -55,8 +55,8 @@ local function ScanSingleAchievement(id, isCompleted, month, day, year)
if critCompleted then if critCompleted then
table.insert(CriteriaCache, tostring(j)) table.insert(CriteriaCache, tostring(j))
else else
if reqQuantity > 1 then if (reqQuantity or 0) > 1 then -- CoA: GetAchievementCriteriaInfo can return nil quantities for custom/partial achievements
table.insert(CriteriaCache, j .. ":" .. quantity) table.insert(CriteriaCache, j .. ":" .. (quantity or 0))
end end
end end
end end
@@ -235,7 +235,8 @@ end
-- *** EVENT HANDLERS *** -- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE() function addon:PLAYER_ALIVE()
-- print("DataStore_Achievements.lua") -- DEBUG 2025 07 21 -- print("DataStore_Achievements.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanAllAchievements() ScanAllAchievements()
ScanProgress() ScanProgress()
@@ -86,10 +86,15 @@ local function OnPlayerMoney()
addon.ThisCharacter.money = GetMoney(); addon.ThisCharacter.money = GetMoney();
end end
local hasScannedThisSession
local function OnPlayerAlive() local function OnPlayerAlive()
-- print("DataStore_Characters.lua") -- DEBUG 2025 07 21 -- CoA: scan once at login. PLAYER_ALIVE also fires on resurrect / Feign-Death cancel
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard -- (unchanged data), so skip those. The previous "only when ghost" gate skipped LOGIN
-- too, so name/level/class/money/XP never populated on a normal login - the root of
-- "no character data". (Same trap as DataStore_Inventory / _Skills; see commit fdcb25a.)
if hasScannedThisSession then return end
hasScannedThisSession = true
local character = addon.ThisCharacter local character = addon.ThisCharacter
character.name = UnitName("player") -- to simplify processing a bit, the name is saved in the table too, in addition to being part of the key character.name = UnitName("player") -- to simplify processing a bit, the name is saved in the table too, in addition to being part of the key
@@ -263,7 +268,7 @@ local function _GetGuildInfo(character)
end end
local function _GetPlayTime(character) local function _GetPlayTime(character)
return character.played return character.played or 0 -- CoA: nil on partial-data alt; callers do arithmetic (AccountSummary)
end end
local function _GetLocation(character) local function _GetLocation(character)
+276 -22
View File
@@ -35,6 +35,24 @@ local MSG_BANKTAB_TRANSFER = 6 -- .. or send the data
local AddonDB_Defaults = { local AddonDB_Defaults = {
global = { global = {
-- CoA (Ascension) Realm Bank: shared per-realm storage, keyed by "Account.Realm"
-- (NOT per-character). Mirrors the guild bank's Tabs layout so the same
-- scan/read helpers can be reused.
RealmBanks = {
['*'] = { -- ["Account.Realm"]
lastUpdate = nil,
Tabs = {
['*'] = { -- tabID = table index [1] to [6]
name = nil,
icon = nil,
size = 0,
ids = {},
links = {},
counts = {}
}
},
}
},
Guilds = { Guilds = {
['*'] = { -- ["Account.Realm.Name"] ['*'] = { -- ["Account.Realm.Name"]
money = nil, money = nil,
@@ -204,6 +222,16 @@ end
local BAGS = 1 -- All bags, 0 to 11, and keyring ( id -2 ) local BAGS = 1 -- All bags, 0 to 11, and keyring ( id -2 )
local BANK = 2 -- 28 main slots local BANK = 2 -- 28 main slots
local GUILDBANK = 3 -- 98 main slots local GUILDBANK = 3 -- 98 main slots
local PERSONALBANK = 4 -- CoA personal bank (per-character), read via guild bank API
local REALMBANK = 5 -- CoA realm bank (per-realm), read via guild bank API
-- CoA reuses the Guild Bank UI for the Personal Bank and the Realm Bank.
-- These container key prefixes keep their scanned tabs out of the regular
-- "Bag0".."Bag11" / "Bag100" / "Bag-2" namespace so item-count loops can tell
-- them apart. Personal bank tabs live under the character; realm bank tabs live
-- in the realm-keyed global table.
local PERSONALBANK_PREFIX = "PBank" -- char.Containers["PBank1".."PBank6"]
local MAX_BANK_TABS = 6
local ContainerTypes = { local ContainerTypes = {
[BAGS] = { [BAGS] = {
@@ -262,9 +290,81 @@ local ContainerTypes = {
GetCooldown = function(self, slotID) GetCooldown = function(self, slotID)
return nil return nil
end, end,
},
-- Personal & Realm banks are read through the guild bank API (CoA reuses that UI).
[PERSONALBANK] = {
GetSize = function(self)
return MAX_GUILDBANK_SLOTS_PER_TAB or 98
end,
GetFreeSlots = function(self)
return nil, nil
end,
GetLink = function(self, slotID, tabID)
return GetGuildBankItemLink(tabID, slotID)
end,
GetCount = function(self, slotID, tabID)
local _, count = GetGuildBankItemInfo(tabID, slotID)
return count
end,
GetCooldown = function(self, slotID)
return nil
end,
},
[REALMBANK] = {
GetSize = function(self)
return MAX_GUILDBANK_SLOTS_PER_TAB or 98
end,
GetFreeSlots = function(self)
return nil, nil
end,
GetLink = function(self, slotID, tabID)
return GetGuildBankItemLink(tabID, slotID)
end,
GetCount = function(self, slotID, tabID)
local _, count = GetGuildBankItemInfo(tabID, slotID)
return count
end,
GetCooldown = function(self, slotID)
return nil
end,
} }
} }
-- *** CoA bank-type detection ***
-- CoA exposes BANK_PERMISSIONS_PAYLOAD via the client-only HasJsonCacheData /
-- GetJsonCacheData / C_Serialize APIs. Guard every call so this is harmless on
-- non-CoA clients (where the personal/realm bank simply never triggers).
local function GetCoABankType()
if not (HasJsonCacheData and GetJsonCacheData and C_Serialize and C_Serialize.FromJSON) then
return "guild"
end
if not HasJsonCacheData("BANK_PERMISSIONS_PAYLOAD", 0) then
return "guild"
end
local json = GetJsonCacheData("BANK_PERMISSIONS_PAYLOAD", 0)
if not json then
return "guild"
end
local jsonObject = C_Serialize:FromJSON(json)
if not jsonObject then
return "guild"
end
if jsonObject.IsPersonalBank then
return "personal"
elseif jsonObject.IsRealmBank then
return "realm"
end
return "guild"
end
local function GetRealmBankKey()
return format("%s.%s", THIS_ACCOUNT, GetRealmName())
end
local function GetThisRealmBank()
return addon.db.global.RealmBanks[GetRealmBankKey()]
end
-- *** Scanning functions *** -- *** Scanning functions ***
local function ScanContainer(bagID, containerType) local function ScanContainer(bagID, containerType)
local Container = ContainerTypes[containerType] local Container = ContainerTypes[containerType]
@@ -273,8 +373,15 @@ local function ScanContainer(bagID, containerType)
if containerType == GUILDBANK then if containerType == GUILDBANK then
local thisGuild = GetThisGuild() local thisGuild = GetThisGuild()
if not thisGuild then return end if not thisGuild then return end
bag = thisGuild.Tabs[bagID] -- bag is actually the current tab bag = thisGuild.Tabs[bagID] -- bag is actually the current tab
elseif containerType == PERSONALBANK then
-- per-character storage, keyed by a tab-specific prefix so it never
-- collides with the normal bag/bank containers
bag = addon.ThisCharacter.Containers[PERSONALBANK_PREFIX .. bagID]
elseif containerType == REALMBANK then
local realmBank = GetThisRealmBank()
bag = realmBank.Tabs[bagID] -- bag is the current tab in the realm-keyed table
else else
bag = addon.ThisCharacter.Containers["Bag" .. bagID] bag = addon.ThisCharacter.Containers["Bag" .. bagID]
wipe(bag.cooldowns) -- does not exist for a guild bank wipe(bag.cooldowns) -- does not exist for a guild bank
@@ -315,8 +422,14 @@ local function ScanContainer(bagID, containerType)
bag.cooldowns[index] = startTime .."|".. duration .. "|" .. 1 bag.cooldowns[index] = startTime .."|".. duration .. "|" .. 1
end end
end end
addon.ThisCharacter.lastUpdate = time() if containerType == REALMBANK then
GetThisRealmBank().lastUpdate = time()
else
-- personal bank, bags, bank and guild bank all stamp the character;
-- this is what gates DataStore's "no value" guard for char-based getters
addon.ThisCharacter.lastUpdate = time()
end
end end
local function ScanBagSlotsInfo() local function ScanBagSlotsInfo()
@@ -463,25 +576,76 @@ local function OnBankFrameOpened()
addon:RegisterEvent("PLAYERBANKSLOTS_CHANGED", OnPlayerBankSlotsChanged) addon:RegisterEvent("PLAYERBANKSLOTS_CHANGED", OnPlayerBankSlotsChanged)
end end
-- Records the name/icon of a personal-bank tab. Reuses the per-character
-- container created by ScanContainer (PERSONALBANK_PREFIX .. tabID).
local function ScanPersonalBankInfo(tabID)
local bag = addon.ThisCharacter.Containers[PERSONALBANK_PREFIX .. tabID]
bag.name, bag.icon = GetGuildBankTabInfo(tabID)
end
local function ScanRealmBankInfo(tabID)
local t = GetThisRealmBank().Tabs[tabID]
t.name, t.icon = GetGuildBankTabInfo(tabID)
end
local function OnGuildBankFrameClosed() local function OnGuildBankFrameClosed()
addon:UnregisterEvent("GUILDBANKFRAME_CLOSED") addon:UnregisterEvent("GUILDBANKFRAME_CLOSED")
addon:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED") addon:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED")
local guildName = GetGuildInfo("player") -- only broadcast guild bank timestamps for the actual guild bank
if guildName then if addon.coaBankType == "guild" then
GuildBroadcast(MSG_SEND_BANK_TIMESTAMPS, GetBankTimestamps(guildName)) local guildName = GetGuildInfo("player")
if guildName then
GuildBroadcast(MSG_SEND_BANK_TIMESTAMPS, GetBankTimestamps(guildName))
end
end end
addon.coaBankType = nil
addon.coaBankAvailableTabs = nil
end end
local function OnGuildBankBagSlotsChanged() local function OnGuildBankBagSlotsChanged()
ScanContainer(GetCurrentGuildBankTab(), GUILDBANK) local currentTab = GetCurrentGuildBankTab()
ScanGuildBankInfo()
if addon.coaBankType == "personal" then
ScanContainer(currentTab, PERSONALBANK)
ScanPersonalBankInfo(currentTab)
elseif addon.coaBankType == "realm" then
ScanContainer(currentTab, REALMBANK)
ScanRealmBankInfo(currentTab)
else
-- regular guild bank, unchanged behaviour
ScanContainer(currentTab, GUILDBANK)
ScanGuildBankInfo()
end
end end
local function OnGuildBankFrameOpened() local function OnGuildBankFrameOpened()
-- CoA reuses the guild bank UI for the personal & realm banks; detect which
-- one this is BEFORE doing anything guild-specific. Harmless ("guild") when
-- the CoA JSON APIs are absent.
addon.coaBankType = GetCoABankType()
addon:RegisterEvent("GUILDBANKFRAME_CLOSED", OnGuildBankFrameClosed) addon:RegisterEvent("GUILDBANKFRAME_CLOSED", OnGuildBankFrameClosed)
addon:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED", OnGuildBankBagSlotsChanged) addon:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED", OnGuildBankBagSlotsChanged)
if addon.coaBankType == "personal" or addon.coaBankType == "realm" then
-- Pre-query the other tabs so we snapshot the whole bank without the
-- player having to click each tab. Each QueryGuildBankTab triggers a
-- GUILDBANKBAGSLOTS_CHANGED for that tab, handled above.
if QueryGuildBankTab then
local currentTab = GetCurrentGuildBankTab and GetCurrentGuildBankTab() or 0
for tabID = 1, MAX_BANK_TABS do
local avail = GetGuildBankTabInfo(tabID)
if type(avail) == "string" and tabID ~= currentTab then
QueryGuildBankTab(tabID)
end
end
end
return
end
-- regular guild bank
local thisGuild = GetThisGuild() local thisGuild = GetThisGuild()
if thisGuild then if thisGuild then
thisGuild.money = GetGuildBankMoney() thisGuild.money = GetGuildBankMoney()
@@ -514,25 +678,28 @@ local BagTypeStrings = {
local function _GetContainerInfo(character, containerID) local function _GetContainerInfo(character, containerID)
local bag = _GetContainer(character, containerID) local bag = _GetContainer(character, containerID)
if type(bag) ~= "table" then return end -- CoA: unscanned bag on partial-data alt; was an index-nil crash
return bag.icon, bag.link, bag.size, bag.freeslots, BagTypeStrings[bag.bagtype] return bag.icon, bag.link, bag.size, bag.freeslots, BagTypeStrings[bag.bagtype]
end end
local function _GetContainerSize(character, containerID) local function _GetContainerSize(character, containerID)
-- containerID can be number or string -- containerID can be number or string
return character.Containers["Bag" .. containerID].size local bag = character.Containers["Bag" .. containerID] -- CoA: nil for unscanned bag on partial-data alt
return bag and bag.size
end end
local function _GetSlotInfo(bag, slotID) local function _GetSlotInfo(bag, slotID)
assert(type(bag) == "table") -- this is the pointer to a bag table, obtained through addon:GetContainer() -- CoA: partial-data alts can have an unscanned/nil bag pointer (GetContainer returns nil
assert(type(slotID) == "number") -- for a "BagN" the Containers module never scanned); return empties instead of asserting.
if type(bag) ~= "table" or type(slotID) ~= "number" then return end
-- return itemID, itemLink, itemCount -- return itemID, itemLink, itemCount
return bag.ids[slotID], bag.links[slotID], bag.counts[slotID] or 1 return bag.ids and bag.ids[slotID], bag.links and bag.links[slotID], (bag.counts and bag.counts[slotID]) or 1
end end
local function _GetContainerCooldownInfo(bag, slotID) local function _GetContainerCooldownInfo(bag, slotID)
assert(type(bag) == "table") -- this is the pointer to a bag table, obtained through addon:GetContainer() -- CoA: partial-data alts can have an unscanned/nil bag pointer; degrade to nil gracefully.
assert(type(slotID) == "number") if type(bag) ~= "table" or type(slotID) ~= "number" or type(bag.cooldowns) ~= "table" then return end
local cd = bag.cooldowns[slotID] local cd = bag.cooldowns[slotID]
if cd then if cd then
@@ -550,19 +717,22 @@ end
local function _GetContainerItemCount(character, searchedID) local function _GetContainerItemCount(character, searchedID)
local bagCount = 0 local bagCount = 0
local bankCount = 0 local bankCount = 0
local personalBankCount = 0
local id local id
for containerName, container in pairs(character.Containers) do for containerName, container in pairs(character.Containers) do
for slotID=1, container.size do for slotID=1, container.size do
id = container.ids[slotID] id = container.ids[slotID]
if (id) and (id == searchedID) then if (id) and (id == searchedID) then
local itemCount = container.counts[slotID] or 1 local itemCount = container.counts[slotID] or 1
if (containerName == "Bag100") then if (containerName == "Bag100") then
bankCount = bankCount + itemCount bankCount = bankCount + itemCount
elseif (containerName == "Bag-2") then elseif (containerName == "Bag-2") then
bagCount = bagCount + itemCount bagCount = bagCount + itemCount
elseif string.sub(containerName, 1, #PERSONALBANK_PREFIX) == PERSONALBANK_PREFIX then
personalBankCount = personalBankCount + itemCount -- CoA personal bank tab
else else
local bagNum = tonumber(string.sub(containerName, 4)) local bagNum = tonumber(string.sub(containerName, 4))
if (bagNum >= 0) and (bagNum <= 4) then if (bagNum >= 0) and (bagNum <= 4) then
@@ -575,7 +745,79 @@ local function _GetContainerItemCount(character, searchedID)
end end
end end
return bagCount, bankCount -- 3rd return value (personal bank) is additive and backward-compatible:
-- existing callers that expect (bags, bank) just ignore it.
return bagCount, bankCount, personalBankCount
end
-- *** CoA personal bank (per-character) ***
local function _GetPersonalBankItemCount(character, searchedID)
local count = 0
for containerName, container in pairs(character.Containers) do
if string.sub(containerName, 1, #PERSONALBANK_PREFIX) == PERSONALBANK_PREFIX then
for slotID = 1, container.size do
if container.ids[slotID] == searchedID then
count = count + (container.counts[slotID] or 1)
end
end
end
end
return count
end
local function _GetPersonalBankTabCount(character)
-- number of personal bank tabs that have been scanned (have a size)
local n = 0
for tabID = 1, MAX_BANK_TABS do
local container = character.Containers[PERSONALBANK_PREFIX .. tabID]
if container and (container.size or 0) > 0 then
n = n + 1
end
end
return n
end
-- *** CoA realm bank (per-realm, NOT char-based) ***
-- These take a realm key string ("Account.Realm") directly and read the global
-- RealmBanks table, so they bypass DataStore's char/guild "no value" wrapper.
local function _GetRealmBankKey(realm, account)
realm = realm or GetRealmName()
account = account or THIS_ACCOUNT
return format("%s.%s", account, realm)
end
local function _GetRealmBank(realm, account)
return addon.db.global.RealmBanks[_GetRealmBankKey(realm, account)]
end
local function _GetRealmBankItemCount(realm, account, searchedID)
local realmBank = _GetRealmBank(realm, account)
if not realmBank then return 0 end
local count = 0
for _, tab in pairs(realmBank.Tabs) do
for slotID, id in pairs(tab.ids) do
if id == searchedID then
count = count + (tab.counts[slotID] or 1)
end
end
end
return count
end
local function _GetRealmBankTab(realm, account, tabID)
local realmBank = _GetRealmBank(realm, account)
return realmBank and realmBank.Tabs[tabID]
end
local function _GetRealmBankTabName(realm, account, tabID)
local tab = _GetRealmBankTab(realm, account, tabID)
return tab and tab.name
end
local function _GetRealmBankLastUpdate(realm, account)
local realmBank = _GetRealmBank(realm, account)
return realmBank and realmBank.lastUpdate
end end
local function _GetNumBagSlots(character) local function _GetNumBagSlots(character)
@@ -629,7 +871,7 @@ end
local function _GetGuildBankTabItemCount(guild, tabID, searchedID) local function _GetGuildBankTabItemCount(guild, tabID, searchedID)
local count = 0 local count = 0
local container = guild.Tabs[tabID] local container = guild.Tabs[tabID]
if type(container) ~= "table" or type(container.ids) ~= "table" then return count end -- CoA: unscanned guild bank tab; was a pairs(nil) crash on item tooltips
for slotID, id in pairs(container.ids) do for slotID, id in pairs(container.ids) do
if (id == searchedID) then if (id == searchedID) then
count = count + (container.counts[slotID] or 1) count = count + (container.counts[slotID] or 1)
@@ -721,6 +963,14 @@ local PublicMethods = {
RejectBankTabRequest = _RejectBankTabRequest, RejectBankTabRequest = _RejectBankTabRequest,
SendBankTabToGuildMember = _SendBankTabToGuildMember, SendBankTabToGuildMember = _SendBankTabToGuildMember,
GetGuildBankTabSuppliers = _GetGuildBankTabSuppliers, GetGuildBankTabSuppliers = _GetGuildBankTabSuppliers,
-- CoA personal bank (per-character)
GetPersonalBankItemCount = _GetPersonalBankItemCount,
GetPersonalBankTabCount = _GetPersonalBankTabCount,
-- CoA realm bank (per-realm; takes realm/account, not a character/guild key)
GetRealmBankItemCount = _GetRealmBankItemCount,
GetRealmBankTab = _GetRealmBankTab,
GetRealmBankTabName = _GetRealmBankTabName,
GetRealmBankLastUpdate = _GetRealmBankLastUpdate,
} }
-- *** Guild Comm *** -- *** Guild Comm ***
@@ -809,7 +1059,11 @@ function addon:OnInitialize()
DataStore:SetCharacterBasedMethod("GetNumFreeBagSlots") DataStore:SetCharacterBasedMethod("GetNumFreeBagSlots")
DataStore:SetCharacterBasedMethod("GetNumBankSlots") DataStore:SetCharacterBasedMethod("GetNumBankSlots")
DataStore:SetCharacterBasedMethod("GetNumFreeBankSlots") DataStore:SetCharacterBasedMethod("GetNumFreeBankSlots")
DataStore:SetCharacterBasedMethod("GetPersonalBankItemCount")
DataStore:SetCharacterBasedMethod("GetPersonalBankTabCount")
-- Realm bank methods are intentionally NOT char/guild based: they take a
-- realm/account string pair directly and read the realm-keyed global table.
DataStore:SetGuildBasedMethod("GetGuildBankItemCount") DataStore:SetGuildBasedMethod("GetGuildBankItemCount")
DataStore:SetGuildBasedMethod("GetGuildBankTab") DataStore:SetGuildBasedMethod("GetGuildBankTab")
DataStore:SetGuildBasedMethod("GetGuildBankTabName") DataStore:SetGuildBasedMethod("GetGuildBankTabName")
@@ -2,7 +2,7 @@
## Title: DataStore_Containers ## Title: DataStore_Containers
## Notes: Stores information about character bags, bank, and guild banks ## Notes: Stores information about character bags, bank, and guild banks
## Author: Thaoky (EU-Marécages de Zangar) ## Author: Thaoky (EU-Marécages de Zangar)
## Version: 3.3.001 ## Version: 3.3.001-coa.9
## Dependencies: DataStore ## Dependencies: DataStore
## OptionalDeps: Ace3 ## OptionalDeps: Ace3
## SavedVariables: DataStore_ContainersDB ## SavedVariables: DataStore_ContainersDB
+6 -2
View File
@@ -546,7 +546,8 @@ end
-- *** Event Handlers *** -- *** Event Handlers ***
local function OnPlayerAlive() local function OnPlayerAlive()
-- print("DataStore_Crafts.lua") -- DEBUG 2025 07 21 -- print("DataStore_Crafts.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanProfessionLinks() ScanProfessionLinks()
end end
@@ -625,6 +626,9 @@ local function _GetProfessionInfo(profession)
end end
local function _GetNumCraftLines(profession) local function _GetNumCraftLines(profession)
-- CoA: profession is nil for an unscanned/custom profession on a partial-data alt;
-- callers use this as a numeric `for` limit, so return 0 instead of crashing on #nil.Crafts
if type(profession) ~= "table" or type(profession.Crafts) ~= "table" then return 0 end
return #profession.Crafts return #profession.Crafts
end end
@@ -709,7 +713,7 @@ local function _GetNumRecipesByColor(profession)
for i = 1, _GetNumCraftLines(profession) do for i = 1, _GetNumCraftLines(profession) do
local isHeader, color = _GetCraftLineInfo(profession, i) local isHeader, color = _GetCraftLineInfo(profession, i)
if not isHeader then if not isHeader and color and counts[color] then -- CoA: custom-profession craft lines can carry an out-of-range/nil color
counts[color] = counts[color] + 1 counts[color] = counts[color] + 1
end end
end end
+2 -1
View File
@@ -46,7 +46,8 @@ end
-- *** Event Handlers *** -- *** Event Handlers ***
local function OnPlayerAlive() local function OnPlayerAlive()
-- print("DataStore_Pets.lua") -- DEBUG 2025 07 21 -- print("DataStore_Pets.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanCompanions("CRITTER") ScanCompanions("CRITTER")
ScanCompanions("MOUNT") ScanCompanions("MOUNT")
+6 -3
View File
@@ -177,7 +177,8 @@ end
-- *** Event Handlers *** -- *** Event Handlers ***
local function OnPlayerAlive() local function OnPlayerAlive()
-- print("DataStore_Quests.lua") -- DEBUG 2025 07 21 -- print("DataStore_Quests.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanQuests() ScanQuests()
end end
@@ -274,8 +275,10 @@ local function _GetQuestLogRewardInfo(character, index, rewardIndex)
end end
local function _GetQuestInfo(link) local function _GetQuestInfo(link)
assert(type(link) == "string") -- CoA: GetQuestLogInfo can hand back a nil link for a partial-data alt; degrade to nil
-- returns instead of asserting (callers already nil-check the returned name/level).
if type(link) ~= "string" then return end
local questID, questLevel = link:match("quest:(%d+):(-?%d+)") local questID, questLevel = link:match("quest:(%d+):(-?%d+)")
local questName = link:match("%[(.+)%]") local questName = link:match("%[(.+)%]")
@@ -15,9 +15,10 @@ local THIS_ACCOUNT = "Default"
local AddonDB_Defaults = { local AddonDB_Defaults = {
global = { global = {
Characters = { Characters = {
['*'] = { -- ["Account.Realm.Name"] ['*'] = { -- ["Account.Realm.Name"]
lastUpdate = nil, lastUpdate = nil,
Factions = {}, Factions = {}, -- [factionName] = "bottom|top|earned"
Headers = {}, -- [factionName] = headerName (in-game category, ex: "Wrath of the Lich King"). Lets the UI group dynamically incl. CoA custom factions.
} }
} }
} }
@@ -59,13 +60,19 @@ local function _GetRawReputationInfo(character, faction)
end end
local function _GetReputations(character) local function _GetReputations(character)
return character.Factions return character.Factions or {}
end
local function _GetReputationHeaders(character)
-- [factionName] = headerName (in-game category). May be empty for chars scanned before this field existed; UI must guard with "or {}".
return character.Headers or {}
end end
local PublicMethods = { local PublicMethods = {
GetReputationInfo = _GetReputationInfo, GetReputationInfo = _GetReputationInfo,
GetRawReputationInfo = _GetRawReputationInfo, GetRawReputationInfo = _GetRawReputationInfo,
GetReputations = _GetReputations, GetReputations = _GetReputations,
GetReputationHeaders = _GetReputationHeaders,
} }
function addon:OnInitialize() function addon:OnInitialize()
@@ -75,6 +82,7 @@ function addon:OnInitialize()
DataStore:SetCharacterBasedMethod("GetReputationInfo") DataStore:SetCharacterBasedMethod("GetReputationInfo")
DataStore:SetCharacterBasedMethod("GetRawReputationInfo") DataStore:SetCharacterBasedMethod("GetRawReputationInfo")
DataStore:SetCharacterBasedMethod("GetReputations") DataStore:SetCharacterBasedMethod("GetReputations")
DataStore:SetCharacterBasedMethod("GetReputationHeaders")
end end
function addon:OnEnable() function addon:OnEnable()
@@ -125,13 +133,24 @@ local function ScanReputations()
SaveHeaders() SaveHeaders()
local factions = addon.ThisCharacter.Factions local factions = addon.ThisCharacter.Factions
local headers = addon.ThisCharacter.Headers or {}
addon.ThisCharacter.Headers = headers
wipe(factions) wipe(factions)
wipe(headers)
local currentHeader = "" -- track the in-game category so the UI can group factions dynamically (incl. CoA custom factions)
for i = 1, GetNumFactions() do -- 2nd pass, data collection for i = 1, GetNumFactions() do -- 2nd pass, data collection
local name, _, _, bottom, top, earned, _, _, isHeader, _, hasRep = GetFactionInfo(i) local name, _, _, bottom, top, earned, _, _, isHeader, _, hasRep = GetFactionInfo(i)
if (not isHeader) or (isHeader and hasRep) then if (not isHeader) or (isHeader and hasRep) then
-- new in 3.0.2, headers may have rep, ex: alliance vanguard + horde expedition -- new in 3.0.2, headers may have rep, ex: alliance vanguard + horde expedition
factions[name] = bottom .. "|" .. top .. "|" .. earned factions[name] = bottom .. "|" .. top .. "|" .. earned
headers[name] = currentHeader
end
if isHeader then
-- any header (pure category like "Classic"/"Wrath of the Lich King", or a rep-bearing
-- header like "Alliance Vanguard") becomes the group for the factions listed beneath it
currentHeader = name or ""
end end
end end
@@ -158,7 +177,8 @@ end
-- *** EVENT HANDLERS *** -- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE() function addon:PLAYER_ALIVE()
-- print("DataStore_Reputations.lua") -- DEBUG 2025 07 21 -- print("DataStore_Reputations.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanReputations() ScanReputations()
end end
@@ -2,7 +2,7 @@
## Title: DataStore_Reputations ## Title: DataStore_Reputations
## Notes: Stores information about character reputation levels ## Notes: Stores information about character reputation levels
## Author: Thaoky (EU-Marécages de Zangar) ## Author: Thaoky (EU-Marécages de Zangar)
## Version: 3.3.001 ## Version: 3.3.001-coa.10
## Dependencies: DataStore ## Dependencies: DataStore
## OptionalDeps: Ace3 ## OptionalDeps: Ace3
## SavedVariables: DataStore_ReputationsDB ## SavedVariables: DataStore_ReputationsDB
+86 -3
View File
@@ -7,7 +7,7 @@ if not DataStore then return end
local addonName = "DataStore_Skills" local addonName = "DataStore_Skills"
_G[addonName] = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0") _G[addonName] = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0")
local addon = _G[addonName] local addon = _G[addonName]
@@ -35,6 +35,44 @@ local function _GetPrimaryProfessions(character)
return character.Skills[L["Professions"]] return character.Skills[L["Professions"]]
end end
-- CoA: a character can know ALL professions at once (no retail 2-primary limit),
-- and the two custom professions Woodcutting/Woodworking may sit under a different
-- skill-window header. To render "all professions" reliably we don't trust the
-- "Professions" category header alone: we collect every known crafting/gathering
-- profession by name across all categories. English skill names are the keys; on
-- non-English clients these names won't match and the list falls back to whatever
-- sits under L["Professions"] via _GetPrimaryProfessions (caller handles that).
local PRIMARY_PROFESSION_NAMES = {
"Alchemy", "Blacksmithing", "Enchanting", "Engineering", "Inscription",
"Jewelcrafting", "Leatherworking", "Tailoring", "Skinning", "Mining",
"Herbalism", "Woodcutting", "Woodworking",
}
-- Returns an ordered array of { name = <skillName>, rank = <n>, maxRank = <n> }
-- for every primary profession the character actually knows. Never returns nil.
local function _GetPrimaryProfessionList(character)
local result = {}
local skills = character.Skills
if not skills then return result end
for _, profName in ipairs(PRIMARY_PROFESSION_NAMES) do
for _, category in pairs(skills) do
local skill = category[profName]
if skill then
local rank, maxRank = strsplit("|", skill)
result[#result + 1] = {
name = profName,
rank = tonumber(rank) or 0,
maxRank = tonumber(maxRank) or 0,
}
break -- found this profession, move to the next name
end
end
end
return result
end
local function _GetSecondaryProfessions(character) local function _GetSecondaryProfessions(character)
return character.Skills[L["Secondary Skills"]] return character.Skills[L["Secondary Skills"]]
end end
@@ -77,8 +115,25 @@ local function _GetRidingRank(character)
return _GetSkillInfoByCategory(character, L["Secondary Skills"], L["Riding"]) return _GetSkillInfoByCategory(character, L["Secondary Skills"], L["Riding"])
end end
-- CoA (Ascension Vol'jin) adds two custom professions on top of the vanilla 15:
-- Woodcutting (gathering, base spell 13977860) and Woodworking (crafting,
-- ranks 1005008-1005011). They register as normal skill lines in the in-game
-- skill UI under their English names "Woodcutting" / "Woodworking" (confirmed
-- against coa-professionmenu, which reads them via GetSkillLineInfo by name).
-- ScanSkills() already buckets them by whatever category header they sit under,
-- so a name-based lookup across all categories retrieves them regardless of the
-- header. On non-CoA realms the skill simply doesn't exist and these return 0, 0.
local function _GetWoodcuttingRank(character)
return _GetSkillInfo(character, "Woodcutting")
end
local function _GetWoodworkingRank(character)
return _GetSkillInfo(character, "Woodworking")
end
local PublicMethods = { local PublicMethods = {
GetPrimaryProfessions = _GetPrimaryProfessions, GetPrimaryProfessions = _GetPrimaryProfessions,
GetPrimaryProfessionList = _GetPrimaryProfessionList,
GetSecondaryProfessions = _GetSecondaryProfessions, GetSecondaryProfessions = _GetSecondaryProfessions,
GetSkillInfo = _GetSkillInfo, GetSkillInfo = _GetSkillInfo,
GetSkillInfoByCategory = _GetSkillInfoByCategory, GetSkillInfoByCategory = _GetSkillInfoByCategory,
@@ -86,6 +141,8 @@ local PublicMethods = {
GetCookingRank = _GetCookingRank, GetCookingRank = _GetCookingRank,
GetFishingRank = _GetFishingRank, GetFishingRank = _GetFishingRank,
GetRidingRank = _GetRidingRank, GetRidingRank = _GetRidingRank,
GetWoodcuttingRank = _GetWoodcuttingRank,
GetWoodworkingRank = _GetWoodworkingRank,
} }
function addon:OnInitialize() function addon:OnInitialize()
@@ -93,6 +150,7 @@ function addon:OnInitialize()
DataStore:RegisterModule(addonName, addon, PublicMethods) DataStore:RegisterModule(addonName, addon, PublicMethods)
DataStore:SetCharacterBasedMethod("GetPrimaryProfessions") DataStore:SetCharacterBasedMethod("GetPrimaryProfessions")
DataStore:SetCharacterBasedMethod("GetPrimaryProfessionList")
DataStore:SetCharacterBasedMethod("GetSecondaryProfessions") DataStore:SetCharacterBasedMethod("GetSecondaryProfessions")
DataStore:SetCharacterBasedMethod("GetSkillInfo") DataStore:SetCharacterBasedMethod("GetSkillInfo")
DataStore:SetCharacterBasedMethod("GetSkillInfoByCategory") DataStore:SetCharacterBasedMethod("GetSkillInfoByCategory")
@@ -100,15 +158,25 @@ function addon:OnInitialize()
DataStore:SetCharacterBasedMethod("GetCookingRank") DataStore:SetCharacterBasedMethod("GetCookingRank")
DataStore:SetCharacterBasedMethod("GetFishingRank") DataStore:SetCharacterBasedMethod("GetFishingRank")
DataStore:SetCharacterBasedMethod("GetRidingRank") DataStore:SetCharacterBasedMethod("GetRidingRank")
DataStore:SetCharacterBasedMethod("GetWoodcuttingRank")
DataStore:SetCharacterBasedMethod("GetWoodworkingRank")
end end
function addon:OnEnable() function addon:OnEnable()
-- CoA fix: the old build only scanned in PLAYER_ALIVE while the player was a
-- ghost (see below), so a living character that never died was never scanned
-- and every profession rank rendered as 0 ("Skills shows no data"). We now scan
-- on login and whenever the skill window changes so every character populates.
addon:RegisterEvent("PLAYER_ALIVE") addon:RegisterEvent("PLAYER_ALIVE")
addon:RegisterEvent("PLAYER_ENTERING_WORLD", "ScanOnLogin")
addon:RegisterEvent("SKILL_LINES_CHANGED", "ScanOnLogin")
addon:RegisterEvent("CHAT_MSG_SKILL") addon:RegisterEvent("CHAT_MSG_SKILL")
end end
function addon:OnDisable() function addon:OnDisable()
addon:UnregisterEvent("PLAYER_ALIVE") addon:UnregisterEvent("PLAYER_ALIVE")
addon:UnregisterEvent("PLAYER_ENTERING_WORLD")
addon:UnregisterEvent("SKILL_LINES_CHANGED")
addon:UnregisterEvent("CHAT_MSG_SKILL") addon:UnregisterEvent("CHAT_MSG_SKILL")
end end
@@ -171,11 +239,26 @@ end
-- *** EVENT HANDLERS *** -- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE() function addon:PLAYER_ALIVE()
-- print("DataStore_Skills.lua") -- DEBUG 2025 07 21 -- print("DataStore_Skills.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanSkills() ScanSkills()
end end
-- CoA fix: scan once shortly after login / when skills change. SKILL_LINES_CHANGED
-- can fire many times in a burst (once per skill line during login), so throttle to
-- a single deferred scan instead of scanning on every event.
local scanScheduled
function addon:ScanOnLogin()
if scanScheduled then return end
scanScheduled = true
-- defer so the skill UI / API is fully populated before we read GetNumSkillLines()
addon:ScheduleTimer(function()
scanScheduled = nil
ScanSkills()
end, 2)
end
-- this turns -- this turns
-- "Your skill in %s has increased to %d." -- "Your skill in %s has increased to %d."
-- into -- into
+1 -1
View File
@@ -2,7 +2,7 @@
## Title: DataStore_Skills ## Title: DataStore_Skills
## Notes: Stores information about character skills ## Notes: Stores information about character skills
## Author: Thaoky (EU-Marécages de Zangar) ## Author: Thaoky (EU-Marécages de Zangar)
## Version: 3.3.002 ## Version: 3.3.002-coa.10
## Dependencies: DataStore ## Dependencies: DataStore
## OptionalDeps: Ace3 ## OptionalDeps: Ace3
## SavedVariables: DataStore_SkillsDB ## SavedVariables: DataStore_SkillsDB
+2 -1
View File
@@ -105,7 +105,8 @@ end
-- *** EVENT HANDLERS *** -- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE() function addon:PLAYER_ALIVE()
-- print("DataStore_Spells.lua") -- DEBUG 2025 07 21 -- print("DataStore_Spells.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanSpells() ScanSpells()
end end
+2 -1
View File
@@ -156,7 +156,8 @@ end
-- *** EVENT HANDLERS *** -- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE() function addon:PLAYER_ALIVE()
-- print("DataStore_Stats.lua") -- DEBUG 2025 07 21 -- print("DataStore_Stats.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanStats() ScanStats()
end end
+21 -15
View File
@@ -234,41 +234,47 @@ local function _GetReferenceTable()
end end
local function _GetClassReference(class) local function _GetClassReference(class)
assert(type(class) == "string") -- CoA: custom classes (MONK, BARBARIAN, …) have no vanilla reference table; return nil
-- instead of asserting/crashing so callers can degrade gracefully.
if type(class) ~= "string" then return end
return addon.ref.global[class] return addon.ref.global[class]
end end
local function _GetTreeReference(class, tree) local function _GetTreeReference(class, tree)
assert(type(class) == "string") -- CoA: custom classes (MONK, etc.) may have no/partial talent reference data, so a
assert(type(tree) == "string") -- tree lookup can arrive with a nil tree name. Degrade to nil instead of asserting.
return addon.ref.global[class].Trees[tree] if type(class) ~= "string" or type(tree) ~= "string" then return end
local c = addon.ref.global[class]
if not c or not c.Trees then return end
return c.Trees[tree]
end end
local function _IsClassKnown(class) local function _IsClassKnown(class)
class = class or "" -- if by any chance nil is passed, trap it to make sure the function does not fail, but returns nil anyway class = class or "" -- if by any chance nil is passed, trap it to make sure the function does not fail, but returns nil anyway
local ref = _GetClassReference(class) local ref = _GetClassReference(class)
if ref.Order then -- if the Order field is not nil, we have data for this class if ref and ref.Order then -- CoA: ref is nil for custom classes; was an unguarded index crash
return true return true
end end
end end
local function _ImportClassReference(class, data) local function _ImportClassReference(class, data)
assert(type(class) == "string") -- CoA: data arrives over Comm/AccountSharing; a peer with no reference for a custom
assert(type(data) == "table") -- class can send nil, which used to crash the import. Skip silently instead.
if type(class) ~= "string" or type(data) ~= "table" then return end
addon.ref.global[class] = data addon.ref.global[class] = data
end end
local function _GetClassTrees(class) local function _GetClassTrees(class)
assert(type(class) == "string") -- CoA: ref is nil for custom classes; guard so the `for tree in DS:GetClassTrees()`
-- loops in Talents.lua get an empty iterator instead of an index-nil crash.
local ref = _GetClassReference(class) local ref = _GetClassReference(class)
local order = ref.Order local order = ref and ref.Order
if order then if order then
return order:gmatch("([^,]+)") return order:gmatch("([^,]+)")
end end
-- to do, add a return value that does not require validity testing by the caller return function() return nil end -- empty iterator so callers can loop safely
end end
local function _GetTreeInfo(class, tree) local function _GetTreeInfo(class, tree)
@@ -281,8 +287,7 @@ end
local function _GetTreeNameByID(class, id) local function _GetTreeNameByID(class, id)
-- returns the name of tree "id" for a given class -- returns the name of tree "id" for a given class
assert(type(class) == "string") -- CoA: _GetClassTrees now yields an empty iterator for custom classes, so no assert needed
local index = 1 local index = 1
for name in _GetClassTrees(class) do for name in _GetClassTrees(class) do
if index == id then if index == id then
@@ -414,7 +419,8 @@ end
-- *** EVENT HANDLERS *** -- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE() function addon:PLAYER_ALIVE()
-- print("DataStore_Talents.lua") -- DEBUG 2025 07 21 -- print("DataStore_Talents.lua") -- DEBUG 2025 07 21
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard if addon.coaScannedThisSession then return end -- CoA: scan once at login (was ghost-gated, so data never saved on a normal login - the cause of "data not saved")
addon.coaScannedThisSession = true
ScanTalents() ScanTalents()
ScanTalentReference() ScanTalentReference()
+9
View File
@@ -5,6 +5,15 @@ Altoholic: modified development for WotLK
Ported for the Ascension CoA (Vol'jin) 3.3.5a client by the Exiles guild. Released as `*-coa.N` tags via Gitea Actions; see `Exiles/coa-altoholic`. Ported for the Ascension CoA (Vol'jin) 3.3.5a client by the Exiles guild. Released as `*-coa.N` tags via Gitea Actions; see `Exiles/coa-altoholic`.
- **3.3.002b-coa.12** — Bigger main window (real size, not scale): `AltoholicFrame` 447→555px tall; AuctionFrame background art re-anchored with a middle filler; every scrolling tab shows more rows (18px tabs 14→20, 41px tabs 7/8→10) with matching `$parentEntryN` frames + grown scrollframes. Visual polish (art seam, scrollbar track length) may need a tweak after testing.
- **3.3.002b-coa.11** — Hotfix: Skills tab crash — `$parentSkill1` ButtonText global-name collision left `Skill1NormalText` nil; made it self-contained.
- **3.3.002b-coa.10** — Three CoA data-coverage features:
- **Reputation** is now data-driven: shows every faction a character actually has (grouped by in-game category), so CoA's custom factions (and future ones) appear automatically. The old hardcoded faction tree is kept only as an icon lookup.
- **Class icons** for CoA custom classes (1232) now render from a bundled CoA atlas (`Altoholic/images/coa-classes.blp`, texcoords from the CoA Details fork) instead of falling back to the Warrior glue icon.
- **Skills** tab shows ALL known professions (dynamic list, not 2 fixed slots) incl. Woodcutting/Woodworking, and fixes "no profession data" — `DataStore_Skills` now scans on login (`PLAYER_ENTERING_WORLD`/`SKILL_LINES_CHANGED`) instead of only on death/ghost-release.
- **3.3.002b-coa.9** — Reverted the 1.4 default scale (it only zoomed, didn't show more content; scale stays user-opt-in at 1.0 default, applied on open). Hardened `Options:Get/Set` against a nil `options` table (`TabOptions.lua:442` crash). Guild Members: guard `Level_OnClick` against cleared/stale row IDs (clicking AiL crashed). **New:** Personal + Realm bank tracking ported from coa-bagnon (detects CoA `BANK_PERMISSIONS_PAYLOAD`, personal=per-char, realm=per-realm; surfaced in Search + BagUsage tooltip). **New:** Woodcutting + Woodworking columns on the Skills tab (CoA custom professions). NOTE: Skills "all professions" redesign, profession data population, character icons, and reputation factions are still in progress.
- **3.3.002b-coa.8** — Title bar reads just `Altoholic <version>` (from the live `.toc`), dropping the "by Thaoky (Edited by Telkar-RG 1.04a)" string. Window now opens at the AtlasLoot-ish default scale (`UIScale` 1.4, ≈ 1105×640); scale is applied on every open (upstream only applied it after visiting Options), with a one-time bump for profiles still on the old 1.0 default.
- **3.3.002b-coa.7** — Skills tab: `GetColor()` now nil-safe and the per-skill rank fields (`skillRank1/2`, `cooking`, `firstaid`, `fishing`, `riding`) default to `0` — they're nil for chars `DataStore_Characters` hasn't scanned, which crashed the Skills summary (`floor(rank/…)` arithmetic and the `>= 300` riding check).
- **3.3.002b-coa.6** — Final straggler: guarded `AccountSharing.lua` realm/name line (name getter was the last `format` arg, so a no-value collapsed it to a format error). Concludes the frame sweep. - **3.3.002b-coa.6** — Final straggler: guarded `AccountSharing.lua` realm/name line (name getter was the last `format` arg, so a no-value collapsed it to a format error). Concludes the frame sweep.
- **3.3.002b-coa.5** — Refactor + completeness pass: - **3.3.002b-coa.5** — Refactor + completeness pass:
- Extracted the duplicated character header/row blocks into `Altoholic:AddCharacterTooltipHeader()` and `Altoholic:SetCharacterRowNameLevel()` — the nil-guards now live in one place instead of being copy-pasted across frames. - Extracted the duplicated character header/row blocks into `Altoholic:AddCharacterTooltipHeader()` and `Altoholic:SetCharacterRowNameLevel()` — the nil-guards now live in one place instead of being copy-pasted across frames.