Module:ST2

From Darkest Dungeon Wiki
Jump to navigation Jump to search
Template-info.svg Documentation

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

BleedToxicBlock+DiseaseVulnerableMove

MoveBleedToxicBlock+DiseaseVulnerable

VulnerableMoveBleedToxicBlock+Disease

DiseaseVulnerableMoveBleedToxicBlock+

Block+DiseaseVulnerableMoveBleedToxic

ToxicBlock+DiseaseVulnerableMoveBleed

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:

Output ST2 Code Regular Wikitext
+20% Burn RES Piercing {{Burn2|pierce|+20}} <span class="buff-dd2">+20%</span> [[File:Burn dd2.png|size=20px|alt=Burn|link=Burn (Darkest Dungeon II)]] RES Piercing
AffinityAffinityAffinity, AffinityAffinity {{Affinity|+3}}, {{Affinity|-2}} [[File:Positive yellow affinity pip.png|size=20px|alt=Affinity|link=Affinity]][[File:Positive yellow affinity pip.png|size=20px|alt=Affinity|link=Affinity]][[File:Positive yellow affinity pip.png|size=20px|alt=Affinity|link=Affinity]], [[File:Affinity negative blue.png|size=20px|alt=Affinity|link=Affinity]][[File:Affinity negative blue.png|size=20px|alt=Affinity|link=Affinity]]

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:

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 Bleed symbol to be formatted as if were Blight Blight instead, like this: Bleed4.

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
}