Module:Dice: Difference between revisions
From Teriock
More actions
// via Wikitext Extension for VSCode |
No edit summary |
||
| (7 intermediate revisions by the same user not shown) | |||
| Line 40: | Line 40: | ||
end | end | ||
-- Parse an individual part into prefix, dice, and | -- Parse an individual part into prefix, dice, qualifiers, and constant | ||
local function parsePart(part) | local function parsePart(part) | ||
local s = trim(part) | local s = trim(part) | ||
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = nil, nil, nil, nil, {} | local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = nil, nil, nil, nil, {} | ||
local constValue = nil | |||
-- Extract qualifiers in [brackets] | -- Extract qualifiers in [brackets] | ||
| Line 52: | Line 53: | ||
table.insert(qualifiers, q) | table.insert(qualifiers, q) | ||
end | end | ||
table.sort(qualifiers) | table.sort(qualifiers) | ||
end | end | ||
| Line 59: | Line 59: | ||
local rawPref = s:match("^%(([^)]+)%)") | local rawPref = s:match("^%(([^)]+)%)") | ||
if rawPref then | if rawPref then | ||
local num, flag = rawPref:match("^(%d+)%s*%*%s*@(%a+)$") | local num, flag = rawPref:match("^(%d+)%s*%*%s*@(%a+)$") | ||
if not num then | if not num then num, flag = rawPref:match("^(%d*)@(%a+)$") end | ||
prefixCount = tonumber(num) or 1 | prefixCount = tonumber(num) or 1 | ||
prefixFlag = flag | prefixFlag = flag | ||
s = s:gsub("^%b()", "", 1) | s = s:gsub("^%b()", "", 1) | ||
else | else | ||
local pure = s:match("^@(%a+)$") | local pure = s:match("^@(%a+)$") | ||
if pure then | if pure then | ||
| Line 82: | Line 78: | ||
diceCount = tonumber(cnt) or 1 | diceCount = tonumber(cnt) or 1 | ||
diceFaces = faces | diceFaces = faces | ||
else | |||
-- If not dice, check for a bare numeric constant | |||
local num = s:match("^(%d+)$") | |||
if num then | |||
constValue = tonumber(num) | |||
end | |||
end | end | ||
return prefixCount, prefixFlag, diceCount, diceFaces, qualifiers | return prefixCount, prefixFlag, diceCount, diceFaces, qualifiers, constValue | ||
end | end | ||
| Line 96: | Line 98: | ||
for _, part in ipairs(parts) do | for _, part in ipairs(parts) do | ||
if part == "+" or part == "-" then | if part == "+" or part == "-" then | ||
-- operator | |||
table.insert(quickParts, part) | table.insert(quickParts, part) | ||
table.insert(content, " " .. part .. " ") | table.insert(content, " " .. part .. " ") | ||
else | else | ||
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = parsePart(part) | -- parse dice, prefix, or constant | ||
-- Build quick-roll sequence (ignore flags) | local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers, constValue = parsePart(part) | ||
-- If it's truly plain (no dice/prefix/qualifiers), keep old behavior | |||
if not diceCount and not prefixFlag and #qualifiers == 0 and not constValue then | |||
table.insert(quickParts, part) | |||
table.insert(content, part) | |||
else | |||
-- Build quick-roll sequence (ignore flags) | |||
if diceCount then | |||
table.insert(quickParts, diceCount .. "d" .. diceFaces) | |||
elseif constValue then | |||
table.insert(quickParts, tostring(constValue)) | |||
end | |||
-- Prefix template for typed rolls (only before dice, as before) | |||
if prefixFlag and typ ~= "none" and diceCount then | |||
local tpl = "{{" .. string.upper(prefixFlag) | |||
if prefixCount and prefixCount ~= 1 then tpl = tpl .. "|" .. prefixCount end | |||
tpl = tpl .. "}}" | |||
table.insert(content, frame:preprocess(tpl)) | |||
end | |||
-- Build dice link or output constant/pure prefix | |||
if diceCount then | |||
local displayDice = diceCount .. "d" .. diceFaces | |||
local linkLabel = displayDice | |||
if prefixFlag then | |||
if typ == "none" and #qualifiers == 0 then | |||
local prefixLabel = string.upper(prefixFlag) | |||
if prefixCount and prefixCount > 1 then prefixLabel = prefixCount .. prefixLabel end | |||
linkLabel = prefixLabel .. "d" .. diceFaces | |||
prefixLabel = | else | ||
linkLabel = "d" .. diceFaces | |||
end | end | ||
end | end | ||
table.insert(content, | |||
string.format("[https://dice.run/#/d/%dd%s %s]", diceCount, diceFaces, linkLabel) | |||
) | |||
elseif constValue then | |||
-- numeric constant (e.g., 4[lethal]) | |||
table.insert(content, tostring(constValue)) | |||
elseif prefixFlag then | |||
-- pure prefix | |||
local tpl = "{{" .. string.upper(prefixFlag) | |||
if prefixCount and prefixCount ~= 1 then tpl = tpl .. "|" .. prefixCount end | |||
tpl = tpl .. "}}" | |||
table.insert(content, frame:preprocess(tpl)) | |||
else | |||
-- fallback: unknown token but with qualifiers; keep original token text | |||
table.insert(content, part) | |||
end | end | ||
-- Qualifier labels | |||
for _, q in ipairs(qualifiers) do | |||
table.insert(content, frame:preprocess(" {{L|" .. capitalize(typ) .. "|" .. capitalize(q) .. "}}")) | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
-- | -- assemble quick-roll and output | ||
local quickRoll = table.concat(quickParts, " ") | local quickRoll = table.concat(quickParts, " ") | ||
local inner = table.concat(content) | local inner = table.concat(content) | ||
return html.create('span') | |||
:addClass('dice') | :addClass('dice') | ||
:attr('data-full-roll', fullRoll) | :attr('data-full-roll', fullRoll) | ||
| Line 154: | Line 173: | ||
:attr('data-type', typ) | :attr('data-type', typ) | ||
:wikitext(inner) | :wikitext(inner) | ||
:tag("span") | |||
:addClass("metadata") | |||
:attr("data-full-roll", fullRoll) | |||
:attr("data-quick-roll", quickRoll) | |||
:attr("data-type", "roll") | |||
:attr("data-roll-type", typ) | |||
:done() | |||
:allDone() | |||
end | end | ||
return p | return p | ||
Latest revision as of 20:08, 19 November 2025
Documentation for this module may be created at Module:Dice/doc
-- Module:Dice
-- Processes dice input strings and outputs HTML spans with roll links, templates, and data attributes
local p = {}
local html = mw.html
-- Trim whitespace from both ends
local function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
-- Split the full roll string into parts and operators (+, -)
local function splitParts(s)
local parts = {}
local i = 1
while i <= #s do
local c = s:sub(i,i)
if c == "+" or c == "-" then
table.insert(parts, c)
i = i + 1
elseif c == " " then
i = i + 1
else
local j = s:find("[%+%-]", i)
if j then
table.insert(parts, trim(s:sub(i, j-1)))
i = j
else
table.insert(parts, trim(s:sub(i)))
break
end
end
end
return parts
end
-- Capitalize first letter
local function capitalize(s)
return (s:gsub("^%l", string.upper))
end
-- Parse an individual part into prefix, dice, qualifiers, and constant
local function parsePart(part)
local s = trim(part)
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = nil, nil, nil, nil, {}
local constValue = nil
-- Extract qualifiers in [brackets]
local main, qualStr = s:match("^(.-)%[([^%]]+)%]$")
if qualStr then
s = trim(main)
for q in qualStr:gmatch("([^ ]+)") do
table.insert(qualifiers, q)
end
table.sort(qualifiers)
end
-- Extract parenthesized prefix, e.g. (2@p), (@f), or (2 * @p)
local rawPref = s:match("^%(([^)]+)%)")
if rawPref then
local num, flag = rawPref:match("^(%d+)%s*%*%s*@(%a+)$")
if not num then num, flag = rawPref:match("^(%d*)@(%a+)$") end
prefixCount = tonumber(num) or 1
prefixFlag = flag
s = s:gsub("^%b()", "", 1)
else
local pure = s:match("^@(%a+)$")
if pure then
prefixCount = 1
prefixFlag = pure
s = ""
end
end
-- Extract dice, e.g. 2d6 or d8
if s:match("^%d*d%d+") then
local cnt, faces = s:match("^(%d*)d(%d+)")
diceCount = tonumber(cnt) or 1
diceFaces = faces
else
-- If not dice, check for a bare numeric constant
local num = s:match("^(%d+)$")
if num then
constValue = tonumber(num)
end
end
return prefixCount, prefixFlag, diceCount, diceFaces, qualifiers, constValue
end
-- Main entry for #invoke:Dice|d|<roll>|<type>
function p.d(frame)
local fullRoll = frame.args[1] or ""
local typ = frame.args[2] or "none"
local parts = splitParts(fullRoll)
local quickParts, content = {}, {}
for _, part in ipairs(parts) do
if part == "+" or part == "-" then
-- operator
table.insert(quickParts, part)
table.insert(content, " " .. part .. " ")
else
-- parse dice, prefix, or constant
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers, constValue = parsePart(part)
-- If it's truly plain (no dice/prefix/qualifiers), keep old behavior
if not diceCount and not prefixFlag and #qualifiers == 0 and not constValue then
table.insert(quickParts, part)
table.insert(content, part)
else
-- Build quick-roll sequence (ignore flags)
if diceCount then
table.insert(quickParts, diceCount .. "d" .. diceFaces)
elseif constValue then
table.insert(quickParts, tostring(constValue))
end
-- Prefix template for typed rolls (only before dice, as before)
if prefixFlag and typ ~= "none" and diceCount then
local tpl = "{{" .. string.upper(prefixFlag)
if prefixCount and prefixCount ~= 1 then tpl = tpl .. "|" .. prefixCount end
tpl = tpl .. "}}"
table.insert(content, frame:preprocess(tpl))
end
-- Build dice link or output constant/pure prefix
if diceCount then
local displayDice = diceCount .. "d" .. diceFaces
local linkLabel = displayDice
if prefixFlag then
if typ == "none" and #qualifiers == 0 then
local prefixLabel = string.upper(prefixFlag)
if prefixCount and prefixCount > 1 then prefixLabel = prefixCount .. prefixLabel end
linkLabel = prefixLabel .. "d" .. diceFaces
else
linkLabel = "d" .. diceFaces
end
end
table.insert(content,
string.format("[https://dice.run/#/d/%dd%s %s]", diceCount, diceFaces, linkLabel)
)
elseif constValue then
-- numeric constant (e.g., 4[lethal])
table.insert(content, tostring(constValue))
elseif prefixFlag then
-- pure prefix
local tpl = "{{" .. string.upper(prefixFlag)
if prefixCount and prefixCount ~= 1 then tpl = tpl .. "|" .. prefixCount end
tpl = tpl .. "}}"
table.insert(content, frame:preprocess(tpl))
else
-- fallback: unknown token but with qualifiers; keep original token text
table.insert(content, part)
end
-- Qualifier labels
for _, q in ipairs(qualifiers) do
table.insert(content, frame:preprocess(" {{L|" .. capitalize(typ) .. "|" .. capitalize(q) .. "}}"))
end
end
end
end
-- assemble quick-roll and output
local quickRoll = table.concat(quickParts, " ")
local inner = table.concat(content)
return html.create('span')
:addClass('dice')
:attr('data-full-roll', fullRoll)
:attr('data-quick-roll', quickRoll)
:attr('data-type', typ)
:wikitext(inner)
:tag("span")
:addClass("metadata")
:attr("data-full-roll", fullRoll)
:attr("data-quick-roll", quickRoll)
:attr("data-type", "roll")
:attr("data-roll-type", typ)
:done()
:allDone()
end
return p