Module:NPCTrainer: Difference between revisions

From Pokemon Revolution Online Wiki
Jump to navigation Jump to search
Cae (talk | contribs)
Created page with "local p = {} local DATA_FORMAT = 'CSV with header' local function nonEmpty(s) if s == nil then return nil end s = mw.text.trim(tostring(s)) if s == '' then return nil end return s end local function lcfirst(s) local first = mw.ustring.sub(s, 1, 1) local rest = mw.ustring.sub(s, 2) return mw.ustring.lower(first) .. rest end local function splitList(s, count) local out = {} if nonEmpty(s) then for _, part in ipairs(mw.text.split(s, ',')) do out[#out + 1]..."
 
Cae (talk | contribs)
No edit summary
Line 63: Line 63:
end
end
return megaCache[name] or nil
return megaCache[name] or nil
end
local typeColorCache = {}
local function typeColor(frame, pokeType)
pokeType = nonEmpty(pokeType) or 'Normal'
if not typeColorCache[pokeType] then
typeColorCache[pokeType] = {
base = frame:expandTemplate{ title = pokeType .. ' color' },
light = frame:expandTemplate{ title = pokeType .. ' color light' },
dark = frame:expandTemplate{ title = pokeType .. ' color dark' },
}
end
return typeColorCache[pokeType]
end
end


Line 135: Line 148:
local iargs = frame.args
local iargs = frame.args


local border = nonEmpty(iargs.Border) or '#000000'
local border = nonEmpty(iargs.Border)
local cell = nonEmpty(iargs.Cell) or '#FFFFFF'
local cell = nonEmpty(iargs.Cell)
if not border or not cell then
-- No Theme anywhere upstream: fall back to the Normal type's colors
-- rather than flat black/white.
local normal = typeColor(frame, 'Normal')
border = border or ('#' .. normal.dark)
cell = cell or ('#' .. normal.light)
end


local rows = pokemonRows()
local rows = pokemonRows()
Line 166: Line 186:
local iargs = frame.args
local iargs = frame.args


local border = nonEmpty(iargs.Border) or '#000000'
local border = nonEmpty(iargs.Border)
local cell = nonEmpty(iargs.Cell) or '#FFFFFF'
local cell = nonEmpty(iargs.Cell)
local background = nonEmpty(iargs.Background) or '#FFFFFF'
local background = nonEmpty(iargs.Background)
if not border or not cell or not background then
-- No Theme anywhere upstream: fall back to the Normal type's colors
-- rather than flat black/white.
local normal = typeColor(frame, 'Normal')
border = border or ('#' .. normal.dark)
cell = cell or ('#' .. normal.light)
background = background or ('#' .. normal.base)
end


local class = nonEmpty(pargs.Class)
local class = nonEmpty(pargs.Class)
Line 200: Line 228:
label .. '</div></td></tr>'
label .. '</div></td></tr>'


