|
|
| Line 1: |
Line 1: |
| -- Provides generic access to game data files | | -- Throwaway experiment. Checks how mw.ext.tabber.render treats a tab label that |
| | -- contains HTML, which is what an untranslated string looks like once |
| | -- Module:Dictionary appends Template:NeedsTranslationTooltip to it. |
| | -- |
| | -- On a /ru page: 'Notes' has a Russian value, so its label is plain text. |
| | -- 'Music' is English-only, so its label arrives with the tooltip markup attached. |
|
| |
|
| local p = {} | | local p = {} |
| | local dictionary = require("Module:Dictionary") |
|
| |
|
| -- Dataset descriptors. Values are the data file paths passed to mw.loadJsonData.
| | function p.test(frame) |
| -- Callers should use these constants rather than raw strings.
| | local tabs = { |
| p.Dataset = {
| | { label = dictionary.translate('Notes'), content = 'panel one' }, |
| ITEMS = "Data:ItemData.json",
| | { label = dictionary.translate('Music'), content = 'panel two' }, |
| ABILITIES = "Data:AbilityData.json",
| | } |
| HEROES = "Data:HeroData.json",
| | return mw.ext.tabber.render(tabs) |
| 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 | | end |
|
| |
|
| return p | | return p |