Module:Sandbox/Vergir

From The Deadlock Wiki
Revision as of 23:47, 6 September 2026 by ~2026-EnduringDurationJump1108 (talk) (Audit script v3: formatted label values + classification column (with help from vergir-bot LLM))
Jump to navigation Jump to search

Documentation for this module may be created at Module:Sandbox/Vergir/doc

-- Audit: for every item stat that goes through Infobox item's append_label
-- with attribute styling enabled (Passive/Active Main stats), compute the
-- label link target under the CURRENT priority (attr link > StatLinks)
-- and the PROPOSED priority (StatLinks > attr link), and classify the change.
local p = {}
local lang = require "Module:Lang"
local data = mw.loadJsonData("Data:ItemCards.json")
local stat_links = mw.loadJsonData("Data:StatLinks.json")
local ATTR = require("Module:Icon/attr")

local LABEL_KEY_REMAP = { DotDuration = "DOTDuration" }
local IGNORED = { BuildUpPerShot = true }

-- Keys whose StatLinks entry is worse than the current type link.
-- Proposed StatLinks fix noted alongside.
local STATLINKS_FIX = {
    SlowPercent = "Movement Slow",
    MaxSlowPercent = "Movement Slow",
}
local DEBATABLE = {
    GroundDashReductionPercent = true,
}

local function get_pair(base, s1, s2)
    local v = lang.get_string(base .. s1)
    if v and v ~= "" then return v end
    v = lang.get_string(base .. s2)
    if v and v ~= "" then return v end
    return ""
end

local function resolve_label_key(prop_key, loc_override)
    local base = (loc_override or prop_key):gsub("^#", "")
    return LABEL_KEY_REMAP[base] or base
end

local function norm(s)
    if s == nil or s == "" then return nil end
    return (tostring(s):gsub("_", " "))
end

-- Resolve a title through one redirect hop; returns final page name
local resolve_cache = {}
local function resolve(t)
    if t == nil then return nil end
    if resolve_cache[t] == nil then
        local title = mw.title.new(t)
        local r = t
        if title and title.exists and title.isRedirect then
            local tgt = title.redirectTarget
            if tgt then r = tgt.prefixedText end
        elseif not title or not title.exists then
            r = t .. " (MISSING)"
        end
        resolve_cache[t] = r
    end
    return resolve_cache[t]
end

local function fmt_value(label_key, value, label)
    local n = tonumber(tostring(value):match("^-?%d+%.?%d*"))
    if not n then return label end
    local prefix = get_pair(label_key, "_prefix", "_Prefix")
    local postfix = get_pair(label_key, "_postfix", "_Postfix")
    if prefix == "{s:sign}" then prefix = n >= 0 and "+" or "" end
    local s = tostring(n)
    if prefix ~= "" then s = prefix .. s end
    if postfix ~= "" then s = s .. (postfix == "HP/s" and " " or "") .. postfix end
    return s .. " " .. label
end

function p.audit(frame)
    local names = {}
    for key, item in pairs(data) do
        if not item.IsDisabled then table.insert(names, key) end
    end
    table.sort(names, function(a, b)
        return (data[a].Name or a) < (data[b].Name or b)
    end)

    local rows = {}
    local counts = {}
    local n_items, n_stats, n_same = 0, 0, 0
    for _, key in ipairs(names) do
        local item = data[key]
        n_items = n_items + 1
        local i = 1
        while item["Info" .. i] do
            local sec = item["Info" .. i]
            if sec.Type ~= "Innate" then
                local seen = {}
                for _, obj in ipairs(sec.Main or {}) do
                    local pk = obj.Key
                    if not seen[pk] and not IGNORED[pk] and not pk:match("^StatusEffect") then
                        seen[pk] = true
                        local label_key = resolve_label_key(pk, obj.LocTokenOverride)
                        local label = get_pair(label_key, "_label", "_Label")
                        if label ~= "" then
                            n_stats = n_stats + 1
                            local attr = obj.Type and ATTR[obj.Type] or nil
                            local attr_link = norm(attr and attr.link)
                            local sl = norm(stat_links[label_key])
                            local cur = attr_link or sl
                            local new = sl or attr_link
                            if (cur or "") == (new or "") then
                                n_same = n_same + 1
                            else
                                local kind
                                if STATLINKS_FIX[label_key] then
                                    kind = "Regression → fixed via StatLinks (" .. label_key .. " = " .. STATLINKS_FIX[label_key] .. ")"
                                elseif DEBATABLE[label_key] then
                                    kind = "Debatable"
                                elseif resolve(cur) == resolve(new) then
                                    kind = "NOP (redirect to same page)"
                                else
                                    kind = "Fix"
                                end
                                counts[kind] = (counts[kind] or 0) + 1
                                table.insert(rows, string.format(
                                    "|-\n| [[%s]] || %s || [[%s]] || [[%s]] || %s",
                                    item.Name or key, fmt_value(label_key, obj.Value, label),
                                    cur or "", new or "", kind))
                            end
                        end
                    end
                end
            end
            i = i + 1
        end
    end

    local out = {}
    table.insert(out, string.format(
        "Items scanned: %d. Passive/Active main stats with a label: %d. Unchanged: %d. Changed: %d.\n",
        n_items, n_stats, n_same, #rows))
    local ks = {}
    for k in pairs(counts) do table.insert(ks, k) end
    table.sort(ks)
    for _, k in ipairs(ks) do table.insert(out, string.format("* %s: %d", k, counts[k])) end
    table.insert(out, '{| class="wikitable sortable"\n! Item !! Label !! Current target !! Proposed target !! Type')
    for _, r in ipairs(rows) do table.insert(out, r) end
    table.insert(out, "|}")
    return table.concat(out, "\n")
end

return p