Module:AbilityHints: Difference between revisions

From The Deadlock Wiki
Jump to navigation Jump to search
Vergir (talk | contribs)
mNo edit summary
Use the standard interacts text for the enhanced state (with help from vergir-bot LLM)
 
(25 intermediate revisions by 7 users not shown)
Line 1: Line 1:
-- Ability interaction hints: small item icons on ability cards showing how an
-- Small item icons on ability cards showing how an ability interacts with items (dispellable, reduced, blocked, etc.).
-- ability interacts with items (dispellable, reduced, blocked, etc.).
-- Used by Template:AbilitySection. Styles are there too.
-- Membership is decided by Module:AbilityTable.is_member so these icons always
 
-- agree with the dynamic ability-interaction tables.
-- label_dict_key names the [[Data:Dictionary]] entry holding the tooltip text. It is
-- resolved at render time rather than here, so the tooltip follows the page language
-- and so no frame is needed while the module is still loading.
local CONFIG = {
    { list = "melee",            file = "Melee Lifesteal.png",  label_dict_key = "AbilityHintsMelee" },
    { list = "dontbreakstealth", file = "Veil Walker.png",      label_dict_key = "AbilityHintsDontBreakStealth" },
    { list = "movementbased",    file = "Slowing Hex.png",      label_dict_key = "AbilityHintsMovementBased" },
    { list = "debuffresist",    file = "Debuff Reducer.png",  label_dict_key = "AbilityHintsDebuffResist" },
    { list = "heal",            file = "Healing Booster.png",  label_dict_key = "AbilityHintsHeal" },
    { list = "healingtempo",    file = "Healing Tempo.png",    label_dict_key = "AbilityHintsHealingTempo" },
    { list = "reactivebarrier",  file = "Reactive Barrier.png", label_dict_key = "AbilityHintsReactiveBarrier" },
    { list = "dispelmagic",      file = "Dispel Magic.png",    label_dict_key = "AbilityHintsDispelMagic" },
    { list = "indomitable",      file = "Indomitable.png",      label_dict_key = "AbilityHintsIndomitable" },
    { list = "unstoppable",      file = "Unstoppable.png",      label_dict_key = "AbilityHintsUnstoppable" },
    { list = "platedarmor",      file = "Plated Armor.png",    label_dict_key = "AbilityHintsPlatedArmor" },
    { list = "summons",          file = "Monster Rounds.png",  label_dict_key = "AbilityHintsSummons" },
}
 
-- Tooltip wording for a manually placed icon, keyed by its iconbox state. Each entry
-- takes the item title as {{{1}}}, so translations can put the object where their
-- grammar wants it rather than always after the verb. The enhanced state has no entry
-- of its own: the glow already marks it, so it falls through to the normal wording.
local STATE_DICT_KEYS = {
    normal  = "AbilityHintsInteracts",
    disabled = "AbilityHintsInteractsNot",
    partial  = "AbilityHintsInteractsPartial",
}


local AbilityTable = require("Module:AbilityTable")
local AbilityTable = require("Module:AbilityTable")
local icon_module  = require("Module:Icon")
local dictionary = require("Module:Dictionary")
local ability_data = mw.loadJsonData("Data:AbilityData.json")
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")
local item_data = mw.loadJsonData("Data:ItemData.json")


local p = {}
local p = {}


-- Display order. Each entry:
-- Display order: item type (Weapon => Vitality => Spirit), then cost, then name.
--  list          AbilityTable list name (Module:AbilityTable/Lists)
local function sort_key(file)
--  item          item whose icon represents the interaction
    local base = mw.ustring.lower((file:gsub('%.%w+$', '')))
--  tip            short hover text
    local entry = resource_lookup[base]
--  suppressed_by  optional: hide this icon when the named list is also a member
     local item = (entry ~= nil and entry.type == 'item') and item_data[entry.key] or nil
local CONFIG = {
     if item == nil then
     { list = "melee",          item = "Melee Lifesteal",  tip = "Benefits from melee items" },
        return { 99, math.huge, base }
     { list = "movementbased",  item = "Slowing Hex",      tip = "Disabled by Slowing Hex" },
     end
{ list = "debuffresist",   item = "Debuff Reducer",   tip = "Duration reduced by Debuff Resist" },
     local slot = item["Slot"]
     { list = "heal",            item = "Healing Booster",  tip = "Healing amplified by Healing Booster and affected by healing reduction" },
     local slot_rank = (slot == "Weapon" and 1) or (slot == "Armor" and 2) or (slot == "Tech" and 3) or 98
     { list = "reactivebarrier", item = "Reactive Barrier", tip = "Triggers Reactive Barrier" },
     return {
     { list = "dispelmagic",    item = "Dispel Magic",    tip = "Effect can be dispelled" },
        slot_rank,
    { list = "indomitable",    item = "Indomitable",      tip = "Effect blocked by Indomitable" },
        tonumber(item["Cost"]) or math.huge,
     { list = "unstoppable",     item = "Unstoppable",      tip = "Disable prevented by Unstoppable" },
        mw.ustring.lower(item["Name"] or base),
    { list = "platedarmor",    item = "Plated Armor",    tip = "Bullet effect prevented by Plated Armor" },
     }
}
end


