Module:Sandbox/LVL: Difference between revisions

From The Deadlock Wiki
Jump to navigation Jump to search
LVL (talk | contribs)
No edit summary
LVL (talk | contribs)
No edit summary
Line 87: Line 87:
             local val = calculate_bps(hero, bb)
             local val = calculate_bps(hero, bb)
             if val > 0 then
             if val > 0 then
                -- Calculate shots needed using the raw, unrounded value
                -- math.ceil rounds 5.0001 up to 6
                local shots = math.ceil(100 / val)
               
                 table.insert(hero_list, {
                 table.insert(hero_list, {
                     name = hero["Name"],
                     name = hero["Name"],
                     bps = val
                     bps = val,
                    shots = shots
                 })
                 })
             end
             end
Line 104: Line 109:
     headerRow:tag('th'):wikitext('Hero')
     headerRow:tag('th'):wikitext('Hero')
     headerRow:tag('th'):wikitext('% per shot')
     headerRow:tag('th'):wikitext('% per shot')
    headerRow:tag('th'):wikitext('Shots to Proc') -- New Column


     for _, h in ipairs(hero_list) do
     for _, h in ipairs(hero_list) do
         local row = html:tag('tr')
         local row = html:tag('tr')
        -- Expand HeroIcon template
         local icon_template = frame:expandTemplate{ title = 'HeroIcon', args = { h.name } }
         local icon_template = frame:expandTemplate{ title = 'HeroIcon', args = { h.name } }
         row:tag('td'):wikitext(icon_template)
         row:tag('td'):wikitext(icon_template)
         row:tag('td'):wikitext(string.format("%.1f", h.bps))
         row:tag('td'):wikitext(string.format("%.1f", h.bps))
        row:tag('td'):wikitext(tostring(h.shots)) -- New Value
     end
     end



Revision as of 18:34, 17 January 2026

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

local p = {}

-- Load Data Files
local heroes_data = mw.loadJsonData("Data:HeroData.json")
local items_data = mw.loadJsonData("Data:ItemData.json")

--------------------------------------------------------------------------------
-- HELPER FUNCTIONS (Logic only, no Frame stuff)
--------------------------------------------------------------------------------

local function get_item_bb(target_name)
    -- Clean whitespace
    target_name = mw.text.trim(target_name)
    
    if tonumber(target_name) then return tonumber(target_name) end

    for key, item in pairs(items_data) do
        if item["Name"] == target_name then
            -- Deep nested check
            if item["m_mapAbilityProperties"] and item["m_mapAbilityProperties"]["BuildUpPerShot"] then
                return tonumber(item["m_mapAbilityProperties"]["BuildUpPerShot"]["m_strValue"])
            end
            -- Flat check
            if item["BuildUpPerShot"] then
                return tonumber(item["BuildUpPerShot"])
            end
        end
    end
    return nil
end

local function get_hero_data(hero_name)
    hero_name = mw.text.trim(hero_name)
    
    -- Try direct Key
    if heroes_data[hero_name] then return heroes_data[hero_name] end
    -- Try "hero_" prefix
    if heroes_data["hero_" .. string.lower(hero_name)] then return heroes_data["hero_" .. string.lower(hero_name)] end
    
    -- Search by Name field
    for k, v in pairs(heroes_data) do
        if v["Name"] == hero_name then return v end
    end
    return nil
end

local function calculate_bps(hero, bb)
    if not hero or not hero.Weapon then return 0 end

    local rps = tonumber(hero.Weapon.RoundsPerSecond or 0)
    local spin_rps = tonumber(hero.Weapon.RoundsPerSecondAtMaxSpin or 0)
    
    -- Handle Spin-up logic (McGinnis/Bebop)
    if spin_rps > 0 then rps = spin_rps end

    if rps == 0 then return 0 end

    -- Formula: 100 / (BB * (RPS + 1))
    return 100 / (bb * (rps + 1))
end

--------------------------------------------------------------------------------
-- OUTPUT GENERATORS
--------------------------------------------------------------------------------

local function generate_single_value(hero_name, item_name)
    local hero = get_hero_data(hero_name)
    if not hero then return "Hero Not Found" end

    local bb = get_item_bb(item_name)
    if not bb then return "Item Not Found" end

    local result = calculate_bps(hero, bb)
    return string.format("%.1f", result)
end

local function generate_table(item_name, frame)
    local bb = get_item_bb(item_name)
    if not bb then return "Error: Item '" .. item_name .. "' not found." end

    local hero_list = {}
    for key, hero in pairs(heroes_data) do
        local is_dev = hero["InDevelopment"] == true
        local is_disabled = hero["IsDisabled"] == true
        
        if not is_dev and not is_disabled and hero["Weapon"] then
            local val = calculate_bps(hero, bb)
            if val > 0 then
                -- Calculate shots needed using the raw, unrounded value
                -- math.ceil rounds 5.0001 up to 6
                local shots = math.ceil(100 / val)
                
                table.insert(hero_list, {
                    name = hero["Name"],
                    bps = val,
                    shots = shots
                })
            end
        end
    end

    table.sort(hero_list, function(a, b) return a.name < b.name end)

    local html = mw.html.create('table')
    html:addClass('wikitable sortable mw-collapsible')
    html:tag('caption'):wikitext('Buildup Per Shot: ' .. item_name)

    local headerRow = html:tag('tr')
    headerRow:tag('th'):wikitext('Hero')
    headerRow:tag('th'):wikitext('% per shot')
    headerRow:tag('th'):wikitext('Shots to Proc') -- New Column

    for _, h in ipairs(hero_list) do
        local row = html:tag('tr')
        local icon_template = frame:expandTemplate{ title = 'HeroIcon', args = { h.name } }
        row:tag('td'):wikitext(icon_template)
        row:tag('td'):wikitext(string.format("%.1f", h.bps))
        row:tag('td'):wikitext(tostring(h.shots)) -- New Value
    end

    return tostring(html)
end

--------------------------------------------------------------------------------
-- MAIN TRAFFIC COP
--------------------------------------------------------------------------------

function p.main(frame)
    local args = frame:getParent().args
    -- If called via #invoke directly with no parent template
    if not args[1] and not args['item'] then args = frame.args end

    local arg1 = args[1] or args['hero']
    local arg2 = args[2] or args['item']

    -- LOGIC:
    -- If 2 args provided -> It's Hero + Item -> Return Single Number
    -- If 1 arg provided  -> It's Item Only   -> Return Table

    if arg1 and arg2 then
        return generate_single_value(arg1, arg2)
    elseif arg1 then
        return generate_table(arg1, frame)
    else
        return "Error: Please provide an Item Name (for table) or Hero + Item (for single stat)."
    end
end

return p