Module:HeroDataArraysGive feedback
Overview
This module consists of one generic function, heroDataArray, and a series of functions that create specific wikitables using the output from heroDataArray.
local p = {};
local util_module = require('Module:Utilities')
local allHeroData = mw.loadJsonData("Data:HeroData.json")
local soul_unlock = require('Module:SoulUnlock')
local inGameHeroData = {}
-- Just get heroes that are playable and have a "name" in HeroData
for i, heroData in pairs(allHeroData) do
if heroData["InDevelopment"] == false then
if heroData["IsDisabled"] == false then
if heroData["Name"] ~= nil then
table.insert(inGameHeroData, heroData)
end
end
end
end
-- Use sort name for edge cases like "The Doorman"
local function sortHeroes(rows)
local function getSortName(name)
return (name:gsub("^The ", ""))
end
table.sort(rows, function(a, b)
local nameA = getSortName(a.sortName or a.name or "")
local nameB = getSortName(b.sortName or b.name or "")
return nameA:lower() < nameB:lower()
end)
end
-- Helper function to round numeric values
local function roundValue(value)
-- if type(value) == "number" then
-- return util_module.round_to_sig_fig(value, 3)
-- end
return value
end
-- Round to an integer (up to 2 digits)
local function roundValueInteger(value)
if type(value) == "number" then
return util_module.round_to_sig_fig(value, 2)
end
return value
end
-- Generate tables from given properties
local function buildTable(frame, rows, tableDef, options)
options = options or {}
-- option to ignore heroes who don't have any of the given properties. true by default
local filterZero = options.filterZero or true
local filteredRows = {}
for _, r in ipairs(rows) do
local hasValue = not filterZero
if filterZero then
for _, col in ipairs(tableDef.columns) do
if r[col.field] ~= 0 and r[col.field] ~= nil then
if r[col.field] ~= "+0" then
hasValue = true
break
end
end
end
end
if hasValue then
table.insert(filteredRows, r)
end
end
if #filteredRows == 0 then
return ""
end
-- Build header
local header = ""
for _, col in ipairs(tableDef.columns) do
header = header .. " !! " .. col.label
end
-- Start table
local t = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n" ..
"|+ " .. tableDef.title .. " \n" ..
"! Hero" .. header .. " \n"
-- Rows
for _, r in ipairs(filteredRows) do
local cells = ""
for _, col in ipairs(tableDef.columns) do
cells = cells .. " || " .. (r[col.field] or 0)
end
-- A row may carry an alternate sort key and a label suffix, used by
-- secondary fire rows so they stay underneath their hero's main row
local nameCell = "| style=\"text-align:left;\""
if r.sortName then
nameCell = nameCell .. " data-sort-value=\"" .. r.sortName .. "\""
end
nameCell = nameCell .. " | " ..
frame:expandTemplate{ title = "PageRef", args = { r.name } } ..
(r.nameSuffix or "")
t = t ..
"|- \n" ..
nameCell ..
cells .. " \n"
end
t = t .. "|}"
return t
end
-- heroDataArray takes as argument a table of any length containing keys or
-- paths of keys in the HeroData JSON, e.g. {Name, MaxHealth, LevelScaling>Health}.
-- A path of keys should be specified as such, with the ">" character separating
-- subsequent keys.
local heroDataArray = function(args)
local outData = {}
for i, heroData in ipairs(inGameHeroData) do -- Iterate over each hero
local out = {} -- First creating a table to hold data
for j, key in pairs(args) do -- Iterate over each key:
if string.find(key, ">") ~= nil then -- If the key is actually a path of keys,
local node = heroData -- start with the biggest node heroData, and
for k in string.gmatch(key, "([^>]+)") do -- iterate over each key, splitting on ">",
if node and node[k] ~= nil then -- trying the key as a string e.g. "1", (Added 'node and' for safety)
node = node[k]
elseif node and node[tonumber(k)] ~= nil then -- else using the key as numeric, (Added 'node and' for safety)
node = node[tonumber(k)]
else -- If at any point the path breaks, the result is nil
node = nil
break
end -- drilling down the JSON node after node
end
out[key] = roundValue(node) -- until outputting the final node (rounded)
else
out[key] = roundValue(heroData[key]) -- or just grab the data if not a path (rounded)
end
end
outData[heroData["Name"]] = out -- Save data in the big table of heroes
end
return(outData)
end
p.heroHealthTable = function(frame)
-- Pull data
local heroHealthData = heroDataArray({"Name", "MaxHealth", "LevelScaling>MaxHealth"})
local rows = {}
for name, data in pairs(heroHealthData) do
table.insert(rows, {
name = name,
maxHealth = data["MaxHealth"] or 0,
scalingHealth = "+" .. data["LevelScaling>MaxHealth"] or 0,
maxScalingHealth = roundValue(data["MaxHealth"] + data["LevelScaling>MaxHealth"] * soul_unlock.get_max("PowerIncrease")) or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Health Stats",
columns = {
{ label = "Starting", field = "maxHealth" },
{ label = "Added per Boon", field = "scalingHealth" },
{ label = "At Max Boon", field = "maxScalingHealth" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroRegenTable = function(frame)
local heroRegenData = heroDataArray({"Name", "BaseHealthRegen", "SpiritScaling>BaseHealthRegen"})
local rows = {}
for name, data in pairs(heroRegenData) do
table.insert(rows, {
name = name,
baseHealthRegen = data["BaseHealthRegen"] or 0,
spiritHealthRegen = "+".. (data["SpiritScaling>BaseHealthRegen"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Health Regen Stats",
columns = {
{ label = "HP/s", field = "baseHealthRegen" },
{ label = "Spirit Scaling", field = "spiritHealthRegen" }
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroDpsTable = function(frame)
local heroDpsData = heroDataArray({"Name","Weapon>DPS","Weapon>ReloadSingle","Weapon>ReloadDelay","Weapon>ReloadTime",
"Weapon>ClipSize","Weapon>RoundsPerSecond","Weapon>BulletDamage","Weapon>BulletsPerShot","Weapon>AltFire>DPS",
"Weapon>AltFire>AmmoConsumedPerShot","Weapon>AltFire>RoundsPerSecond","Weapon>AltFire>BulletDamage","Weapon>AltFire>BulletsPerShot"})
local rows = {}
for name, data in pairs(heroDpsData) do
table.insert(rows, {
name = name,
DPS = data["Weapon>DPS"] or 0,
reloadDelay = data["Weapon>ReloadDelay"] or 0,
reloadTime = data["Weapon>ReloadTime"] or 0,
clipSize = data["Weapon>ClipSize"] or 0,
roundsPerSecond = data["Weapon>RoundsPerSecond"] or 0,
bulletDamage = data["Weapon>BulletDamage"] or 0,
bulletsPerShot = data["Weapon>BulletsPerShot"] or 0,
altDPS = data["Weapon>AltFire>DPS"] or 0,
altAmmoConsumedPerShot = data["Weapon>AltFire>AmmoConsumedPerShot"] or 0,
altRoundsPerSecond = data["Weapon>AltFire>RoundsPerSecond"] or 0,
altBulletDamage = data["Weapon>AltFire>BulletDamage"] or 0,
altBulletsPerShot = data["Weapon>AltFire>BulletsPerShot"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Damage Stats",
columns = {
{ label = "Damage per sec", field = "DPS" },
{ label = "Reload Delay (s)", field = "reloadDelay" },
{ label = "Reload Time (s)", field = "reloadTime" },
{ label = "Ammo", field = "clipSize" },
{ label = "Bullets per sec", field = "roundsPerSecond" },
{ label = "Bullet Damage", field = "bulletDamage" },
{ label = "Bullets Per Shot", field = "bulletsPerShot" },
}
},
{
title = "Base Alt-Fire Damage Stats",
columns = {
{ label = "Damage per sec", field = "altDPS" },
{ label = "Ammo Per Shot", field = "altAmmoConsumedPerShot" },
{ label = "Bullets per sec", field = "altRoundsPerSecond" },
{ label = "Bullet Damage", field = "altBulletDamage" },
{ label = "Bullets Per Shot", field = "altBulletsPerShot" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroBulletSpeedTable = function(frame)
local heroBulletSpeedData = heroDataArray({
"Name",
"Weapon>BulletSpeed",
"Weapon>AltFire>BulletSpeed"
})
local rows = {}
for name, data in pairs(heroBulletSpeedData) do
table.insert(rows, {
name = name,
bulletSpeed = data["Weapon>BulletSpeed"] or 0,
altBulletSpeed = data["Weapon>AltFire>BulletSpeed"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Bullet Velocity",
columns = {
{ label = "m/s", field = "bulletSpeed" }
}
},
{
title = "Base Alt-Fire Bullet Velocity",
columns = {
{ label = "m/s", field = "altBulletSpeed" }
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroClipSizeTable = function(frame)
local heroClipSizeData = heroDataArray({"Name", "Weapon>ClipSize","Weapon>BulletsPerBurst","Weapon>AltFire>AmmoConsumedPerShot","SpiritScaling>ClipSize"})
local rows = {}
for name, data in pairs(heroClipSizeData) do
table.insert(rows, {
name = name,
clipSize = data["Weapon>ClipSize"] or 0,
spiritClipSize = "+".. (data["SpiritScaling>ClipSize"] or 0),
bulletsPerBurst = data["Weapon>BulletsPerBurst"] or 0,
burstClipSize = data["Weapon>ClipSize"] or 0 / data["Weapon>BulletsPerBurst"] or 0,
ammoConsumedPerShot = data["Weapon>AltFire>AmmoConsumedPerShot"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Ammo Count",
columns = {
{ label = "Ammo", field = "clipSize" },
{ label = "Ammo Per Shot", field = "bulletsPerBurst" },
{ label = "Spirit Scaling", field = "spiritClipSize" },
}
},
{
title = "Alt-Fire Ammo Cost",
columns = {
{ label = "Ammo Per Shot", field = "ammoConsumedPerShot" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroRoundsPerSecondTable = function(frame)
local heroRoundsPerSecondData = heroDataArray({"Name", "Weapon>RoundsPerSecond", "Weapon>AltFire>RoundsPerSecond", "SpiritScaling>RoundsPerSecond", "SpiritScaling>FireRate"})
local rows = {}
for name, data in pairs(heroRoundsPerSecondData) do
table.insert(rows, {
name = name,
roundsPerSecond = data["Weapon>RoundsPerSecond"] or 0,
altRoundsPerSecond = data["Weapon>AltFire>RoundsPerSecond"] or 0,
spiritRoundsPerSecond = "+".. (data["SpiritScaling>RoundsPerSecond"] or 0),
spiritFireRate = "+".. (data["SpiritScaling>FireRate"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Fire Rate",
columns = {
{ label = "Rounds/s", field = "roundsPerSecond" },
{ label = "Spirit Scaling", field = "spiritRoundsPerSecond" },
}
},
{
title = "Base Alt-Fire Fire Rate",
columns = {
{ label = "Rounds/s", field = "altRoundsPerSecond" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroFalloffRangeTable = function(frame)
local heroFalloffRangeData = heroDataArray({"Name", "Weapon>FalloffStartRange", "Weapon>FalloffEndRange", "Weapon>AltFire>FalloffStartRange", "Weapon>AltFire>FalloffEndRange",
"Weapon>AltFire>ExplosionRadius", "Weapon>FalloffEndScale", "Weapon>AltFire>ExplosionDamageScaleAtMaxRadius",})
local rows = {}
for name, data in pairs(heroFalloffRangeData) do
table.insert(rows, {
name = name,
falloffStartRange = data["Weapon>FalloffStartRange"] or 0,
falloffEndRange = data["Weapon>FalloffEndRange"] or 0,
falloffEndScale = -100 + (100 * (data["Weapon>FalloffEndScale"] or 0)),
altFalloffStartRange = data["Weapon>AltFire>FalloffStartRange"] or 0,
altFalloffEndRange = data["Weapon>AltFire>FalloffEndRange"] or 0,
altExplosionRadius = data["Weapon>AltFire>ExplosionRadius"] or 0,
altExplosionEndScale = (100 * (data["Weapon>AltFire>ExplosionDamageScaleAtMaxRadius"] or 0))
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Falloff Range",
columns = {
{ label = "Minimum Range (m)", field = "falloffStartRange" },
{ label = "Maximum Range (m)", field = "falloffEndRange" },
{ label = "Damage Falloff (%)", field = "falloffEndScale" },
}
} ,
{
title = "Base Alt-Fire Falloff Range",
columns = {
{ label = "Minimum Range (m)", field = "altFalloffStartRange" },
{ label = "Maximum Range (m)", field = "altFalloffEndRange" },
{ label = "Explosion Radius (m)", field = "altExplosionRadius" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroBulletDamageTable = function(frame)
-- Pull data
local heroBulletDamageData = heroDataArray({"Name", "Weapon>BulletDamage", "LevelScaling>BulletDamage", "Weapon>AltFire>BulletDamage", "LevelScaling>BulletDamageAltFire",
"SpiritScaling>BulletDamage", "Weapon>BulletsPerShot", "Weapon>BulletsPerBurst"})
local rows = {}
for name, data in pairs(heroBulletDamageData) do
table.insert(rows, {
name = name,
bulletDamage = data["Weapon>BulletDamage"] or 0,
bulletDamagePerShot = ((data["Weapon>BulletDamage"] or 0) * (data["Weapon>BulletsPerShot"] or 0) * (data["Weapon>BulletsPerBurst"] or 0)),
scalingDamage = "+" .. data["LevelScaling>BulletDamage"] or 0,
maxScalingDamage = (roundValue(data["Weapon>BulletDamage"] + data["LevelScaling>BulletDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0,
altBulletDamage = data["Weapon>AltFire>BulletDamage"] or 0,
spiritScalingDamage = "+" .. (data["SpiritScaling>BulletDamage"] or 0),
})
-- Secondary fire is listed as its own row, right under the hero's main row
local altDamage = data["Weapon>AltFire>BulletDamage"]
if altDamage and altDamage ~= 0 then
local altScaling = data["LevelScaling>BulletDamageAltFire"] or 0
table.insert(rows, {
name = name,
sortName = name .. " (alt fire)",
nameSuffix = " (alt fire)",
bulletDamage = altDamage,
scalingDamage = "+" .. altScaling,
maxScalingDamage = roundValue(altDamage + altScaling * soul_unlock.get_max("PowerIncrease")),
spiritScalingDamage = "+0",
})
end
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Bullet Damage Stats",
columns = {
{ label = "Starting", field = "bulletDamage" },
{ label = "Added per Boon", field = "scalingDamage" },
{ label = "At Max Boon", field = "maxScalingDamage" },
{ label = "Spirit Scaling", field = "spiritScalingDamage" },
}
},
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroMeleeTable = function(frame)
-- Pull data
local heroMeleeData = heroDataArray({"Name", "LightMeleeDamage", "LevelScaling>LightMeleeDamage", "HeavyMeleeDamage", "LevelScaling>HeavyMeleeDamage", "SpiritScaling>HeavyMeleeDamage"})
local rows = {}
for name, data in pairs(heroMeleeData) do
table.insert(rows, {
name = name,
lightMeleeDamage = data["LightMeleeDamage"] or 0,
lightScalingDamage = "+" .. data["LevelScaling>LightMeleeDamage"] or 0,
lightMaxScalingDamage = (roundValue(data["LightMeleeDamage"] + data["LevelScaling>LightMeleeDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0,
heavyMeleeDamage = data["HeavyMeleeDamage"] or 0,
heavyScalingDamage = "+" .. data["LevelScaling>HeavyMeleeDamage"] or 0,
heavyMaxScalingDamage = (roundValue(data["HeavyMeleeDamage"] + data["LevelScaling>HeavyMeleeDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0,
heavySpiritScalingDamage = "+" .. (data["SpiritScaling>HeavyMeleeDamage"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Light & Heavy Melee Stats",
columns = {
{ label = "Starting Light", field = "lightMeleeDamage" },
{ label = "Added per Boon (L)", field = "lightScalingDamage" },
{ label = "At Max Boon (L)", field = "lightMaxScalingDamage" },
{ label = "Starting Heavy", field = "heavyMeleeDamage" },
{ label = "Added per Boon (H)", field = "heavyScalingDamage" },
{ label = "At Max Boon (H)", field = "heavyMaxScalingDamage" },
{ label = "Spirit Scaling (H)", field = "heavySpiritScalingDamage" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroLightMeleeTable = function(frame)
-- Pull data
local heroLightMeleeData = heroDataArray({"Name", "LightMeleeDamage", "LevelScaling>LightMeleeDamage"})
local rows = {}
for name, data in pairs(heroLightMeleeData) do
table.insert(rows, {
name = name,
lightMeleeDamage = data["LightMeleeDamage"] or 0,
scalingDamage = "+" .. data["LevelScaling>LightMeleeDamage"] or 0,
maxScalingDamage = (roundValue(data["LightMeleeDamage"] + data["LevelScaling>LightMeleeDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0,
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Light Melee Stats",
columns = {
{ label = "Starting", field = "lightMeleeDamage" },
{ label = "Added per Boon", field = "scalingDamage" },
{ label = "At Max Boon", field = "maxScalingDamage" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroHeavyMeleeTable = function(frame)
-- Pull data
local heroHeavyMeleeData = heroDataArray({"Name", "HeavyMeleeDamage", "LevelScaling>HeavyMeleeDamage", "SpiritScaling>HeavyMeleeDamage"})
local rows = {}
for name, data in pairs(heroHeavyMeleeData) do
table.insert(rows, {
name = name,
heavyMeleeDamage = data["HeavyMeleeDamage"] or 0,
scalingDamage = "+" .. data["LevelScaling>HeavyMeleeDamage"] or 0,
maxScalingDamage = (roundValue(data["HeavyMeleeDamage"] + data["LevelScaling>HeavyMeleeDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0,
spiritScalingDamage = "+" .. (data["SpiritScaling>HeavyMeleeDamage"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Heavy Melee Stats",
columns = {
{ label = "Starting", field = "heavyMeleeDamage" },
{ label = "Added per Boon", field = "scalingDamage" },
{ label = "At Max Boon", field = "maxScalingDamage" },
{ label = "Spirit Scaling", field = "spiritScalingDamage" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroStaminaTable = function(frame)
local heroStaminaData = heroDataArray({"Name", "Stamina", "StaminaCooldown"})
local rows = {}
for name, data in pairs(heroStaminaData) do
table.insert(rows, {
name = name,
stamina = data["Stamina"] or 0,
staminaCooldown = data["StaminaCooldown"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Stamina Stats",
columns = {
{ label = "Stamina Bars", field = "stamina" },
{ label = "Cooldown (s)", field = "staminaCooldown" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroDashTable = function(frame)
local heroDashData = heroDataArray({"Name", "GroundDashSpeed", "GroundDashDuration", "AirDashSpeed", "AirDashDuration"})
local rows = {}
for name, data in pairs(heroDashData) do
table.insert(rows, {
name = name,
dashSpeed = data["GroundDashSpeed"] or 0,
dashDuration = data["GroundDashDuration"] or 0,
airDashSpeed = data["AirDashSpeed"] or 0,
airDashDuration = data["AirDashDuration"] or 0,
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Dash Speed Stats",
columns = {
{ label = "Dash Speed (m/s)", field = "dashSpeed" },
{ label = "Dash Duration (s)", field = "dashDuration" },
{ label = "Air Dash Speed (m/s)", field = "airDashSpeed" },
{ label = "Air Dash Duration (s)", field = "airDashDuration" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroReloadTable = function(frame)
local heroReloadData = heroDataArray({"Name", "Weapon>ReloadTime"})
local rows = {}
for name, data in pairs(heroReloadData) do
table.insert(rows, {
name = name,
reloadTime = data["Weapon>ReloadTime"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Reload Time",
columns = {
{ label = "Reload Time (s)", field = "reloadTime" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroReloadDelayTable = function(frame)
local heroReloadDelayData = heroDataArray({"Name", "Weapon>ReloadDelay"})
local rows = {}
for name, data in pairs(heroReloadDelayData) do
table.insert(rows, {
name = name,
reloadDelay = data["Weapon>ReloadDelay"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Reload Delay",
columns = {
{ label = "Reload Delay (s)", field = "reloadDelay" }
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroMoveSpeedTable = function(frame)
local heroMoveSpeedData = heroDataArray({"Name", "MaxMoveSpeed", "SprintSpeed", "Weapon>ShootMoveSpeed", "SpiritScaling>MaxMoveSpeed", "SpiritScaling>SprintSpeed"})
local rows = {}
for name, data in pairs(heroMoveSpeedData) do
table.insert(rows, {
name = name,
moveSpeed = data["MaxMoveSpeed"] or 0,
sprintSpeed = data["SprintSpeed"] or 0,
totalSpeed = data["MaxMoveSpeed"] + data["SprintSpeed"] or 0,
shootSpeed = (data["Weapon>ShootMoveSpeed"] or 0) * 100,
totalShootingSpeed = roundValue((data["MaxMoveSpeed"] + data["SprintSpeed"] or 0) * (data["Weapon>ShootMoveSpeed"] or 0)),
spiritMoveSpeed = "+" .. (data["SpiritScaling>MaxMoveSpeed"] or 0),
spiritSprintSpeed = "+" .. (data["SpiritScaling>SprintSpeed"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Move Speed Stats",
columns = {
{ label = "Move Speed (m/s)", field = "moveSpeed" },
{ label = "Sprint Speed (m/s)", field = "sprintSpeed" },
{ label = "Total Speed (m/s)", field = "totalSpeed" },
{ label = "Shooting Speed (m/s)", field = "totalShootingSpeed" },
{ label = "Shooting Mult (%)", field = "shootSpeed" },
{ label = "MS Spirit Scaling", field = "spiritMoveSpeed" },
{ label = "SS Spirit Scaling", field = "spiritSprintSpeed" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroDamageResistTable = function(frame)
local heroDamageResistData = heroDataArray({"Name", "BulletResist", "LevelScaling>BulletResist", "TechResist", "LevelScaling>TechResist", "MeleeResist", "CritDamageReceivedPercent",
"SpiritScaling>BulletResist", "SpiritScaling>TechResist",
})
local rows = {}
for name, data in pairs(heroDamageResistData) do
table.insert(rows, {
name = name,
bulletResist = data["BulletResist"] or 0,
bulletResistScaling = "+".. (data["LevelScaling>BulletResist"] or 0),
bulletResistScalingMax = ((roundValue((data["BulletResist"] or 0) + (data["LevelScaling>BulletResist"] or 0) * soul_unlock.get_max("PowerIncrease"))) or 0),
bulletResistSpiritScaling = "+".. (data["SpiritScaling>BulletResist"] or 0),
spiritResist = data["TechResist"] or 0,
spiritResistScaling = "+".. (data["LevelScaling>TechResist"] or 0),
spiritResistScalingMax = ((roundValue((data["TechResist"] or 0) + (data["LevelScaling>TechResist"] or 0) * soul_unlock.get_max("PowerIncrease"))) or 0),
spiritResistSpiritScaling = "+".. (data["SpiritScaling>TechResist"] or 0),
meleeResist = data["MeleeResist"] or 0,
critReduction = data["CritDamageReceivedPercent"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Bullet Resist Stats",
columns = {
{ label = "Starting (%)", field = "bulletResist" },
{ label = "Added per Boon (%)", field = "bulletResistScaling" },
{ label = "At Max Boon (%)", field = "bulletResistScalingMax" },
{ label = "Spirit Scaling (%)", field = "bulletResistSpiritScaling" },
}
},
{
title = "Base Spirit Resist Stats",
columns = {
{ label = "Starting (%)", field = "spiritResist" },
{ label = "Added per Boon (%)", field = "spiritResistScaling" },
{ label = "At Max Boon (%)", field = "spiritResistScalingMax" },
{ label = "Spirit Scaling (%)", field = "spiritResistSpiritScaling" },
}
},
{
title = "Base Melee Resist Stats",
columns = {
{ label = "Melee Resist (%)", field = "meleeResist" },
}
},
{
title = "Base Crit Reduction Stats",
columns = {
{ label = "Crit Reduction (%)", field = "critReduction" },
}
},
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroCritTable = function(frame)
local heroCritData = heroDataArray({"Name", "CritDamageBonusPercent"})
local rows = {}
for name, data in pairs(heroCritData) do
table.insert(rows, {
name = name,
critBonusScale = data["CritDamageBonusPercent"] or 0
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Crit Bonus Scale Stats",
columns = {
{ label = "Crit Bonus Scale (%)", field = "critBonusScale" }
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroSpiritTable = function(frame)
local heroSpiritData = heroDataArray({"Name", "LevelScaling>TechPower"})
local rows = {}
for name, data in pairs(heroSpiritData) do
table.insert(rows, {
name = name,
spiritScaling = "+".. data["LevelScaling>TechPower"] or 0,
maxSpiritScaling = (data["LevelScaling>TechPower"] * soul_unlock.get_max("PowerIncrease")) or 0,
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Spirit Scaling Stats",
columns = {
{ label = "Spirit Per Boon", field = "spiritScaling" },
{ label = "At Max Boon", field = "maxSpiritScaling" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.heroGravityTable = function(frame)
local heroGravityData = heroDataArray({"Name", "GravityChange"})
local rows = {}
for name, data in pairs(heroGravityData) do
table.insert(rows, {
name = name,
gravityScale = (data["GravityChange"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Gravity Scale Stats",
columns = {
{ label = "Gravity Scale (%)", field = "gravityScale" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.bulletGravityTable = function(frame)
local heroGravityData = heroDataArray({"Name", "Weapon>BulletGravityScale", "Weapon>AltFire>BulletGravityScale"})
local rows = {}
for name, data in pairs(heroGravityData) do
table.insert(rows, {
name = name,
gravityScale = (data["Weapon>BulletGravityScale"] or 0),
altGravityScale = (data["Weapon>AltFire>BulletGravityScale"] or 0),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Bullet Gravity Scale Stats",
columns = {
{ label = "Gravity Scale", field = "gravityScale" },
}
},
{
title = "Base Alt-fire Bullet Gravity Scale Stats",
columns = {
{ label = "Gravity Scale", field = "altGravityScale" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
p.bulletRadiusTable = function(frame)
local heroBulletRadiusData = heroDataArray({"Name", "Weapon>BulletRadius", "Weapon>AltFire>BulletRadius"})
local rows = {}
for name, data in pairs(heroBulletRadiusData) do
table.insert(rows, {
name = name,
bulletRadius = (data["Weapon>BulletRadius"] or 0) * 100,
bulletRadiusInches = roundValueInteger((data["Weapon>BulletRadius"] or 0) * 100 * 0.394),
altBulletRadius = (data["Weapon>AltFire>BulletRadius"] or 0) * 100,
altBulletRadiusInches = roundValueInteger((data["Weapon>AltFire>BulletRadius"] or 0) * 100 * 0.394),
})
end
sortHeroes(rows)
local tableDefs = {
{
title = "Base Bullet Radius Stats",
columns = {
{ label = "Bullet Radius (cm)", field = "bulletRadius" },
{ label = "Bullet Radius (in)", field = "bulletRadiusInches" },
}
},
{
title = "Alt-fire Bullet Radius Stats",
columns = {
{ label = "Bullet Radius (cm)", field = "altBulletRadius" },
{ label = "Bullet Radius (in)", field = "altBulletRadiusInches" },
}
}
}
local output = {}
for _, def in ipairs(tableDefs) do
local t = buildTable(frame, rows, def, def.options)
if t ~= "" then
table.insert(output, t)
end
end
return table.concat(output, "\n\n")
end
return p