Aller au contenu

Module:Apprentissage/bac à sable

De Poképédia

La documentation pour ce module peut être créée à Module:Apprentissage/bac à sable/doc

local p = {}

local donneesCapacite = require( "Module:Ressources/donneesCapacite" )

local GENERATION_ACTUELLE = 9

local generation_to_jeux = {
	{ "RB", "J" },
	{ "OA", "C" },
	{ "RS", "E", "RFVF" },
	{ "DP", "Pt", "HGSS" },
	{ "NB", "N2B2" },
	{ "XY", "ROSA" },
	{ "SL", "USUL", "LGPE" },
	{ { "EB", "DEPS" }, "LPA" },
	{ "EV", "LPZA" },
	{ "VV" }
}

local titres_concours = {
	[3] = { "[[Concours Pokémon|Type Concours]]", "[[Charme (concours)|Charme]]",
	        "[[Blocage (concours)|Blocage]]" },
	[4] = { "[[Concours Pokémon|Type Concours]]", "[[Charme (concours)|Charme]]" },
	[6] = { "[[Concours Pokémon|Type Concours]]", "[[Charme (concours)|Charme]]",
	        "[[Blocage (concours)|Blocage]]" }
}

local chiffres_romains = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X" }

local titres_communs = {
	niveau = { "[[Capacité|Nom]]", "[[Type]]", "[[Catégorie de capacité|Catégorie]]",
		"[[Puissance (statistique)|Puissance]]", "[[Précision]]", "[[PP]]" },
	capsule = { "Capacité", "Type", "Catégorie", "Puissance", "Précision", "PP" },
	reproduction = { "[[Capacité]]", "[[Type]]", "[[Catégorie de capacité|Cat.]]",
		"[[Puissance (statistique)|Puis.]]", "[[Précision|Préc.]]", "[[PP]]" },
	usuel = { "[[Capacité]]", "[[Type]]", "[[Catégorie de capacité|Catégorie]]",
		"[[Puissance (statistique)|Puissance]]", "[[Précision|Précision]]", "[[PP]]" }
}

local capacites_secretes = {
	["Coupe"]      = { true, true, true, true, true, true },
	["Vol"]        = { true, true, true, true, true, true },
	["Surf"]       = { true, true, true, true, true, true },
	["Force"]      = { true, true, true, true, true, true },
	["Flash"]      = { true, true, true, false, false, false },
	["Cascade"]    = { false, true, true, true, true, true },
	["Escalade"]   = { false, false, false, true, false, false },
	["Siphon"]     = { false, true, false, "selon le jeu", false, false,
		dpp = false, hgss = true },
	["Éclate-Roc"] = { false, false, true, true, false, "selon le jeu",
		xy = false, rosa = true },
	["Plongée"]    = { false, false, "selon le jeu", false, true, "selon le jeu",
		rse = true, rfvf = false, xy = false, rosa = true },
	["Anti-Brume"] = { false, false, false, "selon le jeu", false, false,
		dpp = true, hgss = false }
}

local jeu_vers_cle_des_capacites_secretes = {
	d = "dpp", p = "dpp", dp = "dpp", pt = "dpp",
	hg = "hgss", ss = "hgss",
	ru = "rse", sa = "rse", rs = "rse", e = "rse",
	rf = "rfvf", vf = "rfvf",
	ruo = "rosa", saa = "rosa"
}

local parametres_nommes = {
	"génération", "type", "jeux", "notes", "légende", "identifiant", "masquer",
	"couleur-type", "colonnes-jeux", "colonnes-générations", "depuis",
	"pokémon", "colonne", "méthodes", "prix-unité", "queulorior"
}

local style_bouton = "font-size:100%; font-family:inherit; padding:3px 6px; line-height:1.4"

local style_compteur = "font-size:90%; color:var(--color-subtle, #54595d)"

local style_visible_des_lecteurs_seuls = "position:absolute; width:1px; height:1px;"
	.. " overflow:hidden; clip-path:inset(50%)"

local octets_non_ascii = "[" .. string.char( 128 ) .. "-" .. string.char( 255 ) .. "]"

local icone_maitrise = "[[Fichier:Icône Maîtrise LPA.png|x20px|Maîtrise|link=Maîtrise]]"
local icone_capacite_plus =
	"[[Fichier:Icône Capacité Plus LPZA.png|x20px|Capacité Plus|link=Capacité Plus]]"

local function non_vide( valeur )
	if valeur == nil then return false end
	valeur = mw.text.trim( valeur )
	return valeur ~= "" and valeur ~= "-" and valeur ~= "—"
end

local function decouper( texte, separateur )
	local morceaux = mw.text.split( texte, separateur )
	for rang, morceau in ipairs( morceaux ) do
		morceaux[rang] = mw.text.trim( morceau )
	end
	return morceaux
end

local function nombre_cache_dans( texte )
	local chiffres = string.gsub( texte, "%D", "" )
	return tonumber( chiffres )
end

local function condense( texte )
	local somme = 5381
	for rang = 1, #texte do
		somme = ( somme * 33 + string.byte( texte, rang ) ) % 2147483647
	end
	return string.format( "%x", somme )
end

local function lire_arg( frame, nom )
	local valeur = frame.args[nom]
	if valeur == nil then return nil end
	valeur = mw.text.trim( valeur )
	if valeur == "" then return nil end
	return valeur
end

local function oui( frame, nom )
	local valeur = lire_arg( frame, nom )
	return valeur == "oui" or valeur == "1"
end

local function lire_generation( frame )
	local valeur = lire_arg( frame, "génération" )
	if valeur == nil then return GENERATION_ACTUELLE end
	local generation = tonumber( valeur )
	if generation == nil or generation ~= math.floor( generation ) then return nil end
	return generation
