Editing Module:HeroData

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 dictionary_module = require('Module:Dictionary')
--------------------------------------------------------------------------------
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 18:
end
end


-- Hero table for a hero_* key or an exact English name, or nil.
-- Returns an array of item tables that have the same properties
local function hero_by_key_or_name(input)
-- @function   get_similar_items
local hero = heroes_data[input]
-- @param      {string}
if (hero == nil) then hero = p.get_json_item(input) end
-- @return    {array of tables}
return hero
local function get_similar_items(property)
end
local similarItems = {}
 
for _, v in pairs(heroes_data) do
-- Value of a stat, checking the hero's top level, then Weapon, then AltFire.
if (v[property] ~= nil) then
-- Returns nil when the hero has no such stat.
table.insert(similarItems, v)
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
end
end
return scalings
return similarItems
end
end


-- DEPRECATED, kept for [[Module:HeroComparisonTable]]. Use p.get_scalings.
-- returns the key of the specified hero's english name
-- Keyed by scaling value, so it silently drops an entry when Level and Spirit
function p.get_hero_key(frame)
-- scale by the same amount, and it keeps zero scalings.
    local hero_name_input = frame.args[1] -- Get the hero name from the template argument
function p.get_hero_scaling_data(hero_data, hero_stat_key)
    if not hero_name_input then
local scaling_data_returned = {}
        return "Error: Hero name not specified."
-- Walked backwards so Level is written last and wins a value collision,
    end
-- 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.
    hero_name_input = hero_name_input:lower() -- For case-independent search
-- 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
return nil
end


-- A hero's Nth roster tag (1-3) by English name, or '' when there is no such tag.
    -- Go through all the characters and look for a name match.
function p.hero_tag(hero_en, index)
    for hero_key, hero_data in pairs(heroes_data) do
local tagname = hero_en and TAG_NAME_MAP[hero_en]
        if hero_data["Name"] then
if (tagname == nil) then return '' end
            local current_hero_name = hero_data["Name"]:lower()
if (index ~= 1 and index ~= 2 and index ~= 3) then return '' end
            if current_hero_name == hero_name_input then
return lang_module.get_string(string.format("Citadel_%s_HeroTag_%d", tagname, index)) or ''
                return hero_key -- Return the key if the name matches
end
            end
 
        end
--------------------------------------------------------------------------------
    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}}--
    return "Hero not found." -- If nothing is found
-- 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
end


Line 189: Line 61:
     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 = heroes_data[hero_name]
    if(hero == nil) then hero = p.get_json_item(hero_name) end
     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)
    -- Check top-level first, then Weapon, then AltFire
     local var_value = hero[hero_stat_key]
    if var_value == nil and hero.Weapon then
        var_value = hero.Weapon[hero_stat_key]
    end
    if var_value == nil and hero.Weapon and hero.Weapon.AltFire then
        var_value = hero.Weapon.AltFire[hero_stat_key]
    end
   
     if(var_value == nil) then return 0 end
     if(var_value == nil) then return 0 end
      
      
Line 215: Line 96:
     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 = heroes_data[hero_name]
    if(hero == nil) then hero = p.get_json_item(hero_name) end
     if(hero == nil) then return "Hero Not Found" end
     if(hero == nil) then return "Hero Not Found" end
      
      
Line 242: Line 124:
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 = heroes_data[hero_name]
if(hero == nil) then hero = p.get_json_item(hero_name) end
if(hero == nil) then return "Hero Not Found" end
if(hero == nil) then return "Hero Not Found" end
local list = p.get_stat(hero, var)
-- Check top-level first, then Weapon, then AltFire
local list = hero[var]
if list == nil and hero.Weapon then
    list = hero.Weapon[var]
end
if list == nil and hero.Weapon and hero.Weapon.AltFire then
    list = hero.Weapon.AltFire[var]
end
if (list == nil) then return "" 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 293: Line 185:
if (hero == nil) then return "Hero not found, must be unlocalized" end
if (hero == nil) then return "Hero not found, must be unlocalized" end
local template_title = 'PageRef'
local template_title = 'PageRef'
for i, item_key in ipairs(hero["RecommendedItems"] or {}) do
for i, item_key in ipairs(hero["RecommendedItems"]) do
local template_args = {}
template_args = {}
template_args[1] = lang_module.get_string(item_key, 'en')
template_args[1] = lang_module.get_string(item_key, 'en')
template_args['alt_name'] = localize(item_key, item_key)
template_args['alt_name'] = localize(item_key, item_key)
Line 302: Line 194:
return str
return str
end
end


--If the hero scales with the stat, it returns {{Ss|value}} or {{PI|value}}, else blank string
--If the hero scales with the stat, it returns {{Ss|value}} or {{PI|value}}, else blank string
Line 312: Line 205:
local no_template = frame.args["no_template"]
local no_template = frame.args["no_template"]
local hero_data = hero_by_key_or_name(hero_key)
local hero_data = heroes_data[hero_key]
if(hero_data == nil) then hero_data = p.get_json_item(hero_key) end
if(hero_data == nil) then return "Hero not found." end
if(hero_data == nil) then return "Hero not found." end
local scaling_value = raw_scaling(hero_data, scaling_type, hero_stat_key)
local scaling_data = hero_data[scaling_type .. "Scaling"]
if scaling_data == nil then return "" end
local scaling_value = scaling_data[hero_stat_key]
if scaling_value == nil then return "" end
if scaling_value == nil then return "" end


