Compare commits

...

4 Commits

Author SHA1 Message Date
florian.berthold 3fde42454e coa.4: rebrand title to Exiles fork + frame hardening changelog
release / release (push) Successful in 4s
Title drops Telkar-RG's personal 1.04a; now 'Altoholic (Exiles)'.
Author keeps Thaoky/Telkar-RG credit. Bundles the frame partial-record
guards from the previous commit.
2026-05-28 23:54:32 +02:00
florian.berthold f19ff36733 Harden Altoholic frames against partial alt records (no-value char getters)
DataStore char-based getters return *no value* for any module that hasn't
scanned a given char (DataStore.lua: 'if not arg1.lastUpdate then return end').
Fresh CoA alts have partial per-module data, so the frames crashed feeding
'no value' into format()/concat/arithmetic/pairs. Guarded every such site:
AccountSummary, Activity, BagUsage, Quests, Reputations, TabCharacters,
DrawCharacterTooltip, recipe tooltip. No DataStore contract change.
2026-05-28 23:45:09 +02:00
florian.berthold b332499098 coa.3: guard XP getters in DataStore_Characters for partial records
release / release (push) Successful in 5s
GetXPRate / GetRestXPRate did raw arithmetic on character.XP / .XPMax /
.lastLogoutTimestamp, which are nil for own alts seen via guild comm but
never fully scanned. Crashed AccountSummary on Vol'jin - CoA Beta.
2026-05-28 22:40:04 +02:00
florian.berthold 961290d9bd coa.2: guard CoA data shapes (custom classes, comm-seeded records, ungeared chars)
release / release (push) Successful in 5s
Fixes login/UI crashes on Vol'jin - CoA Beta:
- DataStore_Inventory: GetAverageItemLevel returns 0 not nil (Altoholic sort
  + AccountSummary iLvl format); guard login AIL broadcast and 0/0 average.
- DataStore_Pets: GetNumPets returns 0 for unscanned companion table instead
  of assert-crashing TabCharacters.
- DataStore_Characters: GetColoredCharacterName tolerates nil name.
- Altoholic/Characters.lua: GetLineType returns nil for stale line id.