end

local function lire_type( frame )
	local valeur = lire_arg( frame, "type" )
	if valeur == nil then return "inconnu" end

	local minuscules = string.gsub( valeur, "É", "é" )
	minuscules = string.lower( minuscules )

	local caracteres_interdits = string.gsub( minuscules, "[%w%-]", "" )
	caracteres_interdits = string.gsub( caracteres_interdits, octets_non_ascii, "" )
	if caracteres_interdits ~= "" then return "inconnu" end
	return minuscules
end

local function lire_jeux( frame, generation, jeu_a_retirer )
	local valeur = lire_arg( frame, "jeux" )
	if valeur ~= nil then return decouper( valeur, "/" ) end

	local jeux = {}
	for _, jeu in ipairs( generation_to_jeux[generation] ) do
		if jeu ~= jeu_a_retirer then table.insert( jeux, jeu ) end
	end
	return jeux
end

local function lire_notes( frame )
	local valeur = lire_arg( frame, "notes" )
	if valeur == nil then return {} end
	local notes = {}
	for _, ligne in ipairs( decouper( valeur, "\n" ) ) do
		if ligne ~= "" then table.insert( notes, ligne ) end
	end
	return notes
end

local function generation_invalide( valeur )
	local categorie = ""
	if mw.title.getCurrentTitle().namespace == 0 then
		categorie = "[[Catégorie:Page avec un paramètre d'apprentissage incorrect]]"
	end
	return '<span class="error">Module:Apprentissage : génération «&#160;'
		.. mw.text.nowiki( tostring( valeur ) ) .. "&#160;» inconnue.</span>" .. categorie
end

local tables_de_donnees = {}

local function donnees_brutes( generation, capacite )
	if tables_de_donnees[generation] == nil then
		local ok, data = pcall( mw.loadData,
			"Module:Data/Données Capacités Génération " .. generation )
		tables_de_donnees[generation] = ( ok and type( data ) == "table" ) and data or false
	end
	if tables_de_donnees[generation] == false then return nil end
	return tables_de_donnees[generation][capacite]
end

local donnees_champions = false

local function capacite_champions( nom )
	if donnees_champions == false then
		local ok, data = pcall( mw.loadData, "Module:Data/Données Capacités Champions" )
		donnees_champions = ( ok and type( data ) == "table" ) and data or nil
	end
	if donnees_champions == nil then return nil end
	return donnees_champions[nom]
end

local function tri_nombre( valeur )
	if valeur == nil then return -2 end
	local nombre = tonumber( valeur )
	if nombre == nil then return 1000 end
	if nombre == 0 then return -1 end
	return nombre
end

local a_echapper_dans_un_attribut = { ["&"] = "&amp;", ['"'] = "&quot;", ["<"] = "&lt;" }

local function attribut_tri( valeur )
	return ' data-sort-value="'
		.. string.gsub( tostring( valeur or "" ), '[&"<]', a_echapper_dans_un_attribut ) .. '"'
end

local function nettoyer_nom( champ )
	local sans_appel_de_note = string.gsub( champ, "note%(%d+%)", "" )
	return mw.text.trim( sans_appel_de_note )
end

local function appliquer_notes( texte, ancre )
	if texte == nil then return texte end
	local avec_renvois = string.gsub( texte, "note%((%d+)%)", function( numero )
		return "<sup>[[#" .. ancre .. "-" .. numero .. "|" .. numero .. "]]</sup>"
	end )
	return avec_renvois
end

local function nom_et_renvoi( champ, ancre )
	local renvoi = string.match( champ, "note%(%d+%)" ) or ""
	return nettoyer_nom( champ ), appliquer_notes( renvoi, ancre )
end

local function lignes_de_notes( notes, ancre, colspan )
	if #notes == 0 then return "" end
	local morceaux = { '<tr><td class="colonne-toutes" colspan="' .. colspan
		.. '" style="text-align:left; font-size:90%">' }
	for numero, texte in ipairs( notes ) do
		table.insert( morceaux, '<span id="' .. ancre .. "-" .. numero .. '">'
			.. numero .. ".</span> " .. texte .. "<br />" )
	end
	table.insert( morceaux, "</td></tr>" )
	return table.concat( morceaux, "" )
end

local function nb_communes( generation )
	if titres_concours[generation] == nil then return 6 end
	return 6 + #titres_concours[generation]
end

local function ouvrir_table( contexte, triable )
	local classes = { "tableaustandard", contexte.type, "centre" }
	if triable then table.insert( classes, "sortable" ) end
	table.insert( classes, "tableau-overflow" )

	local ouverture = '<div class="center"><table class="' .. table.concat( classes, " " )
		.. '" style="max-width: 100%;">'
	if contexte.legende ~= nil then
		ouverture = ouverture .. "<caption>" .. contexte.legende .. "</caption>"
	end
	return ouverture .. "<thead>"
end

local function fermer_table( contexte, colspan, ligne_en_plus )
	local pied = ( ligne_en_plus or "" )
		.. lignes_de_notes( contexte.notes, contexte.ancre, colspan )
	if pied ~= "" then pied = "<tfoot>" .. pied .. "</tfoot>" end
	return "</tbody>" .. pied .. "</table></div>"
end

local function entetes_communes( contexte, depart, titres )
	local morceaux = {}
	for rang, titre in ipairs( titres ) do
		table.insert( morceaux, '<th class="colonne-' .. ( depart + rang - 1 )
			.. '" scope="col">' .. titre .. "</th>" )
	end
	for rang, titre in ipairs( titres_concours[contexte.generation] or {} ) do
		table.insert( morceaux, '<th class="colonne-' .. ( depart + 5 + rang )
			.. '" scope="col">' .. titre .. "</th>" )
	end
	return table.concat( morceaux, "" )
