Module:StreetBrawlItems: Difference between revisionsGive feedback
Jump to navigation
Jump to search
Created blank page |
Populate Street Brawl item-weighting helper: joins Data:StreetBrawlData.json with ItemData/HeroData, renders favored/unfavored/common/counter groups for hero and item pages (with help from vergir-bot LLM) |
||
| Line 1: | Line 1: | ||
local p = {} | |||
local sb = mw.loadJsonData("Data:StreetBrawlData.json")["item-buckets"] | |||
local items = mw.loadJsonData("Data:ItemData.json") | |||
local heroes = mw.loadJsonData("Data:HeroData.json") | |||
local NAME_TO_KEY, KEY_TO_NAME, RELEASED = {}, {}, {} | |||
for key, h in pairs(heroes) do | |||
if type(h) == "table" and h.Name then | |||
KEY_TO_NAME[key] = h.Name | |||
NAME_TO_KEY[mw.ustring.lower(h.Name)] = key | |||
if not h.InDevelopment and not h.IsDisabled then RELEASED[key] = true end | |||
end | |||
end | |||
local function hero_key(input) | |||
if not input then return nil end | |||
input = mw.text.trim(input) | |||
if input == "" then return nil end | |||
if heroes[input] then return input end | |||
return NAME_TO_KEY[mw.ustring.lower(input)] | |||
end | |||
local function iname(code) local it = items[code]; return it and it.Name or nil end | |||
local function iok(code) local it = items[code]; return it ~= nil and it.Name ~= nil end | |||
local function itier(code) | |||
local it = items[code]; if not it then return nil end | |||
if it.StreetBrawl == true then return 5 end | |||
return it.Tier | |||
end | |||
local function render_item(frame, code, style) | |||
local name = iname(code); if not name then return nil end | |||
if style == "icon" then return frame:expandTemplate{title = "ItemIcon", args = {name}} end | |||
return frame:expandTemplate{title = "ItemBox", args = {name, showTierTag = "true"}} | |||
end | |||
local function render_hero(frame, key) | |||
local name = KEY_TO_NAME[key]; if not name then return nil end | |||
return frame:expandTemplate{title = "HeroIcon", args = {name}} | |||
end | |||
-- Wraps a rendered list with an optional bold label / italic note, or a collapsible box. | |||
-- Returns "" when the list is empty, so nothing (not even the label) shows. | |||
local function wrap(inner, label, note, collapse) | |||
if inner == "" then return "" end | |||
if collapse then | |||
local header = (label and label ~= "") and label or " " | |||
return '{| class="wikitable mw-collapsible mw-collapsed" style="width:100%"\n' | |||
.. '! style="text-align:left" | ' .. header .. '\n|-\n| ' .. inner .. '\n|}' | |||
end | |||
local pre = "" | |||
if label and label ~= "" then pre = pre .. "'''" .. label .. "'''<br>" end | |||
if note and note ~= "" then pre = pre .. "''" .. note .. "''<br>" end | |||
return pre .. inner | |||
end | |||
local function hero_sets(hkey) | |||
local favored, unfavored, special, counter = {}, {}, {}, {} | |||
for _, o in ipairs(sb.Outliers) do | |||
if o.Hero == hkey then | |||
if o.Bucket == "Good" then favored[o.Item] = true; special[o.Item] = true | |||
elseif type(o.Weight) == "number" and o.Weight < 1 then unfavored[o.Item] = true; special[o.Item] = true end | |||
if o.Counter ~= nil then counter[#counter + 1] = o.Item end | |||
end | |||
end | |||
return favored, unfavored, special, counter | |||
end | |||
local function item_code(input) | |||
input = mw.text.trim(input or "") | |||
if input == "" then return nil end | |||
if items[input] then return input end | |||
for k, v in pairs(items) do | |||
if type(v) == "table" and v.Name == input and v.IsDisabled ~= true then return k end | |||
end | |||
for k, v in pairs(items) do | |||
if type(v) == "table" and v.Name == input then return k end | |||
end | |||
return nil | |||
end | |||
-- {{#invoke:StreetBrawlItems|hero|HERO|GROUP|TIER|label=..|collapse=yes|style=card|icon}} | |||
-- GROUP = favored | unfavored | common (use hero_counter for counters) | |||
function p.hero(frame) | |||
local hkey = hero_key(frame.args[1]); if not hkey then return "" end | |||
local group = mw.text.trim(frame.args[2] or "") | |||
local tier = tonumber(frame.args[3]) | |||
local style = frame.args.style | |||
if not style or style == "" then style = (group == "common") and "icon" or "card" end | |||
local collapse = (frame.args.collapse == "yes" or frame.args.collapse == "true") | |||
local favored, unfavored, special = hero_sets(hkey) | |||
local pred | |||
if group == "favored" then pred = function(c) return favored[c] end | |||
elseif group == "unfavored" then pred = function(c) return unfavored[c] end | |||
elseif group == "common" then pred = function(c) return not special[c] end | |||
else return "" end | |||
local codes = {} | |||
for _, c in ipairs(sb.AvailableItems) do | |||
if iok(c) and (tier == nil or itier(c) == tier) and pred(c) then codes[#codes + 1] = c end | |||
end | |||
table.sort(codes, function(a, b) return (iname(a) or a) < (iname(b) or b) end) | |||
local parts = {} | |||
for _, c in ipairs(codes) do local r = render_item(frame, c, style); if r then parts[#parts + 1] = r end end | |||
return wrap(table.concat(parts), frame.args.label, nil, collapse) | |||
end | |||
-- {{#invoke:StreetBrawlItems|hero_counter|HERO|heading=..|note=..}} | |||
-- Returns the whole counter block (heading + note + cards), or "" when empty. | |||
function p.hero_counter(frame) | |||
local hkey = hero_key(frame.args[1]); if not hkey then return "" end | |||
local _, _, _, counter = hero_sets(hkey) | |||
local codes = {} | |||
for _, c in ipairs(counter) do if iok(c) then codes[#codes + 1] = c end end | |||
if #codes == 0 then return "" end | |||
table.sort(codes, function(a, b) return (iname(a) or a) < (iname(b) or b) end) | |||
local parts = {} | |||
for _, c in ipairs(codes) do local r = render_item(frame, c, "card"); if r then parts[#parts + 1] = r end end | |||
local out = {} | |||
local heading = frame.args.heading | |||
if heading and heading ~= "" then out[#out + 1] = "=== " .. heading .. " ===" end | |||
local note = frame.args.note | |||
if note and note ~= "" then out[#out + 1] = "''" .. note .. "''\n" end | |||
out[#out + 1] = table.concat(parts) | |||
return table.concat(out, "\n") | |||
end | |||
-- {{#invoke:StreetBrawlItems|item|ITEM|GROUP|label=..|note=..}} | |||
-- GROUP = favored | unfavored | counter | |||
function p.item(frame) | |||
local code = item_code(frame.args[1]); if not code then return "" end | |||
local group = mw.text.trim(frame.args[2] or "") | |||
local fav, unf, ctr = {}, {}, {} | |||
for _, o in ipairs(sb.Outliers) do | |||
if o.Item == code and RELEASED[o.Hero] then | |||
if o.Bucket == "Good" then fav[#fav + 1] = o.Hero | |||
elseif type(o.Weight) == "number" and o.Weight < 1 then unf[#unf + 1] = o.Hero end | |||
if o.Counter ~= nil then ctr[#ctr + 1] = o.Hero end | |||
end | |||
end | |||
local list = (group == "favored" and fav) or (group == "unfavored" and unf) or (group == "counter" and ctr) | |||
if not list then return "" end | |||
table.sort(list, function(a, b) return (KEY_TO_NAME[a] or a) < (KEY_TO_NAME[b] or b) end) | |||
local parts = {} | |||
for _, k in ipairs(list) do local r = render_hero(frame, k); if r then parts[#parts + 1] = r end end | |||
return wrap(table.concat(parts), frame.args.label, frame.args.note, false) | |||
end | |||
-- {{#invoke:StreetBrawlItems|item_in_pool|ITEM}} -> "1" if in the Street Brawl pool, else "" | |||
function p.item_in_pool(frame) | |||
local code = item_code(frame.args[1]); if not code then return "" end | |||
for _, c in ipairs(sb.AvailableItems) do if c == code then return "1" end end | |||
return "" | |||
end | |||
return p | |||