Jump to content

Module:Campeao: Difference between revisions

From WikiPStory
Seletor com icone, detalhe em duas colunas, placeholder preto de video
Requisitos mais completos: itens de cura, tempo limite, limite semanal e salas
 
(2 intermediate revisions by the same user not shown)
Line 38: 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 93: 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 124: 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')

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