end

local function cellules_communes( contexte, rendu, brutes, depart, suffixe_nom )
	local morceaux = { '<td class="colonne-' .. depart .. '">' .. rendu[1]
		.. ( suffixe_nom or "" ) .. "</td>" }

	local classe_type = "colonne-" .. ( depart + 1 )
	if contexte.couleur_type and rendu["type"] ~= nil and rendu["type"] ~= "inconnu" then
		classe_type = classe_type .. " " .. rendu["type"]
	end
	table.insert( morceaux, '<td class="' .. classe_type .. '"'
		.. attribut_tri( brutes and brutes[1] ) .. ">" .. rendu[2] .. "</td>" )

	table.insert( morceaux, '<td class="colonne-' .. ( depart + 2 ) .. '"'
		.. attribut_tri( brutes and brutes[2] ) .. ">" .. rendu[3] .. "</td>" )
	for rang = 3, 5 do
		table.insert( morceaux, '<td class="colonne-' .. ( depart + rang ) .. '"'
			.. attribut_tri( tri_nombre( brutes and brutes[rang] ) ) .. ">"
			.. rendu[rang + 1] .. "</td>" )
	end

	if titres_concours[contexte.generation] ~= nil then
		table.insert( morceaux, '<td class="colonne-' .. ( depart + 6 ) .. '"'
			.. attribut_tri( brutes and brutes[6] ) .. ">" .. rendu[7] .. "</td>" )
		for rang = 7, 5 + #titres_concours[contexte.generation] do
			table.insert( morceaux, '<td class="colonne-' .. ( depart + rang ) .. '"'
				.. attribut_tri( tri_nombre( brutes and brutes[rang] ) ) .. ">"
				.. rendu[rang + 1] .. "</td>" )
		end
	end
	return table.concat( morceaux, "" )
end

local function ligne_bouton( contexte, colspan, total )
	local replie = oui( contexte.frame, "masquer" )
	local action = replie and "afficher" or "masquer"
	local libelle_courant = replie and "[+] Afficher" or "[-] Masquer"
	local libelle_du_prochain_clic = replie and "[-] Masquer" or "[+] Afficher"

	local compteur_hors_du_bouton = ""
	if total > 0 then
		compteur_hors_du_bouton = ' <span class="apprentissage-compteur" style="' .. style_compteur
			.. '">' .. total .. ( total == 1 and " capacité" or " capacités" ) .. "</span>"
	end

	return '<tr><td class="colonne-toutes" colspan="' .. colspan
		.. '"><button type="button" name="' .. contexte.groupe
		.. '" value="' .. action .. '" style="' .. style_bouton
		.. '" title="' .. libelle_du_prochain_clic .. '">' .. libelle_courant
		.. "</button>" .. compteur_hors_du_bouton
		.. '</td></tr></thead><tbody class="' .. contexte.groupe .. '">'
end

local function ligne_vide( colspan, phrase )
	return '</thead><tbody><tr><td class="colonne-toutes" colspan="' .. colspan
		.. '">' .. phrase .. "</td></tr>"
end

local function cellule_symbole( symbole, valeur_tri, sens )
	local contenu = symbole
	if sens ~= nil then
		contenu = '<span style="' .. style_visible_des_lecteurs_seuls .. '">' .. sens .. "</span>"
			.. '<span aria-hidden="true">' .. symbole .. "</span>"
	end
	return '<td class="colonne-niveau"' .. attribut_tri( valeur_tri ) .. ">"
		.. contenu .. "</td>"
end

local function nb_colonnes_jeux( contexte )
	if contexte.colonnes_jeux == nil then return 0 end
	return #contexte.colonnes_jeux
end

local function abreviation( frame, jeu )
	if type( jeu ) ~= "table" then
		return frame:expandTemplate{ title = "Abréviation", args = { jeu, "" } }
	end
	local libelles = {}
	for _, code in ipairs( jeu ) do
		table.insert( libelles,
			frame:expandTemplate{ title = "Abréviation", args = { code, "" } } )
	end
	return table.concat( libelles, ", " )
end

local function entetes_jeux( contexte )
	if contexte.colonnes_jeux == nil then return "" end
	local morceaux = {}
	for _, jeu in ipairs( contexte.colonnes_jeux ) do
		table.insert( morceaux, '<th class="colonne-niveau" scope="col">'
			.. abreviation( contexte.frame, jeu ) .. "</th>" )
	end
	return table.concat( morceaux, "" )
end

local function cellules_jeux( contexte, champs, premier_champ )
	if contexte.colonnes_jeux == nil then return "" end
	local morceaux = {}
	for rang = 1, #contexte.colonnes_jeux do
		local valeur = champs[premier_champ + rang - 1]
		if non_vide( valeur ) then
			valeur = mw.text.trim( valeur )
			if valeur == "oui" or string.lower( valeur ) == "x" then valeur = "✔" end
			table.insert( morceaux, '<td class="colonne-niveau" data-sort-value="1">'
				.. appliquer_notes( valeur, contexte.ancre ) .. "</td>" )
		else
			table.insert( morceaux, cellule_symbole( "—", 0 ) )
		end
	end
	return table.concat( morceaux, "" )
end

