Modulo:Wikilib/learnlists

da Pokémon Central Wiki, l'enciclopedia Pokémon in italiano.
Vai alla navigazione Vai alla ricerca
Questo modulo non ha ancora un manuale. Creane uno!
-- Libreria per i moduli learnlist e movelist

local lib = {}

-- stylua: ignore start
local w = require('Modulo:Wikilib')
local txt = require('Modulo:Wikilib/strings')
local ms = require('Modulo:MiniSprite')
local box = require('Modulo:Box')
local c = mw.loadData('Modulo:Colore/data')
local css = require('Modulo:Css')
local sig = mw.loadData('Modulo:Sigle/data')
local s = mw.loadData('Modulo:Sup/data')
local abbrLib = require('Modulo:Wikilib/sigle')
local links = require('Modulo:Links')
local pokes = mw.loadData('Modulo:Poké/data')
local moves = mw.loadData('Modulo:Move/data')
local tmdata = mw.loadData('Modulo:Machines/data')
local forms = require('Modulo:Wikilib/forms')
local multigen = require('Modulo:Wikilib/multigen')
local evolib = require('Modulo:Wikilib/evos')
local wdata = mw.loadData('Modulo:Wikilib/data')
-- stylua: ignore end

local strings = {
    -- Wikicode per la cella di un gioco nell'entry level
    GAMELEVELCELL = "| ",
    -- Wikicode per gli entrynull
    ENTRYNULL = [[|-
! class="white-bg" style="padding: 0.1em 0.3em;" colspan="${cs}" | Questo Pokémon non impara nessuna mossa ${ending}.]],
}

-- local trimOnly = {'x v zA'}

local entryNullEnd = {
    level = "aumentando di livello",
    tm = "tramite MT",
    breed = "tramite accoppiamento",
    tutor = "dall'Insegnamosse",
    preevo = "tramite evoluzioni precedenti",
    event = "tramite evento",
}

-- Contiene i title per le pre-evoluzioni
lib.preevott = {
    T = links.tt("*", "Mossa appresa dall'Esperto Mosse"),
    E = links.tt("†", "Mossa appresa tramite evento"),
    D = links.tt("‡", "Mossa appresa nel Dream World"),
}

-- Games for various kinds of learn, divided by gen
lib.games = {
    level = {
        { "RB", "G" },
        { "OA", "C" },
        { "RZ", "RFVF", "S" },
        { "DP", "Pt", "HGSS" },
        { "NB", "N2B2" },
        { "XY", "ROZA" },
        { "SL", "USUL" },
        { "SpSc", "DLPS" },
        { "SV" },
    },
    -- TODO make effective this table, right now only gen 8 is used
    tm = { {}, {}, {}, {}, {}, {}, {}, { "SpSc", "DLPS" }, { "SV" } },
    breed = {
        {},
        { "OA", "C" },
        { "RZ", "RFVF", "S" },
        { "DP", "Pt", "HGSS" },
        { "NB", "N2B2" },
        { "XY", "ROZA" },
        { "SL", "USUL" },
        { "SpSc", "DLPS" },
        { "SV" },
    },
    tutor = {
        {},
        { "C" },
        { "RFVF", "S", "XD" },
        { "DP", "Pt", "HGSS" },
        { "NB", "N2B2" },
        { "XY", "ROZA" },
        { "SL", "USUL" },
        { "SpSc", "IA", "DLPS" },
        { "SV" },
    },
}

--[[

Normalize the type "coleot" to "coleottero",
the only form that (in my dreams) should be
used within modules but for printing

I do know this REALLY seems a function I should
put in a shared library (Wikilib-something), but
it's here because my hope is for it to be a
temporary workaround until I normalize data
modules with "coleottero", confining "coleot"
to outputs and banishing it forever from internal
representations.
Heed my words, future mantainer of this code (that
is, whp, just myself), for I hereby task you with
such a burden.
-- Flavio, 02/05/20

Pls sign yourself here with the date whenever you
use this function, so we can witness the failure
of my proposal.

Used again (I hoped it would take longer) -- Flavio, 07/05/2020
Again (copied in PokémonData) -- Flavio, 16/04/2023

--]]
local normalizeColeot = function(type)
    return type == "coleot" and "coleottero" or type
end

