Module:Campeao: Difference between revisions
Appearance
Painel de Habilidades com seletor e detalhe, no formato da referencia |
Requisitos mais completos: itens de cura, tempo limite, limite semanal e salas |
||
| (9 intermediate revisions by the same user not shown) | |||
| Line 22: | Line 22: | ||
-- no placeholder, entao a ficha ja fica montada e so troca a imagem depois. | -- no placeholder, entao a ficha ja fica montada e so troca a imagem depois. | ||
local function chip(nomeItem, qtd) | local function chip(nomeItem, qtd) | ||
local | -- Os sprites do jogo sao GIF animado na maioria, e PNG em alguns. Procura | ||
-- as duas extensoes antes de cair no placeholder, para nao perder animacao. | |||
local base = 'Item ' .. nomeItem:gsub('[%(%):]', ''):gsub('%s+', ' ') | |||
local usar = PLACEHOLDER | |||
for _, ext in ipairs({ '.gif', '.png' }) do | |||
local t = mw.title.new('File:' .. base .. ext) | |||
if t and t.fileExists then | |||
usar = base .. ext | |||
break | |||
end | |||
end | |||
local w = mw.html.create('span'):addClass('cp-item') | local w = mw.html.create('span'):addClass('cp-item') | ||
| Line 30: | Line 38: | ||
:wikitext('[[File:' .. usar .. '|40px|link=|alt=' .. nomeItem .. ']]') | :wikitext('[[File:' .. usar .. '|40px|link=|alt=' .. nomeItem .. ']]') | ||
if qtd and qtd ~= '' then | if qtd and qtd ~= '' then | ||
w:tag('span'):addClass('cp-item-qty'):wikitext(qtd) | -- "25 a 50" vira "x25-50": o rotulo pendura embaixo do icone e precisa | ||
-- ser curto. O x fica num span proprio para poder ser apagado na cor. | |||
local etiqueta = w:tag('span'):addClass('cp-item-qty') | |||
etiqueta:tag('span'):addClass('cp-item-qty-x'):wikitext('×') | |||
etiqueta:wikitext((qtd:gsub(' a ', '–'))) | |||
end | end | ||
local tip = w:tag('span'):addClass('cp-tip') | local tip = w:tag('span'):addClass('cp-tip') | ||
| Line 85: | Line 97: | ||
local ul = box:tag('ul') | local ul = box:tag('ul') | ||
ul:tag('li'):wikitext("Nível '''" .. ((dif == 'Hard') and '425' or '350') .. "''' ou superior.") | ul:tag('li'):wikitext("Nível '''" .. ((dif == 'Hard') and '425' or '350') .. "''' ou superior.") | ||
ul:tag('li'):wikitext("Equipe de '''exatamente 3 jogadores'''.") | ul:tag('li'):wikitext("Equipe de '''exatamente 3 jogadores''' — não dá para entrar sozinho nem em dupla.") | ||
ul:tag('li'):wikitext("'''1 Pokémon''' por jogador, sem troca durante a batalha.") | ul:tag('li'):wikitext("'''1 Pokémon''' por jogador, sem troca durante a batalha.") | ||
ul:tag('li'):wikitext("'''1 Champion's Ticket''' para entrar.") | ul:tag('li'):wikitext("'''1 Champion's Ticket''' para entrar.") | ||
ul:tag('li'):wikitext('Acesso liberado com o ' .. (c.aceTrainer or '') .. '.') | ul:tag('li'):wikitext('Acesso liberado com o ' .. (c.aceTrainer or '') .. '.') | ||
ul:tag('li'):wikitext("Sem '''Potions''', '''Revives''' nem itens de cura.") | |||
if c.tempoLimite then | |||
ul:tag('li'):wikitext("A batalha dura no máximo '''" .. c.tempoLimite .. " minutos'''.") | |||
end | |||
if dados.config and dados.config.limiteSemanal then | |||
ul:tag('li'):wikitext("Limite de '''" .. dados.config.limiteSemanal .. " conclusões''' por semana.") | |||
end | |||
if c.salas then | |||
ul:tag('li'):wikitext("O painel mostra quantas das '''" .. c.salas | |||
.. " salas''' do Campeão estão ocupadas.") | |||
end | |||
if dif == 'Hard' then | if dif == 'Hard' then | ||
ul:tag('li'):wikitext("O Campeão enfrentado é a versão '''Shiny'''.") | ul:tag('li'):wikitext("O Campeão enfrentado é a versão '''Shiny'''.") | ||
| Line 116: | Line 139: | ||
local difs = { 'Normal' } | local difs = { 'Normal' } | ||
if c.hard then table.insert(difs, 'Hard') end | if c.hard then table.insert(difs, 'Hard') end | ||
-- Esconde o titulo da pagina: a ficha ja exibe o nome no cabecalho. Feito | |||
-- por DISPLAYTITLE em vez de :has() no CSS, que so existe em navegador | |||
-- recente. O texto dentro do span e o proprio titulo, senao o MediaWiki | |||
-- recusa a troca. | |||
frame:callParserFunction('DISPLAYTITLE', | |||
'<span class="cp-h1-oculto">' .. mw.title.getCurrentTitle().text .. '</span>') | |||
local raiz = mw.html.create('div'):addClass('cp-boss'):attr('data-tema', 'claro') | local raiz = mw.html.create('div'):addClass('cp-boss'):attr('data-tema', 'claro') | ||
| Line 132: | Line 162: | ||
info:tag('div'):addClass('cp-title'):wikitext(c.nome) | info:tag('div'):addClass('cp-title'):wikitext(c.nome) | ||
info:tag('span'):addClass('cp-badge'):wikitext("Campeão das Expedições") | info:tag('span'):addClass('cp-badge'):wikitext("Campeão das Expedições") | ||
local stats = info:tag('div'):addClass('cp-stats') | local stats = info:tag('div'):addClass('cp-stats') | ||
stats:node(stat('Expedition', c.expedition)) | stats:node(stat('Expedition', c.expedition)) | ||
| Line 167: | Line 194: | ||
for i, dif in ipairs(difs) do | for i, dif in ipairs(difs) do | ||
local lista = c.habilidades and c.habilidades[dif:lower()] or {} | local lista = c.habilidades and c.habilidades[dif:lower()] or {} | ||
if | -- lista[1] em vez de #lista: mw.loadData guarda os valores no metatable, | ||
-- entao o operador # devolve zero mesmo com a tabela cheia. | |||
if lista[1] then | |||
temAlguma = true | temAlguma = true | ||
local grupo = pHab:tag('div'):addClass('cp-skills'):attr('data-dif', dif:lower()) | local grupo = pHab:tag('div'):addClass('cp-skills'):attr('data-dif', dif:lower()) | ||
| Line 176: | Line 205: | ||
local b = barra:tag('span'):addClass('cp-skill-pick') | local b = barra:tag('span'):addClass('cp-skill-pick') | ||
:attr('data-skill', tostring(n)):attr('role', 'button') | :attr('data-skill', tostring(n)):attr('role', 'button') | ||
:attr('tabindex', '0'):wikitext(h.nome) | :attr('tabindex', '0'):attr('title', h.nome) | ||
if h.icone and h.icone ~= '' then | |||
b:wikitext('[[File:' .. h.icone .. '|36px|link=|alt=' .. h.nome .. ']]') | |||
else | |||
-- Sem icone ainda: quadrado neutro, como na referencia. | |||
b:addClass('cp-skill-pick--vazio'):wikitext('img') | |||
end | |||
if n == 1 then b:addClass('is-active') end | if n == 1 then b:addClass('is-active') end | ||
end | end | ||
| Line 184: | Line 219: | ||
:attr('data-skill', tostring(n)) | :attr('data-skill', tostring(n)) | ||
if n > 1 then det:addClass('is-hidden') end | if n > 1 then det:addClass('is-hidden') end | ||
det:tag('div'):addClass('cp-skill-name'):wikitext(h.nome) | local corpo = det:tag('div'):addClass('cp-skill-body') | ||
local esq = corpo:tag('div'):addClass('cp-skill-texto') | |||
esq:tag('div'):addClass('cp-skill-name'):wikitext(h.nome) | |||
local meta = | local meta = esq:tag('div'):addClass('cp-skill-meta') | ||
meta:tag('span'):wikitext(h.tipo) | meta:tag('span'):wikitext(h.tipo) | ||
if h.elemento and h.elemento ~= '' then | if h.elemento and h.elemento ~= '' then | ||
meta:tag('span'):wikitext(h.elemento) | meta:tag('span'):wikitext(h.elemento) | ||
end | end | ||
if h.chance and h.chance < 100 then | if h.chance and h.chance < 100 then | ||
| Line 204: | Line 236: | ||
if h.desc and h.desc ~= '' then | if h.desc and h.desc ~= '' then | ||
esq:tag('div'):addClass('cp-skill-desc'):wikitext(h.desc) | |||
else | else | ||
esq:tag('div'):addClass('cp-skill-desc cp-empty') | |||
:wikitext('Descrição ainda não documentada.') | :wikitext('Descrição ainda não documentada.') | ||
end | end | ||
local dir = corpo:tag('div'):addClass('cp-skill-midia') | |||
if h.video and h.video ~= '' then | if h.video and h.video ~= '' then | ||
-- .mp4 e .webm nao podem ser <video> direto no wikitext: o | |||
:wikitext('[[File:' .. h.video .. ' | -- sanitizador remove a tag, por isso o Widget. | ||
if h.video:lower():match('%.mp4$') or h.video:lower():match('%.webm$') then | |||
dir:wikitext('{{#widget:Video|url={{filepath:' .. h.video .. '}}|width=480}}') | |||
else | |||
dir:wikitext('[[File:' .. h.video .. '|480px|link=]]') | |||
end | |||
else | else | ||
-- Placeholder preto, do tamanho do video que vai entrar ali. | |||
dir:tag('div'):addClass('cp-skill-video-vazio') | |||
end | end | ||
end | end | ||
Latest revision as of 17:49, 12 September 2026
Documentation for this module may be created at Module:Campeao/doc
-- Renderizador das fichas de Campeao do Champion's Path.
--
-- A pagina de cada Campeao deve conter uma linha e nada mais:
-- {{Campeao|Tyranitar}}
--
-- Todo o layout vive aqui. Se for preciso duplicar marcacao entre dois
-- Campeoes, o erro esta neste modulo, nao na pagina.
local p = {}
local dados = mw.loadData('Module:Campeao/dados')
local PLACEHOLDER = 'ArquivoExemplo.png'
-- Formata 1000000 como "1.000.000"
local function numero(n)
local s = tostring(n)
local r = s:reverse():gsub('(%d%d%d)', '%1.'):reverse()
return (r:gsub('^%.', ''))
end
-- Chip de item: icone, contador e tooltip. Enquanto nao houver sprite, cai
-- no placeholder, entao a ficha ja fica montada e so troca a imagem depois.
local function chip(nomeItem, qtd)
-- Os sprites do jogo sao GIF animado na maioria, e PNG em alguns. Procura
-- as duas extensoes antes de cair no placeholder, para nao perder animacao.
local base = 'Item ' .. nomeItem:gsub('[%(%):]', ''):gsub('%s+', ' ')
local usar = PLACEHOLDER
for _, ext in ipairs({ '.gif', '.png' }) do
local t = mw.title.new('File:' .. base .. ext)
if t and t.fileExists then
usar = base .. ext
break
end
end
local w = mw.html.create('span'):addClass('cp-item')
w:tag('span'):addClass('cp-item-icon')
:wikitext('[[File:' .. usar .. '|40px|link=|alt=' .. nomeItem .. ']]')
if qtd and qtd ~= '' then
-- "25 a 50" vira "x25-50": o rotulo pendura embaixo do icone e precisa
-- ser curto. O x fica num span proprio para poder ser apagado na cor.
local etiqueta = w:tag('span'):addClass('cp-item-qty')
etiqueta:tag('span'):addClass('cp-item-qty-x'):wikitext('×')
etiqueta:wikitext((qtd:gsub(' a ', '–')))
end
local tip = w:tag('span'):addClass('cp-tip')
tip:tag('span'):addClass('cp-tip-name'):wikitext(nomeItem)
if qtd and qtd ~= '' then
tip:tag('span'):addClass('cp-tip-qty'):wikitext('Quantidade: ' .. qtd)
end
return w
end
local function stat(rotulo, valor)
local d = mw.html.create('div'):addClass('cp-stat')
d:tag('span'):addClass('cp-stat-label'):wikitext(rotulo)
d:tag('span'):addClass('cp-stat-value'):wikitext(valor)
return d
end
local function painel(titulo, modificador)
local s = mw.html.create('div'):addClass('cp-panel')
if modificador then s:addClass('cp-panel--' .. modificador) end
-- div, nao h3: um heading de verdade faria o MediaWiki criar secao e
-- indice automatico dentro do painel, alem de reestruturar o HTML.
s:tag('div'):addClass('cp-panel-title'):wikitext(titulo)
return s
end
-- Recompensas de uma dificuldade: EXP em destaque e os itens como chips.
local function recompensas(c, dif)
local box = mw.html.create('div'):addClass('cp-rewards'):attr('data-dif', dif:lower())
local exp = (dif == 'Hard') and c.expHard or c.expNormal
if exp then
box:tag('div'):addClass('cp-rewards-label'):wikitext('Experiência')
local linha = box:tag('div'):addClass('cp-rewards-row')
linha:tag('span'):addClass('cp-exp'):wikitext(numero(exp) .. ' EXP')
end
box:tag('div'):addClass('cp-rewards-label'):wikitext('Itens possíveis')
local linha = box:tag('div'):addClass('cp-rewards-row')
for _, r in ipairs(dados.loot[dif] or {}) do
linha:node(chip(r.item, r.qtd))
end
if c.megaStone and c.megaStone ~= '' then
linha:node(chip(c.megaStone, '1'))
end
box:tag('div'):addClass('cp-rewards-hint')
:wikitext('Passe o mouse sobre um item para ver o nome.')
return box
end
local function requisitos(c, dif)
local box = mw.html.create('div'):addClass('cp-req'):attr('data-dif', dif:lower())
local ul = box:tag('ul')
ul:tag('li'):wikitext("Nível '''" .. ((dif == 'Hard') and '425' or '350') .. "''' ou superior.")
ul:tag('li'):wikitext("Equipe de '''exatamente 3 jogadores''' — não dá para entrar sozinho nem em dupla.")
ul:tag('li'):wikitext("'''1 Pokémon''' por jogador, sem troca durante a batalha.")
ul:tag('li'):wikitext("'''1 Champion's Ticket''' para entrar.")
ul:tag('li'):wikitext('Acesso liberado com o ' .. (c.aceTrainer or '') .. '.')
ul:tag('li'):wikitext("Sem '''Potions''', '''Revives''' nem itens de cura.")
if c.tempoLimite then
ul:tag('li'):wikitext("A batalha dura no máximo '''" .. c.tempoLimite .. " minutos'''.")
end
if dados.config and dados.config.limiteSemanal then
ul:tag('li'):wikitext("Limite de '''" .. dados.config.limiteSemanal .. " conclusões''' por semana.")
end
if c.salas then
ul:tag('li'):wikitext("O painel mostra quantas das '''" .. c.salas
.. " salas''' do Campeão estão ocupadas.")
end
if dif == 'Hard' then
ul:tag('li'):wikitext("O Campeão enfrentado é a versão '''Shiny'''.")
end
return box
end
local function conquistas(dif)
local box = mw.html.create('div'):addClass('cp-ach'):attr('data-dif', dif:lower())
local ul = box:tag('ul'):addClass('cp-ach-list')
local chave = (dif == 'Hard') and 'hard' or 'normal'
for _, a in ipairs(dados.conquistas or {}) do
if a[chave] then ul:tag('li'):wikitext(a.desc) end
end
return box
end
function p.ficha(frame)
local chave = frame.args[1] or frame.args.campeao or ''
chave = mw.text.trim(chave)
local c = dados.campeoes[chave]
if not c then
return '<span class="error">Campeão desconhecido: ' .. mw.text.nowiki(chave)
.. '. Cadastre-o em [[Module:Campeao/dados]].</span>'
end
local difs = { 'Normal' }
if c.hard then table.insert(difs, 'Hard') end
-- Esconde o titulo da pagina: a ficha ja exibe o nome no cabecalho. Feito
-- por DISPLAYTITLE em vez de :has() no CSS, que so existe em navegador
-- recente. O texto dentro do span e o proprio titulo, senao o MediaWiki
-- recusa a troca.
frame:callParserFunction('DISPLAYTITLE',
'<span class="cp-h1-oculto">' .. mw.title.getCurrentTitle().text .. '</span>')
local raiz = mw.html.create('div'):addClass('cp-boss'):attr('data-tema', 'claro')
-- Alternador de tema. O JS em MediaWiki:Common.js troca data-tema e guarda
-- a preferencia; sem JS o botao some e a ficha fica no tema claro.
raiz:tag('span'):addClass('cp-tema'):attr('role', 'button'):attr('tabindex', '0')
:attr('data-cp-tema', '1'):css('display', 'none'):wikitext('Modo escuro')
-- Cabecalho
local head = raiz:tag('div'):addClass('cp-head')
local arte = (c.arte and c.arte ~= '') and c.arte or PLACEHOLDER
head:tag('div'):addClass('cp-art')
:wikitext('[[File:' .. arte .. '|200px|link=|alt=' .. c.nome .. ']]')
local info = head:tag('div'):addClass('cp-head-info')
info:tag('div'):addClass('cp-title'):wikitext(c.nome)
info:tag('span'):addClass('cp-badge'):wikitext("Campeão das Expedições")
local stats = info:tag('div'):addClass('cp-stats')
stats:node(stat('Expedition', c.expedition))
stats:node(stat('Ace Trainer', c.aceTrainer))
stats:node(stat('Jogadores', '3'))
stats:node(stat('Tempo alvo', c.tempoAlvo .. ' min'))
-- Abas de dificuldade
local abas = raiz:tag('div'):addClass('cp-tabs')
for i, dif in ipairs(difs) do
local b = abas:tag('span'):addClass('cp-tab'):attr('data-dif', dif:lower())
:attr('role', 'button'):attr('tabindex', '0'):wikitext(dif)
if i == 1 then b:addClass('is-active') end
end
-- Paineis lado a lado
local split = raiz:tag('div'):addClass('cp-split')
local pReq = painel('Requisitos')
local pRec = painel('Recompensas', 'rewards')
for i, dif in ipairs(difs) do
local r1, r2 = requisitos(c, dif), recompensas(c, dif)
if i > 1 then r1:addClass('is-hidden'); r2:addClass('is-hidden') end
pReq:node(r1)
pRec:node(r2)
end
split:node(pReq)
split:node(pRec)
-- Habilidades: seletor em cima, detalhe da escolhida embaixo.
local pHab = painel('Habilidades', 'skills')
local temAlguma = false
for i, dif in ipairs(difs) do
local lista = c.habilidades and c.habilidades[dif:lower()] or {}
-- lista[1] em vez de #lista: mw.loadData guarda os valores no metatable,
-- entao o operador # devolve zero mesmo com a tabela cheia.
if lista[1] then
temAlguma = true
local grupo = pHab:tag('div'):addClass('cp-skills'):attr('data-dif', dif:lower())
if i > 1 then grupo:addClass('is-hidden') end
local barra = grupo:tag('div'):addClass('cp-skill-bar')
for n, h in ipairs(lista) do
local b = barra:tag('span'):addClass('cp-skill-pick')
:attr('data-skill', tostring(n)):attr('role', 'button')
:attr('tabindex', '0'):attr('title', h.nome)
if h.icone and h.icone ~= '' then
b:wikitext('[[File:' .. h.icone .. '|36px|link=|alt=' .. h.nome .. ']]')
else
-- Sem icone ainda: quadrado neutro, como na referencia.
b:addClass('cp-skill-pick--vazio'):wikitext('img')
end
if n == 1 then b:addClass('is-active') end
end
for n, h in ipairs(lista) do
local det = grupo:tag('div'):addClass('cp-skill-detail')
:attr('data-skill', tostring(n))
if n > 1 then det:addClass('is-hidden') end
local corpo = det:tag('div'):addClass('cp-skill-body')
local esq = corpo:tag('div'):addClass('cp-skill-texto')
esq:tag('div'):addClass('cp-skill-name'):wikitext(h.nome)
local meta = esq:tag('div'):addClass('cp-skill-meta')
meta:tag('span'):wikitext(h.tipo)
if h.elemento and h.elemento ~= '' then
meta:tag('span'):wikitext(h.elemento)
end
if h.chance and h.chance < 100 then
meta:tag('span'):wikitext('Chance ' .. h.chance .. '%')
end
if h.nevermiss then
meta:tag('span'):addClass('cp-skill-flag'):wikitext('Não erra')
end
if h.desc and h.desc ~= '' then
esq:tag('div'):addClass('cp-skill-desc'):wikitext(h.desc)
else
esq:tag('div'):addClass('cp-skill-desc cp-empty')
:wikitext('Descrição ainda não documentada.')
end
local dir = corpo:tag('div'):addClass('cp-skill-midia')
if h.video and h.video ~= '' then
-- .mp4 e .webm nao podem ser <video> direto no wikitext: o
-- sanitizador remove a tag, por isso o Widget.
if h.video:lower():match('%.mp4$') or h.video:lower():match('%.webm$') then
dir:wikitext('{{#widget:Video|url={{filepath:' .. h.video .. '}}|width=480}}')
else
dir:wikitext('[[File:' .. h.video .. '|480px|link=]]')
end
else
-- Placeholder preto, do tamanho do video que vai entrar ali.
dir:tag('div'):addClass('cp-skill-video-vazio')
end
end
end
end
if not temAlguma then
pHab:tag('div'):addClass('cp-empty')
:wikitext('O moveset deste Campeão ainda não foi cadastrado.')
end
raiz:node(pHab)
-- Conquistas
local pCon = painel('Conquistas', 'ach')
for i, dif in ipairs(difs) do
local bloco = conquistas(dif)
if i > 1 then bloco:addClass('is-hidden') end
pCon:node(bloco)
end
raiz:node(pCon)
local tip = raiz:tag('div'):addClass('cp-tip-bar')
tip:tag('span'):addClass('cp-tip-tag'):wikitext('Dica')
tip:tag('span'):wikitext('Não há Potions nem Revives nesta sala: sobreviver às mecânicas vale mais do que causar dano.')
return frame:extensionTag{ name = 'templatestyles', args = { src = 'Campeao/styles.css' } }
.. tostring(raiz)
end
return p