Module:Hero

local module = {} local Data = {} local Stat = setmetatable({}, {   __call = function(self, ...)        return setmetatable(self, { __call = function(self, data_stat, stat, value) return {name = data_stat[stat].name, stat = stat, value = value} end, })(...)   end, })

local MaxStat = setmetatable({}, {   __call = function(self, ...)        local calculate = function(self, hero, power)            local hero_type = hero.type            local data_stat = Data.stat[hero_type]            local data_stat_stat = data_stat.stat            local data_stat_main = data_stat.main            local hero_main_stat = (hero.main_stat or {}).stat            local level = hero.level or data_stat_stat[0].value

self[0] = level

function add(t, key, value) self[key] = self[key] + value t[key] = t[key] + value end

local meta = { __index = function(self, key) return 0 end, }           setmetatable(self, meta) -- unset values return 0

local stat_base = setmetatable({}, meta) local stat_level = setmetatable({}, meta) local stat_skins = setmetatable({}, meta) local stat_items = setmetatable({}, {               __index = function(self, key)                    local data = setmetatable({}, meta)

rawset(self, key, data)

return data end, })           local stat_glyphs = setmetatable({}, meta)            local stat_gote = setmetatable({}, meta)            local stat_artifacts = setmetatable({}, meta)            local stat_ascension_role = setmetatable({}, meta)            local stat_ascension_hero = setmetatable({}, meta)            -- statGroups            local statGroup = {                base = stat_base,                level = stat_level,                skins = stat_skins,                glyphs = stat_glyphs,                gote = stat_gote,                artifacts = stat_artifacts,                ascension_role = stat_ascension_role,                ascension_hero = stat_ascension_hero,            }            hero.statGroup = statGroup            -- copy base values            for k, v in pairs(hero.base or {}) do                add(stat_base, k, v)            end            -- add attributes from level if power then level = level ^ power end

for k, v in pairs(hero.star or {}) do               add(stat_level, k, level * v)            end -- add all skins for k, v in ipairs(hero.skin or {}) do               v = v.stat

add(stat_skins, v.stat, v.value) end -- add all items if hero.item then local items = setmetatable({}, meta) local rank_bonus = hero.rank_bonus or 2 local rank = hero.rank

for r, v in ipairs(hero.item) do                   local items_rank = stat_items[r] -- add bonus if v.bonus then for k, v in pairs(v.bonus) do                           add(items_rank, k, v)                        end elseif r > 1 then -- fallback for k in pairs(data_stat_main) do                           add(items_rank, k, rank_bonus) end end

statGroup['items' .. r] = items_rank

if rank == r then break end

for k, v in ipairs(v) do                       for k, v in pairs(v.stat) do                            local k = v.stat

add(items_rank, k, v.value) end end -- combine ranks for k, v in pairs(items_rank) do                       items[k] = items[k] + v                    end end

statGroup.items = items end -- add all glyphs for k, v in ipairs(hero.glyph or {}) do               k = v.stat

add(stat_glyphs, k, v.value) end -- add gift of the elements if hero_main_stat then local data_gift = Data.gift[hero_type]

if data_gift then for k in pairs(data_stat_main) do                       add(stat_gote, k, data_gift) end -- main stat gets twice the bonus add(stat_gote, hero_main_stat, data_gift) end end -- add artifacts for k, v in pairs(hero.artifact or {}) do               add(stat_artifacts, k, v)            end -- add ascension if hero.ascension then for k, v in pairs(hero.ascension.role) do                   add(stat_ascension_role, k, v.value) end

for k, v in ipairs(hero.ascension.hero) do                   for k, v in ipairs(v) do                        local stat = v.stat

if stat then add(stat_ascension_hero, stat.stat, stat.value) end end end end -- resolve effect of main attributes for k, v in pairs(data_stat_stat) do               for m, v in pairs(v.calculation or {}) do                    self[k] = self[k] + self[m] * v                end end -- add physical damage from main stat if hero_main_stat then self[5] = self[5] + self[hero_main_stat] end -- remove metatable return setmetatable(self, nil) end

return setmetatable(self, {           __call = function(self, hero, power)                return setmetatable({}, { __ipairs = function(self) return ipairs(calculate(self, hero, power)) end, __pairs = function(self) return pairs(calculate(self, hero, power)) end, __index = function(self, key) return calculate(self, hero, power)[key] end, })           end,        })(...) end, })

local Power = setmetatable({}, {   __call = function(self, ...)        local calculate = function(self, hero)            setmetatable(self, nil) -- to prevent an infinite loop

local power_total = 0 local level = hero.stat[0] local hero_type = hero.type local data_power = Data.power[hero_type] local data_power_skill = data_power.skill

local function calculatePower(stat) local power

for k, v in pairs(stat) do                   power = (power or 0) + v * (data_power[k] or 0) end

if power then power_total = power_total + power

return power end end

for k, v in pairs(hero.statGroup or {stat = hero.stat}) do               self[k] = calculatePower(v) end -- remove items from total power because itemX are also present if self.items then power_total = power_total - self.items end -- add power from skills if data_power_skill then self.skills = level * data_power_skill[1] * data_power_skill[2] power_total = power_total + self.skills end -- add power from artifact weapon local weapon = hero.weapon

if weapon then local power = weapon.value * data_power[weapon.stat] * weapon.mul

self.artifacts = self.artifacts + power power_total = power_total + power end -- add power for ascension skills if hero.ascension then local power = data_power_skill[2]

for k, v in ipairs(hero.ascension.hero) do                   for k, v in ipairs(v) do                        if v.skill and tonumber(v.skill) > 0 then self.ascension_hero = self.ascension_hero + power power_total = power_total + power end end end end

self.total = power_total

return self end

return setmetatable(self, {           __call = function(self, hero)                return setmetatable({}, { __ipairs = function(self) return ipairs(calculate(self, hero)) end, __pairs = function(self) return pairs(calculate(self, hero)) end, __index = function(self, key) return calculate(self, hero)[key] end, })           end,        })(...) end, })

local Skill = setmetatable({}, {   __call = function(self, ...)        local lang = mw.getContentLanguage        local function formatNum(value)            local round = math.floor(value)            -- convert to integer if float ends with .0            if round == value then                value = round            end

return lang:formatNum(value) end

local function formatProduct(data_stat, mul) data_stat = data_stat or {}

local output = {}

if data_stat.percentage then output = {formatNum(mul * 100) .. '%', concat = ' ', data_stat.name} else output = {formatNum(mul), concat = ' * ', data_stat.name} end

return table.concat(output, output.concat) end

local frame = mw.getCurrentFrame local color_params = {title = 'Skill/Color'} local function applyColor(...) -- damageType, number color_params.args = {...}

return frame:expandTemplate(color_params) end

local function formatSingleValue(self) return applyColor(nil, self.value) end

local function formatTime(self) return applyColor(nil, self.value) .. ((tonumber(self.value) == 1) and ' second' or ' seconds') end

local function formatPrime(self) return string.format('%s (%s)', applyColor(self.damage_type, formatNum(self.value)), self.formula) end

local function getPrime(args, key, data_stat, max_stat, skillLevel) local attribute = args[key .. '_attribute']

if attribute then local sum = 0 local formula = {} local stat = data_stat.lookup[attribute] local scale = tonumber(args[key .. '_scale']) local level = tonumber(args[key .. '_level']) local base = tonumber(args[key .. '_base']) local round = tonumber(args[key .. '_round']) local data_stat = data_stat.stat

if scale then round = round or 0 sum = sum + max_stat[stat] * scale

table.insert(formula, {data_stat[stat], scale}) end

if level then round = round or -1 sum = sum + max_stat[0] * level

table.insert(formula, {data_stat[0], level}) end

if base or skillLevel > 0 then round = round or -1 base = base or 0 sum = sum + base -- scale base to the lowest skill Level base = base + (level or 0) * skillLevel

if base ~= 0 then table.insert(formula, {nil, base}) end end

if #formula == 0 then -- no formula error(string.format('Dummy entry in "%s"!', key)) end

if round >= 0 then round = 10^round sum = math.floor(sum * round + 0.5) / round end

local signs = {formatProduct(unpack(table.remove(formula, 1)))}

for _, v in ipairs(formula) do                   local stat, value = unpack(v)

if value < 0 then table.insert(signs, ' - ') table.insert(signs, formatProduct(stat, -value)) else table.insert(signs, ' + ') table.insert(signs, formatProduct(stat, value)) end end

return { value = sum, damage_type = args[key .. '_damage_type'], formula = table.concat(signs), format = formatPrime, }           end end

local function getTime(args, key, data_stat, max_stat, skillLevel) return {value = args[key], format = formatTime} end

local form = { prime = getPrime, secondary = getPrime, duration = getTime, cooldown = getTime, initial_cooldown = getTime, }       local function formatForumla(self, description, max_stat, args, prefix, data_stat, skillLevel, hero_name) local params = setmetatable({               name = {value = hero_name, format = function(self) return self.value end},                level = {value = max_stat[0], format = formatSingleValue},            }, {                __index = function(self, key)                    local data                    local form = form[key]                    local key = prefix .. key

if form then data = form(args, key, data_stat, max_stat, skillLevel) else data = {value = tonumber(args[key]), format = formatSingleValue} end

if data then rawset(self, key, data)

return data else rawset(self, key, false) end end, })           if not description then                local output = {}

self.form:gsub('%%([%a_]+)%%', function(key)                   local value = params[key]

if not value then error(string.format('Missing parameter "%s" in description:%s', key, description)) end

output[key] = value end)

return output end

return description:gsub('%%([%a_]+)%%', function(key)               local value = params[key]

if not value then error(string.format('Missing parameter "%s" in description:%s (prefix: %s)', key, description, prefix)) end

return value:format end)       end

local function resolve(self, key, args, hero_stat, data_stat, skillLevel, title) self.name = args[key .. 'name'] self.form = args[key .. 'format'] self.format = function(self, description, fake_stat) return formatForumla(self, description, fake_stat or hero_stat, args, key, data_stat, skillLevel or 0, title) end

return setmetatable(self, nil) end

local function resolveTable(idx, ...) local args = {'skill_' .. idx .. '_', ...}

return setmetatable({}, {               __ipairs = function(self) return ipairs(resolve(self, unpack(args))) end,                __pairs = function(self) return pairs(resolve(self, unpack(args))) end,                __index = function(self, key) return resolve(self, unpack(args))[key] end,            }) end

return setmetatable(self, {           __call = function(self, hero, colors) -- hero, value                local output = {}                local args = hero.args                local title = hero.title                local hero_stat = hero.stat                local data_stat = Data.stat[hero.type]

if colors then local data_skillLevel = Data.skillLevel[hero.type] or {}

for _, color in ipairs(colors) do                       output[color] = resolveTable(color, args, hero_stat, data_stat, data_skillLevel[color], title) end else local idx = 0 local form = 'skill_%d_name' local key = form:format(idx)

while args[key] do                       output[idx] = resolveTable(idx, args, hero_stat, data_stat, 0, title)

idx = idx + 1 key = form:format(idx) end end

return output end, })(...)   end, })

