League of Legends Wiki
League of Legends Wiki
League of Legends Wiki

Данная группа модулей содержит функции и списки по работе с предметами из игры League of Legends. Перечень модулей:

From Модуль:ItemData/doc


-- <pre>
local p = {}

local items = mw.loadData('Модуль:ItemData/data')
local get   = require("Модуль:ItemData/getter")
local IL    = require('Модуль:ImageLink')
local lib   = require('Модуль:Feature')
local uError= require('Dev:User error')

--[[================
    Приватные методы
    ================]]

-- Является ли название страницы, где вызван скрипт, названием предмета-корня дерева состава
local function _isTitular(item)
    return item == mw.title.getCurrentTitle()
end

-- Является трансформацией предмета? (имеет что-то в составе, но стоимость сборки равна 0)
local function _isTransformation(itemData)
	return (itemData.comb == 0 or not(itemData.comb)) and itemData.recipe and not(itemData.builds)
end

-- Возвращает строку со стоимостью предмета и стоимостью сборки
-- На страницах выглядит как подпись под предметом в дереве состава предмета
local function _getCostString(item)
    local cost = ""
    if(get.buy(item) ~= nil and get.buy(item) ~= 0) then 
        cost = cost .. mw.ustring.format("{{g|%s|size=16}}", tostring(get.buy(item)))
    end
    if(get.comb(item) ~= nil and get.comb(item) ~= 0) then
        cost = cost .. mw.ustring.format(" ({{g|%s|size=16}})", tostring(get.comb(item)))
    end
    return cost
end

-- Проверяет, является ли характеристика процентной
local function _isPercentile(stat)
	local PERCENTILES = {
		["as"] = true,
		["cdr"] = true,
		["cdrunique"] = true,
		["crit"] = true,
		["lifesteal"] = true,
		["crit"] = true,
		["critunique"] = true,
		["hppct"] = true,
		["hp5"] = true,
		["mp5"] = true,
		["ms"] = true,
		["msunique"] = true,
		["hsp"] = true,
	}
	return PERCENTILES[stat]
end

-- Округляет до 2 знаков после запятой. Убирает незначащие нули в любом случае
local function _round2(num)
    return math.floor(num * 100 + 0.5) / 100
end

-- Реализует тот же функционал, что и Шаблон:Рецепт/Предмет
-- Создает блок, в котором дается иконка предмета, подпись и стоимость
-- Является составной частью дерева состава предмета
-- Возвращает mw.html объект
local function _buildRecipeTreeItem(item)
    --Состоит из трех блоков: иконки, текста и цены под ним
    local itemNode	= mw.html.create("div"):addClass("lit__leaf")
    itemNode:wikitext(tostring(p.itemIcon{
        ["item"] = item,
        ["text"] = "*none*",
        ["size"] = "32",
        ["class"] = "lit__leaf-spanned",
        ["iconclass"] = "lit__leaf-icon"
    }))

    local labelNode = mw.html.create("div")
    labelNode
    	:addClass("item-lua-icon lit__leaf-title")
        :attr("data-param", item)
        :attr("data-game", "lol")
        :wikitext(mw.ustring.format("[[%s]]", get.formatname(item)))
        :done()
    
    local costNode = mw.html.create("div")
    costNode
        :addClass("lit__leaf-cost")
        :wikitext(_getCostString(item))
        :done()
    
    itemNode
        :node(iconNode)
        :node(labelNode)
   	itemNode:node(costNode)
   	
    
    itemNode:allDone()
    
    return itemNode
end

