| Latest revision |
Your text |
| Line 1: |
Line 1: |
|
| |
| local p = {} | | local p = {} |
|
| |
| local lib = require('Module:Feature') | | local lib = require('Module:Feature') |
| local ne = lib.isNotEmpty | | local ne = lib.isNotEmpty |
| local TemplateData = require('Module:TemplateData') | | local namespace = require('Module:Namespace_detect')._main |
| | local getTemplateArgs = require('Module:Parser').getTemplateArgs |
| local array = require('Module:Array') | | local array = require('Module:Array') |
| local CentralData = require('Module:CentralData')
| |
|
| |
|
| local allData = CentralData.dataSheets | | local allData = { |
| | --{'Neighbor', mw.loadData('Module:CentralData/Neighbors')}, |
| | {'Creature', mw.loadData('Module:CentralData/Creatures')}, |
| | --{'Furniture', mw.loadData('Module:CentralData/Furniture')}, |
| | } |
|
| |
|
| local CARD_ARGS = { | | local COLON = ':' |
| {
| |
| name = 'name',
| |
| alias = 1,
| |
| default = 'Unknown',
| |
| },
| |
| {
| |
| name = 'label',
| |
| alias = {2, 'text', 'count', 'amount', 'x'},
| |
| },
| |
| {
| |
| name = 'show_label',
| |
| alias = 'show_text',
| |
| type = '1',
| |
| },
| |
| {
| |
| name = 'quality',
| |
| alias = 'rarity',
| |
| default = 'Common',
| |
| },
| |
| {
| |
| name = 'type',
| |
| },
| |
| {
| |
| name = 'split',
| |
| },
| |
| {
| |
| name = 'suffix',
| |
| default = '',
| |
| },
| |
| {
| |
| name = 'image',
| |
| },
| |
| {
| |
| name = 'image_size',
| |
| alias = {'imageSize', 'size'},
| |
| },
| |
| {
| |
| name = 'image_ext',
| |
| alias = {'imageExt', 'ext', 'extension'},
| |
| default = 'png',
| |
| },
| |
| {
| |
| name = 'link',
| |
| },
| |
| {
| |
| name = 'link_suffix',
| |
| default = '',
| |
| },
| |
| {
| |
| name = 'no_link',
| |
| type = '1',
| |
| },
| |
| {
| |
| name = 'caption',
| |
| },
| |
| {
| |
| name = 'show_caption',
| |
| type = '1',
| |
| },
| |
| {
| |
| name = 'caption_width',
| |
| alias = 'captionWidth',
| |
| },
| |
| {
| |
| name = 'mini',
| |
| type = '1',
| |
| },
| |
| {
| |
| name = 'first',
| |
| type = '1',
| |
| },
| |
| {
| |
| name = 'up',
| |
| alias = 'UP',
| |
| type = '1',
| |
| },
| |
| {
| |
| name = 'notice',
| |
| },
| |
| }
| |
|
| |
|
| function p.main(frame) | | function p.main(frame) |
| local args = require('Module:Arguments').getArgs(frame, { | | local args = require('Module:Arguments').getArgs(frame, { |
| wrapper = {'Template:Card'}, | | wrapper = { 'Template:Card' }, |
| parentOnly = false | | parentOnly = false |
| }) | | }) |
|
| |
| return p._main(args) | | return p._main(args) |
| end | | end |
|
| |
|
| local function getDataByName(dataType, dataTable, name)
| | function p._main(args) |
| local data = dataTable[name] | | local item = args[1] or args.name or 'Unknown' |
| | | local label = args[2] or args.label or args.text or args.count or args.amount or args.x or nil |
| if data then | | |
| return data
| | -- Reset agent-specific data to prevent carry-over from previous cards |
| end | | local agQuality, nbGalaxy, crQuality, crGalaxy, outQuality, notice = nil, nil, nil, nil, nil, nil |
| | | |
| local normalizedType = tostring(dataType or ''):lower() | | -- Caption Params: |
| if normalizedType == 'neighbor' or normalizedType == 'neighbors' then | | local caption = args.caption or nil |
| return nil
| | local caption_width = args.caption_width or args.captionWidth or nil |
| end | | |
| | | -- Image File Params: |
| for quality, names in pairs(dataTable) do | | local type, data = p.getTypeAndData(item, args.type) |
| if type(names) == 'table' then
| | local suffix = args.suffix or '' |
| for _, itemName in ipairs(names) do
| | local imageSize = args.image_size or args.imageSize or lib.ternary(args.mini == '1', 48, 70) |
| if itemName == name then
| | local imageExt = args.image_ext or args.imageExt or 'png' |
| return {
| | |
| quality = quality
| | -- Link Params: |
| }
| | local link = args.link or lib.ternary(item == 'Unknown', '', item) |
| end
| | local link_suffix = args.link_suffix or '' |
| end | | |
| | -- Rank Param: |
| | local quality = args.quality or args.rarity or nil |
| | |
| | -- Try to get Rank From Data |
| | if lib.isEmpty(quality) and data then |
| | if data.quality then |
| | quality = data.quality |
| end | | end |
| end | | end |
| | | |
| return nil | | -- If quality is still empty, set it to "Gray" |
| end
| | if lib.isEmpty(quality) then |
| | | quality = 'Gray' |
| function p.getTypeAndData(name, requestedType)
| |
| local img_type = requestedType or 'Item' | |
| local img_data = nil
| |
| | |
| for _, v in ipairs(allData) do
| |
| local dataType = v[1]
| |
| local dataTable = v[2]
| |
| | |
| if lib.isEmpty(requestedType) or dataType == requestedType then
| |
| local data = getDataByName(dataType, dataTable, name)
| |
| | |
| if data then
| |
| img_type = dataType
| |
| img_data = data
| |
| break
| |
| end
| |
| end
| |
| end | | end |
| | | |
| return img_type, img_data | | -- If quality contains a semicolon, then split and get first |
| end
| | if string.find(quality, ";") then |
| | |
| local function normalizeBool(value)
| |
| return value == true or value == '1' or value == 1 or value == 'yes' | |
| end
| |
| | |
| local function normalizeClassName(value)
| |
| return tostring(value or '')
| |
| :lower()
| |
| :gsub('%s+', '-')
| |
| end
| |
| | |
| local function normalizeQuality(quality)
| |
| quality = tostring(quality or '')
| |
| | |
| if quality:find(';', 1, true) then | |
| quality = array._main({ | | quality = array._main({ |
| quality, | | quality, |
| Line 170: |
Line 66: |
| }) | | }) |
| end | | end |
| | | |
| if lib.isEmpty(quality) then | | -- Make sure quality is uppercase |
| quality = 'Common'
| | quality = quality:upper() |
| end | | |
| | | -- Modifiers: |
| return normalizeClassName(quality) | | local isFirst = args.first or false |
| end
| | local isUp = args.UP or args.up or false |
| | | |
| local function stripLinkPrefix(value) | | -- Attribute/galaxy |
| value = tostring(value or '') | | local galaxy = args.galaxy or nil |
| | | |
| -- Converts "Luca: Myriad Meadows" to "Myriad Meadows". | | if lib.isEmpty(suffix) then |
| return mw.text.trim(value:gsub('^.-:%s*', '', 1)) | | local lcType = type:lower() |
| end
| | if (lcType == 'neighbor') then |
| | | suffix = ' Icon' |
| local function setCaptionWidth(node, width)
| | --Grabs Agent Rank, Attribute and Specialty from Data to display icons |
| if width == 'auto' or lib.isEmpty(width) then | | agQuality = data['quality'] |
| node:addClass('auto-width') | | agAttribute = data['attribute'] |
| else
| | nbGalaxy = data['galaxy'] |
| node:css('width', width) | | |
| end
| | --In Full-Size Card-Mode, if there is no specified text, Agents will default to using their full name displayed underneath. |
| end
| | if args.mini ~= "1" then |
| | | if (label == nil) then label = item end |
| -- Generates the second icon for a split card. | | end |
| local function makeSplitIcon(name, mini)
| | -- In Mini-Size Card-Mode, if there is no specified text, Agents will use their "shortName" pulled from /data as the displayed text. |
| name = tostring(name or '')
| | -- In cases where Agents do not have a "shortName", their full name will be displayed instead. |
| | | if args.mini == "1" then |
| local size = mini and 74 or 56
| | if (label == nil) and data['shortName'] ~= nil then label = data['shortName'] |
| | | elseif (label == nil) and data['shortName'] == nil then label = item |
| return '[[File:' ..
| | end |
| lib.sanitizeFileName(name) ..
| | end |
| ' Icon.png|' ..
| | |
| size .. 'x' .. size ..
| | -- hackish workaround to account for the slight transparent borders on hoyolab agent icons |
| 'px|link=' ..
| | if (tonumber(imageSize) ~= nil) then |
| name ..
| | imageSize = tonumber(imageSize) + 2 |
| ']]'
| |
| end
| |
| | |
| function p._processArgs(args)
| |
| local out = TemplateData.processArgs(args, CARD_ARGS)
| |
| | |
| -- Basic identity
| |
| out.name = out.name or 'Unknown'
| |
| out.split = out.split or ''
| |
| | |
| -- Use only a manually supplied type for lookup.
| |
| local requestedType = out.nonDefaults
| |
| and out.nonDefaults.type
| |
| or nil
| |
| | |
| out.type, out.data = p.getTypeAndData(
| |
| out.name,
| |
| requestedType
| |
| )
| |
| | |
| -- Base name for assets.
| |
| -- Removes a trailing disambiguation such as "(Currency)".
| |
| out.image_name = out.name--:gsub('%s*%([^()]*%)$', '')
| |
| | |
| -- Boolean parameters
| |
| out.mini = normalizeBool(out.mini)
| |
| out.first = normalizeBool(out.first)
| |
| out.up = normalizeBool(out.up)
| |
| out.no_link = normalizeBool(out.no_link)
| |
| out.show_label = normalizeBool(out.show_label)
| |
| out.show_caption = normalizeBool(out.show_caption)
| |
| | |
| -- Image defaults
| |
| out.suffix = out.suffix or ''
| |
| out.image_ext = out.image_ext or 'png'
| |
| | |
| if lib.isEmpty(out.image_size) then
| |
| out.image_size = out.mini and 48 or 70
| |
| end
| |
| | |
| if lib.isEmpty(out.suffix) then
| |
| out.suffix = ' Icon'
| |
| end
| |
| | |
| -- Data-driven defaults
| |
| if out.data then
| |
| if out.data.name then
| |
| out.defaults.label = out.data.name
| |
| out.defaults.caption = out.data.name
| |
| end
| |
| | |
| if out.data.title then
| |
| out.defaults.caption = out.data.title | |
| end
| |
| | |
| if out.data.quality then
| |
| out.defaults.quality = out.data.quality | |
| | |
| if not (out.nonDefaults and out.nonDefaults.quality) then | |
| out.quality = out.data.quality | |
| end | | end |
| | |
| | elseif (lcType == 'creature') then |
| | suffix = ' Icon' |
| | --Grabs Creature Rank and Galaxy from Data to display icons |
| | crQuality = data['quality'] |
| | crGalaxy = data['galaxy'] |
| | |
| | elseif (lcType == 'npc') then |
| | suffix = ' Icon' |
| | |
| | elseif (lcType == 'clothing') then |
| | suffix = ' Icon' |
| | --Grabs Outfit Rank from Data to display icons |
| | outQuality = data['quality'] |
| end | | end |
| end | | end |
|
| |
|
| -- Quality / rarity | | local final_file = args.image or type .. ' ' .. lib.sanitizeFileName(item) .. suffix .. '.' .. imageExt |
| out.quality = normalizeQuality(out.quality) | | |
| | | local final_link = link .. link_suffix |
| -- Label handling | | |
| if out.show_label then | | local file = '[[File:' .. final_file .. '|' .. imageSize .. 'px|alt=' .. item .. '|link=' .. final_link .. ']]' |
| out.label = out.data | | |
| and (out.data.name or out.data.title)
| | local result = mw.html.create() |
| or out.name
| | |
| | local card_container = result:tag('div') |
| | :addClass('card-container') |
| | :addClass('card-quality-' .. quality) |
| | |
| | if args.mini == "1" then |
| | card_container:addClass('mini-card') |
| end | | end |
| | |
| | local card_wrapper = card_container:tag('div') |
| | :addClass('card-wrapper') |
| | |
| | local card_body = card_wrapper:tag('div') |
| | :addClass('card-body') |
| | |
| | local card_link = card_wrapper:tag('div') |
| | :addClass('card-link') |
| | :wikitext('[[' .. final_link .. ']]') |
| | |
| | local card_image = card_body:tag('div') |
| | :addClass('card-image-container') |
| | :wikitext(file) |
|
| |
|
| if out.label == '0' then
| | --[[if label and not (args.label == "0" or args.text == "0") then --]] |
| out.label = nil | | if (label or args.show_text) and (args.label ~= "0" or args.text ~= "0") then |
| end | | if args.show_label == "1" or args.show_text == "1" then |
| | label = data and data.name or item |
| | end |
| | |
| | local card_label = card_container:tag('div') |
| | :addClass('card-label') |
| | :wikitext(label) |
| | end |
|
| |
|
| -- Type-specific behavior | | if (caption or args.show_caption) and args.show_caption ~= "0" then |
| local lcType = tostring(out.type or ''):lower()
| | if args.show_caption == "1" then |
| | | caption = (data and data.name) or item |
| if lcType == 'neighbor' then
| |
| if lib.isEmpty(out.label) then | |
| if out.mini and out.data and out.data.shortName then | |
| out.label = out.data.shortName
| |
| elseif not out.mini then
| |
| out.label = out.name
| |
| end
| |
| end | | end |
| | | local card_caption = card_container:tag('div') |
| if tonumber(out.image_size) then | | :addClass('card-caption') |
| out.image_size = tonumber(out.image_size) + 2 | | :wikitext('[[', final_link, '|', caption, ']]') |
| end
| | |
| end
| | if caption_width == 'auto' or lib.isEmpty(caption_width) then |
| | | card_caption:addClass('auto-width') |
| if lcType == 'redirect' then
| |
| out.defaults.link = stripLinkPrefix(out.name)
| |
| | |
| if not (out.nonDefaults and out.nonDefaults.link) then
| |
| out.link = stripLinkPrefix(out.name) | |
| end | |
| end
| |
| | |
| -- Caption defaults
| |
| if out.show_caption and lib.isEmpty(out.caption) then
| |
| if out.data then
| |
| out.caption = out.data.title | |
| or out.data.name
| |
| or out.name
| |
| else | | else |
| out.caption = out.name | | card_caption:css('width', caption_width) |
| end | | end |
| end | | end |
| | | |
| -- Link handling | | if (isFirst) then |
| if out.link == nil then | | local card_mod_first = card_wrapper:tag('div') |
| out.link = lib.ternary( | | :addClass('card-modifier card-modifier-first') |
| out.name == 'Unknown',
| | :wikitext('[[File:Icon Item First.png|x25px]]') |
| '', | |
| out.name | |
| )
| |
| end | | end |
| | | |
| out.link_suffix = out.link_suffix or ''
| | if (isUp) then |
| out.final_link = out.link .. out.link_suffix | | local card_mod_up = card_wrapper:tag('div') |
| | | :addClass('card-modifier card-modifier-up') |
| if out.no_link then | | :wikitext('[[File:Icon Item UP.png|x25px]]') |
| out.final_link = '' | |
| end | | end |
| | | |
| -- Main image filename | | -- Attribute icon, applied automatically for agents or manually specified for anything else |
| if ne(out.image) then | | if args.mini ~= "1" then -- Only display the icons if mini is not set to "1" |
| out.final_file = out.image
| | if ne(nbGalaxy) then |
| else
| | card_body:addClass('card-galaxy-' .. string.lower(nbGalaxy)) |
| out.final_file =
| | card_body:tag('span') |
| lib.sanitizeFileName(out.image_name) ..
| | :addClass('card-icon-galaxy') |
| out.suffix .. | | :wikitext('[[File:Icon ', nbGalaxy, '.png|alt=', nbGalaxy, '|link=', nbGalaxy, '|20px]]') |
| '.' .. | | end |
| out.image_ext
| | if ne(crGalaxy) then |
| | card_body:addClass('card-galaxy-' .. string.lower(crGalaxy)) |
| | card_body:tag('span') |
| | :addClass('card-icon-galaxy') |
| | :wikitext('[[File:Icon ', crGalaxy, '.png|alt=', crGalaxy, '|link=', crGalaxy, '|20px]]') |
| | end |
| | local notice = args.notice or nil |
| | if ne(notice) then |
| | card_body:tag('span') |
| | :addClass('card-notice-label') |
| | :wikitext(notice) |
| | end |
| end | | end |
| | | |
| -- Main image wikitext | | return tostring(result) |
| local linkPart = out.no_link | |
| and '|link='
| |
| or ('|link=' .. out.final_link)
| |
| | |
| out.image_wikitext = table.concat({
| |
| '[[File:',
| |
| out.final_file,
| |
| '|',
| |
| out.image_size,
| |
| 'px|alt=',
| |
| out.data and (out.data.name or out.data.title)
| |
| or out.name,
| |
| linkPart,
| |
| ']]'
| |
| })
| |
| | |
| return out
| |
| end | | end |
|
| |
|
| function p._main(args) | | function p.getTypeAndData(name, type) |
| local a = p._processArgs(args) | | local img_type = type or 'Item' |
| local isSplit = ne(a.split) | | local img_data = nil |
| | | |
| -- Outer container | | for i, v in ipairs(allData) do |
| local node_container = mw.html.create('div') | | -- Only if the type was either not provided in the card args or if the provided type from card args is equal |
| :addClass('card-container')
| | if lib.isEmpty(type) or v[1] == type then |
| | | local data = v[2][name] |
| if isSplit then
| | if data then |
| node_container:addClass('card-split-card') | | img_type = v[1] |
| end
| | img_data = data |
| | | break |
| if a.mini then
| | end |
| node_container:addClass('mini-card')
| |
| end
| |
| | |
| -- Wrapper and body
| |
| local node_card = node_container:tag('span')
| |
| :addClass('card-wrapper')
| |
| :tag('span')
| |
| :addClass('card-body')
| |
| | |
| -- Galaxy class on body
| |
| if not a.mini and ne(a.galaxy) then
| |
| node_card:addClass(
| |
| 'card-galaxy-' .. normalizeClassName(a.galaxy)
| |
| )
| |
| end
| |
| | |
| -- Main image container
| |
| local imageContainer = node_card:tag('span')
| |
| :addClass('card-image-container')
| |
| :addClass('card-quality-' .. a.quality)
| |
| | |
| -- Split or regular image
| |
| if isSplit then
| |
| imageContainer:addClass('card-split-image')
| |
| | |
| -- First item: top-left half
| |
| imageContainer:tag('span')
| |
| :addClass('card-split-half')
| |
| :addClass('card-split-half-a')
| |
| :wikitext(a.image_wikitext)
| |
| | |
| -- Second item: bottom-right half
| |
| imageContainer:tag('span')
| |
| :addClass('card-split-half')
| |
| :addClass('card-split-half-b')
| |
| :wikitext(makeSplitIcon(a.split, a.mini))
| |
| else
| |
| imageContainer:wikitext(a.image_wikitext) | |
| end
| |
| | |
| -- Notice label
| |
| if not a.mini and ne(a.notice) then
| |
| node_card:tag('span')
| |
| :addClass('card-notice-label')
| |
| :wikitext(a.notice)
| |
| end
| |
| | |
| -- First modifier
| |
| if a.first then
| |
| node_card:tag('span')
| |
| :addClass('card-modifier')
| |
| :addClass('card-modifier-first')
| |
| :wikitext(
| |
| '[[File:Icon Item First.png|x25px|link=|alt=First]]'
| |
| )
| |
| end
| |
| | |
| -- UP modifier
| |
| if a.up then
| |
| node_card:tag('span')
| |
| :addClass('card-modifier')
| |
| :addClass('card-modifier-up')
| |
| :wikitext(
| |
| '[[File:Icon Item UP.png|x25px|link=|alt=UP]]'
| |
| )
| |
| end
| |
| | |
| -- Label below the image
| |
| if ne(a.label) then
| |
| node_container:tag('span')
| |
| :addClass('card-label')
| |
| :wikitext(a.label) | |
| end
| |
| | |
| -- Caption
| |
| -- Split captions are opt-in and use spans instead of list items.
| |
| if a.show_caption and isSplit then
| |
| local node_caption = node_container:tag('span')
| |
| :addClass('card-caption')
| |
| :addClass('card-split-caption')
| |
| | |
| local list = node_caption:tag('span')
| |
| :addClass('card-split-caption-list')
| |
| | |
| list:tag('span')
| |
| :addClass('card-split-caption-item')
| |
| :wikitext('[[' .. a.name .. ']]')
| |
| | |
| list:tag('span')
| |
| :addClass('card-split-caption-item') | |
| :wikitext('[[' .. a.split .. ']]')
| |
| | |
| setCaptionWidth(node_caption, a.caption_width)
| |
| | |
| elseif a.show_caption and ne(a.caption) then
| |
| local node_caption = node_container:tag('span')
| |
| :addClass('card-caption')
| |
| | |
| if a.no_link or lib.isEmpty(a.final_link) then
| |
| node_caption:wikitext(a.caption)
| |
| else
| |
| node_caption:wikitext(
| |
| '[[' .. a.final_link .. '|' .. a.caption .. ']]' | |
| ) | |
| end | | end |
|
| |
| setCaptionWidth(node_caption, a.caption_width)
| |
| end | | end |
| | | |
| return tostring(node_container)
| | return img_type, img_data |
| end
| |
| | |
| function p.templateData(frame)
| |
| return TemplateData.templateData(CARD_ARGS, { | |
| description = 'Creates an item-style card with image, quality, label, split images, modifiers, and optional data-driven icons.',
| |
| format = 'inline'
| |
| }, frame)
| |
| end
| |
| | |
| function p.syntax(frame)
| |
| return TemplateData.syntax(CARD_ARGS, frame) | |
| end | | end |
|
| |
|
| return p | | return p |