local Skin = setmetatable({}, {   __call = function(self, ...)        local function resolve(self, data_stat, data_skin)            setmetatable(self, nil)

local name = self.name local stat = data_stat.lookup[self.attribute]

self.stat = Stat(data_stat.stat, stat, self.value and tonumber(self.value) or ((data_skin[name] or data_skin)[stat]))

return self end

return setmetatable(self, {           __call = function(self, hero, skin)                local output = {}                local hero_type = hero.type                local data_skin = Data.skin[hero_type]                local data_stat = Data.stat[hero_type]                local meta = {                    __ipairs = function(self) return ipairs(resolve(self, data_stat, data_skin)) end,                    __pairs = function(self) return pairs(resolve(self, data_stat, data_skin)) end,                    __index = function(self, key) return resolve(self, data_stat, data_skin)[key] end,                }                for k, v in ipairs(skin) do                    output[k] = setmetatable(v, meta)                end

return output end, })(...)   end, })

local Item_Hero = setmetatable({}, {   __call = function(self, ...)        local data_item = Data.item        local function resolve(self, type, platform, item)            setmetatable(self, nil)

local data_item = data_item[type] -- copy item for k, v in pairs(item) do               self[k] = v            end -- resolve items for k, v in ipairs(self) do               self[k] = data_item[v][platform] end

return self end

return setmetatable(self, {           __call = function(self, type, platform, item)                local output = {                    raw = item,                }                for k, v in ipairs(item) do                    output[k] = setmetatable({}, { __ipairs = function(self) return ipairs(resolve(self, type, platform, v)) end, __pairs = function(self) return pairs(resolve(self, type, platform, v)) end, __index = function(self, key) return resolve(self, type, platform, v)[key] end, })               end

return output end, })(...)   end, })

local Item_Pet = setmetatable({}, {   __call = function(self, ...)        local data_item = Data.item        local data_stone = Data.stone        local data_stone_item = data_stone.item        local data_stone_class = data_stone.class

local function resolve(self, type, stone, item) setmetatable(self, nil)

local data_item = Data.item[type]

for k, v in pairs(item) do               self[k] = v            end

for k, v in ipairs(self) do               local stone = stone[v[1]] local class = data_stone_class[v[2]]

self[k] = data_item[class and table.concat({class, stone}, ' ') or stone].browser end

return self end

return setmetatable(self, {           __call = function(self, type, stone)                local output = {                    raw = stone,                }                for k, v in ipairs(data_stone_item) do                    output[k] = setmetatable({}, { __ipairs = function(self) return ipairs(resolve(self, type, stone, v)) end, __pairs = function(self) return pairs(resolve(self, type, stone, v)) end, __index = function(self, key) return resolve(self, type, stone, v)[key] end, })               end

return output end, })(...)   end, })

local Glyph = function(type, glyph) local output = {} local data_glyph = Data.glyph[type] local data_stat_lookup = Data.stat[type].lookup

for k, v in ipairs(glyph) do       local glyph = data_glyph[data_stat_lookup[v]]

if not glyph then error(string.format('Glyph: Invalid attribute "%s" (check spelling, only first character uppercase)', v)) end

output[k] = glyph end

return output end

local Ascension = setmetatable({}, {   __call = function(self, ...)        return setmetatable(self, { __call = function(self, hero, ascension) if ascension then local hero_type = hero.type local data_ascension = Data.ascension[hero_type]

if data_ascension then local role = {} local data_stat = Data.stat[hero_type].stat

for k, v in pairs(data_ascension[hero.args.role1:lower]) do                           role[k] = Stat(data_stat, k, v)                        end

return {role = role, hero = ascension} end end end, })(...)   end, })