local _allTypes = table.map(wdata.allTypes, normalizeColeot)

--[[

Processa i parametri passati al modulo, effettuando
trim e first_uppercase.

--]]
lib.sanitize = function(tab)
    return w.trimAndMap(tab, function(str)
        if str == "x v zA" then
            return str
        end
        return txt.firstUppercase(str)
    end)
end

--[[

Crea le note: il primo parametro sono le note vere e
proprie, i seguenti eventuali altre stringhe da aggiungere.

Se le note sono una sigla, se ne fa il sup; se invece
sono la stringa vuota o un solo carattere, lo si lascia
invariato; negli altri casi le si usano come title.

--]]
lib.makeNotes = function(notes, ...)
    local pieces = { ... }
    local firstGame = notes:match("^(%S+)")

    if s[firstGame] then
        table.insert(pieces, 1, abbrLib.concatAbbrs(notes, s))
    elseif notes:len() < 2 then
        table.insert(pieces, 1, notes)
    else
        table.insert(pieces, 1, links.tt("*", notes))
    end
    return table.concat(pieces)
end

--[[

Ritorna un sup con il livello se l'argomento
è valudo, altrimenti la stringa vuota

--]]
lib.makeLevel = function(level)
    return level and table.concat({ "<sup>Lv.", level, "</sup>" }) or ""
end

