Module:Sandbox/Spazou

From Idle Wizard Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Sandbox/Spazou/doc

--<nowiki>
local p = {}
 
local upgrades = require("Module:Data/Upgrades")
 
local classesToString = function (t)
    if next(t) == nil then return "All" end
 
    local tt = {}
    local tf = {}
    for _, v in ipairs(upgrades.Classes) do
        if t[v] ~= nil then tt[#tt+1] = v
        else tf[#tf+1] = v
        end
    end
 
    if #tf == 0 then return "All" end
    if #tf < #tt then
        return "All except " .. table.concat(tf, ", ")
    else
        return table.concat(tt, ", ")
    end
end
 
local mul2List = {
        ["Building.1.Level"] = "Mana Gem",
        ["Building.2.Level"] = "Grimoire",
        ["Building.3.Level"] = "Spell Fountain",
        ["Building.4.Level"] = "Enchanted Tree",
        ["Building.5.Level"] = "Alchemy Desk",
        ["Building.6.Level"] = "Circle of Power",
        ["Building.7.Level"] = "Dimensional Rift",
        ["Building.8.Level"] = "Nexus"
    }
local mul2ToString = buildingToString
local buildingToString = function (s)
    if mul2List[s] ~= nil then return mul2List[s] end
    return "Not supported"
end
 
local costToString = function (u)
    if u.CostExpo < 6 then
        return u.Cost * math.pow(10, u.CostExpo)
    end
    return u.Cost .. "e" .. u.CostExpo
end
local getName = function (u) return u.Name end
 
local sortByCost = function (u1, u2)
    if u1.CostExpo < u2.CostExpo then return true end
    if u1.CostExpo > u2.CostExpo then return false end
    return u1.Cost < u2.Cost
end
local sortByEffectAndCost = function (u1, u2)
    if u1.Mul ~= 1 and u2.Mul == 1 then return false end
    if u1.Mul == 1 and u2.Mul ~= 1 then return true end
    return sortByCost(u1,u2)
end
local sortByAffectAndCost = function(u1, u2)
    if u1.Affect < u2.Affect then return true end
    if u1.Affect > u2.Affect then return false end
    return sortByCost(u1,u2)
end
 
local signAdd = function(a)
    if a < 0 then return "" end
    return "+"
end
local bonusDefault = function (u, fa, fm)
    fa = fa or function(a) return a end
    fm = fm or function(m) return m end
    if u.Add ~= 0 and u.Mul ~= 1 and u.Mul ~= 0 and u.Mul2 == nil then return " " .. signAdd(u.Add) .. fa(u.Add) .. " and x" .. fm(u.Mul) end
    if u.Add ~= 0 then return " " .. signAdd(u.Add) .. fa(u.Add) end
    if u.Mul ~= 1 and u.Mul2 == nil then return " x" .. fm(u.Mul) end
    if u.Mul2 ~= nil then
        if u.Effect == "PowA" then
            return string.format(" x (1 + %s x (1 + %s)<sup>%s</sup>)", u.Add, mul2ToString(u.Mul2), u.Mul)
        end
        return string.format(" x (1 + %s x %s)", mul2ToString(u.Mul2), fm(u.Mul))
    end
    return "Not supported"
end
local bonusTotalDefault = function (t, fa, fm)
    fa = fa or function(a) return a end
    fm = fm or function(m) return m end
    local a = 0
    local m = 1
    for _, u in pairs(t) do
        if u.Add ~= 0 then a = a + u.Add end
        if u.Mul ~= 1 and u.Mul ~= 0 and u.Mul2 == nil then m = m * u.Mul end
    end
    if m ~= 1 and a ~= 0 then return " " .. signAdd(a) .. fa(a) .. " and x" .. fm(m) end
    if a ~= 0 then return " " .. signAdd(a) .. fa(a) end
    if m ~= 1 then return " x" .. fm(m) end   
    return "Not supported"
end
 
local genericDescription = {
    ["Spell.BaseProgress"] = "%s average spell shards per second",
    ["Base.AllBuildingsProfit"] = "All profits %s",
    ["Hero.AbilityPower"] = "Hero power %s",
    ["Base.OfflineProduction"] = "Offline production %s",
    ["Base.IdleBonus"] = "Idle profit %s",
    ["Click.Profit"] = "Click profits %s",
    ["Click.AutoClickProfit"] = "Autoclick profits %s",
    ["VoidMana.Bonus"] = "Void Mana from traps %s",
    ["Spell.MaxCharge"] = "Spell charges %s",
    ["Pet.AbilityPower"] = "Pet power %s",
    ["Spell.EvocationEfficiency"] = "Evocation efficiency %s",
    ["Spell.IncantationEfficiency"] = "Incantation efficiency %s",
    ["Spell.SummoningEfficiency"] = "Summoning efficiency %s",
    ["Pet.BonusExp"] = "Pet experience %s",
    ["Hero.ExpMS"] = "Experience from Mana Sources %s",
    ["Spell.AccumutaledCasts"] = "Accumulated spells starting cast %s",
    ["Hero.ExpBoost"] = "Experience from xp actions %s",
    ["Bats.SpawnRate"] = "Bats spawn rate %s",
    ["Chrono.MaxDistortion"] = "Maximum Time Distortion %s",
    ["Shadow.PassiveIncome"] = "Passive Liquid Shadow income %s per sec",
 
    ["Unknown"] = "Not supported"
}
local genericDescriptionPercent = {
    ["Spell.NonShardCostReduction"] = "Non-shard spell cost %s",
    ["Spell.CostReduction"] = "Spell cost %s",
    ["Base.SoulPower"] = "Mystery power %s"
}
 
local categoryOrder = {
    "Clicking",
    "Hero",
    "Idle",
    "Mana Production",
    "Mana Sources",
    "Offline",
    "Pets",
    "Spells",
    "Void Mana",
    "Upgrades based on time played this Exile",
    "Upgrades based on Achievements",
    "Remaining Class Unique Upgrades",
    "Error - Uncategorized"
}
 
local HeaderCost = { Header = "Cost", Value = costToString }
local HeaderName = { Header = "Name", Value = getName }
local HeaderClass = { Header = "Class", Value = function(u) return classesToString(u.Classes) end }
 
local categories = {
    {
        Name = "Upgrades based on time played this Exile", 
        SubCategory = {
            {
                Name = "Upgrades based on time played this Exile", DisplayOrder = 1,
                Upgrades = {},
                UpgradeCheck = 
function (u)
    if u.CAccess == "More" and u.CAParam == "Base.PlayedTime" then return true end
    return false
end
                ,Bonus = bonusDefault,
 
                DisplayColumns = {
                    { Header = "Days played req.", Value = function(u) return math.floor(tonumber(u.CAArg) / 360 / 24 + 0.5) / 10 end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value = 
function(u, sc)
    if genericDescription[u.Affect] ~= nil then return string.format(genericDescription[u.Affect], sc.Bonus(u)) end
    return genericDescription["Unknown"]
end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Upgrades based on Achievements", 
        SubCategory = {
            {
                Name = "Upgrades based on Achievements", DisplayOrder = 1,
                Upgrades = {}, UpgradeCheck =
function(u)
    if u.CAccess == "Achieve" then return true end return false
end
                ,Bonus = bonusDefault,
                DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value = 
function(u, sc)
    if genericDescription[u.Affect] ~= nil then return string.format(genericDescription[u.Affect], sc.Bonus(u)) end
    if genericDescriptionPercent[u.Affect] ~= nil then return string.format(genericDescriptionPercent[u.Affect], sc.Bonus(u, function(a) return a*100 .. "%" end)) end
    return genericDescription["Unknown"]
end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Clicking", 
        SubCategory = {
            { SubText = "===All clicks===", DisplayOrder = 1 },
            { SubText = "===Critical clicks===", DisplayOrder = 5 },
            { SubText = "===Autoclicks===", DisplayOrder = 9 },
            {
                Name = "Click profit bonus per click", DisplayOrder = 3
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.Profit" and u.Mul2 == "Click.Total" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "All time mana req.", Value = 
function(u)
    if u.CAccess == "More" and u.CAParam == "Base.ManaAllTime" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Click profit bonus per click", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                }
            },{
                Name = "Click profit bonus", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.Profit" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "All time clicks req.", Value =
function(u, sc)
    if u.CAccess == "" then return "" end
    if u.CAccess == "More" and u.CAParam == "Click.TotalAllTime" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Click profit bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Click profit bonus per mana production", DisplayOrder = 4
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.PercentPPS" then return true end return false
end
                ,Bonus = function(u) return bonusDefault(u, function(a) return a*100 .. "% of mana/sec" end) end
                ,BonusTotal = function(u) return bonusTotalDefault(u, function(a) return a*100 .. "% of mana/sec" end) end
                ,DisplayColumns = {
                    { Header = "All time clicks req.", Value =
function(u)
    if u.CAccess == "" then return "" end
    if u.CAccess == "More" and u.CAParam == "Click.TotalAllTime" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Click profit bonus per mana production", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Critical click chance", DisplayOrder = 6
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.CritChance" then return true end return false
end
                ,Bonus = function(u) return bonusDefault(u, function(a) return a .. "%" end) end
                ,BonusTotal = function(u) return bonusTotalDefault(u, function(a) return a .. "%" end) end
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Critical click chance", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Critical click profit", DisplayOrder = 7
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.CritProfit" then return true end return false
end
                ,Bonus = function(u) return bonusDefault(u, function(a) return a*100 .. "%" end) end
                ,BonusTotal = function(u) return bonusTotalDefault(u, function(a) return a*100 .. "%" end) end
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Critical click profit multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Critical rating", DisplayOrder = 8
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.CritRating" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Critical rating modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Autoclick profit modifier", DisplayOrder = 10
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Click.AutoClickProfit" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "Autoclicks this exile req.", Value = 
function(u)
    if u.CAccess == "" then return "" end
    if u.CAParam == "Click.AutoTotal" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Autoclicks modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Hero", 
        SubCategory = {
            {
                Name = "Hero ability modifier", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Hero.AbilityPower" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "All-time max hero level req.", Value =
function(u)
    if u.CAccess == "" then return "" end
    if u.CAParam == "Hero.MaxLevelAllTime" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Ability modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Idle", 
        SubCategory = {
            {
                Name = "Idle bonus modifier", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Base.IdleBonus" then return true end return false
end
                ,Bonus = function(u) return bonusDefault(u, function(a) return a*100 .. "%" end) end
                ,BonusTotal = function(u) return bonusTotalDefault(u, function(a) return a*100 .. "%" end) end
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Idle bonus modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Mana Production", 
        SubCategory = {
            {
                Name = "Global profit from Achievement points", DisplayOrder = 4
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Base.AllBuildingsProfit" and u.Mul2 == "Achiev.Points" then return true end return false
end
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Global profit bonus", Value = function(u) return " x (1 + " .. u.Mul .. " x points)" end }
                    ,HeaderClass
                }
            },{
                Name = "Global profit from Nexi", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Base.AllBuildingsProfit" and u.CAParam == "Building.8.Level" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "Nexi req.", Value = function (u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Global profit bonus", Total = true, Value = function (u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Global profit bonus from Mana sources", DisplayOrder = 3
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Base.AllBuildingsProfit" and u.Mul2 ~= nil and string.find(u.Mul2, "Building") == 1 then
        return true
    end
    return false
end
                ,UpgradeSort =
function(u1,u2)
    local n1 = next(u1.Classes)
    local n2 = next(u2.Classes)
    if n1 == nil and n2 == nil then return sortByCost(u1,u2) end
    if n1 == nil and n2 ~= nil then return true end
    if n1 ~= nil and n2 == nil then return false end
    if n1 == n2 then return sortByCost(u1,u2) end
    return n1 < n2
end
                ,DisplayColumns = {
                    { Header = "Require", Value = 
function (u, sc)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Click.TotalAllTime" then return u.CAArg .. " all-time clicks" end
    if string.find(u.CAParam, "Building") == 1 then return u.CAArg .. " " .. buildingToString(u.CAParam) end
    return "Not supported"
end }
                    ,{ Header = "Mana source", Value = function(u) return buildingToString(u.Mul2) end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Global profit bonus", Value =
function(u)
    if u.Effect == "PowA" then
        return string.format(" x (1 + %s x (1 + amount)<sup>%s</sup> )", u.Add, u.Mul)
    end
    return string.format(" x (1 + %s x amount)", u.Mul)
end }
                    ,HeaderClass
                }
            },{
                Name = "Global profit bonus", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Base.AllBuildingsProfit" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "Mysteries req.", Value =
function(u)
    if u.CAArg == "" then return "" end
    if u.CAParam == "Base.Souls" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Global profit bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Mana Sources", 
        SubCategory = {
            {
                Name = "Mana Gem production", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.1.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Mana Gems req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.1.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Grimoire production", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.2.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Grimoires req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.2.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Spell Fountain production", DisplayOrder = 3
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.3.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Spell Fountains req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.3.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Enchanted Tree production", DisplayOrder = 4
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.4.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Enchanted Trees req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.4.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Alchemy Desk production", DisplayOrder = 5
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.5.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Alchemy Desks req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.5.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Circle of Power production", DisplayOrder = 6
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.6.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Circles of Power req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.6.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Dimensional Rift production", DisplayOrder = 7
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.7.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Dimensional Rifts req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.7.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Nexus production", DisplayOrder = 8
                ,Upgrades = {}, UpgradeCheck = function(u) if u.Affect == "Building.8.Profit" then return true end return false end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "Nexi req.", Value = 
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Building.8.Level" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Production bonus", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Offline", 
        SubCategory = {
            {
                Name = "Offline production modifier", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Base.OfflineProduction" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort =
function(u1,u2)
    local n1 = next(u1.Classes)
    local n2 = next(u2.Classes)
    if n1 == nil and n2 == nil then return sortByCost(u1,u2) end
    if n1 == nil and n2 ~= nil then return true end
    if n1 ~= nil and n2 == nil then return false end
    if n1 == n2 then return sortByCost(u1,u2) end
    return n1 < n2
end
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Offline production modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Pets", 
        SubCategory = {
            {
                Name = "Pet ability modifier", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Pet.AbilityPower" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "All-time max pet level req.", Value =
function(u)
    if u.CAParam == "" then return "" end
    if u.CAParam == "Pet.MaxLevelAllTime" then return u.CAArg end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Ability modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Pet experience modifier", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Pet.BonusExp" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Experience modifier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Spells", 
        SubCategory = {
            { SubText = "===Spellcasting===", DisplayOrder = 1 },
            { SubText = "===Spell Shards Gain===", DisplayOrder = 5 },
            { SubText = "===Spell Efficiency===", DisplayOrder = 9 },
            {
                Name = "Spell Scrolls", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.ScrollCount" then return true end return false
end
                ,DisplayColumns = {
                    { Header = "Hero level req.", Value = function(u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Effect", Value = function(u) return " +1 Spell Scroll" end }
                    ,HeaderClass
                }
            },{
                Name = "Spell Autocast", DisplayOrder = 3
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.AutoCastCount" then return true end return false
end
                ,DisplayColumns = {
                    { Header = "All-time spell cast req.", Value = function(u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Effect", Value = function(u) return "Can autocast +1 spell" end }
                    ,HeaderClass
                }
            },{
                Name = "Spell Charges", DisplayOrder = 4
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.MaxCharge" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    { Header = "All-time spell cast req.", Value = function(u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Spell charges added", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Shard Pool accumulation time", DisplayOrder = 6
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.MaxPoolTime" then return true end return false
end
                ,Bonus = function(u) return bonusDefault(u, function(a) return a .. "s" end) end
                ,BonusTotal = function(u) return bonusTotalDefault(u, function(a) return a .. "s" end) end
                ,DisplayColumns = {
                    { Header = "Clicks this exile req.", Value = function(u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Accumulation time", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                }
            },{
                Name = "Spell Shards from clicking", DisplayOrder = 7
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.ClickProgress" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "All-time shards collected req.", Value = function(u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Shards per click", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Spell Shards per second", DisplayOrder = 8
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.BaseProgress" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    { Header = "All-time shards collected req.", Value = function(u) return u.CAArg end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Shards per second", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Evocation Efficiency", DisplayOrder = 10
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "Spell.EvocationEfficiency" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Efficiency Multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Incantation Efficiency", DisplayOrder = 11
                ,Upgrades = {}, UpgradeCheck = 
function(u)
    if u.Affect == "Spell.IncantationEfficiency" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Efficiency Multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Summoning Efficiency", DisplayOrder = 12
                ,Upgrades = {}, UpgradeCheck = 
function(u)
    if u.Affect == "Spell.SummoningEfficiency" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Efficiency Multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Summoning Duration", DisplayOrder = 13
                ,Upgrades = {}, UpgradeCheck = 
function(u)
    if u.Affect == "Spell.SummoningDuration" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Duration Multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Void Mana",
        SubCategory = {
            {
                Name = "Void mana received per entity", DisplayOrder = 1
                ,Upgrades = {}, UpgradeCheck = 
function (u)
    if u.Affect == "VoidMana.Bonus" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Void Mana received", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Profit per Void mana", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck = 
function (u)
    if u.Affect == "VoidMana.Power" then return true end return false
end
                ,Bonus = function(u) return bonusDefault(u, function(a) return a*100 .. "%" end) end
                ,BonusTotal = function(u) return bonusTotalDefault(u, function(a) return a*100 .. "%" end) end
                ,UpgradeSort = sortByEffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Profit bonus per Void Mana", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Void Entity lifetime", DisplayOrder = 3
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "VoidMana.LifeTime" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Lifetime multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            },{
                Name = "Void Entity spawn speed", DisplayOrder = 4
                ,Upgrades = {}, UpgradeCheck =
function(u)
    if u.Affect == "VoidMana.SpawnSpeed" then return true end return false
end
                ,Bonus = bonusDefault
                ,BonusTotal = bonusTotalDefault
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Spawn speed multiplier", Total = true, Value = function(u, sc) return sc.Bonus(u) end }
                    ,HeaderClass
                }
            }
        }
    },{
        Name = "Remaining Class Unique Upgrades",
        SubCategory = {
            {
                SubText = "===[[Apprentice]]===", DisplayOrder = 1, AttachedTo = "Apprentice unique upgrades"
            },{
                Name = "Apprentice unique upgrades", DisplayOrder = 2
                ,Upgrades = {}, UpgradeCheck = 
function(u)
    local i, _ = next(u.Classes)
    if i == "Apprentice" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                }
            },{
                SubText = "===[[Druid]]===", DisplayOrder = 3, AttachedTo = "Druid unique upgrades"
            },{
                SubText = "The Druid's upgrade focus on Autoclicks and Clicks in general.",
                DisplayOrder = 4, AttachedTo = "Druid unique upgrades"
            },{
                Name = "Druid unique upgrades", DisplayOrder = 5
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Druid" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                }
            },{
                SubText = "===[[Demonologist]]===", DisplayOrder = 6, AttachedTo = "Demonologist unique upgrades"
            },{
                SubText = "The Demonologist focuses completely on boosting his [[Pets]].",
                DisplayOrder = 7, AttachedTo = "Demonologist unique upgrades"
            },{
                Name = "Demonologist unique upgrades", DisplayOrder = 8
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Demonologist" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                }
            },{
                SubText = "===[[Necromancer]]===", DisplayOrder = 9, AttachedTo = "Necromancer unique upgrades"
            },{
                SubText = "The Necromancer is a master summoner, and a specialist of the idle gameplay.",
                DisplayOrder = 10, AttachedTo = "Necromancer unique upgrades"
            },{
                Name = "Necromancer unique upgrades", DisplayOrder = 10
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Necromancer" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                }
            },{
                SubText = "===[[Arcanist]]===", DisplayOrder = 11, AttachedTo = "Arcanist unique upgrades"
            },{
                SubText = "The Arcanist can generate spell shards better than anyone.",
                DisplayOrder = 12, AttachedTo = "Arcanist unique upgrades"
            },{
                Name = "Arcanist unique upgrades", DisplayOrder = 13
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Arcanist" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                }
            },{
                SubText = "===[[Prodigy]]===", DisplayOrder = 14, AttachedTo = "Prodigy unique upgrades"
            },{
                SubText = "The Prodigy has access to upgrades making all [[Sources|Mana Sources]] cheaper",
                DisplayOrder = 15, AttachedTo = "Prodigy unique upgrades"
            },{
                Name = "Prodigy unique upgrades", DisplayOrder = 16
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Prodigy" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    { Header = "Req.", Value =
function(u)
    if u.CAArg == "" then return "" end
    if string.find(u.CAParam, "Building") == 1 then return u.CAArg .. " " .. buildingToString(u.CAParam) end
    return "Not supported"
end }
                    ,HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value =
function(u, sc)
    local desc = sc.UniqueDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
 
    desc = genericDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
    return "Not supported"
end }
                }
                ,UniqueDescription = {
                    ["Building.1.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Mana Gems]] by %s",
                    ["Building.2.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Grimoires]] by %s",
                    ["Building.3.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Spell Fountains]] by %s",
                    ["Building.4.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Enchanted Trees]] by %s",
                    ["Building.5.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Alchemy Desks]] by %s",
                    ["Building.6.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Circles of Power]] by %s",
                    ["Building.7.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Dimensional Rifts]] by %s",
                    ["Building.8.CostGrowth"] = "Reduces the cost increase multiplier of [[Sources|Nexi]] by %s"
                }
            },{
                SubText = "===[[Voidmancer]]===", DisplayOrder = 17, AttachedTo = "Voidmancer unique upgrades"
            },{
                SubText = "The Voidmancer has access to a wide range of Void-related upgrades.",
                DisplayOrder = 18, AttachedTo = "Voidmancer unique upgrades"
            },{
                Name = "Voidmancer unique upgrades", DisplayOrder = 19
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Voidmancer" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value = 
function(u, sc)
    local desc = sc.UniqueDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
 
    desc = genericDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
    return "Not supported"
end }
                }
                ,UniqueDescription = {
                    ["VoidMana.Decrease"] = "Void Mana degeneration %s"
                }
            },{
                SubText = "===[[Exorcist]]===", DisplayOrder = 20, AttachedTo = "Exorcist unique upgrades"
            },{
                SubText = "The Exorcist has access to upgrades related to his unique Hallowed Click mechanic.",
                DisplayOrder = 21, AttachedTo = "Exorcist unique upgrades"
            },{
                Name = "Exorcist unique upgrades", DisplayOrder = 22
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Exorcist" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value =
function(u, sc)
    local desc = sc.UniqueDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
 
    desc = genericDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
    return "Not supported"
end }
                }
                ,UniqueDescription = {
                    ["Exorcist.MaxCharges"] = "Increase maximum amount of Hallowed Clicks by %s",
                    ["Exorcist.ChargeTime"] = "Reduce Hallowed Click generation time by %s",
                    ["Exorcist.PerClick"] = "Clicking Orb expends %s additional HC"
                }
            },{
                SubText = "===[[Chronomancer]]===", DisplayOrder = 23, AttachedTo = "Chronomancer unique upgrades"
            },{
                SubText = "The Chronomancer has access to upgrades related to his unique compressed time mechanic.",
                DisplayOrder = 24, AttachedTo = "Chronomancer unique upgrades"
            },{
                Name = "Chronomancer unique upgrades", DisplayOrder = 25
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Chronomancer" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value =
function(u, sc)
    local desc = sc.UniqueDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
 
    desc = genericDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
    return "Not supported"
end }
                }
                ,UniqueDescription = {
                    ["Chrono.Charge"] = "Increase compressed time per tick %s"
                }
            },{
                SubText = "===[[Umbramancer]]===", DisplayOrder = 26, AttachedTo = "Umbramancer unique upgrades"
            },{
                SubText = "The Umbramancer has access to upgrades related to his unique Liquid Shadow mechanic.",
                DisplayOrder = 27, AttachedTo = "Umbramancer unique upgrades"
            },{
                Name = "Umbramancer unique upgrades", DisplayOrder = 28
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Umbramancer" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value =
function(u, sc)
    local desc = sc.UniqueDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
 
    desc = sc.UniqueDescriptionPercent[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u, function(a) return a*100 .. "%" end)) end
 
    desc = genericDescription[u.Affect]
    if desc ~= nil then return string.format(desc, bonusDefault(u)) end
    return "Not supported"
end }
                }
                ,UniqueDescription = {
                    ["Shadow.PassiveIncome"] = "Increase passive income of Liquid Shadow by %s per second",
                    ["Shadow.LifeTime"] = "Increase life time of Shadow Clots by %s",
                    ["Shadow.SpawnSpeed"] = "Increase Shadow Clots spawn rate by %s",
                    ["Shadow.Period"] = "Decreases delay between charging spells with Liquid Shadows by %s second",
                }
                ,UniqueDescriptionPercent = {
                    ["Shadow.Charging"] = "Increase amount of Liquid Shadow converted to spell charges per tick by %s"
                }
            },{
                SubText = "===[[Alchemist]]===", DisplayOrder = 29, AttachedTo = "Alchemist unique upgrades"
            },{
                SubText = "",
                DisplayOrder = 30, AttachedTo = "Alchemist unique upgrades"
            },{
                Name = "Alchemist unique upgrades", DisplayOrder = 31
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Alchemist" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value =
function(u, sc)
    return "Not supported"
end }
                }
            },{
                SubText = "===[[Ironsoul]]===", DisplayOrder = 32, AttachedTo = "Ironsoul unique upgrades"
            },{
                SubText = "",
                DisplayOrder = 33, AttachedTo = "Ironsoul unique upgrades"
            },{
                Name = "Ironsoul unique upgrades", DisplayOrder = 34
                ,Upgrades = {}, UpgradeCheck =
function(u)
    local i, _ = next(u.Classes)
    if i == "Ironsoul" and next(u.Classes, i) == nil then return true end return false
end
                ,UpgradeSort = sortByAffectAndCost
                ,DisplayColumns = {
                    HeaderCost
                    ,HeaderName
                    ,{ Header = "Description", Value =
function(u, sc)
    return "Not supported"
end }
                }
            }
        }
    },{
        Name = "Error - Uncategorized",
        SubCategory = {
            {
                Name = "Error list",
                Upgrades = {}, UpgradeCheck =
function (u)
    return true
end
            }
        }
    }
}
 
for _, cat in pairs(categories) do
    cat.SubUpgradeCount = 0
    for _, scat in pairs(cat.SubCategory) do
        if scat.DisplayColumns ~= nil then
            if scat.UpgradeSort == nil then scat.UpgradeSort = sortByCost end
            for _, dc in pairs(scat.DisplayColumns) do
                if dc.Total ~= nil then scat.Total = true break end
            end
        end
    end
    cat.DisplayOrder = #categoryOrder + 1
    for i, disp in pairs(categoryOrder) do
        if disp == cat.Name then
            cat.DisplayOrder = i
            break
        end
    end
end
 
function p.List(frame)
    local class = frame.args[1] or mw.title.getCurrentTitle().text
 
    if string.find(class, ":") ~= nil then class = string.sub(class, string.find(class, ":") + 1) end
    FilterByClass(class)
    FillCategories()
    RemoveEmptyCategories()
 
    local sortDisplay = function(c1,c2) return c1.DisplayOrder < c2.DisplayOrder end
    table.sort(categories, sortDisplay)
    for _, cat in pairs(categories) do
        table.sort(cat.SubCategory, sortDisplay)
        for _, scat in pairs(cat.SubCategory) do
            if scat.UpgradeSort ~= nil then table.sort(scat.Upgrades, scat.UpgradeSort) end
        end
    end
 
    return table.concat(ProcessCategories(), "\n")
    --return frame:preprocess(table.concat(ProcessCategories(), "\n"))
end
 
function FillCategories()
    for i, upgrade in pairs(upgrades.Upgrades) do
        local categorized = false
        for _, cat in pairs(categories) do
            for _,scat in pairs(cat.SubCategory) do
                if scat.UpgradeCheck ~= nil and scat.UpgradeCheck(upgrade) then
                    table.insert(scat.Upgrades, upgrade)
                    categorized = true
                    break
                end
            end
            if categorized then
                cat.SubUpgradeCount = cat.SubUpgradeCount + 1
                break
            end
        end
    end
end
 
function FilterByClass(class)
    local classfound = false
    for i, v in pairs(upgrades.Classes) do
        if v == class then classfound = true break end
    end
    if classfound then
        for i, u in pairs(upgrades.Upgrades) do
            if next(u.Classes) ~= nil then
                local ok = false
                for c, _ in pairs(u.Classes) do
                    if c == class then ok = true break end
                end
                if not ok then upgrades.Upgrades[i] = nil end
            end
        end
        RemoveHeaderClass()
    end
end
 
function RemoveEmptyCategories()
    for _, cat in pairs(categories) do
        local todel = {}
        for i, scat in pairs(cat.SubCategory) do
            if scat.Upgrades ~= nil and #scat.Upgrades == 0 then
                todel[#todel+1] = scat
                cat.SubCategory[i] = nil
            end
        end
        for i, scat in pairs(cat.SubCategory) do
            if scat.Upgrades == nil then
                for _, td in pairs(todel) do
                    if scat.AttachedTo == td.Name then cat.SubCategory[i] = nil end
                end
            end
        end
 
        local recreate = {}
        for i, scat in pairs(cat.SubCategory) do
            recreate[#recreate+1] = scat
        end
        cat.SubCategory = recreate
    end
    for i, cat in pairs(categories) do
        if next(cat.SubCategory) == nil then categories[i] = nil end
    end
 
    local recreate = {}
    for _, cat in pairs(categories) do
        recreate[#recreate+1] = cat
    end
    categories = recreate
end
 
function RemoveHeaderClass()
    for _, cat in pairs(categories) do
        for _, subcat in pairs(cat.SubCategory) do
            if subcat.DisplayColumns ~= nil then
                for i, v in pairs(subcat.DisplayColumns) do
                    if v == HeaderClass then subcat.DisplayColumns[i] = nil end
                end
            end
        end
    end
end
 
function ProcessCategories()
    local r = {}
 
    for _, cat in pairs(categories) do
        r[#r+1] = "== " .. cat.Name .. " =="
        for _, scat in pairs(cat.SubCategory) do
            if scat.SubText ~= nil then
                r[#r+1] = scat.SubText
            else
                r[#r+1] = string.format([=[{| class="mw-collapsible mw-collapsed wikitable" style="width:100%%;" id="%s" data-expandtext="Reveal" data-collapsetext="Conceal" border="0" cellpadding="10"]=], scat.Name)
                if scat.Total ~= nil and scat.Total == true then
                    r[#r+1] = string.format([=[! colspan="%s" |%s (%s upgrades, total : %s)]=], #scat.DisplayColumns, scat.Name, #scat.Upgrades, scat.BonusTotal(scat.Upgrades))
                else
                    r[#r+1] = string.format([=[! colspan="%s" |%s (%s upgrades)]=], #scat.DisplayColumns, scat.Name, #scat.Upgrades)
                end
                r[#r+1] = "|-"
                for _, dc in pairs(scat.DisplayColumns) do
                    r[#r+1] = [=[! scope="col"|]=] .. dc.Header
                end
                for _, upg in pairs(scat.Upgrades) do
                    r[#r+1] = "|-"
                    for _, dc in pairs(scat.DisplayColumns) do
                        r[#r+1] = "|" .. dc.Value(upg, scat)
                    end
                end
                r[#r+1] = "|}"
            end
        end
        r[#r+1] = ""
    end
 
    return r
end
 
return p
--</nowiki>