Module:AbilitySection: Difference between revisions

From The Deadlock Wiki
Jump to navigation Jump to search
Vergir (talk | contribs)
Add Interactions tab/parameter alongside Notes and Tips (with help from vergir-bot LLM)
Vergir (talk | contribs)
m support creating ability hints from interactions tab
Line 1: Line 1:
-- see /doc for docs
-- See /doc for docs


local p = {}
local p = {}
Line 27: Line 27:
end
end


local function build_tabber(wikitext_map)
local function process_interactions(html, ability_num)
local manual, idx, seen = {}, 0, {}
local out = html:gsub('(<div class="ai%-box)([^>]*)(>)', function(pre, attrs, post)
local file = attrs:match('data%-ai%-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%-ai%-title="([^"]*)"') or '',
disabled = attrs:find('ai%-disabled') ~= nil,
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 = {}
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
table.insert(filled, { key = tab.key, label = tab.label, wikitext = wikitext_map[tab.key] })
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
end
end
Line 43: Line 76:
table.insert(tabs, {
table.insert(tabs, {
label  = section.label,
label  = section.label,
content = '<div class="ability-details-panel">\n' .. section.wikitext .. '\n</div>',
content = '<div class="ability-details-panel">\n' .. section.tab_wt .. '\n</div>',
})
})
end
end


-- "Show All" tab wraps each section's wikitext in a <div> to give the parser
-- a fresh block context per section, preventing html mangling.
if #filled >= 2 then
if #filled >= 2 then
local all_parts = {}
local all_parts = {}
Line 54: Line 85:
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.wikitext .. '\n</div>'
.. '<div class="ability-details-section-content">\n' .. section.all_wt .. '\n</div>'
)
)
end
end
Line 99: Line 130:
}
}


local hints_col = ''
local interactions_ided, manual = interactions, {}
if ability_key ~= nil then
if interactions ~= '' then
hints_col = AbilityHints.get_icons(ability_key, ability_num)
interactions_ided, manual = process_interactions(interactions, ability_num)
end
end


Line 110: Line 141:
}
}


local tabber = build_tabber(wikitext_map)
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 = ''
local details_col = ''
if tabber ~= '' then
if tabber ~= '' then
Line 116: Line 153:
end
end


-- Group the card and the hint rail together so the rail wraps with the card
-- (never onto the notes' line). Abilities with no interactions have no rail,
-- so they keep the flat single-card structure.
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

Revision as of 20:56, 14 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 icon_module     = require "Module:Icon"
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

local function process_interactions(html, ability_num)
	local manual, idx, seen = {}, 0, {}
	local out = html:gsub('(<div class="ai%-box)([^>]*)(>)', function(pre, attrs, post)
		local file = attrs:match('data%-ai%-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%-ai%-title="([^"]*)"') or '',
					disabled = attrs:find('ai%-disabled') ~= nil,
					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)
			-- Use the English ability key for icon render; icon module indexes by English name
			local ability_name_en = lang.get_string(ability.Key, 'en', ability.Key)
			local icon_html = icon_module._render(ability_name_en, { size = '30px', ['icon-only'] = 'true', ['no-link'] = 'true' })
			heading = '<h3 class="ability-name">' .. icon_html .. ' ' .. 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