--[[

Manda a capo ogni tot mini sprite, utilizzata
nell'entry per il breed, nella cella dei padri

--]]
lib.insertnwlns = function(str, linelength, gen, nobox)
    str = str:gsub("<br>", "")
    linelength = tonumber(linelength) or 7
    gen = gen or ""

    local res, newLinesCount = {}, 1
    local pattern, op
    if str:match("File") then
        pattern = "%[%[File:.-MS%.png|.-|link=.-%]%]"
        op = function(sprite)
            return sprite
        end
    else
        pattern = "#(.-)#"
        op = function(ndex)
            return ms.staticLua({ ndex = ndex, gen = gen })
        end
    end

    table.insert(res, "<div>")
    for minisprite in str:gmatch(pattern) do
        table.insert(res, op(minisprite))

        if (#res - newLinesCount) % linelength == 0 then
            table.insert(res, "</div><div>")
            newLinesCount = newLinesCount + 1
        end
    end
    if nobox then
        table.insert(res, "</div>")
    else
        table.insert(res, "</div></div>")

        if #res > linelength + 2 then
            table.insert(
                res,
                1,
                txt.interp(
                    '<div class="roundy-5 mw-collapsible mw-collapsed" style="background: #${bg}; margin: 0.3em 0;">&nbsp;<div class="mw-collapsible-content">',
                    { bg = c.background }
                )
            )
            table.insert(res, "</div>")
        else
            table.insert(
                res,
                1,
                txt.interp(
                    '<div class="roundy-5" style="background: #${bg}; margin: 0.3em 0;">',
                    { bg = c.background }
                )
            )
        end
    end

    return table.concat(res)
end

-- Interfaccia per mediaWiki della funzione di cui sopra
lib.newline = function(frame)
    return lib.insertnwlns(txt.trim(frame.args[1]), frame.args[2])
end

--[[

Given a string encloses it within a modal. textDisplay is the text displayed
inside the element that binds the modal (defaults to '✔')

--]]
lib.toModal = function(str, textDisplay)
    return table.concat({
        '<span class="open-popup-element explain">',
        textDisplay or "✔",
        '<div class="mfp-hide pull-center max-width-xl-80 roundy white-bg" style="display: table; padding: 0.5em;">',
        str,
        "</div></span>",
    })
end

--[=[

Creates a modal containing a list of MS from the list of ndex or MS passed.

The first parameter is a string in one of the following formats:
* a list of MS links in Wikicode syntax ([[File:.-MS.png|.-|link=.-]])
* a list of ndexes, each surrounded by a pair of #
The second parameter is the gen for the ms (unused if list is in the first
format, default to the latest), the third is the text displayed inside the
element that binds the modal (default to '✔'), the fourth is the max number of
ms in a single line (default nil, that means no line breaks).

--]=]
lib.mslistToModal = function(list, gen, textDisplay, linelength)
    list = list:gsub("<br>", "")
    gen = tostring(gen or "")

    local res = {}
    local pattern, op
    if list:match("File") then
        pattern = "%[%[File:.-MS%.png|.-|link=.-%]%]"
        op = function(sprite)
            return sprite
        end
    else
        pattern = "#(.-)#"
        op = function(ndex)
            return ms.staticLua({ ndex = ndex, gen = gen })
        end
    end

    table.insert(res, "<div>")
    local mscount = 0
    for minisprite in list:gmatch(pattern) do
        table.insert(res, op(minisprite))
        mscount = mscount + 1
        if linelength and mscount % linelength == 0 then
            table.insert(res, "</div><div>")
        end
    end
    table.insert(res, "</div>")

    return lib.toModal(table.concat(res), textDisplay)
end

--[=[

Creates a modal containing a list of MS from an array of ndexes.

The second parameter is the gen for the ms (default to the latest), the third
is the text displayed inside the element that binds the modal (default to '✔'),
the fourth is the max number of ms in a single line (default nil, that means no
line breaks).

--]=]
lib.msarrayToModal = function(array, gen, textDisplay, linelength)
    gen = gen or ""

    local res = {}

    table.insert(res, "<div>")
    local mscount = 0
    for _, ndex in ipairs(array) do
        table.insert(
            res,
            ms.staticLua({
                ndex = type(ndex) == "number" and txt.ff(ndex) or ndex,
                gen = gen,
            })
        )
        mscount = mscount + 1
        if linelength and mscount % linelength == 0 then
            table.insert(res, "</div><div>")
        end
    end
    table.insert(res, "</div>")

    return lib.toModal(table.concat(res), textDisplay)
end

-- Funzione che restituisce i cuori per le gare
lib.concathearts = function(n, black)
    local N = tonumber(n)
    if N == nil or N == 0 then
        return tostring(n)
    end
    return table.concat({
        n,
        ' <span style="color:#',
        black and "000" or "FFAAAA",
        ';">',
        string.rep("♥", N),
        "</span>",
    })
end

-- Interfaccia per mediaWiki della funzione di cui sopra
lib.hearts = function(frame)
    return lib.concathearts(
        txt.trim(frame.args[1]),
        txt.trim(frame.args[2] or ""):lower() == "black"
    )
end

-- ========================= Entry building functions =========================
--[[

Le celle comuni a tutti gli entry nelle generazioni
precedenti l'introduzione delle categorie danno.

--]]
lib.basicentry = function(stab, mossa, notes, tipo, pw, acc, pp)
    local tipobox
    if table.search(_allTypes, tipo:lower()) then
        tipobox = box.boxTipoLua(tipo, { "thick" })
    else
        tipobox = box.boxLua(tipo, tipo, nil, "thick", "box-sconosciuto")
    end
    return txt.interp(
        [=[|| style="padding: 0.3em;" class="black-text" | ${stab}${mossa}${stab}${notes}
| style="padding: 0.8ex 0.3ex; height: 100%;" | ${tipo}
| style="padding: 0.1em 0.3em;" | ${pw}
| style="padding: 0.1em 0.3em;" | ${acc}%
| style="padding: 0.1em 0.3em;" | ${pp}]=],
        {
            mossa = mossa == "&nbsp;" and mossa
                or table.concat({ "[[", mossa, "]]" }),
            stab = stab,
            notes = notes,
            tipo = tipobox,
            pw = pw,
            acc = acc,
            pp = pp,
        }
    )
end

--[[

Le celle comuni a tutti gli entry nelle generazioni
successive l'introduzione delle categorie danno.

--]]
lib.categoryentry = function(stab, mossa, notes, tipo, cat, pw, acc, pp)
    local tipobox
    -- This thing is ineficient af, but anyway
    if table.search(_allTypes, tipo:lower()) then
        tipobox = box.boxTipoLua(tipo, { "thick" })
    else
        tipobox = box.boxLua(tipo, tipo, nil, "thick", "box-sconosciuto")
    end
    return txt.interp(
        [=[

| class="black-text" style="padding: 0.1em 0.3em;" | ${stab}${mossa}${stab}${notes}
| class="height-100" style="padding: 0.8ex 0.3ex;" | ${tipo}
| class="height-100" style="padding: 0.8ex 0.3ex;" | ${cat}
| style="padding: 0.1em 0.3em;" | ${pw}
| style="padding: 0.1em 0.3em;" | ${acc}%
| style="padding: 0.1em 0.3em;" | ${pp}]=],
        {
            mossa = mossa == "&nbsp;" and mossa
                or table.concat({ "[[", mossa, "]]" }),
            stab = stab,
            notes = notes,
            tipo = tipobox,
            cat = box.boxLua(
                cat,
                "Categoria danno#" .. cat,
                cat,
                { "thick" },
                nil,
                nil,
                c[cat .. "_text"]
            ),
            pw = pw,
            acc = acc,
            pp = pp,
        }
    )
end

--[[

DA USARE CON UNO DEI PRECEDENTI ENTRY.

Ritorna soltanto le celle relative alle gare, vale
a dire virtù, fascino e, se passato, intralcio

--]]
lib.contestentry = function(gara, fash, intr)
    return txt.interp(
        [=[|| style="padding: 0.8ex 0.3ex; height: 100%;" | ${gara}
| style="padding: 0.1em 0.3em;" | ${fash}${intr}]=],
        {
            gara = box.boxLua(gara, gara .. " (gara)", gara, { "thick" }),
            fash = lib.concathearts(fash, false),
            intr = intr and table.concat({
                ' || style="padding: 0.3em;" | ',
                lib.concathearts(intr, true),
            }) or "",
        }
    )
end

--[[

Restituisce le prime celle dei level, in numero
pari ai livelli diversi inseriti.

--]]
lib.gameslevel = function(first, second, third)
    if not third then
        --Only one of them
        if not second then
            return table.concat({ strings.GAMELEVELCELL, " | ", first })
        elseif first == second then -- Only two of them
            return table.concat({
                strings.GAMELEVELCELL,
                ' colspan = "2" | ',
                first,
            })
        else
            return table.concat({
                strings.GAMELEVELCELL,
                " | ",
                first,
                " |",
                strings.GAMELEVELCELL,
                " | ",
                second,
            })
        end
    end

    -- All three are the same
    if first == second and second == third then
        return table.concat({
            strings.GAMELEVELCELL,
            ' colspan = "3" | ',
            first,
        })

        -- First and second are the same but third is different
    elseif first == second then
        return table.concat({
            strings.GAMELEVELCELL,
            ' colspan = "2" | ',
            first,
            " |",
            strings.GAMELEVELCELL,
            " | ",
            third,
        })

        -- First and third are the same, but second is different
    elseif first == third then
        return table.concat({
            strings.GAMELEVELCELL,
            ' colspan = "2" | ',
            first,
            " |",
            strings.GAMELEVELCELL,
            " | ",
            second,
        })

        -- Second and third are the same, but first is different
    elseif second == third then
        return table.concat({
            strings.GAMELEVELCELL,
            " | ",
            first,
            " |",
            strings.GAMELEVELCELL,
            ' colspan = "2" | ',
            second,
        })

        -- All of them are different
    else
        return table.concat({
            strings.GAMELEVELCELL,
            " | ",
            first,
            " |",
            strings.GAMELEVELCELL,
            " | ",
            second,
            " |",
            strings.GAMELEVELCELL,
            " | ",
            third,
        })
    end
end

--[[

Restituisce l'inizio dell'entry delle mosse tutor, comprensivo
delle celle dei giochi: si aspetta una table di tables, che
contengono a loro volta, nell'ordine, sigla del gioco e
'Yes' o 'No' a seconda che la mossa possa essere insegnata
nel gioco o meno. La sigla visualizzata e i colori vengono presi
da Sigle/data Es:
	tutorgames{{'NB', 'No'}, {'N2B2', 'Yes'}}

Non si possono usare indici stringa per le sottotables per
questioni di ordinamento, mentre all'interno delle stesse
allungherebbe soltanto la chiamata

--]]
lib.tutorgames = function(games)
    local cells = table.map(games, function(game)
        --[[
				Uso del Modulo:Sigle/data per ricavare il
				colore del gioco dalla sigla
			--]]
        local gameData = sig[game[1]][1]
        local cell = { '| style="padding: 0.8ex 0.5ex;" |' }

        if game[2] == "Yes" then
            if gameData.display[2] then
                table.insert(
                    cell,
                    txt.interp(
                        [=[
<div class="text-center roundy-5 white-text" style="${bg}; padding: 0 0.5ex; margin-bottom: 0.2ex;">[[${gamesLink}|<span style="padding: 0.3em 0;">'''${game1sig}'''</span><span style="padding: 0.3em 0;">'''${game2sig}'''</span>]]</div>]=],
                        {
                            bg = css.horizGradLua({
                                gameData.display[1][2],
                                "dark",
                                gameData.display[2][2],
                                "dark",
                            }),
                            gamesLink = gameData.link,
                            game1sig = gameData.display[1][1],
                            game2sig = gameData.display[2][1],
                        }
                    )
                )
            else
                table.insert(
                    cell,
                    txt.interp(
                        [=[
<div class="text-center roundy-5 white-text" style="${bg}; padding: 0 0.5ex; margin-bottom: 0.2ex;">[[${gamesLink}|<span style="padding: 0.3em 0;">'''${gamesig}'''</span>]]</div>]=],
                        {
                            bg = css.horizGradLua({
                                gameData.display[1][2],
                                "dark",
                                gameData.display[1][2],
                                "normale",
                            }),
                            gamesLink = gameData.link,
                            gamesig = gameData.display[1][1],
                        }
                    )
                )
            end
        else
            if gameData.display[2] then
                table.insert(
                    cell,
                    txt.interp(
                        [=[
[[${gamesLink}|<span style="padding: 0.3em 0; color: #${game1color};">'''${game1sig}'''</span><span style="padding: 0.3em 0; color: #${game2color};">'''${game2sig}'''</span>]]]=],
                        {
                            gamesLink = gameData.link,
                            game1sig = gameData.display[1][1],
                            game2sig = gameData.display[2][1],
                            game1color = c[gameData.display[1][2]].dark,
                            game2color = c[gameData.display[2][2]].dark,
                        }
                    )
                )
            else
                table.insert(
                    cell,
                    txt.interp(
                        [=[
[[${gamesLink}|<span style="padding: 0.3em 0; color: #${gamecolor};">'''${gamesig}'''</span>]]]=],
                        {
                            gamesLink = gameData.link,
                            gamesig = gameData.display[1][1],
                            gamecolor = c[gameData.display[1][2]].dark,
                        }
                    )
                )
            end
        end

        return table.concat(cell)
    end)

    -- Inizio dell'entry, bisogna inserire una nuova table row
    table.insert(cells, 1, "|-")
    return table.concat(cells, "\n")
end

-- Genera le prime celle per gli entry dei preevo
lib.preevodata = function(pars, gen)
    local ani1, tt1 = "", ""
    if pars[4] then
        ani1 =
            ms.staticLua({ pars[4], gen = gen, link = pars[5] or "Bulbasaur" })
        tt1 = lib.preevott[pars[6]] or ""
    end
    return txt.interp(
        [=[|-
| style="padding: 0.1em 0.3em;" | ${ani}${tt}${ani1}${tt1}]=],
        {
            ani = ms.staticLua({
                ndex = pars[1] or "000",
                gen = gen,
                link = pars[2] or "Bulbasaur",
            }),
            tt = lib.preevott[pars[3]] or "",
            ani1 = ani1,
            tt1 = tt1,
        }
    )
end

--[[

Build the first cell of a preevo entry, autocomputing the name of the
poke from the ndex.
Right now is an interface to the previous function, ideally someone will
TODO refactor this

Arguments:
    - pars: a table that, from a certain index onward, contains an alternating
             sequence of ndex and notes, representing each an ndex of a preevo
             and the relative notes (optional)
    - idx: the index at which the required sequence of values starts in pars
    - gen: the generation of the entry

--]]
lib.autopreevo = function(pars, idx, gen)
    local fakep = {}
    local i = 1
    local j = idx
    -- I don't like an indexed iteration, but it seems for the best here
    -- Can't use #pars because there are nils if some parameter is not given
    while pars[j] do
        fakep[i] = pars[j]
        fakep[i + 2] = pars[j + 1]

        fakep[i + 1] = (pokes[fakep[i]] or pokes[tonumber(fakep[i])]).name
        i = i + 3
        j = j + 2
    end
    return lib.preevodata(fakep, tostring(gen))
end

-- La cella dell'entry null, utilizzata per i Pokémon che non imparano
-- mosse in un certo modo
lib.entrynull = function(entry, cs)
    return txt.interp(strings.ENTRYNULL, {
        ending = entryNullEnd[entry],
        cs = tostring(cs),
    })
end

--[[

Computes the STAB value given the ndex and move name. If ndex or movename
doesn't matches an entry of the respective module, an empty string is returned.
Arguments:
	- ndex: either the ndex or the Pokémon's name, all but the abbr lowercase
	- movename: name of the move to compute the STAB against
	- form (optional): extended form name
	- gen (optional): the generation in which compute the STAB. Defaults to the
					  latest

--]]
lib.computeSTAB = function(ndex, movename, form, gen)
    local name, abbr = forms.getnameabbr(tostring(ndex), form)
    local iname = forms.toEmptyAbbr(abbr) == "" and name
        or (type(name) == "number" and txt.ff(name) or name)
            .. forms.toEmptyAbbr(abbr)
    -- The or pokes[name] is needed for useless forms, not indexed in Poké-data
    local pokedata = multigen.getGen(pokes[iname] or pokes[name], gen)
    local movedata = moves[movename:lower()]
    local movetype = multigen.getGenValue(movedata.type, gen)
    if
        not pokedata
        or not movedata
        or (
            multigen.getGenValue(movedata.power, gen) == "&mdash;"
            and not multigen.getGenValue(movedata.stab, gen)
        )
    then
        return ""
    elseif
        movetype == normalizeColeot(pokedata.type1)
        or movetype == normalizeColeot(pokedata.type2)
    then
        return "'''"
    elseif
        table.search(
            table.map(evolib.evoTypesList(iname, gen), normalizeColeot),
            movetype
        )
        or table.search(
            table.map(evolib.formTypesList(iname, gen), normalizeColeot),
            movetype
        )
    then
        return "''"
    else
        return ""
    end
end

--[[

Given the table for a learnlist breed of a Pokémon in a gen and a game, return
the list of parents for that game.
Arguments:
	- movedata: table associated with the move in PokéMoves[poke].breed
	- game: the abbr of the game

--]]
lib.moveParentsGame = function(movedata, game)
    for _, v in ipairs(movedata) do
        if v.games and table.search(v.games, game) then
            return v
        end
    end
    return movedata[1]
end

--[[

Make the text to signal that a move is
learned upon evolution (ie: "Evoluzione"
on desktop, "Evo" on mobile)

--]]
---@deprecated use makeLevelText
lib.makeEvoText = function(t)
    return (t == "Evo" or t == "Evoluzione")
            and 'Evo<span class="hidden-xs">luzione</span>'
        or t
end

--[[

Make any transformation needed for a text used as the level uppon which a
Pokémon learns a move. Namely, it makes the following things:
- "Evo" and "Evoluzione" are transformerd so that they appear as "Evoluzione"
  on desktop and "Evo" on mobile
- "Ricorda" and "R" are transformed so that they appear as "Ricorda"

--]]
lib.makeLevelText = function(t)
    if t == "Evo" or t == "Evoluzione" then
        return 'Evo<span class="hidden-xs">luzione</span>'
    elseif t == "Ricorda" or t == "R" then
        return "Ricorda"
    else
        return t
    end
end

--[[

Autocomputes the kind and number of a TM in a given game. Returns a pair
<kind, number>, where kind is the kind of machine ("MT", "MN", "DT")
Arguments:
	- move: name of the move (lowercase)
	- gen: generation in which look for the move
	- game (optional): game in which look for the move. If not given and the
		move appears as a TM in more than one game in that generation, this
		function may return the tm kind and number in ANY of the games of that
		generation

--]]
lib.getTMNum = function(move, gen, game)
    local tmgendata = tmdata[gen]
    local nfig = gen <= 8 and 2 or 3

    if game then
        tmgendata = table.map(tmgendata, function(val)
            return table.map(val, function(singlemove)
                if type(singlemove) == "table" then
                    local found = table.find(singlemove, function(g)
                        return g[1] == game
                    end)
                    return found and singlemove[found][2] or nil
                else
                    return singlemove
                end
            end)
        end)
    end

    local tmkind, tmnum, _ = table.deepSearch(tmgendata, move)
    if tmnum then
        return tmkind, txt.nFigures(tmnum, nfig)
    end
end

return lib