Module:HeroDataArrays: Difference between revisions

stamina and dash speed
added alt fire stats to all tables where applicable
 
(78 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                 -- trying the key as a string e.g. "1",
                     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]                       
                     else                                    -- else using the key as numeric,
                     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"})


     -- Input table will be called a, output wikitable will be called z
     local rows = {}
    -- From a to z, haha get it
    for name, data in pairs(heroHealthData) do
    z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
        table.insert(rows, {
        "|+ Hero Base Health Stats \n"..
            name = name,
         "! Hero !! Starting !! Added per Boon !! At Max Boon \n"
            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)


     -- Iterate over heroes adding a new row to the wikitable for each
     local tableDefs = {
    for i,a in pairs(heroHealthData) do
         {
         z = z..
             title = "Base Health Stats",
             "|- \n"..
             columns = {
             "| style=\"text-align:left;\" | "..
                 { label = "Starting", field = "maxHealth" },
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                 { label = "Added per Boon", field = "scalingHealth" },
                 " || "..a["MaxHealth"].." || +"..a["LevelScaling>MaxHealth"].." || "..
                { label = "At Max Boon", field = "maxScalingHealth" },
                tostring(roundValue(tonumber(a["MaxHealth"])+tonumber(a["LevelScaling>MaxHealth"])*soul_unlock.get_max('PowerIncrease'))).." \n"
            }
        }
    }
   
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
   
 
     -- Cap it off
     return table.concat(output, "\n\n")
    z = z.."|}"
   
    return z
end
end


p.heroRegenTable = function(frame)
p.heroRegenTable = function(frame)
     heroRegenData = heroDataArray({"Name", "BaseHealthRegen"})
     local heroRegenData = heroDataArray({"Name", "BaseHealthRegen", "SpiritScaling>BaseHealthRegen"})
      
      
     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
     local rows = {}
        "|+ Hero Base Health Regen \n"..
    for name, data in pairs(heroRegenData) do
         "! Hero !! HP/s \n"
        table.insert(rows, {
            name = name,
            baseHealthRegen = data["BaseHealthRegen"] or 0,
            spiritHealthRegen = "+".. (data["SpiritScaling>BaseHealthRegen"] or 0),
         })
    end
    sortHeroes(rows)


     for i,a in pairs(heroRegenData) do
     local tableDefs = {
         z = z..
         {
             "|- \n"..
             title = "Base Health Regen Stats",
             "| style=\"text-align:left;\" | "..
             columns = {
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                { label = "HP/s", field = "baseHealthRegen" },
                " || "..a["BaseHealthRegen"].." \n"
                 { 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
     z = z.."|}"
 
    return z
     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"})
    
    
     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
     local rows = {}
        "|+ Hero Base Damage \n"..
    for name, data in pairs(heroDpsData) do
         "! Hero !! Reload Delay (s) !! Reload Time (s) !! Ammo !! Bullets per sec !! Bullet Damage !! Bullets Per Shot \n"
        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
         })
          
          
    for i,a in pairs(heroDpsData) do
         local altBulletDamage = data["Weapon>AltFire>BulletDamage"]
         z = z..
        local altDPS = data["Weapon>AltFire>DPS"]
            "|- \n"..
        local altRoundsPerSecond = data["Weapon>AltFire>RoundsPerSecond"]
            "| style=\"text-align:left;\" | "..
        local altBulletsPerShot = data["Weapon>AltFire>BulletsPerShot"]
                frame:expandTemplate{title="PageRef", args={a["Name"]}}..
        if altBulletDamage and altBulletDamage ~= 0 then
                 " || "..a["ReloadDelay"].." || "..a["ReloadTime"].."  || "..a["ClipSize"]..
            table.insert(rows, {
                 " || "..a["RoundsPerSecond"].." || "..a["BulletDamage"].." || "..a["BulletsPerShot"].." \n"
                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
     end
     z = z.."|}"
     sortHeroes(rows)
     return z
 
    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
 
     return table.concat(output, "\n\n")
end
end


p.heroBulletSpeedTable = function(frame)
p.heroBulletSpeedTable = function(frame)
     heroBulletSpeedData = heroDataArray({"Name", "BulletSpeed"})
     local heroBulletSpeedData = heroDataArray({
      
        "Name",
     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
        "Weapon>BulletSpeed",
         "|+ Base Bullet Velocity \n"..
        "Weapon>AltFire>BulletSpeed"
        "! Hero !! m/s \n"
    })
 
     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 i,a in pairs(heroBulletSpeedData) do
    local output = {}
         z = z..
     for _, def in ipairs(tableDefs) do
            "|- \n"..
         local t = buildTable(frame, rows, def, def.options)
             "| style=\"text-align:left;\" | "..
        if t ~= "" then
                frame:expandTemplate{title="PageRef", args={a["Name"]}}..
             table.insert(output, t)
                " || "..a["BulletSpeed"].." \n"
        end
     end
     end
     z = z.."|}"
 
    return z
     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"})
      
      
     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
     local rows = {}
        "|+ Base Ammo Count \n"..
     for name, data in pairs(heroClipSizeData) do
        "! Hero !! Ammo \n"
         table.insert(rows, {
 
            name = name,
     for i,a in pairs(heroClipSizeData) do
            clipSize = data["Weapon>ClipSize"] or 0,
         z = z..
             spiritClipSize = "+".. (data["SpiritScaling>ClipSize"] or 0),
             "|- \n"..
             bulletsPerBurst = data["Weapon>BulletsPerBurst"] or 0,
             "| style=\"text-align:left;\" | "..
            burstClipSize = data["Weapon>ClipSize"] or 0 / data["Weapon>BulletsPerBurst"] or 0,
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
            ammoConsumedPerShot = data["Weapon>AltFire>AmmoConsumedPerShot"] or 0
                 " || "..a["ClipSize"].." \n"
        })
       
        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
    z = z.."|}"
     return z
     sortHeroes(rows)
end


p.heroMoveSpeedTable = function(frame)
    local tableDefs = {
    heroMoveSpeedData = heroDataArray({"Name", "MoveSpeed"})
    {
   
            title  = "Base Ammo Count",
    z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
            columns = {
         "|+ Base Movement Speed \n"..
                { label = "Ammo", field = "clipSize" },
        "! Hero !! m/s \n"
                { label = "Ammo Per Shot", field = "bulletsPerBurst"  },
                { label = "Spirit Scaling", field = "spiritClipSize" },
            }
         }
    }


     for i,a in pairs(heroMoveSpeedData) do
    local output = {}
         z = z..
     for _, def in ipairs(tableDefs) do
            "|- \n"..
         local t = buildTable(frame, rows, def, def.options)
             "| style=\"text-align:left;\" | "..
        if t ~= "" then
                frame:expandTemplate{title="PageRef", args={a["Name"]}}..
             table.insert(output, t)
                " || "..a["MoveSpeed"].." \n"
        end
     end
     end
     z = z.."|}"
 
    return z
     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"})
      
      
     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
     local rows = {}
        "|+ Base Fire Rate \n"..
    for name, data in pairs(heroRoundsPerSecondData) do
         "! Hero !! Rounds/s \n"
        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)


     for i,a in pairs(heroRoundsPerSecondData) do
     local tableDefs = {
        z = z..
    {
             "|- \n"..
             title  = "Base Fire Rate",
             "| style=\"text-align:left;\" | "..
             columns = {
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                { label = "Rounds/s", field = "roundsPerSecond" },
                " || "..a["RoundsPerSecond"].." \n"
                 { label = "Spirit Scaling", field = "spiritRoundsPerSecond" },
            }
        }
    }
 
    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
     z = z.."|}"
 
    return z
     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))
        })
      
      
    z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
        local altFalloffStartRange = data["Weapon>AltFire>FalloffStartRange"]
        "|+ Base Falloff Range \n"..
        local altFalloffEndRange = data["Weapon>AltFire>FalloffEndRange"]
        "! Hero !! Minimum Range (m) !! Maximum Range (m) \n"
        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)


     for i,a in pairs(heroFalloffRangeData) do
     local tableDefs = {
        z = z..
    {
             "|- \n"..
             title  = "Base Falloff Range",
             "| style=\"text-align:left;\" | "..
             columns = {
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                { label = "Minimum Range (m)", field = "falloffStartRange" },
                 " || "..a["FalloffStartRange"].." || "..a["FalloffEndRange"].." \n"
                 { label = "Maximum Range (m)", field = "falloffEndRange" },
                 { label = "Damage Falloff (%)", field = "falloffEndScale" },
                { 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
     end
     z = z.."|}"
 
    return z
     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"})


     -- Input table will be called a, output wikitable will be called z
     local rows = {}
    -- From a to z, haha get it
    for name, data in pairs(heroBulletDamageData) do
    z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
        table.insert(rows, {
        "|+ Hero Base Bullet Damage Stats \n"..
            name = name,
         "! Hero !! Starting !! Added per Boon !! At Max Boon \n"
            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),
         })


    -- Iterate over heroes adding a new row to the wikitable for each
        -- Secondary fire is listed as its own row, right under the hero's main row
    for i,a in pairs(heroBulletDamageData) do
        local altDamage = data["Weapon>AltFire>BulletDamage"]
         z = z..
         if altDamage and altDamage ~= 0 then
             "|- \n"..
             local altScaling = data["LevelScaling>BulletDamageAltFire"] or 0
            "| style=\"text-align:left;\" | "..
            table.insert(rows, {
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                name = name,
                 " || "..a["BulletDamage"].." || +"..a["LevelScaling>BulletDamage"].." || "..
                sortName = name .. " (alt fire)",
                tostring(roundValue(tonumber(a["BulletDamage"])+tonumber(a["LevelScaling>BulletDamage"])*soul_unlock.get_max('PowerIncrease'))).." \n"
                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
     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 = (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
    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"  },
               
            }
        }
    }
      
      
     -- Cap it off
     local output = {}
     z = z.."|}"
     for _, def in ipairs(tableDefs) do
      
        local t = buildTable(frame, rows, def, def.options)
     return z
        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"})


     -- Input table will be called a, output wikitable will be called z
     local rows = {}
    -- From a to z, haha get it
    for name, data in pairs(heroLightMeleeData) do
    z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
        table.insert(rows, {
        "|+ Hero Base Light Melee Stats \n"..
            name = name,
         "! Hero !! Starting !! Added per Boon !! At Max Boon \n"
            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)


     -- Iterate over heroes adding a new row to the wikitable for each
     local tableDefs = {
    for i,a in pairs(heroLightMeleeData) do
    {
        z = z..
            title  = "Base Light Melee Stats",
            "|- \n"..
             columns = {
             "| style=\"text-align:left;\" | "..
                 { label = "Starting", field = "lightMeleeDamage" },
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                 { label = "Added per Boon", field = "scalingDamage" },
                 " || "..a["LightMeleeDamage"].." || +"..a["LevelScaling>LightMeleeDamage"].." || "..
                { label = "At Max Boon", field = "maxScalingDamage" },
                tostring(roundValue(tonumber(a["LightMeleeDamage"])+tonumber(a["LevelScaling>LightMeleeDamage"])*soul_unlock.get_max('PowerIncrease'))).." \n"
            }
        }
    }
   
    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
   
 
     -- Cap it off
     return table.concat(output, "\n\n")
    z = z.."|}"
   
    return z
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"})


     -- Input table will be called a, output wikitable will be called z
     local rows = {}
    -- From a to z, haha get it
    for name, data in pairs(heroStaminaData) do
    z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
        table.insert(rows, {
         "|+ Hero Base Heavy Melee Stats \n"..
            name = name,
        "! Hero !! Starting !! Added per Boon !! At Max Boon \n"
            stamina = data["Stamina"] or 0,
            staminaCooldown = data["StaminaCooldown"] or 0
         })
    end
    sortHeroes(rows)


     -- Iterate over heroes adding a new row to the wikitable for each
     local tableDefs = {
    for i,a in pairs(heroHeavyMeleeData) do
    {
        z = z..
            title  = "Base Stamina Stats",
            "|- \n"..
             columns = {
             "| style=\"text-align:left;\" | "..
                 { label = "Stamina Bars", field = "stamina" },
                 frame:expandTemplate{title="PageRef", args={a["Name"]}}..
                 { label = "Cooldown (s)", field = "staminaCooldown" },
                 " || "..a["HeavyMeleeDamage"].." || +"..a["LevelScaling>HeavyMeleeDamage"].." || "..
            }
                tostring(roundValue(tonumber(a["HeavyMeleeDamage"])+tonumber(a["LevelScaling>HeavyMeleeDamage"])*soul_unlock.get_max('PowerIncrease'))).." \n"
        }
    }
   
    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
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"  },
            }
        }
    }
      
      
     -- Cap it off
     local output = {}
     z = z.."|}"
     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"  },
            }
        }
    }
      
      
     return z
    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
