Module:Engineers: Difference between revisions
Jump to navigation
Jump to search
Repair content model (previous save incorrectly wrote this as wikitext) + add p.count() |
No edit summary |
||
| Line 1: | Line 1: | ||
-- Module:Engineers | -- Module:Engineers | ||
-- Reads canonical Engineer facts from Module:Data/Engineers/<Name> (JSON | -- Reads canonical Engineer facts from Module:Data/Engineers/<Name> (JSON | ||
-- content-model pages) and formats them for | -- content-model pages) and formats them for multiple consumers: | ||
-- | -- | ||
-- Adding a new | -- * Engineer infoboxes via Template:Engineer infobox | ||
-- | -- * List of Engineers | ||
-- * Engineer Capability Comparison | |||
-- | -- | ||
-- This module adds appropriate internal wiki links when rendering content. | |||
-- | |||
-- Adding a new Engineer: | |||
-- 1. Create Module:Data/Engineers/<Name> | |||
-- 2. Use the same JSON schema as the existing Engineer records | |||
-- 3. Append the Engineer name to Module:Data/Engineers/index | |||
-- | |||
-- See Elite Dangerous Field Manual:Adding Structured Data. | |||
local p = {} | local p = {} | ||
-- Field order | -- Field order and labels shared by both the structured-data path and the | ||
-- fallback path | -- manual fallback path. | ||
local FIELD_ORDER = { | local FIELD_ORDER = { | ||
{ key = 'engineer_type', label = 'Engineer type', group = 'ENGINEER' }, | { key = 'engineer_type', label = 'Engineer type', group = 'ENGINEER' }, | ||
| Line 25: | Line 29: | ||
{ key = 'specialties', label = 'Specialties' }, | { key = 'specialties', label = 'Specialties' }, | ||
{ key = 'max_grade', label = 'Maximum grade' }, | { key = 'max_grade', label = 'Maximum grade' }, | ||
{ key = 'prerequisite_engineer', label = 'Prerequisite', group = 'ACCESS' }, | { key = 'prerequisite_engineer', label = 'Prerequisite', group = 'ACCESS' }, | ||
{ key = 'discovery_requirement', label = 'Discovery' }, | { key = 'discovery_requirement', label = 'Discovery' }, | ||
| Line 30: | Line 35: | ||
{ key = 'unlock_requirement', label = 'Unlock' }, | { key = 'unlock_requirement', label = 'Unlock' }, | ||
} | } | ||
------------------------------------------------------------------------ | |||
-- Utility functions | |||
------------------------------------------------------------------------ | |||
-- Converts a plain structured-data value into an internal wiki link. | |||
-- | |||
-- Examples: | |||
-- | |||
-- wikilink('Deciat') | |||
-- -> [[Deciat]] | |||
-- | |||
-- wikilink('Farseer Inc') | |||
-- -> [[Farseer Inc]] | |||
-- | |||
-- Existing wiki links are left untouched so explicit/manual overrides | |||
-- remain safe. | |||
local function wikilink(value, target) | |||
if value == nil or value == '' then | |||
return value | |||
end | |||
if type(value) ~= 'string' then | |||
return value | |||
end | |||
-- Already contains a wiki link. | |||
if value:find('%[%[') then | |||
return value | |||
end | |||
target = target or value | |||
if target == value then | |||
return '[[' .. value .. ']]' | |||
end | |||
return '[[' .. target .. '|' .. value .. ']]' | |||
end | |||
------------------------------------------------------------------------ | |||
-- Data loading | |||
------------------------------------------------------------------------ | |||
local function loadRecord(name) | local function loadRecord(name) | ||
local title = mw.title.new('Data/Engineers/' .. name, 'Module') | local title = mw.title.new('Data/Engineers/' .. name, 'Module') | ||
if not title then | if not title then | ||
return nil | return nil | ||
end | end | ||
local content = title:getContent() | local content = title:getContent() | ||
if not content then | if not content then | ||
return nil | return nil | ||
end | end | ||
local ok, data = pcall(mw.text.jsonDecode, content) | local ok, data = pcall(mw.text.jsonDecode, content) | ||
if not ok then | if not ok then | ||
return nil | return nil | ||
end | end | ||
return data | return data | ||
end | end | ||
local function loadIndex() | local function loadIndex() | ||
local title = mw.title.new('Data/Engineers/index', 'Module') | local title = mw.title.new('Data/Engineers/index', 'Module') | ||
local content = title and title:getContent() | local content = title and title:getContent() | ||
if not content then | if not content then | ||
return {} | return {} | ||
end | end | ||
local ok, data = pcall(mw.text.jsonDecode, content) | local ok, data = pcall(mw.text.jsonDecode, content) | ||
if not ok then | if not ok then | ||
return {} | return {} | ||
end | end | ||
return data | return data | ||
end | end | ||
local function joinSpecialties(d) | |||
if d.specialties | ------------------------------------------------------------------------ | ||
return table.concat( | -- Specialty formatting | ||
------------------------------------------------------------------------ | |||
-- Structured Engineer records should preferably store specialties as an | |||
-- array: | |||
-- | |||
-- "specialties": [ | |||
-- "Frame Shift Drive", | |||
-- "Power Plant", | |||
-- "Thrusters" | |||
-- ] | |||
-- | |||
-- This allows each module name to become its own internal link. | |||
local function joinSpecialties(d, linked) | |||
if not d or not d.specialties then | |||
return nil | |||
end | |||
if type(d.specialties) == 'table' then | |||
local values = {} | |||
for _, specialty in ipairs(d.specialties) do | |||
if linked then | |||
table.insert(values, wikilink(specialty)) | |||
else | |||
table.insert(values, specialty) | |||
end | |||
end | |||
return table.concat(values, ', ') | |||
end | end | ||
-- Preserve legacy/string values unchanged. These may contain descriptive | |||
-- prose rather than a single module name, so automatically linking the | |||
-- entire string would not always be appropriate. | |||
return d.specialties | return d.specialties | ||
end | end | ||
-- | |||
-- | ------------------------------------------------------------------------ | ||
-- | -- Unlock formatting | ||
------------------------------------------------------------------------ | |||
-- Supports a newer structured unlock format while retaining compatibility | |||
-- with the existing unlock_requirement prose field. | |||
-- | |||
-- Example JSON: | |||
-- | |||
-- "unlock": { | |||
-- "action": "Deliver", | |||
-- "quantity": 1, | |||
-- "item": "Meta-Alloys" | |||
-- } | |||
-- | |||
-- Produces: | |||
-- | |||
-- Deliver 1 unit of [[Meta-Alloys]] | |||
-- | |||
-- Optional fields: | |||
-- | |||
-- "unit": "unit" | |||
-- "unit_plural": "units" | |||
-- "item_target": "Meta-Alloys" | |||
-- | |||
-- For unlock requirements that do not fit this structure cleanly, continue | |||
-- using unlock_requirement as a display string. | |||
local function formatUnlock(d) | |||
if not d then | |||
return nil | |||
end | |||
if type(d.unlock) == 'table' and d.unlock.item then | |||
local action = d.unlock.action or 'Deliver' | |||
local quantity = d.unlock.quantity or 1 | |||
local quantityNumber = tonumber(quantity) | |||
local singularUnit = d.unlock.unit or 'unit' | |||
local pluralUnit = d.unlock.unit_plural or (singularUnit .. 's') | |||
local unit | |||
if quantityNumber == 1 then | |||
unit = singularUnit | |||
else | |||
unit = pluralUnit | |||
end | |||
local item = wikilink( | |||
d.unlock.item, | |||
d.unlock.item_target or d.unlock.item | |||
) | |||
return string.format( | |||
'%s %s %s of %s', | |||
action, | |||
tostring(quantity), | |||
unit, | |||
item | |||
) | |||
end | |||
-- Backward compatibility with existing records. | |||
return d.unlock_requirement | |||
end | |||
------------------------------------------------------------------------ | |||
-- Field construction | |||
------------------------------------------------------------------------ | |||
-- Builds the display values used by an Engineer infobox. | |||
-- | |||
-- Structured data supplies the defaults. Explicit parameters supplied by | |||
-- an article override those defaults. | |||
local function buildFields(d, pargs) | local function buildFields(d, pargs) | ||
local fields = {} | local fields = {} | ||
if d then | if d then | ||
-- Start with the canonical values. | |||
for _, f in ipairs(FIELD_ORDER) do | for _, f in ipairs(FIELD_ORDER) do | ||
fields[f.key] = d[f.key] | fields[f.key] = d[f.key] | ||
end | end | ||
fields.specialties = joinSpecialties(d) | |||
-------------------------------------------------------------------- | |||
-- Automatically linked structured fields | |||
-------------------------------------------------------------------- | |||
fields.system = wikilink(d.system) | |||
fields.body = wikilink(d.body) | |||
fields.facility = wikilink(d.facility) | |||
fields.region = wikilink(d.region) | |||
fields.allegiance = wikilink(d.allegiance) | |||
-- Each specialty/module gets its own internal link. | |||
fields.specialties = joinSpecialties(d, true) | |||
-------------------------------------------------------------------- | |||
-- Prerequisite Engineer | |||
-------------------------------------------------------------------- | |||
if d.prerequisite_engineer then | if d.prerequisite_engineer then | ||
local note = d.prerequisite_engineer_note and | local note = '' | ||
fields.prerequisite_engineer = | |||
if d.prerequisite_engineer_note | |||
and d.prerequisite_engineer_note ~= '' then | |||
note = ' ' .. d.prerequisite_engineer_note | |||
end | |||
fields.prerequisite_engineer = | |||
wikilink(d.prerequisite_engineer) .. note | |||
else | else | ||
fields.prerequisite_engineer = d.prerequisite_engineer_display or 'None — available from the start' | fields.prerequisite_engineer = | ||
d.prerequisite_engineer_display | |||
or 'None — available from the start' | |||
end | end | ||
-------------------------------------------------------------------- | |||
-- Unlock | |||
-------------------------------------------------------------------- | |||
fields.unlock_requirement = formatUnlock(d) | |||
else | else | ||
-- No structured record exists yet. Fall back entirely to manually | |||
-- supplied template parameters. | |||
for _, f in ipairs(FIELD_ORDER) do | for _, f in ipairs(FIELD_ORDER) do | ||
fields[f.key] = pargs[f.key] | fields[f.key] = pargs[f.key] | ||
| Line 89: | Line 293: | ||
end | end | ||
-- | ------------------------------------------------------------------------ | ||
-- | -- Explicit article overrides | ||
-- | ------------------------------------------------------------------------ | ||
-- | -- | ||
-- This preserves: | |||
-- | |||
-- {{Engineer infobox | |||
-- |image = DifferentPortrait.png | |||
-- }} | |||
-- | |||
-- as a valid way to override one structured value without duplicating | |||
-- the rest of the Engineer record. | |||
-- | |||
-- Empty parameters do not erase structured defaults. | |||
for _, f in ipairs(FIELD_ORDER) do | for _, f in ipairs(FIELD_ORDER) do | ||
if pargs[f.key] ~= nil and pargs[f.key] ~= '' then | if pargs[f.key] ~= nil and pargs[f.key] ~= '' then | ||
| Line 102: | Line 316: | ||
end | end | ||
-- {{#invoke:Engineers|infobox}} | |||
-- | ------------------------------------------------------------------------ | ||
-- record exists | -- Engineer infobox | ||
-- | ------------------------------------------------------------------------ | ||
-- {{#invoke:Engineers|infobox}} | |||
-- | |||
-- Called by Template:Engineer infobox. | |||
-- | |||
-- If a canonical JSON record exists, it supplies the defaults. | |||
-- Explicit template parameters override structured values. | |||
-- If no JSON record exists, manual parameters are used instead. | |||
function p.infobox(frame) | function p.infobox(frame) | ||
local parent = frame:getParent() | local parent = frame:getParent() | ||
local pargs = parent and parent.args or {} | local pargs = parent and parent.args or {} | ||
local name = pargs.name or mw.title.getCurrentTitle().text | local name = pargs.name or mw.title.getCurrentTitle().text | ||
local d = loadRecord(name) | local d = loadRecord(name) | ||
local fields = buildFields(d, pargs) | local fields = buildFields(d, pargs) | ||
------------------------------------------------------------------------ | |||
-- Image | |||
------------------------------------------------------------------------ | |||
local image = pargs.image | local image = pargs.image | ||
if ( image == nil or image == '' ) and d and d.image then | |||
if (image == nil or image == '') | |||
and d | |||
and d.image | |||
and d.image ~= '' then | |||
image = d.image | image = d.image | ||
end | end | ||
local args = { name = name, image = image } | ------------------------------------------------------------------------ | ||
-- Build Template:Infobox parameters | |||
------------------------------------------------------------------------ | |||
local args = { | |||
name = name, | |||
image = image | |||
} | |||
local n = 0 | local n = 0 | ||
for _, f in ipairs(FIELD_ORDER) do | for _, f in ipairs(FIELD_ORDER) do | ||
local value = fields[f.key] | local value = fields[f.key] | ||
if value ~= nil and value ~= '' then | if value ~= nil and value ~= '' then | ||
n = n + 1 | n = n + 1 | ||
if f.group then | if f.group then | ||
args['group' .. n] = f.group | args['group' .. n] = f.group | ||
end | end | ||
args['label' .. n] = f.label | args['label' .. n] = f.label | ||
args['row' .. n] = value | args['row' .. n] = value | ||
| Line 132: | Line 376: | ||
end | end | ||
return frame:expandTemplate{ title = 'Infobox', args = args } | return frame:expandTemplate { | ||
title = 'Infobox', | |||
args = args | |||
} | |||
end | end | ||
-- {{#invoke:Engineers|list}}, | |||
------------------------------------------------------------------------ | |||
-- Engineer list | |||
------------------------------------------------------------------------ | |||
-- {{#invoke:Engineers|list}} | |||
-- | |||
-- Generates a sortable Engineer table from the canonical index. | |||
-- | |||
-- Engineer names, systems, and specialties are linked automatically. | |||
function p.list() | function p.list() | ||
local rows = {} | local rows = {} | ||
for _, name in ipairs(loadIndex()) do | for _, name in ipairs(loadIndex()) do | ||
local d = loadRecord(name) | local d = loadRecord(name) | ||
if d then | if d then | ||
table.insert(rows, string.format( | table.insert( | ||
rows, | |||
string.format( | |||
'|-\n| %s || %s || %s || %s', | |||
wikilink(d.name or name), | |||
wikilink(d.system) or '—', | |||
joinSpecialties(d, true) or '—', | |||
d.max_grade or 'Not yet confirmed' | |||
) | |||
) | |||
end | end | ||
end | end | ||
return '{| class="wikitable sortable"\n! Engineer !! System !! Specialties !! Maximum grade\n' | |||
.. table.concat(rows, '\n') .. '\n|}' | return | ||
'{| class="wikitable sortable"\n' | |||
.. '! Engineer !! System !! Specialties !! Maximum grade\n' | |||
.. table.concat(rows, '\n') | |||
.. '\n|}' | |||
end | end | ||
-- {{#invoke:Engineers|comparison}}, | |||
------------------------------------------------------------------------ | |||
-- Engineer capability comparison | |||
------------------------------------------------------------------------ | |||
-- {{#invoke:Engineers|comparison}} | |||
-- | |||
-- Generates a comparison table from canonical Engineer data. | |||
-- | |||
-- Engineer names, specialties, prerequisite Engineers, and structured | |||
-- unlock items are linked automatically. | |||
function p.comparison() | function p.comparison() | ||
local rows = {} | local rows = {} | ||
for _, name in ipairs(loadIndex()) do | for _, name in ipairs(loadIndex()) do | ||
local d = loadRecord(name) | local d = loadRecord(name) | ||
if d then | if d then | ||
local prereq | local prereq | ||
table.insert(rows, string.format( | |||
if d.prerequisite_engineer then | |||
prereq = wikilink(d.prerequisite_engineer) | |||
else | |||
prereq = | |||
d.prerequisite_engineer_display | |||
or 'None' | |||
end | |||
table.insert( | |||
rows, | |||
string.format( | |||
'|-\n| %s || %s || %s || %s', | |||
wikilink(d.name or name), | |||
joinSpecialties(d, true) or '—', | |||
prereq, | |||
formatUnlock(d) or 'Not yet confirmed' | |||
) | |||
) | |||
end | end | ||
end | end | ||
return '{| class="wikitable sortable"\n! Engineer !! Specialties !! Prerequisite Engineer !! Unlock requirement\n' | |||
.. table.concat(rows, '\n') .. '\n|}' | return | ||
'{| class="wikitable sortable"\n' | |||
.. '! Engineer !! Specialties !! Prerequisite Engineer !! Unlock requirement\n' | |||
.. table.concat(rows, '\n') | |||
.. '\n|}' | |||
end | end | ||
-- {{#invoke:Engineers|count}} | |||
-- canonical | ------------------------------------------------------------------------ | ||
-- Engineer count | |||
------------------------------------------------------------------------ | |||
-- {{#invoke:Engineers|count}} | |||
-- | |||
-- Returns the number of canonical Engineer records currently present in | |||
-- Module:Data/Engineers/index. | |||
function p.count() | function p.count() | ||
return tostring(#loadIndex()) | return tostring(#loadIndex()) | ||
end | end | ||
return p | return p | ||
Revision as of 20:26, 19 August 2026
Documentation for this module may be created at Module:Engineers/doc
-- Module:Engineers
-- Reads canonical Engineer facts from Module:Data/Engineers/<Name> (JSON
-- content-model pages) and formats them for multiple consumers:
--
-- * Engineer infoboxes via Template:Engineer infobox
-- * List of Engineers
-- * Engineer Capability Comparison
--
-- This module adds appropriate internal wiki links when rendering content.
--
-- Adding a new Engineer:
-- 1. Create Module:Data/Engineers/<Name>
-- 2. Use the same JSON schema as the existing Engineer records
-- 3. Append the Engineer name to Module:Data/Engineers/index
--
-- See Elite Dangerous Field Manual:Adding Structured Data.
local p = {}
-- Field order and labels shared by both the structured-data path and the
-- manual fallback path.
local FIELD_ORDER = {
{ key = 'engineer_type', label = 'Engineer type', group = 'ENGINEER' },
{ key = 'system', label = 'System' },
{ key = 'body', label = 'Body' },
{ key = 'facility', label = 'Facility' },
{ key = 'region', label = 'Region' },
{ key = 'allegiance', label = 'Allegiance' },
{ key = 'specialties', label = 'Specialties' },
{ key = 'max_grade', label = 'Maximum grade' },
{ key = 'prerequisite_engineer', label = 'Prerequisite', group = 'ACCESS' },
{ key = 'discovery_requirement', label = 'Discovery' },
{ key = 'invitation_requirement', label = 'Invitation' },
{ key = 'unlock_requirement', label = 'Unlock' },
}
------------------------------------------------------------------------
-- Utility functions
------------------------------------------------------------------------
-- Converts a plain structured-data value into an internal wiki link.
--
-- Examples:
--
-- wikilink('Deciat')
-- -> [[Deciat]]
--
-- wikilink('Farseer Inc')
-- -> [[Farseer Inc]]
--
-- Existing wiki links are left untouched so explicit/manual overrides
-- remain safe.
local function wikilink(value, target)
if value == nil or value == '' then
return value
end
if type(value) ~= 'string' then
return value
end
-- Already contains a wiki link.
if value:find('%[%[') then
return value
end
target = target or value
if target == value then
return '[[' .. value .. ']]'
end
return '[[' .. target .. '|' .. value .. ']]'
end
------------------------------------------------------------------------
-- Data loading
------------------------------------------------------------------------
local function loadRecord(name)
local title = mw.title.new('Data/Engineers/' .. name, 'Module')
if not title then
return nil
end
local content = title:getContent()
if not content then
return nil
end
local ok, data = pcall(mw.text.jsonDecode, content)
if not ok then
return nil
end
return data
end
local function loadIndex()
local title = mw.title.new('Data/Engineers/index', 'Module')
local content = title and title:getContent()
if not content then
return {}
end
local ok, data = pcall(mw.text.jsonDecode, content)
if not ok then
return {}
end
return data
end
------------------------------------------------------------------------
-- Specialty formatting
------------------------------------------------------------------------
-- Structured Engineer records should preferably store specialties as an
-- array:
--
-- "specialties": [
-- "Frame Shift Drive",
-- "Power Plant",
-- "Thrusters"
-- ]
--
-- This allows each module name to become its own internal link.
local function joinSpecialties(d, linked)
if not d or not d.specialties then
return nil
end
if type(d.specialties) == 'table' then
local values = {}
for _, specialty in ipairs(d.specialties) do
if linked then
table.insert(values, wikilink(specialty))
else
table.insert(values, specialty)
end
end
return table.concat(values, ', ')
end
-- Preserve legacy/string values unchanged. These may contain descriptive
-- prose rather than a single module name, so automatically linking the
-- entire string would not always be appropriate.
return d.specialties
end
------------------------------------------------------------------------
-- Unlock formatting
------------------------------------------------------------------------
-- Supports a newer structured unlock format while retaining compatibility
-- with the existing unlock_requirement prose field.
--
-- Example JSON:
--
-- "unlock": {
-- "action": "Deliver",
-- "quantity": 1,
-- "item": "Meta-Alloys"
-- }
--
-- Produces:
--
-- Deliver 1 unit of [[Meta-Alloys]]
--
-- Optional fields:
--
-- "unit": "unit"
-- "unit_plural": "units"
-- "item_target": "Meta-Alloys"
--
-- For unlock requirements that do not fit this structure cleanly, continue
-- using unlock_requirement as a display string.
local function formatUnlock(d)
if not d then
return nil
end
if type(d.unlock) == 'table' and d.unlock.item then
local action = d.unlock.action or 'Deliver'
local quantity = d.unlock.quantity or 1
local quantityNumber = tonumber(quantity)
local singularUnit = d.unlock.unit or 'unit'
local pluralUnit = d.unlock.unit_plural or (singularUnit .. 's')
local unit
if quantityNumber == 1 then
unit = singularUnit
else
unit = pluralUnit
end
local item = wikilink(
d.unlock.item,
d.unlock.item_target or d.unlock.item
)
return string.format(
'%s %s %s of %s',
action,
tostring(quantity),
unit,
item
)
end
-- Backward compatibility with existing records.
return d.unlock_requirement
end
------------------------------------------------------------------------
-- Field construction
------------------------------------------------------------------------
-- Builds the display values used by an Engineer infobox.
--
-- Structured data supplies the defaults. Explicit parameters supplied by
-- an article override those defaults.
local function buildFields(d, pargs)
local fields = {}
if d then
-- Start with the canonical values.
for _, f in ipairs(FIELD_ORDER) do
fields[f.key] = d[f.key]
end
--------------------------------------------------------------------
-- Automatically linked structured fields
--------------------------------------------------------------------
fields.system = wikilink(d.system)
fields.body = wikilink(d.body)
fields.facility = wikilink(d.facility)
fields.region = wikilink(d.region)
fields.allegiance = wikilink(d.allegiance)
-- Each specialty/module gets its own internal link.
fields.specialties = joinSpecialties(d, true)
--------------------------------------------------------------------
-- Prerequisite Engineer
--------------------------------------------------------------------
if d.prerequisite_engineer then
local note = ''
if d.prerequisite_engineer_note
and d.prerequisite_engineer_note ~= '' then
note = ' ' .. d.prerequisite_engineer_note
end
fields.prerequisite_engineer =
wikilink(d.prerequisite_engineer) .. note
else
fields.prerequisite_engineer =
d.prerequisite_engineer_display
or 'None — available from the start'
end
--------------------------------------------------------------------
-- Unlock
--------------------------------------------------------------------
fields.unlock_requirement = formatUnlock(d)
else
-- No structured record exists yet. Fall back entirely to manually
-- supplied template parameters.
for _, f in ipairs(FIELD_ORDER) do
fields[f.key] = pargs[f.key]
end
end
------------------------------------------------------------------------
-- Explicit article overrides
------------------------------------------------------------------------
--
-- This preserves:
--
-- {{Engineer infobox
-- |image = DifferentPortrait.png
-- }}
--
-- as a valid way to override one structured value without duplicating
-- the rest of the Engineer record.
--
-- Empty parameters do not erase structured defaults.
for _, f in ipairs(FIELD_ORDER) do
if pargs[f.key] ~= nil and pargs[f.key] ~= '' then
fields[f.key] = pargs[f.key]
end
end
return fields
end
------------------------------------------------------------------------
-- Engineer infobox
------------------------------------------------------------------------
-- {{#invoke:Engineers|infobox}}
--
-- Called by Template:Engineer infobox.
--
-- If a canonical JSON record exists, it supplies the defaults.
-- Explicit template parameters override structured values.
-- If no JSON record exists, manual parameters are used instead.
function p.infobox(frame)
local parent = frame:getParent()
local pargs = parent and parent.args or {}
local name = pargs.name or mw.title.getCurrentTitle().text
local d = loadRecord(name)
local fields = buildFields(d, pargs)
------------------------------------------------------------------------
-- Image
------------------------------------------------------------------------
local image = pargs.image
if (image == nil or image == '')
and d
and d.image
and d.image ~= '' then
image = d.image
end
------------------------------------------------------------------------
-- Build Template:Infobox parameters
------------------------------------------------------------------------
local args = {
name = name,
image = image
}
local n = 0
for _, f in ipairs(FIELD_ORDER) do
local value = fields[f.key]
if value ~= nil and value ~= '' then
n = n + 1
if f.group then
args['group' .. n] = f.group
end
args['label' .. n] = f.label
args['row' .. n] = value
end
end
return frame:expandTemplate {
title = 'Infobox',
args = args
}
end
------------------------------------------------------------------------
-- Engineer list
------------------------------------------------------------------------
-- {{#invoke:Engineers|list}}
--
-- Generates a sortable Engineer table from the canonical index.
--
-- Engineer names, systems, and specialties are linked automatically.
function p.list()
local rows = {}
for _, name in ipairs(loadIndex()) do
local d = loadRecord(name)
if d then
table.insert(
rows,
string.format(
'|-\n| %s || %s || %s || %s',
wikilink(d.name or name),
wikilink(d.system) or '—',
joinSpecialties(d, true) or '—',
d.max_grade or 'Not yet confirmed'
)
)
end
end
return
'{| class="wikitable sortable"\n'
.. '! Engineer !! System !! Specialties !! Maximum grade\n'
.. table.concat(rows, '\n')
.. '\n|}'
end
------------------------------------------------------------------------
-- Engineer capability comparison
------------------------------------------------------------------------
-- {{#invoke:Engineers|comparison}}
--
-- Generates a comparison table from canonical Engineer data.
--
-- Engineer names, specialties, prerequisite Engineers, and structured
-- unlock items are linked automatically.
function p.comparison()
local rows = {}
for _, name in ipairs(loadIndex()) do
local d = loadRecord(name)
if d then
local prereq
if d.prerequisite_engineer then
prereq = wikilink(d.prerequisite_engineer)
else
prereq =
d.prerequisite_engineer_display
or 'None'
end
table.insert(
rows,
string.format(
'|-\n| %s || %s || %s || %s',
wikilink(d.name or name),
joinSpecialties(d, true) or '—',
prereq,
formatUnlock(d) or 'Not yet confirmed'
)
)
end
end
return
'{| class="wikitable sortable"\n'
.. '! Engineer !! Specialties !! Prerequisite Engineer !! Unlock requirement\n'
.. table.concat(rows, '\n')
.. '\n|}'
end
------------------------------------------------------------------------
-- Engineer count
------------------------------------------------------------------------
-- {{#invoke:Engineers|count}}
--
-- Returns the number of canonical Engineer records currently present in
-- Module:Data/Engineers/index.
function p.count()
return tostring(#loadIndex())
end
return p