Module:AbilitySection: Difference between revisions

From The Deadlock Wiki
Jump to navigation Jump to search
Vergir (talk | contribs)
m update comments
LVL (talk | contribs)
Switch to visually hidden H3 for TOC parsing; remove unused icon module dependency and dead code.
Line 4: Line 4:
local utils          = require "Module:Abilities/utils"
local utils          = require "Module:Abilities/utils"
local lang            = require "Module:Lang"
local lang            = require "Module:Lang"
local icon_module    = require "Module:Icon"
local AbilityHints    = require "Module:AbilityHints"
local AbilityHints    = require "Module:AbilityHints"
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")
Line 10: Line 9:
-- Tab definitions — order here determines display order.
-- Tab definitions — order here determines display order.
local TABS = {
local TABS = {
{ key = 'notes',        label = 'Notes'        },
    { key = 'notes',        label = 'Notes'        },
{ key = 'tips',        label = 'Tips'        },
    { key = 'tips',        label = 'Tips'        },
{ key = 'interactions', label = 'Interactions' },
    { key = 'interactions', label = 'Interactions' },
}
}


local function get_hero_key(hero_name)
local function get_hero_key(hero_name)
if hero_name == nil or hero_name == '' then return nil end
    if hero_name == nil or hero_name == '' then return nil end
if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end
    if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end
local entry = resource_lookup[hero_name:lower()]
    local entry = resource_lookup[hero_name:lower()]
if entry == nil then return nil end
    if entry == nil then return nil end
return entry.key
    return entry.key
end
end


local function trim(s)
local function trim(s)
return s:match('^%s*(.-)%s*$')
    return s:match('^%s*(.-)%s*$')
end
end


-- Process content in the Interactions tab to build list of icons to draw in ability hints beside ability card
-- Process content in the Interactions tab to build list of icons to draw in ability hints beside ability card
local function process_interactions(html, ability_num)
local function process_interactions(html, ability_num)
local manual, idx, seen = {}, 0, {}
    local manual, idx, seen = {}, 0, {}
local out = html:gsub('(<div)([^>]*data%-iconbox%-icon="[^"]*"[^>]*)(>)', function(pre, attrs, post)
    local out = html:gsub('(<div)([^>]*data%-iconbox%-icon="[^"]*"[^>]*)(>)', function(pre, attrs, post)
local file = attrs:match('data%-iconbox%-icon="([^"]*)"')
        local file = attrs:match('data%-iconbox%-icon="([^"]*)"')
if file and file ~= '' then
        if file and file ~= '' then
local base  = mw.ustring.lower((file:gsub('%.%w+$', '')))
            local base  = mw.ustring.lower((file:gsub('%.%w+$', '')))
local entry = resource_lookup[base]
            local entry = resource_lookup[base]
if entry ~= nil and entry.type == 'item' and not seen[base] then
            if entry ~= nil and entry.type == 'item' and not seen[base] then
seen[base] = true
                seen[base] = true
idx = idx + 1
                idx = idx + 1
local box_id = 'ai-int-' .. ability_num .. '-' .. idx
                local box_id = 'ai-int-' .. ability_num .. '-' .. idx
table.insert(manual, {
                table.insert(manual, {
file  = file,
                    file  = file,
title  = attrs:match('data%-iconbox%-title="([^"]*)"') or '',
                    title  = attrs:match('data%-iconbox%-title="([^"]*)"') or '',
state  = attrs:match('data%-iconbox%-state="([^"]*)"') or 'normal',
                    state  = attrs:match('data%-iconbox%-state="([^"]*)"') or 'normal',
anchor = box_id,
                    anchor = box_id,
})
                })
return pre .. attrs .. ' id="' .. box_id .. '"' .. post
                return pre .. attrs .. ' id="' .. box_id .. '"' .. post
end
            end
end
        end
return pre .. attrs .. post
        return pre .. attrs .. post
end)
    end)
return out, manual
    return out, manual
end
end


local function build_tabber(wikitext_map, tab_overrides)
local function build_tabber(wikitext_map, tab_overrides)
tab_overrides = tab_overrides or {}
    tab_overrides = tab_overrides or {}
local filled = {}
    local filled = {}
for _, tab in ipairs(TABS) do
    for _, tab in ipairs(TABS) do
if wikitext_map[tab.key] ~= '' then
        if wikitext_map[tab.key] ~= '' then
local tab_wt = tab_overrides[tab.key]
            local tab_wt = tab_overrides[tab.key]
if tab_wt == nil or tab_wt == '' then tab_wt = wikitext_map[tab.key] end
            if tab_wt == nil or tab_wt == '' then tab_wt = wikitext_map[tab.key] end
