Module:GameData: Difference between revisionsGive feedback
Jump to navigation
Jump to search
Return visible error message when entity/property not found in get_prop (with help from vergir-bot LLM) |
Refactor get_prop: use ResourceLookup type for direct routing, unified find_entity, clearer error messages (with help from vergir-bot LLM) |
||
| Line 75: | Line 75: | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- Unified property lookup: get_prop | -- Unified property lookup: get_prop | ||
-- | -- Uses ResourceLookup to route directly to the correct dataset. | ||
-- Returns raw values for use in templates and expressions. | |||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| Line 92: | Line 93: | ||
if not _hero_data then _hero_data = mw.loadJsonData(p.Dataset.HEROES) end | if not _hero_data then _hero_data = mw.loadJsonData(p.Dataset.HEROES) end | ||
return _hero_data | return _hero_data | ||
end | |||
local function load_resource_lookup() | |||
if not _resource_lookup then _resource_lookup = mw.loadJsonData("Data:ResourceLookup.json") end | |||
return _resource_lookup | |||
end | end | ||
-- Find an | -- Find an entity by display name or internal key. | ||
local function | -- Uses ResourceLookup type field to go directly to the right dataset. | ||
local | -- Returns (entity_record, type_string) or (nil, nil). | ||
if | local function find_entity(identifier) | ||
local resource = load_resource_lookup()[identifier:lower()] | |||
if resource then | |||
local data | |||
if resource.type == "ability" then | |||
data = load_ability_data() | |||
elseif resource.type == "hero" then | |||
data = load_hero_data() | |||
elseif resource.type == "item" then | |||
data = load_item_data() | |||
end | |||
if data and data[resource.key] then | |||
return data[resource.key], resource.type | |||
end | |||
end | end | ||
-- | -- Fallback: try as direct internal key | ||
local datasets = { | |||
local | { load_ability_data, "ability" }, | ||
for _, | { load_hero_data, "hero" }, | ||
{ load_item_data, "item" }, | |||
} | |||
for _, ds in ipairs(datasets) do | |||
local data = ds[1]() | |||
if data[identifier] then return data[identifier], ds[2] end | |||
end | end | ||
return nil, nil | |||
end | end | ||
| Line 144: | Line 148: | ||
-- {{#invoke:GameData|get_prop|ENTITY_NAME|PROPERTY}} | -- {{#invoke:GameData|get_prop|ENTITY_NAME|PROPERTY}} | ||
-- | -- Finds entity via ResourceLookup, returns the raw value (no formatting). | ||
-- Supports dot notation for nested properties (e.g. "Scale.Value"). | -- Supports dot notation for nested properties (e.g. "Scale.Value"). | ||
-- Tables with a .Value field are automatically unwrapped. | -- Tables with a .Value field are automatically unwrapped. | ||
| Line 152: | Line 156: | ||
if not name or not prop then return "" end | if not name or not prop then return "" end | ||
local entity, etype = find_entity(name) | |||
local entity = | if not entity then | ||
if entity then | return '<span style="color:red;">Entity not found: ' .. name .. '</span>' | ||
end | end | ||
local result = resolve_prop(entity, prop) | |||
if result ~= nil then return result end | |||
if entity.Weapon then | |||
result = resolve_prop(entity.Weapon, prop) | |||
if entity then | |||
if result ~= nil then return result end | if result ~= nil then return result end | ||
end | end | ||
return '<span style="color:red;">Prop not found: ' .. name .. ' | return '<span style="color:red;">Prop not found: ' .. name .. '/' .. prop .. '</span>' | ||
end | end | ||
return p | return p | ||
Revision as of 15:19, 1 June 2026
Documentation for this module may be created at Module:GameData/doc
-- Module:GameData
-- Provides generic access to game data files, with consistent active-entity filtering.
-- Used by ItemTables, and intended for future AbilityTables, HeroTables, etc.
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",
}
-- 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
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 v = v["Value"] 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 ~= "AutoRegisterModifierValueFromAbilityPropertyName" 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 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) then
for _, prop in ipairs(properties) do
if record_matches_prop(record, prop) then
table.insert(results, record)
break
end
end
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.
--------------------------------------------------------------------------------
-- Lazy-loaded data caches
local _ability_data, _item_data, _hero_data, _resource_lookup
local function load_ability_data()
if not _ability_data then _ability_data = mw.loadJsonData(p.Dataset.ABILITIES) end
return _ability_data
end
local function load_item_data()
if not _item_data then _item_data = mw.loadJsonData(p.Dataset.ITEMS) end
return _item_data
end
local function load_hero_data()
if not _hero_data then _hero_data = mw.loadJsonData(p.Dataset.HEROES) end
return _hero_data
end
local function load_resource_lookup()
if not _resource_lookup then _resource_lookup = mw.loadJsonData("Data:ResourceLookup.json") end
return _resource_lookup
end
-- 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) or (nil, nil).
local function find_entity(identifier)
local resource = load_resource_lookup()[identifier:lower()]
if resource then
local data
if resource.type == "ability" then
data = load_ability_data()
elseif resource.type == "hero" then
data = load_hero_data()
elseif resource.type == "item" then
data = load_item_data()
end
if data and data[resource.key] then
return data[resource.key], resource.type
end
end
-- Fallback: try as direct internal key
local datasets = {
{ load_ability_data, "ability" },
{ load_hero_data, "hero" },
{ load_item_data, "item" },
}
for _, ds in ipairs(datasets) do
local data = ds[1]()
if data[identifier] then return data[identifier], ds[2] end
end
return nil, nil
end
-- Traverse a table using dot notation, unwrap tables with a .Value field
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
element = element[segment]
if element == nil then return nil end
end
if type(element) == "table" then
return element.Value or ""
end
return element
end
-- {{#invoke:GameData|get_prop|ENTITY_NAME|PROPERTY}}
-- 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 automatically unwrapped.
function p.get_prop(frame)
local name = frame.args[1]
local prop = frame.args[2]
if not name or not prop then return "" end
local entity, etype = find_entity(name)
if not entity then
return '<span style="color:red;">Entity not found: ' .. name .. '</span>'
end
local result = resolve_prop(entity, prop)
if result ~= nil then return result end
if entity.Weapon then
result = resolve_prop(entity.Weapon, prop)
if result ~= nil then return result end
end
return '<span style="color:red;">Prop not found: ' .. name .. '/' .. prop .. '</span>'
end
return p