Module:Card
Appearance
Documentation for this module may be created at Module:Card/doc
local p = {}
local lib = require('Module:Feature')
local TemplateData = require('Module:TemplateData')
local Icon = require('Module:Icon')
local QUALITY_STARS = {
['1'] = '[[File:1 Star Icon.png|x16px|link=|alt=Quality 1]]',
['2'] = '[[File:2 Stars Icon.png|x16px|link=|alt=Quality 2]]',
['3'] = '[[File:3 Stars Icon.png|x16px|link=|alt=Quality 3]]',
['4'] = '[[File:4 Stars Icon.png|x16px|link=|alt=Quality 4]]',
['5'] = '[[File:5 Stars Icon.png|x16px|link=|alt=Quality 5]]'
}
local PREFIX_ICONS = {
['Instructions: '] = {name='Instructions', type='Item'},
['Diagram: '] = {name='Diagram', type='Item'},
['Recipe: '] = {name='Recipe', type='Item'},
['Formula: '] = {name='Formula', type='Item'},
['Blueprint: '] = {name='Furnishing Blueprint', type='Icon'},
}
-- main template/module parameters
local CARD_ARGS = {
{
name='name', alias={1,'character'}, displayName=1,
status='suggested',
label='Name',
description='The name of the image (without the prefix or extension).',
default='Unknown',
example={'Mora', 'Primogem', 'Amber'},
},
{
name='text', alias=2, displayName=2,
label='Card Text',
description='The text below the image.',
example={'100', 'Lv. 1', 'Set'},
},
{
name='multiline_text', type='1',
label='Allow Multiple Lines of Card Text',
trueDescription='allow card text to wrap to a new line if necessary.',
},
{
name='text_size', type='string',
label='Size of Card Text',
description='Adds the "card-text-<text_size>" class, including "small", "smaller".',
},
{
name='quality', displayType='number',
label='Card Quality',
description='The Quality of the item. For light background, add 10. Use 0 for white version. Use multiple numbers for split rarities.',
default='0',
displayDefault='determined based on Image Name; "0" if unknown',
example={'1', '2', '3', '4', '5', '123', '23', '34', '45'},
},
{
name='type', placeholderFor='Module:Icon',
},
{
name='suffix', placeholderFor='Module:Icon',
},
{
name='extension', placeholderFor='Module:Icon',
},
{
name='link', displayType='wiki-page-name',
label='Card Link',
description='The page to link to when image or caption text is clicked.',
displayDefault='same as Image Name',
defaultFrom='name',
example={'Mora', 'Dressing Room', 'Shops', 'Amber'},
},
{
name='link_suffix',
label='Page Link Suffix',
description='Text to append to the Page Link. (Usually used to link to page sections.)',
default='',
example='#Trivia',
},
{
name='nolink', type='1',
label='No Link',
trueDescription='remove default links from card image, icons, and caption.',
displayDefault='"1" if Image Name is default ("Unknown")',
},
{
name='icon', displayType='content',
label='Icon',
description='The icon at the top left of the card.',
example={'{{Icon/Element|Pyro|25}}', '[[File:Item Primogem.png|25px]]'},
},
{
name='icon_style', displayType='string',
label='Icon Style',
description='The custom style of the icon at the top left of the card. Omit the \'\' used for CSS.',
example='background: red;',
},
{
name='icon_right', displayType='content',
label='Top-Right Icon',
description='Icon on the top right of the card.',
example='{{Icon/Element|Pyro|25}}',
},
{
name='icon_right_style', displayType='string',
label='Top-Right Icon Style',
description='The custom style of the icon at the top right of the card. Omit the \'\' used for CSS.',
example='background: red;',
},
{
name='icon_bottom_left', displayType='content',
label='Bottom-Left Icon',
description='Icon on the bottom left of the card.',
example='{{Icon/Element|Pyro|25}}',
},
{
name='icon_bottom_left_style', displayType='string',
label='Bottom-Left Icon Style',
description='The custom style of the icon at the bottom left of the card. Omit the \'\' used for CSS.',
example='background: red;',
},
{
name='icon_bottom_right', displayType='content',
label='Bottom-Right Icon',
description='Icon on the bottom right of the card.',
example='{{Icon/Element|Pyro|25}}',
},
{
name='icon_bottom_right_style', displayType='string',
label='Bottom-Right Icon Style',
description='The custom style of the icon at the bottom right of the card. Omit the \'\' used for CSS.',
example='background: red;',
},
{
name='show_caption', type='1',
label='Show Caption',
trueDescription='show caption text below the card.',
},
{
name='caption', displayType='string',
label='Caption Text',
description='The linked caption text that shows below the card.',
displayDefault='same as Name',
defaultFrom='name',
example={'Apple', 'Rewards'},
},
{
name='caption_width',
label='Caption Width',
description='Width of the captions. Set to \'auto\' to automatically increase the caption width to prevent it from wrapping in the middle of words.',
displayDefault='same as card width (74px, or 40px for mini cards)',
example={'200px', 'auto'},
},
{
name='note', displayType='string',
label='Caption Note',
description='The unlinked caption text that shows below the linked caption text.',
example='(Bonus reward)',
},
{
name='refinement', alias='r', displayType='number',
label='Refinement Rank',
description='The refinement of the weapon. Set to \'1a\' to denote a weapon that cannot be refined.',
example={'1', '2', '3', '4', '5', '1a'},
},
{
name='ascension', placeholderFor='Module:Icon',
},
{
name='constellation', alias='c', displayType='number',
label='Constellation Level',
description='Constellation Level of the Character.',
example={'0', '1', '2', '3', '4', '5', '6'},
},
{
name='outfit', placeholderFor='Module:Icon',
},
{
name='stars', type='1',
label='Show Item Stars',
trueDescription='show the stars of the quality of the item. (Does not work if quality is "0" or split (e.g., "123" or "34").)',
},
{
name='set', type='1',
label='Show Set Icon',
displayDefault='"1" if the card type is "Artifact Set"',
trueDescription='show set icon at the top right corner of the card.',
},
{
name='vol', displayType='number',
label='Book Volume',
description='Volume number of the book (for cards that show books). Sets Card Text and Page Link Suffix to point to the given volume.',
example={'1', '2', '3'},
},
{
name='danger', type='1',
label='Danger',
trueDescription='add Warning icon as the Top-Right Icon.',
},
{
name='mini', type='1',
label='Mini',
trueDescription='show a smaller card. (Not all parameters apply to mini cards.)',
},
{
name='miliastra_wonderland', type='1',
label='Miliastra Wonderland',
trueDescription='Shows styles for Miliastra Wonderland items and cosmetics.',
},
{
name='mobile_list', type='1',
label='Mobile List Item',
trueDescription='show Template:Item-like display on mobile',
},
}
-- add parameters inherited from Icon for the main image, top-left/right icon, and equipped icon
Icon.addIconArgs(CARD_ARGS, '', '', { -- exclude args already present in CARD_ARGS or that don't apply to the main image
name=false,
link=false,
size=false,
alt=false,
})
Icon.addIconArgs(CARD_ARGS, 'icon_', 'Top-Left Icon ', { -- override to add `element` alias and document automatic icons
name={
alias='element',
description='Name of the icon to display at the top left of the card (without the prefix or extension).',
displayDefault='element icon for characters; furnishing blueprint/recipe/diagram icon if Name is prefixed accordingly; empty otherwise',
example={'Furnishing Blueprint', 'Fruit Paste Bait', 'Anemo'}
},
})
Icon.addIconArgs(CARD_ARGS, 'icon_right_', 'Top-Right Icon ', {
name={
description='Name of the icon to display at the top right of the card (without the prefix or extension).',
example={'Furnishing Blueprint', 'Fruit Paste Bait', 'Anemo'}
},
})
Icon.addIconArgs(CARD_ARGS, 'icon_bottom_left_', 'Bottom-Left Icon ', {
name={
description='Name of the icon to display at the bottom left of the card (without the prefix or extension).',
example={'Furnishing Blueprint', 'Fruit Paste Bait', 'Anemo'}
},
})
Icon.addIconArgs(CARD_ARGS, 'icon_bottom_right_', 'Bottom-Right Icon ', {
name={
description='Name of the icon to display at the bottom right of the card (without the prefix or extension).',
example={'Furnishing Blueprint', 'Fruit Paste Bait', 'Anemo'}
},
})
Icon.addIconArgs(CARD_ARGS, 'equipped_', 'Equipped ', { -- overrides to add `equipped`/`e` aliases and change default size
name={
alias={'equipped', 'e'},
description='Name of the weapon or character to display at the top right of the card (or bottom left, if Constellation Level is present; name does not include the prefix or extension).',
example={'Song of Broken Pines', 'Hu Tao'}
},
size={default='30'},
})
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame, {
parentFirst = true,
wrapper = { 'Template:Card' }
})
return p._main(args, frame)
end
-- apply defaults; load and apply data (e.g., quality, character element)
function p._processArgs(args)
local out = TemplateData.processArgs(args, CARD_ARGS)
-- set defaults that depend on other arguments
out.defaults.show_caption = lib.isNotEmpty(out.nonDefaults.caption)
out.defaults.nolink = out.name == out.defaults.name and lib.isEmpty(out.nonDefaults.link)
if out.vol ~= nil then
out.defaults.text = 'Vol. ' .. out.vol
out.defaults.link_suffix = '#Vol._' .. out.vol
end
-- handle deprecated caption value
if tostring(out.caption) == '1' then
out.uses_deprecated_params = true
out.caption = nil
out.show_caption = true
end
-- handle nolink
out.final_link = out.nolink and '' or (out.link .. out.link_suffix)
-- build args for main card image
local mainImageArgs = Icon.extractIconArgs(out)
mainImageArgs.link = out.final_link
mainImageArgs.size = 74
-- check for prefixes in `name` that correspond to top-left icons
-- note: priority of icon specification: `icon` arg > `icon_name` arg > prefix of `name` > character element from data
local baseName, prefix = Icon.stripPrefixes(out.name, PREFIX_ICONS)
if prefix then
-- configure icon if none is specified by `icon_name`,
-- making sure not to change `icon_type` if `icon_name` is explicitly specified
if out.icon_name == out.defaults.icon_name then
local prefixIcon = PREFIX_ICONS[prefix]
out.icon_name = prefixIcon.name
out.icon_type = prefixIcon.type
end
mainImageArgs.name = baseName
end
-- create card image and get type/data
local card_type, data
out.image, card_type, data = Icon.createIcon(mainImageArgs)
-- set defaults based on data
if data then
if card_type == 'Character' then
if out.icon_name == out.defaults.icon_name and data.element ~= 'Adaptive' and lib.isNotEmpty(data.element) then
out.icon_name = data.element
out.icon_type = 'Element'
out.icon_link = ''
end
out.element = data.element or out.element
out.weapon = data.weapon or out.weapon
out.defaults.text = data.name or out.name
out.defaults.text_size = data.text_size or out.text_size
end
if card_type == 'Enemy' then
if out.icon_name == out.defaults.icon_name and lib.isNotEmpty(data.element) then
out.icon_name = data.element
out.icon_type = 'Element'
out.icon_link = ''
end
out.element = data.element or out.element
end
if card_type == 'Fish' then
if out.icon_name == out.defaults.icon_name and lib.isNotEmpty(data.bait) then
out.icon_name = data.bait
out.icon_type = 'Item'
out.icon_link = ''
end
out.prefix = ''
end
if card_type == 'Food' then
if out.icon_name == out.defaults.icon_name and lib.isNotEmpty(data.effectType) then
out.icon_name = data.effectType
out.icon_type = 'Icon'
out.icon_link = ''
end
end
if card_type == 'Cosmetic Component' or card_type == 'Cosmetic Set' or data.mode == 'Miliastra Wonderland' then
out.miliastra_wonderland = true
end
if data.quality then
out.defaults.quality = data.quality
end
if data.title then
out.defaults.caption = data.title
end
end
-- set other defaults based on card type
if card_type == 'Artifact Set' then
out.defaults.set = true
end
-- add top-left icon (if not specified by 'icon' wikitext)
if out.icon == out.defaults.icon then
local iconImage = Icon.createIcon(out, 'icon_')
iconImage.defaults.size = lib.ternary(out.mini, 15, 20)
-- disable link if nolink is explicitly set, but icon_link is not
if out.nonDefaults.nolink then
iconImage.defaults.link = ''
end
out.icon = iconImage:buildString()
end
-- add top-right icon (if not specified by 'icon_right' wikitext)
if out.icon_right == out.defaults.icon_right then
local iconImage = Icon.createIcon(out, 'icon_right_')
iconImage.defaults.size = lib.ternary(out.mini, 15, 20)
-- disable link if nolink is explicitly set, but icon_link is not
if out.nonDefaults.nolink then
iconImage.defaults.link = ''
end
out.icon_right = iconImage:buildString()
end
-- add top-right icon (if not specified by 'icon_bottom_left' wikitext)
if out.icon_bottom_left == out.defaults.icon_bottom_left then
local iconImage = Icon.createIcon(out, 'icon_bottom_left_')
iconImage.defaults.size = lib.ternary(out.mini, 15, 20)
-- disable link if nolink is explicitly set, but icon_link is not
if out.nonDefaults.nolink then
iconImage.defaults.link = ''
end
out.icon_bottom_left = iconImage:buildString()
end
-- add top-right icon (if not specified by 'icon_bottom_right' wikitext)
if out.icon_bottom_right == out.defaults.icon_bottom_right then
local iconImage = Icon.createIcon(out, 'icon_bottom_right_')
iconImage.defaults.size = lib.ternary(out.mini, 10, 20)
-- disable link if nolink is explicitly set, but icon_link is not
if out.nonDefaults.nolink then
iconImage.defaults.link = ''
end
out.icon_bottom_right = iconImage:buildString()
end
-- add equipped icon
if lib.isNotEmpty(out.equipped_name) then
local equippedImage, equippedType = Icon.createIcon(out, 'equipped_')
if equippedType == 'Character' then
equippedImage.defaults.size = 50
equippedImage.defaults.suffix = 'Side Icon'
equippedImage.defaults.alt = 'Equipped on ' .. equippedImage.alt
else
equippedImage.defaults.alt = 'Equipped with ' .. equippedImage.alt
end
if out.nonDefaults.nolink then
equippedImage.defaults.link = ''
end
out.equipped = equippedImage:buildString()
end
-- add enemy danger icon
if out.danger then
out.icon_right = '[[File:Icon Warning.png|25x25px|link=|alt=Warning icon]]'
out.icon_right_style = 'top: -8px; right: -10px; z-index: 1'
end
return out
end
local function setCaptionWidth(node, width)
if width == 'auto' then
node:addClass('auto-width')
else
node:css('width', width)
end
end
function p._main(args, frame)
local a = p._processArgs(args)
local node_container = mw.html.create('div'):addClass('card-container')
if a.mini then
node_container:addClass('mini-card')
end
if a.miliastra_wonderland then
node_container:addClass('miliastra-wonderland')
end
if a.text and tonumber((tostring(a.text):gsub(',', ''):gsub('[kKmMbBtT%%]$', '')))~=nil then
local mult = a.text:gsub('^.-([kKmMbBtT%%])$', '%1')
a.text = lib.thousandsSeparator((tostring(a.text):gsub(',', ''):gsub('[kKmMbBtT%%]$', ''))) .. (mult == a.text and '' or mult)
end
-- create two wrapper divs for the main card body
-- (required for CSS positioning and rounded corners, respectively)
local node_card = node_container:tag('span')
:addClass('card-wrapper')
:tag('span')
:addClass('card-body')
local node_image = node_card:tag('span')
:addClass('card-image-container')
if lib.isNotEmpty(a.quality) then
if tonumber(a.quality)==nil then node_image:addClass('card-quality-' .. string.lower(a.quality:gsub('%D', ''):gsub(' ', '%-'))) end
node_image:addClass('card-quality-' .. string.lower(a.quality:gsub(' ', '%-')))
end
if lib.isNotEmpty(a.weapon) then node_image:addClass('card-weapon-' .. string.lower(a.weapon:gsub(' ', '%-'))) end
if lib.isNotEmpty(a.element) then node_image:addClass('card-element-' .. string.lower(a.element:gsub(' ', '%-'))) end
node_image:tag('span')
:wikitext(a.image:buildString())
if lib.isNotEmpty(a.icon) then
node_card:tag('span')
:addClass('card-icon')
:cssText(a.icon_style)
:wikitext(a.icon)
end
if a.mini then
if lib.isNotEmpty(a.icon_right) then
node_card:tag('span')
:addClass('card-icon-right')
:cssText(a.icon_right_style)
:wikitext(a.icon_right)
end
else
if a.set then
node_card:tag('span')
:addClass('card-set-container')
:tag('span')
:addClass('icon')
:wikitext('[[File:Set.svg|14px|link=|alt=Artifact set]]')
end
if a.stars then
local starsImage = QUALITY_STARS[a.quality]
if starsImage then
node_card:tag('span')
:addClass('card-stars')
:tag('span')
:wikitext(starsImage)
end
end
if lib.isNotEmpty(a.icon_right) then
node_card:tag('span')
:addClass('card-icon-right')
:cssText(a.icon_right_style)
:wikitext(a.icon_right)
end
if lib.isNotEmpty(a.icon_bottom_left) then
node_card:tag('span')
:addClass('card-icon-bottom-left')
:cssText(a.icon_bottom_left_style)
:wikitext(a.icon_bottom_left)
end
if lib.isNotEmpty(a.icon_bottom_right) then
node_card:tag('span')
:addClass('card-icon-bottom-right')
:cssText(a.icon_bottom_right_style)
:wikitext(a.icon_bottom_right)
end
if lib.isNotEmpty(a.equipped) then
node_card:tag('span')
:addClass('card-equipped')
:wikitext(a.equipped)
end
if lib.isNotEmpty(a.text) then
local node_text = node_card:tag('span')
:addClass('card-text')
:addClass('card-font')
:wikitext(a.text)
if a.text_size then
node_text:addClass('card-text-' .. a.text_size)
end
if a.multiline_text then
node_text:addClass('multi-line')
end
end
if lib.isNotEmpty(a.refinement) then
node_card:tag('span')
:addClass('card-refinement')
:addClass('refine-' .. a.refinement)
:wikitext(' R', (a.refinement:gsub('a', ''))) -- extra parens to take only first value returned from gsub
end
if lib.isNotEmpty(a.constellation) then
node_card:tag('span')
:addClass('card-constellation')
:addClass('constellation-' .. a.constellation)
:wikitext(' C', a.constellation)
node_card:addClass('card-with-constellation')
end
end
if a.show_caption and lib.isNotEmpty(a.caption) then
local node_caption = node_container:tag('span')
:addClass('card-caption')
:wikitext(' ', a.nolink and a.caption or ('[[' .. a.final_link .. '|' .. a.caption .. ']]'))
setCaptionWidth(node_caption, a.caption_width)
end
if lib.isNotEmpty(a.note) then
local node_note = node_container:tag('span')
:addClass('card-caption')
:wikitext(a.note)
setCaptionWidth(node_note, a.caption_width)
end
if a.mobile_list then
if a.text ~= '—' or a.caption or a.note then
local node_mobile_text = node_card
:tag('span')
:addClass('card-mobile-text')
if a.nolink then
node_mobile_text:wikitext(' ', a.caption)
else
node_mobile_text:wikitext(' [[', a.final_link, '|', a.caption, ']]')
end
if tonumber((a.text:gsub(',', ''))) ~= nil then
node_mobile_text:wikitext(' ×')
end
if (a.caption or ''):gsub('­', '') ~= (a.text or ''):gsub('­', '') and a.text ~= '—' then
node_mobile_text:wikitext(' ', a.text)
end
if a.note then
node_mobile_text:wikitext(' (', a.note, ')')
end
end
end
if a.uses_deprecated_params then
node_container:wikitext('[[Category:Pages Using Deprecated Template Parameters]]')
end
return node_container
end
function p.templateData(frame)
return TemplateData.templateData(CARD_ARGS, {
description="This template is used to create an item display that is similar to the items in the Inventory in-game. Text can be shown below the item's image, background and stars can match the quality, refinement, icons, and set can be added.\n\nIf the image name matches an existing Item, then the quality will automatically be set.\n\nIf the image name matches an existing Character of Weapon, then the format will automatically change to match that type item.",
format='inline'
}, frame)
end
function p.syntax(frame)
return TemplateData.syntax(CARD_ARGS, frame)
end
return p