Module:Buildup: Difference between revisions

LVL (talk | contribs)
Module for item buildup stats
 
LVL (talk | contribs)
No edit summary
Line 55: Line 55:
-- Core calculation logic
-- Core calculation logic
local function calculate_bps(hero, bb)
local function calculate_bps(hero, bb)
if not hero or not hero.Weapon then return 0 end
    if not hero or not hero.Weapon then return 0 end


local rps = tonumber(hero.Weapon.RoundsPerSecond or 0)
    local rps = tonumber(hero.Weapon.RoundsPerSecond or 0)
local spin_rps = tonumber(hero.Weapon.RoundsPerSecondAtMaxSpin or 0)
    local spin_rps = tonumber(hero.Weapon.RoundsPerSecondAtMaxSpin or 0)
   
-- Handle Spin-up logic (McGinnis/Bebop)
    -- 1. Handle Spin-up (McGinnis/Bebop)
-- If they have a max spin speed, use that instead of base RPS
    if spin_rps > 0 then  
if spin_rps > 0 then rps = spin_rps end
        rps = spin_rps  
    end


if rps == 0 then return 0 end
    -- 2. Handle "Fake Burst" Logic (Paige/Doorman)
    -- If they have a burst interval, but only 1 bullet per burst,
    -- the game ignores that interval for Buildup calculation.
    local burst_count = tonumber(hero.Weapon.BulletsPerBurst or 1)
    local burst_interval = tonumber(hero.Weapon.BurstInterShotInterval or 0)


-- Formula: 100 / (BB * (RPS + 1))
    if burst_count == 1 and burst_interval > 0 and rps > 0 then
return 100 / (bb * (rps + 1))
        -- Convert RPS back to time
        local total_time = 1 / rps
        -- Remove the interval
        local effective_time = total_time - burst_interval
       
        -- Prevent division by zero if something is weird
        if effective_time > 0.001 then
            rps = 1 / effective_time
        end
    end
 
    if rps == 0 then return 0 end
 
    -- Formula: 100 / (BB * (RPS + 1))
    return 100 / (bb * (rps + 1))
end
end


Line 89: Line 108:
--Generates a sortable wikitable for all heroes
--Generates a sortable wikitable for all heroes
p.write_buildup_table = function(frame)
p.write_buildup_table = function(frame)
local item_name = frame.args[1]
    local item_name = frame.args[1]
   
local bb = get_item_bb(item_name)
    local bb = get_item_bb(item_name)
if not bb then return "Error: Item '" .. (item_name or "nil") .. "' not found." end
    if not bb then return "Error: Item '" .. (item_name or "nil") .. "' 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
                local shots = math.ceil(100 / val)
               
                table.insert(hero_list, {
                    name = hero["Name"],
                    bps = val,
                    shots = shots
                })
            end
        end
    end


local hero_list = {}
    -- Sorting Logic
    table.sort(hero_list, function(a, b)  
-- Collect valid heroes
        -- Helper to strip "The " from names just for sorting
for key, hero in pairs(heroes_data) do
        local function clean_sort_name(name)
local is_dev = hero["InDevelopment"] == true
            if string.sub(name, 1, 4) == "The " then
local is_disabled = hero["IsDisabled"] == true
                return string.sub(name, 5)
            end
if not is_dev and not is_disabled and hero["Weapon"] then
            return name
local val = calculate_bps(hero, bb)
        end
if val > 0 then
-- Calculate shots needed (Rounding UP is crucial for 19.99% edge cases)
local shots = math.ceil(100 / val)
table.insert(hero_list, {
name = hero["Name"],
bps = val,
shots = shots
})
end
end
end


-- Sort Alphabetically
        return clean_sort_name(a.name) < clean_sort_name(b.name)
table.sort(hero_list, function(a, b) return a.name < b.name end)
    end)


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


-- Headers
    -- Headers
local headerRow = html:tag('tr')
    local headerRow = html:tag('tr')
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')
    headerRow:tag('th'):wikitext('Shots to Proc')


-- Rows
    -- Rows
for _, h in ipairs(hero_list) do
    for _, h in ipairs(hero_list) do
local row = html:tag('tr')
        local row = html:tag('tr')
        local icon_template = frame:expandTemplate{ title = 'HeroIcon', args = { h.name } }
-- Expand HeroIcon template
       
local icon_template = frame:expandTemplate{ title = 'HeroIcon', args = { h.name } }
        -- Use data-sort-value so the table sorts correctly even with the icon
        row:tag('td')
row:tag('td'):wikitext(icon_template)
            :attr('data-sort-value', h.name) -- Uses the name including "The" for consistent default sort
row:tag('td'):wikitext(string.format("%.1f", h.bps))
            :wikitext(icon_template)
row:tag('td'):wikitext(tostring(h.shots))
           
end
        row:tag('td'):wikitext(string.format("%.1f", h.bps))
        row:tag('td'):wikitext(tostring(h.shots))
    end


return tostring(html)
    return tostring(html)
end
end