-- Pairs that merge into ONE diagonally-split icon when BOTH match the ability:
local function compare(a, b)
-- the first list fills the upper-left triangle, the second the lower-right.
     for i = 1, 3 do
-- (Replaces suppressed_by for these pairs.)
        if a.sort[i] ~= b.sort[i] then return a.sort[i] < b.sort[i] end
local COMBINE = {
    end
     { "reactivebarrier", "indomitable" },
    return false
}
 
local DEF_BY_LIST = {}
for _, def in ipairs(CONFIG) do
    DEF_BY_LIST[def.list] = def
end
end


-- Renders one item icon as a title-less external link (so Page Previews / Popups
-- partial: split the icon so the top-left half is grayscale (mirrors the iconbox-partial box icon).
-- ignores it) wrapping just the image.
-- An empty link makes the icon non-clickable; automatic hints link from their tooltip text instead.
local function icon_img(item)
local function icon_img(file, link, partial)
     local url = tostring(mw.uri.canonicalUrl(item))
     link = link or ''
     return icon_module._render(item, { size = '24px', ['icon-only'] = 'true', link = url })
     if partial then
        return '<span class="ability-hint-icon"><span class="ability-hint-split">'
            .. '[[File:' .. file .. '|24px|link=' .. link .. ']]'
            .. '<span class="ability-hint-split-gray">[[File:' .. file .. '|24px|link=]]</span>'
            .. '</span></span>'
    end
    return '<span class="ability-hint-icon">[[File:' .. file .. '|24px|link=' .. link .. ']]</span>'
end
end


-- A normal single hint icon.
local function hint_icon(def)
local function single_icon(def)
     return '<span class="ability-hint">' .. icon_img(def.file)
     return '<span class="ability-hint">' .. icon_img(def.item)
         .. '<span class="ability-hint-tip">' .. dictionary.translate(def.label_dict_key) .. '</span></span>'
         .. '<span class="ability-hint-tip">' .. def.tip .. '</span></span>'
end
end


-- A combined icon split along the diagonal: tl = upper-left, br = lower-right.
local function manual_icon(m)
-- Each half links to its own item; the tooltip lists both notes.
    local cls = 'ability-hint'
local function split_icon(tl, br)
    local dict_key = STATE_DICT_KEYS.normal
     return '<span class="ability-hint ability-hint--split">'
    local partial = false
         .. '<span class="ability-hint-half ability-hint-half--tl">' .. icon_img(tl.item) .. '</span>'
     if m.state == 'disabled' then
        .. '<span class="ability-hint-half ability-hint-half--br">' .. icon_img(br.item) .. '</span>'
        cls = cls .. ' ability-hint--disabled'
         .. '<span class="ability-hint-tip">' .. tl.tip .. '<br>' .. br.tip .. '</span>'
        dict_key = STATE_DICT_KEYS.disabled
        .. '</span>'
    elseif m.state == 'partial' then
         dict_key = STATE_DICT_KEYS.partial
        partial = true
    elseif m.state == 'enhanced' then
        cls = cls .. ' ability-hint--enhanced'
    end
    local tip = dictionary.translate(dict_key, nil, nil, { m.title })
        .. '<br>' .. dictionary.translate('AbilityHintsClickReveal')
    return '<span class="' .. cls .. '">' .. icon_img(m.file, '#' .. m.anchor, partial)
         .. '<span class="ability-hint-tip">' .. tip .. '</span></span>'
end
end


-- Renders the interaction icon rail for one ability.
function p.get_icons(ability_key, ability_num, manual)
--  ability_key  the ability's internal key (AbilityCards .Key)
     manual = manual or {}
