+Written by: {{author.short_name}}
+
+{% if contributor != nil and contributor != false %}
+
+
+
+{% endif %}
+
+{% endif %}
diff --git a/_sass/content-nocharset.scss b/_sass/content-nocharset.scss
new file mode 100644
index 0000000000..032f8ef159
--- /dev/null
+++ b/_sass/content-nocharset.scss
@@ -0,0 +1,237 @@
+// Styles for rendered markdown in the .main-content container
+// stylelint-disable selector-no-type, max-nesting-depth, selector-max-compound-selectors, selector-max-type, selector-max-specificity, selector-max-id
+
+.main-content {
+ line-height: $content-line-height;
+
+ ol,
+ ul,
+ dl,
+ pre,
+ address,
+ blockquote,
+ .table-wrapper {
+ margin-top: 0.5em;
+ }
+
+ a {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ ul,
+ ol {
+ padding-left: 1.5em;
+ }
+
+ li {
+ .highlight {
+ margin-top: $sp-1;
+ }
+ }
+
+ ol {
+ list-style-type: none;
+ counter-reset: step-counter;
+
+ > li {
+ position: relative;
+
+ &::before {
+ position: absolute;
+ top: 0.2em;
+ left: -1.6em;
+ color: $grey-dk-000;
+ content: counter(step-counter);
+ counter-increment: step-counter;
+ @include fs-3;
+
+ @include mq(sm) {
+ top: 0.11em;
+ }
+ }
+
+ ol {
+ counter-reset: sub-counter;
+
+ > li {
+ &::before {
+ content: counter(sub-counter, lower-alpha);
+ counter-increment: sub-counter;
+ }
+ }
+ }
+ }
+ }
+
+ ul {
+ list-style: none;
+
+ > li {
+ &::before {
+ position: absolute;
+ margin-left: -1.4em;
+ color: $grey-dk-000;
+ content: "•";
+ }
+ }
+ }
+
+ .task-list-item {
+ &::before {
+ content: "";
+ }
+ }
+
+ .task-list-item-checkbox {
+ margin-right: 0.6em;
+ margin-left: -1.4em;
+
+ // The same margin-left is used above for ul > li::before
+ }
+
+ hr + * {
+ margin-top: 0;
+ }
+
+ h1:first-of-type {
+ margin-top: 0.5em;
+ }
+
+ dl {
+ display: grid;
+ grid-template: auto / 10em 1fr;
+ }
+
+ dt,
+ dd {
+ margin: 0.25em 0;
+ }
+
+ dt {
+ grid-column: 1;
+ font-weight: 500;
+ text-align: right;
+
+ &::after {
+ content: ":";
+ }
+ }
+
+ dd {
+ grid-column: 2;
+ margin-bottom: 0;
+ margin-left: 1em;
+
+ blockquote,
+ div,
+ dl,
+ dt,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ li,
+ ol,
+ p,
+ pre,
+ table,
+ ul,
+ .table-wrapper {
+ &:first-child {
+ margin-top: 0;
+ }
+ }
+ }
+
+ dd,
+ ol,
+ ul {
+ dl:first-child {
+ dt:first-child,
+ dd:nth-child(2) {
+ margin-top: 0;
+ }
+ }
+ }
+
+ .anchor-heading {
+ position: absolute;
+ right: -$sp-4;
+ width: $sp-5;
+ height: 100%;
+ padding-right: $sp-1;
+ padding-left: $sp-1;
+ overflow: visible;
+
+ @include mq(md) {
+ right: auto;
+ left: -$sp-5;
+ }
+
+ svg {
+ display: inline-block;
+ width: 100%;
+ height: 100%;
+ color: $link-color;
+ visibility: hidden;
+ }
+ }
+
+ .anchor-heading:hover,
+ .anchor-heading:focus,
+ h1:hover > .anchor-heading,
+ h2:hover > .anchor-heading,
+ h3:hover > .anchor-heading,
+ h4:hover > .anchor-heading,
+ h5:hover > .anchor-heading,
+ h6:hover > .anchor-heading {
+ svg {
+ visibility: visible;
+ }
+ }
+
+ summary {
+ cursor: pointer;
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ #toctitle {
+ position: relative;
+ margin-top: 1.5em;
+ margin-bottom: 0.25em;
+
+ + table,
+ + .table-wrapper,
+ + .code-example,
+ + .highlighter-rouge,
+ + .sectionbody .listingblock {
+ margin-top: 1em;
+ }
+
+ + p:not(.label) {
+ margin-top: 0;
+ }
+ }
+
+ > h1:first-child,
+ > h2:first-child,
+ > h3:first-child,
+ > h4:first-child,
+ > h5:first-child,
+ > h6:first-child,
+ > .sect1:first-child > h2,
+ > .sect2:first-child > h3,
+ > .sect3:first-child > h4,
+ > .sect4:first-child > h5,
+ > .sect5:first-child > h6 {
+ margin-top: $sp-2;
+ }
+}
diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss
new file mode 100644
index 0000000000..51766ac182
--- /dev/null
+++ b/_sass/custom/custom.scss
@@ -0,0 +1,226 @@
+/****************************/
+/***** Jekyll Glossary ******/
+/****************************/
+
+/* vendored from https://raw.githubusercontent.com/erikw/jekyll-glossary_tooltip/main/lib/jekyll-glossary_tooltip/jekyll-glossary_tooltip.css */
+
+.jekyll-glossary {
+ position: relative;
+ display: inline-block;
+ border-bottom: 2px dotted #0074bd;
+ cursor: help;
+}
+
+.jekyll-glossary .jekyll-glossary-tooltip {
+ visibility: hidden;
+ width: 120px;
+ background-color: black;
+ color: #fff;
+ text-align: center;
+ font-size: 0.5em;
+ padding: 5px;
+ border-radius: 6px;
+
+ /* Position the tooltip text - see examples below! */
+ position: absolute;
+ z-index: 1;
+
+ width: 160px;
+ bottom: 100%;
+ left: 50%;
+ margin-left: -80px; /* Use half of the width to center the tooltip */
+
+}
+
+/* Show the tooltip text when you mouse over the tooltip container */
+.jekyll-glossary:hover .jekyll-glossary-tooltip {
+ visibility: visible;
+}
+
+/* Style the source link (if there is one provided in the glossary entry). */
+.jekyll-glossary-source-link:before {
+ content: "[source]"; // "(reference)", "" or whatever you want.
+}
+
+/* Arrow created with borders. */
+.jekyll-glossary .jekyll-glossary-tooltip::after {
+ content: " ";
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: black transparent transparent transparent;
+}
+
+/* Animation from invisible to visible on hover. */
+.jekyll-glossary .jekyll-glossary-tooltip {
+ opacity: 0;
+ transition: opacity 1s;
+}
+.jekyll-glossary:hover .jekyll-glossary-tooltip {
+ opacity: 1;
+}
+
+/***************************/
+/***** Theme switcher ******/
+/***************************/
+
+// vendored from https://github.com/argyleink/gui-challenges/blob/main/theme-switch
+
+@import"https://unpkg.com/open-props/easings.min.css";
+
+.sun-and-moon>:is(.moon,.sun,.sun-beams) {
+ transform-origin:center center
+}
+.sun-and-moon>:is(.moon,
+.sun) {
+ fill:var(--icon-fill)
+}
+.theme-toggle:is(:hover,
+:focus-visible)>.sun-and-moon>:is(.moon,
+.sun) {
+ fill:var(--icon-fill-hover)
+}
+.sun-and-moon>.sun-beams {
+ stroke:var(--icon-fill);
+ stroke-width:2px
+}
+.theme-toggle:is(:hover,
+:focus-visible) .sun-and-moon>.sun-beams {
+ stroke:var(--icon-fill-hover)
+}
+[data-theme=dark] .sun-and-moon>.sun {
+ transform:scale(1.75)
+}
+[data-theme=dark] .sun-and-moon>.sun-beams {
+ opacity:0
+}
+[data-theme=dark] .sun-and-moon>.moon>circle {
+ transform:translate(-7px)
+}
+@supports (cx: 1) {
+ [data-theme=dark] .sun-and-moon>.moon>circle {
+ transform:translate(0);
+ cx:17
+ }
+}
+@media (prefers-reduced-motion: no-preference) {
+ .sun-and-moon>.sun {
+ transition:transform .5s var(--ease-elastic-3)
+ }
+ .sun-and-moon>.sun-beams {
+ transition:transform .5s var(--ease-elastic-4),opacity .5s var(--ease-3)
+ }
+ .sun-and-moon .moon>circle {
+ transition:transform .25s var(--ease-out-5)
+ }
+ @supports (cx: 1) {
+ .sun-and-moon .moon>circle {
+ transition:cx .25s var(--ease-out-5)
+ }
+ }
+ [data-theme=dark] .sun-and-moon>.sun {
+ transform:scale(1.75);
+ transition-timing-function:var(--ease-3);
+ transition-duration:.25s
+ }
+ [data-theme=dark] .sun-and-moon>.sun-beams {
+ transform:rotate(-25deg);
+ transition-duration:.15s
+ }
+ [data-theme=dark] .sun-and-moon>.moon>circle {
+ transition-delay:.25s;
+ transition-duration:.5s
+ }
+}
+.theme-toggle {
+ --size: 2rem;
+ --icon-fill: hsl(210 10% 30%);
+ --icon-fill-hover: hsl(210 10% 15%);
+ background:none;
+ border:none;
+ padding:0;
+ inline-size:var(--size);
+ block-size:var(--size);
+ aspect-ratio:1;
+ border-radius:50%;
+ cursor:pointer;
+ touch-action:manipulation;
+ -webkit-tap-highlight-color:transparent;
+ outline-offset:5px
+}
+.theme-toggle>svg {
+ inline-size:100%;
+ block-size:100%;
+ stroke-linecap:round
+}
+[data-theme=dark] .theme-toggle {
+ --icon-fill: hsl(210 10% 70%);
+ --icon-fill-hover: hsl(210 15% 90%)
+}
+@media (hover: none) {
+ .theme-toggle {
+ --size: 48px
+ }
+}
+
+/***************************/
+/***** Custom Stuff ********/
+/***************************/
+
+// unvendored do not remove
+html[data-theme="dark"] button.theme-toggle, button.theme-toggle {
+ height: 100%;
+ margin: 0 0 0 1rem;
+}
+
+/* Enlarged dt */
+.main-content dl.big-dt, html[data-theme="dark"] dl.big-dt {
+ grid-template: auto / fit-content(20em) 1fr;
+}
+
+/* dt allowed to expand/contract */
+.main-content dl.dl-auto, html[data-theme="dark"] dl.dl-auto {
+ grid-template: auto / auto 1fr;
+}
+
+.aux-nav, html[data-theme="dark"] .aux-nav {
+ padding-right: 0;
+}
+
+.aux-nav-img img {
+ filter: invert(27%) sepia(14%) saturate(463%) hue-rotate(166deg) brightness(93%) contrast(85%);
+}
+
+.aux-nav-list-item:hover .aux-nav-img img {
+ filter: invert(13%) sepia(6%) saturate(958%) hue-rotate(169deg) brightness(96%) contrast(95%);
+}
+
+html:not(data-theme) .aux-nav-matrix img, html[data-theme="light"] .aux-nav-matrix img {
+ background-image: url("/spring/assets/matrix-logo.svg");
+}
+
+html[data-theme="dark"] .aux-nav-matrix img {
+ background-image: url("/spring/assets/matrix-logo-white.svg");
+}
+
+html:not(data-theme) .aux-nav-github img, html[data-theme="light"] .aux-nav-github img {
+ background-image: url("/spring/assets/github-mark.svg");
+}
+
+html[data-theme="dark"] .aux-nav-github img {
+ background-image: url("/spring/assets/github-mark-white.svg");
+}
+
+html .aux-nav-img img {
+ display: block;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ background-repeat: no-repeat;
+ background-size: cover;
+ height: 32px;
+ padding-left: 100%; /* Equal to width of new image */
+}
diff --git a/_sass/modules-nocharset.scss b/_sass/modules-nocharset.scss
new file mode 100644
index 0000000000..cdf9509b4d
--- /dev/null
+++ b/_sass/modules-nocharset.scss
@@ -0,0 +1,16 @@
+// Import external dependencies
+@import "./vendor/normalize.scss/normalize";
+
+// Modules
+@import "./base";
+@import "./layout";
+@import "./content-nocharset";
+@import "./navigation";
+@import "./typography";
+@import "./labels";
+@import "./buttons";
+@import "./search";
+@import "./tables";
+@import "./code";
+@import "./utilities/utilities";
+@import "./print";
diff --git a/_scripts/get_engine_data.sh b/_scripts/get_engine_data.sh
new file mode 100755
index 0000000000..dc0b16d74f
--- /dev/null
+++ b/_scripts/get_engine_data.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+DATA_DIR="$SCRIPT_DIR/../_data"
+DATA_FILE="$DATA_DIR/latest_release.json"
+WORK_DIR="$SCRIPT_DIR/tmp"
+CONFIG_FILE="$DATA_DIR/configs.json"
+WDEFS_FILE="$DATA_DIR/weapondefs.json"
+COMMANDS_FILE="$DATA_DIR/unsynced_commands.json"
+SYNCED_COMMANDS_FILE="$DATA_DIR/synced_commands.json"
+
+DOWNLOAD_URL=$(jq -r '.assets[] | select(.name | contains("_linux-64-minimal-portable")).browser_download_url' $DATA_FILE)
+
+echo "> downloading latest engine release from $DOWNLOAD_URL"
+
+mkdir $WORK_DIR
+cd $WORK_DIR
+
+curl -L $DOWNLOAD_URL -o engine.7z
+7z -y e engine.7z spring
+
+echo "> writing $CONFIG_FILE"
+rm -f $CONFIG_FILE
+./spring --list-config-vars | grep -v "^\[t=" > $CONFIG_FILE
+
+echo "> writing $WDEFS_FILE"
+rm -f $WDEFS_FILE
+./spring --list-def-tags | grep -v "^\[t=" > $WDEFS_FILE
+
+echo "> writing $COMMANDS_FILE"
+rm -f $COMMANDS_FILE
+./spring --list-unsynced-commands | grep -v "^\[t=" > $COMMANDS_FILE
+
+echo "> writing $SYNCED_COMMANDS_FILE"
+rm -f $SYNCED_COMMANDS_FILE
+./spring --list-synced-commands | grep -v "^\[t=" > $SYNCED_COMMANDS_FILE
diff --git a/_scripts/get_release_data.sh b/_scripts/get_release_data.sh
new file mode 100755
index 0000000000..6199cd88c2
--- /dev/null
+++ b/_scripts/get_release_data.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+DATA_FILE="$SCRIPT_DIR/../_data/latest_release.json"
+
+echo "> writing $DATA_FILE"
+
+rm $DATA_FILE
+curl -s https://api.github.com/repos/beyond-all-reason/spring/releases/latest \
+ > $DATA_FILE
diff --git a/about.markdown b/about.markdown
new file mode 100644
index 0000000000..b6ac31f041
--- /dev/null
+++ b/about.markdown
@@ -0,0 +1,10 @@
+---
+layout: default
+title: About
+permalink: /about/
+---
+
+Recoil is an RTS engine.
+
+You can find the source code for Recoil at GitHub:
+[spring](https://github.com/beyond-all-reason/spring)
diff --git a/articles.markdown b/articles.markdown
new file mode 100644
index 0000000000..972cf8783e
--- /dev/null
+++ b/articles.markdown
@@ -0,0 +1,10 @@
+---
+layout: default
+title: Articles
+nav_order: 2
+has_children: true
+permalink: articles
+---
+
+# Articles
+{: .no_toc }
diff --git a/articles/ceg-operators.markdown b/articles/ceg-operators.markdown
new file mode 100644
index 0000000000..b7fa2132d5
--- /dev/null
+++ b/articles/ceg-operators.markdown
@@ -0,0 +1,218 @@
+---
+layout: post
+title: CEG operators
+parent: Articles
+permalink: articles/ceg-operators
+author: sprunk
+---
+
+## Basics
+
+You can customize some CEG entries by specifying a series of operators instead of just a number.
+This lets you apply limited logic to customize explosions.
+Each operation is specified by a single string.
+
+### Raw numbers
+
+A single number means just that number. So for example:
+```lua
+sizeGrowth = "3",
+```
+This sets `sizeGrowth` to 3. So far so simple.
+
+Some parameters, especially 3D vectors, accept multiple values. In that case, we separate then with the `,` (comma) operator.
+For example:
+```lua
+pos = "1, 2, 3",
+```
+This sets the position vector to x=1, y=2, and z=3.
+
+### Running value
+
+There is an implicit running value, which starts at 0 and on which all the operators work.
+A raw number is actually an "operatorless" operator that performs simple addition to that running value.
+So, one could think of the examples above as really being `0 +3` for `sizeGrowth` and `0 +1`, `0 +2`, and `0 +3` as the components of the `pos` vector respectively.
+
+
+To illustrate how this matters, consider this example. Note that there is no `,` between these!
+```lua
+sizeGrowth = "1 2 3",
+```
+The result of this is 6. This is because each of these is addition: `0 +1 +2 +3`, which nets 6.
+
+Similarly:
+```lua
+pos = "1 2, 3 4, 5 6",
+```
+This sets the components to 3 (0 +1 +2), 7 (0 +3 +4) and 11 (0 +5 +6) respectively.
+
+
+Putting a bunch of raw numbers next to each other doesn't make much sense, since we could have just written the sum directly, but the principle starts to matter when you mix operators.
+
+### Random (`r`)
+
+The `r` operator also works on the running value, but adds a random value between 0 and the operand.
+So, for example, `r4` gives a random value between 0 and 4. Practical hints:
+ * very useful to make explosions look less artificial.
+ * if you don't want to roll from 0, then just add the offset (remember a raw operatorless number performs addition).
+So, `3 r4` gives a value between 3 and 7.
+ * a common desire is to roll negative values, for example so that a directional particle can go either way.
+In that case, also use the offset. A common idiom is to use, for example, `-15 r30` to roll ±15.
+ * the value is distributed uniformly, but with some knowledge of statistics you could tweak it by stacking rolls.
+For example `r6` produces a flat uniform 0-6 distribution, `r3 r3` a sort of triangle where 3 is much more likely than 0 or 6, and `r2 r2 r2` something smoother still, more resembling a bell curve.
+In practice this seems very underused though, and you can't make the distribution asymmetrical via this basic method,
+though you can via the more advanced ones below.
+
+### Index (`i`)
+
+The `i` operator multiplies its operand by the index of the particle, and adds this to the running value.
+When a generator spawns multiple particles of the same kind, each one is assigned an increasing index: 0, 1, 2, etc.
+
+For example, if an explosion spawns 4 particles and `size = "3 i2"`, then they will have sizes of 3, 5, 7 and 9 respectively.
+
+ * useful for spawning stuff in something resembling a line or cone.
+ * useful for scaling explosions via particle count, since the low-index particles will behave the same as before.
+ * remember this doesn't multiply the running value, or anything other than the operand.
+
+### Damage (`d`)
+
+The `d` operator multiplies its operand by the "damage" of an explosion.
+For example `d0.1` will net 10 for a 100-damage explosion, and 50 for a 500-damage explosion.
+
+Some practical remarks:
+ * for regular weapons, this is the "default" damage. Beware if you treat it as the "features" armor class (since they can't have a real armor class)!
+ * for CEG trails, damage is the remaining TTL of the projectile. So you can for example make missile trails burn out.
+ * makes sure adjustments of damage (both ingame such as buffs or upgrades, or metagame changes such as balance changes after a patch) are subtly reflected in the visuals.
+ * also lets you reuse the same CEG for multiple weapons of a similar type, for great visual consistency.
+ * existing games prefer to have a separate effect for each similar weapon, so this is quite an uncommon operator as far as examples to look at.
+
+## Advanced
+
+In addition to the running value, you have an access to a buffer with 16 "slots" to which you can save values for later use.
+Other than allowing complex math, these let you reuse a value for multiple components of a vector (across the `,` boundary which normally resets the running value).
+There are also some operators that involve more complex values than just addition.
+
+### Yank (`y`), add (`a`), and multiply (`x`)
+
+The `y` operator saves ("yanks") the current running value to the buffer under given index, and resets the running value to 0.
+The `a` operator adds to the current running value from given buffer.
+The `x` operator multiplies the running value by the value of given buffer.
+
+Examples:
+ * `10 r20 y7 5 r10 x7`. Rolls a random value 10-30 (see earlier lesson), saves it to buffer #7 (which resets running value to 0), rolls a different one 5-15, then multiplies it by the value of the contents of buffer #7 (i.e. the previous roll). In general, the `foo yN bar xN` pattern is how you multiply `foo` and `bar`.
+ * `r10 y9 a9, 0, a9`. Rolls a value, saves it to buffer #9, loads it right back because of the reset. Reuses the value for the third component of the vector. This is how you can get diagonal vectors.
+
+### Sinus (`s`)
+
+The `s` operator treats its operand as an amplitude and the current running value as the phase, and _replaces_ the current running value with the result.
+For example `3 s2` is about 0.28, because that's `2 * sin(3 radians)`.
+
+ * only really makes sense with sources of unpredictability such as `r`, `i`, or `d`.
+ * there is no separate cosinus operator, but you can make a ghetto cosinus via `cos(x) = sin(π/2 + x)`, i.e. just do `1.57 sX` instead of just `sX`.
+ * good for making circular or spherical volumetric effects (for non-volumetric there's basic spread parameters like `emitRot`).
+
+### Sawtooth/modulo (`m`)
+
+Applies the modulo operator to the running value. So for example:
+ * `1 m7` is 1
+ * `6 m7` is 6
+ * `7 m7` is 0
+ * `8 m7` is 1
+ * `13 m7` is 6
+ * `14 m7` is 0
+
+In combination with the `i` operator, or the `d` operator for CEG trails, you can get periodic or "line stipple" style effects,
+since those are where multiple consecutive particles will be spawned with consecutive values for the sawtooth to work on.
+For example `numParticles = "d0.125 m1 0.125"` gets you one particle every 8 frames in a CEG trail.
+
+### Discretize (`k`)
+
+Truncates the running value to a multiple of the operand:
+ * `1 k7` is 0
+ * `6 k7` is 0
+ * `7 k7` is 7
+ * `8 k7` is 7
+ * `13 k7` is 7
+ * `14 k7` is 14
+
+You can use this to perform comparisons and have ghetto boolean logic, as long as you can provide some sort of upper bound on the running value.
+It's admittedly a pretty inane way to do it and if you're at that point consider whether it would not be better to just make particles via Lua though.
+* say you want to check "if x >= 123" and can assume that x < 10000.
+* do `(...) 9877 k10000 -9999`
+* now the running value is 0 or 1 depending on whether it was less or more than 123 before.
+* you can then use it to perform further operations.
+* in particular, multiplying/summing such "booleans" gives you the AND/OR logic operators respectively.
+
+Some obvious use cases achievable by conditionally setting `numParticles`:
+* you can make a CEG trail fizzle out a bit earlier than nominal projectile expiration by checking if the remaining TTL is low enough.
+* in a CEG used for multiple similar weapons via the damage operator, you can enable extra particle types for big enough explosions.
+* comparisons and boolean logic open up a lot of possibilities, making this perhaps the most powerful operator.
+
+### Power (`p`) and power buffer (`q`)
+
+The `p` operator raises the running value to the operandth power. The `q` operator is similar but takes the power from given buffer. The main use case is probably for getting x² or √x for making volumetric effects more or less center-heavy. Examples:
+ * `3 p4` is 81, since that's 3⁴.
+ * `4 y7 3 q7` is also 81 (and leaves the 7th buffer slot with the value of 4).
+
+## Table
+
+Notation: V is the running value, X is the operand, and B denotes the buffer.
+
+
+
+
operator
+
effect
+
+
+
(none)
+
V += X
+
+
+
r
+
V += random(0; X)
+
+
+
i
+
V += X * index
+
+
+
d
+
V += X * damage
+
+
+
y
+
B[X] = V V = 0
+
+
+
a
+
V += B[X]
+
+
+
x
+
V *= B[X]
+
+
+
s
+
V = X * sin(V)
+
+
+
m
+
V = V % X
+
+
+
k
+
V = floor(V / X) * X
+
+
+
p
+
V = VX
+
+
+
q
+
V = VB[X]
+
+
+
,
+
result = V V = 0
+
+
diff --git a/articles/modrules-and-others.markdown b/articles/modrules-and-others.markdown
new file mode 100644
index 0000000000..44334f25a8
--- /dev/null
+++ b/articles/modrules-and-others.markdown
@@ -0,0 +1,39 @@
+---
+layout: post
+title: Modrules and (un)related concepts
+parent: Articles
+permalink: articles/modrules-and-others
+author: sprunk
+---
+
+### Mod options, mod info, mod rules, rules params
+These concepts have similar names (which stems from how Recoil games used to be called "mods", even original/standalone ones)
+and all deal with customizability in some way, so often get confused with each other. But they are not directly related.
+This article will briefly describe all of them in a way that hopefully prevents any ambiguity.
+
+### Mod info
+* metadata about the game's archive. Things like name and version.
+* lives in `./modinfo.lua`, hence the name.
+* many mature games put `"$VERSION"` as the version. This is a magic value replaced by the Rapid distribution system with the actual version string. Check out Rapid's documentation for specifics.
+* this file is both necessary and sufficient for a Recoil game, and its contents can consist of just the `name` entry as well.
+* for engine devs: note that in engine internals, modinfo is **not** represented by the `CModInfo` class, that one is actually _mod rules_! Modinfo is a "class 1 meta file" only really referred to in the archive scanner.
+
+### Mod rules
+* a hardcoded set of knobs for tweaking engine behaviour.
+* some technical (like the choice of pathfinding system or allocation limits) and some gameplay related (like the resource cost scaling for repairing).
+* everything is a single constant global value. No per-unit rules, no changing at runtime, no rules outside the limited set exposed by the engine.
+* read from `./gamedata/modrules.lua`, hence the name. Internal engine C++ code stores them in the `CModInfo` class.
+* can depend on modoptions.
+
+### Mod options
+* per-match game setup.
+* a set of arbitrary key-value pairs supplied by the host; interpretation is solely up to the game.
+* usually live in `./modoptions.lua`, but this is just a convention for lobbies (this is where `unitsync` looks). The engine doesn't read this, nor enforce defaults etc.
+* usually used for things like enabling custom modes and game setups (add lava, disable units etc.), but also is the technical means for supplying singleplayer mission data.
+* maps can suggest and interpret modoptions too, these are often called mapoptions.
+
+### Rules params
+* dynamic per-unit/team or gamewide values.
+* arbitrary key-value pairs set and interpreted by the game.
+* usually used for storing attributes like "is burning" or "slowdown %" or such.
+* can depend on both modoptions and modrules.
diff --git a/articles/netcode-overview.markdown b/articles/netcode-overview.markdown
new file mode 100644
index 0000000000..bb935bcdc1
--- /dev/null
+++ b/articles/netcode-overview.markdown
@@ -0,0 +1,56 @@
+---
+layout: post
+title: Netcode overview
+parent: Articles
+permalink: articles/netcode-overview
+author: sprunk
+---
+
+Here's an overview of some aspects of Recoil's so-called netcode. The **tl;dr is that it uses lockstep simulation which is essentially the same as basically every classic RTS from the 90s and 00s**. Read on for a bit more detailed explanation.
+
+### What gets sent?
+
+**The Recoil network protocol does not involve sending parts of the simulation state**, such as unit positions or health. The **only thing sent over is player inputs**, such as a "move unit X to position Y" order.
+How does each client know the game state, then?
+The **inputs are sufficient to simulate the gamestate**: if unit X is ordered to move to position Y then its position will change according to its speed (speed being defined in a local file, so does not need to be sent),
+maybe it then finds enemy unit Z, which it can shoot according to its range (also defined in a local file) and then that unit's health will decrease by some number - again the calculations will look the same on each client.
+Also, keep in mind the **gamestate can easily get magnitudes larger than the size of the inputs** - for example the two orders "build unit, repeat" on a factory will produce a quickly growing gamestate.
+
+### Ping and the packet queue
+Of course, packets do not arrive at the server immediately, your ping is involved.
+But that is not all: **to guard against network instability**, they are not broadcast by the server to other clients immediately. Instead, **packets are queued for a bit later in a buffer**.
+By default this is about 3-6 simulation frames (100-200ms) on top of your "normal" ping, though **games can adjust it and the engine also adjusts it on its own if a match is "laggy"**.
+In interfaces such as the `/info` playerlist and `Spring.GetPlayerInfo`, the **ping is in terms of sim frames after taking the queue into account**.
+
+### Sync
+What makes the "simulate inputs" approach work is that **the engine takes utmost care to keep calculations identical** on each client.
+This is not trivial because you still have to work with **things that naturally differ on each client**, such as mouse position or which units are selected - this is **called the unsynced state**.
+On top of that, there can be **hardware differences** that have to be worked around to get identical results - the huge effort involved is one of the reasons why **Recoil is not available outside x86-64**.
+Finally, **desync can cascade in a sort of butterfly effect**.
+For example, maybe a unit deals a bit less damage than it should, which makes its target survive an otherwise lethal hit;
+that unit then bumps into a constructor, slightly delaying the construction of a resource collector.
+A difference in stored resources then stems from an incorrect damage calculation minutes earlier.
+The **huge effort of hunting down sources of desync** is one of the biggest drawbacks of the architecture.
+
+### Replays and saves
+A **replay is just a replication of the network queue from the live game**.
+Instead of receiving packets from the server, you read them from your local replay file. Otherwise **everything proceeds largely the same as a live game**.
+
+A save is an **imperfect snapshot** of the gamestate.
+While doable, a **perfect replication is difficult and not currently implemented**.
+Running identical inputs on a save will proceed differently compared to running them on the original state from which the save was produced.
+This means that **using the save mechanism is not currently possible to get mid-game joins or skipping backwards in replays** since it would desync, although could happen if somebody contributes the effort.
+
+### Tradeoffs involved
+The **benefits** of this setup are:
+* extremely small bandwidth use (remember Recoil has its roots in Total Annihilation back from 1997)
+* small replay size
+* you cannot easily cheat by modifying your own gamestate (cheat engine style), since there are no inputs that will replicate that on the server.
+* the server doesn't need to simulate a game, since clients already do. This makes hosting much easier. See [the dedicated server article]({{ site.baseurl }}{% link guides/headless-and-dedi.markdown %}).
+* for development: easy to reproduce bugs - running the same inputs will result in the same simulation, containing the bug.
+
+But there are also **drawbacks**:
+* you cannot jump to any specific time, either in a replay or e.g. to rejoin an ongoing game at its current state.
+The gamestate at any simulation frame has to be simulated from the previous frame, and that frame has to be simulated from the one before it, beginning all the way back at game start.
+* you also have to simulate the whole game state including enemy units, so maphack is possible.
+* making sure desync doesn't happen takes a huge effort.
diff --git a/articles/pathfinding.markdown b/articles/pathfinding.markdown
new file mode 100644
index 0000000000..4ee2abc927
--- /dev/null
+++ b/articles/pathfinding.markdown
@@ -0,0 +1,7 @@
+---
+layout: default
+title: Pathfinding
+parent: Articles
+permalink: articles/pathfinding
+published: false
+---
diff --git a/articles/select-command.markdown b/articles/select-command.markdown
new file mode 100644
index 0000000000..efb6938de9
--- /dev/null
+++ b/articles/select-command.markdown
@@ -0,0 +1,221 @@
+---
+layout: default
+title: The select command
+parent: Articles
+permalink: /articles/select-command/
+---
+
+# The `select` command
+
+You can bind the command `select` to a key, in order to define custom selection commands. After select, you can specify a **selector**, that defines *which* units will be selected.
+
+This selector consists of three parts: `SOURCE+FILTER+CONCLUSION+`
+
+- *SOURCE*: Which units to choose from.
+- *FILTER*: Narrow down the list of units.
+- *CONCLUSION*: What to do exactly with the units that were chosen and filtered.
+
+These three parts are all succeeded by a literal plus sign (`+`), so there is one `+` between *SOURCE* and *FILTER*, one between *FILTER* and *CONCLUSION*, and one after *CONCLUSION*. Do **not** use a space (` `) anywhere in the entire expression.
+
+For example, `AllMap+_Builder+_SelectAll+` is a valid selector, where:
+
+- `AllMap` is the *SOURCE*
+- `_Builder` is the *FILTER* and
+- `_SelectAll` is the *CONCLUSION*.
+
+Note that there is an **underscore** (`_`) between each element, even if there is already a plus sign (`+`), for hysterical raisins...
+
+## Source
+
+The *SOURCE* describes the set of units that you want to filter and pick a selection from. For players, these are restricted your team. It can be one of
+
+- `AllMap`: All active units on the entire map.
+- `Visible`: All active units that are currently visible.
+- `PrevSelection`: All units present in previous selection; that is, the one active before you hit the selection key.
+- `FromMouse_`: All units that are at most a distance of `` away from the mouse cursor.
+- `FromMouseC_`: Same as above, but using a vertical cylinder instead of a sphere. This is good for selecting airplanes or ships on deep water.
+
+## Filter
+
+The *FILTER* is an arbitrarily long list of filters.
+
+Here are the filters. Note that "units" generally means both buildings and mobile units. Typing both got old real quick.
+
+### `Not`
+
+ Every filter can be preceded by `Not` to negate it. You have to use a `_` to separate the `Not` from the filter, as in `Not_Builder`.
+
+### `AbsoluteHealth_`
+
+ Keep only units that have an absolute health greater than `` points.
+
+ - `AbsoluteHealth_100`: Keep only units that have **more** than 100 health points left.
+ - `Not_AbsoluteHealth_100`: Keep only units that have **less** than 101 health points left.
+
+### `Aircraft`
+
+ Keep only units that can fly.
+
+### `Builder`
+
+ Keep only units and buildings that can construct. This means Factories, Construction Turrets, Constructors, but not Rezzers.
+
+### `Buildoptions`
+
+ Keep only units that can build other units or buildings.
+
+### `Building`
+
+ Keep only buildings, not mobile units.
+
+### `Category_`
+
+ Keep only units of category ``
+
+### `Cloak`
+
+ Keep only units that can cloak.
+
+### `Cloaked`
+
+ Keep only units that are currently cloaked.
+
+### `Guarding`
+
+ Keep only units that currently have a **Guard** order.
+
+### `Patrolling`
+
+ Keep only units that have a **Patrol** order early in the queue (first 4 commands, including sub-orders spawned by Patrol).
+
+### `IdMatches_`
+
+ Keep only units whose internal name (unitDef name) matches `` **exactly**. Differently from other filters further invocations will match units matching one name **OR** another.
+
+ - `IdMatches_armcom`: keep only Armada Commanders (internally named `armcom`).
+ - `IdMatches_armcom_IdMatches_armflea`: keep only Armada Commanders or Fleas.
+ - `Not_IdMatches_armcom_Not_IdMatches_armflea`: keep all units that are not Armada Commanders or Fleas.
+
+### `Idle`
+
+ Keep only units that are currently idle, i.e. do not have any active order.
+
+### `InGroup_`
+
+ Keep only units that are in control group ``.
+
+ - `Not_InGroup_`: keep all units that are **not** currently in control group ``.
+
+### `InHotkeyGroup`
+
+ Keep only units that are in any control group.
+
+ - `Not_InHotkeyGroup`: keep all units that are **not** currently in any control group.
+
+### `InPrevSel`
+
+ Keep only units of the same type (unitDefID) as any unit in the selection before this `select` command was run.
+
+### `Jammer`
+
+ Keep only units that have a jammer radius greater than 0.
+
+### `ManualFireUnit`:
+
+ Keep only units that have a weapon that requires manual firing (currently only the commanders and Armada Thor).
+
+### `NameContain_`
+
+ Keep only units whose name contains the ``.
+
+### `Radar`
+
+ Keep only units that have a radar or sonar radius greater than 0.
+
+### `Resurrect`
+
+ Keep only units that can resurrect other units.
+
+### `RelativeHealth_`
+
+ Keep only units that have health greater than `` percent.
+
+ Example:
+ - `Not_RelativeHealth_10`: Keep only units that have less than 10% health.
+
+### `RulesParamEquals__`
+
+ Keep only units where the `` rules parameter has the exact value ``.
+
+### `Stealth`
+
+ Keep only units that are stealthy.
+
+### `Transport`
+
+ Keep only units that can transport other units.
+
+### `Waiting`
+
+ Keep only units that currently have a **Wait** order.
+
+### `WeaponRange_`
+
+ Keep only units that have a maximum weapon range greater than ``.
+
+### `Weapons`
+
+ Keep only units that have any weapons.
+
+
+## Conclusion
+
+The *CONCLUSION* specifies what to do with the units that are left from the source after running through all the filters.
+
+If the *CONCLUSION* starts with `_ClearSelection`, your new selection will replace the old one; otherwise, it will just add to it. It must be followed by exactly one of:
+
+- `SelectAll`: all units
+- `SelectOne`: one unit, will also center the camera on that unit. This command remembers which unit was selected, on repeating the selection command, the **next** unit will be selected, so you can cycle through all matching units.
+- `SelectClosestToCursor`: one unit, the one closest to the mouse cursor.
+- `SelectNum_`: `` units. Repeating this command adds `` more units.
+- `SelectPart_`: `` percent of the units.
+
+
+# Examples
+Recall that between every two tokens, there must be an underscore `_`, even if there is also a `+`. Another way to put it is that before every word in your selector except the *SOURCE*, there must be an underscore.
+
+Some examples. Again, "unit" also includes buildings.
+
+- `AllMap++_ClearSelection_SelectAll+`
+
+ Selects everything on the entire map.
+
+- `AllMap+_Builder_Idle+_ClearSelection_SelectOne+`
+
+ Selects any (one) idle builder (unit or building) on entire map. Repeatedly running this command will cycle through all idle builders.
+
+- `AllMap+_Buildoptions_Building+_ClearSelection_SelectNum_1+`
+
+ Selects any (one) building that can produce units (i.e. factories), map-wide. Repeatedly running this command will cycle through all factories. Unlike the above example, this selector will **not** snap the camera to the factory. Replace `SelectNum_1` with `SelectOne` in order to achieve this.
+
+- `AllMap+_Radar+_ClearSelection_SelectAll+`
+
+ Selects all units with radar/sonar/jammer.
+
+- `AllMap+_Not_Aircraft_Weapons+_ClearSelection_SelectAll+`
+
+ Selects all non-aircraft armed units
+
+- `AllMap+_InPrevSel+_ClearSelection_SelectAll+`
+
+ Selects all units of any type that was in your previous selection.
+
+ Note that up to now, all keys said `ClearSelection`, hence they replaced your old selection.
+
+- `AllMap+_InPrevSel_Not_InHotkeyGroup+_SelectAll+`
+
+ Selects all units of any type that was in your previous selection, unless they are already in a hotkey group.
+
+- `PrevSelection+_Not_Building_Not_RelativeHealth_30+_ClearSelection_SelectAll+`
+
+ From your previous selection, leaves everything that is below 30% health, and not a building. (Use this to quickly retreat damaged units.)
diff --git a/articles/team-terminology.markdown b/articles/team-terminology.markdown
new file mode 100644
index 0000000000..f2225763f6
--- /dev/null
+++ b/articles/team-terminology.markdown
@@ -0,0 +1,89 @@
+---
+layout: post
+title: Team terminology
+parent: Articles
+permalink: articles/team-terminology
+author: sprunk
+---
+
+## Overview
+
+Often players want to play as a team, or conduct diplomacy with each other.
+This article aims to explain Recoil's somewhat confusing terminology behind the various team-related entities and how they relate to each other.
+
+### Allyteam vs team
+
+What most people would naturally call a team is called an *"allyteam"* in Recoil terminology.
+A *"team"* is a single element of an *"allyteam"*. *Teams* are permanently bound to an *allyteam*.
+
+For example, NATO would be an *allyteam* while USA and UK would be *teams*, because they are separate entities but are in the same alliance, sharing goals and intel.
+For modders coming from Supreme Commander, the *team* is what SupCom calls an "army".
+
+*Teams* each have their own:
+ * units
+ * resources
+ * colour
+ * starting point
+ * faction (aka side)
+ * any other custom extra traits you add.
+
+*Teams* in the same *allyteam* always share some traits (meaning that, these operate on the level of *allyteams*):
+ * visibility (this includes sight, other sensors such as radar, but also reading of various unit traits that aren't readable by enemies even in sight, for example mana if a game wants to implement that)
+ * diplomacy towards other *allyteams* (in particular, all *teams* in an *allyteam* are never hostile to each other)
+ * victory goals
+
+### Team vs player
+
+A *team* is generally controlled by a *player* or an AI.
+An AI is not considered a *player* and the interfaces for dealing with AIs are separate to those for dealing with players,
+but generally they fill the same role of controlling a *team*.
+
+Control of a *team* is not exclusive.
+In particular, a *team* can be controlled by two or more *players* (or AIs, or a mix thereof) simultaneously (think SC2 Archon Mode).
+This is sometimes referred to as "comsharing" in the community for historical reasons.
+In that case all of them can exercise control over the team equally.
+
+Control of a *team* is also not permanent.
+*Players* can start controlling a different team (losing control of the previous *team*).
+In particular *players* can also control no team at all, in which case they're just spectators.
+It is up to the game to let *players* change their *team* to a different one (they cannot do so at will), though they can always become a spectator.
+
+To go with the analogy above, if the US Army is a *team* then Patton and Eisenhower are *players*: they both have control over the army (simultaneously with each other), and the army itself is generally unaffected by personal changes in the command staff: it is not bound to its generals, but the generals are bound to the army.
+
+A *team* can also have no controllers.
+This usually happens when somebody disconnects, but you can have teams that are uncontrolled by design (for example to have a perspective change in a singleplayer mission, or to have rescuable units).
+
+### Regular player vs spectator
+
+Spectators are *players* not controlling any *team*.
+They have two modes of spectating, one is a full-view mode where they can see everything, and the other is to spectate as if they were on a particular *team* (meaning their UI will display that team's resources, they will only see what that team sees, etc).
+Spectators can change what team they are spectating, and to and from the full-view mode, at will.
+The engine also has an option to allow new players to join mid-game (as opposed to regular players reconnecting); such added people always end up as spectators (in particular, this means they will have seen the full state of the game while simulating).
+
+### Player vs AI
+
+An AI fulfils a similar role to a *player*, being there to control a *team*.
+One difference is that an AI is permanently bound to a *team* and cannot spectate.
+The API to deal with AIs is also separate to *players* (so, for example, the function to get all *players* will not return them, and a *team* may look uncontrolled if care is not taken to handle both of its *player* and AI controllers).
+There are two main types of AI: Lua AI and Skirmish AI.
+
+### Lua AI vs Skirmish AI
+
+A "skirmish" AI is hosted by one of the players and generally acts very similar to a *player*.
+It can read the game state via AI interface and works by giving units commands.
+Strictly speaking, it is their hosting player relaying commands - this means that this type of AI is subject to lag and will drop if the hosting player quits.
+On the other hand, only the host player is taking on the burden of simulating the AI.
+There are currently Skirmish AI bindings for C and Java (though distributing the Java runtime environment for a Java skirmish AI is up to the game).
+A game does not need explicit support for this kind of AI (meaning for example, somebody can homebrew one), though it will likely want to handle distribution and infrastructure issues (for example to block homebrew AI).
+
+A Lua AI generally has two components: a piece of game mechanics, and the AI instance itself which is just a handle to tell game mechanics which teams are legal to control.
+Since game mechanics have full control over the game state, this type of AI can do things like spawn units on its own (for a sort of PvE experience) .
+It can also control teams that aren't explicitly marked for control by the LuaAI handle (for example a LuaAI can be made to automatically control AFK players' teams, or Gaia units - see below).
+This type of AI is written in Lua (like all game code), has to be included in the game itself, and the code runs for every player in the game.
+
+### Gaia
+
+Gaia is a special *team* that is always present, uncontrolled, and is always in its own *allyteam*.
+It is generally meant for ownership of map features such as trees and rocks, but it also can have resources and own units.
+This can be used for example for PvE enemies - note that game mechanics still have control an uncontrolled *team* so it is not necessary to have an explicit AI for it.
+At the moment there is only one Gaia.
diff --git a/articles/technicalities-of-starting-a-match.markdown b/articles/technicalities-of-starting-a-match.markdown
new file mode 100644
index 0000000000..02ed79cd00
--- /dev/null
+++ b/articles/technicalities-of-starting-a-match.markdown
@@ -0,0 +1,43 @@
+---
+layout: post
+title: Technicalities of starting a match
+parent: Articles
+permalink: articles/technicalities-of-starting-a-match
+author: sprunk
+---
+
+What does starting a game look like, from a technical point of view? What are the responsibilities of each component?
+
+### Engine
+
+The only thing that the engine needs to initiate a game is a _start script_: plaintext data containing the setup such as game, map, players, modoptions, etc. Alternatively, the engine can use match metadata (in particular, host IP) to connect to a match hosted elsewhere. Then it receives the startscript directly from the host, after connection.
+
+* in-engine Lua code can start a game via `Spring.Restart(string commandLineArgs, string startScript)`. This is available from LuaMenu (e.g. Chobby) but also regular ingame Lua instances.
+* external lobbies can run the engine and pass the startscript/metadata as standard input or command line args.
+* replays and save files generally work equivalently to a startscript (and contain the startscript for their game).
+* **practical tip**: during development it's convenient to have a `startscript.txt` file and drag-n-drop it to `spring.exe` (or equivalent). The engine leaves a `_script.txt` file containing the last used startscript so you can produce an intricate setup via a graphical lobby, or by watching a replay, and then reuse it.
+* **practical tip**: replays are associated with a specific engine version, so if you want to support watching old replays you'll need to handle it externally (it can be a simple script communicating with LuaMenu via a socket though, not necessarily a "full" lobby).
+
+Every other component's role is merely to facilitate the creation of startscripts and exchange of metadata.
+
+### Lobby client
+
+Either in-engine LuaMenu such as Chobby, or external programs.
+
+* for singleplayer, or multiplayer games where the player is the host, lobbies are responsible for generating the startscript based on game setup.
+* sometimes you already have a startscript somehow (e.g. maybe it comes from a mission archive, or you're just running a replay) and then the lobby is just responsible for passing it to the engine.
+* for multiplayer where the player is not a host, lobby clients handle receiving match metadata from the host, usually via a lobby server.
+
+### Autohost
+
+As far as the process of starting a game, autohost is just a stub lobby client.
+* it is practically always used as the host of a room on a lobbyserver, and thus needs to generate the startscript based on game setup.
+* **practical tip**: some lobby servers claim that a player "is the host" of a room where actually he is just the boss of a room where the actual host is a bot/autohost. In this case the autohost is responsible for generating the startscript. Don't confuse those.
+
+### Lobbyserver
+
+The lobbyserver is only responsible for transmitting metadata from the host to the other players so they can connect, but you can use any other alternative. For example one could use the Steam p2p direct connection for transmitting metadata to play co-op with a friend.
+
+### Startscript format
+
+See [here](https://github.com/beyond-all-reason/spring/blob/BAR105/doc/StartScriptFormat.txt).
diff --git a/articles/unit-defs.markdown b/articles/unit-defs.markdown
new file mode 100644
index 0000000000..c23d4248cc
--- /dev/null
+++ b/articles/unit-defs.markdown
@@ -0,0 +1,178 @@
+---
+layout: post
+title: Unit defs
+parent: Articles
+permalink: articles/unit-defs
+author: sprunk
+---
+
+# Unit types basics
+
+**Each unit in Spring/Recoil belongs to a single type**.
+By default, all units of a type are the same as far as most traits (for example health) go.
+Units don't necessarily need to fully conform to their type - **most traits can be changed per-unit**.
+
+This is **similar to other RTS engines**.
+For example, a default Starcraft2 marine has 45 health.
+Specific marines can actually have 55 health (after an upgrade), or 22 health (with 50% difficulty handicap), or 200 health (if they're the special campaign marine Jim Raynor).
+But marines, as a generalized type, have 45 health.
+It works **essentially the same way in Recoil**.
+Note that the **type itself cannot be modified at runtime**, though you can still easily apply a modified value to every unit of a type.
+
+The **set of unit types is static**.
+You **cannot dynamically add new types**, though you **can generate them beforehand**, including via code.
+
+The information about a unit type is usually called a **unit def** (from "definition"), and sometimes the type itself is referred to by "unit def" as well.
+This article will talk about and compare the two ways that unit defs are often dealt with that are often confused.
+
+As a general remark, the same notes apply not just to unit types, but also feature types and weapon types and can be applied there directly.
+
+## Unit def files
+
+At their simplest, games can provide **a set of simple Lua files with unit defs in the `./units` subfolder**, which **return a table** like this:
+```lua
+-- ./units/light_tank.lua
+return {
+ light_tank = {
+ health = 100,
+ maxVelocity = 1,
+ requiresSuperSecretResearch = false,
+ customParams = { flammable = 1, },
+ ...
+ },
+}
+```
+The above defines a unit def named "light_tank" with the appropriate stats.
+Note that each def **must have a unique name**.
+
+Note also the keys used inside: `health` and `maxVelocity` are standard and interpreted by the engine, you can find their meanings in the documentation.
+**The `customParams` table is somewhat special as it is itself standard but its contents are not**: anything inside (in this case `customParams.flammable`) needs to be handled by the game.
+Lastly, `requiresSuperSecretResearch` is non-standard.
+We will get back to the non-standard ones later.
+
+In the example above, the filename matches the single def inside and is basically just a static set of values.
+This **is generally the convention** for various reasons (for example it makes it easier for a mod to replace units one by one, and the files can get fairly long), but it **doesn't have to** be the case.
+In particular, you **can put multiple units there and have Lua code inside** to generate values, for example:
+```lua
+-- ./units/various_tanks.lua
+local base_tank_health = Shared.base_tank_health
+return {
+ light_tank = {
+ health = base_tank_health * 1,
+ maxVelocity = 1,
+ requiresSuperSecretResearch = false,
+ customParams = { flammable = 1, },
+ ...
+ },
+ heavy_tank = {
+ health = base_tank_health * 2.5,
+ maxVelocity = 0.3,
+ requiresSuperSecretResearch = true,
+ customParams = { flammable = 0, },
+ ...
+ }
+}
+```
+
+At some point you **might want to post-process** these values.
+For example you'd like to try out a sweeping design change, or maybe the match at hand is a custom scenario with different rules.
+For those cases, **default engine content lets you supply a file, `./gamedata/unitdefs_post.lua`**, which has access to a `UnitDefs` table with all the defs.
+For example, let's say you want to make all units slightly faster, but the healthiest ones more fragile using some arbitrary formula:
+```lua
+-- ./gamedata/unitdefs_post.lua
+for unitDefName, unitDef in pairs(UnitDefs) do
+ UnitDefs[unitDefName] = lowerkeys(unitDef)
+end
+
+for unitDefName, unitDef in pairs(UnitDefs) do
+ if unitDef.maxvelocity then
+ unitDef.maxvelocity = unitDef.maxvelocity * 1.2
+ end
+
+ if unitDef.health and unitDef.health > 300 then
+ unitDef.health = 300 + math.sqrt(unitDef.health - 300)
+ end
+
+ if unitDef.requiressupersecretresearch then
+ unitDef.customparams.tech_level_required = 4
+ end
+end
+```
+
+First, the `lowerkeys` function.
+Maybe some def files defined "maxVelocity" with an uppercase 'V' and some "maxvelocity" with a lowercase 'v', maybe even some used "MAXVELOCITY".
+In Lua, these are all different keys.
+Calling lowerkeys **makes sure that handling those in post-processing does not become a hassle**.
+Some games put the lowerkeys call inside the individual def files as a convention; you also **don't have to do this at all** if you pay attention and don't expect this to be a problem.
+
+Note that **there are checks** so that calculation only happens if the value is defined, **even for the standard ones** (i.e. `if unitDef.health and...`).
+This is because **while the engine does fill in defaults** (so you don't need to define velocity for buildings, for instance) **it does so at a later point**: so far everything is still **just a regular Lua table**.
+In particular, you can make use of the fact that an entry is not defined and/or fill it with some default calculated by you.
+
+Another thing going on here is the handling non-standard entries in the table.
+Anything which is not a standard key **is going to be discarded by the engine after this point unless it's inside the `customparams` table**.
+This can be useful if you just want to define a helper for post-processing - in particular this can even be something like a function.
+`customParams` let you keep non-standard values, but **only allows strings as keys, and strings and numbers as values**.
+
+There is also a **pre-processing file, `./gamedata/unitdefs_pre.lua`**, which can prepare data **before any def files are read**.
+It exposes **a global `Shared` table**, which can be populated there and which can be seen used in one of the examples above.
+Use it to propagate reference values to multiple defs without having to redefine them.
+
+## The `UnitDefs` table inside wupgets
+
+This is where things get somewhat messy. The engine exposes a table also called `UnitDefs` to wupgets.
+However, **this is NOT the same table as the one above**.
+The table above gets parsed into internal engine structures and is gone.
+The engine exposes a new table with those parsed values. This means that, compared to unit def files:
+
+ * the overall table is **indexed by numerical IDs, not the name**.
+So it's (say) `UnitDefs[123]` instead of `UnitDefs["light_tank"]`.
+Default engine content provides **a `UnitDefNames` table indexed by the name** though.
+The internal name is also **provided in the table under the key `"name"`**.
+The majority of **wupget interfaces use the numerical ID**.
+
+ * keys are **not the same**.
+For example, metal cost is read as `buildCostMetal` from unit def files, but exposed as `metalCost` in `UnitDefs`.
+In particular, they often **have "proper" uppercase even if the post-processing file is all lowercase**.
+This may well be **the most common misconception** and mistake when it comes to defs!
+Remember, **don't copy-paste keys between unit def files and wupgets** or vice versa.
+
+ * unused values are discarded.
+In the example above, `UnitDefs[x].requireSuperSecretTech` (and the lowercase spelling) is `nil`.
+
+ * **values are not the same**.
+For example, speed is read as elmos/frame from unit def files, but exposed as elmos/second.
+Some values have caps or offsets applied. See the documentation for specifics.
+
+## Advanced technicalities
+
+Here's some looser remarks around these topics.
+
+### Unit def files
+
+In truth, the engine **only directly reads one file for all def file processing, which is `./gamedata/defs.lua`**.
+This file is **provided in basecontent** and for all the various def types (unit, weapon, etc) it loads a pre-processing file, individual legacy def files (TDF, FBI and other Total Annihilation formats), the individual Lua def files under `./units/`, and then the post-processing file.
+The practical effect is that you can **customize the loading process somewhat** (load things in a different order, or from elsewhere than `./units/`, etc.) if you don't like the default one.
+
+A limited number of **wupget interfaces are available** during def loading.
+This includes **getters involving the match** in general (map, player roster etc), of which most importantly **mod-options**.
+You can use them to customise a match (for example, maybe aircraft fly higher on maps with lava or something like that).
+
+VFS is also available, **as a game dev you can expose interfaces for modders** or mappers by attempting to load specific files.
+
+### UnitDefs
+
+A unitDef is a proxy table with the `__index` meta-method.
+**According to measurements** this makes it somewhat **slower than a plain Lua table**, so it might be worth **caching if a wupget mostly uses a single field** from it.
+
+There is a **defs-editing dev mode where you can edit defs**, toggled via `/editdefs` (requires cheats).
+In this mode, changes are done by just **assigning to a unitDef in Lua code**, which **isn't normally possible**.
+Keep in mind that there is **no standard widget** yet to allow easy editing, and that **editing the def files will do nothing**
+(of course unless you make your editing widget read them, but remember the caveat where the keys and values differ between
+unit defs and `UnitDefs`). This mode is **not usable for game logic** and will desync if used in multiplayer.
+
+There's three **minor differences between `WeaponDefs` and `UnitDefs`/`FeatureDefs`**:
+ * `WeaponDefs` are 0-indexed while the others are 1-indexed. Beware of `for i = 1, #WeaponDefs do`, this is incorrect!
+ * **negative weaponDefIDs are valid and mean things like lava or collisions**. Check the `Game.envDamageTypes` table.
+ * it is possible to iterate over all keys of a unitDef via `for key, value in unitDef:pairs() do`, but this is currently not possible for either weapon or feature defs.
+
diff --git a/articles/units-of-measurement.markdown b/articles/units-of-measurement.markdown
new file mode 100644
index 0000000000..5d8eea376c
--- /dev/null
+++ b/articles/units-of-measurement.markdown
@@ -0,0 +1,55 @@
+---
+layout: post
+title: Units of measurement
+parent: Articles
+permalink: articles/units-of-measurement
+author: sprunk
+---
+
+## Units of measurement
+
+In addition to standard units of measurement such as seconds or radians, Recoil uses some in-house units that warrant a bit of explanation.
+
+### Base
+
+ * **frames**. These represent the discrete, fundamental unit of time for simulation purposes.
+Everything in a simulation happens between two frames.
+Currently, a simulation at x1 speed runs frames at a constant 30 Hz (so a frame is 0.033s).
+On the other hand, it can run slower or faster depending on gamespeed (including at thousands Hz when catching up or skipping over a replay).
+Therefore, do not use frames to measure time for things that should use wall-clock time (for example interface animations), i.e. use `widget:Update(dt)` over `widget:GameFrame(f)`.
+The base frequency value in Hz is available to Lua as `Game.gameSpeed`.
+It is currently hardcoded by the engine and not configurable by games (despite being in the `Game` table and not `Engine`).
+
+* **elmos**. "Elmo" is the name for Recoil's arbitrary unit of distance. It is purposefully underdefined so that each game can have its own sense of scale.
+For example, maybe one game is some sort of galactic war and 1 elmo represents 1 parsec, while another game is about a war between bacteria and viruses and 1 elmo is 1 μm.
+Most existing content seems to assume it's 1 m or 12.5 cm, or within this order of magnitude, but there is nothing to enforce consistency.
+Almost all length/distance values are given in elmos (or derived values such as elmo/s for speed), unless otherwise noted.
+
+* **arbitrary**: many values, such as mass, are also in arbitrary units that a game could define on its own if it wanted to for world-building reasons.
+Unlike the elmo these aren't even named, so they're typically referred to as just, for example, "100 mass", "100 energy", or "100 map hardness".
+
+### Derived
+
+* **game square**. The map is divided into a grid of squares.
+The high-resolution yardmap grid is in squares, as is the heightmap (height is an interpolation between the corners of a square).
+The length of the edge of a game square in elmo is available as `Game.squareSize` to Lua.
+It is not configurable by games and is currently hard-coded to 8 (so a 1x1 square is 8x8 elmos).
+Note that for map creation, the supplied heightmap represents corners of squares (which is why its size has to be N/8 + 1).
+* **footprint square**. Footprints in unit defs are defined in squares of game squares (for historical reasons).
+Regular-resolution yardmap is defined per footprint square.
+The length of the edge of the footprint square in game squares is available to Lua as `Game.footprintScale` and is currently hard-coded to 2 (so a 1x1 footprint is 2x2 game squares).
+* **build square**. The grid to which construction of buildings is aligned.
+Similarly to footprints, it's in multiples of a regular game square.
+The length of the edge of a build square in elmos is available as `Game.buildSquareSize` to Lua and is currently hard-coded to 16 (which is the same as a footprint square, meaning that except for high-resolution yardmaps you can always fit buildings next to each other tightly).
+* **metalmap square**. The metal-map is divided into a grid which covers multiple game squares.
+Interfaces such as `Spring.GetMetalAmount` accept the co-ordinates in this unit.
+It is available to Lua as `Game.metalMapSquareSize` and its value is currently 16.
+Note that for map creation, the supplied metalmap represents the insides of metalmap squares (which is why its size has to be N/16).
+* **lobby map size**. This is the size of maps typically shown in lobbies and other such places.
+A 1x1 map is 512x512 elmos.
+This is merely a convention (though a strong one for historical reasons) and thus is not directly available to Lua (you can derive it via `Game.mapSizeX / Game.mapX` if you have a map loaded, though there isn't much point because `Game.mapX` is already about the only useful value in this unit).
+* **slow update**. Some performance-heavy things only happen to units once per slow-update.
+A slow-update happens once per 15 frames.
+It is a bit of an implementation detail, so it's not directly exposed, though some def entries related to allowing things to run more often are usually capped at the slow-update rate.
+* **TA angular unit**. A full circle is 65536 TA angular units.
+Used in some unit and weapon def entries - consult defs documentation for specifics.
diff --git a/articles/vfs-basics.markdown b/articles/vfs-basics.markdown
new file mode 100644
index 0000000000..78b09a059e
--- /dev/null
+++ b/articles/vfs-basics.markdown
@@ -0,0 +1,82 @@
+---
+layout: post
+title: VFS Basics
+parent: Articles
+permalink: articles/vfs-basics
+author: sprunk
+---
+
+## VFS basics
+
+### What is loaded?
+
+Recoil Engine **loads content from three main places** into its Virtual Filesystem (VFS): the game, the map, and the user's local files.
+The game and the map **are specified by the lobby** when launching an instance of Recoil.
+**Game content is primary** and it usually decides whether to even load content from the other two, with some exceptions (for example map layout is always taken from the map archive).
+
+### Archives
+
+Games and maps **typically come as archives** (a single compressed file).
+For development purposes, they can also be **regular folders as long as their name ends with ".SDD"**, though this is **not recommended for production** (for performance reasons).
+Archives **can specify dependency** upon other archives, so **if you want to build upon an existing game you don't need to copy-paste its whole contents** (and probably shouldn't).
+By default, games live in the `./games/` subfolder of your Recoil folder and maps live in `./maps/`, but a **lobby can specify arbitrary archives**.
+
+{: .warning }
+> At the moment, **files in dependencies are completely overridden** in the VFS and are not accessible.
+
+The user can also **specify local content folders** which are then loaded from, **if the game allows** that.
+By default, the Recoil folder is the read directory.
+In this case, there is usually **no archive - loose files are seen by the VFS**.
+Of course **an archive can be such loose file** and there are interfaces to load its contents.
+
+### Sync
+
+The **game and map are synced**, meaning their contents can be used as **authoritative data related to the simulation**.
+Local files are unsynced, so they **cannot even be accessed from synced contexts**.
+For example, the game can define that a tank has 100 health.
+The game **can give the map an opportunity to modify this**, so for example the map can say the tank has 200 health instead.
+But there is no way for local files to modify that further, unit health is part of the game mechanics simulation so the game cannot defer to local files here even if it wants to.
+What this means is that **local files are largely for local content like the UI**, and it is generally **safe to assume the VFS is under a game dev's control even if you don't pay attention**, as far as mechanics are concerned.
+
+### How do I defer to the other content?
+
+VFS interfaces tend to **expect a _mode_ parameter**, which lets you specify **where to look for and in what order**.
+The values are strings so **you can combine them** using the `..` operator, and they are read left-to-right.
+For example, if you want to include a file from the map but also have a game-side backup, you'd pass `VFS.MAP..VFS.GAME` to `VFS.Include`.
+Since `VFS.RAW` was not passed, any existing loose file with the appropriate name among the user's local files is ignored.
+See below for a listing of modes accessible to Lua.
+
+By paying attention to the VFS mode, you can **prevent loading unwanted content**.
+As mentioned above, requesting unsynced modes in synced contexts is also ignored.
+
+### Loose remarks
+
+There is a fourth place where content is loaded from, the **basecontent** archive.
+The engine **always loads** it and it contains various **bare necessities** for a functional game, such as default water texture or the basic wupget frameworks.
+Its content is **entirely optional** and can be avoided via the usual VFS mode interface, though even mature games will usually want to make use of its facilities.
+
+The engine **can also write files** in addition to reading them.
+Unlike multiple read-folders, there is **only a single write-folder** (defaults to the Recoil folder).
+Writing is done by general Lua interfaces such as `io` and `os`, not `VFS`.
+
+A somewhat unorthodox way to pass (synced) content is **via modoptions**.
+Modoptions can **contain data** that gameside code can act upon, and if you're brave enough you can even **pass Lua code** as a modoption to be excuted.
+This is one of the ways to let people **run their local files in a synced way**, by just forwarding them as modoptions.
+
+## VFS mode listing
+Here are the fundamental modes:
+
+* `VFS.GAME` - anything included by either a game OR its dependencies, as long as they are not basecontent. There is no way to tell where a file comes from, but dependencies override!
+* `VFS.MAP` - the map archive.
+* `VFS.BASE` - loaded basecontent archives. Some of those are always implicit dependencies. Any dependency with the appropriate archive type fits though.
+* `VFS.MENU` - the loaded menu (lobby) archive, i.e. in practice Chobby.
+* `VFS.RAW` - anything not in an archive, i.e. any loose files the client may have in his data folder, or even anywhere on the filesystem. Only unsynced content can access these.
+
+And here are the convenience/legacy ones:
+
+* `VFS.ZIP` = `VFS.GAME .. VFS.MAP .. VFS.BASE`. Synced content can only use subsets of this. Note that this doesn't mean actual `.zip` (aka `.sdz`) archives, `.sdd` and `.sd7` still apply.
+* `VFS.ZIP_FIRST` = `VFS.ZIP .. VFS.RAW`.
+* `VFS.RAW_FIRST` = `VFS.RAW .. VFS.ZIP`.
+* `VFS.MOD` = `VFS.GAME`. This is NOT for mods, they are indistinguishable from the main game from the VFS' point of view! It's just a legacy synonym.
+* `VFS.RAW_ONLY` = `VFS.RAW`.
+* `VFS.ZIP_ONLY` = `VFS.ZIP`.
diff --git a/articles/wupget-best-practices.markdown b/articles/wupget-best-practices.markdown
new file mode 100644
index 0000000000..2f4f55e38d
--- /dev/null
+++ b/articles/wupget-best-practices.markdown
@@ -0,0 +1,35 @@
+---
+layout: post
+title: Wupget best practices
+parent: Articles
+permalink: articles/wupget-best-practices
+author: sprunk
+---
+
+
+* `Spring.Echo` and similar debugging functions accept multiple args. When echoing variables, use `,` instead of `..` since more things can be printed standalone than natively glued together:
+
+```diff
+- Spring.Echo("foo is " .. foo) -- breaks when it is nil, table, function...
++ Spring.Echo("foo", foo) -- prints e.g. "foo, "
+```
+* for behaviours, use customparams instead of hardcoding unit def names in gadgets.
+* when hardcoding unit types for non-behaviour purposes (list of things to spawn in a mission etc.), use def names instead of numerical def IDs. Numerical ID can change between matches.
+* localize `UnitDefs` (and similar) accesses if you do it in an event that happens often, and you only want a limited number of traits.
+Avoid:
+
+```lua
+function wupget:UnitDamaged(unitID, unitDefID, ...) -- common event
+ if UnitDefs[unitDefID].health > 123 then
+```
+
+Prefer instead:
+```lua
+local healths = {}
+for unitDefID, unitDefID in pairs(UnitDefs) do
+ healths[unitDefID] = unitDef.health
+end
+
+function wupget:UnitDamaged(unitID, unitDefID, ...)
+ if healths[unitDefID] > 123 then
+```
diff --git a/articles/wupget-communication.markdown b/articles/wupget-communication.markdown
new file mode 100644
index 0000000000..709c2ea43d
--- /dev/null
+++ b/articles/wupget-communication.markdown
@@ -0,0 +1,88 @@
+---
+layout: post
+title: Wupget communication
+parent: Articles
+permalink: articles/wupget-communication
+author: sprunk
+---
+
+There are multiple Lua environments (see [old wiki](https://springrts.com/wiki/Lua:Environments)), and within an environment each wupget is also separated to some degree. How do these communicate?
+
+### WG and GG
+
+* the basic way to communicate - just tables putting globals inside, visible from wupgets in given environment.
+* are just regular tables exposed by the wupget handlers by convention.
+* in LuaUI it's called `WG` (widget global), elsewhere it's `GG` (gadget global)
+* keep in mind that the `GG` in unsynced LuaRules, the `GG` in synced LuaRules, and the `GG` in synced LuaGaia are all separate tables despite the same name! For communication between environments you'll have to use one of the methods below.
+
+### Rules params
+
+* synced LuaRules can set global key/value pairs as well as attach them to units, teams, players; all other environments can then read them
+* the standard way to expose new gameplay-relevant traits.
+* see also the article.
+* examples: "unit is on fire", "unit is slowed by 30%", "team 2 is cursed", "next night falls at 6:00", "there are 7 mex spots on the map at coordinates ..." .
+
+### `Script.LuaXYZ` events and "proper" `_G` globals
+
+* `_G` is the "real" global table of each environment (as opposed to `GG` and `WG` which are only "logically" global),
+i.e. it is the one accessible directly by the engine to be called from the outside of that environment (in particular, it is still per-environment)
+* wupget handlers also expose `wupgetHandler:RegisterGlobal('EventName', func)` which is essentially `_G.EventName = func` with some basic safety on top
+* you can then do `Script.LuaXYZ.EventName(args)` to call the given function
+* the two LuaRules environments can call `Script.LuaRules` to refer to themselves (but not each other!) and `Script.LuaGaia` (also only of the same syncedness), while unsynced LuaRules/LuaGaia and LuaUI can call `Script.LuaUI`
+* this only works if the global in question is a function, and you won't receive any return values
+* you could put "normal" vars there, but it is discouraged (use `GG`/`WG`)
+* the main use case for this is calling events "native style" where you do `Script.LuaXYZ.UnitDamaged` which is then handled by the wupget handler and distributed to individual wupgets
+* it is safe to call functions this way even if there is nothing on the other side.
+You can use the function notation to check whether anything is linked though, e.g. for optimisation:
+```lua
+if Script.LuaXYZ("Bla") then
+ local args = ExpensiveCalculation()
+ Script.LuaXYZ.Bla(args)
+end
+```
+* other examples of events you could add: nuclear launch detected, language changed in settings, metal spot added at runtime.
+
+### `SendToUnsynced` and `RecvFromSynced`
+* synced gadgets can pass data to unsynced gadgets by calling `SendToUnsynced(args)`
+* unsynced gadgets receive it via `function gadget:RecvFromSynced(args)`
+* usually the first arg is a magic value that lets the correct gadget consume the message
+* in unsynced, you can do `gadgetHandler:AddSyncAction("foobar", func)` which is generally equivalent to
+```lua
+function gadget:RecvFromSynced(magic, args)
+ if magic == "foobar" then
+ func(magic, args)
+ end
+end
+```
+* laundering via unsynced LuaRules is the way to call `Script.LuaUI` style events from synced LuaRules.
+
+### `Spring.SendLuaRulesMsg` and `gadget:RecvLuaMsg`
+* LuaUI can send messages to LuaRules
+* similar to `SendToUnsynced` in use
+* there's also `Spring.SendLuaGaiaMsg`
+* use to make UI to interact with mechanics where unit orders won't suffice
+
+### `Spring.SendLuaUIMsg` and `widget:RecvLuaMsg`
+* use these one to communicate with other players' widgets
+* lets you send to everyone, only allies, or only spectators
+* use for things like broadcasting cursor or selection, or UI interaction like custom markers
+
+### `SYNCED` proxy
+* synced LuaRules' global table (`_G`) can also be accessed from unsynced LuaRules
+* synced `_G.foo` can be accessed from unsynced as `SYNCED.foo`
+* note that this makes a copy on each access, so is very slow and will not reflect changes. Cache it, but remember to refresh
+* it only copies basic types (tables, strings, numbers, bools). No functions, metatables and whatnot
+* generally not recommended. Listen to the same events as synced and build the table in parallel
+
+### With the outside, unsynced
+* there's `/luaui foo` for a player to pass data to LuaUI.
+* LuaUI and LuaMenu have a socket API to communicate with the outside.
+
+### With the outside, synced
+* arbitrary setup data can be passed in the startscript as modoptions and player/team/AI custom keys. This would be done by the lobby or autohost.
+* players, incl. the autohost, can use `/luarules foo` commands to send arbitrary data to the synced state. Lua sees the autohost as playerID 255.
+* if the autohost uses [a dedicated instance (as opposed to headless)]({{ site.baseurl }}{% link guides/headless-and-dedi.markdown %}), players can send messages back to the autohost via whispers (`/wByNum 255 foo`) for non-sensitive data.
+
+### Others
+* a game can designate some other regular table for globals. For example many games put useful functions in `Spring.Utilities` which is not actually a native part of the `Spring` table.
+* two environments could include the same file to get the same data (in contents but not identity). LuaUI and LuaMenu can also read arbitrary files (outside the VFS).
diff --git a/assets/css/just-the-docs-themes.scss b/assets/css/just-the-docs-themes.scss
new file mode 100644
index 0000000000..b6fd209f28
--- /dev/null
+++ b/assets/css/just-the-docs-themes.scss
@@ -0,0 +1,83 @@
+---
+---
+
+{% if site.logo %}
+$logo: "{{ site.logo | relative_url }}";
+{% endif %}
+@import "./support/support";
+@import "./custom/setup";
+
+@import "./color_schemes/light";
+@import "./modules";
+{% include css/callouts.scss.liquid color_scheme = "light" %}
+
+html[data-theme="dark"] {
+@import "./color_schemes/dark";
+@import "./modules-nocharset";
+{% include css/callouts.scss.liquid color_scheme = "dark" %}
+}
+
+// fix for search using class in html tag
+html[data-theme="dark"].search-active {
+ .search {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ }
+
+ .search-input-wrap {
+ height: $sp-10;
+ border-radius: 0;
+
+ @include mq(md) {
+ width: $search-results-width;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+ }
+ }
+
+ .search-input {
+ background-color: $grey-dk-250;
+
+ @include mq(md) {
+ padding-left: 2.3rem;
+ }
+ }
+
+ .search-label {
+ @include mq(md) {
+ padding-left: 0.6rem;
+ }
+ }
+
+ .search-results {
+ display: block;
+ }
+
+ .search-overlay {
+ width: 100%;
+ height: 100%;
+ opacity: 1;
+ transition: opacity ease $transition-duration, width 0s, height 0s;
+ }
+
+ @include mq(md) {
+ .main {
+ position: fixed;
+ right: 0;
+ left: 0;
+ }
+ }
+
+ .main-header {
+ padding-top: $sp-10;
+
+ @include mq(md) {
+ padding-top: 0;
+ }
+ }
+}
+
+{% include css/custom.scss.liquid %}
diff --git a/assets/github-mark-white.svg b/assets/github-mark-white.svg
new file mode 100644
index 0000000000..d5e6491854
--- /dev/null
+++ b/assets/github-mark-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/github-mark.svg b/assets/github-mark.svg
new file mode 100644
index 0000000000..37fa923df3
--- /dev/null
+++ b/assets/github-mark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/guides/lua-vbo-vao-1.png b/assets/guides/lua-vbo-vao-1.png
new file mode 100644
index 0000000000..981aece9cb
Binary files /dev/null and b/assets/guides/lua-vbo-vao-1.png differ
diff --git a/assets/guides/lua-vbo-vao-2.png b/assets/guides/lua-vbo-vao-2.png
new file mode 100644
index 0000000000..f57b111c67
Binary files /dev/null and b/assets/guides/lua-vbo-vao-2.png differ
diff --git a/assets/guides/lua-vbo-vao-3.png b/assets/guides/lua-vbo-vao-3.png
new file mode 100644
index 0000000000..10933e8475
Binary files /dev/null and b/assets/guides/lua-vbo-vao-3.png differ
diff --git a/assets/guides/lua-vbo-vao-4.png b/assets/guides/lua-vbo-vao-4.png
new file mode 100644
index 0000000000..b4d2b6d914
Binary files /dev/null and b/assets/guides/lua-vbo-vao-4.png differ
diff --git a/assets/js/theme-switcher.js b/assets/js/theme-switcher.js
new file mode 100644
index 0000000000..88354f197d
--- /dev/null
+++ b/assets/js/theme-switcher.js
@@ -0,0 +1,58 @@
+const storageKey = 'theme-preference'
+
+const onClick = () => {
+ // flip current value
+ theme.value = theme.value === 'light'
+ ? 'dark'
+ : 'light'
+
+ setPreference()
+}
+
+const getColorPreference = () => {
+ if (localStorage.getItem(storageKey))
+ return localStorage.getItem(storageKey)
+ else
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
+ ? 'dark'
+ : 'light'
+}
+
+const setPreference = () => {
+ localStorage.setItem(storageKey, theme.value)
+ reflectPreference()
+}
+
+const reflectPreference = () => {
+ document.firstElementChild
+ .setAttribute('data-theme', theme.value)
+
+ document
+ .querySelector('#theme-toggle')
+ ?.setAttribute('aria-label', theme.value)
+}
+
+const theme = {
+ value: getColorPreference(),
+}
+
+// set early so no page flashes / CSS is made aware
+reflectPreference()
+
+window.onload = () => {
+ // set on load so screen readers can see latest value on the button
+ reflectPreference()
+
+ // now this script can find and listen for clicks on the control
+ document
+ .querySelector('#theme-toggle')
+ .addEventListener('click', onClick)
+}
+
+// sync with system changes
+window
+ .matchMedia('(prefers-color-scheme: dark)')
+ .addEventListener('change', ({matches:isDark}) => {
+ theme.value = isDark ? 'dark' : 'light'
+ setPreference()
+ })
diff --git a/changelogs.markdown b/changelogs.markdown
new file mode 100644
index 0000000000..467a670b25
--- /dev/null
+++ b/changelogs.markdown
@@ -0,0 +1,10 @@
+---
+layout: default
+title: Changelogs
+nav_order: 6
+has_children: true
+permalink: changelogs
+---
+
+# Changelogs
+{: .no_toc }
diff --git a/changelogs/changelog-105-1214.markdown b/changelogs/changelog-105-1214.markdown
new file mode 100644
index 0000000000..f7a3e5ee20
--- /dev/null
+++ b/changelogs/changelog-105-1214.markdown
@@ -0,0 +1,88 @@
+---
+layout: post
+title: Release 105-1214
+parent: Changelogs
+permalink: changelogs/changelog-105-1214
+author: sprunk
+---
+
+The changelog since release 105-966 **until release 105-1214**, which happened in October 2022.
+
+## Caveats
+* there is now only one `UnitUnitCollision` event for each unit pair, and return values from UnitUnitCollision are now ignored!
+* `vsync` springsettng now defaults to -1
+* ships now obey `upright = false` when pulled out of water (still always upright when floating).
+* killed most of ARB shader fallbacks
+
+## Features and fixes
+
+### Unit collisions
+* multi-threaded unit steering and collisions.
+* there is now only one `UnitUnitCollision` event for each unit pair, and return values from UnitUnitCollision are now ignored!
+* added modrule, `movement.groundUnitCollisionAvoidanceUpdateRate`, for controlling steering performance vs quality tradeoff load. Reduce to get better quality at the cost of perf. Default value is 3 for cycling through all units over 3 sim frames.
+* added modrule, `movement.unitQuadPositionUpdateRate`. Similar perf tradeoff to the above, but affects collision accuracy (incl. with projectiles). In particular, if this value isn't 1 then sometimes beamlasers/lightning can visibly ghost through units. Defaults to 3.
+* added modrule, `movement.maxCollisionPushMultiplier`, limits the speed (as a multiplier relative to their base speed) units can push each other at. Defaults to infinity/unlimited.
+
+### Particles
+* add animated "flipbook" CEGs: `animParams = "numX, numY, animLength"`. Enabled for BitmapMuzzleFlame, HeatCloudProjectile, SimpleParticleSystem and GroundFlash.
+* some semi-transparent objects can cast colored shadows
+* fixed smoke-trails with a non-standard smoke period
+* `/dumpatlas` for now only works with `proj` argument to dump projectiles atlases to disk
+
+### Units on slopes
+* ships now obey `upright = false` when pulled out of water (still always upright when floating).
+* added `upDirSmoothing` to unit def so units could update their upward vector smoother than by default
+* floating mobiles can be built over seafloor slopes
+
+### Bugger off!
+* add `Spring.SetFactoryBuggerOff(unitID, bool? active, number? distanceFromFactoryCenter, number? radius, number? relativeHeading, bool? spherical, bool? forced) → bool active`. Forced means it also asks push-resistant units to move. Everything is optional.
+* add `Spring.GetFactoryBuggerOff(unitID) → active, distance, radius, relativeHeading, spherical, forced`, same units as above.
+* add `Spring.BuggerOff(x, y = groundHeight, z, radius, teamID, spherical = true, forced = true, excludeUnitID = nil, excludeUnitDefIDArray = nil) → nil`, for standalone bugger-off in-world (like happens when units stand on a construction site).
+* `/debugcolvol` now shows factory bugger off, in cyan. Standalone bugger off not shown.
+
+### Key press scan codes
+* add `Spring.GetPressedScans() → {[scanCode] = true, [scanName] = true, ...}`, similar to `Spring.GetPressedKeys` but with scancodes
+* add `Spring.GetKeyFromScanSymbol(scanSymbol) → string keyName` that receives a scancode and returns the user's correspondent key in the current keyboard layout
+* add `wupget:KeyMapChanged() → nil` callin for when the user switches keyboard layouts, for example switching input method language.
+
+### Selection box
+* add `Spring.SetBoxSelectionByEngine(bool) → nil`. If set to false, engine won't apply selection when you release the selection box.
+* add `Spring.GetSelectionBox() → minX, maxY, maxX, minY` (sic) returning the screen coordinates of the selection box. Nil if not dragging a box.
+
+### Rendering internals
+* lots of engine internals (in particular, fonts) use modern approaches to rendering. Expect speed-ups.
+* applied more robust algorithm of culling shadow projection matrix. The net result is that shadows should not disappear any longer on small maps, at oblique camera angles, etc.
+* allow skipping the use of Texture Memory Pool to save ~512 MB (by default) of statically allocated memory. Note this might be detrimental due to possible memory fragmentation. See `TextureMemPoolSize` springsettings
+* removed most of the sanity checks from window positioning code (reverted back to 105.0 like code). Now the engine won't resist to resizing the window as the user/widget wants. There are still some rules in place, but they shouldn't be too restrictive.
+* FontConfig should be more robust upon failure and don't crash that often on FC errors
+* fix multiple camera issues.
+* fix the hardware cursor on Linux/Wayland.
+* fixed incorrect cubemap texture mapping
+
+### Rendering API
+* add `DrawGroundDeferred` callin such that bound g-buffers could be updated from say game side decals renderer.
+* projectiles transformation matrices are now available through LuaVBO
+
+Added the following `GL.` constants:
+* `TEXTURE_{1,2,3}D`
+* `TEXTURE_CUBE_MAP`
+* `TEXTURE_2D_MULTISAMPLE`
+* `COLOR_ATTACHMENT{0...15}{,_EXT}`
+* `DEPTH_ATTACHMENT{,_EXT}`
+* `STENCIL_ATTACHMENT{,_EXT}`
+
+### Miscellaneous
+* add `Spring.GetSyncedGCInfo(bool forceCollectionBeforeReturning = false) → number usage in KiB`. Use from unsynced to query the status of synced memory occupation and force garbage collection
+* introduced `RapidTagResolutionOrder` option to control priority of domains when resolving rapid tags in case multiple Rapid mirrors are used at the same time.
+* `/dumprng` switch to tap out the state of synced RNG, works similar to `/dumpstate`
+* skirmish AI interface: add FeatureDef_isAutoreclaimable
+* fix the 1 second pause/freeze players experience when a player or spectator disconnects from the match.
+* fix SaveLoad hang in case when AllyTeams > 2 and one or more teams died.
+* fix SaveLoad memory leaks.
+
+## pr-downloader
+* add options to override rapid and springfiles url.
+* add option to download from Rapid without using `streamer.cgi`, but files directly. See https://github.com/beyond-all-reason/pr-downloader/pull/9
+* improve file IO performance by multithreading it (big performance boost on Windows)
+* fix handling of unicode paths on Windows
+* fix finding and loading of SSL certificates on multiple Linux distributions (on Windows also switches to system certificate storage)
diff --git a/changelogs/changelog-105-1354.markdown b/changelogs/changelog-105-1354.markdown
new file mode 100644
index 0000000000..4524f2f50b
--- /dev/null
+++ b/changelogs/changelog-105-1354.markdown
@@ -0,0 +1,54 @@
+---
+layout: post
+title: Release 105-1354
+parent: Changelogs
+permalink: changelogs/changelog-105-1354
+author: sprunk
+---
+
+The changelog since release 105-1214 **until release 105-1354**, which happened in November 2022.
+
+## Caveats
+* Ctrl and Alt as camera movement modifiers were unhardcoded and turned into bindings. The engine binds them same as before by default, but if you wipe bindings and handle them on your own then you may want to handle rebinding them.
+* `/dynamicSky` command and `AdvSky` springsetting were removed.
+* resurrection no longer sets health to 5%. See the "migrating from Spring" guide for a replacement. Note that the Lua function to do this 100% correctly only got added in a further release.
+* respect `gl.Color()` in `gl.Text()`. May require taking care that the appropriate colour is set, possibly via inline color codes.
+
+## Features and fixes
+
+### Minimap
+* new boolean springsetting `MiniMapCanFlip`, default 0. If enabled, flips minimap coordinates when camera rotation is between 90 and 270 degrees (with rotating cameras, or when flipping via `/viewtaflip` with the default non-rotating camera).
+* add `Spring.GetMiniMapRotation() → rot`, currently only 0, or π if flipped.
+* minimap now allows making labels when cursor is inside, transposed to map location
+* new boolean springsetting: `MiniMapCanDraw`, defaults to 0. Enable the other draw events (line/erase) to happen via minimap
+
+### Camera control
+* add `Spring.GetCameraRotation() → rotX, rotY, rotZ`. Also useful for minimap flipping detection.
+* Ctrl and Alt as camera movement modifiers were unhardcoded and turned into bindings. The engine binds them same as before by default, but if you wipe bindings and handle them on your own then you may want to handle rebinding them.
+* add `movetilt`, `movereset` and `moverotate` actions for previously hardcoded behavior, engine defaults to `Any+ctrl` and `Any+alt` respectively (i.e. previous behaviour).
+* make the `InvertMouse` springsetting respected when inverting the "hold middle mouse button to pan camera" functionality
+ * add configurability of movefast and moveslow speed via added config values (`CameraMoveFastMult`, `CameraMoveSlowMult`) and scaling factors for specific cameras (`CamSpringFastScaleMouseMove`, `CamSpringFastScaleMousewheelMove`, `CamOverheadFastScale`)
+* fix `movefast` and `moveslow` actions not being respected in some circumstances.
+
+### Keys
+* accept `keyreload` and `keyload` commands in uikeys
+* add `/keydefaults` command, loads the defaults
+* accept filename arguments for `keyload`, `keyreload` and `keysave`
+
+### Skybox
+* allow loading skybox from equirectangular 2D texture
+* deprecate `AdvSky` springsetting
+* remove the obsolete `/dynamicsky` option
+
+### Miscellaneous
+* add `Platform.cpuLogicalCores` and `Platform.cpuPhysicalCores` constants
+* new action: `group unset`, removes any group assignment to selected units
+* add `Spring.GetTeamAllyTeamID(teamID) → allyTeamID`
+* add `Spring.GetProjectileAllyTeamID(projectileID) → allyTeamID`
+* add `Spring.SetWindow{Min,Max}imized() → bool success` (also in LuaIntro)
+* add `Spring.LoadModelTextures(string modelName) -> bool success` to preload model textures. Returns false if model not found or is 3do.
+* add `damage.debris` modrule for debris damage, default 50 (same as previous).
+* add `reclaim.unitDrainHealth` modrule, whether reclaim drains health. Mostly for the reverse wireframe method
+* add 5th and 6th params to `Spring.MarkerErasePosition`, onlyLocal and playerID. Makes the erase happen locally, optionally as if done by given player, same as the existing interface for MarkerAddPoint and MarkerAddLine. NOTE: 4th arg is currently unused (reserved for radius)!
+* fix functionality for `DualScreenMode` setting, it draws on the window area of the left or rightmost display depending on `DualScreenMiniMapOnLeft` setting. Fix engine related issues with view positioning and adds `DualScreenMiniMapAspectRatio` to draw minimap with preserved aspect ratio.
+* fixed a crash caused by kicking a player
diff --git a/changelogs/changelog-105-1544.markdown b/changelogs/changelog-105-1544.markdown
new file mode 100644
index 0000000000..56ad57e889
--- /dev/null
+++ b/changelogs/changelog-105-1544.markdown
@@ -0,0 +1,91 @@
+---
+layout: post
+title: Release 105-1544
+parent: Changelogs
+permalink: changelogs/changelog-105-1544
+author: sprunk
+---
+
+The changelog since release 105-1354 **until release 105-1544**, which happened in February 2023.
+
+## Caveats
+* rebranded the fork to Recoil. Everything where a rename would have technical consequences (`spring.exe` filename, `Spring.Foo` Lua API etc) has been kept as-is.
+* `Spring.GetConfig{Int,Float,String}` second parameter can now be `nil`, which means it will return `nil` if the requested setting is not set. Formerly returned `0`, `0`, or `""` respectively. Note that for settings used by the engine the value is never nil (so this is only for custom ones). To get back previous behaviour:
+```lua
+local originalGetConfigInt = Spring.GetConfigInt
+Spring.GetConfigInt = function (key, def)
+ return originalGetConfigInt(key, def) or 0
+end
+-- ditto for Float and String
+```
+
+## Features and fixes
+
+### General multi-threading improvements
+* main thread is now allowed to move between cores when hyperthreading is unavailable. This helps when a server is running multiple headless or opengl instances.
+* `LoadingMT` springsetting no longer accepts -1 for autodetect (which meant 0 for Intel/Mesa graphics drivers and 1 for others)
+* add `Spring.Yield() → bool hintShouldKeepCallingYield` to give back OpenGL context from game-loading thread back to main thread to process events and draw with LuaIntro. It's safe to call this regardless of LoadingMT. In practice this means it should be called from unsynced wupget initializers periodically as long as the returned hint value is true, the common implementation is to call it after a wupget is loaded like so:
+```lua
+local wantYield = true
+for wupget in wupgetsToLoad do
+ loadWupget(wupget)
+ wantYield = wantYield and Spring.Yield()
+end
+```
+
+### Multi-threading modrules
+Note from the future, these were removed after release 1775.
+* added `system.pfForceSingleThreaded` to force pathing requests to be sent single threaded.
+* added `system.pfForceUpdateSingleThreaded` to force pathing vertex updates to be processed single threaded.
+* added `movement.forceCollisionsSingleThreaded` to force collisions system to use only a single thread.
+* added `movement.forceCollisionAvoidanceSingleThreaded` to force collision avoidance system to use only a single thread.
+
+### Debugging improvements
+* added boolean springsetting `DumpGameStateOnDesync`. When set to true on the server, it will request all clients to produce a local copy of the current game state (effectively calling `/dumpstate`). Doesn't do anything if set client-side.
+* add `/debug reset` to reset the collected debug stats
+* added the `/desync` command for non-debug builds
+* fixed issue where desyncs could not be detected before the 300th sim frame (10 seconds)
+
+### Standalone model piece getters
+Unlike similar existing functions these don't require a unit/feature instance to exist.
+* `Spring.GetModelPieceMap(string modelName) → { body = 0, turret = 1, ... }`
+* `Spring.GetModelPieceList(string modelName) → { "body", "turret", ... }`
+
+### New selectkeys filters
+* `Buildoptions` - for units that have a non-zero number of build options (meaning constructors and factories)
+* `Resurrect` - for units that can resurrect
+* `Cloaked` - for units that are currently cloaked
+* `Cloak` - for units of a type that can cloak (regardless of current cloak state)
+* `Stealth` - for units of a type that is stealth by default (regardless of current stealth state)
+* (note that there is no filter for "currently stealthed" like there is for cloak)
+
+### Drawing related changes
+* add `wupget:DrawPreDecals()` callin
+* add `AlwaysSendDrawGroundEvents` boolean springsetting (default false) to always send `DrawGround{Pre,Post}{Forward,Deferred}` events, otherwise/previously they are skipped when non-Lua rendering pipeline was in use.
+* add `/drawSky` which toggles drawing of the sky (mostly for performance measurements now)
+* Maps with missing/invalid details texture no longer get tint with red. Improved reporting of missing map textures.
+* Shadows color buffer can be made greyscale with springsettings `ShadowColorMode = 0` or `/shadows` switches.
+* fix Bumpwater rendering on non-NVIDIA drivers
+
+### Miscellaneous features
+* add `Game.metalMapSquareSize` for use with API like `Spring.GetMetalAmount` etc
+* add mirrored looping of animated CEG sprites (animates backwards until it reaches the start and then bounces again), enabled by making the animation speed parameter negative (e.g. `animParams = "4,4,-30"`).
+Available for `BitmapMuzzleFlame`, `HeatCloudProjectile`, `SimpleParticleSystem` and `GroundFlash` CEG classes.
+* add `Spring.GiveOrderArrayToUnit`, accepts a single unitID and an order array and otherwise works the same as the existing `GiveOrderXYZ` family of functions
+
+### Miscellaneous fixes
+* fix `Spring.GetSelectionBox` not returning nil when there's an active mouse owner (activeReceiver)
+* fix multiple issues with movement and pathing quality and performance
+* fix a crash on loading a game with Circuit/Barbarian AI.
+* when scanning archives (at early loading), the window should no longer be considered frozen/unresponsive by the OS
+* `PreloadModels = 0` (non-default) should work correctly now, though it comes with its own set of compromises.
+
+## pr-downloader
+* implement concurrent fetching of multiple mods and maps with single tool invocation
+* replace potentially racy `If-Modified-Since` caching with ETags.
+* harden downloading rapid packages by saving sdp file only after full successful download
+* implement writing `md5sum` files for assets downloaded from springfiles to allow future full game
+files validation
+* drop lsl from the repo and separate RapidTools into separate repo
+* fix resolving of dependent rapid archives
+* fix compilation issues under MSVC making some parts of codebase more platform agnostic
diff --git a/changelogs/changelog-105-1775.markdown b/changelogs/changelog-105-1775.markdown
new file mode 100644
index 0000000000..3a790150fe
--- /dev/null
+++ b/changelogs/changelog-105-1775.markdown
@@ -0,0 +1,100 @@
+---
+layout: post
+title: Release 105-1775
+parent: Changelogs
+permalink: changelogs/changelog-105-1775
+author: sprunk
+---
+
+The changelog since release 105-1544 **until release 105-1775**, which happened in June 2023.
+
+## Caveats
+* `Spring.GetSelectedUnits{Sorted,Counts}` no longer returns the `n` inside the table, it's now the 2nd return value from the function
+* LuaUI can receive attacker info in `UnitDamaged` and similar, when appropriate. Set them to `nil` in the widget handler (or just don't pass them to widgets) if you don't want this behaviour.
+* remove the deprecated `Game.allowTeamColors` entry that was always `true`.
+* PNG is now the default `/screenshot` format.
+* new Recoil icons and loadscreens.
+* Due to the introduction of skinning/bones (see below), the maximum number of pieces per model is limited by 254 pieces
+* unit shader changes required related to skinning and bones. This requires updates of code that calls `vbo:ModelsVBO()` such that you'd replace:
+ ```
+ layout (location = 5) in uint pieceIndex;
+ ```
+ with
+ ```
+layout (location = 5) in uvec2 bonesInfo; // boneIDs, boneWeights
+#define pieceIndex (bonesInfo.x & 0x000000FFu)
+```
+
+## Features and fixes
+
+### Catching up performance
+* CPU usage reserved for drawing and minimum frames draw by second are now configurable via springsettings: `MinSimDrawBalance` which controls the minimum CPU fraction dedicated to drawing; more means more FPS but slower catch-up (defaults to 0.15) and `MinDrawFPS` which controls the minimum desired FPS below which it won't go (defaults to 2).
+* fixed how this drawing/simulating proportion interacts with the "slow the game for laggers" feature.
+
+### Modelling
+* added skinning and bones. Not supported for `.s3o` or `.3do`, supported by `.dae` (the other formats supported by the engine are fairly uncommon and untested, but those that can carry bone information should work as well). No extra work needed, bones created by an industry standard tool (for example Blender) should work out of the box.
+* shader change is needed, see above in the caveats section.
+* allow instanced rendering without instance VBO attached. `gl_InstanceID` can be used to index into an SSBO or used to produce instance attributes algorithmically.
+* bump model pool from 2560 to 3840 and log when it gets exhausted
+* bump up 3do atlas size (4k^2)
+* change default `MaxTextureAtlasSize{X,Y}` springsettings from 2048 to 4096.
+* fix 3do shattered pieces rendering (no texture)
+* fix AMD / Windows issues with invalid "under-construction" model rendering
+* fix invalid asset checksum calculations
+
+### Terrain rendering
+* add the `wupget:DrawShadowPassTransparent() → nil` and `wupget:DrawWaterPost() → nil` callins
+* infotexture shaders (F1, F2 etc) are more tolerant to potato drivers
+* improved terrain shadow quality and visibility checks.
+* add `/debugVisibility` for debugging the visible quadfield quads (for engine devs, mostly useless otherwise)
+* add `/debugShadowFrustum` to draw the shadow frustum on the minimap (ditto; also beware, doesn't exactly correspond to the actual camera frustum so probably doesn't have much use outside shadows specifically due to minor bugs)
+* fix the overview camera not being centered in 2nd and subsequent games if the terrain is different
+* fix bump water and terrain rendering on Intel
+
+### Selecting units
+ * add `InGroup_N` filter for selections, to select given control group. For example: `Visible+_InGroup_1+_ClearSelection_SelectAll`
+ * add `group select` and `group focus` group subactions. Note that the previous `bind X group N` is equivalent to `bind X group select N` + `bind X,X group focus N`. The old `group N` still works.
+
+### Unit defs: weapon
+ * add `weaponAimAdjustPriority` numerical tag to weapon (note, NOT weapon def! This is the table inside a unit def, where e.g. target categories are). This is a multiplier for the importance of picking targets in the direction the weapon is already aiming at. Defaults to 1.
+ * and `fastAutoRetargeting` boolean tag to weapon. Makes the unit acquire a new target if its current ones dies (the tradeoff is the perf cost, and a buff if applies to existing units). Defaults to false (target acquisition check every 0.5s).
+
+### Unit heading
+ * changed how unit rotation works. This should make cases like spiders pathing across near-vertical peaks preserve heading better
+ * add `Spring.Set{Unit,Feature}HeadingAndUpDir(thingID, heading, x, y, z) → nil`, note that the previously existing `Spring.SetUnitDirection` only receives one vector so does not allow setting an object's rotation unambiguously.
+
+### Pathing
+* TKPFS merged into the default HAPFS.
+* add a new modrule, `system.pathFinderUpdateRateScale` (default 1.0), to control pathfinder update rate. Note that it gets removed sometime after 1775.
+* the pathing overlay (F2) update rate increased x4.
+* engine line-move now tries to maintain relative unit position to some extent (so equivalent units don't cross each other).
+* fix various pathing quality issues and improve performance.
+
+### Miscellaneous API changes
+* allow shallow recursion in `Spring.TransferUnit` (16 levels deep, same as for other callouts)
+* add `Game.footprintScale`, the footprint multiplier compared to defs
+* add `Game.buildSquareSize`, the building alignment grid size
+* add `system.LuaAllocLimit` modrule to control global Lua alloc limit, default 1536 (in megabytes)
+* `Spring.GetPlayerInfo` returns a 12th value, boolean isDesynced. Keep in mind the 11th will be `nil` if you opt out of the customkeys table (i.e. the new 12th value does NOT get "pushed back" to fill the "missing" spot)
+* add `Spring.GetFeaturesInScreenRectangle(x1, y1, x2, z2) → {featureID, featureID, ...}`, similar to the existing one for units
+* `Spring.SetProjectileCEG` now accepts numerical cegID in addition to CEG name
+* add `Game.demoPlayName` to get the filename of the running replay.
+* add 7th boolean param to `Spring.MarkerErasePosition`, which makes the command erase any marker when `localOnly` and the current player is spectating. This allows spectators to erase players markers which otherwise they can't do outside of `/clearmapmarks`.
+* `Spring.SetActiveCommand(nil)` now cancels the command (previous method of `-1` still works)
+* add `StoreDefaultSettings` springsetting, default false. If enabled, settings equal to their defaults will still be saved. This means that `Spring.GetConfigInt` et al does not need to do a nil check for the defaults, but also the defaults won't get automatically updated if they change since they already have a concrete value.
+* reimplement Lua mem pools, controlled by the `UseLuaMemPools` springsettings.
+
+### Miscellaneous
+* added Tracy support for debugging.
+* default minimum sim speed changed from 0.3 to 0.1
+* add `--list-[un]synced-commands` flag to dump the command list when running the exe directly
+* fixed `/iconsAsUI 1` leaking information about dead ghosted buildings.
+* fixed piece projectiles (debris flying off from exploding units), and weapon projectiles with `collideFireBase` set to true and no other `collisionX` set to false, failing to collide with units.
+
+## pr-downloader
+* improved performance of verifying if there is an update needed: it now takes ~0.25s on all OSes
+* introduce custom X-Prd-Retry-Num to inform server at which retry are we when fetching files
+* fixed sdp downloading behavior when all files are already present
+* fixed progress bar in presence of redirects and when total download size is >2GiB
+* fixed `--disable-all-logging` to actually do that
+* fixed the handling of Unicode env vars on Windows
diff --git a/changelogs/changelog-105-2314.markdown b/changelogs/changelog-105-2314.markdown
new file mode 100644
index 0000000000..cdd55d8531
--- /dev/null
+++ b/changelogs/changelog-105-2314.markdown
@@ -0,0 +1,298 @@
+---
+layout: post
+title: Release 105-2314
+parent: Changelogs
+permalink: changelogs/changelog-105-2314
+author: sprunk
+---
+
+This is the changelog since release 1775 **until release 2314**, which happened on 2024-02-13.
+
+# Caveats
+These are the entries which may require special attention when migrating:
+
+### Removals
+* removed `gl.GetMatrix` and the interface accessible from the returned matrix object. Apparently these were unused, so we offer no replacement guide at the moment.
+* removed `spairs`, `sipairs` and `snext`. These have been equivalent to the regular `pairs`, `ipairs` and `next` for years now, use the regular versions instead.
+You can replace these functions before migrating, and known existing games have already received patches to do so.
+* removed `VFS.MapArchive` and `VFS.UnmapArchive`. They were very sync-unsafe. Hopefully they will be back at some point, but no timeline is available yet. Use `VFS.UseArchive` in the meantime.
+* removed LuaUI's access to `Script.LuaRules` and `Script.LuaGaia`.
+* removed the recently added `Spring.MakeGLDBQuery` and `Spring.GetGLDBQuery`.
+
+### Behaviour changes
+* QTPFS had a major overhaul, with multiple modrule changes and behaviour changes. See the section below.
+* many invalid def entries now cause the unit to be rejected; on the other hand, many (in particular, metal cost and weapon damage) can now be 0. Watch out for division by 0!
+Check the "def validity checks" section below for details.
+* nanoturret (immobile builder) build-range now only needs to reach the edge of the buildee's radius instead of its center. Mobile builders already worked this way.
+* raw move via `Spring.SetUnitMoveGoal` is now considered completed not only when the unit reaches its goal but also when it touches another unit who is at the goal. As of 105-2314 there is no easy way to detect that this happened.
+* screenshots are postfixed with UTC timestamp instead of number.
+* add `SMFTextureStreaming` boolean springsetting, defaults to false. If true, dynamically load and unload SMF Diffuse textures, which saves VRAM, but worse performance and image quality.
+Previous behaviour was equivalent to `true`, so if you get VRAM issues try changing it.
+* it's now possible to play fixed and random start positions with more teams than the map specifies.
+The extras are considered to start in the (0, 0) corner and it is now up to the game to handle this case correctly.
+* the `movement.allowGroundUnitGravity` mod rule now defaults to `false`. All known games have an explicit value set, so this should only affect new games.
+* `/ally` no longer announces this to unrelated players via a console message. The affected players still see one.
+Use the `TeamChanged` call-in to make a replacement if you want it to be public.
+* manually shared units no longer receive the Stop command. Use the `UnitGiven` callin to get back the previous behaviour.
+* `DumpGameStateOnDesync` springsetting is now enabled by default
+
+### Deprecation
+No changes yet, but these will happen in the future and possibly break things.
+
+* the `acceleration` and `brakeRate` unit def entries are scheduled for a unit change from elmo/frame to elmo/second. There is no change yet,
+but if you prefer not to have to add processing later you might want to change to `maxAcc` and `maxDec` respectively (which will stay elmo/frame).
+* the `CSphereParticleSpawner` (alias `simpleparticlespawner`) CEG class is scheduled for removal. It can be entirely drop-in replaced with `CSimpleParticleSystem` (alias `simpleparticlesystem`)
+since it has always had the same behaviour, just different internal implementation. Known games using the class will receive PRs before this happens.
+* there are now explicit facilities for generating a blank map (see below). Existing "random" map generator (that always produced a blank map) stays as-is,
+but may be repurposed into a "real" random map generator sometime in the future (not immediately planned though).
+* `UsePBO` springsetting is now deprecated. It already did nothing though.
+
+# QTPFS
+
+The QTPFS pathfinder has received a large overhaul. There are major improvements in both quality (fewer cases of units getting stuck, cutting corners etc.)
+and performance (processing speed, memory use, even disk usage). Every facet of QTPFS should generally work better. Try it out!
+
+* debug path drawer now draws into the minimap, showing the map damage updates waiting to be processed. The more intense the colour, the more layers (MoveTypes) that still need to process the change.
+* added modrule, `system.pfRepathDelayInFrames`, which controls how many frames at least must pass between checks for whether a unit is making enough progress to its current waypoint
+or whether a new path should be requested. Defaults to 60 (2 seconds). Adjust to find the right balance between how quickly units can get unstuck and how much CPU power is used.
+Smaller intervals increase the chance of slow units triggering unnecessary re-pathing requests, but reduces the chance that players may believe a unit is getting stuck and is not handling itself well.
+* added modrule, `system.pfRepathMaxRateInFrames`, which controls the minimum amount of frames that must pass before a unit is allowed to request a new path. By default, it is 150 frames (5 seconds).
+This is mostly for rate limiting and prevent excessive CPU wastage, because processing path requests are not cheap.
+* added modrule, `system.pfUpdateRateScale`. This is a multiplier for the update rate and defaults to 1. Increase to get faster updates but more CPU usage.
+* added modrule, `system.pfRawMoveSpeedThreshold`. Controls the speed modifier (which includes typemap boosts and up/down hill modifiers) under which units will never do raw move,
+regardless of distance etc. Defaults to 0, which means units will not try to raw-move into unpathable terrain (e.g. typemapped lava, cliffs, water). You can set it to some positive
+value to make them avoid pathable but very slow terrain (for example if you set it to 0.2 then they will not raw-move across terrain where they move at 20% speed or less, and will use
+normal pathing instead - which may still end up taking them through that path).
+* added modrule, `system.qtMaxNodesSearched`, can be used to limit the absolute number of nodes searched.
+* added modrule, `system.qtMaxNodesSearchedRelativeToMapOpenNodes`, can be used to limit the number of nodes searche relative to the number of pathable quads on the map.
+The final limit will be the larger between the relative and the absolute limits.
+* added modrule, `system.pfHcostMult`, a float value between 0 and 2, defaults to 0.2. Controls how aggressively the pathing search prioritizes nodes going in the direction of the goal.
+Higher values mean pathing is cheaper, but can start producing degenerate paths where the unit goes straight at the goal and then has to hug a wall.
+* added modrule, `system.qtRefreshPathMinDist`, can be used to configure the minimum size a path has to be in order to be eligible for an automatic incomplete path repath.
+* removed modrules: `system.pfForceUpdateSingleThreaded` and `system.pfForceSingleThreaded`. Multithreading has shown itself stable.
+* removed modrule: `system.pathFinderUpdateRate`, since `system.pfUpdateRateScale` now serves the same general role but has different units.
+* added **map** config entry, set in mapinfo under `(info).pfs.qtpfsConstants` subtable: `maxNodesSearched`, controls the absolute limit for how many nodes to search.
+Can only increase the modrule limit, not reduce it. Defaults to 0. Use for maps with large but unconnected areas (think lava & two hills).
+* added map config entry, `maxRelativeNodesSearched`. Controls the relative limit compared to total nodes on the map.
+Can only increase the modrule limit, not reduce it. Defaults to 0.
+
+# Defs unification
+
+Unit defs (i.e. `/units/*.lua`) and `UnitDefs` (in wupgets) referring to the same thing under different names and sometimes even different units of measurement has always been a point of confusion.
+Some of this has been alleviated, with a unified name being available for many mismatched keys. Usually it's one already existing on either "side" of the divide.
+
+## New def keys
+The following unit def keys now accept the same spelling as the ones exposed via `UnitDefs`.
+The old spelling still works (old → new).
+* metalUse → metalUpkeep
+* energyUse → energyUpkeep
+* buildCostMetal → metalCost
+* buildCostEnergy → energyCost
+* unitRestricted → maxThisUnit
+* name → humanName
+
+These two also accept a new spelling, and both the old and new spellings are in elmo/frame.
+However, consider migrating to the new spellings ASAP because the original spellings will be
+changed to use elmo/s sometime in the future.
+* acceleration → maxAcc
+* brakeRate → maxDec
+
+The following unit def keys now accept a spelling and measurement unit
+as the one exposed via `UnitDefs` (old → new). The old spelling still works,
+and is still in the old measurement unit.
+* maxVelocity (elmo/frame) → speed (elmo/second)
+* maxReverseVelocity (elmo/frame) → rSpeed (elmo/second)
+
+The following unit def keys now accept a new spelling, which hasn't been previously
+available in `UnitDefs`, but which has also been added there in this update.
+Old spelling still works.
+* losEmitHeight → sightEmitHeight
+* cruiseAlt → cruiseAltitude
+
+Added `fastQueryPointUpdate` to weapon (note, this is the entry inside a unit def that also sets target categories and direction; NOT weapon def!).
+When enabled, the `QueryWeapon` family of functions in the script is called every frame (instead of every 15 in slow update). This fixes friendly fire for rapid-fire multi-barrel weapons.
+
+## New `UnitDefs` members
+The following `UnitDefs` keys now accept the same spelling as the ones
+accepted for unit def files. The old spelling still works (old → new).
+* tooltip → description
+* wreckName → corpse
+* buildpicname → buildPic
+* canSelfD → canSelfDestruct
+* selfDCountdown → selfDestructCountdown
+* losRadius → sightDistance
+* airLosRadius → airSightDistance
+* radarRadius → radarDistance
+* jammerRadius → radarDistanceJam
+* sonarRadius → sonarDistance
+* sonarJamRadius → sonarDistanceJam
+* seismicRadius → seismicDistance
+* kamikazeDist → kamikazeDistance
+* targfac → isTargetingUpgrade
+
+The following keys receive a new spelling which hasn't previously been
+available for unit defs, but which has also been added in this update.
+Old spellings still work.
+* losHeight → sightEmitHeight
+* wantedHeight → cruiseAltitude
+
+Added the missing `radarEmitHeight` to UnitDefs. The unit def file key was also already `radarEmitHeight`.
+
+## Def validity checks
+
+Some invalid and/or missing defs are now handled differently.
+
+* negative values for health, (reverse) speed, and metal/energy/buildtime
+now cause the unit def to be rejected; previously each was clamped to 0.1
+* negative values for acceleration and brake rate now cause the unit def to
+be rejected; previously the absolute value was taken
+* values (0; 0.1) now allowed for health and buildtime (0 still prohibited)
+* values [0; 0.1) now allowed for metal cost (0 now allowed)
+* undefined metal cost now defaults to 0 instead of 1
+* undefined health and buildtime now each default to 100 instead of 0.1
+* weapon `edgeEffectiveness` can now be 1 (previously capped at 0.999)
+* unit armor multiplier (aka `damageModifier`) can now be 0 (previously capped at 0.0001)
+* damage in weapon defs can now be 0 (previously capped at 0.0001)
+* damage and armor can also be negative again (so that the target is healed),
+but keep in mind weapons will still always target enemies and never allies,
+so avoid using it outside of manually-triggered contexts, death explosions, and such
+
+### Deprecated UnitDefs removal
+
+All deprecated UnitDefs keys (who returned zero and produced a warning) have been removed, listed below:
+* techLevel
+* harvestStorage
+* extractSquare
+* canHover
+* drag
+* isAirBase
+* cloakTimeout
+* minx
+* miny
+* minz
+* maxx
+* maxy
+* maxz
+* midx
+* midy
+* midz
+
+# Features and fixes
+
+### Builder behaviour
+* nanoturret (immobile builder) build-range now only needs to reach the edge of the buildee's radius instead of its center. Mobile builders already worked this way.
+* added `buildeeBuildRadius` unit def entry, the radius for the purposes of being built (placing the nanoframe and then nanolathing).
+Negative values make it use the model radius (this is the default). Set to zero to require builders to reach the center.
+* added `Spring.Get/SetUnitBuildeeRadius(unitID)` for controlling the above dynamically.
+* fixed builders not placing nanoframes from their maximum range.
+* units vacating a build area (aka "bugger off") will now try to use the fastest route out.
+* bugger off now applies correctly to units sitting outside the area, but with a large enough footprint to block construction.
+* added `Spring.GetUnitWorkerTask(unitID) → cmdID, targetID`. Similar to `Spring.GetUnitCurrentCommand`, but shows what the unit is actually doing,
+so will differ when the unit is guarding or out of range. Also resolves Build vs Repair. Only shows worker tasks (i.e. things related to nanolathing).
+* `gadget:AllowUnitCreation` now has two return values. The first one is still a boolean on whether to allow creating the unit (no change here).
+The new second value is a boolean, if the creation was not allowed, whether to drop the order (defaults to true, which is the previous behaviour).
+If set to false, the builder or factory will keep retrying.
+* added `Spring.GetUnitEffectiveBuildRange(unitID[, buildeeDefID]) → number`. Returns the effective build range for given builder towards the center of the prospective buildee,
+i.e. the same way engine measures build distance. Useful for setting the goal radius for raw move orders.
+This doesn't solve all known cases yet (doesn't handle features, or terraform) which are pending a solution; for now, the function returns just the build range if `buildeeDefID` is nil.
+* added `Spring.GetUnitIsBeingBuilt(unitID) → bool beingBuilt, number buildProgress`. Note that this doesn't bring new _capability_ because `buildProgress` was already available
+from the 5th return of `Spring.GetUnitHealth`, and `beingBuilt` from the 3rd return of `Spring.GetUnitIsStunned`, but it wasn't terribly convenient or intuitive.
+* fixed it being possible to place a unit that requires a geothermal vent anywhere.
+
+### Rules params
+* added player rules params. Controlled by the new interfaces: `Spring.SetPlayerRulesParam`, `GetPlayerRulesParam` and `GetPlayerRulesParams`,
+similar to other existing rules params. There's currently two visibility levels, public and private. A notable difference is that the private level
+is only visible to that player, not his allyteam, and not even his (comsharing) team; this is partially for technical reasons and can be changed if need be.
+Synced and specs see everything. Not yet available to the Skirmish AI interface.
+* added boolean value support to rules params, including the new player rules params.
+Skirmish AI and the unit rules param selection filter can read them via existing numerical interface as 0 and 1.
+
+### Map textures
+* added `SMFTextureStreaming` boolean springsetting, defaults to false. If true, dynamically load and unload SMF Diffuse textures, which saves VRAM, but worse performance and image quality.
+Previous behaviour was equivalent to `true`, so if you get VRAM issues try changing it.
+* added `SMFTextureLodBias` numerical springsetting, defaults to 0. In case `SMFTextureStreaming = false`, this parameter controls the sampling lod bias applied to diffuse texture.
+* added a 5th integer param to `Spring.GetMapSquareTexture(x, y, lodMin, texName[, lodMax = lodMin])`. It controls the max lod and defaults to the 3rd parameter,
+which is now the minimum (instead of being the final value).
+
+### FFA support
+* it's now possible to play fixed and random start positions with more teams than the map specifies.
+The extras are considered to start in the (0, 0) corner and it is now up to the game to handle this case correctly.
+* `/ally` no longer announces this to unrelated players via a console message. The affected players still see one.
+Use the `TeamChanged` call-in to make a replacement if you want it to be public.
+* added `system.allowEnginePlayerlist`, defaults to true. If false, the built-in `/info` playerlist won't display.
+Use for "anonymous players" modes in conjunction with `Spring.GetPlayerInfo` poisoning, or just to prevent ugliness.
+* added `Spring.SetAllyTeamStartBox(allyTeamID, xMin, zMin, xMax, zMax) → nil`, sets that allyteam's startbox edges, in elmos.
+
+### VFS
+* added a 4th boolean parameter to `VFS.DirList` and `VFS.SubDirs`, defaults to false. If set to true, the search is recursive.
+* fixed `VFS.SubDirs` applying the passed pattern to the whole paths instead of just the individual folder names in `VFS.RAW` mode.
+
+### Unit selection
+* added `Spring.DeselectUnit(unitID) → nil`.
+* added `Spring.SelectUnit(unitID[, bool append]]) → nil`, a single-unit version of `Spring.SelectUnit{Array,Map}` that doesn't require a table.
+The unitID can be nil.
+* added `Spring.DeselectUnitArray({[any] = unitID, [any] = unitID, ...}) → nil` and `Spring.DeselectUnitMap({[unitID] = any, [unitID] = any, ...}) → nil`.
+These are the counterparts to the existing `Spring.SelectUnitArray` and `Spring.SelectUnitMap`.
+* the table in `Spring.SelectUnitArray` can now have arbitrary keys. Previously they had to be numbers, but the table did not actually have to be an array.
+
+### Root pieces
+* added `Spring.GetModelRootPiece(modelName) → number pieceID` which returns the root piece.
+* added `Spring.GetUnitRootPiece(unitID) → number pieceID` and `Spring.GetFeatureRootPiece(featureID) → number pieceID`, likewise.
+
+### Colored text
+* added an inline colour code `\254`, followed by 8 bytes: RGBARGBA, where the first four describe the following text colour and the next four the text's outline.
+* added the `Game.textColorCodes` table, containing the constants `Color` (`\255`), `ColorAndOutline` (the newly added `\254`), and `Reset` (`\008`).
+
+### Miscellaneous additions
+* add `Spring.IsPosInMap(x, z) → bool inPlayArea, bool inMap`. Currently, both of the returned values are the same and just check whether the position
+is in the map's rectangle. Perhaps in the future, or if a game overrides the function, there will be cases of limited play area (think SupCom singleplayer
+map extension; 0 A.D. circular maps; or just an external decoration area).
+* add `Spring.GetFacingFromHeading(number heading) → number facing` and `Spring.GetHeadingFromFacing(number facing) → number heading` for unit conversion.
+* added `wupget:Unit{Entered,Left}Underwater(unitID, unitDefID, teamID) → nil`, similar to existing UnitEnteredWater.
+Note that EnteredWater happens when the unit dips its toes into the water while EnteredUnderwater is when it becomes completely submerged.
+* add new `/remove` cheat-only command, it removes selected units similar to `/destroy` except the units are just removed (no wreck, no death explosion).
+* added new startscript entry: `FixedRNGSeed`. Defaults to 0 which means to generate a random seed for synced RNG (current behaviour).
+Otherwise, given value is used as the seed. Use for reproducible runs (benchmarks, mission cutscenes...).
+* added `Script.DelayByFrames(frameDelay, function, args...)`. **Beware**, it's `Script`, not `Spring`! Runs `function(args...)` after a delay of the specified number of frames (at least 1).
+Multiple functions can be queued onto the same frame and run in the order they were added, just before that frame's `GameFrame` call-in. Use to avoid manual tracking in GameFrame.
+* added `Spring.GetUnitSeismicSignature(unitID) → number` and `Spring.SetUnitSeismicSignature(unitID, number newSignature) → nil`.
+* added `Spring.SetUnitShieldRechargeDelay(unitID, [weaponNum], [seconds]) → nil`. Resets a unit's shield regeneration delay.
+The weapon number is optional if the unit has a single shield. The timer value is also optional: if you leave it nil it will emulate a weapon hit.
+Note that a weapon hit (both via `nil` here, and "real" hits) will never decrease the remaining timer, though it can increase it.
+An explicit numerical value always sets the timer to that many seconds.
+* added `MaxFontTries` numerical springsetting, defaults to 5. Represents the maximum number of attempts to search for a glyph replacement when rendering text (lower = foreign glyphs may fail to render, higher = searching for foreign glyphs can lag the game). The search is optimized to look in the "best" places so the chance of finding the glyph does not increase linearly alongside search time.
+* added `GL.DEPTH_COMPONENT{16,24,32,32F}` constants.
+* added the following `GL` constants for use in `gl.BlendEquation`: `FUNC_ADD`, `FUNC_SUBTRACT`, `FUNC_REVERSE_SUBTRACT`, `MIN` and `MAX`.
+* added `Spring.GetWindowDisplayMode() → number width, number height, number bitsPerPixel, number refreshRateHz, string pixelFormatName`.
+The pixel format name is something like, for example, "SDL_PIXELFORMAT_RGB565".
+* added `/dumpatlas 3do`.
+
+### Blank map generation
+* new way to specify blank map colour: `blank_map_color_{r,g,b}`, number 0-255.
+* a new start-script entry, `InitBlank` (at root level). Alias for existing `MapSeed`.
+* new built-in mapoptions, `blank_map_x` and `blank_map_y`, aliases for existing `new_map_x/y`.
+
+### Weapon fixes
+* fix `Cannon` type weapons aiming at the (0, 0) corner of the world if they can't find a physical firing solution due to target leading
+* fix `Cannon` type weapons being too lenient in friendly-fire avoidance when firing over allies
+* fix `MissileLauncher` weapons with high `trajectoryHeight` not performing ground and ally avoidance correctly
+* fix `MissileLauncher` weapons with high `trajectoryHeight`, zero `turnRate` and high `wobble` having an unstable trajectory and prematurely falling down when firing onto higher elevations
+* fix `DGun` weapon type projectile direction (previously shot at an angle that would be valid from the `AimFromWeapon` piece and not the `QueryWeapon` piece)
+
+### Basecontent fixes
+* moved the `cursornormal` cursor from the "Spring cursors" archive to basecontent. The significance
+of this is that `modinfo.lua` is now sufficient for an archive to be a valid Recoil game that doesn't crash.
+* fixed the initial spawn gadget.
+* fixed action handler key press/release events.
+
+### Miscellaneous fixes
+* fixed hovercraft/ship movement types being able to encroach onto land across sheer cliffs.
+* fixed skirmish AI API getTeamResourcePull (used to return max storage instead).
+* `Spring.SetSunDirection` no longer causes broken shadows if you pass an unnormalized vector.
+* fixed being unable to drag-select units with `/specfullview 0`.
+* fixed weirdly-boned Assimp (`.dae`) models being loaded incorrectly.
+* fixed COB `SetMaxReloadTime` receiving a value 10% smaller than it was supposed to.
+* fix screenshots saved as PNG having an inflated file size via a redundant fully-opaque alpha channel.
+* fix clicking during loadscreen sometimes registering as startbox placement later.
+* fix a crash on self-attach via `Spring.UnitAttach`.
+* fix `Spring.MoveCtrl.SetMoveDef` not working with numerical movedef IDs.
diff --git a/changelogs/changelog-105-2511.markdown b/changelogs/changelog-105-2511.markdown
new file mode 100644
index 0000000000..01847dc952
--- /dev/null
+++ b/changelogs/changelog-105-2511.markdown
@@ -0,0 +1,113 @@
+---
+layout: post
+title: Release 105-2511
+parent: Changelogs
+permalink: changelogs/changelog-105-2511
+author: sprunk
+---
+
+This is the changelog since version 2314 **until release 2511**, which happened on 2024-06-07.
+
+# Caveats
+These are the entries which may require special attention when migrating:
+* some animations are now multi-threaded. It shouldn't cause desyncs, but an `AnimationMT` springsetting has been provided to disable it, just in case. See below.
+* when building the engine via CMake, only native C++ AIs are now built by default.
+* unit def `trackStretch` values now treated reciprocally compared to previous, i.e. a stretch factor of 2 now means the track is stretched x2 longer (was squeezed x0.5 shorter previously).
+* ground decals may behave a bit different because there's a new implementation. There are a handful of known issues, see the decals section above.
+* ground decals may no longer work on potato hardware.
+* instead of 4 default explosion decals in basecontent (`bitmaps/scars/scarN.bmp`, 1-4), there's 2 new normal-mapped ones (`bitmaps/scars/scarN.tga` and `bitmaps/scars/scarN_normal.tga`, 1-2).
+Note the format change from BMP to TGA.
+You might want to check your `gamedata/resources.lua` to see if you're referencing the old default scars (either explicitly or e.g. autogenerating from the `bitmaps/scars` folder).
+You might also want to produce more to counteract the reduced variety.
+* default targeting priority for typed units no longer has a ±30% random component. Use `gadget:AllowWeaponTarget` to get back the previous behaviour.
+* engine line-move formations now use unit type power rather than a function of cost (60M+E) to distribute units around the formation. Power defaults to 60M+E and all known games use Lua customformations.
+* burst weapons can now be made to respect firing angle restrictions. See the burst section below.
+
+# Features
+* The `select` action now composes `IdMatches` filters as *OR* statements see [The select command]({{ site.baseurl }}{% link articles/select-command.markdown %}#idmatches_string) for further reference.
+* added a new optional boolean parameter to `Spring.GetUnitHeading`, default false. If true, the value returned is in radians instead of the TA 16-bit angular unit.
+* added a new callin, `GameFramePost(number frame)`. This is the last callin in a sim frame (regular `GameFrame` is the first).
+* added a new callin, `UnitArrivedAtGoal(unitID, unitDefID, teamID)`, for when a unit arrives at its movement goal (incl. raw move).
+Use for batching events that happened during the frame to be sent to unsynced for use in draw frames before the next sim frame.
+* added `Spring.GetTeamMaxUnits(teamID) -> number maxUnits, number? currentUnits`. The second value is only returned if you have read access to that team (max is public).
+There is currently no corresponding Set.
+* added `GAME/ShowServerName` startscript entry. If not empty, the initial connection screen's "Connecting to: X" message will display the value of that option instead of the host's IP.
+* added `Spring.GetModOption(string key) -> string? value`. Returns a single modoption; replaces the `Spring.GetModOptions().foo` pattern for greater performance.
+* added `Spring.GetMapOption(string key) -> string? value`, ditto for a single mapoption.
+* add a `Patrolling` selection filter. Applies to units that have a Patrol command among the first 4 commands (remember Patrol prepends Fight, which itself prepends Attack).
+* the `SpringDataRoot` springsetting now accepts multiple data root paths. Split them via ';' (on Windows) or ':' (elsewhere).
+* `Spring.GetUnitWorkerTask` now works on factories.
+* major performance (incl. loading time and Lua memory usage) improvements.
+* further Tracy instrumentation.
+
+### New ground decals
+* added normal mapping to all decals (explosion scars, building plates, tank tracks).
+Name the normalmap the same as the base decal but with `_normal` at the end., e.g. when you use `scar1.tga` as a decal diffuse/alpha, use `scar1_normal.tga` as a normal map texture.
+* alpha channel of normal map is now used for explosion decal glow. Scales with weapon damage. Full opacity (255) represents the hottest part.
+* added a Lua interface to create and edit decals. Check the [control](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedCtrl.html#Decals)
+and [read](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedRead.html#Decals) parts in the API listings.
+* added a shader interface for decal rendering. No documentation of uniforms/attributes/etc seems to exist at the moment,
+but you can look up the default shader implementation ([fragment](https://github.com/beyond-all-reason/spring/blob/BAR105/cont/base/springcontent/shaders/GLSL/GroundDecalsFragProg.glsl), [vertex](https://github.com/beyond-all-reason/spring/blob/BAR105/cont/base/springcontent/shaders/GLSL/GroundDecalsVertProg.glsl)).
+* replaced scar bitmaps in basecontent. See the caveats section above.
+* unit def `trackStretch` values now treated reciprocally compared to previous, i.e. a stretch factor of 2 now means the track is stretched x2 longer (was squeezed x0.5 shorter previously).
+* ground decals may no longer work on potato hardware.
+* known issue: tracks/footprints no longer maintain the texture offset on stutter-step.
+* known issue: building decals may not render correctly underwater.
+
+### Bursts and firing angles
+* added a new weapon (not weapon def, the weapon table in a unit def) numerical tag, `burstControlWhenOutOfArc`. By setting it to 1 or 2, ongoing bursts (from 2nd shot onwards) can now be made to respect firing cones coming from `mainDir` + `maxAngleDif` (or equivalently from `turret = false` + `tolerance`), and also the other cone from `fireTolerance` compared to the aim-from piece direction.
+* if 0, further shots don't respect the cones. This is the default and the previous behaviour.
+* if 1, the weapon will fail to produce shots aimed at targets out of the cone, wasting them. Aiming in the cone again will resume producing projectiles (but not "refund" the wasted ones, the overall timing of the burst is kept). The `EndBurst` script event still runs even if the ending shot was wasted, but per-shot events don't run for wasted shots.
+* if 2, the weapon will just ignore attempts to aim outside the cone and keep firing in whatever direction its aim-from piece was pointing at.
+
+### More interfaces in `defs.lua`
+The following functions are now available in the `defs.lua` phase:
+* `Spring.GetMapOption` (new, see above)
+* `Spring.GetModOption` (new, see above)
+* `Spring.GetTeamLuaAI`
+* `Spring.GetTeamList`
+* `Spring.GetGaiaTeamID`
+* `Spring.GetPlayerList`
+* `Spring.GetAllyTeamList`
+* `Spring.GetTeamInfo`
+* `Spring.GetAllyTeamInfo`
+* `Spring.GetAIInfo`
+* `Spring.GetTeamAllyTeamID`
+* `Spring.AreTeamsAllied`
+* `Spring.ArePlayersAllied`
+* `Spring.GetSideData`
+
+### Water
+* add `Spring.GetWaterLevel(x, z) -> number waterHeight`. Similar to `Spring.GetGroundHeight` except returns the height of water at that spot.
+Currently water height is 0 everywhere. Use where appropriate to be future-proof for when Recoil gets dynamic water, or just to give a name to the otherwise magic constant.
+* add `Spring.GetWaterPlaneLevel() -> number waterPlaneHeight`. Ditto, except encodes that you expect the water to be a flat plane.
+Use as above but where you have no x/z coordinates.
+
+### Interpolated game seconds
+* added `Spring.GetGameSecondsInterpolated() -> number` function to unsynced Lua.
+Unlike `GetGameSeconds` it flows during rendering. Unlike `GetDrawSeconds` its flow reflects gamespeed (incl. stopping when paused).
+And unlike `GetGameFrame` and `GetFrameTimeOffset` it is in a natural unit instead of the technical frame abstraction.
+* shaders: changed the `timeInfo.z` uniform from draw frame number to interpolated game seconds.
+
+### Skidding
+* units will skid if hit with impulses sufficiently large in the direction opposite their movement vector. Previously units would only skid on large impulses that hit their sides.
+* added `Spring.SetUnitPhysicalStateBit(number unitID, number stateBit) -> nil`, for setting a unit's physical state bit. Gotta use magic constants for bits at the moment.
+Use for example to unattach units from the grounds and trigger skidding.
+* added unit def: `rollingResistanceCoefficient`, used to reduce a unit's speed when exceeding their normal max speed. Defaults to 0.05.
+* added unit def: `groundFrictionCoefficient`, used to reduce a unit's speed when skidding. Defaults to 0.01.
+* added unit def: `atmosphericDragCoefficient`, reduces a unit's speed when skidding and exceeding speed maximum. Defaults to 1.0.
+
+### Debugging tools
+* `/debugcolvol` now also draws the selection volume, in green.
+* `/track 1 unitID unitID unitID` lets you specify units to track via the command. If no unitID is given it still behaves the old way and uses the current selection.
+* added an `AnimationMT` boolean springsetting. Defaults to true. Set to false if there's desync problems. Will be removed after some time, when MT animations prove to be sync-safe.
+* COB piece errors say the culprit's unit def name (since the same script can be shared by many units, with different piece lists).
+
+# Fixes
+* inserting (via `CMD.INSERT`) a "build unit" command to a factory with a SHIFT and/or CTRL modifier (i.e. x5/20) will now work correctly (previously ignored and went x1).
+* fix an issue where a unit that kills something via `SFX.FIRE_WEAPON` would sometimes continue to shoot at the location it was standing at at the time.
+* fixed `VFS` functions that deal with file paths being exceedingly slow.
+* fixed `Spring.GetTeamUnitsByDefs` revealing much more information than it should.
+* `Spring.GetUnitWeaponState(unitID, "burstRate")` now correctly returns fractional values (was only full integers before).
+* fixed camera rotation via the middle mouse button canceling unit tracking mode.
+* fixed timed out clients sometimes managing to send packets and claim a desync.
diff --git a/changelogs/changelog-105-2590.markdown b/changelogs/changelog-105-2590.markdown
new file mode 100644
index 0000000000..f2b929e0f0
--- /dev/null
+++ b/changelogs/changelog-105-2590.markdown
@@ -0,0 +1,51 @@
+---
+layout: post
+title: Release 105-2590
+parent: Changelogs
+permalink: changelogs/changelog-105-2590
+author: sprunk
+---
+
+This is the changelog since version 2511 **until release 2590**, which happened on 2024-09-30.
+
+# Caveats
+These are the entries which may require special attention when migrating:
+* removed the `movement.allowDirectionalPathing` modrule. Pathing is now always directional. All known games had directional pathing enabled already.
+* unitdef `waterline` is now ignored if the unit has a movedef, and is taken from the movedef via its new `waterline` def, unless the movedef sets the new `overrideUnitWaterline` boolean def to false.
+
+# Features
+
+### Movement
+* removed the `movement.allowDirectionalPathing` modrule. Pathing is now always directional, i.e. going downhill does not incur a speed penalty. All known games had directional pathing enabled already.
+* added `allowDirectionalPathing` boolean movedef entry, default false. Allows the HAPFS (aka legacy) pathfinder to take direction into account. If false, it will avoid going downhill to the same extent as climbing that slope uphill (note that this is the previous behaviour). Does not apply to QTPFS, which cannot use directional pathing.
+* added `preferShortestPath` boolean movedef entry, default false. Makes the QTPFS pathfinder ignore speed modifiers (from any source: typemap, slope, water) when deciding the path. Still avoids 0% modifiers (i.e. won't try to move across completely unpathable terrain). Does not apply to HAPFS, which cannot use this and will always take speed modifiers into account.
+* added `waterline` numerical entry to movedef - how deep the unit sits in water, in elmos, similar to the existing unit def waterline. Defaults to 1 for ships, to the unit width (according to the movedef footprint, converted to elmos) for submarines, and 0 for everything else. Overrides the existing `waterline` from the unit def by default.
+* added `overrideUnitWaterline` boolean entry to movedef, defaults to true. If set to false, it will use the unit def waterline after all.
+* added `separationDistance` numerical movedef entry, default 0. Treated as extra radius for the purposes of colliding with other mobiles during movement (i.e. doesn't apply to impulse-based collisions or to pathing near buildings/terrain). Use to loosen up tight formations without affecting where the unit can path.
+
+### Yardmaps
+* added 'u' yardmap tile: not buildable, but pathable. Good replacement for indoor 'y'.
+* added 'e' yardmap tile: not buildable, exit-only. Units cannot path from a normal tile to an exit-only tile (but pathing between adjacent exit-only tiles is fine).
+Decent for factory construction areas, but keep in mind it only affects ground units (not aircraft, not wrecks, etc) and even ground units can still enter such tiles via non-pathing means (e.g. via impulse).
+
+### Smooth mesh
+* added `Spring.RebuildSmoothMesh() → nil` synced callout to immediately rebuild the smooth mesh.
+* added `system.smoothMeshResDivider` numerical modrule, default 2. Reduces the resolution of the smoothmesh. Increase to get better performance at the cost of worse accuracy.
+* added `system.smoothMeshSmoothRadius` numerical modrule, default 40. The radius for smoothing, in elmos.
+
+### Lua wupget API
+* added `Spring.RebuildSmoothMesh() → nil`, see above.
+* added `Spring.GetUnitPhysicalState(unitID) → number bitmask`. See engine source for the meanings of bits. Only available to synced.
+* added `Spring.GetUnitArrayCentroid({unitID, unitID, ...}) → numbers x, y, z`. Returns the centroid (average position), or nil if the array is empty.
+* added `Spring.GetUnitMapCentroid({[unitID] = any, [unitID] = any, ...}) → numbers x, y, z`. Ditto but the unitIDs are keys instead of values in the accepted table.
+* added `Spring.GetUnitCosts(unitID) → number buildTime, number metal, number energy`.
+* added `Spring.GetUnitCostTable(unitID) → { metal = number, energy = number }, number buildTime`. Note that buildtime is not a regular resource and is returned separately.
+* added `Spring.GetTeamDamageStats(teamID) → number damageDealt, number damageReceived`. Same as the values already available from `Spring.GetTeamStatsHistory`, but without most of the overhead.
+
+### Misc and fixes
+* something happened to terraforming rate (via restore command, or ground flattening before construction).
+* lots of general performance improvements.
+* fixed the stack warning spam if `wupget:UnitArrivedAtGoal` was defined.
+* fixed factory UnitDefs being able to have `canAssist` set to true
+* fixed mouse not warping correctly (when using `Spring.WarpMouse`) on Unix/Wayland
+* fixed invalid corpse names being left unsanitized in UnitDefs `corpse`
diff --git a/changelogs/changelog-105-861.markdown b/changelogs/changelog-105-861.markdown
new file mode 100644
index 0000000000..0b99e0bc40
--- /dev/null
+++ b/changelogs/changelog-105-861.markdown
@@ -0,0 +1,143 @@
+---
+layout: post
+title: Release 105-861
+parent: Changelogs
+permalink: changelogs/changelog-105-861
+author: sprunk
+---
+
+The changelog since the fork **until release 105-861**, which happened in February 2022.
+
+## Caveats
+* `wupget:GameProgress` interval 10 → 5 seconds
+* basecontent now provides the `treetype0` to `treetype15` features with a model and some customparams
+* buildings can be placed inside other buildings' open yardmap gaps
+* demos are saved with UTC clock filename (could break filename parsing etc)
+
+## Features and fixes
+
+### Icons as UI
+* new springsetting `UnitIconsAsUI` and an ingame `/iconsAsUI` command (all below have a `/command` variant without the "Unit"). Set to 1 to make radar icons behave like UI elements instead of in-world objects (e.g. no obscuring by terrain, distance fog, blurring or reflection in the water etc.
+* new springsetting `UnitIconScaleUI`. Size scaling for icons when drawn as UI.
+* new springsetting `UnitIconsHideWithUI`. Whether icons get hidden via F5.
+* new springsettings `UnitIconFadeStart` and `UnitIconFadeVanish`, for making icons fade smoothly
+
+### Grass
+* `Spring.{Add,Get,Remove}Grass` now works even if grass rendering is disabled (also fixes desync)
+* `Spring.AddGrass` now has a 3rd param, grass intensity (returned by `Get`; putting 0 equivalent to using `Remove`)
+
+### Recursion
+Allow shallow recursion (up to 16 calls deep) for the following callouts/callins:
+* `Spring.{Create,Destroy}{Unit,Feature}`
+* `Spring.GiveOrder{,Array}ToUnit{,Array,Map}`
+
+Additionally:
+* `Spring.SpawnExplosion` works inside `Projectile{Created,Destroyed}`
+
+### Texture Atlases
+* add `gl.CreateTextureAtlas(number x, number y, number? allocType, string? atlasName) → string texName`.
+`x` and `y` have to be between 256 and the value of the springsetting `MaxTextureAtlasSizeX` (Y respectively).
+`allocType` defaults to 0 if not specified. `atlasName` is an optional name for the atlas.
+* add `gl.FinalizeTextureAtlas(string id) → bool success`.
+* add `gl.DeleteTextureAtlas(string id) → bool success`.
+* add `gl.AddAtlasTexture(string id, string luaTex, string? subAtlasTexName)`. Adds texture `luaTexStr` to sub-atlas `subAtlasTextureName` (defaulting to same name as `luaTex` if not specified) of atlas `id`. The lua tex has to be a regular 2D RGBA/UNORM texture. DDS textures can also be put into the atlas.
+* add `gl.GetAtlasTexture(string texName, string luaTex) → number s, p, t, q`. Query an atlas texture for the UV coordinates (minU, maxU, minV, maxV) of a texture saved earlier with `Spring.AddAtlasTexture`.
+
+### Particles
+* CEGs from allied projectiles now visible in the fog
+* add `/softParticles` toggle and `SoftParticles` springsetting to make particles (esp. groundflashes) clipping into the ground fade a bit instead of having a jagged edge
+* add `Spring.SetNanoProjectileParams(r, v, a, randR, randV, randA) → nil`. All params are numbers, and default to 0 when not given. The first three are starting rotation in °, rotation speed in °/s, and rotation acceleration in °/s². The other three are the same but multiplied with a random (-1; +1) before being added to the former, rolled once per particle at the start of its lifetime.
+* add `Spring.GetNanoProjectileParams() → r, v, a, randR, randV, randA`, returns the parameters as above.
+* add `/drawOrderParticles` command which makes particles obey draw order, supplied in CEG definition under the `drawOrder` key
+* add `rotParams` key to `CSimpleParticleSystem` and `CBitmapMuzzleFlame` CEGs, a vector of 3 floats: rotation speed °/s, rotation acceleration °/s², starting rotation in °
+
+### Advanced GL4 (shaders, VAO, etc)
+* add `gl.GetVAO` which returns an object with a bunch of methods.
+* add `gl.GetVBO`.
+* add `Spring.GetSelectedUnitsCount` to the shader container (use case: F2 pathmap replacement, can show a general slope map if 0 units selected)
+* add `gl.{Unit,Feature}{,Shape}GL4` temporary functions
+* added a ton of uniforms for shaders
+* add LuaShaders::Set{Unit,Feature}BufferUniforms for GPU side per unit/feature "uniforms" (SSBO in fact)
+* gl.Uniform/gl.UniformInt/etc don't require location as a first param, name is fine too.
+
+### Lua constants for rendering
+* add `Platform.glHave{AMD,NVidia,Intel,GLSL,GL4}` bools
+* remove `Platform.glSupport24bitDepthBuffer` and add `Platform.glSupportDepthBufferBitDepth` instead
+* add the following constants to `GL`: `LINE_STRIP_ADJACENCY`, `LINES_ADJACENCY`, `TRIANGLE_STRIP_ADJACENCY`, `TRIANGLES_ADJACENCY`, `PATCHES`
+* add the following constants to `GL`: `[UNSINGED_]BYTE`, `[UNSINGED_]SHORT`, `[UNSINGED_]INT[_VEC4]`, `FLOAT[_VEC4]`, `FLOAT_MAT4`
+* add the following constants to `GL`: `ELEMENT_ARRAY_BUFFER`, `ARRAY_BUFFER`, `UNIFORM_BUFFER`, `SHADER_STORAGE_BUFFER`
+* add the following constants to `GL`: `READ_ONLY`, `WRITE_ONLY`, `READ_WRITE`
+* add a ton of image type specifiers (`RGBA32` etc) to `GL`
+* add a ton of barrier bit constants to `GL`
+
+### Other rendering-adjacent stuff
+* most CEGs and weapon visuals are tested for texture validity such that sprites with invalid textures are not displayed. No more need for 1x1 empty texture
+* fix skybox stretching and seams (in case it's represented by a cubemap)
+* smoother area-command circle, vertices 20 → 100
+* add `Spring.{Set,Get}{Unit,Feature}AlwaysUpdateMatrix` which makes the unit "always update matrix"
+* add `/reloadTextures [lua, smf, s3o, ceg]` to reload textures, all groups if none specified.
+* filling the nano wireframe works on the current model heights (meaning units with pieces hidden high above but moved downward work correctly)
+* add `globalRenderingInfo.availableVideoModes `
+* add `gl.GetFixedState` that returns a bunch of various GL constants and tables, it does a lot depending on args.
+* `DeprecatedGLWarnLevel` springsetting default value 2 → 0 (to reduce spam)
+* add `gl.AlphaToCoverage(bool enable, bool? force) → nil`, defined only if `GLEW_ARB_multisample` is supported by the platform, and otherwise the func itself is nil; `force` makes it apply even if MSAA level is under 4
+* improve AMD gfx card detection (looks for more strings, eg. "radeon", and in more places)
+* add `gl.ClipDistance(number clipID, bool enabled) → nil`, `clipID` is 1 or 2.
+* change `TextureMemPoolSize` springsetting default value 256 → 512 MB
+* expose `windSpeed` to `Spring.{Get,Set}WaterParams`
+* new bumpwater params (defaults): waveOffsetFactor (0.0), waveLength (0.15), waveFoamDistortion (0.05), waveFoamIntensity (0.5), causticsResolution (75.0), causticsStrength (0.08)
+* fix sky reflections on the ground (`skyReflectModTex`)
+
+### Missile smoke trails
+* weapon defs: add bool `smokeTrailCastShadow`, number `smokePeriod` (default 8), number `smokeTime` (default 60), number `smokeSize` (default 7), and number `smokeColor` (default 0.7, single value for all three RGB channels tint) to things with smoke trails
+* weapon defs: add bool `castShadow`, for the projectile itself
+
+### Camera tweaks
+* add integer springsetting `SmoothTimeOffset`. This attempts to smooth out the TimeOffset parameter that is used to calculated the tweened draw frames. Default 0, old behaviour. Recommended value of 2, this attempts to keep the actualy timeoffset within 90% of the true time. Best with vsync on.
+* add boolean springsetting `CamFrameTimeCorrection`. Default false is the current behaviour, use true to get better interpolation during high load.
+
+### Builders
+* new yardmap options for "stacked" buildings
+* add `Spring.{Set,Get}UnitBuildParams(unitID, "buildDistance"/"buildRange3D", value) → nil` about unit build range (number) and whether it's spherical (bool)
+* add `Spring.GetUnitInBuildStance(unitID) → bool`
+* add the `gadget:AllowUnitCaptureStep(capturerID, capturerTeamID, victimID, victimTeamID, progressDiff) → bool` callin
+* fix seaplanes being unbuildable on water if they were set to float rather than submerge
+
+### Other additions to Lua API
+* `math.random()` now correctly accepts arguments when parsing defs
+* allow `nil` as the first arg to `Spring.SetCameraState`
+* `Spring.SetWMIcon` new 2nd arg, bool `force`: ignores the 32x32 icon restriction on Windows
+* allow empty argument for `Spring.GetKeyBindings` to return all keybindings
+* add `Spring.GetUnitsInScreenRectangle`
+* added `Spring.SetWindowGeometry` for easy window positioning.
+* add `Spring.ForceTesselationUpdate(bool normalMesh = true, bool shadowMesh = false) → bool isROAM` for ROAM
+* parameterless `Spring.GetUnitFlanking()` now has an 8th return value, collected flanking bonus mobility
+
+### Misc features
+* selection keys: add an `IdMatches` filter to use the internal name
+* the default pathfinder (set via 0 in modrules) is now multi-threaded. In release 861, the old single-threaded pathfinder is temporarily accessible by setting the modrule to 2, but this gets removed later on in release 1544 when MT issues are fixed.
+* add `/setSpeed x` to set game speed directly, still obeys min/max
+* add `/endGraph 2` to bring the player back to the menu (1 still quits)
+* keybinds: same action with different arguments now differentiated
+* added a Load Game button to the raw spring.exe menu
+* command cache increased 1024 → 2048
+* native AI interface: add `Feature_getResurrectDef` and `Feature_getBuildingFacing`
+* native AI interface: add `spherical` param to `Get{Friendly,Enemy,Neutral,}{Units,Features}{In,}`
+
+### Misc fixes
+* fix CEGs from allied projectiles: now visible in the fog
+* fix disk read failures (files that get read failure errors are retried multiple times before giving up)
+* fix enabling globallos not revealing unseen terrain changes immediately
+* fix height bounds calculation, it now reflects the actual heights rather than the historical min/max (for example if the highest point went 100 → 90 → 110 → 100, previously `Spring.GetGroundExtremes` would return 100 → 100 → 110 → 110)
+* fix `Spring.SpawnExplosion` crashing on some valid data
+* fixed aircraft using smoothmesh to update if terrain was modified pre-game
+* multiple fixes to save/load
+* fix spring-dedicated not correctly showing output on Windows when not redirected (spring-headless already worked correctly)
+* fix a crash when trying to add enemy units to a control group (e.g. selected via /godmode)
+* fix a desync caused by locale-dependent string sorting
+* fix a crash trying to load a save done on a map with LuaGaia when currently playing on a map without it
+* fix resources sometimes getting into the negative (on the order of 1e-15) due to float inaccuracies; in particular this caused units that cost 0 resources to shoot to be unable to fire due to insufficient resources
+* fix "zombie torpedoes" bouncing under the map
+* fix projectiles going through units sometimes
+* fix the "unload dead unit" synced crash
+* probably works on Linux Wayland (implementation doesn't support hardware cursor yet; added in a later release)
\ No newline at end of file
diff --git a/changelogs/changelog-105-902.markdown b/changelogs/changelog-105-902.markdown
new file mode 100644
index 0000000000..f13ff0f888
--- /dev/null
+++ b/changelogs/changelog-105-902.markdown
@@ -0,0 +1,45 @@
+---
+layout: post
+title: Release 105-902
+parent: Changelogs
+permalink: changelogs/changelog-105-902
+author: sprunk
+---
+
+The changelog since release 105-861 **until minor release 105-902**, which happened in April 2022.
+
+## Caveats
+* Default trees are now provided by basecontent. Chopped engine trees (removed engine tree generators and renderers). Trees have models/textures donated by 0 A.D. so these have outlived their utility.
+
+## Features and fixes
+
+### GLDB queries
+
+{: .warning }
+> This feature only existed until release 105-2314 and is now removed.
+
+Add `Spring.MakeGLDBQuery(bool forced) → bool ok` to create a query to the OpenGL drivers database. There's generally only one query at a time allowed, forcing rewrites it.
+
+Add `Spring.GetGLDBQuery(bool blocking)` to receive the results of the query made with the call above.
+Blocking means it will wait for the answer so probably only use it in the lobby and not the game.
+Possible returns:
+* `nil` if there was no query or the query failed completely
+* `false` if it's not ready yet and call isn't blocking
+* `true, false` if it finished and drivers don't have issues
+* `true, true, number glMajor, number glMinor, string URL, string driver`.
+
+### Texturing
+* engine can load HDR textures (`.hdr` format/extension) with automatic upcasting to FLOAT internal storage format (takes 4x more memory). No user side changes are needed
+* LegacyAtlas now produces mipmaps. Previously it never did.
+* s3o textures reload fix for Assimp models
+
+### Defs
+* fix unitdef.useFootPrintCollisionVolume. Now the `useFootPrintCollisionVolume` tag takes precedence over the default sphere that appears when `collisionVolumeScales` are zero.
+* basecontent `setupdefs.lua` fills `UnitDefs[x].hasShield` correctly
+* added modrules to set default flanking min/max damages, though this was already possible (and is better done) via unitdefs_post.
+
+### Miscellaneous
+* add boolean springsetting `RotOverheadClampMap`, default true, disabling it allows the rotatable overhead camera to move outside of map boundaries.
+* selection keys: add new filter `Guarding` (first command in the queue is guard) and new count `SelectClosestToCursor` (always a single unit, doesn't work with `SelectNum_X` and doesn't pick the second-closest if appending, etc).
+* area-reclaim: always ignore wrecks being rezzed (now the CTRL modifier is only for features with `autoreclaimable = false` in their def; use single-target reclaim for things being rezzed)
+* fixed `Spring.TraceScreenRay()` failing to hit terrain
diff --git a/changelogs/changelog-105-941.markdown b/changelogs/changelog-105-941.markdown
new file mode 100644
index 0000000000..d97c8b5dff
--- /dev/null
+++ b/changelogs/changelog-105-941.markdown
@@ -0,0 +1,25 @@
+---
+layout: post
+title: Release 105-941
+parent: Changelogs
+permalink: changelogs/changelog-105-941
+author: sprunk
+---
+
+The changelog since release 105-902 **until minor release 105-941**, which happened in May 2022.
+
+### Drawing
+* enable 3D and cubemaps for luatextures
+* `Spring.GetRender{Units,Features}` now support GL4 CUS.
+* add `Spring.Clear{Units,Features}PreviousDrawFlag() → nil`
+* add `Spring.GetRender{Units,Features}DrawFlagChanged(bool returnMasks = false) → changedIDs[, changedMasks]`
+
+### Timers
+ * Add new `Spring.GetTimerMicros() → number`, which gets timers in usec precision
+ * `Spring.DiffTimers` now takes a fourth arg, if you want usec precision in millisecond numbers, and passed in microsec level timers
+
+### Miscellaneous
+* added a new 'b' designator for yardmaps to declare an area that is buildable, but is not walkable. This allows for the current method of upgradable buildings to create a locking pattern that won't break pathing.
+* add `group [subcommand] N` action handlers where subcommand is one of `add`, `set`, `selecttoggle`, `selectadd`, or `selectclear`. This allows for more granular group binding control than `groupN` (which checks for keyboard modifiers in the action). When subcommand is not present, select group N.
+* Skirmish AI: call `PostLoad()` after load event and only if `loadSupported != yes`
+* accept an integer quality argument in `screenshot` action, as in `screenshot [format] [quality]`.
diff --git a/changelogs/changelog-105-966.markdown b/changelogs/changelog-105-966.markdown
new file mode 100644
index 0000000000..0ff4549337
--- /dev/null
+++ b/changelogs/changelog-105-966.markdown
@@ -0,0 +1,58 @@
+---
+layout: post
+title: Release 105-966
+parent: Changelogs
+permalink: changelogs/changelog-105-966
+author: sprunk
+---
+
+The changelog since release 105-941 **until minor release 105-966**, which happened in June 2022.
+
+## Caveats
+* removed LOD unit rendering (far 2d sprites) including `/distdraw` command and `UnitLodDist` springconfig entry
+* hovercraft are no longer forced to be upright regardless on the terrain slope they are travelling over, they now obey the `upright` unit def.
+* the `Up+` modifier in keybinds is now deprecated, a warning is displayed if attempted to bind. No change in behaviour though.
+
+## Features and fixes
+
+### Keybinds
+* `wupget:KeyPress` and `wupget:KeyRelease` callins receive an additional scanCode parameter.
+* `sc_foo` keysets are introduced for scancodes and handled in conjunction with keycode bindings preserving the order on which the actions where bound. `Any+` order is preserved (`Any+` bindings are at the bottom)
+* `/keysave` and `/keyprint` now preserve the binding order, providing a better output
+* extend `Spring.GetKeyBindings` to receive an additional keyset string to retrieve scancode actions
+* add `Spring.GetScanSymbol` callout in a similar fashion to `Spring.GetKeySymbol`
+* basecontent wupget handlers are modified to support scancodes. Games that wish to support lua actions via scancodes are recommended to adopt these changes, otherwise behavior should be identical to before.
+* fix issue where keysets could be bound to duplicate actions when the action line contained different comments
+* fix issue where name retrieval for keycodes would never return a non hex value, this affected the label parameter of KeyPress and KeyRelease callins
+* fix issue where `Spring.GetKeyBindings` would return incorrect actions when keychains were bound, e.g. if actions bound to m,n and b,n both would be returned on GetKeyBindings('n') regardless of the previous pressed key
+
+### Rendering
+* add `gl.GetEngineAtlasTextures("$explosions" | "$groundfx") → { texName = {x1, y1, x2, y2}, texName = {...}, ... }`.
+* atlasses now have a deterministic order given an identical alphabetical ordering of identically sized textures. This allows Lua construction of pairs of atlasses, e.g. pairs of diffuse and normal maps for multiple decals.
+
+### Aircraft smooth mesh
+* The Smooth Height Mesh will now respond to changes in the height map and recalculate itself over the impacted area. This means aircraft using the smooth mesh will continue to be able to navigate appropriately after extended battles have modified the terrain significantly.
+* smooth mesh can now be disabled with the boolean modrule `system.enableSmoothMesh`.
+
+### Original heightmap
+Lua can now change what is considered the "original" heightmap, mostly for random map generators. These new interfaces work the same as the existing ones for the current heightmap.
+* `Spring.AdjustOriginalHeightMap(x1, z1[, x2, z2], height) → nil`, adds height.
+* Spring.LevelOriginalHeightMap(x1, z1[, x2, z2], height) → nil`, sets height.
+* `Spring.RevertOriginalHeightMap(x1, z1[, x2, z2], factor) → nil`, reverts to the "original original" heightmap as defined in the map file; `factor` is a 0-1 value that interpolates between the current and the map file height.
+* `Spring.SetOriginalHeightMapFunc(func, arg1, arg2, ...) → number totalChange`, calls a function that can call one of the two functions below.
+* `Spring.AddOriginalHeightMap(x, z, height) → newHeight`, can only be called from `Spring.SetOriginalHeightMapFunc` and adds height at given co-ordinates.
+* `Spring.SetOriginalHeightMap(x, z, height[, factor= 1]) → heightChange`, can only be called from `Spring.SetOriginalHeightMapFunc` and interpolates height at given co-ordinates towards the given value with given factor.
+
+Usage example:
+```lua
+Spring.SetOriginalHeightMapFunc(function(amplitude, period)
+ for z = 0, Game.mapSizeZ, Game.squareSize do
+ for x = 0, Game.mapSizeX, Game.squareSize do
+ Spring.SetOriginalHeightMap(x, z, 100 + amplitude * math.cos((x + z) / period))
+ end end
+end, 123, 456) -- args to pass (in this case amplitude and period)
+```
+
+### Miscellaneous
+* add `Spring.AddUnitExperience(unitID, delta_xp)`. Can subtract, but the result will be clamped to 0 if negative.
+* fixed the `/crash` command to crash properly.
diff --git a/changelogs/running-changelog.markdown b/changelogs/running-changelog.markdown
new file mode 100644
index 0000000000..b221d91951
--- /dev/null
+++ b/changelogs/running-changelog.markdown
@@ -0,0 +1,205 @@
+---
+layout: post
+title: Running changelog
+parent: Changelogs
+permalink: changelogs/running-changelog
+author: sprunk
+---
+
+This is the changelog **since version 2590**.
+
+# Caveats
+These are the entries which may require special attention when migrating:
+* missing models crash the game in order to avoid desyncs (for real this time).
+There's a pop-up, for external detection look for error messages in the infolog.
+* a bunch of changes in Lua events for damage and death, see below.
+* moved sky rendering right after the terrain rendering and before `DrawWorldPreUnit`.
+This will affect things drawn in the pre-unit layer by wupgets.
+* changing camera mode (TA, rotatable overhead etc) now tries to keep rotation/position
+instead of going back to the rotation/position it was last in when in that mode.
+* allow the attack command onto units out of map. Ground-targeted still not allowed.
+* allow the guard command when either the guard or the guardee are out of map.
+* weapons with `groundBounce = true` will now bounce even if `numBounces`
+is left undefined. This is because `numBounces = -1`, which is the default
+value, will now result in infinite bounces as intended instead of 0.
+* `GroundDecals` springsetting is now a boolean. The previous semantic of that
+parameter serving as a decal duration multiplier is gone. Use the new `scarTTL`
+weapon def tag to increase durations if needed.
+* explicitly specified weapon def `scarTTL` value is now the actual TTL.
+Previously it was multiplied by the ground decals level springsetting,
+which defaults to 3, so if you had tweaked scars they may turn out to have
+significantly different TTL now.
+* live mobile units' footprint is now the size of their movedef. This affects
+things like responding to bugger-off, blocking construction, or the size of their
+built-in selection square. Note that unit defs are unaffected and that individual
+units adjust if their movedef changes at runtime.
+* support for non-power-of-2 textures, float textures, and frame buffer objects
+is now mandatory. Apparently these were all common 15 years ago already so should
+be safe even for relative potatoes.
+* removed `tdfID` from weapon defs, including the WeaponDefs table. Any remaining
+interfaces receive the weaponDefID instead.
+* files in an archive's root folder with a name starting with dot are now ignored
+for the purpose of checksum (as if `springignore.txt` had `\..*` on the first row,
+this cannot be overridden)
+* archive scanner version changed to 17, won't be able to reuse an old archive cache.
+Expect a rescan of archives.
+
+
+# Features
+
+### rmlUI
+RmlUI is here! It is a GUI framework that will let you create widgets using web technologies (html, css and all that nonsense).
+See [the reference](https://mikke89.github.io/RmlUiDoc/pages/lua_manual/api_reference.html) for the Lua API; it is available to LuaUI under the `RmlUi` global.
+Differences compared to upstream:
+* `context:LoadDocument(filepath, widget)` takes a second argument that is the widget itself,
+so that inline event handlers can call its functions like so: `onclick="widget:OnReloadClick()"`.
+* `document` is available too.
+* our data model is reactive only on the first depth level.
+All keys for reactivity are expected to exist at creation.
+Behaviour is unspecified if the same data model is "opened" repeatedly.
+* there is a custom html tag called `texture` that provides access to engine textures, e.g. `src="$heightmap"`
+
+Look for upcoming examples in games, especially BAR.
+The API is currently v1 and may be subject to change, use the new `Engine.FeatureSupport.rmlUiApiVersion` var for compat (see below).
+
+### Feature support table
+
+There is a new `Engine.FeatureSupport` table containing various feature support checks.
+Use for engine version compatibility, similar to existing `Script.IsEngineMinVersion`,
+except self-documenting and does not assume linear availability / commit numbering.
+
+So far contains two vars:
+* `rmlUiApiVersion` = `1`
+* `hasExitOnlyYardmaps` = `true`
+
+More will be added in the future as new features are added.
+
+### Death events
+* `wupget:UnitDestroyed` will pass the builder as the killer if a unit gets reclaimed. Note that
+reclaim still does not generate `UnitDamaged` events.
+* `wupget:UnitDestroyed` now receives a 7th argument, weaponDefID, with the cause of death.
+Widgets receive this info regardless of LoS on the attacker (no new hax, `UnitDamaged` already did this).
+* the weaponDefID above is never `nil`, all causes of death are attributable including things like
+"was being built in a factory, got cancelled" or "died automatically due to `isFeature` def tag".
+Added a bunch of `Game.envDamageTypes` constants for this purpose. See the table at the bottom of the page.
+* the 1000000 damage that applies to units being transported in a non-`releaseHeld` transport when it
+dies for non-selfD reasons will now be attributed to the new `TransportKilled` damage type in `UnitDamaged`,
+previously was `Killed`.
+* construction decay now produces an event: `wupget:UnitConstructionDecayed(unitID, unitDefID, unitTeam, timeSinceLastBuild, iterationPeriod, part)`.
+Time and iteration period are in seconds, part is a fraction.
+
+### Dolly camera
+Added a dolly camera that follows a predetermined path, activated via Lua.
+Added the following functions to the `Spring` table, check the main Lua API docs for their signatures and what they do:
+* `RunDollyCamera`
+* `PauseDollyCamera`
+* `ResumeDollyCamera`
+* `SetDollyCameraMode`
+* `SetDollyCameraPosition`
+* `SetDollyCameraCurve`
+* `SetDollyCameraLookCurve`
+* `SetDollyCameraLookPosition`
+* `SetDollyCameraLookUnit`
+* `SetDollyCameraRelativeMode`
+* `SolveNURBSCurve`
+
+### Other camera work
+* add `CamTransitionMode` integer springsetting to control how discrete camera movement is interpolated.
+0 - exponential decay, 1 - spring dampened, 2 - spring dampened timed, 3 - linear. Defaults to 0 - exponential decay.
+* add `CamSpringHalflife` float springsetting, the time in milliseconds at which the timed spring dampened interpolation mode should be approximately halfway towards the goal.
+* add `OverheadMinZoomDistance` and `CamSpringMinZoomDistance` float springsettings to set minimum camera zoom distances for their respective cameras. Note that this is the distance in the looking direction of the frustum and not height.
+* changing camera mode (TA, rotatable overhead etc) now tries to keep rotation/position
+instead of going back to the rotation/position it was last in when in that mode.
+
+### Out of map orders
+* allow centering area commands out of map.
+* allow the attack command onto units out of map. Ground-targeted still not allowed
+* allow the guard command when either the guard or the guardee are out of map
+
+### Lua microoptimisation
+* add `math.normalize(x1, x2, ...) → numbers xn1, xn2, ...`. Normalizes a vector. Can have any dimensions (pass and receive each as a separate value).
+Returns a zero vector if passed a zero vector.
+* add `Spring.AllocateTable(arraySlots, hashSlots) → {}`. Returns an empty table with more space allocated.
+Use as a microoptimisation when you have a big table which you are going to populate with a known number of elements, for example `#UnitDefs`.
+* `Script.LuaXYZ.Foo()` no longer produces a warning if there is no such function in the LuaXYZ environment. In practice this means you
+can avoid calling `Script.LuaXYZ("Foo")` each time, but it can still be a good idea e.g. to avoid needless calculation or to warn manually.
+* add variadic variants of LUS `Turn`, `Move`, `Spin`, `StopSpin`, `Explode`, and `SetPieceVisibility`, each has the same name with "Multi" prepended (so `MultiTurn` etc).
+These accept multiple full sets of arguments compared to the regular function so you can avoid extra function calls.
+
+### Rendering
+* engine now draws the sky before the `wupget:DrawWorldPreUnit` layer.
+* `wupget:DrawWorldPreParticles` now has four boolean parameters depending on which phase is being drawn: above water, below water, reflection, refraction.
+They aren't mutually exclusive.
+* drawing now occurs once every 30s when minimized (was intended already but didn't actually happen).
+* add `MinSampleShadingRate` springsetting, float between 0 and 1, default 0. A value of 1 indicates that each sample in the framebuffer should be independently shaded. A value of 0 effectively allows rendering to ignore sample rate shading. Any value between 0 and 1 allows the GL to shade only a subset of the total samples within each covered fragment.
+* add new 'm' character to Lua texture options, which disables trilinear mipmap filtering for both DDS and uncompressed textures.
+* particles (incl. ground decals) now have 4 levels of mipmaps.
+
+### Defs
+* add `windup` weapon def tag. Delay in seconds before the first projectile of a salvo appears.
+Has the same mechanics as burst (obeys the recent out-of-arc tags and the delay can be set/read via burst Lua API).
+* live mobile units' footprint is now the size of their movedef. This affects
+things like responding to bugger-off, blocking construction, or the size of their
+built-in selection square. Note that unit defs are unaffected and that individual
+units adjust if their movedef changes at runtime.
+* weapon defs now have a new `animParamsN` (N = 1-4) tag for flipbook animations for given texture 1-4, same format as CEGs (three numbers: sprite count X, Y, and duration).
+* removed `tdfID` from weapons.
+
+### Archive scanning
+* fixed timezone changes (e.g. daylight saving time) causing a complete archive rescan on Windows.
+* the loadscreen now shows more info when scanning archives.
+* files in an archive's root folder with a name starting with dot are now ignored
+for the purpose of checksum (as if `springignore.txt` had `\..*` on the first row,
+this cannot be overridden)
+* archive scanner version changed to 17, won't be able to reuse an old archive cache.
+Expect a rescan of archives.
+* optimize performance when scanning files on a HDD.
+* fixed the archive scanner sometimes failing due to having more files opened in parallel than the OS allows.
+
+### Misc
+* add `SelectThroughGround` float springsetting. Controls how far through ground you can single-click a unit. Default is 200, in elmos (same behaviour as previous).
+* add `Spring.ForceUnitCollisionUpdate(unitID) → nil`. Forces a unit to have correct collisions. Normally, collisions are updated according
+to the `unitQuadPositionUpdateRate` modrule, which may leave them unable to be hit by some weapons when moving. Call this for targets of important
+weapons (e.g. in `script.FireWeapon` if it's hitscan) if the modrule has a value greater than 1 to ensure reliable hit detection.
+* built-in endgame graphs have a toggle for log scale instead of linear.
+* `gl.SaveImage` can now save in the `.hdr` format (apparently).
+* `pairs()` now looks at the `__pairs` metamethod in tables, same as in Lua 5.2.
+
+## Fixes
+* fix draw position for asymmetric models, they no longer disappear when not appropriate.
+* fix streaming very small sound files.
+* fix `Spring.SetCameraTarget` reverting to the old position.
+* fix the `/iconsHideWithUI` command not saving the relevant springsetting properly.
+* fix `Spring.GetFeatureSelectionVolumeData` missing from the API since about 105-800ish.
+* fix `/groundDecals` not enabling decals if they were disabled at engine startup.
+* fix rightclicking and area-commands sometimes failing to include radar dots if they wobbled too far from real position.
+* fix a crash when loading unknown glyphs from a font.
+* fix runtime metalmap adjustments not being saved/loaded (would use map default).
+* fix metal extractors not being saved/loaded correctly (would allow duplicates).
+* fix an issue where sometimes `C:\a\_temp\msys\msys64\var\cache\fontconfig` would be created on the user's disk.
+* the `quadFieldQuadSizeInElmos` modrule now only accepts powers of two instead of breaking at the edges of the map.
+
+### Death damage types listing
+
+| Game.envDamageTypes.??? | Description |
+|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| AircraftCrashed | Aircraft hitting the ground |
+| Kamikaze | Unit exploding due to its kamikaze ability |
+| SelfD | Unit exploding after a self-D command and countdown |
+| ConstructionDecay | Abandoned nanoframe disappearing (the process itself is HP removal) |
+| Reclaimed | Killed via reclaim (the process itself is HP removal) |
+| TurnedIntoFeature | Unit dying on completion, without explosion, due to `isFeature` def tag |
+| TransportKilled | Unit was in transport which had no `releaseHeld`. If the transport was not self-destructed, the unit also receives 1000000 damage of this type before dying. |
+| FactoryKilled | Unit was being built in a factory which died. No direct way to discern how exactly the factory died currently, you'll have to wait for the factory's death event. |
+| FactoryCancel | Unit was being built in a factory but the order was cancelled. |
+| UnitScript | COB unit script ordered the unit's death. Note that LUS has access to normal kill/damage interfaces instead. |
+| SetNegativeHealth | A unit had less than 0 health for non-damage reasons (e.g. Lua set it so). |
+| OutOfBounds | A unit was thrown way out of map bounds. |
+| KilledByCheat | The `/remove` or `/destroy` commands were used. |
+| KilledByLua | Default cause of death when using `Spring.DestroyUnit`. |
+
+`KilledByLua` is guaranteed to be the "last" value, so you can define your own custom damage types for Lua mechanics with a guarantee of no overlap via e.g.
+```
+Game.envDamageTypes.CullingStrike = Game.KilledByLua - 1
+Game.envDamageTypes.SummonTimerExpired = Game.KilledByLua - 2
+```
diff --git a/development.markdown b/development.markdown
new file mode 100644
index 0000000000..96ac79495f
--- /dev/null
+++ b/development.markdown
@@ -0,0 +1,10 @@
+---
+layout: default
+title: Development
+nav_order: 4
+has_children: true
+permalink: development
+---
+
+# Development
+{: .no_toc }
diff --git a/development/build-with-docker.markdown b/development/build-with-docker.markdown
new file mode 100644
index 0000000000..82e3faafc0
--- /dev/null
+++ b/development/build-with-docker.markdown
@@ -0,0 +1,139 @@
+---
+layout: post
+title: Building with Docker
+parent: Development
+permalink: development/build-with-docker
+author: verybadsoldier
+---
+
+# Building with Docker
+
+## Introduction
+
+There is a docker image available that is based on Ubuntu being able to compile the engine.
+
+The image can be used as a one-shot command to compile the engine and provide the artifacts by a docker volume but it can also be used as a development environment.
+
+### Docker Image
+There are two ways to obtain the docker image.
+
+1. Checkout the repository, change into the directory `docker-build` and build the image yourself using the command: `docker build -t springrts-build .`. Additional arguments can be passed to configure the image creation process (see below).
+
+2. The image is also available on [Docker Hub](https://hub.docker.com/repository/docker/verybadsoldier/springrts-build). It will be auto-downloaded if you use it in regular docker commands like `docker run verybadsoldier/springrts-build:latest build`. So you can replace the image name with `verybadsoldier/springrts-build:latest` in all docker commands described below.
+The image on Docker Hub might not be updated at all times but we try to keep it up-to-date as good as possible.
+
+When building the image the following arguments are available:
+
+| Parameter| Default | Description |
+|:----------:|:-------------:|:------|
+| mxe_version | 02852a7b690aa411ce2a2089deea25a7292a33d6 | Version of MXE to use. This can be a commit hash but also a tag or branch name |
+| mxe_gcc | gcc11 | Defines which gcc plugin to use in MXE |
+| cmake_version | 3.16.* | Defines a CMake version string to be used when installing CMake via `pip` |
+| ccache_version | v4.5.1 | Version of ccache to use. This can be a commit hash but also a tag or branch name |
+
+## Quickstart
+
+### Building
+
+To build branch `BAR105` from `https://github.com/beyond-all-reason/spring` (default values) run this command:
+```bash
+docker run -it springrts-build build
+```
+
+The output artifacts can be found in a volume linked to the container. Accessible e.g. via the Docker Desktop application.
+
+### Develop
+To start a development environment:
+```bash
+docker run -it springrts-build dev
+```
+
+You will get a bash shell with the spring source code checked out ready to make modifications and compile by typing `make` or `cmake --build .`:
+```
+----------------------------------------------
+SpringRTS development environment has been set up successfully
+Source code directory: /spring
+Build directory: /spring/build
+----------------------------------------------
+ root@e0d3fbe4fffd:/spring/build$ _
+```
+
+## General
+The image is based on Ubuntu 18.04 so created Linux binaries are runnable on that version and later. Windows binaries are build using a MXE cross-compile environment.
+
+The image utilizes `ccache` to speedup consecutive compilation processes. It is recommended to reuse the cache data in different containers by configuring the ccache volume accordingly.
+
+The image can be used with different commands that are described below:
+1. `build`
+2. `dev`
+3. `shell` (just open a bash shell)
+
+The build process consists of multiple steps. Each step is represented by a shell script and they are run consecutively:
+
+1. `00_setup.sh`
+2. `01_clone.sh`
+3. `02_configure.sh`
+4. `04_fill_portable_dir_linux-64.sh`and `04_fill_portable_dir_windows-64.sh`
+5. `05_fill_debugsymbol_dir.sh`
+6. `06_fill_build_options_file.sh`
+7. `07_pack_build_artifacts.sh`
+8. `08_copy_to_publish_dir.sh`
+
+## Parameters
+Both commands can be configured with these arguments:
+
+| Parameter| Default | Description |
+|:----------:|:-------------:|:------|
+| -b | BAR105 | The branch to build from the spring project |
+| -u | https://github.com/beyond-all-reason/spring | URL to a Spring Git repository |
+| -a | https://github.com/beyond-all-reason | Prefix for the URLs used to clone auxiliary repos. The following URL will e.g. be cloned: https://github.com/beyond-all-reason/BARbarIAn |
+| -d | 0 | Dummy mode: to not actually clone or compile but just produce zero-size artifacts
+| -e | 1 | Enable ccache
+| -c | \ | Configures gcc's `-march` and `-mtune`flag
+| -r | -O3 -g -DNDEBUG | CXX flags for RELWITHDEBINFO config
+| -f | \ | CXXFLAGS and CFLAGS flags for gcc
+| -s | 1 | Enable stripping of symbols from artifacts
+| -z | | Enable generation ccache debug data in /ccache_dbg
+| -p | windows-64 | Which target platform to setup build for (use "linux-64" or "windows-64")
+
+### Building (command `run`)
+
+The container can be started by passing `run` as the first parameter to run the compilation process for one target platform and produce an archive package that will contain the runnable engine with all library dependencies.
+
+The following directories contain output data and can be used as a volume to access the files:
+1. `/publish` - This is the directory the build process will copy all produced artifacts into
+2. `/ccache` - The image is using `ccache` to speed up the build process. To make use of the cache you have to run different build runs using the same cache data directory.
+2. `/ccache_dbg` - Directory where ccache debug data will be placed
+
+```bash
+docker run -v D:\myspringbuild:/publish -it springrts-build
+```
+
+### Building from another GitHub Repository
+
+```bash
+Build branch `gl4` from repository at `https://github.com/beyond-all-reason/spring`:
+docker run -it springrts-build build -u https://github.com/beyond-all-reason/spring -b gl4 -p linux-64
+```
+
+## Development
+
+The image can also be used as a development environment. When starting the development mode then only the first three steps are executed:
+1. `00_setup.sh`
+2. `01_clone.sh`
+3. `02_configure.sh`
+
+So the source code will be cloned into the container and the build will be configured using CMake. You will find yourself inside the development shell ready to start the compilation process.
+In you started the container with a raw `/bin/bash` shell (didn't use `build` or `dev` command) you can start the dev shell manually by running `dev.sh` with the usual arguments.
+
+When working in the development shell you can always start the step scripts manually if needed. But be aware that running e.g. `01_clone.sh` will wipe the `/spring` directory completely and create a clean checkout. So make sure to not lose your changes!
+As some of the steps will modify shell variables all scripts should be sourced (instead of started regularly), e.g. `. /scripts/01.clone.sh`.
+
+### Reconfiguring
+In case you want to switch the configuration inside the development shell you you can run `00_setup.sh `.
+
+For example:
+
+```bash
+. /scripts/00_setup.sh -p linux-64
+```
diff --git a/development/build-without-docker.markdown b/development/build-without-docker.markdown
new file mode 100644
index 0000000000..413322692f
--- /dev/null
+++ b/development/build-without-docker.markdown
@@ -0,0 +1,229 @@
+---
+layout: post
+title: Building without Docker
+parent: Development
+permalink: development/build-without-docker
+author: p2004a
+---
+
+# Building without Docker
+
+The [https://github.com/beyond-all-reason/spring/tree/BAR105/docker-build/scripts](scripts) folder is the source of truth and best reference to figure out how to invoke and configure things.
+
+## Compilation
+
+It's arguable that compilation of spring for both Linux and Windows is just easier from Linux. So, we will only describe compilation on Linux.
+- Windows: There is [WSL](https://docs.microsoft.com/en-us/windows/wsl/) and it works great.
+- Linux: To not have to install all the dev dependencies, compilers etc directly in the base system, and for compatibility, you can use [distrobox](https://github.com/89luca89/distrobox) and develop there. It's fine to do it without that, but it's just very convenient.
+
+This instruction was tested on Debian based system and Arch, but it should also work on all other Linux distributions once you figure out the list of packages to install.
+
+### Install system dependencies
+
+#### Debian based systems
+
+Compilers, basic utilities, and helpful for developing:
+
+```bash
+sudo apt-get install -y cmake g++ ccache ninja-build clang lld git clangd socat \
+ python3-pip g++-mingw-w64-x86-64-posix
+sudo pip install compdb
+```
+
+Spring engine dependencies
+
+```bash
+sudo apt-get install -y doxygen libsdl2-dev libdevil-dev libcurl4-openssl-dev \
+ p7zip-full libopenal-dev libogg-dev libvorbis-dev libunwind-dev libfreetype-dev \
+ libglew-dev libminizip-dev libfontconfig-dev libjsoncpp-dev
+```
+
+#### Arch
+
+Compilers, basic utilities, and helpful for developing (except for mingw cross compiler that is instelled for Debian based distros):
+
+```bash
+sudo pacman -S base-devel cmake ccache git openssh ninja lld socat clang python-pip
+sudo pip install compdb
+```
+
+Recoil engine dependencies
+
+```bash
+sudo pacman -S curl sdl2 devil p7zip openal libogg libvorbis libunwind freetype2 glew \
+ minizip fontconfig jsoncpp
+```
+
+And to make sure that openal has some functioning sound backend:
+
+```bash
+sudo pacman -S libpulse
+```
+
+### Fetch source
+
+```bash
+git clone https://github.com/beyond-all-reason/spring.git
+cd spring
+git submodule update --init --recursive
+# for windows compilation
+git clone https://github.com/beyond-all-reason/mingwlibs64.git mingwlibs64
+```
+
+### Compilation
+
+This part is the most annoying: configuring build is done using cmake, and the command lines are quite large.
+
+#### Toolchains
+
+First, you should have a few toolchains configured. Toolchains select the compiler and target operating system. You can store them in the `toolchain` directory in the spring repo. Linux toolchains use `lld` linker as it's much faster.
+
+`toolchain/clang_x86_64-pc-linux-gnu.cmake`:
+```cmake
+SET(CMAKE_SYSTEM_NAME Linux)
+SET(CMAKE_C_COMPILER "clang")
+SET(CMAKE_CXX_COMPILER "clang++")
+SET(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
+SET(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
+SET(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
+```
+
+`toolchain/gcc_x86_64-pc-linux-gnu.cmake`:
+```cmake
+SET(CMAKE_SYSTEM_NAME Linux)
+SET(CMAKE_C_COMPILER "gcc")
+SET(CMAKE_CXX_COMPILER "g++")
+SET(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
+SET(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
+SET(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
+```
+
+`toolchain/gcc_x86_64-pc-windows-gnu.cmake`:
+```cmake
+SET(CMAKE_SYSTEM_NAME Windows)
+SET(CMAKE_C_COMPILER "x86_64-w64-mingw32-gcc-posix")
+SET(CMAKE_CXX_COMPILER "x86_64-w64-mingw32-g++-posix")
+SET(CMAKE_RC_COMPILER "x86_64-w64-mingw32-windres")
+SET(WINDRES_BIN "x86_64-w64-mingw32-windres")
+SET(CMAKE_DLLTOOL "x86_64-w64-mingw32-dlltool")
+SET(DLLTOOL "x86_64-w64-mingw32-dlltool")
+```
+
+#### CMake command lines
+
+With cmake we are building outside of the source, so create directory like `builddir-win`, or `builddir-dbg` and inside them we can run cmake invocations. There are plenty of possible configuration so we will just list a bunch that can be used as a starting point.
+
+In all of them:
+- We use Ninja generator, as Ninja is the quickest to actually execute the build process, scan for changes etc.
+- Using ccache to make next compilation quicker
+- Install dir is simply `install`, so that after configuing build with cmake, you can just run `ninja && ninja install` and get all the files ready for usage in the `install` directory in builddir.
+
+----
+Basic release with debug info, shared libraries Linux build with GCC:
+
+```bash
+cmake \
+ -DCMAKE_TOOLCHAIN_FILE="../toolchain/gcc_x86_64-pc-linux-gnu.cmake" \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
+ -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O3 -g -DNDEBUG -fdiagnostics-color=always" \
+ -DCMAKE_C_FLAGS_RELWITHDEBINFO="-O3 -g -DNDEBUG -fdiagnostics-color=always" \
+ -DCMAKE_BUILD_TYPE=RELWITHDEBINFO \
+ -DAI_TYPES=NATIVE \
+ -DINSTALL_PORTABLE=ON \
+ -DCMAKE_USE_RELATIVE_PATHS:BOOL=1
+ -DBINDIR:PATH=./ \
+ -DLIBDIR:PATH=./ \
+ -DDATADIR:PATH=./ \
+ -DCMAKE_INSTALL_PREFIX=install \
+ -G Ninja \
+ ..
+```
+
+Fast unoptimized debug Linux shared libraries build with Clang and generation of [`compile_commands.json`](https://clang.llvm.org/docs/JSONCompilationDatabase.html) file.
+```bash
+cmake \
+ -DCMAKE_TOOLCHAIN_FILE="../toolchain/clang_x86_64-pc-linux-gnu.cmake" \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
+ -DCMAKE_CXX_FLAGS_DEBUG="-O1 -g -fcolor-diagnostics" \
+ -DCMAKE_C_FLAGS_DEBUG="-O1 -g -fcolor-diagnostics" \
+ -DCMAKE_BUILD_TYPE=DEBUG \
+ -DDEBUG_MAX_WARNINGS=OFF \
+ -DAI_TYPES=NATIVE \
+ -DINSTALL_PORTABLE=ON \
+ -DCMAKE_USE_RELATIVE_PATHS:BOOL=1 \
+ -DBINDIR:PATH=./ \
+ -DLIBDIR:PATH=./ \
+ -DDATADIR:PATH=./ \
+ -DCMAKE_INSTALL_PREFIX=install \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
+ -G Ninja \
+ ..
+```
+
+Windows release with minimal line debug info static cross compilation with mingw64:
+```bash
+cmake \
+ -DCMAKE_TOOLCHAIN_FILE="../toolchain/gcc_x86_64-pc-windows-gnu.cmake" \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
+ -DCMAKE_BUILD_TYPE=RELWITHDEBINFO \
+ -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O3 -g1 -DNDEBUG -fdiagnostics-color=always" \
+ -DCMAKE_C_FLAGS_RELWITHDEBINFO="-O3 -g1 -DNDEBUG -fdiagnostics-color=always" \
+ -DAI_TYPES=NATIVE \
+ -DINSTALL_PORTABLE=ON \
+ -DCMAKE_INSTALL_PREFIX:PATH=install \
+ -GNinja ..
+```
+
+----
+
+### Source code completion
+
+In the clang debug above, we also enabled creation of `compile_commands.json` file that can be then used by IMHO the best C++ language server [clangd](https://clangd.llvm.org/). The main problem with cmake generated compilation databases is that it doesn't contain entries for header files. That can be fixed with [compdb](https://github.com/Sarcasm/compdb) utility installed in the beggining. Running from top repo directory:
+
+```bash
+compdb -p builddir-clang/ list > compile_commands.json
+```
+
+Clangd will then just pick it up.
+
+If running in distrobox, and with clangd in distrobox, you might need to override clangd invocation in the LSP supporting editor to something like: `distrobox enter --no-tty {container_name} spring -- socat tcp-listen:{port},reuseaddr exec:clangd`
+
+When developing on Windows, with clangd on Linux, and WSL container mapped to `L:` drive, the invocation might looks like this: `wsl.exe socat tcp-listen:${port},reuseaddr exec:'clangd --path-mappings=L:/home=/home,L:/usr=/usr'`. Only drawback is that ofc `#ifdef _WIN32` blocks won't have completion in such setup.
+
+Example `spring.sublime-project` for Sublime Text on Linux with [LSP](https://lsp.sublimetext.io/) server pckages:
+
+```json
+{
+ "folders":
+ [
+ {
+ "path": "."
+ }
+ ],
+ "settings": {
+ "tab_size": 4,
+ "LSP": {
+ "clangd": {
+ "enabled": true,
+ "command": [
+ "distrobox",
+ "enter",
+ "--no-tty",
+ "spring",
+ "--",
+ "socat",
+ "tcp-listen:{port},reuseaddr",
+ "exec:clangd"
+ ],
+ "tcp_port": 0,
+ "scopes": ["source.c", "source.c++"],
+ "syntaxes": [
+ "Packages/C++/C.sublime-syntax",
+ "Packages/C++/C++.sublime-syntax",
+ ],
+ "languageId": "cpp",
+ }
+ }
+ }
+}
+```
diff --git a/development/profiling-with-tracy.markdown b/development/profiling-with-tracy.markdown
new file mode 100644
index 0000000000..77bcde4ca5
--- /dev/null
+++ b/development/profiling-with-tracy.markdown
@@ -0,0 +1,45 @@
+---
+layout: post
+title: Profiling with Tracy
+parent: Development
+permalink: development/profiling-with-tracy
+author: beherith
+---
+
+# How to profile the engine using tracy
+
+## Using a prebuilt binary:
+
+1. Download Tracy, unzip it anywhere: https://github.com/wolfpld/tracy/releases/tag/v0.9.1
+
+2. Get the exact engine version we are currently using from https://engine-builds.beyondallreason.dev/index.html
+
+3. Find the exact engine version folder you are currently using (for example, for BAR that would be `BAR/data/engine`)
+
+4. A. Rename the old `spring.exe` to `spring_vanilla.exe`, and extract `spring.exe` from the archive to this engine folder.
+
+5. A. Start the game as you would otherwise via the launcher.
+
+6. Launch `Tracy.exe`, and hit connect. If it throws an instrumentation error, connect again.
+
+![image](https://github.com/beyond-all-reason/spring/assets/109391/830e5c6e-b37f-48ab-9adc-cc297cefff46)
+
+8. Analyze profile.
+
+## Building with Tracy support
+
+The following options are available:
+
+- `TRACY_ENABLE`: Enable tracy profiling
+- `TRACY_ON_DEMAND`: On demand profiling is *slightly* more expensive, but it
+allows to run the build with tracing like regular build and attach late in game,
+where regular trace would just run out of memory because of size.
+- `TRACY_PROFILE_MEMORY`: Profile memory allocations. It's pretty expensive and
+some places that use raw malloc have to be used with care.
+- `RECOIL_DETAILED_TRACY_ZONING`: Enable additional detailed tracy zones (only enable this for testing/debugging)
+
+For example, building with docker and tracy support enabled:
+
+```bash
+./build.sh -o -t RELEASE -C -DTRACY_ENABLE=1 -p linux-64
+```
diff --git a/guides.markdown b/guides.markdown
new file mode 100644
index 0000000000..b06af0f4f5
--- /dev/null
+++ b/guides.markdown
@@ -0,0 +1,10 @@
+---
+layout: default
+title: Guides
+nav_order: 3
+has_children: true
+permalink: guides
+---
+
+# Guides
+{: .no_toc }
diff --git a/guides/choose-recoil.markdown b/guides/choose-recoil.markdown
new file mode 100644
index 0000000000..9730220f2b
--- /dev/null
+++ b/guides/choose-recoil.markdown
@@ -0,0 +1,43 @@
+---
+layout: post
+title: Choose Recoil
+parent: Guides
+permalink: guides/choose-recoil
+author: sprunk
+---
+
+## Choose Recoil!
+
+Picking the engine for your game is a very important decision to make.
+Here's how Recoil compares to other choices.
+
+### Why shouldn't I pick a general engine (Unity, Godot, etc)?
+ * Recoil has **built-in multiplayer RTS facilities**.
+This means systems such as pathfinding, targeting, the concept of unit commands, projectile physics, resource system, networking and synchronisation, replays, and many others.
+With general engines you'd need to bother integrating these yourself.
+ * Recoil **embraces open-source**.
+The engine is both free and welcoming to modification.
+The GPL license means that Recoil games can always share code with each other, though you can still make a successful commercial game: content/assets such as models and maps, or access to your servers, can remain proprietary.
+ * Recoil **is a platform**.
+There's a community of veteran RTS enthusiasts around the project that will gladly help you start out.
+Host your budding game on existing servers, reuse existing content, don't worry about issues such as distribution and marketing until your game is mature enough to take them on.
+
+### My game is heavily inspired by some existing game XYZ, why shouldn't I just mod it?
+ * Recoil **is an open-source platform**, see above.
+ * Recoil is **built with modding in mind**.
+Most commercial games require you to put in some effort to replace content, and often it is not possible to replace some deeper mechanics without major hackery.
+Meanwhile Recoil exposes interfaces for you to do everything you want.
+
+### I'm making a little Starcraft style mod. Why shouldn't I just use SC2 Arcade? It's also a platform for mods.
+ * well, perhaps you should! Recoil is oriented towards Total Annihilation style games, and SC2 is still a great platform for modding. **However:**
+ * Recoil is **constantly being developed**.
+Improvements and features are being added over time.
+If your game finds a feature missing you can talk to engine developers who will be glad to try and accomodate your needs.
+ * Recoil doesn't interfere with your **ownership of content**.
+Code has to be open-sourced, but full ownership of content is still yours, not Blizzard's.
+
+### Recoil is just a fork of Spring, why shouldn't I use the original?
+ * **active and easy to reach maintainers** who are happy to accomodate your game's needs.
+ * the API (incl. documentation), the feature set, and engine performance have **all improved and keep improving**, check out the changelog.
+ * most **major Spring games made the move** and are happy with it.
+ * if you still want to pick Spring, that's fine because **Recoil is very back-compatible so you can easily switch later**.
diff --git a/guides/configuration-variables.markdown b/guides/configuration-variables.markdown
new file mode 100644
index 0000000000..a8d45355f8
--- /dev/null
+++ b/guides/configuration-variables.markdown
@@ -0,0 +1,49 @@
+---
+layout: default
+title: Configuration Variables
+parent: Guides
+permalink: /guides/configuration-variables/
+---
+
+# Configuration Variables
+
+Recoil supports configuration variables that can be set during runtime
+(with
+[`Spring.{Set,Get}Config*`]({{ site.baseurl }}/ldoc/modules/UnsyncedCtrl.html#Engine_Config)
+) or via `springsettings.cfg`.
+
+Here we provide a list of them:
+
+
+
+
Name
+
Description
+
Values
+
+ {% for row in site.data.configs %}
+ {% assign row_data = row[1] %}
+
diff --git a/guides/headless-and-dedi.markdown b/guides/headless-and-dedi.markdown
new file mode 100644
index 0000000000..0907d59ea3
--- /dev/null
+++ b/guides/headless-and-dedi.markdown
@@ -0,0 +1,32 @@
+---
+layout: post
+title: Headless and dedicated
+parent: Guides
+permalink: guides/headless-and-dedi
+author: sprunk
+---
+
+## Overview
+
+Recoil builds some **extra binaries** that **behave differently** to the primary 'spring.exe' one.
+This guide briefly describes what they do and when to use them.
+
+### Headless
+
+The headless build **only does the simulation** and does **not do any rendering**.
+**Widgets still work** and are the main way to provide input, all rendering-related call-ins and call-outs just do nothing.
+
+It is **great for automated tasks** such as:
+ * gathering **data from replays, general analysis**. Spectate a replay, use a widget to gather data.
+ * simulating **AI vs AI** games (either for AI development, or just to produce something analyzable in conjunction with the above).
+ * simulate games played on a dedicated binary, to **verify results**. See below.
+ * be a game server for a multiplayer game, though only **on limited scale** (perhaps for important games like tournaments). See below.
+
+### Dedicated (aka dedi)
+
+Simulating games can get fairly heavy, such that a game server would **struggle to simulate more than a handful**.
+However, players simulate the game anyway, so **a server technically doesn't need to** - this is the idea behind the dedicated binary.
+It only collects and **relays network traffic without running the simulation**.
+
+Typically, dedicated servers are used for **automated hosting in large games** since they are quite lightweight.
+In theory this leaves games without a single source of truth, but **replays are still produced** which can be ran for verification **later, as needed**.
diff --git a/guides/lua-vbo-vao.markdown b/guides/lua-vbo-vao.markdown
new file mode 100644
index 0000000000..fee826b23e
--- /dev/null
+++ b/guides/lua-vbo-vao.markdown
@@ -0,0 +1,164 @@
+---
+layout: post
+title: Lua VBO and VAO
+parent: Guides
+permalink: guides/lua-vbo-vao
+author: lhog
+---
+
+# Lua VBO and VAO
+
+## Crash course
+
+### What is VBO/VAO
+
+Read excellent intro here: [Hello-Triangle](https://learnopengl.com/Getting-started/Hello-Triangle)
+
+{: .note-title }
+> TLDR
+>
+> It's a modern and most performant way of loading geometry attributes like position, color, normal vector, texture coordinates and generally any attribute you need to GPU for rendering.
+
+### What is VBO?
+
+VBO is simply an array of interleaved data.
+
+Interleaved means that data of different types go one after another forming an element.
+
+After the first element comes second element structured in the same way, etc. See:
+
+![image]({{ site.baseurl }}/assets/guides/lua-vbo-vao-1.png)
+
+### What types of VBO exist:
+
+Two main types of VBO are vertex buffer and index buffer.
+
+Vertex buffer holds vertex data, index buffer holds indices used to index into vertex buffer.
+
+One canonical example of the use of index buffer is drawing of rectangle.
+
+Rectangles have 4 vertices, but since GPU draws with triangles, rectangle needs to broken down into triangles. In this case triangles are specified as list of indices referencing vertex buffer to render the rectangle:
+
+![image]({{ site.baseurl }}/assets/guides/lua-vbo-vao-2.png)
+
+{: .note }
+Use of index buffer is optional, you can skip it, but you will have to duplicate vertex data instead to produce the same two triangles, which is often not desired, especially if you have big geometry.
+
+One additional more advanced type of VBO is instance buffer.
+
+Unlike vertex VBO, which defines per-vertex data, instance VBO defines per instance (per shape) data.
+
+For example you might want to draw exactly the same complex shape N times in N different places, this is where instancing makes sense. You can read more [here](https://learnopengl.com/Advanced-OpenGL/Instancing)
+
+As said, in Recoil instancing is done by means of instance buffer (alternative implementations are possible by out of scope of this basic tutorial):
+
+![image]({{ site.baseurl }}/assets/guides/lua-vbo-vao-3.png)
+
+Here in the example we use instance buffer to offset instances of rectangle in screen space, other possibilities exist too: rotate, re-color, etc.
+
+### What is VAO
+
+VAO serves as glue to tie together various VBOs above, description of their type, description of what each individual attribute inside each VBO means: name, size, type, etc. VAO allows one to define attributes completely flexible.
+
+Usually you want something like position, color, texture coordinates, but you can absolutely skip each one and supply whatever information you need.
+
+Below the schematic of what VAO is to VBOs:
+
+![image]({{ site.baseurl }}/assets/guides/lua-vbo-vao-4.png)
+
+There can be up to 16 input attributes, this number is shared between vertex attributes (mandatory most of the time) and instance attributes (optional).
+
+## VBO and VAO creation
+
+```lua
+local someVAO = gl.GetVAO() --get empty VAO
+
+local vertVBO = gl.GetVBO(GL.ARRAY_BUFFER, true) --empty VBO, "GL.ARRAY_BUFFER" means it's either vertex or instance buffer, "true" means this buffer will be optimized by GL driver for frequent updates. Here by the variable name you can guess it's supposed to be vertex buffer
+
+local instVBO = gl.GetVBO(GL.ARRAY_BUFFER, true) --empty VBO, "GL.ARRAY_BUFFER" means it's either vertex or instance buffer, "true" means this buffer will be optimized by GL driver for frequent updates. Here by the variable name you can guess it's supposed to be instance buffer
+
+local indxVBO = gl.GetVBO(GL.ELEMENT_ARRAY_BUFFER, false) -- empty index buffer, not going to be frequently updated ("false").
+
+vertVBO:Define(1000, { --someVBO is created to hold 1000 "elements", see pics above what element is. If suddenly the number of elements exceeds 1000, the buffer will not accept new data, "someVBO" will need to be remand and rebound to VAO
+ {id = 0, name = "pos", size = 4}, -- "pos" attribute will hold 4 floats (float is the default type, if "type" is not specified). "id" in the shader must be 0
+ {id = 1, name = "color", type=GL.UNSIGNED_BYTE, normalized = true, size = 3}, -- "color" is represented by 3 unsigned bytes (values from 0 to 255), values are normalized (in this case divided by 255 to get float inside shader). "id" in the shader must be 1. This can be useful to hold RGB data.
+})
+
+instVBO:Define(2, { --reserve space for 2 elements of the type described below
+ {id = 2, name = "posBias", size = 4}, -- posBias is 4 floats attribute of id = 2, note that ids here and ids of vertVBO cannot duplicate. We will use it to offset instances in space
+})
+
+indxVBO:Define(2000, GL.UNSIGNED_INT) --defines index buffer with capacity of 2000 elements of type unsigned integer (32 bit integer), other possibilities for type are GL.UNSIGNED_SHORT and GL.UNSIGNED_BYTE, representing 16 bit and 8 bit unsigned integers respectively. If no type is given GL.UNSIGNED_SHORT is the default) - it makes sense as it allows to index 65534 vertices and occupies only 2 bytes per one index.
+
+--here we attach (glue) VBOs into VAO. Note in theory you can use (and I use sometimes) completely empty VAO (no attached buffers), but most often you will want to attach (and create before) at least vertex buffer.
+someVAO:AttachVertexBuffer(vertVBO) --note vertVBO and instVBO were created with the same command (except for definition), the only way to tell apart instance buffer from vertex buffer is to see what command was used to attach the VBO to the VAO.
+someVAO:AttachInstanceBuffer(instVBO)
+someVAO:AttachIndexBuffer(indxVBO)
+-- only one attachment of certain type can be made. E.g. you can't attach two vertex buffers.
+```
+
+## Uploading VBO data to VBOs
+Now we have VBOs structure defined. Time to upload some useful data, that will later be used to draw stuff.
+```lua
+local vertexData = {
+ -- element #1
+ 0, 0, 0, 1, --this goes into "pos"
+ 127, 0, 0, --this goes into "color"
+ -- element #2
+ 1, 1, 1, 1, --this goes into "pos"
+ 127, 127, 0, --this goes into "color"
+ -- element #3
+ 1, 0, 0, 1, --this goes into "pos"
+ 127, 127, 127, --this goes into "color"
+ -- etc
+}
+vertVBO:Upload(vertexData)
+
+local instanceData = {
+ -- element #1
+ 100, 100, 0, 1, --this goes into "posBias"
+ -- element #2
+ 100, 200, 0, 1, --this goes into "posBias"
+ -- element #3
+ 200, 100, 0, 1, --this goes into "posBias"
+ -- etc
+}
+instVBO:Upload(instanceData)
+
+local indexData = {
+ 0, 1, 2, --one triangle
+ 1, 2, 3, --second triangle
+ -- etc
+}
+indxVBO:Upload(indexData)
+```
+Here I'm showing very basic upload use. All data is uploaded in one go. This is recommended on initial upload or in case your data is static.
+Upload() has tons of options to upload only selected attribute, do partial upload, etc:
+Upload(tableData, optionalAttribIdx, optionalElementOffset, optionalLuaStartIndex, optionalLuaFinishIndex).
+Ask for extended description when you master basic upload!
+
+### Performance Notes
+
+The Upload function as shown above allows updating contiguous blocks of the VBO, which can allow for batched updating of some or all elements of a VBO.
+Generally, uploading a single element takes about 5us for 5 vec4's of data. However, we have found that in general, if you want to update more than about 20% of the contents of a VBO, it is faster to just upload the whole VBO instead of partial updates.
+
+See the code here for the benchmark:
+
+https://gist.github.com/Beherith/c965cff2a81253e37aed25f4db0c0fce
+
+
+### Drawing with VAOs
+```lua
+---somewhere in widget:Draw...()
+
+-- draw WITHOUT index buffer (index buffer is not needed and won't be used if attached)
+gl.UseShader(someShader) --yes, you need a shader. No, without shader you won't see a pixel
+someVAO:DrawArrays(GL.TRIANGLES, numberOfElements, firstElementIndex, numberOfInstances, firstInstanceIndex) --GL.TRIANGLES means every 3 element in vertex buffer are used to output a triangle. Besides GL.TRIANGLES you can draw with points, lines, stripes, and tons of other stuff. See https://docs.gl/gl4/glDrawArrays , the rest of options are optional and self descriptive
+gl.UseShader(0)
+
+
+-- draw WITH index buffer (index buffer must be attached to VAO)
+gl.UseShader(someShader) --yes, you need a shader. No, without shader you won't see a pixel
+someVAO:DrawElements(GL.TRIANGLES, numberOfIndices, indexOfFirstIndex, numberOfInstances, baseVertex) --GL.TRIANGLES means every 3 element in index buffer are used to index into vertex buffer to output a triangle. Besides GL.TRIANGLES you can draw with points, lines, stripes, and tons of other stuff. See https://docs.gl/gl4/glDrawElements , the rest of options are optional and mostly self descriptive
+gl.UseShader(0)
+```
diff --git a/guides/synced-commands.markdown b/guides/synced-commands.markdown
new file mode 100644
index 0000000000..ac2e1ef432
--- /dev/null
+++ b/guides/synced-commands.markdown
@@ -0,0 +1,47 @@
+---
+layout: default
+title: Synced Commands
+parent: Guides
+permalink: /guides/synced-commands/
+---
+
+# Synced Commands
+
+Game developers and players can issue synced commands from chat, e.g.
+("/give unit"), or `Spring.SendCommands`:
+
+Here we provide a list of them, some commands require cheats enabled ("/cheat"):
+
+
+
+
Name
+
Description
+
Arguments
+
+ {% for row in site.data.synced_commands %}
+ {% assign row_data = row[1] %}
+
diff --git a/guides/unsynced-commands.markdown b/guides/unsynced-commands.markdown
new file mode 100644
index 0000000000..1cd38c93c4
--- /dev/null
+++ b/guides/unsynced-commands.markdown
@@ -0,0 +1,48 @@
+---
+layout: default
+title: Unsynced Commands
+parent: Guides
+permalink: /guides/unsynced-commands/
+---
+
+# Unsynced Commands
+
+Game developers and players can issue unsynced commands from chat, e.g.
+("/showmetalmap") or `Spring.SendCommands`.
+
+Here we provide a list of them, some commands require cheats enabled ("/cheat"):
+
+
diff --git a/guides/widgets.markdown b/guides/widgets.markdown
new file mode 100644
index 0000000000..8f1e71d225
--- /dev/null
+++ b/guides/widgets.markdown
@@ -0,0 +1,7 @@
+---
+layout: default
+title: Widgets
+parent: Guides
+permalink: guides/widgets
+published: false
+---
diff --git a/index.markdown b/index.markdown
new file mode 100644
index 0000000000..2bf0e5c68b
--- /dev/null
+++ b/index.markdown
@@ -0,0 +1,88 @@
+---
+layout: default
+title: Home
+nav_order: 1
+description: "Recoil is an RTS engine designed for game flexibility and large scale."
+permalink: /
+---
+
+# Design large scale RTS games
+{: .fs-9 }
+
+Recoil is a battle tested open-source RTS engine that, allied with a flexible
+Lua API, allows you to implement the perfect UI and mechanics for your game
+with the ability to support thousands of complex units simultaneously.
+
+Some of the games powered by Recoil: [Beyond All Reason], [ZeroK], [TA Prime]
+and [Metal Factions].
+
+{: .fs-6 .fw-300 }
+
+[Get started now](#getting-started){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
+[View it on GitHub][Recoil repo]{: .btn .fs-5 .mb-4 .mb-md-0 }
+
+---
+
+{: .warning }
+> Recoil is a recent hard fork of [Spring] from the [105 tree], many references
+to it might and will be present. Overall most documented Spring API and
+tutorials are compatible with Recoil since they are based on the [105 tree].
+
+## Getting started
+
+{: .note }
+This site is an early work-in-progress so content will mostly be references to
+Spring documentation until its own guides are written.
+
+References:
+
+- [Spring Wiki]
+- [Recoil Lua API]
+- [Recoil Github Wiki]
+
+### Download
+
+The latest stable release is `{{site.data.latest_release.name}}` available at:
+
+{% assign releases = site.data.latest_release.assets | where_exp: "asset", "asset.browser_download_url contains 'minimal-portable'" %}
+
+{% for rel in releases %}
+- [`{{rel.name}}`]({{rel.browser_download_url}})
+{% endfor %}
+
+See the [release page]({{site.data.latest_release.html_url}}) for more options.
+
+## Contributing
+
+See [Development](development.markdown) for guides on how to build and
+develop Recoil.
+
+When contributing to this repository, please first discuss the change you wish
+to make via [GitHub issues], our [Discord server] or any other method with the
+owners of this repository before making a change.
+
+### Thank you to the contributors of Recoil!
+
+
diff --git a/migrating-from-spring.markdown b/migrating-from-spring.markdown
new file mode 100644
index 0000000000..7efb997ac2
--- /dev/null
+++ b/migrating-from-spring.markdown
@@ -0,0 +1,222 @@
+---
+layout: default
+title: Migrating from Spring
+permalink: /migrating-from-spring/
+nav_order: 5
+---
+
+# Migrating from Spring
+{: .no_toc }
+
+While Recoil is mostly compatible with Spring 105 some divergences naturally
+developed over time.
+
+Here we list the most relevant breaking changes and how to fix them when
+migrating from Spring.
+
+
+
+ Table of contents
+
+ {: .text-delta }
+- TOC
+{:toc}
+
+
+## Callouts
+
+### Font rendering
+
+Rendering fonts now obeys GL color state. This means that sometimes text will
+not be the same color as previously. To get back previous behaviour, you might
+need to add
+[`gl.Color`](https://beyond-all-reason.github.io/spring/ldoc/modules/OpenGL.html#gl.Text)
+in front of
+[`gl.Text`](https://beyond-all-reason.github.io/spring/ldoc/modules/OpenGL.html#gl.Color)
+calls. Alternatively, you can add inline colour codes (`\255\rrr\ggg\bbb`).
+
+### Spring.Marker usage
+
+All three [Spring.Marker] functions (Point, Line, Erase) no longer accept
+numerical 0 as false for `onlyLocal` (now follows regular Lua language rules
+where 0 is true). To get back old behaviour, change `0` to `false`, e.g.:
+
+```diff
+-Spring.MarkerAddPoint(x, y, z, text, 0)
++Spring.MarkerAddPoint(x, y, z, text, false)
+```
+
+```diff
+-Spring.MarkerAddLine(x1, y1, z1, x2, y2, z2, 0, playerId)
++Spring.MarkerAddLine(x1, y1, z1, x2, y2, z2, false, playerId)
+```
+
+```diff
+-Spring.MarkerErasePosition(x, y, z, noop, 0, playerId)
++Spring.MarkerErasePosition(x, y, z, noop, false, playerId)
+```
+
+### Spring.GetConfig usage
+
+[`Spring.GetConfigInt`](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedRead.html#Spring.GetConfigInt),
+[`Spring.GetConfigFloat`](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedRead.html#Spring.GetConfigFloat) and
+[`Spring.GetConfigString`](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedRead.html#Spring.GetConfigString)
+now accept nil as the second argument (what to return by default if not set).
+Previously it was treated as 0 (Int and Float) or "" (String).
+To get back previous behaviour:
+
+```lua
+local originalGetConfigInt = Spring.GetConfigInt
+Spring.GetConfigInt = function(key, def)
+ return originalGetConfigInt(key, def) or 0
+end
+```
+
+### Spring.GetSelectedUnits{Sorted,Counts}
+
+The tables returned by
+[`Spring.GetSelectedUnitsSorted`](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedRead.html#Spring.GetSelectedUnitsSorted) and
+[`Spring.GetSelectedUnitsCounts`](https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedRead.html#Spring.GetSelectedUnitsCounts)
+no longer have an additional `n` key with corresponding value containining the
+number of unitdefs.
+
+Instead use the second return value, e.g.:
+
+```diff
+- local counts = Spring.GetSelectedUnitsCounts()
+- local unitDefsCount = counts.n
+- counts.n = nil
++ local counts, unitDefsCount = Spring.GetSelectedUnitsCounts()
+```
+
+## Stop command on manual share
+Manually shared units no longer receive the Stop command.
+Replicate the previous behaviour via the `UnitGiven` callin:
+```lua
+function wupget:UnitGiven(unitID, unitDefID, newTeam)
+ if newTeam == Spring.GetMyTeamID() then -- if doing in unsynced
+ Spring.GiveOrderToUnit(unitID, CMD.STOP, 0, 0)
+ end
+end
+```
+
+## Defs
+
+- Hovercraft and ships brought out of water no longer forced to be upright.
+To get back previous behaviour, put the `upright = true` tag in all unit defs
+whose move def is of the hovercraft or ship type.
+- Units with `useFootPrintCollisionVolume` but no `collisionVolumeScales` set
+will now use the footprint volume (previously mistakenly used the model's sphere).
+
+ To keep the old hitvolume, set `useFootPrintCollisionVolume` to false for units
+ with no `collisionVolumeScales`. Assuming you apply `lowerkeys` to unit defs,
+ this can also be achieved by putting the following in unit defs post-processing:
+
+ ```lua
+ for unitDefID, unitDef in pairs(UnitDefs) do
+ if not unitDef.collisionvolumescales then
+ unitDef.usefootprintcollisionvolume = nil
+ end
+ end
+ ```
+- Tree feature defs `treetype0` through `treetype16` are now provided by the
+basecontent archive instead of the engine.
+
+ No known games ship their own basecontent and they would know what to do if so.
+
+- The `firestarter` weapon tag no longer capped at 10000 in defs (which
+becomes 100 in Lua WeaponDefs after rescale), now uncapped.
+
+ To get back previous behaviour, put the following in weapon defs
+ post-processing:
+
+ ```lua
+ for weaponDefID, weaponDef in pairs(WeaponDefs) do
+ if weaponDef.firestarter then
+ weaponDef.firestarter = math.min(weaponDef.firestarter, 10000)
+ end
+ end
+ ```
+
+- It is heavily recommended to replace `acceleration` and `brakeRate`
+with `maxAcc` and `maxDec` respectively (possibly in post-processing).
+While the old spelling still works the way it always did, at some point
+in the future it will be changed from elmo/frame to elmo/second.
+
+## Camera modifiers
+
+The following keyboard modifiers were unhardcoded from engine:
+
+- On spring camera: rotating on the x (pitch) or y (yaw) axis with ALT and
+middle mouse button pressed while moving the cursor.
+- Resetting camera settings on ALT pressed and mousewheelscroll down.
+- Rotating on the x axis (pitch) with CTRL pressed and mousewheelscroll.
+
+If games and players do not change engine defaults, no action is needed,
+however to enable these modifiers as previously the following keybindings must
+be set:
+
+```
+bind Any+ctrl movetilt // rotates the camera over the x axis on mousewheel move
+bind Any+alt movereset // resets camera state to maxzoom/minzoom on mousewheel move, additionally resets tilt on Overhead cam
+bind Any+alt moverotate // rotates the camera in x and y axis on mmb move (Spring cam)
+```
+
+## Resurrecting units
+
+Resurrecting units no longer overrides their health to 5%.
+To get back the old behaviour define a gadget with the following callin:
+
+```lua
+function gadget:UnitCreated(unitID, unitDefID, teamID, builderID)
+ if builderID and Spring.GetUnitWorkerTask(builderID) == CMD.RESURRECT then
+ Spring.SetUnitHealth(unitID, Spring.GetUnitHealth(unitID) * 0.05)
+ end
+end
+```
+
+## VFS mapping API
+
+`Spring.MapArchive` and `Spring.UnmapArchive` have been temporarily removed due to sync unsafety.
+In the meantime, use `Spring.UseArchive`. These functions are going to come back at some point,
+but there is no concrete timeline for that yet.
+
+## Iterating synced proxy tables
+
+Functions for iterating synced proxy tables: `snext`, `spairs` and `sipairs` have been removed.
+These have been able to be replaced by the regular `next`, `pairs` and `ipairs` for some time
+already (so the change can be done before migrating):
+```diff
+ local syncedProxyTable = SYNCED.foo
+-for key, value in spairs(syncedProxyTable) do
++for key, value in pairs(syncedProxyTable) do
+```
+
+## General
+
+- Paletted image files are no longer accepted. Convert your images not to be paletted.
+- The return value from the
+[`UnitUnitCollision`](https://beyond-all-reason.github.io/spring/ldoc/modules/LuaHandle.html#UnitUnitCollision)
+callin is now ignored and there is only one event for each collision.
+There is no way to get back the old behaviour for now,
+but if someone needs it it could be arranged.
+- Removed the following constants:
+ - `Platform.glSupport16bitDepthBuffer`
+ - `Platform.glSupport24bitDepthBuffer`
+ - `Platform.glSupport32bitDepthBuffer`
+
+ To get back previous behaviour, replace with
+ ```lua
+ Platform.glSupportDepthBufferBitDepth >= 16 -- or 24, or 32, respectively
+ ```
+- Removed LOD rendering (2D unit billboards when zoomed out far), including the
+`/distdraw` command and the `UnitLodDist` springsettings entry
+- Removed the `AdvSky` springsetting and the `/dynamicsky` command,
+which made clouds move across the sky. You cannot easily get back
+previous behaviour, though you can probably achieve something similar
+by rendering moving clouds yourself.
+- The deprecated `Game.allowTeamColors`, whose value was always `true`, has been removed. Note that this inverts logic if you used it like a bool.
+- The default `/screenshot` format was changed to PNG. Check any automated processing you do on these.
+- Screenshots and replay demo files now have a different filename format, with an UTC timestamp. Check any automated file parsing you might have.
+
+[Spring.Marker]: https://beyond-all-reason.github.io/spring/ldoc/modules/UnsyncedCtrl.html#Markers