Stamp -coa.2 + X-Edited-By: Exiles on touched addons; README CoA changelog.
2026-05-28 22:16:44 +02:00
17 changed files with 98 additions and 73 deletions
+9 -9
View File
@@ -765,23 +765,23 @@ function Altoholic:DrawCharacterTooltip(self, charName)
AltoTooltip:ClearLines();
AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character))
AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"],
GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1)
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)
local zone, subZone = DS:GetLocation(character)
AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..zone, GOLD..subZone),1,1,1)
AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..(zone or "?"), GOLD..(subZone or "")),1,1,1)
local restXP = DS:GetRestXP(character)
if restXP and restXP > 0 then
AltoTooltip:AddLine(format("%s: %s", L["Rest XP"], GREEN..restXP),1,1,1)
end
AltoTooltip:AddLine("Average iLevel: " .. GREEN .. format("%.1f", DS:GetAverageItemLevel(character)),1,1,1);
AltoTooltip:AddLine("Average iLevel: " .. GREEN .. format("%.1f", DS:GetAverageItemLevel(character) or 0),1,1,1);
if IsAddOnLoaded("DataStore_Achievements") then
if DS:GetNumCompletedAchievements(character) > 0 then
AltoTooltip:AddLine(ACHIEVEMENTS_COMPLETED ..": " .. GREEN .. DS:GetNumCompletedAchievements(character) .. "/"..DS:GetNumAchievements(character))
AltoTooltip:AddLine(ACHIEVEMENT_TITLE ..": " .. GREEN .. DS:GetNumAchievementPoints(character))
if (DS:GetNumCompletedAchievements(character) or 0) > 0 then
AltoTooltip:AddLine(ACHIEVEMENTS_COMPLETED ..": " .. GREEN .. DS:GetNumCompletedAchievements(character) .. "/"..(DS:GetNumAchievements(character) or 0))
AltoTooltip:AddLine(ACHIEVEMENT_TITLE ..": " .. GREEN .. (DS:GetNumAchievementPoints(character) or 0))
end
end
+4 -3
View File
@@ -1,18 +1,19 @@
## Interface: 30300
## Title: Altoholic (|cFF69CCF0Telkar-RG|cFFFFFFFF 1.04a|r)
## Title: Altoholic (|cFFFFD100Exiles|r)
## X-Curse-Packaged-Version: r90
## X-Curse-Project-Name: Altoholic
## X-Curse-Project-ID: altoholic
## X-Curse-Repository-ID: wow/altoholic/mainline
## Notes: Provides information about your alts
## Notes: Provides information about your alts (CoA fork)
## Notes-ruRU: Предоставляет информацию о вашем персонажах
## Notes-zhTW: 讓你即時撿閱你所有角色的資料
## Notes-zhCN: 能让你全面掌握你的所有角色的信息。
## Author: Thaoky, Telkar-RG
## Version: 3.3.002b
## X-Edited-By: Exiles (Sub-Net) — florian.berthold@sub-net.at
## Version: 3.3.002b-coa.4
## X-Category: Inventory, Tradeskill, Mail
## X-Localizations: enUS, frFR, zhCN, zhTW, deDE, koKR, esES, esMX, ruRU
## X-Website: http://wow.curse.com/downloads/wow-addons/details/altoholic.aspx
+3 -1
View File
@@ -277,7 +277,9 @@ function ns:GetInfo(index)
end
function ns:GetLineType(index)
return mod(characterList[index].linetype, 3)
local c = characterList[index] -- CoA: hover can fire with a stale/out-of-range line id; callers (Level_OnEnter) already handle a nil return
if not c then return end
return mod(c.linetype, 3)
end
function ns:GetField(index, field)
+19 -17
View File
@@ -249,12 +249,14 @@ function ns:Update()
_G[entry..i.."Name"]:SetWidth(170)
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0)
_G[entry..i.."NameNormalText"]:SetWidth(170)
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character)))
_G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(character))
-- CoA: DataStore char-based getters return *no value* for any module that hasn't scanned this char
-- (DataStore.lua: "if not arg1.lastUpdate then return end"). Fresh alts have partial module data, so guard every result.
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character) or "?", DS:GetCharacterClass(character) or ""))
_G[entry..i.."Level"]:SetText(GREEN .. (DS:GetCharacterLevel(character) or 0))
_G[entry..i.."Money"]:SetText(addon:GetMoneyString(DS:GetMoney(character)))
_G[entry..i.."Played"]:SetText(addon:GetTimeString(DS:GetPlayTime(character)))
_G[entry..i.."XP"]:SetText(GREEN .. DS:GetXPRate(character) .. "%")
_G[entry..i.."Money"]:SetText(addon:GetMoneyString(DS:GetMoney(character) or 0))
_G[entry..i.."Played"]:SetText(addon:GetTimeString(DS:GetPlayTime(character) or 0))
_G[entry..i.."XP"]:SetText(GREEN .. (DS:GetXPRate(character) or 0) .. "%")
if DS:GetCharacterLevel(character) == MAX_PLAYER_LEVEL then
_G[entry..i.."Rested"]:SetText(WHITE .. "0%")
@@ -262,7 +264,7 @@ function ns:Update()
_G[entry..i.."Rested"]:SetText( addon:GetRestedXP(character) )
end
_G[entry..i.."AvgILevelNormalText"]:SetText(YELLOW..format("%.1f", DS:GetAverageItemLevel(character)))
_G[entry..i.."AvgILevelNormalText"]:SetText(YELLOW..format("%.1f", DS:GetAverageItemLevel(character) or 0))
elseif (lineType == INFO_TOTAL_LINE) then
_G[entry..i.."Collapse"]:Hide()
@@ -343,22 +345,22 @@ function ns:Level_OnEnter(frame)
AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT");
AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character))
AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"],
GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1)
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)
local zone, subZone = DS:GetLocation(character)
AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..zone, GOLD..subZone),1,1,1)
AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..(zone or "?"), GOLD..(subZone or "")),1,1,1)
local guildName = DS:GetGuildInfo(character)
if guildName then
AltoTooltip:AddLine(format("%s: %s", GUILD, GREEN..guildName),1,1,1)
end
AltoTooltip:AddLine(EXPERIENCE_COLON .. " "
.. GREEN .. DS:GetXP(character) .. WHITE .. "/"
.. GREEN .. DS:GetXPMax(character) .. WHITE .. " ("
.. GREEN .. DS:GetXPRate(character) .. "%"
.. WHITE .. ")",1,1,1);
AltoTooltip:AddLine(EXPERIENCE_COLON .. " "
.. GREEN .. (DS:GetXP(character) or 0) .. WHITE .. "/"
.. GREEN .. (DS:GetXPMax(character) or 0) .. WHITE .. " ("
.. GREEN .. (DS:GetXPRate(character) or 0) .. "%"
.. WHITE .. ")",1,1,1);
local restXP = DS:GetRestXP(character)
if restXP and restXP > 0 then
@@ -448,7 +450,7 @@ function ns:AIL_OnEnter(frame)
AltoTooltip:ClearLines();
AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT");
AltoTooltip:AddLine(DS:GetColoredCharacterName(character),1,1,1);
AltoTooltip:AddLine(WHITE .. L["Average Item Level"] ..": " .. GREEN.. format("%.1f", DS:GetAverageItemLevel(character)),1,1,1);
AltoTooltip:AddLine(WHITE .. L["Average Item Level"] ..": " .. GREEN.. format("%.1f", DS:GetAverageItemLevel(character) or 0),1,1,1);
addon:AiLTooltip()
AltoTooltip:Show();
+12 -12
View File
@@ -100,8 +100,8 @@ function ns:Update()
_G[entry..i.."Name"]:SetWidth(170)
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0)
_G[entry..i.."NameNormalText"]:SetWidth(170)
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character)))
_G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(character))
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character) or "?", DS:GetCharacterClass(character) or ""))
_G[entry..i.."Level"]:SetText(GREEN .. (DS:GetCharacterLevel(character) or 0))
local color
local num = DS:GetNumMails(character) or 0
@@ -112,7 +112,7 @@ function ns:Update()
color = GREEN -- green by default, red if at least one mail is about to expire
local threshold = DataStore:GetOption("DataStore_Mails", "MailWarningThreshold")
if DS:GetNumExpiredMails(character, threshold) > 0 then
if (DS:GetNumExpiredMails(character, threshold) or 0) > 0 then
color = RED
end
end
@@ -182,17 +182,17 @@ function ns:OnEnter(self)
AltoTooltip:SetOwner(self, "ANCHOR_RIGHT");
AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character))
AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"],
GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1)
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)
local zone, subZone = DS:GetLocation(character)
AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..zone, GOLD..subZone),1,1,1)
AltoTooltip:AddLine(EXPERIENCE_COLON .. " "
.. GREEN .. DS:GetXP(character) .. WHITE .. "/"
.. GREEN .. DS:GetXPMax(character) .. WHITE .. " ("
.. GREEN .. DS:GetXPRate(character) .. "%"
.. WHITE .. ")",1,1,1);
AltoTooltip:AddLine(format("%s: %s |r(%s|r)", L["Zone"], GOLD..(zone or "?"), GOLD..(subZone or "")),1,1,1)
AltoTooltip:AddLine(EXPERIENCE_COLON .. " "
.. GREEN .. (DS:GetXP(character) or 0) .. WHITE .. "/"
.. GREEN .. (DS:GetXPMax(character) or 0) .. WHITE .. " ("
.. GREEN .. (DS:GetXPRate(character) or 0) .. "%"
.. WHITE .. ")",1,1,1);
local restXP = DS:GetRestXP(character)
if restXP and restXP > 0 then
+16 -16
View File
@@ -96,26 +96,26 @@ function ns:Update()
_G[entry..i.."Name"]:SetWidth(170)
_G[entry..i.."Name"]:SetPoint("TOPLEFT", 10, 0)
_G[entry..i.."NameNormalText"]:SetWidth(170)
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character), DS:GetCharacterClass(character)))
_G[entry..i.."Level"]:SetText(GREEN .. DS:GetCharacterLevel(character))
_G[entry..i.."FreeBags"]:SetText(GREEN .. DS:GetNumFreeBagSlots(character))
_G[entry..i.."FreeBank"]:SetText(GREEN .. DS:GetNumFreeBankSlots(character))
_G[entry..i.."NameNormalText"]:SetText(icon .. format("%s (%s)", DS:GetColoredCharacterName(character) or "?", DS:GetCharacterClass(character) or ""))
_G[entry..i.."Level"]:SetText(GREEN .. (DS:GetCharacterLevel(character) or 0))
_G[entry..i.."FreeBags"]:SetText(GREEN .. (DS:GetNumFreeBagSlots(character) or 0))
_G[entry..i.."FreeBank"]:SetText(GREEN .. (DS:GetNumFreeBankSlots(character) or 0))
_G[entry..i.."BagSlotsNormalText"]:SetJustifyH("LEFT")
_G[entry..i.."BankSlotsNormalText"]:SetJustifyH("LEFT")
-- Normal bags
_G[entry..i.."BagSlotsNormalText"]:SetText(format("%s/%s|r/%s|r/%s|r/%s |r(%s|r)",
DS:GetContainerSize(character, 0),
WHITE .. DS:GetContainerSize(character, 1),
WHITE .. DS:GetContainerSize(character, 2),
WHITE .. DS:GetContainerSize(character, 3),
WHITE .. DS:GetContainerSize(character, 4),
CYAN .. DS:GetNumBagSlots(character)))
DS:GetContainerSize(character, 0) or 0,
WHITE .. (DS:GetContainerSize(character, 1) or 0),
WHITE .. (DS:GetContainerSize(character, 2) or 0),
WHITE .. (DS:GetContainerSize(character, 3) or 0),
WHITE .. (DS:GetContainerSize(character, 4) or 0),
CYAN .. (DS:GetNumBagSlots(character) or 0)))
-- Bank bags
if DS:GetNumBankSlots(character) < 28 then
if (DS:GetNumBankSlots(character) or 0) < 28 then
_G[entry..i.."BankSlotsNormalText"]:SetText(L["Bank not visited yet"])
else
_G[entry..i.."BankSlotsNormalText"]:SetText(format("%s/%s|r/%s|r/%s|r/%s|r/%s|r/%s|r/%s |r(%s|r)",
@@ -183,8 +183,8 @@ function ns:OnEnter(self)
AltoTooltip:ClearLines();
AltoTooltip:SetOwner(self, "ANCHOR_RIGHT");
AltoTooltip:AddDoubleLine(DS:GetColoredCharacterName(character), DS:GetColoredCharacterFaction(character))
AltoTooltip:AddLine(format("%s %s |r%s %s", L["Level"],
GREEN..DS:GetCharacterLevel(character), DS:GetCharacterRace(character), DS:GetCharacterClass(character)),1,1,1)
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)
AltoTooltip:AddLine(" ",1,1,1);
local id = self:GetID()
@@ -203,7 +203,7 @@ function ns:OnEnter(self)
end
numSlots = DS:GetNumBagSlots(character)
numFree = DS:GetNumFreeBagSlots(character)
elseif DS:GetNumBankSlots(character) < 28 then
elseif (DS:GetNumBankSlots(character) or 0) < 28 then
AltoTooltip:AddLine(L["Bank not visited yet"],1,1,1);
AltoTooltip:Show();
return
+1 -1
View File
@@ -40,7 +40,7 @@ function ns:Update()
local DS = DataStore
if DS:GetQuestLogSize(character) == 0 then
if (DS:GetQuestLogSize(character) or 0) == 0 then
AltoholicTabCharactersStatus:SetText(L["No quest found for "] .. addon:GetCurrentCharacter())
addon:ClearScrollFrame( _G[ frame.."ScrollFrame" ], entry, VisibleLines, 18)
return
+1 -1
View File
@@ -310,7 +310,7 @@ function ns:OnEnter(frame)
AltoTooltip:SetOwner(frame, "ANCHOR_LEFT");
AltoTooltip:ClearLines();
AltoTooltip:AddLine(DS:GetColoredCharacterName(character) .. WHITE .. " @ " .. TEAL .. faction,1,1,1);
AltoTooltip:AddLine((DS:GetColoredCharacterName(character) or "?") .. WHITE .. " @ " .. TEAL .. faction,1,1,1);
rate = format("%d", floor(rate)) .. "%"
AltoTooltip:AddLine(format("%s: %d/%d (%s)", status, currentLevel, maxLevel, rate),1,1,1 )
+1 -1
View File
@@ -228,7 +228,7 @@ function ns:UpdateViewIcons()
AltoholicTabCharacters_FirstAid:Show()
local i = 1
for skillName, skill in pairs(DS:GetPrimaryProfessions(character)) do
for skillName, skill in pairs(DS:GetPrimaryProfessions(character) or {}) do -- CoA: getter returns no value for chars DataStore_Crafts hasn't scanned
local itemName = "AltoholicTabCharacters_Prof" .. i
local item = _G[itemName]
local spellID = DataStore:GetProfessionSpellID(skillName)
+1 -1
View File
@@ -448,7 +448,7 @@ local function GetRecipeOwners(professionName, link, recipeLevel)
table.insert(know, coloredName)
else
local currentLevel = DataStore:GetSkillInfo(character, professionName)
if currentLevel > 0 then
if currentLevel and currentLevel > 0 then -- CoA: getter returns no value for chars DataStore_Skills hasn't scanned
if currentLevel < recipeLevel then
table.insert(willLearn, format("%s |r(%d)", coloredName, currentLevel))
else
@@ -169,7 +169,7 @@ do
end
local function _GetColoredCharacterName(character)
return (ClassColors[character.englishClass] or WHITE) .. character.name
return (ClassColors[character.englishClass] or WHITE) .. (character.name or "?") -- CoA: records seeded from guild comm before a full scan have no name yet
end
local function _GetClassColor(character)
@@ -206,7 +206,9 @@ local function _GetXP(character)
end
local function _GetXPRate(character)
return floor((character.XP / character.XPMax) * 100)
local xpMax = character.XPMax or 0 -- CoA: comm-seeded / max-level / unscanned char has no XP data (also avoids /0)
if xpMax == 0 then return 0 end
return floor(((character.XP or 0) / xpMax) * 100)
end
local function _GetXPMax(character)
@@ -234,7 +236,7 @@ local function _GetRestXPRate(character)
-- divide rest xp by this value 20400 / 204 = 100 ==> rest xp rate
local rate = 0
if character.RestXP then
if character.RestXP and character.XPMax and character.XPMax > 0 then -- CoA: guard nil/zero XPMax
rate = (character.RestXP / ((character.XPMax / 100) * 1.5))
end
@@ -242,7 +244,7 @@ local function _GetRestXPRate(character)
-- (elapsed time / 3600) * 0.625 * (2/3) simplifies to elapsed time / 8640
-- 0.625 comes from 8 hours rested = 5% of a level, *2/3 because 100% rested = 150% of xp (1.5 level)
if character.lastLogoutTimestamp ~= 0 then -- time since last logout, 0 for current char, <> for all others
if character.lastLogoutTimestamp and character.lastLogoutTimestamp ~= 0 then -- time since last logout, 0 for current char, <> for all others (CoA: nil for comm-seeded chars => "nil ~= 0" is true and crashed)
if character.isResting then
rate = rate + ((time() - character.lastLogoutTimestamp) / 8640)
else
@@ -2,7 +2,8 @@
## Title: DataStore_Characters
## Notes: Stores information about characters (race, level, etc..)
## Author: Thaoky (EU-Marécages de Zangar)
## Version: 3.3.001
## X-Edited-By: Exiles (Sub-Net)
## Version: 3.3.001-coa.3
## Dependencies: DataStore
## OptionalDeps: Ace3
## SavedVariables: DataStore_CharactersDB
+3 -3
View File
@@ -88,7 +88,7 @@ local function GetAIL(alts)
local out = {}
local character = DataStore:GetCharacter() -- this character
local ail = DataStore:GetAverageItemLevel(character)
local ail = DataStore:GetAverageItemLevel(character) or 0 -- CoA: guard nil so the login AIL broadcast can't crash (mirrors the alt loop below)
table.insert(out, format("%s:%d", UnitName("player"), ail))
if strlen(alts) > 0 then
@@ -162,7 +162,7 @@ function ScanInventory()
end
end
addon.ThisCharacter.averageItemLvl = totalItemLevel / itemCount
addon.ThisCharacter.averageItemLvl = (itemCount > 0) and (totalItemLevel / itemCount) or 0 -- CoA: ungeared char => itemCount 0; avoid 0/0 nan
addon.ThisCharacter.lastUpdate = time()
end
@@ -202,7 +202,7 @@ local function _GetInventoryItemCount(character, searchedID)
end
local function _GetAverageItemLevel(character)
return character.averageItemLvl
return character.averageItemLvl or 0 -- CoA: chars known only via guild comm / not yet scanned have no iLvl; callers do arithmetic on it (Altoholic sort, AccountSummary format)
end
local sentRequests -- recently sent requests
+2 -1
View File
@@ -2,7 +2,8 @@
## Title: DataStore_Inventory
## Notes: Stores information about character inventory
## Author: Thaoky (EU-Marécages de Zangar)
## Version: 3.3.002
## X-Edited-By: Exiles (Sub-Net)
## Version: 3.3.002-coa.2
## Dependencies: DataStore
## OptionalDeps: Ace3
## SavedVariables: DataStore_InventoryDB
+1 -1
View File
@@ -70,7 +70,7 @@ local function _GetPets(character, companionType)
end
local function _GetNumPets(pets)
assert(type(pets) == "table") -- this is the pointer to a pet table, obtained through GetPets()
if type(pets) ~= "table" then return 0 end -- CoA: char may have no scanned CRITTER/MOUNT table yet; upstream assert() crashed TabCharacters
return #pets
end
+2 -1
View File
@@ -2,7 +2,8 @@
## Title: DataStore_Pets
## Notes: Stores information about character pets and mounts
## Author: Thaoky (EU-Marécages de Zangar)
## Version: 3.3.001
## X-Edited-By: Exiles (Sub-Net)
## Version: 3.3.001-coa.2
## Dependencies: DataStore
## OptionalDeps: Ace3
## SavedVariables: DataStore_PetsDB
+15
View File
@@ -1,6 +1,21 @@
# wow-Altoholic-dev
Altoholic: modified development for WotLK
## CoA fork (Exiles)
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.4** — Rebranded to the Exiles fork (title `Altoholic (Exiles)`; Thaoky/Telkar-RG still credited as Author). Hardened **all** Altoholic frames against partial alt records: DataStore char-based getters return *no value* for any module that hasn't scanned a char, and the frames assumed full data everywhere. Guarded every `format`/concat/arithmetic/`pairs` site across AccountSummary, Activity, BagUsage, Quests, Reputations, TabCharacters, `DrawCharacterTooltip`, and the recipe tooltip. No DataStore contract change.
- **3.3.002b-coa.3** — More partial-record guards in `DataStore_Characters` (own alts seen via guild comm but never fully scanned):
- `GetXPRate` — guard nil/zero `XPMax` (crashed AccountSummary; also fixes div-by-zero at max level).
- `GetRestXPRate` — guard nil/zero `XPMax` and nil `lastLogoutTimestamp` (`nil ~= 0` was true and crashed).
- **3.3.002b-coa.2** — Defensive guards for CoA data shapes (custom classes, records seeded from guild comm before a full scan, ungeared chars). Fixes login/UI crashes:
- `DataStore_Inventory``GetAverageItemLevel` returns `0` instead of `nil` (crashed the Altoholic char sort and AccountSummary iLvl format); guarded the login AIL broadcast and the `0/0` average for ungeared chars.
- `DataStore_Pets``GetNumPets` returns `0` for an unscanned companion table instead of `assert`-crashing TabCharacters.
- `DataStore_Characters``GetColoredCharacterName` tolerates a `nil` name (records known only via guild comm).
- `Altoholic/Characters.lua``GetLineType` returns `nil` for a stale/out-of-range line id (caller already handles it) instead of indexing a nil row.
- **3.3.002b-coa.1** — Initial CoA packaging (Altoholic + Altoholic_Achievements + 16 DataStore modules).
Added "Keys" Tab for better overview of owned keys and some attunement quests.
## Changes
- [Edited Version 1.04a](https://github.com/telkar-rg/wow-Altoholic-dev/releases/tag/t1.04a)