--  ability_num  the ability's slot (1-4); coerced to number
-- Returns rail HTML, or '' when the ability has no interactions.
function p.get_icons(ability_key, ability_num)
     local record = ability_data[ability_key]
    if record == nil then return '' end
    local num = tonumber(ability_num)


     local member = {}
     local manual_by_file = {}
     for _, def in ipairs(CONFIG) do
     for _, m in ipairs(manual) do
         member[def.list] = AbilityTable.is_member(def.list, record, num)
         manual_by_file[mw.ustring.lower(m.file)] = m
     end
     end


    -- Resolve combos: when both lists match, the second is consumed and drawn as
     local entries = {}
    -- the lower-right half of the first list's icon.
     local placed = {}
     local split_with = {}
     local consumed = {}
    for _, c in ipairs(COMBINE) do
        local tl, br = c[1], c[2]
        if member[tl] and member[br] then
            split_with[tl] = br
            consumed[br] = true
        end
    end


     local icons = {}
     local num = tonumber(ability_num)
     for _, def in ipairs(CONFIG) do
     for _, def in ipairs(CONFIG) do
         if member[def.list] and not consumed[def.list]
         if AbilityTable.is_member(def.list, ability_key, num) then
          and not (def.suppressed_by and member[def.suppressed_by]) then
            local key = mw.ustring.lower(def.file)
             if split_with[def.list] then
            local m = manual_by_file[key]
                 table.insert(icons, split_icon(def, DEF_BY_LIST[split_with[def.list]]))
             if m ~= nil then
                 table.insert(entries, { file = m.file, html = manual_icon(m) })
                placed[key] = true
             else
             else
                 table.insert(icons, single_icon(def))
                 table.insert(entries, { file = def.file, html = hint_icon(def) })
             end
             end
         end
         end
     end
     end


     if #icons == 0 then return '' end
    for _, m in ipairs(manual) do
        if not placed[mw.ustring.lower(m.file)] then
            table.insert(entries, { file = m.file, html = manual_icon(m) })
        end
    end
 
     if #entries == 0 then return '' end
 
    for _, e in ipairs(entries) do e.sort = sort_key(e.file) end
    table.sort(entries, compare)
 
    local icons = {}
    for _, e in ipairs(entries) do table.insert(icons, e.html) end
 
     return '<div class="ability-section-hints">' .. table.concat(icons) .. '</div>'
     return '<div class="ability-section-hints">' .. table.concat(icons) .. '</div>'
end
end


return p
return p

Latest revision as of 11:29, 2 August 2026

Overview

[edit source]

Renders a small strip of item icons showing how an ability interacts with items and used by Module:AbilitySection. Each icon displays a configured image and has a hover tooltip.

Hints can be "automatic" and "manual".

  • Automatic come from Module:AbilityTable/Lists. If an ability is part of 'debuffresist' list, then it will show automatic debuff resist hint. List of enabled automatic hints is configured through CONFIG in this module. Clicking on an icon does nothing, however tooltip often will have a link to relevant item/mechanic.
  • Manual hints come from {{AbilityInteraction}} blocks used on AbilitySection. Whenetever an editor describes an interaction using that block a hint will be displayed. Clicking on a hint reveals that hand-written block.

Entry point

[edit source]

p.get_icons(ability_key, ability_num)

[edit source]

Returns the rail HTML for one ability, or an empty string when the ability matches no lists.

Configuration

[edit source]

CONFIG

[edit source]

List of the icons the rail can show. Order within CONFIG does not affect the rendered output — see Display order. Each entry:

Field Description
list List name in Module:AbilityTable/Lists whose membership triggers the icon.
file Image file to display, e.g. Reactive Barrier.png. Also determines the icon's position in the rail.
tip Hover tooltip text. May contain wikilinks and simple HTML (<br>, <b>).

To add an icon, add a CONFIG row: pick the triggering list, the display file, and the tip. To re-scope which abilities an icon covers, edit the list in Module:AbilityTable/Lists.


-- Small item icons on ability cards showing how an ability interacts with items (dispellable, reduced, blocked, etc.).
-- Used by Template:AbilitySection. Styles are there too.

-- label_dict_key names the [[Data:Dictionary]] entry holding the tooltip text. It is
-- resolved at render time rather than here, so the tooltip follows the page language
-- and so no frame is needed while the module is still loading.
local CONFIG = {
    { list = "melee",            file = "Melee Lifesteal.png",  label_dict_key = "AbilityHintsMelee" },
    { list = "dontbreakstealth", file = "Veil Walker.png",      label_dict_key = "AbilityHintsDontBreakStealth" },
    { list = "movementbased",    file = "Slowing Hex.png",      label_dict_key = "AbilityHintsMovementBased" },
    { list = "debuffresist",     file = "Debuff Reducer.png",   label_dict_key = "AbilityHintsDebuffResist" },
    { list = "heal",             file = "Healing Booster.png",  label_dict_key = "AbilityHintsHeal" },
    { list = "healingtempo",     file = "Healing Tempo.png",    label_dict_key = "AbilityHintsHealingTempo" },
    { list = "reactivebarrier",  file = "Reactive Barrier.png", label_dict_key = "AbilityHintsReactiveBarrier" },
    { list = "dispelmagic",      file = "Dispel Magic.png",     label_dict_key = "AbilityHintsDispelMagic" },
    { list = "indomitable",      file = "Indomitable.png",      label_dict_key = "AbilityHintsIndomitable" },
    { list = "unstoppable",      file = "Unstoppable.png",      label_dict_key = "AbilityHintsUnstoppable" },
    { list = "platedarmor",      file = "Plated Armor.png",     label_dict_key = "AbilityHintsPlatedArmor" },
    { list = "summons",          file = "Monster Rounds.png",   label_dict_key = "AbilityHintsSummons" },
}

