Editing Module:HeroData

Jump to navigation Jump to search
Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
local p = {};
local p = {};
local heroes_data = mw.loadJsonData("Data:HeroData.json")
local heroes_data = mw.loadJsonData("Data:HeroData.json")
local attributes_data = mw.loadJsonData("Data:AttributeData.json")
local attribute_orders = mw.loadJsonData("Data:StatInfoboxOrder.json")
local util_module = require('Module:Utilities')
local util_module = require('Module:Utilities')
local lang_module = require('Module:Lang')
local lang_module = require('Module:Lang')
 
local attribute_module = require('Module:AttributeData')
--------------------------------------------------------------------------------
-- Internal helpers
--------------------------------------------------------------------------------
 
-- Localized string for a key, falling back to a readable version of `fallback`
-- plus a "missing translation" tooltip.
local function localize(key, fallback)
local result = lang_module.get_string(key)
if (result == "") or (result == nil) then
result = util_module.add_space_before_cap(fallback) .. mw.getCurrentFrame():expandTemplate{title="MissingValveTranslationTooltip"}
end
return result
end
 
-- Raw scaling value of a stat under LevelScaling / SpiritScaling, or nil when
-- the hero has no scaling of that type for that stat.
local function raw_scaling(hero_data, scaling_type, stat)
local scaling = hero_data and hero_data[scaling_type .. "Scaling"]
if (scaling == nil or stat == nil) then return nil end
return scaling[stat]
end
 
-- Mapping from hero name to internal tag name
local TAG_NAME_MAP = {
["Abrams"] = "Abrams",
["Bebop"] = "Bebop",
["Billy"] = "Punkgoat",
["Calico"] = "Nano",
["The Doorman"] = "Doorman",
["Drifter"] = "Drifter",
["Dynamo"] = "Dynamo",
["Apollo"] = "Fencer",
["Graves"] = "Necro",
["Grey Talon"] = "Orion",
["Haze"] = "Haze",
["Holliday"] = "Astro",
["Infernus"] = "Inferno",
["Ivy"] = "Tengu",
["Kelvin"] = "Kelvin",
["Lady Geist"] = "Geist",
["Lash"] = "Lash",
["McGinnis"] = "Engineer",
["Mina"] = "VampireBat",
["Mirage"] = "Mirage",
["Mo & Krill"] = "Digger",
["Paige"] = "Bookworm",
["Paradox"] = "Chrono",
["Celeste"] = "Unicorn",
["Pocket"] = "Synth",
["Rem"] = "Familiar",
["Seven"] = "Gigawatt",
["Shiv"] = "Shiv",
["Silver"] = "Werewolf",
["Silver (Transformed)"] = "Werewolf",
["Sinclair"] = "Magician",
["Venator"] = "Priest",
["Victor"] = "Frank",
["Vindicta"] = "Vindicta",
["Viscous"] = "Viscous",
["Vyper"] = "Viper",
["Warden"] = "Warden",
["Wraith"] = "Wraith",
["Yamato"] = "Yamato",
}
 
--------------------------------------------------------------------------------
-- Data access, for use by other modules
--------------------------------------------------------------------------------


-- returns the table of a specific item, used by external modules
-- returns the table of a specific item, used by external modules
Line 83: Line 17:
end
end


-- Hero table for a hero_* key or an exact English name, or nil.
-- returns the key of the specified hero's english name
local function hero_by_key_or_name(input)
function p.get_hero_key(name)
local hero = heroes_data[input]
for i,v in pairs(heroes_data) do
if (hero == nil) then hero = p.get_json_item(input) end
if (v["Name"] == name) then
return hero
return i
end
 
-- Value of a stat, checking the hero's top level, then Weapon, then AltFire.
-- Returns nil when the hero has no such stat.
function p.get_stat(hero_data, stat)
if (hero_data == nil or stat == nil) then return nil end
local value = hero_data[stat]
if (value == nil and hero_data.Weapon) then
value = hero_data.Weapon[stat]
end
if (value == nil and hero_data.Weapon and hero_data.Weapon.AltFire) then
value = hero_data.Weapon.AltFire[stat]
end
return value
end
 