table.insert(filled, {
            table.insert(filled, {
key    = tab.key,
                key    = tab.key,
label  = tab.label,
                label  = tab.label,
tab_wt = tab_wt,
                tab_wt = tab_wt,
all_wt = wikitext_map[tab.key],
                all_wt = wikitext_map[tab.key],
})
            })
end
        end
end
    end


if #filled == 0 then
    if #filled == 0 then
return ''
        return ''
end
    end


local tabs = {}
    local tabs = {}
for _, section in ipairs(filled) do
    for _, section in ipairs(filled) do
table.insert(tabs, {
        table.insert(tabs, {
label  = section.label,
            label  = section.label,
content = '<div class="ability-details-panel">\n' .. section.tab_wt .. '\n</div>',
            content = '<div class="ability-details-panel">\n' .. section.tab_wt .. '\n</div>',
})
        })
end
    end


if #filled >= 2 then
    if #filled >= 2 then
local all_parts = {}
        local all_parts = {}
for _, section in ipairs(filled) do
        for _, section in ipairs(filled) do
table.insert(all_parts,
            table.insert(all_parts,
'<div class="ability-details-section-header">' .. section.label .. '</div>'
                '<div class="ability-details-section-header">' .. section.label .. '</div>'
.. '<div class="ability-details-section-content">\n' .. section.all_wt .. '\n</div>'
                .. '<div class="ability-details-section-content">\n' .. section.all_wt .. '\n</div>'
)
            )
end
        end
table.insert(tabs, {
        table.insert(tabs, {
label  = 'Show All',
            label  = 'Show All',
content = '<div class="ability-details-panel">' .. table.concat(all_parts) .. '</div>',
            content = '<div class="ability-details-panel">' .. table.concat(all_parts) .. '</div>',
})
        })
end
    end


local rendered = mw.ext.tabber.render(tabs)
    local rendered = mw.ext.tabber.render(tabs)
if #filled == 1 then
    if #filled == 1 then
return '<div class="single-tabbed">' .. rendered .. '</div>'
        return '<div class="single-tabbed">' .. rendered .. '</div>'
end
    end
return rendered
    return rendered
end
end


