| Latest revision |
Your text |
| Line 1: |
Line 1: |
| -- Small item icons on ability cards showing how an ability interacts with items (dispellable, reduced, blocked, etc.). | | -- Ability interaction hints: small item icons on ability cards showing how an |
| -- Used by Template:AbilitySection. Styles are there too. | | -- ability interacts with items (dispellable, reduced, blocked, etc.). |
| | | -- Membership is decided by Module:AbilityTable.is_member so these icons always |
| -- label_dict_key names the [[Data:Dictionary]] entry holding the tooltip text. It is
| | -- agree with the dynamic ability-interaction tables. |
| -- 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 dictionary = require("Module:Dictionary") | | local icon_module = require("Module:Icon") |
| local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json") | | local ability_data = mw.loadJsonData("Data:AbilityData.json") |
| local item_data = mw.loadJsonData("Data:ItemData.json")
| |
|
| |
|
| local p = {} | | local p = {} |
|
| |
|
| -- Display order: item type (Weapon => Vitality => Spirit), then cost, then name. | | -- Display order. Each entry: { list = "...", item = "...", tip = "..." } |
| local function sort_key(file)
| | -- The icon is shown when the ability is a member of `list`. |
| local base = mw.ustring.lower((file:gsub('%.%w+$', '')))
| | local CONFIG = { |
| local entry = resource_lookup[base]
| | { list = "melee", item = "Melee Lifesteal", tip = "Benefits from melee items" }, |
| local item = (entry ~= nil and entry.type == 'item') and item_data[entry.key] or nil | | { list = "movementbased", item = "Slowing Hex", tip = "Disabled by Slowing Hex" }, |
| if item == nil then | | { list = "debuffresist", item = "Debuff Reducer", tip = "Debuff duration reduced by Debuff Resist" }, |
| return { 99, math.huge, base }
| | { list = "heal", item = "Healing Booster", tip = "Healing amplified by Healing Booster and affected by healing reduction" }, |
| end | | { list = "reactivebarrier", item = "Reactive Barrier", tip = "Triggers Reactive Barrier" }, |
| local slot = item["Slot"] | | { list = "dispelmagic", item = "Dispel Magic", tip = "Effect can be dispelled" }, |
| local slot_rank = (slot == "Weapon" and 1) or (slot == "Armor" and 2) or (slot == "Tech" and 3) or 98 | | { list = "indomitable", item = "Indomitable", tip = "Disable prevented by Indomitable" }, |
| return { | | { list = "unstoppable", item = "Unstoppable", tip = "Disable prevented by Unstoppable" }, |
| slot_rank,
| | { list = "platedarmor", item = "Plated Armor", tip = "On-shot effect prevented by Plated Armor" }, |
| tonumber(item["Cost"]) or math.huge,
| | } |
| mw.ustring.lower(item["Name"] or base),
| |
| } | |
| end
| |
|
| |
|
| local function compare(a, b) | | -- Renders one item icon as a title-less external link (so Page Previews / Popups |
| for i = 1, 3 do | | -- ignores it) wrapping just the image. |
| if a.sort[i] ~= b.sort[i] then return a.sort[i] < b.sort[i] end
| | local function icon_img(item) |
| end
| | local url = tostring(mw.uri.canonicalUrl(item)) |
| return false
| | return icon_module._render(item, { size = '24px', ['icon-only'] = 'true', link = url }) |
| end | | end |
|
| |
|
| -- partial: split the icon so the top-left half is grayscale (mirrors the iconbox-partial box icon). | | -- One full-size hint icon with a tooltip. |
| -- An empty link makes the icon non-clickable; automatic hints link from their tooltip text instead.
| | local function hint_icon(item, tip) |
| local function icon_img(file, link, partial) | | return '<span class="ability-hint">' .. icon_img(item) |
| 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>' | | .. '<span class="ability-hint-tip">' .. tip .. '</span></span>' |
| end | | end |
|
| |
|
| function p.get_icons(ability_key, ability_num, manual) | | -- Renders the interaction icon rail for one ability. |
| manual = manual or {} | | -- ability_key the ability's internal key (AbilityCards .Key) |
| | -- 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 manual_by_file = {} | | local icons = {} |
| 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 | | for _, def in ipairs(CONFIG) do |
| if AbilityTable.is_member(def.list, ability_key, num) then | | if AbilityTable.is_member(def.list, record, num) then |
| local key = mw.ustring.lower(def.file) | | table.insert(icons, hint_icon(def.item, def.tip)) |
| 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 |
| end | | end |
|
| |
|
| if #entries == 0 then return '' end | | if #icons == 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 |