Module:Card: Difference between revisions

No edit summary
No edit summary
 
(29 intermediate revisions by 4 users not shown)
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 namespace = require('Module:Namespace_detect')._main
local TemplateData = require('Module:TemplateData')
local getTemplateArgs = require('Module:Parser').getTemplateArgs
local array = require('Module:Array')
local array = require('Module:Array')
local CentralData = require('Module:CentralData')


local allData = {
local allData = CentralData.dataSheets
{'Agent', mw.loadData('Module:CentralData/Agents')},
 
{'W-Engine', mw.loadData('Module:CentralData/W-Engines')},
local CARD_ARGS = {
{'Drive Disc', mw.loadData('Module:CentralData/Drive Discs')},
{
{'Bangboo', mw.loadData('Module:CentralData/Bangboo')},
name = 'name',
{'Enemy', mw.loadData('Module:CentralData/Enemies')},
alias = 1,
{'Resonium', mw.loadData('Module:CentralData/Resonia')},
default = 'Unknown',
{'Item', mw.loadData('Module:CentralData/Items')},
},
{'Companion', mw.loadData('Module:CentralData/Companions')},
{
{'Outfit', mw.loadData('Module:CentralData/Outfits')},
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',
},
}
}
local COLON = ':'


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


function p._main(args)
local function getDataByName(dataType, dataTable, name)
local item = args[1] or args.name or 'Unknown'
local data = dataTable[name]
local label = args[2] or args.label or args.text or args.count or args.amount or args.x or nil
 
if data then
-- Reset agent-specific data to prevent carry-over from previous cards
return data
local agRank, agAttribute, agSpecialty, bbRank, weRank, weSpecialty, outRank, notice = nil, nil, nil, nil, nil, nil, nil, nil
end
 
-- Caption Params:
local normalizedType = tostring(dataType or ''):lower()
local caption = args.caption or nil
if normalizedType == 'neighbor' or normalizedType == 'neighbors' then
local caption_width = args.caption_width or args.captionWidth or nil
return nil
end
    -- Image File Params:
 
local type, data = p.getTypeAndData(item, args.type)
for quality, names in pairs(dataTable) do
local suffix = args.suffix or ''
if type(names) == 'table' then
local imageSize = args.image_size or args.imageSize or lib.ternary(args.mini == '1', 48, 70)
for _, itemName in ipairs(names) do
local imageExt = args.image_ext or args.imageExt or 'png'
if itemName == name then
return {
-- Link Params:
quality = quality
local link = args.link or lib.ternary(item == 'Unknown', '', item)
}
local link_suffix = args.link_suffix or ''
end
end
-- Rank Param:
local rank = args.rank or args.quality or args.rarity or nil
-- Try to get Rank From Data
if lib.isEmpty(rank) and data then
if data.rank then
rank = data.rank
end
end
end
end
 
-- If rank is still empty, set it to "C"
return nil
if lib.isEmpty(rank) then
end
rank = 'C'
 
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
 
-- If rank contains a semicolon, then split and get first
return img_type, img_data
if string.find(rank, ";") then
end
rank = array._main({
 
rank,
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,
';',
';',
firstItemOnly = 1
firstItemOnly = 1
})
})
end
end
 
-- Make sure rank is uppercase
if lib.isEmpty(quality) then
rank = rank:upper()
quality = 'Common'
end
-- Modifiers:
 
local isFirst = args.first or false
return normalizeClassName(quality)
local isUp = args.UP or args.up or false
end
 
-- Attribute/specialty
local function stripLinkPrefix(value)
local attribute = args.attr or args.attribute or nil
value = tostring(value or '')
local specialty = args.spec or args.fightSpec or args.fs 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 == 'agent') 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
agRank = data['rank']
node:addClass('auto-width')
agAttribute = data['attribute']
else
agSpecialty = data['specialty']
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
end
 
elseif (lcType == 'bangboo') then
function p._processArgs(args)
suffix = ' Icon'
local out = TemplateData.processArgs(args, CARD_ARGS)
--Grabs Bangboo Rank from Data to display icons
 
bbRank = data['rank']
-- Basic identity
out.name = out.name or 'Unknown'
elseif (lcType == 'w-engine') then
out.split = out.split or ''
suffix = ' Icon'
 
--Grabs W-Engine Rank and Specialty from Data to display icons
-- Use only a manually supplied type for lookup.
weRank = data['rank']
local requestedType = out.nonDefaults
weSpecialty = data['specialty']
and out.nonDefaults.type
or nil
elseif (lcType == 'enemy') then
 
