Module:Creo/Components/Abilities
Appearance
This documentation is transcluded from Module:Creo/Components/Abilities/doc.
Output the abilities table for Module:Creo using Module:TableLua.
require( 'strict' )
local gameTabs = require( 'Module:GameTabs' )
local tableLua = require( 'Module:TableLua' )
local level = require( 'Module:Level' )._main
local nameCell = require( 'Module:Creo/NameCell' ).render
local getAbilityThumbnail = require( 'Module:Ability' ).getThumbnail
local ecGameData = require( 'Module:GameData/EvoCreo' )
local ec2GameData = require( 'Module:GameData/EvoCreo2' )
local p = {}
--- Cached tutor lookup (nil until first use).
local tutors
--- Where each EvoCreo 2 ability is taught.
---
--- Derived from the tutor NPC dialogue in the EvoCreo 2 localization
--- workbook, whose keys encode the map the NPC stands on.
---
--- @return table
local function getTutors()
if tutors == nil then
tutors = mw.loadJsonData( 'Module:Creo/Components/Abilities/Tutors.json' )
end
return tutors
end
--- Get abilities for EvoCreo2 from creoTypes
---
--- Several abilities are reachable from more than one creo type -- Dive from
--- both Aquatic and Diver, Smash from Golem, Brawler and Smasher -- so a Creo
--- carrying two of them would otherwise list that ability once per type.
--- Ahura, Bibo, Seacean and Seffapede each hit this.
---
--- @param data table[]
--- @return table[]
local function getEvoCreo2AbilityMaps( data )
if data == {} then
return {}
end
local abilities = {}
local seen = {}
local creoTypeNames = {}
for _, creoType in ipairs( data ) do
table.insert( creoTypeNames, creoType.name )
end
for _, creoTypeName in ipairs( creoTypeNames ) do
local creoType = ec2GameData.getCreoType( creoTypeName )
if creoType then
for _, ability in ipairs( creoType.abilities ) do
if ability.name ~= nil and not seen[ability.name] then
seen[ability.name] = true
table.insert( abilities, ability )
end
end
end
end
return abilities
end
--- @param levelNum number
--- @return string
local function getLevelWikitext( levelNum )
-- HACK: Create a hidden element so that Module:TableLua can sort by level
-- We need this because we are passing HTML in getRows()
local sort = mw.html.create( 'span' )
sort
:attr( 'hidden', 'true' )
:css( 'display', 'none' )
:wikitext( tostring( levelNum ) )
return tostring( sort ) .. ' ' .. level( levelNum )
end
--- Only EvoCreo 2 has ability art, and EvoCreo 1's field abilities are a
--- different set, but a handful of names overlap (Ride, Dive). The icon is
--- asked for either way and Module:Creo/NameCell drops it when the file does
--- not exist, so the overlapping rows borrow the EvoCreo 2 art and everything
--- else stays text-only.
---
--- @param name string
--- @return string
local function getAbilityWikitext( name )
return nameCell( getAbilityThumbnail( name ), name, name )
end
--- Name the tutor who teaches an ability and link where they are found, e.g.
--- 'Fly tutor in Electree City'.
---
--- @param abilityName string
--- @return string|nil
local function getTutorWikitext( abilityName )
local tutor = getTutors()[abilityName]
if tutor == nil or tutor.locations == nil then
return nil
end
local links = {}
for _, location in ipairs( tutor.locations ) do
table.insert( links, string.format( '[[%s]]', location ) )
end
if #links == 0 then
return nil
end
return string.format( '%s tutor in %s', abilityName, table.concat( links, ' or ' ) )
end
--- @param args table
--- @param gameId string
--- @param abilityName string
--- @return string|nil
local function getUnlockArg( args, gameId, abilityName )
local argName = string.format( '%s_%s_unlock', gameId, abilityName )
return args[argName]
end
--- @param gameId string
--- @return TableColumn[]
local function getColumns( gameId )
if gameId == 'evocreo2' then
return {
{ id = 'name', label = 'Name' },
{ id = 'unlock', label = 'Unlock' }
}
elseif gameId == 'evocreo' then
return {
{ id = 'name', label = 'Name' },
{ id = 'unlock', label = 'Unlock' }
}
end
return {}
end
--- @param data table
--- @param gameId string
--- @param args table
--- @return TableRow[]
local function getRows( data, gameId, args )
local rows = {}
if gameId == 'evocreo2' then
for _, ability in ipairs( getEvoCreo2AbilityMaps( data ) ) do
if ability.name ~= nil then
local row = {
getAbilityWikitext( ability.name ),
getUnlockArg( args, gameId, ability.name )
or getTutorWikitext( ability.name )
or '-'
}
table.insert( rows, row )
end
end
elseif gameId == 'evocreo' then
for _, ability in ipairs( data ) do
if ability.id ~= nil then
local abilityName = ecGameData.getName( ability.id )
local row = {
getAbilityWikitext( abilityName ),
getUnlockArg( args, gameId, abilityName )
or getLevelWikitext( ability.level )
}
table.insert( rows, row )
end
end
end
return rows
end
--- @param data table
--- @param gameId string
--- @param args table
--- @return TableProps
local function getTableProps( data, gameId, args )
return {
caption = 'Abilities',
hideCaption = true,
columns = getColumns( gameId ),
data = getRows( data, gameId, args ),
-- NOTE: no game defines a `level` column here, so this sort is a silent
-- no-op and the rows keep their game-data order. Left as-is because the
-- EvoCreo 2 unlock cell holds a tutor location rather than a level,
-- which would sort alphabetically and read as arbitrary.
sort = {
level = 'asc',
},
class = table.concat( {
't-creo-abilities-table',
'wikitable--fluid' -- Citizen built-in class for broad tables
}, ' ' )
}
end
--- @param gameData table
--- @param gameId string
--- @param args table
--- @return string
local function getGameTabWikitext( gameData, gameId, args )
local html = mw.html.create( 'div' ):addClass( 't-tabbedCard-table-container' )
html:wikitext( tableLua.render( getTableProps( gameData, gameId, args ) ) )
return tostring( html )
end
--- @param data table
--- @param args table
--- @return mw.html
local function getAbilitiesHtml( data, args )
local html = mw.html.create( 'div' ):addClass( 't-creo-abilities' )
local gameTabsData = gameTabs.mapGameArgs( data, function ( gameData, game )
return getGameTabWikitext( gameData, game.id, args )
end )
gameTabsData.noPadding = true
html:wikitext( gameTabs._main( gameTabsData ) )
return html
end
--- @param context ComponentContext
--- @return string
function p.render( context )
return tostring( getAbilitiesHtml( context.data, context.args ) )
end
return p