end


p.heroStaminaTable = function(frame)
p.heroMoveSpeedTable = function(frame)
     heroStaminaData = heroDataArray({"Name", "Stamina"})
     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)


     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
     local tableDefs = {
         "|+ Hero Base Stamina Stats \n"..
    {
        "! Hero !! Stamina Bars \n"
            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" },
            }
        }
    }


     for i,a in pairs(heroStaminaData) do
    local output = {}
         z = z..
     for _, def in ipairs(tableDefs) do
            "|- \n"..
         local t = buildTable(frame, rows, def, def.options)
             "| style=\"text-align:left;\" | "..
        if t ~= "" then
                frame:expandTemplate{title="PageRef", args={a["Name"]}}..
             table.insert(output, t)
                " || "..a["Stamina"].." \n"
        end
     end
     end
     z = z.."|}"
 
    return z
     return table.concat(output, "\n\n")
end
end


p.heroDashTable = function(frame)
p.bulletRadiusTable = function(frame)
     heroDashData = heroDataArray({"Name", "GroundDashDuration"})
     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)


     z = "{| class=\"wikitable sortable mw-collapsible\" style=\"text-align:center\" \n"..
     local tableDefs = {
        "|+ Hero Base Dash Speed Stats \n"..
    {
         "! Hero !! Dash Speed \n"
            title  = "Base Bullet Radius Stats",
            columns = {
                { label = "Bullet Radius (cm)", field = "bulletRadius" },
                { label = "Bullet Radius (in)", field = "bulletRadiusInches" },
            }
         }
    }


     for i,a in pairs(heroDashData) do
    local output = {}
         z = z..
     for _, def in ipairs(tableDefs) do
            "|- \n"..
         local t = buildTable(frame, rows, def, def.options)
             "| style=\"text-align:left;\" | "..
        if t ~= "" then
                frame:expandTemplate{title="PageRef", args={a["Name"]}}..
             table.insert(output, t)
                " || "..a["GroundDashDuration"].." \n"
        end
     end
     end
     z = z.."|}"
 
    return z
     return table.concat(output, "\n\n")
end
end


return p
return p