suffix = '' --icon
out.type, out.data = p.getTypeAndData(
enemyType = data and data['type'] or ''
out.name,
enemyTier = data and data['tier'] or ''
requestedType
enemyWeakness = data and data['weakness'] or ''
)
imageSize = lib.ternary(args.mini == '1', 48, 70)
 
-- Base name for assets.
elseif (lcType == 'drive disc') then
-- Removes a trailing disambiguation such as "(Currency)".
suffix = ' Icon'
out.image_name = out.name--:gsub('%s*%([^()]*%)$', '')
 
elseif (lcType == 'npc') then
-- Boolean parameters
suffix = ' Icon'
out.mini = normalizeBool(out.mini)
out.first = normalizeBool(out.first)
elseif (lcType == 'outfit') then
out.up = normalizeBool(out.up)
suffix = ' Icon'
out.no_link = normalizeBool(out.no_link)
--Grabs Outfit Rank from Data to display icons
out.show_label = normalizeBool(out.show_label)
outRank = data['rank']
out.show_caption = normalizeBool(out.show_caption)
end
 
-- 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
end


local final_file = args.image or type .. ' ' .. lib.sanitizeFileName(item) .. suffix .. '.' .. imageExt
if lib.isEmpty(out.suffix) then
out.suffix = ' Icon'
local final_link = link .. link_suffix
local file = '[[File:' .. final_file .. '|' .. imageSize .. 'px|alt=' .. item .. '|link=' .. final_link .. ']]'
local result = mw.html.create()
local card_container = result:tag('div')
:addClass('card-container')
:addClass('card-rank-' .. rank)
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 label and not (args.label == "0" or args.text == "0") then --]]
-- Data-driven defaults
    if (label or args.show_text) and (args.label ~= "0" or args.text ~= "0") then  
if out.data then
if args.show_label == "1" or args.show_text == "1" then
if out.data.name then
label = data and data.name or item
out.defaults.label = out.data.name
end
out.defaults.caption = out.data.name
end
local card_label = card_container:tag('div')
:addClass('card-label')
:wikitext(label)
end


if (caption or args.show_caption) and args.show_caption ~= "0" then
if out.data.title then
if args.show_caption == "1" then
out.defaults.caption = out.data.title
caption = (data and data.name) or item
end
end
local card_caption = card_container:tag('div')
 
:addClass('card-caption')
if out.data.quality then
:wikitext('[[', final_link, '|', caption, ']]')
out.defaults.quality = out.data.quality
 
if caption_width == 'auto' or lib.isEmpty(caption_width) then
if not (out.nonDefaults and out.nonDefaults.quality) then
card_caption:addClass('auto-width')
out.quality = out.data.quality
else
end
card_caption:css('width', caption_width)
end
end
end
end
 
if (isFirst) then
-- Quality / rarity
local card_mod_first = card_wrapper:tag('div')
out.quality = normalizeQuality(out.quality)
:addClass('card-modifier card-modifier-first')
 
:wikitext('[[File:Icon Item First.png|x25px]]')
-- Label handling
if out.show_label then
out.label = out.data
and (out.data.name or out.data.title)
or out.name
end
end
 
if (isUp) then
if out.label == '0' then
local card_mod_up = card_wrapper:tag('div')
out.label = nil
:addClass('card-modifier card-modifier-up')
:wikitext('[[File:Icon Item UP.png|x25px]]')
end
end
 
-- Attribute icon, applied automatically for agents or manually specified for anything else
-- Type-specific behavior
if args.mini ~= "1" then -- Only display the icons if mini is not set to "1"
local lcType = tostring(out.type or ''):lower()
if ne(agRank) then
 
card_body:addClass('card-rank-' .. string.lower(agRank))
if lcType == 'neighbor' then
card_body:tag('span')
if lib.isEmpty(out.label) then
:addClass('card-icon-rank')
if out.mini and out.data and out.data.shortName then
:wikitext('[[File:Icon AgentRank ', agRank, '.png|alt=', agRank, '|link=|20px]]')
out.label = out.data.shortName
elseif not out.mini then
out.label = out.name
end
end
end
if ne(bbRank) then
 
card_body:addClass('card-rank-' .. string.lower(bbRank))
if tonumber(out.image_size) then
card_body:tag('span')
out.image_size = tonumber(out.image_size) + 2
:addClass('card-icon-rank')
:wikitext('[[File:Icon Bangboo Rank ', bbRank, '.png|alt=', bbRank, '|link=|20px]]')
end
end
if ne(weRank) then
end
card_body:addClass('card-rank-' .. string.lower(weRank))
 