local Hero = setmetatable({}, {   __index = function(self, key)        self.loadSkin = function(args)            local skin = {}

while true do               local field = 'skin' .. (#skin + 1)

local name = args[field .. '_name'] local attribute = args[field .. '_attribute']

if not name or not attribute then break end

table.insert(skin, {name = name, attribute = attribute, value = args[field .. '_value']}) end

return skin end

self.loadItem = function(data_stat, args, params, rank, default_bonus) local item = {}

default_bonus = default_bonus or 2

for k, v in ipairs(params) do               local bonus = {} local row = {bonus = bonus} local key = 'gear_' .. v .. '_'

if k > 1 then local bonus_main = tonumber(args[key .. 'bonus']) or default_bonus

for k in pairs(data_stat.main) do                       bonus[k] = bonus_main end end

for k, v in pairs(data_stat.param) do                   local arg = args[string.format("%sbonus_%s", key, v)]

if arg then bonus[k] = tonumber(arg) end end

item[k] = row -- stop loading if rank is reached if k == rank then break end

for i = 1, 6 do                   local value = args[key .. i]

if not value or value == '' then error(string.format('Item: Item parameter "%s%d" not set!', key, i)) end

row[i] = value end end

return item end

self.loadSkill = function(data_stat_param, args, colors) local output = {}

for _, color in ipairs(colors) do               local key = 'skill_' .. color .. '_'               local skill = {}

while true do                   local key = key .. (#skill + 1) .. '_'                   local attr = {}

for k, v in pairs(data_stat_param) do                       local arg = args[key .. v]

if arg then table.insert(attr, {stat = k, value = tonumber(arg)}) end end

local arg = args[key .. 'base']

if arg then table.insert(attr, {value = tonumber(arg)}) end

local arg = args[key .. 'round']

if arg then attr.round = tonumber(arg) end

if #attr == 0 then break end

table.insert(skill, attr) end

if #skill > 0 then output[color] = skill end end

return output end

self.loadAttributes = function(data_stat_param, args, key) local output = {} local key = key or ''

for k, v in pairs(data_stat_param) do               local arg = args[key .. v]

if arg then output[k] = tonumber(arg) end end

if next(output) then return output end end

self.loadAscension = function(data_stat, args) if args.ascension_1_0 then local r = 1 local i = 0 local output = {} local form = 'ascension_%d_%d' local key = form:format(r, i)               local value = args[key] local trim = mw.text.trim local split = mw.text.split local data_stat_stat = data_stat.stat local data_stat_lookup = data_stat.lookup

while value do                   local rank = {}

while value do                       local cost = {} local reward = {} local x, y, parent = unpack(split(value, ',', true)) local attribute, attribute_value = unpack(split(args[key .. '_data'] or '', ',')) local data = { x = tonumber(x), y = tonumber(y), parent = tonumber(parent), cost = cost, reward = reward, }                       if attribute ~= '' then attribute = trim(attribute)

if attribute == 'Skill' then data.skill = tonumber(attribute_value) else data.stat = Stat(data_stat_stat, data_stat_lookup[attribute], tonumber(attribute_value)) end end

for k, v in ipairs(split(args[key .. '_cost'] or '', ',')) do                           cost[trim(k)] = tonumber(v) end

for k, v in ipairs(split(args[key .. '_reward'] or '', ',')) do                           reward[trim(k)] = tonumber(v) end

table.insert(rank, data)

i = i + 1 key = form:format(r, i)                       value = args[key] end

table.insert(output, rank)

i = 0 r = r + 1 key = form:format(r, i)                   value = args[key] end

return output end end

return setmetatable(self, nil)[key] end, })

setmetatable(Data, {   __index = function(self, key)        -- load data on first access        local data = mw.loadData('Module:Hero/Data')        local get = require('Module:Data').get        local frame = mw.getCurrentFrame

local function createInitMetatable(data, index, newindex) return { __index = function(self, key) local init = index[key]

if init then init = init else init = data[key] end

rawset(self, key, init)

return init end, __newindex = function(self, key, value) local init = newindex[key]

if init then init = init(value) else init = value end

return rawset(self, key, init) end, }       end -- resolve data on access return setmetatable(self, createInitMetatable(data, { stat = function local data_stat = data.stat local lookup_meta = { __index = function(self, key) error(string.format('Lookup: Invalid attribute "%s" (check spelling, only first character uppercase)', key or '')) end, }               return setmetatable({}, {                    __index = function(self, key)                        local stat = {}                        local param = {}                        local lookup = {}                        local output = {                            stat = stat,                            param = param,                            lookup = setmetatable(lookup, lookup_meta),                        }                        self[key] = output

for k, v in pairs(data_stat[key] or {}) do                           stat[k] = v                        end

for k, v in pairs(stat.main or {}) do                           stat[k] = v                        end

output.main = stat.main stat.main = nil

for k, v in pairs(stat) do                           local name = v.name

param[k] = name:lower:gsub(' ', '_') lookup[name] = k                       end

return output end, })           end,            hero = function                local skillColors = {'white', 'green', 'blue', 'violet', 'ascension_white', 'ascension_green', 'ascension_blue', 'ascension_violet'}                local data_stat = Data.stat                local data_rank = Data.rank

return setmetatable({}, {                   __call = function(self, key, settings, args)                        local output = {}

args = args or Data.load['Heroes/' .. key] settings = settings or {}

for platform, args in pairs(args:split) do                           local type = 'hero_' .. platform local data_stat = data_stat[type] local data_stat_stat = data_stat.stat local data_stat_param = data_stat.param local data_stat_lookup = data_stat.lookup local hero = { type = type, title = key, args = args, }                           local main_stat = args.main_stat

output[platform] = hero

setmetatable(hero, createInitMetatable({}, { stat = function return MaxStat(hero) end, power = function return Power(hero) end, skill = function return Skill(hero, skillColors) end, }, {                               main_stat = function(value) return Stat(data_stat_stat, data_stat_lookup[value]) end, skin = function(value) table.insert(value, 1, {name = 'Default Skin', attribute = main_stat})

return Skin(hero, value) end, glyph = function(value) return Glyph(type, value) end, artifact = function(value) local stat = {} local main_stat = hero.main_stat.stat local data_artifact = Data.artifact[type] -- add book for k, v in ipairs(data_artifact[data_artifact.lookup[value]].stat) do                                       stat[v.stat] = v.value end -- add ring stat[main_stat] = data_artifact.ring[main_stat]

return stat end, item = function(value) return Item_Hero(type, platform, value) end, weapon = function(value) local stat = Stat(data_stat_stat, data_stat_lookup[value[1]], tonumber(value[2]))

stat.mul = 0.5

return stat end, ascension = function(value) return Ascension(hero, value) end, }))                           hero.main_stat = main_stat hero.level = tonumber(settings.level) hero.glyph = {args['glyph1'], args['glyph2'], args['glyph3'], args['glyph4'], main_stat} hero.artifact = args.artifact_book hero.skin = Hero.loadSkin(args) hero.item = Hero.loadItem(data_stat, args, data_rank[platform].param, settings.color) hero.star = Hero.loadAttributes(data_stat_param, args, string.format('star%s_', settings.star or '6')) hero.base = Hero.loadAttributes(data_stat_param, args, 'base_') hero.weapon = {args.artifact_weapon_attribute, args.artifact_weapon_value} hero.ascension = Hero.loadAscension(data_stat, args) end

self[key] = output

return output end, __index = function(self, key) local succuess, retn = pcall(self, key)

if succuess then return retn end end, })           end,            pet = function                local data_stat = Data.stat                local skillColors = {'white', 'green', 'violet'}

return setmetatable({}, {                   __call = function(self, key, settings, args)                        local output = {}

args = args or Data.load['Pets/' .. key] settings = settings or {}

for platform, args in pairs(args:split) do                           local type = 'pet_' .. platform local data_stat = data_stat[type] local data_stat_param = data_stat.param local pet = { type = type, title = key, args = args, }                           output[platform] = pet

setmetatable(pet, createInitMetatable({}, { stat = function return MaxStat(pet) end, power = function return Power(pet) end, skill = function return Skill(pet, skillColors) end, }, {                               item = function(value) return Item_Pet(type, value) end, }))                           pet.level = tonumber(settings.level) pet.item = {args.stone1, args.stone2} -- ToDo: settings.color ? pet.star = Hero.loadAttributes(data_stat_param, args, string.format('star%s_', settings.star or '6')) pet.star = Hero.loadAttributes(data_stat_param, args, 'star6_') pet.base = Hero.loadAttributes(data_stat_param, args, 'base_')

if old then pet.skill = Hero.loadSkill(data_stat_param, args, skillColors) end end

self[key] = output

return output end, __index = function(self, key) local succuess, retn = pcall(self, key)

if succuess then return retn end end, })           end,            titan = function                local data_stat = Data.stat                local skillColors = {'white', 'green'}

return setmetatable({}, {                   __call = function(self, key, settings, args)                        local output = {}

args = args or Data.load['Titans/' .. key] settings = settings or {}

for platform, args in pairs(args:split) do                           local type = 'titan_' .. platform local data_stat = data_stat[type] local data_stat_stat = data_stat.stat local data_stat_param = data_stat.param local data_stat_lookup = data_stat.lookup local titan = { type = type, title = key, args = args }                           output[platform] = titan

setmetatable(titan, createInitMetatable({}, { stat = function return MaxStat(titan, 1.5) end, power = function return Power(titan) end, skill = function return Skill(titan, skillColors) end, }, {                               skin = function(value) return Skin(titan, value) end, artifact = function(data) local stat = {} local data_artifact = Data.artifact[type] -- add crown for k, v in pairs(data_artifact.crown) do                                       stat[k] = (stat[k] or 0) + v                                    end -- add seal and spirit for _, v in ipairs(data) do                                       for k, v in ipairs(data_artifact[data_artifact.lookup[v]].stat) do                                            stat[v.stat] = (stat[v.stat] or 0) + v.value end end

return stat end, weapon = function(value) local stat = data_stat_lookup[value[1]] local value = tonumber(value[2])

if platform == 'browser' then -- passive stat bonus titan.artifact[stat] = (titan.artifact[stat] or 0) + value else -- mobile - activates on use like heroes local stat = Stat(data_stat_stat, stat, value)

stat.mul = 2

return stat end end, }))

titan.element = args.element titan.level = tonumber(settings.level) titan.artifact = {args.artifact_seal, args.element} titan.skin = Hero.loadSkin(args) titan.star = Hero.loadAttributes(data_stat_param, args, string.format('star%s_', settings.star or '6')) titan.base = Hero.loadAttributes(data_stat_param, args, 'base_') titan.weapon = {args.artifact_weapon_attribute, args.artifact_weapon_value} end

self[key] = output

return output end, __index = function(self, key) local succuess, retn = pcall(self, key)

if succuess then return retn end end, })           end,            creep = function                local data_stat = Data.stat                local data_rank = Data.rank

return setmetatable({}, {                   __call = function(self, key, settings, args)                        local output = {}

args = args or Data.load['Enemies/' .. key] settings = settings or {}

for platform, args in pairs(args:split) do                           local type = 'creep_' .. platform local data_stat = data_stat[type] local data_stat_stat = data_stat.stat local data_stat_param = data_stat.param local data_stat_lookup = data_stat.lookup local creep = { type = type, title = key, args = args, }                           output[platform] = creep

setmetatable(creep, createInitMetatable({}, { stat = function return MaxStat(creep) end, power = function return Power(creep) end, skill = function return Skill(creep) end, }, {                               main_stat = function(value) return Stat(data_stat_stat, data_stat_lookup[value]) end, item = function(value) return Item_Hero(type, platform, value) end, }))                           creep.main_stat = args.main_stat creep.level = tonumber(settings.level) creep.item = Hero.loadItem(data_stat, args, data_rank[platform].param, settings.color) creep.star = Hero.loadAttributes(data_stat_param, args, string.format('star%s_', settings.star or '6')) creep.base = Hero.loadAttributes(data_stat_param, args, 'base_') end

self[key] = output

return output end, __index = function(self, key) local succuess, retn = pcall(self, key)

if succuess then return retn end end, })           end,            outland = function                local data_stat = Data.stat                local data_rank = Data.rank

return setmetatable({}, {                   __call = function(self, key, settings, args)                        local output = {}

args = args or Data.load['Outland/' .. key] settings = settings or {}

for platform, args in pairs(args:split) do                           local type = 'creep_' .. platform local data_stat = data_stat[type] local data_stat_stat = data_stat.stat local data_stat_param = data_stat.param local data_stat_lookup = data_stat.lookup local boss = { type = type, title = key, args = args, }                           output[platform] = boss

setmetatable(boss, createInitMetatable({}, { skill = function return Skill(boss) end, }, {                               main_stat = function(value) return Stat(data_stat_stat, data_stat_lookup[value]) end, }))                           boss.main_stat = args.main_stat -- load level entries local level = 1 local levels = {} local form = 'level_%d_' local key = form:format(level) local value = args[key .. 'level'] local main_stat = boss.main_stat.stat local data_rank_lookup = data_rank[platform].lookup

while value do                               local stat = Hero.loadAttributes(data_stat_param, args, key) local color = args[key .. 'color'] local data = { type = type, color = color, rank = data_rank_lookup[color], level = tonumber(value), star = tonumber(args[key .. 'star']), stat = stat, }                               -- calculate power with core stats data.power = Power(data).total -- resolve effect of main attributes for k, v in pairs(data_stat_stat) do                                   for m, v in pairs(v.calculation or {}) do                                        stat[k] = (stat[k] or 0) + (stat[m] or 0) * v                                    end end -- add physical damage from main stat stat[5] = (stat[5] or 0) + (stat[main_stat] or 0) -- remove stats with 0 for k, v in pairs(stat) do                                   if v == 0 then stat[k] = nil end end

table.insert(levels, data) -- next entry level = level + 1 key = form:format(level) value = args[key .. 'level'] end

boss.levels = levels end

self[key] = output

return output end, __index = function(self, key) return self(key) end, })           end,            guild_raid = function                local data_stat = Data.stat                local data_rank = Data.rank

return setmetatable({}, {                   __call = function(self, key, settings, args)                        local output = {}

args = args or Data.load['Guild/Asgard/Guild_Raid/' .. key] settings = settings or {}

for platform, args in pairs(args:split) do                           local type = 'creep_' .. platform local data_stat = data_stat[type] local data_stat_stat = data_stat.stat local data_stat_param = data_stat.param local data_stat_lookup = data_stat.lookup local boss = { type = type, title = key, args = args, }                           output[platform] = boss

setmetatable(boss, createInitMetatable({}, { skill = function return Skill(boss) end, }, {                               main_stat = function(value) return Stat(data_stat_stat, data_stat_lookup[value]) end, }))                           boss.main_stat = args.main_stat -- load level entries local level = 1 local levels = {} local form = 'level_%d' local key = form:format(level) local value = args[key] local split = mw.text.split local main_stat = boss.main_stat.stat local stat = Hero.loadAttributes(data_stat_param, args, 'stat_') local formulaA, formulaB, formulaC = unpack(split(args.formula or '', ','))

while value do                               local levelData = split(value, ';') local statLevel = tonumber(table.remove(levelData, 1)) local mul = ((statLevel ^ formulaA) / formulaB) + formulaC local newStat = {} local data = { type = type, level = statLevel, stat = newStat, data = levelData, }                               -- calculate and round stats for k, v in pairs(stat) do                                   newStat[k] = math.floor(v * mul * 100 + 0.5) / 100 end -- calculate power with core stats data.power = Power(data).total -- resolve effect of main attributes for k, v in pairs(data_stat_stat) do                                   for m, v in pairs(v.calculation or {}) do                                        newStat[k] = (newStat[k] or 0) + (newStat[m] or 0) * v                                    end end -- add physical damage from main stat newStat[5] = (newStat[5] or 0) + (newStat[main_stat] or 0) -- remove stats with 0 for k, v in pairs(newStat) do                                   if v == 0 then newStat[k] = nil end end

table.insert(levels, data) -- next entry level = level + 1 key = form:format(level) value = args[key] end

boss.levels = levels end

self[key] = output

return output end, __index = function(self, key) return self(key) end, })           end,            item = function                local data_stat = Data.stat                local data_load = Data.load                local param = {                    hero_browser = function(s) return s.param end,                    hero_mobile = function(s) return s.param end,                    pet_browser = function(s) return {                            [10] = 'armor_penetration_max',                            [11] = 'magic_penetration_max',                            [14] = 'skill_power_max',                            [15] = 'patronage_power_max',                        }                    end,                    creep_browser = function(s) return s.param end,                    creep_mobile = function(s) return s.param end,                }                local title = {                    hero_browser = 'Heroes/Equipment/',                    hero_mobile = 'Heroes/Equipment/', pet_browser = 'Pets/Equipment/', creep_browser = 'Heroes/Equipment/', creep_mobile = 'Heroes/Equipment/', }               return setmetatable({}, {                    __index = function(self, ttype)                        local data_stat = data_stat[ttype]                        local data_stat_stat = data_stat.stat                        local param = param[ttype](data_stat)                        local title = title[ttype]

return setmetatable({}, {                           __index = function(self, key)                                local output = {}

for platform, args in pairs(data_load[title .. key]:split) do                                   local stat = {} local item = { title = key, stat = stat, args = args, }                                   output[platform] = item

for idx, param in pairs(param) do                                       local s = args[param]

if s then stat[idx] = Stat(data_stat_stat, idx, s)                                       end end end

rawset(self, key, output)

return output end, })                   end,                }) end, glyph = function local data_stat = Data.stat local data_glyph = data.glyph

return setmetatable({}, {                   __index = function(self, key)                        local glyphs = {}                        local data_stat = data_stat[key].stat

for k, v in pairs(data_glyph[key]) do                           glyphs[k] = Stat(data_stat, k, v)                        end

rawset(self, key, glyphs)

return glyphs end, })           end,            artifact = function                local data_stat = Data.stat                local data_artifact = data.artifact                local function createLookup(artifacts, data)                    artifacts.lookup = setmetatable({}, { __index = function(self, key) local lookup = {}

for k, v in pairs(data) do                               if v.name then lookup[v.name] = k                               end end

artifacts.lookup = setmetatable(lookup, {                               __index = function(self, key)                                    error(string.format('Lookup: Invalid artifact "%s" (check spelling)', key or ''))                                end,                            }) setmetatable(self, { __index = lookup })

return lookup[key] end, })               end

local function resolveHero(key) local data_artifact = data_artifact[key] local data_stat = data_stat[key].stat

return setmetatable({}, {                       __index = function(self, key)                            local artifacts = {                                weapon = data_artifact[1],                                ring = data_artifact[3],                            }                            local ref = data_artifact[2].stat                            -- loop through books                            for k, v in pairs(data_artifact[2]) do                                artifacts[k] = setmetatable({}, { __index = function(self, key) local artifact = {}

for k, v in pairs(v) do                                           artifact[k] = v                                        end

local stat = {}

for k, v in ipairs(artifact.stat) do                                           stat[k] = Stat(data_stat, v, ref[k][v]) end

artifact.stat = stat artifacts[k] = artifact

setmetatable(self, { __index = artifact })

return artifact[key] end, })                           end

createLookup(artifacts, data_artifact[2])

return setmetatable(self, { __index = artifacts })[key] end, })               end

local function resolveTitan(key) local data_artifact = data_artifact[key] local data_stat = data_stat[key].stat

return setmetatable({}, {                       __index = function(self, key)                            local artifacts = {                                weapon = data_artifact[1],                                crown = data_artifact[2],                            }                            -- loop through seals and sprit                            for k, v in pairs(data_artifact[3]) do                                artifacts[k] = setmetatable({}, { __index = function(self, key) local artifact = {}

for k, v in pairs(v) do                                           artifact[k] = v                                        end

local stat = {}

for k, v in pairs(artifact.stat) do                                           table.insert(stat, Stat(data_stat, k, v)) end

artifact.stat = stat artifacts[k] = artifact

setmetatable(self, { __index = artifact })

return artifact[key] end, })                           end

createLookup(artifacts, data_artifact[3])

return setmetatable(self, { __index = artifacts })[key] end, })               end

return setmetatable({}, createInitMetatable({}, { hero_browser = function return resolveHero('hero_browser') end, hero_mobile = function return resolveHero('hero_mobile') end, titan_browser = function return resolveTitan('titan_browser') end, titan_mobile = function return resolveTitan('titan_mobile') end, }))           end, rank = function local ranks = { browser = { 'white', 'green', 'green +1', 'blue', 'blue +1', 'blue +2', 'violet', 'violet +1', 'violet +2', 'violet +3', 'orange', 'orange +1', 'orange +2', 'orange +3', 'orange +4', 'red', 'red +1', 'red +2', },                   mobile = { 'white', 'green', 'green +1', 'blue', 'blue +1', 'blue +2', 'violet', 'violet +1', 'violet +2', 'violet +3', 'orange', 'orange +1', 'orange +2', 'orange +3', 'orange +4', },               }                local output = {} local meta = { -- try lowercase input if not found __index = function(self, key) local value = self[key:lower]

rawset(self, key, value)

return value end, }               for platform, ranks in pairs(ranks) do                    local lookup = setmetatable({}, meta) local params = {}

output[platform] = { ranks = ranks, lookup = lookup, param = params, }                   for k, v in ipairs(ranks) do                        local param, matches = v:gsub(' %+', '')

params[k] = (matches > 0) and param or (param .. '0') lookup[v] = k                   end end

return output end, load = function local splitTable = {} local newTableMeta = { __index = function(self, key) local data = {}

self[key] = data

return data end, }               function splitArgs(args, key) local function load(self) setmetatable(self, newTableMeta)

for k, v in pairs(args) do                           if type(k) == 'string' then local platform, param = k:match('([^_]+)_(.+)') if platform then -- filter non-platform args self[platform][param] = v	                           end end end

return setmetatable(self, nil) end

local data = setmetatable({}, {                       __ipairs = function(self) return ipairs(load(self)) end,                        __pairs = function(self) return pairs(load(self)) end,                        __index = function(self, key) return load(self)[key] end,                    }) rawset(splitTable, key, data)

return data end

return setmetatable({}, {                   __call = function(self, key, args)                        if not key then                            key = mw.title.getCurrentTitle.text                        end

if not args then args = {}

for k, v in pairs(frame.getParent and frame:getParent.args or {}) do                               args[k] = v                            end end

rawset(self, key, args)

return setmetatable(args, {                           __index = {                                split = function(self)                                    return splitTable[key] or splitArgs(self, key)                                end,                            }                        }) end, __index = function(self, key) return self(key, get(frame, {title = key})) end, })           end,        }))[key]    end, })

function module.page(frame, args, pargs) args = args or frame.args

local output = {} local template = args.template local pargs = Data.load(nil, pargs) local title = pargs.title or mw.title.getCurrentTitle.subpageText local hero = Data[pargs.type or args.type or template](title, nil, pargs)

if hero then local data_stat = Data.stat local platforms = {}

for platform in pairs(hero) do           table.insert(platforms, platform) end

table.sort(platforms)

for _, platform in ipairs(platforms) do           local hero = hero[platform]

local args = { title = hero.title, platform = platform, }           -- copy all args for k, v in pairs(hero.args) do               args[k] = v            end -- check if flavor is present, if not copy from global flavor parameter if not args.flavor then args.flavor = pargs.flavor end -- format skills for k, v in pairs(hero.skill) do               local key = 'skill_' .. k               local form = args[key .. '_format']

if form then args[key .. '_description'] = v:format(form) end end -- pass stats local data_stat = data_stat[hero.type] local param = data_stat.param

for k, v in pairs(hero.stat) do               local param = param[k] mw.log(platform, param, v)               args[param] = v            end -- pass power local hero_power = hero.power or {}

for k, v in pairs(hero_power) do               local param = 'power_' .. k

args[param] = v           end -- pass stats per group local groups = hero.statGroup or {}

for stat, _ in pairs(param) do               local param = param[stat] .. '_'

for k, v in pairs(groups) do                   local param = param .. k                   local value = v[stat] or 0

if value > 0 then args[param] = value end end end -- generate ascension plot if hero.ascension then local template = {title = 'Ascension/Plot/Lua'}

for k, v in ipairs(hero.ascension.hero) do                   local eargs = { width = 250, height = 300, size = 50, platform = platform, hero = hero.title, rank = k,                   } for k, v in ipairs(v) do                       if v.skill then table.insert(eargs, 'skill') table.insert(eargs, v.x)                           table.insert(eargs, v.y)                            table.insert(eargs, v.parent or -1) table.insert(eargs, v.skill) else local stat = v.stat

table.insert(eargs, param[stat.stat]) table.insert(eargs, v.x)                           table.insert(eargs, v.y)                            table.insert(eargs, v.parent or -1) end end

template.args = eargs args['ascension_plot' .. k] = frame:expandTemplate(template) end end -- format args for template local eargs = {} local prefix = platform .. '_'

for k, v in pairs(args) do               eargs[k] = v                eargs[prefix .. k] = v

mw.log(platform, k, v)           end

table.insert(output, {platform, frame:expandTemplate{title = template, args = eargs}}) end

if #output == 1 then return output[1][2] else for k, v in ipairs(output) do               output[k] = string.format('|-|%s=%s', v[1]:gsub('^%l', string.upper), v[2]) end end else error('Hero "' .. title .. '" not in database!') end

return frame:extensionTag{name = 'tabber', content = table.concat(output)} end

function module.page_test(type, title, hero) local hero = hero or 'Yasmine' local frame = mw:getCurrentFrame local pargs = require('Module:Data').get(frame, {title = title or 'Heroes/' .. hero})

pargs.title = hero

return module.page(frame, {type = type or 'hero', template = 'data'}, pargs) end

function module.heroStats(frame, args, pargs) local args = args or (frame.getParent and frame:getParent.args) or frame.args or {} local template = args.template

if template then local title = args.name or mw.title.getCurrentTitle.subpageText local eargs = { level = args.level, color = args.color, star = args.star, }       local hero = Data[args.type:lower](title, eargs, pargs and Data.load(nil, pargs))

if hero then local platform = args.platform:lower local hero = hero[platform]

if hero then eargs.title = title eargs.platform = platform eargs.power = hero.power.total -- format skills for k, v in pairs(hero.skill) do                   local key = 'skill_' .. k                   local form = args[key .. '_format']

if form then eargs[key .. '_description'] = v:format(form) end end -- pass stats local param = Data.stat[hero.type].param

for k, v in pairs(hero.stat) do                   eargs[param[k]] = v                end

for k, v in pairs(eargs) do mw.log(k, v) end

return frame:expandTemplate({title = template, args = eargs}) end end end end

function module.heroStats_test return module.heroStats(mw:getCurrentFrame, {template = 'Data', platform = 'browser', type = 'hero', name = 'Alvanor', skill_green_format = '%prime%'}) end

function module.stats(frame, args) local fargs = args or frame.args or {} local template = fargs.row_template

if template then local type = (fargs.type or 'hero'):lower local platform = (fargs.platform or 'browser'):lower local dpl = require('Module:Data').dpl local heroes = dpl(frame, {           category = platform,            uses = 'Template:Infobox ' .. type:gsub('^%l', string.upper),        }) local data_hero = Data[type]

table.sort(heroes)

local output = {} local params = {title = template}

for _, v in ipairs(heroes) do           v = v:sub(8)

local hero = data_hero[v]

if hero then local hero = hero[platform]

if hero then local args = hero.stat

args.title = v                   args.platform = platform args.power = hero.power.total

params.args = args v = args[0] -- call calculate

table.insert(output, frame:expandTemplate(params)) end end end

return table.concat(output) end end

function module.stats_test return module.stats(mw:getCurrentFrame, {row_template = 'Hero/Gist/Stats'}) end

function module.faceless(frame, args) local fargs = args or frame.args or {} local template = fargs.row_template

if template then local platform = string.lower(fargs.platform or 'Browser') local dpl = require('Module:Data').dpl local heroes = dpl(frame, {           category = platform,            uses = 'Template:Infobox Hero',        }) local lang = mw.getContentLanguage local data_hero = Data.hero local sep = fargs.sep

table.sort(heroes)

local hero = data_hero.Faceless[platform] local clevel = hero.skill.white:format.prime.value local stat = {} -- copy faceless stat for k, v in pairs(hero.stat) do           stat[k] = v        end -- overwrite level stat[0] = clevel

local output = {} local params = {title = template}

for _, v in ipairs(heroes) do           local hero = data_hero[v:sub(8)]

if hero then hero = hero[platform]

if hero then local skill_white = hero.skill.white local valuesOriginal = skill_white:format local valuesCopy = skill_white:format(nil, stat)

local diff = {} local copy = {} local original = {}

for k, o in pairs(valuesOriginal) do                       local ovalue = o.value local cvalue = valuesCopy[k].value

if ovalue ~= cvalue then local d = cvalue - ovalue

table.insert(original, ovalue) table.insert(copy, cvalue) table.insert(diff, d >= 0 and ('+' .. d) or d)                       end end

params.args = { title = hero.title, platform = platform, original = table.concat(original, sep), copy = table.concat(copy, sep), diff = table.concat(diff, sep), }                   table.insert(output, frame:expandTemplate(params)) end end end

return table.concat(output) end end

function module.faceless_test return module.faceless(mw:getCurrentFrame, {row_template = 'Hero/Gist/Faceless', sep = ' '}) end

function module.equipment(frame, args) local fargs = args or frame.args or {} local column_template = fargs.column_template local row_template = fargs.row_template

if column_template and row_template then local platform = (fargs.platform or 'browser'):lower local module_data = require('Module:Data') local dpl = module_data.dpl local get = module_data.get local heroes = dpl(frame, {           category = platform,            uses = 'Template:Infobox Hero',        }) local data_hero = Data.hero

table.sort(heroes)

local params = {title = column_template} local total_count = setmetatable({}, {           __index = function(self, key)                rawset(self, #self + 1, key)

return 0 end, })       local counts = {}        local header = {            class = 'sortable',            style = 'white-space:nowrap',            sortType1 = 'text',            'Item'        }        for k, v in ipairs(heroes) do            v = v:sub(8)

local hero = data_hero[v]

if hero then hero = hero[platform]

local count = { title = v,                   platform = platform, }               if hero then for _, v in ipairs(hero.item.raw) do                       for _, v in ipairs(v) do                            count[v] = (count[v] or 0) + 1 total_count[v] = total_count[v] + 1 end end end

params.args = count

table.insert(header, frame:expandTemplate(params)) table.insert(counts, count) end end

table.insert(header, 'Total')

local items = {} local cellFormat = '|title="%s"|%s' local args = {title = 'Template:' .. row_template, platform = platform}

params.title = 'DataTable/Row'

for k, item in ipairs(total_count) do           args.item = item

local row = get(frame, args) local content = {}

for k, v in ipairs(counts) do               content[k] = cellFormat:format(v.title, v[item] or ' ') end

content[#content + 1] = cellFormat:format('Total', total_count[item])

params.args = row row.content = table.concat(content, '\r\n')

local key = row.sortValue1

items[k] = key items[key] = frame:expandTemplate(params) end

table.sort(items)

for k, v in ipairs(items) do           items[k] = items[v] end

header.content = table.concat(items, '\r\n')

return frame:expandTemplate({title = 'DataTable', args = header}) end end

function module.equipment_test return module.equipment(mw:getCurrentFrame, {column_template = 'Hero/Gist/Equipment/Column', row_template = 'Hero/Gist/Equipment/Row'}) end

function module.infobox_item_platform(frame, args) local fargs = args or frame.args

local item = fargs.item local template = fargs.template local template_raw = fargs.template_raw local template_used_by_item = fargs.template_used_by_item local template_used_by_hero = fargs.template_used_by_hero local template_found_in = fargs.template_found_in local template_created_with = fargs.template_created_with

if item and template and template_used_by_item and template_used_by_hero and template_found_in and template_created_with and template_raw then local dpl = require('Module:Data').dpl local data_item = Data.item local data_hero = Data.hero local data_load = Data.load local args = { item = item }       for k, v in pairs(fargs) do            args[k] = v        end

local parent = frame:getParent local platforms = {}

if parent then local pargs = data_load

for k, v in pairs(pargs) do               args[k] = v            end

platforms = pargs:split else platforms = {browser = true, mobile = true} -- for testing end

local meta_createSubTable = { __index = function(self, key) local value = {}

self[key] = value

return value end, }       local meta_createSubSubTable = { __index = function(self, key) local value = setmetatable({}, meta_createSubTable)

self[key] = value

return value end, }       local meta_created_with_internal = { __newindex = function(self, key, value) table.insert(self, key)

return rawset(self, key, value) end, }       local meta_created_with = { __index = function(self, key) local value = setmetatable({}, meta_created_with_internal)

self[key] = value

return value end, }       local created_with = setmetatable({}, {            __index = function(self, item)                local created_with = setmetatable({}, meta_created_with)

self[item] = created_with

for platform in pairs(platforms) do                   local data = data_item['hero_' .. platform][item][platform]

if data then local args = data.args local gold = args.created_with_gold

if gold then local created_with = created_with[platform]

local idx = 1 local key = 'created_with_1' local param = args.created_with_1

while param do                               created_with[param] = tonumber(args[key .. '_count'] or 1)

idx = idx + 1 key = 'created_with_' .. idx param = args[key] end

local recipe = args.created_with_recipe

if recipe then created_with[item .. '/Recipe'] = tonumber(recipe) end

local fragment = args.created_with_fragment

if fragment then created_with[item .. '/Fragment'] = tonumber(fragment) end

-- created_with.gold = gold end end end

return setmetatable(created_with, nil) end, })       local used_by_item = setmetatable({}, { __index = function(self, item) local used_by_item = setmetatable({}, meta_createSubTable) local created_with = created_with

self[item] = used_by_item

local data = dpl(frame, {                   linksto = 'Heroes/Equipment/' .. item,                    uses = 'Template:Infobox item|Template:Infobox Item',                }) for _, v in ipairs(data) do -- remove 'Heroes/Equipment/' v = v:sub(18)

for platform, created_with in pairs(created_with[v]) do                       local count = created_with[item]

if count then -- only direct items used_by_item[platform][v] = count end end end

return used_by_item end, })       local used_by_hero = setmetatable({}, { __index = function(self, item) local used_by = setmetatable({}, {                   __index = function(self, item)                        local data = dpl(frame, { linksto = 'Heroes/Equipment/' .. item, uses = 'Template:Infobox Hero', })                       for k, v in ipairs(data) do                            data[k] = v:sub(8)                        end

self[item] = data

return data end, })               local function search(data, item) -- goes through all used_by items and adds all heroes to data                    if not data[item] then                        data[item] = true                        -- add heroes                        for _, v in ipairs(used_by[item]) do                            if not data[v] then                                table.insert(data, v)

data[v] = true end end -- search for further items for _, used_by_item in pairs(used_by_item[item]) do                            for item in pairs(used_by_item) do                                search(data, item) end end end

return data end

return setmetatable(self, {                   __index = function(self, item)                        local used_by_hero = search({}, item)

table.sort(used_by_hero)

self[item] = used_by_hero

return used_by_hero end, })[item]           end,        }) do -- image for platform in pairs(platforms) do               args[platform .. '_image'] = item end end

do -- created_with local pargs = {} local params = {title = template_created_with, args = pargs}

for platform, created_with in pairs(created_with[item]) do               local output = {}

pargs.platform = platform

for idx, item in ipairs(created_with) do                   pargs.item = item pargs.count = created_with[item] pargs.index = idx

table.insert(output, frame:expandTemplate(params)) end

args[platform .. '_created_with'] = table.concat(output) end end

do -- created_with_raw local meta = { __index = function(self, key) rawset(self, #self + 1, key) rawset(self, key, 0)

return 0 end, }           local pargs = {} local params = {title = template_raw, args = pargs}

for platform in pairs(platforms) do               local items = setmetatable({}, meta) local show = false

local function go(name, pcount) local data = created_with[name][platform]

if data then for _, key in ipairs(data) do                           local count = pcount * data[key] local data = created_with[key][platform]

if data and next(data) then go(key, count)

show = true -- if more than the base items are found else items[key] = items[key] + count end end end end

go(item, 1)

if show then local output = {}

table.sort(items, function(a, b)                       return items[a] < items[b] -- sort by count                    end)

pargs.platform = platform

for k, v in ipairs(items) do                       pargs.item = v                        pargs.count = items[v] pargs.index = k

table.insert(output, frame:expandTemplate(params)) end

args[platform .. '_created_with_raw'] = table.concat(output) end end end

do -- used_by_item local pargs = {} local params = {title = template_used_by_item, args = pargs}

for platform, used_by_item in pairs(used_by_item[item]) do               local items = {}

for k, _ in pairs(used_by_item) do                   table.insert(items, k)                end

pargs.platform = platform

if #items > 0 then table.sort(items)

for k, v in ipairs(items) do                       pargs.item = v                        pargs.count = used_by_item[v] pargs.index = k

items[k] = frame:expandTemplate(params) end

args[platform .. '_used_by_item'] = table.concat(items) end end end

do -- used_by_hero local pargs = {} local used_by_hero = used_by_hero[item] local params = {title = template_used_by_hero, args = pargs} local total_count_meta = { __index = function(self, key) rawset(self, #self + 1, key)

return 0 end, }           for platform in pairs(platforms) do                local item_count = setmetatable({}, meta_createSubTable) local total_count = setmetatable({}, total_count_meta) -- count hero items for _, hero in ipairs(used_by_hero) do                   local data = data_hero[hero][platform]

if data then for _, items in ipairs(data.item.raw) do                           for _, item in ipairs(items) do                                local item_count = item_count[item]

item_count[hero] = (item_count[hero] or 0) + 1 end end end end -- sum up counts of used_by_items local function go(item, parent_count) for hero, count in pairs(item_count[item]) do                       total_count[hero] = total_count[hero] + count * parent_count end

for item, count in pairs(used_by_item[item][platform]) do                       go(item, count * parent_count) end end

go(item, 1)

table.sort(total_count)

local sum = 0 local output = {} local item_count = item_count[item]

pargs.platform = platform

for idx, hero in ipairs(total_count) do                   pargs.hero = hero pargs.index = idx

local count, total = item_count[hero], total_count[hero] -- split count that directly need that item (count1) and needed for creating other items (count2) if count then pargs.count1 = count pargs.count2 = total - count else pargs.count1 = 0 pargs.count2 = total end

sum = sum + total

table.insert(output, frame:expandTemplate(params)) end

if sum > 0 then args[platform .. '_used_by_hero'] = table.concat(output) args[platform .. '_used_by_hero_total'] = sum end end end

do -- found_in local params = {title = template_found_in} local data = dpl(frame, {               category = '**Missions',                linksto = 'Heroes/Equipment/' .. item,                uses = 'Template:Infobox Campaign',                ordermethod = 'sortkey',            }) for platform in pairs(platforms) do               local missions = {}

for _, mission in ipairs(data) do                   local args = data_load[mission]:split[platform]

if args then local idx = 1 local param = args.loot1

while param do                           if param == item then table.insert(missions, mission)

missions[mission] = args break end

idx = idx + 1 param = args['loot' .. idx] end end end

local output = {}

for idx, mission in ipairs(missions) do                   local args = missions[mission]

args.title = mission args.index = idx args.platform = platform

params.args = args

table.insert(output, frame:expandTemplate(params)) end

args[platform .. '_found_in'] = table.concat(output) end end

for k, v in pairs(args) do mw.log(k, v) end

return frame:expandTemplate{title = template, args = args} end end

function module.infobox_item_test return module.infobox_item_platform(mw.getCurrentFrame, {       item = "Blue/Dragon's Heart",        template = 'Infobox Item',        template_raw = 'Infobox Item/Hero/Raw',        template_created_with = 'Infobox Item/Hero/Created With',        template_found_in = 'Campaign/Match/Output',        template_used_by_hero = 'Infobox Item/Hero/Hero',        template_used_by_item = 'Infobox Item/Hero/Item',    }) end

function module.skins(frame, args) frame = frame or mw.getCurrentFrame

local fargs = args or frame.args or {} local pargs = (frame.getParent and (frame:getParent or {}).args) or {} local template = pargs.template or fargs.template

if template then local type = fargs.type or 'Hero' local platform = string.lower(pargs.platform or 'Browser') local dpl = require('Module:Data').dpl local output = {} local skin = fargs.skin local params = {title = template} local data_hero = Data[type:lower] local meta = { __index = function(self, key) local data = {}

table.insert(self, key)

self[key] = data

return data end, }       if skin then -- one skin, passes each hero to the template individually, sorts by year if given local data = dpl(frame, {               category = string.format('%ss (%s)', skin, platform:gsub('^%l', string.upper))            }) local istitan = type == 'Titan' local heroes = {}

for _, v in ipairs(data) do               if v:match(type) then local hero, stat = data_hero[v:sub(8)][platform] for _, v in ipairs(hero.skin or {}) do                       if v.name == skin then stat = v.stat break end end table.insert(heroes, {                       skin = skin,                        hero = hero.title,                        value = fargs[string.format('%s_%s', platform, hero.title:lower)],                        attribute = stat.name,                        skin_stones = istitan and 'Titan' or hero.main_stat.name,                        platform = platform,                    }) end end -- sort by name table.sort(heroes, function(a, b) return a.hero < b.hero end) -- sort by sort value local sortBy = setmetatable({}, meta)

for _, v in ipairs(heroes) do               table.insert(sortBy[tostring(v.value or 0)], v)            end

table.sort(sortBy)

for _, v in ipairs(sortBy) do               for _, v in ipairs(sortBy[v]) do                    params.args = v

table.insert(output, frame:expandTemplate(params)) end end else -- list all skins, passes skin and all heroes to template local skins = setmetatable({}, meta) local heroes = dpl(frame, {               category = platform,                uses = 'Template:Infobox ' .. type,            }) table.sort(heroes)

for _, v in ipairs(heroes) do               v = v:sub(8) local hero = data_hero[v] if hero then hero = hero[platform] if hero then for _, v in ipairs(hero.skin or {}) do                           v.hero = hero table.insert(skins[v.name], v)                       end end end end

table.sort(skins)

for k, v in ipairs(skins) do               local heroes = { skin = v               } for _, v in ipairs(skins[v]) do                   table.insert(heroes, v.hero.title) end

table.sort(heroes)

params.args = heroes

output[k] = frame:expandTemplate(params) end end

return table.concat(output) end end

function module.skins_test(type, skin) type = type or 'Hero'

return module.skins(mw.getCurrentFrame, {type = type, skin = skin, lian = 2020, template = type ..'/Gist/Skins'}) end

function module.skinlist(frame, args) local frame = frame or mw.getCurrentFrame local args = args or frame.args or {} local template = args.template

if template then local data_load = Data.load local dpl = require('Module:Data').dpl local platform = (args.platform or 'browser'):lower local skins = dpl(frame, {           category = '*Skins',            uses = 'Template:Hero/Gist/Skin',        }) local meta = {} local list = setmetatable({}, meta)

meta.__index = function(self, key) local data = setmetatable({}, meta)

rawset(self, key, data) rawset(self, #self + 1, key)

return data end

for _, page in ipairs(skins) do           local skin = page:match('[^/]+$') local sargs = data_load[page]:split[platform]

for k, v in pairs(sargs) do               table.insert(list[v][skin], k)            end end

local heroeNames = dpl(frame, {           uses = 'Template:Infobox Hero',        }) for _, v in ipairs(heroeNames) do           local name = v:match('[^/]+$')

heroeNames[name:lower] = name end

table.sort(list)

local output = {} local params = {title = template}

for _, value in ipairs(list) do           local skins = list[value] local eargs = { platform = platform, value = value, }           params.args = eargs

table.sort(skins)

for _, skin in ipairs(skins) do               local heroes = skins[skin]

eargs.skin = skin

table.sort(heroes)

for k, v in ipairs(heroes) do                   eargs['hero_' .. k] = heroeNames[v] end

table.insert(output, frame:expandTemplate(params)) end end

return table.concat(output) end end

function module.infobox_creep(frame, pargs) local data_load = Data.load local pargs = data_load(nil, pargs) local dpl = require('Module:Data').dpl local dplvar = pargs.dplvar local title = pargs.title or mw.title.getCurrentTitle.subpageText local fulltitle = 'Enemies/' .. title local missions = dpl(frame, {       category = '**Missions',        linksto = fulltitle,        uses = 'Template:Infobox Campaign',        ordermethod = 'sortkey',    }) local platforms = pargs:split

if not next(platforms) then platforms = {browser = true, mobile = true} end

local data_rank = Data.rank local data_creep = Data.creep local variants = {} local meta = { __newindex = function(self, key, value) rawset(self, key, value) rawset(self, #self + 1, key) end, }   for platform in pairs(platforms) do        local vdata = setmetatable({}, meta)

variants[platform] = vdata

for _, mission in ipairs(missions) do           local args = data_load[mission]:split[platform]

if args then local weave = 1 local position = 1 local form = 'enemy%d_%d' local param = args[form:format(1, 1)] local data_rank_lookup = data_rank[platform].lookup

while param do                   if param == fulltitle then local suffix = string.format('%d_%d', weave, position) local rank = args['rank' .. suffix]:lower local level = args['level' .. suffix] local star = args['star' .. suffix] local color = data_rank_lookup[rank] local hero = data_creep(title, {                           level = level,                            color = color,                            star = star,                        }, pargs)

if hero then local hero = hero[platform]

if hero then local key = hero.power.total

if not vdata[key] then hero.mission = setmetatable({}, meta) hero.level = level hero.rank = rank:gsub('^%l', string.upper) hero.star = star hero.color = color

vdata[key] = hero end

vdata[key].mission[mission] = true end end end

position = position + 1 param = args[form:format(weave, position)]

if not param then weave = weave + 1 position = 1 param = args[form:format(weave, position)] end end end end end

local params = {title = 'Infobox Creep/Variant'} local data_stat = Data.stat

for platform, variants in pairs(variants) do       local tabber = {}

table.sort(variants)

for k, v in ipairs(variants) do           local hero = variants[v] local eargs = { title = title, level = hero.level, rank = hero.rank, star = hero.star, platform = platform, power = hero.power.total, scale = pargs[platform.. '_scale'] or 1 }           -- format skills local skills = {}

for k, v in pairs(hero.skill) do               local form = pargs[string.format('%s_skill_%d_format', platform, k)]

if form then skills[k] = v:format(form) end end

if dplvar then local vkey = string.format('%s_%d_%d_%s_', platform, hero.star, hero.level, hero.color) local args = {}

for k, v in pairs(skills) do                   table.insert(args, vkey .. k)                    table.insert(args, v)

mw.log('dplvar', vkey .. k, v)               end

frame:callParserFunction{name = '#dplvar:set', args = args} else local args = {}

for k, _ in pairs(skills) do                   table.insert(args, k)                end

table.sort(args)

for k, v in ipairs(args) do                   local key = 'skill_' .. k                   local name = pargs[string.format('%s_skill_%d_name', platform, v)] local value = skills[v]

eargs[key .. '_name'] = name eargs[key .. '_description'] = value end end -- pass stats local param = data_stat[hero.type].param

for k, v in pairs(hero.stat) do               eargs[param[k]] = v            end

for k, v in ipairs(hero.mission) do               eargs['mission' .. k] = v           end

local power = hero.power.total

for k, v in pairs(eargs) do               mw.log(power, k, v)            end

params.args = eargs

table.insert(tabber, string.format('|-|%s=%s', hero.level, frame:expandTemplate(params))) end

if #tabber > 0 then pargs[platform .. '_variant'] = frame:extensionTag{name = 'tabber', content = table.concat(tabber)} end end

for k, v in pairs(pargs) do mw.log(k, v) end

return frame:expandTemplate({title = 'Infobox Creep/Internal', args = pargs}) end

function module.infobox_creep_test(title) local title = title or 'Seymour' local args = { title = title, }   for k, v in pairs(Data.load['Enemies/' .. title]) do        args[k] = v    end

return module.infobox_creep(mw.getCurrentFrame, args) end

function module.infobox_outland(frame, pargs) local data_load = Data.load local pargs = data_load(nil, pargs) local title = pargs.title or mw.title.getCurrentTitle.subpageText local boss = Data.outland(title, nil, pargs)

if boss then local params = {title = 'Infobox Outland/Variant'} local data_stat = Data.stat

for platform, boss in pairs(boss) do           local tabber = {} local skills = {} local args = boss.args local data_stat_param = data_stat[boss.type].param -- load skills for k, v in pairs(boss.skill) do               local key = 'skill_' .. k               local form = args[key .. '_format']

if form then table.insert(skills, {                       key = k,                        skill = v,                        format = form,                    }) end end

table.sort(skills, function(a, b)               return a.key < b.key            end) -- loop levels for k, v in ipairs(boss.levels) do               local stat = v.stat local power = v.power local eargs = { title = title, level = v.level, rank = v.color:gsub('^%l', string.upper), star = v.star, platform = platform, power = power, }               -- format skills for k, v in ipairs(skills) do                   local key = 'skill_' .. k                   local skill = v.skill

eargs[key .. '_name'] = skill.name eargs[key .. '_description'] = skill:format(v.format, stat) end -- pass stats for k, v in pairs(stat) do                   eargs[data_stat_param[k]] = v                end

for k, v in pairs(eargs) do                   mw.log(platform, power, k, v)                end

params.args = eargs

table.insert(tabber, string.format('|-|%s=%s', v.level, frame:expandTemplate(params))) end

if #tabber > 0 then pargs[platform .. '_variant'] = frame:extensionTag{name = 'tabber', content = table.concat(tabber)} end end

-- for k, v in pairs(pargs) do mw.log(k, v) end

return frame:expandTemplate({title = 'Infobox Outland/Internal', args = pargs}) end end

function module.infobox_outland_test(title) local title = title or 'Vadjar the Incinerator' local args = { title = title, }   for k, v in pairs(Data.load['Outland/' .. title]) do        args[k] = v    end

return module.infobox_outland(mw.getCurrentFrame, args) end

function module.infobox_guild_raid(frame, pargs) local data_load = Data.load local pargs = data_load(nil, pargs) local title = pargs.title or mw.title.getCurrentTitle.subpageText local boss = Data.guild_raid(title, nil, pargs)

if boss then local params_variant = {title = 'Infobox Guild Raid/Variant'} local params_skill = {title = 'Infobox Guild Raid/Skill'} local data_stat = Data.stat local split = mw.text.split

for platform, boss in pairs(boss) do           local tabber = {} local skills = {} local args = boss.args local data_stat_param = data_stat[boss.type].param -- load skills for k, v in pairs(boss.skill) do               local key = 'skill_' .. k               local form = args[key .. '_format']

if form then table.insert(skills, {                       key = k,                        skill = v,                        format = form,                    }) end end

table.sort(skills, function(a, b)               return a.key < b.key            end) -- loop levels for k, v in ipairs(boss.levels) do               local stat = v.stat local statLevel = v.level local skills_formatted = {} local unitLevels = setmetatable({}, {                   __newindex = function(self, key, value)                        rawset(self, #self + 1, tonumber(key))                        rawset(self, key, value)                    end,                }) local eargs = { title = title, level = statLevel, platform = platform, power = v.power, }               -- pass stats for k, v in pairs(stat) do                   eargs[data_stat_param[k]] = v                end -- pass and format data for k, v in ipairs(v.data) do                   local key = 'data_' .. k                   local data = split(v, ',') local join = {}

eargs[key] = table.remove(data, 1)

for k, v in ipairs(data) do                       local data = split(v, '-') local raidLevel = table.remove(data, 1)

table.insert(join, raidLevel) table.insert(join, table.concat(data, ', '))

for _, v in ipairs(data) do                           unitLevels[v] = true end end

eargs[key .. '_level'] = table.concat(join, ';') end -- format skills table.sort(unitLevels)

for _, unitLevel in ipairs(unitLevels) do                   local eargs = { platform = platform }                   stat[0] = unitLevel params_skill.args = eargs

for k, v in ipairs(skills) do                       local key = 'skill_' .. k                       local skill = v.skill eargs[key .. '_name'] = skill.name eargs[key .. '_description'] = skill:format(v.format, stat) end

table.insert(skills_formatted, string.format('|-|%d=%s', unitLevel, frame:expandTemplate(params_skill))) end

if #skills_formatted > 0 then eargs.skills = frame:extensionTag{name = 'tabber', content = table.concat(skills_formatted)} end

for k, v in pairs(eargs) do                   mw.log(platform, statLevel, k, v)                end

params_variant.args = eargs

table.insert(tabber, string.format('|-|%s=%s', statLevel, frame:expandTemplate(params_variant))) end

if #tabber > 0 then pargs[platform .. '_variant'] = frame:extensionTag{name = 'tabber', content = table.concat(tabber)} end end

-- for k, v in pairs(pargs) do mw.log(k, v) end

return frame:expandTemplate({title = 'Infobox Guild Raid/Internal', args = pargs}) end end

function module.infobox_guild_raid_test(title) local title = title or 'Stellar Soul Healer' local args = { title = title, }   for k, v in pairs(Data.load['Guild/Asgard/Guild_Raid/' .. title]) do        args[k] = v    end

return module.infobox_guild_raid(mw.getCurrentFrame, args) end

function module.selftest --assert(module.page_test('hero', 'Heroes/Alvanor', 'Alvanor'), 'page_test hero') --assert(module.page_test('titan', 'Titans/Eden', 'Eden'), 'page_test titan') --assert(module.page_test('pet', 'Pets/Albus', 'Albus'), 'page_test pet') assert(module.heroStats_test, 'heroStats_test') assert(module.stats_test, 'stats_test') assert(module.faceless_test, 'faceless_test') assert(module.equipment_test, 'equipment_test') assert(module.infobox_item_test, 'infobox_item_test') assert(module.skins_test('Hero'), 'skins_test') assert(module.skins_test('Titan'), 'skins_test') assert(module.infobox_creep_test('Demon Archer'), 'infobox_creep_test') assert(module.infobox_outland_test, 'infobox_outland_test') end

return module