Module:AbilityHints: Difference between revisions

Vergir (talk | contribs)
mNo edit summary
Use the standard interacts text for the enhanced state (with help from vergir-bot LLM)
 
(24 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 = "On-shot 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