Module:Companion table

From Final Fantasy XIV Online Wiki
Jump to navigation Jump to search
Documentation for Module:Companion table [view] [edit] [history] [purge] (How does this work?)

This module implements {{Companion table}}.


-- Module:CompanionTable
-- Renders one or more companion/NPC info tables, splitting into multiple
-- tables when the number of NPCs exceeds |maxcols| (default 4).
--
-- Template usage example:
--   {{#invoke:CompanionTable|main
--    | maxcols  = 4
--    | imgwidth = 160px
--    | npc1   = Wuk Lamat  | image1 = DS Wuk Lamat.png
--    | class1 = {{WAR}} [[File:Red warrior.png|Warrior|20px|link=]] Intrepid
--    | role1  = Tank or DPS
--    | npc2   = G'raha Tia | image2 = DS G'raha Tia1.png
--    | class2 = {{PLD}} {{WHM}} All-rounder
--    | role2  = Tank or Healer
--    | ...
--   }}

local p = {}

-- Default image width.  Override globally with |imgwidth=, or per-image with |imgwidthN=.
local DEFAULT_IMG_WIDTH = "150px"

-- Default maximum columns per table.
local DEFAULT_MAX_COLS  = 4

-- Names whose wiki article differs from their display name.
-- Key: display name (as passed in |npcN=)
-- Value: article title to link to
local DISAMBIG = {
    ["Sphene"] = "Sphene (NPC)",
}

-------------------------------------------------------------------------------
-- Helper: return a wikilink for an NPC, using DISAMBIG when needed.
-- Produces [[Target|Display]] when the article title differs, else [[Name]].
-------------------------------------------------------------------------------
local function npcLink(name, isTrust)
    if name == "" then return "" end
    local target = DISAMBIG[name] or name
    local display = isTrust and (name .. "'s Avatar") or name
    if display ~= target then
        return "[[" .. target .. "|" .. display .. "]]"
    else
        return "[[" .. target .. "]]"
    end
end

-------------------------------------------------------------------------------
-- Helper: collect NPC data from frame args into an ordered list.
-- Each entry: { name, image, class, role }
-------------------------------------------------------------------------------
local function collectNPCs(args, globalImgWidth)
    local npcs = {}
    local i = 1
    while args["npc" .. i] or args["image" .. i] do
        npcs[#npcs + 1] = {
            name     = args["npc"      .. i] or "",
            image    = args["image"    .. i] or "",
            imgwidth = args["imgwidth" .. i] or globalImgWidth,
            class    = args["class"    .. i] or "",
            role     = args["role"     .. i] or "",
        }
        i = i + 1
    end
    return npcs
end

-------------------------------------------------------------------------------
-- Helper: render a single wiki-table for a slice of NPCs.
-------------------------------------------------------------------------------
local function renderTable(slice, isTrust)
    local lines = {}

    -- Table open
    lines[#lines + 1] = '{| {{STDT|npc align-left}}'

    -- Header row: blank lead cell + one cell per NPC name
    local headerCells = { "!" }   -- blank corner
    for _, npc in ipairs(slice) do
        headerCells[#headerCells + 1] = "! " .. npcLink(npc.name, isTrust)
    end
    lines[#lines + 1] = table.concat(headerCells, "\n")

    -- Image row
    lines[#lines + 1] = "|-"
    local imgCells = { "|" }   -- blank lead cell
    for _, npc in ipairs(slice) do
        local fileLink
        if npc.image ~= "" then
            fileLink = "[[File:" .. npc.image .. "|" .. npc.imgwidth .. "]]"
        else
            fileLink = ""
        end
        imgCells[#imgCells + 1] = "| " .. fileLink
    end
    lines[#lines + 1] = table.concat(imgCells, "\n")

    -- Class row
    lines[#lines + 1] = "|-"
    local classCells = { "| '''Class'''" }
    for _, npc in ipairs(slice) do
        classCells[#classCells + 1] = "| " .. npc.class
    end
    lines[#lines + 1] = table.concat(classCells, "\n")

    -- Role row
    lines[#lines + 1] = "|-"
    local roleCells = { "| '''Role'''" }
    for _, npc in ipairs(slice) do
        roleCells[#roleCells + 1] = "| " .. npc.role
    end
    lines[#lines + 1] = table.concat(roleCells, "\n")

    -- Table close
    lines[#lines + 1] = "|}"

    return table.concat(lines, "\n")
end

-------------------------------------------------------------------------------
-- Main entry point.
-------------------------------------------------------------------------------
function p.main(frame)
    -- Merge parent template args with direct invocation args
    local args = {}
    for k, v in pairs(frame.args) do
        args[k] = mw.text.trim(v)
    end
    -- Also pull in parent frame args so the module works when called from a
    -- template that received the parameters.
    if frame:getParent() then
        for k, v in pairs(frame:getParent().args) do
            if args[k] == nil then
                args[k] = mw.text.trim(v)
            end
        end
    end

    -- Configuration
    local maxCols  = tonumber(args["maxcols"]) or DEFAULT_MAX_COLS
    local imgWidth = args["imgwidth"] or DEFAULT_IMG_WIDTH
    local isTrust  = args["is-trust"] and args["is-trust"] ~= ""

    -- Collect all NPCs (global imgwidth used as per-NPC fallback)
    local npcs = collectNPCs(args, imgWidth)

    if #npcs == 0 then
        return "<!-- Module:CompanionTable: no NPC parameters found -->"
    end

    -- Split into chunks of maxCols and render each as a separate table
    local tables = {}
    local i = 1
    while i <= #npcs do
        local slice = {}
        for j = i, math.min(i + maxCols - 1, #npcs) do
            slice[#slice + 1] = npcs[j]
        end
        tables[#tables + 1] = renderTable(slice, isTrust)
        i = i + maxCols
    end

    -- Join tables with a blank line between them so they render as separate blocks
    return frame:preprocess(table.concat(tables, "\n"))
end

return p