-- Ordered list of a hero's scalings for a stat, Level first then Spirit:
--  { { type = "Level", value = 1.5 }, { type = "Spirit", value = 0.04 } }
-- Absent scalings are omitted, so an empty list means "no scaling". Zero
-- scalings are omitted too unless include_zero is set, which only the
-- deprecated get_hero_scaling_data below needs.
function p.get_scalings(hero_data, stat, include_zero)
local scalings = {}
for _, scaling_type in ipairs({"Level", "Spirit"}) do
local value = raw_scaling(hero_data, scaling_type, stat)
if (value ~= nil and (include_zero or value ~= 0)) then
table.insert(scalings, { type = scaling_type, value = value })
end
end
return scalings
end
 
-- DEPRECATED, kept for [[Module:HeroComparisonTable]]. Use p.get_scalings.
-- Keyed by scaling value, so it silently drops an entry when Level and Spirit
-- scale by the same amount, and it keeps zero scalings.
function p.get_hero_scaling_data(hero_data, hero_stat_key)
local scaling_data_returned = {}
-- Walked backwards so Level is written last and wins a value collision,
-- matching the original Spirit-then-Level assignment order.
local scalings = p.get_scalings(hero_data, hero_stat_key, true)
for i = #scalings, 1, -1 do
scaling_data_returned[scalings[i].value] = scalings[i].type
end
return scaling_data_returned
end
 
-- Whether a hero has a stat at all: a non-zero base value, or any scaling.
function p.has_stat(hero_data, stat)
local base = p.get_stat(hero_data, stat)
if (base ~= nil and base ~= 0) then return true end
return #p.get_scalings(hero_data, stat) > 0
end
 
-- Resolves a hero_* key or an English hero name to a hero_* key.
-- Returns nil when the input matches no hero. Name matching is case-insensitive.
function p.find_hero_key(input)
if (input == nil or input == '') then return nil end
if (heroes_data[input] ~= nil) then return input end
local wanted = mw.ustring.lower(input)
for hero_key, hero_data in pairs(heroes_data) do
if (hero_data["Name"] and mw.ustring.lower(hero_data["Name"]) == wanted) then
return hero_key
end
end
end
end
Line 155: Line 27:
end
end