buf[#buf + 1] = pargs.Lineup or ''
-- Trimmed: callers commonly write "Lineup=\n{{NPCLineup|...}}\n", and a
-- stray leading/trailing newline sitting loose inside this <table> can
-- make MediaWiki's paragraph-insertion pass add unwanted blank space.
buf[#buf + 1] = mw.text.trim(pargs.Lineup or '')


local notes = nonEmpty(pargs.Notes)
local notes = nonEmpty(pargs.Notes)

Revision as of 15:35, 30 July 2026

Documentation for this module may be created at Module:NPCTrainer/doc

local p = {}

local DATA_FORMAT = 'CSV with header'

local function nonEmpty(s)
	if s == nil then return nil end
	s = mw.text.trim(tostring(s))
	if s == '' then return nil end
	return s
end

local function lcfirst(s)
	local first = mw.ustring.sub(s, 1, 1)
	local rest = mw.ustring.sub(s, 2)
	return mw.ustring.lower(first) .. rest
end

local function splitList(s, count)
	local out = {}
	if nonEmpty(s) then
		for _, part in ipairs(mw.text.split(s, ',')) do
			out[#out + 1] = mw.text.trim(part)
		end
	end
	for i = 1, count do
		out[i] = out[i] or ''
	end
	return out
end

local pokemonRowsCache = nil
local function pokemonRows()
	if pokemonRowsCache then return pokemonRowsCache end
	local csvData = mw.ext.externalData.getExternalData{
		url = 'https://wiki.pokemonrevolution.net/index.php?title=Special:GetData/PokemonRawList',
		format = DATA_FORMAT,
		data = { name = 'Name', number = 'Pokedex Number' },
	}
	pokemonRowsCache = {}
	if csvData then
		for _, row in ipairs(csvData) do
			if nonEmpty(row.name) then
				pokemonRowsCache[row.name] = row
			end
		end
	end
	return pokemonRowsCache
end

local megaCache = {}
local function megaByName(name)
	if megaCache[name] == nil then
		local ok, values = pcall(function()
			return mw.ext.externalData.getFileData{
				source = 'data',
				['file name'] = 'RawMegaPokemonList.csv',
				format = DATA_FORMAT,
				filters = 'Name=' .. name,
				data = 'name=Name,number=Pokedex Number',
			}
		end)
		megaCache[name] = (ok and values and values.name ~= nil) and values or false
	end
	return megaCache[name] or nil
end

local typeColorCache = {}
local function typeColor(frame, pokeType)
	pokeType = nonEmpty(pokeType) or 'Normal'
	if not typeColorCache[pokeType] then
		typeColorCache[pokeType] = {
			base = frame:expandTemplate{ title = pokeType .. ' color' },
			light = frame:expandTemplate{ title = pokeType .. ' color light' },
			dark = frame:expandTemplate{ title = pokeType .. ' color dark' },
		}
	end
	return typeColorCache[pokeType]
end

local PokemonForms = require('Module:PokemonForms')
local FORM_SUFFIXES = PokemonForms.FORM_SUFFIXES
local MEGA_VARIANTS = PokemonForms.MEGA_VARIANTS or { '', ' X', ' Y' }

local function rawListFormName(form, stem)
	if form.rawList == 'suffix' then
		return stem .. '-' .. form.suffix
	elseif form.rawList == 'prefix-hyphen' then
		return form.label .. '-' .. stem
	elseif form.rawList == 'suffix-label' then
		return stem .. '-' .. form.label
	elseif form.rawList == 'suffix-space' then
		return stem .. ' ' .. form.label
	end
	return form.label .. ' ' .. stem
end

local function resolveFormRow(rows, baseName, formsText)
	local lower = mw.ustring.lower(formsText)
	if mw.ustring.sub(lower, 1, 4) == 'mega' then
		for _, variant in ipairs(MEGA_VARIANTS) do
			if lower == mw.ustring.lower('Mega' .. variant) then
				return megaByName(baseName .. variant)
			end
		end
	end
	for _, form in ipairs(FORM_SUFFIXES) do
		if mw.ustring.lower(form.label) == lower or mw.ustring.lower(form.suffix) == lower then
			local candidate = rawListFormName(form, baseName)
			if rows[candidate] then
				return rows[candidate]
			end
		end
	end
	return nil
end

local function slotHtml(rows, pokeName, level, formsText, cell, border)
	if not nonEmpty(pokeName) then
		return '<td width="16%"><table style="width: 100%; height: 55px; border: 1px solid ' .. border ..
			'; background-color: ' .. border .. '; border-radius: 15px;"></table></td>'
	end

	local row = rows[pokeName]
	local formNote = ''
	if nonEmpty(formsText) and not tonumber(formsText) then
		formNote = ' (' .. formsText .. ')'
		local formRow = resolveFormRow(rows, pokeName, formsText)
		if formRow then
			row = formRow
		end
	end

	local number = (row and nonEmpty(row.number)) or ''
	local displayName = (row and nonEmpty(row.name)) or pokeName

	return '<td width="16%"><table style="width: 100%; height: 55px; border: 1px solid ' .. border ..
		'; background-color: ' .. cell .. '; border-radius: 15px;">' ..
		'<tr><td rowspan="2" style="text-align: right;">[[File:' .. number .. 'Icon.png]]</td>' ..
		'<td>[[' .. displayName .. ']]' .. formNote .. '</td></tr>' ..
		'<tr><td colspan="2" style="font-weight: bold; color: ' .. border .. ';"> Lv. ' ..
		(nonEmpty(level) or '???') .. '</td></tr>' ..
		'</table></td>'
end

function p.lineup(frame)
	local parent = frame:getParent()
	local pargs = (parent and parent.args) or frame.args
	local iargs = frame.args

	local border = nonEmpty(iargs.Border)
	local cell = nonEmpty(iargs.Cell)
	if not border or not cell then
		-- No Theme anywhere upstream: fall back to the Normal type's colors
		-- rather than flat black/white.
		local normal = typeColor(frame, 'Normal')
		border = border or ('#' .. normal.dark)
		cell = cell or ('#' .. normal.light)
	end

	local rows = pokemonRows()
	local pokeList = splitList(pargs.Pokemon, 6)
	local levels = splitList(pargs.Levels, 6)
	local forms = splitList(pargs.Forms, 6)

	local cells = {}
	for i = 1, 6 do
		cells[#cells + 1] = slotHtml(rows, pokeList[i], levels[i], forms[i], cell, border)
	end

	local out = '<tr>' .. table.concat(cells) .. '</tr>'

	local bonus = nonEmpty(pargs.BonusPayout)
	if bonus then
		out = out .. '<tr><td colspan="6" style="background-color: ' .. cell ..
			'; padding: 3px; text-align: left; border-radius: 15px; border: 1px solid ' .. border .. ';">' ..
			'<span style="margin-left: 15px; font-weight: bold; color: ' .. border .. ';">Bonus payout:</span> ' ..
			bonus .. '</td></tr>'
	end

	return out
end

function p.trainer(frame)
	local parent = frame:getParent()
	local pargs = (parent and parent.args) or frame.args
	local iargs = frame.args

	local border = nonEmpty(iargs.Border)
	local cell = nonEmpty(iargs.Cell)
	local background = nonEmpty(iargs.Background)
	if not border or not cell or not background then
		-- No Theme anywhere upstream: fall back to the Normal type's colors
		-- rather than flat black/white.
		local normal = typeColor(frame, 'Normal')
		border = border or ('#' .. normal.dark)
		cell = cell or ('#' .. normal.light)
		background = background or ('#' .. normal.base)
	end

	local class = nonEmpty(pargs.Class)
	local gender = nonEmpty(pargs.Gender)
	local name = nonEmpty(pargs.Name)

	local showClassRaw = pargs.ShowClass
	if showClassRaw == nil then showClassRaw = 'yes' end
	local showClass = nonEmpty(showClassRaw) ~= nil

	local portrait
	if class then
		local genderSuffix = gender and ('_(' .. lcfirst(gender) .. ')') or ''
		portrait = '[[File:' .. class .. genderSuffix .. '.png]]'
	elseif name then
		portrait = '[[File:' .. name .. '.png]]'
	else
		portrait = ''
	end

	local classPart = (class and showClass) and class or ''
	local namePart = name or ''
	local label = mw.text.trim(classPart .. ' ' .. namePart)

	local buf = {}
	buf[#buf + 1] = '<table style="width: 100%; padding: 5px; border-spacing: 7px; border: 4px solid ' .. border ..
		'; background-color: ' .. background .. '; border-radius: 15px;">'
	buf[#buf + 1] = '<tr><td colspan="6"><div style="border-radius: 15px; font-weight: bold; color: ' .. border ..
		'; background-color: ' .. cell .. '; padding: 8px; margin: auto auto; width: 50%; border: 1px solid ' ..
		border .. ';"><div class="middle-align" style="margin-top: 5px;">' .. portrait .. '</div>' ..
		label .. '</div></td></tr>'

	-- Trimmed: callers commonly write "Lineup=\n{{NPCLineup|...}}\n", and a
	-- stray leading/trailing newline sitting loose inside this <table> can
	-- make MediaWiki's paragraph-insertion pass add unwanted blank space.
	buf[#buf + 1] = mw.text.trim(pargs.Lineup or '')

	local notes = nonEmpty(pargs.Notes)
	if notes then
		buf[#buf + 1] = '<tr><td colspan="6" style="text-align:left; background-color:' .. cell ..
			'; padding: 3px; border-radius: 15px; border: 1px solid ' .. border .. ';">' ..
			'<p style="margin-left: 15px;"><b>Notes:</b> ' .. notes .. '.</p></td></tr>'
	end

	local cooldown = nonEmpty(pargs.Cooldown)
	if cooldown then
		local cdText = (cooldown == 'none') and 'None, this is a one-time battle.' or cooldown
		buf[#buf + 1] = '<tr><td colspan="6" style="text-align: left; background-color: ' .. cell ..
			'; padding: 3px; border-radius: 15px; border: 1px solid ' .. border .. ';">' ..
			'<p style="margin-left: 15px;"><b>Cooldown:</b> ' .. cdText .. '</p></td></tr>'
	end

	buf[#buf + 1] = '</table>'

	return '<tr><td>' .. table.concat(buf) .. '</td></tr>'
end

return p