Editing Module:AbilityTable

Jump to navigation Jump to search
Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 7: Line 7:


local p = {}
local p = {}
-- ============================================================
-- Extra columns: maps stat names to their column headers and
-- cell renderer functions from ComplexRenderers.
-- Stats not listed here get a plain Hero + Ability + Notes table.
-- ============================================================


local extra_columns = {
local extra_columns = {
    ["barrier"] = {
        headers  = { "Barrier" },
        get_cells = Renderers.RenderBarrier,
    },
     ["charges"] = {
     ["charges"] = {
         headers  = { "Charges", "Time Between Charges", "Notes" },
         headers  = { "Charges", "Time Between Charges", "Notes" },
Line 24: Line 26:
         headers  = { "Base Damage", "Heavy Melee Scaling", "Notes" },
         headers  = { "Base Damage", "Heavy Melee Scaling", "Notes" },
         get_cells = Renderers.RenderHeavyMelee,
         get_cells = Renderers.RenderHeavyMelee,
    },
    ["scalingspirit"] = {
        headers  = { "Spirit Scaling", "Notes" },
        get_cells = Renderers.RenderSpiritScaling,
     },
     },
     ["healreduce"] = {
     ["healreduce"] = {
         headers  = { "Heal Reduction", "Notes" },
         headers  = { "Heal Reduction", "Notes" },
         get_cells = Renderers.RenderHealReduce,
         get_cells = Renderers.RenderHealReduce,
    },
    ["dispelmagic"] = {
        headers  = { "Notes" },
        get_cells = function(ability) return Renderers.RenderDebuff(ability, "dispelmagic") end,
     },
     },
     ["moveslow"] = {
     ["moveslow"] = {
Line 40: Line 42:
         headers  = { "Dash Slow", "Notes" },
         headers  = { "Dash Slow", "Notes" },
         get_cells = Renderers.RenderDashSlow,
         get_cells = Renderers.RenderDashSlow,
    },
    ["extradebuffresist"] = {
        headers  = { "Debuff Resist" },
        get_cells = Renderers.RenderExtraDebuffResist,
     },
     },
}
}
Line 78: Line 76:


-- ============================================================
-- ============================================================
-- Ability views
-- Filtering
--
-- ============================================================
-- In-game Ability may containe 1 or more records in AbilityData.json
 
-- Example: Vexing Bolt is 2 records (Launch Bolt + Redirect Bolt).
local function filter_abilities(stat, abilities, key_lookup)
    local excluded = {}
    if Lists.exclude_abilities[stat] then
        for _, name in ipairs(Lists.exclude_abilities[stat]) do
            excluded[name] = true
        end
    end
 
    local result = {}
    for _, ability in ipairs(abilities) do
        local key  = ability["Key"]
        local info = key and key_lookup[key]
        if info and not excluded[ability["Name"]] then
            local skip = false
            if stat == "dispelmagic" and info.abilityNum == 4 then
                skip = true
            end
            if not skip then
                table.insert(result, ability)
            end
        end
    end
    return result
end


-- "Ability View" is a main-record + any relevant sub-ability records.
-- ============================================================
-- Hero lookup
-- ============================================================
-- ============================================================


local _views = nil   -- card key -> { view = record, heroName = ..., abilityNum = ... }
local _key_lookup  = nil
local _order = nil   -- array of card keys, sorted by hero then slot
local _name_to_key = nil


local function build_views()
local function build_lookup()
     if _views then return _views, _order end
     if _key_lookup then return _key_lookup, _name_to_key end
     _views, _order = {}, {}
     _key_lookup  = {}
    _name_to_key = {}


     local hero_data = mw.loadJsonData("Data:HeroData.json")
     local hero_data = mw.loadJsonData("Data:HeroData.json")
Line 105: Line 128:
     end
     end


    local ability_data = mw.loadJsonData(GameData.Dataset.ABILITIES)
     local cards = mw.loadJsonData("Data:AbilityCards.json")
     local cards       = mw.loadJsonData("Data:AbilityCards.json")
 
    local card_of = {}
     for _, hero_entry in pairs(cards) do
     for _, hero_entry in pairs(cards) do
         if type(hero_entry) == "table" then
         if type(hero_entry) == "table" then
