")+'"')
+;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e}
+if("illegal"===o.type&&""===a)return 1
+;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches")
+;return R+=a,a.length}const _=O(e)
+;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
+;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[]
+;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope)
+;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{
+if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
+I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A
+;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e)
+;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e,
+value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){
+if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n),
+illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A,
+context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{
+language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
+;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
+const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
+;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1)))
+;s.unshift(n);const o=s.sort(((e,t)=>{
+if(e.relevance!==t.relevance)return t.relevance-e.relevance
+;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
+;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r
+;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{
+let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
+;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
+;return t||(X(a.replace("{}",n[1])),
+X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
+return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
+;if(N("before:highlightElement",{el:e,language:n
+}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
+;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
+console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
+console.warn("The element with unescaped HTML:"),
+console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML)
+;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
+;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
+;e.classList.add("hljs"),e.classList.add("language-"+i)
+})(e,n,o.language),e.result={language:o.language,re:o.relevance,
+relevance:o.relevance},o.secondBest&&(e.secondBest={
+language:o.secondBest.language,relevance:o.secondBest.relevance
+}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){
+"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0
+}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
+function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
+s[e.toLowerCase()]=t}))}function k(e){const t=O(e)
+;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{
+e[n]&&e[n](t)}))}
+"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{
+y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_,
+highlightElement:w,
+highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"),
+G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)},
+initHighlighting:()=>{
+_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
+initHighlightingOnLoad:()=>{
+_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
+},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
+if(W("Language definition for '{}' could not be registered.".replace("{}",e)),
+!r)throw t;W(t),s=l}
+s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{
+languageName:e})},unregisterLanguage:e=>{delete i[e]
+;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
+listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v,
+autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{
+e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
+e["before:highlightBlock"](Object.assign({block:t.el},t))
+}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
+e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)},
+removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{
+r=!1},n.safeMode=()=>{r=!0},n.versionString="11.10.0",n.regex={concat:h,
+lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
+;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n
+},ne=te({});return ne.newInstance=()=>te({}),ne}()
+;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `c` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,t=e.COMMENT("//","$",{
+contains:[{begin:/\\\n/}]
+}),a="decltype\\(auto\\)",s="[a-zA-Z_]\\w*::",i="("+a+"|"+n.optional(s)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={
+className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{
+match:/\batomic_[a-z]{3,6}\b/}]},l={className:"string",variants:[{
+begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{
+begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
+end:"'",illegal:"."},e.END_SAME_AS_BEGIN({
+begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
+className:"number",variants:[{begin:"\\b(0b[01']+)"},{
+begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"
+},{
+begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"
+}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
+keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include"
+},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{
+className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={
+className:"title",begin:n.optional(s)+e.IDENT_RE,relevance:0
+},_=n.optional(s)+e.IDENT_RE+"\\s*\\(",u={
+keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","typeof","typeof_unqual","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],
+type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_BitInt","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal96","_Decimal128","_Decimal64x","_Decimal128x","_Float16","_Float32","_Float64","_Float128","_Float32x","_Float64x","_Float128x","const","static","constexpr","complex","bool","imaginary"],
+literal:"true false NULL",
+built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"
+},g=[c,r,t,e.C_BLOCK_COMMENT_MODE,o,l],m={variants:[{begin:/=/,end:/;/},{
+begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],
+keywords:u,contains:g.concat([{begin:/\(/,end:/\)/,keywords:u,
+contains:g.concat(["self"]),relevance:0}]),relevance:0},p={
+begin:"("+i+"[\\*&\\s]+)+"+_,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,
+keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{
+begin:_,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})],
+relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,
+keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,l,o,r,{begin:/\(/,
+end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,l,o,r]
+}]},r,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u,
+disableAutodetect:!0,illegal:"",contains:[].concat(m,p,g,[c,{
+begin:e.IDENT_RE+"::",keywords:u},{className:"class",
+beginKeywords:"enum class struct union",end:/[{;:<>=]/,contains:[{
+beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c,
+strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})();/*! `diff` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const a=e.regex;return{name:"Diff",
+aliases:["patch"],contains:[{className:"meta",relevance:10,
+match:a.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)
+},{className:"comment",variants:[{
+begin:a.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),
+end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{
+className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,
+end:/$/}]}}})();hljs.registerLanguage("diff",e)})();/*! `javascript` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict"
+;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s)
+;return o=>{const l=o.regex,b=e,d={begin:/<[A-Za-z0-9\\._:-]+/,
+end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{
+const a=e[0].length+e.index,t=e.input[a]
+;if("<"===t||","===t)return void n.ignoreMatch();let s
+;">"===t&&(((e,{after:n})=>{const a=""+e[0].slice(1)
+;return-1!==e.input.indexOf(a,n)})(e,{after:a})||n.ignoreMatch())
+;const r=e.input.substring(a)
+;((s=r.match(/^\s*=/))||(s=r.match(/^\s+extends\s+/))&&0===s.index)&&n.ignoreMatch()
+}},g={$pattern:e,keyword:n,literal:a,built_in:i,"variable.language":c
+},u="[0-9](_?[0-9])*",m=`\\.(${u})`,E="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",A={
+className:"number",variants:[{
+begin:`(\\b(${E})((${m})|\\.)?|(${m}))[eE][+-]?(${u})\\b`},{
+begin:`\\b(${E})\\b((${m})\\b|\\.)?|(${m})\\b`},{
+begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{
+begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{
+begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
+begin:"\\b0[0-7]+n?\\b"}],relevance:0},y={className:"subst",begin:"\\$\\{",
+end:"\\}",keywords:g,contains:[]},h={begin:".?html`",end:"",starts:{end:"`",
+returnEnd:!1,contains:[o.BACKSLASH_ESCAPE,y],subLanguage:"xml"}},N={
+begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,
+contains:[o.BACKSLASH_ESCAPE,y],subLanguage:"css"}},_={begin:".?gql`",end:"",
+starts:{end:"`",returnEnd:!1,contains:[o.BACKSLASH_ESCAPE,y],
+subLanguage:"graphql"}},f={className:"string",begin:"`",end:"`",
+contains:[o.BACKSLASH_ESCAPE,y]},p={className:"comment",
+variants:[o.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
+begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",
+begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,
+excludeBegin:!0,relevance:0},{className:"variable",begin:b+"(?=\\s*(-)|$)",
+endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]
+}),o.C_BLOCK_COMMENT_MODE,o.C_LINE_COMMENT_MODE]
+},v=[o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,{match:/\$\d+/},A]
+;y.contains=v.concat({begin:/\{/,end:/\}/,keywords:g,contains:["self"].concat(v)
+});const S=[].concat(p,y.contains),w=S.concat([{begin:/(\s*)\(/,end:/\)/,
+keywords:g,contains:["self"].concat(S)}]),R={className:"params",begin:/(\s*)\(/,
+end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w},O={variants:[{
+match:[/class/,/\s+/,b,/\s+/,/extends/,/\s+/,l.concat(b,"(",l.concat(/\./,b),")*")],
+scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{
+match:[/class/,/\s+/,b],scope:{1:"keyword",3:"title.class"}}]},k={relevance:0,
+match:l.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),
+className:"title.class",keywords:{_:[...t,...s]}},I={variants:[{
+match:[/function/,/\s+/,b,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],
+className:{1:"keyword",3:"title.function"},label:"func.def",contains:[R],
+illegal:/%/},x={
+match:l.concat(/\b/,(T=[...r,"super","import"].map((e=>e+"\\s*\\(")),
+l.concat("(?!",T.join("|"),")")),b,l.lookahead(/\s*\(/)),
+className:"title.function",relevance:0};var T;const C={
+begin:l.concat(/\./,l.lookahead(l.concat(b,/(?![0-9A-Za-z$_(])/))),end:b,
+excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={
+match:[/get|set/,/\s+/,b,/(?=\()/],className:{1:"keyword",3:"title.function"},
+contains:[{begin:/\(\)/},R]
+},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={
+match:[/const|var|let/,/\s+/,b,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)],
+keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]}
+;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{
+PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/,
+contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{
+label:"use_strict",className:"meta",relevance:10,
+begin:/^\s*['"]use (strict|asm)['"]/
+},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,N,_,f,p,{match:/\$\d+/},A,k,{
+className:"attr",begin:b+l.lookahead(":"),relevance:0},$,{
+begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
+keywords:"return throw case",relevance:0,contains:[p,o.REGEXP_MODE,{
+className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{
+className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{
+className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,
+excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0
+},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:">"},{
+match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:d.begin,
+"on:begin":d.isTrulyOpeningTag,end:d.end}],subLanguage:"xml",contains:[{
+begin:d.begin,end:d.end,skip:!0,contains:["self"]}]}]},I,{
+beginKeywords:"while if switch catch for"},{
+begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
+returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:b,
+className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+b,
+relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},
+contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,
+className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})()
+;hljs.registerLanguage("javascript",e)})();/*! `json` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],s={
+scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",aliases:["jsonc"],
+keywords:{literal:a},contains:[{className:"attr",
+begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/,
+className:"punctuation",relevance:0
+},e.QUOTE_STRING_MODE,s,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],
+illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `markdown` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/,
+end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/,
+relevance:0},{
+begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
+relevance:2},{
+begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),
+relevance:2},{begin:/\[.+?\]\([./?].*?\)/,relevance:1},{
+begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/
+},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,
+returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",
+excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",
+end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[],
+variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]
+},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{
+begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[]
+}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c)
+;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g)
+})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{
+className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{
+begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",
+contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",
+end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g,
+end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{
+begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{
+begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",
+contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{
+begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{
+className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{
+className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal",
+match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})()
+;hljs.registerLanguage("markdown",e)})();/*! `plaintext` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var t=(()=>{"use strict";return t=>({name:"Plain text",
+aliases:["text","txt"],disableAutodetect:!0})})()
+;hljs.registerLanguage("plaintext",t)})();/*! `shell` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var s=(()=>{"use strict";return s=>({name:"Shell Session",
+aliases:["console","shellsession"],contains:[{className:"meta.prompt",
+begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,
+subLanguage:"bash"}}]})})();hljs.registerLanguage("shell",s)})();/*! `sql` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict";return e=>{
+const r=e.regex,t=e.COMMENT("--","$"),n=["true","false","unknown"],a=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],i=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=i,c=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!i.includes(e))),l={
+begin:r.concat(/\b/,r.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}}
+;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{
+$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:r,when:t}={})=>{const n=t
+;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e))
+})(c,{when:e=>e.length<3}),literal:n,type:a,
+built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"]
+},contains:[{begin:r.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/,
+keyword:c.concat(s),literal:n,type:a}},{className:"type",
+begin:r.either("double precision","large object","with timezone","without timezone")
+},l,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string",
+variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,
+contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{
+className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,
+relevance:0}]}}})();hljs.registerLanguage("sql",e)})();/*! `typescript` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict"
+;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s)
+;function o(o){const l=o.regex,d=e,b={begin:/<[A-Za-z0-9\\._:-]+/,
+end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{
+const a=e[0].length+e.index,t=e.input[a]
+;if("<"===t||","===t)return void n.ignoreMatch();let s
+;">"===t&&(((e,{after:n})=>{const a=""+e[0].slice(1)
+;return-1!==e.input.indexOf(a,n)})(e,{after:a})||n.ignoreMatch())
+;const r=e.input.substring(a)
+;((s=r.match(/^\s*=/))||(s=r.match(/^\s+extends\s+/))&&0===s.index)&&n.ignoreMatch()
+}},g={$pattern:e,keyword:n,literal:a,built_in:i,"variable.language":c
+},u="[0-9](_?[0-9])*",m=`\\.(${u})`,E="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",A={
+className:"number",variants:[{
+begin:`(\\b(${E})((${m})|\\.)?|(${m}))[eE][+-]?(${u})\\b`},{
+begin:`\\b(${E})\\b((${m})\\b|\\.)?|(${m})\\b`},{
+begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{
+begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{
+begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
+begin:"\\b0[0-7]+n?\\b"}],relevance:0},y={className:"subst",begin:"\\$\\{",
+end:"\\}",keywords:g,contains:[]},p={begin:".?html`",end:"",starts:{end:"`",
+returnEnd:!1,contains:[o.BACKSLASH_ESCAPE,y],subLanguage:"xml"}},N={
+begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,
+contains:[o.BACKSLASH_ESCAPE,y],subLanguage:"css"}},f={begin:".?gql`",end:"",
+starts:{end:"`",returnEnd:!1,contains:[o.BACKSLASH_ESCAPE,y],
+subLanguage:"graphql"}},_={className:"string",begin:"`",end:"`",
+contains:[o.BACKSLASH_ESCAPE,y]},h={className:"comment",
+variants:[o.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
+begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",
+begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,
+excludeBegin:!0,relevance:0},{className:"variable",begin:d+"(?=\\s*(-)|$)",
+endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]
+}),o.C_BLOCK_COMMENT_MODE,o.C_LINE_COMMENT_MODE]
+},S=[o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,p,N,f,_,{match:/\$\d+/},A]
+;y.contains=S.concat({begin:/\{/,end:/\}/,keywords:g,contains:["self"].concat(S)
+});const v=[].concat(h,y.contains),w=v.concat([{begin:/(\s*)\(/,end:/\)/,
+keywords:g,contains:["self"].concat(v)}]),R={className:"params",begin:/(\s*)\(/,
+end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w},k={variants:[{
+match:[/class/,/\s+/,d,/\s+/,/extends/,/\s+/,l.concat(d,"(",l.concat(/\./,d),")*")],
+scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{
+match:[/class/,/\s+/,d],scope:{1:"keyword",3:"title.class"}}]},x={relevance:0,
+match:l.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),
+className:"title.class",keywords:{_:[...t,...s]}},O={variants:[{
+match:[/function/,/\s+/,d,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],
+className:{1:"keyword",3:"title.function"},label:"func.def",contains:[R],
+illegal:/%/},I={
+match:l.concat(/\b/,(C=[...r,"super","import"].map((e=>e+"\\s*\\(")),
+l.concat("(?!",C.join("|"),")")),d,l.lookahead(/\s*\(/)),
+className:"title.function",relevance:0};var C;const T={
+begin:l.concat(/\./,l.lookahead(l.concat(d,/(?![0-9A-Za-z$_(])/))),end:d,
+excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={
+match:[/get|set/,/\s+/,d,/(?=\()/],className:{1:"keyword",3:"title.function"},
+contains:[{begin:/\(\)/},R]
+},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={
+match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)],
+keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]}
+;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{
+PARAMS_CONTAINS:w,CLASS_REFERENCE:x},illegal:/#(?![$_A-z])/,
+contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{
+label:"use_strict",className:"meta",relevance:10,
+begin:/^\s*['"]use (strict|asm)['"]/
+},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,p,N,f,_,h,{match:/\$\d+/},A,x,{
+className:"attr",begin:d+l.lookahead(":"),relevance:0},$,{
+begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
+keywords:"return throw case",relevance:0,contains:[h,o.REGEXP_MODE,{
+className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{
+className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{
+className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,
+excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0
+},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:">"},{
+match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:b.begin,
+"on:begin":b.isTrulyOpeningTag,end:b.end}],subLanguage:"xml",contains:[{
+begin:b.begin,end:b.end,skip:!0,contains:["self"]}]}]},O,{
+beginKeywords:"while if switch catch for"},{
+begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
+returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:d,
+className:"title.function"})]},{match:/\.\.\./,relevance:0},T,{match:"\\$"+d,
+relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},
+contains:[R]},I,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,
+className:"variable.constant"},k,M,{match:/\$[(.]/}]}}return t=>{
+const s=o(t),r=e,l=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],d={
+begin:[/namespace/,/\s+/,t.IDENT_RE],beginScope:{1:"keyword",3:"title.class"}
+},b={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{
+keyword:"interface extends",built_in:l},contains:[s.exports.CLASS_REFERENCE]
+},g={$pattern:e,
+keyword:n.concat(["type","interface","public","private","protected","implements","declare","abstract","readonly","enum","override","satisfies"]),
+literal:a,built_in:i.concat(l),"variable.language":c},u={className:"meta",
+begin:"@"+r},m=(e,n,a)=>{const t=e.contains.findIndex((e=>e.label===n))
+;if(-1===t)throw Error("can not find mode to replace");e.contains.splice(t,1,a)}
+;Object.assign(s.keywords,g),s.exports.PARAMS_CONTAINS.push(u)
+;const E=s.contains.find((e=>"attr"===e.className))
+;return s.exports.PARAMS_CONTAINS.push([s.exports.CLASS_REFERENCE,E]),
+s.contains=s.contains.concat([u,d,b]),
+m(s,"shebang",t.SHEBANG()),m(s,"use_strict",{className:"meta",relevance:10,
+begin:/^\s*['"]use strict['"]/
+}),s.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(s,{
+name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),s}})()
+;hljs.registerLanguage("typescript",e)})();/*! `yaml` grammar compiled for Highlight.js 11.10.0 */
+(()=>{var e=(()=>{"use strict";return e=>{
+const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={
+className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/
+},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",
+variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{
+variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={
+end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/,
+end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",
+contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{
+begin:/\w[\w :()\./-]*:(?=[ \t]|$)/},{begin:/"\w[\w :()\./-]*":(?=[ \t]|$)/},{
+begin:/'\w[\w :()\./-]*':(?=[ \t]|$)/}]},{className:"meta",begin:"^---\\s*$",
+relevance:10},{className:"string",
+begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{
+begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,
+relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",
+begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a
+},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",
+begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",
+relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{
+className:"number",
+begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"
+},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b]
+;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0,
+aliases:["yml"],contains:b}}})();hljs.registerLanguage("yaml",e)})();
\ No newline at end of file
diff --git a/docs/javascripts/init.js b/docs/javascripts/init.js
new file mode 100644
index 000000000000..66ce4773c3d9
--- /dev/null
+++ b/docs/javascripts/init.js
@@ -0,0 +1 @@
+hljs.highlightAll();
diff --git a/docs/mapping/design.md b/docs/mapping/design.md
new file mode 100644
index 000000000000..0eb78cf21fc8
--- /dev/null
+++ b/docs/mapping/design.md
@@ -0,0 +1,197 @@
+# Mapping Design Guide
+
+The purpose of this guide is to instruct how to design maps for Paradise. While
+there are many resources on the technical aspects of mapping, this guide instead
+focuses on considering mapping from a thematic, functional, and balance
+perspective.
+
+## Design Guidelines
+
+Maps are one of the most visible ways of conveying the world and setting of the
+server to players. Maps should work to preserve that setting. Paradise Station
+takes place in a 26th century universe where multiple space-faring species work
+on stations owned by a pangalactic corporate conglomerate. New maps, ruins, and
+remaps should make sense within that world.
+
+1. ***Use the appropriate decorative elements and turf types.*** Department
+ flooring should use their associated colors: red for Security, brown for
+ Cargo, blue for Medbay, and so on. Stations should always use standard walls
+ and reinforced walls, and not e.g. plastitanium walls. Stations should always
+ use standard airlocks, and not e.g. syndicate hatches or Centcomm airlocks.
+
+2. ***Avoid excessive use of decals or floor tile variants.*** Using too many
+ decals or floor tile variants causes unnecessary visual noise. Only use
+ decals such as warning tape where it is sensible, e.g. around airlocks that
+ lead to space. Decal overuse contributes to maptick slowdown.
+
+2. ***Avoid "Big Room Disease".*** "Big Room Disease" refers to areas on a map
+ that are unnecessarily large, empty, and/or square. Rooms should be large
+ enough to handle crew foot traffic and facilitate their use, but no larger.
+ Furniture should be placed appropriately inside rooms, not just lined along
+ their walls. Large rooms should rarely be perfectly square or rectangular.
+
+3. ***Public areas should be interesting.*** "Interesting" is subjective, but
+ generally, areas such as public hallways should include space for crew
+ interaction; decorations such as posters, flags, and other decorative
+ structures; windows that look out onto space, and floor tiles and decals that
+ delineate the space.
+
+4. ***Use appropriate hall sizes.*** Primary hallways should be three tiles
+ wide. Arterial hallways off the primary halls should be two tiles wide.
+ Intradepartmental corridors should be one or two tiles wide. Exceptions to
+ this include the Brig, Medbay, and Science, whose main halls can be three
+ tiles wide due to the amount of foot traffic and number of sub departments
+ within their space.
+
+5. ***Properly signpost maintenance areas.*** "Signposting" refers to
+ environmental factors that make it clear what part of maintenance the player
+ is in. For example, while "med maints" on Cyberiad is the area around medbay,
+ it is also distinguishable from its abandoned cryo tube, medical dufflebag,
+ operating table, and so on. Note however that this signposting does not need
+ to be directly related to nearby departments: for example, mining maints on
+ Cyberiad has a small abandoned gambling room, a laundry room, and several
+ abandoned shower/bathroom areas. This distinguishes it from other maintenance
+ areas despite not directly referencing mining, as players will eventually
+ associate these distinct features with that area of maintenance.
+
+6. ***Ensure continuity of scale.*** The size of rooms should make sense
+ relative to one another. The chef’s freezer should not be larger than their
+ kitchen. The dorm’s bathroom should not be larger than the Captain’s office.
+ The scale of rooms should make sense for their expected occupancy and
+ purpose. For example, the Heads of Staff Meeting Room should be large enough
+ to seat all staff comfortably around a table, with extra space for navigating
+ foot traffic around the table.
+
+
+## Balance Guidelines
+
+Maps should be an unbiased playing field for players, whether ordinary crew,
+silicon, antagonists, or midrounds. Players should not be able to rely on a
+specific station layout to gain unique advantages over other players.
+
+1. ***Maintain consistent loot counts and opportunities.*** The amount of
+ maintenance loot drops should remain consistent, with a slight scaling factor
+ based on expected station population. There should be no "treasure troves" or
+ hoards of loot hidden that can only be found with specific map knowledge.
+ Department supplies should be consistent across maps. Do not place any
+ syndicate items/traitor tools on station; always use the provided maintenance
+ loot spawners to maintain proper statistical likelihood of rare loot spawns.
+
+2. ***Use appropriate reinforcement.*** Most of the station should be delineated
+ with ordinary walls and reinforced windows. Only secure areas should use
+ reinforced walls and grilled spawners, and electrified windows should only be
+ used in rare cases: department head offices, technical storage, brig,
+ xenobio, and AI satellite.
+
+3. ***AI cameras should not have full coverage.*** The AI should not be
+ permitted to see into every single room. This makes it challenging for
+ antagonists to accomplish their objectives _in situ_. It is not enough to
+ have cameras that antagonists can disable, since an AI will notice that the
+ camera is out, when it is not usually. Areas appropriate for lacking cameras
+ include Operating Rooms, the Therapist’s office, the Execution Chamber, and
+ Dormitory bathrooms and shower rooms. Similarly:
+
+4. ***AI cameras should never be placed in maints.*** This is prohibited
+ completely. It provides a disproportionately competitive advantage for sec
+ against antagonists. Currently there is one exception to this, and that is
+ the cameras immediately outside the solar array maintenance areas on
+ Cyberiad. These give AI only a vague hint of what is happening nearby, and
+ very limited visibility into events in maints.
+
+5. ***Weak points are expected.*** The station is not a battle fortress, and it
+ is not fun for antagonists to attempt to ingress/egress deliberately
+ impenetrable areas. For example, Permabrig areas will typically have one or
+ two tools just out of reach for prisoners to attempt escape. There is a
+ toolbox in the far end of the gulag island to give gulag prisoners a chance
+ to escape. The Head of Security’s Office is bordered by outer space on two
+ station maps. Attempting to break into and out of sensitive areas should be
+ challenging, but not impossible.
+
+6. ***Occasionally place vents and scrubbers under furniture.*** Having all
+ vents and scrubbers prominently visible hinders ventcrawling antagonists. It
+ is easy for crew to forget to weld vents they cannot see.
+
+7. ***Ensure security/antag balance for maintenance tunnels.*** This includes
+ but is not limited to: having a primary path that allows navigability from
+ all entrances; providing ways for security to flank antagonists and
+ coordinate ambushes at maintenance entrances; ensuring that the majority of
+ the primary corridors are 2 tiles wide to allow for serpentine movement and
+ avoiding projectiles; ensuring that dead ends are rare; and providing places
+ for antagonists to hide using hidden walls or similarly difficult to find
+ places.
+
+8. ***Allow for escape routes and antag break-in routes.*** Departments should
+ be connected to maintenance through a back or side door. This lets players
+ escape and allows antags to break in. If this is not possible, departments
+ should have extra entry and exit points.
+
+## Functional Guidelines
+
+Stations are malleable. Players can build, rebuild, decorate, upholster, and
+equip the station in many ways. Mappers should take this into account when
+designing areas and departments. This goes doubly so for ruins: players will
+always find a way to work around the restrictions and intended flow of your
+ruin. Attempting to enforce a "correct" way of interacting with a map without
+deviation is impossible.
+
+1. ***Rooms should have specific and clear functions.*** Public rooms should
+ have a clear purpose. Large maintenance areas should appear to have had a
+ clear purpose—an abandoned robotics department, for example, or a disused
+ monkey-fighting ring. The `/area/station` subtypes enumerate most of what
+ rooms are expected within a station and its departments. Even if a room is
+ largely meant for player expansion, it should use an appropriate type and
+ name, such as the Vacant Office.
+
+2. ***Do not create "perfect" departments.*** The stations are not ideal
+ workplaces, not state-of-the-art, and not diligently maintained by
+ Nanotrasen. There should always be a gap between the ideal station and how
+ the maps are designed. Departments should not come fully featured and
+ configured, and should require crew interaction to set up and use
+ effectively. Examples of this include Medbay preparing Operating Rooms, Cargo
+ arranging the office to make access to the autolathe more convenient, and
+ Engineering reconfiguring the supermatter’s pipenet. This scarcity is also
+ critical to crew interactions: the Kitchen should have to rely on botany to
+ make the full range of recipes, etc.
+
+3. ***Provide surfaces.*** All jobs require managing many different objects,
+ items, and pieces of equipment. There should be an adequate number of tables
+ and racks available for department members to place things down and drop
+ things off.
+
+4. ***Place emergency lockers at appropriate intervals.*** Emergency closets and
+ fire-safety closets should be accessible to crew at regular intervals in
+ primary hallways, or just off primary hallways in adjacent maintenance
+ tunnels.
+
+## Ruin-Specific Guidance
+
+1. ***Balance the risk/reward ratio of a ruin appropriately.*** If a player
+ decides that the risk of running a ruin is not worth the reward, they will
+ stop running it.
+
+2. ***Not all ruins should provide rare loot.*** "Low-reward" ruins should exist
+ to balance out random generation, so that every ruin in a round is not a loot
+ resource. These ruins can be purely decorative, provide a place for
+ role-play, or provide diegetic/environmental storytelling.
+
+3. ***Ruins should fit the setting, and have a well signposted purpose.*** If,
+ for example, your ruin is some kind of abandoned technical/research facility,
+ it should should have appropriately defined areas: an obvious entrance, a
+ working area for staff and crew, a testing lab, containment area for living
+ specimens, some way for staff to have food, restrooms, and living
+ quarters/showers if they are intended to stay on the facility for extended
+ periods of time.
+
+4. ***Avoid ‘magic’ power whenever possible.*** While infinitely regenerating
+ APCs and SMES units exist, they should be used sparingly. Ruins should be as
+ realistic as possible and afford players the ability to take advantage of
+ being powered or unpowered to navigate or exploit the ruin when possible.
+
+## Shuttle-Specific Guidance
+
+1. ***Shuttles should have clearly defined secure areas and bridges.*** Secure
+ areas are for security and prisoner seating. Bridges are for all Command and
+ dignitaries, and include the emergency shuttle console. Consideration should
+ be given for hijackers and accessibility to the emergency shuttle console, as
+ well as the ability of crew to storm the bridge if necessary to prevent a
+ hijack.
diff --git a/docs/mapping/images/atmos_pipes_aligned.png b/docs/mapping/images/atmos_pipes_aligned.png
new file mode 100644
index 000000000000..06001f5c82bc
Binary files /dev/null and b/docs/mapping/images/atmos_pipes_aligned.png differ
diff --git a/docs/mapping/images/atmos_pipes_unaligned.png b/docs/mapping/images/atmos_pipes_unaligned.png
new file mode 100644
index 000000000000..3e181b3c7594
Binary files /dev/null and b/docs/mapping/images/atmos_pipes_unaligned.png differ
diff --git a/docs/mapping/images/complex_pipes.png b/docs/mapping/images/complex_pipes.png
new file mode 100644
index 000000000000..8f43df990ff9
Binary files /dev/null and b/docs/mapping/images/complex_pipes.png differ
diff --git a/docs/mapping/quickstart.md b/docs/mapping/quickstart.md
new file mode 100644
index 000000000000..130b6f1e341d
--- /dev/null
+++ b/docs/mapping/quickstart.md
@@ -0,0 +1,82 @@
+# Mapping Quickstart
+
+The purpose of this guide is to provide basic instructions on how to start
+mapping for Paradise Station and the tools needed to do so.
+
+## Tooling
+
+Once you have set up your [development environment][env], you will need several
+other tools to edit maps and publish your changes for Pull Requests.
+
+### Mapmerge
+
+If you have a map change published as a PR, and someone else makes a change to
+that map which is merged before yours, it is likely that there will be a merge
+conflict. Because of the way map files are formatted, using git to resolve these
+merge conflicts directly will result in a broken map.
+
+To deal with this, a separate tool, *Mapmerge*, is integrated into git. Mapmerge
+has the ability to look at the changes between two maps, merge them together
+correctly, and provide markers on the map where it requires a contributor to
+make a manual change.
+
+To install Mapmerge, run `\tools\hooks\Install.bat`. Further usage of Mapmerge
+is documented in the [Guide to Mapmerge]().
+
+
+
+Unless you know how to use git effectively, install Mapmerge **before** having
+to deal with a map merge conflict.
+
+
+
+[env]: ../contributing/getting_started.md
+
+### StrongDMM
+
+[StrongDMM][] is the recommended tool for editing maps by a wide margin. It is
+fast, provides easy searching for both objects on maps and objects in the
+codebase, an intuitive varediting system, the ability to hide categories of
+objects on the map while editing, and more.
+
+When using StrongDMM, the following options must be enabled. They can be found
+under _File -> Preferences_:
+
+ - "Sanitize Variables" must be checked. This removes variables that are
+ declared on the map, but are the same as their initial value.. (For example:
+ A standard floor turf that has `dir = 2` declared on the map will have that
+ variable deleted as it is redundant.)
+ - "Save Format" must be set to "TGM".
+ - "Nudge Mode" must be set to "pixel_x/pixel_y".
+
+[StrongDMM]: https://github.com/SpaiR/StrongDMM/releases
+
+### UpdatePaths
+
+_UpdatePaths_ is a utility which make it easier for mappers to share simple
+large-scale changes across maps. It does this by allowing mappers to write
+scripts which describe those changes, which are then applied to maps. For
+example, when migrating pixel-pushed ATMs to their directional helpers, this
+script was written:
+
+```
+/obj/machinery/economy/atm{pixel_x=-32} : /obj/machinery/economy/atm/directional/west
+/obj/machinery/economy/atm{pixel_x=32} : /obj/machinery/economy/atm/directional/east
+/obj/machinery/economy/atm{pixel_y=-32} : /obj/machinery/economy/atm/directional/south
+/obj/machinery/economy/atm{pixel_y=32} : /obj/machinery/economy/atm/directional/north
+```
+
+This takes each object found on a map with the specified `pixel_x`/`y` varedits,
+and replaces them with the object on the right side of the line.
+
+More information on UpdatePaths and how to use it is available in the
+[UpdatePaths documentation][upd].
+
+[upd]: https://github.com/ParadiseSS13/Paradise/blob/master/tools/UpdatePaths/readme.md
+
+## Mapping Tutorial
+
+Until a more specific guide is written for Paradise Station, /tg/station's
+[Guide to Mapping](https://hackmd.io/@tgstation/SyVma0dS5#san7890s-A-Z-Guide-to-Mapping)
+written by san7890 is a recommended resource for use SDMM, test mapping changes,
+and reconcile map merge conflicts.
diff --git a/docs/mapping/requirements.md b/docs/mapping/requirements.md
new file mode 100644
index 000000000000..c0a01c5f406f
--- /dev/null
+++ b/docs/mapping/requirements.md
@@ -0,0 +1,196 @@
+# Mapping Requirements
+
+In order for mapping changes to comply with our existing codebase, conventions,
+and in-game systems, there are several guidelines that must be followed.
+
+## Technical Standards
+
+### Atmospherics and Cables
+
+1. Unless absolutely necessary, do not run atmospherics pipes or disposals pipes
+ under wall turfs. **NEVER** run cables under wall turfs.
+
+2. Every room should contain at least one air vent and scrubber. Use the
+ following "on" subtype of vents and scrubbers as opposed to varediting:
+ `/obj/machinery/atmospherics/unary/vent_scrubber/on` and
+ `/obj/machinery/atmospherics/unary/vent_pump/on`.
+
+3. Run air pipes together where possible. The first example below is to be
+ avoided, the second is optimal:
+
+ ![](./images/atmos_pipes_unaligned.png) ![](./images/atmos_pipes_aligned.png)
+
+ Pipe layouts should be logical and predictable, easy to understand at a
+ glance. Always avoid complex layouts like in this example:
+
+ ![](./images/complex_pipes.png)
+
+4. External areas, or areas where depressurisation is expected and normal,
+ should use airless turf variants to prevent additional atmospherics load.
+
+5. Tiny fans (`/obj/structure/fans/tiny`) can be used to block airflow into
+ problematic areas, but are not a substitute for proper door and firelock
+ combinations. They are useful under blast doors that lead to space when
+ opened.
+
+### Wall Mounts
+
+1. Every station area (`/area/station` subtypes) should contain only one APC and
+ air alarm.
+
+2. Critical infrastructure rooms (such as the engine, arrivals, and medbay
+ areas) should be given an APC with a larger power cell. Use the
+ `/obj/machinery/power/apc/important` and `/obj/machinery/power/apc/critical`
+ mapping helpers for this purpose.
+
+3. Every room should contain at least one fire alarm. Fire alarms should not be
+ placed next to expected heat sources.
+
+4. Every room should contain at least one station intercom. Intercoms should be
+ set to frequency `145.9`, and be speaker ON Microphone OFF. This is so radio
+ signals can reach people even without headsets on. Larger rooms will require
+ more than one at a time.
+
+5. Every room should have at least one security camera with the caveats listed
+ in the [Design Guide](design.md). Larger rooms may require more than one
+ security camera. All security cameras should have a descriptive name that
+ makes it easy to find on a camera console. A good example would be the
+ template \[Department name\] - \[Area\], so Brig - Cell 1, or Medbay -
+ Treatment Center. Consistency is key to good camera naming.
+
+6. Every room should have at least one light switch. When possible, light
+ switches should be placed in such a position that a player can activate them
+ while standing on the same tile as the room's airlock. Players should not
+ have to wander through a dark room to find the light switch.
+
+7. Head of Staff offices should contain a requests console, using the
+ `/obj/machinery/requests_console/directional` helpers. Console department
+ names and types should not be varedited.
+
+8. Use lights sparingly. They draw a significant amount of power.
+
+### Windows, Walls, and Floors
+
+1. Electrochromic windows (`/obj/structure/window/reinforced/polarized`) and
+ doors/windoors (using the `/obj/effect/mapping_helpers/airlock/polarized`
+ helper) are preferred over shutters as the method of restricting view to a
+ room through windows. Shutters are sill appropriate in industrial/hazardous
+ areas of the station (engine rooms, HoP line, science test chamber, etc.).
+ Electrochromic window/windoor/door sets require a unique ID var, and a
+ window tint button (`/obj/machinery/button/windowtint`) with a matching ID
+ var. The default `range` of the button is 7 tiles but can be amended with a
+ varedit.
+
+2. Windows to secure areas or external areas should be reinforced. Windows in
+ engine areas should be reinforced plasma glass. Windows in high security
+ areas, such as the brig, bridge, and head of staff offices, should be
+ electrified by placing a wire node under the window.
+
+3. Engine areas, or areas with a high probability of receiving explosions,
+ should use reinforced flooring if appropriate.
+
+### Airlocks, Windoors, and Firelocks
+
+1. Firelocks should be used at area boundaries over doors and windoors, but not
+ windows. Firelocks can also be used to break up hallways at reasonable
+ intervals. Double firelocks are not permitted. Maintenance access doors
+ should never have firelocks placed over them.
+
+2. Door and windoor access must be correctly set by the
+ `/obj/effect/mapping_helpers/airlock/access` and
+ `/obj/effect/mapping_helpers/airlock/windoor/access` [helpers][],
+ respectively. Pay attention to the `any` and `all` subtypes; the `any`
+ subtype allows someone with any of the accesses on the airlock to use it,
+ and the `all` subtypes requires the user to have all of the access on the
+ airlock to use it.
+
+ For example, on the Cerebron (Metastation), miners must walk through the
+ Cargo Bay to access the Mining Dock. They do not have Cargo Bay access,
+ rather the Cargo Bay airlocks have two access helpers on them:
+
+ - `/obj/effect/mapping_helpers/airlock/access/any/supply/cargo_bay`
+ - `/obj/effect/mapping_helpers/airlock/access/any/supply/mining`
+
+ This allows both cargo technicians and miners to use those airlocks.
+
+ Old doors that use var edited access should be updated to use the correct
+ access helper, and the var edit on the door should be cleaned.
+
+### Other
+
+1. Edits in mapping tools should almost always be possible to replicate
+ in-game. For this reason, avoid stacking multiple structures on the same
+ tile (e.g. placing a light and an APC on the same wall).
+
+2. When adding new shuttles, or remapping departures areas, contributors must
+ ensure that all existing and new shuttles continue to fit and dock to the
+ correct airlocks as expected. Any time docking ports are edited, the author
+ needs to confirm the `width`, `height`, and `dwidth` variables between the
+ two permanent ports and mobile port are compatible.
+
+[helpers]: https://github.com/ParadiseSS13/Paradise/blob/master/code/modules/mapping/access_helpers.dm
+
+### Varedits
+
+*Varediting*, or variable editing, is the term for modifying a variable of an
+object on the map, instead of in code. There are many legitimate reasons to do
+so. For example, since nearly all floor tiles on the station are the same
+object, their `icon_state` and `dir` variables need to be edited to modify their
+appearance.
+
+However, there are also cases when varediting is not appropriate. In general,
+when modifying the behavior of an object, creating a subtype in code is almost
+always the better option. For example, let's say you have an `/obj/helmet` with
+a variable, `strength`, which defines how much damage it can take. The default
+is 10. You want to create a stronger helmet, so you add one into a map, and
+varedit its `strength` to be 20. This may work for now, but what if the strength
+of a helmet no longer is based off that variable? Your helmet will no longer
+work as expected. If you instead made an `/obj/helmet/strong`, and made the
+variable change there, then if the implementation for `/obj/helmet` changes,
+your object will benefit from those changes.
+
+Another example of inappropriate varediting is doing it to an object with many
+instances on a map, or multiple instances across maps. If you need to change the
+variable, you will then have to find every instance of it across all of the
+maps, and change them all.
+
+Areas should **never** be varedited on a map. All areas of a single type,
+altered instance or not, are considered the same area within the code, and
+editing their variables on a map can lead to issues with powernets and event
+subsystems which are difficult to debug.
+
+Subtypes only intended to be used on ruins should be contained within an .dm
+file with a name corresponding to that map within `code\modules\ruins`. This is
+so in the event that the map is removed, that subtype will be removed at the
+same time as well to minimize leftover/unused data within the repo.
+
+When not using StrongDMM (which handles the following automatically) please
+attempt to clean out any dirty variables that may be contained within items you
+alter through varediting. For example changing the `pixel_x` variable from 23 to
+0 will leave a dirty record in the map's code of `pixel_x = 0`.
+
+Unless they require custom placement, when placing the following items use the
+relevant directional mapper, as it has predefined pixel offsets and directions
+that are standardised: APC, Air alarm, Fire alarm, station intercom, newscaster,
+extinguisher cabinet, light switches.
+
+## Mapper Contribution Guidelines
+
+These guidelines apply to **all** mapping contributors.
+
+For mapping PRs, we do not accept 'change for the sake of change' remaps, unless
+you have very good reasoning to do so. Maintainers reserve the right to close
+your PR if we disagree with your reasoning. Large remaps, such as those to a
+department, must be justified with clear, specific reasons.
+
+Before committing a map change, you **MUST** run Mapmerge to normalise your
+changes. You can do this manually before every commit with
+`tools\mapmerge2\Run Before Committing.bat` or by letting the
+[git hooks](./quickstart.md#mapmerge) do it for you. Failure to run Mapmerge on
+a map after editing greatly increases the risk of the map's key dictionary
+becoming corrupted by future edits after running map merge. Resolving the
+corruption issue involves rebuilding the map's key dictionary.
+
+If you are making non-minor edits to an area or room, (non-minor being anything
+more than moving a few objects or fixing small bugs) then you should ensure the
+entire area/room is updated to meet these standards.
diff --git a/.github/AUTODOC_GUIDE.md b/docs/references/autodoc.md
similarity index 53%
rename from .github/AUTODOC_GUIDE.md
rename to docs/references/autodoc.md
index 80c366fecf8f..bb1278becf86 100644
--- a/.github/AUTODOC_GUIDE.md
+++ b/docs/references/autodoc.md
@@ -1,47 +1,51 @@
# dmdoc
-[DOCUMENTATION]: https://codedocs.paradisestation.org/
-[BYOND]: https://secure.byond.com/
-
-[DMDOC]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dmdoc
-
-[DMDOC] is a documentation generator for DreamMaker, the scripting language
-of the [BYOND] game engine. It produces simple static HTML files based on
+[dmdoc] is a documentation generator for DreamMaker, the scripting language of
+the [BYOND] game engine. It produces simple static HTML files based on
documented files, macros, types, procs, and vars.
-We use **dmdoc** to generate [DOCUMENTATION] for our code, and that documentation
-is automatically generated and built on every new commit to the master branch
+We use **dmdoc** to generate [documentation] for our code, and that
+documentation is automatically generated and built on every new commit to the
+master branch
+
+This gives new developers a clickable reference [documentation] they can browse
+to better help gain understanding of the Paradise codebase structure and api
+reference.
-This gives new developers a clickable reference [DOCUMENTATION] they can browse to better help
-gain understanding of the Paradise codebase structure and api reference.
+[documentation]: https://codedocs.paradisestation.org/
+[BYOND]: https://secure.byond.com/
+[dmdoc]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dmdoc
-## Documenting code on Paradise
-We use block comments to document procs and classes, and we use `///` line comments
-when documenting individual variables.
+## Documenting Code On Paradise
+We use block comments to document procs and classes, and we use `///` line
+comments when documenting individual variables.
-Documentation is not required at Paradise, but it is highly recommended that all new code be covered with DMdoc code, according to the [Specifications](#Specification)
+Documentation is not required at Paradise, but it is highly recommended that all
+new code be covered with DMdoc code, according to the
+[Specifications](#specification).
We also recommend that when you touch older code, you document the functions that you
have touched in the process of updating that code
### Specification
-A class *should* always be autodocumented, and all public functions *should* be documented
+A class *should* always be auto-documented, and all public functions *should* be
+documented.
-All class level defined variables *should* be documented
+All class level defined variables *should* be documented.
-Internal functions *can* be documented, but may not be
+Internal functions *can* be documented, but may not be.
A public function is any function that a developer might reasonably call while using
or interacting with your object. Internal functions are helper functions that your
-public functions rely on to implement logic
-
+public functions rely on to implement logic.
### Documenting a proc
When documenting a proc, we give a short one line description (as this is shown
next to the proc definition in the list of all procs for a type or global
namespace), then a longer paragraph which will be shown when the user clicks on
the proc to jump to it's definition
-```
+
+```dm
/**
* Short description of the proc
*
@@ -53,14 +57,13 @@ the proc to jump to it's definition
*/
```
-### Documenting a class
-We first give the name of the class as a header, this can be omitted if the name is
-just going to be the typepath of the class, as dmdoc uses that by default
+### Documenting Classes
+We first give the name of the class as a header, this can be omitted if the name
+is just going to be the typepath of the class, as dmdoc uses that by default.
+Then we give a short one-line description of the class. Finally we give a longer
+multi paragraph description of the class and it's details.
-Then we give a short oneline description of the class
-
-Finally we give a longer multi paragraph description of the class and it's details
-```
+```dm
/**
* # Classname (Can be omitted if it's just going to be the typepath)
*
@@ -75,7 +78,8 @@ Finally we give a longer multi paragraph description of the class and it's detai
### Documenting a variable/define
Give a short explanation of what the variable, in the context of the class, or define is.
-```
+
+```dm
/// Type path of item to go in suit slot
var/suit = null
```
@@ -89,20 +93,20 @@ that will also be rendered and added to the modules tree. The structure for
these is deliberately not defined, so you can be as freeform and as wheeling as
you would like.
-[Here is a representative example of what you might write](https://codedocs.paradisestation.org/code/modules/keybindings/readme.html)
-
## Special variables
-You can use certain special template variables in DM DOC comments and they will be expanded
-```
- [DEFINE_NAME] - Expands to a link to the define definition if documented
- [/mob] - Expands to a link to the docs for the /mob class
- [/mob/proc/Dizzy] - Expands to a link that will take you to the /mob class and anchor you to the dizzy proc docs
- [/mob/var/stat] - Expands to a link that will take you to the /mob class and anchor you to the stat var docs
-```
+You can use certain special template variables in DM DOC comments and they will
+be expanded.
+
+- `[DEFINE_NAME]` expands to a link to the define definition if documented.
+- `[/mob]` expands to a link to the docs for the /mob class.
+- `[/mob/proc/Dizzy]` expands to a link that will take you to the /mob class and
+ anchor you to the dizzy proc docs.
+- `[/mob/var/stat]` expands to a link that will take you to the /mob class and
+ anchor you to the stat var docs
-You can customise the link name by using `[link name][link shorthand].`
+You can customise the link name by using `[link name][link shorthand]`.
-eg. `[see more about dizzy here] [/mob/proc/Dizzy]`
+e.g. `[see more about dizzy here][/mob/proc/Dizzy]`
This is very useful to quickly link to other parts of the autodoc code to expand
-upon a comment made, or reasoning about code
+upon a comment made, or reasoning about code.
diff --git a/.github/USING_FEEDBACK_DATA.md b/docs/references/feedback_data.md
similarity index 54%
rename from .github/USING_FEEDBACK_DATA.md
rename to docs/references/feedback_data.md
index 889c14e5ff9f..4c94ce915371 100644
--- a/.github/USING_FEEDBACK_DATA.md
+++ b/docs/references/feedback_data.md
@@ -2,13 +2,18 @@
## Introduction
-`Feedback` is the name of the data storage system used for logging game statistics to the database. It is managed by `SSblackbox` and can be recorded in many formats. This guide will contain information on how to record feedback data properly, as well as what should and should not be recorded.
+`Feedback` is the name of the data storage system used for logging game
+statistics to the database. It is managed by `SSblackbox` and can be recorded in
+many formats. This guide will contain information on how to record feedback data
+properly, as well as what should and should not be recorded.
## Things you should and should not record
-Feedback data can be useful, depending on how you use it. You need to be careful with what you record to make sure you are not saving useless data. Examples of good things to record:
+Feedback data can be useful, depending on how you use it. You need to be careful
+with what you record to make sure you are not saving useless data. Examples of
+good things to record:
-- Antagonist win/loss rates if a new gamemode or antagonist is being added
+- Antagonist win/loss rates if a new game mode or antagonist is being added
- Department performance (IE: Slime cores produced in science)
- Basically anything which has actual meaning
@@ -16,17 +21,21 @@ Examples of bad things to record:
- Amount of times a wrench is used (Why)
- Hours spent on the server (We have other means of that)
-- Basically, just think about it and ask yourself "Is this actually useful to base game design around"
+- Basically, just think about it and ask yourself "Is this actually useful to
+ base game design around"
-Also note that feedback data **must** be anonymous. The only exception here is for data *anyone* on the server can see, such as round end antagonist reports.
+Also note that feedback data **must** be anonymous. The only exception here is
+for data *anyone* on the server can see, such as round end antagonist reports.
## Feedback Data Recording
-Feedback data can be reocrded in 5 formats. `amount`, `associative`, `nested tally`, `tally` and `text`.
+Feedback data can be recorded in 5 formats. `amount`, `associative`, `nested
+tally`, `tally` and `text`.
### Amount
-`amount` is the simplest form of feedback data recording. They are simply a numerical number which increase with each feedback increment. For example:
+`amount` is the simplest form of feedback data recording. They are simply a
+numerical number which increase with each feedback increment. For example:
These DM calls:
@@ -40,15 +49,23 @@ Will produce the following JSON:
```json
{
- "data":10
+ "data": 10
}
```
-Notice the lack of any specific identification other than it being the value of the `data` key. Amounts are designed to be simple with minimal complexity, and are useful for logging statistics such as how many times one specific, distinct action is done (e.g.: How many MMIs have been filled). If you want to log multiple similar things (e.g.: How many mechas have been created, broken down by the mecha type), use a `tally` with a sub-key for each different mecha, instead of an amount with its own key per mecha.
+Notice the lack of any specific identification other than it being the value of
+the `data` key. Amounts are designed to be simple with minimal complexity, and
+are useful for logging statistics such as how many times one specific, distinct
+action is done (e.g.: How many MMIs have been filled). If you want to log
+multiple similar things (e.g.: How many mechas have been created, broken down by
+the mecha type), use a `tally` with a sub-key for each different mecha, instead
+of an amount with its own key per mecha.
### Associative
-`associative` is used to record text that's associated with multiple key-value pairs. (e.g: coordinates). Further calls to the same key will append a new list to existing data. For example:
+`associative` is used to record text that's associated with multiple key-value
+pairs. (e.g: coordinates). Further calls to the same key will append a new list
+to existing data. For example:
```dm
SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4))
@@ -59,28 +76,39 @@ Will produce the following JSON:
```json
{
- "data":{
- "1":{
- "text":"example",
- "path":"/obj/item",
- "number":"4"
+ "data": {
+ "1": {
+ "text": "example",
+ "path": "/obj/item",
+ "number": "4"
},
- "2":{
- "number":"7",
- "text":"example",
- "other text":"sample"
+ "2": {
+ "number": "7",
+ "text": "example",
+ "other text": "sample"
}
}
}
```
-Notice how everything is cast to strings, and each new entry added has its index increased ("1", "2", etc). Also take note how the `increment` parameter is not used here. It does nothing to the data, and `1` is used just as the value for consistency.
+Notice how everything is cast to strings, and each new entry added has its index
+increased ("1", "2", etc). Also take note how the `increment` parameter is not
+used here. It does nothing to the data, and `1` is used just as the value for
+consistency.
### Nested Tally
-`nested tally` is used to track the number of occurances of structured semi-relational values (e.g.: the results of arcade machines). You can think of it as a running total, with the key being a list of strings (rather than a single string), with elements incrementally identifying the entity in question.
+`nested tally` is used to track the number of occurrences of structured
+semi-relational values (e.g.: the results of arcade machines). You can think of
+it as a running total, with the key being a list of strings (rather than a
+single string), with elements incrementally identifying the entity in question.
-Technically, the values are nested in a multi-dimensional array. The final element in the data list is used as the tracking key, and all prior elements are used for nesting. Further calls to the same key will add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position, and append the key and it's value if it doesn't exist already. This one is quite complicated, but an example is below:
+Technically, the values are nested in a multi-dimensional array. The final
+element in the data list is used as the tracking key, and all prior elements are
+used for nesting. Further calls to the same key will add or subtract from the
+saved value of the data key if it already exists in the same multi-dimensional
+position, and append the key and it's value if it doesn't exist already. This
+one is quite complicated, but an example is below:
```dm
SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
@@ -115,7 +143,8 @@ Will produce the following JSON:
#### NOTE
-Tracking values associated with a number can't merge with a nesting value, trying to do so will append the list
+Tracking values associated with a number can't merge with a nesting value,
+trying to do so will append the list
```dm
SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange"))
@@ -145,11 +174,15 @@ Will produce the following JSON:
}
```
-Avoid doing this, since having duplicate keys in JSON (data.fruit.orange) will break when parsing.
+Avoid doing this, since having duplicate keys in JSON (data.fruit.orange) will
+break when parsing.
### Tally
-`tally` is used to track the number of occurances of multiple related values (e.g.: how many times each type of gun is fired). Further calls to the same key will add or subtract from the saved value of the data key if it already exists, and append the key and it's value if it doesn't exist.
+`tally` is used to track the number of occurrences of multiple related values
+(e.g.: how many times each type of gun is fired). Further calls to the same key
+will add or subtract from the saved value of the data key if it already exists,
+and append the key and it's value if it doesn't exist.
```dm
SSblackbox.record_feedback("tally", "example", 1, "sample data")
@@ -170,7 +203,10 @@ Will produce the following JSON:
### Text
-`text` is used for simple single-string records (e.g.: the current chaplain religion). Further calls to the same key will append saved data unless the overwrite argument is true or it already exists. When encoded, calls made with overwrite will lack square brackets.
+`text` is used for simple single-string records (e.g.: the current chaplain
+religion). Further calls to the same key will append saved data unless the
+overwrite argument is true or it already exists. When encoded, calls made with
+overwrite will lack square brackets.
```dm
SSblackbox.record_feedback("text", "example", 1, "sample text")
@@ -189,11 +225,16 @@ Will produce the following JSON:
}
```
-Note how `"sample text"` only appears once. `text` is a set with no duplicates, instead of a list with duplicates. Also take note how the `increment` parameter is not used here. It does nothing to the data, and `1` is used just as the value for consistency.
+Note how `"sample text"` only appears once. `text` is a set with no duplicates,
+instead of a list with duplicates. Also take note how the `increment` parameter
+is not used here. It does nothing to the data, and `1` is used just as the value
+for consistency.
## Feedback Versioning
-If the logging content (i.e.: What data is logged) for a variable is ever changed, the version needs bumping. This can be done with the `versions` list on the subsystem definition itself. All values default to `1`.
+If the logging content (i.e.: What data is logged) for a variable is ever
+changed, the version needs bumping. This can be done with the `versions` list on
+the subsystem definition itself. All values default to `1`.
```dm
var/list/versions = list(
@@ -202,4 +243,5 @@ var/list/versions = list(
"gun_fired" = 2)
```
-If you are doing a type change (i.e.: Changing from a `tally` to a `nested tally`), **USE AN ENTIRELY NEW KEY NAME**.
+If you are doing a type change (i.e.: Changing from a `tally` to a `nested
+tally`), **USE AN ENTIRELY NEW KEY NAME**.
diff --git a/docs/references/glossary.md b/docs/references/glossary.md
new file mode 100644
index 000000000000..ab9f8bf59713
--- /dev/null
+++ b/docs/references/glossary.md
@@ -0,0 +1,263 @@
+Below is a glossary of common used terms along with their meanings relating to
+the coding side of SS13. If you notice any missing terms of discrepancies in
+descriptions, feel free to contribute to the list! Feel free to ask any
+questions in `#coding-chat` on the discord. Non-coding related Glossary can be
+found on the [wiki][]. More in depth information can be found at BYOND's
+official [documentation][].
+
+[wiki]: https://paradisestation.org/wiki/index.php?title=Glossary
+[documentation]: https://secure.byond.com/docs/ref/#/DM/
+
+## .DM
+DreamMaker code files, or .dm files are the file format for BYOND source code.
+These files must be "ticked" in the .dme file for them to be included in the
+game.
+
+## .DMB
+"DreamMaker Build" or DMB files are compiled DME files and are
+used with Dream Daemon to run the server.
+
+## .DME
+"DreamMaker Environment" or DME files are what BYOND uses to compile the game.
+It is a list of all .dm files used in the game, if you add a new file you will
+need to "Tick" it or add it to this file manually.
+
+## .DMI
+DreamMaker images or DMI files is how BYOND stores images (also known as icons),
+these can be edited with BYOND's tools or external tools.
+
+## .DMM
+DreamMaker maps or DMM files is how BYOND stores maps. These can be edited with
+BYOND's tools or something like [StrongDMM](#strongdmm)
+
+## Area
+From the [BYOND documentation](https://secure.byond.com/docs/ref/#/area/):
+
+> "Areas are derived from /area. Regions on the map may be assigned to an area
+by painting it onto the map. Areas off the map serve as rooms that objects may
+enter and exit. For each area type defined, one area object is created at
+runtime. So for areas on the map, all squares with the same area type belong to
+the same instance of the area."
+
+In SS13, this is often used to delineate station departments and station systems
+such as power and atmospherics networks.
+
+## Atmos
+The atmospherics system in SS13, which is very often old and confusing and/or
+broken code.
+
+## Atom
+From the [BYOND documentation](https://secure.byond.com/docs/ref/#/atom/):
+
+> "The /atom object type is the ancestor of all mappable
+objects in the game. The types /area, /turf, /obj, and /mob are all
+derived from /atom. You should not create instances of /atom directly
+but should use /area, /turf, /obj, and /mob for actual objects. The
+/atom object type exists for the purpose of defining variables or
+procedures that are shared by all of the other 'physical' objects.
+These are also the only objects for which verbs may be accessible to the
+user. /atom is derived from /datum, so it inherits the basic properties that
+are shared by all DM objects."
+
+## Baseturf
+An SS13 variable that saves the data of what is underneath if that that is
+removed. For example, under station floors there would be a space turf and under
+Lavaland turfs there would be lava.
+
+## Buff
+A buff is a change to a gameplay mechanic that makes it more powerful or more
+useful. Generally the opposite of a [nerf](#nerf).
+
+## Commit
+A record of files changed and how they were changed, they are each assigned a
+special ID called a hash that specifies the changes it makes.
+
+## Config
+The config.toml file for changing things about your local server. You will need
+to copy this from the config/example folder if you haven't already.
+
+## Datum
+From the [BYOND documentation](https://secure.byond.com/docs/ref/#/datum/):
+
+> "The datum object is the ancestor of all other data types in DM. (The only
+exceptions are currently `/world`, `/client`, `/list`, and `/savefile`, but
+those will be brought into conformance soon.) That means that the variables and
+procedures of /datum are inherited by all other types of objects."
+
+Datums are useful to represent abstractions that don't physically exist in the
+game world, such as information about a spell or a Syndicate uplink item. They
+are also useful for vars or procs that all other data-types use.
+
+## Define
+A way of declaring variable either global (across the whole game) or in a whole
+file using DM's `#DEFINE` macro syntax. They should always be found at the
+beginning of a file. Defines should always be capitalized (LIKE_THIS) and if not
+global should undefined at the end of a file.
+
+## Fastmos
+Fast atmos, usually featuring explosive decomposition and lots of in game death.
+Fastmos is not used on Paradise.
+
+## Garbage
+The garbage collector handles items being deleted and allows them to clean up
+references, this allows objects to delete much more efficiently.
+
+## Head Admin
+Head of the admin team and overseeing overall changes and the direction for the
+entire Paradise codebase and server. Contact them or the Balance team about
+large changes or balance changes before making a PR, including map additions,
+new roles, new antagonists, and other similar things.
+
+## Icondiffbot
+A tool on GitHub that renders before and after images of BYOND icons. It can be
+viewed on any PR by scrolling down to the checks section and clicking details
+next to it.
+
+## LGTM
+"Looks Good To Me", used during code reviews.
+
+## Local
+Your copy of your remote repository on your local machine or computer. Commits
+need to be published to your remote repo before they can be pushed to the
+upstream repo for a pull request.
+
+## Maintainer
+A no longer used title, previously used for people who made sure code is
+quality. Maintainers were split up into the Balance Team, Design Team, and several
+other groups. Check [PR #18000](https://github.com/ParadiseSS13/Paradise/pull/18000/) for more
+information.
+
+## Map merge
+Tools that automatically attempt to merge maps when merging master or
+committing. Map merge is a work in progress and may require manual editing too.
+
+## Mapdiffbot
+A tool on GitHub that renders before and after images of BYOND maps. It can be
+viewed on any PR by scrolling down to the checks section and clicking details
+next to it.
+
+## Master Controller
+The Master Controller controls all subsystems of
+the game, such as the [garbage collector](#garbage).
+
+## MC
+Short for Short for [Master Controller](#master-controller).
+
+## Merge Master
+The process of merging master into your PR's branch, often to update it.
+
+## Mob
+Mobs are "mobile objects", these include players and animals. This does not
+include stuff like conveyors.
+
+## Nerf
+Nerfs are changes to a gameplay mechanic that make it less powerful or decreases
+its utility, typically done for the sake of improving game balance and
+enjoyability. Generally the opposite of a [buff](#buff).
+
+## NPFC
+"No Player-Facing Changes", used in the changelog of a PR, most often in
+refractors and exploit fixes.
+
+## Object
+Objects are things you can interact with in game, including things that do not
+move. This includes weapons, items, machinery (consoles and machines), and
+several other things.
+
+## Origin
+Typically another name for your [remote repo](#remote).
+
+## PR
+Short for [Pull Request](#pull-request).
+
+## Proc
+Procs or Procedures are block of code that only runs when it is called. These
+are similar to something like functions in other languages.
+
+## Publish
+Uploading your code from your local machine.
+
+## Pull Request
+A request to the Paradise Github Repository for certain
+changes to be made to the code of the game. This includes maps and sprites.
+
+## Pulling code
+Pulling is transferring commits from the main repo to your remote repo, or from
+your remote repository to your local repository.
+
+## Pushing code
+Pushing is how you transfer commits from your repository to the Upstream repo.
+
+## qdel
+A function, `qdel()`, which tells the [garbage collector](#garbage) to handle
+destruction of an object. This should always be used over `del()`.
+
+## QDEL_NULL
+A [qdel](#qdel) function which first nulls out a variable before telling the
+garbage collector to handle it.
+
+## Remote
+Your forked copy of the upstream repo that you have complete access over. your
+clean copy of master and any published branches you've made can be found here.
+
+## Repo
+Short for [repository](#repository).
+
+## Repository
+A collection of code which tracks the commits and changes to it. There are three
+main types you will find the upstream repository, your remote repository, and
+your local repository.
+
+## Runechat Chat
+Chat messages which appear above player's characters, a feature added by
+[#14141](https://github.com/ParadiseSS13/Paradise/pull/14141/). Often a joke
+about players now missing important things in the chat window since they no
+longer have to look there for reading messages from people.
+
+## Runtime
+Runtimes most often refer to runtime errors, which are errors that happen after
+compiling and happen in game.
+
+## StrongDMM
+A [robust mapping tool](https://github.com/SpaiR/StrongDMM/) that is highly
+recommended over BYOND's DMM editor, as it is much quicker and has much more
+options. Using any version below 2.0 makes your PR very unlikely to be accepted
+as it messes with variables.
+
+## TGUI
+A JavaScript based format for displaying an interface. It is
+used for our user interfaces (except OOC stuff like admin panels), or
+are planned to be converted to TGUI. TGUI uses InfernoJS (based off of
+reactJS) which is an extension to JavaScript. More information can be
+found at the [TGUI Tutorial][].
+
+[TGUI Tutorial]: https://github.com/ParadiseSS13/Paradise/blob/master/tgui/docs/tutorial-and-examples.md
+
+## Turf
+Turfs are floors, stuff like space, floors, carpets, or lava, or walls. This
+does not include windows, as they are objects.
+
+## Upstream
+The original repo that you have forked your remote repository from. For us, it
+is [ParadiseSS13/Paradise](https://github.com/ParadiseSS13/Paradise/).
+
+## Var
+A variable, used for temporarily storing data. For more
+permanent data, check out [defines](#define).
+
+## Verb
+A special type of proc, which is only available to mobs.
+
+## View Variables
+An admin tool that can be used in game to view the variables of anything, giving
+you more information about them. Very useful for debugging.
+
+## VSC
+Short for [Visual Studio Code](https://code.visualstudio.com/).
+
+## VV
+Short for [View Variables](#view-variables).
+
+## WYCI
+"When You Code It", a joking response to someone asking when something will be
+added to the game.
diff --git a/docs/references/tick_order.md b/docs/references/tick_order.md
new file mode 100644
index 000000000000..84116c536646
--- /dev/null
+++ b/docs/references/tick_order.md
@@ -0,0 +1,53 @@
+# BYOND Tick Order
+
+This document roughly describes the order in which BYOND performs operations in a given tick.
+
+The BYOND tick proceeds as follows:
+
+1. Procs sleeping via `walk()` are resumed (I don't know why these are first).
+
+2. Normal sleeping procs are resumed, in the order they went to sleep in the
+ first place. This is where the MC wakes up and processes subsystems. A
+ consequence of this is that the MC almost never resumes before other sleeping
+ procs, because it only goes to sleep for 1 tick 99% of the time, and 99% of
+ procs either go to sleep for less time than the MC (which guarantees that
+ they entered the sleep queue earlier when its time to wake up) and/or were
+ called synchronously from the MC's execution, almost all of the time the MC
+ is the last sleeping proc to resume in any given tick. This is good because
+ it means the MC can account for the cost of previous resuming procs in the
+ tick, and minimizes overtime.
+
+3. Control is passed to BYOND after all of our code's procs stop execution for this tick.
+
+4. A few small things happen in BYOND internals.
+
+5. SendMaps is called for this tick, which processes the game state for all
+ clients connected to the game and handles sending them changes in appearances
+ within their view range. This is expensive and takes up a significant portion
+ of our tick, about 0.45% per connected player as of 3/20/2022. This means
+ that with 50 players, 22.5% of our tick is being used up by just SendMaps,
+ after all of our code has stopped executing. That's only the average across
+ all rounds, for most high-pop rounds it can look like 0.6% of the tick per
+ player, which is 30% for 50 players.
+
+6. After SendMaps ends, client verbs sent to the server are executed, and it's
+ the last major step before the next tick begins. During the course of the
+ tick, a client can send a command to the server saying that they have
+ executed any verb. The actual code defined for that /verb/name() proc isn't
+ executed until this point, and the way the MC is designed makes this
+ especially likely to make verbs "overrun" the bounds of the tick they
+ executed in, stopping the other tick from starting and thus delaying the MC
+ firing in that tick.
+
+The master controller can derive how much of the tick was used in: procs
+executing before it woke up (because of world.tick_usage), and SendMaps (because
+of world.map_cpu, since this is a running average you cant derive the tick spent
+on maptick on any particular tick). It cannot derive how much of the tick was
+used for sleeping procs resuming after the MC ran, or for verbs executing after
+SendMaps.
+
+It is for these reasons why you should heavily limit processing done in verbs.
+While procs resuming after the MC are rare, verbs are not, and are much more
+likely to cause overtime since they're literally at the end of the tick. If you
+make a verb, try to offload any expensive work to the beginning of the next tick
+via a verb management subsystem.
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 000000000000..e5ea2885260c
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,88 @@
+site_name: Paradise Contributor Documentation
+
+theme:
+ name: material
+ icon:
+ logo: material/palm-tree
+ palette:
+ accent: custom
+ primary: custom
+ favicon: ./images/favicon.png
+
+extra:
+ social:
+ - icon: material/forum
+ name: Paradise Station Discussion Forum
+ link: https://www.paradisestation.org/forum
+ - icon: fontawesome/brands/wikipedia-w
+ name: Paradise Station Wiki
+ link: https://www.paradisestation.org/wiki
+ - icon: fontawesome/brands/github
+ name: Paradise Station GitHub
+ link: https://github.com/ParadiseSS13/Paradise
+ - icon: fontawesome/brands/discord
+ name: Paradise Station Discord
+ link: https://discord.gg/paradisess13
+ - icon: fontawesome/brands/patreon
+ name: Paradise Station Patreon
+ link: https://www.patreon.com/ParadiseStation
+ generator: false
+
+plugins:
+ - gh-admonitions
+
+markdown_extensions:
+ - admonition
+ - attr_list
+ - smarty
+ - pymdownx.highlight:
+ use_pygments: false
+ - pymdownx.details
+ - pymdownx.superfences
+ - pymdownx.snippets
+
+hooks:
+ - 'docs/hooks/contributing_path.py'
+
+extra_javascript:
+ - 'javascripts/highlight.min.js'
+ - 'javascripts/highlight-dm.js'
+ - 'javascripts/init.js'
+
+extra_css:
+ - 'css/para.css'
+ - 'css/atom-one-dark.css'
+
+nav:
+ # TODO: Coding / Code Style Guide
+ # TODO: Mapping / Guide to Mapmerge
+ # TODO: Spriting
+ # TODO: SS13 for Experienced Coders (maybe)
+
+ - 'Introduction': 'index.md'
+ - 'Code of Conduct': 'CODE_OF_CONDUCT.md'
+ - 'Contributing Guidelines': 'CONTRIBUTING.md'
+
+ - 'Contributing':
+ - 'Getting Started': './contributing/getting_started.md'
+ - 'Reviewer Crash Course': './contributing/reviewer.md'
+ - 'Writing Quality PRs': './contributing/quality_prs.md'
+
+ - 'Coding':
+ - 'Coding Quickstart': './coding/quickstart.md'
+ - 'Guide to Testing': './coding/testing_guide.md'
+ - 'Guide to Debugging': './coding/debugging.md'
+ - 'Style Guidelines': './coding/style_guidelines.md'
+ - 'Coding Requirements': './coding/coding_requirements.md'
+ - 'Testing Requirements': './coding/testing_requirements.md'
+
+ - 'Mapping':
+ - 'Mapping Quickstart': './mapping/quickstart.md'
+ - 'Mapping Requirements': './mapping/requirements.md'
+ - 'Design Guidelines': './mapping/design.md'
+
+ - 'References':
+ - 'Glossary': './references/glossary.md'
+ - 'Autodoc Guide': './references/autodoc.md'
+ - 'Using Feedback Data': './references/feedback_data.md'
+ - 'Tick Order': './references/tick_order.md'