Module:Learnsets
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Learnsets/doc
local p = {}
local function getArgs(frame)
local args = {}
local parent = frame:getParent()
if parent then
for k, v in pairs(parent.args) do
if v ~= '' then
args[k] = v
end
end
end
for k, v in pairs(frame.args) do
if v ~= '' then
args[k] = v
end
end
return args
end
local moveDataCache = nil
local function blankToNil(v)
if v == nil or v == '' then
return nil
end
return v
end
local function loadMoveData()
if moveDataCache then
return moveDataCache
end
local csvData = mw.ext.externalData.getExternalData({
url = 'https://wiki.pokemonrevolution.net/index.php?title=Special:GetData/RawMovesList',
format = 'CSV with header',
data = {
Name = 'Name',
Type = 'Type',
Category = 'Category',
Power = 'Power',
Accuracy = 'Accuracy',
PP = 'PP',
Broken = 'Broken',
Typeless = 'Typeless',
Priority = 'Priority',
TM = 'TM',
['Move Tutor'] = 'Move Tutor',
Egg = 'Egg',
},
})
moveDataCache = {}
if csvData then
for _, row in ipairs(csvData) do
moveDataCache[row.Name] = {
type = blankToNil(row.Type) or 'Normal',
category = blankToNil(row.Category) or 'Status',
power = blankToNil(row.Power),
accuracy = blankToNil(row.Accuracy),
pp = blankToNil(row.PP),
broken = row.Broken or 'N',
typeless = row.Typeless or 'N',
priority = blankToNil(row.Priority) or '0',
tm = blankToNil(row.TM),
move_tutor = blankToNil(row['Move Tutor']),
egg = blankToNil(row.Egg)
}
end
end
return moveDataCache
end
local function loadLearnset(pokemonName)
local jsonPath = '$[' .. mw.text.jsonEncode(pokemonName) .. ']'
local ok, result, errors = pcall(function()
return mw.ext.externalData.getFileData({
source = 'data',
['file name'] = 'Learnsets.json',
format = 'JSON',
['use jsonpath'] = true,
data = { poke = jsonPath },
})
end)
if not ok then
return nil, 'Lua error: ' .. tostring(result)
end
if errors and #errors > 0 then
return nil, 'ExternalData error: ' .. table.concat(errors, ' | ')
end
local raw = result and result.poke
if type(raw) ~= 'table' then
return nil, 'No learnset data found for ' .. pokemonName
end
return raw, nil
end
local function sentenceCase(s)
local first = mw.ustring.sub(s, 1, 1)
local rest = mw.ustring.sub(s, 2)
return mw.ustring.upper(first) .. mw.ustring.lower(rest)
end
local FORM_SUFFIXES = {
{ suffix = 'Alolan', label = 'Alolan', rawList = 'prefix' },
{ suffix = 'Galar', label = 'Galarian', rawList = 'prefix' },
{ suffix = 'Hisui', label = 'Hisuian', rawList = 'prefix' },
{ suffix = 'Paldea', label = 'Paldean', rawList = 'prefix' },
{ suffix = 'Sandy', label = 'Sandy', rawList = 'suffix' },
{ suffix = 'Trash', label = 'Trash', rawList = 'suffix' },
{ suffix = 'Sky', label = 'Sky', rawList = 'suffix' },
{ suffix = 'Fan', label = 'Fan', rawList = 'suffix' },
{ suffix = 'Frost', label = 'Frost', rawList = 'suffix' },
{ suffix = 'Heat', label = 'Heat', rawList = 'suffix' },
{ suffix = 'Mow', label = 'Mow', rawList = 'suffix' },
{ suffix = 'Wash', label = 'Wash', rawList = 'suffix' },
{ suffix = 'Ash', label = 'Ash', learnsetStyle = 'prefix-space', rawList = 'prefix-hyphen' },
{ suffix = 'Bloodmoon', label = 'Bloodmoon', rawList = 'suffix' },
{ suffix = 'Low-Key', label = 'Low Key', rawList = 'suffix-label' },
{ suffix = 'F', label = 'Female', rawList = 'suffix-space', baseLabel = 'Male' },
{ suffix = 'Blue-Striped', label = 'Blue-Striped', rawList = 'suffix-label' },
{ suffix = 'White-Striped', label = 'White-Striped', rawList = 'suffix-label' },
{ suffix = 'Rainy', label = 'Rainy', rawList = 'suffix-label' },
{ suffix = 'Snowy', label = 'Snowy', rawList = 'suffix-label' },
{ suffix = 'Sunny', label = 'Sunny', rawList = 'suffix-label' },
{ suffix = 'Sunshine', label = 'Sunshine', rawList = 'suffix-label' },
{ suffix = 'Attack', label = 'Attack', rawList = 'suffix-label' },
{ suffix = 'Defense', label = 'Defense', rawList = 'suffix-label' },
{ suffix = 'Speed', label = 'Speed', rawList = 'suffix-label' },
{ suffix = 'Unbound', label = 'Unbound', rawList = 'suffix-label' },
{ suffix = 'Black', label = 'Black', rawList = 'suffix-label' },
{ suffix = 'White', label = 'White', rawList = 'suffix-label' },
{ suffix = 'Dusk', label = 'Dusk', rawList = 'suffix-label' },
{ suffix = 'Midnight', label = 'Midnight', rawList = 'suffix-label' },
{ suffix = 'Pirouette', label = 'Pirouette', rawList = 'suffix-label' },
{ suffix = 'Yellow', label = 'Yellow', rawList = 'suffix-label' },
{ suffix = 'School', label = 'School', rawList = 'suffix-label' },
{ suffix = 'Crowned', label = 'Crowned', rawList = 'suffix-label' },
}
local STRIP_SUFFIXES = {
'Alolan', 'Galar', 'Hisui', 'Paldea', 'Sandy', 'Trash', 'Plant', 'Amped', 'Male',
'Ice-Face', 'Midday', 'Solo',
}
local function stripKnownFormSuffix(name)
for _, suffix in ipairs(STRIP_SUFFIXES) do
local tail = '-' .. suffix
if mw.ustring.sub(name, -mw.ustring.len(tail)) == tail then
return mw.ustring.sub(name, 1, -mw.ustring.len(tail) - 1)
end
end
return nil
end
local learnsetCache = {}
local learnsetErrorCache = {}
local function primeLearnsetCache(names)
local uniqueNames, seen = {}, {}
for _, name in ipairs(names) do
if not seen[name] and learnsetCache[name] == nil and learnsetErrorCache[name] == nil then
seen[name] = true
table.insert(uniqueNames, name)
end
end
if #uniqueNames == 0 then
return
end
local dataSpec = {}
local keyToName = {}
for i, name in ipairs(uniqueNames) do
local key = 'k' .. i
dataSpec[key] = '$[' .. mw.text.jsonEncode(name) .. ']'
keyToName[key] = name
end
local ok, result, errors = pcall(function()
return mw.ext.externalData.getFileData({
source = 'data',
['file name'] = 'Learnsets.json',
format = 'JSON',
['use jsonpath'] = true,
data = dataSpec,
})
end)
if not ok then
local msg = 'Lua error: ' .. tostring(result)
for _, name in ipairs(uniqueNames) do
learnsetErrorCache[name] = msg
end
return
end
if errors and #errors > 0 then
local msg = 'ExternalData error: ' .. table.concat(errors, ' | ')
for _, name in ipairs(uniqueNames) do
learnsetErrorCache[name] = msg
end
return
end
for key, name in pairs(keyToName) do
local val = result and result[key]
if type(val) == 'table' then
learnsetCache[name] = val
else
learnsetErrorCache[name] = 'No learnset data found for ' .. name
end
end
end
local function getCachedLearnset(pokemonName)
if learnsetCache[pokemonName] == nil and learnsetErrorCache[pokemonName] == nil then
local poke, err = loadLearnset(pokemonName)
if not poke then
local altName = sentenceCase(pokemonName)
if altName ~= pokemonName then
local altPoke = loadLearnset(altName)
if altPoke then
poke, err = altPoke, nil
end
end
end
learnsetCache[pokemonName], learnsetErrorCache[pokemonName] = poke, err
end
return learnsetCache[pokemonName], learnsetErrorCache[pokemonName]
end
local function resolvePagePokemon(pokemonName)
local poke, err = getCachedLearnset(pokemonName)
if poke then
return poke, nil, pokemonName
end
local baseName = stripKnownFormSuffix(pokemonName)
if baseName then
local basePoke, baseErr = getCachedLearnset(baseName)
if basePoke then
return basePoke, nil, baseName
end
end
return nil, err, pokemonName
end
local pokemonTypeCache = nil
local function loadPokemonTypes()
if pokemonTypeCache then return pokemonTypeCache end
local csvData = mw.ext.externalData.getExternalData({
url = 'https://wiki.pokemonrevolution.net/index.php?title=Special:GetData/PokemonRawList',
format = 'CSV with header',
data = {
Name = 'Name',
Type1 = 'Type1',
Type2 = 'Type2',
},
})
pokemonTypeCache = {}
if csvData then
for _, row in ipairs(csvData) do
pokemonTypeCache[row.Name] = {
type1 = row.Type1 or '',
type2 = row.Type2 or '',
}
end
end
return pokemonTypeCache
end
local typeColorCache = {}
local function typeColor(frame, pokeType)
pokeType = (pokeType and pokeType ~= '') and 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 categoryColorCache = {}
local function categoryColor(frame, category)
category = (category and category ~= '') and category or 'Status'
if not categoryColorCache[category] then
categoryColorCache[category] = {
base = frame:expandTemplate{ title = category .. ' color' },
dark = frame:expandTemplate{ title = category .. ' color dark' },
}
end
return categoryColorCache[category]
end
local function isSTAB(moveType, primaryType, secondaryType)
return moveType == primaryType or moveType == secondaryType
end
local function formatItemNumber(label, num)
num = tonumber(num)
if not num then
return '—'
end
return label .. string.format('%02d', num)
end
function p.renderLevelUp(frame)
local args = getArgs(frame)
local pokemonName = args[1] or args.pokemon
local primaryType = args.type1 or ""
local secondaryType = args.type2 or ""
local pokemon, lsErr = getCachedLearnset(pokemonName)
local moveData = loadMoveData()
if not pokemon then
return "<span style='color:red'>Data load error: " .. (lsErr or 'unknown') .. "</span>"
end
local moves = pokemon.level_up or {}
local colors = typeColor(frame, primaryType)
local html = mw.html.create('div')
:css('width', '77%')
:css('margin', 'auto')
local table = html:tag('table')
:addClass('sortable')
:css('width', '100%')
:css('text-align', 'center')
:css('border-radius', '15px 15px 0 0')
:css('border', '3px solid #' .. colors.dark)
:css('border-bottom', 'none')
:css('background-color', '#' .. colors.base)
:css('padding', '7px')
local header = table:tag('tr')
:css('background-color', '#' .. colors.light)
:css('color', '#' .. colors.dark)
header:tag('th'):wikitext('Level'):css('width', '10%'):css('border-radius', '15px 1px 1px 1px')
header:tag('th'):wikitext('Move'):css('width', '15%')
header:tag('th'):wikitext('Type'):css('width', '17%')
header:tag('th'):wikitext('Category'):css('width', '16%')
header:tag('th'):wikitext('Base Power'):css('width', '17%')
header:tag('th'):wikitext('Accuracy'):css('width', '15%')
header:tag('th'):wikitext('PP'):css('width', '10%'):css('border-radius', '1px 15px 1px 1px')
for _, move in ipairs(moves) do
local moveName = move.move
local level = move.level
local moveInfo = moveData[moveName] or {}
local row = table:tag('tr'):css('background-color', '#FFFFFF')
row:tag('td'):wikitext(tostring(level))
local moveLink = '[[' .. moveName .. ']]'
if isSTAB(moveInfo.type, primaryType, secondaryType) and moveInfo.category ~= "Status" then
moveLink = "'''" .. moveLink .. "'''"
end
if moveInfo.broken == "Y" then
moveLink = "''" .. moveLink .. "''"
end
row:tag('td'):wikitext(moveLink)
local moveTypeColors = typeColor(frame, moveInfo.type)
row:tag('td')
:css('background-color', '#' .. moveTypeColors.base)
:css('border', '2px solid #' .. moveTypeColors.dark)
:wikitext('[[' .. (moveInfo.type or 'Normal') .. '_(type)|<span style="color:#FFFFFF;">' .. (moveInfo.type or 'Normal') .. '</span>]]')
local moveCatColors = categoryColor(frame, moveInfo.category)
row:tag('td')
:css('background-color', '#' .. moveCatColors.base)
:css('border', '2px solid #' .. moveCatColors.dark)
:wikitext('[[' .. (moveInfo.category or 'Status') .. ' moves|<span style="color:#FFFFFF;">' .. (moveInfo.category or 'Status') .. '</span>]]')
row:tag('td'):wikitext(moveInfo.power or '—')
row:tag('td'):wikitext(moveInfo.accuracy or '—')
row:tag('td'):wikitext(moveInfo.pp or '—')
end
html:tag('div')
:css('text-align', 'left')
:css('background-color', '#' .. colors.light)
:css('padding', '10px')
:css('border', '3px solid #' .. colors.dark)
:css('border-top', 'none')
:css('border-radius', '0 0 15px 15px')
:wikitext([=[
* '''Emboldened''' moves denote this Pokémon avails from a STAB bonus for it.
* ''Italicized'' moves are [[Broken_Moves_%26_Abilities|broken]]; see their individual pages for more information.
* All moves subsumed on this list are relearnable via the [[Move Relearner]]s if lost.
]=])
return tostring(html) .. '<br clear="all">'
end
function p.renderTMsHMs(frame)
local args = getArgs(frame)
local pokemonName = args[1] or args.pokemon
local primaryType = args.type1 or ""
local secondaryType = args.type2 or ""
local pokemon = getCachedLearnset(pokemonName)
local moveData = loadMoveData()
if not pokemon then return '' end
local tms = pokemon.tms or {}
local hms = pokemon.hms or {}
local colors = typeColor(frame, primaryType)
local html = mw.html.create('div')
:css('width', '77%')
:css('margin', 'auto')
local table = html:tag('table')
:addClass('sortable')
:css('width', '100%')
:css('text-align', 'center')
:css('border-radius', '25px 25px 0 0')
:css('border', '3px solid #' .. colors.dark)
:css('border-bottom', 'none')
:css('background-color', '#' .. colors.base)
:css('padding', '7px')
local header = table:tag('tr')
:css('background-color', '#' .. colors.light)
:css('color', '#' .. colors.dark)
header:tag('th'):attr('colspan', '2'):wikitext('TM'):css('width', '10%'):css('border-top-left-radius', '25px')
header:tag('th'):wikitext('Move'):css('width', '15%')
header:tag('th'):wikitext('Type'):css('width', '17%')
header:tag('th'):wikitext('Category'):css('width', '16%')
header:tag('th'):wikitext('Base Power'):css('width', '17%')
header:tag('th'):wikitext('Accuracy'):css('width', '15%')
header:tag('th'):wikitext('PP'):css('width', '10%'):css('border-top-right-radius', '25px')
for _, moveName in ipairs(tms) do
local moveInfo = moveData[moveName] or {}
local row = table:tag('tr'):css('background-color', '#FFFFFF')
row:tag('td'):attr('colspan', '2'):wikitext(formatItemNumber('TM', moveInfo.tm))
local moveLink = '[[' .. moveName .. ']]'
if isSTAB(moveInfo.type, primaryType, secondaryType) and moveInfo.category ~= "Status" then
moveLink = "'''" .. moveLink .. "'''"
end
if moveInfo.broken == "Y" then
moveLink = "''" .. moveLink .. "''"
end
row:tag('td'):wikitext(moveLink)
local moveTypeColors = typeColor(frame, moveInfo.type)
row:tag('td')
:css('background-color', '#' .. moveTypeColors.base)
:css('border', '2px solid #' .. moveTypeColors.dark)
:wikitext('[[' .. (moveInfo.type or 'Normal') .. '_(type)|<span style="color:#FFFFFF;">' .. (moveInfo.type or 'Normal') .. '</span>]]')
local moveCatColors = categoryColor(frame, moveInfo.category)
row:tag('td')
:css('background-color', '#' .. moveCatColors.base)
:css('border', '2px solid #' .. moveCatColors.dark)
:wikitext('[[' .. (moveInfo.category or 'Status') .. ' moves|<span style="color:#FFFFFF;">' .. (moveInfo.category or 'Status') .. '</span>]]')
row:tag('td'):wikitext(moveInfo.power or '—')
row:tag('td'):wikitext(moveInfo.accuracy or '—')
row:tag('td'):wikitext(moveInfo.pp or '—')
end
for _, moveName in ipairs(hms) do
local moveInfo = moveData[moveName] or {}
local row = table:tag('tr'):css('background-color', '#FFFFCC')
row:tag('td'):attr('colspan', '2'):wikitext(formatItemNumber('HM', moveInfo.tm)):css('font-weight', 'bold')
local moveLink = '[[' .. moveName .. ']]'
if isSTAB(moveInfo.type, primaryType, secondaryType) and moveInfo.category ~= "Status" then
moveLink = "'''" .. moveLink .. "'''"
end
if moveInfo.broken == "Y" then
moveLink = "''" .. moveLink .. "''"
end
row:tag('td'):wikitext(moveLink)
local moveTypeColors = typeColor(frame, moveInfo.type)
row:tag('td')
:css('background-color', '#' .. moveTypeColors.base)
:css('border', '2px solid #' .. moveTypeColors.dark)
:wikitext('[[' .. (moveInfo.type or 'Normal') .. '_(type)|<span style="color:#FFFFFF;">' .. (moveInfo.type or 'Normal') .. '</span>]]')
local moveCatColors = categoryColor(frame, moveInfo.category)
row:tag('td')
:css('background-color', '#' .. moveCatColors.base)
:css('border', '2px solid #' .. moveCatColors.dark)
:wikitext('[[' .. (moveInfo.category or 'Status') .. ' moves|<span style="color:#FFFFFF;">' .. (moveInfo.category or 'Status') .. '</span>]]')
row:tag('td'):wikitext(moveInfo.power or '—')
row:tag('td'):wikitext(moveInfo.accuracy or '—')
row:tag('td'):wikitext(moveInfo.pp or '—')
end
html:tag('div')
:css('text-align', 'left')
:css('background-color', '#' .. colors.light)
:css('padding', '10px')
:css('border', '3px solid #' .. colors.dark)
:css('border-top', 'none')
:css('border-radius', '0 0 25px 25px')
:wikitext([=[
* '''Emboldened''' moves denote that this Pokémon avails from a STAB bonus for it.
* ''Italicized'' moves are [[Broken_Moves_%26_Abilities|broken]]; see their individual pages for more information.
* Refer to [[TMs and HMs]] for information on each move's obtainability.
]=])
return tostring(html) .. '<br clear="all">'
end
function p.renderMoveTutors(frame)
local args = getArgs(frame)
local pokemonName = args[1] or args.pokemon
local primaryType = args.type1 or ""
local secondaryType = args.type2 or ""
local pokemon = getCachedLearnset(pokemonName)
local moveData = loadMoveData()
if not pokemon then return '' end
local moves = pokemon.move_tutors or {}
if #moves == 0 then
return ""
end
local colors = typeColor(frame, primaryType)
local html = mw.html.create('div')
:css('width', '77%')
:css('margin', 'auto')
local table = html:tag('table')
:addClass('sortable')
:css('width', '100%')
:css('text-align', 'center')
:css('border-radius', '25px 25px 0 0')
:css('border', '3px solid #' .. colors.dark)
:css('border-bottom', 'none')
:css('background-color', '#' .. colors.base)
:css('padding', '7px')
local header = table:tag('tr')
:css('background-color', '#' .. colors.light)
:css('color', '#' .. colors.dark)
header:tag('th'):wikitext('Move'):css('width', '20%'):css('border-top-left-radius', '25px')
header:tag('th'):wikitext('Type'):css('width', '17%')
header:tag('th'):wikitext('Category'):css('width', '16%')
header:tag('th'):wikitext('Base Power'):css('width', '17%')
header:tag('th'):wikitext('Accuracy'):css('width', '15%')
header:tag('th'):wikitext('PP'):css('width', '15%'):css('border-top-right-radius', '25px')
for _, moveName in ipairs(moves) do
local moveInfo = moveData[moveName] or {}
local row = table:tag('tr'):css('background-color', '#FFFFFF')
local moveLink = '[[' .. moveName .. ']]'
if isSTAB(moveInfo.type, primaryType, secondaryType) and moveInfo.category ~= "Status" then
moveLink = "'''" .. moveLink .. "'''"
end
if moveInfo.broken == "Y" then
moveLink = "''" .. moveLink .. "''"
end
row:tag('td'):wikitext(moveLink)
local moveTypeColors = typeColor(frame, moveInfo.type)
row:tag('td')
:css('background-color', '#' .. moveTypeColors.base)
:css('border', '2px solid #' .. moveTypeColors.dark)
:wikitext('[[' .. (moveInfo.type or 'Normal') .. '_(type)|<span style="color:#FFFFFF;">' .. (moveInfo.type or 'Normal') .. '</span>]]')
local moveCatColors = categoryColor(frame, moveInfo.category)
row:tag('td')
:css('background-color', '#' .. moveCatColors.base)
:css('border', '2px solid #' .. moveCatColors.dark)
:wikitext('[[' .. (moveInfo.category or 'Status') .. ' moves|<span style="color:#FFFFFF;">' .. (moveInfo.category or 'Status') .. '</span>]]')
row:tag('td'):wikitext(moveInfo.power or '—')
row:tag('td'):wikitext(moveInfo.accuracy or '—')
row:tag('td'):wikitext(moveInfo.pp or '—')
end
html:tag('div')
:css('text-align', 'left')
:css('background-color', '#' .. colors.light)
:css('padding', '10px')
:css('border', '3px solid #' .. colors.dark)
:css('border-top', 'none')
:css('border-radius', '0 0 25px 25px')
:wikitext([=[
* '''Emboldened''' moves denote that this Pokémon avails from a STAB bonus for it.
* ''Italicized'' moves are [[Broken_Moves_%26_Abilities|broken]]; see their individual pages for more information.
* Refer to the [[Move Tutors]] article for tutorship information for all moves hereon.
]=])
return tostring(html) .. '<br clear="all">'
end
function p.renderEggMoves(frame)
local args = getArgs(frame)
local pokemonName = args[1] or args.pokemon
local primaryType = args.type1 or ""
local secondaryType = args.type2 or ""
local pokemon = getCachedLearnset(pokemonName)
local moveData = loadMoveData()
if not pokemon then return '' end
local moves = pokemon.egg_moves or {}
if #moves == 0 then
return ""
end
local colors = typeColor(frame, primaryType)
local html = mw.html.create('div')
:css('width', '77%')
:css('margin', 'auto')
local table = html:tag('table')
:addClass('sortable')
:css('width', '100%')
:css('text-align', 'center')
:css('border-radius', '25px 25px 0 0')
:css('border', '3px solid #' .. colors.dark)
:css('border-bottom', 'none')
:css('background-color', '#' .. colors.base)
:css('padding', '7px')
local header = table:tag('tr')
:css('background-color', '#' .. colors.light)
:css('color', '#' .. colors.dark)
header:tag('th'):wikitext('Move'):css('width', '20%'):css('border-top-left-radius', '25px')
header:tag('th'):wikitext('Type'):css('width', '17%')
header:tag('th'):wikitext('Category'):css('width', '16%')
header:tag('th'):wikitext('Base Power'):css('width', '17%')
header:tag('th'):wikitext('Accuracy'):css('width', '15%')
header:tag('th'):wikitext('PP'):css('width', '15%'):css('border-top-right-radius', '25px')
for _, moveName in ipairs(moves) do
local moveInfo = moveData[moveName] or {}
local row = table:tag('tr'):css('background-color', '#FFFFFF')
local moveLink = '[[' .. moveName .. ']]'
if isSTAB(moveInfo.type, primaryType, secondaryType) and moveInfo.category ~= "Status" then
moveLink = "'''" .. moveLink .. "'''"
end
if moveInfo.broken == "Y" then
moveLink = "''" .. moveLink .. "''"
end
row:tag('td'):wikitext(moveLink)
local moveTypeColors = typeColor(frame, moveInfo.type)
row:tag('td')
:css('background-color', '#' .. moveTypeColors.base)
:css('border', '2px solid #' .. moveTypeColors.dark)
:wikitext('[[' .. (moveInfo.type or 'Normal') .. '_(type)|<span style="color:#FFFFFF;">' .. (moveInfo.type or 'Normal') .. '</span>]]')
local moveCatColors = categoryColor(frame, moveInfo.category)
row:tag('td')
:css('background-color', '#' .. moveCatColors.base)
:css('border', '2px solid #' .. moveCatColors.dark)
:wikitext('[[' .. (moveInfo.category or 'Status') .. ' moves|<span style="color:#FFFFFF;">' .. (moveInfo.category or 'Status') .. '</span>]]')
row:tag('td'):wikitext(moveInfo.power or '—')
row:tag('td'):wikitext(moveInfo.accuracy or '—')
row:tag('td'):wikitext(moveInfo.pp or '—')
end
html:tag('div')
:css('text-align', 'left')
:css('background-color', '#' .. colors.light)
:css('padding', '10px')
:css('border', '3px solid #' .. colors.dark)
:css('border-top', 'none')
:css('border-radius', '0 0 25px 25px')
:wikitext([=[
* '''Emboldened''' moves denote that this Pokémon avails from a STAB bonus for it.
* ''Italicized'' moves are [[Broken_Moves_%26_Abilities|broken]]; see their individual pages for more information.
* For more information on egg-move-tutors, refer to: [[Egg Moves]].
]=])
return tostring(html) .. '<br clear="all">'
end
local function renderEmptyNotice(frame, primaryType, radius, message)
local colors = typeColor(frame, primaryType)
local html = mw.html.create('div')
:css('width', '77%')
:css('margin', 'auto')
:css('text-align', 'center')
:css('background-color', '#' .. colors.light)
:css('padding', '20px 10px')
:css('border', '3px solid #' .. colors.dark)
:css('border-radius', radius)
:wikitext("''" .. message .. "''")
return tostring(html) .. '<br clear="all">'
end
local function hyphenSpaceVariant(s)
if mw.ustring.find(s, '-', 1, true) then
return (mw.ustring.gsub(s, '%-', ' '))
end
return (mw.ustring.gsub(s, ' ', '-'))
end
local function resolveType(pokemonName, type1Override, type2Override)
if type1Override ~= '' then
return type1Override, type2Override
end
local typeData = loadPokemonTypes()
local pt = typeData[pokemonName]
or typeData[sentenceCase(pokemonName)]
or typeData[hyphenSpaceVariant(pokemonName)]
if pt then
return pt.type1 or '', pt.type2 or ''
end
return '', ''
end
function p.main(frame)
local args = getArgs(frame)
local pokemonName = args[1] or args.pokemon or ''
local originalName = pokemonName
if pokemonName == '' then
return "<span style='color:red'>Usage: {{Learnsets|PokemonName}}</span>"
end
primeLearnsetCache({ pokemonName })
local poke, lsErr, resolvedName = resolvePagePokemon(pokemonName)
if not poke then
return "<span style='color:red'>Data load error: " .. (lsErr or 'unknown') .. "</span>"
end
pokemonName = resolvedName
local primaryType, secondaryType = resolveType(originalName, args.type1 or '', args.type2 or '')
if primaryType == '' and originalName ~= pokemonName then
primaryType, secondaryType = resolveType(pokemonName, '', '')
end
local function callRender(fn, name, type1, type2)
return fn(frame:newChild({
title = frame:getTitle(),
args = { name, type1 = type1, type2 = type2 },
}))
end
local formCandidates = {}
for _, form in ipairs(FORM_SUFFIXES) do
local candidateName
if form.learnsetStyle == 'prefix-space' then
candidateName = form.suffix .. ' ' .. pokemonName
else
candidateName = pokemonName .. '-' .. form.suffix
end
table.insert(formCandidates, { form = form, candidateName = candidateName })
end
do
local names = {}
for _, fc in ipairs(formCandidates) do
table.insert(names, fc.candidateName)
end
primeLearnsetCache(names)
end
local forms = {}
for _, fc in ipairs(formCandidates) do
local form, candidateName = fc.form, fc.candidateName
local candidatePoke = getCachedLearnset(candidateName)
if candidatePoke then
local rawListName
if form.rawList == 'suffix' then
rawListName = candidateName
elseif form.rawList == 'prefix-hyphen' then
rawListName = form.label .. '-' .. pokemonName
elseif form.rawList == 'suffix-label' then
rawListName = pokemonName .. '-' .. form.label
elseif form.rawList == 'suffix-space' then
rawListName = pokemonName .. ' ' .. form.label
else
rawListName = form.label .. ' ' .. pokemonName
end
local t1, t2 = resolveType(rawListName, '', '')
table.insert(forms, {
name = form.label,
fullName = candidateName,
displayName = rawListName,
poke = candidatePoke,
type1 = t1,
type2 = t2,
baseLabel = form.baseLabel,
})
end
end
local sectionDefs = {
{ heading = 'Level-up', plain = 'Level-up', radius = '15px', render = p.renderLevelUp,
hasData = function(pk) return #(pk.level_up or {}) > 0 end },
{ heading = '[[TMs and HMs]]', plain = 'TMs and HMs', radius = '25px', render = p.renderTMsHMs,
hasData = function(pk) return #(pk.tms or {}) > 0 or #(pk.hms or {}) > 0 end },
{ heading = '[[Move Tutors]]', plain = 'Move Tutors', radius = '25px', render = p.renderMoveTutors,
hasData = function(pk) return #(pk.move_tutors or {}) > 0 end },
{ heading = '[[Egg Moves]]', plain = 'Egg Moves', radius = '25px', render = p.renderEggMoves,
hasData = function(pk) return #(pk.egg_moves or {}) > 0 end },
}
local baseLabel = 'Normal'
for _, f in ipairs(forms) do
if f.baseLabel then
baseLabel = f.baseLabel
break
end
end
local baseDisplayName = (baseLabel ~= 'Normal') and (pokemonName .. ' ' .. baseLabel) or pokemonName
local allParticipants = { { label = baseLabel, name = pokemonName, displayName = baseDisplayName, type1 = primaryType, type2 = secondaryType, poke = poke, isBase = true } }
for _, f in ipairs(forms) do
table.insert(allParticipants, { label = f.name, name = f.fullName, displayName = f.displayName, type1 = f.type1, type2 = f.type2, poke = f.poke })
end
local sections = {}
local usedTabs = false
for _, def in ipairs(sectionDefs) do
local anyHasData = false
for _, part in ipairs(allParticipants) do
if def.hasData(part.poke) then
anyHasData = true
break
end
end
if anyHasData then
table.insert(sections, "===" .. def.heading .. "===")
if #allParticipants == 1 then
local part = allParticipants[1]
table.insert(sections, callRender(def.render, part.name, part.type1, part.type2))
else
usedTabs = true
local tabHeaders, infoBoxes = '', ''
for _, part in ipairs(allParticipants) do
tabHeaders = tabHeaders .. "{{Tabs:MultiformTab|Form=" .. part.label .. "}}"
local box
if def.hasData(part.poke) then
box = callRender(def.render, part.name, part.type1, part.type2)
else
box = renderEmptyNotice(frame, part.type1, def.radius,
'No ' .. def.plain .. ' data for ' .. (part.displayName or part.label) .. '.')
end
if part.isBase then
infoBoxes = infoBoxes .. "{{Tabs:MultiformTableAdditional|Table=" .. box .. "}}"
else
infoBoxes = infoBoxes .. "{{Tabs:MultiformTableAdditional|Form=" .. part.label .. "|Table=" .. box .. "}}"
end
end
table.insert(sections,
"{{TabbedInfoBoxes|Width=85|Class=|TabHeaders=" .. tabHeaders .. "|InfoBoxes=\n"
.. infoBoxes .. "\n}}")
end
end
end
if #sections == 0 then
return ''
end
local output = "==Moves==\n" .. table.concat(sections, '\n')
if usedTabs then
return frame:preprocess(output)
end
return output
end
return p