<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://armsofgod.wiki.spellsandguns.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ta1ha</id>
	<title>Official Arms of God Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://armsofgod.wiki.spellsandguns.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ta1ha"/>
	<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/Special:Contributions/Ta1ha"/>
	<updated>2026-07-07T23:41:08Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Arms_of_God_Wiki&amp;diff=2129</id>
		<title>Arms of God Wiki</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Arms_of_God_Wiki&amp;diff=2129"/>
		<updated>2026-06-24T17:42:37Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: Official Arms of God Wiki&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; align-items:flex-start; justify-content:center; top; flex-wrap:wrap;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;text-align:center; width:100%; max-width:1100px; display:inline-block; padding:30px;&amp;quot;&amp;gt;&lt;br /&gt;
		[[File:Arms-of-god-main.png|link=https://darkjay.com/armsofgod/|x400px]]&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:center; font-weight:bold;&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-size:32pt;&amp;quot;&amp;gt;Welcome to &#039;&#039;&#039;Official Arms of God Wiki&#039;&#039;&#039;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-size:14pt;&amp;quot;&amp;gt;The comprehensive Arms of God Wiki is written and maintained by the players. [[Special:ActiveUsers|{{NUMBEROFUSERS}} people]] are currently maintaining [[Special:Statistics|{{NUMBEROFPAGES}} pages ({{NUMBEROFARTICLES}} articles)]] on this Wiki so far! Please feel free to contribute by creating new articles or expanding existing ones. &amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&amp;quot;color: green; font-size: 20px;&amp;quot;&amp;gt; &amp;lt;b&amp;gt;New featured articles: [[Weapons]] and [[Blessings]]&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;div&amp;gt;&lt;br /&gt;
			&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt;Content&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Story]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Updates]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Game modes]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Achievements_ForbiddenKnowledge.jpg|link=Achievements|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Achievements]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Community]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; flex-wrap:width:100%; nowrap; justify-content:space-around; border:1px solid #DD952A;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Heroes_primaris.png|link=Characters|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Characters]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Weapons_Cutter.png|link=Weapons|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Weapons]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Blessings_distant_judgment.png|link=Blessings|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Blessings]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Passives_damage_i.png|link=Passives|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Passives]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Upgrades_Incinerator_Coil.png|link=Upgrades|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Upgrades]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Cross_DefaultUpgrade.png|link=Crux|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Crux]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; flex-wrap:width:100%; nowrap; justify-content:space-around; border:1px solid #DD952A;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Stat_Damage.png|link=Stats|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Stats]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Raziel.png|link=Enemies|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Enemies]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Prayers]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Codex]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;width:100%; max-width:600px; display:inline-block; padding:30px;&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:nowrap; justify-content:space-around;&amp;quot;&amp;gt;&lt;br /&gt;
			[[File:DarkJay_Logo_Color.png|link=https://darkjay.com/|x200px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-bottom:10px;&amp;quot;&amp;gt;About Arms of God&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:left; margin:9px; font-size:14pt; &amp;quot;&amp;gt;Gear up for a battle through hell in this roguelite Bullet Heaven Arena Autoshooter with Doom-inspired gore vibes, metal music, and a satisfying combat feel. Wield five weapons at once, upgrade and merge them to forge indestructible builds, and bring divine justice to a world on the brink of chaos. &amp;lt;br&amp;gt; [https://store.steampowered.com/app/3100310/Arms_of_God/ Buy on Steam.] &amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt; Arms of God Community&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; justify-content:space-evenly; align-items:center;&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;p style=&amp;quot;text-align:center; font-size:14pt;&amp;quot;&amp;gt;Have strategy questions or want to talk to fellow Arms of God players? Check out the official subreddit and Discord server.&amp;lt;/p&amp;gt;&lt;br /&gt;
			[[File:Reddit_logo.png|link=https://www.reddit.com/r/armsofgod/|x90px]]&lt;br /&gt;
			[[File:Discord_logo.png|link=https://discord.com/invite/QeUqjGFCYb|x90px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt;Latest Trailer&amp;lt;/p&amp;gt;&lt;br /&gt;
		{{#evu:https://www.youtube.com/watch?v=jKuVy4DtbXU|alignment=center}}&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:40px;&amp;quot;&amp;gt;Official Links&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:20px;&amp;quot;&amp;gt;&lt;br /&gt;
			[[File:Steam_Logo.png|link=https://store.steampowered.com/app/3100310/Arms_of_God/|x90px]]&lt;br /&gt;
			[[File:Youtube_Logo.png|link=https://www.youtube.com/@darkjaystudio|x120px]]&lt;br /&gt;
			[[File:Twitter_Logo.png|link=https://x.com/ArmsOfGodGame|x120px]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2128</id>
		<title>Module:Compare</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2128"/>
		<updated>2026-06-11T02:49:01Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: docs: clarify sentinel scoping comment&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Compare — sortable side-by-side comparison tables.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Builds the big comparison tables (used on the Weapons index, the&lt;br /&gt;
--   Enemies index, and the Weapon comparison / Hero comparison pages;&lt;br /&gt;
--   &#039;Enemy comparison&#039; is a redirect to Enemies since 2026-06-10f),&lt;br /&gt;
--   including the COMPUTED columns that are not stored anywhere:&lt;br /&gt;
--   * Weapon DPS = (Damage + Holy + Fire + Electric) x Attack Speed,&lt;br /&gt;
--     rounded to 1 decimal.&lt;br /&gt;
--   * Enemy wave-10 projections = base + per-wave x 9.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Melee}}   melee weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Ranged}}  ranged weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Enemies}}         base + per-wave + wave-10&lt;br /&gt;
--   {{#invoke:Compare|render|Characters}}      hero stat modifiers&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Weapons.json, Data:Enemies.json, Data:Characters.json&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * Every table opens with a Thumbnail column (icon only, header marked&lt;br /&gt;
--     class=&amp;quot;unsortable&amp;quot;) followed by a sortable linked Name column.&lt;br /&gt;
--   * Stat sentinels apply ONLY to the weapon Pierce/Bounce columns&lt;br /&gt;
--     (numcell): 1000 -&amp;gt; infinity sign (unlimited), &amp;lt;= -1000 -&amp;gt; em-dash.&lt;br /&gt;
--     Every other column (incl. Range, enemy HP/Wave, hero stats) uses&lt;br /&gt;
--     plaincell and shows the real number, however large.&lt;br /&gt;
--   * Each row carries data-element / data-class / data-tier / data-type&lt;br /&gt;
--     attributes — MediaWiki:Common.js uses them for the filter chips&lt;br /&gt;
--     above the tables. Removing them breaks the chip filtering.&lt;br /&gt;
--   * To add a column, add a header in the relevant *Rows() builder and&lt;br /&gt;
--     a matching cell in the same position.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL_STYLE = &#039;font-size:0.92em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&#039;&lt;br /&gt;
local TH_STYLE  = &#039;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&#039;&lt;br /&gt;
&lt;br /&gt;
-- Colored element swatch for column headers. When the damage-type hub&lt;br /&gt;
-- page exists (existence-checked via Core.statPageTarget) the label links&lt;br /&gt;
-- to it; an inner span keeps the element color inside the anchor.&lt;br /&gt;
local function swatch(label, tok, hexv, statName)&lt;br /&gt;
  local inner = label&lt;br /&gt;
  local target = statName and Core.statPageTarget(statName)&lt;br /&gt;
  if target then&lt;br /&gt;
    inner = &#039;[[&#039; .. target .. &#039;|&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039;&lt;br /&gt;
      .. hexv .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. label .. &#039;&amp;lt;/span&amp;gt;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039; .. hexv&lt;br /&gt;
    .. &#039;);font-weight:bold;white-space:nowrap;&amp;quot;&amp;gt;&amp;amp;#9679; &#039; .. inner .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Abbreviated column headers -&amp;gt; the stat key whose page they should link&lt;br /&gt;
-- to. Full-name headers resolve via Core.statPageTarget directly.&lt;br /&gt;
local HDR_STAT = {&lt;br /&gt;
  [&#039;Atk Speed&#039;] = &#039;Attack Speed&#039;,&lt;br /&gt;
  [&#039;Crit&#039;] = &#039;Critical Chance&#039;,&lt;br /&gt;
  [&#039;Proj.&#039;] = &#039;Projectiles&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Link a plain-text column header to its stat reference page when one&lt;br /&gt;
-- exists; non-stat headers (and already-HTML headers like the swatches)&lt;br /&gt;
-- pass through untouched. Page-less stats stay plain — never a redlink.&lt;br /&gt;
local function headerLink(h)&lt;br /&gt;
  if h:find(&#039;[&amp;lt;%[]&#039;) then return h end&lt;br /&gt;
  local target = Core.statPageTarget(HDR_STAT[h] or h)&lt;br /&gt;
  if not target then return h end&lt;br /&gt;
  if target == h then return &#039;[[&#039; .. h .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. h .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coerce a raw stat value to {display, sortval}: &#039;15%&#039; keeps the %&lt;br /&gt;
-- sign for display but sorts by the number 15.&lt;br /&gt;
local function num(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;∞&#039;, 1e9 end&lt;br /&gt;
    if v &amp;lt;= -1000 then return &#039;—&#039;, -1 end&lt;br /&gt;
    return Core.fmtNum(v), v&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numcell(v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Sentinel-FREE number coercion. The &amp;gt;=1000 / &amp;lt;=-1000 sentinels in num()&lt;br /&gt;
-- are a WEAPON convention (Pierce 1000 = unlimited, Bounce -1000 = hidden);&lt;br /&gt;
-- enemy/character stats use real large numbers (e.g. a boss&#039;s HP/Wave of&lt;br /&gt;
-- 5000), so those tables must format plainly or 5000 wrongly renders as inf.&lt;br /&gt;
local function plain(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then return Core.fmtNum(v), v end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function plaincell(v)&lt;br /&gt;
  local t, s = plain(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DMG = {&lt;br /&gt;
  Holy = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  Fire = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  Electric = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chipcell(dmg, v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  if s == nil or s &amp;lt;= 0 or t == &#039;—&#039; then return {t = &#039;—&#039;, s = 0} end&lt;br /&gt;
  local c = DMG[dmg]&lt;br /&gt;
  return {t = &#039;&amp;lt;span class=&amp;quot;&#039; .. c[3] .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. c[1] .. &#039;, &#039;&lt;br /&gt;
    .. c[2] .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Header marker for the icon-only Thumbnail column: the renderer emits&lt;br /&gt;
-- class=&amp;quot;unsortable&amp;quot; on this th so the sort arrows skip it.&lt;br /&gt;
local THUMB = {&#039;Thumbnail&#039;, unsortable = true}&lt;br /&gt;
&lt;br /&gt;
-- Icon-only Thumbnail cell (links to the entity page; em-dash when the&lt;br /&gt;
-- record has no icon). 48px matches the category-index thumbnail size.&lt;br /&gt;
local function iconcell(rec)&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
    return {t = &#039;[[File:&#039; .. rec.icon .. &#039;|48px|link=&#039; .. slug&lt;br /&gt;
      .. &#039;|alt=&#039; .. (rec.name or slug) .. &#039;]]&#039;}&lt;br /&gt;
  end&lt;br /&gt;
  return {t = &#039;—&#039;}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Linked-name cell, sortable by lowercased name, wrapped in .wm-tip&lt;br /&gt;
-- (Common.js hover-infobox popup).&lt;br /&gt;
local function namecell(rec)&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local t = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  return {t = t, s = mw.ustring.lower(name)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat(rec, k)&lt;br /&gt;
  return (rec.stats or {})[k]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function nstat(rec, k)&lt;br /&gt;
  return Core.numStat(stat(rec, k))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalDPS(rec)&lt;br /&gt;
  local s = rec.stats or {}&lt;br /&gt;
  local atk = Core.numStat(s[&#039;Attack Speed&#039;])&lt;br /&gt;
  if atk == nil then return nil end&lt;br /&gt;
  local total = Core.numStat(s[&#039;Damage&#039;]) or 0&lt;br /&gt;
  for _, k in ipairs({&#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;}) do&lt;br /&gt;
    local v = Core.numStat(s[k])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then total = total + v end&lt;br /&gt;
  end&lt;br /&gt;
  return Core.round1(total * atk)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wave10(rec, base, per)&lt;br /&gt;
  local b, pv = nstat(rec, base), nstat(rec, per)&lt;br /&gt;
  if b == nil or pv == nil then return nil end&lt;br /&gt;
  return Core.round1(b + pv * 9)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- dataset builders ----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function weaponRows(class)&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Tier&#039;, &#039;Price&#039;, &#039;Damage&#039;,&lt;br /&gt;
    swatch(&#039;Holy&#039;, &#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;Holy Damage&#039;),&lt;br /&gt;
    swatch(&#039;Fire&#039;, &#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;Fire Damage&#039;),&lt;br /&gt;
    swatch(&#039;Electric&#039;, &#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;Electric Damage&#039;),&lt;br /&gt;
    &#039;DPS&#039;, &#039;Atk Speed&#039;, &#039;Crit&#039;, &#039;Range&#039;, &#039;Pierce&#039;, &#039;Proj.&#039;, &#039;Bounce&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    if Core.weaponClass(rec) == class then&lt;br /&gt;
      -- Only Pierce/Bounce use the +/-1000 on/off sentinel (numcell);&lt;br /&gt;
      -- every other column is a real number (Range 1400 is a real range,&lt;br /&gt;
      -- not &amp;quot;unlimited&amp;quot;), so use the sentinel-free plaincell.&lt;br /&gt;
      local cells = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        plaincell(rec.tier),&lt;br /&gt;
        plaincell(rec.price),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Holy&#039;, stat(rec, &#039;Holy Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Fire&#039;, stat(rec, &#039;Fire Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Electric&#039;, stat(rec, &#039;Electric Damage&#039;)),&lt;br /&gt;
        plaincell(totalDPS(rec)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Pierce&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Projectiles&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Bounce&#039;)),&lt;br /&gt;
      }&lt;br /&gt;
      local elements = {}&lt;br /&gt;
      for _, e in ipairs({{&#039;holy&#039;, &#039;Holy Damage&#039;}, {&#039;fire&#039;, &#039;Fire Damage&#039;},&lt;br /&gt;
                          {&#039;electric&#039;, &#039;Electric Damage&#039;}}) do&lt;br /&gt;
        local v = nstat(rec, e[2])&lt;br /&gt;
        if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then elements[#elements + 1] = e[1] end&lt;br /&gt;
      end&lt;br /&gt;
      local attrs = &#039; data-element=&amp;quot;&#039; .. (#elements &amp;gt; 0 and table.concat(elements, &#039; &#039;) or &#039;none&#039;)&lt;br /&gt;
        .. &#039;&amp;quot; data-class=&amp;quot;&#039; .. class:lower()&lt;br /&gt;
        .. &#039;&amp;quot; data-tier=&amp;quot;&#039; .. tostring(rec.tier or &#039;&#039;) .. &#039;&amp;quot;&#039;&lt;br /&gt;
      rows[#rows + 1] = {a = attrs, c = cells}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  -- DPS descending (cell 9 after the Thumbnail/Name split; stable:&lt;br /&gt;
  -- name asc tiebreak via the Name cell sortkey, cell 2).&lt;br /&gt;
  table.sort(rows, function(x, y)&lt;br /&gt;
    local dx, dy = x.c[9].s or 0, y.c[9].s or 0&lt;br /&gt;
    if dx ~= dy then return dx &amp;gt; dy end&lt;br /&gt;
    return (x.c[2].s or &#039;&#039;) &amp;lt; (y.c[2].s or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function enemyRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health&#039;, &#039;HP/Wave&#039;, &#039;HP @ W10&#039;, &#039;Damage&#039;,&lt;br /&gt;
                   &#039;Dmg/Wave&#039;, &#039;Dmg @ W10&#039;, &#039;Speed&#039;, &#039;Resources&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Enemies&#039;)) do&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, k in ipairs(rec.tags or {}) do keys[#keys + 1] = k:lower() end&lt;br /&gt;
    rows[#rows + 1] = {&lt;br /&gt;
      a = &#039; data-type=&amp;quot;&#039; .. (#keys &amp;gt; 0 and table.concat(keys, &#039; &#039;) or &#039;other&#039;) .. &#039;&amp;quot;&#039;,&lt;br /&gt;
      c = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        plaincell(stat(rec, &#039;Health&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;HP per Wave&#039;)),&lt;br /&gt;
        plaincell(wave10(rec, &#039;Health&#039;, &#039;HP per Wave&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        plaincell(wave10(rec, &#039;Damage&#039;, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Resources&#039;)),&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function characterRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health Points&#039;, &#039;Damage&#039;, &#039;Critical Chance&#039;,&lt;br /&gt;
                   &#039;Attack Speed&#039;, &#039;Range&#039;, &#039;Speed&#039;, &#039;Armor&#039;, &#039;Dodge&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Characters&#039;)) do&lt;br /&gt;
    rows[#rows + 1] = {a = &#039;&#039;, c = {&lt;br /&gt;
      iconcell(rec),&lt;br /&gt;
      namecell(rec),&lt;br /&gt;
      plaincell(stat(rec, &#039;Health Points&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Armor&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Dodge&#039;)),&lt;br /&gt;
    }}&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DATASETS = {&lt;br /&gt;
  Weapons_Melee = function() return weaponRows(&#039;Melee&#039;) end,&lt;br /&gt;
  Weapons_Ranged = function() return weaponRows(&#039;Ranged&#039;) end,&lt;br /&gt;
  Enemies = enemyRows,&lt;br /&gt;
  Characters = characterRows,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- renderer --------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function cell(c)&lt;br /&gt;
  local t = tostring(c.t or &#039;—&#039;)&lt;br /&gt;
  if c.s ~= nil then&lt;br /&gt;
    local s = c.s&lt;br /&gt;
    if type(s) == &#039;number&#039; then s = Core.fmtNum(s) end&lt;br /&gt;
    return &#039;| data-sort-value=&amp;quot;&#039; .. tostring(s) .. &#039;&amp;quot; | &#039; .. t&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;| &#039; .. t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local ds = frame.args[1] or frame.args.cat&lt;br /&gt;
  if not ds or ds == &#039;&#039; then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: missing dataset arg&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  ds = ds:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  local build = DATASETS[ds]&lt;br /&gt;
  if not build then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: unknown dataset &#039;&lt;br /&gt;
      .. mw.text.nowiki(ds) .. &#039;&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local headers, rows = build()&lt;br /&gt;
  local out = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;&#039; .. TBL_STYLE .. &#039;&amp;quot;&#039; }&lt;br /&gt;
  out[#out+1] = &#039;|-&#039;&lt;br /&gt;
  for _, h in ipairs(headers) do&lt;br /&gt;
    local label, attrs = h, &#039;&#039;&lt;br /&gt;
    if type(h) == &#039;table&#039; then&lt;br /&gt;
      label = h[1]&lt;br /&gt;
      if h.unsortable then attrs = &#039; class=&amp;quot;unsortable&amp;quot;&#039; end&lt;br /&gt;
    end&lt;br /&gt;
    out[#out+1] = &#039;!&#039; .. attrs .. &#039; style=&amp;quot;&#039; .. TH_STYLE .. &#039;&amp;quot; | &#039;&lt;br /&gt;
      .. headerLink(tostring(label))&lt;br /&gt;
  end&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039; .. tostring(row.a or &#039;&#039;)&lt;br /&gt;
    for _, c in ipairs(row.c) do&lt;br /&gt;
      out[#out+1] = cell(c)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2127</id>
		<title>Module:Compare</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2127"/>
		<updated>2026-06-11T02:47:04Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: fix: scope the +/-1000 Unlimited/disabled sentinel to Pierce/Bounce only (weapon Range 1400, hero/enemy large stats are real numbers)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Compare — sortable side-by-side comparison tables.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Builds the big comparison tables (used on the Weapons index, the&lt;br /&gt;
--   Enemies index, and the Weapon comparison / Hero comparison pages;&lt;br /&gt;
--   &#039;Enemy comparison&#039; is a redirect to Enemies since 2026-06-10f),&lt;br /&gt;
--   including the COMPUTED columns that are not stored anywhere:&lt;br /&gt;
--   * Weapon DPS = (Damage + Holy + Fire + Electric) x Attack Speed,&lt;br /&gt;
--     rounded to 1 decimal.&lt;br /&gt;
--   * Enemy wave-10 projections = base + per-wave x 9.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Melee}}   melee weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Ranged}}  ranged weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Enemies}}         base + per-wave + wave-10&lt;br /&gt;
--   {{#invoke:Compare|render|Characters}}      hero stat modifiers&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Weapons.json, Data:Enemies.json, Data:Characters.json&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * Every table opens with a Thumbnail column (icon only, header marked&lt;br /&gt;
--     class=&amp;quot;unsortable&amp;quot;) followed by a sortable linked Name column.&lt;br /&gt;
--   * Stat sentinels: a value &amp;gt;= 1000 renders as the infinity sign&lt;br /&gt;
--     (sorts to the top); &amp;lt;= -1000 renders as an em-dash (sorts bottom).&lt;br /&gt;
--   * Each row carries data-element / data-class / data-tier / data-type&lt;br /&gt;
--     attributes — MediaWiki:Common.js uses them for the filter chips&lt;br /&gt;
--     above the tables. Removing them breaks the chip filtering.&lt;br /&gt;
--   * To add a column, add a header in the relevant *Rows() builder and&lt;br /&gt;
--     a matching cell in the same position.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL_STYLE = &#039;font-size:0.92em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&#039;&lt;br /&gt;
local TH_STYLE  = &#039;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&#039;&lt;br /&gt;
&lt;br /&gt;
-- Colored element swatch for column headers. When the damage-type hub&lt;br /&gt;
-- page exists (existence-checked via Core.statPageTarget) the label links&lt;br /&gt;
-- to it; an inner span keeps the element color inside the anchor.&lt;br /&gt;
local function swatch(label, tok, hexv, statName)&lt;br /&gt;
  local inner = label&lt;br /&gt;
  local target = statName and Core.statPageTarget(statName)&lt;br /&gt;
  if target then&lt;br /&gt;
    inner = &#039;[[&#039; .. target .. &#039;|&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039;&lt;br /&gt;
      .. hexv .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. label .. &#039;&amp;lt;/span&amp;gt;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039; .. hexv&lt;br /&gt;
    .. &#039;);font-weight:bold;white-space:nowrap;&amp;quot;&amp;gt;&amp;amp;#9679; &#039; .. inner .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Abbreviated column headers -&amp;gt; the stat key whose page they should link&lt;br /&gt;
-- to. Full-name headers resolve via Core.statPageTarget directly.&lt;br /&gt;
local HDR_STAT = {&lt;br /&gt;
  [&#039;Atk Speed&#039;] = &#039;Attack Speed&#039;,&lt;br /&gt;
  [&#039;Crit&#039;] = &#039;Critical Chance&#039;,&lt;br /&gt;
  [&#039;Proj.&#039;] = &#039;Projectiles&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Link a plain-text column header to its stat reference page when one&lt;br /&gt;
-- exists; non-stat headers (and already-HTML headers like the swatches)&lt;br /&gt;
-- pass through untouched. Page-less stats stay plain — never a redlink.&lt;br /&gt;
local function headerLink(h)&lt;br /&gt;
  if h:find(&#039;[&amp;lt;%[]&#039;) then return h end&lt;br /&gt;
  local target = Core.statPageTarget(HDR_STAT[h] or h)&lt;br /&gt;
  if not target then return h end&lt;br /&gt;
  if target == h then return &#039;[[&#039; .. h .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. h .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coerce a raw stat value to {display, sortval}: &#039;15%&#039; keeps the %&lt;br /&gt;
-- sign for display but sorts by the number 15.&lt;br /&gt;
local function num(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;∞&#039;, 1e9 end&lt;br /&gt;
    if v &amp;lt;= -1000 then return &#039;—&#039;, -1 end&lt;br /&gt;
    return Core.fmtNum(v), v&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numcell(v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Sentinel-FREE number coercion. The &amp;gt;=1000 / &amp;lt;=-1000 sentinels in num()&lt;br /&gt;
-- are a WEAPON convention (Pierce 1000 = unlimited, Bounce -1000 = hidden);&lt;br /&gt;
-- enemy/character stats use real large numbers (e.g. a boss&#039;s HP/Wave of&lt;br /&gt;
-- 5000), so those tables must format plainly or 5000 wrongly renders as inf.&lt;br /&gt;
local function plain(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then return Core.fmtNum(v), v end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function plaincell(v)&lt;br /&gt;
  local t, s = plain(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DMG = {&lt;br /&gt;
  Holy = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  Fire = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  Electric = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chipcell(dmg, v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  if s == nil or s &amp;lt;= 0 or t == &#039;—&#039; then return {t = &#039;—&#039;, s = 0} end&lt;br /&gt;
  local c = DMG[dmg]&lt;br /&gt;
  return {t = &#039;&amp;lt;span class=&amp;quot;&#039; .. c[3] .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. c[1] .. &#039;, &#039;&lt;br /&gt;
    .. c[2] .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Header marker for the icon-only Thumbnail column: the renderer emits&lt;br /&gt;
-- class=&amp;quot;unsortable&amp;quot; on this th so the sort arrows skip it.&lt;br /&gt;
local THUMB = {&#039;Thumbnail&#039;, unsortable = true}&lt;br /&gt;
&lt;br /&gt;
-- Icon-only Thumbnail cell (links to the entity page; em-dash when the&lt;br /&gt;
-- record has no icon). 48px matches the category-index thumbnail size.&lt;br /&gt;
local function iconcell(rec)&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
    return {t = &#039;[[File:&#039; .. rec.icon .. &#039;|48px|link=&#039; .. slug&lt;br /&gt;
      .. &#039;|alt=&#039; .. (rec.name or slug) .. &#039;]]&#039;}&lt;br /&gt;
  end&lt;br /&gt;
  return {t = &#039;—&#039;}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Linked-name cell, sortable by lowercased name, wrapped in .wm-tip&lt;br /&gt;
-- (Common.js hover-infobox popup).&lt;br /&gt;
local function namecell(rec)&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local t = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  return {t = t, s = mw.ustring.lower(name)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat(rec, k)&lt;br /&gt;
  return (rec.stats or {})[k]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function nstat(rec, k)&lt;br /&gt;
  return Core.numStat(stat(rec, k))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalDPS(rec)&lt;br /&gt;
  local s = rec.stats or {}&lt;br /&gt;
  local atk = Core.numStat(s[&#039;Attack Speed&#039;])&lt;br /&gt;
  if atk == nil then return nil end&lt;br /&gt;
  local total = Core.numStat(s[&#039;Damage&#039;]) or 0&lt;br /&gt;
  for _, k in ipairs({&#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;}) do&lt;br /&gt;
    local v = Core.numStat(s[k])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then total = total + v end&lt;br /&gt;
  end&lt;br /&gt;
  return Core.round1(total * atk)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wave10(rec, base, per)&lt;br /&gt;
  local b, pv = nstat(rec, base), nstat(rec, per)&lt;br /&gt;
  if b == nil or pv == nil then return nil end&lt;br /&gt;
  return Core.round1(b + pv * 9)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- dataset builders ----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function weaponRows(class)&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Tier&#039;, &#039;Price&#039;, &#039;Damage&#039;,&lt;br /&gt;
    swatch(&#039;Holy&#039;, &#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;Holy Damage&#039;),&lt;br /&gt;
    swatch(&#039;Fire&#039;, &#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;Fire Damage&#039;),&lt;br /&gt;
    swatch(&#039;Electric&#039;, &#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;Electric Damage&#039;),&lt;br /&gt;
    &#039;DPS&#039;, &#039;Atk Speed&#039;, &#039;Crit&#039;, &#039;Range&#039;, &#039;Pierce&#039;, &#039;Proj.&#039;, &#039;Bounce&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    if Core.weaponClass(rec) == class then&lt;br /&gt;
      -- Only Pierce/Bounce use the +/-1000 on/off sentinel (numcell);&lt;br /&gt;
      -- every other column is a real number (Range 1400 is a real range,&lt;br /&gt;
      -- not &amp;quot;unlimited&amp;quot;), so use the sentinel-free plaincell.&lt;br /&gt;
      local cells = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        plaincell(rec.tier),&lt;br /&gt;
        plaincell(rec.price),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Holy&#039;, stat(rec, &#039;Holy Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Fire&#039;, stat(rec, &#039;Fire Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Electric&#039;, stat(rec, &#039;Electric Damage&#039;)),&lt;br /&gt;
        plaincell(totalDPS(rec)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Pierce&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Projectiles&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Bounce&#039;)),&lt;br /&gt;
      }&lt;br /&gt;
      local elements = {}&lt;br /&gt;
      for _, e in ipairs({{&#039;holy&#039;, &#039;Holy Damage&#039;}, {&#039;fire&#039;, &#039;Fire Damage&#039;},&lt;br /&gt;
                          {&#039;electric&#039;, &#039;Electric Damage&#039;}}) do&lt;br /&gt;
        local v = nstat(rec, e[2])&lt;br /&gt;
        if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then elements[#elements + 1] = e[1] end&lt;br /&gt;
      end&lt;br /&gt;
      local attrs = &#039; data-element=&amp;quot;&#039; .. (#elements &amp;gt; 0 and table.concat(elements, &#039; &#039;) or &#039;none&#039;)&lt;br /&gt;
        .. &#039;&amp;quot; data-class=&amp;quot;&#039; .. class:lower()&lt;br /&gt;
        .. &#039;&amp;quot; data-tier=&amp;quot;&#039; .. tostring(rec.tier or &#039;&#039;) .. &#039;&amp;quot;&#039;&lt;br /&gt;
      rows[#rows + 1] = {a = attrs, c = cells}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  -- DPS descending (cell 9 after the Thumbnail/Name split; stable:&lt;br /&gt;
  -- name asc tiebreak via the Name cell sortkey, cell 2).&lt;br /&gt;
  table.sort(rows, function(x, y)&lt;br /&gt;
    local dx, dy = x.c[9].s or 0, y.c[9].s or 0&lt;br /&gt;
    if dx ~= dy then return dx &amp;gt; dy end&lt;br /&gt;
    return (x.c[2].s or &#039;&#039;) &amp;lt; (y.c[2].s or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function enemyRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health&#039;, &#039;HP/Wave&#039;, &#039;HP @ W10&#039;, &#039;Damage&#039;,&lt;br /&gt;
                   &#039;Dmg/Wave&#039;, &#039;Dmg @ W10&#039;, &#039;Speed&#039;, &#039;Resources&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Enemies&#039;)) do&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, k in ipairs(rec.tags or {}) do keys[#keys + 1] = k:lower() end&lt;br /&gt;
    rows[#rows + 1] = {&lt;br /&gt;
      a = &#039; data-type=&amp;quot;&#039; .. (#keys &amp;gt; 0 and table.concat(keys, &#039; &#039;) or &#039;other&#039;) .. &#039;&amp;quot;&#039;,&lt;br /&gt;
      c = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        plaincell(stat(rec, &#039;Health&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;HP per Wave&#039;)),&lt;br /&gt;
        plaincell(wave10(rec, &#039;Health&#039;, &#039;HP per Wave&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        plaincell(wave10(rec, &#039;Damage&#039;, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Resources&#039;)),&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function characterRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health Points&#039;, &#039;Damage&#039;, &#039;Critical Chance&#039;,&lt;br /&gt;
                   &#039;Attack Speed&#039;, &#039;Range&#039;, &#039;Speed&#039;, &#039;Armor&#039;, &#039;Dodge&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Characters&#039;)) do&lt;br /&gt;
    rows[#rows + 1] = {a = &#039;&#039;, c = {&lt;br /&gt;
      iconcell(rec),&lt;br /&gt;
      namecell(rec),&lt;br /&gt;
      plaincell(stat(rec, &#039;Health Points&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Armor&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Dodge&#039;)),&lt;br /&gt;
    }}&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DATASETS = {&lt;br /&gt;
  Weapons_Melee = function() return weaponRows(&#039;Melee&#039;) end,&lt;br /&gt;
  Weapons_Ranged = function() return weaponRows(&#039;Ranged&#039;) end,&lt;br /&gt;
  Enemies = enemyRows,&lt;br /&gt;
  Characters = characterRows,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- renderer --------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function cell(c)&lt;br /&gt;
  local t = tostring(c.t or &#039;—&#039;)&lt;br /&gt;
  if c.s ~= nil then&lt;br /&gt;
    local s = c.s&lt;br /&gt;
    if type(s) == &#039;number&#039; then s = Core.fmtNum(s) end&lt;br /&gt;
    return &#039;| data-sort-value=&amp;quot;&#039; .. tostring(s) .. &#039;&amp;quot; | &#039; .. t&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;| &#039; .. t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local ds = frame.args[1] or frame.args.cat&lt;br /&gt;
  if not ds or ds == &#039;&#039; then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: missing dataset arg&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  ds = ds:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  local build = DATASETS[ds]&lt;br /&gt;
  if not build then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: unknown dataset &#039;&lt;br /&gt;
      .. mw.text.nowiki(ds) .. &#039;&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local headers, rows = build()&lt;br /&gt;
  local out = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;&#039; .. TBL_STYLE .. &#039;&amp;quot;&#039; }&lt;br /&gt;
  out[#out+1] = &#039;|-&#039;&lt;br /&gt;
  for _, h in ipairs(headers) do&lt;br /&gt;
    local label, attrs = h, &#039;&#039;&lt;br /&gt;
    if type(h) == &#039;table&#039; then&lt;br /&gt;
      label = h[1]&lt;br /&gt;
      if h.unsortable then attrs = &#039; class=&amp;quot;unsortable&amp;quot;&#039; end&lt;br /&gt;
    end&lt;br /&gt;
    out[#out+1] = &#039;!&#039; .. attrs .. &#039; style=&amp;quot;&#039; .. TH_STYLE .. &#039;&amp;quot; | &#039;&lt;br /&gt;
      .. headerLink(tostring(label))&lt;br /&gt;
  end&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039; .. tostring(row.a or &#039;&#039;)&lt;br /&gt;
    for _, c in ipairs(row.c) do&lt;br /&gt;
      out[#out+1] = cell(c)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Core&amp;diff=2126</id>
		<title>Module:Core</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Core&amp;diff=2126"/>
		<updated>2026-06-11T02:47:03Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: fix: scope the +/-1000 Unlimited/disabled sentinel to Pierce/Bounce only (weapon Range 1400, hero/enemy large stats are real numbers)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Core — the engine room of this wiki.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Every infobox value, index table, badge, tag link, sort order and&lt;br /&gt;
--   lore join on this wiki is computed here (or in a module that&lt;br /&gt;
--   require()s this one) at render time, from the ten source&lt;br /&gt;
--   Data:&amp;lt;Category&amp;gt;.json pages. There are no precomputed data pages:&lt;br /&gt;
--   edit a value on a Data page, purge, and every view of it updates.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   You normally don&#039;t invoke Core directly. Each category module&lt;br /&gt;
--   (Module:Weapons, Module:Enemies, ...) is a thin shim that binds a&lt;br /&gt;
--   category name and forwards to the entry points at the bottom of&lt;br /&gt;
--   this file:&lt;br /&gt;
--     {{#invoke:Weapons|infobox|id=Cutter}}     -&amp;gt; p.infoboxEntry&lt;br /&gt;
--     {{#invoke:Weapons|body|id=Cutter}}        -&amp;gt; p.bodyEntry&lt;br /&gt;
--     {{#invoke:Weapons|index}}                 -&amp;gt; p.indexEntry&lt;br /&gt;
--     {{#invoke:Weapons|render|id=Cutter}}      -&amp;gt; p.renderEntry (all-in-one)&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (see each page&#039;s own `description` for fields)&lt;br /&gt;
--   Data:Weapons.json, Data:Characters.json, Data:Blessings.json,&lt;br /&gt;
--   Data:Upgrades.json, Data:Crux.json, Data:Passives.json,&lt;br /&gt;
--   Data:Enemies.json, Data:Achievements.json, Data:Codex.json,&lt;br /&gt;
--   Data:Tags.json&lt;br /&gt;
--   Common record fields: id, slug, name, icon, tier, price, locked,&lt;br /&gt;
--   type, state, tags[] (keys into Data:Tags.json), stats{}, description.&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * `slug` IS the page title. Never rebuild it from `name` in Lua —&lt;br /&gt;
--     always read rec.slug.&lt;br /&gt;
--   * Presentation config (stat display order, glyphs, infobox field&lt;br /&gt;
--     lists, index columns, sort keys) lives in the tables below and is&lt;br /&gt;
--     editable on-wiki in this one place.&lt;br /&gt;
--   * Cross-category functions (find, tagMembership, codexFor) load&lt;br /&gt;
--     several Data pages in one parse; the host runs Scribunto with a&lt;br /&gt;
--     raised (256 MiB) memory cap to allow this.&lt;br /&gt;
--   * See Help:Wiki Editing for the full architecture guide.&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- registry&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
p.CATEGORIES = {&lt;br /&gt;
  {name = &#039;Weapons&#039;,      singular = &#039;Weapon&#039;},&lt;br /&gt;
  {name = &#039;Characters&#039;,   singular = &#039;Character&#039;},&lt;br /&gt;
  {name = &#039;Blessings&#039;,    singular = &#039;Blessing&#039;},&lt;br /&gt;
  {name = &#039;Upgrades&#039;,     singular = &#039;Upgrade&#039;},&lt;br /&gt;
  {name = &#039;Crux&#039;,         singular = &#039;Crux&#039;},&lt;br /&gt;
  {name = &#039;Passives&#039;,     singular = &#039;Passive&#039;},&lt;br /&gt;
  {name = &#039;Enemies&#039;,      singular = &#039;Enemy&#039;},&lt;br /&gt;
  {name = &#039;Achievements&#039;, singular = &#039;Achievement&#039;},&lt;br /&gt;
  {name = &#039;Codex&#039;,        singular = &#039;Codex&#039;},&lt;br /&gt;
  {name = &#039;Tags&#039;,         singular = &#039;Tag&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local ALIAS = {}&lt;br /&gt;
for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
  ALIAS[c.name:lower()] = c.name&lt;br /&gt;
  ALIAS[c.singular:lower()] = c.name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;Weapon&#039; / &#039;weapons&#039; / &#039;Weapons&#039; -&amp;gt; &#039;Weapons&#039;; nil when not a category.&lt;br /&gt;
function p.resolveCategory(word)&lt;br /&gt;
  if not word or word == &#039;&#039; then return nil end&lt;br /&gt;
  return ALIAS[(word:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)):lower()]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- data loading (memoized per parse)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
&lt;br /&gt;
-- mw.loadJsonData returns a read-only table with quirky numeric keys;&lt;br /&gt;
-- copy it once into a plain mutable table so the rest of the module can&lt;br /&gt;
-- treat records as ordinary Lua tables.&lt;br /&gt;
local function fix(t)&lt;br /&gt;
  if type(t) ~= &#039;table&#039; then return t end&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for k, v in pairs(t) do&lt;br /&gt;
    out[k] = fix(v)&lt;br /&gt;
    if type(k) == &#039;number&#039; then out[tostring(k)] = out[k] end&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local _data = {}&lt;br /&gt;
function p.load(cat)&lt;br /&gt;
  cat = p.resolveCategory(cat) or cat&lt;br /&gt;
  if _data[cat] == nil then&lt;br /&gt;
    local ok, d = pcall(mw.loadJsonData, &#039;Data:&#039; .. cat .. &#039;.json&#039;)&lt;br /&gt;
    if ok and type(d) == &#039;table&#039; then&lt;br /&gt;
      _data[cat] = fix(d).records or {}&lt;br /&gt;
    else&lt;br /&gt;
      _data[cat] = {}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _data[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local _byId = {}&lt;br /&gt;
function p.byId(cat)&lt;br /&gt;
  cat = p.resolveCategory(cat) or cat&lt;br /&gt;
  if _byId[cat] == nil then&lt;br /&gt;
    local m = {}&lt;br /&gt;
    for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
      if r.id then m[r.id] = r end&lt;br /&gt;
      if r.slug then m[r.slug] = r end&lt;br /&gt;
    end&lt;br /&gt;
    _byId[cat] = m&lt;br /&gt;
  end&lt;br /&gt;
  return _byId[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Iterate every category&#039;s records: returns a list of {cat=&amp;lt;name&amp;gt;, recs=&amp;lt;list&amp;gt;}.&lt;br /&gt;
function p.all()&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
    out[#out + 1] = {cat = c.name, recs = p.load(c.name)}&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- First record across all categories matching slug or (lowercased) name.&lt;br /&gt;
-- Registry order wins on collisions (live entity beats its Codex entry).&lt;br /&gt;
function p.find(key)&lt;br /&gt;
  if not key or key == &#039;&#039; then return nil, nil end&lt;br /&gt;
  local k = mw.ustring.lower(key)&lt;br /&gt;
  for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
    for _, r in ipairs(p.load(c.name)) do&lt;br /&gt;
      if r.slug == key or (r.slug and mw.ustring.lower(r.slug) == k)&lt;br /&gt;
        or (r.name and mw.ustring.lower(r.name) == k) then&lt;br /&gt;
        return c.name, r&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return nil, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- small helpers&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
function p.slugFor(rec) return rec and rec.slug or &#039;&#039; end&lt;br /&gt;
function p.iconFor(rec) return rec and rec.icon or &#039;&#039; end&lt;br /&gt;
&lt;br /&gt;
function p.normName(s)&lt;br /&gt;
  s = mw.ustring.lower(s or &#039;&#039;)&lt;br /&gt;
  return (mw.ustring.gsub(s, &#039;[^%w]&#039;, &#039;&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.link(rec)&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  return &#039;[[&#039; .. slug .. &#039;|&#039; .. (rec.name or slug) .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- icon + linked name wrapped in .wm-tip (Common.js hover-infobox popup).&lt;br /&gt;
function p.iconLink(rec, size)&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local tip = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    return tip .. &#039;[[File:&#039; .. rec.icon .. &#039;|&#039; .. (size or 24) .. &#039;px|link=&#039;&lt;br /&gt;
      .. slug .. &#039;]] [[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return tip .. &#039;[[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;56.1&#039; / &#039;6&#039; (no trailing .0); non-numbers pass through as strings.&lt;br /&gt;
function p.fmtNum(v)&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v == math.floor(v) then return string.format(&#039;%d&#039;, v) end&lt;br /&gt;
    return tostring(v)&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v or &#039;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.round1(x)&lt;br /&gt;
  return math.floor(x * 10 + 0.5) / 10&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parse a stat value to a number: 11 -&amp;gt; 11, &#039;15%&#039; -&amp;gt; 15, else nil.&lt;br /&gt;
function p.numStat(v)&lt;br /&gt;
  if type(v) == &#039;number&#039; then return v end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    return tonumber(v) or tonumber(v:match(&#039;^%s*([%-%d%.]+)&#039;))&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Stats that use the +/-1000 on/off sentinel — a WEAPON convention only:&lt;br /&gt;
-- Pierce 1000 = &amp;quot;Unlimited&amp;quot;, Pierce/Bounce -1000 = &amp;quot;disabled&amp;quot; (row dropped).&lt;br /&gt;
-- EVERY other stat is a real number even when large (a sniper&#039;s Range 1400,&lt;br /&gt;
-- a boss&#039;s HP/Wave 5000), so it must NEVER be sentinel-folded.&lt;br /&gt;
p.SENTINEL_STATS = { Pierce = true, Bounce = true }&lt;br /&gt;
&lt;br /&gt;
-- Render one stat value. `signed` adds a &#039;+&#039; prefix for modifier-style&lt;br /&gt;
-- categories (heroes, blessings, upgrades, ...). `key` is the stat name;&lt;br /&gt;
-- the +/-1000 sentinel applies ONLY when key is a SENTINEL_STATS member.&lt;br /&gt;
function p.fmtStat(v, signed, key)&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if p.SENTINEL_STATS[key] then&lt;br /&gt;
      if v &amp;gt;= 1000 then return &#039;Unlimited&#039; end&lt;br /&gt;
      if v &amp;lt;= -1000 then return nil end&lt;br /&gt;
    end&lt;br /&gt;
    local s = p.fmtNum(v)&lt;br /&gt;
    if signed and v &amp;gt; 0 then s = &#039;+&#039; .. s end&lt;br /&gt;
    return s&lt;br /&gt;
  end&lt;br /&gt;
  local s = tostring(v or &#039;&#039;)&lt;br /&gt;
  if signed and s ~= &#039;&#039; and s:sub(1, 1) ~= &#039;-&#039; and s:sub(1, 1) ~= &#039;+&#039; then&lt;br /&gt;
    s = &#039;+&#039; .. s&lt;br /&gt;
  end&lt;br /&gt;
  return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.firstSentence(text, cap)&lt;br /&gt;
  local t = (text or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;)&lt;br /&gt;
  t = t:match(&#039;^[^\n]*&#039;) or t&lt;br /&gt;
  local s = t:match(&#039;^(.-[%.!%?])%s&#039;) or t:match(&#039;^(.-[%.!%?])$&#039;) or t&lt;br /&gt;
  cap = cap or 140&lt;br /&gt;
  if mw.ustring.len(s) &amp;gt; cap then s = mw.ustring.sub(s, 1, cap) end&lt;br /&gt;
  return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- stat presentation config&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
&lt;br /&gt;
-- Canonical display order for the stats block (JSON object key order is&lt;br /&gt;
-- not visible to Lua). Unknown stats append alphabetically at the end.&lt;br /&gt;
local STAT_ORDER = {&lt;br /&gt;
  &#039;Damage&#039;, &#039;Damage per Wave&#039;, &#039;Health&#039;, &#039;HP per Wave&#039;,&lt;br /&gt;
  &#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;,&lt;br /&gt;
  &#039;Damage vs Burning&#039;, &#039;Damage vs Shocked&#039;, &#039;Melee Damage&#039;, &#039;Range Damage&#039;,&lt;br /&gt;
  &#039;Attack Speed&#039;, &#039;Critical Chance&#039;, &#039;Critical Damage&#039;, &#039;Range&#039;,&lt;br /&gt;
  &#039;Health Points&#039;, &#039;HP Rate&#039;, &#039;HP Delay&#039;,&lt;br /&gt;
  &#039;Pierce&#039;, &#039;Projectiles&#039;, &#039;Bounce&#039;,&lt;br /&gt;
  &#039;Explosive Chance&#039;, &#039;Explosive Radius&#039;, &#039;Explosive Range&#039;, &#039;HP Steal&#039;,&lt;br /&gt;
  &#039;Armor&#039;, &#039;Dodge&#039;, &#039;Speed&#039;, &#039;Resources&#039;, &#039;Rarity Find&#039;,&lt;br /&gt;
  &#039;Stagger&#039;, &#039;Stun&#039;, &#039;Freeze&#039;, &#039;XP Gain&#039;, &#039;Pickup Range&#039;, &#039;Cooldown&#039;,&lt;br /&gt;
}&lt;br /&gt;
local STAT_RANK = {}&lt;br /&gt;
for i, k in ipairs(STAT_ORDER) do STAT_RANK[k] = i end&lt;br /&gt;
&lt;br /&gt;
-- Stat name -&amp;gt; in-game glyph file (18px next to each stat row).&lt;br /&gt;
local STAT_GLYPHS = {&lt;br /&gt;
  [&#039;Damage&#039;] = &#039;Stat_Damage.png&#039;,&lt;br /&gt;
  [&#039;Fire Damage&#039;] = &#039;Stat_Fire.png&#039;,&lt;br /&gt;
  [&#039;Electric Damage&#039;] = &#039;Stat_Electric.png&#039;,&lt;br /&gt;
  [&#039;Holy Damage&#039;] = &#039;Stat_Physical.png&#039;,&lt;br /&gt;
  [&#039;Attack Speed&#039;] = &#039;Stat_AttackSpeed.png&#039;,&lt;br /&gt;
  [&#039;Critical Chance&#039;] = &#039;Stat_Critical.png&#039;,&lt;br /&gt;
  [&#039;Critical Damage&#039;] = &#039;Stat_CriticalDmg.png&#039;,&lt;br /&gt;
  [&#039;Range&#039;] = &#039;Stat_Range.png&#039;,&lt;br /&gt;
  [&#039;Pierce&#039;] = &#039;Stat_Pierce.png&#039;,&lt;br /&gt;
  [&#039;Bounce&#039;] = &#039;Stat_Bounce.png&#039;,&lt;br /&gt;
  [&#039;Projectiles&#039;] = &#039;Stat_Projectiles.png&#039;,&lt;br /&gt;
  [&#039;Stagger&#039;] = &#039;Stat_Stagger.png&#039;,&lt;br /&gt;
  [&#039;Stun&#039;] = &#039;Stat_Stun.png&#039;,&lt;br /&gt;
  [&#039;Freeze&#039;] = &#039;Stat_Freeze.png&#039;,&lt;br /&gt;
  [&#039;Explosive Chance&#039;] = &#039;Stat_ExplosiveChance.png&#039;,&lt;br /&gt;
  [&#039;Explosive Radius&#039;] = &#039;Stat_ExplosiveRadius.png&#039;,&lt;br /&gt;
  [&#039;Explosive Range&#039;] = &#039;Stat_ExplosiveRadius.png&#039;,&lt;br /&gt;
  [&#039;HP Steal&#039;] = &#039;Stat_HPSteal.png&#039;,&lt;br /&gt;
  [&#039;Health Points&#039;] = &#039;Stat_HP.png&#039;,&lt;br /&gt;
  [&#039;HP Rate&#039;] = &#039;Stat_HPRate.png&#039;,&lt;br /&gt;
  [&#039;HP Delay&#039;] = &#039;Stat_HPDelay.png&#039;,&lt;br /&gt;
  [&#039;Speed&#039;] = &#039;Stat_Speed.png&#039;,&lt;br /&gt;
  [&#039;Armor&#039;] = &#039;Stat_Armor.png&#039;,&lt;br /&gt;
  [&#039;Dodge&#039;] = &#039;Stat_Dodge.png&#039;,&lt;br /&gt;
  [&#039;Resources&#039;] = &#039;Stat_Resources.png&#039;,&lt;br /&gt;
  [&#039;Pickup Range&#039;] = &#039;Stat_PickupRange.png&#039;,&lt;br /&gt;
  [&#039;Rarity Find&#039;] = &#039;Stat_RarityFind.png&#039;,&lt;br /&gt;
  [&#039;XP Gain&#039;] = &#039;Stat_XPGain.png&#039;,&lt;br /&gt;
  [&#039;Cooldown&#039;] = &#039;Stat_Cooldown.png&#039;,&lt;br /&gt;
  [&#039;Damage vs Burning&#039;] = &#039;Stat_VsFire.png&#039;,&lt;br /&gt;
  [&#039;Damage vs Shocked&#039;] = &#039;Stat_VsElectric.png&#039;,&lt;br /&gt;
  [&#039;Health&#039;] = &#039;Stat_HitPoints.png&#039;,&lt;br /&gt;
}&lt;br /&gt;
p.STAT_GLYPHS = STAT_GLYPHS&lt;br /&gt;
&lt;br /&gt;
function p.statGlyph(stat)&lt;br /&gt;
  local f = STAT_GLYPHS[stat]&lt;br /&gt;
  if f then return &#039;[[File:&#039; .. f .. &#039;|18px|link=]] &#039; end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Elemental damage accent chips (dark-theme tokens with inline fallbacks).&lt;br /&gt;
p.DMG_CHIP = {&lt;br /&gt;
  [&#039;Holy Damage&#039;]     = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  [&#039;Fire Damage&#039;]     = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  [&#039;Electric Damage&#039;] = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- stat page links -------------------------------------------------&lt;br /&gt;
-- Per-stat reverse-lookup pages exist for every stat modified by at least&lt;br /&gt;
-- one hero / blessing / upgrade / passive / Crux power, and the codex Tips&lt;br /&gt;
-- pages double as hubs for the stats sharing their title (Holy / Fire /&lt;br /&gt;
-- Electric Damage, Critical Damage, Pierce, Bounce, Projectiles, Stagger,&lt;br /&gt;
-- Stun). A stat label is linkified ONLY when one of those pages exists, so&lt;br /&gt;
-- a page-less stat renders as plain text — never a redlink. Both sets are&lt;br /&gt;
-- computed from the source Data pages at render time, mirroring how the&lt;br /&gt;
-- page set itself is derived at emit time, so they match by construction.&lt;br /&gt;
&lt;br /&gt;
-- Same alias fold as Module:StatIndex: the game data calls one mechanic&lt;br /&gt;
-- both &#039;Explosive Range&#039; and &#039;Explosive Radius&#039;; the page lives at&lt;br /&gt;
-- &#039;Explosive Radius&#039;.&lt;br /&gt;
local STAT_PAGE_ALIAS = {[&#039;Explosive Range&#039;] = &#039;Explosive Radius&#039;}&lt;br /&gt;
&lt;br /&gt;
-- Enemy-only scaling keys: never booster-modified, never get stat pages.&lt;br /&gt;
local NON_PAGE_STATS = {[&#039;Health&#039;] = true, [&#039;HP per Wave&#039;] = true,&lt;br /&gt;
                        [&#039;Damage per Wave&#039;] = true}&lt;br /&gt;
&lt;br /&gt;
local MODIFIER_CATS = {&#039;Characters&#039;, &#039;Blessings&#039;, &#039;Upgrades&#039;, &#039;Passives&#039;, &#039;Crux&#039;}&lt;br /&gt;
&lt;br /&gt;
-- Set (key -&amp;gt; true) of stat keys modified by at least one booster record&lt;br /&gt;
-- (memoized per parse).&lt;br /&gt;
local _boosterKeys = nil&lt;br /&gt;
function p.boosterStatKeys()&lt;br /&gt;
  if _boosterKeys == nil then&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, cat in ipairs(MODIFIER_CATS) do&lt;br /&gt;
      for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
        for k in pairs(r.stats or {}) do keys[k] = true end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    for k in pairs(NON_PAGE_STATS) do keys[k] = nil end&lt;br /&gt;
    if keys[&#039;Explosive Range&#039;] then keys[&#039;Explosive Radius&#039;] = true end&lt;br /&gt;
    _boosterKeys = keys&lt;br /&gt;
  end&lt;br /&gt;
  return _boosterKeys&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Page title of a stat&#039;s reference page, or nil when no page exists.&lt;br /&gt;
-- Codex-hosted hubs win (link target = the codex record&#039;s slug — never&lt;br /&gt;
-- rebuilt from the name); otherwise booster-modified stats point at their&lt;br /&gt;
-- emitted reverse-lookup page, whose title IS the stat name.&lt;br /&gt;
function p.statPageTarget(stat)&lt;br /&gt;
  if not stat or stat == &#039;&#039; then return nil end&lt;br /&gt;
  local key = STAT_PAGE_ALIAS[stat] or stat&lt;br /&gt;
  if NON_PAGE_STATS[key] then return nil end&lt;br /&gt;
  local cx = p.codexFor(key, &#039;Tips&#039;)&lt;br /&gt;
  if cx and cx.slug then return cx.slug end&lt;br /&gt;
  if p.boosterStatKeys()[key] then return key end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;[[&amp;lt;page&amp;gt;|&amp;lt;stat&amp;gt;]]&#039; when the stat has a reference page, else the plain&lt;br /&gt;
-- label unchanged.&lt;br /&gt;
function p.statLabelLink(stat)&lt;br /&gt;
  local target = p.statPageTarget(stat)&lt;br /&gt;
  if not target then return stat end&lt;br /&gt;
  if target == stat then return &#039;[[&#039; .. stat .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. stat .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Ordered {key, value} pairs of rec.stats per STAT_ORDER (extras appended&lt;br /&gt;
-- alphabetically).&lt;br /&gt;
function p.statPairs(rec)&lt;br /&gt;
  local stats = (rec and rec.stats) or {}&lt;br /&gt;
  local known, extra = {}, {}&lt;br /&gt;
  for k in pairs(stats) do&lt;br /&gt;
    if STAT_RANK[k] then known[#known + 1] = k else extra[#extra + 1] = k end&lt;br /&gt;
  end&lt;br /&gt;
  table.sort(known, function(a, b) return STAT_RANK[a] &amp;lt; STAT_RANK[b] end)&lt;br /&gt;
  table.sort(extra)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, k in ipairs(known) do out[#out + 1] = {k, stats[k]} end&lt;br /&gt;
  for _, k in ipairs(extra) do out[#out + 1] = {k, stats[k]} end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Infobox &#039;Stats&#039; cell: one glyph+label+value line per stat, &amp;lt;br&amp;gt;-joined.&lt;br /&gt;
-- Every stat label whose reference page exists links to it (elemental&lt;br /&gt;
-- damage labels to the damage-type hubs, booster-modified stats to their&lt;br /&gt;
-- reverse-lookup pages); page-less stats stay plain text. Values and&lt;br /&gt;
-- glyphs are untouched.&lt;br /&gt;
function p.statsBlock(rec, signed)&lt;br /&gt;
  local lines = {}&lt;br /&gt;
  for _, kv in ipairs(p.statPairs(rec)) do&lt;br /&gt;
    local k, v = kv[1], kv[2]&lt;br /&gt;
    local val = p.fmtStat(v, signed, k)&lt;br /&gt;
    if val ~= nil then&lt;br /&gt;
      local label = p.statLabelLink(k)&lt;br /&gt;
      local chip = p.DMG_CHIP[k]&lt;br /&gt;
      if chip then&lt;br /&gt;
        lines[#lines + 1] = p.statGlyph(k) .. &#039;&amp;lt;span class=&amp;quot;&#039; .. chip[3]&lt;br /&gt;
          .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. chip[1] .. &#039;, &#039; .. chip[2]&lt;br /&gt;
          .. &amp;quot;);\&amp;quot;&amp;gt;&#039;&#039;&#039;&amp;quot; .. label .. &amp;quot;:&#039;&#039;&#039; &amp;quot; .. val .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
      else&lt;br /&gt;
        lines[#lines + 1] = p.statGlyph(k) .. &amp;quot;&#039;&#039;&#039;&amp;quot; .. label .. &amp;quot;:&#039;&#039;&#039; &amp;quot; .. val&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(lines, &#039;&amp;lt;br&amp;gt;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- One-line plain-text summary for index columns (Effects / Modifiers).&lt;br /&gt;
function p.effectsSummary(rec, signed)&lt;br /&gt;
  local parts = {}&lt;br /&gt;
  for _, kv in ipairs(p.statPairs(rec)) do&lt;br /&gt;
    local val = p.fmtStat(kv[2], signed, kv[1])&lt;br /&gt;
    if val ~= nil then parts[#parts + 1] = kv[1] .. &#039; &#039; .. val end&lt;br /&gt;
  end&lt;br /&gt;
  if #parts &amp;gt; 0 then return table.concat(parts, &#039;, &#039;) end&lt;br /&gt;
  local texts = {}&lt;br /&gt;
  for _, key in ipairs(rec.tags or {}) do&lt;br /&gt;
    local t = p.tagText(key)&lt;br /&gt;
    if t and t ~= &#039;&#039; then texts[#texts + 1] = t end&lt;br /&gt;
  end&lt;br /&gt;
  if #texts &amp;gt; 0 then return table.concat(texts, &#039; &#039;) end&lt;br /&gt;
  local d = (rec.description or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;)&lt;br /&gt;
  if d ~= &#039;&#039; then return mw.ustring.sub(p.firstSentence(d, 120), 1, 120) end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.availability(locked)&lt;br /&gt;
  if locked then return &#039;Must be unlocked&#039; end&lt;br /&gt;
  return &#039;Unlocked from start&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CRUX_STATE = {&lt;br /&gt;
  Acquired = &#039;Default&#039;,&lt;br /&gt;
  Unlocked = &#039;Unlocked from start&#039;,&lt;br /&gt;
  Locked = &#039;Must be unlocked&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Melee vs Ranged: the melee weapons carry the Melee tag (or raw&lt;br /&gt;
-- tag_source family &#039;Melee&#039;); everything else is Ranged.&lt;br /&gt;
function p.weaponClass(rec)&lt;br /&gt;
  for _, k in ipairs(rec.tags or {}) do&lt;br /&gt;
    if k == &#039;Melee&#039; then return &#039;Melee&#039; end&lt;br /&gt;
  end&lt;br /&gt;
  if rec.tag_source == &#039;Melee&#039; then return &#039;Melee&#039; end&lt;br /&gt;
  return &#039;Ranged&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- tags&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local _tagByKey = nil&lt;br /&gt;
function p.tagRec(key)&lt;br /&gt;
  if _tagByKey == nil then&lt;br /&gt;
    _tagByKey = {}&lt;br /&gt;
    for _, r in ipairs(p.load(&#039;Tags&#039;)) do&lt;br /&gt;
      _tagByKey[r.id or r.name] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _tagByKey[key]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.tagText(key)&lt;br /&gt;
  local r = p.tagRec(key)&lt;br /&gt;
  return (r and r.text) or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Tag link wrapped for the hover-tooltip JS. Slug comes from the tag&#039;s&lt;br /&gt;
-- own source record (never recomputed here).&lt;br /&gt;
function p.tagLink(key)&lt;br /&gt;
  local r = p.tagRec(key)&lt;br /&gt;
  if not r or not r.slug then return key end&lt;br /&gt;
  return &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. r.slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. r.slug .. &#039;|&#039; .. key .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Body &#039;Tags&#039; section: list of &amp;quot;&amp;lt;link&amp;gt; – &amp;lt;text&amp;gt;&amp;quot; strings.&lt;br /&gt;
function p.tagsBody(rec)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, key in ipairs(rec.tags or {}) do&lt;br /&gt;
    local text = p.tagText(key)&lt;br /&gt;
    if text ~= &#039;&#039; then&lt;br /&gt;
      out[#out + 1] = p.tagLink(key) .. &#039; – &#039; .. text&lt;br /&gt;
    else&lt;br /&gt;
      out[#out + 1] = p.tagLink(key)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Cross-category tag membership map: {tagkey -&amp;gt; {catName -&amp;gt; {rec,...}}}.&lt;br /&gt;
-- One scan per parse (memoized); powers tag pages + Used-by counts.&lt;br /&gt;
local TAG_BEARING = {&#039;Weapons&#039;, &#039;Characters&#039;, &#039;Blessings&#039;, &#039;Upgrades&#039;,&lt;br /&gt;
                     &#039;Passives&#039;, &#039;Enemies&#039;}&lt;br /&gt;
p.TAG_BEARING = TAG_BEARING&lt;br /&gt;
&lt;br /&gt;
local _membership = nil&lt;br /&gt;
function p.tagMembership()&lt;br /&gt;
  if _membership == nil then&lt;br /&gt;
    _membership = {}&lt;br /&gt;
    for _, cat in ipairs(TAG_BEARING) do&lt;br /&gt;
      for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
        for _, key in ipairs(r.tags or {}) do&lt;br /&gt;
          _membership[key] = _membership[key] or {}&lt;br /&gt;
          _membership[key][cat] = _membership[key][cat] or {}&lt;br /&gt;
          local bucket = _membership[key][cat]&lt;br /&gt;
          bucket[#bucket + 1] = r&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _membership&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.usedByCount(key)&lt;br /&gt;
  local m = p.tagMembership()[key]&lt;br /&gt;
  if not m then return 0 end&lt;br /&gt;
  local n = 0&lt;br /&gt;
  for _, recs in pairs(m) do n = n + #recs end&lt;br /&gt;
  return n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- codex joins (by normalized name)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local _codexByType = nil&lt;br /&gt;
local function codexIndex()&lt;br /&gt;
  if _codexByType == nil then&lt;br /&gt;
    _codexByType = {}&lt;br /&gt;
    for _, r in ipairs(p.load(&#039;Codex&#039;)) do&lt;br /&gt;
      local t = r.type or &#039;&#039;&lt;br /&gt;
      _codexByType[t] = _codexByType[t] or {}&lt;br /&gt;
      _codexByType[t][p.normName(r.name or r.id)] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _codexByType&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- The codex entry describing an entity (Weapons / Enemies join), or nil.&lt;br /&gt;
function p.codexFor(name, codexType)&lt;br /&gt;
  local idx = codexIndex()&lt;br /&gt;
  local key = p.normName(name)&lt;br /&gt;
  if codexType then&lt;br /&gt;
    return (idx[codexType] or {})[key]&lt;br /&gt;
  end&lt;br /&gt;
  for _, bucket in pairs(idx) do&lt;br /&gt;
    if bucket[key] then return bucket[key] end&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- computed infobox (per category) — same field names/values the&lt;br /&gt;
-- infobox Templates and index columns have always consumed.&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local INFOBOX_BUILDERS = {&lt;br /&gt;
  Weapons = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Class = p.weaponClass(rec),&lt;br /&gt;
      Tier = rec.tier,&lt;br /&gt;
      Price = p.fmtNum(rec.price),&lt;br /&gt;
      Availability = p.availability(rec.locked),&lt;br /&gt;
      Stats = p.statsBlock(rec, false),&lt;br /&gt;
      Damage = rec.stats and rec.stats[&#039;Damage&#039;] ~= nil&lt;br /&gt;
        and p.fmtNum(rec.stats[&#039;Damage&#039;]) or nil,&lt;br /&gt;
      [&#039;Attack Speed&#039;] = rec.stats and rec.stats[&#039;Attack Speed&#039;] ~= nil&lt;br /&gt;
        and p.fmtNum(rec.stats[&#039;Attack Speed&#039;]) or nil,&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Characters = function(rec)&lt;br /&gt;
    local hp = rec.stats and rec.stats[&#039;Health Points&#039;]&lt;br /&gt;
    return {&lt;br /&gt;
      Availability = p.availability(rec.locked),&lt;br /&gt;
      Stats = p.statsBlock(rec, true),&lt;br /&gt;
      Modifiers = p.effectsSummary(rec, true),&lt;br /&gt;
      [&#039;Health Points&#039;] = hp ~= nil and p.fmtStat(hp, true) or nil,&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Crux = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Type = rec.type,&lt;br /&gt;
      Availability = CRUX_STATE[rec.state] or rec.state or &#039;&#039;,&lt;br /&gt;
      Stats = p.statsBlock(rec, true),&lt;br /&gt;
      Effects = p.effectsSummary(rec, true),&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Enemies = function(rec)&lt;br /&gt;
    local ib = {Stats = p.statsBlock(rec, false)}&lt;br /&gt;
    if rec.tags and #rec.tags &amp;gt; 0 then&lt;br /&gt;
      local links = {}&lt;br /&gt;
      for _, k in ipairs(rec.tags) do links[#links + 1] = p.tagLink(k) end&lt;br /&gt;
      ib.Classification = table.concat(links, &#039;, &#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local st = rec.stats or {}&lt;br /&gt;
    for _, k in ipairs({&#039;Health&#039;, &#039;Damage&#039;, &#039;Speed&#039;, &#039;Resources&#039;}) do&lt;br /&gt;
      if st[k] ~= nil then ib[k] = p.fmtNum(st[k]) end&lt;br /&gt;
    end&lt;br /&gt;
    if st[&#039;HP per Wave&#039;] ~= nil then ib[&#039;HP/Wave&#039;] = p.fmtNum(st[&#039;HP per Wave&#039;]) end&lt;br /&gt;
    if st[&#039;Damage per Wave&#039;] ~= nil then ib[&#039;Dmg/Wave&#039;] = p.fmtNum(st[&#039;Damage per Wave&#039;]) end&lt;br /&gt;
    return ib&lt;br /&gt;
  end,&lt;br /&gt;
  Achievements = function(rec)&lt;br /&gt;
    return {Objective = rec.description or &#039;&#039;}&lt;br /&gt;
  end,&lt;br /&gt;
  Codex = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Type = rec.type,&lt;br /&gt;
      Summary = p.firstSentence(rec.description or &#039;&#039;, 140),&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Tags = function(rec)&lt;br /&gt;
    local text = rec.text or &#039;&#039;&lt;br /&gt;
    if text == &#039;&#039; then text = &amp;quot;&#039;&#039;(no effect text provided by the game data)&#039;&#039;&amp;quot; end&lt;br /&gt;
    return {Effect = text, [&#039;Used by&#039;] = p.usedByCount(rec.id or rec.name)}&lt;br /&gt;
  end,&lt;br /&gt;
}&lt;br /&gt;
local function shopInfobox(rec)&lt;br /&gt;
  return {&lt;br /&gt;
    Tier = rec.tier,&lt;br /&gt;
    Price = p.fmtNum(rec.price),&lt;br /&gt;
    Availability = p.availability(rec.locked),&lt;br /&gt;
    Stats = p.statsBlock(rec, true),&lt;br /&gt;
    Effects = p.effectsSummary(rec, true),&lt;br /&gt;
  }&lt;br /&gt;
end&lt;br /&gt;
INFOBOX_BUILDERS.Blessings = shopInfobox&lt;br /&gt;
INFOBOX_BUILDERS.Upgrades = shopInfobox&lt;br /&gt;
INFOBOX_BUILDERS.Passives = shopInfobox&lt;br /&gt;
&lt;br /&gt;
function p.computedInfobox(cat, rec)&lt;br /&gt;
  local b = INFOBOX_BUILDERS[cat]&lt;br /&gt;
  if not b then return {} end&lt;br /&gt;
  return b(rec)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- body sections (computed)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
function p.bodySection(cat, rec, section)&lt;br /&gt;
  if section == &#039;Tags&#039; then&lt;br /&gt;
    return p.tagsBody(rec)&lt;br /&gt;
  end&lt;br /&gt;
  if section == &#039;Description&#039; or section == &#039;Objective&#039; then&lt;br /&gt;
    return rec.description or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  if section == &#039;Lore&#039; then&lt;br /&gt;
    if cat == &#039;Weapons&#039; or cat == &#039;Enemies&#039; then&lt;br /&gt;
      local cx = p.codexFor(rec.name, cat)&lt;br /&gt;
      if cx and cx.description and cx.description ~= &#039;&#039; then&lt;br /&gt;
        return cx.description&lt;br /&gt;
      end&lt;br /&gt;
      if cat == &#039;Enemies&#039; then return rec.description or &#039;&#039; end&lt;br /&gt;
      return &#039;&#039;&lt;br /&gt;
    end&lt;br /&gt;
    -- Characters / Codex: own prose.&lt;br /&gt;
    return rec.description or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BODY_ORDER = {&lt;br /&gt;
  Weapons = {&#039;Lore&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Characters = {&#039;Lore&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Blessings = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Upgrades = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Passives = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Crux = {&#039;Description&#039;},&lt;br /&gt;
  Enemies = {&#039;Lore&#039;},&lt;br /&gt;
  Achievements = {&#039;Objective&#039;},&lt;br /&gt;
  Codex = {&#039;Lore&#039;},&lt;br /&gt;
  Tags = {},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- sorting (per-category index order, computed at render time so the&lt;br /&gt;
-- source page order never matters)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local CRUX_TYPE_ORDER = {Unique = 1, Action = 2, Buff = 3, Debuff = 4, Aura = 5}&lt;br /&gt;
local CODEX_TYPE_ORDER = {Enemies = 1, Weapons = 2, Events = 3, Tips = 4}&lt;br /&gt;
local ROMAN = {I = 1, II = 2, III = 3, IV = 4, V = 5}&lt;br /&gt;
&lt;br /&gt;
local function familyKey(name)&lt;br /&gt;
  local head, tail = name:match(&#039;^(.*)%s(%S+)$&#039;)&lt;br /&gt;
  if head and ROMAN[tail] then&lt;br /&gt;
    return mw.ustring.lower(head), ROMAN[tail]&lt;br /&gt;
  end&lt;br /&gt;
  if name:sub(-5) == &#039; Plus&#039; then&lt;br /&gt;
    return mw.ustring.lower(name:sub(1, -6)), 2&lt;br /&gt;
  end&lt;br /&gt;
  return mw.ustring.lower(name), 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local SORT_KEYS = {&lt;br /&gt;
  Weapons = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Characters = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Blessings = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Upgrades = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Passives = function(r)&lt;br /&gt;
    local fam, rank = familyKey(r.name or &#039;&#039;)&lt;br /&gt;
    return {fam, rank, r.name or &#039;&#039;}&lt;br /&gt;
  end,&lt;br /&gt;
  Crux = function(r) return {CRUX_TYPE_ORDER[r.type] or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Enemies = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Achievements = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Codex = function(r) return {CODEX_TYPE_ORDER[r.type] or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Tags = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function keyLess(a, b)&lt;br /&gt;
  for i = 1, math.max(#a, #b) do&lt;br /&gt;
    local x, y = a[i], b[i]&lt;br /&gt;
    if x == nil then return true end&lt;br /&gt;
    if y == nil then return false end&lt;br /&gt;
    if x ~= y then&lt;br /&gt;
      if type(x) == &#039;number&#039; and type(y) == &#039;number&#039; then return x &amp;lt; y end&lt;br /&gt;
      return tostring(x) &amp;lt; tostring(y)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Stable per-category sort of the records list (returns a new list).&lt;br /&gt;
function p.sorted(cat)&lt;br /&gt;
  local keyfn = SORT_KEYS[cat] or function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end&lt;br /&gt;
  local list = {}&lt;br /&gt;
  for i, r in ipairs(p.load(cat)) do list[#list + 1] = {i = i, r = r, k = keyfn(r)} end&lt;br /&gt;
  table.sort(list, function(a, b)&lt;br /&gt;
    if keyLess(a.k, b.k) then return true end&lt;br /&gt;
    if keyLess(b.k, a.k) then return false end&lt;br /&gt;
    return a.i &amp;lt; b.i&lt;br /&gt;
  end)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, e in ipairs(list) do out[#out + 1] = e.r end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- index page config + entry points shared by the per-category shims&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local INDEX_OPTS = {&lt;br /&gt;
  Weapons = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Damage&#039;, source = &#039;Damage&#039;}, {name = &#039;Attack Speed&#039;, source = &#039;Attack Speed&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Characters = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Modifiers&#039;, source = &#039;Modifiers&#039;}, {name = &#039;Health Points&#039;, source = &#039;Health Points&#039;}}, thumbnail_size = 64},&lt;br /&gt;
  Blessings = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Upgrades = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Passives = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Crux = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Type&#039;, source = &#039;Type&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Enemies = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Health&#039;, source = &#039;Health&#039;}, {name = &#039;HP/Wave&#039;, source = &#039;HP/Wave&#039;}, {name = &#039;Damage&#039;, source = &#039;Damage&#039;}, {name = &#039;Dmg/Wave&#039;, source = &#039;Dmg/Wave&#039;}, {name = &#039;Speed&#039;, source = &#039;Speed&#039;}, {name = &#039;Resources&#039;, source = &#039;Resources&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Achievements = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Objective&#039;, source = &#039;Objective&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Codex = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Type&#039;, source = &#039;Type&#039;}, {name = &#039;Summary&#039;, source = &#039;Summary&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Tags = {columns = {{name = &#039;Tag&#039;, source = &#039;link&#039;}, {name = &#039;Effect&#039;, source = &#039;Effect&#039;}, {name = &#039;Used by&#039;, source = &#039;Used by&#039;}}, thumbnail_size = 16},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Visible infobox fields per category — MUST match the rows declared in&lt;br /&gt;
-- Template:&amp;lt;Cat&amp;gt;_infobox.&lt;br /&gt;
local INFOBOX_FIELD_ORDER = {&lt;br /&gt;
  Weapons = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;, &#039;Class&#039;},&lt;br /&gt;
  Characters = {&#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Blessings = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Upgrades = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Passives = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Crux = {&#039;Type&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Enemies = {&#039;Classification&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Achievements = {},&lt;br /&gt;
  Codex = {&#039;Type&#039;},&lt;br /&gt;
  Tags = {&#039;Effect&#039;, &#039;Used by&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Record (shallow copy) augmented with the computed infobox dict, so the&lt;br /&gt;
-- pure Module:Index renderer keeps working unchanged.&lt;br /&gt;
function p.augmented(cat, rec)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for k, v in pairs(rec) do out[k] = v end&lt;br /&gt;
  out.infobox = p.computedInfobox(cat, rec)&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function resolveRec(cat, frame)&lt;br /&gt;
  local id = frame and frame.args and (frame.args.id or frame.args[1])&lt;br /&gt;
  if id then id = (tostring(id):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)) end&lt;br /&gt;
  if not id or id == &#039;&#039; then return nil, &#039;&#039; end&lt;br /&gt;
  local rec = p.byId(cat)[id]&lt;br /&gt;
  if not rec then&lt;br /&gt;
    return nil, mw.getCurrentFrame():preprocess(&lt;br /&gt;
      &#039;&amp;lt;i&amp;gt;Unknown &#039; .. cat .. &#039; record: &#039; .. mw.text.nowiki(id) .. &#039;&amp;lt;/i&amp;gt;&#039;)&lt;br /&gt;
  end&lt;br /&gt;
  return rec, nil&lt;br /&gt;
end&lt;br /&gt;
p.resolveRec = resolveRec&lt;br /&gt;
&lt;br /&gt;
local function asParam(v)&lt;br /&gt;
  if v == nil then return &#039;&#039; end&lt;br /&gt;
  return tostring(v)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderInfobox(cat, rec)&lt;br /&gt;
  local ib = p.computedInfobox(cat, rec)&lt;br /&gt;
  local out = {&#039;{{&#039; .. cat .. &#039;_infobox&#039;}&lt;br /&gt;
  out[#out + 1] = &#039;| name = &#039; .. asParam(rec.name)&lt;br /&gt;
  out[#out + 1] = &#039;| id   = &#039; .. asParam(rec.id)&lt;br /&gt;
  out[#out + 1] = &#039;| icon = &#039; .. asParam(rec.icon)&lt;br /&gt;
  for _, field in ipairs(INFOBOX_FIELD_ORDER[cat] or {}) do&lt;br /&gt;
    out[#out + 1] = &#039;| &#039; .. field .. &#039; = &#039; .. asParam(ib[field])&lt;br /&gt;
  end&lt;br /&gt;
  out[#out + 1] = &#039;}}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderSectionBody(content)&lt;br /&gt;
  if content == nil or content == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  if type(content) == &#039;table&#039; then&lt;br /&gt;
    if #content == 0 then return &#039;&#039; end&lt;br /&gt;
    local lines = {}&lt;br /&gt;
    for _, line in ipairs(content) do lines[#lines + 1] = &#039;* &#039; .. tostring(line) end&lt;br /&gt;
    return table.concat(lines, &#039;\n&#039;)&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- entry points used by the per-category shim modules -------------&lt;br /&gt;
&lt;br /&gt;
function p.infoboxEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(renderInfobox(cat, rec))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.bodyEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local section = frame and frame.args and frame.args.section&lt;br /&gt;
  if section and section ~= &#039;&#039; then&lt;br /&gt;
    return mw.getCurrentFrame():preprocess(&lt;br /&gt;
      renderSectionBody(p.bodySection(cat, rec, section)))&lt;br /&gt;
  end&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, sec in ipairs(BODY_ORDER[cat] or {}) do&lt;br /&gt;
    local body = renderSectionBody(p.bodySection(cat, rec, sec))&lt;br /&gt;
    if body ~= &#039;&#039; then&lt;br /&gt;
      out[#out + 1] = &#039;&#039;&lt;br /&gt;
      out[#out + 1] = &#039;== &#039; .. sec .. &#039; ==&#039;&lt;br /&gt;
      out[#out + 1] = body&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(out, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.indexEntry(cat, frame)&lt;br /&gt;
  local Index = require(&#039;Module:Index&#039;)&lt;br /&gt;
  local recs = {}&lt;br /&gt;
  for _, r in ipairs(p.sorted(cat)) do&lt;br /&gt;
    recs[#recs + 1] = p.augmented(cat, r)&lt;br /&gt;
  end&lt;br /&gt;
  -- Linkify stat-name column headers to their reference pages (existence-&lt;br /&gt;
  -- checked via statPageTarget; non-stat headers pass through untouched).&lt;br /&gt;
  -- Works on a copy so the shared INDEX_OPTS table is never mutated.&lt;br /&gt;
  local base = INDEX_OPTS[cat] or {}&lt;br /&gt;
  local opts = {thumbnail_size = base.thumbnail_size,&lt;br /&gt;
                sort_default = base.sort_default, columns = {}}&lt;br /&gt;
  for _, c in ipairs(base.columns or {}) do&lt;br /&gt;
    local nm = c.name&lt;br /&gt;
    local target = p.statPageTarget(nm)&lt;br /&gt;
    if target then&lt;br /&gt;
      nm = (target == nm) and (&#039;[[&#039; .. nm .. &#039;]]&#039;)&lt;br /&gt;
        or (&#039;[[&#039; .. target .. &#039;|&#039; .. nm .. &#039;]]&#039;)&lt;br /&gt;
      if base.sort_default == c.name then opts.sort_default = nm end&lt;br /&gt;
    end&lt;br /&gt;
    opts.columns[#opts.columns + 1] = {name = nm, source = c.source}&lt;br /&gt;
  end&lt;br /&gt;
  return Index.render(recs, opts)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local CrossRef = require(&#039;Module:CrossRef&#039;)&lt;br /&gt;
  local parts = {&lt;br /&gt;
    renderInfobox(cat, rec),&lt;br /&gt;
    p.bodyEntry(cat, frame),&lt;br /&gt;
    CrossRef.compute(cat, rec),&lt;br /&gt;
  }&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(parts, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2125</id>
		<title>Module:Compare</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2125"/>
		<updated>2026-06-11T02:39:06Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: fix: enemy/character comparison stats must not use the weapon &amp;gt;=1000 inf sentinel (e.g. Azrael HP/Wave 5000)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Compare — sortable side-by-side comparison tables.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Builds the big comparison tables (used on the Weapons index, the&lt;br /&gt;
--   Enemies index, and the Weapon comparison / Hero comparison pages;&lt;br /&gt;
--   &#039;Enemy comparison&#039; is a redirect to Enemies since 2026-06-10f),&lt;br /&gt;
--   including the COMPUTED columns that are not stored anywhere:&lt;br /&gt;
--   * Weapon DPS = (Damage + Holy + Fire + Electric) x Attack Speed,&lt;br /&gt;
--     rounded to 1 decimal.&lt;br /&gt;
--   * Enemy wave-10 projections = base + per-wave x 9.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Melee}}   melee weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Ranged}}  ranged weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Enemies}}         base + per-wave + wave-10&lt;br /&gt;
--   {{#invoke:Compare|render|Characters}}      hero stat modifiers&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Weapons.json, Data:Enemies.json, Data:Characters.json&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * Every table opens with a Thumbnail column (icon only, header marked&lt;br /&gt;
--     class=&amp;quot;unsortable&amp;quot;) followed by a sortable linked Name column.&lt;br /&gt;
--   * Stat sentinels: a value &amp;gt;= 1000 renders as the infinity sign&lt;br /&gt;
--     (sorts to the top); &amp;lt;= -1000 renders as an em-dash (sorts bottom).&lt;br /&gt;
--   * Each row carries data-element / data-class / data-tier / data-type&lt;br /&gt;
--     attributes — MediaWiki:Common.js uses them for the filter chips&lt;br /&gt;
--     above the tables. Removing them breaks the chip filtering.&lt;br /&gt;
--   * To add a column, add a header in the relevant *Rows() builder and&lt;br /&gt;
--     a matching cell in the same position.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL_STYLE = &#039;font-size:0.92em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&#039;&lt;br /&gt;
local TH_STYLE  = &#039;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&#039;&lt;br /&gt;
&lt;br /&gt;
-- Colored element swatch for column headers. When the damage-type hub&lt;br /&gt;
-- page exists (existence-checked via Core.statPageTarget) the label links&lt;br /&gt;
-- to it; an inner span keeps the element color inside the anchor.&lt;br /&gt;
local function swatch(label, tok, hexv, statName)&lt;br /&gt;
  local inner = label&lt;br /&gt;
  local target = statName and Core.statPageTarget(statName)&lt;br /&gt;
  if target then&lt;br /&gt;
    inner = &#039;[[&#039; .. target .. &#039;|&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039;&lt;br /&gt;
      .. hexv .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. label .. &#039;&amp;lt;/span&amp;gt;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039; .. hexv&lt;br /&gt;
    .. &#039;);font-weight:bold;white-space:nowrap;&amp;quot;&amp;gt;&amp;amp;#9679; &#039; .. inner .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Abbreviated column headers -&amp;gt; the stat key whose page they should link&lt;br /&gt;
-- to. Full-name headers resolve via Core.statPageTarget directly.&lt;br /&gt;
local HDR_STAT = {&lt;br /&gt;
  [&#039;Atk Speed&#039;] = &#039;Attack Speed&#039;,&lt;br /&gt;
  [&#039;Crit&#039;] = &#039;Critical Chance&#039;,&lt;br /&gt;
  [&#039;Proj.&#039;] = &#039;Projectiles&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Link a plain-text column header to its stat reference page when one&lt;br /&gt;
-- exists; non-stat headers (and already-HTML headers like the swatches)&lt;br /&gt;
-- pass through untouched. Page-less stats stay plain — never a redlink.&lt;br /&gt;
local function headerLink(h)&lt;br /&gt;
  if h:find(&#039;[&amp;lt;%[]&#039;) then return h end&lt;br /&gt;
  local target = Core.statPageTarget(HDR_STAT[h] or h)&lt;br /&gt;
  if not target then return h end&lt;br /&gt;
  if target == h then return &#039;[[&#039; .. h .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. h .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coerce a raw stat value to {display, sortval}: &#039;15%&#039; keeps the %&lt;br /&gt;
-- sign for display but sorts by the number 15.&lt;br /&gt;
local function num(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;∞&#039;, 1e9 end&lt;br /&gt;
    if v &amp;lt;= -1000 then return &#039;—&#039;, -1 end&lt;br /&gt;
    return Core.fmtNum(v), v&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numcell(v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Sentinel-FREE number coercion. The &amp;gt;=1000 / &amp;lt;=-1000 sentinels in num()&lt;br /&gt;
-- are a WEAPON convention (Pierce 1000 = unlimited, Bounce -1000 = hidden);&lt;br /&gt;
-- enemy/character stats use real large numbers (e.g. a boss&#039;s HP/Wave of&lt;br /&gt;
-- 5000), so those tables must format plainly or 5000 wrongly renders as inf.&lt;br /&gt;
local function plain(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then return Core.fmtNum(v), v end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function plaincell(v)&lt;br /&gt;
  local t, s = plain(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DMG = {&lt;br /&gt;
  Holy = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  Fire = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  Electric = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chipcell(dmg, v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  if s == nil or s &amp;lt;= 0 or t == &#039;—&#039; then return {t = &#039;—&#039;, s = 0} end&lt;br /&gt;
  local c = DMG[dmg]&lt;br /&gt;
  return {t = &#039;&amp;lt;span class=&amp;quot;&#039; .. c[3] .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. c[1] .. &#039;, &#039;&lt;br /&gt;
    .. c[2] .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Header marker for the icon-only Thumbnail column: the renderer emits&lt;br /&gt;
-- class=&amp;quot;unsortable&amp;quot; on this th so the sort arrows skip it.&lt;br /&gt;
local THUMB = {&#039;Thumbnail&#039;, unsortable = true}&lt;br /&gt;
&lt;br /&gt;
-- Icon-only Thumbnail cell (links to the entity page; em-dash when the&lt;br /&gt;
-- record has no icon). 48px matches the category-index thumbnail size.&lt;br /&gt;
local function iconcell(rec)&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
    return {t = &#039;[[File:&#039; .. rec.icon .. &#039;|48px|link=&#039; .. slug&lt;br /&gt;
      .. &#039;|alt=&#039; .. (rec.name or slug) .. &#039;]]&#039;}&lt;br /&gt;
  end&lt;br /&gt;
  return {t = &#039;—&#039;}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Linked-name cell, sortable by lowercased name, wrapped in .wm-tip&lt;br /&gt;
-- (Common.js hover-infobox popup).&lt;br /&gt;
local function namecell(rec)&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local t = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  return {t = t, s = mw.ustring.lower(name)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat(rec, k)&lt;br /&gt;
  return (rec.stats or {})[k]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function nstat(rec, k)&lt;br /&gt;
  return Core.numStat(stat(rec, k))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalDPS(rec)&lt;br /&gt;
  local s = rec.stats or {}&lt;br /&gt;
  local atk = Core.numStat(s[&#039;Attack Speed&#039;])&lt;br /&gt;
  if atk == nil then return nil end&lt;br /&gt;
  local total = Core.numStat(s[&#039;Damage&#039;]) or 0&lt;br /&gt;
  for _, k in ipairs({&#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;}) do&lt;br /&gt;
    local v = Core.numStat(s[k])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then total = total + v end&lt;br /&gt;
  end&lt;br /&gt;
  return Core.round1(total * atk)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wave10(rec, base, per)&lt;br /&gt;
  local b, pv = nstat(rec, base), nstat(rec, per)&lt;br /&gt;
  if b == nil or pv == nil then return nil end&lt;br /&gt;
  return Core.round1(b + pv * 9)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- dataset builders ----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function weaponRows(class)&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Tier&#039;, &#039;Price&#039;, &#039;Damage&#039;,&lt;br /&gt;
    swatch(&#039;Holy&#039;, &#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;Holy Damage&#039;),&lt;br /&gt;
    swatch(&#039;Fire&#039;, &#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;Fire Damage&#039;),&lt;br /&gt;
    swatch(&#039;Electric&#039;, &#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;Electric Damage&#039;),&lt;br /&gt;
    &#039;DPS&#039;, &#039;Atk Speed&#039;, &#039;Crit&#039;, &#039;Range&#039;, &#039;Pierce&#039;, &#039;Proj.&#039;, &#039;Bounce&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    if Core.weaponClass(rec) == class then&lt;br /&gt;
      local cells = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        numcell(rec.tier),&lt;br /&gt;
        numcell(rec.price),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Holy&#039;, stat(rec, &#039;Holy Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Fire&#039;, stat(rec, &#039;Fire Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Electric&#039;, stat(rec, &#039;Electric Damage&#039;)),&lt;br /&gt;
        numcell(totalDPS(rec)),&lt;br /&gt;
        numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Pierce&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Projectiles&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Bounce&#039;)),&lt;br /&gt;
      }&lt;br /&gt;
      local elements = {}&lt;br /&gt;
      for _, e in ipairs({{&#039;holy&#039;, &#039;Holy Damage&#039;}, {&#039;fire&#039;, &#039;Fire Damage&#039;},&lt;br /&gt;
                          {&#039;electric&#039;, &#039;Electric Damage&#039;}}) do&lt;br /&gt;
        local v = nstat(rec, e[2])&lt;br /&gt;
        if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then elements[#elements + 1] = e[1] end&lt;br /&gt;
      end&lt;br /&gt;
      local attrs = &#039; data-element=&amp;quot;&#039; .. (#elements &amp;gt; 0 and table.concat(elements, &#039; &#039;) or &#039;none&#039;)&lt;br /&gt;
        .. &#039;&amp;quot; data-class=&amp;quot;&#039; .. class:lower()&lt;br /&gt;
        .. &#039;&amp;quot; data-tier=&amp;quot;&#039; .. tostring(rec.tier or &#039;&#039;) .. &#039;&amp;quot;&#039;&lt;br /&gt;
      rows[#rows + 1] = {a = attrs, c = cells}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  -- DPS descending (cell 9 after the Thumbnail/Name split; stable:&lt;br /&gt;
  -- name asc tiebreak via the Name cell sortkey, cell 2).&lt;br /&gt;
  table.sort(rows, function(x, y)&lt;br /&gt;
    local dx, dy = x.c[9].s or 0, y.c[9].s or 0&lt;br /&gt;
    if dx ~= dy then return dx &amp;gt; dy end&lt;br /&gt;
    return (x.c[2].s or &#039;&#039;) &amp;lt; (y.c[2].s or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function enemyRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health&#039;, &#039;HP/Wave&#039;, &#039;HP @ W10&#039;, &#039;Damage&#039;,&lt;br /&gt;
                   &#039;Dmg/Wave&#039;, &#039;Dmg @ W10&#039;, &#039;Speed&#039;, &#039;Resources&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Enemies&#039;)) do&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, k in ipairs(rec.tags or {}) do keys[#keys + 1] = k:lower() end&lt;br /&gt;
    rows[#rows + 1] = {&lt;br /&gt;
      a = &#039; data-type=&amp;quot;&#039; .. (#keys &amp;gt; 0 and table.concat(keys, &#039; &#039;) or &#039;other&#039;) .. &#039;&amp;quot;&#039;,&lt;br /&gt;
      c = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        plaincell(stat(rec, &#039;Health&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;HP per Wave&#039;)),&lt;br /&gt;
        plaincell(wave10(rec, &#039;Health&#039;, &#039;HP per Wave&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        plaincell(wave10(rec, &#039;Damage&#039;, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
        plaincell(stat(rec, &#039;Resources&#039;)),&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function characterRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health Points&#039;, &#039;Damage&#039;, &#039;Critical Chance&#039;,&lt;br /&gt;
                   &#039;Attack Speed&#039;, &#039;Range&#039;, &#039;Speed&#039;, &#039;Armor&#039;, &#039;Dodge&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Characters&#039;)) do&lt;br /&gt;
    rows[#rows + 1] = {a = &#039;&#039;, c = {&lt;br /&gt;
      iconcell(rec),&lt;br /&gt;
      namecell(rec),&lt;br /&gt;
      plaincell(stat(rec, &#039;Health Points&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Armor&#039;)),&lt;br /&gt;
      plaincell(stat(rec, &#039;Dodge&#039;)),&lt;br /&gt;
    }}&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DATASETS = {&lt;br /&gt;
  Weapons_Melee = function() return weaponRows(&#039;Melee&#039;) end,&lt;br /&gt;
  Weapons_Ranged = function() return weaponRows(&#039;Ranged&#039;) end,&lt;br /&gt;
  Enemies = enemyRows,&lt;br /&gt;
  Characters = characterRows,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- renderer --------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function cell(c)&lt;br /&gt;
  local t = tostring(c.t or &#039;—&#039;)&lt;br /&gt;
  if c.s ~= nil then&lt;br /&gt;
    local s = c.s&lt;br /&gt;
    if type(s) == &#039;number&#039; then s = Core.fmtNum(s) end&lt;br /&gt;
    return &#039;| data-sort-value=&amp;quot;&#039; .. tostring(s) .. &#039;&amp;quot; | &#039; .. t&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;| &#039; .. t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local ds = frame.args[1] or frame.args.cat&lt;br /&gt;
  if not ds or ds == &#039;&#039; then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: missing dataset arg&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  ds = ds:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  local build = DATASETS[ds]&lt;br /&gt;
  if not build then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: unknown dataset &#039;&lt;br /&gt;
      .. mw.text.nowiki(ds) .. &#039;&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local headers, rows = build()&lt;br /&gt;
  local out = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;&#039; .. TBL_STYLE .. &#039;&amp;quot;&#039; }&lt;br /&gt;
  out[#out+1] = &#039;|-&#039;&lt;br /&gt;
  for _, h in ipairs(headers) do&lt;br /&gt;
    local label, attrs = h, &#039;&#039;&lt;br /&gt;
    if type(h) == &#039;table&#039; then&lt;br /&gt;
      label = h[1]&lt;br /&gt;
      if h.unsortable then attrs = &#039; class=&amp;quot;unsortable&amp;quot;&#039; end&lt;br /&gt;
    end&lt;br /&gt;
    out[#out+1] = &#039;!&#039; .. attrs .. &#039; style=&amp;quot;&#039; .. TH_STYLE .. &#039;&amp;quot; | &#039;&lt;br /&gt;
      .. headerLink(tostring(label))&lt;br /&gt;
  end&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039; .. tostring(row.a or &#039;&#039;)&lt;br /&gt;
    for _, c in ipairs(row.c) do&lt;br /&gt;
      out[#out+1] = cell(c)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Help:Wiki_Editing&amp;diff=2124</id>
		<title>Help:Wiki Editing</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Help:Wiki_Editing&amp;diff=2124"/>
		<updated>2026-06-10T19:36:17Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Enemies page = enemy comparison table + Thumbnail column; Enemy comparison -&amp;gt; redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- Bot-published page. Source of truth: 5-build-pages/code/_game/help_wiki_editing.wiki&lt;br /&gt;
     (substituted + copied by emit_pages.py). Direct edits to this wiki page are&lt;br /&gt;
     overwritten on the next re-publish — improve the source instead. --&amp;gt;&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-|How this wiki works=&lt;br /&gt;
== The one-sentence version ==&lt;br /&gt;
&#039;&#039;&#039;All data on this wiki lives in ten JSON pages — &amp;lt;code&amp;gt;Data:Weapons.json&amp;lt;/code&amp;gt; through &amp;lt;code&amp;gt;Data:Tags.json&amp;lt;/code&amp;gt; — and every table, infobox, navbox, tag page and stat page is computed from them by Lua modules at render time.&#039;&#039;&#039; There are no precomputed pages: edit a value once on a Data page, purge, and every view of that value updates everywhere.&lt;br /&gt;
&lt;br /&gt;
== Data flow ==&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);border:1px solid var(--panel-border, #3a3c44);&amp;quot;&amp;gt;&lt;br /&gt;
 SOURCE OF TRUTH (edit values here)&lt;br /&gt;
 ┌────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │ Data:Weapons.json   Data:Characters.json   Data:Blessings.json │&lt;br /&gt;
 │ Data:Upgrades.json  Data:Crux.json         Data:Passives.json  │&lt;br /&gt;
 │ Data:Enemies.json   Data:Achievements.json Data:Codex.json     │&lt;br /&gt;
 │ Data:Tags.json                                                 │&lt;br /&gt;
 └───────────────┬────────────────────────────────────────────────┘&lt;br /&gt;
                 │  read at render time by&lt;br /&gt;
                 ▼&lt;br /&gt;
 ┌─ Module:Core ──────────────────────────────────────────────────┐&lt;br /&gt;
 │  loads + sorts records, computes infobox values, tag links,    │&lt;br /&gt;
 │  stat blocks, lore joins; shared by every module below         │&lt;br /&gt;
 └───┬───────────┬──────────┬───────────┬───────────┬─────────────┘&lt;br /&gt;
     ▼           ▼          ▼           ▼           ▼&lt;br /&gt;
 Module:      Module:    Module:     Module:     Module:&lt;br /&gt;
 &amp;lt;Category&amp;gt;   Compare    Navbox      TagIndex    StatIndex&lt;br /&gt;
 (infobox,    (DPS,      (bottom     (tag        (per-stat +&lt;br /&gt;
  body,        wave-10    navboxes)   member      damage-type&lt;br /&gt;
  index)       tables)                pages)      lookups)&lt;br /&gt;
     │           │          │           │           │&lt;br /&gt;
     ▼           ▼          ▼           ▼           ▼&lt;br /&gt;
            RENDERED PAGES (recomputed on every purge)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to edit, where ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | You want to change… !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Edit this !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | What happens&lt;br /&gt;
|-&lt;br /&gt;
| A &#039;&#039;&#039;value&#039;&#039;&#039;: a stat number, price, tier, lore text, tag key, icon filename || The record in the matching &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; page || Every infobox, index row, comparison table, navbox and lookup that shows the value updates after a cache purge. Don&#039;t rename &#039;&#039;&#039;field keys&#039;&#039;&#039; — modules look them up by name.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Prose&#039;&#039;&#039; on one entity&#039;s page: intro text, strategy notes, screenshots, trivia || The detail page itself (e.g. &amp;lt;code&amp;gt;Cutter&amp;lt;/code&amp;gt;) || Detail pages are first-write-only: the bot never overwrites them, so your edits persist across re-publishes.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;How&#039;&#039;&#039; something renders: column order, section layout, sort order, labels || The Lua module — [[Module:Core]] for most things (infobox field lists, index columns, stat display order, sorting); [[Module:Compare]] / [[Module:Navbox]] / etc. for their own views || All affected pages re-render on purge. Note: module edits are overwritten by the next scheduled bot re-publish, so durable changes should also be reported upstream.&lt;br /&gt;
|-&lt;br /&gt;
| The &#039;&#039;&#039;infobox panel&#039;&#039;&#039; look (rows, header, fallbacks) || &amp;lt;code&amp;gt;Template:&amp;amp;lt;Category&amp;amp;gt;_infobox&amp;lt;/code&amp;gt; || Presentation only — values are computed by Module:Core and passed in as parameters.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Site-wide look&#039;&#039;&#039;: colors, theme tokens, filter chips, hover tooltips || [[MediaWiki:Common.css]] / [[MediaWiki:Common.js]] || Theme tokens (&amp;lt;code&amp;gt;--infobox-*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--table-*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--dmg-*&amp;lt;/code&amp;gt;) restyle everything at once; inline fallbacks keep pages readable without them.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Seeing your edit ==&lt;br /&gt;
MediaWiki caches rendered pages. After editing a Data page or module, other pages that use it may show the old version until their cache expires. To force it: open the page and add &amp;lt;code&amp;gt;?action=purge&amp;lt;/code&amp;gt; to the URL (or use the &amp;quot;Purge&amp;quot; option). The job queue eventually refreshes everything on its own.&lt;br /&gt;
&lt;br /&gt;
== What the bot does on re-publish ==&lt;br /&gt;
* &#039;&#039;&#039;Overwrites&#039;&#039;&#039; the framework layer: &amp;lt;code&amp;gt;Module:*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Template:*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data:*&amp;lt;/code&amp;gt; pages, category index pages, comparison/stat pages, and this help page. Value edits you made on Data pages are replaced by the game&#039;s current data — report data corrections to the developers so they land in the source.&lt;br /&gt;
* &#039;&#039;&#039;Never touches&#039;&#039;&#039; existing detail pages (one per weapon / hero / blessing / …). Your prose, images and trivia are safe there.&lt;br /&gt;
&lt;br /&gt;
|-|Module catalogue=&lt;br /&gt;
One expandable entry per module. Every module is readable on its &amp;lt;code&amp;gt;Module:&amp;lt;/code&amp;gt; page and carries the same documentation in its header comment.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue-controls&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;wm-expand-all&amp;quot;&amp;gt;▸ Expand all&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;quot;wm-collapse-all&amp;quot;&amp;gt;▾ Collapse all&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Core]] — the engine room: computes every infobox value, index table, tag link, sort order and lore join&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| The shared library every other module &amp;lt;code&amp;gt;require()&amp;lt;/code&amp;gt;s. Loads and memoizes the ten Data pages, resolves category names, computes infobox value sets, stat blocks (with glyphs and elemental color chips), tag links and membership maps, codex lore joins, per-category sort orders, and the per-category presentation config (infobox field lists in &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER&amp;lt;/code&amp;gt;, index columns in &amp;lt;code&amp;gt;INDEX_OPTS&amp;lt;/code&amp;gt;, stat display order in &amp;lt;code&amp;gt;STAT_ORDER&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Not invoked directly — the per-category modules forward to its entry points (&amp;lt;code&amp;gt;infoboxEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bodyEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;indexEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renderEntry&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| All ten &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; pages.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Module:Weapons … Module:Tags (per-category modules) — infobox / body / index / crossRefs for one category&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What they do&lt;br /&gt;
| One tiny module per category ([[Module:Weapons]], [[Module:Characters]], [[Module:Blessings]], [[Module:Upgrades]], [[Module:Crux]], [[Module:Passives]], [[Module:Enemies]], [[Module:Achievements]], [[Module:Codex]], [[Module:Tags]]). Each only binds its category name and forwards to Module:Core — so all rendering logic lives in ONE place. [[Module:Codex]] additionally provides the shared &amp;lt;code&amp;gt;lore&amp;lt;/code&amp;gt; lookup (see its entry below).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|infobox|id=Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — the infobox panel&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|body|id=Cutter|section=Tags}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — one body section (omit &amp;lt;code&amp;gt;section&amp;lt;/code&amp;gt; for all)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|index}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — the sortable category table&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — variant / tag-member links&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|render|id=Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — infobox + body + crossRefs in one call&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Read&lt;br /&gt;
| The category&#039;s own &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; (via Module:Core).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Index]] — generic sortable-table renderer behind every category index&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Pure presentation: given records + a column spec, emits one sortable wikitable. Holds no data — Module:Core&#039;s &amp;lt;code&amp;gt;indexEntry&amp;lt;/code&amp;gt; feeds it sorted records augmented with computed infobox values. Column lists per category live in Module:Core&#039;s &amp;lt;code&amp;gt;INDEX_OPTS&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Not invoked directly; pages use &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|index}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Whatever records Module:Core passes it.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Compare]] — side-by-side comparison tables with computed DPS and wave-10 columns&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Builds the comparison tables on [[Weapons]], [[Enemies]] (which hosts the enemy comparison table with wave-10 projections; &#039;&#039;Enemy comparison&#039;&#039; redirects there), [[Weapon comparison]] and [[Hero comparison]]. Every table opens with an unsortable Thumbnail column followed by a sortable linked Name column. Computes columns that are stored nowhere: weapon &#039;&#039;&#039;DPS&#039;&#039;&#039; = (Damage + Holy + Fire + Electric) × Attack Speed; enemy &#039;&#039;&#039;wave-10 projections&#039;&#039;&#039; = base + per-wave × 9. Rows carry &amp;lt;code&amp;gt;data-element/class/tier/type&amp;lt;/code&amp;gt; attributes that power the JS filter chips.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Weapons_Melee}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Weapons_Ranged}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Enemies}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Characters}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Weapons.json]], [[Data:Enemies.json]], [[Data:Characters.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Navbox]] — the collapsible navigation box at the bottom of detail pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders a category&#039;s full member list as icon + link chains, bucketed into rows (Weapons/Blessings/Upgrades/Passives by tier, Crux/Codex by type, Characters/Enemies flat). The per-category grouping config is the &amp;lt;code&amp;gt;CONFIG&amp;lt;/code&amp;gt; table at the top of the module.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Pages use the wrapper templates: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Crux}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, … (each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Navbox|render|&amp;lt;Category&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| The named category&#039;s &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Iconbox]] — an entity&#039;s small icon + linked name, anywhere&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Looks up an entity by category + name (or bare name) and renders its icon plus a link to its page. Icons come from each entity&#039;s own record — there is no icon lookup table. Unknown entities or missing art degrade to a plain link.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Via [[Template:Iconbox]]: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter|icononly}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Blessing|Sanctified Halo|size=32}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · bare-name fallback &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Any of the ten &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; pages, depending on the call.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:CrossRef]] — &amp;quot;related entities&amp;quot; sections (base ↔ Plus variants, tag members)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Computes the &#039;Base variant&#039; / &#039;Upgraded variant&#039; links between a blessing or upgrade and its &#039;&amp;lt;X&amp;gt; Plus&#039; version (matched on the raw &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;, because Upgrades&#039; display names can differ from ids), and delegates Tag pages&#039; member sections to Module:TagIndex.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Wired by the category modules: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Blessings.json]], [[Data:Upgrades.json]]; tag-bearing categories via Module:TagIndex.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:TagIndex]] — &amp;quot;everything that carries this tag&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders the member sections on Tag pages (&#039;Weapons with this tag&#039;, …). Membership is never stored — it scans every tag-bearing category&#039;s &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays at render time, so adding a tag key to any record updates the tag page on purge.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:TagIndex|members|Melee}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (bare key or &amp;lt;code&amp;gt;Tag-Melee&amp;lt;/code&amp;gt; slug)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Tags.json]] + [[Data:Weapons.json]], [[Data:Characters.json]], [[Data:Blessings.json]], [[Data:Upgrades.json]], [[Data:Passives.json]], [[Data:Enemies.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:StatIndex]] — &amp;quot;what modifies this stat?&amp;quot; reverse lookups&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders the lookup tables on per-stat pages ([[Armor]], [[Speed]], …) and the damage-type hubs: every hero / blessing / upgrade / passive / Crux power modifying a stat (sorted by amount), and every weapon dealing an elemental damage type.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:StatIndex|boosters|Armor}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:StatIndex|weapons|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| boosters: [[Data:Characters.json]], [[Data:Blessings.json]], [[Data:Upgrades.json]], [[Data:Passives.json]], [[Data:Crux.json]] — weapons: [[Data:Weapons.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:DamageType]] — readable aliases for the damage-type hub pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Thin wrappers over Module:StatIndex so the [[Holy Damage]] / [[Fire Damage]] / [[Electric Damage]] hubs read naturally. No logic of its own.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:DamageType|dealtBy|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:DamageType|boostedBy|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Same as Module:StatIndex.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Codex]] — Codex category module + the shared lore lookup&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Standard category rendering for Codex pages, plus the &amp;lt;code&amp;gt;lore&amp;lt;/code&amp;gt; entry point that powers &#039;== Lore ==&#039; sections on weapon / enemy / character pages: it finds the matching Codex entry by normalized name at render time, falling back to the entity&#039;s own &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;. Edit the lore once in [[Data:Codex.json]] and every page showing it updates.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|lore|Cutter|Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (category-scoped) · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|lore|Azrael}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (cross-category)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Codex.json]] + the named entity&#039;s category Data page.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-|Template catalogue=&lt;br /&gt;
One expandable entry per user-facing template (or template family). Infobox values and navbox rows are computed by the modules — these templates are the presentation wrappers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue-controls&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;wm-expand-all&amp;quot;&amp;gt;▸ Expand all&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;quot;wm-collapse-all&amp;quot;&amp;gt;▾ Collapse all&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Template:Iconbox]] — inline entity icon + linked name&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → icon + linked name. Add &amp;lt;code&amp;gt;icononly&amp;lt;/code&amp;gt; for just the glyph: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter|icononly}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;. Optional &amp;lt;code&amp;gt;size=32&amp;lt;/code&amp;gt;. Category accepts singular or plural (Weapon, Character, Blessing, Upgrade, Crux, Passive, Enemy, Achievement, Codex, Tag); bare-name form &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; scans all categories. The go-to template for mentioning any entity in prose.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Backed by&lt;br /&gt;
| [[Module:Iconbox]] → the entity&#039;s own record in &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Template:Tip]] — highlighted gameplay-advice callout&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Tip|Pierce-heavy builds love this weapon — pair it with Penetrating Light.}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → a green-edged callout box. Use on detail pages for strategy notes that should stand out from lore.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:&amp;amp;lt;Category&amp;amp;gt;_infobox (10 templates) — the infobox panel on every detail page&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| Never called by hand. Detail pages call &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;; Module:Core computes the values from the Data page and passes them in. The templates ([[Template:Weapons_infobox]], [[Template:Characters_infobox]], [[Template:Blessings_infobox]], [[Template:Upgrades_infobox]], [[Template:Crux_infobox]], [[Template:Passives_infobox]], [[Template:Enemies_infobox]], [[Template:Achievements_infobox]], [[Template:Codex_infobox]], [[Template:Tags_infobox]]) own only the presentation: row layout, tier/type-colored headers, em-dash fallbacks. Each template&#039;s own page documents its parameters and the safe row shape.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:Navbox_&amp;amp;lt;Category&amp;amp;gt; (8 templates) — bottom-of-page navigation&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; at the bottom of a detail page. Available: [[Template:Navbox_Weapons]], [[Template:Navbox_Characters]], [[Template:Navbox_Blessings]], [[Template:Navbox_Upgrades]], [[Template:Navbox_Crux]], [[Template:Navbox_Passives]], [[Template:Navbox_Enemies]], [[Template:Navbox_Codex]]. Each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Navbox|render|&amp;lt;Category&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — rows are computed from the category&#039;s Data page, so new entities appear automatically.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:&amp;amp;lt;Category&amp;amp;gt;_Tags (5 templates) — the &#039;== Tags ==&#039; section on detail pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Weapons_Tags|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → the tag list with effect text for one record. Available for Weapons, Characters, Blessings, Upgrades, Passives. Each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|body|id={{{1}}}|section=Tags}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; so the section can be restyled in one place.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Utility templates (general-purpose page furniture)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:170px;&amp;quot; | [[Template:Stub]]&lt;br /&gt;
| Banner for short pages needing expansion (auto-categorizes).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:NeedsImage]]&lt;br /&gt;
| Banner for pages missing art.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Disambig]]&lt;br /&gt;
| Marks disambiguation pages.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Quote]]&lt;br /&gt;
| Block quotation with attribution.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:GameVersion]]&lt;br /&gt;
| Inline version stamp: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{GameVersion|1.2.3}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → &amp;quot;As of v1.2.3&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Spoiler]]&lt;br /&gt;
| Collapsible spoiler block.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Color]]&lt;br /&gt;
| Inline colored text: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Color|#ff6b35|burning}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:External]]&lt;br /&gt;
| External link with icon.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Hover]]&lt;br /&gt;
| Plain-text tooltip on hover.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Breadcrumb]]&lt;br /&gt;
| Breadcrumb navigation line.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Transclude]]&lt;br /&gt;
| Transclude a page or labeled section: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Transclude|PageName#Section}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Anchor]]&lt;br /&gt;
| Invisible anchor target for in-page links.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Clear]]&lt;br /&gt;
| Clears floats (e.g. below an infobox).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Documentation]]&lt;br /&gt;
| The &amp;lt;code&amp;gt;{{documentation}}&amp;lt;/code&amp;gt; convention for template doc subpages.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-|Data schemas=&lt;br /&gt;
The ten source Data pages and their REAL record fields. Every Data page also carries this documentation in its own top-level &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; field. Shared by all categories: &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; (the game&#039;s internal id), &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; (the wiki page title of the entity&#039;s detail page — never recompute it from the name), &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; (display name), &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name; empty = no staged art).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue-controls&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;wm-expand-all&amp;quot;&amp;gt;▸ Expand all&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;quot;wm-collapse-all&amp;quot;&amp;gt;▾ Collapse all&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Weapons.json]] — one record per weapon (65)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt; || number || Shop tier 1–3.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt; || number || Shop price.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || boolean || &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; = must be unlocked.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tag_source&amp;lt;/code&amp;gt; || string || The game&#039;s raw tag family (Melee, Laser, Grenade, …). &amp;lt;code&amp;gt;Melee&amp;lt;/code&amp;gt; here (or a Melee tag) makes the weapon class Melee; everything else is Ranged.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag KEYS — join [[Data:Tags.json]] for effect text and tag pages.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Raw numbers keyed by stat name: &amp;lt;code&amp;gt;Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Holy/Fire/Electric Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Attack Speed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Critical Chance&amp;lt;/code&amp;gt; (percent values stay strings like &amp;lt;code&amp;gt;&amp;quot;15%&amp;quot;&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;Range&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Pierce&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Projectiles&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Bounce&amp;lt;/code&amp;gt;. Sentinels: ≥ 1000 renders as &#039;&#039;Unlimited&#039;&#039;, ≤ −1000 is hidden. &#039;&#039;&#039;DPS is not stored&#039;&#039;&#039; — [[Module:Compare]] computes it at render time.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core (infobox/body/index), Module:Compare, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Characters.json]] — one record per playable hero (10)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Unlock metadata; &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; drives the Availability row.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]] (only some heroes carry tags).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat &#039;&#039;&#039;modifiers&#039;&#039;&#039; applied to base stats (e.g. &amp;lt;code&amp;gt;Armor: 6&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Attack Speed&amp;quot;: &amp;quot;-12%&amp;quot;&amp;lt;/code&amp;gt;) — not absolute values.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Lore prose, shown under == Lore ==.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Compare (hero comparison), Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Blessings.json]] — one record per blessing (94)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price, unlock state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers; percent values stay strings (&amp;lt;code&amp;gt;&amp;quot;5%&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Flavor prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;&amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt; Plus&amp;lt;/code&amp;gt; records are upgraded variants: ids pair &amp;lt;code&amp;gt;&amp;amp;lt;x&amp;amp;gt;-plus&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;lt;x&amp;amp;gt;&amp;lt;/code&amp;gt;, linked by [[Module:CrossRef]] at render time. Consumed by Module:Core, Module:CrossRef, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Upgrades.json]] — one record per shop upgrade (88)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price, unlock state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Rarely present (3 of 88).&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;&#039;Caution:&#039;&#039;&#039; &#039;&#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; can differ completely (id &amp;lt;code&amp;gt;Shield Transform&amp;lt;/code&amp;gt; displays as &amp;lt;code&amp;gt;Lifesteal Chamber&amp;lt;/code&amp;gt;) — that&#039;s why &#039;&#039;&amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt; Plus&amp;lt;/code&amp;gt;&#039;&#039; variant pairing matches on the raw id ([[Module:CrossRef]]). Consumed by Module:Core, Module:CrossRef, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Crux.json]] — one record per Crux power (34)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt; || number || Tier.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Unique / Action / Buff / Debuff / Aura&amp;lt;/code&amp;gt; — drives index grouping, navbox rows and the infobox header color.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Acquired / Unlocked / Locked&amp;lt;/code&amp;gt; → rendered as the Availability row.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Effect prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Navbox, Module:StatIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Passives.json]] — one record per passive (55)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price; every passive starts locked.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Flavor prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Ranked families (Armor I/II/III) are grouped consecutively on the index by Module:Core&#039;s sort — the stored order doesn&#039;t matter. Consumed by Module:Core, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Enemies.json]] — one record per enemy type (27)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || &amp;lt;code&amp;gt;Health&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;HP per Wave&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Damage per Wave&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Speed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Resources&amp;lt;/code&amp;gt;. Per-wave values scale linearly; wave-10 projections are computed by [[Module:Compare]], not stored.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Classification keys (&amp;lt;code&amp;gt;Melee / Range / Big / Boss&amp;lt;/code&amp;gt;) → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The enemy&#039;s own lore. When a Codex entry matches by name, [[Module:Codex]] shows the fuller codex text instead at render time.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Compare, Module:Navbox, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Achievements.json]] — one record per Steam achievement (100)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The unlock objective (e.g. &amp;quot;Kill 10,000 enemies&amp;quot;) — rendered as both the infobox Objective row and the == Objective == section.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core (infobox/index) and Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Codex.json]] — one record per encyclopedia entry (90)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Enemies / Weapons / Events / Tips&amp;lt;/code&amp;gt; — drives index grouping and navbox rows.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The full lore text.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;redirect_slug&amp;lt;/code&amp;gt; || string (optional) || Present when the entry matched a weapon/enemy by name: &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; then points at the ENTITY&#039;s page (which inlines this lore), and &amp;lt;code&amp;gt;redirect_slug&amp;lt;/code&amp;gt; is the old &amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt;-Codex&amp;lt;/code&amp;gt; title kept as a redirect.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Tips entries named after a stat (e.g. &amp;quot;Holy Damage&amp;quot;) are that stat&#039;s hub page. [[Module:Codex]] joins entries to weapons/enemies by normalized name at render time. Consumed by Module:Core, Module:Codex, Module:Navbox, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Tags.json]] — one record per tag / perk keyword (84)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; || string || The raw tag KEY exactly as used in other categories&#039; &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays (e.g. &amp;lt;code&amp;gt;BreakArmor&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; || string || Tag page title, always &amp;lt;code&amp;gt;Tag-&amp;amp;lt;Key&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt; || string || The effect text; empty when the game data ships none.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Which entities carry a tag is NOT stored here — Module:Core scans the other categories&#039; &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays at render time (rendered by [[Module:TagIndex]]). Consumed by Module:Core (tag links + effect text everywhere), Module:TagIndex, Module:CrossRef.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-|MediaWiki Guide=&lt;br /&gt;
{{:Help:MediaWiki Guide}}&lt;br /&gt;
&lt;br /&gt;
|-|Special Pages=&lt;br /&gt;
This tab highlights MediaWiki&#039;s special pages most useful for working on this wiki.&lt;br /&gt;
&lt;br /&gt;
* [[Special:SpecialPages]] — List of all special pages.&lt;br /&gt;
* [[Special:WantedPages]] — Pages linked-to but missing. After a scheduled re-publish, this should be near-zero (every bot-published link target exists). Non-zero usually means an editor-added link points at a non-existent page.&lt;br /&gt;
* [[Special:WantedTemplates]] — Templates called but missing. Should be exactly zero on this wiki.&lt;br /&gt;
* [[Special:WantedFiles]] — Image files referenced but never uploaded. Should be near-zero after a clean re-publish.&lt;br /&gt;
* [[Special:WhatLinksHere]] — Reverse-lookup: which pages link to a given page. Useful for &amp;quot;where is this template used?&amp;quot;&lt;br /&gt;
* [[Special:Categories]] — All categories on the wiki.&lt;br /&gt;
* [[Special:UncategorizedPages]] — Main-namespace pages without a &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;[[Category:...]]&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; tag. Should be near-zero — unexpected entries here are bot-publishing bugs worth reporting.&lt;br /&gt;
* [[Special:RecentChanges]] — Live feed of edits.&lt;br /&gt;
* [[MediaWiki:Sidebar]] — Edit the navigation sidebar.&lt;br /&gt;
* [[Special:Upload]] / [[Special:BatchUpload]] — Image upload (single + multi).&lt;br /&gt;
* [[Special:Version]] — Installed MediaWiki extensions, skins, libraries, and version info.&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Help]]&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Category:Enemies&amp;diff=2123</id>
		<title>Category:Enemies</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Category:Enemies&amp;diff=2123"/>
		<updated>2026-06-10T19:36:16Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Enemies page = enemy comparison table + Thumbnail column; Enemy comparison -&amp;gt; redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The 27 enemy types with base stats, per-wave scaling and &#039;&#039;&#039;wave-10 projections&#039;&#039;&#039; (base + per-wave × 9). Sort by HP @ W10 to see the late-game tanks, or use the filter chips to narrow by enemy type. Click through for each enemy&#039;s full stat block and lore.&lt;br /&gt;
&lt;br /&gt;
See the curated, sortable [[Enemies]] index for the full table. The pages below are listed alphabetically.&lt;br /&gt;
&lt;br /&gt;
{{Navbox_Enemies}}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;small&amp;gt;See the [[Glossary]] for term definitions and [[Stats]] for the full stat reference.&amp;lt;/small&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Enemy_comparison&amp;diff=2122</id>
		<title>Enemy comparison</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Enemy_comparison&amp;diff=2122"/>
		<updated>2026-06-10T19:36:15Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Enemies page = enemy comparison table + Thumbnail column; Enemy comparison -&amp;gt; redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Enemies]]&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Enemies&amp;diff=2121</id>
		<title>Enemies</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Enemies&amp;diff=2121"/>
		<updated>2026-06-10T19:36:14Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Enemies page = enemy comparison table + Thumbnail column; Enemy comparison -&amp;gt; redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#seo:|description=The 27 enemy types with base stats, per-wave scaling and wave-10 projections (base + per-wave × 9).|og:type=article}}&lt;br /&gt;
The 27 enemy types with base stats, per-wave scaling and &#039;&#039;&#039;wave-10 projections&#039;&#039;&#039; (base + per-wave × 9). Sort by HP @ W10 to see the late-game tanks, or use the filter chips to narrow by enemy type. Click through for each enemy&#039;s full stat block and lore.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-filterbar&amp;quot; style=&amp;quot;display:none;margin:0.6em 0;padding:5px 10px;border:1px solid var(--panel-border, #3a3c44);border-radius:6px;background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-weight:bold;margin-right:8px;&amp;quot;&amp;gt;Filter:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;wm-filtergroup&amp;quot; style=&amp;quot;margin-right:16px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;opacity:0.75;margin-right:4px;&amp;quot;&amp;gt;Type:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;wm-chip&amp;quot; data-group=&amp;quot;type&amp;quot; data-val=&amp;quot;melee&amp;quot; style=&amp;quot;display:inline-block;cursor:pointer;padding:1px 10px;margin:2px 4px 2px 0;border-radius:12px;font-size:0.9em;border:1px solid var(--panel-border, #3a3c44);background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);&amp;quot;&amp;gt;Melee&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;wm-chip&amp;quot; data-group=&amp;quot;type&amp;quot; data-val=&amp;quot;range&amp;quot; style=&amp;quot;display:inline-block;cursor:pointer;padding:1px 10px;margin:2px 4px 2px 0;border-radius:12px;font-size:0.9em;border:1px solid var(--panel-border, #3a3c44);background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);&amp;quot;&amp;gt;Ranged&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;wm-chip&amp;quot; data-group=&amp;quot;type&amp;quot; data-val=&amp;quot;big&amp;quot; style=&amp;quot;display:inline-block;cursor:pointer;padding:1px 10px;margin:2px 4px 2px 0;border-radius:12px;font-size:0.9em;border:1px solid var(--panel-border, #3a3c44);background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);&amp;quot;&amp;gt;Big&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;wm-chip&amp;quot; data-group=&amp;quot;type&amp;quot; data-val=&amp;quot;boss&amp;quot; style=&amp;quot;display:inline-block;cursor:pointer;padding:1px 10px;margin:2px 4px 2px 0;border-radius:12px;font-size:0.9em;border:1px solid var(--panel-border, #3a3c44);background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);&amp;quot;&amp;gt;Boss&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;wm-chip wm-chip-clear&amp;quot; style=&amp;quot;display:inline-block;cursor:pointer;padding:1px 10px;margin:2px 0;border-radius:12px;font-size:0.9em;border:1px dashed var(--panel-border, #3a3c44);background:transparent;color:var(--panel-text, #e6e6e6);opacity:0.8;&amp;quot;&amp;gt;Reset&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Compare|render|Enemies}}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;small&amp;gt;See the [[Glossary]] for term definitions and [[Stats]] for the full stat reference.&amp;lt;/small&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2120</id>
		<title>Module:Compare</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2120"/>
		<updated>2026-06-10T19:36:14Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Enemies page = enemy comparison table + Thumbnail column; Enemy comparison -&amp;gt; redirect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Compare — sortable side-by-side comparison tables.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Builds the big comparison tables (used on the Weapons index, the&lt;br /&gt;
--   Enemies index, and the Weapon comparison / Hero comparison pages;&lt;br /&gt;
--   &#039;Enemy comparison&#039; is a redirect to Enemies since 2026-06-10f),&lt;br /&gt;
--   including the COMPUTED columns that are not stored anywhere:&lt;br /&gt;
--   * Weapon DPS = (Damage + Holy + Fire + Electric) x Attack Speed,&lt;br /&gt;
--     rounded to 1 decimal.&lt;br /&gt;
--   * Enemy wave-10 projections = base + per-wave x 9.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Melee}}   melee weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Ranged}}  ranged weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Enemies}}         base + per-wave + wave-10&lt;br /&gt;
--   {{#invoke:Compare|render|Characters}}      hero stat modifiers&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Weapons.json, Data:Enemies.json, Data:Characters.json&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * Every table opens with a Thumbnail column (icon only, header marked&lt;br /&gt;
--     class=&amp;quot;unsortable&amp;quot;) followed by a sortable linked Name column.&lt;br /&gt;
--   * Stat sentinels: a value &amp;gt;= 1000 renders as the infinity sign&lt;br /&gt;
--     (sorts to the top); &amp;lt;= -1000 renders as an em-dash (sorts bottom).&lt;br /&gt;
--   * Each row carries data-element / data-class / data-tier / data-type&lt;br /&gt;
--     attributes — MediaWiki:Common.js uses them for the filter chips&lt;br /&gt;
--     above the tables. Removing them breaks the chip filtering.&lt;br /&gt;
--   * To add a column, add a header in the relevant *Rows() builder and&lt;br /&gt;
--     a matching cell in the same position.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL_STYLE = &#039;font-size:0.92em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&#039;&lt;br /&gt;
local TH_STYLE  = &#039;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&#039;&lt;br /&gt;
&lt;br /&gt;
-- Colored element swatch for column headers. When the damage-type hub&lt;br /&gt;
-- page exists (existence-checked via Core.statPageTarget) the label links&lt;br /&gt;
-- to it; an inner span keeps the element color inside the anchor.&lt;br /&gt;
local function swatch(label, tok, hexv, statName)&lt;br /&gt;
  local inner = label&lt;br /&gt;
  local target = statName and Core.statPageTarget(statName)&lt;br /&gt;
  if target then&lt;br /&gt;
    inner = &#039;[[&#039; .. target .. &#039;|&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039;&lt;br /&gt;
      .. hexv .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. label .. &#039;&amp;lt;/span&amp;gt;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039; .. hexv&lt;br /&gt;
    .. &#039;);font-weight:bold;white-space:nowrap;&amp;quot;&amp;gt;&amp;amp;#9679; &#039; .. inner .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Abbreviated column headers -&amp;gt; the stat key whose page they should link&lt;br /&gt;
-- to. Full-name headers resolve via Core.statPageTarget directly.&lt;br /&gt;
local HDR_STAT = {&lt;br /&gt;
  [&#039;Atk Speed&#039;] = &#039;Attack Speed&#039;,&lt;br /&gt;
  [&#039;Crit&#039;] = &#039;Critical Chance&#039;,&lt;br /&gt;
  [&#039;Proj.&#039;] = &#039;Projectiles&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Link a plain-text column header to its stat reference page when one&lt;br /&gt;
-- exists; non-stat headers (and already-HTML headers like the swatches)&lt;br /&gt;
-- pass through untouched. Page-less stats stay plain — never a redlink.&lt;br /&gt;
local function headerLink(h)&lt;br /&gt;
  if h:find(&#039;[&amp;lt;%[]&#039;) then return h end&lt;br /&gt;
  local target = Core.statPageTarget(HDR_STAT[h] or h)&lt;br /&gt;
  if not target then return h end&lt;br /&gt;
  if target == h then return &#039;[[&#039; .. h .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. h .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coerce a raw stat value to {display, sortval}: &#039;15%&#039; keeps the %&lt;br /&gt;
-- sign for display but sorts by the number 15.&lt;br /&gt;
local function num(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;∞&#039;, 1e9 end&lt;br /&gt;
    if v &amp;lt;= -1000 then return &#039;—&#039;, -1 end&lt;br /&gt;
    return Core.fmtNum(v), v&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numcell(v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DMG = {&lt;br /&gt;
  Holy = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  Fire = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  Electric = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chipcell(dmg, v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  if s == nil or s &amp;lt;= 0 or t == &#039;—&#039; then return {t = &#039;—&#039;, s = 0} end&lt;br /&gt;
  local c = DMG[dmg]&lt;br /&gt;
  return {t = &#039;&amp;lt;span class=&amp;quot;&#039; .. c[3] .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. c[1] .. &#039;, &#039;&lt;br /&gt;
    .. c[2] .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Header marker for the icon-only Thumbnail column: the renderer emits&lt;br /&gt;
-- class=&amp;quot;unsortable&amp;quot; on this th so the sort arrows skip it.&lt;br /&gt;
local THUMB = {&#039;Thumbnail&#039;, unsortable = true}&lt;br /&gt;
&lt;br /&gt;
-- Icon-only Thumbnail cell (links to the entity page; em-dash when the&lt;br /&gt;
-- record has no icon). 48px matches the category-index thumbnail size.&lt;br /&gt;
local function iconcell(rec)&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
    return {t = &#039;[[File:&#039; .. rec.icon .. &#039;|48px|link=&#039; .. slug&lt;br /&gt;
      .. &#039;|alt=&#039; .. (rec.name or slug) .. &#039;]]&#039;}&lt;br /&gt;
  end&lt;br /&gt;
  return {t = &#039;—&#039;}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Linked-name cell, sortable by lowercased name, wrapped in .wm-tip&lt;br /&gt;
-- (Common.js hover-infobox popup).&lt;br /&gt;
local function namecell(rec)&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local t = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  return {t = t, s = mw.ustring.lower(name)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat(rec, k)&lt;br /&gt;
  return (rec.stats or {})[k]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function nstat(rec, k)&lt;br /&gt;
  return Core.numStat(stat(rec, k))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalDPS(rec)&lt;br /&gt;
  local s = rec.stats or {}&lt;br /&gt;
  local atk = Core.numStat(s[&#039;Attack Speed&#039;])&lt;br /&gt;
  if atk == nil then return nil end&lt;br /&gt;
  local total = Core.numStat(s[&#039;Damage&#039;]) or 0&lt;br /&gt;
  for _, k in ipairs({&#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;}) do&lt;br /&gt;
    local v = Core.numStat(s[k])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then total = total + v end&lt;br /&gt;
  end&lt;br /&gt;
  return Core.round1(total * atk)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wave10(rec, base, per)&lt;br /&gt;
  local b, pv = nstat(rec, base), nstat(rec, per)&lt;br /&gt;
  if b == nil or pv == nil then return nil end&lt;br /&gt;
  return Core.round1(b + pv * 9)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- dataset builders ----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function weaponRows(class)&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Tier&#039;, &#039;Price&#039;, &#039;Damage&#039;,&lt;br /&gt;
    swatch(&#039;Holy&#039;, &#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;Holy Damage&#039;),&lt;br /&gt;
    swatch(&#039;Fire&#039;, &#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;Fire Damage&#039;),&lt;br /&gt;
    swatch(&#039;Electric&#039;, &#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;Electric Damage&#039;),&lt;br /&gt;
    &#039;DPS&#039;, &#039;Atk Speed&#039;, &#039;Crit&#039;, &#039;Range&#039;, &#039;Pierce&#039;, &#039;Proj.&#039;, &#039;Bounce&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    if Core.weaponClass(rec) == class then&lt;br /&gt;
      local cells = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        numcell(rec.tier),&lt;br /&gt;
        numcell(rec.price),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Holy&#039;, stat(rec, &#039;Holy Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Fire&#039;, stat(rec, &#039;Fire Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Electric&#039;, stat(rec, &#039;Electric Damage&#039;)),&lt;br /&gt;
        numcell(totalDPS(rec)),&lt;br /&gt;
        numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Pierce&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Projectiles&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Bounce&#039;)),&lt;br /&gt;
      }&lt;br /&gt;
      local elements = {}&lt;br /&gt;
      for _, e in ipairs({{&#039;holy&#039;, &#039;Holy Damage&#039;}, {&#039;fire&#039;, &#039;Fire Damage&#039;},&lt;br /&gt;
                          {&#039;electric&#039;, &#039;Electric Damage&#039;}}) do&lt;br /&gt;
        local v = nstat(rec, e[2])&lt;br /&gt;
        if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then elements[#elements + 1] = e[1] end&lt;br /&gt;
      end&lt;br /&gt;
      local attrs = &#039; data-element=&amp;quot;&#039; .. (#elements &amp;gt; 0 and table.concat(elements, &#039; &#039;) or &#039;none&#039;)&lt;br /&gt;
        .. &#039;&amp;quot; data-class=&amp;quot;&#039; .. class:lower()&lt;br /&gt;
        .. &#039;&amp;quot; data-tier=&amp;quot;&#039; .. tostring(rec.tier or &#039;&#039;) .. &#039;&amp;quot;&#039;&lt;br /&gt;
      rows[#rows + 1] = {a = attrs, c = cells}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  -- DPS descending (cell 9 after the Thumbnail/Name split; stable:&lt;br /&gt;
  -- name asc tiebreak via the Name cell sortkey, cell 2).&lt;br /&gt;
  table.sort(rows, function(x, y)&lt;br /&gt;
    local dx, dy = x.c[9].s or 0, y.c[9].s or 0&lt;br /&gt;
    if dx ~= dy then return dx &amp;gt; dy end&lt;br /&gt;
    return (x.c[2].s or &#039;&#039;) &amp;lt; (y.c[2].s or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function enemyRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health&#039;, &#039;HP/Wave&#039;, &#039;HP @ W10&#039;, &#039;Damage&#039;,&lt;br /&gt;
                   &#039;Dmg/Wave&#039;, &#039;Dmg @ W10&#039;, &#039;Speed&#039;, &#039;Resources&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Enemies&#039;)) do&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, k in ipairs(rec.tags or {}) do keys[#keys + 1] = k:lower() end&lt;br /&gt;
    rows[#rows + 1] = {&lt;br /&gt;
      a = &#039; data-type=&amp;quot;&#039; .. (#keys &amp;gt; 0 and table.concat(keys, &#039; &#039;) or &#039;other&#039;) .. &#039;&amp;quot;&#039;,&lt;br /&gt;
      c = {&lt;br /&gt;
        iconcell(rec),&lt;br /&gt;
        namecell(rec),&lt;br /&gt;
        numcell(stat(rec, &#039;Health&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;HP per Wave&#039;)),&lt;br /&gt;
        numcell(wave10(rec, &#039;Health&#039;, &#039;HP per Wave&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        numcell(wave10(rec, &#039;Damage&#039;, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Resources&#039;)),&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function characterRows()&lt;br /&gt;
  local headers = {THUMB, &#039;Name&#039;, &#039;Health Points&#039;, &#039;Damage&#039;, &#039;Critical Chance&#039;,&lt;br /&gt;
                   &#039;Attack Speed&#039;, &#039;Range&#039;, &#039;Speed&#039;, &#039;Armor&#039;, &#039;Dodge&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Characters&#039;)) do&lt;br /&gt;
    rows[#rows + 1] = {a = &#039;&#039;, c = {&lt;br /&gt;
      iconcell(rec),&lt;br /&gt;
      namecell(rec),&lt;br /&gt;
      numcell(stat(rec, &#039;Health Points&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Armor&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Dodge&#039;)),&lt;br /&gt;
    }}&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DATASETS = {&lt;br /&gt;
  Weapons_Melee = function() return weaponRows(&#039;Melee&#039;) end,&lt;br /&gt;
  Weapons_Ranged = function() return weaponRows(&#039;Ranged&#039;) end,&lt;br /&gt;
  Enemies = enemyRows,&lt;br /&gt;
  Characters = characterRows,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- renderer --------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function cell(c)&lt;br /&gt;
  local t = tostring(c.t or &#039;—&#039;)&lt;br /&gt;
  if c.s ~= nil then&lt;br /&gt;
    local s = c.s&lt;br /&gt;
    if type(s) == &#039;number&#039; then s = Core.fmtNum(s) end&lt;br /&gt;
    return &#039;| data-sort-value=&amp;quot;&#039; .. tostring(s) .. &#039;&amp;quot; | &#039; .. t&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;| &#039; .. t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local ds = frame.args[1] or frame.args.cat&lt;br /&gt;
  if not ds or ds == &#039;&#039; then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: missing dataset arg&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  ds = ds:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  local build = DATASETS[ds]&lt;br /&gt;
  if not build then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: unknown dataset &#039;&lt;br /&gt;
      .. mw.text.nowiki(ds) .. &#039;&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local headers, rows = build()&lt;br /&gt;
  local out = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;&#039; .. TBL_STYLE .. &#039;&amp;quot;&#039; }&lt;br /&gt;
  out[#out+1] = &#039;|-&#039;&lt;br /&gt;
  for _, h in ipairs(headers) do&lt;br /&gt;
    local label, attrs = h, &#039;&#039;&lt;br /&gt;
    if type(h) == &#039;table&#039; then&lt;br /&gt;
      label = h[1]&lt;br /&gt;
      if h.unsortable then attrs = &#039; class=&amp;quot;unsortable&amp;quot;&#039; end&lt;br /&gt;
    end&lt;br /&gt;
    out[#out+1] = &#039;!&#039; .. attrs .. &#039; style=&amp;quot;&#039; .. TH_STYLE .. &#039;&amp;quot; | &#039;&lt;br /&gt;
      .. headerLink(tostring(label))&lt;br /&gt;
  end&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039; .. tostring(row.a or &#039;&#039;)&lt;br /&gt;
    for _, c in ipairs(row.c) do&lt;br /&gt;
      out[#out+1] = cell(c)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Arms_of_God_Wiki&amp;diff=2119</id>
		<title>Arms of God Wiki</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Arms_of_God_Wiki&amp;diff=2119"/>
		<updated>2026-06-10T19:16:35Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: logo change&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; align-items:flex-start; justify-content:center; top; flex-wrap:wrap;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;text-align:center; width:100%; max-width:1100px; display:inline-block; padding:30px;&amp;quot;&amp;gt;&lt;br /&gt;
		[[File:Arms-of-god-main.png|link=https://darkjay.com/armsofgod/|x400px]]&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:center; font-weight:bold;&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-size:32pt;&amp;quot;&amp;gt;Welcome to &amp;lt;i&amp;gt;Arms of God&amp;lt;/i&amp;gt; Wiki&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-size:14pt;&amp;quot;&amp;gt;The comprehensive Arms of God Wiki is written and maintained by the players. [[Special:ActiveUsers|{{NUMBEROFUSERS}} people]] are currently maintaining [[Special:Statistics|{{NUMBEROFPAGES}} pages ({{NUMBEROFARTICLES}} articles)]] on this Wiki so far! Please feel free to contribute by creating new articles or expanding existing ones. &amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&amp;quot;color: green; font-size: 20px;&amp;quot;&amp;gt; &amp;lt;b&amp;gt;New featured articles: [[Weapons]] and [[Blessings]]&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;div&amp;gt;&lt;br /&gt;
			&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt;Content&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Story]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Updates]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Game modes]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Achievements_ForbiddenKnowledge.jpg|link=Achievements|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Achievements]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Community]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; flex-wrap:width:100%; nowrap; justify-content:space-around; border:1px solid #DD952A;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Heroes_primaris.png|link=Characters|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Characters]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Weapons_Cutter.png|link=Weapons|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Weapons]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Blessings_distant_judgment.png|link=Blessings|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Blessings]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Passives_damage_i.png|link=Passives|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Passives]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Upgrades_Incinerator_Coil.png|link=Upgrades|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Upgrades]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Cross_DefaultUpgrade.png|link=Crux|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Crux]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; flex-wrap:width:100%; nowrap; justify-content:space-around; border:1px solid #DD952A;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Stat_Damage.png|link=Stats|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Stats]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Raziel.png|link=Enemies|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Enemies]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Prayers]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;width:100%; max-width:600px; display:inline-block; padding:30px;&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:nowrap; justify-content:space-around;&amp;quot;&amp;gt;&lt;br /&gt;
			[[File:DarkJay_Logo_Color.png|link=https://darkjay.com/|x200px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-bottom:10px;&amp;quot;&amp;gt;About Arms of God&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:left; margin:9px; font-size:14pt; &amp;quot;&amp;gt;Gear up for a battle through hell in this roguelite Bullet Heaven Arena Autoshooter with Doom-inspired gore vibes, metal music, and a satisfying combat feel. Wield five weapons at once, upgrade and merge them to forge indestructible builds, and bring divine justice to a world on the brink of chaos. &amp;lt;br&amp;gt; [https://store.steampowered.com/app/3100310/Arms_of_God/ Buy on Steam.] &amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt; Arms of God Community&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; justify-content:space-evenly; align-items:center;&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;p style=&amp;quot;text-align:center; font-size:14pt;&amp;quot;&amp;gt;Have strategy questions or want to talk to fellow Arms of God players? Check out the official subreddit and Discord server.&amp;lt;/p&amp;gt;&lt;br /&gt;
			[[File:Reddit_logo.png|link=https://www.reddit.com/r/armsofgod/|x90px]]&lt;br /&gt;
			[[File:Discord_logo.png|link=https://discord.com/invite/QeUqjGFCYb|x90px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt;Latest Trailer&amp;lt;/p&amp;gt;&lt;br /&gt;
		{{#evu:https://www.youtube.com/watch?v=jKuVy4DtbXU|alignment=center}}&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:40px;&amp;quot;&amp;gt;Official Links&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:20px;&amp;quot;&amp;gt;&lt;br /&gt;
			[[File:Steam_Logo.png|link=https://store.steampowered.com/app/3100310/Arms_of_God/|x90px]]&lt;br /&gt;
			[[File:Youtube_Logo.png|link=https://www.youtube.com/@darkjaystudio|x120px]]&lt;br /&gt;
			[[File:Twitter_Logo.png|link=https://x.com/ArmsOfGodGame|x120px]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=File:Arms-of-god-main.png&amp;diff=2118</id>
		<title>File:Arms-of-god-main.png</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=File:Arms-of-god-main.png&amp;diff=2118"/>
		<updated>2026-06-10T19:16:17Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: Ta1ha uploaded a new version of File:Arms-of-god-main.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2117</id>
		<title>Module:Compare</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2117"/>
		<updated>2026-06-10T18:49:28Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: linkify stat labels -&amp;gt; stat pages (infobox + comparison/index headers)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Compare — sortable side-by-side comparison tables.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Builds the big comparison tables (used on the Weapons index,&lt;br /&gt;
--   Weapon comparison, Enemy comparison and Hero comparison pages),&lt;br /&gt;
--   including the COMPUTED columns that are not stored anywhere:&lt;br /&gt;
--   * Weapon DPS = (Damage + Holy + Fire + Electric) x Attack Speed,&lt;br /&gt;
--     rounded to 1 decimal.&lt;br /&gt;
--   * Enemy wave-10 projections = base + per-wave x 9.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Melee}}   melee weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Ranged}}  ranged weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Enemies}}         base + per-wave + wave-10&lt;br /&gt;
--   {{#invoke:Compare|render|Characters}}      hero stat modifiers&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Weapons.json, Data:Enemies.json, Data:Characters.json&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * Stat sentinels: a value &amp;gt;= 1000 renders as the infinity sign&lt;br /&gt;
--     (sorts to the top); &amp;lt;= -1000 renders as an em-dash (sorts bottom).&lt;br /&gt;
--   * Each row carries data-element / data-class / data-tier / data-type&lt;br /&gt;
--     attributes — MediaWiki:Common.js uses them for the filter chips&lt;br /&gt;
--     above the tables. Removing them breaks the chip filtering.&lt;br /&gt;
--   * To add a column, add a header in the relevant *Rows() builder and&lt;br /&gt;
--     a matching cell in the same position.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL_STYLE = &#039;font-size:0.92em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&#039;&lt;br /&gt;
local TH_STYLE  = &#039;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&#039;&lt;br /&gt;
&lt;br /&gt;
-- Colored element swatch for column headers. When the damage-type hub&lt;br /&gt;
-- page exists (existence-checked via Core.statPageTarget) the label links&lt;br /&gt;
-- to it; an inner span keeps the element color inside the anchor.&lt;br /&gt;
local function swatch(label, tok, hexv, statName)&lt;br /&gt;
  local inner = label&lt;br /&gt;
  local target = statName and Core.statPageTarget(statName)&lt;br /&gt;
  if target then&lt;br /&gt;
    inner = &#039;[[&#039; .. target .. &#039;|&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039;&lt;br /&gt;
      .. hexv .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. label .. &#039;&amp;lt;/span&amp;gt;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039; .. hexv&lt;br /&gt;
    .. &#039;);font-weight:bold;white-space:nowrap;&amp;quot;&amp;gt;&amp;amp;#9679; &#039; .. inner .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Abbreviated column headers -&amp;gt; the stat key whose page they should link&lt;br /&gt;
-- to. Full-name headers resolve via Core.statPageTarget directly.&lt;br /&gt;
local HDR_STAT = {&lt;br /&gt;
  [&#039;Atk Speed&#039;] = &#039;Attack Speed&#039;,&lt;br /&gt;
  [&#039;Crit&#039;] = &#039;Critical Chance&#039;,&lt;br /&gt;
  [&#039;Proj.&#039;] = &#039;Projectiles&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Link a plain-text column header to its stat reference page when one&lt;br /&gt;
-- exists; non-stat headers (and already-HTML headers like the swatches)&lt;br /&gt;
-- pass through untouched. Page-less stats stay plain — never a redlink.&lt;br /&gt;
local function headerLink(h)&lt;br /&gt;
  if h:find(&#039;[&amp;lt;%[]&#039;) then return h end&lt;br /&gt;
  local target = Core.statPageTarget(HDR_STAT[h] or h)&lt;br /&gt;
  if not target then return h end&lt;br /&gt;
  if target == h then return &#039;[[&#039; .. h .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. h .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coerce a raw stat value to {display, sortval}: &#039;15%&#039; keeps the %&lt;br /&gt;
-- sign for display but sorts by the number 15.&lt;br /&gt;
local function num(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;∞&#039;, 1e9 end&lt;br /&gt;
    if v &amp;lt;= -1000 then return &#039;—&#039;, -1 end&lt;br /&gt;
    return Core.fmtNum(v), v&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numcell(v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DMG = {&lt;br /&gt;
  Holy = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  Fire = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  Electric = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chipcell(dmg, v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  if s == nil or s &amp;lt;= 0 or t == &#039;—&#039; then return {t = &#039;—&#039;, s = 0} end&lt;br /&gt;
  local c = DMG[dmg]&lt;br /&gt;
  return {t = &#039;&amp;lt;span class=&amp;quot;&#039; .. c[3] .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. c[1] .. &#039;, &#039;&lt;br /&gt;
    .. c[2] .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function iconlinkcell(rec)&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local t&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    t = &#039;[[File:&#039; .. rec.icon .. &#039;|24px|link=&#039; .. slug .. &#039;|alt=&#039; .. name&lt;br /&gt;
      .. &#039;]] [[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&#039;&lt;br /&gt;
  else&lt;br /&gt;
    t = &#039;[[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  t = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  return {t = t, s = mw.ustring.lower(name)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat(rec, k)&lt;br /&gt;
  return (rec.stats or {})[k]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function nstat(rec, k)&lt;br /&gt;
  return Core.numStat(stat(rec, k))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalDPS(rec)&lt;br /&gt;
  local s = rec.stats or {}&lt;br /&gt;
  local atk = Core.numStat(s[&#039;Attack Speed&#039;])&lt;br /&gt;
  if atk == nil then return nil end&lt;br /&gt;
  local total = Core.numStat(s[&#039;Damage&#039;]) or 0&lt;br /&gt;
  for _, k in ipairs({&#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;}) do&lt;br /&gt;
    local v = Core.numStat(s[k])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then total = total + v end&lt;br /&gt;
  end&lt;br /&gt;
  return Core.round1(total * atk)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wave10(rec, base, per)&lt;br /&gt;
  local b, pv = nstat(rec, base), nstat(rec, per)&lt;br /&gt;
  if b == nil or pv == nil then return nil end&lt;br /&gt;
  return Core.round1(b + pv * 9)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- dataset builders ----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function weaponRows(class)&lt;br /&gt;
  local headers = {&#039;Weapon&#039;, &#039;Tier&#039;, &#039;Price&#039;, &#039;Damage&#039;,&lt;br /&gt;
    swatch(&#039;Holy&#039;, &#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;Holy Damage&#039;),&lt;br /&gt;
    swatch(&#039;Fire&#039;, &#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;Fire Damage&#039;),&lt;br /&gt;
    swatch(&#039;Electric&#039;, &#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;Electric Damage&#039;),&lt;br /&gt;
    &#039;DPS&#039;, &#039;Atk Speed&#039;, &#039;Crit&#039;, &#039;Range&#039;, &#039;Pierce&#039;, &#039;Proj.&#039;, &#039;Bounce&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    if Core.weaponClass(rec) == class then&lt;br /&gt;
      local cells = {&lt;br /&gt;
        iconlinkcell(rec),&lt;br /&gt;
        numcell(rec.tier),&lt;br /&gt;
        numcell(rec.price),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Holy&#039;, stat(rec, &#039;Holy Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Fire&#039;, stat(rec, &#039;Fire Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Electric&#039;, stat(rec, &#039;Electric Damage&#039;)),&lt;br /&gt;
        numcell(totalDPS(rec)),&lt;br /&gt;
        numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Pierce&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Projectiles&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Bounce&#039;)),&lt;br /&gt;
      }&lt;br /&gt;
      local elements = {}&lt;br /&gt;
      for _, e in ipairs({{&#039;holy&#039;, &#039;Holy Damage&#039;}, {&#039;fire&#039;, &#039;Fire Damage&#039;},&lt;br /&gt;
                          {&#039;electric&#039;, &#039;Electric Damage&#039;}}) do&lt;br /&gt;
        local v = nstat(rec, e[2])&lt;br /&gt;
        if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then elements[#elements + 1] = e[1] end&lt;br /&gt;
      end&lt;br /&gt;
      local attrs = &#039; data-element=&amp;quot;&#039; .. (#elements &amp;gt; 0 and table.concat(elements, &#039; &#039;) or &#039;none&#039;)&lt;br /&gt;
        .. &#039;&amp;quot; data-class=&amp;quot;&#039; .. class:lower()&lt;br /&gt;
        .. &#039;&amp;quot; data-tier=&amp;quot;&#039; .. tostring(rec.tier or &#039;&#039;) .. &#039;&amp;quot;&#039;&lt;br /&gt;
      rows[#rows + 1] = {a = attrs, c = cells}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  -- DPS descending (stable: name asc tiebreak via the first cell sortkey).&lt;br /&gt;
  table.sort(rows, function(x, y)&lt;br /&gt;
    local dx, dy = x.c[8].s or 0, y.c[8].s or 0&lt;br /&gt;
    if dx ~= dy then return dx &amp;gt; dy end&lt;br /&gt;
    return (x.c[1].s or &#039;&#039;) &amp;lt; (y.c[1].s or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function enemyRows()&lt;br /&gt;
  local headers = {&#039;Enemy&#039;, &#039;Health&#039;, &#039;HP/Wave&#039;, &#039;HP @ W10&#039;, &#039;Damage&#039;,&lt;br /&gt;
                   &#039;Dmg/Wave&#039;, &#039;Dmg @ W10&#039;, &#039;Speed&#039;, &#039;Resources&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Enemies&#039;)) do&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, k in ipairs(rec.tags or {}) do keys[#keys + 1] = k:lower() end&lt;br /&gt;
    rows[#rows + 1] = {&lt;br /&gt;
      a = &#039; data-type=&amp;quot;&#039; .. (#keys &amp;gt; 0 and table.concat(keys, &#039; &#039;) or &#039;other&#039;) .. &#039;&amp;quot;&#039;,&lt;br /&gt;
      c = {&lt;br /&gt;
        iconlinkcell(rec),&lt;br /&gt;
        numcell(stat(rec, &#039;Health&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;HP per Wave&#039;)),&lt;br /&gt;
        numcell(wave10(rec, &#039;Health&#039;, &#039;HP per Wave&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        numcell(wave10(rec, &#039;Damage&#039;, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Resources&#039;)),&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function characterRows()&lt;br /&gt;
  local headers = {&#039;Hero&#039;, &#039;Health Points&#039;, &#039;Damage&#039;, &#039;Critical Chance&#039;,&lt;br /&gt;
                   &#039;Attack Speed&#039;, &#039;Range&#039;, &#039;Speed&#039;, &#039;Armor&#039;, &#039;Dodge&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Characters&#039;)) do&lt;br /&gt;
    rows[#rows + 1] = {a = &#039;&#039;, c = {&lt;br /&gt;
      iconlinkcell(rec),&lt;br /&gt;
      numcell(stat(rec, &#039;Health Points&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Armor&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Dodge&#039;)),&lt;br /&gt;
    }}&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DATASETS = {&lt;br /&gt;
  Weapons_Melee = function() return weaponRows(&#039;Melee&#039;) end,&lt;br /&gt;
  Weapons_Ranged = function() return weaponRows(&#039;Ranged&#039;) end,&lt;br /&gt;
  Enemies = enemyRows,&lt;br /&gt;
  Characters = characterRows,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- renderer --------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function cell(c)&lt;br /&gt;
  local t = tostring(c.t or &#039;—&#039;)&lt;br /&gt;
  if c.s ~= nil then&lt;br /&gt;
    local s = c.s&lt;br /&gt;
    if type(s) == &#039;number&#039; then s = Core.fmtNum(s) end&lt;br /&gt;
    return &#039;| data-sort-value=&amp;quot;&#039; .. tostring(s) .. &#039;&amp;quot; | &#039; .. t&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;| &#039; .. t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local ds = frame.args[1] or frame.args.cat&lt;br /&gt;
  if not ds or ds == &#039;&#039; then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: missing dataset arg&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  ds = ds:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  local build = DATASETS[ds]&lt;br /&gt;
  if not build then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: unknown dataset &#039;&lt;br /&gt;
      .. mw.text.nowiki(ds) .. &#039;&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local headers, rows = build()&lt;br /&gt;
  local out = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;&#039; .. TBL_STYLE .. &#039;&amp;quot;&#039; }&lt;br /&gt;
  out[#out+1] = &#039;|-&#039;&lt;br /&gt;
  for _, h in ipairs(headers) do&lt;br /&gt;
    out[#out+1] = &#039;! style=&amp;quot;&#039; .. TH_STYLE .. &#039;&amp;quot; | &#039; .. headerLink(tostring(h))&lt;br /&gt;
  end&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039; .. tostring(row.a or &#039;&#039;)&lt;br /&gt;
    for _, c in ipairs(row.c) do&lt;br /&gt;
      out[#out+1] = cell(c)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Core&amp;diff=2116</id>
		<title>Module:Core</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Core&amp;diff=2116"/>
		<updated>2026-06-10T18:49:26Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: linkify stat labels -&amp;gt; stat pages (infobox + comparison/index headers)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Core — the engine room of this wiki.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Every infobox value, index table, badge, tag link, sort order and&lt;br /&gt;
--   lore join on this wiki is computed here (or in a module that&lt;br /&gt;
--   require()s this one) at render time, from the ten source&lt;br /&gt;
--   Data:&amp;lt;Category&amp;gt;.json pages. There are no precomputed data pages:&lt;br /&gt;
--   edit a value on a Data page, purge, and every view of it updates.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   You normally don&#039;t invoke Core directly. Each category module&lt;br /&gt;
--   (Module:Weapons, Module:Enemies, ...) is a thin shim that binds a&lt;br /&gt;
--   category name and forwards to the entry points at the bottom of&lt;br /&gt;
--   this file:&lt;br /&gt;
--     {{#invoke:Weapons|infobox|id=Cutter}}     -&amp;gt; p.infoboxEntry&lt;br /&gt;
--     {{#invoke:Weapons|body|id=Cutter}}        -&amp;gt; p.bodyEntry&lt;br /&gt;
--     {{#invoke:Weapons|index}}                 -&amp;gt; p.indexEntry&lt;br /&gt;
--     {{#invoke:Weapons|render|id=Cutter}}      -&amp;gt; p.renderEntry (all-in-one)&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (see each page&#039;s own `description` for fields)&lt;br /&gt;
--   Data:Weapons.json, Data:Characters.json, Data:Blessings.json,&lt;br /&gt;
--   Data:Upgrades.json, Data:Crux.json, Data:Passives.json,&lt;br /&gt;
--   Data:Enemies.json, Data:Achievements.json, Data:Codex.json,&lt;br /&gt;
--   Data:Tags.json&lt;br /&gt;
--   Common record fields: id, slug, name, icon, tier, price, locked,&lt;br /&gt;
--   type, state, tags[] (keys into Data:Tags.json), stats{}, description.&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * `slug` IS the page title. Never rebuild it from `name` in Lua —&lt;br /&gt;
--     always read rec.slug.&lt;br /&gt;
--   * Presentation config (stat display order, glyphs, infobox field&lt;br /&gt;
--     lists, index columns, sort keys) lives in the tables below and is&lt;br /&gt;
--     editable on-wiki in this one place.&lt;br /&gt;
--   * Cross-category functions (find, tagMembership, codexFor) load&lt;br /&gt;
--     several Data pages in one parse; the host runs Scribunto with a&lt;br /&gt;
--     raised (256 MiB) memory cap to allow this.&lt;br /&gt;
--   * See Help:Wiki Editing for the full architecture guide.&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- registry&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
p.CATEGORIES = {&lt;br /&gt;
  {name = &#039;Weapons&#039;,      singular = &#039;Weapon&#039;},&lt;br /&gt;
  {name = &#039;Characters&#039;,   singular = &#039;Character&#039;},&lt;br /&gt;
  {name = &#039;Blessings&#039;,    singular = &#039;Blessing&#039;},&lt;br /&gt;
  {name = &#039;Upgrades&#039;,     singular = &#039;Upgrade&#039;},&lt;br /&gt;
  {name = &#039;Crux&#039;,         singular = &#039;Crux&#039;},&lt;br /&gt;
  {name = &#039;Passives&#039;,     singular = &#039;Passive&#039;},&lt;br /&gt;
  {name = &#039;Enemies&#039;,      singular = &#039;Enemy&#039;},&lt;br /&gt;
  {name = &#039;Achievements&#039;, singular = &#039;Achievement&#039;},&lt;br /&gt;
  {name = &#039;Codex&#039;,        singular = &#039;Codex&#039;},&lt;br /&gt;
  {name = &#039;Tags&#039;,         singular = &#039;Tag&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local ALIAS = {}&lt;br /&gt;
for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
  ALIAS[c.name:lower()] = c.name&lt;br /&gt;
  ALIAS[c.singular:lower()] = c.name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;Weapon&#039; / &#039;weapons&#039; / &#039;Weapons&#039; -&amp;gt; &#039;Weapons&#039;; nil when not a category.&lt;br /&gt;
function p.resolveCategory(word)&lt;br /&gt;
  if not word or word == &#039;&#039; then return nil end&lt;br /&gt;
  return ALIAS[(word:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)):lower()]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- data loading (memoized per parse)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
&lt;br /&gt;
-- mw.loadJsonData returns a read-only table with quirky numeric keys;&lt;br /&gt;
-- copy it once into a plain mutable table so the rest of the module can&lt;br /&gt;
-- treat records as ordinary Lua tables.&lt;br /&gt;
local function fix(t)&lt;br /&gt;
  if type(t) ~= &#039;table&#039; then return t end&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for k, v in pairs(t) do&lt;br /&gt;
    out[k] = fix(v)&lt;br /&gt;
    if type(k) == &#039;number&#039; then out[tostring(k)] = out[k] end&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local _data = {}&lt;br /&gt;
function p.load(cat)&lt;br /&gt;
  cat = p.resolveCategory(cat) or cat&lt;br /&gt;
  if _data[cat] == nil then&lt;br /&gt;
    local ok, d = pcall(mw.loadJsonData, &#039;Data:&#039; .. cat .. &#039;.json&#039;)&lt;br /&gt;
    if ok and type(d) == &#039;table&#039; then&lt;br /&gt;
      _data[cat] = fix(d).records or {}&lt;br /&gt;
    else&lt;br /&gt;
      _data[cat] = {}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _data[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local _byId = {}&lt;br /&gt;
function p.byId(cat)&lt;br /&gt;
  cat = p.resolveCategory(cat) or cat&lt;br /&gt;
  if _byId[cat] == nil then&lt;br /&gt;
    local m = {}&lt;br /&gt;
    for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
      if r.id then m[r.id] = r end&lt;br /&gt;
      if r.slug then m[r.slug] = r end&lt;br /&gt;
    end&lt;br /&gt;
    _byId[cat] = m&lt;br /&gt;
  end&lt;br /&gt;
  return _byId[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Iterate every category&#039;s records: returns a list of {cat=&amp;lt;name&amp;gt;, recs=&amp;lt;list&amp;gt;}.&lt;br /&gt;
function p.all()&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
    out[#out + 1] = {cat = c.name, recs = p.load(c.name)}&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- First record across all categories matching slug or (lowercased) name.&lt;br /&gt;
-- Registry order wins on collisions (live entity beats its Codex entry).&lt;br /&gt;
function p.find(key)&lt;br /&gt;
  if not key or key == &#039;&#039; then return nil, nil end&lt;br /&gt;
  local k = mw.ustring.lower(key)&lt;br /&gt;
  for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
    for _, r in ipairs(p.load(c.name)) do&lt;br /&gt;
      if r.slug == key or (r.slug and mw.ustring.lower(r.slug) == k)&lt;br /&gt;
        or (r.name and mw.ustring.lower(r.name) == k) then&lt;br /&gt;
        return c.name, r&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return nil, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- small helpers&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
function p.slugFor(rec) return rec and rec.slug or &#039;&#039; end&lt;br /&gt;
function p.iconFor(rec) return rec and rec.icon or &#039;&#039; end&lt;br /&gt;
&lt;br /&gt;
function p.normName(s)&lt;br /&gt;
  s = mw.ustring.lower(s or &#039;&#039;)&lt;br /&gt;
  return (mw.ustring.gsub(s, &#039;[^%w]&#039;, &#039;&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.link(rec)&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  return &#039;[[&#039; .. slug .. &#039;|&#039; .. (rec.name or slug) .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- icon + linked name wrapped in .wm-tip (Common.js hover-infobox popup).&lt;br /&gt;
function p.iconLink(rec, size)&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local tip = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    return tip .. &#039;[[File:&#039; .. rec.icon .. &#039;|&#039; .. (size or 24) .. &#039;px|link=&#039;&lt;br /&gt;
      .. slug .. &#039;]] [[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return tip .. &#039;[[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;56.1&#039; / &#039;6&#039; (no trailing .0); non-numbers pass through as strings.&lt;br /&gt;
function p.fmtNum(v)&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v == math.floor(v) then return string.format(&#039;%d&#039;, v) end&lt;br /&gt;
    return tostring(v)&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v or &#039;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.round1(x)&lt;br /&gt;
  return math.floor(x * 10 + 0.5) / 10&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parse a stat value to a number: 11 -&amp;gt; 11, &#039;15%&#039; -&amp;gt; 15, else nil.&lt;br /&gt;
function p.numStat(v)&lt;br /&gt;
  if type(v) == &#039;number&#039; then return v end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    return tonumber(v) or tonumber(v:match(&#039;^%s*([%-%d%.]+)&#039;))&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Render one stat value. The game data uses huge sentinels for on/off&lt;br /&gt;
-- mechanics: &amp;gt;= 1000 means &amp;quot;Unlimited&amp;quot;, &amp;lt;= -1000 means &amp;quot;disabled&amp;quot; (we&lt;br /&gt;
-- return nil so the caller drops the row). `signed` adds a &#039;+&#039; prefix&lt;br /&gt;
-- for modifier-style categories (heroes, blessings, upgrades, ...).&lt;br /&gt;
function p.fmtStat(v, signed)&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;Unlimited&#039; end&lt;br /&gt;
    if v &amp;lt;= -1000 then return nil end&lt;br /&gt;
    local s = p.fmtNum(v)&lt;br /&gt;
    if signed and v &amp;gt; 0 then s = &#039;+&#039; .. s end&lt;br /&gt;
    return s&lt;br /&gt;
  end&lt;br /&gt;
  local s = tostring(v or &#039;&#039;)&lt;br /&gt;
  if signed and s ~= &#039;&#039; and s:sub(1, 1) ~= &#039;-&#039; and s:sub(1, 1) ~= &#039;+&#039; then&lt;br /&gt;
    s = &#039;+&#039; .. s&lt;br /&gt;
  end&lt;br /&gt;
  return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.firstSentence(text, cap)&lt;br /&gt;
  local t = (text or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;)&lt;br /&gt;
  t = t:match(&#039;^[^\n]*&#039;) or t&lt;br /&gt;
  local s = t:match(&#039;^(.-[%.!%?])%s&#039;) or t:match(&#039;^(.-[%.!%?])$&#039;) or t&lt;br /&gt;
  cap = cap or 140&lt;br /&gt;
  if mw.ustring.len(s) &amp;gt; cap then s = mw.ustring.sub(s, 1, cap) end&lt;br /&gt;
  return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- stat presentation config&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
&lt;br /&gt;
-- Canonical display order for the stats block (JSON object key order is&lt;br /&gt;
-- not visible to Lua). Unknown stats append alphabetically at the end.&lt;br /&gt;
local STAT_ORDER = {&lt;br /&gt;
  &#039;Damage&#039;, &#039;Damage per Wave&#039;, &#039;Health&#039;, &#039;HP per Wave&#039;,&lt;br /&gt;
  &#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;,&lt;br /&gt;
  &#039;Damage vs Burning&#039;, &#039;Damage vs Shocked&#039;, &#039;Melee Damage&#039;, &#039;Range Damage&#039;,&lt;br /&gt;
  &#039;Attack Speed&#039;, &#039;Critical Chance&#039;, &#039;Critical Damage&#039;, &#039;Range&#039;,&lt;br /&gt;
  &#039;Health Points&#039;, &#039;HP Rate&#039;, &#039;HP Delay&#039;,&lt;br /&gt;
  &#039;Pierce&#039;, &#039;Projectiles&#039;, &#039;Bounce&#039;,&lt;br /&gt;
  &#039;Explosive Chance&#039;, &#039;Explosive Radius&#039;, &#039;Explosive Range&#039;, &#039;HP Steal&#039;,&lt;br /&gt;
  &#039;Armor&#039;, &#039;Dodge&#039;, &#039;Speed&#039;, &#039;Resources&#039;, &#039;Rarity Find&#039;,&lt;br /&gt;
  &#039;Stagger&#039;, &#039;Stun&#039;, &#039;Freeze&#039;, &#039;XP Gain&#039;, &#039;Pickup Range&#039;, &#039;Cooldown&#039;,&lt;br /&gt;
}&lt;br /&gt;
local STAT_RANK = {}&lt;br /&gt;
for i, k in ipairs(STAT_ORDER) do STAT_RANK[k] = i end&lt;br /&gt;
&lt;br /&gt;
-- Stat name -&amp;gt; in-game glyph file (18px next to each stat row).&lt;br /&gt;
local STAT_GLYPHS = {&lt;br /&gt;
  [&#039;Damage&#039;] = &#039;Stat_Damage.png&#039;,&lt;br /&gt;
  [&#039;Fire Damage&#039;] = &#039;Stat_Fire.png&#039;,&lt;br /&gt;
  [&#039;Electric Damage&#039;] = &#039;Stat_Electric.png&#039;,&lt;br /&gt;
  [&#039;Holy Damage&#039;] = &#039;Stat_Physical.png&#039;,&lt;br /&gt;
  [&#039;Attack Speed&#039;] = &#039;Stat_AttackSpeed.png&#039;,&lt;br /&gt;
  [&#039;Critical Chance&#039;] = &#039;Stat_Critical.png&#039;,&lt;br /&gt;
  [&#039;Critical Damage&#039;] = &#039;Stat_CriticalDmg.png&#039;,&lt;br /&gt;
  [&#039;Range&#039;] = &#039;Stat_Range.png&#039;,&lt;br /&gt;
  [&#039;Pierce&#039;] = &#039;Stat_Pierce.png&#039;,&lt;br /&gt;
  [&#039;Bounce&#039;] = &#039;Stat_Bounce.png&#039;,&lt;br /&gt;
  [&#039;Projectiles&#039;] = &#039;Stat_Projectiles.png&#039;,&lt;br /&gt;
  [&#039;Stagger&#039;] = &#039;Stat_Stagger.png&#039;,&lt;br /&gt;
  [&#039;Stun&#039;] = &#039;Stat_Stun.png&#039;,&lt;br /&gt;
  [&#039;Freeze&#039;] = &#039;Stat_Freeze.png&#039;,&lt;br /&gt;
  [&#039;Explosive Chance&#039;] = &#039;Stat_ExplosiveChance.png&#039;,&lt;br /&gt;
  [&#039;Explosive Radius&#039;] = &#039;Stat_ExplosiveRadius.png&#039;,&lt;br /&gt;
  [&#039;Explosive Range&#039;] = &#039;Stat_ExplosiveRadius.png&#039;,&lt;br /&gt;
  [&#039;HP Steal&#039;] = &#039;Stat_HPSteal.png&#039;,&lt;br /&gt;
  [&#039;Health Points&#039;] = &#039;Stat_HP.png&#039;,&lt;br /&gt;
  [&#039;HP Rate&#039;] = &#039;Stat_HPRate.png&#039;,&lt;br /&gt;
  [&#039;HP Delay&#039;] = &#039;Stat_HPDelay.png&#039;,&lt;br /&gt;
  [&#039;Speed&#039;] = &#039;Stat_Speed.png&#039;,&lt;br /&gt;
  [&#039;Armor&#039;] = &#039;Stat_Armor.png&#039;,&lt;br /&gt;
  [&#039;Dodge&#039;] = &#039;Stat_Dodge.png&#039;,&lt;br /&gt;
  [&#039;Resources&#039;] = &#039;Stat_Resources.png&#039;,&lt;br /&gt;
  [&#039;Pickup Range&#039;] = &#039;Stat_PickupRange.png&#039;,&lt;br /&gt;
  [&#039;Rarity Find&#039;] = &#039;Stat_RarityFind.png&#039;,&lt;br /&gt;
  [&#039;XP Gain&#039;] = &#039;Stat_XPGain.png&#039;,&lt;br /&gt;
  [&#039;Cooldown&#039;] = &#039;Stat_Cooldown.png&#039;,&lt;br /&gt;
  [&#039;Damage vs Burning&#039;] = &#039;Stat_VsFire.png&#039;,&lt;br /&gt;
  [&#039;Damage vs Shocked&#039;] = &#039;Stat_VsElectric.png&#039;,&lt;br /&gt;
  [&#039;Health&#039;] = &#039;Stat_HitPoints.png&#039;,&lt;br /&gt;
}&lt;br /&gt;
p.STAT_GLYPHS = STAT_GLYPHS&lt;br /&gt;
&lt;br /&gt;
function p.statGlyph(stat)&lt;br /&gt;
  local f = STAT_GLYPHS[stat]&lt;br /&gt;
  if f then return &#039;[[File:&#039; .. f .. &#039;|18px|link=]] &#039; end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Elemental damage accent chips (dark-theme tokens with inline fallbacks).&lt;br /&gt;
p.DMG_CHIP = {&lt;br /&gt;
  [&#039;Holy Damage&#039;]     = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  [&#039;Fire Damage&#039;]     = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  [&#039;Electric Damage&#039;] = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- stat page links -------------------------------------------------&lt;br /&gt;
-- Per-stat reverse-lookup pages exist for every stat modified by at least&lt;br /&gt;
-- one hero / blessing / upgrade / passive / Crux power, and the codex Tips&lt;br /&gt;
-- pages double as hubs for the stats sharing their title (Holy / Fire /&lt;br /&gt;
-- Electric Damage, Critical Damage, Pierce, Bounce, Projectiles, Stagger,&lt;br /&gt;
-- Stun). A stat label is linkified ONLY when one of those pages exists, so&lt;br /&gt;
-- a page-less stat renders as plain text — never a redlink. Both sets are&lt;br /&gt;
-- computed from the source Data pages at render time, mirroring how the&lt;br /&gt;
-- page set itself is derived at emit time, so they match by construction.&lt;br /&gt;
&lt;br /&gt;
-- Same alias fold as Module:StatIndex: the game data calls one mechanic&lt;br /&gt;
-- both &#039;Explosive Range&#039; and &#039;Explosive Radius&#039;; the page lives at&lt;br /&gt;
-- &#039;Explosive Radius&#039;.&lt;br /&gt;
local STAT_PAGE_ALIAS = {[&#039;Explosive Range&#039;] = &#039;Explosive Radius&#039;}&lt;br /&gt;
&lt;br /&gt;
-- Enemy-only scaling keys: never booster-modified, never get stat pages.&lt;br /&gt;
local NON_PAGE_STATS = {[&#039;Health&#039;] = true, [&#039;HP per Wave&#039;] = true,&lt;br /&gt;
                        [&#039;Damage per Wave&#039;] = true}&lt;br /&gt;
&lt;br /&gt;
local MODIFIER_CATS = {&#039;Characters&#039;, &#039;Blessings&#039;, &#039;Upgrades&#039;, &#039;Passives&#039;, &#039;Crux&#039;}&lt;br /&gt;
&lt;br /&gt;
-- Set (key -&amp;gt; true) of stat keys modified by at least one booster record&lt;br /&gt;
-- (memoized per parse).&lt;br /&gt;
local _boosterKeys = nil&lt;br /&gt;
function p.boosterStatKeys()&lt;br /&gt;
  if _boosterKeys == nil then&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, cat in ipairs(MODIFIER_CATS) do&lt;br /&gt;
      for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
        for k in pairs(r.stats or {}) do keys[k] = true end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    for k in pairs(NON_PAGE_STATS) do keys[k] = nil end&lt;br /&gt;
    if keys[&#039;Explosive Range&#039;] then keys[&#039;Explosive Radius&#039;] = true end&lt;br /&gt;
    _boosterKeys = keys&lt;br /&gt;
  end&lt;br /&gt;
  return _boosterKeys&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Page title of a stat&#039;s reference page, or nil when no page exists.&lt;br /&gt;
-- Codex-hosted hubs win (link target = the codex record&#039;s slug — never&lt;br /&gt;
-- rebuilt from the name); otherwise booster-modified stats point at their&lt;br /&gt;
-- emitted reverse-lookup page, whose title IS the stat name.&lt;br /&gt;
function p.statPageTarget(stat)&lt;br /&gt;
  if not stat or stat == &#039;&#039; then return nil end&lt;br /&gt;
  local key = STAT_PAGE_ALIAS[stat] or stat&lt;br /&gt;
  if NON_PAGE_STATS[key] then return nil end&lt;br /&gt;
  local cx = p.codexFor(key, &#039;Tips&#039;)&lt;br /&gt;
  if cx and cx.slug then return cx.slug end&lt;br /&gt;
  if p.boosterStatKeys()[key] then return key end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;[[&amp;lt;page&amp;gt;|&amp;lt;stat&amp;gt;]]&#039; when the stat has a reference page, else the plain&lt;br /&gt;
-- label unchanged.&lt;br /&gt;
function p.statLabelLink(stat)&lt;br /&gt;
  local target = p.statPageTarget(stat)&lt;br /&gt;
  if not target then return stat end&lt;br /&gt;
  if target == stat then return &#039;[[&#039; .. stat .. &#039;]]&#039; end&lt;br /&gt;
  return &#039;[[&#039; .. target .. &#039;|&#039; .. stat .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Ordered {key, value} pairs of rec.stats per STAT_ORDER (extras appended&lt;br /&gt;
-- alphabetically).&lt;br /&gt;
function p.statPairs(rec)&lt;br /&gt;
  local stats = (rec and rec.stats) or {}&lt;br /&gt;
  local known, extra = {}, {}&lt;br /&gt;
  for k in pairs(stats) do&lt;br /&gt;
    if STAT_RANK[k] then known[#known + 1] = k else extra[#extra + 1] = k end&lt;br /&gt;
  end&lt;br /&gt;
  table.sort(known, function(a, b) return STAT_RANK[a] &amp;lt; STAT_RANK[b] end)&lt;br /&gt;
  table.sort(extra)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, k in ipairs(known) do out[#out + 1] = {k, stats[k]} end&lt;br /&gt;
  for _, k in ipairs(extra) do out[#out + 1] = {k, stats[k]} end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Infobox &#039;Stats&#039; cell: one glyph+label+value line per stat, &amp;lt;br&amp;gt;-joined.&lt;br /&gt;
-- Every stat label whose reference page exists links to it (elemental&lt;br /&gt;
-- damage labels to the damage-type hubs, booster-modified stats to their&lt;br /&gt;
-- reverse-lookup pages); page-less stats stay plain text. Values and&lt;br /&gt;
-- glyphs are untouched.&lt;br /&gt;
function p.statsBlock(rec, signed)&lt;br /&gt;
  local lines = {}&lt;br /&gt;
  for _, kv in ipairs(p.statPairs(rec)) do&lt;br /&gt;
    local k, v = kv[1], kv[2]&lt;br /&gt;
    local val = p.fmtStat(v, signed)&lt;br /&gt;
    if val ~= nil then&lt;br /&gt;
      local label = p.statLabelLink(k)&lt;br /&gt;
      local chip = p.DMG_CHIP[k]&lt;br /&gt;
      if chip then&lt;br /&gt;
        lines[#lines + 1] = p.statGlyph(k) .. &#039;&amp;lt;span class=&amp;quot;&#039; .. chip[3]&lt;br /&gt;
          .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. chip[1] .. &#039;, &#039; .. chip[2]&lt;br /&gt;
          .. &amp;quot;);\&amp;quot;&amp;gt;&#039;&#039;&#039;&amp;quot; .. label .. &amp;quot;:&#039;&#039;&#039; &amp;quot; .. val .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
      else&lt;br /&gt;
        lines[#lines + 1] = p.statGlyph(k) .. &amp;quot;&#039;&#039;&#039;&amp;quot; .. label .. &amp;quot;:&#039;&#039;&#039; &amp;quot; .. val&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(lines, &#039;&amp;lt;br&amp;gt;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- One-line plain-text summary for index columns (Effects / Modifiers).&lt;br /&gt;
function p.effectsSummary(rec, signed)&lt;br /&gt;
  local parts = {}&lt;br /&gt;
  for _, kv in ipairs(p.statPairs(rec)) do&lt;br /&gt;
    local val = p.fmtStat(kv[2], signed)&lt;br /&gt;
    if val ~= nil then parts[#parts + 1] = kv[1] .. &#039; &#039; .. val end&lt;br /&gt;
  end&lt;br /&gt;
  if #parts &amp;gt; 0 then return table.concat(parts, &#039;, &#039;) end&lt;br /&gt;
  local texts = {}&lt;br /&gt;
  for _, key in ipairs(rec.tags or {}) do&lt;br /&gt;
    local t = p.tagText(key)&lt;br /&gt;
    if t and t ~= &#039;&#039; then texts[#texts + 1] = t end&lt;br /&gt;
  end&lt;br /&gt;
  if #texts &amp;gt; 0 then return table.concat(texts, &#039; &#039;) end&lt;br /&gt;
  local d = (rec.description or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;)&lt;br /&gt;
  if d ~= &#039;&#039; then return mw.ustring.sub(p.firstSentence(d, 120), 1, 120) end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.availability(locked)&lt;br /&gt;
  if locked then return &#039;Must be unlocked&#039; end&lt;br /&gt;
  return &#039;Unlocked from start&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CRUX_STATE = {&lt;br /&gt;
  Acquired = &#039;Default&#039;,&lt;br /&gt;
  Unlocked = &#039;Unlocked from start&#039;,&lt;br /&gt;
  Locked = &#039;Must be unlocked&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Melee vs Ranged: the melee weapons carry the Melee tag (or raw&lt;br /&gt;
-- tag_source family &#039;Melee&#039;); everything else is Ranged.&lt;br /&gt;
function p.weaponClass(rec)&lt;br /&gt;
  for _, k in ipairs(rec.tags or {}) do&lt;br /&gt;
    if k == &#039;Melee&#039; then return &#039;Melee&#039; end&lt;br /&gt;
  end&lt;br /&gt;
  if rec.tag_source == &#039;Melee&#039; then return &#039;Melee&#039; end&lt;br /&gt;
  return &#039;Ranged&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- tags&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local _tagByKey = nil&lt;br /&gt;
function p.tagRec(key)&lt;br /&gt;
  if _tagByKey == nil then&lt;br /&gt;
    _tagByKey = {}&lt;br /&gt;
    for _, r in ipairs(p.load(&#039;Tags&#039;)) do&lt;br /&gt;
      _tagByKey[r.id or r.name] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _tagByKey[key]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.tagText(key)&lt;br /&gt;
  local r = p.tagRec(key)&lt;br /&gt;
  return (r and r.text) or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Tag link wrapped for the hover-tooltip JS. Slug comes from the tag&#039;s&lt;br /&gt;
-- own source record (never recomputed here).&lt;br /&gt;
function p.tagLink(key)&lt;br /&gt;
  local r = p.tagRec(key)&lt;br /&gt;
  if not r or not r.slug then return key end&lt;br /&gt;
  return &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. r.slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. r.slug .. &#039;|&#039; .. key .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Body &#039;Tags&#039; section: list of &amp;quot;&amp;lt;link&amp;gt; – &amp;lt;text&amp;gt;&amp;quot; strings.&lt;br /&gt;
function p.tagsBody(rec)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, key in ipairs(rec.tags or {}) do&lt;br /&gt;
    local text = p.tagText(key)&lt;br /&gt;
    if text ~= &#039;&#039; then&lt;br /&gt;
      out[#out + 1] = p.tagLink(key) .. &#039; – &#039; .. text&lt;br /&gt;
    else&lt;br /&gt;
      out[#out + 1] = p.tagLink(key)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Cross-category tag membership map: {tagkey -&amp;gt; {catName -&amp;gt; {rec,...}}}.&lt;br /&gt;
-- One scan per parse (memoized); powers tag pages + Used-by counts.&lt;br /&gt;
local TAG_BEARING = {&#039;Weapons&#039;, &#039;Characters&#039;, &#039;Blessings&#039;, &#039;Upgrades&#039;,&lt;br /&gt;
                     &#039;Passives&#039;, &#039;Enemies&#039;}&lt;br /&gt;
p.TAG_BEARING = TAG_BEARING&lt;br /&gt;
&lt;br /&gt;
local _membership = nil&lt;br /&gt;
function p.tagMembership()&lt;br /&gt;
  if _membership == nil then&lt;br /&gt;
    _membership = {}&lt;br /&gt;
    for _, cat in ipairs(TAG_BEARING) do&lt;br /&gt;
      for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
        for _, key in ipairs(r.tags or {}) do&lt;br /&gt;
          _membership[key] = _membership[key] or {}&lt;br /&gt;
          _membership[key][cat] = _membership[key][cat] or {}&lt;br /&gt;
          local bucket = _membership[key][cat]&lt;br /&gt;
          bucket[#bucket + 1] = r&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _membership&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.usedByCount(key)&lt;br /&gt;
  local m = p.tagMembership()[key]&lt;br /&gt;
  if not m then return 0 end&lt;br /&gt;
  local n = 0&lt;br /&gt;
  for _, recs in pairs(m) do n = n + #recs end&lt;br /&gt;
  return n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- codex joins (by normalized name)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local _codexByType = nil&lt;br /&gt;
local function codexIndex()&lt;br /&gt;
  if _codexByType == nil then&lt;br /&gt;
    _codexByType = {}&lt;br /&gt;
    for _, r in ipairs(p.load(&#039;Codex&#039;)) do&lt;br /&gt;
      local t = r.type or &#039;&#039;&lt;br /&gt;
      _codexByType[t] = _codexByType[t] or {}&lt;br /&gt;
      _codexByType[t][p.normName(r.name or r.id)] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _codexByType&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- The codex entry describing an entity (Weapons / Enemies join), or nil.&lt;br /&gt;
function p.codexFor(name, codexType)&lt;br /&gt;
  local idx = codexIndex()&lt;br /&gt;
  local key = p.normName(name)&lt;br /&gt;
  if codexType then&lt;br /&gt;
    return (idx[codexType] or {})[key]&lt;br /&gt;
  end&lt;br /&gt;
  for _, bucket in pairs(idx) do&lt;br /&gt;
    if bucket[key] then return bucket[key] end&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- computed infobox (per category) — same field names/values the&lt;br /&gt;
-- infobox Templates and index columns have always consumed.&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local INFOBOX_BUILDERS = {&lt;br /&gt;
  Weapons = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Class = p.weaponClass(rec),&lt;br /&gt;
      Tier = rec.tier,&lt;br /&gt;
      Price = p.fmtNum(rec.price),&lt;br /&gt;
      Availability = p.availability(rec.locked),&lt;br /&gt;
      Stats = p.statsBlock(rec, false),&lt;br /&gt;
      Damage = rec.stats and rec.stats[&#039;Damage&#039;] ~= nil&lt;br /&gt;
        and p.fmtNum(rec.stats[&#039;Damage&#039;]) or nil,&lt;br /&gt;
      [&#039;Attack Speed&#039;] = rec.stats and rec.stats[&#039;Attack Speed&#039;] ~= nil&lt;br /&gt;
        and p.fmtNum(rec.stats[&#039;Attack Speed&#039;]) or nil,&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Characters = function(rec)&lt;br /&gt;
    local hp = rec.stats and rec.stats[&#039;Health Points&#039;]&lt;br /&gt;
    return {&lt;br /&gt;
      Availability = p.availability(rec.locked),&lt;br /&gt;
      Stats = p.statsBlock(rec, true),&lt;br /&gt;
      Modifiers = p.effectsSummary(rec, true),&lt;br /&gt;
      [&#039;Health Points&#039;] = hp ~= nil and p.fmtStat(hp, true) or nil,&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Crux = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Type = rec.type,&lt;br /&gt;
      Availability = CRUX_STATE[rec.state] or rec.state or &#039;&#039;,&lt;br /&gt;
      Stats = p.statsBlock(rec, true),&lt;br /&gt;
      Effects = p.effectsSummary(rec, true),&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Enemies = function(rec)&lt;br /&gt;
    local ib = {Stats = p.statsBlock(rec, false)}&lt;br /&gt;
    if rec.tags and #rec.tags &amp;gt; 0 then&lt;br /&gt;
      local links = {}&lt;br /&gt;
      for _, k in ipairs(rec.tags) do links[#links + 1] = p.tagLink(k) end&lt;br /&gt;
      ib.Classification = table.concat(links, &#039;, &#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local st = rec.stats or {}&lt;br /&gt;
    for _, k in ipairs({&#039;Health&#039;, &#039;Damage&#039;, &#039;Speed&#039;, &#039;Resources&#039;}) do&lt;br /&gt;
      if st[k] ~= nil then ib[k] = p.fmtNum(st[k]) end&lt;br /&gt;
    end&lt;br /&gt;
    if st[&#039;HP per Wave&#039;] ~= nil then ib[&#039;HP/Wave&#039;] = p.fmtNum(st[&#039;HP per Wave&#039;]) end&lt;br /&gt;
    if st[&#039;Damage per Wave&#039;] ~= nil then ib[&#039;Dmg/Wave&#039;] = p.fmtNum(st[&#039;Damage per Wave&#039;]) end&lt;br /&gt;
    return ib&lt;br /&gt;
  end,&lt;br /&gt;
  Achievements = function(rec)&lt;br /&gt;
    return {Objective = rec.description or &#039;&#039;}&lt;br /&gt;
  end,&lt;br /&gt;
  Codex = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Type = rec.type,&lt;br /&gt;
      Summary = p.firstSentence(rec.description or &#039;&#039;, 140),&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Tags = function(rec)&lt;br /&gt;
    local text = rec.text or &#039;&#039;&lt;br /&gt;
    if text == &#039;&#039; then text = &amp;quot;&#039;&#039;(no effect text provided by the game data)&#039;&#039;&amp;quot; end&lt;br /&gt;
    return {Effect = text, [&#039;Used by&#039;] = p.usedByCount(rec.id or rec.name)}&lt;br /&gt;
  end,&lt;br /&gt;
}&lt;br /&gt;
local function shopInfobox(rec)&lt;br /&gt;
  return {&lt;br /&gt;
    Tier = rec.tier,&lt;br /&gt;
    Price = p.fmtNum(rec.price),&lt;br /&gt;
    Availability = p.availability(rec.locked),&lt;br /&gt;
    Stats = p.statsBlock(rec, true),&lt;br /&gt;
    Effects = p.effectsSummary(rec, true),&lt;br /&gt;
  }&lt;br /&gt;
end&lt;br /&gt;
INFOBOX_BUILDERS.Blessings = shopInfobox&lt;br /&gt;
INFOBOX_BUILDERS.Upgrades = shopInfobox&lt;br /&gt;
INFOBOX_BUILDERS.Passives = shopInfobox&lt;br /&gt;
&lt;br /&gt;
function p.computedInfobox(cat, rec)&lt;br /&gt;
  local b = INFOBOX_BUILDERS[cat]&lt;br /&gt;
  if not b then return {} end&lt;br /&gt;
  return b(rec)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- body sections (computed)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
function p.bodySection(cat, rec, section)&lt;br /&gt;
  if section == &#039;Tags&#039; then&lt;br /&gt;
    return p.tagsBody(rec)&lt;br /&gt;
  end&lt;br /&gt;
  if section == &#039;Description&#039; or section == &#039;Objective&#039; then&lt;br /&gt;
    return rec.description or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  if section == &#039;Lore&#039; then&lt;br /&gt;
    if cat == &#039;Weapons&#039; or cat == &#039;Enemies&#039; then&lt;br /&gt;
      local cx = p.codexFor(rec.name, cat)&lt;br /&gt;
      if cx and cx.description and cx.description ~= &#039;&#039; then&lt;br /&gt;
        return cx.description&lt;br /&gt;
      end&lt;br /&gt;
      if cat == &#039;Enemies&#039; then return rec.description or &#039;&#039; end&lt;br /&gt;
      return &#039;&#039;&lt;br /&gt;
    end&lt;br /&gt;
    -- Characters / Codex: own prose.&lt;br /&gt;
    return rec.description or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BODY_ORDER = {&lt;br /&gt;
  Weapons = {&#039;Lore&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Characters = {&#039;Lore&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Blessings = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Upgrades = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Passives = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Crux = {&#039;Description&#039;},&lt;br /&gt;
  Enemies = {&#039;Lore&#039;},&lt;br /&gt;
  Achievements = {&#039;Objective&#039;},&lt;br /&gt;
  Codex = {&#039;Lore&#039;},&lt;br /&gt;
  Tags = {},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- sorting (per-category index order, computed at render time so the&lt;br /&gt;
-- source page order never matters)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local CRUX_TYPE_ORDER = {Unique = 1, Action = 2, Buff = 3, Debuff = 4, Aura = 5}&lt;br /&gt;
local CODEX_TYPE_ORDER = {Enemies = 1, Weapons = 2, Events = 3, Tips = 4}&lt;br /&gt;
local ROMAN = {I = 1, II = 2, III = 3, IV = 4, V = 5}&lt;br /&gt;
&lt;br /&gt;
local function familyKey(name)&lt;br /&gt;
  local head, tail = name:match(&#039;^(.*)%s(%S+)$&#039;)&lt;br /&gt;
  if head and ROMAN[tail] then&lt;br /&gt;
    return mw.ustring.lower(head), ROMAN[tail]&lt;br /&gt;
  end&lt;br /&gt;
  if name:sub(-5) == &#039; Plus&#039; then&lt;br /&gt;
    return mw.ustring.lower(name:sub(1, -6)), 2&lt;br /&gt;
  end&lt;br /&gt;
  return mw.ustring.lower(name), 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local SORT_KEYS = {&lt;br /&gt;
  Weapons = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Characters = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Blessings = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Upgrades = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Passives = function(r)&lt;br /&gt;
    local fam, rank = familyKey(r.name or &#039;&#039;)&lt;br /&gt;
    return {fam, rank, r.name or &#039;&#039;}&lt;br /&gt;
  end,&lt;br /&gt;
  Crux = function(r) return {CRUX_TYPE_ORDER[r.type] or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Enemies = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Achievements = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Codex = function(r) return {CODEX_TYPE_ORDER[r.type] or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Tags = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function keyLess(a, b)&lt;br /&gt;
  for i = 1, math.max(#a, #b) do&lt;br /&gt;
    local x, y = a[i], b[i]&lt;br /&gt;
    if x == nil then return true end&lt;br /&gt;
    if y == nil then return false end&lt;br /&gt;
    if x ~= y then&lt;br /&gt;
      if type(x) == &#039;number&#039; and type(y) == &#039;number&#039; then return x &amp;lt; y end&lt;br /&gt;
      return tostring(x) &amp;lt; tostring(y)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Stable per-category sort of the records list (returns a new list).&lt;br /&gt;
function p.sorted(cat)&lt;br /&gt;
  local keyfn = SORT_KEYS[cat] or function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end&lt;br /&gt;
  local list = {}&lt;br /&gt;
  for i, r in ipairs(p.load(cat)) do list[#list + 1] = {i = i, r = r, k = keyfn(r)} end&lt;br /&gt;
  table.sort(list, function(a, b)&lt;br /&gt;
    if keyLess(a.k, b.k) then return true end&lt;br /&gt;
    if keyLess(b.k, a.k) then return false end&lt;br /&gt;
    return a.i &amp;lt; b.i&lt;br /&gt;
  end)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, e in ipairs(list) do out[#out + 1] = e.r end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- index page config + entry points shared by the per-category shims&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local INDEX_OPTS = {&lt;br /&gt;
  Weapons = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Damage&#039;, source = &#039;Damage&#039;}, {name = &#039;Attack Speed&#039;, source = &#039;Attack Speed&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Characters = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Modifiers&#039;, source = &#039;Modifiers&#039;}, {name = &#039;Health Points&#039;, source = &#039;Health Points&#039;}}, thumbnail_size = 64},&lt;br /&gt;
  Blessings = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Upgrades = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Passives = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Crux = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Type&#039;, source = &#039;Type&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Enemies = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Health&#039;, source = &#039;Health&#039;}, {name = &#039;HP/Wave&#039;, source = &#039;HP/Wave&#039;}, {name = &#039;Damage&#039;, source = &#039;Damage&#039;}, {name = &#039;Dmg/Wave&#039;, source = &#039;Dmg/Wave&#039;}, {name = &#039;Speed&#039;, source = &#039;Speed&#039;}, {name = &#039;Resources&#039;, source = &#039;Resources&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Achievements = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Objective&#039;, source = &#039;Objective&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Codex = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Type&#039;, source = &#039;Type&#039;}, {name = &#039;Summary&#039;, source = &#039;Summary&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Tags = {columns = {{name = &#039;Tag&#039;, source = &#039;link&#039;}, {name = &#039;Effect&#039;, source = &#039;Effect&#039;}, {name = &#039;Used by&#039;, source = &#039;Used by&#039;}}, thumbnail_size = 16},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Visible infobox fields per category — MUST match the rows declared in&lt;br /&gt;
-- Template:&amp;lt;Cat&amp;gt;_infobox.&lt;br /&gt;
local INFOBOX_FIELD_ORDER = {&lt;br /&gt;
  Weapons = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;, &#039;Class&#039;},&lt;br /&gt;
  Characters = {&#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Blessings = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Upgrades = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Passives = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Crux = {&#039;Type&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Enemies = {&#039;Classification&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Achievements = {},&lt;br /&gt;
  Codex = {&#039;Type&#039;},&lt;br /&gt;
  Tags = {&#039;Effect&#039;, &#039;Used by&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Record (shallow copy) augmented with the computed infobox dict, so the&lt;br /&gt;
-- pure Module:Index renderer keeps working unchanged.&lt;br /&gt;
function p.augmented(cat, rec)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for k, v in pairs(rec) do out[k] = v end&lt;br /&gt;
  out.infobox = p.computedInfobox(cat, rec)&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function resolveRec(cat, frame)&lt;br /&gt;
  local id = frame and frame.args and (frame.args.id or frame.args[1])&lt;br /&gt;
  if id then id = (tostring(id):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)) end&lt;br /&gt;
  if not id or id == &#039;&#039; then return nil, &#039;&#039; end&lt;br /&gt;
  local rec = p.byId(cat)[id]&lt;br /&gt;
  if not rec then&lt;br /&gt;
    return nil, mw.getCurrentFrame():preprocess(&lt;br /&gt;
      &#039;&amp;lt;i&amp;gt;Unknown &#039; .. cat .. &#039; record: &#039; .. mw.text.nowiki(id) .. &#039;&amp;lt;/i&amp;gt;&#039;)&lt;br /&gt;
  end&lt;br /&gt;
  return rec, nil&lt;br /&gt;
end&lt;br /&gt;
p.resolveRec = resolveRec&lt;br /&gt;
&lt;br /&gt;
local function asParam(v)&lt;br /&gt;
  if v == nil then return &#039;&#039; end&lt;br /&gt;
  return tostring(v)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderInfobox(cat, rec)&lt;br /&gt;
  local ib = p.computedInfobox(cat, rec)&lt;br /&gt;
  local out = {&#039;{{&#039; .. cat .. &#039;_infobox&#039;}&lt;br /&gt;
  out[#out + 1] = &#039;| name = &#039; .. asParam(rec.name)&lt;br /&gt;
  out[#out + 1] = &#039;| id   = &#039; .. asParam(rec.id)&lt;br /&gt;
  out[#out + 1] = &#039;| icon = &#039; .. asParam(rec.icon)&lt;br /&gt;
  for _, field in ipairs(INFOBOX_FIELD_ORDER[cat] or {}) do&lt;br /&gt;
    out[#out + 1] = &#039;| &#039; .. field .. &#039; = &#039; .. asParam(ib[field])&lt;br /&gt;
  end&lt;br /&gt;
  out[#out + 1] = &#039;}}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderSectionBody(content)&lt;br /&gt;
  if content == nil or content == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  if type(content) == &#039;table&#039; then&lt;br /&gt;
    if #content == 0 then return &#039;&#039; end&lt;br /&gt;
    local lines = {}&lt;br /&gt;
    for _, line in ipairs(content) do lines[#lines + 1] = &#039;* &#039; .. tostring(line) end&lt;br /&gt;
    return table.concat(lines, &#039;\n&#039;)&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- entry points used by the per-category shim modules -------------&lt;br /&gt;
&lt;br /&gt;
function p.infoboxEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(renderInfobox(cat, rec))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.bodyEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local section = frame and frame.args and frame.args.section&lt;br /&gt;
  if section and section ~= &#039;&#039; then&lt;br /&gt;
    return mw.getCurrentFrame():preprocess(&lt;br /&gt;
      renderSectionBody(p.bodySection(cat, rec, section)))&lt;br /&gt;
  end&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, sec in ipairs(BODY_ORDER[cat] or {}) do&lt;br /&gt;
    local body = renderSectionBody(p.bodySection(cat, rec, sec))&lt;br /&gt;
    if body ~= &#039;&#039; then&lt;br /&gt;
      out[#out + 1] = &#039;&#039;&lt;br /&gt;
      out[#out + 1] = &#039;== &#039; .. sec .. &#039; ==&#039;&lt;br /&gt;
      out[#out + 1] = body&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(out, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.indexEntry(cat, frame)&lt;br /&gt;
  local Index = require(&#039;Module:Index&#039;)&lt;br /&gt;
  local recs = {}&lt;br /&gt;
  for _, r in ipairs(p.sorted(cat)) do&lt;br /&gt;
    recs[#recs + 1] = p.augmented(cat, r)&lt;br /&gt;
  end&lt;br /&gt;
  -- Linkify stat-name column headers to their reference pages (existence-&lt;br /&gt;
  -- checked via statPageTarget; non-stat headers pass through untouched).&lt;br /&gt;
  -- Works on a copy so the shared INDEX_OPTS table is never mutated.&lt;br /&gt;
  local base = INDEX_OPTS[cat] or {}&lt;br /&gt;
  local opts = {thumbnail_size = base.thumbnail_size,&lt;br /&gt;
                sort_default = base.sort_default, columns = {}}&lt;br /&gt;
  for _, c in ipairs(base.columns or {}) do&lt;br /&gt;
    local nm = c.name&lt;br /&gt;
    local target = p.statPageTarget(nm)&lt;br /&gt;
    if target then&lt;br /&gt;
      nm = (target == nm) and (&#039;[[&#039; .. nm .. &#039;]]&#039;)&lt;br /&gt;
        or (&#039;[[&#039; .. target .. &#039;|&#039; .. nm .. &#039;]]&#039;)&lt;br /&gt;
      if base.sort_default == c.name then opts.sort_default = nm end&lt;br /&gt;
    end&lt;br /&gt;
    opts.columns[#opts.columns + 1] = {name = nm, source = c.source}&lt;br /&gt;
  end&lt;br /&gt;
  return Index.render(recs, opts)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local CrossRef = require(&#039;Module:CrossRef&#039;)&lt;br /&gt;
  local parts = {&lt;br /&gt;
    renderInfobox(cat, rec),&lt;br /&gt;
    p.bodyEntry(cat, frame),&lt;br /&gt;
    CrossRef.compute(cat, rec),&lt;br /&gt;
  }&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(parts, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Help:Wiki_Editing&amp;diff=2115</id>
		<title>Help:Wiki Editing</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Help:Wiki_Editing&amp;diff=2115"/>
		<updated>2026-06-10T16:21:33Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Expand all / Collapse all controls for Help catalogues&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- Bot-published page. Source of truth: 5-build-pages/code/_game/help_wiki_editing.wiki&lt;br /&gt;
     (substituted + copied by emit_pages.py). Direct edits to this wiki page are&lt;br /&gt;
     overwritten on the next re-publish — improve the source instead. --&amp;gt;&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-|How this wiki works=&lt;br /&gt;
== The one-sentence version ==&lt;br /&gt;
&#039;&#039;&#039;All data on this wiki lives in ten JSON pages — &amp;lt;code&amp;gt;Data:Weapons.json&amp;lt;/code&amp;gt; through &amp;lt;code&amp;gt;Data:Tags.json&amp;lt;/code&amp;gt; — and every table, infobox, navbox, tag page and stat page is computed from them by Lua modules at render time.&#039;&#039;&#039; There are no precomputed pages: edit a value once on a Data page, purge, and every view of that value updates everywhere.&lt;br /&gt;
&lt;br /&gt;
== Data flow ==&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);border:1px solid var(--panel-border, #3a3c44);&amp;quot;&amp;gt;&lt;br /&gt;
 SOURCE OF TRUTH (edit values here)&lt;br /&gt;
 ┌────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │ Data:Weapons.json   Data:Characters.json   Data:Blessings.json │&lt;br /&gt;
 │ Data:Upgrades.json  Data:Crux.json         Data:Passives.json  │&lt;br /&gt;
 │ Data:Enemies.json   Data:Achievements.json Data:Codex.json     │&lt;br /&gt;
 │ Data:Tags.json                                                 │&lt;br /&gt;
 └───────────────┬────────────────────────────────────────────────┘&lt;br /&gt;
                 │  read at render time by&lt;br /&gt;
                 ▼&lt;br /&gt;
 ┌─ Module:Core ──────────────────────────────────────────────────┐&lt;br /&gt;
 │  loads + sorts records, computes infobox values, tag links,    │&lt;br /&gt;
 │  stat blocks, lore joins; shared by every module below         │&lt;br /&gt;
 └───┬───────────┬──────────┬───────────┬───────────┬─────────────┘&lt;br /&gt;
     ▼           ▼          ▼           ▼           ▼&lt;br /&gt;
 Module:      Module:    Module:     Module:     Module:&lt;br /&gt;
 &amp;lt;Category&amp;gt;   Compare    Navbox      TagIndex    StatIndex&lt;br /&gt;
 (infobox,    (DPS,      (bottom     (tag        (per-stat +&lt;br /&gt;
  body,        wave-10    navboxes)   member      damage-type&lt;br /&gt;
  index)       tables)                pages)      lookups)&lt;br /&gt;
     │           │          │           │           │&lt;br /&gt;
     ▼           ▼          ▼           ▼           ▼&lt;br /&gt;
            RENDERED PAGES (recomputed on every purge)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to edit, where ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | You want to change… !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Edit this !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | What happens&lt;br /&gt;
|-&lt;br /&gt;
| A &#039;&#039;&#039;value&#039;&#039;&#039;: a stat number, price, tier, lore text, tag key, icon filename || The record in the matching &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; page || Every infobox, index row, comparison table, navbox and lookup that shows the value updates after a cache purge. Don&#039;t rename &#039;&#039;&#039;field keys&#039;&#039;&#039; — modules look them up by name.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Prose&#039;&#039;&#039; on one entity&#039;s page: intro text, strategy notes, screenshots, trivia || The detail page itself (e.g. &amp;lt;code&amp;gt;Cutter&amp;lt;/code&amp;gt;) || Detail pages are first-write-only: the bot never overwrites them, so your edits persist across re-publishes.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;How&#039;&#039;&#039; something renders: column order, section layout, sort order, labels || The Lua module — [[Module:Core]] for most things (infobox field lists, index columns, stat display order, sorting); [[Module:Compare]] / [[Module:Navbox]] / etc. for their own views || All affected pages re-render on purge. Note: module edits are overwritten by the next scheduled bot re-publish, so durable changes should also be reported upstream.&lt;br /&gt;
|-&lt;br /&gt;
| The &#039;&#039;&#039;infobox panel&#039;&#039;&#039; look (rows, header, fallbacks) || &amp;lt;code&amp;gt;Template:&amp;amp;lt;Category&amp;amp;gt;_infobox&amp;lt;/code&amp;gt; || Presentation only — values are computed by Module:Core and passed in as parameters.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Site-wide look&#039;&#039;&#039;: colors, theme tokens, filter chips, hover tooltips || [[MediaWiki:Common.css]] / [[MediaWiki:Common.js]] || Theme tokens (&amp;lt;code&amp;gt;--infobox-*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--table-*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--dmg-*&amp;lt;/code&amp;gt;) restyle everything at once; inline fallbacks keep pages readable without them.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Seeing your edit ==&lt;br /&gt;
MediaWiki caches rendered pages. After editing a Data page or module, other pages that use it may show the old version until their cache expires. To force it: open the page and add &amp;lt;code&amp;gt;?action=purge&amp;lt;/code&amp;gt; to the URL (or use the &amp;quot;Purge&amp;quot; option). The job queue eventually refreshes everything on its own.&lt;br /&gt;
&lt;br /&gt;
== What the bot does on re-publish ==&lt;br /&gt;
* &#039;&#039;&#039;Overwrites&#039;&#039;&#039; the framework layer: &amp;lt;code&amp;gt;Module:*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Template:*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data:*&amp;lt;/code&amp;gt; pages, category index pages, comparison/stat pages, and this help page. Value edits you made on Data pages are replaced by the game&#039;s current data — report data corrections to the developers so they land in the source.&lt;br /&gt;
* &#039;&#039;&#039;Never touches&#039;&#039;&#039; existing detail pages (one per weapon / hero / blessing / …). Your prose, images and trivia are safe there.&lt;br /&gt;
&lt;br /&gt;
|-|Module catalogue=&lt;br /&gt;
One expandable entry per module. Every module is readable on its &amp;lt;code&amp;gt;Module:&amp;lt;/code&amp;gt; page and carries the same documentation in its header comment.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue-controls&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;wm-expand-all&amp;quot;&amp;gt;▸ Expand all&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;quot;wm-collapse-all&amp;quot;&amp;gt;▾ Collapse all&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Core]] — the engine room: computes every infobox value, index table, tag link, sort order and lore join&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| The shared library every other module &amp;lt;code&amp;gt;require()&amp;lt;/code&amp;gt;s. Loads and memoizes the ten Data pages, resolves category names, computes infobox value sets, stat blocks (with glyphs and elemental color chips), tag links and membership maps, codex lore joins, per-category sort orders, and the per-category presentation config (infobox field lists in &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER&amp;lt;/code&amp;gt;, index columns in &amp;lt;code&amp;gt;INDEX_OPTS&amp;lt;/code&amp;gt;, stat display order in &amp;lt;code&amp;gt;STAT_ORDER&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Not invoked directly — the per-category modules forward to its entry points (&amp;lt;code&amp;gt;infoboxEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bodyEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;indexEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renderEntry&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| All ten &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; pages.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Module:Weapons … Module:Tags (per-category modules) — infobox / body / index / crossRefs for one category&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What they do&lt;br /&gt;
| One tiny module per category ([[Module:Weapons]], [[Module:Characters]], [[Module:Blessings]], [[Module:Upgrades]], [[Module:Crux]], [[Module:Passives]], [[Module:Enemies]], [[Module:Achievements]], [[Module:Codex]], [[Module:Tags]]). Each only binds its category name and forwards to Module:Core — so all rendering logic lives in ONE place. [[Module:Codex]] additionally provides the shared &amp;lt;code&amp;gt;lore&amp;lt;/code&amp;gt; lookup (see its entry below).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|infobox|id=Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — the infobox panel&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|body|id=Cutter|section=Tags}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — one body section (omit &amp;lt;code&amp;gt;section&amp;lt;/code&amp;gt; for all)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|index}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — the sortable category table&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — variant / tag-member links&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|render|id=Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — infobox + body + crossRefs in one call&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Read&lt;br /&gt;
| The category&#039;s own &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; (via Module:Core).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Index]] — generic sortable-table renderer behind every category index&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Pure presentation: given records + a column spec, emits one sortable wikitable. Holds no data — Module:Core&#039;s &amp;lt;code&amp;gt;indexEntry&amp;lt;/code&amp;gt; feeds it sorted records augmented with computed infobox values. Column lists per category live in Module:Core&#039;s &amp;lt;code&amp;gt;INDEX_OPTS&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Not invoked directly; pages use &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|index}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Whatever records Module:Core passes it.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Compare]] — side-by-side comparison tables with computed DPS and wave-10 columns&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Builds the comparison tables on [[Weapons]], [[Weapon comparison]], [[Enemy comparison]] and [[Hero comparison]]. Computes columns that are stored nowhere: weapon &#039;&#039;&#039;DPS&#039;&#039;&#039; = (Damage + Holy + Fire + Electric) × Attack Speed; enemy &#039;&#039;&#039;wave-10 projections&#039;&#039;&#039; = base + per-wave × 9. Rows carry &amp;lt;code&amp;gt;data-element/class/tier/type&amp;lt;/code&amp;gt; attributes that power the JS filter chips.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Weapons_Melee}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Weapons_Ranged}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Enemies}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Characters}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Weapons.json]], [[Data:Enemies.json]], [[Data:Characters.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Navbox]] — the collapsible navigation box at the bottom of detail pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders a category&#039;s full member list as icon + link chains, bucketed into rows (Weapons/Blessings/Upgrades/Passives by tier, Crux/Codex by type, Characters/Enemies flat). The per-category grouping config is the &amp;lt;code&amp;gt;CONFIG&amp;lt;/code&amp;gt; table at the top of the module.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Pages use the wrapper templates: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Crux}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, … (each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Navbox|render|&amp;lt;Category&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| The named category&#039;s &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Iconbox]] — an entity&#039;s small icon + linked name, anywhere&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Looks up an entity by category + name (or bare name) and renders its icon plus a link to its page. Icons come from each entity&#039;s own record — there is no icon lookup table. Unknown entities or missing art degrade to a plain link.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Via [[Template:Iconbox]]: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter|icononly}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Blessing|Sanctified Halo|size=32}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · bare-name fallback &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Any of the ten &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; pages, depending on the call.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:CrossRef]] — &amp;quot;related entities&amp;quot; sections (base ↔ Plus variants, tag members)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Computes the &#039;Base variant&#039; / &#039;Upgraded variant&#039; links between a blessing or upgrade and its &#039;&amp;lt;X&amp;gt; Plus&#039; version (matched on the raw &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;, because Upgrades&#039; display names can differ from ids), and delegates Tag pages&#039; member sections to Module:TagIndex.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Wired by the category modules: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Blessings.json]], [[Data:Upgrades.json]]; tag-bearing categories via Module:TagIndex.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:TagIndex]] — &amp;quot;everything that carries this tag&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders the member sections on Tag pages (&#039;Weapons with this tag&#039;, …). Membership is never stored — it scans every tag-bearing category&#039;s &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays at render time, so adding a tag key to any record updates the tag page on purge.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:TagIndex|members|Melee}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (bare key or &amp;lt;code&amp;gt;Tag-Melee&amp;lt;/code&amp;gt; slug)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Tags.json]] + [[Data:Weapons.json]], [[Data:Characters.json]], [[Data:Blessings.json]], [[Data:Upgrades.json]], [[Data:Passives.json]], [[Data:Enemies.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:StatIndex]] — &amp;quot;what modifies this stat?&amp;quot; reverse lookups&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders the lookup tables on per-stat pages ([[Armor]], [[Speed]], …) and the damage-type hubs: every hero / blessing / upgrade / passive / Crux power modifying a stat (sorted by amount), and every weapon dealing an elemental damage type.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:StatIndex|boosters|Armor}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:StatIndex|weapons|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| boosters: [[Data:Characters.json]], [[Data:Blessings.json]], [[Data:Upgrades.json]], [[Data:Passives.json]], [[Data:Crux.json]] — weapons: [[Data:Weapons.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:DamageType]] — readable aliases for the damage-type hub pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Thin wrappers over Module:StatIndex so the [[Holy Damage]] / [[Fire Damage]] / [[Electric Damage]] hubs read naturally. No logic of its own.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:DamageType|dealtBy|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:DamageType|boostedBy|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Same as Module:StatIndex.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Codex]] — Codex category module + the shared lore lookup&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Standard category rendering for Codex pages, plus the &amp;lt;code&amp;gt;lore&amp;lt;/code&amp;gt; entry point that powers &#039;== Lore ==&#039; sections on weapon / enemy / character pages: it finds the matching Codex entry by normalized name at render time, falling back to the entity&#039;s own &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;. Edit the lore once in [[Data:Codex.json]] and every page showing it updates.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|lore|Cutter|Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (category-scoped) · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|lore|Azrael}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (cross-category)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Codex.json]] + the named entity&#039;s category Data page.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-|Template catalogue=&lt;br /&gt;
One expandable entry per user-facing template (or template family). Infobox values and navbox rows are computed by the modules — these templates are the presentation wrappers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue-controls&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;wm-expand-all&amp;quot;&amp;gt;▸ Expand all&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;quot;wm-collapse-all&amp;quot;&amp;gt;▾ Collapse all&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Template:Iconbox]] — inline entity icon + linked name&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → icon + linked name. Add &amp;lt;code&amp;gt;icononly&amp;lt;/code&amp;gt; for just the glyph: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter|icononly}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;. Optional &amp;lt;code&amp;gt;size=32&amp;lt;/code&amp;gt;. Category accepts singular or plural (Weapon, Character, Blessing, Upgrade, Crux, Passive, Enemy, Achievement, Codex, Tag); bare-name form &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; scans all categories. The go-to template for mentioning any entity in prose.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Backed by&lt;br /&gt;
| [[Module:Iconbox]] → the entity&#039;s own record in &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Template:Tip]] — highlighted gameplay-advice callout&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Tip|Pierce-heavy builds love this weapon — pair it with Penetrating Light.}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → a green-edged callout box. Use on detail pages for strategy notes that should stand out from lore.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:&amp;amp;lt;Category&amp;amp;gt;_infobox (10 templates) — the infobox panel on every detail page&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| Never called by hand. Detail pages call &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;; Module:Core computes the values from the Data page and passes them in. The templates ([[Template:Weapons_infobox]], [[Template:Characters_infobox]], [[Template:Blessings_infobox]], [[Template:Upgrades_infobox]], [[Template:Crux_infobox]], [[Template:Passives_infobox]], [[Template:Enemies_infobox]], [[Template:Achievements_infobox]], [[Template:Codex_infobox]], [[Template:Tags_infobox]]) own only the presentation: row layout, tier/type-colored headers, em-dash fallbacks. Each template&#039;s own page documents its parameters and the safe row shape.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:Navbox_&amp;amp;lt;Category&amp;amp;gt; (8 templates) — bottom-of-page navigation&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; at the bottom of a detail page. Available: [[Template:Navbox_Weapons]], [[Template:Navbox_Characters]], [[Template:Navbox_Blessings]], [[Template:Navbox_Upgrades]], [[Template:Navbox_Crux]], [[Template:Navbox_Passives]], [[Template:Navbox_Enemies]], [[Template:Navbox_Codex]]. Each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Navbox|render|&amp;lt;Category&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — rows are computed from the category&#039;s Data page, so new entities appear automatically.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:&amp;amp;lt;Category&amp;amp;gt;_Tags (5 templates) — the &#039;== Tags ==&#039; section on detail pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Weapons_Tags|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → the tag list with effect text for one record. Available for Weapons, Characters, Blessings, Upgrades, Passives. Each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|body|id={{{1}}}|section=Tags}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; so the section can be restyled in one place.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Utility templates (general-purpose page furniture)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:170px;&amp;quot; | [[Template:Stub]]&lt;br /&gt;
| Banner for short pages needing expansion (auto-categorizes).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:NeedsImage]]&lt;br /&gt;
| Banner for pages missing art.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Disambig]]&lt;br /&gt;
| Marks disambiguation pages.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Quote]]&lt;br /&gt;
| Block quotation with attribution.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:GameVersion]]&lt;br /&gt;
| Inline version stamp: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{GameVersion|1.2.3}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → &amp;quot;As of v1.2.3&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Spoiler]]&lt;br /&gt;
| Collapsible spoiler block.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Color]]&lt;br /&gt;
| Inline colored text: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Color|#ff6b35|burning}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:External]]&lt;br /&gt;
| External link with icon.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Hover]]&lt;br /&gt;
| Plain-text tooltip on hover.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Breadcrumb]]&lt;br /&gt;
| Breadcrumb navigation line.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Transclude]]&lt;br /&gt;
| Transclude a page or labeled section: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Transclude|PageName#Section}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Anchor]]&lt;br /&gt;
| Invisible anchor target for in-page links.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Clear]]&lt;br /&gt;
| Clears floats (e.g. below an infobox).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Documentation]]&lt;br /&gt;
| The &amp;lt;code&amp;gt;{{documentation}}&amp;lt;/code&amp;gt; convention for template doc subpages.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-|Data schemas=&lt;br /&gt;
The ten source Data pages and their REAL record fields. Every Data page also carries this documentation in its own top-level &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; field. Shared by all categories: &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; (the game&#039;s internal id), &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; (the wiki page title of the entity&#039;s detail page — never recompute it from the name), &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; (display name), &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name; empty = no staged art).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;wm-catalogue-controls&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;wm-expand-all&amp;quot;&amp;gt;▸ Expand all&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;quot;wm-collapse-all&amp;quot;&amp;gt;▾ Collapse all&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Weapons.json]] — one record per weapon (65)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt; || number || Shop tier 1–3.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt; || number || Shop price.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || boolean || &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; = must be unlocked.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tag_source&amp;lt;/code&amp;gt; || string || The game&#039;s raw tag family (Melee, Laser, Grenade, …). &amp;lt;code&amp;gt;Melee&amp;lt;/code&amp;gt; here (or a Melee tag) makes the weapon class Melee; everything else is Ranged.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag KEYS — join [[Data:Tags.json]] for effect text and tag pages.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Raw numbers keyed by stat name: &amp;lt;code&amp;gt;Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Holy/Fire/Electric Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Attack Speed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Critical Chance&amp;lt;/code&amp;gt; (percent values stay strings like &amp;lt;code&amp;gt;&amp;quot;15%&amp;quot;&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;Range&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Pierce&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Projectiles&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Bounce&amp;lt;/code&amp;gt;. Sentinels: ≥ 1000 renders as &#039;&#039;Unlimited&#039;&#039;, ≤ −1000 is hidden. &#039;&#039;&#039;DPS is not stored&#039;&#039;&#039; — [[Module:Compare]] computes it at render time.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core (infobox/body/index), Module:Compare, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Characters.json]] — one record per playable hero (10)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Unlock metadata; &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; drives the Availability row.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]] (only some heroes carry tags).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat &#039;&#039;&#039;modifiers&#039;&#039;&#039; applied to base stats (e.g. &amp;lt;code&amp;gt;Armor: 6&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Attack Speed&amp;quot;: &amp;quot;-12%&amp;quot;&amp;lt;/code&amp;gt;) — not absolute values.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Lore prose, shown under == Lore ==.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Compare (hero comparison), Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Blessings.json]] — one record per blessing (94)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price, unlock state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers; percent values stay strings (&amp;lt;code&amp;gt;&amp;quot;5%&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Flavor prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;&amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt; Plus&amp;lt;/code&amp;gt; records are upgraded variants: ids pair &amp;lt;code&amp;gt;&amp;amp;lt;x&amp;amp;gt;-plus&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;lt;x&amp;amp;gt;&amp;lt;/code&amp;gt;, linked by [[Module:CrossRef]] at render time. Consumed by Module:Core, Module:CrossRef, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Upgrades.json]] — one record per shop upgrade (88)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price, unlock state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Rarely present (3 of 88).&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;&#039;Caution:&#039;&#039;&#039; &#039;&#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; can differ completely (id &amp;lt;code&amp;gt;Shield Transform&amp;lt;/code&amp;gt; displays as &amp;lt;code&amp;gt;Lifesteal Chamber&amp;lt;/code&amp;gt;) — that&#039;s why &#039;&#039;&amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt; Plus&amp;lt;/code&amp;gt;&#039;&#039; variant pairing matches on the raw id ([[Module:CrossRef]]). Consumed by Module:Core, Module:CrossRef, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Crux.json]] — one record per Crux power (34)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt; || number || Tier.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Unique / Action / Buff / Debuff / Aura&amp;lt;/code&amp;gt; — drives index grouping, navbox rows and the infobox header color.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Acquired / Unlocked / Locked&amp;lt;/code&amp;gt; → rendered as the Availability row.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Effect prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Navbox, Module:StatIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Passives.json]] — one record per passive (55)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price; every passive starts locked.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Flavor prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Ranked families (Armor I/II/III) are grouped consecutively on the index by Module:Core&#039;s sort — the stored order doesn&#039;t matter. Consumed by Module:Core, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Enemies.json]] — one record per enemy type (27)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || &amp;lt;code&amp;gt;Health&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;HP per Wave&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Damage per Wave&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Speed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Resources&amp;lt;/code&amp;gt;. Per-wave values scale linearly; wave-10 projections are computed by [[Module:Compare]], not stored.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Classification keys (&amp;lt;code&amp;gt;Melee / Range / Big / Boss&amp;lt;/code&amp;gt;) → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The enemy&#039;s own lore. When a Codex entry matches by name, [[Module:Codex]] shows the fuller codex text instead at render time.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Compare, Module:Navbox, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Achievements.json]] — one record per Steam achievement (100)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The unlock objective (e.g. &amp;quot;Kill 10,000 enemies&amp;quot;) — rendered as both the infobox Objective row and the == Objective == section.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core (infobox/index) and Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Codex.json]] — one record per encyclopedia entry (90)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Enemies / Weapons / Events / Tips&amp;lt;/code&amp;gt; — drives index grouping and navbox rows.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The full lore text.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;redirect_slug&amp;lt;/code&amp;gt; || string (optional) || Present when the entry matched a weapon/enemy by name: &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; then points at the ENTITY&#039;s page (which inlines this lore), and &amp;lt;code&amp;gt;redirect_slug&amp;lt;/code&amp;gt; is the old &amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt;-Codex&amp;lt;/code&amp;gt; title kept as a redirect.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Tips entries named after a stat (e.g. &amp;quot;Holy Damage&amp;quot;) are that stat&#039;s hub page. [[Module:Codex]] joins entries to weapons/enemies by normalized name at render time. Consumed by Module:Core, Module:Codex, Module:Navbox, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Tags.json]] — one record per tag / perk keyword (84)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; || string || The raw tag KEY exactly as used in other categories&#039; &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays (e.g. &amp;lt;code&amp;gt;BreakArmor&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; || string || Tag page title, always &amp;lt;code&amp;gt;Tag-&amp;amp;lt;Key&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt; || string || The effect text; empty when the game data ships none.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Which entities carry a tag is NOT stored here — Module:Core scans the other categories&#039; &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays at render time (rendered by [[Module:TagIndex]]). Consumed by Module:Core (tag links + effect text everywhere), Module:TagIndex, Module:CrossRef.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-|MediaWiki Guide=&lt;br /&gt;
{{:Help:MediaWiki Guide}}&lt;br /&gt;
&lt;br /&gt;
|-|Special Pages=&lt;br /&gt;
This tab highlights MediaWiki&#039;s special pages most useful for working on this wiki.&lt;br /&gt;
&lt;br /&gt;
* [[Special:SpecialPages]] — List of all special pages.&lt;br /&gt;
* [[Special:WantedPages]] — Pages linked-to but missing. After a scheduled re-publish, this should be near-zero (every bot-published link target exists). Non-zero usually means an editor-added link points at a non-existent page.&lt;br /&gt;
* [[Special:WantedTemplates]] — Templates called but missing. Should be exactly zero on this wiki.&lt;br /&gt;
* [[Special:WantedFiles]] — Image files referenced but never uploaded. Should be near-zero after a clean re-publish.&lt;br /&gt;
* [[Special:WhatLinksHere]] — Reverse-lookup: which pages link to a given page. Useful for &amp;quot;where is this template used?&amp;quot;&lt;br /&gt;
* [[Special:Categories]] — All categories on the wiki.&lt;br /&gt;
* [[Special:UncategorizedPages]] — Main-namespace pages without a &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;[[Category:...]]&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; tag. Should be near-zero — unexpected entries here are bot-publishing bugs worth reporting.&lt;br /&gt;
* [[Special:RecentChanges]] — Live feed of edits.&lt;br /&gt;
* [[MediaWiki:Sidebar]] — Edit the navigation sidebar.&lt;br /&gt;
* [[Special:Upload]] / [[Special:BatchUpload]] — Image upload (single + multi).&lt;br /&gt;
* [[Special:Version]] — Installed MediaWiki extensions, skins, libraries, and version info.&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Help]]&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=MediaWiki:Common.js&amp;diff=2114</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=MediaWiki:Common.js&amp;diff=2114"/>
		<updated>2026-06-10T16:21:32Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Expand all / Collapse all controls for Help catalogues&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*&lt;br /&gt;
 * MediaWiki:Common.js — baseline JS.&lt;br /&gt;
 *&lt;br /&gt;
 * Ships unchanged across all games. No substitution slots — this file is fully&lt;br /&gt;
 * generic. Per-game customizations layer on top by editing&lt;br /&gt;
 * MediaWiki:Common.js/&amp;lt;gameslug&amp;gt;.js on the live wiki (manually, not pipeline-managed).&lt;br /&gt;
 *&lt;br /&gt;
 * SCOPE DISCIPLINE (mirrors MediaWiki:Common.css):&lt;br /&gt;
 *   - Tab init for .wm-tabs (forward-looking — Phase 12 catalogue #1 variant-rarity&lt;br /&gt;
 *     tabbed infoboxes will emit elements with these data attributes)&lt;br /&gt;
 *   - Collapsible-section toggle for .wm-collapsible (alternative to MediaWiki&#039;s&lt;br /&gt;
 *     built-in mw-collapsible when we want consistent class names)&lt;br /&gt;
 *   - NO business logic, NO third-party libs, NO modal/popup chrome&lt;br /&gt;
 *&lt;br /&gt;
 * Graceful degradation: every interactive piece must be functional WITHOUT JS&lt;br /&gt;
 * (server-rendered content is what readers see; JS adds interactivity, never&lt;br /&gt;
 * gates the content).&lt;br /&gt;
 *&lt;br /&gt;
 * ES5 only — MediaWiki:Common.js runs without transpilation.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	$( document ).ready( function () {&lt;br /&gt;
		initTabs();&lt;br /&gt;
		initCollapsibles();&lt;br /&gt;
		initCarousels();&lt;br /&gt;
	} );&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Tabbed-panel init. Activates any container with:&lt;br /&gt;
	 *&lt;br /&gt;
	 *   &amp;lt;div class=&amp;quot;wm-tabs&amp;quot; data-wm-tabs&amp;gt;&lt;br /&gt;
	 *     &amp;lt;div class=&amp;quot;wm-tabs__buttons&amp;quot; data-wm-tabs-btns&amp;gt;&lt;br /&gt;
	 *       &amp;lt;div class=&amp;quot;wm-tabs__button&amp;quot; data-wm-tabs-btn=&amp;quot;1&amp;quot;&amp;gt;I&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *       &amp;lt;div class=&amp;quot;wm-tabs__button&amp;quot; data-wm-tabs-btn=&amp;quot;2&amp;quot;&amp;gt;II&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *     &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *     &amp;lt;div class=&amp;quot;wm-tabs__panels&amp;quot; data-wm-tabs-panels&amp;gt;&lt;br /&gt;
	 *       &amp;lt;div class=&amp;quot;wm-tabs__panel&amp;quot; data-wm-tabs-panel=&amp;quot;1&amp;quot;&amp;gt;…&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *       &amp;lt;div class=&amp;quot;wm-tabs__panel wm-tabs__panel--hidden&amp;quot; data-wm-tabs-panel=&amp;quot;2&amp;quot;&amp;gt;…&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *     &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *   &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *&lt;br /&gt;
	 * Click on a button hides every panel except the matching one. First panel&lt;br /&gt;
	 * is visible by default (server-rendered); JS only swaps on click.&lt;br /&gt;
	 *&lt;br /&gt;
	 * If JS is off, the first panel shows and others stay hidden — readers&lt;br /&gt;
	 * see SOMETHING (the most common variant), just not the tabs.&lt;br /&gt;
	 */&lt;br /&gt;
	function initTabs() {&lt;br /&gt;
		var $containers = $( &#039;[data-wm-tabs]&#039; );&lt;br /&gt;
		if ( !$containers.length ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
		$containers.each( function () {&lt;br /&gt;
			var $container = $( this );&lt;br /&gt;
			var $btns = $container.find( &#039;[data-wm-tabs-btn]&#039; );&lt;br /&gt;
			var $panels = $container.find( &#039;[data-wm-tabs-panel]&#039; );&lt;br /&gt;
&lt;br /&gt;
			$btns.on( &#039;click&#039;, function () {&lt;br /&gt;
				var key = $( this ).attr( &#039;data-wm-tabs-btn&#039; );&lt;br /&gt;
				$btns.removeClass( &#039;wm-tabs__button--active&#039; );&lt;br /&gt;
				$( this ).addClass( &#039;wm-tabs__button--active&#039; );&lt;br /&gt;
				$panels.addClass( &#039;wm-tabs__panel--hidden&#039; );&lt;br /&gt;
				$panels.filter( &#039;[data-wm-tabs-panel=&amp;quot;&#039; + key + &#039;&amp;quot;]&#039; )&lt;br /&gt;
					.removeClass( &#039;wm-tabs__panel--hidden&#039; );&lt;br /&gt;
			} );&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Collapsible-section toggle. Activates any element with:&lt;br /&gt;
	 *&lt;br /&gt;
	 *   &amp;lt;div class=&amp;quot;wm-collapsible&amp;quot; data-wm-collapsible&amp;gt;&lt;br /&gt;
	 *     &amp;lt;div class=&amp;quot;wm-collapsible__header&amp;quot; data-wm-collapsible-toggle&amp;gt;Section header (click to collapse)&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *     &amp;lt;div class=&amp;quot;wm-collapsible__body&amp;quot;&amp;gt;…&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *   &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *&lt;br /&gt;
	 * Click on the header toggles a `wm-collapsible--collapsed` class on the&lt;br /&gt;
	 * container. CSS in MediaWiki:Common.css handles the show/hide via&lt;br /&gt;
	 * `.wm-collapsible--collapsed .wm-collapsible__body { display: none; }`.&lt;br /&gt;
	 *&lt;br /&gt;
	 * If JS is off, sections stay expanded (the readable default).&lt;br /&gt;
	 *&lt;br /&gt;
	 * MediaWiki ships a built-in `mw-collapsible` that does similar work; this&lt;br /&gt;
	 * exists for the cases where we want a consistent wm-* class namespace&lt;br /&gt;
	 * across our templates (so MediaWiki:Common.css selectors stay simple).&lt;br /&gt;
	 */&lt;br /&gt;
	function initCollapsibles() {&lt;br /&gt;
		$( &#039;[data-wm-collapsible-toggle]&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
			$( this ).closest( &#039;[data-wm-collapsible]&#039; )&lt;br /&gt;
				.toggleClass( &#039;wm-collapsible--collapsed&#039; );&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Carousel init. Paired with Widget:Carousel emitting.&lt;br /&gt;
	 *&lt;br /&gt;
	 * Indexing convention: data-* attributes are 0-indexed (DOM convention);&lt;br /&gt;
	 * aria-labels are 1-indexed (&amp;quot;Slide 1&amp;quot;, &amp;quot;Slide 2&amp;quot; — human convention).&lt;br /&gt;
	 * Don&#039;t &amp;quot;fix&amp;quot; this — matches WAI-ARIA carousel patterns.&lt;br /&gt;
	 *&lt;br /&gt;
	 *&lt;br /&gt;
	 *   &amp;lt;div class=&amp;quot;wm-carousel&amp;quot; data-wm-carousel&amp;gt;&lt;br /&gt;
	 *     &amp;lt;div data-wm-carousel-slides&amp;gt;&lt;br /&gt;
	 *       &amp;lt;div data-wm-carousel-slide=&amp;quot;0&amp;quot; class=&amp;quot;wm-carousel__slide--active&amp;quot;&amp;gt;…&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *       &amp;lt;div data-wm-carousel-slide=&amp;quot;1&amp;quot;&amp;gt;…&amp;lt;/div&amp;gt;&lt;br /&gt;
	 *     &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *     &amp;lt;div data-wm-carousel-btns&amp;gt;&lt;br /&gt;
	 *       &amp;lt;button data-wm-carousel-btn=&amp;quot;0&amp;quot; class=&amp;quot;wm-carousel__btn--active&amp;quot;&amp;gt;…&amp;lt;/button&amp;gt;&lt;br /&gt;
	 *       &amp;lt;button data-wm-carousel-btn=&amp;quot;1&amp;quot;&amp;gt;…&amp;lt;/button&amp;gt;&lt;br /&gt;
	 *     &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *   &amp;lt;/div&amp;gt;&lt;br /&gt;
	 *&lt;br /&gt;
	 * Click a button → opacity-fade the matching slide in, others out. First&lt;br /&gt;
	 * slide is server-rendered visible (opacity:1); JS-off readers see only that.&lt;br /&gt;
	 *&lt;br /&gt;
	 * Active-state CSS lives in MediaWiki:Common.css. JS toggles class names +&lt;br /&gt;
	 * inline opacity (the inline rule is for graceful degradation when CSS&lt;br /&gt;
	 * hasn&#039;t loaded yet).&lt;br /&gt;
	 */&lt;br /&gt;
	function initCarousels() {&lt;br /&gt;
		$( &#039;[data-wm-carousel]&#039; ).each( function () {&lt;br /&gt;
			var $car = $( this );&lt;br /&gt;
			var $slides = $car.find( &#039;[data-wm-carousel-slide]&#039; );&lt;br /&gt;
			var $btns = $car.find( &#039;[data-wm-carousel-btn]&#039; );&lt;br /&gt;
&lt;br /&gt;
			$btns.on( &#039;click&#039;, function () {&lt;br /&gt;
				var idx = $( this ).attr( &#039;data-wm-carousel-btn&#039; );&lt;br /&gt;
				$btns.removeClass( &#039;wm-carousel__btn--active&#039; ).css( &#039;background&#039;, &#039;transparent&#039; );&lt;br /&gt;
				$( this ).addClass( &#039;wm-carousel__btn--active&#039; ).css( &#039;background&#039;, &#039;#888&#039; );&lt;br /&gt;
				$slides.removeClass( &#039;wm-carousel__slide--active&#039; ).css( &#039;opacity&#039;, &#039;0&#039; );&lt;br /&gt;
				$slides.filter( &#039;[data-wm-carousel-slide=&amp;quot;&#039; + idx + &#039;&amp;quot;]&#039; )&lt;br /&gt;
					.addClass( &#039;wm-carousel__slide--active&#039; ).css( &#039;opacity&#039;, &#039;1&#039; );&lt;br /&gt;
			} );&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
}( jQuery ) );&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Arms of God — per-game interactivity block (appended after the generic&lt;br /&gt;
 * library block by 5-build-pages/code/emit_pages.py::emit_css_js).&lt;br /&gt;
 *&lt;br /&gt;
 * 1. Hover-infobox tooltips: any element with class=&amp;quot;wm-tip&amp;quot;&lt;br /&gt;
 *    data-tip-title=&amp;quot;&amp;lt;page&amp;gt;&amp;quot; (emitted by Module:Iconbox, Module:CrossRef,&lt;br /&gt;
 *    Module:Index, Module:LinkGrid, Module:StatIndex and the comparison&lt;br /&gt;
 *    datasets) shows the target page&#039;s infobox in a dark floating popup on&lt;br /&gt;
 *    hover. The infobox HTML is fetched once per title via api.php&lt;br /&gt;
 *    action=parse and cached for the page view. No JS / touch-only device /&lt;br /&gt;
 *    page without an infobox -&amp;gt; plain link, no popup.&lt;br /&gt;
 *&lt;br /&gt;
 * 2. Comparison filter chips: .wm-filterbar bars (shipped display:none so&lt;br /&gt;
 *    no-JS readers never see inert controls) are revealed; clicking a&lt;br /&gt;
 *    .wm-chip toggles it and hides table rows whose data-&amp;lt;group&amp;gt; attribute&lt;br /&gt;
 *    (data-element / data-class / data-tier / data-type) has no value in&lt;br /&gt;
 *    common with the active chips of that group. Groups AND together;&lt;br /&gt;
 *    chips within a group OR together. Reset clears everything.&lt;br /&gt;
 *&lt;br /&gt;
 * 3. Catalogue expand/collapse-all: .wm-expand-all / .wm-collapse-all&lt;br /&gt;
 *    buttons inside a .wm-catalogue wrapper (Help:Wiki Editing module /&lt;br /&gt;
 *    template / data-schema catalogues) expand or collapse every&lt;br /&gt;
 *    jquery.makeCollapsible table inside THAT wrapper only.&lt;br /&gt;
 *&lt;br /&gt;
 * ES5 only, jQuery + mw provided by MediaWiki. Dark styling lives in&lt;br /&gt;
 * MediaWiki:Common.css (.wm-tipbox / .wm-chip / .wm-row-hidden /&lt;br /&gt;
 * .wm-catalogue-controls).&lt;br /&gt;
 */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	$( document ).ready( function () {&lt;br /&gt;
		initTipPopups();&lt;br /&gt;
		initFilterChips();&lt;br /&gt;
		initCatalogueControls();&lt;br /&gt;
	} );&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------- hover tooltips ---- */&lt;br /&gt;
&lt;br /&gt;
	function initTipPopups() {&lt;br /&gt;
		// Hover-driven feature: skip entirely on touch-only devices.&lt;br /&gt;
		if ( window.matchMedia &amp;amp;&amp;amp; window.matchMedia( &#039;(hover: none)&#039; ).matches ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
		var cache = {};   // title -&amp;gt; html string, or false (= no popup)&lt;br /&gt;
		var timer = null;&lt;br /&gt;
		var $box = null;&lt;br /&gt;
		var current = null;&lt;br /&gt;
&lt;br /&gt;
		function apiUrl() {&lt;br /&gt;
			return mw.config.get( &#039;wgScriptPath&#039; ) + &#039;/api.php&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		function ensureBox() {&lt;br /&gt;
			if ( !$box ) {&lt;br /&gt;
				$box = $( &#039;&amp;lt;div&amp;gt;&#039; ).addClass( &#039;wm-tipbox&#039; )&lt;br /&gt;
					.css( {&lt;br /&gt;
						position: &#039;absolute&#039;,&lt;br /&gt;
						zIndex: 1000,&lt;br /&gt;
						maxWidth: &#039;360px&#039;,&lt;br /&gt;
						background: &#039;#1c1d22&#039;,&lt;br /&gt;
						color: &#039;#e6e6e6&#039;,&lt;br /&gt;
						border: &#039;1px solid #3a3c44&#039;,&lt;br /&gt;
						borderRadius: &#039;5px&#039;,&lt;br /&gt;
						boxShadow: &#039;0 6px 18px rgba(0,0,0,0.65)&#039;,&lt;br /&gt;
						padding: &#039;6px&#039;,&lt;br /&gt;
						fontSize: &#039;0.9em&#039;,&lt;br /&gt;
						pointerEvents: &#039;none&#039;,&lt;br /&gt;
						display: &#039;none&#039;&lt;br /&gt;
					} )&lt;br /&gt;
					.appendTo( document.body );&lt;br /&gt;
			}&lt;br /&gt;
			return $box;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		function position( $b, x, y ) {&lt;br /&gt;
			var w = $b.outerWidth() || 360;&lt;br /&gt;
			var winRight = $( window ).scrollLeft() + $( window ).width();&lt;br /&gt;
			var left = ( x + 18 + w &amp;gt; winRight ) ? Math.max( 4, x - w - 12 ) : x + 18;&lt;br /&gt;
			$b.css( { left: left + &#039;px&#039;, top: ( y + 14 ) + &#039;px&#039; } );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		function extract( html ) {&lt;br /&gt;
			var $root = $( &#039;&amp;lt;div&amp;gt;&#039; ).html( html );&lt;br /&gt;
			var $ib = $root.find( &#039;.infobox&#039; ).first();&lt;br /&gt;
			if ( $ib.length ) {&lt;br /&gt;
				// neutralise the on-page float/width inline styles&lt;br /&gt;
				$ib.css( { float: &#039;none&#039;, width: &#039;auto&#039;, maxWidth: &#039;340px&#039;, margin: 0 } );&lt;br /&gt;
				return $( &#039;&amp;lt;div&amp;gt;&#039; ).append( $ib ).html();&lt;br /&gt;
			}&lt;br /&gt;
			var $p = $root.find( &#039;p&#039; ).filter( function () {&lt;br /&gt;
				return $.trim( $( this ).text() ).length &amp;gt; 0;&lt;br /&gt;
			} ).first();&lt;br /&gt;
			if ( $p.length ) {&lt;br /&gt;
				var text = $.trim( $p.text() );&lt;br /&gt;
				if ( text.length &amp;gt; 320 ) {&lt;br /&gt;
					text = text.slice( 0, 317 ) + &#039;…&#039;;&lt;br /&gt;
				}&lt;br /&gt;
				return $( &#039;&amp;lt;div&amp;gt;&#039; ).text( text ).html();&lt;br /&gt;
			}&lt;br /&gt;
			return false;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		function show( title, x, y ) {&lt;br /&gt;
			if ( cache[ title ] === false ) {&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			if ( typeof cache[ title ] === &#039;string&#039; ) {&lt;br /&gt;
				var $b = ensureBox();&lt;br /&gt;
				$b.html( cache[ title ] ).show();&lt;br /&gt;
				position( $b, x, y );&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			$.getJSON( apiUrl(), {&lt;br /&gt;
				action: &#039;parse&#039;,&lt;br /&gt;
				page: title,&lt;br /&gt;
				prop: &#039;text&#039;,&lt;br /&gt;
				redirects: 1,&lt;br /&gt;
				disablelimitreport: 1,&lt;br /&gt;
				formatversion: 2,&lt;br /&gt;
				format: &#039;json&#039;&lt;br /&gt;
			} ).done( function ( data ) {&lt;br /&gt;
				var html = data &amp;amp;&amp;amp; data.parse &amp;amp;&amp;amp; data.parse.text;&lt;br /&gt;
				cache[ title ] = html ? extract( html ) : false;&lt;br /&gt;
				// only show if the cursor is still on the same link&lt;br /&gt;
				if ( current === title &amp;amp;&amp;amp; cache[ title ] ) {&lt;br /&gt;
					var $b = ensureBox();&lt;br /&gt;
					$b.html( cache[ title ] ).show();&lt;br /&gt;
					position( $b, x, y );&lt;br /&gt;
				}&lt;br /&gt;
			} ).fail( function () {&lt;br /&gt;
				cache[ title ] = false;&lt;br /&gt;
			} );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		function hide() {&lt;br /&gt;
			current = null;&lt;br /&gt;
			if ( timer ) {&lt;br /&gt;
				clearTimeout( timer );&lt;br /&gt;
				timer = null;&lt;br /&gt;
			}&lt;br /&gt;
			if ( $box ) {&lt;br /&gt;
				$box.hide();&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		$( document ).on( &#039;mouseenter&#039;, &#039;.wm-tip&#039;, function ( e ) {&lt;br /&gt;
			// no popups for links rendered inside a popup&lt;br /&gt;
			if ( $( this ).closest( &#039;.wm-tipbox&#039; ).length ) {&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			var title = $( this ).attr( &#039;data-tip-title&#039; );&lt;br /&gt;
			if ( !title ) {&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			var x = e.pageX, y = e.pageY;&lt;br /&gt;
			current = title;&lt;br /&gt;
			if ( timer ) {&lt;br /&gt;
				clearTimeout( timer );&lt;br /&gt;
			}&lt;br /&gt;
			timer = setTimeout( function () {&lt;br /&gt;
				if ( current === title ) {&lt;br /&gt;
					show( title, x, y );&lt;br /&gt;
				}&lt;br /&gt;
			}, 160 );&lt;br /&gt;
		} ).on( &#039;mouseleave&#039;, &#039;.wm-tip&#039;, hide );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------- filter chips ------ */&lt;br /&gt;
&lt;br /&gt;
	function initFilterChips() {&lt;br /&gt;
		var $bars = $( &#039;.wm-filterbar&#039; );&lt;br /&gt;
		if ( !$bars.length ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
		var GROUPS = [ &#039;element&#039;, &#039;class&#039;, &#039;tier&#039;, &#039;type&#039; ];&lt;br /&gt;
&lt;br /&gt;
		function rowSelector() {&lt;br /&gt;
			var sel = [];&lt;br /&gt;
			for ( var i = 0; i &amp;lt; GROUPS.length; i++ ) {&lt;br /&gt;
				sel.push( &#039;tr[data-&#039; + GROUPS[ i ] + &#039;]&#039; );&lt;br /&gt;
			}&lt;br /&gt;
			return sel.join( &#039;,&#039; );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		function apply() {&lt;br /&gt;
			var active = {};&lt;br /&gt;
			$( &#039;.wm-chip-on&#039; ).each( function () {&lt;br /&gt;
				var g = $( this ).attr( &#039;data-group&#039; );&lt;br /&gt;
				var v = $( this ).attr( &#039;data-val&#039; );&lt;br /&gt;
				if ( !g || !v ) {&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				( active[ g ] = active[ g ] || [] ).push( v );&lt;br /&gt;
			} );&lt;br /&gt;
			$( rowSelector() ).each( function () {&lt;br /&gt;
				var row = this;&lt;br /&gt;
				var hidden = false;&lt;br /&gt;
				for ( var g in active ) {&lt;br /&gt;
					var rv = row.getAttribute( &#039;data-&#039; + g );&lt;br /&gt;
					if ( rv === null ) {&lt;br /&gt;
						continue; // row not classified on this axis&lt;br /&gt;
					}&lt;br /&gt;
					var tokens = rv.split( &#039; &#039; );&lt;br /&gt;
					var hit = false;&lt;br /&gt;
					for ( var i = 0; i &amp;lt; tokens.length &amp;amp;&amp;amp; !hit; i++ ) {&lt;br /&gt;
						for ( var j = 0; j &amp;lt; active[ g ].length &amp;amp;&amp;amp; !hit; j++ ) {&lt;br /&gt;
							if ( tokens[ i ] === active[ g ][ j ] ) {&lt;br /&gt;
								hit = true;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					if ( !hit ) {&lt;br /&gt;
						hidden = true;&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				$( row ).toggleClass( &#039;wm-row-hidden&#039;, hidden )&lt;br /&gt;
					.css( &#039;display&#039;, hidden ? &#039;none&#039; : &#039;&#039; );&lt;br /&gt;
			} );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		$bars.show(); // bars ship display:none; only JS users see them&lt;br /&gt;
&lt;br /&gt;
		$( document ).on( &#039;click&#039;, &#039;.wm-filterbar .wm-chip&#039;, function () {&lt;br /&gt;
			var $chip = $( this );&lt;br /&gt;
			if ( $chip.hasClass( &#039;wm-chip-clear&#039; ) ) {&lt;br /&gt;
				$chip.closest( &#039;.wm-filterbar&#039; ).find( &#039;.wm-chip-on&#039; )&lt;br /&gt;
					.removeClass( &#039;wm-chip-on&#039; )&lt;br /&gt;
					.each( restyleChip );&lt;br /&gt;
				apply();&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			$chip.toggleClass( &#039;wm-chip-on&#039; );&lt;br /&gt;
			restyleChip.call( this );&lt;br /&gt;
			apply();&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
		// Inline-style fallback so the active state is visible even if the&lt;br /&gt;
		// site CSS is stale (every shipped surface styles inline-first).&lt;br /&gt;
		function restyleChip() {&lt;br /&gt;
			var $chip = $( this );&lt;br /&gt;
			if ( $chip.hasClass( &#039;wm-chip-on&#039; ) ) {&lt;br /&gt;
				$chip.css( { background: &#039;#f1e9d2&#039;, color: &#039;#111&#039;, fontWeight: &#039;bold&#039; } );&lt;br /&gt;
			} else {&lt;br /&gt;
				$chip.css( { background: &#039;&#039;, color: &#039;&#039;, fontWeight: &#039;&#039; } );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ------------------------------- catalogue expand/collapse all ---- */&lt;br /&gt;
&lt;br /&gt;
	function initCatalogueControls() {&lt;br /&gt;
		$( document ).on( &#039;click&#039;, &#039;.wm-expand-all, .wm-collapse-all&#039;, function ( e ) {&lt;br /&gt;
			e.preventDefault();&lt;br /&gt;
			var expand = $( this ).hasClass( &#039;wm-expand-all&#039; );&lt;br /&gt;
			var $scope = $( this ).closest( &#039;.wm-catalogue&#039; );&lt;br /&gt;
			if ( !$scope.length ) {&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			// makeCollapsible ships with core page rendering, but wait for&lt;br /&gt;
			// the module anyway so the data API is guaranteed initialized.&lt;br /&gt;
			mw.loader.using( &#039;jquery.makeCollapsible&#039; ).then( function () {&lt;br /&gt;
				$scope.find( &#039;.mw-collapsible&#039; ).each( function () {&lt;br /&gt;
					var $el = $( this );&lt;br /&gt;
					var api = $el.data( &#039;mw-collapsible&#039; );&lt;br /&gt;
					if ( api &amp;amp;&amp;amp; api.expand &amp;amp;&amp;amp; api.collapse ) {&lt;br /&gt;
						if ( expand ) {&lt;br /&gt;
							api.expand();&lt;br /&gt;
						} else {&lt;br /&gt;
							api.collapse();&lt;br /&gt;
						}&lt;br /&gt;
						return;&lt;br /&gt;
					}&lt;br /&gt;
					// Fallback: synthetic toggle click, but only when the&lt;br /&gt;
					// current collapsed state mismatches the target so it&lt;br /&gt;
					// never flips a table the wrong way.&lt;br /&gt;
					var collapsed = $el.hasClass( &#039;mw-collapsed&#039; );&lt;br /&gt;
					if ( collapsed === expand ) {&lt;br /&gt;
						$el.find( &#039;.mw-collapsible-toggle&#039; ).first().trigger( &#039;click&#039; );&lt;br /&gt;
					}&lt;br /&gt;
				} );&lt;br /&gt;
			} );&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}( jQuery ) );&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=MediaWiki:Common.css&amp;diff=2113</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=MediaWiki:Common.css&amp;diff=2113"/>
		<updated>2026-06-10T16:21:31Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: Expand all / Collapse all controls for Help catalogues&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ===================================================================&lt;br /&gt;
   Arms of God — dark theme (paste into MediaWiki:Common.css).&lt;br /&gt;
   The skin darkens the page to rgba(0,0,0,0.7) with light body text;&lt;br /&gt;
   these tokens make every pipeline-shipped panel (infobox, navbox,&lt;br /&gt;
   index/comparison wikitables, callouts, badges) match it.&lt;br /&gt;
&lt;br /&gt;
   All bot-published pages reference these tokens inline as&lt;br /&gt;
   var(--token, &amp;lt;dark fallback&amp;gt;) — once this block is live the tokens&lt;br /&gt;
   below win, and editing a value here restyles the whole wiki.&lt;br /&gt;
   If you re-paste an updated version, replace everything between the&lt;br /&gt;
   WM-BLOCK-BEGIN / WM-BLOCK-END markers.&lt;br /&gt;
   =================================================================== */&lt;br /&gt;
:root {&lt;br /&gt;
  /* base palette */&lt;br /&gt;
  --theme-primary: #26272e;&lt;br /&gt;
  --theme-accent: #f1e9d2;&lt;br /&gt;
&lt;br /&gt;
  /* infobox */&lt;br /&gt;
  --infobox-bg: #1c1d22;&lt;br /&gt;
  --infobox-border: #3a3c44;&lt;br /&gt;
  --infobox-header-bg: #26272e;&lt;br /&gt;
  --infobox-header-fg: #f1e9d2;&lt;br /&gt;
  --infobox-text: #e6e6e6;&lt;br /&gt;
&lt;br /&gt;
  /* navbox */&lt;br /&gt;
  --navbox-bar-bg: #26272e;&lt;br /&gt;
  --navbox-bar-fg: #f1e9d2;&lt;br /&gt;
  --navbox-bg: #1c1d22;&lt;br /&gt;
  --navbox-text: #dcdcdc;&lt;br /&gt;
  --navbox-border: #3a3c44;&lt;br /&gt;
&lt;br /&gt;
  /* tables (category indexes, comparison tables, navbox bodies) */&lt;br /&gt;
  --table-header-bg: #26272e;&lt;br /&gt;
  --table-row-odd: #1b1c20;&lt;br /&gt;
  --table-row-even: #202127;&lt;br /&gt;
  --table-text: #e6e6e6;&lt;br /&gt;
  --table-border: #3a3c44;&lt;br /&gt;
  --table-row-hover: #2a2c34;&lt;br /&gt;
&lt;br /&gt;
  /* links on dark */&lt;br /&gt;
  --link-fg: #7db8ff;&lt;br /&gt;
&lt;br /&gt;
  /* weapon/blessing/upgrade/passive tier badges (manifest rarity colors) */&lt;br /&gt;
  --tier-1-bg: #bbbbbb;&lt;br /&gt;
  --tier-2-bg: #69c343;&lt;br /&gt;
  --tier-3-bg: #e94040;&lt;br /&gt;
&lt;br /&gt;
  /* damage-type accents (elemental chips in infoboxes + comparison cells) */&lt;br /&gt;
  --dmg-holy: #e8c34a;&lt;br /&gt;
  --dmg-fire: #ff6b35;&lt;br /&gt;
  --dmg-electric: #4ea3ff;&lt;br /&gt;
&lt;br /&gt;
  /* callouts */&lt;br /&gt;
  --tip-bg: rgba(255, 255, 255, 0.05);&lt;br /&gt;
  --tip-border: #69c343;&lt;br /&gt;
  --cursed-fg: #ff6b6b;&lt;br /&gt;
&lt;br /&gt;
  /* panels: template /doc boxes, disambig notes, edit notices, needs-image */&lt;br /&gt;
  --panel-bg: #1c1d22;&lt;br /&gt;
  --panel-border: #3a3c44;&lt;br /&gt;
  --panel-text: #e6e6e6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- wikitables: indexes, comparison tables, navbox bodies ---------- */&lt;br /&gt;
.mw-parser-output .wikitable {&lt;br /&gt;
  background: var(--table-row-odd);&lt;br /&gt;
  color: var(--table-text);&lt;br /&gt;
  border-color: var(--table-border);&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .wikitable &amp;gt; tr &amp;gt; th,&lt;br /&gt;
.mw-parser-output .wikitable &amp;gt; * &amp;gt; tr &amp;gt; th {&lt;br /&gt;
  background: var(--table-header-bg);&lt;br /&gt;
  color: var(--infobox-header-fg);&lt;br /&gt;
  border-color: var(--table-border);&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .wikitable &amp;gt; tr &amp;gt; td,&lt;br /&gt;
.mw-parser-output .wikitable &amp;gt; * &amp;gt; tr &amp;gt; td {&lt;br /&gt;
  border-color: var(--table-border);&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .wikitable &amp;gt; * &amp;gt; tr:nth-child(even) &amp;gt; td {&lt;br /&gt;
  background: var(--table-row-even);&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .wikitable &amp;gt; * &amp;gt; tr:hover &amp;gt; td {&lt;br /&gt;
  background: var(--table-row-hover);&lt;br /&gt;
}&lt;br /&gt;
/* keep the sortable arrows readable on the dark header */&lt;br /&gt;
.client-js .mw-parser-output .wikitable.sortable th {&lt;br /&gt;
  color: var(--infobox-header-fg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- infobox -------------------------------------------------------- */&lt;br /&gt;
.mw-parser-output .infobox {&lt;br /&gt;
  background: var(--infobox-bg);&lt;br /&gt;
  color: var(--infobox-text);&lt;br /&gt;
  border-color: var(--infobox-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- navbox ---------------------------------------------------------- */&lt;br /&gt;
.mw-parser-output .navbox {&lt;br /&gt;
  background: var(--navbox-bg);&lt;br /&gt;
  color: var(--navbox-text);&lt;br /&gt;
  border-color: var(--navbox-border);&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .navbox-bar {&lt;br /&gt;
  background: var(--navbox-bar-bg);&lt;br /&gt;
  color: var(--navbox-bar-fg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- links: readable blue on dark; redlinks lightened ---------------- */&lt;br /&gt;
.mw-parser-output a:not(.new):not(.external) { color: var(--link-fg); }&lt;br /&gt;
.mw-parser-output a.new { color: #ff8080; }&lt;br /&gt;
&lt;br /&gt;
/* ---- tier badges + damage-type chips (class hooks) ------------------- */&lt;br /&gt;
.wm-badge { display: inline-block; padding: 1px 8px; border-radius: 3px;&lt;br /&gt;
            font-size: 0.85em; font-weight: bold; }&lt;br /&gt;
.wm-badge-tier1 { background: var(--tier-1-bg); color: #111; }&lt;br /&gt;
.wm-badge-tier2 { background: var(--tier-2-bg); color: #111; }&lt;br /&gt;
.wm-badge-tier3 { background: var(--tier-3-bg); color: #fff; }&lt;br /&gt;
.wm-dmg-holy { color: var(--dmg-holy); }&lt;br /&gt;
.wm-dmg-fire { color: var(--dmg-fire); }&lt;br /&gt;
.wm-dmg-electric { color: var(--dmg-electric); }&lt;br /&gt;
&lt;br /&gt;
/* ---- callouts ({{Tip}}) ---------------------------------------------- */&lt;br /&gt;
.wm-callout {&lt;br /&gt;
  background: var(--tip-bg);&lt;br /&gt;
  color: var(--table-text);&lt;br /&gt;
  border-color: var(--tip-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- panels: /doc boxes, disambig, edit notices, needs-image ---------- */&lt;br /&gt;
.mw-parser-output .wm-template-doc, .wm-template-doc,&lt;br /&gt;
.mw-parser-output .wm-disambig, .wm-disambig,&lt;br /&gt;
.mw-parser-output .wm-needs-image, .wm-needs-image,&lt;br /&gt;
.editnotice {&lt;br /&gt;
  background: var(--panel-bg);&lt;br /&gt;
  color: var(--panel-text);&lt;br /&gt;
  border-color: var(--panel-border);&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .wm-quote, .wm-quote {&lt;br /&gt;
  border-left-color: var(--panel-border);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- category-links footer bar (operator addition, rev 1849) ---------- */&lt;br /&gt;
.catlinks {&lt;br /&gt;
  background: var(--infobox-bg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===================================================================&lt;br /&gt;
   2026-06-10d feature block: hover-infobox tooltips + filter chips&lt;br /&gt;
   (paired with the .wm-tip / .wm-chip handlers in MediaWiki:Common.js)&lt;br /&gt;
   =================================================================== */&lt;br /&gt;
&lt;br /&gt;
/* ---- hover tooltip popup (.wm-tip links -&amp;gt; floating infobox) --------- */&lt;br /&gt;
.wm-tipbox {&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  z-index: 1000;&lt;br /&gt;
  max-width: 360px;&lt;br /&gt;
  background: var(--panel-bg, #1c1d22);&lt;br /&gt;
  color: var(--panel-text, #e6e6e6);&lt;br /&gt;
  border: 1px solid var(--panel-border, #3a3c44);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.65);&lt;br /&gt;
  padding: 6px;&lt;br /&gt;
  font-size: 0.9em;&lt;br /&gt;
  pointer-events: none;&lt;br /&gt;
}&lt;br /&gt;
/* the fetched infobox floats right at 280px on its own page — neutralise */&lt;br /&gt;
.wm-tipbox .infobox {&lt;br /&gt;
  float: none !important;&lt;br /&gt;
  width: auto !important;&lt;br /&gt;
  max-width: 340px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.wm-tipbox p { margin: 0.2em 0; }&lt;br /&gt;
.wm-tip &amp;gt; a { cursor: help; }&lt;br /&gt;
&lt;br /&gt;
/* ---- filter chips (comparison tables + tiered indexes) --------------- */&lt;br /&gt;
.wm-filterbar { line-height: 2; }&lt;br /&gt;
.wm-chip {&lt;br /&gt;
  -webkit-user-select: none;&lt;br /&gt;
  user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.wm-chip:hover { background: var(--table-row-hover, #2a2c34); }&lt;br /&gt;
.wm-chip-on {&lt;br /&gt;
  background: var(--theme-accent, #f1e9d2) !important;&lt;br /&gt;
  color: #111 !important;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.wm-row-hidden { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ---- linked damage-type chip labels keep their accent color ---------- */&lt;br /&gt;
.wm-dmg-holy a, .wm-dmg-fire a, .wm-dmg-electric a { color: inherit; }&lt;br /&gt;
&lt;br /&gt;
/* ---- Help:Wiki Editing catalogue expand/collapse-all controls --------- */&lt;br /&gt;
/* (paired with the .wm-expand-all / .wm-collapse-all handler in&lt;br /&gt;
   MediaWiki:Common.js; same dark-button look as .wm-chip) */&lt;br /&gt;
.wm-catalogue-controls { margin: 4px 0 10px; }&lt;br /&gt;
.wm-expand-all,&lt;br /&gt;
.wm-collapse-all {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  padding: 2px 10px;&lt;br /&gt;
  margin-right: 6px;&lt;br /&gt;
  border: 1px solid var(--panel-border, #3a3c44);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: var(--panel-bg, #1c1d22);&lt;br /&gt;
  color: var(--panel-text, #e6e6e6);&lt;br /&gt;
  font-size: 0.9em;&lt;br /&gt;
  -webkit-user-select: none;&lt;br /&gt;
  user-select: none;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.wm-expand-all:hover,&lt;br /&gt;
.wm-collapse-all:hover { background: var(--table-row-hover, #2a2c34); }&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Arms_of_God_Wiki&amp;diff=2112</id>
		<title>Arms of God Wiki</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Arms_of_God_Wiki&amp;diff=2112"/>
		<updated>2026-06-10T16:18:04Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: game is launched, new trailer, minor tweaks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; align-items:flex-start; justify-content:center; top; flex-wrap:wrap;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;text-align:center; width:100%; max-width:1100px; display:inline-block; padding:30px;&amp;quot;&amp;gt;&lt;br /&gt;
		[[File:Arms-of-god-main.jpg|link=https://darkjay.com/armsofgod/|x400px]]&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:center; font-weight:bold;&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-size:32pt;&amp;quot;&amp;gt;Welcome to &amp;lt;i&amp;gt;Arms of God&amp;lt;/i&amp;gt; Wiki&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;font-size:14pt;&amp;quot;&amp;gt;The comprehensive Arms of God Wiki is written and maintained by the players. [[Special:ActiveUsers|{{NUMBEROFUSERS}} people]] are currently maintaining [[Special:Statistics|{{NUMBEROFPAGES}} pages ({{NUMBEROFARTICLES}} articles)]] on this Wiki so far! Please feel free to contribute by creating new articles or expanding existing ones. &amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&amp;quot;color: green; font-size: 20px;&amp;quot;&amp;gt; &amp;lt;b&amp;gt;New featured articles: [[Weapons]] and [[Blessings]]&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;div&amp;gt;&lt;br /&gt;
			&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt;Content&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Story]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Updates]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Game modes]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Achievements_ForbiddenKnowledge.jpg|link=Achievements|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Achievements]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Community]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; flex-wrap:width:100%; nowrap; justify-content:space-around; border:1px solid #DD952A;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Heroes_primaris.png|link=Characters|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Characters]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Weapons_Cutter.png|link=Weapons|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Weapons]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Blessings_distant_judgment.png|link=Blessings|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Blessings]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Passives_damage_i.png|link=Passives|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Passives]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Upgrades_Incinerator_Coil.png|link=Upgrades|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Upgrades]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Cross_DefaultUpgrade.png|link=Crux|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Crux]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; flex-wrap:width:100%; nowrap; justify-content:space-around; border:1px solid #DD952A;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
			&amp;lt;div style=&amp;quot;display:flex; justify-content:space-evenly; flex-wrap:wrap; padding:2%; font-size:15pt; font-weight:bold;&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Stat_Damage.png|link=Stats|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Stats]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					[[File:Raziel.png|link=Enemies|x90px]]&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Enemies]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
				&amp;lt;div style=&amp;quot;display:inline-block; text-align:center; min-width:120px;&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;p&amp;gt;[[Prayers]]&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;width:100%; max-width:600px; display:inline-block; padding:30px;&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:nowrap; justify-content:space-around;&amp;quot;&amp;gt;&lt;br /&gt;
			[[File:DarkJay_Logo_Color.png|link=https://darkjay.com/|x200px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-bottom:10px;&amp;quot;&amp;gt;About Arms of God&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;p style=&amp;quot;text-align:left; margin:9px; font-size:14pt; &amp;quot;&amp;gt;Gear up for a battle through hell in this roguelite Bullet Heaven Arena Autoshooter with Doom-inspired gore vibes, metal music, and a satisfying combat feel. Wield five weapons at once, upgrade and merge them to forge indestructible builds, and bring divine justice to a world on the brink of chaos. &amp;lt;br&amp;gt; [https://store.steampowered.com/app/3100310/Arms_of_God/ Buy on Steam.] &amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt; Arms of God Community&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; justify-content:space-evenly; align-items:center;&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;p style=&amp;quot;text-align:center; font-size:14pt;&amp;quot;&amp;gt;Have strategy questions or want to talk to fellow Arms of God players? Check out the official subreddit and Discord server.&amp;lt;/p&amp;gt;&lt;br /&gt;
			[[File:Reddit_logo.png|link=https://www.reddit.com/r/armsofgod/|x90px]]&lt;br /&gt;
			[[File:Discord_logo.png|link=https://discord.com/invite/QeUqjGFCYb|x90px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:30px;&amp;quot;&amp;gt;Latest Trailer&amp;lt;/p&amp;gt;&lt;br /&gt;
		{{#evu:https://www.youtube.com/watch?v=jKuVy4DtbXU|alignment=center}}&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p style=&amp;quot;border:1px solid #DD952A; text-align:center; font-size:32px; padding:3px; font-weight:bold; margin:9px; border-radius:10px; margin-top:40px;&amp;quot;&amp;gt;Official Links&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:20px;&amp;quot;&amp;gt;&lt;br /&gt;
			[[File:Steam_Logo.png|link=https://store.steampowered.com/app/3100310/Arms_of_God/|x90px]]&lt;br /&gt;
			[[File:Youtube_Logo.png|link=https://www.youtube.com/@darkjaystudio|x120px]]&lt;br /&gt;
			[[File:Twitter_Logo.png|link=https://x.com/ArmsOfGodGame|x120px]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Help:Wiki_Editing&amp;diff=2111</id>
		<title>Help:Wiki Editing</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Help:Wiki_Editing&amp;diff=2111"/>
		<updated>2026-06-10T16:04:56Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- Bot-published page. Source of truth: 5-build-pages/code/_game/help_wiki_editing.wiki&lt;br /&gt;
     (substituted + copied by emit_pages.py). Direct edits to this wiki page are&lt;br /&gt;
     overwritten on the next re-publish — improve the source instead. --&amp;gt;&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-|How this wiki works=&lt;br /&gt;
== The one-sentence version ==&lt;br /&gt;
&#039;&#039;&#039;All data on this wiki lives in ten JSON pages — &amp;lt;code&amp;gt;Data:Weapons.json&amp;lt;/code&amp;gt; through &amp;lt;code&amp;gt;Data:Tags.json&amp;lt;/code&amp;gt; — and every table, infobox, navbox, tag page and stat page is computed from them by Lua modules at render time.&#039;&#039;&#039; There are no precomputed pages: edit a value once on a Data page, purge, and every view of that value updates everywhere.&lt;br /&gt;
&lt;br /&gt;
== Data flow ==&lt;br /&gt;
&amp;lt;pre style=&amp;quot;background:var(--panel-bg, #1c1d22);color:var(--panel-text, #e6e6e6);border:1px solid var(--panel-border, #3a3c44);&amp;quot;&amp;gt;&lt;br /&gt;
 SOURCE OF TRUTH (edit values here)&lt;br /&gt;
 ┌────────────────────────────────────────────────────────────────┐&lt;br /&gt;
 │ Data:Weapons.json   Data:Characters.json   Data:Blessings.json │&lt;br /&gt;
 │ Data:Upgrades.json  Data:Crux.json         Data:Passives.json  │&lt;br /&gt;
 │ Data:Enemies.json   Data:Achievements.json Data:Codex.json     │&lt;br /&gt;
 │ Data:Tags.json                                                 │&lt;br /&gt;
 └───────────────┬────────────────────────────────────────────────┘&lt;br /&gt;
                 │  read at render time by&lt;br /&gt;
                 ▼&lt;br /&gt;
 ┌─ Module:Core ──────────────────────────────────────────────────┐&lt;br /&gt;
 │  loads + sorts records, computes infobox values, tag links,    │&lt;br /&gt;
 │  stat blocks, lore joins; shared by every module below         │&lt;br /&gt;
 └───┬───────────┬──────────┬───────────┬───────────┬─────────────┘&lt;br /&gt;
     ▼           ▼          ▼           ▼           ▼&lt;br /&gt;
 Module:      Module:    Module:     Module:     Module:&lt;br /&gt;
 &amp;lt;Category&amp;gt;   Compare    Navbox      TagIndex    StatIndex&lt;br /&gt;
 (infobox,    (DPS,      (bottom     (tag        (per-stat +&lt;br /&gt;
  body,        wave-10    navboxes)   member      damage-type&lt;br /&gt;
  index)       tables)                pages)      lookups)&lt;br /&gt;
     │           │          │           │           │&lt;br /&gt;
     ▼           ▼          ▼           ▼           ▼&lt;br /&gt;
            RENDERED PAGES (recomputed on every purge)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What to edit, where ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | You want to change… !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Edit this !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | What happens&lt;br /&gt;
|-&lt;br /&gt;
| A &#039;&#039;&#039;value&#039;&#039;&#039;: a stat number, price, tier, lore text, tag key, icon filename || The record in the matching &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; page || Every infobox, index row, comparison table, navbox and lookup that shows the value updates after a cache purge. Don&#039;t rename &#039;&#039;&#039;field keys&#039;&#039;&#039; — modules look them up by name.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Prose&#039;&#039;&#039; on one entity&#039;s page: intro text, strategy notes, screenshots, trivia || The detail page itself (e.g. &amp;lt;code&amp;gt;Cutter&amp;lt;/code&amp;gt;) || Detail pages are first-write-only: the bot never overwrites them, so your edits persist across re-publishes.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;How&#039;&#039;&#039; something renders: column order, section layout, sort order, labels || The Lua module — [[Module:Core]] for most things (infobox field lists, index columns, stat display order, sorting); [[Module:Compare]] / [[Module:Navbox]] / etc. for their own views || All affected pages re-render on purge. Note: module edits are overwritten by the next scheduled bot re-publish, so durable changes should also be reported upstream.&lt;br /&gt;
|-&lt;br /&gt;
| The &#039;&#039;&#039;infobox panel&#039;&#039;&#039; look (rows, header, fallbacks) || &amp;lt;code&amp;gt;Template:&amp;amp;lt;Category&amp;amp;gt;_infobox&amp;lt;/code&amp;gt; || Presentation only — values are computed by Module:Core and passed in as parameters.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Site-wide look&#039;&#039;&#039;: colors, theme tokens, filter chips, hover tooltips || [[MediaWiki:Common.css]] / [[MediaWiki:Common.js]] || Theme tokens (&amp;lt;code&amp;gt;--infobox-*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--table-*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--dmg-*&amp;lt;/code&amp;gt;) restyle everything at once; inline fallbacks keep pages readable without them.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Seeing your edit ==&lt;br /&gt;
MediaWiki caches rendered pages. After editing a Data page or module, other pages that use it may show the old version until their cache expires. To force it: open the page and add &amp;lt;code&amp;gt;?action=purge&amp;lt;/code&amp;gt; to the URL (or use the &amp;quot;Purge&amp;quot; option). The job queue eventually refreshes everything on its own.&lt;br /&gt;
&lt;br /&gt;
== What the bot does on re-publish ==&lt;br /&gt;
* &#039;&#039;&#039;Overwrites&#039;&#039;&#039; the framework layer: &amp;lt;code&amp;gt;Module:*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Template:*&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Data:*&amp;lt;/code&amp;gt; pages, category index pages, comparison/stat pages, and this help page. Value edits you made on Data pages are replaced by the game&#039;s current data — report data corrections to the developers so they land in the source.&lt;br /&gt;
* &#039;&#039;&#039;Never touches&#039;&#039;&#039; existing detail pages (one per weapon / hero / blessing / …). Your prose, images and trivia are safe there.&lt;br /&gt;
&lt;br /&gt;
|-|Module catalogue=&lt;br /&gt;
One expandable entry per module. Every module is readable on its &amp;lt;code&amp;gt;Module:&amp;lt;/code&amp;gt; page and carries the same documentation in its header comment.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Core]] — the engine room: computes every infobox value, index table, tag link, sort order and lore join&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| The shared library every other module &amp;lt;code&amp;gt;require()&amp;lt;/code&amp;gt;s. Loads and memoizes the ten Data pages, resolves category names, computes infobox value sets, stat blocks (with glyphs and elemental color chips), tag links and membership maps, codex lore joins, per-category sort orders, and the per-category presentation config (infobox field lists in &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER&amp;lt;/code&amp;gt;, index columns in &amp;lt;code&amp;gt;INDEX_OPTS&amp;lt;/code&amp;gt;, stat display order in &amp;lt;code&amp;gt;STAT_ORDER&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Not invoked directly — the per-category modules forward to its entry points (&amp;lt;code&amp;gt;infoboxEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bodyEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;indexEntry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renderEntry&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| All ten &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; pages.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Module:Weapons … Module:Tags (per-category modules) — infobox / body / index / crossRefs for one category&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What they do&lt;br /&gt;
| One tiny module per category ([[Module:Weapons]], [[Module:Characters]], [[Module:Blessings]], [[Module:Upgrades]], [[Module:Crux]], [[Module:Passives]], [[Module:Enemies]], [[Module:Achievements]], [[Module:Codex]], [[Module:Tags]]). Each only binds its category name and forwards to Module:Core — so all rendering logic lives in ONE place. [[Module:Codex]] additionally provides the shared &amp;lt;code&amp;gt;lore&amp;lt;/code&amp;gt; lookup (see its entry below).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|infobox|id=Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — the infobox panel&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|body|id=Cutter|section=Tags}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — one body section (omit &amp;lt;code&amp;gt;section&amp;lt;/code&amp;gt; for all)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|index}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — the sortable category table&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — variant / tag-member links&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|render|id=Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — infobox + body + crossRefs in one call&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Read&lt;br /&gt;
| The category&#039;s own &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; (via Module:Core).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Index]] — generic sortable-table renderer behind every category index&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Pure presentation: given records + a column spec, emits one sortable wikitable. Holds no data — Module:Core&#039;s &amp;lt;code&amp;gt;indexEntry&amp;lt;/code&amp;gt; feeds it sorted records augmented with computed infobox values. Column lists per category live in Module:Core&#039;s &amp;lt;code&amp;gt;INDEX_OPTS&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Not invoked directly; pages use &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|index}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Whatever records Module:Core passes it.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Compare]] — side-by-side comparison tables with computed DPS and wave-10 columns&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Builds the comparison tables on [[Weapons]], [[Weapon comparison]], [[Enemy comparison]] and [[Hero comparison]]. Computes columns that are stored nowhere: weapon &#039;&#039;&#039;DPS&#039;&#039;&#039; = (Damage + Holy + Fire + Electric) × Attack Speed; enemy &#039;&#039;&#039;wave-10 projections&#039;&#039;&#039; = base + per-wave × 9. Rows carry &amp;lt;code&amp;gt;data-element/class/tier/type&amp;lt;/code&amp;gt; attributes that power the JS filter chips.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Weapons_Melee}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Weapons_Ranged}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Enemies}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Compare|render|Characters}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Weapons.json]], [[Data:Enemies.json]], [[Data:Characters.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Navbox]] — the collapsible navigation box at the bottom of detail pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders a category&#039;s full member list as icon + link chains, bucketed into rows (Weapons/Blessings/Upgrades/Passives by tier, Crux/Codex by type, Characters/Enemies flat). The per-category grouping config is the &amp;lt;code&amp;gt;CONFIG&amp;lt;/code&amp;gt; table at the top of the module.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Pages use the wrapper templates: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Crux}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, … (each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Navbox|render|&amp;lt;Category&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| The named category&#039;s &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Iconbox]] — an entity&#039;s small icon + linked name, anywhere&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Looks up an entity by category + name (or bare name) and renders its icon plus a link to its page. Icons come from each entity&#039;s own record — there is no icon lookup table. Unknown entities or missing art degrade to a plain link.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Via [[Template:Iconbox]]: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter|icononly}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Blessing|Sanctified Halo|size=32}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · bare-name fallback &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Any of the ten &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt; pages, depending on the call.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:CrossRef]] — &amp;quot;related entities&amp;quot; sections (base ↔ Plus variants, tag members)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Computes the &#039;Base variant&#039; / &#039;Upgraded variant&#039; links between a blessing or upgrade and its &#039;&amp;lt;X&amp;gt; Plus&#039; version (matched on the raw &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;, because Upgrades&#039; display names can differ from ids), and delegates Tag pages&#039; member sections to Module:TagIndex.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| Wired by the category modules: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Blessings.json]], [[Data:Upgrades.json]]; tag-bearing categories via Module:TagIndex.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:TagIndex]] — &amp;quot;everything that carries this tag&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders the member sections on Tag pages (&#039;Weapons with this tag&#039;, …). Membership is never stored — it scans every tag-bearing category&#039;s &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays at render time, so adding a tag key to any record updates the tag page on purge.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:TagIndex|members|Melee}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (bare key or &amp;lt;code&amp;gt;Tag-Melee&amp;lt;/code&amp;gt; slug)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Tags.json]] + [[Data:Weapons.json]], [[Data:Characters.json]], [[Data:Blessings.json]], [[Data:Upgrades.json]], [[Data:Passives.json]], [[Data:Enemies.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:StatIndex]] — &amp;quot;what modifies this stat?&amp;quot; reverse lookups&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Renders the lookup tables on per-stat pages ([[Armor]], [[Speed]], …) and the damage-type hubs: every hero / blessing / upgrade / passive / Crux power modifying a stat (sorted by amount), and every weapon dealing an elemental damage type.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:StatIndex|boosters|Armor}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:StatIndex|weapons|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| boosters: [[Data:Characters.json]], [[Data:Blessings.json]], [[Data:Upgrades.json]], [[Data:Passives.json]], [[Data:Crux.json]] — weapons: [[Data:Weapons.json]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:DamageType]] — readable aliases for the damage-type hub pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Thin wrappers over Module:StatIndex so the [[Holy Damage]] / [[Fire Damage]] / [[Electric Damage]] hubs read naturally. No logic of its own.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:DamageType|dealtBy|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:DamageType|boostedBy|Fire Damage}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| Same as Module:StatIndex.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Module:Codex]] — Codex category module + the shared lore lookup&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | What it does&lt;br /&gt;
| Standard category rendering for Codex pages, plus the &amp;lt;code&amp;gt;lore&amp;lt;/code&amp;gt; entry point that powers &#039;== Lore ==&#039; sections on weapon / enemy / character pages: it finds the matching Codex entry by normalized name at render time, falling back to the entity&#039;s own &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;. Edit the lore once in [[Data:Codex.json]] and every page showing it updates.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Invoke&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|lore|Cutter|Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (category-scoped) · &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|lore|Azrael}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; (cross-category)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Reads&lt;br /&gt;
| [[Data:Codex.json]] + the named entity&#039;s category Data page.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|-|Template catalogue=&lt;br /&gt;
One expandable entry per user-facing template (or template family). Infobox values and navbox rows are computed by the modules — these templates are the presentation wrappers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Template:Iconbox]] — inline entity icon + linked name&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → icon + linked name. Add &amp;lt;code&amp;gt;icononly&amp;lt;/code&amp;gt; for just the glyph: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Weapon|Cutter|icononly}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;. Optional &amp;lt;code&amp;gt;size=32&amp;lt;/code&amp;gt;. Category accepts singular or plural (Weapon, Character, Blessing, Upgrade, Crux, Passive, Enemy, Achievement, Codex, Tag); bare-name form &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Iconbox|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; scans all categories. The go-to template for mentioning any entity in prose.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Backed by&lt;br /&gt;
| [[Module:Iconbox]] → the entity&#039;s own record in &amp;lt;code&amp;gt;Data:&amp;amp;lt;Category&amp;amp;gt;.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Template:Tip]] — highlighted gameplay-advice callout&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Tip|Pierce-heavy builds love this weapon — pair it with Penetrating Light.}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → a green-edged callout box. Use on detail pages for strategy notes that should stand out from lore.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:&amp;amp;lt;Category&amp;amp;gt;_infobox (10 templates) — the infobox panel on every detail page&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| Never called by hand. Detail pages call &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;; Module:Core computes the values from the Data page and passes them in. The templates ([[Template:Weapons_infobox]], [[Template:Characters_infobox]], [[Template:Blessings_infobox]], [[Template:Upgrades_infobox]], [[Template:Crux_infobox]], [[Template:Passives_infobox]], [[Template:Enemies_infobox]], [[Template:Achievements_infobox]], [[Template:Codex_infobox]], [[Template:Tags_infobox]]) own only the presentation: row layout, tier/type-colored headers, em-dash fallbacks. Each template&#039;s own page documents its parameters and the safe row shape.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:Navbox_&amp;amp;lt;Category&amp;amp;gt; (8 templates) — bottom-of-page navigation&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Navbox_Weapons}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; at the bottom of a detail page. Available: [[Template:Navbox_Weapons]], [[Template:Navbox_Characters]], [[Template:Navbox_Blessings]], [[Template:Navbox_Upgrades]], [[Template:Navbox_Crux]], [[Template:Navbox_Passives]], [[Template:Navbox_Enemies]], [[Template:Navbox_Codex]]. Each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Navbox|render|&amp;lt;Category&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — rows are computed from the category&#039;s Data page, so new entities appear automatically.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Template:&amp;amp;lt;Category&amp;amp;gt;_Tags (5 templates) — the &#039;== Tags ==&#039; section on detail pages&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:140px;&amp;quot; | Usage&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Weapons_Tags|Cutter}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → the tag list with effect text for one record. Available for Weapons, Characters, Blessings, Upgrades, Passives. Each wraps &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:&amp;lt;Category&amp;gt;|body|id={{{1}}}|section=Tags}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; so the section can be restyled in one place.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | Utility templates (general-purpose page furniture)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:170px;&amp;quot; | [[Template:Stub]]&lt;br /&gt;
| Banner for short pages needing expansion (auto-categorizes).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:NeedsImage]]&lt;br /&gt;
| Banner for pages missing art.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Disambig]]&lt;br /&gt;
| Marks disambiguation pages.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Quote]]&lt;br /&gt;
| Block quotation with attribution.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:GameVersion]]&lt;br /&gt;
| Inline version stamp: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{GameVersion|1.2.3}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; → &amp;quot;As of v1.2.3&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Spoiler]]&lt;br /&gt;
| Collapsible spoiler block.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Color]]&lt;br /&gt;
| Inline colored text: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Color|#ff6b35|burning}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:External]]&lt;br /&gt;
| External link with icon.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Hover]]&lt;br /&gt;
| Plain-text tooltip on hover.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Breadcrumb]]&lt;br /&gt;
| Breadcrumb navigation line.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Transclude]]&lt;br /&gt;
| Transclude a page or labeled section: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Transclude|PageName#Section}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Anchor]]&lt;br /&gt;
| Invisible anchor target for in-page links.&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Clear]]&lt;br /&gt;
| Clears floats (e.g. below an infobox).&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | [[Template:Documentation]]&lt;br /&gt;
| The &amp;lt;code&amp;gt;{{documentation}}&amp;lt;/code&amp;gt; convention for template doc subpages.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|-|Data schemas=&lt;br /&gt;
The ten source Data pages and their REAL record fields. Every Data page also carries this documentation in its own top-level &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; field. Shared by all categories: &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; (the game&#039;s internal id), &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; (the wiki page title of the entity&#039;s detail page — never recompute it from the name), &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; (display name), &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name; empty = no staged art).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Weapons.json]] — one record per weapon (65)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt; || number || Shop tier 1–3.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt; || number || Shop price.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || boolean || &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; = must be unlocked.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tag_source&amp;lt;/code&amp;gt; || string || The game&#039;s raw tag family (Melee, Laser, Grenade, …). &amp;lt;code&amp;gt;Melee&amp;lt;/code&amp;gt; here (or a Melee tag) makes the weapon class Melee; everything else is Ranged.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag KEYS — join [[Data:Tags.json]] for effect text and tag pages.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Raw numbers keyed by stat name: &amp;lt;code&amp;gt;Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Holy/Fire/Electric Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Attack Speed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Critical Chance&amp;lt;/code&amp;gt; (percent values stay strings like &amp;lt;code&amp;gt;&amp;quot;15%&amp;quot;&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;Range&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Pierce&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Projectiles&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Bounce&amp;lt;/code&amp;gt;. Sentinels: ≥ 1000 renders as &#039;&#039;Unlimited&#039;&#039;, ≤ −1000 is hidden. &#039;&#039;&#039;DPS is not stored&#039;&#039;&#039; — [[Module:Compare]] computes it at render time.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core (infobox/body/index), Module:Compare, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Characters.json]] — one record per playable hero (10)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Unlock metadata; &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; drives the Availability row.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]] (only some heroes carry tags).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat &#039;&#039;&#039;modifiers&#039;&#039;&#039; applied to base stats (e.g. &amp;lt;code&amp;gt;Armor: 6&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Attack Speed&amp;quot;: &amp;quot;-12%&amp;quot;&amp;lt;/code&amp;gt;) — not absolute values.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Lore prose, shown under == Lore ==.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Compare (hero comparison), Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Blessings.json]] — one record per blessing (94)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price, unlock state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers; percent values stay strings (&amp;lt;code&amp;gt;&amp;quot;5%&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Flavor prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;&amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt; Plus&amp;lt;/code&amp;gt; records are upgraded variants: ids pair &amp;lt;code&amp;gt;&amp;amp;lt;x&amp;amp;gt;-plus&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;lt;x&amp;amp;gt;&amp;lt;/code&amp;gt;, linked by [[Module:CrossRef]] at render time. Consumed by Module:Core, Module:CrossRef, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Upgrades.json]] — one record per shop upgrade (88)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price, unlock state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Rarely present (3 of 88).&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;&#039;Caution:&#039;&#039;&#039; &#039;&#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; can differ completely (id &amp;lt;code&amp;gt;Shield Transform&amp;lt;/code&amp;gt; displays as &amp;lt;code&amp;gt;Lifesteal Chamber&amp;lt;/code&amp;gt;) — that&#039;s why &#039;&#039;&amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt; Plus&amp;lt;/code&amp;gt;&#039;&#039; variant pairing matches on the raw id ([[Module:CrossRef]]). Consumed by Module:Core, Module:CrossRef, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Crux.json]] — one record per Crux power (34)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt; || number || Tier.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Unique / Action / Buff / Debuff / Aura&amp;lt;/code&amp;gt; — drives index grouping, navbox rows and the infobox header color.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Acquired / Unlocked / Locked&amp;lt;/code&amp;gt; → rendered as the Availability row.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Effect prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Navbox, Module:StatIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Passives.json]] — one record per passive (55)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tier&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;price&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;locked&amp;lt;/code&amp;gt; || number/boolean || Shop tier 1–3, price; every passive starts locked.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Tag keys → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || Signed stat modifiers.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || Flavor prose.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Ranked families (Armor I/II/III) are grouped consecutively on the index by Module:Core&#039;s sort — the stored order doesn&#039;t matter. Consumed by Module:Core, Module:Navbox, Module:StatIndex, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Enemies.json]] — one record per enemy type (27)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stats{}&amp;lt;/code&amp;gt; || map || &amp;lt;code&amp;gt;Health&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;HP per Wave&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Damage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Damage per Wave&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Speed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Resources&amp;lt;/code&amp;gt;. Per-wave values scale linearly; wave-10 projections are computed by [[Module:Compare]], not stored.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; || string list || Classification keys (&amp;lt;code&amp;gt;Melee / Range / Big / Boss&amp;lt;/code&amp;gt;) → [[Data:Tags.json]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The enemy&#039;s own lore. When a Codex entry matches by name, [[Module:Codex]] shows the fuller codex text instead at render time.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core, Module:Compare, Module:Navbox, Module:TagIndex, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Achievements.json]] — one record per Steam achievement (100)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The unlock objective (e.g. &amp;quot;Kill 10,000 enemies&amp;quot;) — rendered as both the infobox Objective row and the == Objective == section.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Consumed by Module:Core (infobox/index) and Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Codex.json]] — one record per encyclopedia entry (90)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; || string || &amp;lt;code&amp;gt;Enemies / Weapons / Events / Tips&amp;lt;/code&amp;gt; — drives index grouping and navbox rows.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; || string || The full lore text.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;redirect_slug&amp;lt;/code&amp;gt; || string (optional) || Present when the entry matched a weapon/enemy by name: &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; then points at the ENTITY&#039;s page (which inlines this lore), and &amp;lt;code&amp;gt;redirect_slug&amp;lt;/code&amp;gt; is the old &amp;lt;code&amp;gt;&amp;amp;lt;X&amp;amp;gt;-Codex&amp;lt;/code&amp;gt; title kept as a redirect.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Tips entries named after a stat (e.g. &amp;quot;Holy Damage&amp;quot;) are that stat&#039;s hub page. [[Module:Codex]] joins entries to weapons/enemies by normalized name at render time. Consumed by Module:Core, Module:Codex, Module:Navbox, Module:Iconbox.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);text-align:left;&amp;quot; | [[Data:Tags.json]] — one record per tag / perk keyword (84)&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:130px;&amp;quot; | Field !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);width:110px;&amp;quot; | Type !! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; || string || The raw tag KEY exactly as used in other categories&#039; &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays (e.g. &amp;lt;code&amp;gt;BreakArmor&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; || string || Tag page title, always &amp;lt;code&amp;gt;Tag-&amp;amp;lt;Key&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt; || string || The effect text; empty when the game data ships none.&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; | &#039;&#039;Which entities carry a tag is NOT stored here — Module:Core scans the other categories&#039; &amp;lt;code&amp;gt;tags[]&amp;lt;/code&amp;gt; arrays at render time (rendered by [[Module:TagIndex]]). Consumed by Module:Core (tag links + effect text everywhere), Module:TagIndex, Module:CrossRef.&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|-|MediaWiki Guide=&lt;br /&gt;
{{:Help:MediaWiki Guide}}&lt;br /&gt;
&lt;br /&gt;
|-|Special Pages=&lt;br /&gt;
This tab highlights MediaWiki&#039;s special pages most useful for working on this wiki.&lt;br /&gt;
&lt;br /&gt;
* [[Special:SpecialPages]] — List of all special pages.&lt;br /&gt;
* [[Special:WantedPages]] — Pages linked-to but missing. After a scheduled re-publish, this should be near-zero (every bot-published link target exists). Non-zero usually means an editor-added link points at a non-existent page.&lt;br /&gt;
* [[Special:WantedTemplates]] — Templates called but missing. Should be exactly zero on this wiki.&lt;br /&gt;
* [[Special:WantedFiles]] — Image files referenced but never uploaded. Should be near-zero after a clean re-publish.&lt;br /&gt;
* [[Special:WhatLinksHere]] — Reverse-lookup: which pages link to a given page. Useful for &amp;quot;where is this template used?&amp;quot;&lt;br /&gt;
* [[Special:Categories]] — All categories on the wiki.&lt;br /&gt;
* [[Special:UncategorizedPages]] — Main-namespace pages without a &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;[[Category:...]]&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; tag. Should be near-zero — unexpected entries here are bot-publishing bugs worth reporting.&lt;br /&gt;
* [[Special:RecentChanges]] — Live feed of edits.&lt;br /&gt;
* [[MediaWiki:Sidebar]] — Edit the navigation sidebar.&lt;br /&gt;
* [[Special:Upload]] / [[Special:BatchUpload]] — Image upload (single + multi).&lt;br /&gt;
* [[Special:Version]] — Installed MediaWiki extensions, skins, libraries, and version info.&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Help]]&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Tags_infobox&amp;diff=2110</id>
		<title>Template:Tags infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Tags_infobox&amp;diff=2110"/>
		<updated>2026-06-10T16:04:55Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
! Effect&lt;br /&gt;
| {{#if:{{{Effect|}}}|{{{Effect}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Used by&lt;br /&gt;
| {{#if:{{{Used by|}}}|{{{Used by}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Tag page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Tags|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Tags.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;Effect&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Used by&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Tags&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Codex_infobox&amp;diff=2109</id>
		<title>Template:Codex infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Codex_infobox&amp;diff=2109"/>
		<updated>2026-06-10T16:04:55Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
| {{#if:{{{Type|}}}|{{{Type}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Codex page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Codex|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Codex.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Codex&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Achievements_infobox&amp;diff=2108</id>
		<title>Template:Achievements infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Achievements_infobox&amp;diff=2108"/>
		<updated>2026-06-10T16:04:54Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Achievement page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Achievements|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Achievements.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Achievements&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Enemies_infobox&amp;diff=2107</id>
		<title>Template:Enemies infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Enemies_infobox&amp;diff=2107"/>
		<updated>2026-06-10T16:04:54Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Classification&lt;br /&gt;
| {{#if:{{{Classification|}}}|{{{Classification}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Enemy page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Enemies|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Enemies.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Classification&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Enemies&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Passives_infobox&amp;diff=2106</id>
		<title>Template:Passives infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Passives_infobox&amp;diff=2106"/>
		<updated>2026-06-10T16:04:53Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;{{#switch:{{{Tier|}}}|1=background:var(--tier-1-bg, #bbbbbb);color:#000000;|2=background:var(--tier-2-bg, #69c343);color:#000000;|3=background:var(--tier-3-bg, #e94040);color:#ffffff;|#default=background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);}}font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;padding:3px 0;&amp;quot; | {{#if:{{{Tier|}}}|{{#switch:{{{Tier}}}|1=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-1-bg, #bbbbbb);color:#000000;&amp;quot;&amp;gt;Tier 1&amp;lt;/span&amp;gt;|2=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-2-bg, #69c343);color:#000000;&amp;quot;&amp;gt;Tier 2&amp;lt;/span&amp;gt;|3=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-3-bg, #e94040);color:#ffffff;&amp;quot;&amp;gt;Tier 3&amp;lt;/span&amp;gt;|#default={{{Tier}}}}}|}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Tier&lt;br /&gt;
| {{#if:{{{Tier|}}}|{{{Tier}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Price&lt;br /&gt;
| {{#if:{{{Price|}}}|{{{Price}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Passive page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Passives|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Passives.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Tier&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Passives&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Crux_infobox&amp;diff=2105</id>
		<title>Template:Crux infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Crux_infobox&amp;diff=2105"/>
		<updated>2026-06-10T16:04:53Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;{{#switch:{{{Type|}}}|Unique=background:#e94040;color:#ffffff;|Buff=background:#69c343;color:#000000;|Debuff=background:#bbbbbb;color:#000000;|Aura=background:#7b9fe0;color:#000000;|Action=background:#3a3c44;color:#ffffff;|#default=background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);}}font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;padding:3px 0;&amp;quot; | {{#if:{{{Type|}}}|{{#switch:{{{Type}}}|Unique=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:#e94040;color:#ffffff;&amp;quot;&amp;gt;Unique&amp;lt;/span&amp;gt;|Buff=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:#69c343;color:#000000;&amp;quot;&amp;gt;Buff&amp;lt;/span&amp;gt;|Debuff=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:#bbbbbb;color:#000000;&amp;quot;&amp;gt;Debuff&amp;lt;/span&amp;gt;|Aura=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:#7b9fe0;color:#000000;&amp;quot;&amp;gt;Aura&amp;lt;/span&amp;gt;|Action=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:#3a3c44;color:#ffffff;&amp;quot;&amp;gt;Action&amp;lt;/span&amp;gt;|#default={{{Type}}}}}|}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
| {{#if:{{{Type|}}}|{{{Type}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Availability&lt;br /&gt;
| {{#if:{{{Availability|}}}|{{{Availability}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Crux page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Crux|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Crux.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Availability&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Crux&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Upgrades_infobox&amp;diff=2104</id>
		<title>Template:Upgrades infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Upgrades_infobox&amp;diff=2104"/>
		<updated>2026-06-10T16:04:52Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;{{#switch:{{{Tier|}}}|1=background:var(--tier-1-bg, #bbbbbb);color:#000000;|2=background:var(--tier-2-bg, #69c343);color:#000000;|3=background:var(--tier-3-bg, #e94040);color:#ffffff;|#default=background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);}}font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;padding:3px 0;&amp;quot; | {{#if:{{{Tier|}}}|{{#switch:{{{Tier}}}|1=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-1-bg, #bbbbbb);color:#000000;&amp;quot;&amp;gt;Tier 1&amp;lt;/span&amp;gt;|2=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-2-bg, #69c343);color:#000000;&amp;quot;&amp;gt;Tier 2&amp;lt;/span&amp;gt;|3=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-3-bg, #e94040);color:#ffffff;&amp;quot;&amp;gt;Tier 3&amp;lt;/span&amp;gt;|#default={{{Tier}}}}}|}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Tier&lt;br /&gt;
| {{#if:{{{Tier|}}}|{{{Tier}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Price&lt;br /&gt;
| {{#if:{{{Price|}}}|{{{Price}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Availability&lt;br /&gt;
| {{#if:{{{Availability|}}}|{{{Availability}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Upgrade page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Upgrades|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Upgrades.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Tier&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Availability&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Upgrades&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Blessings_infobox&amp;diff=2103</id>
		<title>Template:Blessings infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Blessings_infobox&amp;diff=2103"/>
		<updated>2026-06-10T16:04:52Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;{{#switch:{{{Tier|}}}|1=background:var(--tier-1-bg, #bbbbbb);color:#000000;|2=background:var(--tier-2-bg, #69c343);color:#000000;|3=background:var(--tier-3-bg, #e94040);color:#ffffff;|#default=background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);}}font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;padding:3px 0;&amp;quot; | {{#if:{{{Tier|}}}|{{#switch:{{{Tier}}}|1=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-1-bg, #bbbbbb);color:#000000;&amp;quot;&amp;gt;Tier 1&amp;lt;/span&amp;gt;|2=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-2-bg, #69c343);color:#000000;&amp;quot;&amp;gt;Tier 2&amp;lt;/span&amp;gt;|3=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-3-bg, #e94040);color:#ffffff;&amp;quot;&amp;gt;Tier 3&amp;lt;/span&amp;gt;|#default={{{Tier}}}}}|}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Tier&lt;br /&gt;
| {{#if:{{{Tier|}}}|{{{Tier}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Price&lt;br /&gt;
| {{#if:{{{Price|}}}|{{{Price}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Availability&lt;br /&gt;
| {{#if:{{{Availability|}}}|{{{Availability}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Blessing page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Blessings|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Blessings.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Tier&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Availability&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Blessings&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Characters_infobox&amp;diff=2102</id>
		<title>Template:Characters infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Characters_infobox&amp;diff=2102"/>
		<updated>2026-06-10T16:04:52Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Availability&lt;br /&gt;
| {{#if:{{{Availability|}}}|{{{Availability}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Character page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Characters|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Characters.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Availability&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Characters&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Weapons_infobox&amp;diff=2101</id>
		<title>Template:Weapons infobox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Template:Weapons_infobox&amp;diff=2101"/>
		<updated>2026-06-10T16:04:51Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;div class=&amp;quot;infobox&amp;quot; style=&amp;quot;border:2px solid var(--infobox-border, #3a3c44);background:var(--infobox-bg, #1c1d22);color:var(--infobox-text, #e6e6e6);padding:0.5em;float:right;width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
{| style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;{{#switch:{{{Tier|}}}|1=background:var(--tier-1-bg, #bbbbbb);color:#000000;|2=background:var(--tier-2-bg, #69c343);color:#000000;|3=background:var(--tier-3-bg, #e94040);color:#ffffff;|#default=background:var(--infobox-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);}}font-size:1.2em;text-align:center;&amp;quot; | {{{name|}}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | {{#if:{{{icon|}}}|[[File:{{{icon}}}{{!}}96px{{!}}alt={{{name|}}}]]|—}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;padding:3px 0;&amp;quot; | {{#if:{{{Tier|}}}|{{#switch:{{{Tier}}}|1=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-1-bg, #bbbbbb);color:#000000;&amp;quot;&amp;gt;Tier 1&amp;lt;/span&amp;gt;|2=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-2-bg, #69c343);color:#000000;&amp;quot;&amp;gt;Tier 2&amp;lt;/span&amp;gt;|3=&amp;lt;span style=&amp;quot;display:inline-block;padding:1px 8px;border-radius:3px;font-size:0.85em;font-weight:bold;background:var(--tier-3-bg, #e94040);color:#ffffff;&amp;quot;&amp;gt;Tier 3&amp;lt;/span&amp;gt;|#default={{{Tier}}}}}|}}&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center;&amp;quot; | &amp;lt;small&amp;gt;&amp;lt;code&amp;gt;{{{id|}}}&amp;lt;/code&amp;gt;&amp;lt;/small&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Tier&lt;br /&gt;
| {{#if:{{{Tier|}}}|{{{Tier}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Price&lt;br /&gt;
| {{#if:{{{Price|}}}|{{{Price}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Availability&lt;br /&gt;
| {{#if:{{{Availability|}}}|{{{Availability}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Stats&lt;br /&gt;
| {{#if:{{{Stats|}}}|{{{Stats}}}|—}}&lt;br /&gt;
|-&lt;br /&gt;
! Class&lt;br /&gt;
| {{#if:{{{Class|}}}|{{{Class}}}|—}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
{{#if:{{{desc|}}}|&#039;&#039;{{{desc}}}&#039;&#039;|}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== What this template does ==&lt;br /&gt;
Renders the infobox panel at the top right of every Weapon page. You normally never call it by hand: each detail page calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#invoke:Weapons|infobox|id=&amp;lt;slug&amp;gt;}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, and [[Module:Core]] computes the values from [[Data:Weapons.json]] at render time, then passes them to this template as parameters. This template is presentation only — row layout, header color, em-dash fallbacks. To change a VALUE, edit the source Data page; to change WHICH fields are computed, edit Module:Core; to change how the box looks, edit here.&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; — display name, shown in the colored header.&lt;br /&gt;
* &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; — the game&#039;s internal id, shown small under the icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;icon&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;File:&amp;lt;/code&amp;gt; image name for the entity&#039;s icon.&lt;br /&gt;
* &amp;lt;code&amp;gt;Tier&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Availability&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Stats&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Class&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you add or remove a row here, keep Module:Core&#039;s &amp;lt;code&amp;gt;INFOBOX_FIELD_ORDER.Weapons&amp;lt;/code&amp;gt; list in sync — that list decides which computed values get passed in.&lt;br /&gt;
&lt;br /&gt;
== Row shape ==&lt;br /&gt;
Every body row follows this exact shape (keep it when adding rows):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Label&lt;br /&gt;
| {{#if:{{{Label|}}}|{{{Label}}}|—}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The row markers (&amp;lt;code&amp;gt;|-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;!&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) stay OUTSIDE the &amp;lt;code&amp;gt;#if&amp;lt;/code&amp;gt;; only the value is conditional. Missing data then shows an em-dash instead of breaking the table layout.&lt;br /&gt;
&lt;br /&gt;
== Styling ==&lt;br /&gt;
Colors come from the site theme tokens (&amp;lt;code&amp;gt;var(--infobox-*, fallback)&amp;lt;/code&amp;gt; — defined in [[MediaWiki:Common.css]], with inline fallbacks so the box renders correctly even without site CSS).&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Tags&amp;diff=2100</id>
		<title>Module:Tags</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Tags&amp;diff=2100"/>
		<updated>2026-06-10T16:04:50Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Tags — the Tags category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Tags infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Tags.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Tags pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Tags.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Tags&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Tags|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Tags|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Tags|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Tags|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Tags|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Codex&amp;diff=2099</id>
		<title>Module:Codex</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Codex&amp;diff=2099"/>
		<updated>2026-06-10T16:04:50Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Codex — the Codex category module, plus the shared lore lookup.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Standard category rendering (infobox / body / index / render) for&lt;br /&gt;
--   Codex pages, bound to Module:Core like every other category module.&lt;br /&gt;
--   The extra `lore` entry point powers the &#039;== Lore ==&#039; section on&lt;br /&gt;
--   weapon / enemy / character / codex pages: it finds the matching&lt;br /&gt;
--   Codex entry by (normalized) name at render time, so editing the&lt;br /&gt;
--   lore once in Data:Codex.json updates every page showing it.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Codex|infobox|id=Azrael}} / |body| / |index| / |render|&lt;br /&gt;
--   {{#invoke:Codex|lore|Azrael}}            cross-category name match&lt;br /&gt;
--   {{#invoke:Codex|lore|Cutter|Weapons}}    category-scoped; falls back&lt;br /&gt;
--       to the entity&#039;s own `description` when no codex entry matches.&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Codex.json; `lore` also reads the named entity&#039;s category page&lt;br /&gt;
--   (e.g. Data:Weapons.json) to resolve names and fallbacks.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Codex&#039;&lt;br /&gt;
&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- Codex entry type expected for an entity category (nil = any type).&lt;br /&gt;
local TYPE_FOR_CAT = {Weapons = &#039;Weapons&#039;, Enemies = &#039;Enemies&#039;}&lt;br /&gt;
&lt;br /&gt;
function p.lore(frame)&lt;br /&gt;
  local key = (frame.args[1] or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  if key == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  local catHint = Core.resolveCategory(frame.args[2] or &#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
  local entity = nil&lt;br /&gt;
  if catHint then&lt;br /&gt;
    entity = Core.byId(catHint)[key]&lt;br /&gt;
  end&lt;br /&gt;
  if not entity then&lt;br /&gt;
    local foundCat, rec = Core.find(key)&lt;br /&gt;
    if rec then&lt;br /&gt;
      entity = rec&lt;br /&gt;
      catHint = catHint or foundCat&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  local name = (entity and entity.name) or key&lt;br /&gt;
&lt;br /&gt;
  if catHint == &#039;Codex&#039; then&lt;br /&gt;
    -- standalone codex page: its own prose&lt;br /&gt;
    return (entity and entity.description) or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  local cx = Core.codexFor(name, TYPE_FOR_CAT[catHint])&lt;br /&gt;
  if cx and cx.description and cx.description ~= &#039;&#039; then&lt;br /&gt;
    return cx.description&lt;br /&gt;
  end&lt;br /&gt;
  -- no codex entry: the entity&#039;s own description (heroes, unmatched enemies)&lt;br /&gt;
  return (entity and entity.description) or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Achievements&amp;diff=2098</id>
		<title>Module:Achievements</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Achievements&amp;diff=2098"/>
		<updated>2026-06-10T16:04:49Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Achievements — the Achievements category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Achievements infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Achievements.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Achievements pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Achievements.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Achievements&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Achievements|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Achievements|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Achievements|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Achievements|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Achievements|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Enemies&amp;diff=2097</id>
		<title>Module:Enemies</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Enemies&amp;diff=2097"/>
		<updated>2026-06-10T16:04:49Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Enemies — the Enemies category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Enemies infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Enemies.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Enemies pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Enemies.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Enemies&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Enemies|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Enemies|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Enemies|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Enemies|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Enemies|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Passives&amp;diff=2096</id>
		<title>Module:Passives</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Passives&amp;diff=2096"/>
		<updated>2026-06-10T16:04:48Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Passives — the Passives category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Passives infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Passives.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Passives pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Passives.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Passives&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Passives|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Passives|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Passives|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Passives|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Passives|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Crux&amp;diff=2095</id>
		<title>Module:Crux</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Crux&amp;diff=2095"/>
		<updated>2026-06-10T16:04:48Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Crux — the Crux category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Crux infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Crux.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Crux pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Crux.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Crux&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Crux|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Crux|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Crux|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Crux|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Crux|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Upgrades&amp;diff=2094</id>
		<title>Module:Upgrades</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Upgrades&amp;diff=2094"/>
		<updated>2026-06-10T16:04:47Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Upgrades — the Upgrades category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Upgrades infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Upgrades.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Upgrades pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Upgrades.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Upgrades&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Upgrades|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Upgrades|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Upgrades|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Upgrades|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Upgrades|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Blessings&amp;diff=2093</id>
		<title>Module:Blessings</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Blessings&amp;diff=2093"/>
		<updated>2026-06-10T16:04:46Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Blessings — the Blessings category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Blessings infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Blessings.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Blessings pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Blessings.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Blessings&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Blessings|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Blessings|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Blessings|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Blessings|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Blessings|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Characters&amp;diff=2092</id>
		<title>Module:Characters</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Characters&amp;diff=2092"/>
		<updated>2026-06-10T16:04:46Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Characters — the Characters category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Characters infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Characters.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Characters pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Characters.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Characters&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Characters|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Characters|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Characters|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Characters|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Characters|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Weapons&amp;diff=2091</id>
		<title>Module:Weapons</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Weapons&amp;diff=2091"/>
		<updated>2026-06-10T16:04:45Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Weapons — the Weapons category module.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders Weapons infoboxes, body sections, cross-references and the&lt;br /&gt;
--   category index. This module is intentionally tiny: it only binds&lt;br /&gt;
--   the category name and forwards to Module:Core, which computes&lt;br /&gt;
--   everything from the source Data:Weapons.json at render time (see&lt;br /&gt;
--   that Data page&#039;s own `description` for the record fields, and&lt;br /&gt;
--   Help:Wiki Editing for the architecture guide). To change HOW&lt;br /&gt;
--   Weapons pages render, edit Module:Core; to change the DATA, edit&lt;br /&gt;
--   Data:Weapons.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
local CAT = &#039;Weapons&#039;&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Weapons|infobox|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.infobox(frame) return Core.infoboxEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Weapons|body|id=&amp;lt;slug-or-id&amp;gt;[|section=&amp;lt;name&amp;gt;]}}&lt;br /&gt;
function p.body(frame) return Core.bodyEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Weapons|crossRefs|id=&amp;lt;slug-or-id&amp;gt;}}&lt;br /&gt;
function p.crossRefs(frame)&lt;br /&gt;
  return require(&#039;Module:CrossRef&#039;).entry(CAT, frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Weapons|index}} — the category index table&lt;br /&gt;
function p.index(frame) return Core.indexEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:Weapons|render|id=&amp;lt;slug-or-id&amp;gt;}} — combined fallback&lt;br /&gt;
function p.render(frame) return Core.renderEntry(CAT, frame) end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Index&amp;diff=2090</id>
		<title>Module:Index</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Index&amp;diff=2090"/>
		<updated>2026-06-10T16:04:45Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Index — generic sortable-table renderer for the category&lt;br /&gt;
-- index pages.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Pure presentation: given a list of records and a column spec, it&lt;br /&gt;
--   emits one sortable wikitable. It holds no data and no category&lt;br /&gt;
--   knowledge — Module:Core&#039;s indexEntry() calls Index.render() with&lt;br /&gt;
--   records loaded from Data:&amp;lt;Category&amp;gt;.json (already sorted, and&lt;br /&gt;
--   augmented with the computed `infobox` value dict).&lt;br /&gt;
--&lt;br /&gt;
-- PUBLIC API (Lua-level; pages invoke {{#invoke:&amp;lt;Cat&amp;gt;|index}} instead)&lt;br /&gt;
--   Index.render(records, opts)&lt;br /&gt;
--     records : list of records (each carries slug, name, icon, infobox{})&lt;br /&gt;
--     opts    : { columns = {{name, source}, ...}, thumbnail_size }&lt;br /&gt;
--   Column `source` semantics:&lt;br /&gt;
--     &amp;quot;thumbnail&amp;quot; → [[File:&amp;lt;rec.icon&amp;gt;|&amp;lt;thumbnail_size&amp;gt;px]] (em-dash on empty icon)&lt;br /&gt;
--     &amp;quot;name&amp;quot;      → rec.name (bare text, no link)&lt;br /&gt;
--     &amp;quot;link&amp;quot;      → [[&amp;lt;rec.slug&amp;gt;|&amp;lt;rec.name&amp;gt;]]&lt;br /&gt;
--     any other   → rec.infobox[source], fallback rec[source], else em-dash&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   Never rebuild a slug from a name here — slugs come from the source&lt;br /&gt;
--   records. Column `source` values name the computed infobox FIELD&lt;br /&gt;
--   LABELS (e.g. &#039;Attack Speed&#039;), not raw data keys. The per-category&lt;br /&gt;
--   column lists live in Module:Core&#039;s INDEX_OPTS table.&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function _isEmpty(v)&lt;br /&gt;
  return v == nil or v == &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function _flatten(v)&lt;br /&gt;
  if type(v) ~= &#039;table&#039; then return tostring(v) end&lt;br /&gt;
  local parts = {}&lt;br /&gt;
  for i = 1, #v do&lt;br /&gt;
    local item = v[i]&lt;br /&gt;
    if type(item) == &#039;table&#039; and item.name then&lt;br /&gt;
      table.insert(parts, tostring(item.name))&lt;br /&gt;
    else&lt;br /&gt;
      table.insert(parts, tostring(item))&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(parts, &#039;, &#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function _cell(rec, source, opts)&lt;br /&gt;
  if source == &#039;thumbnail&#039; then&lt;br /&gt;
    local icon = rec.icon&lt;br /&gt;
    if _isEmpty(icon) then return &#039;—&#039; end&lt;br /&gt;
    return string.format(&#039;[[File:%s|%dpx|alt=%s]]&#039;,&lt;br /&gt;
      tostring(icon),&lt;br /&gt;
      opts.thumbnail_size or 48,&lt;br /&gt;
      tostring(rec.name or rec.id or &#039;&#039;))&lt;br /&gt;
  end&lt;br /&gt;
  if source == &#039;name&#039; then&lt;br /&gt;
    if _isEmpty(rec.name) then return &#039;—&#039; end&lt;br /&gt;
    return tostring(rec.name)&lt;br /&gt;
  end&lt;br /&gt;
  if source == &#039;link&#039; then&lt;br /&gt;
    local slug = rec.slug or rec.name or rec.id&lt;br /&gt;
    if _isEmpty(slug) then return &#039;—&#039; end&lt;br /&gt;
    return &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. tostring(slug)&lt;br /&gt;
      .. &#039;&amp;quot;&amp;gt;[[&#039; .. tostring(slug) .. &#039;|&#039; .. tostring(rec.name or slug) .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  -- Plan-declared infobox label first; raw record field as fallback&lt;br /&gt;
  -- (covers top-level scalars like `family_key`).&lt;br /&gt;
  local v = (rec.infobox and rec.infobox[source])&lt;br /&gt;
  if _isEmpty(v) then v = rec[source] end&lt;br /&gt;
  if _isEmpty(v) then return &#039;—&#039; end&lt;br /&gt;
  return _flatten(v)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Stable sort by the column whose name matches sort_default. Sorting&lt;br /&gt;
-- happens on the textual cell value (case-insensitive). Records that&lt;br /&gt;
-- render &#039;—&#039; sort last regardless of column.&lt;br /&gt;
local function _sort(records, columns, sort_default, opts)&lt;br /&gt;
  if not sort_default or #records &amp;lt; 2 then return records end&lt;br /&gt;
  local sort_col&lt;br /&gt;
  for _, col in ipairs(columns or {}) do&lt;br /&gt;
    if col.name == sort_default then sort_col = col; break end&lt;br /&gt;
  end&lt;br /&gt;
  if not sort_col then return records end&lt;br /&gt;
  local source = sort_col.source&lt;br /&gt;
  -- Case-folded sort key for the chosen column. NOTE: this case-fold&lt;br /&gt;
  -- is for SORT ORDER only — it is NOT a slug operation. The library&lt;br /&gt;
  -- never derives a link target from a display name (slugs come from&lt;br /&gt;
  -- rec.slug). Using `string.lower(...)` (function-call form) rather&lt;br /&gt;
  -- than `:lower()` keeps the F1 lint clean: the lint catches the&lt;br /&gt;
  -- method form because that&#039;s the shape slug-computation code in&lt;br /&gt;
  -- prior runs took.&lt;br /&gt;
  local function key(rec)&lt;br /&gt;
    local c = _cell(rec, source, opts)&lt;br /&gt;
    if c == &#039;—&#039; then return string.char(255) end  -- last&lt;br /&gt;
    return string.lower(c)&lt;br /&gt;
  end&lt;br /&gt;
  local list = {}&lt;br /&gt;
  for i, r in ipairs(records) do list[i] = r end&lt;br /&gt;
  table.sort(list, function(a, b) return key(a) &amp;lt; key(b) end)&lt;br /&gt;
  return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(records, opts)&lt;br /&gt;
  records = records or {}&lt;br /&gt;
  opts    = opts or {}&lt;br /&gt;
  local columns = opts.columns or {}&lt;br /&gt;
  if #columns == 0 or #records == 0 then return &#039;&#039; end&lt;br /&gt;
&lt;br /&gt;
  local ordered = _sort(records, columns, opts.sort_default, opts)&lt;br /&gt;
&lt;br /&gt;
  local rows = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&#039; }&lt;br /&gt;
  -- Header row. MediaWiki inline header separator is `!!`, not single `!` —&lt;br /&gt;
  -- `! Icon ! Name` parses as ONE cell with literal `!` text. Use `!!`.&lt;br /&gt;
  local hdr = {}&lt;br /&gt;
  for _, col in ipairs(columns) do&lt;br /&gt;
    table.insert(hdr, &#039;style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | &#039; .. tostring(col.name))&lt;br /&gt;
  end&lt;br /&gt;
  table.insert(rows, &#039;! &#039; .. table.concat(hdr, &#039; !! &#039;))&lt;br /&gt;
  -- Body rows.&lt;br /&gt;
  for _, rec in ipairs(ordered) do&lt;br /&gt;
    local attrs = &#039;&#039;&lt;br /&gt;
    local tier = rec.infobox and rec.infobox.Tier&lt;br /&gt;
    if tier ~= nil and tier ~= &#039;&#039; then&lt;br /&gt;
      attrs = &#039; data-tier=&amp;quot;&#039; .. tostring(tier) .. &#039;&amp;quot;&#039;&lt;br /&gt;
    end&lt;br /&gt;
    table.insert(rows, &#039;|-&#039; .. attrs)&lt;br /&gt;
    local cells = {}&lt;br /&gt;
    for _, col in ipairs(columns) do&lt;br /&gt;
      table.insert(cells, _cell(rec, col.source, opts))&lt;br /&gt;
    end&lt;br /&gt;
    -- Column separators stay plain `||` on a single `|` line, outside&lt;br /&gt;
    -- any #if — conditional table markup breaks the wikitext parser.&lt;br /&gt;
    table.insert(rows, &#039;| &#039; .. table.concat(cells, &#039; || &#039;))&lt;br /&gt;
  end&lt;br /&gt;
  table.insert(rows, &#039;|}&#039;)&lt;br /&gt;
&lt;br /&gt;
  -- preprocess so any template call embedded in a cell expands (a&lt;br /&gt;
  -- plain return from #invoke is not template-expanded again).&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(rows, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:DamageType&amp;diff=2089</id>
		<title>Module:DamageType</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:DamageType&amp;diff=2089"/>
		<updated>2026-06-10T16:04:44Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:DamageType — readable aliases for the damage-type hub pages.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Thin wrappers over Module:StatIndex so the Holy / Fire / Electric&lt;br /&gt;
--   Damage hub pages read naturally. No logic of its own; see&lt;br /&gt;
--   Module:StatIndex for behavior and source data.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:DamageType|dealtBy|Fire Damage}}     == StatIndex|weapons&lt;br /&gt;
--   {{#invoke:DamageType|boostedBy|Fire Damage}}   == StatIndex|boosters&lt;br /&gt;
local StatIndex = require(&#039;Module:StatIndex&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p.dealtBy(frame)&lt;br /&gt;
  return StatIndex.weapons(frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.boostedBy(frame)&lt;br /&gt;
  return StatIndex.boosters(frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:StatIndex&amp;diff=2088</id>
		<title>Module:StatIndex</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:StatIndex&amp;diff=2088"/>
		<updated>2026-06-10T16:04:44Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:StatIndex — &amp;quot;what modifies this stat?&amp;quot; reverse lookups.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders the lookup tables on the per-stat pages (Armor, Speed, ...)&lt;br /&gt;
--   and the damage-type hubs (Holy/Fire/Electric Damage). Nothing is&lt;br /&gt;
--   stored: it scans the stat-bearing source Data pages at render time,&lt;br /&gt;
--   so a balance change edited into a Data page updates every stat page&lt;br /&gt;
--   on purge.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:StatIndex|boosters|Armor}}       every hero / blessing /&lt;br /&gt;
--       upgrade / passive / Crux power modifying the stat (desc by amount)&lt;br /&gt;
--   {{#invoke:StatIndex|weapons|Fire Damage}}  every weapon dealing the&lt;br /&gt;
--       elemental damage type (desc by amount)&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   boosters: Data:Characters.json, Data:Blessings.json,&lt;br /&gt;
--   Data:Upgrades.json, Data:Passives.json, Data:Crux.json.&lt;br /&gt;
--   weapons: Data:Weapons.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL = &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;font-size:0.95em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&#039;&lt;br /&gt;
local TH  = &#039;! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | &#039;&lt;br /&gt;
&lt;br /&gt;
-- Same mechanic, two data names: fold &#039;Explosive Range&#039; into the&lt;br /&gt;
-- &#039;Explosive Radius&#039; lookup key (and vice versa when querying either).&lt;br /&gt;
local ALIAS_GROUPS = {&lt;br /&gt;
  [&#039;Explosive Radius&#039;] = {&#039;Explosive Radius&#039;, &#039;Explosive Range&#039;},&lt;br /&gt;
  [&#039;Explosive Range&#039;] = {&#039;Explosive Radius&#039;, &#039;Explosive Range&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local MODIFIER_CATS = {&#039;Characters&#039;, &#039;Blessings&#039;, &#039;Upgrades&#039;, &#039;Passives&#039;, &#039;Crux&#039;}&lt;br /&gt;
&lt;br /&gt;
local function statKeysFor(query)&lt;br /&gt;
  return ALIAS_GROUPS[query] or {query}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Internal: list of booster entries for a stat key. {rec, cat, tier, value, sort}&lt;br /&gt;
function p._boosters(key)&lt;br /&gt;
  local keys = statKeysFor(key)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, cat in ipairs(MODIFIER_CATS) do&lt;br /&gt;
    for _, rec in ipairs(Core.load(cat)) do&lt;br /&gt;
      local st = rec.stats or {}&lt;br /&gt;
      for _, k in ipairs(keys) do&lt;br /&gt;
        local v = st[k]&lt;br /&gt;
        if v ~= nil then&lt;br /&gt;
          local val = Core.fmtStat(v, true)&lt;br /&gt;
          if val ~= nil then&lt;br /&gt;
            local tier = rec.tier or rec.type or &#039;&#039;&lt;br /&gt;
            out[#out + 1] = {&lt;br /&gt;
              rec = rec, cat = cat, tier = tostring(tier),&lt;br /&gt;
              value = val, sort = Core.numStat(v) or 0,&lt;br /&gt;
            }&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  table.sort(out, function(a, b)&lt;br /&gt;
    if a.sort ~= b.sort then return a.sort &amp;gt; b.sort end&lt;br /&gt;
    return mw.ustring.lower(a.rec.name or &#039;&#039;) &amp;lt; mw.ustring.lower(b.rec.name or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Internal: list of weapons dealing an elemental damage type.&lt;br /&gt;
function p._weapons(key)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    local v = Core.numStat((rec.stats or {})[key])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then&lt;br /&gt;
      out[#out + 1] = {&lt;br /&gt;
        rec = rec, class = Core.weaponClass(rec),&lt;br /&gt;
        tier = tostring(rec.tier or &#039;&#039;), value = Core.fmtNum(v), sort = v,&lt;br /&gt;
      }&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  table.sort(out, function(a, b)&lt;br /&gt;
    if a.sort ~= b.sort then return a.sort &amp;gt; b.sort end&lt;br /&gt;
    return mw.ustring.lower(a.rec.name or &#039;&#039;) &amp;lt; mw.ustring.lower(b.rec.name or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function arg1(frame)&lt;br /&gt;
  local k = frame.args[1] or &#039;&#039;&lt;br /&gt;
  return (k:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.boosters(frame)&lt;br /&gt;
  local key = arg1(frame)&lt;br /&gt;
  local list = p._boosters(key)&lt;br /&gt;
  if #list == 0 then&lt;br /&gt;
    return &amp;quot;&#039;&#039;No heroes, blessings, upgrades, passives or Crux powers &amp;quot; ..&lt;br /&gt;
      &amp;quot;modify this stat in the current game data.&#039;&#039;&amp;quot;&lt;br /&gt;
  end&lt;br /&gt;
  local out = { TBL, &#039;|-&#039;,&lt;br /&gt;
    TH .. &#039;Source&#039;, TH .. &#039;Category&#039;, TH .. &#039;Tier / Type&#039;, TH .. &#039;Amount&#039; }&lt;br /&gt;
  for _, e in ipairs(list) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039;&lt;br /&gt;
    out[#out+1] = &#039;| &#039; .. Core.iconLink(e.rec, 24)&lt;br /&gt;
    out[#out+1] = &#039;| &#039; .. e.cat&lt;br /&gt;
    out[#out+1] = &#039;| &#039; .. ((e.tier ~= &#039;&#039;) and e.tier or &#039;—&#039;)&lt;br /&gt;
    out[#out+1] = &#039;| data-sort-value=&amp;quot;&#039; .. tostring(e.sort) .. &#039;&amp;quot; | &#039; .. e.value&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.weapons(frame)&lt;br /&gt;
  local key = arg1(frame)&lt;br /&gt;
  local list = p._weapons(key)&lt;br /&gt;
  if #list == 0 then&lt;br /&gt;
    return &amp;quot;&#039;&#039;No weapons deal this damage type in the current game data.&#039;&#039;&amp;quot;&lt;br /&gt;
  end&lt;br /&gt;
  local out = { TBL, &#039;|-&#039;,&lt;br /&gt;
    TH .. &#039;Weapon&#039;, TH .. &#039;Class&#039;, TH .. &#039;Tier&#039;, TH .. key }&lt;br /&gt;
  for _, e in ipairs(list) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039;&lt;br /&gt;
    out[#out+1] = &#039;| &#039; .. Core.iconLink(e.rec, 24)&lt;br /&gt;
    out[#out+1] = &#039;| &#039; .. e.class&lt;br /&gt;
    out[#out+1] = &#039;| &#039; .. e.tier&lt;br /&gt;
    out[#out+1] = &#039;| data-sort-value=&amp;quot;&#039; .. tostring(e.sort) .. &#039;&amp;quot; | &#039; .. e.value&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:TagIndex&amp;diff=2087</id>
		<title>Module:TagIndex</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:TagIndex&amp;diff=2087"/>
		<updated>2026-06-10T16:04:43Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:TagIndex — &amp;quot;everything that carries this tag&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders the member sections of a Tag page (&#039;Weapons with this tag&#039;,&lt;br /&gt;
--   &#039;Characters with this tag&#039;, ...). Membership is never stored: it&lt;br /&gt;
--   scans every tag-bearing category&#039;s records[].tags at render time&lt;br /&gt;
--   (via Module:Core&#039;s memoized membership map), so adding a tag key to&lt;br /&gt;
--   any entity&#039;s Data record updates its tag page on purge.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:TagIndex|members|Melee}}      bare tag key&lt;br /&gt;
--   {{#invoke:TagIndex|members|Tag-Melee}}  page slug also accepted&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Tags.json plus the tag-bearing categories: Data:Weapons.json,&lt;br /&gt;
--   Data:Characters.json, Data:Blessings.json, Data:Upgrades.json,&lt;br /&gt;
--   Data:Passives.json, Data:Enemies.json.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Section order + labels (presentation config).&lt;br /&gt;
local SECTIONS = {&lt;br /&gt;
  {&#039;Weapons&#039;, &#039;Weapons with this tag&#039;},&lt;br /&gt;
  {&#039;Characters&#039;, &#039;Characters with this tag&#039;},&lt;br /&gt;
  {&#039;Blessings&#039;, &#039;Blessings with this tag&#039;},&lt;br /&gt;
  {&#039;Upgrades&#039;, &#039;Upgrades with this tag&#039;},&lt;br /&gt;
  {&#039;Passives&#039;, &#039;Passives with this tag&#039;},&lt;br /&gt;
  {&#039;Enemies&#039;, &#039;Enemies with this tag&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Computed &#039;== &amp;lt;Cat&amp;gt; with this tag ==&#039; bullet sections for one tag key.&lt;br /&gt;
function p.sectionsFor(key)&lt;br /&gt;
  if not key or key == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  local m = Core.tagMembership()[key]&lt;br /&gt;
  if not m then return &#039;&#039; end&lt;br /&gt;
  local CrossRef = require(&#039;Module:CrossRef&#039;)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, sec in ipairs(SECTIONS) do&lt;br /&gt;
    local cat, label = sec[1], sec[2]&lt;br /&gt;
    local recs = m[cat]&lt;br /&gt;
    if recs and #recs &amp;gt; 0 then&lt;br /&gt;
      local sorted = {}&lt;br /&gt;
      for i, r in ipairs(recs) do sorted[i] = r end&lt;br /&gt;
      table.sort(sorted, function(a, b)&lt;br /&gt;
        return mw.ustring.lower(a.name or &#039;&#039;) &amp;lt; mw.ustring.lower(b.name or &#039;&#039;)&lt;br /&gt;
      end)&lt;br /&gt;
      out[#out + 1] = CrossRef.renderSection(label, sorted)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.members(frame)&lt;br /&gt;
  local key = (frame.args[1] or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  -- Accept either the bare tag key (&#039;Melee&#039;) or the page slug (&#039;Tag-Melee&#039;).&lt;br /&gt;
  if not Core.tagMembership()[key] and key:sub(1, 4) == &#039;Tag-&#039; then&lt;br /&gt;
    local rec = Core.byId(&#039;Tags&#039;)[key]&lt;br /&gt;
    if rec then key = rec.id or rec.name end&lt;br /&gt;
  end&lt;br /&gt;
  local body = p.sectionsFor(key)&lt;br /&gt;
  if body == &#039;&#039; then&lt;br /&gt;
    return &amp;quot;&#039;&#039;Nothing in the current game data carries this tag.&#039;&#039;&amp;quot;&lt;br /&gt;
  end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(body)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:CrossRef&amp;diff=2086</id>
		<title>Module:CrossRef</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:CrossRef&amp;diff=2086"/>
		<updated>2026-06-10T16:04:43Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:CrossRef — &amp;quot;related entities&amp;quot; sections on detail pages.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Computes cross-reference sections at render time:&lt;br /&gt;
--   * &#039;Base variant&#039; / &#039;Upgraded variant&#039; links between a blessing or&lt;br /&gt;
--     upgrade and its &#039;&amp;lt;X&amp;gt; Plus&#039; version (matched within the category).&lt;br /&gt;
--   * The &amp;quot;&amp;lt;Category&amp;gt; with this tag&amp;quot; member sections on Tag pages&lt;br /&gt;
--     (delegated to Module:TagIndex).&lt;br /&gt;
--   Nothing is stored: every link recomputes from the source Data pages&lt;br /&gt;
--   on purge, so renaming an entity there updates all references.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE (wired by the per-category modules)&lt;br /&gt;
--   {{#invoke:Blessings|crossRefs|id=Blessed_Bounty}}&lt;br /&gt;
--   {{#invoke:Tags|crossRefs|id=Tag-Melee}}&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Blessings.json, Data:Upgrades.json (variant pairing);&lt;br /&gt;
--   the tag-bearing categories via Module:TagIndex for Tag pages.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Render a single labelled section: &#039;== &amp;lt;label&amp;gt; ==&#039; + icon-link bullets.&lt;br /&gt;
function p.renderSection(label, refs)&lt;br /&gt;
  if not refs or #refs == 0 then return &#039;&#039; end&lt;br /&gt;
  local out = {&#039;&#039;, &#039;== &#039; .. label .. &#039; ==&#039;}&lt;br /&gt;
  for _, rec in ipairs(refs) do&lt;br /&gt;
    out[#out + 1] = &#039;* &#039; .. Core.iconLink(rec, 24)&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Variant pairing: RAW id first (&#039;&amp;lt;X&amp;gt; Plus&#039; / &#039;&amp;lt;x&amp;gt;-plus&#039; -&amp;gt; base), then&lt;br /&gt;
-- display name (&#039;&amp;lt;Name&amp;gt; Plus&#039;). Id wins because Upgrades&#039; display-name&lt;br /&gt;
-- split means base and Plus can show unrelated display names (e.g. id&lt;br /&gt;
-- &#039;Shield Transform&#039; displays &#039;Lifesteal Chamber&#039;); the name fallback&lt;br /&gt;
-- covers Blessings, whose kebab-case ids (&#039;blessed-bounty-plus&#039;) pair by&lt;br /&gt;
-- either rule.&lt;br /&gt;
local VARIANT_CATS = {Blessings = true, Upgrades = true}&lt;br /&gt;
&lt;br /&gt;
local _byName = {}&lt;br /&gt;
local function byName(cat)&lt;br /&gt;
  if _byName[cat] == nil then&lt;br /&gt;
    local m = {}&lt;br /&gt;
    for _, r in ipairs(Core.load(cat)) do&lt;br /&gt;
      if r.name then m[r.name] = r end&lt;br /&gt;
    end&lt;br /&gt;
    _byName[cat] = m&lt;br /&gt;
  end&lt;br /&gt;
  return _byName[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function variantSections(cat, rec)&lt;br /&gt;
  if not VARIANT_CATS[cat] then return &#039;&#039; end&lt;br /&gt;
  local ids, names = Core.byId(cat), byName(cat)&lt;br /&gt;
  local id, name = rec.id or &#039;&#039;, rec.name or &#039;&#039;&lt;br /&gt;
  local out = {}&lt;br /&gt;
  local base = nil&lt;br /&gt;
  if id:sub(-5) == &#039; Plus&#039; then base = ids[id:sub(1, -6)] end&lt;br /&gt;
  if not base and id:sub(-5) == &#039;-plus&#039; then base = ids[id:sub(1, -6)] end&lt;br /&gt;
  if not base and name:sub(-5) == &#039; Plus&#039; then base = names[name:sub(1, -6)] end&lt;br /&gt;
  if base and base ~= rec then&lt;br /&gt;
    out[#out + 1] = p.renderSection(&#039;Base variant&#039;, {base})&lt;br /&gt;
  elseif not (id:sub(-5) == &#039; Plus&#039; or id:sub(-5) == &#039;-plus&#039;&lt;br /&gt;
              or name:sub(-5) == &#039; Plus&#039;) then&lt;br /&gt;
    local plus = ids[id .. &#039; Plus&#039;] or ids[id .. &#039;-plus&#039;] or names[name .. &#039; Plus&#039;]&lt;br /&gt;
    if plus and plus ~= rec then&lt;br /&gt;
      out[#out + 1] = p.renderSection(&#039;Upgraded variant&#039;, {plus})&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Full computed cross-ref block for one record (no frame needed).&lt;br /&gt;
function p.compute(cat, rec)&lt;br /&gt;
  if cat == &#039;Tags&#039; then&lt;br /&gt;
    local TagIndex = require(&#039;Module:TagIndex&#039;)&lt;br /&gt;
    return TagIndex.sectionsFor(rec.id or rec.name)&lt;br /&gt;
  end&lt;br /&gt;
  return variantSections(cat, rec)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Frame entry used by the per-category shims.&lt;br /&gt;
function p.entry(cat, frame)&lt;br /&gt;
  local rec, missing = Core.resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local body = p.compute(cat, rec)&lt;br /&gt;
  if body == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(body)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Iconbox&amp;diff=2085</id>
		<title>Module:Iconbox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Iconbox&amp;diff=2085"/>
		<updated>2026-06-10T16:04:42Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Iconbox — an entity&#039;s small icon + linked name, anywhere.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Looks up an entity by category + name (or just name) and renders&lt;br /&gt;
--   its icon and a link to its page. Icons come from each entity&#039;s own&lt;br /&gt;
--   record (rec.icon in Data:&amp;lt;Category&amp;gt;.json) — there is no separate&lt;br /&gt;
--   icon lookup table to maintain.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE (normally via Template:Iconbox)&lt;br /&gt;
--   Category-first form (preferred — collision-safe):&lt;br /&gt;
--     {{Iconbox|Weapon|Cutter}}            icon + linked name&lt;br /&gt;
--     {{Iconbox|Weapon|Cutter|icononly}}   linked icon glyph only&lt;br /&gt;
--     {{Iconbox|Blessing|Sanctified Halo|size=32}}&lt;br /&gt;
--   Category accepts singular or plural (Weapon/Weapons, Tag/Tags, ...).&lt;br /&gt;
--   Bare-name fallback (scans every category; first match in registry&lt;br /&gt;
--   order wins — beware same-named entities in two categories):&lt;br /&gt;
--     {{Iconbox|Cutter}}  /  {{Iconbox|Cutter|icononly}}&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Any/all of the ten Data:&amp;lt;Category&amp;gt;.json pages, depending on the call.&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   Unknown entity or missing icon degrades to a plain page link (never&lt;br /&gt;
--   a broken-file box); icon-only mode degrades to nothing.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function trim(s)&lt;br /&gt;
  return (tostring(s or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function findInCategory(cat, key)&lt;br /&gt;
  local k = mw.ustring.lower(key)&lt;br /&gt;
  local rec = Core.byId(cat)[key]&lt;br /&gt;
  if rec then return rec end&lt;br /&gt;
  for _, r in ipairs(Core.load(cat)) do&lt;br /&gt;
    if (r.name and mw.ustring.lower(r.name) == k)&lt;br /&gt;
      or (r.slug and mw.ustring.lower(r.slug) == k) then&lt;br /&gt;
      return r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local a1 = trim(frame.args[1])&lt;br /&gt;
  if a1 == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  local a2 = trim(frame.args[2])&lt;br /&gt;
  local a3 = trim(frame.args[3])&lt;br /&gt;
&lt;br /&gt;
  local cat = Core.resolveCategory(a1)&lt;br /&gt;
  local key, mode, rec&lt;br /&gt;
  if cat and a2 ~= &#039;&#039; then&lt;br /&gt;
    -- category-first form: {{Iconbox|Weapon|Cutter|icononly?}}&lt;br /&gt;
    key, mode = a2, a3&lt;br /&gt;
    rec = findInCategory(cat, key)&lt;br /&gt;
  else&lt;br /&gt;
    -- bare-name form: {{Iconbox|Cutter|icononly?}}&lt;br /&gt;
    key, mode = a1, a2&lt;br /&gt;
    local _, found = Core.find(key)&lt;br /&gt;
    rec = found&lt;br /&gt;
  end&lt;br /&gt;
  if mode == &#039;&#039; then mode = trim(frame.args.mode) end&lt;br /&gt;
  mode = mw.ustring.lower(mode)&lt;br /&gt;
  local icononly = (mode == &#039;icononly&#039; or mode == &#039;icon&#039; or mode == &#039;noname&#039;)&lt;br /&gt;
  local size = trim(frame.args.size)&lt;br /&gt;
  if size == &#039;&#039; then size = &#039;24&#039; end&lt;br /&gt;
&lt;br /&gt;
  if not rec then&lt;br /&gt;
    if icononly then return &#039;&#039; end&lt;br /&gt;
    return &#039;[[&#039; .. key .. &#039;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local slug = rec.slug or key&lt;br /&gt;
  local name = rec.name or key&lt;br /&gt;
  local icon = rec.icon or &#039;&#039;&lt;br /&gt;
  local tip = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
  if icon == &#039;&#039; then&lt;br /&gt;
    if icononly then return &#039;&#039; end&lt;br /&gt;
    return tip .. &#039;[[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local img = &#039;[[File:&#039; .. icon .. &#039;|&#039; .. size .. &#039;px|link=&#039; .. slug&lt;br /&gt;
    .. &#039;|alt=&#039; .. name .. &#039;]]&#039;&lt;br /&gt;
  if icononly then return img end&lt;br /&gt;
  return tip .. img .. &#039; [[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Navbox&amp;diff=2084</id>
		<title>Module:Navbox</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Navbox&amp;diff=2084"/>
		<updated>2026-06-10T16:04:41Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Navbox — the collapsible navigation box at the bottom of&lt;br /&gt;
-- detail pages.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Renders a category&#039;s full member list, bucketed into rows (by tier,&lt;br /&gt;
--   by type, or one flat row), with each member as icon + link. Rows&lt;br /&gt;
--   are computed at render time from the category&#039;s own Data page, so&lt;br /&gt;
--   a new record appears in every navbox on purge — no stored lists.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Navbox|render|Weapons}}&lt;br /&gt;
--   ...but pages should normally use the wrapper template instead:&lt;br /&gt;
--   {{Navbox_Weapons}}, {{Navbox_Crux}}, etc.&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:&amp;lt;Category&amp;gt;.json for whichever category is named in the call.&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   The CONFIG table below is the presentation contract per category:&lt;br /&gt;
--   group = record field to bucket by (nil = single row), order = bucket&lt;br /&gt;
--   order, label = row label format, icon = icon size in px. Categories&lt;br /&gt;
--   absent from CONFIG render nothing.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local CONFIG = {&lt;br /&gt;
  Weapons    = {group = &#039;tier&#039;, order = {&#039;1&#039;, &#039;2&#039;, &#039;3&#039;}, label = &#039;Tier %s (%d)&#039;,&lt;br /&gt;
                row_header = &#039;Tier&#039;, items_header = &#039;Weapons&#039;, icon = 24},&lt;br /&gt;
  Characters = {group = nil, label = &#039;Characters (%d)&#039;,&lt;br /&gt;
                row_header = &#039;&#039;, items_header = &#039;Characters&#039;, icon = 48},&lt;br /&gt;
  Blessings  = {group = &#039;tier&#039;, order = {&#039;1&#039;, &#039;2&#039;, &#039;3&#039;}, label = &#039;Tier %s (%d)&#039;,&lt;br /&gt;
                row_header = &#039;Tier&#039;, items_header = &#039;Blessings&#039;, icon = 24},&lt;br /&gt;
  Upgrades   = {group = &#039;tier&#039;, order = {&#039;1&#039;, &#039;2&#039;, &#039;3&#039;}, label = &#039;Tier %s (%d)&#039;,&lt;br /&gt;
                row_header = &#039;Tier&#039;, items_header = &#039;Upgrades&#039;, icon = 24},&lt;br /&gt;
  Passives   = {group = &#039;tier&#039;, order = {&#039;1&#039;, &#039;2&#039;, &#039;3&#039;}, label = &#039;Tier %s (%d)&#039;,&lt;br /&gt;
                row_header = &#039;Tier&#039;, items_header = &#039;Passives&#039;, icon = 24},&lt;br /&gt;
  Crux       = {group = &#039;type&#039;, order = {&#039;Unique&#039;, &#039;Action&#039;, &#039;Buff&#039;, &#039;Debuff&#039;, &#039;Aura&#039;},&lt;br /&gt;
                label = &#039;%s (%d)&#039;, row_header = &#039;Type&#039;,&lt;br /&gt;
                items_header = &#039;Crux powers&#039;, icon = 24},&lt;br /&gt;
  Enemies    = {group = nil, label = &#039;Enemies (%d)&#039;,&lt;br /&gt;
                row_header = &#039;&#039;, items_header = &#039;Enemies&#039;, icon = 32},&lt;br /&gt;
  Codex      = {group = &#039;type&#039;, order = {&#039;Enemies&#039;, &#039;Weapons&#039;, &#039;Events&#039;, &#039;Tips&#039;},&lt;br /&gt;
                label = &#039;%s (%d)&#039;, row_header = &#039;Type&#039;,&lt;br /&gt;
                items_header = &#039;Entries&#039;, icon = 24},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chain(items, size)&lt;br /&gt;
  if #items == 0 then return &#039;—&#039; end&lt;br /&gt;
  local parts = {}&lt;br /&gt;
  for _, rec in ipairs(items) do&lt;br /&gt;
    parts[#parts + 1] = Core.iconLink(rec, size)&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(parts, &#039;, &#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function buildRows(cat, cfg)&lt;br /&gt;
  local recs = Core.load(cat)&lt;br /&gt;
  -- bucket&lt;br /&gt;
  local buckets, seen = {}, {}&lt;br /&gt;
  for _, r in ipairs(recs) do&lt;br /&gt;
    local key&lt;br /&gt;
    if cfg.group == nil then&lt;br /&gt;
      key = &#039;all&#039;&lt;br /&gt;
    else&lt;br /&gt;
      key = tostring(r[cfg.group] or &#039;&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    if key ~= &#039;&#039; then&lt;br /&gt;
      if not buckets[key] then buckets[key] = {}; seen[#seen + 1] = key end&lt;br /&gt;
      local b = buckets[key]&lt;br /&gt;
      b[#b + 1] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  local keys = cfg.order or (cfg.group == nil and {&#039;all&#039;} or seen)&lt;br /&gt;
  if cfg.order then&lt;br /&gt;
    -- append unknown keys after the declared order&lt;br /&gt;
    local inOrder = {}&lt;br /&gt;
    for _, k in ipairs(cfg.order) do inOrder[k] = true end&lt;br /&gt;
    keys = {}&lt;br /&gt;
    for _, k in ipairs(cfg.order) do keys[#keys + 1] = k end&lt;br /&gt;
    for _, k in ipairs(seen) do&lt;br /&gt;
      if not inOrder[k] then keys[#keys + 1] = k end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, k in ipairs(keys) do&lt;br /&gt;
    local b = buckets[k]&lt;br /&gt;
    if b and #b &amp;gt; 0 then&lt;br /&gt;
      table.sort(b, function(x, y)&lt;br /&gt;
        return mw.ustring.lower(x.name or &#039;&#039;) &amp;lt; mw.ustring.lower(y.name or &#039;&#039;)&lt;br /&gt;
      end)&lt;br /&gt;
      local label&lt;br /&gt;
      if cfg.group == nil then&lt;br /&gt;
        label = string.format(cfg.label, #b)&lt;br /&gt;
      else&lt;br /&gt;
        label = string.format(cfg.label, k, #b)&lt;br /&gt;
      end&lt;br /&gt;
      rows[#rows + 1] = {label = label, items = b}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local cat = (frame.args[1] or frame.args.category or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  cat = Core.resolveCategory(cat) or cat&lt;br /&gt;
  local cfg = CONFIG[cat]&lt;br /&gt;
  if not cfg then return &#039;&#039; end&lt;br /&gt;
  local rows = buildRows(cat, cfg)&lt;br /&gt;
  if #rows == 0 then return &#039;&#039; end&lt;br /&gt;
&lt;br /&gt;
  local hsty = &#039;! style=&amp;quot;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&amp;quot; | &#039;&lt;br /&gt;
  local lines = {&lt;br /&gt;
    &#039;{| class=&amp;quot;wikitable mw-collapsible&amp;quot; style=&amp;quot;font-size:90%; width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&amp;quot;&#039;,&lt;br /&gt;
    &#039;|-&#039;,&lt;br /&gt;
    hsty .. cfg.row_header .. &#039; !&#039; .. hsty .. cfg.items_header,&lt;br /&gt;
  }&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    lines[#lines + 1] = &#039;|-&#039;&lt;br /&gt;
    lines[#lines + 1] = &amp;quot;| &#039;&#039;&#039;&amp;quot; .. row.label .. &amp;quot;&#039;&#039;&#039; || &amp;quot; .. chain(row.items, cfg.icon)&lt;br /&gt;
  end&lt;br /&gt;
  lines[#lines + 1] = &#039;|}&#039;&lt;br /&gt;
  local body = table.concat(lines, &#039;\n&#039;)&lt;br /&gt;
&lt;br /&gt;
  local title_link = &#039;[[&#039; .. cat .. &#039;|&#039; .. cat .. &#039;]]&#039;&lt;br /&gt;
  return string.format(&lt;br /&gt;
    &#039;\n----\n&amp;lt;div class=&amp;quot;navbox&amp;quot; style=&amp;quot;margin-top:1em; clear:both; border:1px solid var(--navbox-border, #3a3c44); background:var(--navbox-bg, #1c1d22); color:var(--navbox-text, #dcdcdc);&amp;quot;&amp;gt;\n&#039; ..&lt;br /&gt;
    &#039;&amp;lt;div class=&amp;quot;navbox-bar&amp;quot; style=&amp;quot;padding:6px 10px; background:var(--navbox-bar-bg, #26272e); color:var(--navbox-bar-fg, #f1e9d2); font-weight:bold; text-align:center; border:1px solid var(--navbox-border, #3a3c44);&amp;quot;&amp;gt;&#039; ..&lt;br /&gt;
    &#039;&amp;lt;span style=&amp;quot;float:left; font-size:85%%; color:#eee;&amp;quot;&amp;gt;[v · e]&amp;lt;/span&amp;gt;%s&amp;lt;/div&amp;gt;\n%s\n&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    title_link, body&lt;br /&gt;
  )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2083</id>
		<title>Module:Compare</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Compare&amp;diff=2083"/>
		<updated>2026-06-10T16:04:41Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Compare — sortable side-by-side comparison tables.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Builds the big comparison tables (used on the Weapons index,&lt;br /&gt;
--   Weapon comparison, Enemy comparison and Hero comparison pages),&lt;br /&gt;
--   including the COMPUTED columns that are not stored anywhere:&lt;br /&gt;
--   * Weapon DPS = (Damage + Holy + Fire + Electric) x Attack Speed,&lt;br /&gt;
--     rounded to 1 decimal.&lt;br /&gt;
--   * Enemy wave-10 projections = base + per-wave x 9.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Melee}}   melee weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Weapons_Ranged}}  ranged weapons, DPS desc&lt;br /&gt;
--   {{#invoke:Compare|render|Enemies}}         base + per-wave + wave-10&lt;br /&gt;
--   {{#invoke:Compare|render|Characters}}      hero stat modifiers&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (via Module:Core)&lt;br /&gt;
--   Data:Weapons.json, Data:Enemies.json, Data:Characters.json&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * Stat sentinels: a value &amp;gt;= 1000 renders as the infinity sign&lt;br /&gt;
--     (sorts to the top); &amp;lt;= -1000 renders as an em-dash (sorts bottom).&lt;br /&gt;
--   * Each row carries data-element / data-class / data-tier / data-type&lt;br /&gt;
--     attributes — MediaWiki:Common.js uses them for the filter chips&lt;br /&gt;
--     above the tables. Removing them breaks the chip filtering.&lt;br /&gt;
--   * To add a column, add a header in the relevant *Rows() builder and&lt;br /&gt;
--     a matching cell in the same position.&lt;br /&gt;
local Core = require(&#039;Module:Core&#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TBL_STYLE = &#039;font-size:0.92em;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);&#039;&lt;br /&gt;
local TH_STYLE  = &#039;background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);&#039;&lt;br /&gt;
&lt;br /&gt;
local function swatch(label, tok, hexv)&lt;br /&gt;
  return &#039;&amp;lt;span style=&amp;quot;color:var(&#039; .. tok .. &#039;, &#039; .. hexv&lt;br /&gt;
    .. &#039;);font-weight:bold;white-space:nowrap;&amp;quot;&amp;gt;&amp;amp;#9679; &#039; .. label .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Coerce a raw stat value to {display, sortval}: &#039;15%&#039; keeps the %&lt;br /&gt;
-- sign for display but sorts by the number 15.&lt;br /&gt;
local function num(v)&lt;br /&gt;
  if v == nil then return &#039;—&#039;, nil end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    local s = v:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
    local n = tonumber(s) or tonumber(s:match(&#039;^([%-%d%.]+)%%$&#039;))&lt;br /&gt;
    if n == nil then return s, nil end&lt;br /&gt;
    if s:sub(-1) == &#039;%&#039; then return s, n end&lt;br /&gt;
    v = n&lt;br /&gt;
  end&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;∞&#039;, 1e9 end&lt;br /&gt;
    if v &amp;lt;= -1000 then return &#039;—&#039;, -1 end&lt;br /&gt;
    return Core.fmtNum(v), v&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v), nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numcell(v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  return {t = t, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DMG = {&lt;br /&gt;
  Holy = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  Fire = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  Electric = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function chipcell(dmg, v)&lt;br /&gt;
  local t, s = num(v)&lt;br /&gt;
  if s == nil or s &amp;lt;= 0 or t == &#039;—&#039; then return {t = &#039;—&#039;, s = 0} end&lt;br /&gt;
  local c = DMG[dmg]&lt;br /&gt;
  return {t = &#039;&amp;lt;span class=&amp;quot;&#039; .. c[3] .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. c[1] .. &#039;, &#039;&lt;br /&gt;
    .. c[2] .. &#039;);font-weight:bold;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;, s = s}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function iconlinkcell(rec)&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local t&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    t = &#039;[[File:&#039; .. rec.icon .. &#039;|24px|link=&#039; .. slug .. &#039;|alt=&#039; .. name&lt;br /&gt;
      .. &#039;]] [[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&#039;&lt;br /&gt;
  else&lt;br /&gt;
    t = &#039;[[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&#039;&lt;br /&gt;
  end&lt;br /&gt;
  t = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;&#039; .. t .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  return {t = t, s = mw.ustring.lower(name)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat(rec, k)&lt;br /&gt;
  return (rec.stats or {})[k]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function nstat(rec, k)&lt;br /&gt;
  return Core.numStat(stat(rec, k))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalDPS(rec)&lt;br /&gt;
  local s = rec.stats or {}&lt;br /&gt;
  local atk = Core.numStat(s[&#039;Attack Speed&#039;])&lt;br /&gt;
  if atk == nil then return nil end&lt;br /&gt;
  local total = Core.numStat(s[&#039;Damage&#039;]) or 0&lt;br /&gt;
  for _, k in ipairs({&#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;}) do&lt;br /&gt;
    local v = Core.numStat(s[k])&lt;br /&gt;
    if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then total = total + v end&lt;br /&gt;
  end&lt;br /&gt;
  return Core.round1(total * atk)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wave10(rec, base, per)&lt;br /&gt;
  local b, pv = nstat(rec, base), nstat(rec, per)&lt;br /&gt;
  if b == nil or pv == nil then return nil end&lt;br /&gt;
  return Core.round1(b + pv * 9)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- dataset builders ----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function weaponRows(class)&lt;br /&gt;
  local headers = {&#039;Weapon&#039;, &#039;Tier&#039;, &#039;Price&#039;, &#039;Damage&#039;,&lt;br /&gt;
    swatch(&#039;Holy&#039;, &#039;--dmg-holy&#039;, &#039;#e8c34a&#039;),&lt;br /&gt;
    swatch(&#039;Fire&#039;, &#039;--dmg-fire&#039;, &#039;#ff6b35&#039;),&lt;br /&gt;
    swatch(&#039;Electric&#039;, &#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;),&lt;br /&gt;
    &#039;DPS&#039;, &#039;Atk Speed&#039;, &#039;Crit&#039;, &#039;Range&#039;, &#039;Pierce&#039;, &#039;Proj.&#039;, &#039;Bounce&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.load(&#039;Weapons&#039;)) do&lt;br /&gt;
    if Core.weaponClass(rec) == class then&lt;br /&gt;
      local cells = {&lt;br /&gt;
        iconlinkcell(rec),&lt;br /&gt;
        numcell(rec.tier),&lt;br /&gt;
        numcell(rec.price),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Holy&#039;, stat(rec, &#039;Holy Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Fire&#039;, stat(rec, &#039;Fire Damage&#039;)),&lt;br /&gt;
        chipcell(&#039;Electric&#039;, stat(rec, &#039;Electric Damage&#039;)),&lt;br /&gt;
        numcell(totalDPS(rec)),&lt;br /&gt;
        numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Pierce&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Projectiles&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Bounce&#039;)),&lt;br /&gt;
      }&lt;br /&gt;
      local elements = {}&lt;br /&gt;
      for _, e in ipairs({{&#039;holy&#039;, &#039;Holy Damage&#039;}, {&#039;fire&#039;, &#039;Fire Damage&#039;},&lt;br /&gt;
                          {&#039;electric&#039;, &#039;Electric Damage&#039;}}) do&lt;br /&gt;
        local v = nstat(rec, e[2])&lt;br /&gt;
        if v ~= nil and v &amp;gt; 0 and v &amp;lt; 1000 then elements[#elements + 1] = e[1] end&lt;br /&gt;
      end&lt;br /&gt;
      local attrs = &#039; data-element=&amp;quot;&#039; .. (#elements &amp;gt; 0 and table.concat(elements, &#039; &#039;) or &#039;none&#039;)&lt;br /&gt;
        .. &#039;&amp;quot; data-class=&amp;quot;&#039; .. class:lower()&lt;br /&gt;
        .. &#039;&amp;quot; data-tier=&amp;quot;&#039; .. tostring(rec.tier or &#039;&#039;) .. &#039;&amp;quot;&#039;&lt;br /&gt;
      rows[#rows + 1] = {a = attrs, c = cells}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  -- DPS descending (stable: name asc tiebreak via the first cell sortkey).&lt;br /&gt;
  table.sort(rows, function(x, y)&lt;br /&gt;
    local dx, dy = x.c[8].s or 0, y.c[8].s or 0&lt;br /&gt;
    if dx ~= dy then return dx &amp;gt; dy end&lt;br /&gt;
    return (x.c[1].s or &#039;&#039;) &amp;lt; (y.c[1].s or &#039;&#039;)&lt;br /&gt;
  end)&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function enemyRows()&lt;br /&gt;
  local headers = {&#039;Enemy&#039;, &#039;Health&#039;, &#039;HP/Wave&#039;, &#039;HP @ W10&#039;, &#039;Damage&#039;,&lt;br /&gt;
                   &#039;Dmg/Wave&#039;, &#039;Dmg @ W10&#039;, &#039;Speed&#039;, &#039;Resources&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Enemies&#039;)) do&lt;br /&gt;
    local keys = {}&lt;br /&gt;
    for _, k in ipairs(rec.tags or {}) do keys[#keys + 1] = k:lower() end&lt;br /&gt;
    rows[#rows + 1] = {&lt;br /&gt;
      a = &#039; data-type=&amp;quot;&#039; .. (#keys &amp;gt; 0 and table.concat(keys, &#039; &#039;) or &#039;other&#039;) .. &#039;&amp;quot;&#039;,&lt;br /&gt;
      c = {&lt;br /&gt;
        iconlinkcell(rec),&lt;br /&gt;
        numcell(stat(rec, &#039;Health&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;HP per Wave&#039;)),&lt;br /&gt;
        numcell(wave10(rec, &#039;Health&#039;, &#039;HP per Wave&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        numcell(wave10(rec, &#039;Damage&#039;, &#039;Damage per Wave&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
        numcell(stat(rec, &#039;Resources&#039;)),&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function characterRows()&lt;br /&gt;
  local headers = {&#039;Hero&#039;, &#039;Health Points&#039;, &#039;Damage&#039;, &#039;Critical Chance&#039;,&lt;br /&gt;
                   &#039;Attack Speed&#039;, &#039;Range&#039;, &#039;Speed&#039;, &#039;Armor&#039;, &#039;Dodge&#039;}&lt;br /&gt;
  local rows = {}&lt;br /&gt;
  for _, rec in ipairs(Core.sorted(&#039;Characters&#039;)) do&lt;br /&gt;
    rows[#rows + 1] = {a = &#039;&#039;, c = {&lt;br /&gt;
      iconlinkcell(rec),&lt;br /&gt;
      numcell(stat(rec, &#039;Health Points&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Damage&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Critical Chance&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Attack Speed&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Range&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Speed&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Armor&#039;)),&lt;br /&gt;
      numcell(stat(rec, &#039;Dodge&#039;)),&lt;br /&gt;
    }}&lt;br /&gt;
  end&lt;br /&gt;
  return headers, rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local DATASETS = {&lt;br /&gt;
  Weapons_Melee = function() return weaponRows(&#039;Melee&#039;) end,&lt;br /&gt;
  Weapons_Ranged = function() return weaponRows(&#039;Ranged&#039;) end,&lt;br /&gt;
  Enemies = enemyRows,&lt;br /&gt;
  Characters = characterRows,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ----- renderer --------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function cell(c)&lt;br /&gt;
  local t = tostring(c.t or &#039;—&#039;)&lt;br /&gt;
  if c.s ~= nil then&lt;br /&gt;
    local s = c.s&lt;br /&gt;
    if type(s) == &#039;number&#039; then s = Core.fmtNum(s) end&lt;br /&gt;
    return &#039;| data-sort-value=&amp;quot;&#039; .. tostring(s) .. &#039;&amp;quot; | &#039; .. t&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;| &#039; .. t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
  local ds = frame.args[1] or frame.args.cat&lt;br /&gt;
  if not ds or ds == &#039;&#039; then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: missing dataset arg&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  ds = ds:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)&lt;br /&gt;
  local build = DATASETS[ds]&lt;br /&gt;
  if not build then&lt;br /&gt;
    return &#039;&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Module:Compare: unknown dataset &#039;&lt;br /&gt;
      .. mw.text.nowiki(ds) .. &#039;&amp;lt;/strong&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  local headers, rows = build()&lt;br /&gt;
  local out = { &#039;{| class=&amp;quot;wikitable sortable&amp;quot; style=&amp;quot;&#039; .. TBL_STYLE .. &#039;&amp;quot;&#039; }&lt;br /&gt;
  out[#out+1] = &#039;|-&#039;&lt;br /&gt;
  for _, h in ipairs(headers) do&lt;br /&gt;
    out[#out+1] = &#039;! style=&amp;quot;&#039; .. TH_STYLE .. &#039;&amp;quot; | &#039; .. tostring(h)&lt;br /&gt;
  end&lt;br /&gt;
  for _, row in ipairs(rows) do&lt;br /&gt;
    out[#out+1] = &#039;|-&#039; .. tostring(row.a or &#039;&#039;)&lt;br /&gt;
    for _, c in ipairs(row.c) do&lt;br /&gt;
      out[#out+1] = cell(c)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  out[#out+1] = &#039;|}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Core&amp;diff=2082</id>
		<title>Module:Core</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Module:Core&amp;diff=2082"/>
		<updated>2026-06-10T16:04:40Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Module:Core — the engine room of this wiki.&lt;br /&gt;
--&lt;br /&gt;
-- WHAT IT DOES&lt;br /&gt;
--   Every infobox value, index table, badge, tag link, sort order and&lt;br /&gt;
--   lore join on this wiki is computed here (or in a module that&lt;br /&gt;
--   require()s this one) at render time, from the ten source&lt;br /&gt;
--   Data:&amp;lt;Category&amp;gt;.json pages. There are no precomputed data pages:&lt;br /&gt;
--   edit a value on a Data page, purge, and every view of it updates.&lt;br /&gt;
--&lt;br /&gt;
-- HOW TO INVOKE&lt;br /&gt;
--   You normally don&#039;t invoke Core directly. Each category module&lt;br /&gt;
--   (Module:Weapons, Module:Enemies, ...) is a thin shim that binds a&lt;br /&gt;
--   category name and forwards to the entry points at the bottom of&lt;br /&gt;
--   this file:&lt;br /&gt;
--     {{#invoke:Weapons|infobox|id=Cutter}}     -&amp;gt; p.infoboxEntry&lt;br /&gt;
--     {{#invoke:Weapons|body|id=Cutter}}        -&amp;gt; p.bodyEntry&lt;br /&gt;
--     {{#invoke:Weapons|index}}                 -&amp;gt; p.indexEntry&lt;br /&gt;
--     {{#invoke:Weapons|render|id=Cutter}}      -&amp;gt; p.renderEntry (all-in-one)&lt;br /&gt;
--&lt;br /&gt;
-- SOURCE DATA IT READS (see each page&#039;s own `description` for fields)&lt;br /&gt;
--   Data:Weapons.json, Data:Characters.json, Data:Blessings.json,&lt;br /&gt;
--   Data:Upgrades.json, Data:Crux.json, Data:Passives.json,&lt;br /&gt;
--   Data:Enemies.json, Data:Achievements.json, Data:Codex.json,&lt;br /&gt;
--   Data:Tags.json&lt;br /&gt;
--   Common record fields: id, slug, name, icon, tier, price, locked,&lt;br /&gt;
--   type, state, tags[] (keys into Data:Tags.json), stats{}, description.&lt;br /&gt;
--&lt;br /&gt;
-- EDITING NOTES&lt;br /&gt;
--   * `slug` IS the page title. Never rebuild it from `name` in Lua —&lt;br /&gt;
--     always read rec.slug.&lt;br /&gt;
--   * Presentation config (stat display order, glyphs, infobox field&lt;br /&gt;
--     lists, index columns, sort keys) lives in the tables below and is&lt;br /&gt;
--     editable on-wiki in this one place.&lt;br /&gt;
--   * Cross-category functions (find, tagMembership, codexFor) load&lt;br /&gt;
--     several Data pages in one parse; the host runs Scribunto with a&lt;br /&gt;
--     raised (256 MiB) memory cap to allow this.&lt;br /&gt;
--   * See Help:Wiki Editing for the full architecture guide.&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- registry&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
p.CATEGORIES = {&lt;br /&gt;
  {name = &#039;Weapons&#039;,      singular = &#039;Weapon&#039;},&lt;br /&gt;
  {name = &#039;Characters&#039;,   singular = &#039;Character&#039;},&lt;br /&gt;
  {name = &#039;Blessings&#039;,    singular = &#039;Blessing&#039;},&lt;br /&gt;
  {name = &#039;Upgrades&#039;,     singular = &#039;Upgrade&#039;},&lt;br /&gt;
  {name = &#039;Crux&#039;,         singular = &#039;Crux&#039;},&lt;br /&gt;
  {name = &#039;Passives&#039;,     singular = &#039;Passive&#039;},&lt;br /&gt;
  {name = &#039;Enemies&#039;,      singular = &#039;Enemy&#039;},&lt;br /&gt;
  {name = &#039;Achievements&#039;, singular = &#039;Achievement&#039;},&lt;br /&gt;
  {name = &#039;Codex&#039;,        singular = &#039;Codex&#039;},&lt;br /&gt;
  {name = &#039;Tags&#039;,         singular = &#039;Tag&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local ALIAS = {}&lt;br /&gt;
for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
  ALIAS[c.name:lower()] = c.name&lt;br /&gt;
  ALIAS[c.singular:lower()] = c.name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;Weapon&#039; / &#039;weapons&#039; / &#039;Weapons&#039; -&amp;gt; &#039;Weapons&#039;; nil when not a category.&lt;br /&gt;
function p.resolveCategory(word)&lt;br /&gt;
  if not word or word == &#039;&#039; then return nil end&lt;br /&gt;
  return ALIAS[(word:gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)):lower()]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- data loading (memoized per parse)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
&lt;br /&gt;
-- mw.loadJsonData returns a read-only table with quirky numeric keys;&lt;br /&gt;
-- copy it once into a plain mutable table so the rest of the module can&lt;br /&gt;
-- treat records as ordinary Lua tables.&lt;br /&gt;
local function fix(t)&lt;br /&gt;
  if type(t) ~= &#039;table&#039; then return t end&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for k, v in pairs(t) do&lt;br /&gt;
    out[k] = fix(v)&lt;br /&gt;
    if type(k) == &#039;number&#039; then out[tostring(k)] = out[k] end&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local _data = {}&lt;br /&gt;
function p.load(cat)&lt;br /&gt;
  cat = p.resolveCategory(cat) or cat&lt;br /&gt;
  if _data[cat] == nil then&lt;br /&gt;
    local ok, d = pcall(mw.loadJsonData, &#039;Data:&#039; .. cat .. &#039;.json&#039;)&lt;br /&gt;
    if ok and type(d) == &#039;table&#039; then&lt;br /&gt;
      _data[cat] = fix(d).records or {}&lt;br /&gt;
    else&lt;br /&gt;
      _data[cat] = {}&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _data[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local _byId = {}&lt;br /&gt;
function p.byId(cat)&lt;br /&gt;
  cat = p.resolveCategory(cat) or cat&lt;br /&gt;
  if _byId[cat] == nil then&lt;br /&gt;
    local m = {}&lt;br /&gt;
    for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
      if r.id then m[r.id] = r end&lt;br /&gt;
      if r.slug then m[r.slug] = r end&lt;br /&gt;
    end&lt;br /&gt;
    _byId[cat] = m&lt;br /&gt;
  end&lt;br /&gt;
  return _byId[cat]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Iterate every category&#039;s records: returns a list of {cat=&amp;lt;name&amp;gt;, recs=&amp;lt;list&amp;gt;}.&lt;br /&gt;
function p.all()&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
    out[#out + 1] = {cat = c.name, recs = p.load(c.name)}&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- First record across all categories matching slug or (lowercased) name.&lt;br /&gt;
-- Registry order wins on collisions (live entity beats its Codex entry).&lt;br /&gt;
function p.find(key)&lt;br /&gt;
  if not key or key == &#039;&#039; then return nil, nil end&lt;br /&gt;
  local k = mw.ustring.lower(key)&lt;br /&gt;
  for _, c in ipairs(p.CATEGORIES) do&lt;br /&gt;
    for _, r in ipairs(p.load(c.name)) do&lt;br /&gt;
      if r.slug == key or (r.slug and mw.ustring.lower(r.slug) == k)&lt;br /&gt;
        or (r.name and mw.ustring.lower(r.name) == k) then&lt;br /&gt;
        return c.name, r&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return nil, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- small helpers&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
function p.slugFor(rec) return rec and rec.slug or &#039;&#039; end&lt;br /&gt;
function p.iconFor(rec) return rec and rec.icon or &#039;&#039; end&lt;br /&gt;
&lt;br /&gt;
function p.normName(s)&lt;br /&gt;
  s = mw.ustring.lower(s or &#039;&#039;)&lt;br /&gt;
  return (mw.ustring.gsub(s, &#039;[^%w]&#039;, &#039;&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.link(rec)&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  return &#039;[[&#039; .. slug .. &#039;|&#039; .. (rec.name or slug) .. &#039;]]&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- icon + linked name wrapped in .wm-tip (Common.js hover-infobox popup).&lt;br /&gt;
function p.iconLink(rec, size)&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local slug = rec.slug or rec.name or &#039;&#039;&lt;br /&gt;
  local name = rec.name or slug&lt;br /&gt;
  local tip = &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. slug .. &#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
  if rec.icon and rec.icon ~= &#039;&#039; then&lt;br /&gt;
    return tip .. &#039;[[File:&#039; .. rec.icon .. &#039;|&#039; .. (size or 24) .. &#039;px|link=&#039;&lt;br /&gt;
      .. slug .. &#039;]] [[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return tip .. &#039;[[&#039; .. slug .. &#039;|&#039; .. name .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- &#039;56.1&#039; / &#039;6&#039; (no trailing .0); non-numbers pass through as strings.&lt;br /&gt;
function p.fmtNum(v)&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v == math.floor(v) then return string.format(&#039;%d&#039;, v) end&lt;br /&gt;
    return tostring(v)&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(v or &#039;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.round1(x)&lt;br /&gt;
  return math.floor(x * 10 + 0.5) / 10&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parse a stat value to a number: 11 -&amp;gt; 11, &#039;15%&#039; -&amp;gt; 15, else nil.&lt;br /&gt;
function p.numStat(v)&lt;br /&gt;
  if type(v) == &#039;number&#039; then return v end&lt;br /&gt;
  if type(v) == &#039;string&#039; then&lt;br /&gt;
    return tonumber(v) or tonumber(v:match(&#039;^%s*([%-%d%.]+)&#039;))&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Render one stat value. The game data uses huge sentinels for on/off&lt;br /&gt;
-- mechanics: &amp;gt;= 1000 means &amp;quot;Unlimited&amp;quot;, &amp;lt;= -1000 means &amp;quot;disabled&amp;quot; (we&lt;br /&gt;
-- return nil so the caller drops the row). `signed` adds a &#039;+&#039; prefix&lt;br /&gt;
-- for modifier-style categories (heroes, blessings, upgrades, ...).&lt;br /&gt;
function p.fmtStat(v, signed)&lt;br /&gt;
  if type(v) == &#039;number&#039; then&lt;br /&gt;
    if v &amp;gt;= 1000 then return &#039;Unlimited&#039; end&lt;br /&gt;
    if v &amp;lt;= -1000 then return nil end&lt;br /&gt;
    local s = p.fmtNum(v)&lt;br /&gt;
    if signed and v &amp;gt; 0 then s = &#039;+&#039; .. s end&lt;br /&gt;
    return s&lt;br /&gt;
  end&lt;br /&gt;
  local s = tostring(v or &#039;&#039;)&lt;br /&gt;
  if signed and s ~= &#039;&#039; and s:sub(1, 1) ~= &#039;-&#039; and s:sub(1, 1) ~= &#039;+&#039; then&lt;br /&gt;
    s = &#039;+&#039; .. s&lt;br /&gt;
  end&lt;br /&gt;
  return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.firstSentence(text, cap)&lt;br /&gt;
  local t = (text or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;)&lt;br /&gt;
  t = t:match(&#039;^[^\n]*&#039;) or t&lt;br /&gt;
  local s = t:match(&#039;^(.-[%.!%?])%s&#039;) or t:match(&#039;^(.-[%.!%?])$&#039;) or t&lt;br /&gt;
  cap = cap or 140&lt;br /&gt;
  if mw.ustring.len(s) &amp;gt; cap then s = mw.ustring.sub(s, 1, cap) end&lt;br /&gt;
  return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- stat presentation config&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
&lt;br /&gt;
-- Canonical display order for the stats block (JSON object key order is&lt;br /&gt;
-- not visible to Lua). Unknown stats append alphabetically at the end.&lt;br /&gt;
local STAT_ORDER = {&lt;br /&gt;
  &#039;Damage&#039;, &#039;Damage per Wave&#039;, &#039;Health&#039;, &#039;HP per Wave&#039;,&lt;br /&gt;
  &#039;Holy Damage&#039;, &#039;Fire Damage&#039;, &#039;Electric Damage&#039;,&lt;br /&gt;
  &#039;Damage vs Burning&#039;, &#039;Damage vs Shocked&#039;, &#039;Melee Damage&#039;, &#039;Range Damage&#039;,&lt;br /&gt;
  &#039;Attack Speed&#039;, &#039;Critical Chance&#039;, &#039;Critical Damage&#039;, &#039;Range&#039;,&lt;br /&gt;
  &#039;Health Points&#039;, &#039;HP Rate&#039;, &#039;HP Delay&#039;,&lt;br /&gt;
  &#039;Pierce&#039;, &#039;Projectiles&#039;, &#039;Bounce&#039;,&lt;br /&gt;
  &#039;Explosive Chance&#039;, &#039;Explosive Radius&#039;, &#039;Explosive Range&#039;, &#039;HP Steal&#039;,&lt;br /&gt;
  &#039;Armor&#039;, &#039;Dodge&#039;, &#039;Speed&#039;, &#039;Resources&#039;, &#039;Rarity Find&#039;,&lt;br /&gt;
  &#039;Stagger&#039;, &#039;Stun&#039;, &#039;Freeze&#039;, &#039;XP Gain&#039;, &#039;Pickup Range&#039;, &#039;Cooldown&#039;,&lt;br /&gt;
}&lt;br /&gt;
local STAT_RANK = {}&lt;br /&gt;
for i, k in ipairs(STAT_ORDER) do STAT_RANK[k] = i end&lt;br /&gt;
&lt;br /&gt;
-- Stat name -&amp;gt; in-game glyph file (18px next to each stat row).&lt;br /&gt;
local STAT_GLYPHS = {&lt;br /&gt;
  [&#039;Damage&#039;] = &#039;Stat_Damage.png&#039;,&lt;br /&gt;
  [&#039;Fire Damage&#039;] = &#039;Stat_Fire.png&#039;,&lt;br /&gt;
  [&#039;Electric Damage&#039;] = &#039;Stat_Electric.png&#039;,&lt;br /&gt;
  [&#039;Holy Damage&#039;] = &#039;Stat_Physical.png&#039;,&lt;br /&gt;
  [&#039;Attack Speed&#039;] = &#039;Stat_AttackSpeed.png&#039;,&lt;br /&gt;
  [&#039;Critical Chance&#039;] = &#039;Stat_Critical.png&#039;,&lt;br /&gt;
  [&#039;Critical Damage&#039;] = &#039;Stat_CriticalDmg.png&#039;,&lt;br /&gt;
  [&#039;Range&#039;] = &#039;Stat_Range.png&#039;,&lt;br /&gt;
  [&#039;Pierce&#039;] = &#039;Stat_Pierce.png&#039;,&lt;br /&gt;
  [&#039;Bounce&#039;] = &#039;Stat_Bounce.png&#039;,&lt;br /&gt;
  [&#039;Projectiles&#039;] = &#039;Stat_Projectiles.png&#039;,&lt;br /&gt;
  [&#039;Stagger&#039;] = &#039;Stat_Stagger.png&#039;,&lt;br /&gt;
  [&#039;Stun&#039;] = &#039;Stat_Stun.png&#039;,&lt;br /&gt;
  [&#039;Freeze&#039;] = &#039;Stat_Freeze.png&#039;,&lt;br /&gt;
  [&#039;Explosive Chance&#039;] = &#039;Stat_ExplosiveChance.png&#039;,&lt;br /&gt;
  [&#039;Explosive Radius&#039;] = &#039;Stat_ExplosiveRadius.png&#039;,&lt;br /&gt;
  [&#039;Explosive Range&#039;] = &#039;Stat_ExplosiveRadius.png&#039;,&lt;br /&gt;
  [&#039;HP Steal&#039;] = &#039;Stat_HPSteal.png&#039;,&lt;br /&gt;
  [&#039;Health Points&#039;] = &#039;Stat_HP.png&#039;,&lt;br /&gt;
  [&#039;HP Rate&#039;] = &#039;Stat_HPRate.png&#039;,&lt;br /&gt;
  [&#039;HP Delay&#039;] = &#039;Stat_HPDelay.png&#039;,&lt;br /&gt;
  [&#039;Speed&#039;] = &#039;Stat_Speed.png&#039;,&lt;br /&gt;
  [&#039;Armor&#039;] = &#039;Stat_Armor.png&#039;,&lt;br /&gt;
  [&#039;Dodge&#039;] = &#039;Stat_Dodge.png&#039;,&lt;br /&gt;
  [&#039;Resources&#039;] = &#039;Stat_Resources.png&#039;,&lt;br /&gt;
  [&#039;Pickup Range&#039;] = &#039;Stat_PickupRange.png&#039;,&lt;br /&gt;
  [&#039;Rarity Find&#039;] = &#039;Stat_RarityFind.png&#039;,&lt;br /&gt;
  [&#039;XP Gain&#039;] = &#039;Stat_XPGain.png&#039;,&lt;br /&gt;
  [&#039;Cooldown&#039;] = &#039;Stat_Cooldown.png&#039;,&lt;br /&gt;
  [&#039;Damage vs Burning&#039;] = &#039;Stat_VsFire.png&#039;,&lt;br /&gt;
  [&#039;Damage vs Shocked&#039;] = &#039;Stat_VsElectric.png&#039;,&lt;br /&gt;
  [&#039;Health&#039;] = &#039;Stat_HitPoints.png&#039;,&lt;br /&gt;
}&lt;br /&gt;
p.STAT_GLYPHS = STAT_GLYPHS&lt;br /&gt;
&lt;br /&gt;
function p.statGlyph(stat)&lt;br /&gt;
  local f = STAT_GLYPHS[stat]&lt;br /&gt;
  if f then return &#039;[[File:&#039; .. f .. &#039;|18px|link=]] &#039; end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Elemental damage accent chips (dark-theme tokens with inline fallbacks).&lt;br /&gt;
p.DMG_CHIP = {&lt;br /&gt;
  [&#039;Holy Damage&#039;]     = {&#039;--dmg-holy&#039;, &#039;#e8c34a&#039;, &#039;wm-dmg-holy&#039;},&lt;br /&gt;
  [&#039;Fire Damage&#039;]     = {&#039;--dmg-fire&#039;, &#039;#ff6b35&#039;, &#039;wm-dmg-fire&#039;},&lt;br /&gt;
  [&#039;Electric Damage&#039;] = {&#039;--dmg-electric&#039;, &#039;#4ea3ff&#039;, &#039;wm-dmg-electric&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Ordered {key, value} pairs of rec.stats per STAT_ORDER (extras appended&lt;br /&gt;
-- alphabetically).&lt;br /&gt;
function p.statPairs(rec)&lt;br /&gt;
  local stats = (rec and rec.stats) or {}&lt;br /&gt;
  local known, extra = {}, {}&lt;br /&gt;
  for k in pairs(stats) do&lt;br /&gt;
    if STAT_RANK[k] then known[#known + 1] = k else extra[#extra + 1] = k end&lt;br /&gt;
  end&lt;br /&gt;
  table.sort(known, function(a, b) return STAT_RANK[a] &amp;lt; STAT_RANK[b] end)&lt;br /&gt;
  table.sort(extra)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, k in ipairs(known) do out[#out + 1] = {k, stats[k]} end&lt;br /&gt;
  for _, k in ipairs(extra) do out[#out + 1] = {k, stats[k]} end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Infobox &#039;Stats&#039; cell: one glyph+label+value line per stat, &amp;lt;br&amp;gt;-joined.&lt;br /&gt;
-- Elemental damage labels link to the damage-type hub pages.&lt;br /&gt;
function p.statsBlock(rec, signed)&lt;br /&gt;
  local lines = {}&lt;br /&gt;
  for _, kv in ipairs(p.statPairs(rec)) do&lt;br /&gt;
    local k, v = kv[1], kv[2]&lt;br /&gt;
    local val = p.fmtStat(v, signed)&lt;br /&gt;
    if val ~= nil then&lt;br /&gt;
      local chip = p.DMG_CHIP[k]&lt;br /&gt;
      if chip then&lt;br /&gt;
        lines[#lines + 1] = p.statGlyph(k) .. &#039;&amp;lt;span class=&amp;quot;&#039; .. chip[3]&lt;br /&gt;
          .. &#039;&amp;quot; style=&amp;quot;color:var(&#039; .. chip[1] .. &#039;, &#039; .. chip[2]&lt;br /&gt;
          .. &amp;quot;);\&amp;quot;&amp;gt;&#039;&#039;&#039;[[&amp;quot; .. k .. &#039;|&#039; .. k .. &amp;quot;]]:&#039;&#039;&#039; &amp;quot; .. val .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
      else&lt;br /&gt;
        lines[#lines + 1] = p.statGlyph(k) .. &amp;quot;&#039;&#039;&#039;&amp;quot; .. k .. &amp;quot;:&#039;&#039;&#039; &amp;quot; .. val&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return table.concat(lines, &#039;&amp;lt;br&amp;gt;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- One-line plain-text summary for index columns (Effects / Modifiers).&lt;br /&gt;
function p.effectsSummary(rec, signed)&lt;br /&gt;
  local parts = {}&lt;br /&gt;
  for _, kv in ipairs(p.statPairs(rec)) do&lt;br /&gt;
    local val = p.fmtStat(kv[2], signed)&lt;br /&gt;
    if val ~= nil then parts[#parts + 1] = kv[1] .. &#039; &#039; .. val end&lt;br /&gt;
  end&lt;br /&gt;
  if #parts &amp;gt; 0 then return table.concat(parts, &#039;, &#039;) end&lt;br /&gt;
  local texts = {}&lt;br /&gt;
  for _, key in ipairs(rec.tags or {}) do&lt;br /&gt;
    local t = p.tagText(key)&lt;br /&gt;
    if t and t ~= &#039;&#039; then texts[#texts + 1] = t end&lt;br /&gt;
  end&lt;br /&gt;
  if #texts &amp;gt; 0 then return table.concat(texts, &#039; &#039;) end&lt;br /&gt;
  local d = (rec.description or &#039;&#039;):gsub(&#039;^%s+&#039;, &#039;&#039;)&lt;br /&gt;
  if d ~= &#039;&#039; then return mw.ustring.sub(p.firstSentence(d, 120), 1, 120) end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.availability(locked)&lt;br /&gt;
  if locked then return &#039;Must be unlocked&#039; end&lt;br /&gt;
  return &#039;Unlocked from start&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CRUX_STATE = {&lt;br /&gt;
  Acquired = &#039;Default&#039;,&lt;br /&gt;
  Unlocked = &#039;Unlocked from start&#039;,&lt;br /&gt;
  Locked = &#039;Must be unlocked&#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Melee vs Ranged: the melee weapons carry the Melee tag (or raw&lt;br /&gt;
-- tag_source family &#039;Melee&#039;); everything else is Ranged.&lt;br /&gt;
function p.weaponClass(rec)&lt;br /&gt;
  for _, k in ipairs(rec.tags or {}) do&lt;br /&gt;
    if k == &#039;Melee&#039; then return &#039;Melee&#039; end&lt;br /&gt;
  end&lt;br /&gt;
  if rec.tag_source == &#039;Melee&#039; then return &#039;Melee&#039; end&lt;br /&gt;
  return &#039;Ranged&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- tags&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local _tagByKey = nil&lt;br /&gt;
function p.tagRec(key)&lt;br /&gt;
  if _tagByKey == nil then&lt;br /&gt;
    _tagByKey = {}&lt;br /&gt;
    for _, r in ipairs(p.load(&#039;Tags&#039;)) do&lt;br /&gt;
      _tagByKey[r.id or r.name] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _tagByKey[key]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.tagText(key)&lt;br /&gt;
  local r = p.tagRec(key)&lt;br /&gt;
  return (r and r.text) or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Tag link wrapped for the hover-tooltip JS. Slug comes from the tag&#039;s&lt;br /&gt;
-- own source record (never recomputed here).&lt;br /&gt;
function p.tagLink(key)&lt;br /&gt;
  local r = p.tagRec(key)&lt;br /&gt;
  if not r or not r.slug then return key end&lt;br /&gt;
  return &#039;&amp;lt;span class=&amp;quot;wm-tip&amp;quot; data-tip-title=&amp;quot;&#039; .. r.slug .. &#039;&amp;quot;&amp;gt;[[&#039;&lt;br /&gt;
    .. r.slug .. &#039;|&#039; .. key .. &#039;]]&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Body &#039;Tags&#039; section: list of &amp;quot;&amp;lt;link&amp;gt; – &amp;lt;text&amp;gt;&amp;quot; strings.&lt;br /&gt;
function p.tagsBody(rec)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, key in ipairs(rec.tags or {}) do&lt;br /&gt;
    local text = p.tagText(key)&lt;br /&gt;
    if text ~= &#039;&#039; then&lt;br /&gt;
      out[#out + 1] = p.tagLink(key) .. &#039; – &#039; .. text&lt;br /&gt;
    else&lt;br /&gt;
      out[#out + 1] = p.tagLink(key)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Cross-category tag membership map: {tagkey -&amp;gt; {catName -&amp;gt; {rec,...}}}.&lt;br /&gt;
-- One scan per parse (memoized); powers tag pages + Used-by counts.&lt;br /&gt;
local TAG_BEARING = {&#039;Weapons&#039;, &#039;Characters&#039;, &#039;Blessings&#039;, &#039;Upgrades&#039;,&lt;br /&gt;
                     &#039;Passives&#039;, &#039;Enemies&#039;}&lt;br /&gt;
p.TAG_BEARING = TAG_BEARING&lt;br /&gt;
&lt;br /&gt;
local _membership = nil&lt;br /&gt;
function p.tagMembership()&lt;br /&gt;
  if _membership == nil then&lt;br /&gt;
    _membership = {}&lt;br /&gt;
    for _, cat in ipairs(TAG_BEARING) do&lt;br /&gt;
      for _, r in ipairs(p.load(cat)) do&lt;br /&gt;
        for _, key in ipairs(r.tags or {}) do&lt;br /&gt;
          _membership[key] = _membership[key] or {}&lt;br /&gt;
          _membership[key][cat] = _membership[key][cat] or {}&lt;br /&gt;
          local bucket = _membership[key][cat]&lt;br /&gt;
          bucket[#bucket + 1] = r&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _membership&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.usedByCount(key)&lt;br /&gt;
  local m = p.tagMembership()[key]&lt;br /&gt;
  if not m then return 0 end&lt;br /&gt;
  local n = 0&lt;br /&gt;
  for _, recs in pairs(m) do n = n + #recs end&lt;br /&gt;
  return n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- codex joins (by normalized name)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local _codexByType = nil&lt;br /&gt;
local function codexIndex()&lt;br /&gt;
  if _codexByType == nil then&lt;br /&gt;
    _codexByType = {}&lt;br /&gt;
    for _, r in ipairs(p.load(&#039;Codex&#039;)) do&lt;br /&gt;
      local t = r.type or &#039;&#039;&lt;br /&gt;
      _codexByType[t] = _codexByType[t] or {}&lt;br /&gt;
      _codexByType[t][p.normName(r.name or r.id)] = r&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return _codexByType&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- The codex entry describing an entity (Weapons / Enemies join), or nil.&lt;br /&gt;
function p.codexFor(name, codexType)&lt;br /&gt;
  local idx = codexIndex()&lt;br /&gt;
  local key = p.normName(name)&lt;br /&gt;
  if codexType then&lt;br /&gt;
    return (idx[codexType] or {})[key]&lt;br /&gt;
  end&lt;br /&gt;
  for _, bucket in pairs(idx) do&lt;br /&gt;
    if bucket[key] then return bucket[key] end&lt;br /&gt;
  end&lt;br /&gt;
  return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- computed infobox (per category) — same field names/values the&lt;br /&gt;
-- infobox Templates and index columns have always consumed.&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local INFOBOX_BUILDERS = {&lt;br /&gt;
  Weapons = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Class = p.weaponClass(rec),&lt;br /&gt;
      Tier = rec.tier,&lt;br /&gt;
      Price = p.fmtNum(rec.price),&lt;br /&gt;
      Availability = p.availability(rec.locked),&lt;br /&gt;
      Stats = p.statsBlock(rec, false),&lt;br /&gt;
      Damage = rec.stats and rec.stats[&#039;Damage&#039;] ~= nil&lt;br /&gt;
        and p.fmtNum(rec.stats[&#039;Damage&#039;]) or nil,&lt;br /&gt;
      [&#039;Attack Speed&#039;] = rec.stats and rec.stats[&#039;Attack Speed&#039;] ~= nil&lt;br /&gt;
        and p.fmtNum(rec.stats[&#039;Attack Speed&#039;]) or nil,&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Characters = function(rec)&lt;br /&gt;
    local hp = rec.stats and rec.stats[&#039;Health Points&#039;]&lt;br /&gt;
    return {&lt;br /&gt;
      Availability = p.availability(rec.locked),&lt;br /&gt;
      Stats = p.statsBlock(rec, true),&lt;br /&gt;
      Modifiers = p.effectsSummary(rec, true),&lt;br /&gt;
      [&#039;Health Points&#039;] = hp ~= nil and p.fmtStat(hp, true) or nil,&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Crux = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Type = rec.type,&lt;br /&gt;
      Availability = CRUX_STATE[rec.state] or rec.state or &#039;&#039;,&lt;br /&gt;
      Stats = p.statsBlock(rec, true),&lt;br /&gt;
      Effects = p.effectsSummary(rec, true),&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Enemies = function(rec)&lt;br /&gt;
    local ib = {Stats = p.statsBlock(rec, false)}&lt;br /&gt;
    if rec.tags and #rec.tags &amp;gt; 0 then&lt;br /&gt;
      local links = {}&lt;br /&gt;
      for _, k in ipairs(rec.tags) do links[#links + 1] = p.tagLink(k) end&lt;br /&gt;
      ib.Classification = table.concat(links, &#039;, &#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local st = rec.stats or {}&lt;br /&gt;
    for _, k in ipairs({&#039;Health&#039;, &#039;Damage&#039;, &#039;Speed&#039;, &#039;Resources&#039;}) do&lt;br /&gt;
      if st[k] ~= nil then ib[k] = p.fmtNum(st[k]) end&lt;br /&gt;
    end&lt;br /&gt;
    if st[&#039;HP per Wave&#039;] ~= nil then ib[&#039;HP/Wave&#039;] = p.fmtNum(st[&#039;HP per Wave&#039;]) end&lt;br /&gt;
    if st[&#039;Damage per Wave&#039;] ~= nil then ib[&#039;Dmg/Wave&#039;] = p.fmtNum(st[&#039;Damage per Wave&#039;]) end&lt;br /&gt;
    return ib&lt;br /&gt;
  end,&lt;br /&gt;
  Achievements = function(rec)&lt;br /&gt;
    return {Objective = rec.description or &#039;&#039;}&lt;br /&gt;
  end,&lt;br /&gt;
  Codex = function(rec)&lt;br /&gt;
    return {&lt;br /&gt;
      Type = rec.type,&lt;br /&gt;
      Summary = p.firstSentence(rec.description or &#039;&#039;, 140),&lt;br /&gt;
    }&lt;br /&gt;
  end,&lt;br /&gt;
  Tags = function(rec)&lt;br /&gt;
    local text = rec.text or &#039;&#039;&lt;br /&gt;
    if text == &#039;&#039; then text = &amp;quot;&#039;&#039;(no effect text provided by the game data)&#039;&#039;&amp;quot; end&lt;br /&gt;
    return {Effect = text, [&#039;Used by&#039;] = p.usedByCount(rec.id or rec.name)}&lt;br /&gt;
  end,&lt;br /&gt;
}&lt;br /&gt;
local function shopInfobox(rec)&lt;br /&gt;
  return {&lt;br /&gt;
    Tier = rec.tier,&lt;br /&gt;
    Price = p.fmtNum(rec.price),&lt;br /&gt;
    Availability = p.availability(rec.locked),&lt;br /&gt;
    Stats = p.statsBlock(rec, true),&lt;br /&gt;
    Effects = p.effectsSummary(rec, true),&lt;br /&gt;
  }&lt;br /&gt;
end&lt;br /&gt;
INFOBOX_BUILDERS.Blessings = shopInfobox&lt;br /&gt;
INFOBOX_BUILDERS.Upgrades = shopInfobox&lt;br /&gt;
INFOBOX_BUILDERS.Passives = shopInfobox&lt;br /&gt;
&lt;br /&gt;
function p.computedInfobox(cat, rec)&lt;br /&gt;
  local b = INFOBOX_BUILDERS[cat]&lt;br /&gt;
  if not b then return {} end&lt;br /&gt;
  return b(rec)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- body sections (computed)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
function p.bodySection(cat, rec, section)&lt;br /&gt;
  if section == &#039;Tags&#039; then&lt;br /&gt;
    return p.tagsBody(rec)&lt;br /&gt;
  end&lt;br /&gt;
  if section == &#039;Description&#039; or section == &#039;Objective&#039; then&lt;br /&gt;
    return rec.description or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  if section == &#039;Lore&#039; then&lt;br /&gt;
    if cat == &#039;Weapons&#039; or cat == &#039;Enemies&#039; then&lt;br /&gt;
      local cx = p.codexFor(rec.name, cat)&lt;br /&gt;
      if cx and cx.description and cx.description ~= &#039;&#039; then&lt;br /&gt;
        return cx.description&lt;br /&gt;
      end&lt;br /&gt;
      if cat == &#039;Enemies&#039; then return rec.description or &#039;&#039; end&lt;br /&gt;
      return &#039;&#039;&lt;br /&gt;
    end&lt;br /&gt;
    -- Characters / Codex: own prose.&lt;br /&gt;
    return rec.description or &#039;&#039;&lt;br /&gt;
  end&lt;br /&gt;
  return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BODY_ORDER = {&lt;br /&gt;
  Weapons = {&#039;Lore&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Characters = {&#039;Lore&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Blessings = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Upgrades = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Passives = {&#039;Description&#039;, &#039;Tags&#039;},&lt;br /&gt;
  Crux = {&#039;Description&#039;},&lt;br /&gt;
  Enemies = {&#039;Lore&#039;},&lt;br /&gt;
  Achievements = {&#039;Objective&#039;},&lt;br /&gt;
  Codex = {&#039;Lore&#039;},&lt;br /&gt;
  Tags = {},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- sorting (per-category index order, computed at render time so the&lt;br /&gt;
-- source page order never matters)&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local CRUX_TYPE_ORDER = {Unique = 1, Action = 2, Buff = 3, Debuff = 4, Aura = 5}&lt;br /&gt;
local CODEX_TYPE_ORDER = {Enemies = 1, Weapons = 2, Events = 3, Tips = 4}&lt;br /&gt;
local ROMAN = {I = 1, II = 2, III = 3, IV = 4, V = 5}&lt;br /&gt;
&lt;br /&gt;
local function familyKey(name)&lt;br /&gt;
  local head, tail = name:match(&#039;^(.*)%s(%S+)$&#039;)&lt;br /&gt;
  if head and ROMAN[tail] then&lt;br /&gt;
    return mw.ustring.lower(head), ROMAN[tail]&lt;br /&gt;
  end&lt;br /&gt;
  if name:sub(-5) == &#039; Plus&#039; then&lt;br /&gt;
    return mw.ustring.lower(name:sub(1, -6)), 2&lt;br /&gt;
  end&lt;br /&gt;
  return mw.ustring.lower(name), 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local SORT_KEYS = {&lt;br /&gt;
  Weapons = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Characters = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Blessings = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Upgrades = function(r) return {tonumber(r.tier) or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Passives = function(r)&lt;br /&gt;
    local fam, rank = familyKey(r.name or &#039;&#039;)&lt;br /&gt;
    return {fam, rank, r.name or &#039;&#039;}&lt;br /&gt;
  end,&lt;br /&gt;
  Crux = function(r) return {CRUX_TYPE_ORDER[r.type] or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Enemies = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Achievements = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Codex = function(r) return {CODEX_TYPE_ORDER[r.type] or 9, mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
  Tags = function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function keyLess(a, b)&lt;br /&gt;
  for i = 1, math.max(#a, #b) do&lt;br /&gt;
    local x, y = a[i], b[i]&lt;br /&gt;
    if x == nil then return true end&lt;br /&gt;
    if y == nil then return false end&lt;br /&gt;
    if x ~= y then&lt;br /&gt;
      if type(x) == &#039;number&#039; and type(y) == &#039;number&#039; then return x &amp;lt; y end&lt;br /&gt;
      return tostring(x) &amp;lt; tostring(y)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Stable per-category sort of the records list (returns a new list).&lt;br /&gt;
function p.sorted(cat)&lt;br /&gt;
  local keyfn = SORT_KEYS[cat] or function(r) return {mw.ustring.lower(r.name or &#039;&#039;)} end&lt;br /&gt;
  local list = {}&lt;br /&gt;
  for i, r in ipairs(p.load(cat)) do list[#list + 1] = {i = i, r = r, k = keyfn(r)} end&lt;br /&gt;
  table.sort(list, function(a, b)&lt;br /&gt;
    if keyLess(a.k, b.k) then return true end&lt;br /&gt;
    if keyLess(b.k, a.k) then return false end&lt;br /&gt;
    return a.i &amp;lt; b.i&lt;br /&gt;
  end)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, e in ipairs(list) do out[#out + 1] = e.r end&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
-- index page config + entry points shared by the per-category shims&lt;br /&gt;
-- ===================================================================&lt;br /&gt;
local INDEX_OPTS = {&lt;br /&gt;
  Weapons = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Damage&#039;, source = &#039;Damage&#039;}, {name = &#039;Attack Speed&#039;, source = &#039;Attack Speed&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Characters = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Modifiers&#039;, source = &#039;Modifiers&#039;}, {name = &#039;Health Points&#039;, source = &#039;Health Points&#039;}}, thumbnail_size = 64},&lt;br /&gt;
  Blessings = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Upgrades = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Passives = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Tier&#039;, source = &#039;Tier&#039;}, {name = &#039;Price&#039;, source = &#039;Price&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Crux = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Type&#039;, source = &#039;Type&#039;}, {name = &#039;Effects&#039;, source = &#039;Effects&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Enemies = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Health&#039;, source = &#039;Health&#039;}, {name = &#039;HP/Wave&#039;, source = &#039;HP/Wave&#039;}, {name = &#039;Damage&#039;, source = &#039;Damage&#039;}, {name = &#039;Dmg/Wave&#039;, source = &#039;Dmg/Wave&#039;}, {name = &#039;Speed&#039;, source = &#039;Speed&#039;}, {name = &#039;Resources&#039;, source = &#039;Resources&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Achievements = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Objective&#039;, source = &#039;Objective&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Codex = {columns = {{name = &#039;Thumbnail&#039;, source = &#039;thumbnail&#039;}, {name = &#039;Name&#039;, source = &#039;link&#039;}, {name = &#039;Type&#039;, source = &#039;Type&#039;}, {name = &#039;Summary&#039;, source = &#039;Summary&#039;}}, thumbnail_size = 48},&lt;br /&gt;
  Tags = {columns = {{name = &#039;Tag&#039;, source = &#039;link&#039;}, {name = &#039;Effect&#039;, source = &#039;Effect&#039;}, {name = &#039;Used by&#039;, source = &#039;Used by&#039;}}, thumbnail_size = 16},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Visible infobox fields per category — MUST match the rows declared in&lt;br /&gt;
-- Template:&amp;lt;Cat&amp;gt;_infobox.&lt;br /&gt;
local INFOBOX_FIELD_ORDER = {&lt;br /&gt;
  Weapons = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;, &#039;Class&#039;},&lt;br /&gt;
  Characters = {&#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Blessings = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Upgrades = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Passives = {&#039;Tier&#039;, &#039;Price&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Crux = {&#039;Type&#039;, &#039;Availability&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Enemies = {&#039;Classification&#039;, &#039;Stats&#039;},&lt;br /&gt;
  Achievements = {},&lt;br /&gt;
  Codex = {&#039;Type&#039;},&lt;br /&gt;
  Tags = {&#039;Effect&#039;, &#039;Used by&#039;},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Record (shallow copy) augmented with the computed infobox dict, so the&lt;br /&gt;
-- pure Module:Index renderer keeps working unchanged.&lt;br /&gt;
function p.augmented(cat, rec)&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for k, v in pairs(rec) do out[k] = v end&lt;br /&gt;
  out.infobox = p.computedInfobox(cat, rec)&lt;br /&gt;
  return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function resolveRec(cat, frame)&lt;br /&gt;
  local id = frame and frame.args and (frame.args.id or frame.args[1])&lt;br /&gt;
  if id then id = (tostring(id):gsub(&#039;^%s+&#039;, &#039;&#039;):gsub(&#039;%s+$&#039;, &#039;&#039;)) end&lt;br /&gt;
  if not id or id == &#039;&#039; then return nil, &#039;&#039; end&lt;br /&gt;
  local rec = p.byId(cat)[id]&lt;br /&gt;
  if not rec then&lt;br /&gt;
    return nil, mw.getCurrentFrame():preprocess(&lt;br /&gt;
      &#039;&amp;lt;i&amp;gt;Unknown &#039; .. cat .. &#039; record: &#039; .. mw.text.nowiki(id) .. &#039;&amp;lt;/i&amp;gt;&#039;)&lt;br /&gt;
  end&lt;br /&gt;
  return rec, nil&lt;br /&gt;
end&lt;br /&gt;
p.resolveRec = resolveRec&lt;br /&gt;
&lt;br /&gt;
local function asParam(v)&lt;br /&gt;
  if v == nil then return &#039;&#039; end&lt;br /&gt;
  return tostring(v)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderInfobox(cat, rec)&lt;br /&gt;
  local ib = p.computedInfobox(cat, rec)&lt;br /&gt;
  local out = {&#039;{{&#039; .. cat .. &#039;_infobox&#039;}&lt;br /&gt;
  out[#out + 1] = &#039;| name = &#039; .. asParam(rec.name)&lt;br /&gt;
  out[#out + 1] = &#039;| id   = &#039; .. asParam(rec.id)&lt;br /&gt;
  out[#out + 1] = &#039;| icon = &#039; .. asParam(rec.icon)&lt;br /&gt;
  for _, field in ipairs(INFOBOX_FIELD_ORDER[cat] or {}) do&lt;br /&gt;
    out[#out + 1] = &#039;| &#039; .. field .. &#039; = &#039; .. asParam(ib[field])&lt;br /&gt;
  end&lt;br /&gt;
  out[#out + 1] = &#039;}}&#039;&lt;br /&gt;
  return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderSectionBody(content)&lt;br /&gt;
  if content == nil or content == &#039;&#039; then return &#039;&#039; end&lt;br /&gt;
  if type(content) == &#039;table&#039; then&lt;br /&gt;
    if #content == 0 then return &#039;&#039; end&lt;br /&gt;
    local lines = {}&lt;br /&gt;
    for _, line in ipairs(content) do lines[#lines + 1] = &#039;* &#039; .. tostring(line) end&lt;br /&gt;
    return table.concat(lines, &#039;\n&#039;)&lt;br /&gt;
  end&lt;br /&gt;
  return tostring(content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ----- entry points used by the per-category shim modules -------------&lt;br /&gt;
&lt;br /&gt;
function p.infoboxEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(renderInfobox(cat, rec))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.bodyEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local section = frame and frame.args and frame.args.section&lt;br /&gt;
  if section and section ~= &#039;&#039; then&lt;br /&gt;
    return mw.getCurrentFrame():preprocess(&lt;br /&gt;
      renderSectionBody(p.bodySection(cat, rec, section)))&lt;br /&gt;
  end&lt;br /&gt;
  local out = {}&lt;br /&gt;
  for _, sec in ipairs(BODY_ORDER[cat] or {}) do&lt;br /&gt;
    local body = renderSectionBody(p.bodySection(cat, rec, sec))&lt;br /&gt;
    if body ~= &#039;&#039; then&lt;br /&gt;
      out[#out + 1] = &#039;&#039;&lt;br /&gt;
      out[#out + 1] = &#039;== &#039; .. sec .. &#039; ==&#039;&lt;br /&gt;
      out[#out + 1] = body&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(out, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.indexEntry(cat, frame)&lt;br /&gt;
  local Index = require(&#039;Module:Index&#039;)&lt;br /&gt;
  local recs = {}&lt;br /&gt;
  for _, r in ipairs(p.sorted(cat)) do&lt;br /&gt;
    recs[#recs + 1] = p.augmented(cat, r)&lt;br /&gt;
  end&lt;br /&gt;
  return Index.render(recs, INDEX_OPTS[cat] or {})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderEntry(cat, frame)&lt;br /&gt;
  local rec, missing = resolveRec(cat, frame)&lt;br /&gt;
  if missing then return missing end&lt;br /&gt;
  if not rec then return &#039;&#039; end&lt;br /&gt;
  local CrossRef = require(&#039;Module:CrossRef&#039;)&lt;br /&gt;
  local parts = {&lt;br /&gt;
    renderInfobox(cat, rec),&lt;br /&gt;
    p.bodyEntry(cat, frame),&lt;br /&gt;
    CrossRef.compute(cat, rec),&lt;br /&gt;
  }&lt;br /&gt;
  return mw.getCurrentFrame():preprocess(table.concat(parts, &#039;\n&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Data:Tags.json&amp;diff=2081</id>
		<title>Data:Tags.json</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Data:Tags.json&amp;diff=2081"/>
		<updated>2026-06-10T16:04:40Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
	&amp;quot;category&amp;quot;: &amp;quot;Tags&amp;quot;,&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;One record per tag (perk keyword). Fields: id (the raw tag key as used in other categories&#039; tags[] arrays, e.g. &#039;BreakArmor&#039;), slug (tag page title, always &#039;Tag-\u003CKey\u003E&#039;), name (= the key), icon (always empty — tags have no art), text (the effect text; empty when the game data ships none). Which weapons/heroes/blessings/upgrades/passives/enemies carry a tag is NOT stored here — Module:Core.tagMembership scans the other categories&#039; tags[] arrays at render time (rendered by Module:TagIndex). Consumed by Module:Core (tag links + effect text everywhere), Module:TagIndex, Module:CrossRef.&amp;quot;,&lt;br /&gt;
	&amp;quot;records&amp;quot;: [&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;1Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-1Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;1Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Armor&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Armor&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Armor&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ArmorToDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ArmorToDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ArmorToDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each 1% Armor increases Damage by 2%.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ArmorToDodge&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ArmorToDodge&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ArmorToDodge&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Negative Armor increases Dodge Chance by 4% per point. Positive Armor decreases it by 4% per point.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Aura&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Aura&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Aura&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;aura emitter&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Basic&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Basic&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Basic&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Big&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Big&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Big&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Boss&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Boss&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Boss&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BounceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-BounceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;BounceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Bounces on a Critical hit (max 4 times).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BreakArmor&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-BreakArmor&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;BreakArmor&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Breaks the enemy&#039;s Shell.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BurningGround&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-BurningGround&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;BurningGround&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Leaves a burning trail.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;CanRunWithItems&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-CanRunWithItems&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;CanRunWithItems&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Can run while carrying objects.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToAura&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToAura&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToAura&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon type to Aura. Range cannot exceed 800.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToElectric&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToElectric&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToElectric&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon elemental to Electric Damage.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToFire&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToFire&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToFire&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon elemental to Fire Damage.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToFromSky&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToFromSky&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToFromSky&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon type to Air Strike.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToLaser&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToLaser&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToLaser&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon type to Laser.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToPhysical&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToPhysical&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToPhysical&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon elemental to Holy Damage.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ChangeToVortex&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ChangeToVortex&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ChangeToVortex&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Change weapon type to Vortex Emitter.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ConsumeSoul&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ConsumeSoul&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ConsumeSoul&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Consumes one soul per second.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;CriticalStagger&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-CriticalStagger&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;CriticalStagger&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Critical hits cause heavy stagger.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Crux&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Crux&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Crux&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Unlocks Crux abilities.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;CruxDoubleSouls&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-CruxDoubleSouls&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;CruxDoubleSouls&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Souls consumed by the Crux are increased by 50%. Pickup Range is capped at 150.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;DecreeseSkillCooldown&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-DecreeseSkillCooldown&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;DecreeseSkillCooldown&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Reduces character skill cooldown after each kill.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Dodge&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Dodge&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Dodge&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;DodgeToDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-DodgeToDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;DodgeToDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each 1% Dodge increases Damage by 1%.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;DoubleAttack&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-DoubleAttack&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;DoubleAttack&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;50% chance to attack twice&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Electric&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Electric&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Electric&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;EveryXBulletDoCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-EveryXBulletDoCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;EveryXBulletDoCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Every third attack deals critical damage.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;EveryXBulletDoFrost&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-EveryXBulletDoFrost&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;EveryXBulletDoFrost&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Every third attack freeze an enemy.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;EveryXBulletDoStun&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-EveryXBulletDoStun&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;EveryXBulletDoStun&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Every third attack stuns an enemy.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExplodeOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExplodeOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExplodeOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;100% Explosion Chance on a Critical hit.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExtraAttackSpeedOverTime&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExtraAttackSpeedOverTime&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExtraAttackSpeedOverTime&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Increase Attack Speed over time (up to +100%).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExtraDmgOverTime&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExtraDmgOverTime&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExtraDmgOverTime&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Increase Damage over time (up to +100%)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExtraDmgSameType&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExtraDmgSameType&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExtraDmgSameType&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExtraDmgToBig&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExtraDmgToBig&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExtraDmgToBig&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;150% Damage to big enemies.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExtraDmgToMelee&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExtraDmgToMelee&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExtraDmgToMelee&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;150% Damage to melee enemies.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ExtraDmgToRange&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ExtraDmgToRange&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ExtraDmgToRange&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;150% Damage to range enemies.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Fire&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Fire&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Fire&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Flamethrower&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Flamethrower&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Flamethrower&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Ignites all enemies in a straight line within range.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;FreeRefresh&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-FreeRefresh&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;FreeRefresh&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Once per wave, you may refresh the prayers without spending Souls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;FromSky&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-FromSky&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;FromSky&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;air strike&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Grenade&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Grenade&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Grenade&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;grenade launcher&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;HP&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-HP&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;HP&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;HPSteal&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-HPSteal&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;HPSteal&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Hammer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Hammer&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Hammer&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;IncreaseCritOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-IncreaseCritOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;IncreaseCritOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Adds Critical Chance permanently on kill.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;IncreaseDmgOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-IncreaseDmgOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;IncreaseDmgOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Adds Damage permanently on kill (up to +50).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Lance&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Lance&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Lance&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Laser&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Laser&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Laser&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;laser weapon&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LockArmor&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-LockArmor&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;LockArmor&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Armor value is locked at 0.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LockHP&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-LockHP&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;LockHP&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Health value is locked at 80.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LockHPDelay&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-LockHPDelay&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;LockHPDelay&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Health regeneration starts immediately after taking damage (HP Delay is locked at 0).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LockHPRate&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-LockHPRate&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;LockHPRate&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Health regeneration is disabled (HP Rate is locked at 0).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;MainWeaponRefresh1&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-MainWeaponRefresh1&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;MainWeaponRefresh1&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each refresh increases the chance to offer a weapon you already carry by +30%.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;MainWeaponRefresh2&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-MainWeaponRefresh2&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;MainWeaponRefresh2&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each refresh increases the chance to offer a weapon you already carry by +60%.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Melee&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Melee&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Melee&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;melee weapon&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Mine&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Mine&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Mine&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;mine launcher&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;OnlyTier2&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-OnlyTier2&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;OnlyTier2&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Upgrades, Blessings, and Weapons offered in Prayers are limited to Tier II only, except weapons you already carry.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Overkill&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Overkill&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Overkill&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Overkill kills cause explosions.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Physical&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Physical&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Physical&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;PierceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-PierceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;PierceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Pierces on on every Critical hit.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Range&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Range&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Range&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;ranged weapon&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ReasourceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ReasourceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ReasourceOnCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Adds 0.1 Resource on a Critical hit.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ReasourceOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ReasourceOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ReasourceOnKill&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Adds 15% more Resources on kill.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ReduceCost1&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ReduceCost1&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ReduceCost1&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;All Prayers cost 4% less Souls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ReduceCost2&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ReduceCost2&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ReduceCost2&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;All Prayers cost 8% less Souls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;RefreshCost1&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-RefreshCost1&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;RefreshCost1&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each purchased Prayer reduces next Refresh cost by 5%.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;RefreshCost2&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-RefreshCost2&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;RefreshCost2&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each purchased Prayer reduces next Refresh cost by 10%.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Resurection&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Resurection&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Resurection&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Upon fatal damage, you are restored once per run.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SecondWind&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-SecondWind&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;SecondWind&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;A killing strike will not end you. Once per wave, you survive and gain brief invulnerability.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ShootInLine&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-ShootInLine&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ShootInLine&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Multiple projectiles fire in a straight line.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SkillIncreaseCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-SkillIncreaseCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;SkillIncreaseCrit&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each skill use increases weapon critical chance until the end of the wave.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SkillIncreaseDmg&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-SkillIncreaseDmg&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;SkillIncreaseDmg&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each skill use increases weapon damage until the end of the wave (up to +100%).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SkillIncreaseSpeed&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-SkillIncreaseSpeed&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;SkillIncreaseSpeed&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Each skill use increases weapon speed until the end of the wave (up to +100%).&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Speed&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Speed&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Speed&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;StartEnhancer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-StartEnhancer&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;StartEnhancer&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Your Main Weapon begins the crusade already consecrated with an Enhancer. It gives 30% more Damage and Range.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;StartSouls1&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-StartSouls1&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;StartSouls1&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Start each Crusade with +10 Souls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;StartSouls2&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-StartSouls2&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;StartSouls2&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Start each Crusade with +20 Souls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;StartSouls3&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-StartSouls3&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;StartSouls3&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Start each Crusade with +30 Souls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;StartWeaponTier2&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-StartWeaponTier2&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;StartWeaponTier2&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Start each Crusade by choosing a Tier II weapon from your unlocked arsenal.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Sword&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Sword&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Sword&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Tier3Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Tier3Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Tier3Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;Weapon merges can create Tier 3 weapons.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Vortex&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Tag-Vortex&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Vortex&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;text&amp;quot;: &amp;quot;vortex emitter&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	]&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
	<entry>
		<id>https://armsofgod.wiki.spellsandguns.com/index.php?title=Data:Codex.json&amp;diff=2080</id>
		<title>Data:Codex.json</title>
		<link rel="alternate" type="text/html" href="https://armsofgod.wiki.spellsandguns.com/index.php?title=Data:Codex.json&amp;diff=2080"/>
		<updated>2026-06-10T16:04:39Z</updated>

		<summary type="html">&lt;p&gt;Ta1ha: bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
	&amp;quot;category&amp;quot;: &amp;quot;Codex&amp;quot;,&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;One record per in-game encyclopedia entry. Every record: id (the game&#039;s internal id), slug (wiki page title of the entity&#039;s detail page; never recompute it), name (display name), icon (File: image name; empty string = no staged art). Plus: type (Enemies/Weapons/Events/Tips — drives index grouping and navbox rows), description (the full lore text), redirect_slug (present when the entry matched a weapon/enemy by name: slug then points at the ENTITY&#039;s page, which inlines this lore, and redirect_slug is the old &#039;\u003CX\u003E-Codex&#039; title kept as a redirect). Tips entries named after a stat (e.g. &#039;Holy Damage&#039;) are that stat&#039;s hub page. Module:Codex.lore joins entries to weapons/enemies by normalized name at render time. Consumed by Module:Core, Module:Codex, Module:Navbox, Module:Iconbox.&amp;quot;,&lt;br /&gt;
	&amp;quot;records&amp;quot;: [&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Azrael&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Azrael&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Azrael&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Azrael.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Azrael bears the name of the Angel of Death, once believed to guide souls beyond the mortal world. Now it stands deep within hell itself, guarding the path forward with terrifying endurance and overwhelming strength.\n- Double Fist Strike - Slams both fists into the ground with tremendous force. Targets caught between its arms can become trapped with little chance of escape.\n- Melee Attack - Performs a devastating close-range strike against nearby targets.\n- Grasping Swipe - Swings its arm in a wide arc, pulling nearby victims closer on contact.\n- Ranged Attack - Launches powerful attacks from a distance, forcing constant movement.\nAzrael possesses an enormous amount of health, making direct confrontation nearly impossible. Enduring the encounter until the Crux weakens and destroys him may be the only viable strategy.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Azrael-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Bishop&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Bishop&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Bishop&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Bishop.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;The bishop did not see the apocalypse as punishment, but as revelation. He allied himself with demons not as a traitor to faith, but as its most devoted executor.Bishop uses four distinct actions:\n\n- Melee Strike - a close-range attack performed with his staff when the player is nearby. Best avoided with a dodge.\n- Thrown Staff - a ranged melee attack triggered when the player keeps distance. The staff is thrown directly at the player. Dodging is required.\n- Range Attack - releases a spread of ranged projectiles. Circling around the boss is the safest way to avoid damage.\n- Teleport - Bishop teleports directly to the player’s position and immediately performs a melee strike.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Bishop-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Grenader&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Blastfiend&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Blastfiend&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Grenader.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A demonic saboteur that lobs explosives across the battlefield, forcing its enemies into constant movement.\n\nThese enemies deal heavy damage and can make navigation difficult. Their projectiles predict player movement, so frequently changing direction while running is key. Prioritizing them in battle can significantly improve survivability.\n\nOn higher difficulty levels, it can perform a wide swing attack when its victim gets too close.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Blastfiend-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Jumper&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Boneleaper&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Boneleaper&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Jumper.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Leaps at its prey with terrifying speed, shattering the ground and sending bone shards flying in all directions.\n\nThey can attack from extreme distances, making dodging or using stagger-inducing weapons to interrupt their jump attacks a necessity.\n\nOn higher difficulty levels, it releases a wave of hellish energy that damages everything in its path.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Boneleaper-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Summoner&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Broodcaller&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Broodcaller&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Summoner.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;An abomination that calls forth swarms of Doomspore to overwhelm its enemies in waves of destruction.\n\nSince the Broodcaller can spawn spore mines, the best strategy is to eliminate it first before it summons too many.\n\nOn higher difficulty levels, when its victim gets too close, it summons two large spores beside itself.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Broodcaller-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BrotherA&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Brother_Cassian&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Brother Cassian&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_BrotherA.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Once a devoted member of the Blood Brotherhood, Brother Cassian gave himself willingly to forbidden rituals deep beneath the cathedral. The blood of demons granted him monstrous strength, but whatever humanity remained within him was long ago consumed by madness and rage. Now he stands as a grotesque servant of the abyss, guarding the path forward.\n\n- Melee Attack - Performs two brutal close-range attacks that heavily damage nearby targets.\n- Whirlwind Attack - Spins repeatedly while moving across the battlefield, striking enemies multiple times.\n- Hellwave - Releases a demonic wave of energy that tears through everything in its path.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Brother_Cassian-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BrotherC&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Brother_Severin&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Brother Severin&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_BrotherC.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;The corruption inside Brother Severin burns hotter than in the others. Fueled by endless aggression and demonic blood, he attacks with savage fury, giving his enemies no time to recover or escape.\n\n- Melee Attack - Performs brutal close-range attacks that damage nearby targets.\n- Whirlwind Attack - Spins across the battlefield, striking enemies multiple times while advancing forward.\n- Hellwave - Unleashes a wave of demonic energy that devastates everything in its path.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Brother_Severin-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BrotherB&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Brother_Tharos&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Brother Tharos&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_BrotherB.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Brother Tharos was once the shield of the Blood Brotherhood, an unbreakable warrior who protected the order during its darkest rituals. The corruption within him hardened both flesh and faith, transforming him into a relentless executioner clad in monstrous armor and carrying a massive shield.\n\n- Melee Attack - Performs a wide close-range strike against nearby targets.\n- Hellwave - Releases a sinister wave of demonic energy that cannot be stopped.\n- Charge Attack - Charges directly toward his victim and unleashes a devastating strike upon reaching close range.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Brother_Tharos-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Exploder&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Detonant&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Detonant&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Exploder.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A gaunt, fast-moving fiend with a pulsating stomach. It rushes toward its prey with reckless abandon, detonating in a violent explosion upon impact.\n\nThese enemies explode upon close contact with the player. An effective counter is using abilities that grant temporary invulnerability. Executing a dodge or blink at the right moment will cause Detonant to explode without dealing damage.\n\nOn higher difficulty levels, it gains the ability to roll forward. While rolling, it rapidly gains speed but cannot change direction. If it collides with its victim, it explodes.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Detonant-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Astrobane&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Doom_Herald&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Doom Herald&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Astrobane.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A harbinger of destruction, far more powerful than lesser demons. Its arrival signals an impending massacre.\nHe uses three distinct attack patterns:\n\n- Melee Strike - a close-range attack triggered when the player approaches.\n- Ranged Barrage - launches three projectiles toward the player’s predicted position. Changing movement direction is required to avoid the impact.\n- Leap Attack - the boss may briefly retreat to create distance, then perform a powerful jumping strike toward the player’s location.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Doom_Herald-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Walker&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Dreadstalker&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Dreadstalker&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Walker.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A relentless hunter that never ceases its pursuit. Its cold, hollow eyes are the only warning before inevitable death.\n\nThese enemies appear in large groups, making area-of-effect weapons like mine launchers, grenade launchers, explosive weapons, and melee weapons the best choice for clearing them out efficiently.\n\nOn higher difficulty levels, it gains the ability to launch projectiles.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Dreadstalker-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Healer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Fleshweaver&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Fleshweaver&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Healer.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Extends every battle, reviving the fallen and keeping the hordes in the fight.\n\nThey grant healing and a &#039;stone skin&#039; effect, greatly increasing enemy durability. Eliminating them first will prevent prolonged battles.\n\nOn higher difficulty levels, it explodes on contact, dealing heavy damage in a large area.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Fleshweaver-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Ranger&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Harrowfiend&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Harrowfiend&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Ranger.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Circles its prey like a vulture, raining deadly projectiles from a distance.\n\nThese enemies keep their distance from the player while firing projectiles. Long-range weapons are the most effective way to eliminate them without having to get too close.\n\nOn higher difficulty levels, it gains the ability to become invisible, disappearing for a few seconds and becoming immune to attacks.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Harrowfiend-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Runner&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Hellhound&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Hellhound&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Runner.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A demonic sprinter that lunges at its prey with savage force, tearing through anything in its way.\n\nThey attack in packs, so using wide-area weapons is essential. When they charge, dodge sideways instead of running straight ahead or directly towards them, as this will minimize the risk of getting hit.\n\nOn higher difficulty levels, it gains the ability to empower itself, increasing its health, attack power, and movement speed. It is best to eliminate them before that happens.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Hellhound-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Duramen&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Ironmaw&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Ironmaw&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Duramen.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Nearly indestructible, an unyielding wall of flesh and fury. They can perform a deadly charge, attacking three times in quick succession.\n\nAlways dodge to the side rather than running directly away or toward them. Weapons with high stagger values are useful for stopping their charge before it becomes lethal.\n\nOn higher difficulty levels, it gains the ability to summon hellish spikes beneath its victim. The spikes cannot be destroyed and block everything in their path, but disappear after a few seconds.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Ironmaw-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;FireDemon&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Malach&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Malach&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_FireDemon.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;An ancient guardian bound to the gates of hell. Fire bends to its will, sealing the path for all who are unworthy to descend deeper into the abyss.\n- Melee Attack - Performs a massive sweeping claw strike capable of devastating nearby targets.\n- Flame Eruption - Ignites the ground ahead, causing streams of lava to burst from beneath the battlefield. Avoid contact with the burning fissures.\n- Summon Servants - Summons burning skeletal servants that quickly spread across the arena if not eliminated immediately.\n- Charge Strike - Performs a short charge followed by a wide arm swing.\n- Stomp - Slams its foot into the ground, damaging everything in a large area around it.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Malach-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Raziel&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Raziel&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Raziel&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Raziel.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Raziel, the keeper of knowledge and divine secrets, stands as the final guardian deep within hell. Unlike the demons below, his presence carries something far more unsettling - a power that feels undeniably holy.\n- Melee Attack - Performs a massive sweeping arm strike capable of devastating nearby targets.\n- Stomp - Slams the ground with tremendous force, damaging everything around him.\n- Toxic Cloud - Releases a poisonous cloud that inflicts Madness on everything within it while summoning Harrowfiends to aid him in battle.\n- Hell Rings - Activates rings filled with hellish energy that damage anything entering their radius. The rings also fire projectiles toward nearby victims.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Raziel-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Chaser&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Ruinborn&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Ruinborn&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Chaser.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Its reckless charges crush everything in its path. Once it locks onto a target, it won&#039;t stop until there&#039;s nothing left.\n\nBe mindful of their speed-if your character is too slow or if Ruinborn is buffed by Wretchchant, it may outrun you. Avoid such situations at all costs. Staggering weapons can be highly effective in halting their relentless assault.\n\nOn higher difficulty levels, it gains a new attack, performing a spinning strike that damages nearby targets multiple times.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Ruinborn-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Bull&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;The_Beast&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;The Beast&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Bull.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A towering abomination consumed by rage and blind devotion. Once human, now twisted into a horned creature resembling a demonic bull, it guards the cathedral gates as a living symbol of the Church’s corruption. Every step shakes the ground beneath it, and its fury leaves little room for hesitation.\n\n- Melee Attack - swings its massive arm in a wide arc, capable of striking multiple targets at once. Keeping your distance or using a well-timed dodge is the safest way to avoid the blow.\n- Charge Attack - lowers its horns and charges forward at tremendous speed. The best chance to avoid the attack is to quickly change direction as soon as the charge begins.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;The_Beast-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Buffer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Wretchchant&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Wretchchant&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Buffer.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Enemies&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Its eerie incantations empower nearby demons, making them even deadlier.\n\nThese enemies grant buffs that increase the speed, aggression, and damage of their allies. Since they have relatively low health, taking them out quickly is crucial.\n\nOn higher difficulty levels, it can jump in place and damage nearby targets, as well as spit acid from a distance.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Wretchchant-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;AegisCycler&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Aegis_Cycler&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Aegis Cycler&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_AegisCycler.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;After being upgraded, gains the Overkill ability. Excess damage remaining after killing an enemy explodes, damaging nearby enemies. Overkill triggers only if the remaining damage exceeds 50% of the enemy’s maximum health. Investing in high damage is key to fully utilizing this weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Aegis_Cycler-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Annihilator&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Annihilator&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Annihilator&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Annihilator.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Grants an additional projectile and a chance to stun enemies. Scales exceptionally well with percentage-based damage upgrades, making it an excellent support weapon for fast-firing builds. When converted into an upgrade, it provides a large amount of flat damage and bonus explosion chance.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Annihilator-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;AutoCannon&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Auto_Cannon&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Auto Cannon&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_AutoCannon.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Auto Cannon permanently gains Critical Damage chance each time it kills an enemy, making it a weapon that benefits greatly from being acquired early in a run.\nAdding flat damage from other weapons increases its kill potential, which in turn accelerates critical chance growth. Modules that provide bonuses on critical hits are especially effective. AutoCannon is also an ideal merge candidate to boost Critical Damage on another weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Auto_Cannon-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Blaster&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Blaster&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Blaster&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Blaster.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A fire-based weapon that has an additional projectile and Pierce by default, making it an ideal candidate for Bounce upgrades.\nBounce significantly multiplies the effectiveness of both Projectiles and Pierce, allowing Blaster to cover a wide area and hit multiple enemies efficiently.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Blaster-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BlazingCrossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Blazing_Crossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Blazing Crossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_BlazingCrossbow.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Blazing Crossbow has high damage and grants additional resources on enemy kills, making it a strong weapon to invest in. The earlier it is acquired during a run, the more value it provides.\nUpgrading damage is especially effective, as securing kills on hit increases the chance of gaining extra resources. Due to its high damage scaling, Blazing Crossbow is also a strong merge candidate in the late waves, providing a significant damage boost when merged into another weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Blazing_Crossbow-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Handshooker&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Chain_Bolt&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Chain Bolt&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Handshooker.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Chain Bolt can trigger electric explosions on critical hits, making Critical Chance a high-priority stat. As a Vortex Emitter weapon, it benefits greatly from Pierce and Range, which increase coverage and enable stronger chain reactions.\nWhen merged into another weapon, increases explosion radius, enhancing area damage and crowd control potential.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Chain_Bolt-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Detonator&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Detonator&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Detonator&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Detonator.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A Grenade Launcher–type weapon, making it a key tool for crowd control. Detonator provides high damage and a large explosion radius, allowing it to affect multiple enemies at once.\nEven if its damage output is not heavily upgraded, Detonator remains effective when focused on control upgrades such as Stun, Freeze, and Stagger, which can significantly slow or stop enemy advances.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Detonator-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Electrohammer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Electrohammer&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Electrohammer&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Electrohammer.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A heavy melee weapon that performs wide swing attacks, dealing damage over a large area. In its upgraded form, Electrohammer generates an additional shockwave that damages enemies in an area scaling with Range.\nDue to its high base damage, Electrohammer is an excellent upgrade candidate and can later be merged into a faster weapon for a significant damage boost, even in builds that do not primarily focus on melee combat.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Electrohammer-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Igniteron&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Firestorm&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Firestorm&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Igniteron.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Firestorm is a weapon that always hits its target and can trigger explosions. It benefits most from increasing Explosion Chance and Explosion Radius.\nDue to its strong area-of-effect damage, Firestorm does not rely heavily on raw damage upgrades. Instead, it excels when focused on crowd control, making upgrades like Stagger, Stun, and Freeze especially effective.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Firestorm-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Minigun&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Gatling&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Gatling&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Minigun.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;The fastest weapon available in the Templar arsenal. Gatling accelerates further during a run, increasing its attack speed over time. Even when acquired later, it is an excellent candidate for adding flat damage from another weapon. Due to its extreme speed, this can result in a massive damage per second increase.\nGatling is highly effective against single targets, but upgrades to Projectiles, Pierce, and Bounce can also make it very strong at clearing groups of enemies.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Gatling-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;HeavenfallIncinerator&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Heavenfall_Incinerator&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Heavenfall Incinerator&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_HeavenfallIncinerator.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains the ability to ignite the ground on explosion, creating a burning area. Increasing Explosion Chance and Explosion Size significantly enhances the effect of the ignited ground. As a fast-firing weapon, it benefits strongly from flat damage bonuses. A strong strategy is transferring high flat damage and explosion chance from the Detonator weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Heavenfall_Incinerator-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Hellblade&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Hellblade&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Hellblade&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Hellblade.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A fire-based melee weapon that alternates between a wide slash and a forward thrust. Each attack has a chance to trigger a double slash or double thrust, effectively repeating the same attack in quick succession.\nEvery attack also generates a shockwave, with its range scaling based on the Range stat.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Hellblade-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;InfernalCrossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Infernal_Crossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Infernal Crossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_InfernalCrossbow.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;In its upgraded form, it transforms into a Vortex-type weapon, making it scale extremely well with Pierce upgrades. Thanks to its high base damage, it is an excellent candidate for percentage-based damage scaling before being converted into flat damage for another weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Infernal_Crossbow-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Infernoblade&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Infernoblade&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Infernoblade&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Infernoblade.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A fire-based melee weapon that alternates between slashing and thrusting attacks. Infernoblade applies Fire Damage, allowing it to inflict Burning on enemies hit.\nThe upgraded version generates an additional shockwave attack. The shockwave pierces all enemies in its path but does not use Bounce.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Infernoblade-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Infernothrower&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Infernothrower&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Infernothrower&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Infernothrower.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;After being upgraded, increases its projectile count, projectile speed, and damage. It strongly benefits from receiving flat damage transferred from other weapons. Due to its high firing speed and straight-line projectiles, it is especially effective against bosses.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Infernothrower-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;IonRail&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Ion_Rail&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Ion Rail&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_IonRail.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;After being upgraded, gains the Overkill ability. Excess damage remaining after killing an enemy explodes, damaging nearby enemies. Overkill triggers only if the remaining damage exceeds 50% of the enemy’s maximum health. Investing in high damage is key to fully utilizing this weapon. An excellent upgrade candidate that can later be merged into a faster weapon, granting a significant flat damage boost.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Ion_Rail-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Lance&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Lance&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Lance&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Lance.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A forward-thrusting weapon designed to hit enemies in a straight line, making it highly effective at eliminating enemies aligned in rows. Lance deals high base damage and excels at direct, focused attacks.\nWhen upgraded and used as a merge component for another weapon, its high damage provides a significant damage boost, especially due to the additional Pierce it grants.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Lance-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LaserRifle&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Laser_Rifle&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Laser Rifle&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_LaserRifle.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A laser-type weapon, meaning its shots pierce all enemies in a straight line. Additional Bounce and Projectiles can significantly increase its effectiveness and coverage.\nIf focused mainly on damage upgrades, Laser Rifle becomes a strong merge candidate for fast-firing weapons, providing a high flat damage boost.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Laser_Rifle-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LightBeam&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Light_Beam&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Light Beam&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_LightBeam.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Transforms the weapon into a laser-based weapon, allowing it to instantly pierce all enemies. Due to its ability to reflect projectiles on critical hits, investing in Critical Chance is highly effective. Increasing the Projectile Count further enhances its performance.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Light_Beam-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Sharpshooter&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Longshot&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Longshot&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Sharpshooter.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Longshot gains additional value from Critical Damage chance, as its projectiles bounce when a critical hit occurs.\nThis weapon has very high Range, making it an excellent candidate for conversion into a Laser or Vortex Emitter, where its range can be fully utilized for maximum effectiveness.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Longshot-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Megacharger&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Megacharger&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Megacharger&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Megacharger.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A high-damage electric weapon with an extremely high life leech value. Megacharger serves as a strong base for upgrades that increase the number of enemies hit, such as Pierce, Bounce, and Projectiles.\nDue to its powerful life leech, Megacharger is an excellent merge candidate for any weapon, providing a substantial survivability boost regardless of elemental type.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Megacharger-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Overdetonator&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Overdetonator&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Overdetonator&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Overdetonator.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains the ability to ignite the ground on explosion, creating a burning area. Increasing Explosion Chance and Explosion Size significantly enhances the effect of the ignited ground. Its explosion chance exceeds 100%, allowing it to benefit from upgrades that reduce explosion chance in exchange for additional bonuses such as Bounce. Due to its high explosion chance, it is an ideal upgrade donor, granting other weapons the ability for projectiles to explode.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Overdetonator-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Railgun&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Railgun&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Railgun&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Railgun.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A laser-type weapon, meaning its shots pierce all enemies in a straight line. Additional Bounce and Projectiles can greatly increase its effectiveness.\nWhen primarily upgraded for damage, Railgun becomes a strong merge candidate for fast-firing weapons, providing a high flat damage boost. Even without high damage investment, Railgun remains useful for crowd control, as its electric damage slows enemies.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Railgun-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SanctumPike&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Sanctum_Pike&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Sanctum Pike&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_SanctumPike.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Performs a long-range thrust attack. Each attack has a chance to trigger a double thrust, with each hit generating a shockwave whose range scales with the Range stat.\nAn excellent upgrade candidate that can later be merged into a faster weapon, granting a significant flat damage boost.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Sanctum_Pike-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Shockstring&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Shockstring&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Shockstring&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Shockstring.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains a chance to trigger a double attack, which synergizes extremely well with upgrades that increase Projectile Count and Bounce, making those upgrades twice as effective compared to most other weapons.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Shockstring-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Shotgun&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Shotgun&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Shotgun&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Shotgun.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A weapon that fires a large number of projectiles, making it an ideal candidate for Bounce and Pierce upgrades. Shotgun does not have very high base damage, so it benefits greatly from merging with weapons that provide high flat damage.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Shotgun-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Electrocuter&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Skystrike&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Skystrike&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Electrocuter.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;An Air Strike–type weapon whose projectiles chain to additional enemies upon impact. Because Air Strike attacks always hit their targets, Electrocuter pairs exceptionally well with its perk that grants additional resources on enemy kills.\nThis makes it a strong choice for improving run economy, especially when reliable kill confirmation is important.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Skystrike-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SolarRifle&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Solar_Rifle&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Solar Rifle&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_SolarRifle.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;After being upgraded, gains the Overkill ability. Excess damage remaining after killing an enemy explodes, damaging nearby enemies. Overkill triggers only if the remaining damage exceeds 50% of the enemy’s maximum health. Investing in high damage is key to fully utilizing this weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Solar_Rifle-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Deflectotron&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Spinshot&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Spinshot&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Deflectotron.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A Vortex Emitter weapon that gains the most benefit from increasing Pierce. Improving Pierce allows projectiles to sustain longer vortex interactions and create more effective chain reactions.\nThis weapon also synergizes well with Critical Damage chance and upgrades that trigger additional effects on critical hits, such as bonus Pierce. Due to its weapon type, Range plays a crucial role in maximizing coverage and damage potential.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Spinshot-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Stormbreaker&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Stormbreaker&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Stormbreaker&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Stormbreaker.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Performs a wide slashing attack. Each attack has a chance to trigger a double slash, with each hit generating a shockwave whose range scales with the Range stat.\nAn excellent upgrade candidate that can later be merged into a faster weapon, granting a significant flat damage boost.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Stormbreaker-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Stormbringer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Stormbringer&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Stormbringer&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Stormbringer.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains the ability to freeze enemies. Investing in Critical Chance and Explosion Size significantly improves its effectiveness. As a Vortex-type weapon, it benefits heavily from the Pierce stat. A strong crowd control option that does not require high damage to be effective.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Stormbringer-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Stormfall&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Stormfall&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Stormfall&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Stormfall.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains a chance to trigger a double attack, which synergizes exceptionally well with upgrades that increase Projectile Count and Bounce, making those upgrades twice as effective compared to most other weapons.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Stormfall-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SuperBlaster&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Super_Blaster&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Super Blaster&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_SuperBlaster.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains a chance to trigger a double attack, which synergizes extremely well with upgrades that increase Projectile Count and Bounce, making those upgrades twice as effective compared to most other weapons.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Super_Blaster-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SuperShotgun&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Super_Shotgun&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Super Shotgun&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_SuperShotgun.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Projectiles from the highest-tier shotgun gain the ability to explode on critical hits. Investing in Critical Chance and Explosion Size significantly increases its effectiveness, making it an ideal crowd control weapon when combined with a high projectile count.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Super_Shotgun-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Teslabringer&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Teslabringer&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Teslabringer&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Teslabringer.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gains additional bounces, causing deployed mines to split into additional mines. Since the mines seek nearby enemies and detonate on contact, this weapon is well suited for applying slow effects over a large area.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Teslabringer-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Lightinger&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Thunder_Crossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Thunder Crossbow&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Lightinger.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A very fast electric weapon with life leech. Thunder Crossbow gains significant value when merged with weapons that provide high flat damage, allowing its speed and life leech to scale effectively.\nWithout additional upgrades or merge bonuses, the weapon may feel underpowered compared to other options.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Thunder_Crossbow-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;TrinityBlast&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Trinity_Blast&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Trinity Blast&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_TrinityBlast.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Generates resources on critical hits. After being upgraded, gains a high chance to stun enemies. Since stunned enemies always receive critical damage, investing in Bounce allows ricocheted projectiles to reliably hit already-stunned targets, resulting in additional resource generation.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Trinity_Blast-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;TwinBlast&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;TwinBlast&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Twin Blast&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_TwinBlast.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;TwinBlast gains significant value when upgraded from a Machine Gun that provides Critical Chance on kill. In its upgraded form, critical hits grant additional resources, making Critical Damage a high-impact stat.\nEven without heavy damage investment, increasing Critical Damage can make TwinBlast a strong choice for improving resource economy during a run.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Twin_Blast&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Ubercharger&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Ubercharger&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Ubercharger&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Ubercharger.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Increases its Life Leech chance and gains an additional Bounce, while also acquiring the ability to freeze enemies. It works well as a base weapon for stacking Projectile Count and Bounce due to its very high Life Leech value, or as a single Life Leech upgrade when converted for another weapon.&amp;quot;,&lt;br /&gt;
			&amp;quot;redirect_slug&amp;quot;: &amp;quot;Ubercharger-Codex&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;BloodChalice&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Blood_Chalice&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Blood Chalice&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_BloodChalice.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A colossal biomechanical chalice deploys onto the battlefield and establishes a sanctified execution zone. Only demons slain within its radius are converted into sacrificial fuel. Once the vessel reaches saturation, the Order extracts condensed relic fragments forged from demon ichor. The higher the wave, the greater the blood tithe required.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Chest&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Chest&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Chest&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Chest.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A container that grants Shards when opened. Shards is a persistent resource used in the Cathedral to unlock new upgrades. Shards collected during runs is carried over between acts and is not lost on death.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;CorruptedShards&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Corrupted_Shards&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Corrupted Shards&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_CorruptedShards.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Corrupted Shards grow within the remains of the damned - embedded in fused human bones like a blasphemous crystal garden. When the shrine is disturbed, toxic miasma erupts across the battlefield, twisting perception and reversing movement under its suffocating influence. Endure the corruption and the Shards can be claimed, tainted but powerful.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;PathOfTheCrux&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Path_of_the_Crux&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Path of the Crux&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_PathOfTheCrux.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;The banners of the Stations stand abandoned amid ruin and ash. Recover each one to retrace the path of suffering. Completing the procession affirms the crusader’s vow and invokes Edicts issued by the Order as a reward for unwavering faith.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;PowerRelay&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Power_Relay&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Power Relay&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_PowerRelay.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A volatile sanctified core pulses with unstable energy. While carried, movement efficiency decreases due to mass and radiation output. Once installed into the relay device, it unleashes periodic arcs of consecrated lightning that scour nearby enemies. Successful activation yields refined energy cores suitable for future enhancement protocols.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ResurrectionSpire&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Resurrection_Spire&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Resurrection Spire&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_ResurrectionSpire.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;A hell-forged spire of bone and iron anchors itself into the battlefield, reanimating corpses into skeletal servants. The longer it stands, the more undead it weaves from fallen remains. Its destruction disrupts the resurrection field and releases sealed relic fragments forged within its ossuary core.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SermonOfRevelation&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Sermon_of_Revelation&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Sermon of Revelation&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_SermonOfRevelation.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Torn scripture pages lie scattered near the abandoned pulpit, remnants of a sermon interrupted by the apocalypse. Each recovered fragment restores a piece of forbidden doctrine. Once reassembled, the Revelation unlocks new paths within the Cathedral archives - revealing previously sealed Blessings, Weapons, or structural Upgrades for future crusades.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Shrine&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Shrine&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Shrine&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Shrine.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Shrine grants a temporary Blessing when activated. The Blessing modifies all character stats until the Crux unleashes its explosion at the end of the current Crusade. Once the Crusade ends, the Blessing is removed.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;SignalArray&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Signal_Array&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Signal Array&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_SignalArray.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Events&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Mid-crusade, a signal array activates to transmit encrypted battlefield data to the Cathedral network. The device remains exposed during synchronization. If it survives the full transmission cycle, the Order compensates the crusader with recovered energy cores extracted from the completed data stream.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;AirStrike&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Air_Strike&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Air Strike&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_AirStrike.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Air Strike weapons launch a projectile into the air that then descends onto an enemy. The projectile is guided and always hits its target.\nAir Strike projectiles cannot use Pierce, but they can benefit from Bounce and Projectiles. This weapon type is especially effective against enemies positioned behind the front line, and explosion effects tend to land in the center of enemy groups.\nAir Strike weapons always break enemy shells.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;AuraEmitter&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Aura_Emitter&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Aura Emitter&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_AuraEmitter.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Damages all enemies within the weapon’s area of effect. Cannot benefit from Bounce or Pierce. Increasing Projectile Count causes the aura to trigger an additional time. Highly effective for crowd control and holding back melee enemies, with Stagger being a key stat for stopping close-range attackers. Less effective against ranged enemies.\nFor Aura Emitter weapons, Range upgrades are applied with a 25% multiplier, but Range remains a key stat for increasing overall effectiveness.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Bounce&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Bounce&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Bounce&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Bounce.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Bounce determines how many times a single projectile can ricochet after hitting an enemy. Each projectile has its own Bounce value, meaning weapons that fire multiple projectiles track bounces separately for each one. A bounced projectile almost always targets the next nearby enemy.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;CriticalDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Critical_Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Critical Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_CriticalDamage.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Critical Damage represents the chance to deal double damage on hit and cannot exceed 100%. When a critical hit occurs, the attack deals 2x damage. Many upgrades and weapon effects have synergies with Critical Damage and trigger additional effects when a critical hit is applied.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;ElectricDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Electric_Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Electric Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_ElectricDamage.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Electric Damage applies Shock. Shocked enemies are slowed for a short duration. The strength of the slow effect scales with the amount of damage dealt by the attack.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Explosion&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Explosion&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Explosion&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Explosion.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Both projectiles and melee hits can trigger an Explosion. Explosions are defined by two values: Explosion Chance, which determines the probability of an explosion occurring, and Explosion Radius, which defines the size of the blast area.\nFor melee attacks, only the first enemy hit can trigger the explosion effect.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;FireDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Fire_Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Fire Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_FireDamage.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Fire Damage applies Burning. Burning deals damage over time based on the weapon’s damage. A fire weapon effectively deals its damage twice: once on hit, and once spread over time through the Burning effect.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Frost&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Frost&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Frost&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Frost.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Frost applies Freeze, causing enemies to immediately stop their current actions. Killing a frozen enemy triggers an explosion with a 3-meter radius, dealing damage to nearby enemies based on the frozen enemy’s maximum health.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;GameplayTips&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Gameplay_Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Gameplay Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_GameplayTips.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;This section contains gameplay tips and explanations of core mechanics. New entries are unlocked automatically as you progress through the game and discover new systems, upgrades, weapons, or interactions. If something new appears during a run, you will find a detailed explanation here after unlocking the corresponding achievement.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;GrenadeLauncher&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Grenade_Launcher-Codex&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Grenade Launcher&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_GrenadeLauncher.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Grenade Launcher weapons have a very high chance to trigger explosions. Their projectiles travel in a parabolic arc and do not hit enemies along the way. A projectile explodes only upon hitting the ground, making this weapon type especially effective against grouped enemies.\nWith Bounce, the projectile triggers an additional bounce after the explosion, redirecting toward enemies. Due to their nature, grenade projectiles cannot use Pierce.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;HolyDamage&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Holy_Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Holy Damage&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_HolyDamage.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Holy Damage does not apply any special status effects. However, weapons that deal Holy Damage are generally stronger and often include base Critical Damage. Their projectiles are faster than those of other elemental types. For this reason, builds using Holy Damage benefit most from focusing on Critical Damage synergies.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;LaserWeapon&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Laser_Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Laser Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_LaserWeapon.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Laser weapons fire an extremely fast beam that always hits its targets and pierces all enemies in a straight line, up to the limit defined by Range.\nWhen Bounce is applied, the laser splits at the first enemy hit, creating an additional beam that continues to damage enemies along its path.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;MeleeWeapon&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Melee_Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Melee Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_MeleeWeapon.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Melee weapons are close-range weapons that always pierce all enemies within their hitbox. The size of the hitbox scales with the Range stat. For melee weapons, Range upgrades are applied with a 25% multiplier, but Range remains a key stat for increasing effectiveness.\nMelee weapons cannot use Bounce. Increasing Projectiles causes melee weapons to emit an additional shockwave attack.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;MergeWeapons&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Merge_Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Merge Weapons&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_MergeWeapons.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Weapons can be merged to create more powerful versions. Most commonly, combining weapons of the same tier results in a new weapon of a higher tier. All discovered weapons and their merge results are permanently recorded in the Weapons section of the Codex.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;MineLauncher&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Mine_Launcher&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Mine Launcher&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_MineLauncher.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Mine Launcher weapons deploy proximity mines that detect nearby enemies and explode, meaning they always hit a target. Mines cannot use Pierce.\nWhen Bounce is applied, a mine splits into an additional mine upon hitting the ground, increasing area coverage and damage potential.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Pierce&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Pierce&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Pierce&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Pierce.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Pierce allows a projectile to pass through enemies. The Pierce value determines how many enemies a single projectile can penetrate.\nIf a projectile also has Bounce and is reflected off an enemy, it splits into two projectiles:\n- One continues forward, following its remaining Pierce value.\n- The second bounces toward another enemy and also retains its Pierce value, allowing it to penetrate enemies as well.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Projectiles&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Projectiles&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Projectiles&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Projectiles.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Projectiles define how many shots a weapon can fire. Each projectile has its own independent stats, meaning effects like Bounce and Pierce are calculated separately for every projectile.\nWhen added to a melee weapon, Projectiles create a shockwave attack. The shockwave does not bounce but pierces all enemies in its path.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;RangeWeapon&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Ranged_Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Ranged Weapon&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_RangeWeapon.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Ranged weapons are the most common weapon type and fire projectiles toward enemies. When aiming, the projectile trajectory is calculated to hit the target. In some situations, a projectile may still miss.\nRanged weapons can bounce, pierce, and fire multiple projectiles, allowing for a wide variety of projectile-based synergies.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Stagger&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Stagger&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Stagger&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Stagger.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Stagger disrupts an enemy’s balance and interrupts their movement. There are three stagger levels based on the Stagger value:\n- Light Stagger (1)\n- Medium Stagger (2)\n- Heavy Stagger (3)\nOnly Heavy Stagger can interrupt an enemy’s attack animation. Stagger values higher than 3 do not increase the level, but extend the duration of the stagger effect.\nBoss enemies are immune to Stagger.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;Stun&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Stun&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Stun&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_Stun.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Stun is a special status that prevents an enemy from performing any actions. The duration of Stun scales with the damage dealt. While stunned, enemies always receive critical damage. This effect is most effective when combined with modules and upgrades that trigger additional effects on critical hits.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;VortexEmitter&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Vortex_Emitter&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Vortex Emitter&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_VortexEmitter.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Vortex Emitter weapons fire projectiles that travel based on their Range value to a target point, where they begin to orbit in a circular pattern, gradually reducing their radius until they expire.\nWhen a projectile hits an enemy and has Pierce, it passes through the target and reverses its orbit direction. Pierce and Range are critical stats for this weapon type, as they can create chain reactions that damage multiple enemies. Bounce and Projectiles also apply and further increase coverage and damage potential.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;id&amp;quot;: &amp;quot;WeaponPerks&amp;quot;,&lt;br /&gt;
			&amp;quot;slug&amp;quot;: &amp;quot;Weapon_Perks&amp;quot;,&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;Weapon Perks&amp;quot;,&lt;br /&gt;
			&amp;quot;icon&amp;quot;: &amp;quot;Codex_WeaponPerks.png&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;Tips&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Perks marked with a star (*) are unique weapon effects that do not stack with other perks of the same type, even if they provide percentage-based bonuses. Weapon Perks are also not transferred during weapon merging. After merging, the newly created weapon uses its own set of perks and effects.&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	]&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Ta1ha</name></author>
	</entry>
</feed>