local function ensemble_generations( champ )
	local ensemble = {}
	if champ == nil then return ensemble end

	for _, morceau in ipairs( decouper( champ, "," ) ) do
		local special = false
		if string.sub( morceau, -1 ) == "*" then
			special = true
			morceau = mw.text.trim( string.sub( morceau, 1, -2 ) )
		end

		local numero = tonumber( morceau )
		if numero == nil then
			for rang, romain in ipairs( chiffres_romains ) do
				if string.upper( morceau ) == romain then numero = rang end
			end
		end
		if numero ~= nil and chiffres_romains[numero] ~= nil then
			ensemble[numero] = special and "special" or true
		end
	end
	return ensemble
end

local function colonnes_de_generations( contexte )
	if not oui( contexte.frame, "colonnes-générations" ) then return nil end

	local demande = tonumber( lire_arg( contexte.frame, "depuis" ) or "" )
	local forcees = demande ~= nil
	local depuis = math.floor( demande or 1 )
	if depuis < 1 then depuis = 1 end

	local remplies = {}
	for _, ligne in ipairs( contexte.lignes ) do
		for numero in pairs( ensemble_generations( ligne[2] ) ) do
			remplies[numero] = true
		end
	end

	local generations = {}
	for numero = depuis, contexte.generation - 1 do
		if forcees or remplies[numero] then table.insert( generations, numero ) end
	end
	if #generations == 0 then return nil end
	return generations
end

local function entete_de_generation( contexte, numero )
	local libelle = '<abbr title="Génération ' .. chiffres_romains[numero] .. '">'
		.. chiffres_romains[numero] .. "</abbr>"
	local racine = lire_arg( contexte.frame, "pokémon" )
		or mw.title.getCurrentTitle().rootText
	local cible = racine .. "/Génération " .. numero

	local ok, page = pcall( mw.title.new, cible )
	if ok and page ~= nil then
		local lu, existe = pcall( function() return page.exists end )
		if lu and existe then
			libelle = "[[" .. cible .. "|" .. libelle .. "]]"
		end
	end
	return '<th class="colonne-niveau" scope="col">' .. libelle .. "</th>"
end

local function entetes_generations( contexte )
	local morceaux = {}
	for _, numero in ipairs( contexte.generations ) do
		table.insert( morceaux, entete_de_generation( contexte, numero ) )
	end
	return table.concat( morceaux, "" )
end

local function cellules_generations( contexte, champ )
	local ensemble = ensemble_generations( champ )
	local morceaux = {}
	for _, numero in ipairs( contexte.generations ) do
		if ensemble[numero] == "special" then
			table.insert( morceaux, cellule_symbole( "✦", 2,
				"Uniquement par événement ou capacité spéciale" ) )
		elseif ensemble[numero] then
			table.insert( morceaux, cellule_symbole( "✔", 1 ) )
		else
			table.insert( morceaux, cellule_symbole( "—", 0 ) )
		end
	end
	return table.concat( morceaux, "" )
end

local function nom_du_groupe( frame, classe, generation, bloc_de_lignes )
	local nom = classe .. generation
	local identifiant = lire_arg( frame, "identifiant" )
	if identifiant ~= nil then
		identifiant = string.gsub( identifiant, "[^%w]", "" )
		if identifiant ~= "" then return nom .. "-" .. identifiant end
	end

	local morceaux = { bloc_de_lignes }
	for _, parametre in ipairs( parametres_nommes ) do
		table.insert( morceaux, parametre .. "=" .. ( frame.args[parametre] or "" ) )
	end
	return nom .. "-" .. condense( table.concat( morceaux, "|" ) )
end

local function preparer( frame, reglages )
	local bloc_de_lignes = frame.args[1]
	if bloc_de_lignes == nil then return nil, "Aucune information donnée" end

	local generation = lire_generation( frame )
	if generation == nil or generation_to_jeux[generation] == nil then
		return nil, generation_invalide( frame.args["génération"] )
	end
	bloc_de_lignes = mw.text.trim( bloc_de_lignes )

	local contexte = {
		frame = frame,
		generation = generation,
		type = lire_type( frame ),
		jeux = lire_jeux( frame, generation, reglages.jeu_a_retirer ),
		notes = lire_notes( frame ),
		legende = lire_arg( frame, "légende" ),
		couleur_type = oui( frame, "couleur-type" ),
		communes = nb_communes( generation ),
		groupe = nom_du_groupe( frame, reglages.classe, generation, bloc_de_lignes ),
		lignes = {},
		aucune_declaree = bloc_de_lignes == "Aucune"
	}
	contexte.ancre = "note-" .. contexte.groupe

	if oui( frame, "colonnes-jeux" ) then
		local demandes = lire_arg( frame, "jeux" )
		if demandes ~= nil then
			contexte.colonnes_jeux = decouper( demandes, "/" )
		else
			contexte.colonnes_jeux = {}
			for _, jeu in ipairs( generation_to_jeux[generation] ) do
				table.insert( contexte.colonnes_jeux, jeu )
			end
		end
	end

	if not contexte.aucune_declaree then
		for _, ligne in ipairs( mw.text.split( bloc_de_lignes, "\n" ) ) do
			if mw.text.trim( ligne ) ~= "" then
				table.insert( contexte.lignes, mw.text.split( ligne, "/" ) )
			end
		end
	end
	return contexte
end

local function lignes_utiles( contexte, nom_de_la_ligne )
	local utiles = {}
	for _, champs in ipairs( contexte.lignes ) do
		local nom = nom_de_la_ligne( champs )
		if nom ~= nil and nom ~= "" then
			table.insert( utiles, { champs = champs, nom = nom } )
		end
	end
	return utiles
end

local function nom_de_capacite( champ )
	if not non_vide( champ ) then return nil end
	local nom = nettoyer_nom( champ )
	if nom == "" then return nil end
	return nom
