|
|
| Line 916: |
Line 916: |
| end | | end |
|
| |
|
| -- PLUGIN: SkillType (Hero Bar Slot 2) - 2 rows x 3 cells (desktop + mobile). | | -- PLUGIN: QuickStats (Hero Module Slot 2) - 3x2 grid (range/area/cost/cast/cd/duration). |
| -- Rules:
| | -- NOTE: Hits intentionally does NOT belong here (it lives in SkillType). |
| -- - If skill is non-damaging, hide Damage/Element/Hits.
| | function PLUGINS.QuickStats(rec, ctx) |
| -- - If Hits is empty, hide Hits. | | local level = ctx.level or 1 |
| -- - If Combo is empty, hide Combo.
| | local maxLevel = ctx.maxLevel or 1 |
| -- Ordering:
| | local promo = ctx.promo |
| -- - Desktop: Damage, Element, Hits, Target, Cast, Combo
| |
| -- - Mobile: Damage, Element, Target, Cast, Hits, Combo (CSS reorder)
| |
| function PLUGINS.SkillType(rec, ctx) | |
| local typeBlock = (type(rec.Type) == "table") and rec.Type or {} | |
| local mech = (type(rec.Mechanics) == "table") and rec.Mechanics or {} | |
|
| |
|
| local level = ctx.level or 1 | | local mech = (type(rec) == "table" and type(rec.Mechanics) == "table") and rec.Mechanics or {} |
| local maxLevel = ctx.maxLevel or 1 | | local bt = (type(mech["Basic Timings"]) == "table") and mech["Basic Timings"] or {} |
| | local rc = (type(mech["Resource Cost"]) == "table") and mech["Resource Cost"] or {} |
|
| |
|
| local hideDamageBundle = (ctx.nonDamaging == true) | | local function dash() return "—" end |
|
| |
|
| -- valName: extract a display string from typical {Name/ID/Value} objects. | | -- Range (0 => —) |
| local function valName(x) | | local rangeVal = nil |
| if x == nil then return nil end
| | if mech.Range ~= nil and not isNoneLike(mech.Range) then |
| if type(x) == "table" then | | local n = toNum(mech.Range) |
| if x.Name and x.Name ~= "" then return tostring(x.Name) end | | if n ~= nil then |
| if x.ID and x.ID ~= "" then return tostring(x.ID) end | | if n ~= 0 then |
| if x.Value ~= nil then return tostring(x.Value) end | | rangeVal = mw.text.nowiki(formatUnitValue(mech.Range) or tostring(mech.Range)) |
| | end |
| | else |
| | local t = mw.text.trim(tostring(mech.Range)) |
| | if t ~= "" and not isNoneLike(t) then |
| | rangeVal = mw.text.nowiki(t) |
| | end |
| end | | end |
| if type(x) == "string" and x ~= "" then
| |
| return x
| |
| end
| |
| return nil
| |
| end | | end |
|
| |
|
| -- hitsDisplay: find + render Hits from multiple possible structured locations. | | -- Area |
| local function hitsDisplay() | | local areaVal = formatAreaSize(mech.Area) |
| local h =
| |
| typeBlock.Hits or typeBlock["Hits"] or typeBlock["Hit Count"] or typeBlock["Hits Count"] or
| |
| mech.Hits or mech["Hits"] or mech["Hit Count"] or mech["Hits Count"] or
| |
| rec.Hits or rec["Hits"]
| |
|
| |
|
| if h == nil or isNoneLike(h) then
| | -- Timings |
| return nil
| | local castVal = displayFromSeries(seriesFromValuePair(bt["Cast Time"], maxLevel), level) |
| end
| | local cdVal = displayFromSeries(seriesFromValuePair(bt["Cooldown"], maxLevel), level) |
| | local durVal = displayFromSeries(seriesFromValuePair(bt["Duration"], maxLevel), level) |
|
| |
|
| -- ValuePair-style table (Base/Per Level) => dynamic series
| | -- Promote status duration if needed |
| if type(h) == "table" then
| | if (durVal == nil) and type(promo) == "table" and type(promo.durationBlock) == "table" then |
| if h.Base ~= nil or h["Per Level"] ~= nil or type(h["Per Level"]) == "table" then
| | durVal = displayFromSeries(seriesFromValuePair(promo.durationBlock, maxLevel), level) |
| return displayFromSeries(seriesFromValuePair(h, maxLevel), level)
| | end |
| end
| |
| | |
| -- Unit block {Value, Unit}
| |
| if h.Value ~= nil then
| |
| local t = formatUnitValue(h)
| |
| return t and mw.text.nowiki(t) or nil
| |
| end
| |
|
| |
|
| -- Fallback name extraction
| | -- Cost: MP + HP |
| local vn = valName(h)
| | local function labeledSeries(block, label) |
| if vn and not isNoneLike(vn) then
| | local s = seriesFromValuePair(block, maxLevel) |
| return mw.text.nowiki(vn) | | if not s then return nil end |
| | local any = false |
| | for i, v in ipairs(s) do |
| | if v ~= "—" then |
| | s[i] = tostring(v) .. " " .. label |
| | any = true |
| | else |
| | s[i] = "—" |
| end | | end |
| end | | end |
| | | return any and s or nil |
| -- Scalar number/string | |
| if type(h) == "number" then
| |
| return mw.text.nowiki(fmtNum(h))
| |
| end
| |
| if type(h) == "string" then
| |
| local t = trim(h)
| |
| return (t and not isNoneLike(t)) and mw.text.nowiki(t) or nil
| |
| end
| |
| | |
| return nil
| |
| end | | end |
|
| |
|
| -- comboDisplay: render Combo as a compact text block (Type (+ details)). | | local mpS = labeledSeries(rc["Mana Cost"], "MP") |
| local function comboDisplay() | | local hpS = labeledSeries(rc["Health Cost"], "HP") |
| local c = (type(mech.Combo) == "table") and mech.Combo or nil
| |
| if not c then return nil end
| |
|
| |
|
| local typ = trim(c.Type)
| | local costSeries = {} |
| if not typ or isNoneLike(typ) then | | for lv = 1, maxLevel do |
| return nil
| | local mp = mpS and mpS[lv] or "—" |
| end
| | local hp = hpS and hpS[lv] or "—" |
| | |
| local details = {} | |
|
| |
|
| local pct = formatUnitValue(c.Percent) | | if mp ~= "—" and hp ~= "—" then |
| if pct and not isZeroish(pct) then
| | costSeries[lv] = mp .. " + " .. hp |
| table.insert(details, mw.text.nowiki(pct)) | | elseif mp ~= "—" then |
| end | | costSeries[lv] = mp |
| | | elseif hp ~= "—" then |
| local dur = formatUnitValue(c.Duration) | | costSeries[lv] = hp |
| if dur and not isZeroish(dur) then | | else |
| table.insert(details, mw.text.nowiki(dur)) | | costSeries[lv] = "—" |
| end | | end |
| | end |
|
| |
|
| if #details > 0 then
| | local costVal = displayFromSeries(costSeries, level) |
| return mw.text.nowiki(typ) .. " (" .. table.concat(details, ", ") .. ")"
| |
| end
| |
| return mw.text.nowiki(typ)
| |
| end
| |
|
| |
|
| local grid = mw.html.create("div") | | local grid = mw.html.create("div") |
| grid:addClass("sv-type-grid") | | grid:addClass("sv-m4-grid") |
| grid:addClass("sv-compact-root") | | grid:addClass("sv-compact-root") |
|
| |
|
| local added = false
| | local function addCell(label, val) |
| | | local cell = grid:tag("div"):addClass("sv-m4-cell") |
| -- addChunk: add one labeled value cell (key drives CSS ordering).
| | cell:tag("div"):addClass("sv-m4-label"):wikitext(mw.text.nowiki(label)) |
| local function addChunk(key, label, valueHtml) | | cell:tag("div"):addClass("sv-m4-value"):wikitext(val or dash()) |
| if valueHtml == nil or valueHtml == "" then return end
| |
| added = true
| |
| | |
| local chunk = grid:tag("div") | |
| :addClass("sv-type-chunk")
| |
| :addClass("sv-type-" .. tostring(key))
| |
| :attr("data-type-key", tostring(key))
| |
| | |
| chunk:tag("div") | |
| :addClass("sv-type-label")
| |
| :wikitext(mw.text.nowiki(label))
| |
| | |
| chunk:tag("div") | |
| :addClass("sv-type-value")
| |
| :wikitext(valueHtml)
| |
| end
| |
| | |
| -- Damage + Element + Hits bundle (hidden when non-damaging)
| |
| if not hideDamageBundle then
| |
| local dmg = valName(typeBlock.Damage or typeBlock["Damage Type"])
| |
| local ele = valName(typeBlock.Element or typeBlock["Element Type"])
| |
| local hits = hitsDisplay()
| |
| | |
| if dmg and not isNoneLike(dmg) then
| |
| addChunk("damage", "Damage", mw.text.nowiki(dmg))
| |
| end
| |
| if ele and not isNoneLike(ele) then
| |
| addChunk("element", "Element", mw.text.nowiki(ele))
| |
| end
| |
| -- Hits: only render when present and meaningful
| |
| if hits then
| |
| addChunk("hits", "Hits", hits)
| |
| end
| |
| end | | end |
|
| |
|
| -- Target + Cast | | addCell("Range", rangeVal) |
| local tgt = valName(typeBlock.Target or typeBlock["Target Type"])
| | addCell("Area", areaVal) |
| local cst = valName(typeBlock.Cast or typeBlock["Cast Type"]) | | addCell("Cost", costVal) |
| | | addCell("Cast Time", castVal) |
| if tgt and not isNoneLike(tgt) then | | addCell("Cooldown", cdVal) |
| addChunk("target", "Target", mw.text.nowiki(tgt))
| | addCell("Duration", durVal) |
| end | |
| if cst and not isNoneLike(cst) then
| |
| addChunk("cast", "Cast", mw.text.nowiki(cst))
| |
| end | |
| | |
| -- Combo (moved here)
| |
| local combo = comboDisplay() | |
| if combo then
| |
| addChunk("combo", "Combo", combo)
| |
| end
| |
|
| |
|
| return { | | return { |
| inner = added and tostring(grid) or "", | | inner = tostring(grid), |
| classes = "module-skill-type", | | classes = "module-quick-stats", |
| } | | } |
| end | | end |