Line 115: Line 135:
                 for slot_str, ability in pairs(hero_entry) do
                 for slot_str, ability in pairs(hero_entry) do
                     local slot_num = tonumber(slot_str)
                     local slot_num = tonumber(slot_str)
                     local key = type(ability) == "table" and ability["Key"] or nil
                     if slot_num and type(ability) == "table" then
                    if slot_num and key and ability_data[key] then
                        local info = { heroName = hero_name, abilityNum = slot_num }
                        card_of[key] = { heroName = hero_name, abilityNum = slot_num }
                        if ability["Key"] then
                            _key_lookup[ability["Key"]] = info
                        end
                        if ability["Name"] and ability["Key"] then
                            _name_to_key[ability["Name"]] = ability["Key"]
                        end
                     end
                     end
                 end
                 end
Line 124: Line 149:
     end
     end


    -- Attach each non-card record to the longest card key it prefixes.
     return _key_lookup, _name_to_key
    local subs = {}
    for key, record in pairs(ability_data) do
        if not card_of[key] and record["IsDisabled"] ~= true then
            local parent = nil
            for card_key in pairs(card_of) do
                if key:sub(1, #card_key + 1) == card_key .. "_"
                    and (parent == nil or #card_key > #parent)
                then
                    parent = card_key
                end
            end
            if parent then
                subs[parent] = subs[parent] or {}
                table.insert(subs[parent], record)
            end
        end
    end
 
    for key, info in pairs(card_of) do
        local view = {}
        for k, v in pairs(ability_data[key]) do view[k] = v end
        view["_subabilities"] = subs[key]
        _views[key] = { view = view, heroName = info.heroName, abilityNum = info.abilityNum }
        table.insert(_order, key)
    end
 
    table.sort(_order, function(a, b)
        local ia, ib = _views[a], _views[b]
        if ia.heroName ~= ib.heroName then return ia.heroName < ib.heroName end
        return ia.abilityNum < ib.abilityNum
    end)
 
    return _views, _order
end
 
-- ============================================================
-- Membership
-- Pieces of code that help in telling  "is ability X a member of list Y".
-- main function is: p.is_member()
-- ============================================================
 
-- Memoized name-exclusion set per list (from Lists.exclude_abilities).
local _excluded = {}
local function excluded_set(stat)
    if _excluded[stat] then return _excluded[stat] end
    local set = {}
    for _, name in ipairs(Lists.exclude_abilities[stat] or {}) do
        set[name] = true
    end
    _excluded[stat] = set
    return set
end
 
-- Per-list rules that can't be expressed as prop lists or exclusions.
-- Returns true to keep the ability, false to drop it.
local function special_ok(stat, record, ability_num)
    if stat == "dispelmagic" and ability_num == 4 then return false end
    return true
end
 
-- Public: does ability `ability_key` belong to list `stat`?
function p.is_member(stat, ability_key, ability_num)
    local props = Lists.lists[stat]
    if not props then return false end
 
    local views = build_views()
    local entry = views[ability_key]
    local view  = entry and entry.view
 
    if not view then
        view = mw.loadJsonData(GameData.Dataset.ABILITIES)[ability_key]
        if not view then return false end
    end
 
    local name = view["Name"]
    if name and excluded_set(stat)[name] then return false end
 
    ability_num = ability_num or (entry and entry.abilityNum)
    if not special_ok(stat, view, ability_num) then return false end
 
     return GameData.entity_matches(view, props)
end
end


Line 228: Line 172:
     local compact = mw.text.trim(frame.args["compact"] or "") ~= ""
     local compact = mw.text.trim(frame.args["compact"] or "") ~= ""


     local collapsed = mw.text.trim(frame.args["collapsed"] or ""):lower()
     local key_lookup, _ = build_lookup()
 
    local views, order = build_views()
     local spec      = extra_columns[stat]
     local spec      = extra_columns[stat]
     local headers    = spec and spec.headers or {}
     local headers    = spec and spec.headers or {}
Line 236: Line 178:
     local stat_notes = Notes[stat]
     local stat_notes = Notes[stat]


     local filtered = {}
     local abilities = GameData.get_entities(GameData.Dataset.ABILITIES, internal_keys)
    for _, key in ipairs(order) do
    local filtered  = filter_abilities(stat, abilities, key_lookup)
        if p.is_member(stat, key) then
            table.insert(filtered, key)
        end
    end


     if #filtered == 0 then
     if #filtered == 0 then
         return "No abilities found for list: " .. stat
         return "No abilities found for list: " .. stat
     end
     end
    table.sort(filtered, function(a, b)
        local ia = key_lookup[a["Key"]]
        local ib = key_lookup[b["Key"]]
        if not ia and not ib then return (a["Name"] or "") < (b["Name"] or "") end
        if not ia then return false end
        if not ib then return true end
        if ia.heroName ~= ib.heroName then return ia.heroName < ib.heroName end
        return ia.abilityNum < ib.abilityNum
    end)


     local lists_url = mw.uri.fullUrl("Module:AbilityTable/Lists")
     local lists_url = mw.uri.fullUrl("Module:AbilityTable/Lists")
Line 262: Line 210:
         local hero_order    = {}
         local hero_order    = {}
         local hero_abilities = {}
         local hero_abilities = {}
         for _, key in ipairs(filtered) do
         for _, ability in ipairs(filtered) do
             local hero = views[key].heroName
             local info = key_lookup[ability["Key"]]
            local hero = info and info.heroName or "—"
             if not hero_abilities[hero] then
             if not hero_abilities[hero] then
                 table.insert(hero_order, hero)
                 table.insert(hero_order, hero)
                 hero_abilities[hero] = {}
                 hero_abilities[hero] = {}
             end
             end
             table.insert(hero_abilities[hero], views[key].view)
             table.insert(hero_abilities[hero], ability)
         end
         end


Line 306: Line 255:
     local needs_notes = (has_notes_header or stat_notes ~= nil) and not hide_set["Notes"]
     local needs_notes = (has_notes_header or stat_notes ~= nil) and not hide_set["Notes"]
     if needs_notes then table.insert(final_headers, "Notes") end
     if needs_notes then table.insert(final_headers, "Notes") end
    local table_class = 'wikitable sortable ability-table'
    if collapsed ~= "" then
        table_class = table_class .. ' mw-collapsible'
        if collapsed == "yes" or collapsed == "true" or collapsed == "1" then
            table_class = table_class .. ' mw-collapsed'
        end
    end


     local out = {}
     local out = {}
     table.insert(out, disclaimer)
     table.insert(out, disclaimer)
     table.insert(out, '{| class="' .. table_class .. '"')
     table.insert(out, '{| class="wikitable sortable ability-table"')


     local header_row = '! class="ability-table-col-hero" | Hero'
     local header_row = '! class="ability-table-col-hero" | Hero'
Line 327: Line 268:
     table.insert(out, header_row)
     table.insert(out, header_row)


        local hero_counts = {}
     for _, ability in ipairs(filtered) do
     for _, key in ipairs(filtered) do
         local info = key_lookup[ability["Key"]]
         local hero = views[key].heroName
        hero_counts[hero] = (hero_counts[hero] or 0) + 1
    end


    local hero_seen = {}
        local hero_cell
 
        if info then
    for _, key in ipairs(filtered) do
            hero_cell = frame:expandTemplate{ title = "HeroIcon", args = { info.heroName } }
         local entry  = views[key]
         else
        local ability = entry.view
            hero_cell = "—"
         local hero    = entry.heroName
         end
 
         local ability_cell = frame:expandTemplate{ title = "AbilityIcon", args = { ability["Name"] } }
         local ability_cell = frame:expandTemplate{
            title = "AbilityIcon",
            args = { ability["Name"] }
        }


         local extra = get_cells(ability)
         local extra = get_cells(ability)
         enrich_notes_full(extra, stat_notes and stat_notes[ability["Name"]])
         enrich_notes_full(extra, stat_notes and stat_notes[ability["Name"]])


         local row
         local row = '| class="ability-table-cell-hero" | '     .. hero_cell
 
                .. ' || class="ability-table-cell-ability" | ' .. ability_cell
        if not hero_seen[hero] then
            local hero_cell = frame:expandTemplate{
                title = "HeroIcon",
                args = { hero }
            }
 
            if hero_counts[hero] > 1 then
                row = '| rowspan="' .. hero_counts[hero] .. '" class="ability-table-cell-hero" | '
                    .. hero_cell
                    .. ' || class="ability-table-cell-ability" | ' .. ability_cell
            else
                row = '| class="ability-table-cell-hero" | '
                    .. hero_cell
                    .. ' || class="ability-table-cell-ability" | ' .. ability_cell
            end
 
            hero_seen[hero] = true
        else
            row = '| class="ability-table-cell-ability" | ' .. ability_cell
        end
 
         for _, header in ipairs(final_headers) do
         for _, header in ipairs(final_headers) do
             local value = extra[header]
             local value = extra[header]
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

Page included on this page: