Editing Module:GameData

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 38: Line 38:
--  - a plain value: matches if the string appears as a value anywhere in the record
--  - a plain value: matches if the string appears as a value anywhere in the record
-- Returns true if a match is found, false otherwise.
-- Returns true if a match is found, false otherwise.
-- This handles plain single-word targets; the "Key:Value" and dotted-path forms
-- are handled by record_matches_path below.
local function record_matches_prop(record, prop)
local function record_matches_prop(record, prop)
     for k, v in pairs(record) do
     for k, v in pairs(record) do
Line 66: Line 64:
end
end


-- True if a value counts as "present": non-zero, non-empty, and not a
-- Returns true if a record matches at least one of the given properties,
-- placeholder distance. A table is judged by its .Value field when it has one,
-- using the same key-name-or-string-value semantics as get_entities.
-- otherwise by whether it holds anything at all.
local function value_is_meaningful(v)
    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
    return v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m"
end
 
-- True if a value equals `expected`, which must already be lower-cased. A table
-- is unwrapped to its .Value field, so "Damage:90" matches
-- { Value = 90, Scale = {...} }. A table with no .Value never matches, which is
-- what keeps "Damage:spirit" from matching on a nested Damage.Scale.Type.
local function value_equals(v, expected)
    if type(v) == "table" then v = v["Value"] end
    if v == nil or type(v) == "table" then return false end
    return mw.ustring.lower(tostring(v)) == expected
end
 
-- Follows path segments from `index` onward, starting at `node`. Segments after
-- the first are followed strictly: no searching, one step per segment. Numeric
-- segments fall back to array indices ("Upgrades.1.Damage"), and array elements
-- are stepped through transparently, so "Upgrades.Damage" reaches the Damage
-- field of any upgrade entry.
local function follow_path(node, segments, index, value)
    if index > #segments then
        if value ~= nil then return value_equals(node, value) end
        return value_is_meaningful(node)
    end
    if type(node) ~= "table" then return false end
 
    local segment = segments[index]
    local next_node = node[segment]
    if next_node == nil then
        local num = tonumber(segment)
        if num then next_node = node[num] end
    end
    if next_node ~= nil and follow_path(next_node, segments, index + 1, value) then
        return true
    end
 
    for _, element in ipairs(node) do
        if type(element) == "table" and follow_path(element, segments, index, value) then
            return true
        end
    end
    return false
end
 
-- Recursively locates the first path segment at any depth, then follows the rest
-- from there. Searching for the first segment is what lets a path reach into
-- sub-ability records the same way plain key targets already do.
local function record_matches_path(record, segments, value)
    for k, v in pairs(record) do
        if k == segments[1] and follow_path(v, segments, 2, value) then
            return true
        end
        if type(v) == "table" and k ~= "DisabledStateMask" then
            if record_matches_path(v, segments, value) then return true end
        end
    end
    return false
end
 
-- Splits a target into path segments plus an optional expected value. The first
-- colon separates the two halves, so dots inside the value are safe
-- ("Radius:3.5"). Returns nil for a plain single-word target with no colon,
-- which keeps its existing key-name-or-string-value meaning.
local function parse_target(prop)
    if type(prop) ~= "string" then return nil end
 
    local keys, value = prop, nil
    local colon = prop:find(":", 1, true)
    if colon then
        keys  = mw.text.trim(prop:sub(1, colon - 1))
        value = mw.text.trim(prop:sub(colon + 1))
        if keys == "" or value == "" then return nil end
        value = mw.ustring.lower(value)
    end
 
    local segments = {}
    for segment in keys:gmatch("[^%.]+") do table.insert(segments, segment) end
    if #segments == 0 then return nil end
    if #segments == 1 and value == nil then return nil end
 
    return segments, value
end
 
-- Returns true if a record matches at least one of the given properties. A
-- property may take any of four forms:
--  "Key"          the key exists anywhere in the record with a meaningful
--                 value, or the string appears as a value anywhere
--  "Key:Value"    the key exists anywhere and its own value equals Value,
--                 compared case-insensitively
--  "A.B.C"        that exact nested chain exists with a meaningful value
--  "A.B.C:Value"  that chain exists and ends in Value
-- Only the first segment of a path is searched for; the rest are followed one
-- step at a time, so a value nested deeper under the key does not match. A
-- structured target that resolves to nothing falls back to plain matching, so a
-- value that happens to contain a dot ("38.1 50.8") still works as a target.
-- Public so other modules (e.g. Module:AbilityTable membership) can reuse it.
-- Public so other modules (e.g. Module:AbilityTable membership) can reuse it.
function p.entity_matches(record, properties)
function p.entity_matches(record, properties)
     for _, prop in ipairs(properties) do
     for _, prop in ipairs(properties) do
        local segments, value = parse_target(prop)
         if record_matches_prop(record, prop) then
         if segments and record_matches_path(record, segments, value) then
            return true
        elseif record_matches_prop(record, prop) then
             return true
             return true
         end
         end
Line 186: Line 77:


-- Returns all active entities from a dataset that match at least one of the
-- Returns all active entities from a dataset that match at least one of the
-- given properties. A property may be a key name (the property exists with a
-- given properties. A property may be matched either as a key name (the
-- non-zero value), a plain string value anywhere in the record (e.g. "melee"
-- property exists with a non-zero value) or as a plain string value anywhere
-- matching Scale.Type = "melee"), a "Key:Value" pair, or a dotted path with an
-- in the record (e.g. "melee" matching Scale.Type = "melee").
-- optional value ("Scale.Type:cooldown"). See p.entity_matches for details.
-- @param  dataset    string  one of the GameData.Dataset constants
-- @param  dataset    string  one of the GameData.Dataset constants
-- @param  properties  table  array of property strings
-- @param  properties  table  array of property strings
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: