Module:AbilityTable: Difference between revisions

Vergir (talk | contribs)
Merge analyze_charges into charges_cells; move human notes to framework level with extensible table; Notes column auto-injected when notes exist (with help from vergir-bot LLM)
Vergir (talk | contribs)
Restructure globals order; Notes declared in charges headers; add CSS classes to table elements (with help from vergir-bot LLM)
Line 12: Line 12:
--    determine whether an ability appears in the table.
--    determine whether an ability appears in the table.
--
--
-- 2. Optionally add a matching entry to extra_columns to show
-- 2. Add a matching entry to extra_columns:
--    additional data columns beyond Hero and Ability.
--      - headers:    ordered list of column header strings.
--    Copy the "charges" block as a template:
--                    Include "Notes" if this stat generates notes.
--      - headers:    ordered list of column header strings
--                    (do not include "Notes" — it is added automatically)
--      - get_cells:  function(ability) -> { ["Column Name"] = value }
--      - get_cells:  function(ability) -> { ["Column Name"] = value }
--                    The ability is guaranteed to match the stat.
--                    The ability is guaranteed to match the stat.
--                    Return nil or omit a key to show "—" in that cell.
--                    Omit a key or return nil to show "—" in that cell.
--    If no extra_columns entry is added, only Hero and Ability
--    If no extra_columns entry is added, only Hero and Ability
--    columns are shown (plus Notes if human notes exist).
--    columns are shown (plus Notes if human notes exist for the stat).
--
--
-- 3. Optionally add entries to column_suffix to append a unit
-- 3. Optionally add human-authored notes to human_notes:
--   string (e.g. "s", "%") after values in specific columns.
--     human_notes["stat"]["Ability Name"] = "Note text."
--    Notes are appended to any renderer-generated note automatically.
--
--
-- 4. Optionally add human-authored notes to human_notes:
-- 4. Optionally add entries to column_suffix to append a unit
--     human_notes["stat"]["Ability Name"] = "Note text."
--   string (e.g. "s", "%") after cell values in specific columns.
--    Notes are appended as a Notes column automatically.
-- ============================================================
-- ============================================================


Line 34: Line 32:
local GameData = require("Module:GameData")
local GameData = require("Module:GameData")


-- Maps lowercase friendly stat names to internal AbilityData property keys.
-- ============================================================
-- stat_to_props
-- Maps lowercase friendly stat names to internal AbilityData
-- property keys used to filter which abilities appear.
-- ============================================================
local stat_to_props = {
local stat_to_props = {
     ["charges"] = { "AbilityCharges" },
     ["charges"] = { "AbilityCharges" },
}
}


-- Maps column header names to their display unit suffix.
-- ============================================================
local column_suffix = {
-- human_notes
    ["Time Between Charges"] = "s",
-- Editor-authored notes appended to the Notes column.
}
-- Keyed first by stat name, then by ability display Name.
 
