Модуль:Инвентарный слот2

Данная страница входит в проект Обновление модулей.

Черновик разрабатываемой новой версии модуля Инвентарный слот.

------------------------------------------------------------------- --- Модуль для отображения инвентарных слотов в Minecraft Wiki. --- Форк [[:en:Special:Permalink/1326065]] ------------------------------------------------------------------- -- <nowiki> local p = {} local slot_utils = require("Модуль:Инвентарный слот2/Utils") local i18n = { filename = 'Invicon $1', legacyFilename = 'Grid $1.png', modLink = '$1/$2', moduleAliases = [[Модуль:Инвентарный слот2/Псевдонимы]], moduleInvData = [[Модуль:ИнвСпрайт]], moduleRandom = [[Модуль:Случайные числа]], moduleSprite = [[Модуль:Спрайт]], moduleProcessArgs = [[Модуль:ProcessArgs]], -- Список особых префиксов, которые должны быть обработаны другими модулями -- (в том числе убраны из ссылок) prefixes = { any = {'Любой', 'Любая', 'Любое', 'Любые'}, matching = {'Соответствующий', 'Соответствующая', 'Соответствующее', 'Соответствующие'}, damaged = {'Повреждённый', 'Повреждённая', 'Повреждённое', 'Повреждённые'}, }, -- Проверяет строку name на соответствие какой-либо из строк, заданных выше -- в prefixes с ключом prefixName. Возвращает true/false. matchesPrefix = function(name, prefixName) local prefixData = p.i18n.prefixes[prefixName] assert(prefixData, ("недопустимый ID префикса: %s"):format(prefixName)) for _, prefix in ipairs(prefixData) do if name:find(prefix) == 1 then return true end end return false end, stripPrefix = function(name, prefixName) local prefixData = p.i18n.prefixes[prefixName] assert(prefixData, ("недопустимый ID префикса: %s"):format(prefixName)) for _, prefix in ipairs(prefixData) do local name, matched = name:gsub("^" .. prefix .. " ", "") if matched == 1 then return name end end return name end, suffixes = { be = 'BE', lce = 'LCE', }, templateFileUrl = 'FileUrl', moduleModData = "Модуль:ИнвСпрайт/$1" } p.i18n = i18n local random = require( i18n.moduleRandom ).random local sprite = require( i18n.moduleSprite ).sprite local processArgs = require( i18n.moduleProcessArgs ) local modNameAliases = mw.loadData("Модуль:Модификации") local aliases = mw.loadData( i18n.moduleAliases ) local ids = mw.loadData( i18n.moduleInvData )["IDы"] local pageName = mw.title.getCurrentTitle().text local vanillaPrefixes = { v = 1, vanilla = 1, mc = 1, minecraft = 1 } function p.loadModData(mod) return slot_utils.loadMaybeData(("Модуль:ИнвСпрайт/%s"):format(mod)) end -- Создаёт HTML-код предмета. -- -- frame: структура фрейма, содержащая данные фрейма. -- i: индекс фрейма или подфрейма -- args: обработанные аргументы модуля local function makeItem( frame, i, args ) local item = mw.html.create( 'span' ) :addClass( 'invslot-item' ) :addClass( args.imageClass ) -- no-op when given nil :cssText( args.imageStyle ) -- no-op when given nil if frame.name == '' then return item end local category local title = frame.title or ((args.title or ''):gsub("^%s+", ""):gsub("%s+$", "")) local mod = frame.mod local name = frame.name or '' local num = frame.num local description = frame.text local img, idData, modData, enName, imageOverride if mod then local modResults = p.resolveModData(mod, name) modData = modResults.modData idData = modResults.idData img = modResults.img enName = modResults.enName imageOverride = modResults.imageOverride elseif ids[name] then idData = ids[name] elseif name:match( '\.gif$' ) or name:match( '\.png$' ) then img = i18n.filename:gsub( '%$1', name ) -- Remove file extension from name name = name:sub( 0, -5 ) else img = i18n.legacyFilename:gsub( '%$1', name ) end local link = args.link or '' if link == '' then if mod then link = i18n.modLink:gsub( '%$1', mod ):gsub( '%$2', name ) else link = i18n.stripPrefix(name, "damaged") for _, suffix in pairs( i18n.suffixes ) do link = link:gsub( ' ' .. suffix .. '$', '' ) end end elseif link:lower() == 'нет' then link = nil end if link == pageName then link = nil end local formattedTitle local plainTitle if title == '' then plainTitle = name elseif title:lower() ~= 'нет' then plainTitle = title:gsub( '\\\\', '&#92;' ):gsub( '\\&', '&#38;' ) local formatPattern = '&[0-9a-fk-or]' if plainTitle:match( formatPattern ) then formattedTitle = title plainTitle = plainTitle:gsub( formatPattern, '' ) end if plainTitle == '' then plainTitle = name else plainTitle = plainTitle:gsub( '&#92;', '\\' ):gsub( '&#38;', '&' ) end elseif link then if img then formattedTitle = '' else plainTitle = '' end end description = p.resolveMultipage(mod, idData, description) item:attr{ ['data-minetip-title'] = formattedTitle, ['data-minetip-text'] = description, ['data-modinfo-text'] = mod, ['data-minetip-lowtitle'] = enName } if img then -- & is re-escaped because mw.html treats attributes -- as plain text, but MediaWiki doesn't local escapedTitle = ( plainTitle or '' ):gsub( '&', '&#38;' ) item:addClass( 'invslot-item-image' ) :wikitext( '[[File:', img, '|32x32px|link=', link or '', '|', escapedTitle, ']]' ) else local image local data = 'ИнвСпрайт' if mod then image = args.stylesheet or imageOverride data = data .. '/' .. mod end if link then item:wikitext( '[[', link, '|' ) end local image, spriteCat = sprite{ ["масштаб"] = args.scale, ["данныеID"] = idData, ["назв"] = plainTitle, ["изобр"] = image, ["данные"] = data, nourl = args.nourl, } item:node( image ) category = spriteCat end if num and num > 1 and num < 1000 then if img and link then item:wikitext( '[[', link, '|' ) end local number = item :tag( 'span' ) :addClass( 'invslot-stacksize' ) :attr{ title = plainTitle } :wikitext( num ) :cssText( args.numberStyle ) -- no-op when given nil if img and link then item:wikitext( ']]' ) end end if idData and link then item:wikitext( ']]' ) end item:wikitext( category ) return item end ---- Функции для операций над псевдонимами. ------------------------------------ --[[Returns a new table with the parts of the parent frame added to the alias --]] function p.getAlias( aliasFrames, parentFrame ) -- If alias is just a name, return the parent frame with the new name if type( aliasFrames ) == 'string' then local expandedFrame = mw.clone( parentFrame ) expandedFrame.name = aliasFrames return { expandedFrame } end -- Single frame alias, put in list if aliasFrames.name then aliasFrames = { aliasFrames } end local expandedFrames = {} for i, aliasFrame in ipairs( aliasFrames ) do local expandedFrame if type( aliasFrame ) == 'string' then expandedFrame = { name = aliasFrame } else expandedFrame = slot_utils.cloneTable( aliasFrame ) end expandedFrame.title = parentFrame.title or expandedFrame.title expandedFrame.mod = parentFrame.mod or expandedFrame.mod expandedFrame.num = parentFrame.num or expandedFrame.num expandedFrame.text = parentFrame.text or expandedFrame.text expandedFrames[i] = expandedFrame end return expandedFrames end function p.expandAlias( parentFrame, alias ) error("Вызвана устаревшая функция p.expandAlias из модуля Инвентарный слот!") -- return p.getAlias( alias, parentFrame ) end ---- Функции для операций, связанных с модификациями. -------------------------- --[[ Возвращает окончательное название модификации из приведённого. Задействует псевдонимы для обозначения модификаций и оригинальной игры. Параметры: • mod (строка) — название модификации, введённое пользователем. Возвращает каноническое название модификации (строку) или nil, если название указывает на оригинальную игру. ]] function p.resolveMod(mod) if (not mod) or (vanillaPrefixes[mw.ustring.lower(mod)]) or (mod == '') then -- mw.log("mod = " .. (tostring(mod) or "?") .. ", type = " .. type(mod) .. "; rejecting" ) return nil else local mod = modNameAliases[mod] or mod return (mod:gsub('_', ' ')) end end function p.resolveModData(mod, name) local results = {} results.modData = p.loadModData(mod) local idListOverride = results.modData['настройки']['списокID'] results.imageOverride = results.modData['настройки']['изобр'] if idListOverride then results.modData = slot_utils.loadMaybeData('Модуль:' .. idListOverride) end -- River said: "wtb .?"; I agree results.modData = results.modData and results.modData["IDы"] results.idData = results.modData and results.modData[name] results.enName = results.idData and results.idData.en if not (results.modData and results.idData) then results.img = i18n.legacyFilename:gsub( '%$1', name .. ' (' .. mod .. ')' ) end return results end function p.loadModAliases(mod) return slot_utils.loadMaybeData(("Модуль:ИнвСпрайт/%s/Псевдонимы2"):format(mod)) end function p.resolveMultipage(mod, idData, description) if mod == 'GregTech' and idData and idData["страница"] then local mat_info = slot_utils.loadMaybeData('Модуль:Обработка/Материалы/' .. idData["страница"]) if mat_info then local formula = mat_info["Формула"] local melt = mat_info["Плавление"] local boil = mat_info["Испарение"] return (description or '') .. '/&7Формула: &e' .. formula .. '/&6t° плавления: ' .. melt .. 'K/&ct° испарения: ' .. boil .. 'K' end end return description end ---- Функции для операций над фреймами. ---------------------------------------- -- Фрейм — структура, описывающая один кадр анимации инвентарного слота. -- -- Поля фрейма: -- • title (строка): заглавие всплывающей подсказки. Если не задано, подсказки нет. -- • mod (строка): название модификации. Если не задано, предмет берётся из основной игры. -- • name (строка): название предмета. Должно быть задано. -- • num (число): количество предметов в ячейке. Равно nil, если не задано. -- • text (строка): текст всплывающей подсказки. ---- --[[ Возвращает строковое представление структуры фрейма frame. ]] function p.stringifyFrame( frame ) if not frame.name then return '' end return string.format( '[%s]%s:%s,%s[%s]', frame.title or '', frame.mod or 'Minecraft', frame.name, frame.num or '', frame.text or '' ) end --[[ Берёт последовательность структур фреймов frames и заменяет в этой последовательности каждую структуру её строковым представлением. Затем возвращает одну строку, объединяющую все фреймы. ]] function p.stringifyFrames( frames ) for i, frame in ipairs( frames ) do frames[i] = p.stringifyFrame( frame ) end return table.concat( frames, ';' ) end --[[ Берёт текстовое представление фрейма (frameText) и переопределение модификации (mod). Возвращает структуру фрейма, соответствующую тексту frameText. Если во frameText не задана модификация, используется значение mod. ]] function p.makeFrame( frameText, mod ) -- Simple frame with no parts if not frameText:match( '[%[:,]' ) then return { mod = p.resolveMod(mod), name = (frameText:gsub("^%s+", ""):gsub("%s+$", "")), } end local frame = {} -- Эти две функции должны быть вызваны первыми, чтобы gsub ниже не портил подсказки. frameText, frame.title = slot_utils.removePrefix(frameText, '%s*%[%s*(.-)%s*%]%s*') frameText, frame.text = slot_utils.removeSuffix(frameText, '%s*%[%s*(.-)%s*%]%s*') -- Убирает пробелы, окружающие особые символы (квадратные скобки, двоеточия, -- запятые и точки с запятой). frameText = frameText:gsub( '%s*([%[%]:,;])%s*', '%1' ) frameText, frame.mod = slot_utils.removePrefix(frameText, '([^:,;%[%]]+):', mod) frame.mod = p.resolveMod(frame.mod) frameText, frame.name = slot_utils.removePrefix(frameText, '[^,;%[]+') frameText, frame.num = slot_utils.removePrefix(frameText, ',(%d+)') frame.num = math.floor( frame.num or 0 ) if frame.num == 0 then frame.num = nil end return frame end function p.getParts( frameText, mod ) error("Вызвана устаревшая функция p.getParts из модуля Инвентарный слот!") -- return p.makeFrame( frameText, mod ) end ---- Парсер списка фреймов. ---------------------------------------------------- --[[ Разбивает список фреймов на строки, описывающие конкретные фреймы. Учитывает, что разделитель фреймов (точка с запятой) может находиться ещё и в подсказке. Выдаёт ошибку при некорректном использовании квадратных скобок. Возвращает последовательность строковых представлений фреймов. Начальные и конечные пробельные символы в каждой строке уже убраны. ]] local function splitOnUnenclosedSemicolons(text) local semicolon, lbrace, rbrace = (";[]"):byte(1, 3) local nesting = false local splitStart = 1 local frameIndex = 1 local frames = {} for index = 1, text:len() do local byte = text:byte(index) if byte == semicolon and not nesting then frames[frameIndex] = text:sub(splitStart, index - 1) frameIndex = frameIndex + 1 splitStart = index + 1 elseif byte == lbrace then assert(not nesting, "Ошибка синтаксиса: чрезмерные квадратные скобки") nesting = true elseif byte == rbrace then assert(nesting, "Ошибка синтаксиса: несбалансированные квадратные скобки") nesting = false end end assert(not nesting, "Ошибка синтаксиса: несбалансированные квадратные скобки") frames[frameIndex] = text:sub(splitStart, text:len()) for index = 1, #frames do frames[index] = (frames[index]:gsub("^%s+", ""):gsub("%s+$", "")) -- faster mw.text.trim end return frames end --[[ Удаляет синтаксис подфреймов, выдавая ошибку при его некорректном использовании. Принимает текст фрейма (frameText) и состояние наличия подфрейма (subframe). Возвращает новые значения frameText и subframe ]] local function setSubframe(frameText, subframe) local matchedStart, matchedEnd frameText, matchedStart = frameText:gsub('^{%s*', '') if matchedStart == 1 then subframe = true end frameText, matchedEnd = frameText:gsub('}%s*$', '') assert(subframe or matchedEnd == 0, "Неожиданная закрывающая фигурная скобка при отсутствии подфреймов.") if matchedEnd == 1 then subframe = "last" end return frameText, subframe end --[[ Таблица parserArgs должна содержать следующие значения:  • frames: строка со списком фреймов;  • randomize: булево значение; если ложно, воспроизведение в случайном порядке  не допускается;  • defaultMod: название модификации по умолчанию. ]] function p.parseFrameText(parserArgs) local frames = { randomise = parserArgs.randomise } local subframes = {} local subframe local expandedAliases local defaultMod = parserArgs.defaultMod local splitFrames = splitOnUnenclosedSemicolons(parserArgs.frames) for _, frameText in ipairs( splitFrames ) do frameText, subframe = setSubframe(frameText, subframe) local frame = p.makeFrame( frameText, defaultMod ) local modAliases = frame.mod and p.loadModAliases(frame.mod) local newFrame = frame if aliases or modAliases then local id = frame.name if frame.mod then id = frame.mod .. ':' .. id end local alias = modAliases and modAliases[id] or aliases and aliases[id] if alias then newFrame = p.getAlias( alias, frame ) if aliasReference then local curFrame = #frames + 1 local aliasData = { frame = frame, length = #newFrame } if subframe then if not subframes.aliasReference then subframes.aliasReference = {} end subframes.aliasReference[#subframes + 1] = aliasData else if not expandedAliases then expandedAliases = {} end expandedAliases[curFrame] = aliasData end end end end if subframe then slot_utils.mergeList( subframes, newFrame ) -- Randomise starting frame for "Any *" aliases, as long as the alias is the only subframe if frames.randomise ~= 'never' and subframes.randomise == nil and i18n.matchesPrefix(frame.name, "any") then subframes.randomise = true else subframes.randomise = false end if frames.randomise ~= 'never' then frames.randomise = false end if subframe == 'last' then -- No point having a subframe containing a single frame, -- or the subframe being the only frame if #subframes == 1 or #splitFrames == i and #frames == 0 then slot_utils.mergeList( frames, subframes ) else table.insert( frames, subframes ) end subframes = {} subframe = nil end else -- Randomise starting frame for "Any *" aliases, as long as the alias is the only frame if frames.randomise == nil and i18n.matchesPrefix(frame.name, "any") then frames.randomise = true elseif frames.randomise ~= 'never' then frames.randomise = false end slot_utils.mergeList( frames, newFrame ) end end frames.aliasReference = expandedAliases return frames end ---- Основная функция. --------------------------------------------------------- function p.slot_main(pargs) local frames = pargs.frames local animated = frames and #frames > 1 local imgClass = pargs.imageClass local body = mw.html.create( 'span' ) :addClass( 'invslot' ) :addClass( pargs.class ) -- no-op when given nil :css{ ['vertical-align'] = pargs.align } :cssText( pargs.style ) -- no-op when given nil if animated then body:addClass( 'animated2' ) end if pargs.default then body:css( 'background-image', mw.getCurrentFrame():expandTemplate{ title = i18n.templateFileUrl, args = { pargs.default .. '.png' } } ) end local scale = pargs.scale if scale ~= 1 then if scale == 0.5 then body:css('top', '-1px') end local size = scale * 32 body:css{ width = size .. 'px', height = size .. 'px' } end --mw.logObject( frames ) if not frames then return tostring( body ) end local activeFrame = frames.randomise == true and random( #frames ) or 1 for i, frame in ipairs( frames ) do local item -- Table is a list, must contain subframes if frame[1] then item = body:tag( 'span' ):addClass( 'animated2-subframe' ) local subActiveFrame = frame.randomise and random( #frame ) or 1 for sI, sFrame in ipairs( frame ) do local sItem = makeItem( sFrame, sI, pargs ) item:node( sItem ) if sI == subActiveFrame then sItem:addClass( 'animated2-active' ) end end else item = makeItem( frame, i, pargs ) body:node( item ) end if i == activeFrame and animated then item:addClass( 'animated2-active' ) end end return tostring( body ) end ---- Основная точка входа для Шаблон:Инвентарный слот. ------------------------- -- Другие модули должны вызывать p.slot_main. ---- --[[ Параметры фрейма. Формат: Локализованное название (оригинальное название): описание Совместимые с оригинальными:  • первый параметр (содержащий данные слота);  • мод (mod): модификация по умолчанию;  • классизобр (imgclass): CSS-класс изображения;  • стильцифр (numstyle):  • ссылка (link)  • выравн (align)  • класс (class)  • стиль (style)  • умолчание (default)  • назв (title)  • таблспрайтов (spritesheet)  • nourl НЕ СОВМЕСТИМЫЕ с оригинальными:  • parsed: используйте p.slot_main() вместо parsed.  • модпсевдонимы (modaliases): псевдонимы не глобальны для всех модификаций, а локальны для каждой модификации.   Введённые помимо оригинальных:  • стильизобр  • масштаб  • умолчаниеCSS  • Версия ]] function p.slot( f ) local args = require(i18n.moduleProcessArgs).merge(true) local pargs = {} assert(not args.parsed, "Флаг parsed несовместим с переводной версией. Используйте p.slot_main вместо него.") local defaultMod = args["мод"] pargs.stylesheet = args["таблспрайтов"] pargs.class = args["класс"] pargs.imageClass = args["классизобр"] pargs.style = args["стиль"] pargs.imageStyle = args["стильизобр"] pargs.numberStyle = args["стильцифр"] pargs.align = args["выравн"] pargs.scale = tonumber(args["масштаб"] or 1) pargs.default = args["умолчание"] pargs.cssDefault = args["умолчаниеCSS"] pargs.defaultVersion = args["Версия"] pargs.link = args["ссылка"] pargs.title = args["назв"] pargs.frames = p.parseFrameText({ frames = args[1], randomize = pargs.class == 'invslot-large' and 'never' or nil, defaultMod = defaultMod }) return p.slot_main(pargs) end return p -- </nowiki> 
В данной статье используются материалы из статьи «Модуль:Инвентарный слот2» с вики-сайта Minecraft Wiki, расположенного на Фэндоме, и они распространяются согласно лицензии Creative Commons Attribution-NonCommercial-ShareAlike 3.0. Авторы статьи.