Module:AbilityTable: Difference between revisions

Jump to navigation Jump to search
add debuff_cells renderer shared by debuffresist and dispelmagic, outputs upgrade-only debuff notes (with help from vergir-bot LLM)
add debug output to debuff_cells for Shoulder Charge to diagnose missing note (with help from vergir-bot LLM)
Line 210: Line 210:
-- ============================================================
-- ============================================================


-- Filters a list of prop-matched abilities down to those that should appear
-- in the table for the given stat. Handles:
--  1. key_lookup membership (ability belongs to an active released hero)
--  2. exclude_list (manual false-positive suppressions)
--  3. stat-specific logic (e.g. dispelmagic strips slot-4 ultimates)
local function filter_abilities(stat, abilities, key_lookup)
local function filter_abilities(stat, abilities, key_lookup)
     local excluded = {}
     local excluded = {}
Line 229: Line 224:
         if info and not excluded[ability["Name"]] then
         if info and not excluded[ability["Name"]] then
             local skip = false
             local skip = false
            -- dispelmagic: skip ultimates (slot 4)
             if stat == "dispelmagic" and info.abilityNum == 4 then
             if stat == "dispelmagic" and info.abilityNum == 4 then
                 skip = true
                 skip = true
Line 243: Line 237:
-- ============================================================
-- ============================================================
-- Per-stat cell renderers
-- Per-stat cell renderers
-- Each function receives a guaranteed-matching ability record
-- and returns { ["Column Name"] = value }.
-- Human notes are appended by the framework, not here.
-- ============================================================
-- ============================================================


-- Short display names for debuff properties.
local debuff_short_name = {
local debuff_short_name = {
     ["StunDuration"]              = "stun",
     ["StunDuration"]              = "stun",
Line 281: Line 271:
}
}


-- Returns true if prop is found with a non-zero value anywhere in record,
-- Returns the prop name that matched in base, or nil if none matched.
-- excluding the Upgrades key. Mirrors GameData.record_matches_prop logic.
-- Excludes the Upgrades key. Mirrors GameData.record_matches_prop logic.
local function prop_in_base(record, prop)
local function prop_in_base(record, prop)
     for k, v in pairs(record) do
     for k, v in pairs(record) do
Line 301: Line 291:
end
end


-- Renderer shared by debuffresist and dispelmagic.
-- Outputs a Notes cell describing which debuffs are added only by upgrades.
-- If any prop from the stat's list is found in the base ability, outputs nothing.
-- Otherwise lists per-tier debuff names: "T2 slow", "T1 disarm, T2 slow", etc.
local function debuff_cells(ability, stat)
local function debuff_cells(ability, stat)
     local props = stat_to_props[stat]
     local props = stat_to_props[stat]
     local cells = {}
     local cells = {}
    local is_debug = stat == "dispelmagic" and ability["Name"] == "Shoulder Charge"


     -- Check if any prop is present in base (outside Upgrades).
     -- Check base props
     -- If so, this is a full-ability debuff — no note needed.
     local base_hit = nil
     for _, prop in ipairs(props) do
     for _, prop in ipairs(props) do
         if prop_in_base(ability, prop) then
         if prop_in_base(ability, prop) then
             return cells
             base_hit = prop
            break
         end
         end
     end
     end


     -- Scan upgrades: collect props found per tier.
     if is_debug then
        local debug_parts = { "DEBUG:" }
        if base_hit then
            table.insert(debug_parts, "base_hit=" .. base_hit)
        else
            table.insert(debug_parts, "base_hit=none")
        end
        -- scan upgrades
        local upgrades = ability["Upgrades"]
        if type(upgrades) == "table" then
            for i, tier in ipairs(upgrades) do
                if type(tier) == "table" then
                    for _, prop in ipairs(props) do
                        local v = tier[prop]
                        if v ~= nil then
                            if type(v) == "table" then v = v["Value"] end
                            if v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m" then
                                table.insert(debug_parts, "T"..i.."+"..prop.."="..tostring(v))
                            end
                        end
                    end
                end
            end
        end
        cells["Notes"] = table.concat(debug_parts, " | ")
        return cells
    end
 
    if base_hit then return cells end
 
     local upgrades = ability["Upgrades"]
     local upgrades = ability["Upgrades"]
     if type(upgrades) ~= "table" then return cells end
     if type(upgrades) ~= "table" then return cells end


    -- tier_debuffs[i] = list of short names found in tier i
     local tier_debuffs = {}
     local tier_debuffs = {}
     local seen_names  = {} -- deduplicate short names across tiers
     local seen_names  = {}


     for i, tier in ipairs(upgrades) do
     for i, tier in ipairs(upgrades) do
Line 351: Line 367:
     end
     end


    -- Build note: "T1 slow, T2 silence + burn"
     local parts = {}
     local parts = {}
     for i = 1, #upgrades do
     for i = 1, #upgrades do
Line 548: Line 563:
end
end


-- Generic renderer for a numeric stat column.
-- col_name: the column header string
-- fields:  ordered list of property names (first non-zero wins)
-- suffix:  string appended to values in cell and notes (e.g. "%" or "")
-- Upgrade deltas are additive; notes track running totals.
local function val_stat_cells(ability, col_name, fields, suffix)
local function val_stat_cells(ability, col_name, fields, suffix)
     suffix = suffix or ""
     suffix = suffix or ""
Line 692: Line 702:
-- ============================================================
-- ============================================================
-- Hero lookup
-- Hero lookup
--  _key_lookup  : ability key  -> { heroName, abilityNum }
--  _name_to_key : ability name -> ability key
-- Both built together from HeroData + AbilityCards.
-- ============================================================
-- ============================================================