end

local function nom_en_premier_champ( champs )
	return nom_de_capacite( champs[1] )
end

local function cellule_de_niveau( contexte, champ )
	if champ == nil then
		return '<td class="colonne-niveau" style="white-space:nowrap"'
			.. ' data-sort-value="101">—</td>'
	end

	local morceaux = {}
	for _, brut in ipairs( decouper( champ, "," ) ) do
		if brut ~= "" then table.insert( morceaux, brut ) end
	end
	if #morceaux == 0 then morceaux = { "—" } end

	local contenu, valeur_tri = {}, nil
	local exposant = true

	for rang, morceau in ipairs( morceaux ) do
		local icone = ""
		local tete = mw.ustring.sub( morceau, 1, 1 )
		if ( tete == "M" or tete == "P" )
				and tonumber( mw.ustring.sub( morceau, 2 ) ) ~= nil then
			icone = ( tete == "M" ) and icone_maitrise or icone_capacite_plus
			morceau = mw.ustring.sub( morceau, 2 )
		end

		local niveau = tonumber( morceau )
		if morceau == "—" or morceau == "-" or morceau == "" then
			morceau, exposant = "—", false
			valeur_tri = valeur_tri or 101
		elseif string.find( morceau, "Départ" ) or string.find( morceau, "Évolution" ) then
			exposant = false
			valeur_tri = valeur_tri or 0
		elseif niveau ~= nil then
			morceau, exposant = "N." .. morceau, false
			valeur_tri = valeur_tri or niveau
		else
			exposant = not exposant
			valeur_tri = valeur_tri or 0
		end

		if rang ~= 1 and not exposant then table.insert( contenu, "<br/>" ) end
		table.insert( contenu, icone )
		if exposant then
			table.insert( contenu,
				contexte.frame:expandTemplate{ title = "Sup", args = { morceau } } )
		else
			table.insert( contenu, morceau )
		end
	end

	return '<td class="colonne-niveau" style="white-space:nowrap"'
		.. attribut_tri( valeur_tri or 101 ) .. ">"
		.. appliquer_notes( table.concat( contenu, "" ), contexte.ancre ) .. "</td>"
end

local function colonnes_de_jeux_remplies( capacites, tous_les_jeux )
	local remplie, nombre = {}, 0
	for rang = 1, #tous_les_jeux do
		for _, capacite in ipairs( capacites ) do
			if non_vide( capacite.champs[rang + 1] ) then remplie[rang] = true end
		end
		if remplie[rang] then nombre = nombre + 1 end
	end
	return remplie, nombre
end