-- Отрисовывает состав предмета в виде дерева. Рекуррентная функция
local function _buildRecipeTree(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local item = args["item"] or args[1]
    local recipe = get.recipe(item) or nil
    
    local tableNode = mw.html.create("table")
    tableNode:addClass("lol-item-tree"):newline()
    
    local tableRow = mw.html.create("tr")
    tableRow
        :tag("td")
            :attr("colspan", "3")
            :addClass("lit__root")
            :node(_buildRecipeTreeItem(item))
            :done()
        :done()
    
    tableNode:node(tableRow):newline()
    
    if(recipe == nil) then 
        tableNode:allDone()
        return tableNode
    end
    local num_components = 0
    for k in ipairs(recipe) do num_components = num_components + 1 end
    
    local firstComponentRow = mw.html.create("tr")
    firstComponentRow
        :tag("td")
            :attr("rowspan", tostring(num_components * 2 - 1))
            :cssText("width:15px;border-right:1px solid #124b71;")
            :done()
        :tag("td")
            :cssText("height:17px;width:17px;border-bottom:1px solid #124b71;")
            :done()
        :tag("td")
            :attr("rowspan", "2")
            :node(_buildRecipeTree{recipe[1]})
            :done()
        :done()
    tableNode:node(firstComponentRow)
    
    for i, v in ipairs(recipe) do
        if(i ~= 1) then
            tableNode
                :tag("tr")
                    :tag("td")
                        :done()
                    :done()
                :newline()
            local recipeRow = mw.html.create("tr")
            recipeRow
                :tag("td")
                    :cssText("height:17px;width:17px;border-bottom:1px solid #124b71;")
                    :done()
                :tag("td")
                    :attr("rowspan", "2")
                    :node(_buildRecipeTree{v})
                    :done()
                :done()
            tableNode:node(recipeRow):newline()
        end
    end
    tableNode
        :tag("tr")
            :tag("td")
                :done()
            :tag("td")
                :done()
            :done()
    
    tableNode:allDone()
    return tableNode
end

-- Создает блок из предметов, сгруппированных по типу
local function _buildItemRosterBlock(itemList)
    local itemRosterBlock = mw.html.create("div")
    	:addClass("lol-item-roster__group")
    	:done()
    for itemKey, itemValue in lib.pairsByAlphabeticalKeys(itemList) do
        itemRosterBlock
            :tag("div")
    			:addClass("lol-item-roster__item")
                :wikitext(tostring(p.itemIcon
                    {
                        ["item"] = itemKey,
                        ["border"] = "false",
                        ["iconclass"] = "lol-item-roster__item-icon " .. lib.ternary(
                        	itemKey == tostring(mw.title.getCurrentTitle()),
                        	"lol-item-roster__item-icon--selected",
                        	"lol-item-roster__item-icon--default"
                        ),
                        ["size"] = "42px",
                        ["text"] = "*none*"
                    }))
                :done()
            :newline()
    end
    return itemRosterBlock
end

function p.get(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    -- Строки, которые нужно подвергать frame:preprocess
    local PREPROCESSED = {
    	["limit"] = true,
    	["fulllimit"] = true,
    	["req"] = true,
    	["consume"] = true,
    	["consume2"] = true,
    	["pass1"] = true,
    	["pass2"] = true,
    	["pass3"] = true,
    	["pass4"] = true,
    	["pass5"] = true,
    	["pass6"] = true,
    	["aura"] = true,
    	["active"] = true,
    	["mythicdescription"] = true
    }
    
    local item     = args['item'] or args[1]
    local datatype = args['datatype'] or args[2]
    local output   = args['output'] or args[3] or nil
    local omitnil  = args['omitnil'] or true
    
    item = lib.validateItemName(item)
    if(not(get.exists(item))) then
    	return uError("Указанный предмет отсутствует в Модуль:ItemData/data")
	end
    
    if(get[datatype] == nil) then
        return uError('Указанный параметр не существует')
    end
    
    local result = get[datatype](item)
    if output ~= nil and type(result) == "table" then
        if(args["index"]) then
            local i = tonumber(args["index"])
            if(result[i]) then 
                return frame:preprocess(result[i])
            else
                return ""
            end
        end
        if output == "csv" then
            return lib.tbl_concat{result}
        elseif output == "custom" then 
            return frame:preprocess(lib.tbl_concat({result, prepend = args['prepend'], append = args['append'], separator = args['separator'], index = args["index"]}))
        elseif output == "template" then 
            return frame:preprocess(lib.tbl_concat{result, prepend = "{{" .. (args['t_name'] or "Ii") .. "|", append = "}}", separator = args['separator']})
        end
    elseif type(result) == "string" then
    	if(PREPROCESSED[datatype]) then
    		return frame:preprocess(result)
    	else
    		return result
    	end
    elseif result == nil and omitnil then
        return ""
    else
        return result
    end
end

function p.itemIcon(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    	
    local misspellings = mw.loadData("Модуль:ItemData/Misspellings")
    
    args['item'] = args['item'] or args[1]
    
    if(misspellings[args["item"]]) then 
    	args["item"] = misspellings[args["item"]]
    end
    
    local result
    if(get.exists(args['item'])) then
    	args['engname'] = p.get{args['item'], "engname"}
    	result = IL.item(args)
	    return result
    else
    	result = IL.itemOld(args)
    	return result 
    end
end

function p.getRoster(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local removed = args["removed"] or "true"
    
    local starters      = {}
    local consumables   = {}
    local trinkets      = {}
    local distributives = {}
    local basic         = {}
    local boots         = {}
    local epic          = {}
    local legendary     = {}
    local transformation= {}
    local mythic        = {}
    local ornn          = {}
    local championic    = {}
    local unsorted      = {}
    
    for k, v in lib.pairsByAlphabeticalKeys(items) do
        if(get.itemtype(k) == "Начальный") then 
        	starters[k] = v
        elseif(get.itemtype(k) == "Расходуемый") then 
            consumables[k] = v
        elseif(get.itemtype(k) == "Аксессуар") then 
            trinkets[k] = v
        elseif(get.itemtype(k) == "Предоставляемый") then 
            distributives[k] = v 
        elseif(get.itemtype(k) == "Базовый") then
            basic[k] = v
        elseif(get.itemtype(k) == "Обувь") then 
            boots[k] = v
        elseif(get.itemtype(k) == "Эпический") then 
        	epic[k] = v
        elseif(get.itemtype(k) == "Легендарный" and not(_isTransformation(v) or get.itemtype(k) == "Зачарование")) then
            legendary[k] = v
        elseif(get.itemtype(k) == "Легендарный" and _isTransformation(v)) then
        	transformation[k] = v
        	mw.log(k)
        elseif(get.itemtype(k) == "Мифический") then
        	mythic[k] = v
        elseif(get.itemtype(k) == "Орн") then
        	ornn[k] = v
        elseif(get.itemtype(k) == "Чемпионский") then
        	championic[k] = v
        else 
        	table.insert(unsorted, k)
        end
    end
    
    -- Создать блок
    local gridContainer = mw.html.create("div")
    gridContainer:attr("id", "grid"):newline()
    
    local tableContainer = mw.html.create("div")
        :attr("id", "item-grid")
        :cssText("clear:both; display: block; text-align: center;")
        :done()
    
    tableContainer
        :tag("dl")
            :tag("dt")
                :wikitext("Начальные предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(starters))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Расходуемые предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(consumables))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Аксессуары")
                :done()
            :done()
        :node(_buildItemRosterBlock(trinkets))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Предоставляемые предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(distributives))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Базовые предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(basic))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Обувь")
                :done()
            :done()
        :node(_buildItemRosterBlock(boots))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Эпические предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(epic))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Легендарные предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(legendary))
        :node(_buildItemRosterBlock(transformation))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Несравненные предметы Орна")
                :done()
            :done()
        :node(_buildItemRosterBlock(ornn))
        :newline()
        :tag("dl")
            :tag("dt")
                :wikitext("Эксклюзивные предметы")
                :done()
            :done()
        :node(_buildItemRosterBlock(championic))
        :newline()
    
    if(#unsorted > 0) then
        mw.log("Something wrong")
        for _, v in ipairs(unsorted) do mw.log(v) end
    end
    
    if(args["removed"] ~= "false") then
    	local removed = mw.loadData("Модуль:ItemData/data/removed")
    	local removedTable = {}
    	for k, v in lib.pairsByAlphabeticalKeys(removed) do
    		removedTable[k] = v
    	end
        tableContainer
            :tag("dl")
                :tag("dt")
                    :wikitext("Удаленные предметы")
                    :done()
                :done()
            :node(_buildItemRosterBlock(removedTable))
    end
    
    tableContainer:allDone()
    gridContainer:node(tableContainer)
    
    return frame:preprocess(tostring(gridContainer))
end

function p.getCategories(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local item = args['item'] or args[1]
    	
    -- Характеристики в винительном падеже
    -- Список должен быть согласован со всеми существующими/существовавшими статами
    local ACCUSATIVES = {
        ["ap"]         = "силу умений",
        ["armor"]      = "броню",
        ["arpen"]      = "пробивание брони",
        ["lethality"]  = "смертоносность",
        ["lethalityunique"]  = "смертоносность",
        ["ad"]         = "силу атаки",
        ["as"]         = "скорость атаки",
        ["ah"]         = "ускорение умений",
        ["cdr"]        = "сокращение перезарядки",
        ["cdrunique"]  = "сокращение перезарядки",
        ["crit"]       = "шанс критического удара",
        ["critunique"] = "шанс критического удара",
        ["gp10"]       = "золото",
        ["hsp"]        = "эффективность лечения и щитов",
        ["hspunique"]  = "эффективность лечения и щитов",
        ["hp"]         = "здоровье",
        ["hppct"]      = "здоровье",
        ["hp5"]        = "восстановление здоровья",
        ["hp5flat"]    = "восстановление здоровья",
        ["lifesteal"]  = "вампиризм",
        ["mr"]         = "сопротивление магии",
        ["mpen"]       = "магическое пробивание",
        ["mpenflat"]   = "магическое пробивание",
        ["mana"]       = "ману",
        ["mp5"]        = "восстановление маны",
        ["mp5flat"]    = "восстановление маны",
        ["ms"]         = "скорость передвижения",
        ["msunique"]   = "скорость передвижения",
        ["msflat"]     = "скорость передвижения",
        ["pvamp"]      = "физическое вытягивание жизни",
        ["omnivamp"]   = "всестороннее вытягивание жизни",
    }
    
    local categories = {}
    
    -- Категория для чемпионских предметов
    if(p.get{item, "champion"} ~= "") then 
        table.insert(categories, "[[Категория:Эксклюзивные предметы]]")
    end
    
    -- Категории по типу предмета (первому из таблицы)
    local itemType = p.get{item, "itemtype"}
    if(itemType == "Зачарование") then
        table.insert(categories, "[[Категория:Зачарования]]")
    elseif(itemType == "Аксессуар") then
        table.insert(categories, "[[Категория:Аксессуары]]")
    elseif(itemType == "Обувь") then
        table.insert(categories, "[[Категория:Обувь]]")
    elseif(itemType == "Орн") then
        table.insert(categories, "[[Категория:Несравненные предметы Орна]]")
    else
        table.insert(categories, "[[Категория:" .. mw.ustring.sub(itemType, 1, -2) .. "е предметы]]")
    end
    
    local itemStats = p.get{item, "stats"}
    -- Постановка категорий по имеющимся характеристикам
    if(type(itemStats) ~= "string") then
	    for k, v in pairs(itemStats) do
	    	if(ACCUSATIVES[k]) then
	    		table.insert(categories, mw.ustring.format("[[Категория:Предметы на %s]]", ACCUSATIVES[k]))
	    	end
	    end
	end
    
    if(get.active(item) or nil ~= nil) then
        table.insert(categories, "[[Категория:Активируемые предметы]]")
    end
    
    return table.concat(categories)
end

-- Генерирует стандартное вступление к статье о предмете
function p.getIntroduction(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local item = args['item'] or args[1]
    local append = args['append'] or args[2]
    
    if(not(p.get{item, "exists"})) then
    	return uError("Указанный предмет не найден в ItemData/data или ItemData/data/removed", "LuaError")
    end
    
    local introductionText = ""
    
    -- X - это ...
    introductionText = introductionText .. mw.ustring.format("'''%s''' {{англ|%s}} — это ",
        item,
        p.get{item, "engname"})
     
    -- Предмет/Зачарование/Аксессуар  
    local itemType = mw.ustring.lower(p.get{item, "itemtype"})
    
    -- Список типов предметов, для которых строится фраза не "...это [какой-то] предмет..."
    local ITEMTYPE_EXCEPTIONS = {
    	["аксессуар"] = true,
    	["зачарование"] = true,
    	["обувь"] = true,
    	["орн"] = true
    }
    if(ITEMTYPE_EXCEPTIONS[itemType]) then
    	if(itemType == "орн") then
    		introductionText = introductionText .. " один из несравненных предметов {{ci|Орн|Орна}} в ''[[League of Legends]]''"
    	elseif(itemType == "обувь") then
    		introductionText = introductionText .. " вид обуви в ''[[League of Legends]]''"
    	else
    		introductionText = introductionText .. itemType .. " в ''[[League of Legends]]''"
    	end
    else
        introductionText = introductionText .. itemType .. " предмет в ''[[League of Legends]]''"
    end
    
    if(p.get{item, "champion"} ~= "") then 
        introductionText = introductionText .. mw.ustring.format(" для чемпиона {{ci|%s}}", p.get{item, "champion"})
    end
    
    if(p.get{item, "maps"} ~= nil) then
        if(not(p.get{item, "HA"}) and not(p.get{item, "NB"})) then
            introductionText = introductionText .. ". Доступен исключительно в {{tip|Ущелье призывателей}}"
        elseif(not(p.get{item, "SR"}) and not(p.get{item, "NB"})) then
            introductionText = introductionText .. ". Доступен исключительно в {{tip|Воющая бездна|Воющей бездне}}"
        elseif(not(p.get{item, "SR"}) and not(p.get{item, "HA"})) then
            introductionText = introductionText .. ". Доступен исключительно в режиме {{tip|Штурм Нексуса}}"
        end
    end
    introductionText = introductionText .. "."
    if(append ~= nil) then introductionText = introductionText .. " " .. append end
    
    return frame:preprocess(introductionText)
end

-- Выводит состав предмета в виде строчной суммы из иконок
function p.getInfoboxRecipe(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local item = args['item'] or args[1]
    local size = args['size'] or "32"
    
    -- Если такого предмета нет
    if(not(p.get{item, "exists"})) then
        return uError('Указанный предмет не найден')
    end
    
    -- Если у предмета нет состава
    if(not(p.get{item, "recipe"})) then return end
    
    local recipeHtml = ""
    local itemRecipe = lib.cloneTable(p.get{item, "recipe"})
    if(itemRecipe == "") then return end
    for i, component in ipairs(itemRecipe) do
        recipeHtml = recipeHtml .. tostring(p.itemIcon{
            ["item"] = component,
            ["size"] = size,
            ["text"] = "*none*"
        })
        if(i < #itemRecipe) then
            recipeHtml = recipeHtml .. "{{plus}}"
        end
    end
    recipeHtml = recipeHtml .. "<br />{{g|" .. (get.comb(item) or 0) .. "}}"
    
    return frame:preprocess(recipeHtml)
end

function p.getInfoboxBuilds(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local item = args['item'] or args[1]
    
    local builds = p.get{item, "builds"}
    if(builds == nil or builds == "") then
        return ""
    end
    
    table.sort(builds)
    
    return frame:preprocess(lib.tbl_concat({
        ["tbl"] = builds,
        ["prepend"] = "'''{{Ii|",
        ["append"] = "|size=32|labelstyle=font-size:14px;}}'''",
        ["separator"] = "<br/>"
    }))
end

-- Выводит список возможных улучшений предмета для показа в статье
function p.getPageBuilds(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local item = args['item'] or args[1]
    if(item == nil) then uError('Не указан предмет', 'LuaError') end
    
    local builds = items[item].builds
    if(builds == nil) then return end

    local s = ""
    for i, v in ipairs(builds) do
        s = s .. ": '''{{Ii|" .. v .. "|size=32|labelstyle=font-size:14px;}}'''\n"
    end
    return frame:preprocess(s)
end

-- Выводит иконки предметов, в которые можно превратить данный. Используется во всплывающей подсказке
function p.getTooltipBuilds(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local item = args['item'] or args[1]
    if(item == nil) then uError('Не указан предмет', 'LuaError') end
    
    local builds = get.builds(item)
    
    if(builds == nil) then return end
    
    local buildsTable = {}
    for i, v in ipairs(builds) do
        if(i == 10) then table.insert(buildsTable, "<br />") end
        table.insert(buildsTable, tostring(p.itemIcon{
            ['item'] = v,
            ['text'] = "*none*"
        }))
    end
    return table.concat(buildsTable, "")
end

-- Обертка для _buildRecipeTree, чтобы проносить итоговый результат через frame:preprocess
function p.getRecipeTree(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    return frame:preprocess(tostring(_buildRecipeTree(args)))
end

-- Обертка для p.costAnalysis, чтобы обойти баг с отсутствием frame
function p.getCostAnalysis(frame)
    return frame:preprocess(tostring(p.costAnalysis(frame)))
end

-- Производит анализ стоимости характеристик
function p.costAnalysis(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local item = args['item'] or args[1]
    local output = args['output'] or args[2] or 'default'
    local sum = 0
    
    if(not(p.get{item, "exists"})) then
        uError('Предмет не найден в Модуль:ItemData/data или ItemData/data/removed', 'LuaError')
    end
    
    local STANDARDS = mw.loadData("Модуль:ItemData/Standards")
    local PERCENTS  = mw.loadData("Модуль:ItemData/Percents")
    
    -- Таблица родительных падежей характеристик
    local GENITIVES = {
        ["ap"]         = "силы умений",
        ["armor"]      = "брони",
        ["arpen"]      = "пробивания брони",
        ["lethality"]  = "смертоносности",
        ["lethalityunique"]  = "смертоносности",
        ["ad"]         = "силы атаки",
        ["as"]         = "скорости атаки",
        ["ah"]         = "ускорения умений",
        ["cdr"]        = "сокращения перезарядки",
        ["cdrunique"]  = "сокращения перезарядки",
        ["crit"]       = "шанса критического удара",
        ["critunique"] = "шанса критического удара",
        ["gp10"]       = "золота",
        ["hsp"]        = "эффективности лечения и щитов",
        ["hspunique"]  = "эффективности лечения и щитов",
        ["hp"]         = "здоровья",
        ["hppct"]      = "здоровья",
        ["hp5"]        = "восстановления здоровья",
        ["hp5flat"]    = "восстановления здоровья",
        ["lifesteal"]  = "вампиризма",
        ["mr"]         = "сопротивления магии",
        ["mpen"]       = "магического пробивания",
        ["mpenflat"]   = "магического пробивания",
        ["mana"]       = "маны",
        ["mp5"]        = "восстановления маны",
        ["mp5flat"]    = "восстановления маны",
        ["ms"]         = "скорости передвижения",
        ["msunique"]   = "скорости передвижения",
        ["msflat"]     = "скорости передвижения",
        ["ohdmg"]      = "урона при попадании",
        ["pvamp"]      = "физического вытягивание жизни",
        ["omnivamp"]   = "всестороннего вытягивания жизни",
    }
    
    local statsTable = lib.cloneTable(p.get{item, "stats"})
    
    -- Заменяеет значения statsTable на переданные в invoke (если есть)
    for k, v in pairs(STANDARDS) do
    	if(args[k] ~= nil) then
    		statsTable[k] = args[k]
    	end
    end
    
    for k, v in pairs(statsTable) do
        if(k ~= "gp10" and k ~= "spec") then
        	if(STANDARDS[k]) then
        		sum = sum + STANDARDS[k]["val"] * v
        	end
        end
    end
    -- Если нужна только сумма
    if(args['output'] == "sum only") then return tostring(sum) end
    
    local tableNode = mw.html.create("table")
    tableNode
    	:addClass("wikitable")
    	:attr("width", "60%")
    	:tag("tr")
    		:tag("th")
    			:wikitext("Характеристика")
    			:attr("width", "35%")
    			:done()
    		:tag("th")
    			:wikitext("Стоимость")
    			:done()
    		:tag("th")
    			:wikitext("Заметка")
    			:done()
    		:done()
    	:done()
    
    local isEmptyTable = true
    for k, v in pairs(statsTable) do
    	if(k ~= "gp10" and k ~= "spec" and k ~= "spec2") then
    		if(STANDARDS[k]) then
    			isEmptyTable = false
    			tableNode
    				:tag("tr")
    					:tag("td")
    						:wikitext(mw.ustring.format(
    							"{{as|%d%s %s}}",
    							v,
    							lib.ternary(PERCENTS[k], "%", ""),
    							GENITIVES[k]
    							))
    						:done()
    					:tag("td")
    						:wikitext(mw.ustring.format("{{g|%s}}", tostring(_round2(STANDARDS[k]["val"] * v))))
    						:done()
    					:tag("td")
    						:wikitext(mw.ustring.format(
    							"Из расчета {{g|%s}} за {{as|1%s %s}}.",
    							tostring(_round2(STANDARDS[k]["val"])),
    							lib.ternary(PERCENTS[k], "%", ""),
    							GENITIVES[k]
    							))
    					:done()
    				:done()
    			:done()
    		end
    	end
    end
    
    if(isEmptyTable) then
    	tableNode = ""
    else
    	tableNode
    		:tag("tr")
    			:tag("th")
    				:attr("colspan", "3")
    				:wikitext(mw.ustring.format("Итоговая стоимость: {{G|%0.2f}}", _round2(sum)))
    				:done()
    			:done()
    		:done()
    end
    
    return tableNode
end

function p.getStandard(frame)
	local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	local statistic = args["statistic"] or args[1]
	local standards = mw.loadData("Модуль:ItemData/Standards")
	if(standards[statistic]~= nil) then
		return standards[statistic]["val"]
	else
		return 0
	end
end

-- Выводит процентное соотношение выгодности покупки
-- Повторяет функции шаблона Выгодность
function p.getGoldEfficiency(frame)
	local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	
	local item = args["item"] or args[1]
	local itemCost = p.get{item, "buy"}
	local statCost = args[2] or p.costAnalysis{["item"] = item, ["output"] = "sum only"}
	
	local efficiency = _round2(statCost / itemCost * 100)
	local costDiff = _round2(statCost - itemCost)
	
	return frame:preprocess(mw.ustring.format(
		"{{tt|%s%%|%s%s золота}}",
		tostring(efficiency),
		lib.ternary(costDiff > 0, "+", ""),
		tostring(costDiff)
		))
end

function p.getItemTableByStat(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local stat = args['stat'] or args[1]
    if(stat == nil) then 
        return uError('Характеристика не указана', 'LuaError')
    end
    
    local tableNode = mw.html.create('table')
    tableNode
        :addClass('wikitable sortable')
        :cssText("width:93%;margin-left:5%;text-align:center")
        :newline()
        :tag('tr')
            :tag('th')
                :wikitext('Предмет')
                :done()
            :tag('th')
                :attr('data-sort-type', 'number')
                :wikitext('Цена')
                :done()
            :tag('th')
                :wikitext('Количество')
                :done()
            :tag('th')
                :wikitext('Доступность')
                :done()
            :done()
        :newline()
        :done()
    
    -- [1] - название, [2] - размер характеристики, [3] - является уникальной
    local filteredItems = {}
    local percentStats = mw.loadData("Модуль:ItemData/Percents")
    
    
    for k, v in lib.pairsByAlphabeticalKeys(items) do
        if(v["stats"] and v["stats"][stat] ~= nil) then 
            table.insert(filteredItems, {k, v.stats[stat], false}) 
        elseif(v["stats"] and v["stats"][stat .. "unique"] ~= nil) then
            table.insert(filteredItems, {k, v["stats"][stat .. "unique"], true})
        end
    end
    
    for i, v in ipairs(filteredItems) do
        local maps = p.get{v[1], "maps"}
        if(maps["sr"] and not(maps["ha"] or maps["nb"])) then maps = "{{tip|Ущелье призывателей}}"
        elseif(maps["ha"] and not(maps["sr"] or maps["nb"])) then maps = "{{tip|Воющая бездна}}"
        elseif(maps["nb"] and not(maps["sr"] or maps["ha"])) then maps = "{{tip|Штурм Нексуса}}"
        else maps = "Любая карта" end
        
        tableNode
            :tag('tr')
                :tag('td')
                    :wikitext(tostring(p.itemIcon{["item"] = v[1]}))
                    :done()
                :tag('td')
                    :wikitext(mw.ustring.format(
                        "{{g|%s}}",
                        p.get{v[1], "buy"}
                        ))
                    :done()
                :tag('td')
                    :wikitext(mw.ustring.format("%s%s%s",
                        v[2],
                        lib.ternary(percentStats[stat], "%", ""),
                        lib.ternary(v[3], " (Уникально)", "")))
                    :done()
                :tag('td')
                    :wikitext(maps)
                    :done()
                :done()
            :newline()
            :done()
    	:allDone()
    end
    
    return frame:preprocess(tostring(tableNode))
end

-- Создает старинный вариант информации об предмете в виде Data-шаблона
-- Используется для предметов, выводимых в архив
function p.generateLegacyPage(frame)
	 local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	 local itemName = args['item'] or args[1]
	 local itemData = items[itemName]
	 if(itemData == nil) then
	 	return uError("Предмет не найден в ItemData/data", "LuaError")
	 end
	 
	 local result = {"{{{{{1<noinclude>|Item data</noinclude>}}}|" .. itemName .."|{{{2|}}}|{{{3|}}}|{{{4|}}}|{{{5|}}}"}
	 
	 table.insert(result, "|engname = " .. itemData.engname)
	 table.insert(result, "|code = " .. itemData.id)
	 table.insert(result, "|tier = Tier " .. itemData.tier)
	 
	 if(itemData.itemtype) then table.insert(result, "|type = " .. itemData.itemtype) end
	 if(itemData.champion) then table.insert(result, "|champion = " .. itemData.champion) end
	 if(itemData.maps) then table.insert(result, "|maps = " .. itemData.maps) end
	 if(itemData.caption) then table.insert(result, "|caption = " .. itemData.caption) end
	 if(itemData.consume) then table.insert(result, "|consume = " .. itemData.consume) end
	 if(itemData.consume2) then table.insert(result, "|consume2 = " .. itemData.consume2) end
	 
	 -- Характеристики
	 if(itemData.stats) then
		for k, v in pairs(itemData.stats) do
			local key = k
			if(k == "hp") then key = "health" end
			table.insert(result, "|" .. key .. " = " .. tostring(v))
		end
	end
	
	if(itemData.passive) then
		for i, v in ipairs(itemData.passive) do
			if(i == 1) then 
				table.insert(result, "|pass = " .. v)
			else 
				table.insert(result, "|pass" .. i .. " = " .. v)
			end
		end
	end
	
	if(itemData.aura) then
		for i, v in ipairs(itemData.aura) do
			if(i == 1) then 
				table.insert(result, "|aura = " .. v)
			else 
				table.insert(result, "|aura" .. i .. " = " .. v)
			end
		end
	end
	
	if(itemData.active) then table.insert(result, "|act = " .. itemData.active) end
	if(itemData.activecd) then table.insert(result, "|act_cd = " .. itemData.activecd) end
	
	if(itemData.recipe) then
		-- Костыль: таблица из loadData не поддерживает конкатенацию
		local recipeText = ""
		for i, v in pairs(itemData.recipe) do
			recipeText = recipeText .. v .. ","
		end
		table.insert(result, "|recipe = " .. mw.ustring.sub(recipeText, 0, -2))
	end
	if(itemData.builds) then
		-- Костыль: таблица из loadData не поддерживает конкатенацию
		local buildsText = ""
		for i, v in pairs(itemData.builds) do
			buildsText = buildsText .. v .. ","
		end
		table.insert(result, "|builds = " .. mw.ustring.sub(buildsText, 0, -2))
	end
	
	if(itemData.buy) then 
		table.insert(result, "|buy = " .. tostring(itemData.buy)) 
		table.insert(result, "|sell = " .. p.get{itemName, "sell"}) 
	end
	if(itemData.comb) then table.insert(result, "|comb = " .. tostring(itemData.comb)) end
	
	if(itemData.limit) then table.insert(result, "|limit = " .. itemData.limit) end
	if(itemData.req) then table.insert(result, "|req = " .. itemData.req) end
	
	table.insert(result, "|removed = true")
	table.insert(result, "}}")
	return table.concat(result, "\n")
end

-- Реструктурирует старый вариант ItemData/data (ItemData/data/old) в новый в соответствии с обновленной спецификацией полей
function p.restructureItemData()
	local items = mw.loadData("Модуль:ItemData/data/removed")
	local result = {}
	local tab = "    " -- 4 пробела
	for itemKey, itemValue in lib.pairsByAlphabeticalKeys(items) do
		--start 
		local itemInstance = { mw.ustring.format("[\"%s\"] = {", itemKey) }
		
		--id
		if(itemValue.id ~= nil) then
			table.insert(itemInstance, mw.ustring.format("%s[\"id\"] = %d,", tab, itemValue.id))
		end
		
		-- engname
		table.insert(itemInstance, mw.ustring.format("%s[\"engname\"] = \"%s\",", tab, itemValue.engname))
		
		-- champion
		if(itemValue.champion ~= nil) then
			table.insert(itemInstance, mw.ustring.format("%s[\"champion\"] = \"%s\",", tab, itemValue.champion))
		end
		
		-- caption
		if(itemValue.caption ~= nil) then
			table.insert(itemInstance, mw.ustring.format("%s[\"caption\"] = \"%s\",", tab, itemValue.caption))
		end
		
		-- tier
		table.insert(itemInstance, mw.ustring.format("%s[\"tier\"] = %d,", tab, itemValue.tier))
		
		-- itemtype
		local itemtypeTable = { tab .. "[\"itemtype\"] = {" }
		if(itemValue.champion ~= nil) then
			table.insert(itemtypeTable, "\"Чемпионский\",")
		elseif(itemValue.limit ~= nil and mw.ustring.find(itemValue.limit, "Может быть создано только") ~= nil) then
			table.insert(itemtypeTable, "\"Орн\",")
		else
			if(itemValue.itemtype) then
				for i, v in ipairs(itemValue.itemtype) do
					table.insert(itemtypeTable, mw.ustring.format("\"%s\",", v))
				end
			else
		        if itemValue.tier == 1 then 
		            table.insert(itemtypeTable, "\"Базовый\"")
		        elseif itemValue.builds then 
		            table.insert(itemtypeTable, "\"Эпический\"")
		        else 
		            table.insert(itemtypeTable, "\"Легендарный\"")
		        end
			end
		end
		table.insert(itemtypeTable, "},")
		table.insert(itemInstance, table.concat(itemtypeTable, " "))
		
		--maps - доступные карты
		local mapsTable = {"[\"maps\"] = {"}
		if(itemValue.maps == nil) then
			table.insert(mapsTable, tab .. tab .. "[\"sr\"] = true,")
			table.insert(mapsTable, tab .. tab .. "[\"ha\"] = true,")
			table.insert(mapsTable, tab .. tab .. "[\"nb\"] = true")
		else
			if(p.get{itemKey, "SR"}) then
				table.insert(mapsTable, tab .. tab .. "[\"sr\"] = true,")
			else
				table.insert(mapsTable, tab .. tab .. "[\"sr\"] = false,")
			end
			if(p.get{itemKey, "HA"}) then
				table.insert(mapsTable, tab .. tab .. "[\"ha\"] = true,")
			else
				table.insert(mapsTable, tab .. tab .. "[\"ha\"] = false,")
			end
			table.insert(mapsTable, tab .. tab .. "[\"nb\"] = false")
		end
		table.insert(mapsTable, tab .. "},")
		table.insert(itemInstance, tab .. table.concat(mapsTable, "\r\n"))
		
		-- req
		if(itemValue.req) then
			table.insert(itemInstance, mw.ustring.format("%s[\"req\"] = \"%s\",", tab, itemValue.req))
		end
		
		-- limit
		if(itemValue.limit) then
			table.insert(itemInstance, mw.ustring.format("%s[\"limit\"] = \"%s\",", tab, itemValue.limit))
		end
		
		-- stats
		if(itemValue.stats.noe ~= true) then
			local statsTable = { tab .. "[\"stats\"] = {" }
			for statKey, statValue in pairs(itemValue.stats) do
				if(type(statValue) == "number") then
					table.insert(statsTable, mw.ustring.format(
						"%s[\"%s\"] = %d,",
						string.rep(tab, 2),
						statKey,
						statValue))
				elseif(statValue == "string") then
					table.insert(statsTable, mw.ustring.format(
						"%s[\"%s\"] = \"%s\",",
						string.rep(tab, 2),
						statKey,
						statValue))
				end
			end
			table.insert(statsTable, tab .. "},")
			table.insert(itemInstance, table.concat(statsTable, "\r\n"))
		end
		
		-- passives
		if(itemValue.passive) then
			for passiveIndex, passiveValue in ipairs(itemValue.passive) do
				local passiveTable = { tab .. mw.ustring.format("[\"pass%d\"] = {", passiveIndex) }
				local passiveText = passiveValue
				--Уникальные активные эффекты с именем
				if(mw.ustring.match(passiveText, "{{[Uu]nique|[%s%d%a]-|") ~= nil) then
					-- Захватить название эффекта
					table.insert(passiveTable, mw.ustring.format("%s[\"name\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						mw.ustring.match(passiveText, "|([%s%d%a]-)|")
					))
					
					table.insert(passiveTable, mw.ustring.format("%s[\"unique\"] = true,",
						mw.ustring.rep(tab, 2)
					))
					-- Захватить описание
					table.insert(passiveTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						mw.ustring.match(passiveText, "{{[Uu]nique|[%s%d%a]-|(.*)}}")
					))
				-- Уникальные активные эффекты без имени
				elseif(mw.ustring.match(passiveText, "{{[Uu]nique|*") ~= nil) then
					table.insert(passiveTable, mw.ustring.format("%s[\"unique\"] = true,",
						mw.ustring.rep(tab, 2)
					))
					table.insert(passiveTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						mw.ustring.match(passiveText, "{{[Uu]nique|(.*)}}")
					))
				else
					-- Захватить описание
					table.insert(passiveTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						passiveText
					))
				end
				
				table.insert(passiveTable, tab .. "},")
				table.insert(itemInstance, table.concat(passiveTable, "\r\n"))	
			end
		end
		
		-- active
		if(itemValue.active) then
			local activeTable = { tab .. "[\"active\"] = {" }
			local activeText = itemValue.active
			--Уникальные активные эффекты с именем
			if(mw.ustring.match(activeText, "{{[Uu]nique|[%s%d%a]-|") ~= nil) then
				-- Захватить название эффекта
				table.insert(activeTable, mw.ustring.format("%s[\"name\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					mw.ustring.match(activeText, "|(.-)|")
				))
				
				table.insert(activeTable, mw.ustring.format("%s[\"unique\"] = true,",
					mw.ustring.rep(tab, 2)
				))
				-- Захватить описание
				table.insert(activeTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					mw.ustring.match(activeText, "{{[Uu]nique|[%s%d%a]-|(.*)}}")
				))
				if(itemValue.activecd) then
					table.insert(activeTable, mw.ustring.format("%s[\"cd\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						tostring(itemValue.activecd)
				))
				end
			-- Уникальные активные эффекты без имени
			elseif(mw.ustring.match(activeText, "{{[Uu]nique|.*") ~= nil) then
				table.insert(activeTable, mw.ustring.format("%s[\"unique\"] = true,",
					mw.ustring.rep(tab, 2)
				))
				table.insert(activeTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					mw.ustring.match(activeText, "{{[Uu]nique|(.*)}}")
				))
				if(itemValue.activecd) then
					table.insert(activeTable, mw.ustring.format("%s[\"cd\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						tostring(itemValue.activecd)
				))
				end
			else
				-- Захватить описание
				table.insert(activeTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					activeText
				))
				if(itemValue.activecd) then
					table.insert(activeTable, mw.ustring.format("%s[\"cd\"] = \"%s\",",
						mw.ustring.rep(tab, 2),
						tostring(itemValue.activecd)
				))
				end
			end
			
			table.insert(activeTable, tab .. "},")
			table.insert(itemInstance, table.concat(activeTable, "\r\n"))
		end
		
		-- consume
		if(itemValue.consume) then
			local consumeTable = { tab .. "[\"consume\"] = {" }
			local consumeText = itemValue.consume
			--Уникальные эффекты при использовании с именем
			if(mw.ustring.match(consumeText, "{{[Uu]nique|[%s%d%a]-|") ~= nil) then
				-- Захватить название эффекта
				table.insert(consumeTable, mw.ustring.format("%s[\"name\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					mw.ustring.match(consumeText, "|(.-)|")
				))
				
				table.insert(consumeTable, mw.ustring.format("%s[\"unique\"] = true,",
					mw.ustring.rep(tab, 2)
				))
				-- Захватить описание
				table.insert(consumeTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					mw.ustring.match(consumeText, "{{[Uu]nique|[%s%d%a]-|(.*)}}")
				))
			-- Уникальные эффекты при использовании без имени
			elseif(mw.ustring.match(consumeText, "{{[Uu]nique|.*") ~= nil) then
				table.insert(consumeTable, mw.ustring.format("%s[\"unique\"] = true,",
					mw.ustring.rep(tab, 2)
				))
				table.insert(consumeTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					mw.ustring.match(consumeText, "{{[Uu]nique|(.*)}}")
				))
			else
				-- Захватить описание
				table.insert(consumeTable, mw.ustring.format("%s[\"description\"] = \"%s\",",
					mw.ustring.rep(tab, 2),
					consumeText
				))
			end
			
			table.insert(consumeTable, tab .. "},")
			table.insert(itemInstance, table.concat(consumeTable, "\r\n"))
		end
		
		-- recipe
		if(itemValue.recipe) then
			local recipeTable = { tab .. "[\"recipe\"] = {"}
			local recipeClone = lib.cloneTable(itemValue.recipe)
			local recipeText
			for recipeIndex, recipeValue in ipairs(recipeClone) do
				table.insert(recipeTable, mw.ustring.format("\"%s\",", recipeValue))
			end
			if(#recipeClone > 2) then
				recipeText = table.concat(recipeTable, "\r\n" .. mw.ustring.rep(tab, 2))
				recipeText = recipeText .. "\r\n" .. tab .. "},"
			else
				recipeText = table.concat(recipeTable, " ") .. "},"
			end
			table.insert(itemInstance, recipeText)
		end
		
		--builds
		if(itemValue.builds) then
			local buildsTable = { tab .. "[\"builds\"] = {"}
			local buildsClone = lib.cloneTable(itemValue.builds)
			local buildsText
			for buildsIndex, buildsValue in ipairs(buildsClone) do
				table.insert(buildsTable, mw.ustring.format("\"%s\",", buildsValue))
			end
			if(#buildsClone > 2) then
				buildsText = table.concat(buildsTable, "\r\n" .. mw.ustring.rep(tab, 2))
				buildsText = buildsText .. "\r\n" .. tab .. "},"
			else
				buildsText = table.concat(buildsTable, " ") .. "},"
			end
			table.insert(itemInstance, buildsText)
		end
		
		-- buy
		if(itemValue.buy) then
			table.insert(itemInstance, mw.ustring.format("%s[\"buy\"] = %d,", tab, itemValue.buy))
		end
		
		-- comb
		if(itemValue.comb) then
			table.insert(itemInstance, mw.ustring.format("%s[\"comb\"] = %d,", tab, itemValue.comb))
		end
		
		-- sell
		if(itemValue.sell) then
			table.insert(itemInstance, tab .. "[\"sellratio\"] = 0.4,")
		end
		
		table.insert(itemInstance, "},")
		table.insert(result, table.concat(itemInstance, "\r\n"))
	end
	return table.concat(result, "\r\n")
end

function p.createItemCheatSheet()
	local tableNode = mw.html.create("table")
	tableNode
		:addClass("wikitable sortable")
		:attr("width", "100%")
		:newline()
		:done()
	
	tableNode
		:tag("tr")
			:tag("th")
				:attr("width", "40%")
				:wikitext("Предмет")
				:done()
			:tag("th")
				:attr("width", "40%")
				:wikitext("Английское название")
				:done()
			:tag("th")
				:wikitext("ID")
				:attr("data-sort-type", "number")
				:done()
			:done()
		:done()
	
	for k, v in lib.pairsByAlphabeticalKeys(items) do
		local rowNode = mw.html.create("tr")
		rowNode
			:tag("td")
				:wikitext(k)
				:newline()
				:done()
			:tag("td")
				:wikitext(v.engname)
				:newline()
				:done()
			:tag("td")
				:wikitext(v.id)
				:newline()
				:done()
			:newline()
			:done()
		
		tableNode
			:node(rowNode)
			:newline()
			:done()
	end
	return tostring(tableNode)
end

function p.createOldItemCheatSheet()
	local tableNode = mw.html.create("table")
	tableNode
		:addClass("wikitable sortable")
		:attr("width", "100%")
		:newline()
		:done()
	
	tableNode
		:tag("tr")
			:tag("th")
				:attr("width", "40%")
				:wikitext("Предмет")
				:done()
			:tag("th")
				:attr("width", "40%")
				:wikitext("Английское название")
				:done()
			:tag("th")
				:wikitext("ID")
				:attr("data-sort-type", "number")
				:done()
			:done()
		:done()
	
	local oldItems = mw.loadData("Модуль:ItemData/data/removed")
	
	for k, v in lib.pairsByAlphabeticalKeys(oldItems) do
		local rowNode = mw.html.create("tr")
		rowNode
			:tag("td")
				:wikitext(k)
				:newline()
				:done()
			:tag("td")
				:wikitext(v.engname)
				:newline()
				:done()
			:tag("td")
				:wikitext(v.id)
				:newline()
				:done()
			:newline()
			:done()
		
		tableNode
			:node(rowNode)
			:newline()
			:done()
	end
	return tostring(tableNode)
end

-- Данная функция сверяет целостность полей состава предметов и списка улучшений у их компонентов
function p.checkItemDataConsistency()
	local inconsistentRecipe = {}
	local inconsistentUpgrade = {}
	-- Проигнорировать предоставляемые предметы
	IGNORED_ITEMS = {
		["Сломанный секундомер"] = true,
		["Заведенный секундомер"] = true,
		["Ботинки с капелькой магии"] = true
	}
	
	for k, v in pairs(items) do
		repeat
			if(IGNORED_ITEMS[k] == true) then
				break
			end
			local itemRecipe = v["recipe"]
			if(itemRecipe ~= nil) then
				for _, recipeComponent in ipairs(itemRecipe) do
					if(items[recipeComponent] == nil) then
						table.insert(inconsistentUpgrade, {recipeComponent, "(Не найдено)"})
						break
					end
					local itemUpgrades = items[recipeComponent]["builds"]
					if(itemUpgrades ~= nil) then
						if(lib.find(itemUpgrades, k) < 0) then
							table.insert(inconsistentUpgrade, {k, recipeComponent})
						end
					end
				end
			end
			
			local itemBuilds = v["builds"]
			if(v["builds"] ~= nil) then
				for _, upgrade in ipairs(itemBuilds) do
					if(items[upgrade] == nil) then
						table.insert(inconsistentRecipe, {upgrade, "(Не найдено)"})
						break
					end
					local recipe = items[upgrade]["recipe"]
					if(recipe ~= nil) then
						if(lib.find(recipe, k) < 0) then
							table.insert(inconsistentRecipe, {k, upgrade})
						end
					end
				end
			end
		until true
	end
	
	local result = {""}
	for _, v in ipairs(inconsistentRecipe) do
		table.insert(result, mw.ustring.format(
			"Несоответствие: '''%s''' указан как улучшение '''%s''', но тот в составе '''%s''' он не указан",
			v[2],
			v[1],
			v[2])
		)
	end
	for _, v in ipairs(inconsistentUpgrade) do
		table.insert(result, mw.ustring.format(
			"Несоответствие: '''%s''' указан в составе '''%s''', но тот не указан как улучшение у '''%s'''",
			v[2],
			v[1],
			v[2])
		)
	end
	if(#result == 1) then result = {"Ошибок не обнаружено"} end
	return table.concat(result, "<br />")
end

return p
-- </pre>
-- [[Категория:Lua]]