Module:StreetBrawlItems: Difference between revisionsGive feedback
Jump to navigation
Jump to search
Wrap rendered groups in flex rows so cards and hero icons have consistent spacing (with help from vergir-bot LLM) |
Hero section: move styling to classes (Template:HeroStreetBrawl/styles.css), sort items by slot then cost then name, wrap groups for spacing (with help from vergir-bot LLM) |
||
| Line 4: | Line 4: | ||
local items = mw.loadJsonData("Data:ItemData.json") | local items = mw.loadJsonData("Data:ItemData.json") | ||
local heroes = mw.loadJsonData("Data:HeroData.json") | local heroes = mw.loadJsonData("Data:HeroData.json") | ||
-- Slot display order: Weapon -> Vitality (Armor) -> Spirit (Tech) | |||
local SLOT_RANK = { Weapon = 1, Armor = 2, Tech = 3 } | |||
local NAME_TO_KEY, KEY_TO_NAME, RELEASED = {}, {}, {} | local NAME_TO_KEY, KEY_TO_NAME, RELEASED = {}, {}, {} | ||
| Line 28: | Line 31: | ||
if it.StreetBrawl == true then return 5 end | if it.StreetBrawl == true then return 5 end | ||
return it.Tier | return it.Tier | ||
end | |||
local function islot(code) local it = items[code]; return it and it.Slot end | |||
local function icost(code) | |||
local it = items[code]; local c = it and it.Cost | |||
return type(c) == "number" and c or math.huge | |||
end | |||
-- Sort order: slot (Weapon -> Armor -> Tech), then cost ascending, then name. | |||
local function item_less(a, b) | |||
local ra = SLOT_RANK[islot(a)] or 99 | |||
local rb = SLOT_RANK[islot(b)] or 99 | |||
if ra ~= rb then return ra < rb end | |||
local ca, cb = icost(a), icost(b) | |||
if ca ~= cb then return ca < cb end | |||
return (iname(a) or a) < (iname(b) or b) | |||
end | end | ||
| Line 40: | Line 58: | ||
end | end | ||
-- Lays rendered members out in a | -- Lays rendered members out in a container with the given class (styled via CSS). | ||
local function | local function classdiv(parts, class) | ||
if #parts == 0 then return "" end | if #parts == 0 then return "" end | ||
return '<div class="' .. class .. '">' .. table.concat(parts) .. '</div>' | |||
return '<div | |||
end | end | ||
| Line 97: | Line 98: | ||
if not style or style == "" then style = (group == "common") and "icon" or "card" end | 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 collapse = (frame.args.collapse == "yes" or frame.args.collapse == "true") | ||
local label = frame.args.label or "" | |||
local favored, unfavored, special = hero_sets(hkey) | local favored, unfavored, special = hero_sets(hkey) | ||
| Line 109: | Line 111: | ||
if iok(c) and (tier == nil or itier(c) == tier) and pred(c) then codes[#codes + 1] = c end | if iok(c) and (tier == nil or itier(c) == tier) and pred(c) then codes[#codes + 1] = c end | ||
end | end | ||
table.sort(codes, | table.sort(codes, item_less) | ||
local parts = {} | local parts = {} | ||
for _, c in ipairs(codes) do local r = render_item(frame, c, style); if r then parts[#parts + 1] = r end end | for _, c in ipairs(codes) do local r = render_item(frame, c, style); if r then parts[#parts + 1] = r end end | ||
local | if #parts == 0 then return "" end | ||
local | |||
return | if collapse then | ||
local head = (label ~= "" and label) or " " | |||
return '{| class="wikitable mw-collapsible mw-collapsed sb-common"\n' | |||
.. '! class="sb-common-head" | ' .. head .. '\n|-\n| ' | |||
.. classdiv(parts, "sb-icons") .. '\n|}' | |||
end | |||
local row_class = (style == "icon") and "sb-icons" or "sb-cards" | |||
local out = '<div class="sb-group">' | |||
if label ~= "" then out = out .. '<div class="sb-label">' .. label .. '</div>' end | |||
return out .. classdiv(parts, row_class) .. '</div>' | |||
end | end | ||
| Line 127: | Line 139: | ||
for _, c in ipairs(counter) do if iok(c) then codes[#codes + 1] = c end end | for _, c in ipairs(counter) do if iok(c) then codes[#codes + 1] = c end end | ||
if #codes == 0 then return "" end | if #codes == 0 then return "" end | ||
table.sort(codes, | table.sort(codes, item_less) | ||
local parts = {} | local parts = {} | ||
| Line 137: | Line 149: | ||
local note = frame.args.note | local note = frame.args.note | ||
if note and note ~= "" then out[#out + 1] = "''" .. note .. "''" end | if note and note ~= "" then out[#out + 1] = "''" .. note .. "''" end | ||
out[#out + 1] = | out[#out + 1] = classdiv(parts, "sb-cards") | ||
return table.concat(out, "\n") | return table.concat(out, "\n") | ||
end | end | ||
| Line 161: | Line 173: | ||
local parts = {} | local parts = {} | ||
for _, k in ipairs(list) do local r = render_hero(frame, k); if r then parts[#parts + 1] = r end end | for _, k in ipairs(list) do local r = render_hero(frame, k); if r then parts[#parts + 1] = r end end | ||
local | if #parts == 0 then return "" end | ||
local pre = "" | |||
local label = frame.args.label | |||
if label and label ~= "" then pre = pre .. "'''" .. label .. "'''<br>" end | |||
local note = frame.args.note | |||
if note and note ~= "" then pre = pre .. "''" .. note .. "''<br>" end | |||
return pre .. classdiv(parts, "sb-icons") | |||
end | end | ||
Revision as of 13:38, 19 July 2026
Documentation for this module may be created at Module:StreetBrawlItems/doc
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")
-- Slot display order: Weapon -> Vitality (Armor) -> Spirit (Tech)
local SLOT_RANK = { Weapon = 1, Armor = 2, Tech = 3 }
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 islot(code) local it = items[code]; return it and it.Slot end
local function icost(code)
local it = items[code]; local c = it and it.Cost
return type(c) == "number" and c or math.huge
end
-- Sort order: slot (Weapon -> Armor -> Tech), then cost ascending, then name.
local function item_less(a, b)
local ra = SLOT_RANK[islot(a)] or 99
local rb = SLOT_RANK[islot(b)] or 99
if ra ~= rb then return ra < rb end
local ca, cb = icost(a), icost(b)
if ca ~= cb then return ca < cb end
return (iname(a) or a) < (iname(b) or b)
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
-- Lays rendered members out in a container with the given class (styled via CSS).
local function classdiv(parts, class)
if #parts == 0 then return "" end
return '<div class="' .. class .. '">' .. table.concat(parts) .. '</div>'
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 label = frame.args.label or ""
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, item_less)
local parts = {}
for _, c in ipairs(codes) do local r = render_item(frame, c, style); if r then parts[#parts + 1] = r end end
if #parts == 0 then return "" end
if collapse then
local head = (label ~= "" and label) or " "
return '{| class="wikitable mw-collapsible mw-collapsed sb-common"\n'
.. '! class="sb-common-head" | ' .. head .. '\n|-\n| '
.. classdiv(parts, "sb-icons") .. '\n|}'
end
local row_class = (style == "icon") and "sb-icons" or "sb-cards"
local out = '<div class="sb-group">'
if label ~= "" then out = out .. '<div class="sb-label">' .. label .. '</div>' end
return out .. classdiv(parts, row_class) .. '</div>'
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, item_less)
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 .. "''" end
out[#out + 1] = classdiv(parts, "sb-cards")
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
if #parts == 0 then return "" end
local pre = ""
local label = frame.args.label
if label and label ~= "" then pre = pre .. "'''" .. label .. "'''<br>" end
local note = frame.args.note
if note and note ~= "" then pre = pre .. "''" .. note .. "''<br>" end
return pre .. classdiv(parts, "sb-icons")
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