Jump to content

Module:Creo/Components/Infobox

From Creopedia

This documentation is transcluded from Module:Creo/Components/Infobox/doc.

Output an infobox for Module:Creo using Module:InfoboxLua.


require( 'strict' )

local infoboxLua = require( 'Module:InfoboxLua' )
local getDisplayImage = require( 'Module:Creo' ).getDisplayImage

-- TODO: If we use this in other places, then we should inject this into the Module:Creo instead of loading it here
local rarityTable = mw.loadJsonData( 'Module:GameData/EvoCreo2/RarityTable.json' )

local p = {}

--- @param rarityId number
--- @return mw.html|nil
local function getRarityHtml( rarityId )
	local rarity = rarityTable[rarityId]

	if not rarity or not rarity.name then
		return nil
	end

	return require( 'Module:Rarity' )._main( rarity.nameId )
end

--- @param elementMap table
--- @return mw.html|nil
local function getElementHtml( elementMap )
	if not elementMap.element or not elementMap.element.name then
		return nil
	end

	return require( 'Module:Element' )._main( elementMap.element.name )
end

--- One badge per distinct element, leading with the primary one.
---
--- The export writes some single-element Creo twice -- Samurak is AIR/AIR and
--- Karazim EARTH/EARTH -- as two maps that differ only in `primary`, so the raw
--- list renders the same badge twice. Collapsing the repeat and ordering by
--- `primary` is how creosprites.py and the Creo listing table already read this
--- field, so the infobox now agrees with them.
---
--- @param elements table
--- @return mw.html
local function getElementsHtml( elements )
	local html = mw.html.create( 'div' )
	html:addClass( 't-creo-infobox-elements' )

	local seen = {}

	--- Two passes rather than a sort, because table.sort is not stable and would
	--- shuffle Creo whose maps share a `primary` value.
	---
	--- @param wantPrimary boolean
	local function addElements( wantPrimary )
		for _, elementMap in ipairs( elements ) do
			local name = elementMap.element and elementMap.element.name
			local isPrimary = ( elementMap.primary or 0 ) ~= 0

			if name and isPrimary == wantPrimary and not seen[name] then
				local elementHtml = getElementHtml( elementMap )
				if elementHtml then
					seen[name] = true
					html:node( elementHtml )
				end
			end
		end
	end

	addElements( true )
	addElements( false )

	return html
end

--- @param data table
--- @return string
local function getOverlayContent( data )
	return tostring( mw.html.create( 'div' )
		:addClass( 't-creo-infobox-overlay' )
		:node( getRarityHtml( data.rarityId ) )
		:node( getElementsHtml( data.creoElementMaps ) )
	)
end

--- @param args table
--- @return table
local function getInfoboxCss( args )
	local css = {}
	if args.imageOffsetTop then
		css['--t-creo-image-offset-top'] = args.imageOffsetTop
	end
	if args.imageOffsetBottom then
		css['--t-creo-image-offset-bottom'] = args.imageOffsetBottom
	end
	if args.imageOffsetLeft then
		css['--t-creo-image-offset-left'] = args.imageOffsetLeft
	end
	if args.imageOffsetRight then
		css['--t-creo-image-offset-right'] = args.imageOffsetRight
	end
	return css
end

--- @param args table
--- @param data table
--- @return table
local function getInfoboxSections( args, data )
	return {
		{
			columns = 2,
			items = {
				{
					label = 'Height',
					content = ( args.height or tostring( data.creo.height ) or '?' ) .. ' m'
				},
				{
					label = 'Weight',
					content = ( args.weight or tostring( data.creo.weight ) or '?' ) .. ' kg'
				},
				{
					label = 'Lifespan',
					content = ( args.lifespan or tostring( data.creo.lifespan ) or '?' ) .. ' years'
				}
			}
		}
	}
end

--- Build the infobox data needed from the raw data
---
--- @param context table
--- @return table
local function getInfoboxData( context )
	local data = context.data
	local args = context.args or {}

	return {
		class = 't-creo-infobox',
		title = args.name or data.creo.name,
		subtitle = tostring( data.creo.id ),
		css = getInfoboxCss( args ),
		image = {
			-- The display image, not the raw battle sprite: every one of these
			-- shares a 120x116 canvas, so this width-only size renders the same
			-- box on every Creo page.
			src = args.image or getDisplayImage( data.creo.name ),
			size = 240,
			overlay = getOverlayContent( data ),
			class = 'cp-image-pixelated t-creo-infobox-image'
		},
		sections = getInfoboxSections( args, data )
	}
end

--- @param context ComponentContext
--- @return string
function p.render( context )
	return mw.getCurrentFrame():extensionTag {
		name = 'templatestyles', args = { src = 'Module:Creo/Components/Infobox/styles.css' }
	} .. infoboxLua.render( getInfoboxData( context ) )
end

return p