From Arms of God Wiki

bot: operator iteration 2026-06-10d (hover tooltips / filter chips + DPS sort / damage-type hubs / stat reverse-lookup pages)
bot: render-time derivation refactor (phase 2)
Line 1: Line 1:
-- Module:CrossRef — renders the `cross_refs` field as labelled bullet lists.
-- Module:CrossRef — render-time cross-reference computation (Arms of God).
-- Public API: CrossRef.renderAll(rec, labelOrder).
-- Computes at render, from the source Data:<Category>.json pages:
-- API contract:
--  * base <-> '<X> Plus' variant links within a category (id match)
--  rec.cross_refs = { ["<label>"] = { {slug, name, icon}, ... }, ... }
--  * tag membership sections for Tag pages (via Module:TagIndex)
--  labelOrder    = list of label strings; missing labels are silently skipped.
-- Records carry NO baked cross_refs; everything here recomputes on purge.
-- Maintainer don'ts: never recompute a slug here; ref.slug is set by flatten.py
--
-- from the plan's slug_rule. This module renders [[ref.slug|ref.name]] verbatim.
-- Entry point (wired by the per-category shims):
 
--   {{#invoke:<Cat>|crossRefs|id=<slug-or-id>}}
local Core = require('Module:Core')
local p = {}
local p = {}


-- Render a single labelled section: '== <label> ==' followed by a
-- Render a single labelled section: '== <label> ==' + icon-link bullets.
-- bullet list of '* [[<slug>|<name>]]' lines. Returns '' on empty
-- so the caller can concat without an extra newline check.
function p.renderSection(label, refs)
function p.renderSection(label, refs)
   if not refs or #refs == 0 then return '' end
   if not refs or #refs == 0 then return '' end
   local out = {'', '== ' .. label .. ' =='}
   local out = {'', '== ' .. label .. ' =='}
   for _, ref in ipairs(refs) do
   for _, rec in ipairs(refs) do
     local slug = ref.slug or ref.name or '?'
     out[#out + 1] = '* ' .. Core.iconLink(rec, 24)
    local name = ref.name or ref.slug or '?'
    local icon = ref.icon
    local tip = '<span class="wm-tip" data-tip-title="' .. slug .. '">'
    if icon and icon ~= '' then
      table.insert(out, '* ' .. tip .. '[[File:' .. icon .. '|24px|link=' .. slug
        .. ']] [[' .. slug .. '|' .. name .. ']]</span>')
    else
      table.insert(out, '* ' .. tip .. '[[' .. slug .. '|' .. name .. ']]</span>')
    end
   end
   end
   return table.concat(out, '\n')
   return table.concat(out, '\n')
end
end


-- Render every section listed in `order` (a list of label strings)
-- Variant pairing: RAW id first ('<X> Plus' / '<x>-plus' -> base), then
-- for which rec.cross_refs has a non-empty entry. Sections appear in
-- display name ('<Name> Plus'). Id wins because Upgrades' display-name
-- the order the caller declared; unknown labels are skipped silently.
-- split means base and Plus can show unrelated display names (e.g. id
function p.renderAll(rec, order)
-- 'Shield Transform' displays 'Lifesteal Chamber'); the name fallback
  local refs_map = (rec and rec.cross_refs) or {}
-- covers Blessings, whose kebab-case ids ('blessed-bounty-plus') pair by
   if not order or #order == 0 then
-- either rule.
    -- Best-effort fallback: render in iteration order (stable enough
local VARIANT_CATS = {Blessings = true, Upgrades = true}
    -- for spot checks; production callers pass an explicit order).
 
     local out = {}
local _byName = {}
     for label, refs in pairs(refs_map) do
local function byName(cat)
      local section = p.renderSection(label, refs)
   if _byName[cat] == nil then
       if section ~= '' then table.insert(out, section) end
     local m = {}
     for _, r in ipairs(Core.load(cat)) do
       if r.name then m[r.name] = r end
     end
     end
     return table.concat(out, '\n')
     _byName[cat] = m
   end
   end
  return _byName[cat]
end
local function variantSections(cat, rec)
  if not VARIANT_CATS[cat] then return '' end
  local ids, names = Core.byId(cat), byName(cat)
  local id, name = rec.id or '', rec.name or ''
   local out = {}
   local out = {}
   for _, label in ipairs(order) do
   local base = nil
     local section = p.renderSection(label, refs_map[label])
  if id:sub(-5) == ' Plus' then base = ids[id:sub(1, -6)] end
     if section ~= '' then table.insert(out, section) end
  if not base and id:sub(-5) == '-plus' then base = ids[id:sub(1, -6)] end
  if not base and name:sub(-5) == ' Plus' then base = names[name:sub(1, -6)] end
  if base and base ~= rec then
     out[#out + 1] = p.renderSection('Base variant', {base})
  elseif not (id:sub(-5) == ' Plus' or id:sub(-5) == '-plus'
              or name:sub(-5) == ' Plus') then
    local plus = ids[id .. ' Plus'] or ids[id .. '-plus'] or names[name .. ' Plus']
     if plus and plus ~= rec then
      out[#out + 1] = p.renderSection('Upgraded variant', {plus})
    end
   end
   end
   return table.concat(out, '\n')
   return table.concat(out, '\n')
end
-- Full computed cross-ref block for one record (no frame needed).
function p.compute(cat, rec)
  if cat == 'Tags' then
    local TagIndex = require('Module:TagIndex')
    return TagIndex.sectionsFor(rec.id or rec.name)
  end
  return variantSections(cat, rec)
end
-- Frame entry used by the per-category shims.
function p.entry(cat, frame)
  local rec, missing = Core.resolveRec(cat, frame)
  if missing then return missing end
  if not rec then return '' end
  local body = p.compute(cat, rec)
  if body == '' then return '' end
  return mw.getCurrentFrame():preprocess(body)
end
end


return p
return p

Revision as of 08:30, 10 June 2026

Shared helper that renders cross-reference link lists between categories.

Library module shipped by the publishing bot; shared across categories. Bot-published — edits are overwritten on re-publish.


-- Module:CrossRef — render-time cross-reference computation (Arms of God).
-- Computes at render, from the source Data:<Category>.json pages:
--   * base <-> '<X> Plus' variant links within a category (id match)
--   * tag membership sections for Tag pages (via Module:TagIndex)
-- Records carry NO baked cross_refs; everything here recomputes on purge.
--
-- Entry point (wired by the per-category shims):
--   {{#invoke:<Cat>|crossRefs|id=<slug-or-id>}}
local Core = require('Module:Core')
local p = {}

-- Render a single labelled section: '== <label> ==' + icon-link bullets.
function p.renderSection(label, refs)
  if not refs or #refs == 0 then return '' end
  local out = {'', '== ' .. label .. ' =='}
  for _, rec in ipairs(refs) do
    out[#out + 1] = '* ' .. Core.iconLink(rec, 24)
  end
  return table.concat(out, '\n')
end

-- Variant pairing: RAW id first ('<X> Plus' / '<x>-plus' -> base), then
-- display name ('<Name> Plus'). Id wins because Upgrades' display-name
-- split means base and Plus can show unrelated display names (e.g. id
-- 'Shield Transform' displays 'Lifesteal Chamber'); the name fallback
-- covers Blessings, whose kebab-case ids ('blessed-bounty-plus') pair by
-- either rule.
local VARIANT_CATS = {Blessings = true, Upgrades = true}

local _byName = {}
local function byName(cat)
  if _byName[cat] == nil then
    local m = {}
    for _, r in ipairs(Core.load(cat)) do
      if r.name then m[r.name] = r end
    end
    _byName[cat] = m
  end
  return _byName[cat]
end

local function variantSections(cat, rec)
  if not VARIANT_CATS[cat] then return '' end
  local ids, names = Core.byId(cat), byName(cat)
  local id, name = rec.id or '', rec.name or ''
  local out = {}
  local base = nil
  if id:sub(-5) == ' Plus' then base = ids[id:sub(1, -6)] end
  if not base and id:sub(-5) == '-plus' then base = ids[id:sub(1, -6)] end
  if not base and name:sub(-5) == ' Plus' then base = names[name:sub(1, -6)] end
  if base and base ~= rec then
    out[#out + 1] = p.renderSection('Base variant', {base})
  elseif not (id:sub(-5) == ' Plus' or id:sub(-5) == '-plus'
              or name:sub(-5) == ' Plus') then
    local plus = ids[id .. ' Plus'] or ids[id .. '-plus'] or names[name .. ' Plus']
    if plus and plus ~= rec then
      out[#out + 1] = p.renderSection('Upgraded variant', {plus})
    end
  end
  return table.concat(out, '\n')
end

-- Full computed cross-ref block for one record (no frame needed).
function p.compute(cat, rec)
  if cat == 'Tags' then
    local TagIndex = require('Module:TagIndex')
    return TagIndex.sectionsFor(rec.id or rec.name)
  end
  return variantSections(cat, rec)
end

-- Frame entry used by the per-category shims.
function p.entry(cat, frame)
  local rec, missing = Core.resolveRec(cat, frame)
  if missing then return missing end
  if not rec then return '' end
  local body = p.compute(cat, rec)
  if body == '' then return '' end
  return mw.getCurrentFrame():preprocess(body)
end

return p