-- ============================================================
-- Human-authored notes, appended to the Notes column.
-- Keyed by stat name, then by ability display Name.
local human_notes = {
local human_notes = {
     ["charges"] = {
     ["charges"] = {
Line 78: Line 78:
-- Per-stat cell renderers
-- Per-stat cell renderers
-- Each function receives a guaranteed-matching ability record
-- Each function receives a guaranteed-matching ability record
-- and returns { ["Column Name"] = value } for extra columns.
-- and returns { ["Column Name"] = value }.
-- Human notes and the Notes column are handled by the framework.
-- Human notes are appended by the framework, not here.
-- ============================================================
-- ============================================================


Line 133: Line 133:
end
end


-- ============================================================
-- extra_columns
-- Declares headers and cell renderer for each stat.
-- ============================================================
local extra_columns = {
local extra_columns = {
     ["charges"] = {
     ["charges"] = {
         headers  = { "Charges", "Time Between Charges" },
         headers  = { "Charges", "Time Between Charges", "Notes" },
         get_cells = charges_cells,
         get_cells = charges_cells,
     },
     },
}
-- ============================================================
-- column_suffix
-- Unit strings appended to non-nil cell values by column name.
-- ============================================================
local column_suffix = {
    ["Time Between Charges"] = "s",
}
}


Line 195: Line 207:
     end
     end


     local lookup     = build_hero_lookup()
     local lookup     = build_hero_lookup()
     local spec       = extra_columns[stat]
     local spec       = extra_columns[stat]
     local headers     = spec and spec.headers or {}
     local headers   = spec and spec.headers or {}
     local get_cells   = spec and spec.get_cells or function() return {} end
     local get_cells = spec and spec.get_cells or function() return {} end
     local stat_notes = human_notes[stat]
     local stat_notes = human_notes[stat]


     local abilities = GameData.get_entities(GameData.Dataset.ABILITIES, internal_keys)
     local abilities = GameData.get_entities(GameData.Dataset.ABILITIES, internal_keys)
Line 226: Line 238:
     end)
     end)


     -- Determine whether a Notes column is needed:
     -- Determine final header list. "Notes" is included if declared in headers
     -- either the stat renderer produces notes, or human notes exist for this stat.
     -- (meaning the renderer may produce notes) or if human notes exist for
     local needs_notes = stat_notes ~= nil
    -- this stat. Either way it appears exactly once at the end.
     if not needs_notes then
     local has_notes_header = false
        for _, ability in ipairs(filtered) do
    local final_headers = {}
            local extra = get_cells(ability)
     for _, h in ipairs(headers) do
            if extra["Notes"] then
        if h == "Notes" then
                needs_notes = true
            has_notes_header = true
                break
        else
             end
             table.insert(final_headers, h)
         end
         end
     end
     end
 
     local needs_notes = has_notes_header or stat_notes ~= nil
    -- Build final header list: declared headers + Notes if needed
     if needs_notes then
     local final_headers = {}
        table.insert(final_headers, "Notes")
    for _, h in ipairs(headers) do table.insert(final_headers, h) end
    end
     if needs_notes then table.insert(final_headers, "Notes") end


     -- Render wikitable
     -- Render wikitable
     local out = {}
     local out = {}
     local header_row = "! Hero !! Ability"
    table.insert(out, '{| class="wikitable sortable ability-table"')
     if #final_headers > 0 then
 
         header_row = header_row .. " !! " .. table.concat(final_headers, " !! ")
     local header_cells = '! class="ability-table-col-hero" | Hero'
                      .. ' !! class="ability-table-col-ability" | Ability'
     for _, h in ipairs(final_headers) do
         local cls = "ability-table-col-" .. h:lower():gsub("%s+", "-")
        header_cells = header_cells .. ' !! class="' .. cls .. '" | ' .. h
     end
     end
 
     table.insert(out, header_cells)
     table.insert(out, '{| class="wikitable sortable"')
    table.insert(out, header_row)


     for _, ability in ipairs(filtered) do
     for _, ability in ipairs(filtered) do
Line 266: Line 279:
         }
         }


        local cells  = { hero_cell, ability_cell }
         local extra = get_cells(ability)
         local extra = get_cells(ability)


         -- Append human note to any renderer-generated note
         -- Append human note to any renderer-generated note
Line 275: Line 287:
         end
         end


        local row_cells = '| class="ability-table-cell-hero" | '    .. hero_cell
                      .. ' || class="ability-table-cell-ability" | ' .. ability_cell
         for _, header in ipairs(final_headers) do
         for _, header in ipairs(final_headers) do
             local value = extra[header]
             local value = extra[header]
            local cls    = "ability-table-cell-" .. header:lower():gsub("%s+", "-")
            local content
             if value ~= nil then
             if value ~= nil then
                 local suffix = column_suffix[header] or ""
                 local suffix = column_suffix[header] or ""
                 table.insert(cells, value .. suffix)
                 content = value .. suffix
             else
             else
                 table.insert(cells, "—")
                 content = "—"
             end
             end
            row_cells = row_cells .. ' || class="' .. cls .. '" | ' .. content
         end
         end


         table.insert(out, "|-")
         table.insert(out, "|-")
         table.insert(out, "| " .. table.concat(cells, " || "))
         table.insert(out, row_cells)
     end
     end