Module:StreetBrawlItems: Difference between revisionsGive feedback
mNo edit summary |
m make item cards on the Street Brawl section sliiiightly bigger (+5%) |
||
| (9 intermediate revisions by 2 users not shown) | |||
| Line 7: | Line 7: | ||
-- Slot display order: Weapon -> Vitality (Armor) -> Spirit (Tech) | -- Slot display order: Weapon -> Vitality (Armor) -> Spirit (Tech) | ||
local SLOT_RANK = { Weapon = 1, Armor = 2, Tech = 3 } | local SLOT_RANK = { Weapon = 1, Armor = 2, Tech = 3 } | ||
-- | -- Card scale passed to ItemBox. | ||
local CARD_SIZE = "0. | local CARD_SIZE = "0.7" | ||
local NAME_TO_KEY | local NAME_TO_KEY = {} | ||
for key, h in pairs(heroes) do | for key, h in pairs(heroes) do | ||
if type(h) == "table" and h.Name then | if type(h) == "table" and h.Name then | ||
NAME_TO_KEY[mw.ustring.lower(h.Name)] = key | NAME_TO_KEY[mw.ustring.lower(h.Name)] = key | ||
end | end | ||
end | end | ||
| Line 54: | Line 52: | ||
end | end | ||
local function render_item(frame, code | local function render_item(frame, code) | ||
local name = iname(code); if not name then return nil end | local name = iname(code); if not name then return nil end | ||
-- These hero pages place hundreds of cards at once, so the cards render in | |||
local args = { name, size = | -- lowGraphics mode: ItemBox swaps its paper texture for a single cacheable | ||
-- overlay, which keeps scrolling smooth. See [[Template:ItemBox]]. | |||
local args = { name, size = CARD_SIZE, lowGraphics = "true" } | |||
-- Legendary (tier 5) cards omit the tier badge; it is redundant in the Legendary tier. | -- Legendary (tier 5) cards omit the tier badge; it is redundant in the Legendary tier. | ||
if itier(code) ~= 5 then args.showTierTag = "true" end | if itier(code) ~= 5 then args.showTierTag = "true" end | ||
return frame:expandTemplate{title = "ItemBox", args = args} | return frame:expandTemplate{title = "ItemBox", args = args} | ||
end | end | ||
| Line 71: | Line 67: | ||
if #parts == 0 then return "" end | if #parts == 0 then return "" end | ||
return '<div class="' .. class .. '">' .. table.concat(parts) .. '</div>' | return '<div class="' .. class .. '">' .. table.concat(parts) .. '</div>' | ||
end | |||
-- A tier/section heading: a plain div carrying the skin's own heading wrapper | |||
-- classes, so it inherits real heading styling while staying out of the table | |||
-- of contents (and can be hidden inside the gate). | |||
local function heading_div(label) | |||
return '<div class="mw-heading mw-heading4 sb-tier-head">' .. label .. '</div>' | |||
end | end | ||
local function hero_sets(hkey) | local function hero_sets(hkey) | ||
local favored, unfavored | local favored, unfavored, counter = {}, {}, {} | ||
for _, o in ipairs(sb.Outliers) do | for _, o in ipairs(sb.Outliers) do | ||
if o.Hero == hkey then | if o.Hero == hkey then | ||
if o.Bucket == "Good" then favored | if o.Bucket == "Good" then favored[o.Item] = true | ||
elseif type(o.Weight) == "number" and o.Weight < 1 then unfavored | elseif type(o.Weight) == "number" and o.Weight < 1 then unfavored[o.Item] = true end | ||
if o.Counter ~= nil then counter[#counter + 1] = o.Item end | if o.Counter ~= nil then counter[#counter + 1] = o.Item end | ||
end | end | ||
end | end | ||
return favored, unfavored | return favored, unfavored, counter | ||
end | end | ||
-- Returns sorted high (favored), | -- Returns sorted high (favored), average (pool), low (unfavored) item-code lists for a tier. | ||
local function collect(hkey, tier) | local function collect(hkey, tier) | ||
local favored, unfavored = hero_sets(hkey) | local favored, unfavored = hero_sets(hkey) | ||
local high, | local high, average, low = {}, {}, {} | ||
for _, c in ipairs(sb.AvailableItems) do | for _, c in ipairs(sb.AvailableItems) do | ||
if iok(c) and (tier == nil or itier(c) == tier) then | if iok(c) and (tier == nil or itier(c) == tier) then | ||
if favored[c] then high[#high + 1] = c | if favored[c] then high[#high + 1] = c | ||
elseif unfavored[c] then low[#low + 1] = c | elseif unfavored[c] then low[#low + 1] = c | ||
else | else average[#average + 1] = c end | ||
end | end | ||
end | end | ||
table.sort(high, item_less) | table.sort(high, item_less) | ||
table.sort( | table.sort(average, item_less) | ||
table.sort(low, item_less) | table.sort(low, item_less) | ||
return high, | return high, average, low | ||
end | end | ||
-- Builds one tier as conditional columns: | -- Builds one tier as conditional columns: | ||
-- High chance (favored) 35% | -- High chance (favored) takes 35%, the average column fills the rest. | ||
-- Low chance (unfavored) is dropped when empty; a narrow | -- Low chance (unfavored) is dropped when empty; a narrow column on the right when | ||
-- | -- High is present; a fit-content column on the right when High is absent. | ||
local function tier_columns(frame, hkey, tier | local function tier_columns(frame, hkey, tier, hi, avg, lo) | ||
local high, | local high, average, low = collect(hkey, tier) | ||
local function col(codes, cls, head) | local function col(codes, cls, head) | ||
local parts = {} | local parts = {} | ||
for _, c in ipairs(codes) do local r = render_item(frame, c | for _, c in ipairs(codes) do local r = render_item(frame, c); if r then parts[#parts + 1] = r end end | ||
return '<div class="sb-col ' .. cls .. '">' | return '<div class="sb-col ' .. cls .. '">' | ||
.. '<div class="sb-col-head">' .. head .. '</div>' | .. '<div class="sb-col-head">' .. head .. '</div>' | ||
.. '<div class=" | .. '<div class="sb-col-body sb-col-cards">' .. table.concat(parts) .. '</div>' | ||
.. '</div>' | .. '</div>' | ||
end | end | ||
local out = {} | local out = {} | ||
| Line 140: | Line 125: | ||
out[#out + 1] = col(high, "sb-col-high sb-w-high", hi) | out[#out + 1] = col(high, "sb-col-high sb-w-high", hi) | ||
end | end | ||
out[#out + 1] = col( | out[#out + 1] = col(average, "sb-col-average sb-w-fill", avg) | ||
if #low > 0 then | if #low > 0 then | ||
local low_w = (#high > 0) and "sb-low-narrow" or "sb-low-wide" | local low_w = (#high > 0) and "sb-low-narrow" or "sb-low-wide" | ||
out[#out + 1] = col(low, "sb-col-low " .. low_w, lo) | out[#out + 1] = col(low, "sb-col-low " .. low_w, lo) | ||
end | end | ||
return '<div class=" | return '<div class="sb-tier">' .. table.concat(out) .. '</div>' | ||
end | end | ||
-- {{#invoke:StreetBrawlItems| | -- {{#invoke:StreetBrawlItems|tiers|HERO|gate=ID|high=..|average=..|low=..|tier1=..|...|tier5=..}} | ||
-- All five tiers under styled (non-TOC) headings. All display text is supplied by the | |||
-- caller. With gate=ID, the Tier 1 heading and everything after it is wrapped in an open | |||
-- <div id=ID class="sb-gate">, whose inner <div class="sb-gate-body"> the caller must | |||
-- close after any trailing content. The id sits on the outer wrapper so following the | |||
-- anchor lands on the Tier 1 heading rather than the columns below it. | |||
-- | |||
-- | |||
function p.tiers(frame) | function p.tiers(frame) | ||
local hkey = hero_key(frame.args[1]); if not hkey then return "" end | local hkey = hero_key(frame.args[1]); if not hkey then return "" end | ||
local | local hi = frame.args.high or "" | ||
local | local avg = frame.args.average or "" | ||
local lo = frame.args.low or "" | |||
local | local gate = frame.args.gate | ||
local | if gate == "" then gate = nil end | ||
local out = {} | local out = {} | ||
for t = 1, 5 do | for t = 1, 5 do | ||
out[#out + 1] = "== | local head = heading_div(frame.args["tier" .. t] or "") | ||
local cols = tier_columns(frame, hkey, t, hi, avg, lo) | |||
if t == 1 and gate then | |||
out[#out + 1] = '<div id="' .. gate .. '" class="sb-gate">\n' | |||
.. head .. "\n" | |||
.. '<div class="sb-gate-body">\n' | |||
.. cols | |||
else | |||
out[#out + 1] = head .. "\n" .. cols | |||
end | |||
end | end | ||
return table.concat(out, "\n\n") | return table.concat(out, "\n\n") | ||
| Line 231: | Line 164: | ||
-- {{#invoke:StreetBrawlItems|hero_counter|HERO|heading=..|note=..}} | -- {{#invoke:StreetBrawlItems|hero_counter|HERO|heading=..|note=..}} | ||
-- The whole counter block (heading + note + cards), or "" when the hero has none. | |||
function p.hero_counter(frame) | function p.hero_counter(frame) | ||
local hkey = hero_key(frame.args[1]); if not hkey then return "" end | local hkey = hero_key(frame.args[1]); if not hkey then return "" end | ||
local | local _, _, counter = hero_sets(hkey) | ||
local codes = {} | local codes = {} | ||
| Line 241: | Line 175: | ||
local parts = {} | local parts = {} | ||
for _, c in ipairs(codes) do local r = render_item(frame, c | for _, c in ipairs(codes) do local r = render_item(frame, c); if r then parts[#parts + 1] = r end end | ||
local out = {} | local out = {} | ||
local heading = frame.args.heading | local heading = frame.args.heading | ||
if heading and heading ~= "" then out[#out + 1] = | if heading and heading ~= "" then out[#out + 1] = heading_div(heading) end | ||
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 | ||
| Line 252: | Line 186: | ||
end | end | ||
-- {{#invoke:StreetBrawlItems| | -- {{#invoke:StreetBrawlItems|counters_table}} | ||
-- Every hero that has counter items, one row each, sorted by hero name. Items | |||
-- within a row are listed alphabetically. Built as HTML rather than pipe-table | |||
-- markup so the invoke does not have to sit at the start of a line. | |||
function p.counters_table(frame) | |||
local | local by_hero, rows = {}, {} | ||
for _, o in ipairs(sb.Outliers) do | for _, o in ipairs(sb.Outliers) do | ||
if o.Item = | if o.Counter ~= nil and iok(o.Item) then | ||
if | local hero = heroes[o.Hero] | ||
if hero and hero.Name then | |||
local row = by_hero[o.Hero] | |||
if not row then | |||
row = { name = hero.Name, items = {}, seen = {} } | |||
by_hero[o.Hero] = row | |||
rows[#rows + 1] = row | |||
end | |||
-- Guard against a hero/item pair being listed twice. | |||
if not row.seen[o.Item] then | |||
row.seen[o.Item] = true | |||
row.items[#row.items + 1] = iname(o.Item) | |||
end | |||
end | |||
end | end | ||
end | end | ||
if #rows == 0 then return "" end | |||
table.sort(rows, function(a, b) return a.name < b.name end) | |||
table.sort( | |||
local | local tbl = mw.html.create("table"):addClass("wikitable") | ||
local head = tbl:tag("tr") | |||
head:tag("th"):wikitext("Hero") | |||
local | head:tag("th"):wikitext("Items") | ||
for _, row in ipairs(rows) do | |||
table.sort(row.items) | |||
local icons = {} | |||
for _, n in ipairs(row.items) do | |||
return | icons[#icons + 1] = frame:expandTemplate{title = "ItemIcon", args = {n}} | ||
end | |||
local tr = tbl:tag("tr") | |||
tr:tag("td"):wikitext(frame:expandTemplate{title = "HeroIcon", args = {row.name}}) | |||
tr:tag("td"):wikitext(table.concat(icons, ", ")) | |||
end | |||
return tostring(tbl) | |||
end | end | ||
return p | return p | ||