Módulo:Character
Ir para navegação
Ir para pesquisar
A documentação para este módulo pode ser criada em Módulo:Character/doc
local p = {}
--------------------------------------------------------------------------------
-- Utils
--------------------------------------------------------------------------------
local function trim(s)
return (tostring(s or ""):gsub("^%s+", ""):gsub("%s+$", ""))
end
-- Gera URL pública de arquivo (tenta "Special:" e "Especial:" por compat)
local function fileURL(name)
name = trim(name or "")
if name == "" then
return ""
end
-- tenta pegar pela sintaxe wiki [[Arquivo:...]] mantendo só o nome
local base = mw.title.makeTitle("Arquivo", name)
if base and base.file and base.file.exists then
return string.format("[[Arquivo:%s|link=]]", base.text)
end
-- fallback: retorna marcação padrão (o widget costuma renderizar a tag <img> final)
return string.format("[[Arquivo:%s|link=]]", name)
end
-- helper JSON
local function jenc(tbl)
return mw.text.jsonEncode(tbl)
end
local function jdec(s)
local ok, r = pcall(mw.text.jsonDecode, s)
if ok then return r end
return nil
end
--------------------------------------------------------------------------------
-- i18n estático (rótulos)
--------------------------------------------------------------------------------
local I18N = {
pt = {
skills = "Habilidades",
skills_title = "HABILIDADES E PRÉVIAS",
subskills = "Subskills",
skins = "Skins",
skins_title = "SKINS E PRÉVIAS"
},
en = {
skills = "Skills",
skills_title = "SKILLS & PREVIEWS",
subskills = "Subskills",
skins = "Skins",
skins_title = "SKINS & PREVIEWS"
},
es = {
skills = "Habilidades",
skills_title = "HABILIDADES Y PREVIAS",
subskills = "Subskills",
skins = "Aspectos",
skins_title = "ASPECTOS Y PREVIAS"
},
pl = {
skills = "Umiejętności",
skills_title = "UMIEJĘTNOŚCI I PODGLĄDY",
subskills = "Subskills",
skins = "Skórki",
skins_title = "SKÓRKI I PREZENTACJE"
}
}
--------------------------------------------------------------------------------
-- Serializer de skin (para {{Skin}})
--------------------------------------------------------------------------------
function p.skin(frame)
local a = frame.args
local function nz(s)
s = tostring(s or "")
return (mw.text.trim(s) ~= "" and s or nil)
end
-- i18n pack (opcional, mantém legado)
local pack = {
pt = nz(a.tooltip_pt),
en = nz(a.tooltip_en),
es = nz(a.tooltip_es),
pl = nz(a.tooltip_pl),
}
local tooltip
if pack.pt or pack.en or pack.es or pack.pl then
tooltip = mw.text.jsonEncode(pack)
else
tooltip = a.tooltip or ''
end
local obj = {
sprite = a.sprite or '',
background = a.background or '',
tooltip = tooltip,
youtube = a.youtube or '',
source = a.source or ''
}
return mw.text.jsonEncode(obj)
end
--------------------------------------------------------------------------------
-- Componente principal
--------------------------------------------------------------------------------
function p.generate(frame)
local parent = frame:getParent() or frame
local args = parent.args or {}
local html = mw.html.create('div')
local box = html:tag('div'):addClass('character-box')
----------------------------------------------------------------------------
-- Background (suporta |background=Arquivo.png)
----------------------------------------------------------------------------
local bgFile = trim(args.background or "")
if bgFile ~= "" then
box:addClass('has-background')
box:attr('data-background', bgFile)
box:wikitext(string.format('[[Arquivo:%s|link=]]', bgFile))
end
----------------------------------------------------------------------------
-- Header (avatar, título etc.)
----------------------------------------------------------------------------
local header = box:tag('div'):addClass('character-header')
local avatar = trim(args.avatar or "")
if avatar ~= "" then
header:tag('div'):addClass('avatar'):wikitext(string.format('[[Arquivo:%s|link=]]', avatar))
end
local hbox = header:tag('div'):addClass('header-col')
local title = trim(args.nome or args.name or "")
if title ~= "" then
hbox:tag('h2'):addClass('character-title'):wikitext(title)
end
local desc = trim(args.desc or "")
if desc ~= "" then
hbox:tag('div'):addClass('character-desc'):wikitext(desc)
end
-- tags/tier container => a widget lê data-* pra i18n
local tagsWrap = hbox:tag('div'):addClass('class-tags')
local tier = trim(args.tier or "")
if tier ~= "" then
local el = tagsWrap:tag('span'):addClass('class-tag tier'):wikitext(tier)
-- se vierem traduções em data-tier-xx, o widget troca
if args['tier_pt'] then el:attr('data-tier-pt', trim(args['tier_pt'])) end
if args['tier_en'] then el:attr('data-tier-en', trim(args['tier_en'])) end
if args['tier_es'] then el:attr('data-tier-es', trim(args['tier_es'])) end
if args['tier_pl'] then el:attr('data-tier-pl', trim(args['tier_pl'])) end
end
-- tags i18n: aceita JSON (["DPS","Lutador"]) ou CSV
local tags_i18n = {
pt = trim(args['tags_pt'] or ""),
en = trim(args['tags_en'] or ""),
es = trim(args['tags_es'] or ""),
pl = trim(args['tags_pl'] or "")
}
local function normalizeTags(raw)
if raw == "" then return {} end
local obj = jdec(raw)
if type(obj) == "table" then return obj end
-- CSV
local out = {}
for token in mw.text.gsplit(raw, ",", true) do
local t = trim(token)
if t ~= "" then table.insert(out, t) end
end
return out
end
tagsWrap:attr('data-tags-pt', jenc(normalizeTags(tags_i18n.pt)))
tagsWrap:attr('data-tags-en', jenc(normalizeTags(tags_i18n.en)))
tagsWrap:attr('data-tags-es', jenc(normalizeTags(tags_i18n.es)))
tagsWrap:attr('data-tags-pl', jenc(normalizeTags(tags_i18n.pl)))
----------------------------------------------------------------------------
-- Tabs
----------------------------------------------------------------------------
local tabs = box:tag('div'):addClass('character-tabs')
local tabsBar = tabs:tag('div'):addClass('tabs-bar')
local function addTab(id, label)
local b = tabsBar:tag('button'):addClass('tab-btn'):attr('data-tab', id):wikitext(label)
return b
end
addTab('skills', I18N.pt.skills)
addTab('skins', I18N.pt.skins)
local panels = tabs:tag('div'):addClass('tab-panels')
----------------------------------------------------------------------------
-- Painel: Skills (ícones + preview)
----------------------------------------------------------------------------
local skillsPanel = panels:tag('section'):addClass('tab-panel'):attr('data-panel', 'skills')
local skillsHeader = skillsPanel:tag('div'):addClass('panel-header')
skillsHeader:tag('h3'):addClass('panel-title'):wikitext(I18N.pt.skills_title)
local iconBar = skillsPanel:tag('div'):addClass('icon-bar')
local preview = skillsPanel:tag('div'):addClass('preview')
-- o args.skills vem como JSON de Módulo:Character (array de skills)
local skillsJSON = trim(args.skills or "")
if skillsJSON ~= "" then
local ok, arr = pcall(mw.text.jsonDecode, skillsJSON)
if ok and type(arr) == "table" then
for _, sk in ipairs(arr) do
local icon = iconBar:tag('div'):addClass('skill-icon')
icon:attr('data-id', trim(sk.id or ""))
icon:attr('data-name', trim(sk.name or ""))
icon:attr('data-video', trim(sk.preview or ""))
-- descrições por idioma (o widget troca em runtime)
if sk.descPt then icon:attr('data-desc-pt', trim(sk.descPt)) end
if sk.descEn then icon:attr('data-desc-en', trim(sk.descEn)) end
if sk.descEs then icon:attr('data-desc-es', trim(sk.descEs)) end
if sk.descPl then icon:attr('data-desc-pl', trim(sk.descPl)) end
local file = trim(sk.icon or "")
if file ~= "" then
icon:wikitext(string.format('[[Arquivo:%s|link=]]', file))
end
end
end
end
-- preview container básico
preview:tag('div'):addClass('video-box')
preview:tag('div'):addClass('desc-box')
----------------------------------------------------------------------------
-- Painel: Skins (carrossel)
----------------------------------------------------------------------------
local skinsPanel = panels:tag('section'):addClass('tab-panel'):attr('data-panel', 'skins')
local skinsHeader = skinsPanel:tag('div'):addClass('panel-header')
skinsHeader:tag('h3'):addClass('panel-title'):wikitext(I18N.pt.skins_title)
local carousel = skinsPanel:tag('div'):addClass('skins-carousel')
local skinsJSON = trim(args.skins or "")
if skinsJSON ~= "" then
local ok, arr = pcall(mw.text.jsonDecode, skinsJSON)
if ok and type(arr) == "table" then
for _, chunk in ipairs(arr) do
local sk = {}
if type(chunk) == "string" then
local ok2, decoded = pcall(mw.text.jsonDecode, chunk)
if ok2 and type(decoded) == "table" then
sk = decoded
end
elseif type(chunk) == "table" then
sk = chunk
end
-- banner, sprite, tooltip (i18n aware), youtube
local bannerFile = trim(sk.background or "")
local imageFile = trim(sk.sprite or "")
local tooltipRaw = trim(sk.tooltip or "")
local tooltipHtml = ""
local tipPack = nil
-- If tooltip is JSON (i18n), keep the pack and render PT (or fallback) initially
if tooltipRaw:match("^%s*{") then
local ok2, obj2 = pcall(mw.text.jsonDecode, tooltipRaw)
if ok2 and type(obj2) == "table" then
tipPack = obj2
local base = trim(obj2.pt or obj2.en or obj2.es or obj2.pl or "")
if base ~= "" then
tooltipHtml = base:gsub("'''([^']+)'''", "<b>%1</b>"):gsub("\n", "<br>")
end
end
end
-- Legacy plain text
if tooltipHtml == "" then
tooltipHtml = tooltipRaw:gsub("'''([^']+)'''", "<b>%1</b>"):gsub("\n", "<br>")
end
local skinCard = carousel:tag('div'):addClass('skin-card'):attr('data-skin-tooltip', tooltipHtml)
if tipPack then
skinCard:attr('data-skin-tooltip-i18n', mw.text.jsonEncode(tipPack))
end
-- Spotlight do YouTube (opcional)
local yt = trim(sk.youtube or "")
if yt ~= "" then
skinCard:attr('data-youtube', yt):addClass('is-clickable')
end
skinCard:tag('div'):addClass('skin-banner'):wikitext(bannerFile ~= "" and
string.format('[[Arquivo:%s|link=]]', bannerFile) or
""):attr('alt', 'banner')
skinCard:tag('div'):addClass('skin-sprite'):wikitext(imageFile ~= "" and
string.format('[[Arquivo:%s|link=]]', imageFile) or
""):attr('alt', 'sprite')
if sk.source and trim(sk.source) ~= "" then
skinCard:attr('data-source', trim(sk.source))
end
end
end
end
----------------------------------------------------------------------------
-- Rodapé/slots extras se necessários
----------------------------------------------------------------------------
-- (mantido vazio; widgets complementam com JS/CSS)
return tostring(html)
end
--------------------------------------------------------------------------------
-- Carregamento de dados do personagem via Módulo:<Nome>
--------------------------------------------------------------------------------
-- Convenção: Módulo:<Character> retorna tabela com:
-- tier, tags (array),
-- i18n (tier_i18n, tags_i18n), skills (array com descPt/descEn/descEs/descPl), skins (array)
-- Este "info.lua" é quem prepara args.skills/args.skins como JSON pro #invoke:Character|generate
-- (O restante do arquivo com require, helpers e p.generate já está acima. Mantenha os módulos externos intactos.)
--------------------------------------------------------------------------------
-- Retorno
--------------------------------------------------------------------------------
return p