function p.niveau( frame )
	local contexte, message = preparer( frame, { classe = "ListeCapacites" } )
	if contexte == nil then return message end

	local capacites = lignes_utiles( contexte, nom_en_premier_champ )
	local tous_les_jeux = generation_to_jeux[contexte.generation]
	local remplie, nb_colonnes = colonnes_de_jeux_remplies( capacites, tous_les_jeux )
	local colspan = contexte.communes + nb_colonnes
	local vide = contexte.aucune_declaree or #capacites == 0

	local morceaux = { ouvrir_table( contexte, not vide ) }
	table.insert( morceaux, '<tr><th class="cellule-entête" colspan="' .. contexte.communes
		.. '" scope="colgroup">[[Capacité]]</th>' )
	if nb_colonnes > 0 then
		table.insert( morceaux, '<th class="cellule-entête" colspan="' .. nb_colonnes
			.. '" scope="colgroup">[[Niveau]]</th>' )
	end
	table.insert( morceaux, "</tr><tr>" .. entetes_communes( contexte, 1, titres_communs.niveau ) )
	for rang, jeu in ipairs( tous_les_jeux ) do
		if remplie[rang] then
			table.insert( morceaux, '<th class="colonne-niveau" scope="col">'
				.. abreviation( contexte.frame, jeu ) .. "</th>" )
		end
	end
	table.insert( morceaux, "</tr>" )

	if vide then
		table.insert( morceaux, ligne_vide( colspan, "Ce Pokémon n'apprend aucune capacité "
			.. "par montée en niveau lors de cette génération." ) )
	else
		table.insert( morceaux, ligne_bouton( contexte, colspan, #capacites ) )

		for _, capacite in ipairs( capacites ) do
			local champs = capacite.champs
			local _, renvoi = nom_et_renvoi( champs[1], contexte.ancre )

			local jeux_capacite = {}
			for rang, jeu in ipairs( tous_les_jeux ) do
				if non_vide( champs[rang + 1] ) then table.insert( jeux_capacite, jeu ) end
			end

			local rendu = donneesCapacite( contexte.generation, capacite.nom, jeux_capacite )
			local brutes = donnees_brutes( contexte.generation, capacite.nom )
			table.insert( morceaux,
				"<tr>" .. cellules_communes( contexte, rendu, brutes, 1, renvoi ) )
			for rang = 1, #tous_les_jeux do
				if remplie[rang] then
					table.insert( morceaux, cellule_de_niveau( contexte, champs[rang + 1] ) )
				end
			end
			table.insert( morceaux, "</tr>" )
		end
	end

	table.insert( morceaux, fermer_table( contexte, colspan ) )
	return table.concat( morceaux, "" )
end

local function sigle_capsule( disque, capacite, generation, jeu )
	if disque then return "DT" end
	local regle = capacites_secretes[capacite]
	if regle == nil then return "CT" end

	if regle[generation] == true then return "CS" end
	if regle[generation] == "selon le jeu" and jeu ~= nil then
		local code = string.lower( mw.text.trim( jeu ) )
		code = jeu_vers_cle_des_capacites_secretes[code] or code
		if regle[code] == true then return "CS" end
	end
	return "CT"
end

local function capsule_et_disque( disque, frame )
	local mot = disque and "disque" or "capsule"
	local contexte, message = preparer( frame, {
		classe = disque and "ListeDisques" or "ListeCapsules",
		jeu_a_retirer = "LPA"   -- Légendes Arceus n'a ni CT ni DT
	} )
	if contexte == nil then return message end

	local capsules = lignes_utiles( contexte, function( champs )
		if not non_vide( champs[1] ) then return nil end
		return nom_de_capacite( champs[2] or "" )
	end )

	local colspan = 1 + contexte.communes + nb_colonnes_jeux( contexte )
	local vide = contexte.aucune_declaree or #capsules == 0

	local morceaux = { ouvrir_table( contexte, not vide ) }
	table.insert( morceaux, '<tr><th class="colonne-1" scope="col">Numéro</th>'
		.. entetes_communes( contexte, 2, titres_communs.capsule )
		.. entetes_jeux( contexte ) .. "</tr>" )

	if vide then
		table.insert( morceaux, ligne_vide( colspan, "Ce Pokémon n'apprend aucune capacité par "
			.. mot .. " lors de cette génération." ) )
	else
		table.insert( morceaux, ligne_bouton( contexte, colspan, #capsules ) )

		for _, capsule in ipairs( capsules ) do
			local champs = capsule.champs
			local numero = mw.text.trim( champs[1] )
			local sigle = sigle_capsule( disque, capsule.nom, contexte.generation, champs[3] )
			local _, renvoi = nom_et_renvoi( champs[2], contexte.ancre )

			if champs[3] ~= nil and contexte.colonnes_jeux == nil then
				renvoi = renvoi .. "<sup>"
					.. frame:expandTemplate{ title = "Abréviation", args = { champs[3] } }
					.. "</sup>"
			end

			local rendu = donneesCapacite( contexte.generation, capsule.nom, contexte.jeux )
			local brutes = donnees_brutes( contexte.generation, capsule.nom )

			table.insert( morceaux, '<tr><td class="colonne-1"'
				.. attribut_tri( nombre_cache_dans( numero ) or 0 ) .. ">[["
				.. sigle .. numero .. "]]</td>" )
			table.insert( morceaux, cellules_communes( contexte, rendu, brutes, 2, renvoi ) )
			table.insert( morceaux, cellules_jeux( contexte, champs, 3 ) )
			table.insert( morceaux, "</tr>" )
		end
	end

	table.insert( morceaux, fermer_table( contexte, colspan ) )
	return table.concat( morceaux, "" )
end

function p.capsule( frame ) return capsule_et_disque( false, frame ) end
function p.disque( frame ) return capsule_et_disque( true, frame ) end

function p.reproduction( frame )
	local contexte, message = preparer( frame, {
		classe = "ListeReproduction",
		jeu_a_retirer = "LPA"   -- pas de reproduction dans Légendes Arceus
	} )
	if contexte == nil then return message end

	local infosPokemon = require( "Module:Ressources/infosPokemon" )
	local capacites = lignes_utiles( contexte, nom_en_premier_champ )

	local methodes = { "[[niveau]]", "[[reproduction]]" }
	local demandees = lire_arg( frame, "méthodes" )
	if demandees ~= nil then methodes = decouper( demandees, "/" ) end

	local colspan = contexte.communes + #methodes + nb_colonnes_jeux( contexte )
	local vide = contexte.aucune_declaree or #capacites == 0

	local morceaux = { ouvrir_table( contexte, not vide ) }
	table.insert( morceaux, "<tr>" .. entetes_communes( contexte, 1, titres_communs.reproduction ) )
	for _, methode in ipairs( methodes ) do
		table.insert( morceaux, '<th class="colonne-toutes" scope="col">'
			.. "Parents l'apprenant par " .. methode .. "</th>" )
	end
	table.insert( morceaux, entetes_jeux( contexte ) .. "</tr>" )

	if vide then
		table.insert( morceaux, ligne_vide( colspan, "Ce Pokémon n'apprend aucune capacité "
			.. "par reproduction lors de cette génération." ) )
	else
		table.insert( morceaux, ligne_bouton( contexte, colspan, #capacites ) )

		for _, capacite in ipairs( capacites ) do
			local champs = capacite.champs

			local brut, jeu = champs[1], ""
			for _, marqueur in ipairs( { "jeu", "sup" } ) do
				local motif = marqueur .. "%(([^)]*)%)"
				local trouve = string.match( brut, motif )
				if trouve ~= nil then
					jeu = frame:expandTemplate{ title = "Sup", args = { trouve } }
					brut = string.gsub( brut, motif, "" )
				end
			end
			local nom, renvoi = nom_et_renvoi( brut, contexte.ancre )

			local rendu = donneesCapacite( contexte.generation, nom, contexte.jeux )
			local brutes = donnees_brutes( contexte.generation, nom )
			table.insert( morceaux, "<tr>"
				.. cellules_communes( contexte, rendu, brutes, 1, jeu .. renvoi ) )

			for rang = 1, #methodes do
				local parents = champs[rang + 1]
				local contenu = "—"
				if parents ~= nil and mw.text.trim( parents ) ~= "" then
					local miniatures = {}
					for _, parent in ipairs( decouper( parents, "," ) ) do
						table.insert( miniatures, infosPokemon( parent, "miniature" ) )
					end
					contenu = table.concat( miniatures, "" )
				end
				table.insert( morceaux, '<td class="colonne-toutes">' .. contenu .. "</td>" )
			end
			table.insert( morceaux,
				cellules_jeux( contexte, champs, #methodes + 2 ) .. "</tr>" )
		end
	end

	local phrase_queulorior = ""
	if oui( frame, "queulorior" ) then
		phrase_queulorior = '<tr><td class="colonne-toutes" colspan="' .. colspan
			.. '">Grâce à sa capacité [[Gribouille]], [[Queulorior]] est capable '
			.. "d'apprendre toutes ces capacités, qu'il peut également transmettre "
			.. "par reproduction.</td></tr>"
	end

	table.insert( morceaux, fermer_table( contexte, colspan, phrase_queulorior ) )
	return table.concat( morceaux, "" )
end

function p.donneur( frame )
	local contexte, message = preparer( frame, { classe = "ListeDonneur" } )
	if contexte == nil then return message end

	local capacites = lignes_utiles( contexte, nom_en_premier_champ )
	local unite_par_defaut = lire_arg( frame, "prix-unité" ) or "[[Point de Combat|PCo]]"

	local colspan = contexte.communes + 2 + nb_colonnes_jeux( contexte )
	local vide = contexte.aucune_declaree or #capacites == 0

	local morceaux = { ouvrir_table( contexte, not vide ) }
	table.insert( morceaux, "<tr>" .. entetes_communes( contexte, 1, titres_communs.usuel )
		.. '<th class="colonne-lieu" scope="col">Lieu</th>'
		.. '<th class="colonne-coût" scope="col">Coût</th>'
		.. entetes_jeux( contexte ) .. "</tr>" )

	if vide then
		table.insert( morceaux, ligne_vide( colspan, "Ce Pokémon n'apprend aucune capacité "
			.. "par donneur lors de cette génération." ) )
	else
		table.insert( morceaux, ligne_bouton( contexte, colspan, #capacites ) )

		for _, capacite in ipairs( capacites ) do
			local champs = capacite.champs
			local _, renvoi = nom_et_renvoi( champs[1], contexte.ancre )
			local rendu = donneesCapacite( contexte.generation, capacite.nom, contexte.jeux )
			local brutes = donnees_brutes( contexte.generation, capacite.nom )

			local cout, unite = champs[3], champs[4]
			if cout == nil then cout, unite = "—", ""
			elseif unite == nil then unite = unite_par_defaut
			end
			if contexte.colonnes_jeux == nil then
				for rang = 5, #champs do unite = unite .. "/" .. champs[rang] end
			end

			table.insert( morceaux,
				"<tr>" .. cellules_communes( contexte, rendu, brutes, 1, renvoi ) )
			table.insert( morceaux, '<td class="colonne-lieu">'
				.. appliquer_notes( champs[2] or "", contexte.ancre ) .. "</td>" )
			table.insert( morceaux, '<td class="colonne-coût"'
				.. attribut_tri( nombre_cache_dans( cout ) or -1 ) .. ">"
				.. appliquer_notes( cout .. " " .. unite, contexte.ancre ) .. "</td>" )
			table.insert( morceaux, cellules_jeux( contexte, champs, 5 ) .. "</tr>" )
		end
	end

	table.insert( morceaux, fermer_table( contexte, colspan ) )
	return table.concat( morceaux, "" )
end

local function methode_a_colonne_finale( reglages )
	return function( frame )
		local contexte, message = preparer( frame, { classe = reglages.classe } )
		if contexte == nil then return message end

		local capacites = lignes_utiles( contexte, nom_en_premier_champ )
		if reglages.colonnes_generations then
			contexte.generations = colonnes_de_generations( contexte )
		end

		local colspan = contexte.communes
			+ ( contexte.generations and #contexte.generations or 1 )
			+ nb_colonnes_jeux( contexte )
		local vide = contexte.aucune_declaree or #capacites == 0

		local morceaux = { ouvrir_table( contexte, not vide ) }
		table.insert( morceaux, "<tr>" .. entetes_communes( contexte, 1, titres_communs.usuel ) )
		if contexte.generations ~= nil then
			table.insert( morceaux, entetes_generations( contexte ) )
		else
			table.insert( morceaux, '<th class="colonne-toutes" scope="col">'
				.. ( lire_arg( frame, "colonne" ) or reglages.titre ) .. "</th>" )
		end
		table.insert( morceaux, entetes_jeux( contexte ) .. "</tr>" )

		if vide then
			table.insert( morceaux, ligne_vide( colspan, reglages.phrase_vide ) )
		else
			table.insert( morceaux, ligne_bouton( contexte, colspan, #capacites ) )

			local infosPokemon
			if reglages.miniature then
				infosPokemon = require( "Module:Ressources/infosPokemon" )
			end

			for _, capacite in ipairs( capacites ) do
				local champs = capacite.champs
				local _, renvoi = nom_et_renvoi( champs[1], contexte.ancre )
				local rendu = donneesCapacite( contexte.generation, capacite.nom, contexte.jeux )
				local brutes = donnees_brutes( contexte.generation, capacite.nom )

				local derniere_colonne = champs[2]
				if derniere_colonne == nil or mw.text.trim( derniere_colonne ) == "" then
					derniere_colonne = reglages.defaut
				else
					derniere_colonne = mw.text.trim( derniere_colonne )
				end

				table.insert( morceaux,
					"<tr>" .. cellules_communes( contexte, rendu, brutes, 1, renvoi ) )
				if contexte.generations ~= nil then
					table.insert( morceaux, cellules_generations( contexte, champs[2] ) )
				elseif reglages.miniature then
					table.insert( morceaux, '<td class="colonne-toutes" style="text-align:left">'
						.. infosPokemon( derniere_colonne ) .. "</td>" )
				else
					table.insert( morceaux, '<td class="colonne-toutes">'
						.. appliquer_notes( derniere_colonne, contexte.ancre ) .. "</td>" )
				end
				table.insert( morceaux, cellules_jeux( contexte, champs, 3 ) .. "</tr>" )
			end
		end

		table.insert( morceaux, fermer_table( contexte, colspan ) )
		return table.concat( morceaux, "" )
	end
end

p.preevolution = methode_a_colonne_finale{
	classe = "ListePreevolution", titre = "Pré-évolution",
	defaut = "Inconnue", miniature = true,
	phrase_vide = "Ce Pokémon n'apprend aucune capacité par pré-évolution "
		.. "lors de cette génération."
}

p.evenement = methode_a_colonne_finale{
	classe = "ListeEvenement", titre = "Événement",
	defaut = "Inconnu", miniature = false,
	phrase_vide = "Ce Pokémon n'apprend aucune capacité par événement "
		.. "lors de cette génération."
}

p.transfert = methode_a_colonne_finale{
	classe = "ListeTransfert", titre = "Provenance",
	defaut = "—", miniature = false, colonnes_generations = true,
	phrase_vide = "Ce Pokémon n'apprend aucune capacité par transfert "
		.. "lors de cette génération."
}

function p.obscur( frame )
	local contexte, message = preparer( frame, { classe = "ListeObscur" } )
	if contexte == nil then return message end

	local capacites = lignes_utiles( contexte, nom_en_premier_champ )
	local colspan = contexte.communes + 2
	local vide = contexte.aucune_declaree or #capacites == 0

	local morceaux = { ouvrir_table( contexte, not vide ) }
	table.insert( morceaux, "<tr>" .. entetes_communes( contexte, 1, titres_communs.usuel )
		.. '<th class="colonne-toutes" scope="col">Jeu</th>'
		.. '<th class="colonne-toutes" scope="col">Obtention</th></tr>' )

	if vide then
		table.insert( morceaux, ligne_vide( colspan, "Ce Pokémon n'apprend aucune capacité "
			.. "dans Pokémon Colosseum ni Pokémon XD." ) )
	else
		table.insert( morceaux, ligne_bouton( contexte, colspan, #capacites ) )

		for _, capacite in ipairs( capacites ) do
			local champs = capacite.champs
			local _, renvoi = nom_et_renvoi( champs[1], contexte.ancre )
			local brutes = donnees_brutes( contexte.generation, capacite.nom )
			local rendu = donneesCapacite( contexte.generation, capacite.nom, contexte.jeux )
			if brutes == nil then
				local sans_categorie = string.gsub( rendu[1],
					"%[%[Catégorie:Page avec un nom de capacité incorrect%]%]", "" )
				rendu[1] = sans_categorie
			end

			table.insert( morceaux,
				"<tr>" .. cellules_communes( contexte, rendu, brutes, 1, renvoi ) )
			table.insert( morceaux, '<td class="colonne-toutes">'
				.. appliquer_notes( champs[2] or "—", contexte.ancre ) .. "</td>" )
			table.insert( morceaux, '<td class="colonne-toutes">'
				.. appliquer_notes( champs[3] or "—", contexte.ancre ) .. "</td></tr>" )
		end
	end

	table.insert( morceaux, fermer_table( contexte, colspan ) )
	return table.concat( morceaux, "" )
end

function p.champions( frame )
	local contexte, message = preparer( frame, { classe = "ListeChampions" } )
	if contexte == nil then return message end

	local capacites = lignes_utiles( contexte, nom_en_premier_champ )
	local colspan = contexte.communes
	local vide = contexte.aucune_declaree or #capacites == 0

	local morceaux = { ouvrir_table( contexte, not vide ) }
	table.insert( morceaux, "<tr>"
		.. entetes_communes( contexte, 1, titres_communs.usuel ) .. "</tr>" )

	if vide then
		table.insert( morceaux, ligne_vide( colspan, "Ce Pokémon n'est pas disponible "
			.. "dans Pokémon Champions." ) )
	else
		table.insert( morceaux, ligne_bouton( contexte, colspan, #capacites ) )

		for _, capacite in ipairs( capacites ) do
			local _, renvoi = nom_et_renvoi( capacite.champs[1], contexte.ancre )
			local rendu = donneesCapacite( contexte.generation, capacite.nom, contexte.jeux )
			local brutes = capacite_champions( capacite.nom )

			if brutes ~= nil then
				local type_champions = brutes[1]
				rendu["type"] = string.lower( ( string.gsub( type_champions, "É", "é" ) ) )
				rendu[2] = "[[Fichier:Miniature Type " .. type_champions
					.. ".png|class=type|link=" .. type_champions .. " (type)|alt="
					.. type_champions .. "]]"
				local categorie = brutes[2]
				rendu[3] = "[[Fichier:Miniature Catégorie "
					.. string.gsub( categorie, "Spécial", "Spéciale" )
					.. " HOME.png|class=catégorie|link=Catégorie de capacité|alt="
					.. categorie .. "]]"
				rendu[4] = ( brutes[3] == 0 ) and "—" or brutes[3]
				rendu[5] = ( brutes[4] == 0 ) and "—" or ( brutes[4] .. " %" )
				rendu[6] = brutes[5]
			end

			table.insert( morceaux,
				"<tr>" .. cellules_communes( contexte, rendu, brutes, 1, renvoi ) .. "</tr>" )
		end
	end

	table.insert( morceaux, fermer_table( contexte, colspan ) )
	return table.concat( morceaux, "" )
end

return p