Module:StreetBrawlAbilities: Difference between revisionsGive feedback
Jump to navigation
Jump to search
will hold functions related to street brawl ability changes |
Add report() to list Street Brawl ability changes grouped by hero (with help from vergir-bot LLM) |
||
| Line 1: | Line 1: | ||
-- Lists the Street Brawl ability overrides from [[Data:StreetBrawlData.json]] with the | |||
-- {{DataProp}} call that cites each one and the base value it replaces. | |||
local p = {} | |||
local SB_PAGE = "Data:StreetBrawlData.json" | |||
local CARDS_PAGE = "Data:AbilityCards.json" | |||
local HERO_PAGE = "Data:HeroData.json" | |||
local UNMATCHED = "Unmatched abilities" | |||
-- Maps each ability key to the hero who owns it. | |||
local function build_owner_map() | |||
local cards = mw.loadJsonData(CARDS_PAGE) | |||
local heroes = mw.loadJsonData(HERO_PAGE) | |||
local map = {} | |||
for hero_key, card in pairs(cards) do | |||
local hero = heroes[hero_key] | |||
-- Unreleased hero stubs reuse released heroes' ability keys (hero_gunslinger | |||
-- carries Paradox's Pulse Grenade), so only live hero records may claim one. | |||
if type(card) == "table" and hero and hero.Name and hero.IsDisabled ~= true then | |||
for slot = 1, 8 do | |||
local ability = card[tostring(slot)] | |||
if type(ability) == "table" and ability.Key then | |||
map[ability.Key] = { hero = hero.Name, slot = slot, name = ability.Name } | |||
end | |||
end | |||
end | |||
end | |||
return map | |||
end | |||
-- Orders one node's keys: plain props before Upgrades, Value before Scale, tiers numerically. | |||
local function sorted_keys(node, top) | |||
local keys = {} | |||
for k in pairs(node) do | |||
-- Name labels the record; it is not one of the changed properties. | |||
if not (top and k == "Name") then keys[#keys + 1] = k end | |||
end | |||
table.sort(keys, function(a, b) | |||
if top then | |||
if a == "Upgrades" then return false end | |||
if b == "Upgrades" then return true end | |||
end | |||
if a == "Value" then return true end | |||
if b == "Value" then return false end | |||
local na, nb = tonumber(a), tonumber(b) | |||
if na and nb then return na < nb end | |||
return a < b | |||
end) | |||
return keys | |||
end | |||
-- Collects the changed properties as dot-notation paths, the form {{DataProp}} accepts. | |||
local function flatten(node, prefix, out, top) | |||
for _, key in ipairs(sorted_keys(node, top)) do | |||
local value = node[key] | |||
local path = prefix and (prefix .. "." .. key) or key | |||
if type(value) == "table" then | |||
flatten(value, path, out, false) | |||
else | |||
out[#out + 1] = path | |||
end | |||
end | |||
end | |||
-- Builds one property line: the wikitext to copy, its Street Brawl value, and the default. | |||
local function render_line(frame, ability, path) | |||
local call = "{{DataProp|" .. ability .. "|" .. path .. "|streetbrawl}}" | |||
-- Expanded rather than read from the JSON, so this shows what a copied call really gives. | |||
local sb = frame:expandTemplate{ title = "DataProp", args = { ability, path, "streetbrawl" } } | |||
local base = frame:expandTemplate{ title = "DataProp", args = { ability, path } } | |||
return "* <code>" .. mw.text.nowiki(call) .. "</code> → '''" .. sb | |||
.. "''' ''(default " .. base .. ")''" | |||
end | |||
-- {{#invoke:StreetBrawlAbilities|report}} renders every change, grouped by hero. | |||
function p.report(frame) | |||
local changes = mw.loadJsonData(SB_PAGE)["ability-changes"] | |||
local owners = build_owner_map() | |||
local by_hero, hero_order = {}, {} | |||
for key, record in pairs(changes) do | |||
local owner = owners[key] | |||
local hero = owner and owner.hero or UNMATCHED | |||
if not by_hero[hero] then | |||
by_hero[hero] = {} | |||
hero_order[#hero_order + 1] = hero | |||
end | |||
local paths = {} | |||
flatten(record, nil, paths, true) | |||
table.insert(by_hero[hero], { | |||
slot = owner and owner.slot or 99, | |||
name = record.Name or (owner and owner.name) or key, | |||
paths = paths, | |||
}) | |||
end | |||
-- Anything the data adds that no live hero claims is listed last rather than dropped. | |||
table.sort(hero_order, function(a, b) | |||
if a == UNMATCHED then return false end | |||
if b == UNMATCHED then return true end | |||
return a < b | |||
end) | |||
local out = {} | |||
for _, hero in ipairs(hero_order) do | |||
local abilities = by_hero[hero] | |||
table.sort(abilities, function(a, b) return a.slot < b.slot end) | |||
local matched = hero ~= UNMATCHED | |||
out[#out + 1] = "\n== " | |||
.. (matched and frame:expandTemplate{ title = "HeroIcon", args = { hero } } or hero) | |||
.. " ==" | |||
for _, ability in ipairs(abilities) do | |||
out[#out + 1] = "=== " | |||
.. (matched and frame:expandTemplate{ title = "AbilityIcon", args = { ability.name } } or ability.name) | |||
.. " ===" | |||
for _, path in ipairs(ability.paths) do | |||
out[#out + 1] = render_line(frame, ability.name, path) | |||
end | |||
end | |||
end | |||
return table.concat(out, "\n") | |||
end | |||
return p | |||