card_body:tag('span')
if lcType == 'redirect' then
:addClass('card-icon-rank')
out.defaults.link = stripLinkPrefix(out.name)
:wikitext('[[File:Icon AgentRank ', weRank, '.png|alt=', weRank, '|link=|20px]]')
 
if not (out.nonDefaults and out.nonDefaults.link) then
out.link = stripLinkPrefix(out.name)
end
end
if ne(agAttribute) then
end
card_body:addClass('card-attribute-' .. string.lower(agAttribute))
 
card_body:tag('span')
-- Caption defaults
:addClass('card-icon-attribute')
if out.show_caption and lib.isEmpty(out.caption) then
:wikitext('[[File:Icon ', agAttribute, '.png|alt=', agAttribute, '|link=', agAttribute, '|20px]]')
if out.data then
end
out.caption = out.data.title
if ne(agSpecialty) then
or out.data.name
card_body:addClass('card-specialty-' .. string.lower(agSpecialty))
or out.name
card_body:tag('span')
else
:addClass('card-icon-specialty')
out.caption = out.name
:wikitext('[[File:Icon ', agSpecialty, '.png|alt=', agSpecialty, '|link=', agSpecialty, '|20px]]')
end
if ne(weSpecialty) then
card_body:addClass('card-specialty-' .. string.lower(weSpecialty))
card_body:tag('span')
:addClass('card-icon-specialty')
:wikitext('[[File:Icon ', weSpecialty, '.png|alt=', weSpecialty, '|link=', weSpecialty, '|20px]]')
end
if ne(outRank) then
card_body:addClass('card-rank-' .. string.lower(outRank))
card_body:tag('span')
:addClass('card-icon-rank')
:wikitext('[[File:Icon Item Rank ', outRank, '.png|alt=', outRank, '|link=|20px]]')
end
local cinema = args.cinema or args.c or nil
if ne(cinema) then
card_body:tag('span')
:addClass('card-cinema')
:addClass('cinema-' .. cinema)
:wikitext(' C', cinema)
card_body:addClass('card-with-cinema')
end
local phase = args.phase or args.p or nil
if ne(phase) then
card_body:tag('span')
:addClass('card-phase')
:addClass('phase-' .. phase)
:wikitext(' P', phase)
card_body:addClass('card-with-phase')
end
    local notice = args.notice or nil
    if ne(notice) then
    card_body:tag('span')
:addClass('card-notice-label')
:wikitext(notice)
    end
if ne(enemyType) then
card_body:tag('span')
:addClass('card-icon-enemy-type')
:wikitext('[[File:', enemyType, ' Icon.png|alt=', enemyType, '|link=', enemyType, '|28px]]')
end
if ne(enemyWeakness) then
local weaknessContainer = card_body:tag('span')
:addClass('card-enemy-weaknesses')
for weakness in lib.gsplit(enemyWeakness, ';') do
weaknessContainer:wikitext('[[File:Icon ', weakness, '.png|16px|link=', weakness, '|Weak to ', weakness, ']]')
end
end
end
end
end
 
if ne(enemyTier) then
-- Link handling
card_body:addClass('card-enemy card-enemy-' .. enemyTier:gsub(' ', ''))
if out.link == nil then
out.link = lib.ternary(
out.name == 'Unknown',
'',
out.name
)
end
 
out.link_suffix = out.link_suffix or ''
out.final_link = out.link .. out.link_suffix
 
if out.no_link then
out.final_link = ''
end
 
-- Main image filename
if ne(out.image) then
out.final_file = out.image
else
out.final_file =
lib.sanitizeFileName(out.image_name) ..
out.suffix ..
'.' ..
out.image_ext
end
end
 
return tostring(result)
-- Main image wikitext
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.getTypeAndData(name, type)
function p._main(args)
local img_type = type or 'Item'
local a = p._processArgs(args)
local img_data = nil
local isSplit = ne(a.split)
 
for i, v in ipairs(allData) do
-- Outer container
-- Only if the type was either not provided in the card args or if the provided type from card args is equal
local node_container = mw.html.create('div')
if lib.isEmpty(type) or v[1] == type then
:addClass('card-container')
local data = v[2][name]
 
if data then
if isSplit then
img_type = v[1]
node_container:addClass('card-split-card')
img_data = data
end
break
 
end
if a.mini then
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 img_type, img_data
return tostring(node_container)
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