<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.pokemonrevolution.net/index.php?action=history&amp;feed=atom&amp;title=Module%3AMoveLearnability</id>
	<title>Module:MoveLearnability - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.pokemonrevolution.net/index.php?action=history&amp;feed=atom&amp;title=Module%3AMoveLearnability"/>
	<link rel="alternate" type="text/html" href="https://wiki.pokemonrevolution.net/index.php?title=Module:MoveLearnability&amp;action=history"/>
	<updated>2026-11-11T13:45:46Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://wiki.pokemonrevolution.net/index.php?title=Module:MoveLearnability&amp;diff=40419&amp;oldid=prev</id>
		<title>Cae: created page</title>
		<link rel="alternate" type="text/html" href="https://wiki.pokemonrevolution.net/index.php?title=Module:MoveLearnability&amp;diff=40419&amp;oldid=prev"/>
		<updated>2026-07-18T13:46:55Z</updated>

		<summary type="html">&lt;p&gt;created page&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
local PokemonForms = require(&amp;#039;Module:PokemonForms&amp;#039;)&lt;br /&gt;
local FORM_SUFFIXES = PokemonForms.FORM_SUFFIXES&lt;br /&gt;
local RAW_LIST_ALIASES = PokemonForms.RAW_LIST_ALIASES&lt;br /&gt;
local MOB_PREFIXES = PokemonForms.MOB_PREFIXES&lt;br /&gt;
&lt;br /&gt;
local function getArgs(frame)&lt;br /&gt;
    local args = {}&lt;br /&gt;
    local parent = frame:getParent()&lt;br /&gt;
&lt;br /&gt;
    if parent then&lt;br /&gt;
        for k, v in pairs(parent.args) do&lt;br /&gt;
            if v ~= &amp;#039;&amp;#039; then&lt;br /&gt;
                args[k] = v&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for k, v in pairs(frame.args) do&lt;br /&gt;
        if v ~= &amp;#039;&amp;#039; then&lt;br /&gt;
            args[k] = v&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function blankToNil(v)&lt;br /&gt;
    if v == nil or v == &amp;#039;&amp;#039; then&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
    return v&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local moveDataCache = nil&lt;br /&gt;
&lt;br /&gt;
local function loadMoveData()&lt;br /&gt;
    if moveDataCache then&lt;br /&gt;
        return moveDataCache&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local csvData = mw.ext.externalData.getExternalData({&lt;br /&gt;
        url = &amp;#039;https://wiki.pokemonrevolution.net/index.php?title=Special:GetData/RawMovesList&amp;#039;,&lt;br /&gt;
        format = &amp;#039;CSV with header&amp;#039;,&lt;br /&gt;
        data = {&lt;br /&gt;
            Name = &amp;#039;Name&amp;#039;,&lt;br /&gt;
            Type = &amp;#039;Type&amp;#039;,&lt;br /&gt;
            Category = &amp;#039;Category&amp;#039;,&lt;br /&gt;
        },&lt;br /&gt;
    })&lt;br /&gt;
&lt;br /&gt;
    moveDataCache = {}&lt;br /&gt;
    if csvData then&lt;br /&gt;
        for _, row in ipairs(csvData) do&lt;br /&gt;
            moveDataCache[row.Name] = {&lt;br /&gt;
                type = blankToNil(row.Type) or &amp;#039;Normal&amp;#039;,&lt;br /&gt;
                category = blankToNil(row.Category) or &amp;#039;Status&amp;#039;,&lt;br /&gt;
            }&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return moveDataCache&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local pokemonDataCache = nil&lt;br /&gt;
&lt;br /&gt;
local function loadPokemonData()&lt;br /&gt;
    if pokemonDataCache then&lt;br /&gt;
        return pokemonDataCache&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local csvData = mw.ext.externalData.getExternalData({&lt;br /&gt;
        url    = &amp;#039;https://wiki.pokemonrevolution.net/index.php?title=Special:GetData/PokemonRawList&amp;#039;,&lt;br /&gt;
        format = &amp;#039;CSV with header&amp;#039;,&lt;br /&gt;
        data = {&lt;br /&gt;
            Name = &amp;#039;Name&amp;#039;,&lt;br /&gt;
            number = &amp;#039;Pokedex Number&amp;#039;,&lt;br /&gt;
            Type1 = &amp;#039;Type1&amp;#039;,&lt;br /&gt;
            Type2 = &amp;#039;Type2&amp;#039;,&lt;br /&gt;
        },&lt;br /&gt;
    })&lt;br /&gt;
&lt;br /&gt;
    pokemonDataCache = {}&lt;br /&gt;
    if csvData then&lt;br /&gt;
        for _, row in ipairs(csvData) do&lt;br /&gt;
            pokemonDataCache[row.Name] = {&lt;br /&gt;
                number = blankToNil(row.number),&lt;br /&gt;
                type1 = row.Type1 or &amp;#039;&amp;#039;,&lt;br /&gt;
                type2 = row.Type2 or &amp;#039;&amp;#039;,&lt;br /&gt;
            }&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return pokemonDataCache&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function loadMoveLearnset(moveName)&lt;br /&gt;
    local jsonPath = &amp;#039;$[&amp;#039; .. mw.text.jsonEncode(moveName) .. &amp;#039;]&amp;#039;&lt;br /&gt;
    local ok, result, errors = pcall(function()&lt;br /&gt;
        return mw.ext.externalData.getFileData({&lt;br /&gt;
            source = &amp;#039;data&amp;#039;,&lt;br /&gt;
            [&amp;#039;file name&amp;#039;] = &amp;#039;Move_Learnsets.json&amp;#039;,&lt;br /&gt;
            format = &amp;#039;JSON&amp;#039;,&lt;br /&gt;
            [&amp;#039;use jsonpath&amp;#039;] = true,&lt;br /&gt;
            data = { move = jsonPath },&lt;br /&gt;
        })&lt;br /&gt;
    end)&lt;br /&gt;
    if not ok then&lt;br /&gt;
        return nil, &amp;#039;Lua error: &amp;#039; .. tostring(result)&lt;br /&gt;
    end&lt;br /&gt;
    if errors and #errors &amp;gt; 0 then&lt;br /&gt;
        return nil, &amp;#039;ExternalData error: &amp;#039; .. table.concat(errors, &amp;#039; | &amp;#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local raw = result and result.move&lt;br /&gt;
    if type(raw) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
        return nil, &amp;#039;No learnability data found for &amp;#039; .. moveName&lt;br /&gt;
    end&lt;br /&gt;
    return raw, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local typeColorCache = {}&lt;br /&gt;
local function typeColor(frame, pokeType)&lt;br /&gt;
    pokeType = (pokeType and pokeType ~= &amp;#039;&amp;#039;) and pokeType or &amp;#039;Normal&amp;#039;&lt;br /&gt;
    if not typeColorCache[pokeType] then&lt;br /&gt;
        typeColorCache[pokeType] = {&lt;br /&gt;
            light = frame:expandTemplate{ title = pokeType .. &amp;#039; color light&amp;#039; },&lt;br /&gt;
            base = frame:expandTemplate{ title = pokeType .. &amp;#039; color&amp;#039; },&lt;br /&gt;
            dark = frame:expandTemplate{ title = pokeType .. &amp;#039; color dark&amp;#039; },&lt;br /&gt;
        }&lt;br /&gt;
    end&lt;br /&gt;
    return typeColorCache[pokeType]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hyphenSpaceVariant(s)&lt;br /&gt;
    if mw.ustring.find(s, &amp;#039;-&amp;#039;, 1, true) then&lt;br /&gt;
        return (mw.ustring.gsub(s, &amp;#039;%-&amp;#039;, &amp;#039; &amp;#039;))&lt;br /&gt;
    end&lt;br /&gt;
    return (mw.ustring.gsub(s, &amp;#039; &amp;#039;, &amp;#039;-&amp;#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Learnset-style name -&amp;gt; exact PokemonRawList name, or nil.&lt;br /&gt;
local function resolveDirect(name, data)&lt;br /&gt;
    local aliased = RAW_LIST_ALIASES[name]&lt;br /&gt;
    if aliased and data[aliased] then&lt;br /&gt;
        return aliased&lt;br /&gt;
    end&lt;br /&gt;
    if data[name] then&lt;br /&gt;
        return name&lt;br /&gt;
    end&lt;br /&gt;
    local variant = hyphenSpaceVariant(name)&lt;br /&gt;
    if data[variant] then&lt;br /&gt;
        return variant&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for _, form in ipairs(FORM_SUFFIXES) do&lt;br /&gt;
        local base&lt;br /&gt;
        if form.learnsetStyle == &amp;#039;prefix-space&amp;#039; then&lt;br /&gt;
            local prefix = form.suffix .. &amp;#039; &amp;#039;&lt;br /&gt;
            if mw.ustring.sub(name, 1, mw.ustring.len(prefix)) == prefix then&lt;br /&gt;
                base = mw.ustring.sub(name, mw.ustring.len(prefix) + 1)&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
            local tail = &amp;#039;-&amp;#039; .. form.suffix&lt;br /&gt;
            if mw.ustring.sub(name, -mw.ustring.len(tail)) == tail then&lt;br /&gt;
                base = mw.ustring.sub(name, 1, -mw.ustring.len(tail) - 1)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if base then&lt;br /&gt;
            local rawName&lt;br /&gt;
            if form.rawList == &amp;#039;suffix&amp;#039; then&lt;br /&gt;
                rawName = name&lt;br /&gt;
            elseif form.rawList == &amp;#039;prefix-hyphen&amp;#039; then&lt;br /&gt;
                rawName = form.label .. &amp;#039;-&amp;#039; .. base&lt;br /&gt;
            elseif form.rawList == &amp;#039;suffix-label&amp;#039; then&lt;br /&gt;
                rawName = base .. &amp;#039;-&amp;#039; .. form.label&lt;br /&gt;
            elseif form.rawList == &amp;#039;suffix-space&amp;#039; then&lt;br /&gt;
                rawName = base .. &amp;#039; &amp;#039; .. form.label&lt;br /&gt;
            else&lt;br /&gt;
                rawName = form.label .. &amp;#039; &amp;#039; .. base&lt;br /&gt;
            end&lt;br /&gt;
            if data[rawName] then&lt;br /&gt;
                return rawName&lt;br /&gt;
            end&lt;br /&gt;
            local v2 = hyphenSpaceVariant(rawName)&lt;br /&gt;
            if data[v2] then&lt;br /&gt;
                return v2&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Full resolution: direct match first; then special-mob prefix strip&lt;br /&gt;
-- (&amp;quot;Crystal Onix&amp;quot; -&amp;gt; Onix, keeping its own name and page link); then&lt;br /&gt;
-- progressive suffix strip for forms missing from the raw list&lt;br /&gt;
-- (&amp;quot;Arceus-Fire&amp;quot; -&amp;gt; Arceus, piped link to the base page).&lt;br /&gt;
local function resolvePokemon(name)&lt;br /&gt;
    local data = loadPokemonData()&lt;br /&gt;
&lt;br /&gt;
    local rawName = resolveDirect(name, data)&lt;br /&gt;
    if rawName then&lt;br /&gt;
        return { raw = data[rawName], rawName = rawName, display = rawName, link = rawName, direct = true }&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for _, prefix in ipairs(MOB_PREFIXES) do&lt;br /&gt;
        local head = prefix .. &amp;#039; &amp;#039;&lt;br /&gt;
        if mw.ustring.sub(name, 1, mw.ustring.len(head)) == head then&lt;br /&gt;
            rawName = resolveDirect(mw.ustring.sub(name, mw.ustring.len(head) + 1), data)&lt;br /&gt;
            if rawName then&lt;br /&gt;
                return { raw = data[rawName], rawName = rawName, display = name, link = name, direct = false, mob = true }&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local base = name&lt;br /&gt;
    while mw.ustring.find(base, &amp;#039;-&amp;#039;, 1, true) do&lt;br /&gt;
        base = mw.ustring.gsub(base, &amp;#039;%-[^%-]*$&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
        rawName = resolveDirect(base, data)&lt;br /&gt;
        if rawName then&lt;br /&gt;
            return { raw = data[rawName], rawName = rawName, display = name, link = rawName, direct = false }&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return { raw = nil, rawName = nil, display = name, link = name, direct = false }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Resolve, dedupe, and dex-sort one section&amp;#039;s entries.&lt;br /&gt;
-- entries: list of { name = learnsetName, level = number|nil }.&lt;br /&gt;
local function prepareRows(entries)&lt;br /&gt;
    local rows = {}&lt;br /&gt;
    local byKey = {}&lt;br /&gt;
&lt;br /&gt;
    for _, e in ipairs(entries) do&lt;br /&gt;
        local row = resolvePokemon(e.name)&lt;br /&gt;
        row.level = e.level&lt;br /&gt;
&lt;br /&gt;
        local key = (row.rawName or row.display) .. &amp;#039;\0&amp;#039; .. tostring(e.level or &amp;#039;&amp;#039;)&lt;br /&gt;
        local existing = byKey[key]&lt;br /&gt;
        if not existing then&lt;br /&gt;
            byKey[key] = row&lt;br /&gt;
            table.insert(rows, row)&lt;br /&gt;
        elseif row.direct and not existing.direct then&lt;br /&gt;
            for k, v in pairs(row) do&lt;br /&gt;
                existing[k] = v&lt;br /&gt;
            end&lt;br /&gt;
            existing.collapsed = nil&lt;br /&gt;
        elseif not row.direct and not existing.direct and not existing.mob then&lt;br /&gt;
            -- several raw-list-less forms of the same base; show the base once&lt;br /&gt;
            existing.collapsed = true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(rows, function(a, b)&lt;br /&gt;
        local an = a.raw and tonumber(mw.ustring.match(a.raw.number or &amp;#039;&amp;#039;, &amp;#039;^%d+&amp;#039;)) or math.huge&lt;br /&gt;
        local bn = b.raw and tonumber(mw.ustring.match(b.raw.number or &amp;#039;&amp;#039;, &amp;#039;^%d+&amp;#039;)) or math.huge&lt;br /&gt;
        if an ~= bn then&lt;br /&gt;
            return an &amp;lt; bn&lt;br /&gt;
        end&lt;br /&gt;
        local as = a.raw and a.raw.number or &amp;#039;&amp;#039;&lt;br /&gt;
        local bs = b.raw and b.raw.number or &amp;#039;&amp;#039;&lt;br /&gt;
        if as ~= bs then&lt;br /&gt;
            return as &amp;lt; bs&lt;br /&gt;
        end&lt;br /&gt;
        return (a.display or &amp;#039;&amp;#039;) &amp;lt; (b.display or &amp;#039;&amp;#039;)&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function padNumber(num)&lt;br /&gt;
    if mw.ustring.len(num) &amp;lt; 3 then&lt;br /&gt;
        return string.rep(&amp;#039;0&amp;#039;, 3 - mw.ustring.len(num)) .. num&lt;br /&gt;
    end&lt;br /&gt;
    return num&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function addTypeCell(frame, tr, pokeType)&lt;br /&gt;
    local td = tr:tag(&amp;#039;td&amp;#039;)&lt;br /&gt;
    if pokeType and pokeType ~= &amp;#039;&amp;#039; then&lt;br /&gt;
        local c = typeColor(frame, pokeType)&lt;br /&gt;
        td:css(&amp;#039;background-color&amp;#039;, &amp;#039;#&amp;#039; .. c.base)&lt;br /&gt;
            :css(&amp;#039;border&amp;#039;, &amp;#039;2px solid #&amp;#039; .. c.dark)&lt;br /&gt;
            :wikitext(&amp;#039;[[&amp;#039; .. pokeType .. &amp;#039;_(type)|&amp;lt;span style=&amp;quot;color:#FFFFFF;&amp;quot;&amp;gt;&amp;#039; .. pokeType .. &amp;#039;&amp;lt;/span&amp;gt;]]&amp;#039;)&lt;br /&gt;
    else&lt;br /&gt;
        td:wikitext(&amp;#039;—&amp;#039;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderTable(frame, entries, moveType, isDamaging, colors, withLevel)&lt;br /&gt;
    local rows = prepareRows(entries)&lt;br /&gt;
&lt;br /&gt;
    local tbl = mw.html.create(&amp;#039;table&amp;#039;)&lt;br /&gt;
        :attr(&amp;#039;cellspacing&amp;#039;, &amp;#039;3&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;width&amp;#039;, withLevel and &amp;#039;62%&amp;#039; or &amp;#039;60%&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;background-color&amp;#039;, &amp;#039;#&amp;#039; .. colors.base)&lt;br /&gt;
        :css(&amp;#039;border&amp;#039;, &amp;#039;4px solid #&amp;#039; .. colors.dark)&lt;br /&gt;
        :css(&amp;#039;padding&amp;#039;, &amp;#039;4px&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;border-radius&amp;#039;, &amp;#039;25px&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;text-align&amp;#039;, &amp;#039;center&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    local header = tbl:tag(&amp;#039;tr&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;color&amp;#039;, &amp;#039;#&amp;#039; .. colors.dark)&lt;br /&gt;
        :css(&amp;#039;background-color&amp;#039;, &amp;#039;#&amp;#039; .. colors.light)&lt;br /&gt;
    local function th(text)&lt;br /&gt;
        return header:tag(&amp;#039;th&amp;#039;):wikitext(text):css(&amp;#039;border&amp;#039;, &amp;#039;1px solid #&amp;#039; .. colors.dark)&lt;br /&gt;
    end&lt;br /&gt;
    th(&amp;#039;No.&amp;#039;):attr(&amp;#039;colspan&amp;#039;, &amp;#039;2&amp;#039;):css(&amp;#039;width&amp;#039;, &amp;#039;17%&amp;#039;):css(&amp;#039;border-top-left-radius&amp;#039;, &amp;#039;25px&amp;#039;)&lt;br /&gt;
    th(&amp;#039;Pokémon&amp;#039;):css(&amp;#039;width&amp;#039;, &amp;#039;27%&amp;#039;)&lt;br /&gt;
    local t1 = th(&amp;#039;Type 1&amp;#039;):css(&amp;#039;width&amp;#039;, &amp;#039;20%&amp;#039;)&lt;br /&gt;
    local t2 = th(&amp;#039;Type 2&amp;#039;):css(&amp;#039;width&amp;#039;, &amp;#039;20%&amp;#039;)&lt;br /&gt;
    if withLevel then&lt;br /&gt;
        th(&amp;#039;Level&amp;#039;):css(&amp;#039;width&amp;#039;, &amp;#039;13%&amp;#039;):css(&amp;#039;border-top-right-radius&amp;#039;, &amp;#039;25px&amp;#039;)&lt;br /&gt;
    else&lt;br /&gt;
        t2:css(&amp;#039;border-top-right-radius&amp;#039;, &amp;#039;25px&amp;#039;)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for _, row in ipairs(rows) do&lt;br /&gt;
        local tr = tbl:tag(&amp;#039;tr&amp;#039;):css(&amp;#039;background-color&amp;#039;, &amp;#039;#FFFFFF&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
        local num = row.raw and row.raw.number&lt;br /&gt;
        tr:tag(&amp;#039;td&amp;#039;):wikitext(num and padNumber(num) or &amp;#039;—&amp;#039;)&lt;br /&gt;
        tr:tag(&amp;#039;td&amp;#039;):wikitext(num and (&amp;#039;[[File:&amp;#039; .. num .. &amp;#039;Icon.png]]&amp;#039;) or &amp;#039;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
        local display = row.collapsed and row.rawName or row.display&lt;br /&gt;
        local target = row.collapsed and row.rawName or row.link&lt;br /&gt;
        local link&lt;br /&gt;
        if target == display then&lt;br /&gt;
            link = &amp;#039;[[&amp;#039; .. display .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        else&lt;br /&gt;
            link = &amp;#039;[[&amp;#039; .. target .. &amp;#039;|&amp;#039; .. display .. &amp;#039;]]&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
        if isDamaging and row.raw and (row.raw.type1 == moveType or row.raw.type2 == moveType) then&lt;br /&gt;
            link = &amp;quot;&amp;#039;&amp;#039;&amp;#039;&amp;quot; .. link .. &amp;quot;&amp;#039;&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        tr:tag(&amp;#039;td&amp;#039;):wikitext(link)&lt;br /&gt;
&lt;br /&gt;
        addTypeCell(frame, tr, row.raw and row.raw.type1)&lt;br /&gt;
        addTypeCell(frame, tr, row.raw and row.raw.type2)&lt;br /&gt;
&lt;br /&gt;
        if withLevel then&lt;br /&gt;
            tr:tag(&amp;#039;td&amp;#039;):wikitext(row.level and tostring(row.level) or &amp;#039;—&amp;#039;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local footer = tbl:tag(&amp;#039;tr&amp;#039;)&lt;br /&gt;
    footer:tag(&amp;#039;td&amp;#039;)&lt;br /&gt;
        :attr(&amp;#039;colspan&amp;#039;, withLevel and &amp;#039;6&amp;#039; or &amp;#039;5&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;text-align&amp;#039;, &amp;#039;left&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;border&amp;#039;, &amp;#039;1px solid #&amp;#039; .. colors.dark)&lt;br /&gt;
        :css(&amp;#039;border-radius&amp;#039;, &amp;#039;1px 1px 25px 25px&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;background-color&amp;#039;, &amp;#039;#&amp;#039; .. colors.light)&lt;br /&gt;
        :css(&amp;#039;color&amp;#039;, &amp;#039;#&amp;#039; .. colors.dark)&lt;br /&gt;
        :css(&amp;#039;padding-left&amp;#039;, &amp;#039;10px&amp;#039;)&lt;br /&gt;
        :wikitext(&amp;#039;&amp;lt;p style=&amp;quot;margin-top: 8px;&amp;quot;&amp;gt;\&amp;#039;\&amp;#039;\&amp;#039;Emboldened\&amp;#039;\&amp;#039;\&amp;#039; Pokémon avail from a STAB bonus for this move.&amp;lt;/p&amp;gt;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    return tostring(tbl) .. &amp;#039;&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
    local moveName = args[1] or args.move or &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    if moveName == &amp;#039;&amp;#039; then&lt;br /&gt;
        local title = mw.title.getCurrentTitle()&lt;br /&gt;
        moveName = title and title.text or &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    if moveName == &amp;#039;&amp;#039; then&lt;br /&gt;
        return &amp;quot;&amp;lt;span style=&amp;#039;color:red&amp;#039;&amp;gt;Usage: {{MoveLearnability|MoveName}}&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local entry, err = loadMoveLearnset(moveName)&lt;br /&gt;
    if not entry then&lt;br /&gt;
        return &amp;quot;&amp;lt;span style=&amp;#039;color:red&amp;#039;&amp;gt;Data load error: &amp;quot; .. (err or &amp;#039;unknown&amp;#039;) .. &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local moveInfo = loadMoveData()[moveName] or {}&lt;br /&gt;
    local moveType = moveInfo.type or &amp;#039;Normal&amp;#039;&lt;br /&gt;
    local isDamaging = (moveInfo.category or &amp;#039;Status&amp;#039;) ~= &amp;#039;Status&amp;#039;&lt;br /&gt;
    local colors = typeColor(frame, moveType)&lt;br /&gt;
&lt;br /&gt;
    local sectionDefs = {&lt;br /&gt;
        { heading = &amp;#039;Level-Up/Instinctual&amp;#039;, key = &amp;#039;level_up&amp;#039;, withLevel = true },&lt;br /&gt;
        { heading = &amp;#039;[[TMs]]&amp;#039;, key = &amp;#039;tms&amp;#039; },&lt;br /&gt;
        { heading = &amp;#039;[[Move tutor]]&amp;#039;, key = &amp;#039;move_tutors&amp;#039; },&lt;br /&gt;
        { heading = &amp;#039;[[Egg-Inheritable Moves|Egg Moves]]&amp;#039;, key = &amp;#039;egg_moves&amp;#039; },&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local sections = {}&lt;br /&gt;
    for _, def in ipairs(sectionDefs) do&lt;br /&gt;
        local raw = entry[def.key] or {}&lt;br /&gt;
        if #raw &amp;gt; 0 then&lt;br /&gt;
            local entries = {}&lt;br /&gt;
            for _, e in ipairs(raw) do&lt;br /&gt;
                if type(e) == &amp;#039;table&amp;#039; then&lt;br /&gt;
                    table.insert(entries, { name = e.pokemon, level = e.level })&lt;br /&gt;
                else&lt;br /&gt;
                    table.insert(entries, { name = e })&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            table.insert(sections, &amp;#039;===&amp;#039; .. def.heading .. &amp;#039;===&amp;#039;)&lt;br /&gt;
            table.insert(sections, renderTable(frame, entries, moveType, isDamaging, colors, def.withLevel))&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if #sections == 0 then&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return &amp;#039;==Learnability==\n&amp;#039; .. table.concat(sections, &amp;#039;\n&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Cae</name></author>
	</entry>
</feed>