<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?action=history&amp;feed=atom</id>
	<title>Module:Sandbox/Monster Domosed/AbilityTable - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?action=history&amp;feed=atom"/>
	<link rel="alternate" type="text/html" href="https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?action=history"/>
	<updated>2026-09-11T05:03:30Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?diff=146628&amp;oldid=prev</id>
		<title>Monster Domosed at 00:12, 11 September 2026</title>
		<link rel="alternate" type="text/html" href="https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?diff=146628&amp;oldid=prev"/>
		<updated>2026-09-11T00:12:06Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw-interface=&quot;&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 00:12, 11 September 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l3&quot;&gt;Line 3:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 3:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local Lists     = require(&amp;quot;Module:AbilityTable/Lists&amp;quot;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local Lists     = require(&amp;quot;Module:AbilityTable/Lists&amp;quot;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local Notes     = require(&amp;quot;Module:AbilityTable/Notes&amp;quot;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local Notes     = require(&amp;quot;Module:AbilityTable/Notes&amp;quot;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local Renderers = require(&quot;Module:AbilityTable/ComplexRenderers&quot;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local Renderers = require(&quot;Module:&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Sandbox/Monster Domosed/&lt;/ins&gt;AbilityTable/ComplexRenderers&quot;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local GameData  = require(&amp;quot;Module:GameData&amp;quot;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local GameData  = require(&amp;quot;Module:GameData&amp;quot;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key deadlock:diff:1.41:old-146626:rev-146628:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Monster Domosed</name></author>
	</entry>
	<entry>
		<id>https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?diff=146626&amp;oldid=prev</id>
		<title>Monster Domosed: test</title>
		<link rel="alternate" type="text/html" href="https://deadlock.wiki/Module:Sandbox/Monster_Domosed/AbilityTable?diff=146626&amp;oldid=prev"/>
		<updated>2026-09-11T00:11:19Z</updated>

		<summary type="html">&lt;p&gt;test&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Renders sortable wikitables of abilities filtered by a named stat.&lt;br /&gt;
&lt;br /&gt;
local Lists     = require(&amp;quot;Module:AbilityTable/Lists&amp;quot;)&lt;br /&gt;
local Notes     = require(&amp;quot;Module:AbilityTable/Notes&amp;quot;)&lt;br /&gt;
local Renderers = require(&amp;quot;Module:AbilityTable/ComplexRenderers&amp;quot;)&lt;br /&gt;
local GameData  = require(&amp;quot;Module:GameData&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local extra_columns = {&lt;br /&gt;
    [&amp;quot;barrier&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Barrier&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderBarrier,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;charges&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Charges&amp;quot;, &amp;quot;Time Between Charges&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderCharges,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;melee&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Base Damage&amp;quot;, &amp;quot;Light Melee Scaling&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderMelee,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;heavymelee&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Base Damage&amp;quot;, &amp;quot;Heavy Melee Scaling&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderHeavyMelee,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;scalingspirit&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Spirit Scaling&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderSpiritScaling,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;healreduce&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Heal Reduction&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderHealReduce,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;moveslow&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Move Slow&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderMoveSlow,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;dashslow&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Dash Slow&amp;quot;, &amp;quot;Notes&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderDashSlow,&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;extradebuffresist&amp;quot;] = {&lt;br /&gt;
        headers   = { &amp;quot;Debuff Resist&amp;quot; },&lt;br /&gt;
        get_cells = Renderers.RenderExtraDebuffResist,&lt;br /&gt;
    },&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Note enrichment&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
-- Merges auto-generated and human notes for full-table mode.&lt;br /&gt;
-- Capitalizes human note, joins with &amp;quot;. &amp;quot;, ensures trailing period.&lt;br /&gt;
local function enrich_notes_full(extra, human_note)&lt;br /&gt;
    if human_note then&lt;br /&gt;
        human_note = human_note:sub(1,1):upper() .. human_note:sub(2)&lt;br /&gt;
        extra[&amp;quot;Notes&amp;quot;] = extra[&amp;quot;Notes&amp;quot;] and (extra[&amp;quot;Notes&amp;quot;] .. &amp;quot;. &amp;quot; .. human_note) or human_note&lt;br /&gt;
    end&lt;br /&gt;
    if extra[&amp;quot;Notes&amp;quot;] and extra[&amp;quot;Notes&amp;quot;]:sub(-1) ~= &amp;quot;.&amp;quot; then&lt;br /&gt;
        extra[&amp;quot;Notes&amp;quot;] = extra[&amp;quot;Notes&amp;quot;] .. &amp;quot;.&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Merges auto-generated and human notes for compact mode.&lt;br /&gt;
-- Decapitalizes human note (skips T{N} prefixes like &amp;quot;T1 slow&amp;quot;),&lt;br /&gt;
-- joins with &amp;quot;; &amp;quot;, strips trailing period.&lt;br /&gt;
local function enrich_notes_compact(extra, human_note)&lt;br /&gt;
    if human_note then&lt;br /&gt;
        local first = human_note:sub(1,1)&lt;br /&gt;
        if first:match(&amp;quot;%u&amp;quot;) and not human_note:sub(2,2):match(&amp;quot;%d&amp;quot;) then&lt;br /&gt;
            human_note = first:lower() .. human_note:sub(2)&lt;br /&gt;
        end&lt;br /&gt;
        if human_note:sub(-1) == &amp;quot;.&amp;quot; then human_note = human_note:sub(1, -2) end&lt;br /&gt;
        extra[&amp;quot;Notes&amp;quot;] = extra[&amp;quot;Notes&amp;quot;] and (extra[&amp;quot;Notes&amp;quot;] .. &amp;quot;; &amp;quot; .. human_note) or human_note&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Ability views&lt;br /&gt;
--&lt;br /&gt;
-- In-game Ability may containe 1 or more records in AbilityData.json&lt;br /&gt;
-- Example: Vexing Bolt is 2 records (Launch Bolt + Redirect Bolt).&lt;br /&gt;
&lt;br /&gt;
-- &amp;quot;Ability View&amp;quot; is a main-record + any relevant sub-ability records.&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
local _views = nil   -- card key -&amp;gt; { view = record, heroName = ..., abilityNum = ... }&lt;br /&gt;
local _order = nil   -- array of card keys, sorted by hero then slot&lt;br /&gt;
&lt;br /&gt;
local function build_views()&lt;br /&gt;
    if _views then return _views, _order end&lt;br /&gt;
    _views, _order = {}, {}&lt;br /&gt;
&lt;br /&gt;
    local hero_data = mw.loadJsonData(&amp;quot;Data:HeroData.json&amp;quot;)&lt;br /&gt;
    local active_heroes = {}&lt;br /&gt;
    for _, hero in pairs(hero_data) do&lt;br /&gt;
        if type(hero) == &amp;quot;table&amp;quot;&lt;br /&gt;
            and hero[&amp;quot;IsDisabled&amp;quot;] == false&lt;br /&gt;
            and type(hero[&amp;quot;Name&amp;quot;]) == &amp;quot;string&amp;quot;&lt;br /&gt;
            and (hero[&amp;quot;IsSelectable&amp;quot;] == nil or hero[&amp;quot;IsSelectable&amp;quot;] ~= false)&lt;br /&gt;
        then&lt;br /&gt;
            active_heroes[hero[&amp;quot;Name&amp;quot;]] = true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local ability_data = mw.loadJsonData(GameData.Dataset.ABILITIES)&lt;br /&gt;
    local cards        = mw.loadJsonData(&amp;quot;Data:AbilityCards.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local card_of = {}&lt;br /&gt;
    for _, hero_entry in pairs(cards) do&lt;br /&gt;
        if type(hero_entry) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            local hero_name = hero_entry[&amp;quot;Name&amp;quot;]&lt;br /&gt;
            if hero_name and active_heroes[hero_name] then&lt;br /&gt;
                for slot_str, ability in pairs(hero_entry) do&lt;br /&gt;
                    local slot_num = tonumber(slot_str)&lt;br /&gt;
                    local key = type(ability) == &amp;quot;table&amp;quot; and ability[&amp;quot;Key&amp;quot;] or nil&lt;br /&gt;
                    if slot_num and key and ability_data[key] then&lt;br /&gt;
                        card_of[key] = { heroName = hero_name, abilityNum = slot_num }&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Attach each non-card record to the longest card key it prefixes.&lt;br /&gt;
    local subs = {}&lt;br /&gt;
    for key, record in pairs(ability_data) do&lt;br /&gt;
        if not card_of[key] and record[&amp;quot;IsDisabled&amp;quot;] ~= true then&lt;br /&gt;
            local parent = nil&lt;br /&gt;
            for card_key in pairs(card_of) do&lt;br /&gt;
                if key:sub(1, #card_key + 1) == card_key .. &amp;quot;_&amp;quot;&lt;br /&gt;
                    and (parent == nil or #card_key &amp;gt; #parent)&lt;br /&gt;
                then&lt;br /&gt;
                    parent = card_key&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            if parent then&lt;br /&gt;
                subs[parent] = subs[parent] or {}&lt;br /&gt;
                table.insert(subs[parent], record)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    for key, info in pairs(card_of) do&lt;br /&gt;
        local view = {}&lt;br /&gt;
        for k, v in pairs(ability_data[key]) do view[k] = v end&lt;br /&gt;
        view[&amp;quot;_subabilities&amp;quot;] = subs[key]&lt;br /&gt;
        _views[key] = { view = view, heroName = info.heroName, abilityNum = info.abilityNum }&lt;br /&gt;
        table.insert(_order, key)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(_order, function(a, b)&lt;br /&gt;
        local ia, ib = _views[a], _views[b]&lt;br /&gt;
        if ia.heroName ~= ib.heroName then return ia.heroName &amp;lt; ib.heroName end&lt;br /&gt;
        return ia.abilityNum &amp;lt; ib.abilityNum&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    return _views, _order&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Membership&lt;br /&gt;
-- Pieces of code that help in telling  &amp;quot;is ability X a member of list Y&amp;quot;.&lt;br /&gt;
-- main function is: p.is_member()&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
-- Memoized name-exclusion set per list (from Lists.exclude_abilities).&lt;br /&gt;
local _excluded = {}&lt;br /&gt;
local function excluded_set(stat)&lt;br /&gt;
    if _excluded[stat] then return _excluded[stat] end&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for _, name in ipairs(Lists.exclude_abilities[stat] or {}) do&lt;br /&gt;
        set[name] = true&lt;br /&gt;
    end&lt;br /&gt;
    _excluded[stat] = set&lt;br /&gt;
    return set&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Per-list rules that can&amp;#039;t be expressed as prop lists or exclusions.&lt;br /&gt;
-- Returns true to keep the ability, false to drop it.&lt;br /&gt;
local function special_ok(stat, record, ability_num)&lt;br /&gt;
    if stat == &amp;quot;dispelmagic&amp;quot; and ability_num == 4 then return false end&lt;br /&gt;
    return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Public: does ability `ability_key` belong to list `stat`?&lt;br /&gt;
function p.is_member(stat, ability_key, ability_num)&lt;br /&gt;
    local props = Lists.lists[stat]&lt;br /&gt;
    if not props then return false end&lt;br /&gt;
&lt;br /&gt;
    local views = build_views()&lt;br /&gt;
    local entry = views[ability_key]&lt;br /&gt;
    local view  = entry and entry.view&lt;br /&gt;
&lt;br /&gt;
    if not view then&lt;br /&gt;
        view = mw.loadJsonData(GameData.Dataset.ABILITIES)[ability_key]&lt;br /&gt;
        if not view then return false end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local name = view[&amp;quot;Name&amp;quot;]&lt;br /&gt;
    if name and excluded_set(stat)[name] then return false end&lt;br /&gt;
&lt;br /&gt;
    ability_num = ability_num or (entry and entry.abilityNum)&lt;br /&gt;
    if not special_ok(stat, view, ability_num) then return false end&lt;br /&gt;
&lt;br /&gt;
    return GameData.entity_matches(view, props)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Entry point&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
    local stat = mw.text.trim(frame.args[1] or &amp;quot;&amp;quot;):lower()&lt;br /&gt;
&lt;br /&gt;
    local internal_keys = Lists.lists[stat]&lt;br /&gt;
    if not internal_keys then&lt;br /&gt;
        return &amp;#039;&amp;lt;span class=&amp;quot;error&amp;quot;&amp;gt;AbilityTable: unknown list &amp;quot;&amp;#039; .. stat .. &amp;#039;&amp;quot;.&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local hide_set = {}&lt;br /&gt;
    local hide_param = mw.text.trim(frame.args[&amp;quot;hide&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
    if hide_param ~= &amp;quot;&amp;quot; then&lt;br /&gt;
        for col in hide_param:gmatch(&amp;quot;[^,]+&amp;quot;) do hide_set[mw.text.trim(col)] = true end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local compact = mw.text.trim(frame.args[&amp;quot;compact&amp;quot;] or &amp;quot;&amp;quot;) ~= &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    local collapsed = mw.text.trim(frame.args[&amp;quot;collapsed&amp;quot;] or &amp;quot;&amp;quot;):lower()&lt;br /&gt;
&lt;br /&gt;
    local views, order = build_views()&lt;br /&gt;
    local spec       = extra_columns[stat]&lt;br /&gt;
    local headers    = spec and spec.headers or {}&lt;br /&gt;
    local get_cells  = spec and spec.get_cells or function() return {} end&lt;br /&gt;
    local stat_notes = Notes[stat]&lt;br /&gt;
&lt;br /&gt;
    local filtered = {}&lt;br /&gt;
    for _, key in ipairs(order) do&lt;br /&gt;
        if p.is_member(stat, key) then&lt;br /&gt;
            table.insert(filtered, key)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    if #filtered == 0 then&lt;br /&gt;
        return &amp;quot;No abilities found for list: &amp;quot; .. stat&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local lists_url = mw.uri.fullUrl(&amp;quot;Module:AbilityTable/Lists&amp;quot;)&lt;br /&gt;
    local notes_url = mw.uri.fullUrl(&amp;quot;Module:AbilityTable/Notes&amp;quot;, { action = &amp;quot;edit&amp;quot; })&lt;br /&gt;
    local disclaimer = frame:preprocess(&lt;br /&gt;
        &amp;#039;&amp;lt;div class=&amp;quot;ability-table-disclaimer&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
        .. &amp;#039;This is a dynamic list &amp;#039;&lt;br /&gt;
        .. &amp;#039;&amp;lt;span class=&amp;quot;ability-table-list-name&amp;quot;&amp;gt;&amp;quot;&amp;#039; .. stat .. &amp;#039;&amp;quot;&amp;lt;/span&amp;gt;. &amp;#039;&lt;br /&gt;
        .. &amp;#039;[&amp;#039; .. tostring(lists_url) .. &amp;#039; Edit contents]&amp;#039;&lt;br /&gt;
        .. &amp;#039; · &amp;#039;&lt;br /&gt;
        .. &amp;#039;[&amp;#039; .. tostring(notes_url) .. &amp;#039; Edit notes].&amp;#039;&lt;br /&gt;
        .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    if compact then&lt;br /&gt;
        local hero_order     = {}&lt;br /&gt;
        local hero_abilities = {}&lt;br /&gt;
        for _, key in ipairs(filtered) do&lt;br /&gt;
            local hero = views[key].heroName&lt;br /&gt;
            if not hero_abilities[hero] then&lt;br /&gt;
                table.insert(hero_order, hero)&lt;br /&gt;
                hero_abilities[hero] = {}&lt;br /&gt;
            end&lt;br /&gt;
            table.insert(hero_abilities[hero], views[key].view)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local items = {}&lt;br /&gt;
        for _, hero in ipairs(hero_order) do&lt;br /&gt;
            local hero_cell = frame:expandTemplate{ title = &amp;quot;HeroIcon&amp;quot;, args = { hero } }&lt;br /&gt;
            local ability_parts = {}&lt;br /&gt;
            for _, ability in ipairs(hero_abilities[hero]) do&lt;br /&gt;
                local name = ability[&amp;quot;Name&amp;quot;]&lt;br /&gt;
                local ability_cell = frame:expandTemplate{ title = &amp;quot;AbilityIcon&amp;quot;, args = { name } }&lt;br /&gt;
&lt;br /&gt;
                local extra = get_cells(ability)&lt;br /&gt;
                enrich_notes_compact(extra, stat_notes and stat_notes[name])&lt;br /&gt;
&lt;br /&gt;
                local note = extra[&amp;quot;Notes&amp;quot;]&lt;br /&gt;
                if note then&lt;br /&gt;
                    table.insert(ability_parts, ability_cell .. &amp;quot; (&amp;quot; .. note .. &amp;quot;)&amp;quot;)&lt;br /&gt;
                else&lt;br /&gt;
                    table.insert(ability_parts, ability_cell)&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            table.insert(items, &amp;quot;&amp;lt;li&amp;gt;&amp;quot; .. hero_cell .. &amp;quot;: &amp;quot; .. table.concat(ability_parts, &amp;quot;, &amp;quot;) .. &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        return disclaimer .. &amp;quot;&amp;lt;ul&amp;gt;&amp;quot; .. table.concat(items) .. &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local has_notes_header = false&lt;br /&gt;
    local final_headers = {}&lt;br /&gt;
    for _, h in ipairs(headers) do&lt;br /&gt;
        if h == &amp;quot;Notes&amp;quot; then&lt;br /&gt;
            has_notes_header = true&lt;br /&gt;
        elseif not hide_set[h] then&lt;br /&gt;
            table.insert(final_headers, h)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    local needs_notes = (has_notes_header or stat_notes ~= nil) and not hide_set[&amp;quot;Notes&amp;quot;]&lt;br /&gt;
    if needs_notes then table.insert(final_headers, &amp;quot;Notes&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
    local table_class = &amp;#039;wikitable sortable ability-table&amp;#039;&lt;br /&gt;
    if collapsed ~= &amp;quot;&amp;quot; then&lt;br /&gt;
        table_class = table_class .. &amp;#039; mw-collapsible&amp;#039;&lt;br /&gt;
        if collapsed == &amp;quot;yes&amp;quot; or collapsed == &amp;quot;true&amp;quot; or collapsed == &amp;quot;1&amp;quot; then&lt;br /&gt;
            table_class = table_class .. &amp;#039; mw-collapsed&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local out = {}&lt;br /&gt;
    table.insert(out, disclaimer)&lt;br /&gt;
    table.insert(out, &amp;#039;{| class=&amp;quot;&amp;#039; .. table_class .. &amp;#039;&amp;quot;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    local header_row = &amp;#039;! class=&amp;quot;ability-table-col-hero&amp;quot; | Hero&amp;#039;&lt;br /&gt;
                    .. &amp;#039; !! class=&amp;quot;ability-table-col-ability&amp;quot; | Ability&amp;#039;&lt;br /&gt;
    for _, h in ipairs(final_headers) do&lt;br /&gt;
        header_row = header_row .. &amp;#039; !! class=&amp;quot;ability-table-col-&amp;#039;&lt;br /&gt;
            .. h:lower():gsub(&amp;quot;%s+&amp;quot;, &amp;quot;-&amp;quot;) .. &amp;#039;&amp;quot; | &amp;#039; .. h&lt;br /&gt;
    end&lt;br /&gt;
    table.insert(out, header_row)&lt;br /&gt;
&lt;br /&gt;
        -- Pre-compute how many physical table rows each ability occupies. A cell&lt;br /&gt;
    -- may return an array (e.g. multiple Spirit-scaling instances) — one row&lt;br /&gt;
    -- per element; scalar cells then rowspan across the ability&amp;#039;s rows. The&lt;br /&gt;
    -- hero cell must span every row its abilities occupy combined.&lt;br /&gt;
    local hero_rows = {}&lt;br /&gt;
    local info = {}&lt;br /&gt;
    for _, key in ipairs(filtered) do&lt;br /&gt;
        local entry   = views[key]&lt;br /&gt;
        local ability = entry.view&lt;br /&gt;
        local extra   = get_cells(ability)&lt;br /&gt;
        enrich_notes_full(extra, stat_notes and stat_notes[ability[&amp;quot;Name&amp;quot;]])&lt;br /&gt;
&lt;br /&gt;
        local row_count = 1&lt;br /&gt;
        for _, header in ipairs(final_headers) do&lt;br /&gt;
            local v = extra[header]&lt;br /&gt;
            if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
                row_count = math.max(row_count, #v)&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        local hero = entry.heroName&lt;br /&gt;
        hero_rows[hero] = (hero_rows[hero] or 0) + row_count&lt;br /&gt;
        info[key] = { ability = ability, hero = hero, extra = extra, row_count = row_count }&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local hero_seen = {}&lt;br /&gt;
&lt;br /&gt;
    for _, key in ipairs(filtered) do&lt;br /&gt;
        local rec         = info[key]&lt;br /&gt;
        local ability     = rec.ability&lt;br /&gt;
        local hero        = rec.hero&lt;br /&gt;
        local extra       = rec.extra&lt;br /&gt;
        local R           = rec.row_count&lt;br /&gt;
        local hero_total  = hero_rows[hero]&lt;br /&gt;
&lt;br /&gt;
        local ability_cell = frame:expandTemplate{&lt;br /&gt;
            title = &amp;quot;AbilityIcon&amp;quot;,&lt;br /&gt;
            args = { ability[&amp;quot;Name&amp;quot;] }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        -- One entry per physical row this ability occupies.&lt;br /&gt;
        local rows = {}&lt;br /&gt;
        for i = 1, R do rows[i] = {} end&lt;br /&gt;
&lt;br /&gt;
        -- Lead cells (hero + ability) live on the ability&amp;#039;s first row and span&lt;br /&gt;
        -- its remaining rows on down.&lt;br /&gt;
        if not hero_seen[hero] then&lt;br /&gt;
            local hero_cell = frame:expandTemplate{&lt;br /&gt;
                title = &amp;quot;HeroIcon&amp;quot;,&lt;br /&gt;
                args = { hero }&lt;br /&gt;
            }&lt;br /&gt;
            if hero_total &amp;gt; 1 then&lt;br /&gt;
                table.insert(rows[1],&lt;br /&gt;
                    &amp;#039;rowspan=&amp;quot;&amp;#039; .. hero_total .. &amp;#039;&amp;quot; class=&amp;quot;ability-table-cell-hero&amp;quot; | &amp;#039; .. hero_cell)&lt;br /&gt;
            else&lt;br /&gt;
                table.insert(rows[1],&lt;br /&gt;
                    &amp;#039;class=&amp;quot;ability-table-cell-hero&amp;quot; | &amp;#039; .. hero_cell)&lt;br /&gt;
            end&lt;br /&gt;
            hero_seen[hero] = true&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if R &amp;gt; 1 then&lt;br /&gt;
            table.insert(rows[1],&lt;br /&gt;
                &amp;#039;rowspan=&amp;quot;&amp;#039; .. R .. &amp;#039;&amp;quot; class=&amp;quot;ability-table-cell-ability&amp;quot; | &amp;#039; .. ability_cell)&lt;br /&gt;
        else&lt;br /&gt;
            table.insert(rows[1],&lt;br /&gt;
                &amp;#039;class=&amp;quot;ability-table-cell-ability&amp;quot; | &amp;#039; .. ability_cell)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        -- Stat columns: arrays emit one cell per row, scalars get rowspanned.&lt;br /&gt;
        for _, header in ipairs(final_headers) do&lt;br /&gt;
            local cls = &amp;quot;ability-table-cell-&amp;quot; .. header:lower():gsub(&amp;quot;%s+&amp;quot;, &amp;quot;-&amp;quot;)&lt;br /&gt;
            local v   = extra[header]&lt;br /&gt;
&lt;br /&gt;
            if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
                for i = 1, R do&lt;br /&gt;
                    local content = v[i] ~= nil and tostring(v[i]) or &amp;quot;—&amp;quot;&lt;br /&gt;
                    table.insert(rows[i], &amp;#039;class=&amp;quot;&amp;#039; .. cls .. &amp;#039;&amp;quot; | &amp;#039; .. content)&lt;br /&gt;
                end&lt;br /&gt;
            else&lt;br /&gt;
                local content = v ~= nil and tostring(v) or &amp;quot;—&amp;quot;&lt;br /&gt;
                if R &amp;gt; 1 then&lt;br /&gt;
                    table.insert(rows[1],&lt;br /&gt;
                        &amp;#039;rowspan=&amp;quot;&amp;#039; .. R .. &amp;#039;&amp;quot; class=&amp;quot;&amp;#039; .. cls .. &amp;#039;&amp;quot; | &amp;#039; .. content)&lt;br /&gt;
                else&lt;br /&gt;
                    table.insert(rows[1], &amp;#039;class=&amp;quot;&amp;#039; .. cls .. &amp;#039;&amp;quot; | &amp;#039; .. content)&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        for i = 1, R do&lt;br /&gt;
            table.insert(out, &amp;quot;|-&amp;quot;)&lt;br /&gt;
            table.insert(out, &amp;quot;| &amp;quot; .. table.concat(rows[i], &amp;quot; || &amp;quot;))&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.insert(out, &amp;quot;|}&amp;quot;)&lt;br /&gt;
    return table.concat(out, &amp;quot;\n&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Monster Domosed</name></author>
	</entry>
</feed>