Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d440c62a73 | |||
| 219e749046 | |||
| 0a56cbe560 | |||
| b8d619c3bb | |||
| f305f0a226 | |||
| f64d2c9250 | |||
| 1faf213f17 | |||
| ee7770baed |
@@ -495,6 +495,7 @@ function addon:SetItemButtonTexture(button, texture, width, height)
|
||||
height = height or 36
|
||||
|
||||
local itemTexture = _G[button.."IconTexture"]
|
||||
if not itemTexture then return end -- CoA: guard buttons that don't exist / lack an IconTexture region (e.g. iterating more professions than there are _ProfN buttons)
|
||||
|
||||
itemTexture:SetWidth(width);
|
||||
itemTexture:SetHeight(height);
|
||||
@@ -551,6 +552,7 @@ function addon:GetSpellIDFromRecipeLink(link)
|
||||
end
|
||||
|
||||
function addon:GetMoneyString(copper, color, noTexture)
|
||||
copper = copper or 0 -- CoA: callers may pass a no-value DS getter result
|
||||
color = color or "|cFFFFD700"
|
||||
|
||||
local gold = floor( copper / 10000 );
|
||||
@@ -571,6 +573,7 @@ function addon:GetMoneyString(copper, color, noTexture)
|
||||
end
|
||||
|
||||
function addon:GetTimeString(seconds)
|
||||
seconds = seconds or 0 -- CoA: callers may pass a no-value DS getter result
|
||||
local days = floor(seconds / 86400); -- TotalTime is expressed in seconds
|
||||
seconds = mod(seconds, 86400)
|
||||
local hours = floor(seconds / 3600);
|
||||
@@ -647,7 +650,7 @@ function Altoholic:FormatDelay(timeStamp)
|
||||
end
|
||||
|
||||
function addon:GetRestedXP(character)
|
||||
local rate = DS:GetRestXPRate(character)
|
||||
local rate = DS:GetRestXPRate(character) or 0 -- CoA: getter returns no value for unscanned/partial chars
|
||||
|
||||
local coeff = 1
|
||||
if addon.Options:Get("RestXPMode") == 1 then
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
## Author: Thaoky, Telkar-RG
|
||||
## X-Edited-By: Exiles (Sub-Net) — florian.berthold@sub-net.at
|
||||
## Version: 3.3.002b-coa.11
|
||||
## Version: 3.3.002b-coa.19
|
||||
## 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
|
||||
|
||||
@@ -118,15 +118,15 @@ function ns:Update()
|
||||
_G[entry..i.."BankSlotsNormalText"]:SetText(L["Bank not visited yet"])
|
||||
else
|
||||
_G[entry..i.."BankSlotsNormalText"]:SetText(format("%s/%s|r/%s|r/%s|r/%s|r/%s|r/%s|r/%s |r(%s|r)",
|
||||
DS:GetContainerSize(character, 100),
|
||||
WHITE .. DS:GetContainerSize(character, 5),
|
||||
WHITE .. DS:GetContainerSize(character, 6),
|
||||
WHITE .. DS:GetContainerSize(character, 7),
|
||||
WHITE .. DS:GetContainerSize(character, 8),
|
||||
WHITE .. DS:GetContainerSize(character, 9),
|
||||
WHITE .. DS:GetContainerSize(character, 10),
|
||||
WHITE .. DS:GetContainerSize(character, 11),
|
||||
CYAN .. DS:GetNumBankSlots(character)))
|
||||
DS:GetContainerSize(character, 100) or 0, -- CoA: empty/unscanned bank bags return nil size
|
||||
WHITE .. (DS:GetContainerSize(character, 5) or 0),
|
||||
WHITE .. (DS:GetContainerSize(character, 6) or 0),
|
||||
WHITE .. (DS:GetContainerSize(character, 7) or 0),
|
||||
WHITE .. (DS:GetContainerSize(character, 8) or 0),
|
||||
WHITE .. (DS:GetContainerSize(character, 9) or 0),
|
||||
WHITE .. (DS:GetContainerSize(character, 10) or 0),
|
||||
WHITE .. (DS:GetContainerSize(character, 11) or 0),
|
||||
CYAN .. (DS:GetNumBankSlots(character) or 0)))
|
||||
end
|
||||
elseif (lineType == INFO_TOTAL_LINE) then
|
||||
_G[entry..i.."Collapse"]:Hide()
|
||||
|
||||
@@ -178,7 +178,7 @@ local function UpdateSpread()
|
||||
local slotID = bagIndices[line].from - 3 + j
|
||||
local itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID)
|
||||
|
||||
if (slotID <= containerSize) then
|
||||
if (slotID <= (containerSize or 0)) then -- CoA: containerSize nil for unscanned bag on partial-data alt
|
||||
if itemID then
|
||||
Altoholic:SetItemButtonTexture(itemName, GetItemIcon(itemID));
|
||||
|
||||
@@ -278,7 +278,7 @@ local function UpdateAllInOne()
|
||||
local container = DS:GetContainer(character, containerID)
|
||||
local _, _, containerSize = DS:GetContainerInfo(character, containerID)
|
||||
|
||||
for slotID = 1, containerSize do
|
||||
for slotID = 1, (containerSize or 0) do -- CoA: containerSize nil for unscanned bag on partial-data alt
|
||||
local itemID, itemLink, itemCount = DS:GetSlotInfo(container, slotID)
|
||||
if itemID then
|
||||
currentSlotIndex = currentSlotIndex + 1
|
||||
|
||||
@@ -81,8 +81,8 @@ local SecondaryLevelSort = {-- sort functions for the alts
|
||||
end
|
||||
end,
|
||||
["level"] = function(a, b)
|
||||
local levelA = select(4, DataStore:GetGuildMemberInfo(a))
|
||||
local levelB = select(4, DataStore:GetGuildMemberInfo(b))
|
||||
local levelA = select(4, DataStore:GetGuildMemberInfo(a)) or 0 -- CoA: nil level on partial guild data crashed table.sort
|
||||
local levelB = select(4, DataStore:GetGuildMemberInfo(b)) or 0
|
||||
|
||||
if viewSortOrder then
|
||||
return levelA < levelB
|
||||
|
||||
@@ -25,8 +25,8 @@ local PrimaryLevelSort = { -- sort functions for the mains
|
||||
end
|
||||
end,
|
||||
["level"] = function(a, b)
|
||||
local levelA = select(4, DataStore:GetGuildMemberInfo(a.name))
|
||||
local levelB = select(4, DataStore:GetGuildMemberInfo(b.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)) or 0
|
||||
|
||||
if viewSortOrder then
|
||||
return levelA < levelB
|
||||
@@ -76,8 +76,8 @@ local SecondaryLevelSort = {-- sort functions for the alts
|
||||
end
|
||||
end,
|
||||
["level"] = function(a, b)
|
||||
local levelA = select(4, DataStore:GetGuildMemberInfo(a))
|
||||
local levelB = select(4, DataStore:GetGuildMemberInfo(b))
|
||||
local levelA = select(4, DataStore:GetGuildMemberInfo(a)) or 0 -- CoA: nil level on partial guild data crashed table.sort
|
||||
local levelB = select(4, DataStore:GetGuildMemberInfo(b)) or 0
|
||||
|
||||
if viewSortOrder then
|
||||
return levelA < levelB
|
||||
@@ -199,6 +199,7 @@ local function DisplayProfessionLink(frameName, member, index)
|
||||
local icon = addon:TextureToFontstring(addon:GetSpellIcon(tonumber(spellID)), 18, 18) .. " "
|
||||
if link then
|
||||
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
|
||||
text:SetText(icon .. ts:GetColor(curRank) .. curRank .. "/" .. maxRank)
|
||||
else
|
||||
@@ -350,6 +351,7 @@ function ns:OnEnter(self)
|
||||
if not spellID or not link then return end
|
||||
|
||||
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:SetOwner(self, "ANCHOR_RIGHT");
|
||||
|
||||
@@ -118,7 +118,8 @@ function ns:Update()
|
||||
local professions = Characters:GetField(line, "professions")
|
||||
local profText = ""
|
||||
if professions then
|
||||
for _, p in ipairs(professions) do
|
||||
for idx, p in ipairs(professions) do
|
||||
if idx > 6 then profText = profText .. YELLOW .. "+" .. (#professions - 6) .. "|r"; break end -- CoA: cap inline profs so the strip fits its 325px cell
|
||||
local rank = p.rank or 0
|
||||
local profIcon = ""
|
||||
if p.spellID then
|
||||
|
||||
@@ -227,10 +227,14 @@ function ns:UpdateViewIcons()
|
||||
AltoholicTabCharacters_FirstAid.text = professionName
|
||||
AltoholicTabCharacters_FirstAid:Show()
|
||||
|
||||
-- CoA: characters can know far more than the retail 2 primary professions, so the
|
||||
-- _ProfN button row may run out before the profession list does. Stop at the last
|
||||
-- existing button instead of indexing a nil frame (which crashed the character view).
|
||||
local i = 1
|
||||
for skillName, skill in pairs(DS:GetPrimaryProfessions(character) 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 item = _G[itemName]
|
||||
if not item then break end -- no more profession buttons available
|
||||
local spellID = DataStore:GetProfessionSpellID(skillName)
|
||||
|
||||
if spellID then
|
||||
@@ -243,6 +247,12 @@ function ns:UpdateViewIcons()
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
-- Hide any leftover profession buttons this character doesn't fill.
|
||||
while _G["AltoholicTabCharacters_Prof" .. i] do
|
||||
_G["AltoholicTabCharacters_Prof" .. i].text = nil
|
||||
_G["AltoholicTabCharacters_Prof" .. i]:Hide()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function ns:MenuItem_OnClick(frame, button)
|
||||
|
||||
@@ -257,6 +257,50 @@
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<!-- CoA: characters can know many more than 2 primary professions. Extra slots;
|
||||
UpdateViewIcons fills/hides them dynamically and stops at the last one. -->
|
||||
<Button name="$parent_Prof3" inherits="AltoViewIconTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof2" relativePoint="BOTTOMRIGHT" >
|
||||
<Offset x="5" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parent_Prof4" inherits="AltoViewIconTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof3" relativePoint="BOTTOMRIGHT" >
|
||||
<Offset x="5" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parent_Prof5" inherits="AltoViewIconTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parent_Prof1" relativePoint="BOTTOMLEFT" >
|
||||
<Offset x="5" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parent_Prof6" inherits="AltoViewIconTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof5" relativePoint="BOTTOMRIGHT" >
|
||||
<Offset x="5" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parent_Prof7" inherits="AltoViewIconTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof6" relativePoint="BOTTOMRIGHT" >
|
||||
<Offset x="5" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parent_Prof8" inherits="AltoViewIconTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="$parent_Prof7" relativePoint="BOTTOMRIGHT" >
|
||||
<Offset x="5" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<Button name="$parent_Sort1" inherits="AltoSortButtonTemplate" id="1">
|
||||
<Size>
|
||||
|
||||
@@ -337,6 +337,7 @@ function tns:Update(treeIndex)
|
||||
|
||||
-- textures are 90.625% of the original size
|
||||
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_bgTopRight:SetTexture(bg.."-TopRight")
|
||||
AltoholicFrameTalents_bgBottomLeft:SetTexture(bg.."-BottomLeft")
|
||||
|
||||
@@ -380,7 +380,7 @@ local function GetItemCount(searchedID)
|
||||
for tabID = 1, 6 do
|
||||
local tabCount = DataStore:GetGuildBankTabItemCount(guildKey, tabID, searchedID)
|
||||
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
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ local function ScanSingleAchievement(id, isCompleted, month, day, year)
|
||||
if critCompleted then
|
||||
table.insert(CriteriaCache, tostring(j))
|
||||
else
|
||||
if reqQuantity > 1 then
|
||||
table.insert(CriteriaCache, j .. ":" .. quantity)
|
||||
if (reqQuantity or 0) > 1 then -- CoA: GetAchievementCriteriaInfo can return nil quantities for custom/partial achievements
|
||||
table.insert(CriteriaCache, j .. ":" .. (quantity or 0))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -86,9 +86,14 @@ local function OnPlayerMoney()
|
||||
addon.ThisCharacter.money = GetMoney();
|
||||
end
|
||||
|
||||
local hasScannedThisSession
|
||||
local function OnPlayerAlive()
|
||||
-- print("DataStore_Characters.lua") -- DEBUG 2025 07 21
|
||||
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard
|
||||
-- CoA: scan once at login. PLAYER_ALIVE also fires on resurrect / Feign-Death cancel
|
||||
-- (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
|
||||
|
||||
@@ -263,7 +268,7 @@ local function _GetGuildInfo(character)
|
||||
end
|
||||
|
||||
local function _GetPlayTime(character)
|
||||
return character.played
|
||||
return character.played or 0 -- CoA: nil on partial-data alt; callers do arithmetic (AccountSummary)
|
||||
end
|
||||
|
||||
local function _GetLocation(character)
|
||||
|
||||
@@ -678,25 +678,28 @@ local BagTypeStrings = {
|
||||
|
||||
local function _GetContainerInfo(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]
|
||||
end
|
||||
|
||||
local function _GetContainerSize(character, containerID)
|
||||
-- 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
|
||||
|
||||
local function _GetSlotInfo(bag, slotID)
|
||||
assert(type(bag) == "table") -- this is the pointer to a bag table, obtained through addon:GetContainer()
|
||||
assert(type(slotID) == "number")
|
||||
-- CoA: partial-data alts can have an unscanned/nil bag pointer (GetContainer returns nil
|
||||
-- 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 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
|
||||
|
||||
local function _GetContainerCooldownInfo(bag, slotID)
|
||||
assert(type(bag) == "table") -- this is the pointer to a bag table, obtained through addon:GetContainer()
|
||||
assert(type(slotID) == "number")
|
||||
-- CoA: partial-data alts can have an unscanned/nil bag pointer; degrade to nil gracefully.
|
||||
if type(bag) ~= "table" or type(slotID) ~= "number" or type(bag.cooldowns) ~= "table" then return end
|
||||
|
||||
local cd = bag.cooldowns[slotID]
|
||||
if cd then
|
||||
@@ -868,7 +871,7 @@ end
|
||||
local function _GetGuildBankTabItemCount(guild, tabID, searchedID)
|
||||
local count = 0
|
||||
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
|
||||
if (id == searchedID) then
|
||||
count = count + (container.counts[slotID] or 1)
|
||||
|
||||
@@ -625,6 +625,9 @@ local function _GetProfessionInfo(profession)
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@@ -709,7 +712,7 @@ local function _GetNumRecipesByColor(profession)
|
||||
for i = 1, _GetNumCraftLines(profession) do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
@@ -274,7 +274,9 @@ local function _GetQuestLogRewardInfo(character, index, rewardIndex)
|
||||
end
|
||||
|
||||
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 questName = link:match("%[(.+)%]")
|
||||
|
||||
@@ -234,41 +234,47 @@ local function _GetReferenceTable()
|
||||
end
|
||||
|
||||
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]
|
||||
end
|
||||
|
||||
local function _GetTreeReference(class, tree)
|
||||
assert(type(class) == "string")
|
||||
assert(type(tree) == "string")
|
||||
return addon.ref.global[class].Trees[tree]
|
||||
-- CoA: custom classes (MONK, etc.) may have no/partial talent reference data, so a
|
||||
-- tree lookup can arrive with a nil tree name. Degrade to nil instead of asserting.
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
local function _ImportClassReference(class, data)
|
||||
assert(type(class) == "string")
|
||||
assert(type(data) == "table")
|
||||
-- CoA: data arrives over Comm/AccountSharing; a peer with no reference for a custom
|
||||
-- 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
|
||||
end
|
||||
|
||||
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 order = ref.Order
|
||||
local order = ref and ref.Order
|
||||
if order then
|
||||
return order:gmatch("([^,]+)")
|
||||
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
|
||||
|
||||
local function _GetTreeInfo(class, tree)
|
||||
@@ -281,8 +287,7 @@ end
|
||||
|
||||
local function _GetTreeNameByID(class, id)
|
||||
-- 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
|
||||
for name in _GetClassTrees(class) do
|
||||
if index == id then
|
||||
|
||||
Reference in New Issue
Block a user