Editing Module:Sandbox/Vergir

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:
-- Provides generic access to game data files
local p = {}
local p = {}
-- Dataset descriptors. Values are the data file paths passed to mw.loadJsonData.
-- Callers should use these constants rather than raw strings.
p.Dataset = {
    ITEMS    = "Data:ItemData.json",
    ABILITIES = "Data:AbilityData.json",
    HEROES    = "Data:HeroData.json",
    CONVARS  = "Data:Convars.json",
}
-- Street Brawl ships a partial overlay of ability properties rather than a full
-- dataset, so it is deliberately kept out of p.Dataset: its top-level keys are
-- sections ("ability-changes", "item-buckets"), not entity records, and it is
-- not usable with get_entities. Only get_prop consults it.
local STREET_BRAWL = "Data:StreetBrawlData.json"
-- Returns true if a record represents an active, usable entity.
-- Applies consistently across all datasets:
--  - IsDisabled must not be true
--  - Name must not be nil
--  - IsSelectable, if present, must not be false
-- Specifically for hero abilities, some of them are split into two entities, eg. Vexing Bolt has a sub-ability: Redirect Bolt.
-- The secondary sub-abilities most often have nil name and get filtered out by this function
local function is_active(record)
    if record["IsDisabled"] == true then return false end
    if record["Name"] == nil then return false end
    if record["IsSelectable"] ~= nil and record["IsSelectable"] == false then return false end
    return true
end
-- Recursively searches a record for a match against prop, which may be:
--  - a key name: matches if the key exists with a non-zero/non-empty value
--  - a plain value: matches if the string appears as a value anywhere in the record
-- Returns true if a match is found, false otherwise.
local function record_matches_prop(record, prop)
    for k, v in pairs(record) do
        -- Key match: property name exists with a meaningful value
        if k == prop then
            if type(v) == "table" then
                if v["Value"] ~= nil then
                    v = v["Value"]
                else
                    for _ in pairs(v) do return true end
                    return false
                end
            end
            if v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m" then
                return true
            end
        end
        if type(v) == "table" and k ~= "DisabledStateMask" then
            if record_matches_prop(v, prop) then return true end
        elseif type(v) == "string" then
            -- Value match: prop string appears as a value (e.g. Scale.Type = "melee")
            if v == prop then return true end
        end
    end
    return false
end
-- Returns true if a record matches at least one of the given properties,
-- using the same key-name-or-string-value semantics as get_entities.
-- Public so other modules (e.g. Module:AbilityTable membership) can reuse it.
function p.entity_matches(record, properties)
    for _, prop in ipairs(properties) do
        if record_matches_prop(record, prop) then
            return true
        end
    end
    return false
end
-- Returns all active entities from a dataset that match at least one of the
-- given properties. A property may be matched either as a key name (the
-- property exists with a non-zero value) or as a plain string value anywhere
-- in the record (e.g. "melee" matching Scale.Type = "melee").
-- @param  dataset    string  one of the GameData.Dataset constants
-- @param  properties  table  array of property strings
-- @return              table  array of matching entity records
function p.get_entities(dataset, properties)
    local data = mw.loadJsonData(dataset)
    local results = {}
    for _, record in pairs(data) do
        if is_active(record) and p.entity_matches(record, properties) then
            table.insert(results, record)
        end
    end
    return results
end
--------------------------------------------------------------------------------
-- Unified property lookup: get_prop
-- Uses ResourceLookup to route directly to the correct dataset.
-- Returns raw values for use in templates and expressions.
--------------------------------------------------------------------------------
-- Find an entity by display name or internal key.
-- Uses ResourceLookup type field to go directly to the right dataset.
-- Returns (entity_record, type_string, internal_key) or (nil, nil, nil).
local function find_entity(identifier)
    local resource = mw.loadJsonData("Data:ResourceLookup.json")[identifier:lower()]
    if resource then
        local data
        if resource.type == "ability" then
            data = mw.loadJsonData(p.Dataset.ABILITIES)
        elseif resource.type == "hero" then
            data = mw.loadJsonData(p.Dataset.HEROES)
        elseif resource.type == "item" then
            data = mw.loadJsonData(p.Dataset.ITEMS)
        end
        if data and data[resource.key] then
            return data[resource.key], resource.type, resource.key
        end
    end
    -- Fallback: try as direct internal key
    local datasets = {
        { p.Dataset.ABILITIES, "ability" },
        { p.Dataset.HEROES,    "hero" },
        { p.Dataset.ITEMS,    "item" },
    }
    for _, ds in ipairs(datasets) do
        local data = mw.loadJsonData(ds[1])
        if data[identifier] then return data[identifier], ds[2], identifier end
    end
    return nil, nil, nil
