Module:LegendaryPokemon
Documentation for this module may be created at Module:LegendaryPokemon/doc
local p = {}
local DATA_FORMAT = 'CSV with header'
local STAT_MAX = 255
local ROMAN = { 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' }
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 pokemonRowsCache = nil
local function pokemonByName()
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 = {
number = 'Pokedex Number', name = 'Name', type1 = 'Type1', type2 = 'Type2', generation = 'Generation',
hp = 'HP', atk = 'Attack', def = 'Defense', spatk = 'Special Attack', spdef = 'Special Defense',
spd = 'Speed',
},
}
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 typeColorCache = {}
local function typeColor(frame, pokeType)
pokeType = nonEmpty(pokeType) or 'Normal'
if not typeColorCache[pokeType] then
typeColorCache[pokeType] = {
light = frame:expandTemplate{ title = pokeType .. ' color light' },
base = frame:expandTemplate{ title = pokeType .. ' color' },
dark = frame:expandTemplate{ title = pokeType .. ' color dark' },
}
end
return typeColorCache[pokeType]
end
local regionNameCache = {}
local function regionForGen(frame, genRoman)
if not regionNameCache[genRoman] then
regionNameCache[genRoman] = frame:expandTemplate{ title = 'LegendaryRegionFromGen', args = { genRoman } }
end
return regionNameCache[genRoman]
end
local regionColorCache = {}
local function regionColor(frame, regionName)
if not regionColorCache[regionName] then
regionColorCache[regionName] = {
light = frame:expandTemplate{ title = regionName .. ' color light' },
base = frame:expandTemplate{ title = regionName .. ' color' },
dark = frame:expandTemplate{ title = regionName .. ' color dark' },
}
end
return regionColorCache[regionName]
end
local function genToRoman(genValue)
local digits = mw.ustring.match(tostring(genValue or ''), '^%d+')
local n = digits and tonumber(digits)
return (n and ROMAN[n]) or tostring(genValue or '')
end
local function statBar(frame, label, value, dark)
value = tonumber(value) or 0
local pct = math.max(0, math.min(100, (value / STAT_MAX) * 100))
local hue = math.floor((math.max(0, math.min(STAT_MAX, value)) / STAT_MAX) * 130) -- red (low) -> teal (high)
return '<tr><td style="width: 40%; text-align: left; padding: 2px 4px; font-size: 11px; color: #' .. dark ..
';">' .. label .. '</td><td style="padding: 2px 4px;">' ..
'<div style="background: #FFFFFF; border: 1px solid #' .. dark .. '; border-radius: 8px; overflow: hidden;">' ..
'<div style="width: ' .. pct .. '%; background: hsl(' .. hue ..
', 65%, 45%); color: #FFFFFF; font-size: 10px; font-weight: normal; padding: 1px 4px; ' ..
'white-space: nowrap;">' .. value .. '</div></div></td></tr>'
end
local function typeBadges(frame, row)
local t1 = nonEmpty(row.type1)
local t2 = nonEmpty(row.type2)
local c1 = typeColor(frame, t1)
if not t2 then
return '<table align="center" style="width: 80%; font-weight: bold; font-size: 12px;"><tr>' ..
'<td style="background-color: #' .. c1.dark .. '; border-radius: 15px; padding: 2px;">[[' .. t1 ..
'_(type)|<span style="color: #FFFFFF;">' .. t1 .. '</span>]]</td></tr></table>'
end
local c2 = typeColor(frame, t2)
return '<table align="center" style="width: 80%; font-weight: bold; font-size: 12px;"><tr>' ..
'<td style="background-color: #' .. c1.dark .. '; border-radius: 15px 1px 1px 15px; padding: 2px;">[[' ..
t1 .. '_(type)|<span style="color: #FFFFFF;">' .. t1 .. '</span>]]</td>' ..
'<td style="background-color: #' .. c2.dark .. '; border-radius: 1px 15px 15px 1px; padding: 2px;">[[' ..
t2 .. '_(type)|<span style="color: #FFFFFF;">' .. t2 .. '</span>]]</td></tr></table>'
end
local function memberCard(frame, name, showGeneration)
local row = pokemonByName()[name]
if not row then
return '<div style="display: inline-block; vertical-align: top; width: 240px; margin: 4px; ' ..
'color: red;">Data load error: No Pokémon data found for ' .. name .. '</div>', nil
end
local c = typeColor(frame, row.type1)
local bst = (tonumber(row.hp) or 0) + (tonumber(row.atk) or 0) + (tonumber(row.def) or 0) +
(tonumber(row.spatk) or 0) + (tonumber(row.spdef) or 0) + (tonumber(row.spd) or 0)
local genLine = ''
if showGeneration then
local genRoman = genToRoman(row.generation)
genLine = '<br><span style="font-size: 11px;">[[:Category:Generation ' .. genRoman ..
' Pokémon|<span style="color: #' .. c.dark .. ';">Generation ' .. genRoman .. '</span>]]</span>'
end
local buf = {}
buf[#buf + 1] = '<div style="display: inline-block; vertical-align: top; width: 240px; margin: 4px; ' ..
'background-color: #' .. c.base .. '; border: 3px solid #' .. c.dark ..
'; border-radius: 20px; padding: 6px; text-align: center; color: #' .. c.dark .. '; font-weight: bold;">'
buf[#buf + 1] = '<div style="background-color: #' .. c.light .. '; border-radius: 15px; padding: 5px; ' ..
'margin-bottom: 6px;">#' .. (row.number or '') .. ' <span style="font-size: 16px;">[[' .. name ..
'|<span style="color: #' .. c.dark .. ';">' .. name .. '</span>]]</span>' .. genLine .. '</div>'
buf[#buf + 1] = '<div style="background-color: #FFFFFF; border-radius: 15px; height: 150px; display: flex; ' ..
'align-items: center; justify-content: center; margin-bottom: 6px;">[[File:' .. (row.number or '') ..
name .. '.png|150px]]</div>'
buf[#buf + 1] = typeBadges(frame, row)
buf[#buf + 1] = '<table style="width: 100%; margin-top: 6px;">' ..
statBar(frame, 'HP', row.hp, c.dark) ..
statBar(frame, 'Attack', row.atk, c.dark) ..
statBar(frame, 'Defense', row.def, c.dark) ..
statBar(frame, 'Sp. Atk', row.spatk, c.dark) ..
statBar(frame, 'Sp. Def', row.spdef, c.dark) ..
statBar(frame, 'Speed', row.spd, c.dark) ..
'<tr><td style="text-align: left; padding: 3px 4px; font-size: 11px;">Total</td>' ..
'<td style="padding: 3px 4px; font-size: 11px;">' .. bst .. '</td></tr></table>'
buf[#buf + 1] = '</div>'
return table.concat(buf), row
end
local function requirementsBox(dark, requirements)
return '<div style="background-color: #FFFFFF; color: #000000; border-radius: 15px; font-weight: normal; ' ..
'text-align: left; border: 1px solid #' .. dark .. '; padding: 8px; margin-top: 4px;">' ..
(requirements or '') .. '</div>'
end
function p.card(frame)
local parent = frame:getParent()
local args = (parent and parent.args) or frame.args
local name = nonEmpty(args.Name)
if not name then
return "<span style='color:red'>Usage: {{LegendaryPokemon|Name=...|Requirements=...|" ..
"Pokemon=Name1\\Name2 (omit Pokemon= for a single-species entry)}}</span>"
end
local pokemonArg = nonEmpty(args.Pokemon)
local members = {}
if pokemonArg then
for _, part in ipairs(mw.text.split(pokemonArg, '\\', true)) do
local trimmed = mw.text.trim(part)
if trimmed ~= '' then
members[#members + 1] = trimmed
end
end
else
members = { name }
end
local requirements = args.Requirements or ''
if #members == 1 then
local cardHtml, row = memberCard(frame, members[1], true)
if not row then
return cardHtml
end
local c = typeColor(frame, row.type1)
return '<div style="display: inline-block; vertical-align: top; background-color: #' .. c.base ..
'; border: 1px solid #' .. c.dark .. '; border-radius: 22px; padding: 4px; margin: 4px;">' ..
cardHtml .. requirementsBox(c.dark, requirements) .. '</div>'
end
local firstRow = pokemonByName()[members[1]]
local genRoman = nonEmpty(args.Gen) or (firstRow and genToRoman(firstRow.generation)) or '?'
local regionName = regionForGen(frame, genRoman)
local rc = regionColor(frame, regionName)
local cardsHtml = {}
for _, member in ipairs(members) do
local cardHtml = memberCard(frame, member, false)
cardsHtml[#cardsHtml + 1] = cardHtml
end
local buf = {}
buf[#buf + 1] = '<div style="display: inline-block; vertical-align: top; background-color: #' .. rc.base ..
'; border: 3px solid #' .. rc.dark .. '; border-radius: 25px; padding: 6px; margin: 4px; ' ..
'text-align: center; max-width: 100%;">'
buf[#buf + 1] = '<div style="background-color: #' .. rc.light .. '; border-radius: 20px; padding: 8px; ' ..
'color: #' .. rc.dark .. '; font-weight: bold;"><span style="font-size: 18px;">' .. name ..
'</span><br><span style="font-size: 12px;">[[:Category:Generation ' .. genRoman ..
' Pokémon|<span style="color: #' .. rc.dark .. ';">Generation ' .. genRoman .. '</span>]]</span></div>'
buf[#buf + 1] = '<div style="display: flex; flex-wrap: wrap; justify-content: center; margin-top: 6px;">' ..
table.concat(cardsHtml) .. '</div>'
buf[#buf + 1] = '<div style="background-color: #' .. rc.light .. '; border-radius: 20px; padding: 6px; ' ..
'margin-top: 6px; color: #' .. rc.dark .. '; font-weight: bold; text-align: left;">' ..
'<div style="text-align: center;">Availability</div>' .. requirementsBox(rc.dark, requirements) .. '</div>'
buf[#buf + 1] = '</div>'
return table.concat(buf)
end
return p