-- Entry point. Called via {{#invoke:AbilitySection|render}}.
-- Entry point. Called via {{#invoke:AbilitySection|render}}.
function p.render(frame)
function p.render(frame)
local args        = frame.args
    local args        = frame.args
local hero_name  = trim(args.hero_name or '')
    local hero_name  = trim(args.hero_name or '')
local ability_num = trim(args.ability_num or '')
    local ability_num = trim(args.ability_num or '')
local notes        = trim(args.notes or '')
    local notes        = trim(args.notes or '')
local tips        = trim(args.tips or '')
    local tips        = trim(args.tips or '')
local interactions = trim(args.interactions or '')
    local interactions = trim(args.interactions or '')


local hero_key    = get_hero_key(hero_name)
    local hero_key    = get_hero_key(hero_name)
local heading    = ''
    local heading    = ''
local ability_key = nil
    local ability_key = nil
if hero_key ~= nil then
    if hero_key ~= nil then
local ability = utils.get_ability_card_data(hero_key, ability_num)
        local ability = utils.get_ability_card_data(hero_key, ability_num)
if ability ~= nil then
        if ability ~= nil then
ability_key = ability.Key
            ability_key = ability.Key
local ability_name = lang.get_string(ability.Key)
            local ability_name = lang.get_string(ability.Key)
local ability_name_en = lang.get_string(ability.Key, 'en', ability.Key)
            -- Create a visually hidden H3 heading so the Table of Contents can parse
local icon_html = icon_module._render(ability_name_en, { size = '30px', ['icon-only'] = 'true', ['no-link'] = 'true' })
            -- the ability name without displaying a redundant title above the card.
heading = '<h3 class="ability-name">' .. icon_html .. ' ' .. ability_name .. '</h3>'
            heading = '<h3 class="ability-name-toc">' .. ability_name .. '</h3>'
end
        end
end
    end


local card = frame:expandTemplate{
    local card = frame:expandTemplate{
title = 'Ability card v2',
        title = 'Ability card v2',
args  = { hero_name = hero_name, ability_num = ability_num, link_name = 'false' }
        args  = { hero_name = hero_name, ability_num = ability_num, link_name = 'false' }
}
    }


local interactions_ided, manual = interactions, {}
    local interactions_ided, manual = interactions, {}
if interactions ~= '' then
    if interactions ~= '' then
interactions_ided, manual = process_interactions(interactions, ability_num)
        interactions_ided, manual = process_interactions(interactions, ability_num)
end
    end


local wikitext_map = {
    local wikitext_map = {
notes        = notes,
        notes        = notes,
tips        = tips,
        tips        = tips,
interactions = interactions,
        interactions = interactions,
}
    }


local tabber = build_tabber(wikitext_map, { interactions = interactions_ided })
    local tabber = build_tabber(wikitext_map, { interactions = interactions_ided })


local hints_col = ''
    local hints_col = ''
if ability_key ~= nil or #manual > 0 then
    if ability_key ~= nil or #manual > 0 then
hints_col = AbilityHints.get_icons(ability_key, ability_num, manual)
        hints_col = AbilityHints.get_icons(ability_key, ability_num, manual)
end
    end


local details_col = ''
    local details_col = ''
if tabber ~= '' then
    if tabber ~= '' then
details_col = '<div class="ability-section-details">' .. tabber .. '</div>'
        details_col = '<div class="ability-section-details">' .. tabber .. '</div>'
end
    end


local card_block = '<div class="ability-section-card">' .. card .. '</div>'
    local card_block = '<div class="ability-section-card">' .. card .. '</div>'
local left_col
    local left_col
if hints_col ~= '' then
    if hints_col ~= '' then
left_col = '<div class="ability-section-main">' .. card_block .. hints_col .. '</div>'
        left_col = '<div class="ability-section-main">' .. card_block .. hints_col .. '</div>'
else
    else
left_col = card_block
        left_col = card_block
end
    end


local ability_section = '<div class="ability-section">'
    local ability_section = '<div class="ability-section">'
.. left_col
        .. left_col
.. details_col
        .. details_col
.. '</div>'
        .. '</div>'


return '<div class="ability-section-wrapper">'
    return '<div class="ability-section-wrapper">'
.. heading
        .. heading
.. ability_section
        .. ability_section
.. '</div>'
        .. '</div>'
end
end


return p
return p

Revision as of 17:14, 25 July 2026

Overview

Lua backend for Template:AbilitySection. Renders a two-column layout with the ability card on the left and an optional tabbed details panel on the right.

Between the card and the details panel, an interaction-hint rail of small item icons is rendered via Module:AbilityHints (showing how the ability interacts with items — dispellable, reduced, blocked, etc.). When a rail is present, the card and rail are grouped in a .ability-section-main wrapper so the rail always wraps together with the card rather than onto the details' line; abilities with no interactions keep the flat single-card structure.

Only tabs with non-empty content are shown. If two or more tabs have content, a Show All tab is automatically appended that stacks all content in sequence with section headers between them.

The Notes tab is height-constrained with a scrollbar when content overflows (see Template:AbilitySection/styles.css). Other tabs are unconstrained.

If no content params are provided, no details panel is rendered at all — only the ability card (and its hint rail, if any).

Entry point

p.render(frame)

Called via {{#invoke:AbilitySection|render}}.

Argument Required Description
hero_name Yes Passed through to Template:Ability card v2.
ability_num Yes Passed through to Template:Ability card v2.
notes No Wikitext for the Notes tab.
tips No Wikitext for the Tips tab.

Constants

TABS

Defines list of possible tabs. Order here determines display order in the tabber.

local TABS = {
    { key = 'notes',        label = 'Notes'        },
    { key = 'tips',         label = 'Tips'         },
}

Example

{{AbilitySection
| hero_name   = Abrams
| ability_num = 1
| notes =
* This is an example of a note.
| tips =
* This is an example of a tip.
}}

Siphon Life

Siphon Life



Radius 8m




Duration 4s



Number of Charges 0


Cooldown 42s



Drain health from nearby enemies, dealing spirit damage over time and healing for a portion of the damage dealt.

x0.6


22

Damage Per Second



4s

Duration
On Hit:



70%

Lifesteal

35%  Lifesteal vs Non-Heroes 

Ability Point1
-20s Cooldown
Ability Point2
2s Duration
Ability Point5
+2m Radius and +18 DPS with improved Spirit scaling
Healing amplified by Healing Booster and affected by healing reductionHealing triggers Healing Tempo's unique effect
  • This is an example of a note.

  • This is an example of a tip.

Notes
  • This is an example of a note.
Tips
  • This is an example of a tip.

-- See /doc for docs

local p = {}
local utils           = require "Module:Abilities/utils"
local lang            = require "Module:Lang"
local AbilityHints    = require "Module:AbilityHints"
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")

-- Tab definitions — order here determines display order.
local TABS = {
    { key = 'notes',        label = 'Notes'        },
    { key = 'tips',         label = 'Tips'         },
    { key = 'interactions', label = 'Interactions' },
}

local function get_hero_key(hero_name)
    if hero_name == nil or hero_name == '' then return nil end
    if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end
    local entry = resource_lookup[hero_name:lower()]
    if entry == nil then return nil end
    return entry.key
end

local function trim(s)
    return s:match('^%s*(.-)%s*$')
end

-- Process content in the Interactions tab to build list of icons to draw in ability hints beside ability card
local function process_interactions(html, ability_num)
    local manual, idx, seen = {}, 0, {}
    local out = html:gsub('(<div)([^>]*data%-iconbox%-icon="[^"]*"[^>]*)(>)', function(pre, attrs, post)
        local file = attrs:match('data%-iconbox%-icon="([^"]*)"')
        if file and file ~= '' then
            local base  = mw.ustring.lower((file:gsub('%.%w+$', '')))
            local entry = resource_lookup[base]
            if entry ~= nil and entry.type == 'item' and not seen[base] then
                seen[base] = true
                idx = idx + 1
                local box_id = 'ai-int-' .. ability_num .. '-' .. idx
                table.insert(manual, {
                    file   = file,
                    title  = attrs:match('data%-iconbox%-title="([^"]*)"') or '',
                    state  = attrs:match('data%-iconbox%-state="([^"]*)"') or 'normal',
                    anchor = box_id,
                })
                return pre .. attrs .. ' id="' .. box_id .. '"' .. post
            end
        end
        return pre .. attrs .. post
    end)
    return out, manual
end

local function build_tabber(wikitext_map, tab_overrides)
    tab_overrides = tab_overrides or {}
    local filled = {}
    for _, tab in ipairs(TABS) do
        if wikitext_map[tab.key] ~= '' then
            local tab_wt = tab_overrides[tab.key]
            if tab_wt == nil or tab_wt == '' then tab_wt = wikitext_map[tab.key] end
            table.insert(filled, {
                key    = tab.key,
                label  = tab.label,
                tab_wt = tab_wt,
                all_wt = wikitext_map[tab.key],
            })
        end
    end

    if #filled == 0 then
        return ''
    end

    local tabs = {}
    for _, section in ipairs(filled) do
        table.insert(tabs, {
            label   = section.label,
            content = '<div class="ability-details-panel">\n' .. section.tab_wt .. '\n</div>',
        })
    end

    if #filled >= 2 then
        local all_parts = {}
        for _, section in ipairs(filled) do
            table.insert(all_parts,
                '<div class="ability-details-section-header">' .. section.label .. '</div>'
                .. '<div class="ability-details-section-content">\n' .. section.all_wt .. '\n</div>'
            )
        end
        table.insert(tabs, {
            label   = 'Show All',
            content = '<div class="ability-details-panel">' .. table.concat(all_parts) .. '</div>',
        })
    end

    local rendered = mw.ext.tabber.render(tabs)
    if #filled == 1 then
        return '<div class="single-tabbed">' .. rendered .. '</div>'
    end
    return rendered
end

-- Entry point. Called via {{#invoke:AbilitySection|render}}.
function p.render(frame)
    local args        = frame.args
    local hero_name   = trim(args.hero_name or '')
    local ability_num = trim(args.ability_num or '')
    local notes        = trim(args.notes or '')
    local tips         = trim(args.tips or '')
    local interactions = trim(args.interactions or '')

    local hero_key    = get_hero_key(hero_name)
    local heading     = ''
    local ability_key = nil
    if hero_key ~= nil then
        local ability = utils.get_ability_card_data(hero_key, ability_num)
        if ability ~= nil then
            ability_key = ability.Key
            local ability_name = lang.get_string(ability.Key)
            -- Create a visually hidden H3 heading so the Table of Contents can parse 
            -- the ability name without displaying a redundant title above the card.
            heading = '<h3 class="ability-name-toc">' .. ability_name .. '</h3>'
        end
    end

    local card = frame:expandTemplate{
        title = 'Ability card v2',
        args  = { hero_name = hero_name, ability_num = ability_num, link_name = 'false' }
    }

    local interactions_ided, manual = interactions, {}
    if interactions ~= '' then
        interactions_ided, manual = process_interactions(interactions, ability_num)
    end

    local wikitext_map = {
        notes        = notes,
        tips         = tips,
        interactions = interactions,
    }

    local tabber = build_tabber(wikitext_map, { interactions = interactions_ided })

    local hints_col = ''
    if ability_key ~= nil or #manual > 0 then
        hints_col = AbilityHints.get_icons(ability_key, ability_num, manual)
    end

    local details_col = ''
    if tabber ~= '' then
        details_col = '<div class="ability-section-details">' .. tabber .. '</div>'
    end

    local card_block = '<div class="ability-section-card">' .. card .. '</div>'
    local left_col
    if hints_col ~= '' then
        left_col = '<div class="ability-section-main">' .. card_block .. hints_col .. '</div>'
    else
        left_col = card_block
    end

    local ability_section = '<div class="ability-section">'
        .. left_col
        .. details_col
        .. '</div>'

    return '<div class="ability-section-wrapper">'
        .. heading
        .. ability_section
        .. '</div>'
end

return p