Line 329: Line 226:
end
end
if no_template == "true" then
local scaling_str = p.write_scalar_str(scaling_value, scaling_type)
return scaling_value
   
end
    if no_template == "true" then
    return scaling_value
return p.write_scalar_str(scaling_value, scaling_type)
    else
    return scaling_str
    end
end
end


Line 339: Line 238:
-- Returns "true" if found, nil if not (for #if templates)
-- Returns "true" if found, nil if not (for #if templates)
p.hero_has_stat = function(frame)
p.hero_has_stat = function(frame)
local hero_key = frame.args[1]
    local hero_key = frame.args[1]
local stat_key = frame.args[2]
    local stat_key = frame.args[2]
if (hero_key == nil) then return nil end
local hero_data = hero_by_key_or_name(hero_key)
if (hero_data == nil) then return nil end
if p.has_stat(hero_data, stat_key) then return "true" end
return nil
end


-- Function to call hero tags based on name
    if (hero_key == nil) then return nil end
p.get_hero_tag = function (frame)
   
local name = frame.args[1]
    local hero_data = heroes_data[hero_key]
local number = tonumber(frame.args[2])
    if (hero_data == nil) then hero_data = p.get_json_item(hero_key) end
    if (hero_data == nil) then return nil end


-- Validate input
    -- 1. Check Base Value
if not name then
    -- We allow 0 if it's explicitly set, but usually we filter 0 for hiding.
return "Error: Missing name input."
    -- However, for resists, 0 usually means "hide", so we check ~= 0.
end
    local val = hero_data[stat_key]
    if (val ~= nil and val ~= 0) then return "true" end


if number ~= 1 and number ~= 2 and number ~= 3 then
    -- 2. Check Level Scaling (Boons)
return string.format("Error: Invalid number '%s'. Must be 1, 2, or 3.", tostring(frame.args[2]))
    if (hero_data["LevelScaling"] ~= nil and hero_data["LevelScaling"][stat_key] ~= nil and hero_data["LevelScaling"][stat_key] ~= 0) then  
end
        return "true"  
    end


if TAG_NAME_MAP[name] == nil then
    -- 3. Check Spirit Scaling
return string.format("Error: Unknown name '%s'.", name)
    if (hero_data["SpiritScaling"] ~= nil and hero_data["SpiritScaling"][stat_key] ~= nil and hero_data["SpiritScaling"][stat_key] ~= 0) then  
end
        return "true"  
    end


return p.hero_tag(name, number)
    return nil
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
end


Line 402: Line 290:
      
      
return result
return result
end
-- Retrieve scaling value and type of a hero's given stat, if it has scaling, else return 0
function p.get_hero_scaling_data(hero_data, hero_stat_key)
local scaling_type_full
local scaling_data
local scaling_value
local scaling_types = {"Spirit", "Level"}
local scaling_data_returned = {}
for index, scaling_type in ipairs(scaling_types) do
scaling_type_full = scaling_type .. "Scaling"
scaling_data = hero_data[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_data_returned[scaling_value] = scaling_type
end
end
end
return scaling_data_returned
end
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
-- 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",
}
-- Function to call hero tags based on name
p.get_hero_tag = function (frame)
    local name = frame.args[1]
    local number = tonumber(frame.args[2])
    -- Validate input
    if not name then
        return "Error: Missing name input."
    end
    if number ~= 1 and number ~= 2 and number ~= 3 then
        return string.format("Error: Invalid number '%s'. Must be 1, 2, or 3.", tostring(frame.args[2]))
    end
    local tagname = TAG_NAME_MAP[name]
    if not tagname then
        return string.format("Error: Unknown name '%s'.", name)
    end
    -- Build localization key
    local key = string.format("Citadel_%s_HeroTag_%d", tagname, number)
    -- Call Lang.get_string
    local result = lang_module.get_string(key)
    return result or ("[Missing string for key: " .. key .. "]")
end
end


Line 412: Line 405:
         [0.72] = {},
         [0.72] = {},
     }
     }
    local get_dash = function(hero)
        return hero.GroundDashDuration
            or (hero.Weapon and hero.Weapon.GroundDashDuration)
            or (hero.Weapon and hero.Weapon.AltFire and hero.Weapon.AltFire.GroundDashDuration)
    end


     -- Collect heroes
     -- Collect heroes
     for hero_key, hero_data in pairs(heroes_data) do
     for hero_key, hero_data in pairs(heroes_data) do
         if not hero_data.InDevelopment and not hero_data.IsDisabled then
         if not hero_data.InDevelopment and not hero_data.IsDisabled then
             local dash = p.get_stat(hero_data, "GroundDashDuration")
             local dash = get_dash(hero_data)
             if dash and buckets[dash] then
             if dash and buckets[dash] then
                 table.insert(buckets[dash], hero_data.Name or hero_key)
                 table.insert(buckets[dash], hero_data.Name or hero_key)
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