Module:NPCTrainer
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