-- Tooltip wording for a manually placed icon, keyed by its iconbox state. Each entry
-- takes the item title as {{{1}}}, so translations can put the object where their
-- grammar wants it rather than always after the verb. The enhanced state has no entry
-- of its own: the glow already marks it, so it falls through to the normal wording.
local STATE_DICT_KEYS = {
    normal   = "AbilityHintsInteracts",
    disabled = "AbilityHintsInteractsNot",
    partial  = "AbilityHintsInteractsPartial",
}

local AbilityTable = require("Module:AbilityTable")
local dictionary = require("Module:Dictionary")
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")
local item_data = mw.loadJsonData("Data:ItemData.json")

local p = {}

-- Display order: item type (Weapon => Vitality => Spirit), then cost, then name.
local function sort_key(file)
    local base = mw.ustring.lower((file:gsub('%.%w+$', '')))
    local entry = resource_lookup[base]
    local item = (entry ~= nil and entry.type == 'item') and item_data[entry.key] or nil
    if item == nil then
        return { 99, math.huge, base }
    end
    local slot = item["Slot"]
    local slot_rank = (slot == "Weapon" and 1) or (slot == "Armor" and 2) or (slot == "Tech" and 3) or 98
    return {
        slot_rank,
        tonumber(item["Cost"]) or math.huge,
        mw.ustring.lower(item["Name"] or base),
    }
end

local function compare(a, b)
    for i = 1, 3 do
        if a.sort[i] ~= b.sort[i] then return a.sort[i] < b.sort[i] end
    end
    return false
end

-- partial: split the icon so the top-left half is grayscale (mirrors the iconbox-partial box icon).
-- An empty link makes the icon non-clickable; automatic hints link from their tooltip text instead.
local function icon_img(file, link, partial)
    link = link or ''
    if partial then
        return '<span class="ability-hint-icon"><span class="ability-hint-split">'
            .. '[[File:' .. file .. '|24px|link=' .. link .. ']]'
            .. '<span class="ability-hint-split-gray">[[File:' .. file .. '|24px|link=]]</span>'
            .. '</span></span>'
    end
    return '<span class="ability-hint-icon">[[File:' .. file .. '|24px|link=' .. link .. ']]</span>'
end

local function hint_icon(def)
    return '<span class="ability-hint">' .. icon_img(def.file)
        .. '<span class="ability-hint-tip">' .. dictionary.translate(def.label_dict_key) .. '</span></span>'
end

local function manual_icon(m)
    local cls = 'ability-hint'
    local dict_key = STATE_DICT_KEYS.normal
    local partial = false
    if m.state == 'disabled' then
        cls = cls .. ' ability-hint--disabled'
        dict_key = STATE_DICT_KEYS.disabled
    elseif m.state == 'partial' then
        dict_key = STATE_DICT_KEYS.partial
        partial = true
    elseif m.state == 'enhanced' then
        cls = cls .. ' ability-hint--enhanced'
    end
    local tip = dictionary.translate(dict_key, nil, nil, { m.title })
        .. '<br>' .. dictionary.translate('AbilityHintsClickReveal')
    return '<span class="' .. cls .. '">' .. icon_img(m.file, '#' .. m.anchor, partial)
        .. '<span class="ability-hint-tip">' .. tip .. '</span></span>'
end

function p.get_icons(ability_key, ability_num, manual)
    manual = manual or {}

    local manual_by_file = {}
    for _, m in ipairs(manual) do
        manual_by_file[mw.ustring.lower(m.file)] = m
    end

    local entries = {}
    local placed = {}

    local num = tonumber(ability_num)
    for _, def in ipairs(CONFIG) do
        if AbilityTable.is_member(def.list, ability_key, num) then
            local key = mw.ustring.lower(def.file)
            local m = manual_by_file[key]
            if m ~= nil then
                table.insert(entries, { file = m.file, html = manual_icon(m) })
                placed[key] = true
            else
                table.insert(entries, { file = def.file, html = hint_icon(def) })
            end
        end
    end

    for _, m in ipairs(manual) do
        if not placed[mw.ustring.lower(m.file)] then
            table.insert(entries, { file = m.file, html = manual_icon(m) })
        end
    end

    if #entries == 0 then return '' end

    for _, e in ipairs(entries) do e.sort = sort_key(e.file) end
    table.sort(entries, compare)

    local icons = {}
    for _, e in ipairs(entries) do table.insert(icons, e.html) end

    return '<div class="ability-section-hints">' .. table.concat(icons) .. '</div>'
end

return p