Module:Creo/Components/Evolution
Appearance
This documentation is transcluded from Module:Creo/Components/Evolution/doc.
Implements Template:Creo/Evolution.
require( 'strict' )
local getThumbnail = require( 'Module:Creo' ).getThumbnail
local cardLua = require( 'Module:CardLua' )
local element = require( 'Module:Element' )._main
local ec2GameData = require( 'Module:GameData/EvoCreo2' )
local p = {}
--- Build the evolution data structure from the raw arguments
---
--- @param args table
--- @return table
local function getEvolutionsData( args )
local tempNodes = {}
for k, v in pairs( args ) do
if type( k ) == 'string' then
local parts = mw.text.split( k, '_' )
-- Example key: "creo_1_1_name" -> parts: {"creo", "1", "1", "name"}
if parts[1] == 'creo' and #parts >= 3 then
table.remove( parts, 1 ) -- Remove "creo" -> {"1", "1", "name"}
local prop = table.remove( parts, #parts ) -- prop: "name", parts: {"1", "1"}
-- We only care about name and content properties
if prop == 'name' or prop == 'content' then
local path = table.concat( parts, '_' ) -- path: "1_1"
tempNodes[path] = tempNodes[path] or {}
tempNodes[path][prop] = v
end
end
end
end
local evolutions = {}
local nodes = {} -- path -> final node map
local sortedPaths = {}
for path in pairs( tempNodes ) do
table.insert( sortedPaths, path )
end
table.sort( sortedPaths )
for _, path in ipairs( sortedPaths ) do
local data = tempNodes[path]
-- A node is only valid if it has a name.
if data.name then
local node = {
name = data.name,
content = data.content,
evolutions = {}
}
local parentPath = path:match( '^(.*)_[^_]+$' )
if parentPath then
local parentNode = nodes[parentPath]
if parentNode then
table.insert( parentNode.evolutions, node )
end
else -- It's a root node
table.insert( evolutions, node )
end
nodes[path] = node
end
end
return evolutions
end
--- How many nodes a parsed argument tree describes.
---
--- Every Creo article was created with a stub that names only the Creo itself,
--- with the content "Evolution text for <Name>". A lone root describes no
--- family, so it is treated as unwritten and the tree comes from the game data
--- instead; a page that does spell out its family keeps what was written.
---
--- @param evolutions table|nil
--- @return number
local function countNodes( evolutions )
local total = 0
for _, node in ipairs( evolutions or {} ) do
total = total + 1 + countNodes( node.evolutions )
end
return total
end
--- Describe what triggers one evolution.
---
--- The export only ever sets a level or an element affinity, but item and
--- loyalty evolutions exist in the schema and are handled so a future export
--- does not render a blank card.
---
--- @param step table
--- @return string|nil
local function getConditionWikitext( step )
if step.element then
return string.format( 'Maxed %s affinity', tostring( element( step.element ) ) )
end
if step.level then
return string.format( 'Evolves at Level %s', tostring( step.level ) )
end
if step.item then
return string.format( 'Evolves with %s', tostring( step.item ) )
end
if step.loyalty then
return string.format( 'Evolves at %s loyalty', tostring( step.loyalty ) )
end
return nil
end
--- Build the family tree around the Creo whose article this is.
---
--- The index only records where an evolution leads, so the parent map is
--- rebuilt here in order to climb to the base form. Every member of a family
--- then renders the same tree, with its own entry marked as the current one by
--- getEvolutionListHtml.
---
--- @param name string
--- @return table
local function getEvolutionsFromData( name )
local index = ec2GameData.getEvolutionIndex() or {}
local parents = {}
for parent, steps in pairs( index ) do
for _, step in ipairs( steps ) do
parents[step.name] = parent
end
end
-- Climb to the base form. `seen` is paranoia: the export describes a tree
-- today, and a cycle introduced later would otherwise hang the render.
local root = name
local seen = {}
while parents[root] and not seen[root] do
seen[root] = true
root = parents[root]
end
--- @param creoName string
--- @param content string|nil
--- @return table
local function build( creoName, content )
local node = {
name = creoName,
content = content,
evolutions = {}
}
for _, step in ipairs( index[creoName] or {} ) do
table.insert( node.evolutions, build( step.name, getConditionWikitext( step ) ) )
end
return node
end
local content = 'Does not evolve'
if index[root] then
content = 'Base form'
end
return { build( root, content ) }
end
--- Return the wikitext for the evolution list item
---
--- @param name string
--- @param content string
--- @return string
local function getEvolutionListItemWikitext( name, content )
return cardLua.render( {
title = name,
page = name,
description = content,
thumbnail = getThumbnail( name ),
thumbnailSize = '80px',
class = 'cp-image-pixelated'
} )
end
--- Return the wikitext for the evolution list
---
--- @param evolutions table
--- @return mw.html|nil
local function getEvolutionListHtml( evolutions )
if not evolutions or #evolutions == 0 then
return nil
end
local currentPage = mw.title.getCurrentTitle().fullText
local list = mw.html.create( 'ul' ):addClass( 't-creo-evolution-tree' )
for _, evoNode in ipairs( evolutions ) do
local li = list:tag( 'li' ):addClass( 't-creo-evolution-tree-item' )
li:wikitext( getEvolutionListItemWikitext( evoNode.name, evoNode.content ) )
if evoNode.name == currentPage then
li:addClass( 't-creo-evolution-tree-item--current' )
end
local sublist = getEvolutionListHtml( evoNode.evolutions )
if sublist then
li:node( sublist )
end
end
return list
end
--- Build the evolution list HTML recursively
---
--- @param evolutions table
--- @return mw.html
local function getEvolutionsHtml( evolutions )
local html = mw.html.create( 'div' )
html
:addClass( 't-creo-evolution' )
:tag( 'div' )
:addClass( 't-creo-evolution-wrapper' )
:node( getEvolutionListHtml( evolutions ) )
:done()
return html
end
--- Return the wikitext for the evolution list
---
--- @param context ComponentContext
--- @return string
function p.render( context )
local evolutions = getEvolutionsData( context.args or {} )
if countNodes( evolutions ) < 2 then
evolutions = getEvolutionsFromData( mw.title.getCurrentTitle().text )
end
return mw.getCurrentFrame():extensionTag {
name = 'templatestyles', args = { src = 'Module:Creo/Components/Evolution/styles.css' }
} .. tostring( getEvolutionsHtml( evolutions ) )
end
return p