end
-- Traverse a table using dot notation, unwrap tables with a .Value field.
-- Supports numeric indices for arrays (e.g. "Upgrades.1.WeaponDamageBonus").
local function resolve_prop(tbl, prop)
    if not prop or prop == "" then return nil end
    local element = tbl
    for segment in string.gmatch(prop, "[^%.]+") do
        if type(element) ~= "table" then return nil end
        local next = element[segment]
        if next == nil then
            local num = tonumber(segment)
            if num then next = element[num] end
        end
        element = next
        if element == nil then return nil end
    end
    if type(element) == "table" then
        return element.Value or ""
    end
    return element
end
-- Looks up a console variable in Data:Convars.json. A convar value is stored
-- either as a plain scalar (e.g. "adsp_alley_min": 122) or as a table holding
-- the value plus a description (e.g. { value = 75, description = "..." }), in
-- which case only the value is returned. The exact key is tried first, then a
-- lower-case form. Returns (value, found).
local function lookup_convar(key)
    local data = mw.loadJsonData(p.Dataset.CONVARS)
    local entry = data[key]
    if entry == nil then
        entry = data[mw.ustring.lower(key)]
    end
    if entry == nil then
        return nil, false
    end
    if type(entry) == "table" then
        return entry.value, true
    end
    return entry, true
end
-- {{#invoke:GameData|get_prop|...}}
-- Two modes:
--  Entity:  {{#invoke:GameData|get_prop|ENTITY_NAME|PROPERTY|VARIANT}}
--            Finds entity via ResourceLookup, returns the raw value (no
--            formatting). Supports dot notation for nested properties
--            (e.g. "Scale.Value"); tables with a .Value field are unwrapped.
--            The optional VARIANT selects a game mode variant. "Street Brawl"
--            (case and spacing insensitive) returns the value changed for
--            Street Brawl, falling back to the base value when Street Brawl
--            does not change that property.
--  Convar:  {{#invoke:GameData|get_prop|Convar|CONVAR_NAME}}
--            Looks up the convar in Data:Convars.json and returns its value,
--            unwrapping the { value, description } form when present.
function p.get_prop(frame)
    local arg1 = frame.args[1]
    local arg2 = frame.args[2]
    local arg3 = frame.args[3]
    if not arg1 then return "" end
    -- Convar mode: first argument is the literal keyword "Convar".
    if mw.ustring.lower(arg1) == "convar" then
        if not arg2 or arg2 == "" then return "" end
        local value, found = lookup_convar(arg2)
        if not found then
            return '<span style="color:red;">Convar not found: ' .. arg2 .. '</span>'
        end
        if value == nil then return "" end
        return value
    end
    -- Entity mode: search abilities, heroes, and items.
    local name = arg1
    local prop = arg2
    if not prop then return "" end
    local entity, etype, ekey = find_entity(name)
    if not entity then
        return '<span style="color:red;">Entity not found: ' .. name .. '</span>'
    end
    -- Street Brawl changes a handful of ability properties. The overlay lists
    -- only what changed, so a miss here just means "unchanged" and the base
    -- record answers instead. That includes the empty string resolve_prop
    -- returns for a table with no Value field: the overlay records are partial,
    -- so an overridden table often holds nothing but a nested Scale.
    if arg3 and arg3 ~= "" then
        local mode = mw.ustring.lower(mw.text.trim(arg3))
        mode = mode:gsub("%s+", "")
        if mode ~= "streetbrawl" then
            return '<span style="color:red;">Unknown variant: ' .. arg3 .. '</span>'
        end
        if etype == "ability" then
            local changed = mw.loadJsonData(STREET_BRAWL)["ability-changes"][ekey]
            if changed then
                local value = resolve_prop(changed, prop)
                if value ~= nil and value ~= "" then return value end
            end
        end
    end
    local result = resolve_prop(entity, prop)
    if result ~= nil then return result end
    return '<span style="color:red;">Prop not found: ' .. name .. '/' .. prop .. '</span>'
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

Page included on this page: