Module:Item
From Valheim Wiki
Documentation for this module may be created at Module:Item/doc
---The current language. Determines which l10n table to use. local lang local trim = mw.text.trim local cargo = mw.ext.cargo local cache = require 'mw.ext.LuaCache' ---A cached version of the current frame, the interface to the parser. local currentFrame ---Holds the arguments from the template call. local args_table local function is_crafting_station(text) if text == 'Artisan table' or text == 'Black forge' or text == 'Blast furnace' or text == 'Cauldron' or text == 'Cooking station' or text == 'Eitr refinery' or text == 'Forge' or text == 'Galdr table' or text == 'Smelter' or text == 'Spinning wheel' or text == 'Stonecutter' or text == 'Workbench' or text == 'By hand' or text == 'Hammer' then return true end return false end -- specify words that do not have a plural ending with s local function getPlural(text) if text == '' then return '' end -- example if text == 'Sword' then end return text .. 's' end local function getCategoryPlural(text) local plural = getPlural(text) if plural == '' then return '' end return '[[Category:' .. plural .. ']]' end ---Return the l10n string associated with the `key`. ---@param key string ---@return string local function l10n(key) return key; end ---Return a trimmed version of the value of the template parameter with the specified `key`. ---Return `nil` if the parameter is empty or unset. ---@param key string|number ---@return string|nil local function getArg(key) local value = args_table[key] if not value then return nil end value = trim(value) if value == '' then return nil end return value end ---Convert a string of parameters in a `@param1:value^@param2:value^` format to a table. ---Change the `name` and `text` parameters to `1` and `2`, respectively. ---@param paramstr string ---@return table local function parse(paramstr) local args = {} for s in string.gmatch(paramstr, '%b@^') do local k,v = string.match(s, '^@(.-):(.*)^$') args[k] = v end args[1] = args['name'] args[2] = args['text'] return args end ---Split the `str` on each `div` in it and return the result as a table. ---Original version credit: http://richard.warburton.it. This version trims each substring. ---@param div string ---@param str string ---@return table|boolean local function explode(div,str) if (div=='') then return false end local pos,arr = 0,{} -- for each divider found for st,sp in function() return string.find(str,div,pos,true) end do arr[#arr + 1] = trim(string.sub(str,pos,st-1)) -- Attach chars left of current divider pos = sp + 1 -- Jump past current divider end arr[#arr + 1] = trim(string.sub(str,pos)) -- Attach chars right of last divider return arr end ---Extract scale, width, and height from an input string. Up to two of the three can be empty in the input. ---Example: `5x7px*0.75` → `0.75`, `5`, `7` ---@param size string ---@return string basescale ---@return number width ---@return number height local function parseSize(size) if not size then return end local basescale, width, height size, basescale = unpack(explode('*', size)) if size ~= '' then width, height = unpack(explode('x', string.gsub(size, 'px', ''))) width, height = tonumber(width), tonumber(height) if width == 0 then width = nil end if height == 0 then height = nil end end return basescale, width, height end ---Return width, height, and caching date for the specified `imagename` from the Imageinfo cargo table. ---@param imagename string ---@return number width ---@return number height ---@return string cached local function getInfoFromCargo(imagename) -- try to get from cargo cache local result = mw.ext.cargo.query('Imageinfo', 'width, height, cached', { -- escape apostrophes in the imagename input where = 'image='.. "'"..imagename:gsub("'", "\\'"):gsub("'", "\\'").."'", orderBy = "cached DESC", limit = 1, }) for _, row in ipairs(result) do return tonumber(row['width']), tonumber(row['height']), row['cached'] end end ---Store width and height of the specified `imagename` to the Imageinfo cargo table and return them. ---Width and height are computed via the `#imgw:` and `#imgh:` parser functions, respectively. ---@param imagename string ---@return number width ---@return number height local function storeInfoToCargo(imagename) local width, height width = tonumber(currentFrame:callParserFunction( '#imgw', imagename)) if width and width ~= 0 then -- save one expensive call when the file is not a valid image. height = tonumber(currentFrame:callParserFunction( '#imgh', imagename)) if height and height ~= 0 then currentFrame:callParserFunction('#cargo_store:_table=Imageinfo',{ image = imagename, width = width, height = height, cached = os.time(), }) end end return width, height end ---Retrieve the dimensions of the specified `image` from the Imageinfo cargo table. ---If it doesn't have any data for the image yet, store it. ---@param imagename string ---@return number width ---@return number height local function getSizeInfo(imagename) local width, height, cached = getInfoFromCargo(imagename) -- cache missed, init cache if not cached then width, height = storeInfoToCargo(imagename) end if width == 0 then width = nil end if height == 0 then height = nil end return width, height end ---Compute the final width and height of the image. ---If necessary, retrieve data from or store data to the Imageinfo cargo table. ---@param imagename string ---@param width number ---@param height number ---@param scale number ---@param maxwidth number ---@param maxheight number ---@return number width ---@return number height local function getImageSize(imagename, width, height, scale, maxwidth, maxheight) -- get size info from image file itself (may be expensive) local w, h = getSizeInfo(imagename) -- store data to cache -- if width and height are not given as input, but scale/maxwidth/maxheight are, then -- set width and height to the original dimensions of the image if not width and not height and (scale or maxwidth or maxheight) then width, height = w, h end -- apply scale to width/height if needed if scale then if width then width = width * scale end if height then height = height * scale end end -- apply maxwidth/maxheight if maxwidth then if width then if width > maxwidth then width = maxwidth end else if height then width = maxwidth end end end if maxheight then if height then if height > maxheight then height = maxheight end else if width then height = maxheight end end end -- round to natural numbers if width then width = math.ceil(width) end if height then height = math.ceil(height) end return width, height end ---Extract width and height from an input string. ---Example: `6x9px` → `6`, `9` ---@param maxsize string ---@return number maxwidth ---@return number maxheight local function parseMaxSize(maxsize) if not maxsize then return end local maxwidth, maxheight = unpack(explode('x', string.gsub(maxsize, 'px', ''))) maxwidth, maxheight = tonumber(maxwidth), tonumber(maxheight) if maxwidth == 0 then maxwidth = nil end if maxheight == 0 then maxheight = nil end return maxwidth, maxheight end ---Assemble the final wikicode for an image. ---@param imagename string ---@param link string ---@param text string ---@param size string As accepted by the `[[File:` syntax, e.g. `5x7px*0.75`. ---@param scale number This will be multiplied by the scale in `size`, if necessary. ---@param maxsize string ---@return string local function imagecode(imagename, link, text, size, scale, maxsize) mw.log('imagename: ' .. imagename) local image_output = '[[File:' .. imagename .. '|32px|link='.. link .. '|' .. text -- local image_output = '[[File:' .. imagename .. '|32px|link='.. link if size or scale or maxsize then local basescale, width, height = parseSize(size) -- width, height: number or nil (basescale is string!) scale = (tonumber(scale) or 1) * (tonumber(basescale) or 1) -- combine the scale parameter and scale from the size parameter if scale == 0 or scale == 1 then scale = nil end local maxwidth, maxheight = parseMaxSize(maxsize) width, height = getImageSize(imagename, width, height, scale, maxwidth, maxheight) -- can be 0 if width or height then image_output = image_output .. '|' .. (width or '') .. 'x' .. (height or '') .. 'px' end end return image_output .. ']]' end ---Return the full `[[File:` wikicode for each image in the input (multiple are separated with `/`). ---@param image string ---@param link string ---@param text string ---@param size string ---@param scale string ---@param maxsize string ---@return string local function images(image, link, text, size, scale, maxsize) if not image:find('/') then -- there is only one image in the input return imagecode(image, link, text, size, scale, maxsize) end -- there are multiple images in the input, separated with a slash image = explode('/', image) local result = '' if size and size:find('/') then -- there are multiple sizes in the size parameter size = explode('/', size) -- so turn it into a table for i, v in ipairs(image) do -- iterate over the images result = result .. imagecode(v, link, text, size[i], scale, maxsize) -- create the wikicode (using the respective size) end else for i, v in ipairs(image) do -- iterate over the images result = result .. imagecode(v, link, text, size, scale, maxsize) -- create the wikicode end end return result end ---Return a string like `Internal Item ID: `, depending on the `_type`. ---@param _type '"item"'|'"tile"'|'"wall"'|'"npc"'|'"mount"'|'"buff"'|'"projectile"'|'"armor"' ---@return string local function getIdText(_type) local id_text if _type == 'item' then -- a shortcut for faster id_text = l10n('id_text_item') elseif _type == 'tile' then id_text = l10n('id_text_tile') elseif _type == 'wall' then id_text = l10n('id_text_wall') elseif _type == 'npc' then id_text = l10n('id_text_npc') elseif _type == 'mount' then id_text = l10n('id_text_mount') elseif _type == 'buff' or _type == 'debuff' then id_text = l10n('id_text_buff') elseif _type == 'projectile' then id_text = l10n('id_text_projectile') elseif _type == 'armor' then id_text = l10n('id_text_armor') else id_text = l10n('id_text_item') end return id_text end ----------------------------------------------------------------- -- main return object return { parse = parse, is_crafting_station = is_crafting_station, go = function(frame, args) -- cache? if not args then local cached = cache.get(':item:' .. frame.args[1]) if cached then return cached end end -- init var cache currentFrame = frame args_table = args or parse(frame.args[1]) lang = getArg('lang') or 'en' local _arg1 = getArg(1) or '' local _nolink = getArg('nolink') local _link = _nolink and '' or getArg('link') or frame:expandTemplate{ title = 'tr', args = {_arg1, link='y', lang=lang} } -- now: _link == '' means nolink local text = getArg(2) or '' _link = text -- hovertext: {{tr|_arg1}} or text or _link (in that order) local hovertext if _arg1 ~= '' then hovertext = frame:expandTemplate{ title = 'tr', args = {_arg1, lang=lang} } elseif text ~= '' then hovertext = text else hovertext = _link end -- set output flags local output_image, output_text, output_table = true, true, false local _mode = getArg('mode') if _mode then if _mode == 'image' or _mode == 'imageonly' or _mode == 'onlyimage' then output_text = false elseif _mode == 'text' or _mode == 'noimage' then output_image = false elseif _mode == 'table' or _mode == '2-cell' then output_table = true end end local class = 'i' local image_output, text_output -- get wikicode for the image(s) if output_image then local image_arg = getArg('image') if not image_arg then image_arg = string.gsub(_arg1, ":%s*", " ") image_arg = image_arg .. '.' .. (getArg('ext') or 'png') end if string.find(image_arg, '%[%[[fF]ile:') then image_output = '<span class="img">' .. image_arg .. '</span>' else image_output = images(image_arg, _link, hovertext, getArg('size'), getArg('scale'), getArg('maxsize')) end else image_output = '' end -- get wikicode for the text if output_text then local _note, _note2, _showid, _id, _icon = getArg('note'), getArg('note2'), getArg('showid'), getArg('id'), getArg('icons') -- get info from arguments -- prepare: display ID? if _id and not _showid then _showid = true end if _showid and (_showid == 'n' or _showid == 'no') then _showid = false end -- prepare: wrap? local _wrap if _showid or _note2 then _wrap = false else _wrap = getArg('wrap') end -- prepare: link and display text if _link ~= '' then if text == _link then text = '<span data-event=1>[['..text..']]</span>' else text = '<span data-event=2>[['..trim(_link)..'|'..trim(text)..']]</span>' end else text = '<span data-event=3 title="'..trim(hovertext)..'">'..trim(text)..'</span>' end -- prepare: eicons local icon = nil -- if _icon == 'n' or _icon == 'no' or _icon == 'off' then icon = '' -- else -- icon = eicons(getArg('epage') or _arg1, lang, (_showid or _note2 or _wrap or getArg('small')) and 'y') -- end -- assemble HTML code local content = text -- item name link text first. -- '-w' class means 'wrapmode', optimized for multiple lines of text. But it should be disabled for single line text. if _wrap then -- eicons in the same line if icon ~= '' then class = class .. ' -w' content = content .. icon end -- note in a new line if _note then class = class .. ' -w' content = content .. '<span class="note">' .. _note .. '</span>' end else -- note in the same line if _note then content = content .. '<span class="note">' .. _note .. '</span>' end -- eicons in the same line if icon ~= '' then content = content .. icon end -- note2 in a new line if _note2 then class = class .. ' -w' content = content .. '<div class="note">' .. _note2 .. '</div>' end -- id in a new line if _showid then class = class .. ' -w' local idtype = (getArg('type') or 'item'):lower() if not _id then -- get ID automatically via {{itemIdFromName}} or the like _id = frame:expandTemplate{ title = idtype .. 'IdFromName', args = {_arg1} } end local id_text = getIdText(idtype) content = content .. '<div class="id">' .. id_text .. _id .. '</div>' end end text_output = '<span>' .. content .. '</span>' else text_output = '' end -- handle custom CSS local _class, _css = getArg('class'), getArg('css') if _class then class = class .. ' ' .. _class -- add to existing classes end local attr = {class = class} if _css then attr.style = _css -- set the style attribute to parameter value end -- add anchor if getArg('anchor') then text_output = text_output .. '<div class="anchor" id="' .. frame:callParserFunction('anchorencode', _arg1) .. '"></div>' end local return_string if output_table then -- table output attr.class = class local _rowspan = getArg('rowspan') local rowspan_text = (_rowspan and (' rowspan=' .. _rowspan) or '') -- prepare the two cells local first_cell_pre = rowspan_text .. ' class="il1c"' local first_cell_content = mw.text.tag('span', attr, image_output) local second_cell_pre = rowspan_text .. ' class="il2c"' local second_cell_content = mw.text.tag('span', attr, text_output) -- combine return_string = first_cell_pre .. " | " .. first_cell_content .. " || " .. second_cell_pre .. " | " .. second_cell_content else -- non-table output (text/image) return_string = mw.text.tag('span', attr, image_output .. text_output) end -- cache output for later reuse if not args then cache.set(':item:' .. frame.args[1], return_string, 3600*24) -- cache for 24 hours end -- output return return_string end, category = function(frame, args) currentFrame = frame -- global frame cache local args = frame:getParent().args args_table = args return getCategoryPlural(getArg('type') or '') end, }