Module:Sandbox/LVLGive feedback
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