Module:ST2
This page describes the inner workings of ST2 and how to create new ST2 templates. For instructions on how to use the templates themselves, please see their corresponding pages, e.g. Template:Bleed2.
Overview
Darkest Dungeon II uses a lot of symbols, abbreviations, and other jargon to describe its in-game mechanics. ST2, short for Structured Text (Darkest Dungeon II), is a collection of modules and templates intended to render these descriptions in a clear, consistent, and well-linked way on the Wiki.
The primary benefit is to the readers—ST2 templates are designed to have a similar appearance to the in-game text, along with a Wikilink to the page where the effect or mechanic is described. Ideally a new reader should be able to scan a Wiki page and quickly look up any unknown symbols or terms that they encounter. A second benefit, to editors, is that the code for ST2 templates is fairly concise, using short descriptors to generate lots of in-game text. Consider the following examples:
Some other features include:
- ST2 templates generate their own documentation, making it easy for new editors to learn how to use them. See Template:Bleed2 for an example.
- ST2 templates annotate all symbols with alt-text.
- ST2 templates are all wrapped in
<span style="white-space:nowrap">
, preventing the icon from getting accidentally separated from its text.
Creating a New ST2 Template
It is easiest to start with an example. Template:Bleed2 is defined like this:
<noinclude> ==Usage== {{#invoke:ST2/Usage|generate|type=dot}} </noinclude><includeonly><!-- -->{{#invoke:ST2|render |type=dot |name=Bleed |image=Bleed dd2.png |link=Bleed (Darkest Dungeon II) |class=bleed-dd2 }}<!-- --></includeonly>
First and foremost, templates that use ST2 must provide a type, which determines the sorts of arguments that they accept, and their overall appearance. The following are a few examples of supported types:
icon
: The most general use case—an icon with an optional name and/or link, or some formatted text. E.g.Toxic (Template:Toxic) or
Self: When Damaged: +20% DMG (3 Turns) (Template:Buff2).
amount
: General-purpose type for dealing with amounts. E.g. -1(Template:Speed2) or +3
(Template:Bauble).
stat
: Provides some additional functionality for resistances. E.g. +20%RES (Template:Debuff2) or
Leprosy (Template:Disease2).
dot
: Used for damage over time. E.g. +2Dealt (Template:Bleed2).
token
: Adds some tools for dealing with tokens, e.g.x2 (Template:Dodge+) or Ignores
(Template:Blind).
The Wikitext output is generated by invoking ST2's render
function. As usual, this should not be called directly from pages themselves, but rather should only be used inside templates.
Each type handler uses additional arguments to determine what to render on the page. E.g. class=bleed-dd2
tells ST2 to render formatted text using the bleed-dd2
CSS class. These arguments may be overridden when calling the template itself. For example,
{{Bleed2|class=blight-dd2|4}}
will cause the text after the Bleed symbol to be formatted as if were
Blight instead, like this:
4.
Please consult the module code or template pages for a more comprehensive list of supported types and their arguments.
Lastly, template documentation is generated in the <noinclude>
section by invoking the generate
function from the Module:ST2/Usage sub-module.
Implementation
When render
is called, ST2 will attempt to match the arguments against a list of all valid argument patterns for that type. E.g., for the dot
type, it will first check if we are trying to render an icon, then a resistance, then a damage dealt bonus/penalty, then piercing, etc... Once a valid pattern is found, then it will produce the appropriate Wikitext.
This is also how Module:ST2/Usage generates template documentation—it stores a list of the same patterns with their usage notes, and generates specific examples using the current template.
Notes
- Currently there are a number of ST2 Templates that are redundant with other templates, e.g. Template:Bleed versus Template:Bleed2, or Template:Speed versus Template:Speed2. The intent is to eventually migrate all DDII-related templates to using the '2' version so that we can use the other templates for DDI.
- In the long term I would like to add hover tooltips (e.g. see the WarCraft wiki and PCJ's AJAX tooltip scripts) to enable readers to quickly learn what symbols mean without requiring them to visit a different page.
- I have started trying to port ST2 back to Module:ST for Darkest Dungeon. Over time I anticipate it will become redundant with a lot of Template:BuffEffect, but currently the work is very slow because I am very busy.
local getArgs = require("Module:Arguments").getArgs
--
-- Basic utilities for formatting text
--
local span_fmt = '<span class="%s">%s</span>'
local nowrap_fmt = span_fmt:format("nowrap", "%s")
local buff_fmt = span_fmt:format("buff-dd2", "%s")
local debuff_fmt = span_fmt:format("debuff-dd2", "%s")
local plus = string.byte("+")
local minus = string.byte("-")
local img_fmt = "[[File:%s|%s|link=%s|alt=%s]]"
local size_normal = "20px"
local size_large = "35px"
local link_fmt = "[[%s|%s]]"
local function span(txt, class)
return (class ~= nil) and span_fmt:format(class, txt) or txt
end
-- This is necessary to ensure that symbols and text are grouped together on
-- one line instead of spilling over.
local function nowrap(txt)
return nowrap_fmt:format(txt)
end
local function buff(txt)
return buff_fmt:format(txt)
end
local function debuff(txt)
return debuff_fmt:format(txt)
end
local function buff_debuff(txt, invert)
-- Lua doesn't have a native XOR function??!??
local x = txt:byte(1) == minus
local y = invert and true or false
return (x == y) and buff(txt) or debuff(txt)
end
local function img(args)
local size_fmt
if args.size == "large" then
size_fmt = size_large
elseif args.size ~= nil then
size_fmt = args.size
else
size_fmt = size_normal
end
return img_fmt:format(
args.image, size_fmt, args.link, args.alt or args.name)
end
local function link(args)
if #args.link > 0 then
return link_fmt:format(args.link, args.name)
else
return args.name
end
end
--
-- Define argument patterns
--
local function icon(args)
if args[1] == nil then
return img(args)
elseif args[1] == "-" then
return nowrap(img(args) .. " " .. link(args))
end
end
local function icon_fallback(args)
return nowrap(img(args) .. span(args[1], args.class))
end
local function res(args)
local effect = ""
if args[1] == "res" then
effect = "RES"
elseif args[1] == "pierce" then
effect = "RES Piercing"
else
return
end
local amt = ""
if args[2] ~= nil then
amt = buff_debuff(args[2] .. "%")
end
return nowrap(amt .. " " .. img(args) .. " " .. effect)
end
local function dot(args)
local effect, invert = "", false
if args[1] == "dealt" then
effect = "Dealt"
elseif args[1] == "duration" then
effect = "Duration Dealt"
elseif args[1] == "received" then
effect = "Received"
-- invert: lower is better
invert = true
else
return
end
local amt = ""
if args[2] ~= nil then
amt = buff_debuff(args[2], invert)
end
return nowrap(amt .. " " .. img(args) .. " " .. effect)
end
local function move(args)
if args[1] == "shuffle" then
return nowrap(img(args) ..
span("Shuffle", args.class))
end
local res = ""
if args[1] == "forward" then
res = span("Forward", args.class)
elseif args[1] == "back" then
res = span("Back", args.class)
elseif args[1] == "pull" then
res = span("Pull", args.class)
elseif args[1] == "knockback" then
res = span("Knockback", args.class)
end
if args[2] ~= nil then
res = res .. " " .. args[2]
end
return nowrap(img(args) .. res)
end
local function amount(args)
if args[1]:byte(1) == plus or args[1]:byte(1) == minus then
return nowrap(buff_debuff(args[1]) .. " " .. img(args))
end
end
local function heal(args)
if args[1] == "heal" then
return nowrap(span("Heal " .. img(args) .. " " .. args[2], "heal-dd2"))
end
end
local function fatigue(args)
if args[1] == "limit" then
return nowrap(buff(args[2] .. "%") .. " " .. img(args) .. " Fatigue Limit")
end
-- invert: lower is better
return nowrap(buff_debuff(args[1] .. "%", true) .. " " .. img(args) .. " Fatigue")
end
local function stress(args)
-- invert: lower is better
return nowrap(buff_debuff(args[1], true) .. " " .. img(args))
end
local function token(args)
if args[1] == "ignore" then
return nowrap("Ignores " .. img(args))
elseif args[1] == "remove" then
return nowrap("Remove " .. img(args))
else
return nowrap(img(args) .. ("x%s"):format(args[1]))
end
end
local function affinity(args)
local amt = args[1]
if amt:byte(1) == minus then
args.image = args.image2
amt = -tonumber(amt)
else
amt = tonumber(amt)
end
local res, pip = {}, img(args)
for _ = 1, amt do
table.insert(res, pip)
end
return table.concat(res, "")
end
local function execution(args)
return nowrap(img(args) .. span("Execution " .. args[1], args.class))
end
--
-- Match argument patterns to determine what variant we should render
--
local renderers_by_type = {
icon={icon, icon_fallback},
stat={icon, res, icon_fallback},
dot={icon, res, dot, icon_fallback},
move={icon, res, move},
amount={icon, amount, icon_fallback},
health={icon, amount, heal, icon_fallback},
fatigue={icon, heal, fatigue},
stress={icon, res, stress},
token={icon, token},
affinity={icon, affinity},
execution={icon, execution},
}
local function _render(args)
for _, f in ipairs(renderers_by_type[args.type]) do
local result = f(args)
if result ~= nil then
return result
end
end
end
local function render(frame)
return _render(getArgs(frame, {
parentFirst=true,
removeBlanks=false
}))
end
return {
_render=_render,
render=render
}