| 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 |