Module:Dice: Difference between revisions
From Teriock
More actions
// via Wikitext Extension for VSCode |
// via Wikitext Extension for VSCode |
||
| Line 1: | Line 1: | ||
-- Module:Dice | |||
-- Processes dice input strings and outputs HTML spans with roll links, templates, and data attributes | |||
local p = {} | local p = {} | ||
local html = mw.html | |||
-- | -- Trim whitespace from both ends | ||
local function trim(s) | 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 | end | ||
-- | -- Capitalize first letter | ||
local function | local function capitalize(s) | ||
return (s:gsub("^%l", string.upper)) | |||
end | end | ||
-- Parse | -- Parse an individual part into prefix, dice, and qualifiers | ||
local function | local function parsePart(part) | ||
local s = trim(part) | |||
local prefixCount, prefixFlag | |||
local diceCount, diceFaces | |||
local qualifiers = {} | |||
-- Extract qualifiers in [brackets] | |||
local qualStr = s:match("^(.-)%[([^%]]+)%]$") | |||
if qualStr then | |||
s = trim(s:match("^(.-)%[")) | |||
for q in qualStr:gmatch("([^ ]+)") do | |||
table.insert(qualifiers, q) | |||
for | |||
end | end | ||
end | |||
-- Extract parenthesized prefix, e.g. (2@p) or (@f) | |||
local pref = s:match("^%(([%d]*@%a+)%)") | |||
if pref then | |||
local num, flag = pref:match("^(%d*)@(%a+)") | |||
prefixCount = tonumber(num) or 1 | |||
prefixFlag = flag | |||
s = s:gsub("^%b()", "", 1) | |||
else | |||
-- Pure prefix without dice | |||
local pure = s:match("^@(%a+)$") | |||
if pure then | |||
prefixCount = 1 | |||
prefixFlag = pure | |||
s = "" | |||
end | end | ||
end | |||
-- Extract dice, e.g. 2d6 or d8 | |||
if s:match("^d%d+") then | |||
diceCount = 1 | |||
diceFaces = s:match("^d(%d+)") | |||
elseif s:match("^%d+d%d+") then | |||
local cnt, faces = s:match("^(%d+)d(%d+)") | |||
diceCount = tonumber(cnt) | |||
local | diceFaces = faces | ||
if | end | ||
return prefixCount, prefixFlag, diceCount, diceFaces, qualifiers | |||
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 = {} | |||
local content = {} | |||
for _, part in ipairs(parts) do | |||
if part == "+" or part == "-" then | |||
table.insert(quickParts, part) | |||
table.insert(content, " " .. part .. " ") | |||
else | else | ||
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = parsePart(part) | |||
-- Build quick-roll (ignore flags) | |||
if diceCount then | |||
table.insert(quickParts, diceCount .. "d" .. diceFaces) | |||
end | |||
-- Add prefix template when type is not 'none' | |||
if prefixFlag and typ ~= "none" and diceCount then | |||
local tpl = "{{" .. string.upper(prefixFlag) | |||
if prefixCount and prefixCount ~= 1 then | |||
tpl = tpl .. "|" .. prefixCount | |||
end | end | ||
tpl = tpl .. "}}" | |||
table.insert(content, tpl) | |||
end | |||
end | |||
-- | -- Build dice link | ||
if diceCount then | |||
local displayDice = (diceCount == 1) and ("d" .. diceFaces) or (diceCount .. "d" .. diceFaces) | |||
local linkLabel = displayDice | |||
-- Embed prefix in label when no type and no qualifiers | |||
if prefixFlag and typ == "none" and #qualifiers == 0 then | |||
linkLabel = (prefixCount or 1) .. string.upper(prefixFlag) .. displayDice | |||
if | |||
end | end | ||
local link = "[https://dice.run/#/d/" .. diceCount .. "d" .. diceFaces .. " " .. linkLabel .. "]" | |||
table.insert(content, link) | |||
elseif prefixFlag and not diceCount and typ ~= "none" then | |||
-- Pure prefix only for non-none type | |||
table.insert(content, "{{" .. string.upper(prefixFlag) .. "}}") | |||
end | |||
-- Add qualifier labels | |||
if qualifiers and #qualifiers > 0 then | |||
for _, q in ipairs(qualifiers) do | |||
table.insert(content, "{{L|" .. capitalize(typ) .. "|" .. capitalize(q) .. "}}") | |||
end | end | ||
end | |||
end | end | ||
end | |||
-- Assemble quick-roll and content | |||
local quickRoll = table.concat(quickParts, " ") | |||
local inner = table.concat(content) | |||
-- | -- Build final span | ||
local | local span = html.create('span') | ||
:addClass('dice') | |||
:attr('data-full-roll', fullRoll) | |||
:attr('data-quick-roll', quickRoll) | |||
:attr('data-type', typ) | |||
:wikitext(inner) | |||
return span:allDone() | |||
end | end | ||
return p | return p | ||
Revision as of 23:15, 19 June 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, and qualifiers
local function parsePart(part)
local s = trim(part)
local prefixCount, prefixFlag
local diceCount, diceFaces
local qualifiers = {}
-- Extract qualifiers in [brackets]
local qualStr = s:match("^(.-)%[([^%]]+)%]$")
if qualStr then
s = trim(s:match("^(.-)%["))
for q in qualStr:gmatch("([^ ]+)") do
table.insert(qualifiers, q)
end
end
-- Extract parenthesized prefix, e.g. (2@p) or (@f)
local pref = s:match("^%(([%d]*@%a+)%)")
if pref then
local num, flag = pref:match("^(%d*)@(%a+)")
prefixCount = tonumber(num) or 1
prefixFlag = flag
s = s:gsub("^%b()", "", 1)
else
-- Pure prefix without dice
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+") then
diceCount = 1
diceFaces = s:match("^d(%d+)")
elseif s:match("^%d+d%d+") then
local cnt, faces = s:match("^(%d+)d(%d+)")
diceCount = tonumber(cnt)
diceFaces = faces
end
return prefixCount, prefixFlag, diceCount, diceFaces, qualifiers
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 = {}
local content = {}
for _, part in ipairs(parts) do
if part == "+" or part == "-" then
table.insert(quickParts, part)
table.insert(content, " " .. part .. " ")
else
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = parsePart(part)
-- Build quick-roll (ignore flags)
if diceCount then
table.insert(quickParts, diceCount .. "d" .. diceFaces)
end
-- Add prefix template when type is not 'none'
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, tpl)
end
-- Build dice link
if diceCount then
local displayDice = (diceCount == 1) and ("d" .. diceFaces) or (diceCount .. "d" .. diceFaces)
local linkLabel = displayDice
-- Embed prefix in label when no type and no qualifiers
if prefixFlag and typ == "none" and #qualifiers == 0 then
linkLabel = (prefixCount or 1) .. string.upper(prefixFlag) .. displayDice
end
local link = "[https://dice.run/#/d/" .. diceCount .. "d" .. diceFaces .. " " .. linkLabel .. "]"
table.insert(content, link)
elseif prefixFlag and not diceCount and typ ~= "none" then
-- Pure prefix only for non-none type
table.insert(content, "{{" .. string.upper(prefixFlag) .. "}}")
end
-- Add qualifier labels
if qualifiers and #qualifiers > 0 then
for _, q in ipairs(qualifiers) do
table.insert(content, "{{L|" .. capitalize(typ) .. "|" .. capitalize(q) .. "}}")
end
end
end
end
-- Assemble quick-roll and content
local quickRoll = table.concat(quickParts, " ")
local inner = table.concat(content)
-- Build final span
local span = html.create('span')
:addClass('dice')
:attr('data-full-roll', fullRoll)
:attr('data-quick-roll', quickRoll)
:attr('data-type', typ)
:wikitext(inner)
return span:allDone()
end
return p