-- A hero's Nth roster tag (1-3) by English name, or '' when there is no such tag.
function p.hero_tag(hero_en, index)
local tagname = hero_en and TAG_NAME_MAP[hero_en]
if (tagname == nil) then return '' end
if (index ~= 1 and index ~= 2 and index ~= 3) then return '' end
return lang_module.get_string(string.format("Citadel_%s_HeroTag_%d", tagname, index)) or ''
end
--------------------------------------------------------------------------------
-- Wikitext entry points
--------------------------------------------------------------------------------
-- returns the key of the specified hero's english name
function p.get_hero_key(frame)
local hero_name_input = frame.args[1]
if not hero_name_input then
return "Error: Hero name not specified."
end
return p.find_hero_key(hero_name_input) or "Hero not found."
end
--{{#invoke:HeroData|resolve_key|HERO_KEY_OR_NAME}}--
-- The matching hero_* key, or '' when the input matches no hero.
function p.resolve_key(frame)
return p.find_hero_key(frame.args[1]) or ''
end


--{{#invoke:HeroData|get_hero_var|HERO_NAME|STAT_NAME|sig_figs_or_localize}}--
--{{#invoke:HeroData|get_hero_var|HERO_NAME|STAT_NAME|sig_figs_or_localize}}--
--sig_figs optional for rounding floats
--sig_figs optional for rounding floats
p.get_hero_var = function(frame)
p.get_hero_var = function(frame)
    local hero_name = frame.args[1]
local hero_name = frame.args[1]
    local hero_stat_key = frame.args[2]
local hero_stat_key = frame.args[2]
    local sig_figs_or_localize = frame.args[3]
local sig_figs_or_localize = frame.args[3]
   
    local hero = hero_by_key_or_name(hero_name)
local hero = p.get_json_item(hero_name)
    if(hero == nil) then return "Hero Not Found" end
if(hero == nil) then return "Hero Not Found" end
   
    local var_value = p.get_stat(hero, hero_stat_key)
local var_value = hero[hero_stat_key]
    if(var_value == nil) then return 0 end
if(var_value == nil) then return 0 end
   
    --round
--round
    if (sig_figs_or_localize ~= nil and tonumber(sig_figs_or_localize) ~= nil) then
if (sig_figs_or_localize ~= nil and tonumber(sig_figs_or_localize) ~= nil) then
        var_value = util_module.round_to_sig_fig(var_value, sig_figs_or_localize)
var_value = util_module.round_to_sig_fig(var_value, sig_figs_or_localize)
        if (var_value == nil) then return "get_hero_var() error with rounding" end
if (var_value == nil) then return "get_hero_var() error with rounding" end
    end
end
   
    --localize
--localize
    if (sig_figs_or_localize == "true") then
if (sig_figs_or_localize == "true") then
        return lang_module.get_string(var_value)
return lang_module._get_string(var_value)
    end
end


    return var_value
return var_value
end
 
--{{#invoke:HeroData|get_alt_fire_var|HERO_NAME|STAT_NAME|sig_figs_or_localize}}--
p.get_alt_fire_var = function(frame)
    local hero_name = frame.args[1]
    local stat_key = frame.args[2]
    local sig_figs_or_localize = frame.args[3]
   
    local hero = hero_by_key_or_name(hero_name)
    if(hero == nil) then return "Hero Not Found" end
   
    if not (hero.Weapon and hero.Weapon.AltFire) then
        return "No Alt Fire"
    end
   
    local var_value = hero.Weapon.AltFire[stat_key]
    if(var_value == nil) then return 0 end
   
    --round
    if (sig_figs_or_localize ~= nil and tonumber(sig_figs_or_localize) ~= nil) then
        var_value = util_module.round_to_sig_fig(var_value, sig_figs_or_localize)
    end
   
    --localize
    if (sig_figs_or_localize == "true") then
        return lang_module.get_string(var_value)
    end
 
    return var_value
end
end


Line 242: Line 59:
local hero_name = frame.args[1]
local hero_name = frame.args[1]
local var = frame.args[2]
local var = frame.args[2]
local number_int = tonumber(frame.args[3])
local number_str = frame.args[3]
local localize_arg = frame.args[4]
local number_int = tonumber(number_str)
local localize = frame.args[4]
local hero = hero_by_key_or_name(hero_name)
local hero = p.get_json_item(hero_name)
if(hero == nil) then return "Hero Not Found" end
if (hero == nil) then return "Hero " .. hero_name .. " not found" end
local list = hero[var]
local list = p.get_stat(hero, var)
if (list == nil) then return "Hero does not have " .. var .. " variable" end
if (list == nil) then return "" end
local element = list[number_int]
local element = list[number_int]
if (element == nil) then return "" end
if (element == nil) then return "" end
if localize_arg=="true" then  
if localize=="true" then  
element = lang_module.get_string(element)
element = lang_module._get_string(element)
end
end
Line 260: Line 76:
end
end


p.get_ability_key = function(frame)
 
local hero_key = frame.args[1]
--If the hero scales with the stat, it returns {{Ss|value}} or {{Ls|value}}, else blank string
local bound_slot_number = frame.args[2]
--{{#invoke:HeroData|get_hero_scalar_str_invoke|HERO_NAME|STAT_NAME}}--
local hero_data = heroes_data[hero_key]
p.get_hero_scalar_str_invoke = function(frame)
if (hero_data == nil) then return "Hero key "..hero_key.. " not found" end
local hero_name = frame.args[1]
local bound_abilities_data = hero_data["BoundAbilities"]
local hero_stat_key = frame.args[2]
if (bound_abilities_data == nil) then return "Hero key " .. hero_key.. " has no BoundAbilities" end
local hero_data = p.get_json_item(hero_name)
return bound_abilities_data[tonumber(bound_slot_number)]["Key"]
if(hero_data == nil) then return "Hero Not Found" end
return p.get_hero_scalar_str(hero_data, hero_stat_key) --surely theres a better way
end
end


p.write_role_playstyle_quote = function(frame)
function p.get_hero_scalar_str(hero, hero_stat_key)
local hero_key = frame.args[1]
local scaling_type_full
local hero_data = heroes_data[hero_key]
local scaling_data
if (hero_data == nil) then return hero_key.." not found" end
local scaling_value
local role_key = hero_data["Role"]
local scaling_types = {"Spirit", "Level"}
local role_localized = lang_module.get_string(role_key, nil, 'en')
local scaling_abbrevs = {"Ss", "Ls"}
local playstyle_key = hero_data["Playstyle"]
local playstyle_localized = lang_module.get_string(playstyle_key, nil, 'en')
for index, scaling_type in ipairs(scaling_types) do
scaling_type_full = scaling_type .. "Scaling"
scaling_data = hero[scaling_type_full]
--If the scaling data exists
if (scaling_data ~= nil) then  
scaling_value = scaling_data[hero_stat_key]
--If the stat scales
if (scaling_value ~= nil) then
scaling_value = util_module.round_to_sig_fig(scaling_value, 3)
--The hero has a scaling value with this stat
local template_title = "Template:" .. scaling_abbrevs[index] --scaling type's abbreviation
local template_args = {}
template_args["1"] = scaling_value --store in 1st arg for {{{1}}} to grab it from {{SS}} or {{LS}} template
local template_call = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
return template_call
end
end
end
local str = "<b>" .. role_localized .. '</b><br>' .. playstyle_localized
-- Otherwise return empty string
local template_args = {}
return ""
template_args[1] = ""
template_args[2] = str
return frame:expandTemplate{title = 'Quotation', args = template_args}
end
end


p.write_default_items = function(frame)
--HERO_NAME in english
local hero_key = frame.args[1] --unlocalized
--{{#invoke:HeroData|write_infobox|HERO_NAME}}--
if (hero_key == nil) then return "No hero key provided" end
p.write_infobox = function(frame)
local str = ""
-- Get hero data
local hero = heroes_data[hero_key]
    hero_name = frame.args[1]
if (hero == nil) then return "Hero not found, must be unlocalized" end
    hero_data = p.get_json_item(hero_name)
local template_title = 'PageRef'
    if(hero_data == nil) then return "Hero Not Found" end
for i, item_key in ipairs(hero["RecommendedItems"] or {}) do
   
local template_args = {}
    local infobox_attributes = {
template_args[1] = lang_module.get_string(item_key, 'en')
        Weapon = {'DPS','ClipSize','RoundsPerSecond','ReloadTime'},
template_args['alt_name'] = localize(item_key, item_key)
        Vitality = {'MaxHealth','BulletArmorDamageReduction','TechArmorDamageReduction','MaxMoveSpeed'}
local expanded_template = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
    }
str = str .. "* " .. expanded_template .. "\n"
   
    -- Declarations and initializations
    local category_data = attribute_module.get_category_data()
    local stats
    local stat_data
    local stat_value
    local stat_text
    local label
    local postfix
    local should_display
    local image_file_name
    local image_file
    local icon_and_value_str
    local template_args = {}
    local stat_values = {Weapon = "", Vitality = ""}
   
    -- Add the main parameters
    template_args["name_english"] = hero_name
   
    template_args["name_localized"] = lang_module._get_string(p.get_hero_key(hero_name))
   
    local wep_name = hero_data["WeaponName"]
    if (wep_name ~= nil) then template_args["weapon_name"] = lang_module._get_string(wep_name) end
   
    local wep_desc = hero_data["WeaponDescription"]
    if (wep_desc ~= nil) then template_args["weapon_description"] = lang_module._get_string(wep_desc) end
   
    local role = hero_data["Role"]
    if (role ~= nil) then template_args["role"] = lang_module._get_string(role) end
   
    local playstyle = hero_data["Playstyle"]
    if (playstyle ~= nil) then template_args["playstyle"] = lang_module._get_string(playstyle) end
   
    -- Iterate attribute categories
    for category, stats in pairs(infobox_attributes) do
-- Iterate stats
for _, stat_name in ipairs(stats) do
-- Confirm its in attribute data and retrieve it
attribute_data = attributes_data[category]
if (attribute_data == nil) then return "Category " .. category .. " in infobox_attributes is not in Data:AttributeData" end
stat_data = attribute_data[stat_name]
if (stat_data == nil) then return "Attribute " .. stat_name .. " in infobox_attributes is not in Data:AttributeData" end
-- gets the stat's value
stat_value = hero_data[stat_name]
if (stat_value == nil) then
stat_value = 0 --default to 0 if not present
end
--Round value to 3 significant figures
stat_value = util_module.round_to_sig_fig(stat_value, 3)
-- get label and postfix
label = lang_module._get_string(stat_data["label"])
postfix = stat_data["postfix"]
if (postfix == nil) then
postfix = ""
else
-- light grey for postfixes
postfix = lang_module._get_string(postfix)
if (postfix == nil) then
postfix = ""
end
postfix = '<span style="color: #666666;">' .. postfix .. "</span>"
end
-- if a language is missing the postfix, use no postfix
-- Check if icon file exists, and if not, don't use any image
image_file_name = 'File:AttributeIcon' .. stat_name .. '.png'
image_file = util_module.get_image_file(image_file_name, 15, stat_name)
-- Create the template'd icon
local icon_color = attribute_module.get_attr_icon_color(stat_name)
icon_template_title = mw.title.new("Template:Icon/" .. icon_color)
icon_template_args = {}
icon_template_args[1] = image_file
icon_template_args[2] = stat_value .. postfix
icon_and_value_str = mw.getCurrentFrame():expandTemplate{ title = icon_template_title, args = icon_template_args }
label =  ',<span style="color: #999999; background-color: #2F2F2F; display: inline-flex; width: 100%; height: 100%; justify-content: center; align-items: center;">' .. label .. '</span>'
-- First add localized stat name for left column
stat_values[category] = stat_values[category] .. label
-- Then add icon and stat value for right column
stat_values[category] = stat_values[category] .. ',' .. icon_and_value_str
end
-- Add the stat values in the category to i.e weapon_values and vitality_values parameters
template_args[string.lower(category) .. "_values"] = stat_values[category]
end
end
return str
   
    -- Use expandTemplate to evaluate the Infobox_hero template
    local template_title = mw.title.new("User:Sur")
    local expanded_template = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
   
    return expanded_template
end
end


--If the hero scales with the stat, it returns {{Ss|value}} or {{PI|value}}, else blank string
 
--{{#invoke:HeroData|get_hero_scalar|HERO_NAME|SCALING_TYPE|STAT_NAME|sig_figs_or_localize}}--
--{{#invoke:HeroData|write_stat_infoboxes|HERO_NAME}}
p.get_hero_scalar = function(frame)
--Creates {{Infobox_stat}}'s' for the 3 categories Weapon, Vitality, Spirit
local hero_key = frame.args[1]
p.write_stat_infoboxes = function(frame)
local scaling_type = frame.args[2]
local hero_name = frame.args[1]
local hero_stat_key = frame.args[3]
if(hero_name == nil) then return "Hero parameter missing" end
local sig_figs_or_localize = frame.args[4]
local no_template = frame.args["no_template"]
local hero_data = hero_by_key_or_name(hero_key)
-- Use expandTemplate to evaluate the Infobox_hero template
if(hero_data == nil) then return "Hero not found." end
    local template_title = mw.title.new("Template:Infobox_stat") --name of the template
    local template_calls = '<div style="display: flex; flex-direction: column;">' --all template calls concatenated
    local template_call --current template call
local template_args = {} --current template arguments
local cell_values --current cell values
local cell_value --current cell value
local label --current stat's label
local postfix --current stat's postfix
local stat_value --current stat's numerical value in the hero data
local hero_data = p.get_json_item(hero_name)
local stats --stats of the current category
local image_file_name --name of the image_file to check
local image_file --mw returned image file
local extra_blank_cell --add a blank cell after certain stats
local stats_to_add_blank_after = {BulletLifesteal = true, CritDamageReceivedScale = true}
if (hero_data == nil) then return "Hero Not Found in AttrData" end
local category_data = attribute_module.get_category_data()
local should_display
local scaling_value = raw_scaling(hero_data, scaling_type, hero_stat_key)
for _, category in ipairs(attribute_orders["category_order"]) do
if scaling_value == nil then return "" end
stats = attributes_data[category]
 
local category_values = category_data[category]
--round
template_args["box_name"] = category_values.unlocalized_name
if (sig_figs_or_localize ~= nil and tonumber(sig_figs_or_localize) ~= nil) then
template_args["box_rgb"] = category_values.rgb
scaling_value = util_module.round_to_sig_fig(scaling_value, sig_figs_or_localize)
template_args["num_cols"] = 2
if (scaling_value == nil) then return "get_hero_scalar() error with rounding" end
cell_values = ""
end
-- Determine cell values
--localize
for _, stat_name in ipairs(attribute_orders[category]["attribute_order"]) do
if (sig_figs_or_localize == "true") then
stat_data = stats[stat_name]
return lang_module.get_string(scaling_value)
end
-- gets the stat's value if it has that stat, default to 0 if not
stat_value = hero_data[stat_name]
if (stat_value == nil) then
stat_value = 0 --default to 0 if not present
end
--Round value to 3 significant figures
stat_value = util_module.round_to_sig_fig(stat_value, 3)
-- get label and postfix
label = lang_module._get_string(stat_data["label"])
postfix = stat_data["postfix"]
if (postfix == nil) then  
postfix = ""
else
-- light grey for postfixes
postfix = '<span style="color: #666666;">' .. lang_module._get_string(postfix) .. "</span>"
end
-- if a language is missing the postfix, use no postfix
if (postfix == nil) then
postfix = ""
end
-- Check if icon file exists, and if not, don't use any image
image_file_name = 'File:AttributeIcon' .. stat_name .. '.png'
-- 15px and link to stat name (page name might not match perfectly yet)
image_file = util_module.get_image_file(image_file_name, 15, stat_name)
-- Create the template'd icon
local icon_color = attribute_module.get_attr_icon_color(stat_name)
if (icon_color == "Brown") then
image_file = '<span style="position: relative; bottom: 2px; filter: invert(42%) sepia(10%) saturate(2912%) hue-rotate(351deg) brightness(90%) contrast(87%);">' .. image_file .. '</span>'
end
-- use white instead of grey if grey is returned
-- Add an empty cell following some stats to align them correctly as seen in game
if (stats_to_add_blank_after[stat_name]) then
extra_blank_cell = " ," --prefixed space is needed
else
extra_blank_cell = ""
end
-- slightly lighter color for stat name
label = '<span style="color: #3d3d3d;">' .. label .. '</span>'
-- Add the scaling str if it scales
local scalar_str = p.get_hero_scalar_str(hero_data, stat_name)
-- Set cell value as "Icon StatvaluePostfix Statname Scaling,"
-- If icon file already exists, set it to #REDIRECT to the duplicate file page
cell_value = image_file .. " " .. stat_value  .. postfix .. " " .. label .. " " .. scalar_str .. "," .. extra_blank_cell
cell_values = cell_values .. cell_value --add value to values list
end
template_args["cell_values"] = cell_values
-- Write current call
template_call = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
if no_template == "true" then
-- Add call to the set of calls
return scaling_value
template_calls = template_calls .. "\n" .. template_call
end
end
return p.write_scalar_str(scaling_value, scaling_type)
return template_calls .. "</div>"
end
end


-- Check if a hero has a stat (Base, Boons, or Spirit Scaling)
--writes the massive hero comparison table for a specific PI and SP
-- Returns "true" if found, nil if not (for #if templates)
--{{#invoke:HeroData|write_hero_comparison_table|POWER_INCREASES|SPIRIT_POWER}}
p.hero_has_stat = function(frame)
p.write_hero_comparison_table = function(frame)
local hero_key = frame.args[1]
-- process inputs
local stat_key = frame.args[2]
local power_increases = frame.args[1]
if (hero_key == nil) then return nil end
if (power_increases == nil) then power_increases = 0 end
local spirit_power = frame.args[2]
if (spirit_power == nil) then spirit_power = 0 end
-- Initializations and declarations
local row_str = ""
local body_str = ""
local in_development
local is_disabled
local template_title = ""
local template_args = {}
local hero_icon
local hero_td_style
local hero_data = hero_by_key_or_name(hero_key)
-- Exclude some specific stats which are currently constant for all heroes at a negligible value
if (hero_data == nil) then return nil end
local stats_to_exclude = {
BaseWeaponDamageIncrease = true,
FireRate = true,
ClipSizeIncrease = true,
ReloadSpeed = true,
BulletSpeedIncrease = true,
BulletLifesteal = true,
BulletShieldHealth = true,
TechShieldHealth = true,
HealingOutput = true,
DebuffResist = true,
CritDamageReceivedScale = true,
StaminaRegenIncrease = true,
TechCooldown = true,
TechDuration = true,
TechRange = true,
TechLifesteal = true,
MaxChargesIncrease = true,
TechCooldownBetweenChargeUses = true
}
if p.has_stat(hero_data, stat_key) then return "true" end
-- Iterate heroes
return nil
for hero_key, hero_data in pairs(heroes_data) do
end
-- Ensure they are not in development and they are an active hero
 
in_development = hero_data["inDevelopment"]
-- Function to call hero tags based on name
is_disabled = hero_data["isDisabled"]
p.get_hero_tag = function (frame)
if (not in_development and not is_disabled) then  
local name = frame.args[1]
local number = tonumber(frame.args[2])
--Add the row's stats
 
row_str = ""
-- Validate input
if not name then
-- Retrieve hero's localized name
return "Error: Missing name input."
hero_name = lang_module._get_string(hero_key)
end
 
-- Retrieve hero's english name, used for hero icon
if number ~= 1 and number ~= 2 and number ~= 3 then
hero_name_en = hero_data["Name"]
return string.format("Error: Invalid number '%s'. Must be 1, 2, or 3.", tostring(frame.args[2]))
-- Expand hero icon
template_title = "Template:HeroIcon"
template_args[1] = hero_name_en
template_args[2] = hero_name
hero_icon = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
-- First column in each row is hero name
-- This style forces the icon and the name to be on the same line, rather than
-- adding a newline and increasing the height of all other cells in the row
hero_td_style = ' style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"'
row_str = row_str .. "<td" .. hero_td_style .. '>' .. hero_icon .. "</td>"
-- Consecutive columns are then stats
-- Iterate categories
for _, category in ipairs(attribute_orders["category_order"]) do
category_attrs = attributes_data[category]
-- Iterate attributes within the category
for _, attr_key in ipairs(attribute_orders[category]["attribute_order"]) do
if (stats_to_exclude[attr_key] == nil) then
attr_data = category_attrs[attr_key]
--Retrieve the stats value from hero data
stat_value = hero_data[attr_key]
if (stat_value == nil) then stat_value = 0 end
-- Round it
stat_value = util_module.round_to_sig_fig(stat_value, 3)
-- Add it to the row
row_str = row_str .. "<td>" .. stat_value .. "</td>"
end
end
end
-- Add row to the body
row_str = "<tr>" .. row_str .. "</tr>"
body_str = body_str .. row_str
first_hero = false
end
end
end
if TAG_NAME_MAP[name] == nil then
return string.format("Error: Unknown name '%s'.", name)
end
return p.hero_tag(name, number)
end
--{{#invoke:HeroData|has_tags|HERO_ENGLISH_NAME}}--
-- 'true' when the hero has roster tags, '' otherwise.
function p.has_tags(frame)
if p.hero_tag(frame.args[1], 1) ~= '' then return 'true' end
return ''
end
-- Retrieve scaling string of a hero's given stat, if it has scaling, else return blank
-- Scaling string meaning the expanded template {{Ss|scalar}} or {{PI|scalar}}
function p.write_scalar_str(scaling_value, scaling_type, compact)
local scaling_abbrevs = {Spirit = "Ss", Level = "PI"}
-- Return blank if it doesnt scale
if (scaling_value == 0) then return "" end
-- Round it
-- First header is Hero
scaling_value = util_module.round_to_sig_fig(scaling_value, 3)
local headers_str = "<th>" .. "Hero" .. "</th>"
local header_style = ""
local category_data = attribute_module.get_category_data()
--The hero has a scaling value with this stat
-- Iterate stats again for displaying headers with their respective color
local template_title = "Template:" .. scaling_abbrevs[scaling_type]
for _, category in ipairs(attribute_orders["category_order"]) do
local template_args = {}
category_attrs = attributes_data[category]
template_args["1"] = scaling_value
category_rgb = category_data[category]["rgb"]
if compact then  
template_args["compact"] = "yes"
-- Iterate attributes within the category
template_args["show_value"] = "yes"
for _, attr_key in ipairs(attribute_orders[category]["attribute_order"]) do
if (stats_to_exclude[attr_key] == nil) then
attr_data = category_attrs[attr_key]
-- Localize, fallback to attr_key
attr_localized = lang_module._get_string(attr_data["label"])
if (attr_localized == nil) then attr_localized = attr_key end
header_style = ' style="background-color: ' .. "rgb(" .. category_rgb .. ') ;"'
headers_str = headers_str .. "<th" .. header_style .. ">" .. attr_localized .. "</th>"
end
end
end
end
headers_str = "<tr>" .. headers_str .. "</tr>"
local template_call = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
local result = template_call:gsub("\n", ""):gsub("\r", "")
   
return result
end
 
-- Outputs a wikitable of heroes grouped by GroundDashDuration using {{HeroIcon|hero_name}}
-- {{#invoke:HeroData|write_ground_dash_buckets}}
p.write_ground_dash_buckets = function(frame)
    local buckets = {
        [0.62] = {},
        [0.68] = {},
        [0.72] = {},
    }
 
    -- Collect heroes
    for hero_key, hero_data in pairs(heroes_data) do
        if not hero_data.InDevelopment and not hero_data.IsDisabled then
            local dash = p.get_stat(hero_data, "GroundDashDuration")
            if dash and buckets[dash] then
                table.insert(buckets[dash], hero_data.Name or hero_key)
            end
        end
    end
 
    -- Sort alphabetically
    for _, list in pairs(buckets) do
        table.sort(list)
    end
 
    -- Determine max rows dynamically
    local max_rows = 0
    for _, list in pairs(buckets) do
        if #list > max_rows then max_rows = #list end
    end
 
    local output = {"{| class='wikitable mw-collapsible'",
    "|+Hero Dash Buckets", "! Bucket 1 !! Bucket 2 !! Bucket 3"}
    local current_frame = frame or mw.getCurrentFrame()
 
    for i = 1, max_rows do
        local row = {}
        for _, bucket in ipairs({0.62, 0.68, 0.72}) do
            local hero_name = buckets[bucket][i]
            table.insert(row, hero_name and current_frame:expandTemplate{title = "HeroIcon", args = {hero_name}} or "")
        end
        table.insert(output, "|-\n| " .. table.concat(row, " || "))
    end


    table.insert(output, "|}")
return '<table class="wikitable sortable">' .. headers_str .. body_str .. "</table>"
    return table.concat(output, "\n")
end
end


return p
return p
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template