Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Dice: Difference between revisions

From Teriock
Content deleted Content added
// via Wikitext Extension for VSCode
// via Wikitext Extension for VSCode
 
Line 52: Line 52:
table.insert(qualifiers, q)
table.insert(qualifiers, q)
end
end
-- Sort qualifiers alphabetically
table.sort(qualifiers)
table.sort(qualifiers)
end
end
Line 59: Line 58:
local rawPref = s:match("^%(([^)]+)%)")
local rawPref = s:match("^%(([^)]+)%)")
if rawPref then
if rawPref then
-- Match "<number>*@<flag>" or "<number>@<flag>" or just "@<flag>"
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
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
-- Pure prefix without dice
local pure = s:match("^@(%a+)$")
local pure = s:match("^@(%a+)$")
if pure then
if pure then
Line 96: Line 91:
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
-- parse dice, prefix, or constant
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = parsePart(part)
local prefixCount, prefixFlag, diceCount, diceFaces, qualifiers = parsePart(part)
-- Build quick-roll sequence (ignore flags)
-- fallback for plain constants
if diceCount then
if not diceCount and not prefixFlag then
table.insert(quickParts, diceCount .. "d" .. diceFaces)
table.insert(quickParts, part)
table.insert(content, part)
end
else
-- Build quick-roll sequence (ignore flags)
if diceCount then
table.insert(quickParts, diceCount .. "d" .. diceFaces)
end


-- Prefix template for non-'none' types
-- Prefix template for typed rolls
if prefixFlag and typ ~= "none" and diceCount then
if prefixFlag and typ ~= "none" and diceCount then
local tpl = "{{" .. string.upper(prefixFlag)
local tpl = "{{" .. string.upper(prefixFlag)
if prefixCount and prefixCount ~= 1 then tpl = tpl .. "|" .. prefixCount end
if prefixCount and prefixCount ~= 1 then tpl = tpl .. "|" .. prefixCount end
tpl = tpl .. "}}"
tpl = tpl .. "}}"
table.insert(content, frame:preprocess(tpl))
table.insert(content, frame:preprocess(tpl))
end
end


-- Build dice link
-- Build dice link
if diceCount then
if diceCount then
local displayDice = diceCount .. "d" .. diceFaces
local displayDice = diceCount .. "d" .. diceFaces
local linkLabel = displayDice
local linkLabel = displayDice
if prefixFlag then
if prefixFlag then
if typ == "none" and #qualifiers == 0 then
if typ == "none" and #qualifiers == 0 then
-- Merge prefix into label when no type and no qualifiers
local prefixLabel = string.upper(prefixFlag)
local prefixLabel = string.upper(prefixFlag)
if prefixCount and prefixCount > 1 then prefixLabel = prefixCount .. prefixLabel end
if prefixCount and prefixCount > 1 then
linkLabel = prefixLabel .. "d" .. diceFaces
prefixLabel = prefixCount .. prefixLabel
else
linkLabel = "d" .. diceFaces
end
end
linkLabel = prefixLabel .. "d" .. diceFaces
else
-- For typed rolls, label should only show the face string
linkLabel = "d" .. diceFaces
end
end
table.insert(content,
string.format("[https://dice.run/#/d/%dd%s %s]", diceCount, diceFaces, linkLabel)
)
elseif prefixFlag then
-- pure prefix
table.insert(content, frame:preprocess("{{" .. string.upper(prefixFlag) .. "}}"))
end
end
table.insert(content, string.format("[https://dice.run/#/d/%dd%s %s]", diceCount, diceFaces, linkLabel))
elseif prefixFlag then
-- Pure prefix outputs template
table.insert(content, frame:preprocess("{{" .. string.upper(prefixFlag) .. "}}"))
end


-- Qualifier labels
-- Qualifier labels
for _, q in ipairs(qualifiers) do
for _, q in ipairs(qualifiers) do
table.insert(content, frame:preprocess(" {{L|" .. capitalize(typ) .. "|" .. capitalize(q) .. "}}"))
table.insert(content, frame:preprocess(" {{L|" .. capitalize(typ) .. "|" .. capitalize(q) .. "}}"))
end
end
end
end
end
end
end


-- Assemble quick-roll and content
-- 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)


-- Generate span
return html.create('span')
local span = html.create('span')
:addClass('dice')
:addClass('dice')
:attr('data-full-roll', fullRoll)
:attr('data-full-roll', fullRoll)
Line 154: Line 154:
:attr('data-type', typ)
:attr('data-type', typ)
:wikitext(inner)
:wikitext(inner)
:allDone()

return span:allDone()
end
end



Latest revision as of 01:02, 20 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, diceCount, diceFaces, qualifiers = nil, nil, nil, 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
  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, 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 = parsePart(part)
      -- fallback for plain constants
      if not diceCount and not prefixFlag 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)
        end

        -- Prefix template for typed rolls
        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
        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 prefixFlag then
          -- pure prefix
          table.insert(content, frame:preprocess("{{" .. string.upper(prefixFlag) .. "}}"))
        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)
    :allDone()
end

return p