Module:HeroDataArrays: Difference between revisionsGive feedback
Gammaton32 (talk | contribs) melee, updated max boon count |
Gammaton32 (talk | contribs) added alt fire stats to all tables where applicable |
||
| (79 intermediate revisions by 5 users not shown) | |||
| Line 5: | Line 5: | ||
local inGameHeroData = {} | local inGameHeroData = {} | ||
-- Just get heroes that are playable | -- Just get heroes that are playable and have a "name" in HeroData | ||
for i, heroData in pairs(allHeroData) do | for i, heroData in pairs(allHeroData) do | ||
if heroData["InDevelopment"] == false then | |||
if heroData["IsDisabled"] == false then | if heroData["IsDisabled"] == false then | ||
if heroData["Name"] ~= nil then | |||
table.insert(inGameHeroData, heroData) | table.insert(inGameHeroData, heroData) | ||
end | |||
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 | end | ||
| Line 18: | Line 35: | ||
end | end | ||
return value | return value | ||
end | |||
-- Round to 4 digits | |||
local function roundValueFour(value) | |||
if type(value) == "number" then | |||
return util_module.round_to_sig_fig(value, 4) | |||
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 | end | ||
| Line 33: | Line 136: | ||
local node = heroData -- start with the biggest node heroData, and | local node = heroData -- start with the biggest node heroData, and | ||
for k in string.gmatch(key, "([^>]+)") do -- iterate over each key, splitting on ">", | for k in string.gmatch(key, "([^>]+)") do -- iterate over each key, splitting on ">", | ||
if node[k] ~= nil then | if node and node[k] ~= nil then -- trying the key as a string e.g. "1", (Added 'node and' for safety) | ||
node = node[k] | 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)] | 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 -- drilling down the JSON node after node | ||
end | end | ||
| Line 51: | Line 157: | ||
p.heroHealthTable = function(frame) | p.heroHealthTable = function(frame) | ||
-- Pull data | -- Pull data | ||
heroHealthData = heroDataArray({"Name", "MaxHealth", "LevelScaling>MaxHealth"}) | 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 = (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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroRegenTable = function(frame) | p.heroRegenTable = function(frame) | ||
heroRegenData = heroDataArray({"Name", "BaseHealthRegen"}) | 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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroDpsTable = function(frame) | p.heroDpsTable = function(frame) | ||
heroDpsData = heroDataArray({"Name","ReloadSingle","ReloadDelay","ReloadTime", | local heroDpsData = heroDataArray({"Name","Weapon>DPS","Weapon>ReloadSingle","Weapon>ReloadDelay","Weapon>ReloadTime", | ||
"ClipSize","RoundsPerSecond","BulletDamage","BulletsPerShot"}) | "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 | |||
}) | |||
local altBulletDamage = data["Weapon>AltFire>BulletDamage"] | |||
local altDPS = data["Weapon>AltFire>DPS"] | |||
local altRoundsPerSecond = data["Weapon>AltFire>RoundsPerSecond"] | |||
local altBulletsPerShot = data["Weapon>AltFire>BulletsPerShot"] | |||
if altBulletDamage and altBulletDamage ~= 0 then | |||
" | table.insert(rows, { | ||
" | name = name, | ||
sortName = name .. " (alt fire)", | |||
nameSuffix = " (alt fire)", | |||
DPS = altDPS, | |||
reloadDelay = data["Weapon>ReloadDelay"] or 0, | |||
reloadTime = data["Weapon>ReloadTime"] or 0, | |||
clipSize = data["Weapon>ClipSize"] or 0, | |||
roundsPerSecond = altRoundsPerSecond, | |||
bulletDamage = altBulletDamage, | |||
bulletsPerShot = altBulletsPerShot | |||
}) | |||
end | |||
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" }, | |||
} | |||
} | |||
} | |||
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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroBulletSpeedTable = function(frame) | p.heroBulletSpeedTable = function(frame) | ||
heroBulletSpeedData = heroDataArray({"Name", "BulletSpeed"}) | 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 | |||
}) | |||
local altBulletSpeed = data["Weapon>AltFire>BulletSpeed"] | |||
if altBulletSpeed and altBulletSpeed ~= 0 then | |||
table.insert(rows, { | |||
name = name, | |||
sortName = name .. " (alt fire)", | |||
nameSuffix = " (alt fire)", | |||
bulletSpeed = altBulletSpeed | |||
}) | |||
end | |||
end | |||
sortHeroes(rows) | |||
local tableDefs = { | |||
{ | |||
title = "Base Bullet Velocity", | |||
columns = { | |||
{ label = "m/s", field = "bulletSpeed" } | |||
} | |||
} | |||
} | |||
for | 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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroClipSizeTable = function(frame) | p.heroClipSizeTable = function(frame) | ||
heroClipSizeData = heroDataArray({"Name", "ClipSize"}) | 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, | |||
for | 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 | |||
" | }) | ||
local ammoConsumedPerShot = data["Weapon>AltFire>AmmoConsumedPerShot"] | |||
if ammoConsumedPerShot and ammoConsumedPerShot ~= 0 then | |||
table.insert(rows, { | |||
name = name, | |||
sortName = name .. " (alt fire)", | |||
nameSuffix = " (alt fire)", | |||
bulletsPerBurst = ammoConsumedPerShot, | |||
clipSize = data["Weapon>ClipSize"] or 0, | |||
spiritClipSize = "+".. (data["SpiritScaling>ClipSize"] or 0), | |||
}) | |||
end | |||
end | 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" }, | |||
} | |||
} | |||
} | |||
for | 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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroRoundsPerSecondTable = function(frame) | p.heroRoundsPerSecondTable = function(frame) | ||
heroRoundsPerSecondData = heroDataArray({"Name", "RoundsPerSecond"}) | 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), | |||
}) | |||
local altRoundsPerSecond = data["Weapon>AltFire>RoundsPerSecond"] | |||
if altRoundsPerSecond and altRoundsPerSecond ~= 0 then | |||
table.insert(rows, { | |||
name = name, | |||
sortName = name .. " (alt fire)", | |||
nameSuffix = " (alt fire)", | |||
roundsPerSecond = altRoundsPerSecond, | |||
spiritRoundsPerSecond = "+".. (data["SpiritScaling>RoundsPerSecond"] or 0), | |||
}) | |||
end | |||
end | |||
sortHeroes(rows) | |||
local tableDefs = { | |||
{ | |||
title = "Base Fire Rate", | |||
columns = { | |||
{ label = "Rounds/s", field = "roundsPerSecond" }, | |||
{ label = "Spirit Scaling", field = "spiritRoundsPerSecond" }, | |||
} | |||
} | |||
} | |||
for | 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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroFalloffRangeTable = function(frame) | p.heroFalloffRangeTable = function(frame) | ||
heroFalloffRangeData = heroDataArray({"Name", "FalloffStartRange", "FalloffEndRange"}) | 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 = 0, | |||
altExplosionEndScale = (100 * (data["Weapon>AltFire>ExplosionDamageScaleAtMaxRadius"] or 0)) | |||
}) | |||
local altFalloffStartRange = data["Weapon>AltFire>FalloffStartRange"] | |||
local altFalloffEndRange = data["Weapon>AltFire>FalloffEndRange"] | |||
local altExplosionRadius = data["Weapon>AltFire>ExplosionRadius"] or 0 | |||
local altExplosionEndScale = (100 * (data["Weapon>AltFire>ExplosionDamageScaleAtMaxRadius"] or 0)) | |||
if altFalloffStartRange and altFalloffStartRange ~= 0 then | |||
table.insert(rows, { | |||
name = name, | |||
sortName = name .. " (alt fire)", | |||
nameSuffix = " (alt fire)", | |||
falloffStartRange = altFalloffStartRange, | |||
falloffEndRange = altFalloffEndRange, | |||
altExplosionRadius = data["Weapon>AltFire>ExplosionRadius"] or 0, | |||
falloffEndScale = -100 + (100 * (data["Weapon>FalloffEndScale"] or 0)) | |||
}) | |||
end | |||
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" }, | |||
{ label = "Explosion Radius (m)", field = "altExplosionRadius" }, | |||
} | |||
} | |||
} | |||
for | 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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroBulletDamageTable = function(frame) | p.heroBulletDamageTable = function(frame) | ||
-- Pull data | -- Pull data | ||
heroBulletDamageData = heroDataArray({"Name", "BulletDamage", "LevelScaling>BulletDamage"}) | 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 | for name, data in pairs(heroMeleeData) do | ||
table.insert(rows, { | |||
" | name = name, | ||
lightMeleeDamage = data["LightMeleeDamage"] or 0, | |||
lightScalingDamage = "+" .. data["LevelScaling>LightMeleeDamage"] or 0, | |||
lightMaxScalingDamage = (roundValueFour(data["LightMeleeDamage"] + data["LevelScaling>LightMeleeDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0, | |||
heavyMeleeDamage = data["HeavyMeleeDamage"] or 0, | |||
heavyScalingDamage = "+" .. data["LevelScaling>HeavyMeleeDamage"] or 0, | |||
heavyMaxScalingDamage = (roundValueFour(data["HeavyMeleeDamage"] + data["LevelScaling>HeavyMeleeDamage"] * soul_unlock.get_max("PowerIncrease"))) or 0, | |||
heavySpiritScalingDamage = "+" .. (data["SpiritScaling>HeavyMeleeDamage"] or 0), | |||
}) | |||
end | 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) | |||
return | if t ~= "" then | ||
table.insert(output, t) | |||
end | |||
end | |||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroLightMeleeTable = function(frame) | p.heroLightMeleeTable = function(frame) | ||
-- Pull data | -- Pull data | ||
heroLightMeleeData = heroDataArray({"Name", "LightMeleeDamage", "LevelScaling>LightMeleeDamage"}) | 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 = (roundValueFour(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 | end | ||
return table.concat(output, "\n\n") | |||
end | end | ||
p.heroHeavyMeleeTable = function(frame) | p.heroHeavyMeleeTable = function(frame) | ||
-- Pull data | -- Pull data | ||
heroHeavyMeleeData = heroDataArray({"Name", "HeavyMeleeDamage", "LevelScaling>HeavyMeleeDamage"}) | 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 = (roundValueFour(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 | for name, data in pairs(heroStaminaData) do | ||
table.insert(rows, { | |||
name = name, | |||
stamina = data["Stamina"] or 0, | |||
staminaCooldown = data["StaminaCooldown"] or 0 | |||
}) | |||
end | 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) | |||
return | 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), | |||
}) | |||
local altBulletRadius = (data["Weapon>AltFire>BulletRadius"] or 0) * 100 | |||
local altBulletRadiusInches = roundValueInteger((data["Weapon>AltFire>BulletRadius"] or 0) * 100 * 0.394) | |||
if altBulletRadius and altBulletRadius ~= 0 then | |||
table.insert(rows, { | |||
name = name, | |||
sortName = name .. " (alt fire)", | |||
nameSuffix = " (alt fire)", | |||
bulletRadius = altBulletRadius, | |||
bulletRadiusInches = altBulletRadiusInches | |||
}) | |||
end | |||
end | |||
sortHeroes(rows) | |||
local tableDefs = { | |||
{ | |||
title = "Base Bullet Radius Stats", | |||
columns = { | |||
{ label = "Bullet Radius (cm)", field = "bulletRadius" }, | |||
{ label = "Bullet Radius (in)", field = "bulletRadiusInches" }, | |||
} | |||
} | |||
} | |||
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 | end | ||
return p | return p | ||