This commit is contained in:
VorTechnix 2023-07-04 14:50:17 -07:00
commit 5cf155772d
71 changed files with 1141 additions and 298 deletions

View file

@ -100,7 +100,10 @@ date: 2000-01-01
<pre><code>cd path/to/worldmods; <pre><code>cd path/to/worldmods;
git clone https://github.com/Uberi/Minetest-WorldEdit.git worldedit; git clone https://github.com/Uberi/Minetest-WorldEdit.git worldedit;
git clone https://github.com/sbrl/Minetest-WorldEditAdditions.git worldeditadditions;</code></pre> git clone https://github.com/sbrl/Minetest-WorldEditAdditions.git worldeditadditions;
cd worldeditadditions;
git checkout "$(git describe --tags --abbrev=0)";</code></pre>
<p><a class="bigbutton" href="https://github.com/sbrl/Minetest-WorldEditAdditions">Source code on GitHub</a></p> <p><a class="bigbutton" href="https://github.com/sbrl/Minetest-WorldEditAdditions">Source code on GitHub</a></p>
</div> </div>

277
.docs/package-lock.json generated
View file

@ -9,12 +9,12 @@
"version": "1.0.0", "version": "1.0.0",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"@11ty/eleventy": "^2.0.0", "@11ty/eleventy": "^2.0.1",
"chroma-js": "^2.4.2", "chroma-js": "^2.4.2",
"clean-css": "^5.3.2", "clean-css": "^5.3.2",
"columnify": "^1.6.0", "columnify": "^1.6.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"html-entities": "^2.3.3", "html-entities": "^2.4.0",
"html-minifier-terser": "^7.0.0-beta.0", "html-minifier-terser": "^7.0.0-beta.0",
"imagickal": "^5.0.1", "imagickal": "^5.0.1",
"markdown-it-prism": "^2.3.0", "markdown-it-prism": "^2.3.0",
@ -30,13 +30,14 @@
"integrity": "sha512-5R+DsT9LJ9tXiSQ4y+KLFppCkQyXhzAm1AIuBWE/sbU0hSXY5pkhoqQYEcPJQFg/nglL+wD55iv2j+7O96UAvg==" "integrity": "sha512-5R+DsT9LJ9tXiSQ4y+KLFppCkQyXhzAm1AIuBWE/sbU0hSXY5pkhoqQYEcPJQFg/nglL+wD55iv2j+7O96UAvg=="
}, },
"node_modules/@11ty/eleventy": { "node_modules/@11ty/eleventy": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-2.0.1.tgz",
"integrity": "sha512-heNLjt1FD2nx7fvidIgA4zrIvxuslgBK0w5/Ckr5iape1CoLzmDx1uIxPa66Atr1M6YzwG9hcOxoZUYV7PfLXw==", "integrity": "sha512-t8XVUbCJByhVEa1RzO0zS2QzbL3wPY8ot1yUw9noqiSHxJWUwv6jiwm1/MZDPTYtkZH2ZHvdQIRQ5/SjG9XmLw==",
"dependencies": { "dependencies": {
"@11ty/dependency-tree": "^2.0.1", "@11ty/dependency-tree": "^2.0.1",
"@11ty/eleventy-dev-server": "^1.0.3", "@11ty/eleventy-dev-server": "^1.0.4",
"@11ty/eleventy-utils": "^1.0.1", "@11ty/eleventy-utils": "^1.0.1",
"@11ty/lodash-custom": "^4.17.21",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@sindresorhus/slugify": "^1.1.2", "@sindresorhus/slugify": "^1.1.2",
"bcp-47-normalize": "^1.1.1", "bcp-47-normalize": "^1.1.1",
@ -44,23 +45,20 @@
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
"debug": "^4.3.4", "debug": "^4.3.4",
"dependency-graph": "^0.11.0", "dependency-graph": "^0.11.0",
"ejs": "^3.1.8", "ejs": "^3.1.9",
"fast-glob": "^3.2.12", "fast-glob": "^3.2.12",
"graceful-fs": "^4.2.10", "graceful-fs": "^4.2.11",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"hamljs": "^0.6.2", "hamljs": "^0.6.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"iso-639-1": "^2.1.15", "iso-639-1": "^2.1.15",
"kleur": "^4.1.5", "kleur": "^4.1.5",
"liquidjs": "^10.4.0", "liquidjs": "^10.7.0",
"lodash.chunk": "^4.2.0", "luxon": "^3.3.0",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"luxon": "^3.2.1",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"minimist": "^1.2.7", "minimist": "^1.2.8",
"moo": "^0.5.2", "moo": "^0.5.2",
"multimatch": "^5.0.0", "multimatch": "^5.0.0",
"mustache": "^4.2.0", "mustache": "^4.2.0",
@ -73,7 +71,7 @@
"pug": "^3.0.2", "pug": "^3.0.2",
"recursive-copy": "^2.0.14", "recursive-copy": "^2.0.14",
"semver": "^7.3.8", "semver": "^7.3.8",
"slugify": "^1.6.5" "slugify": "^1.6.6"
}, },
"bin": { "bin": {
"eleventy": "cmd.js" "eleventy": "cmd.js"
@ -87,9 +85,9 @@
} }
}, },
"node_modules/@11ty/eleventy-dev-server": { "node_modules/@11ty/eleventy-dev-server": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@11ty/eleventy-dev-server/-/eleventy-dev-server-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@11ty/eleventy-dev-server/-/eleventy-dev-server-1.0.4.tgz",
"integrity": "sha512-SjYQewOO0Oo2jUI5h0Lk87pRJllDBzbdcHGZTYEf00gz966kidP1Hyd3ySaHqL4lFqW2I6jIxNVKPlhwYhp6yA==", "integrity": "sha512-qVBmV2G1KF/0o5B/3fITlrrDHy4bONUI2YuN3/WJ3BNw4NU1d/we8XhKrlgq13nNvHoBx5czYp3LZt8qRG53Fg==",
"dependencies": { "dependencies": {
"@11ty/eleventy-utils": "^1.0.1", "@11ty/eleventy-utils": "^1.0.1",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
@ -97,11 +95,11 @@
"dev-ip": "^1.0.1", "dev-ip": "^1.0.1",
"finalhandler": "^1.2.0", "finalhandler": "^1.2.0",
"mime": "^3.0.0", "mime": "^3.0.0",
"minimist": "^1.2.7", "minimist": "^1.2.8",
"morphdom": "^2.6.1", "morphdom": "^2.7.0",
"please-upgrade-node": "^3.2.0", "please-upgrade-node": "^3.2.0",
"ssri": "^8.0.1", "ssri": "^8.0.1",
"ws": "^8.12.0" "ws": "^8.13.0"
}, },
"bin": { "bin": {
"eleventy-dev-server": "cmd.js" "eleventy-dev-server": "cmd.js"
@ -129,6 +127,18 @@
"url": "https://opencollective.com/11ty" "url": "https://opencollective.com/11ty"
} }
}, },
"node_modules/@11ty/lodash-custom": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@11ty/lodash-custom/-/lodash-custom-4.17.21.tgz",
"integrity": "sha512-Mqt6im1xpb1Ykn3nbcCovWXK3ggywRJa+IXIdoz4wIIK+cvozADH63lexcuPpGS/gJ6/m2JxyyXDyupkMr5DHw==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/11ty"
}
},
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
@ -773,9 +783,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"node_modules/ejs": { "node_modules/ejs": {
"version": "3.1.8", "version": "3.1.9",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
"integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
"dependencies": { "dependencies": {
"jake": "^10.8.5" "jake": "^10.8.5"
}, },
@ -1018,9 +1028,9 @@
} }
}, },
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.10", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
}, },
"node_modules/gray-matter": { "node_modules/gray-matter": {
"version": "4.0.3", "version": "4.0.3",
@ -1092,9 +1102,19 @@
} }
}, },
"node_modules/html-entities": { "node_modules/html-entities": {
"version": "2.3.3", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz",
"integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/mdevils"
},
{
"type": "patreon",
"url": "https://patreon.com/mdevils"
}
]
}, },
"node_modules/html-minifier-terser": { "node_modules/html-minifier-terser": {
"version": "7.0.0-beta.0", "version": "7.0.0-beta.0",
@ -1342,14 +1362,14 @@
} }
}, },
"node_modules/jake": { "node_modules/jake": {
"version": "10.8.5", "version": "10.8.7",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
"integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==",
"dependencies": { "dependencies": {
"async": "^3.2.3", "async": "^3.2.3",
"chalk": "^4.0.2", "chalk": "^4.0.2",
"filelist": "^1.0.1", "filelist": "^1.0.4",
"minimatch": "^3.0.4" "minimatch": "^3.1.2"
}, },
"bin": { "bin": {
"jake": "bin/cli.js" "jake": "bin/cli.js"
@ -1417,9 +1437,9 @@
} }
}, },
"node_modules/liquidjs": { "node_modules/liquidjs": {
"version": "10.5.0", "version": "10.8.3",
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.5.0.tgz", "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.8.3.tgz",
"integrity": "sha512-qs1lbzfeSx5ICegKFrdYvxtKz8VXaw0Rfbu0zaCgC/M5aR62h89aR3wmL1W5C908y5yq+PBRPRFZZisS/gTPyA==", "integrity": "sha512-LqHLYtH3vrkT3LyfOhPU0FJX5KPO4aB6SzGa4HRI29yz8pS0ZxqIe/fWtic8qiust1+qrHI92J67tdt92V4WOA==",
"dependencies": { "dependencies": {
"commander": "^10.0.0" "commander": "^10.0.0"
}, },
@ -1436,9 +1456,9 @@
} }
}, },
"node_modules/liquidjs/node_modules/commander": { "node_modules/liquidjs/node_modules/commander": {
"version": "10.0.0", "version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
} }
@ -1448,26 +1468,11 @@
"resolved": "https://registry.npmjs.org/list-to-array/-/list-to-array-1.1.0.tgz", "resolved": "https://registry.npmjs.org/list-to-array/-/list-to-array-1.1.0.tgz",
"integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==" "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g=="
}, },
"node_modules/lodash.chunk": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
"integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w=="
},
"node_modules/lodash.deburr": { "node_modules/lodash.deburr": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz",
"integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=" "integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s="
}, },
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
},
"node_modules/lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
"integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg=="
},
"node_modules/lower-case": { "node_modules/lower-case": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@ -1488,9 +1493,9 @@
} }
}, },
"node_modules/luxon": { "node_modules/luxon": {
"version": "3.2.1", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz",
"integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==", "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@ -1708,9 +1713,9 @@
} }
}, },
"node_modules/nunjucks": { "node_modules/nunjucks": {
"version": "3.2.3", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz",
"integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==",
"dependencies": { "dependencies": {
"a-sync-waterfall": "^1.0.0", "a-sync-waterfall": "^1.0.0",
"asap": "^2.0.3", "asap": "^2.0.3",
@ -2237,9 +2242,9 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.3.8", "version": "7.5.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
}, },
@ -2253,7 +2258,7 @@
"node_modules/semver-compare": { "node_modules/semver-compare": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
}, },
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
@ -2283,9 +2288,9 @@
} }
}, },
"node_modules/slugify": { "node_modules/slugify": {
"version": "1.6.5", "version": "1.6.6",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.5.tgz", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
"integrity": "sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ==", "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"
} }
@ -2514,9 +2519,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.12.1", "version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
@ -2546,13 +2551,14 @@
"integrity": "sha512-5R+DsT9LJ9tXiSQ4y+KLFppCkQyXhzAm1AIuBWE/sbU0hSXY5pkhoqQYEcPJQFg/nglL+wD55iv2j+7O96UAvg==" "integrity": "sha512-5R+DsT9LJ9tXiSQ4y+KLFppCkQyXhzAm1AIuBWE/sbU0hSXY5pkhoqQYEcPJQFg/nglL+wD55iv2j+7O96UAvg=="
}, },
"@11ty/eleventy": { "@11ty/eleventy": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-2.0.1.tgz",
"integrity": "sha512-heNLjt1FD2nx7fvidIgA4zrIvxuslgBK0w5/Ckr5iape1CoLzmDx1uIxPa66Atr1M6YzwG9hcOxoZUYV7PfLXw==", "integrity": "sha512-t8XVUbCJByhVEa1RzO0zS2QzbL3wPY8ot1yUw9noqiSHxJWUwv6jiwm1/MZDPTYtkZH2ZHvdQIRQ5/SjG9XmLw==",
"requires": { "requires": {
"@11ty/dependency-tree": "^2.0.1", "@11ty/dependency-tree": "^2.0.1",
"@11ty/eleventy-dev-server": "^1.0.3", "@11ty/eleventy-dev-server": "^1.0.4",
"@11ty/eleventy-utils": "^1.0.1", "@11ty/eleventy-utils": "^1.0.1",
"@11ty/lodash-custom": "^4.17.21",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@sindresorhus/slugify": "^1.1.2", "@sindresorhus/slugify": "^1.1.2",
"bcp-47-normalize": "^1.1.1", "bcp-47-normalize": "^1.1.1",
@ -2560,23 +2566,20 @@
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
"debug": "^4.3.4", "debug": "^4.3.4",
"dependency-graph": "^0.11.0", "dependency-graph": "^0.11.0",
"ejs": "^3.1.8", "ejs": "^3.1.9",
"fast-glob": "^3.2.12", "fast-glob": "^3.2.12",
"graceful-fs": "^4.2.10", "graceful-fs": "^4.2.11",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"hamljs": "^0.6.2", "hamljs": "^0.6.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"iso-639-1": "^2.1.15", "iso-639-1": "^2.1.15",
"kleur": "^4.1.5", "kleur": "^4.1.5",
"liquidjs": "^10.4.0", "liquidjs": "^10.7.0",
"lodash.chunk": "^4.2.0", "luxon": "^3.3.0",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"luxon": "^3.2.1",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"minimist": "^1.2.7", "minimist": "^1.2.8",
"moo": "^0.5.2", "moo": "^0.5.2",
"multimatch": "^5.0.0", "multimatch": "^5.0.0",
"mustache": "^4.2.0", "mustache": "^4.2.0",
@ -2589,13 +2592,13 @@
"pug": "^3.0.2", "pug": "^3.0.2",
"recursive-copy": "^2.0.14", "recursive-copy": "^2.0.14",
"semver": "^7.3.8", "semver": "^7.3.8",
"slugify": "^1.6.5" "slugify": "^1.6.6"
} }
}, },
"@11ty/eleventy-dev-server": { "@11ty/eleventy-dev-server": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@11ty/eleventy-dev-server/-/eleventy-dev-server-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@11ty/eleventy-dev-server/-/eleventy-dev-server-1.0.4.tgz",
"integrity": "sha512-SjYQewOO0Oo2jUI5h0Lk87pRJllDBzbdcHGZTYEf00gz966kidP1Hyd3ySaHqL4lFqW2I6jIxNVKPlhwYhp6yA==", "integrity": "sha512-qVBmV2G1KF/0o5B/3fITlrrDHy4bONUI2YuN3/WJ3BNw4NU1d/we8XhKrlgq13nNvHoBx5czYp3LZt8qRG53Fg==",
"requires": { "requires": {
"@11ty/eleventy-utils": "^1.0.1", "@11ty/eleventy-utils": "^1.0.1",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
@ -2603,11 +2606,11 @@
"dev-ip": "^1.0.1", "dev-ip": "^1.0.1",
"finalhandler": "^1.2.0", "finalhandler": "^1.2.0",
"mime": "^3.0.0", "mime": "^3.0.0",
"minimist": "^1.2.7", "minimist": "^1.2.8",
"morphdom": "^2.6.1", "morphdom": "^2.7.0",
"please-upgrade-node": "^3.2.0", "please-upgrade-node": "^3.2.0",
"ssri": "^8.0.1", "ssri": "^8.0.1",
"ws": "^8.12.0" "ws": "^8.13.0"
} }
}, },
"@11ty/eleventy-utils": { "@11ty/eleventy-utils": {
@ -2618,6 +2621,11 @@
"normalize-path": "^3.0.0" "normalize-path": "^3.0.0"
} }
}, },
"@11ty/lodash-custom": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@11ty/lodash-custom/-/lodash-custom-4.17.21.tgz",
"integrity": "sha512-Mqt6im1xpb1Ykn3nbcCovWXK3ggywRJa+IXIdoz4wIIK+cvozADH63lexcuPpGS/gJ6/m2JxyyXDyupkMr5DHw=="
},
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
@ -3097,9 +3105,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"ejs": { "ejs": {
"version": "3.1.8", "version": "3.1.9",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
"integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
"requires": { "requires": {
"jake": "^10.8.5" "jake": "^10.8.5"
} }
@ -3281,9 +3289,9 @@
} }
}, },
"graceful-fs": { "graceful-fs": {
"version": "4.2.10", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
}, },
"gray-matter": { "gray-matter": {
"version": "4.0.3", "version": "4.0.3",
@ -3332,9 +3340,9 @@
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
}, },
"html-entities": { "html-entities": {
"version": "2.3.3", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz",
"integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ=="
}, },
"html-minifier-terser": { "html-minifier-terser": {
"version": "7.0.0-beta.0", "version": "7.0.0-beta.0",
@ -3516,14 +3524,14 @@
"integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==" "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg=="
}, },
"jake": { "jake": {
"version": "10.8.5", "version": "10.8.7",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
"integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==",
"requires": { "requires": {
"async": "^3.2.3", "async": "^3.2.3",
"chalk": "^4.0.2", "chalk": "^4.0.2",
"filelist": "^1.0.1", "filelist": "^1.0.4",
"minimatch": "^3.0.4" "minimatch": "^3.1.2"
} }
}, },
"js-stringify": { "js-stringify": {
@ -3573,17 +3581,17 @@
} }
}, },
"liquidjs": { "liquidjs": {
"version": "10.5.0", "version": "10.8.3",
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.5.0.tgz", "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.8.3.tgz",
"integrity": "sha512-qs1lbzfeSx5ICegKFrdYvxtKz8VXaw0Rfbu0zaCgC/M5aR62h89aR3wmL1W5C908y5yq+PBRPRFZZisS/gTPyA==", "integrity": "sha512-LqHLYtH3vrkT3LyfOhPU0FJX5KPO4aB6SzGa4HRI29yz8pS0ZxqIe/fWtic8qiust1+qrHI92J67tdt92V4WOA==",
"requires": { "requires": {
"commander": "^10.0.0" "commander": "^10.0.0"
}, },
"dependencies": { "dependencies": {
"commander": { "commander": {
"version": "10.0.0", "version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==" "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="
} }
} }
}, },
@ -3592,26 +3600,11 @@
"resolved": "https://registry.npmjs.org/list-to-array/-/list-to-array-1.1.0.tgz", "resolved": "https://registry.npmjs.org/list-to-array/-/list-to-array-1.1.0.tgz",
"integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==" "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g=="
}, },
"lodash.chunk": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
"integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w=="
},
"lodash.deburr": { "lodash.deburr": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz",
"integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=" "integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s="
}, },
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
},
"lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
"integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg=="
},
"lower-case": { "lower-case": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@ -3629,9 +3622,9 @@
} }
}, },
"luxon": { "luxon": {
"version": "3.2.1", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz",
"integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==" "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg=="
}, },
"markdown-it": { "markdown-it": {
"version": "13.0.1", "version": "13.0.1",
@ -3793,9 +3786,9 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
}, },
"nunjucks": { "nunjucks": {
"version": "3.2.3", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz",
"integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==",
"requires": { "requires": {
"a-sync-waterfall": "^1.0.0", "a-sync-waterfall": "^1.0.0",
"asap": "^2.0.3", "asap": "^2.0.3",
@ -4191,9 +4184,9 @@
} }
}, },
"semver": { "semver": {
"version": "7.3.8", "version": "7.5.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
"requires": { "requires": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
} }
@ -4201,7 +4194,7 @@
"semver-compare": { "semver-compare": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
}, },
"shebang-command": { "shebang-command": {
"version": "2.0.0", "version": "2.0.0",
@ -4222,9 +4215,9 @@
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU="
}, },
"slugify": { "slugify": {
"version": "1.6.5", "version": "1.6.6",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.5.tgz", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
"integrity": "sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ==" "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="
}, },
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
@ -4389,9 +4382,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"ws": { "ws": {
"version": "8.12.1", "version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"requires": {} "requires": {}
}, },
"yallist": { "yallist": {

View file

@ -20,12 +20,12 @@
}, },
"homepage": "https://github.com/sbrl/Minetest-WorldEditAdditions#readme", "homepage": "https://github.com/sbrl/Minetest-WorldEditAdditions#readme",
"dependencies": { "dependencies": {
"@11ty/eleventy": "^2.0.0", "@11ty/eleventy": "^2.0.1",
"chroma-js": "^2.4.2", "chroma-js": "^2.4.2",
"clean-css": "^5.3.2", "clean-css": "^5.3.2",
"columnify": "^1.6.0", "columnify": "^1.6.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"html-entities": "^2.3.3", "html-entities": "^2.4.0",
"html-minifier-terser": "^7.0.0-beta.0", "html-minifier-terser": "^7.0.0-beta.0",
"imagickal": "^5.0.1", "imagickal": "^5.0.1",
"markdown-it-prism": "^2.3.0", "markdown-it-prism": "^2.3.0",

View file

@ -0,0 +1,178 @@
local split_shell = require("worldeditadditions_core.utils.strings.split_shell")
describe("split_shell", function()
it("should handle a single case x3", function()
assert.are.same(
{ "yay", "yay", "yay" },
split_shell("yay yay yay")
)
end)
it("should handle double quotes simple", function()
assert.are.same(
{ "dirt", "snow block" },
split_shell("dirt \"snow block\"")
)
end)
it("should handle an escaped double quote inside double quotes", function()
assert.are.same(
{ "yay", "yay\" yay", "yay" },
split_shell("yay \"yay\\\" yay\" yay")
)
end)
it("should handle single quotes", function()
assert.are.same(
{ "yay", "yay", "yay" },
split_shell("yay 'yay' yay")
)
end)
it("should handle single quotes again", function()
assert.are.same(
{ "yay", "inside quotes", "another" },
split_shell("yay 'inside quotes' another")
)
end)
it("should handle single quotes inside double quotes", function()
assert.are.same(
{ "yay", "yay 'inside quotes' yay", "yay" },
split_shell("yay \"yay 'inside quotes' yay\" yay")
)
end)
it("should handle single quotes and an escaped double quote inside double quotes", function()
assert.are.same(
{ "yay", "yay 'inside quotes' yay\"", "yay" },
split_shell("yay \"yay 'inside quotes' yay\\\"\" yay")
)
end)
it("should handle a complex case", function()
assert.are.same(
{ "y\"ay", "yay 'in\"side quotes' yay", "y\"ay" },
split_shell("y\"ay \"yay 'in\\\"side quotes' yay\" y\\\"ay")
)
end)
it("should handle a subtly different complex case", function()
assert.are.same(
{ "y\"ay", "yay", "in\"side quotes", "yay", "y\"ay" },
split_shell("y\"ay yay 'in\\\"side quotes' yay y\\\"ay")
)
end)
it("should handle redundant double quotes", function()
assert.are.same(
{ "cake" },
split_shell("\"cake\"")
)
end)
it("should handle redundant double quotes multi", function()
assert.are.same(
{ "cake", "cake", "cake" },
split_shell("\"cake\" \"cake\" \"cake\"")
)
end)
it("should handle redundant single quotes", function()
assert.are.same(
{ "cake" },
split_shell("'cake'")
)
end)
it("should handle redundant single quotes multi", function()
assert.are.same(
{ "cake", "cake", "cake" },
split_shell("'cake' 'cake' 'cake'")
)
end)
it("should handle redundant double and single quotes", function()
assert.are.same(
{ "cake", "cake", "cake" },
split_shell("'cake' \"cake\" 'cake'")
)
end)
it("should handle redundant double and single quotes opposite", function()
assert.are.same(
{ "cake", "cake", "cake" },
split_shell("\"cake\" 'cake' \"cake\"")
)
end)
it("should handle a random backslash single quotes", function()
assert.are.same(
{ "cake", "ca\\ke" },
split_shell("\"cake\" 'ca\\ke'")
)
end)
it("should handle a random backslash double quotes", function()
assert.are.same(
{ "cake", "ca\\ke" },
split_shell("\"cake\" \"ca\\ke\"")
)
end)
it("should handle a backslash after double quotes", function()
assert.are.same(
{ "\\cake", "cake" },
split_shell("\"\\cake\" \"cake\"")
)
end)
it("should handle a double backslash before double quotes", function()
assert.are.same(
{ "\\\"cake\"", "cake" },
split_shell("\\\\\"cake\" \"cake\"")
)
end)
it("should handle a single backslash before double quotes", function()
assert.are.same(
{ "\"cake\"", "cake" },
split_shell("\\\"cake\" \"cake\"")
)
end)
it("should handle a double backslash before single quotes", function()
assert.are.same(
{ "\\'cake'", "cake" },
split_shell("\\\\'cake' 'cake'")
)
end)
it("should handle a single backslash before single quotes", function()
assert.are.same(
{ "\"cake\"", "cake" },
split_shell("\\\"cake\" \"cake\"")
)
end)
it("should handle redundant double and single quotes again", function()
assert.are.same(
{ "cake", "cake", "cake", "is", "a", "li\\e" },
split_shell("\"cake\" 'cake' \"cake\" is a \"li\\e\"")
)
end)
-- Unclosed quotes are currently considered to last until the end of the string.
it("should handle an unclosed double quote", function()
assert.are.same(
{ "the", "cake is a lie" },
split_shell("the \"cake is a lie")
)
end)
it("should handle an unclosed single quote", function()
assert.are.same(
{ "the", "cake is a lie" },
split_shell("the 'cake is a lie")
)
end)
it("should handle an unclosed single quote at the end", function()
assert.are.same(
{ "the", "cake is a lie'" },
split_shell("the \"cake is a lie'")
)
end)
it("should handle an unclosed single and double quote", function()
assert.are.same(
{ "the", "cake is \"a lie" },
split_shell("the 'cake is \"a lie")
)
end)
end)

View file

@ -4,20 +4,26 @@ It's about time I started a changelog! This will serve from now on as the main c
Note to self: See the bottom of this file for the release template text. Note to self: See the bottom of this file for the release template text.
## v1.14: The untitled update (unreleased) ## v1.14: The multipoint update (unreleased)
- Add `//dome+`, which allows you to change the direction the dome is pointing in, and also create multiple domes at once - Add `//dome+`, which allows you to change the direction the dome is pointing in, and also create multiple domes at once
- Add `//metaball`, which renders 2 or more [metaballs](https://en.wikipedia.org/wiki/Metaballs) in Minetest - Add `//metaball`, which renders 2 or more [metaballs](https://en.wikipedia.org/wiki/Metaballs) in Minetest
- Add `//revolve`, which makes multiple evenly-spaced rotated copies of the defined region
- Migrate from `depends.txt` to `mod.conf`
- `//sculpt`: Fix undefined `default` brush
- Commands that modify the terrain now ignore liquids
- `//hollow`: Fix safe region bug
- Significant backend refactoring to tidy things up - Significant backend refactoring to tidy things up
- Add new multi-point selection wand ![A picture of the multi-point wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions_farwand/textures/worldeditadditions_multiwand.png) to select many points at once. **Not currently compatible with other wands**, as it's a work-in-progress (commands that support/require more than 2 points are hopefully coming soon) - Add new multi-point selection wand ![A picture of the multi-point wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions_farwand/textures/worldeditadditions_multiwand.png) to select many points at once.
- Add `//spline`, for drawing curved lines with an arbitrary number of points **(uses the new multi-point wand)** - Implement custom region boxing UI, which replaces the WorldEdit region box when using WorldEditAdditions wands.
- Is backwards compatible with regular WorldEdit wands and tools, as WorldEditAdditions keeps the new positioning system in sync with WorldEdit's.
- Add [`//spline`](https://worldeditadditions.mooncarrot.space/Reference/#spline), for drawing curved lines with an arbitrary number of points **(uses the new multi-point wand)**
- Add [`//revolve`](https://worldeditadditions.mooncarrot.space/Reference/#revolve), which makes multiple evenly-spaced rotated copies of the defined region **(uses the new multi-point wand)**
- [`//copy+`](https://worldeditadditions.mooncarrot.space/Reference/#copy), [`//move+`](https://worldeditadditions.mooncarrot.space/Reference/#move): Added support for integrated `airapply` mode, which replaces nodes at the target only if they are air - append `airapply`/`aa` to the command to use
### Bugfixes ### Bugfixes and changes
- Migrate from `depends.txt` to `mod.conf`
- Cloud wand: fix typo in item description. - Cloud wand: fix typo in item description.
- Commands that modify the terrain now ignore liquids
- `//sculpt`:
- Fix undefined `default` brush
- Change defaults to `circle`, `height=1`, and `brushsize=8`.
- Change argument ordering to put `height` after `brushsize` instead of the other way around
- `//hollow`: Fix safe region bug
## v1.13: The transformational update (2nd January 2022) ## v1.13: The transformational update (2nd January 2022)
@ -36,10 +42,10 @@ Note to self: See the bottom of this file for the release template text.
- Add [`//sculpt`](https://worldeditadditions.mooncarrot.space/Reference/#sculpt) and [`//sculptlist`](https://worldeditadditions.mooncarrot.space/Reference/#sculptlist) for sculpting terrain using a number of custom brushes. - Add [`//sculpt`](https://worldeditadditions.mooncarrot.space/Reference/#sculpt) and [`//sculptlist`](https://worldeditadditions.mooncarrot.space/Reference/#sculptlist) for sculpting terrain using a number of custom brushes.
- Use [luacheck](https://github.com/mpeterv/luacheck) to find and fix a large number of bugs and other issues [code quality from now on will be significantly improved] - Use [luacheck](https://github.com/mpeterv/luacheck) to find and fix a large number of bugs and other issues [code quality from now on will be significantly improved]
- Multiple commands: Allow using quotes (`"thing"`, `'thing'`) to quote values when splitting - Multiple commands: Allow using quotes (`"thing"`, `'thing'`) to quote values when splitting
- `//layers`: Add optional slope constraint (inspired by [WorldPainter](https://worldpainter.net/)) - [`//layers`](https://worldeditadditions.mooncarrot.space/Reference/#layers): Add optional slope constraint (inspired by [WorldPainter](https://worldpainter.net/))
- `//bonemeal`: Add optional node list constraint - [`//bonemeal`](https://worldeditadditions.mooncarrot.space/Reference/#bonemeal): Add optional node list constraint
- `//walls`: Add optional thickness argument - [`//walls`](https://worldeditadditions.mooncarrot.space/Reference/#walls): Add optional thickness argument
- `//sstack`: Add human-readable approx volumes of regions in the selection stack - [`//sstack`](https://worldeditadditions.mooncarrot.space/Reference/#sstack): Add human-readable approx volumes of regions in the selection stack
### Bugfixes ### Bugfixes

View file

@ -37,7 +37,8 @@ When actually implementing stuff, here are a few guidelines that I recommend to
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ████ ██ ██ ██ ██ ███████ -- ██ ████ ██ ██ ██ ██ ███████
local wea = worldeditadditions local wea = worldeditadditions
local wea_c = worldeditadditions_core local weac = worldeditadditions_core
local Vector3 = weac.Vector3
worldeditadditions_core.register_command("{name}", { worldeditadditions_core.register_command("{name}", {
params = "<argument> <argument=default> <option1|option2|...> [<optional_argument> <optional_argument2> ...] | [<optional_argument> [<optional_argument2>]]", params = "<argument> <argument=default> <option1|option2|...> [<optional_argument> <optional_argument2> ...] | [<optional_argument> [<optional_argument2>]]",
description = "A **brief** description of what this command does", description = "A **brief** description of what this command does",
@ -48,18 +49,18 @@ worldeditadditions_core.register_command("{name}", {
return true, param1, param2 return true, param1, param2
end, end,
nodes_needed = function(name) --Optional nodes_needed = function(name) --Optional
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) return Vector3.volume(weac.pos.get1(name), weac.pos.get2(name))
end, end,
func = function(name, param1, param2) func = function(name, param1, param2)
-- Start a timer -- Start a timer
local start_time = wea_c.get_ms_time() local start_time = weac.get_ms_time()
-- Do stuff -- Do stuff
-- Finish timer -- Finish timer
local time_taken = wea_c.get_ms_time() - start_time local time_taken = weac.get_ms_time() - start_time
minetest.log("This is a logged message!") minetest.log("This is a logged message!")
return true, "Completed command in " .. wea_c.format.human_time(time_taken) return true, "Completed command in " .. weac.format.human_time(time_taken)
end end
}) })
``` ```

View file

@ -227,6 +227,8 @@ Floods all connected nodes of the same type starting at _pos1_ with `<replace_no
### `//wbox <replace_node>` ### `//wbox <replace_node>`
Sets the edges of the current selection to `<replace_node>`to create an outline of a rectangular prism. Useful for roughing in walls. Sets the edges of the current selection to `<replace_node>`to create an outline of a rectangular prism. Useful for roughing in walls.
In other words, creates a wireframe of a box defined by the current selection.
```weacmd ```weacmd
//wbox silver_sandstone //wbox silver_sandstone
//wbox dirt //wbox dirt
@ -331,7 +333,7 @@ By adding 3 extra numbers for the x, y, and z axes respectively, we can control
So in the above example, we scale in the positive x and z directions, and the negative y direction. So in the above example, we scale in the positive x and z directions, and the negative y direction.
### `//copy+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]` ### `//copy+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]] [aa|airapply]`
Fully backwards-compatible with `//copy` from regular WorldEdit, but allows you to specify multiple axes at once in a single copy operation. Each successive axis in the list is specified in the form `<axis> <count>`, where: Fully backwards-compatible with `//copy` from regular WorldEdit, but allows you to specify multiple axes at once in a single copy operation. Each successive axis in the list is specified in the form `<axis> <count>`, where:
- `<axis>` is the name of the axis to move the defined region along - `<axis>` is the name of the axis to move the defined region along
@ -354,6 +356,8 @@ All of the following values are valid axes:
Additionally all the absolute axis names (`x`/`y`/`z`/`-x`/`-y`/`-z`) may also be specified multiple times under the same count - e.g. `xy-z 6`. Additionally all the absolute axis names (`x`/`y`/`z`/`-x`/`-y`/`-z`) may also be specified multiple times under the same count - e.g. `xy-z 6`.
Finally, if the word `airapply` (or `aa` for short) is present at the end of the command invocation it enables the integrated airapply mode, which replaces target nodes only if they are air-like.
``` ```
//copy+ x 6 //copy+ x 6
//copy+ y 10 z 4 //copy+ y 10 z 4
@ -362,12 +366,17 @@ Additionally all the absolute axis names (`x`/`y`/`z`/`-x`/`-y`/`-z`) may also b
//copy+ xz 50 front 22 //copy+ xz 50 front 22
//copy+ yx 25 //copy+ yx 25
//copy+ -xz-y 10 //copy+ -xz-y 10
//copy+ y 45 aa
//copy+ -y 15 z 5 airapply
``` ```
### `//move+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]` ### `//move+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]] [aa|airapply]`
Identical to [`//copy+`](#copy), but instead moves the defined region instead of copying it. Identical to [`//copy+`](#copy), but instead moves the defined region instead of copying it.
Note that the integrated `airapply` (`aa` for short) also works as in [`//copy+`](#copy), but remember that if a given target node is not *not* air-like and the integrated `airapply` mode is enabled, the source node is still moved from the source, but destroyed because it is can't be set at the target.
``` ```
//move+ x 6 //move+ x 6
//move+ y 10 z 4 //move+ y 10 z 4
@ -376,6 +385,8 @@ Identical to [`//copy+`](#copy), but instead moves the defined region instead of
//move+ xz 50 front 22 //move+ xz 50 front 22
//move+ yx 25 //move+ yx 25
//move+ -xz-y 10 //move+ -xz-y 10
//move+ back 20 aa
//move+ -z 45 y 3 airapply
``` ```
@ -668,7 +679,7 @@ Lists all the available sculpting brushes for use with `//sculpt`. If the `previ
``` ```
### `//sculpt [<brush_name=default> [<height=5> [<brush_size=10>]]]` ### `//sculpt [<brush_name=default> [<brush_size=8> [<height=1>]]]`
Applies a specified brush to the terrain at position 1 with a given height and a given size. Multiple brushes exist (see [`//sculptlist`](#sculptlist)) - and are represented as a 2D grid of values between 0 and 1, which are then scaled to the specified height. The terrain around position 1 is first converted to a 2D heightmap (as in [`//convolve`](#convolve) before the brush "heightmap" is applied to it. Applies a specified brush to the terrain at position 1 with a given height and a given size. Multiple brushes exist (see [`//sculptlist`](#sculptlist)) - and are represented as a 2D grid of values between 0 and 1, which are then scaled to the specified height. The terrain around position 1 is first converted to a 2D heightmap (as in [`//convolve`](#convolve) before the brush "heightmap" is applied to it.
Similar to [`//sphere`](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#sphere-radius-node), [`//cubeapply 10 set`](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#cubeapply-sizesizex-sizey-sizez-command-parameters), or [`//cylinder y 5 10 10 dirt`](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#cylinder-xyz-length-radius1-radius2-node) (all from [WorldEdit](https://content.minetest.net/packages/sfan5/worldedit/)), but has a number of added advantages: Similar to [`//sphere`](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#sphere-radius-node), [`//cubeapply 10 set`](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#cubeapply-sizesizex-sizey-sizez-command-parameters), or [`//cylinder y 5 10 10 dirt`](https://github.com/Uberi/Minetest-WorldEdit/blob/master/ChatCommands.md#cylinder-xyz-length-radius1-radius2-node) (all from [WorldEdit](https://content.minetest.net/packages/sfan5/worldedit/)), but has a number of added advantages:
@ -684,9 +695,9 @@ The selection of available brushes is limited at the moment, but see below on ho
``` ```
//sculpt //sculpt
//sculpt default 10 25 //sculpt default 25 3
//sculpt ellipse //sculpt ellipse
//sculpt circle 5 50 //sculpt circle 50 3
``` ```
#### Create your own brushes #### Create your own brushes

View file

@ -128,6 +128,8 @@ cd WorldEditAdditions
git checkout "$(git describe --tags --abbrev=0)"; git checkout "$(git describe --tags --abbrev=0)";
``` ```
If you do not checkout the latest release, you will be using the development version of WorldEditAdditions. While every effort is made to ensure that the development version is stable at all times, this is not a guarantee.
Windows users, you'll need to check the [releases page](https://github.com/sbrl/Minetest-WorldEditAdditions/releases) and find the name of the latest release, then do this instead of the `git checkout` above: Windows users, you'll need to check the [releases page](https://github.com/sbrl/Minetest-WorldEditAdditions/releases) and find the name of the latest release, then do this instead of the `git checkout` above:
```bash ```bash
@ -159,7 +161,18 @@ Contributions are welcome! Please state in your pull request(s) that you release
Please also make sure that the logic for every new command has it's own file. For example, the logic for `//floodfill` goes in `worldeditadditions/floodfill.lua`, the logic for `//overlay` goes in `worldeditadditions/overlay.lua`, etc. More contributing help can be found in [the contributing guide](CONTRIBUTING.md). Please also make sure that the logic for every new command has it's own file. For example, the logic for `//floodfill` goes in `worldeditadditions/floodfill.lua`, the logic for `//overlay` goes in `worldeditadditions/overlay.lua`, etc. More contributing help can be found in [the contributing guide](CONTRIBUTING.md).
I, Starbeamrainbowlabs (@sbrl), have the ultimate final say. ### Inspiration
Want to contribute, but finding it tough to search for inspiration of what to implement? Here are some great places to look:
- [**Our issue tracker:**](https://github.com/sbrl/Minetest-WorldEditAdditions/issues) There are always a bunch of issues open with cool commands and features that have yet to be implemented.
- **Other software:** Software for Minecraft is often far more mature than that available for Minetest. As a result, it's full of cool ideas. A lot of the existing commands in WorldEditAdditions were sourced from here.
- WorldEdit for Minecraft
- VoxelSniper(-Reimagined) for Minecraft
- WorldPainter for Minecraft
- **Do some building:** When you put WorldEditAdditions to use in building projects of your own, things will absolutely stand out to you what you want in the creative building process that WorldEditAdditions doesn't yet have.
- **Watch others build stuff:** Doesn't even have to be Minetest! There are lots of talented Minecraft builders with videos and series on e.g. YouTube. From their creative building processes, many ideas can be derived.
The ultimate goal is for WorldEditAdditions to support the creative building process in a way that enables builders of all backgrounds to create incredible things.
## WorldEditAdditions around the web ## WorldEditAdditions around the web

View file

@ -1,7 +1,7 @@
--- WorldEditAdditions --- WorldEditAdditions
-- @module worldeditadditions -- @namespace worldeditadditions
-- @release 0.1 -- @release 1.13
-- @copyright 2018 Starbeamrainbowlabs -- @copyright 2023 Starbeamrainbowlabs
-- @license Mozilla Public License, 2.0 -- @license Mozilla Public License, 2.0
-- @author Starbeamrainbowlabs -- @author Starbeamrainbowlabs

View file

@ -13,7 +13,7 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ███████ ██ -- ██ ██ ██ ██ ███████ ██
--- Similar to cubeapply, except that it takes 2 positions and only keeps an ellipsoid-shaped area defined by the boundaries of the defined region. --- Like ellipsoidapply, but only keeps changes that replace airlike nodes, and discards any other changes made.
-- Takes a backup copy of the defined region, runs the given function, and then -- Takes a backup copy of the defined region, runs the given function, and then
-- restores the bits around the edge that aren't inside the largest ellipsoid that will fit inside the defined region. -- restores the bits around the edge that aren't inside the largest ellipsoid that will fit inside the defined region.
-- @param {Position} pos1 The 1st position defining the region boundary -- @param {Position} pos1 The 1st position defining the region boundary

View file

@ -2,11 +2,12 @@ local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
--- Bonemeal command. --- Bonemeal command.
-- Applies bonemeal to all notes -- Applies bonemeal to all nodes with an air bloc above then.
-- @module worldeditadditions.overlay -- @param strength The strength to apply - see bonemeal:on_use
-- @param chance Positive integer that represents the chance bonemealing will occur
-- strength The strength to apply - see bonemeal:on_use -- @returns bool,number,number 1. Whether the command succeeded or not.
-- chance Positive integer that represents the chance bonemealing will occur -- 2. The number of nodes actually bonemealed
-- 3. The number of possible candidates we could have bonemealed
function worldeditadditions.bonemeal(pos1, pos2, strength, chance, nodename_list) function worldeditadditions.bonemeal(pos1, pos2, strength, chance, nodename_list)
if not nodename_list then nodename_list = {} end if not nodename_list then nodename_list = {} end
pos1, pos2 = Vector3.sort(pos1, pos2) pos1, pos2 = Vector3.sort(pos1, pos2)

View file

@ -1,8 +1,6 @@
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
--- Copies a region to another location, potentially overwriting the exiting region.
-- @module worldeditadditions.copy
-- ██████ ██████ ██████ ██ ██ -- ██████ ██████ ██████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
@ -10,7 +8,16 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ██████ ██████ ██ ██ -- ██████ ██████ ██ ██
function worldeditadditions.copy(source_pos1, source_pos2, target_pos1, target_pos2) --- Copies a region to another location, potentially overwriting the exiting region.
-- @param source_pos1 Vector3 pos1 of the source region to copy.
-- @param source_pos2 Vector3 pos2 of the source region to copy.
-- @param target_pos1 Vector3 pos1 of the target region to copy to.
-- @param target_pos2 Vector3 pos2 of the target region to copy to.
-- @param airapply=false bool Whether to only replace target nodes that are air-like, leaving those that are not air-like. If false, then all target nodes are replaced regardless of whether they are air-like nodes or not.
-- @returns bool,numbers 1. Whether the copy operation was successful or not
-- 2. The total number of nodes copied.
function worldeditadditions.copy(source_pos1, source_pos2, target_pos1, target_pos2, airapply)
if airapply == nil then airapply = false end
source_pos1, source_pos2 = Vector3.sort(source_pos1, source_pos2) source_pos1, source_pos2 = Vector3.sort(source_pos1, source_pos2)
target_pos1, target_pos2 = Vector3.sort(target_pos1, target_pos2) target_pos1, target_pos2 = Vector3.sort(target_pos1, target_pos2)
@ -26,7 +33,7 @@ function worldeditadditions.copy(source_pos1, source_pos2, target_pos1, target_p
local data_target = manip_target:get_data() local data_target = manip_target:get_data()
-- z y x is the preferred loop order (because CPU cache, since then we're iterating linearly through the data array backwards. This only holds true for little-endian machines however) -- z y x is the preferred loop order (because CPU cache, since then we're iterating linearly through the data array backwards. This only holds true for little-endian machines however)
local total_replaced = 0
for z = source_pos2.z, source_pos1.z, -1 do for z = source_pos2.z, source_pos1.z, -1 do
for y = source_pos2.y, source_pos1.y, -1 do for y = source_pos2.y, source_pos1.y, -1 do
for x = source_pos2.x, source_pos1.x, -1 do for x = source_pos2.x, source_pos1.x, -1 do
@ -35,7 +42,14 @@ function worldeditadditions.copy(source_pos1, source_pos2, target_pos1, target_p
local target = source - offset local target = source - offset
local target_i = area_target:index(target.x, target.y, target.z) local target_i = area_target:index(target.x, target.y, target.z)
data_target[target_i] = data_source[source_i] local should_replace = true
if airapply then
should_replace = wea_c.is_airlike(data_target[target_i])
end
if should_replace then
data_target[target_i] = data_source[source_i]
total_replaced = total_replaced + 1
end
end end
end end
end end
@ -43,5 +57,5 @@ function worldeditadditions.copy(source_pos1, source_pos2, target_pos1, target_p
-- Save the modified nodes back to disk & return -- Save the modified nodes back to disk & return
worldedit.manip_helpers.finish(manip_target, data_target) worldedit.manip_helpers.finish(manip_target, data_target)
return true, worldedit.volume(target_pos1, target_pos2) return true, total_replaced
end end

View file

@ -1,14 +1,19 @@
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
--- Counts the nodes in a given area.
-- @module worldeditadditions.count
-- ██████ ██████ ██ ██ ███ ██ ████████ -- ██████ ██████ ██ ██ ███ ██ ████████
-- ██ ██ ██ ██ ██ ████ ██ ██ -- ██ ██ ██ ██ ██ ████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██████ ██ ████ ██ -- ██████ ██████ ██████ ██ ████ ██
--- Counts the nodes in a given area.
-- @param pos1 Vector3 pos1 of the defined region to count nodes in.
-- @param pos2 Vector3 pos2 of the defined region to count nodes in.
-- @param do_human_counts bool Whether to return human-readable counts (as a string) instead of the raw numbers.
-- @returns bool,table<number,number>,number 1. Whether the operation was successful or not.
-- 2. A table mapping node ids to the number of that node id seen.
-- 3. The total number of nodes counted.
function worldeditadditions.count(pos1, pos2, do_human_counts) function worldeditadditions.count(pos1, pos2, do_human_counts)
pos1, pos2 = Vector3.sort(pos1, pos2) pos1, pos2 = Vector3.sort(pos1, pos2)
-- pos2 will always have the highest co-ordinates now -- pos2 will always have the highest co-ordinates now

View file

@ -10,11 +10,12 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██████ ████ ███████ -- ██ ██ ██████ ████ ███████
function worldeditadditions.move(source_pos1, source_pos2, target_pos1, target_pos2) function worldeditadditions.move(source_pos1, source_pos2, target_pos1, target_pos2, airapply)
--- ---
-- 0: Preamble -- 0: Preamble
--- ---
if airapply == nil then airapply = false end
source_pos1, source_pos2 = Vector3.sort(source_pos1, source_pos2) source_pos1, source_pos2 = Vector3.sort(source_pos1, source_pos2)
target_pos1, target_pos2 = Vector3.sort(target_pos1, target_pos2) target_pos1, target_pos2 = Vector3.sort(target_pos1, target_pos2)
@ -45,9 +46,13 @@ function worldeditadditions.move(source_pos1, source_pos2, target_pos1, target_p
local target = source:subtract(offset) local target = source:subtract(offset)
local target_i = area_target:index(target.x, target.y, target.z) local target_i = area_target:index(target.x, target.y, target.z)
local should_replace = true
data_target[target_i] = data_source[source_i] if airapply then
should_replace = wea_c.is_airlike(data_target[target_i])
end
if should_replace then
data_target[target_i] = data_source[source_i]
end
end end
end end
end end

View file

@ -5,7 +5,7 @@ local Vector3 = wea_c.Vector3
--- Perlin noise generation engine. --- Perlin noise generation engine.
-- Original code by Ken Perlin: http://mrl.nyu.edu/~perlin/noise/ -- Original code by Ken Perlin: http://mrl.nyu.edu/~perlin/noise/
-- Port from this StackOverflow answer: https://stackoverflow.com/a/33425812/1460422 -- Port from this StackOverflow answer: https://stackoverflow.com/a/33425812/1460422
-- @class -- @class worldeditadditions.noise.engines.Perlin
local Perlin = {} local Perlin = {}
Perlin.__index = Perlin Perlin.__index = Perlin

View file

@ -3,12 +3,14 @@ local Vector3 = wea_c.Vector3
--- Like //mix, but replaces a given node instead. --- Like //mix, but replaces a given node instead.
-- @module worldeditadditions.replacemix -- @module worldeditadditions.replacemix
-- TODO: Implement //replacesplat, which picks seeder nodes with a percentage chance, and then some growth passes with e.g. cellular automata? We should probably be pushing towards a release though round about now
-- ██████ ███████ ██████ ██ █████ ██████ ███████ ███ ███ ██ ██ ██ -- ██████ ███████ ██████ ██ █████ ██████ ███████ ███ ███ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██
-- ██████ █████ ██████ ██ ███████ ██ █████ ██ ████ ██ ██ ███ -- ██████ █████ ██████ ██ ███████ ██ █████ ██ ████ ██ ██ ███
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ██ ███████ ██ ██ ██████ ███████ ██ ██ ██ ██ ██ -- ██ ██ ███████ ██ ███████ ██ ██ ██████ ███████ ██ ██ ██ ██ ██
function worldeditadditions.replacemix(pos1, pos2, target_node, target_node_chance, replacements) function worldeditadditions.replacemix(pos1, pos2, target_node, target_node_chance, replacements)
pos1, pos2 = Vector3.sort(pos1, pos2) pos1, pos2 = Vector3.sort(pos1, pos2)
-- pos2 will always have the highest co-ordinates now -- pos2 will always have the highest co-ordinates now

View file

@ -5,10 +5,10 @@ local Vector3 = wea_c.Vector3
--- Applies the given brush with the given height and size to the given position. --- Applies the given brush with the given height and size to the given position.
-- @param pos1 Vector3 The position at which to apply the brush. -- @param pos1 Vector3 The position at which to apply the brush.
-- @param brush_name string The name of the brush to apply. -- @param brush_name string The name of the brush to apply.
-- @param height number The height of the brush application.
-- @param brush_size Vector3 The size of the brush application. Values are interpreted on the X/Y coordinates, and NOT X/Z! -- @param brush_size Vector3 The size of the brush application. Values are interpreted on the X/Y coordinates, and NOT X/Z!
-- @param height number The height of the brush application.
-- @returns bool, string|{ added: number, removed: number } A bool indicating whether the operation was successful or not, followed by either an error message as a string (if it was not successful) or a table of statistics (if it was successful). -- @returns bool, string|{ added: number, removed: number } A bool indicating whether the operation was successful or not, followed by either an error message as a string (if it was not successful) or a table of statistics (if it was successful).
local function apply(pos1, brush_name, height, brush_size) local function apply(pos1, brush_name, brush_size, height)
-- 1: Get & validate brush -- 1: Get & validate brush
local success, brush, brush_size_actual = wea.sculpt.make_brush(brush_name, brush_size) local success, brush, brush_size_actual = wea.sculpt.make_brush(brush_name, brush_size)
if not success then return success, brush end if not success then return success, brush end

View file

@ -3,6 +3,8 @@ local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
--- Returns a smooth gaussian brush. --- Returns a smooth gaussian brush.
-- @name make_gaussian
-- @internal
-- @param size Vector3 The target size of the brush. Note that the actual size of the brush will be different, as the gaussian function has some limitations. -- @param size Vector3 The target size of the brush. Note that the actual size of the brush will be different, as the gaussian function has some limitations.
-- @param sigma=2 number The 'smoothness' of the brush. Higher values are more smooth. -- @param sigma=2 number The 'smoothness' of the brush. Higher values are more smooth.
return function(size, sigma) return function(size, sigma)

View file

@ -2,6 +2,7 @@ local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
--- Makes a circle brush of a given size. --- Makes a circle brush of a given size.
-- @name circle
-- @param size Vector3 The desired sizez of the brush (only X and Y are considered; Z is ignored). -- @param size Vector3 The desired sizez of the brush (only X and Y are considered; Z is ignored).
-- @returns bool,brush,Vector3 Success bool, then the brush, then finally the actual size of the brush generated. -- @returns bool,brush,Vector3 Success bool, then the brush, then finally the actual size of the brush generated.
return function(size) return function(size)

View file

@ -1,5 +1,8 @@
--- Returns a simple square brush with 100% weight for every pixel. --- Returns a simple square brush with 100% weight for every pixel.
-- @name square
-- @param size Vector3 The desired size of the brush. Only the x and y components are used; the z component is ignored.
-- @returns bool,number[],Vector3 1: true, as this function always succeeds. 2: A simple square brush as a zero-indexed flat array. 3: The size of the resulting brush as a Vector3, using the x and y components.
return function(size) return function(size)
local result = {} local result = {}
for y=0, size.y do for y=0, size.y do

View file

@ -3,6 +3,8 @@ local wea = worldeditadditions
local parse_static = dofile(wea.modpath.."/lib/sculpt/parse_static.lua") local parse_static = dofile(wea.modpath.."/lib/sculpt/parse_static.lua")
--- Reads and parses the brush stored in the specified file. --- Reads and parses the brush stored in the specified file.
-- @name import_static
-- @internal
-- @param filepath string The path to file that contains the static brush to read in. -- @param filepath string The path to file that contains the static brush to read in.
-- @returns true,table,Vector3|false,string A success boolean, followed either by an error message as a string or the brush (as a table) and it's size (as an X/Y Vector3) -- @returns true,table,Vector3|false,string A success boolean, followed either by an error message as a string or the brush (as a table) and it's size (as an X/Y Vector3)
return function(filepath) return function(filepath)

View file

@ -3,6 +3,8 @@ local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
--- Parses a static brush definition. --- Parses a static brush definition.
-- @name parse_static
-- @internal
-- @param source string The source string that contains the static brush, formatted as TSV. -- @param source string The source string that contains the static brush, formatted as TSV.
-- @returns true,table,Vector3|false,string A success boolean, followed either by an error message as a string or the brush (as a table) and it's size (as an X/Y Vector3) -- @returns true,table,Vector3|false,string A success boolean, followed either by an error message as a string or the brush (as a table) and it's size (as an X/Y Vector3)
return function(source) return function(source)

View file

@ -22,6 +22,8 @@ end
--- Scans the given directory and imports all static brushes found. --- Scans the given directory and imports all static brushes found.
-- Static brushes have the file extension ".brush.tsv" (without quotes). -- Static brushes have the file extension ".brush.tsv" (without quotes).
-- @name scan_static
-- @internal
-- @param dirpath string The path to directory that contains the static brushs to import. -- @param dirpath string The path to directory that contains the static brushs to import.
-- @returns bool,loaded,errors A success boolean, followed by the number of brushes loaded, followed by the number of errors encountered while loading brushes (errors are logged as warnings with Minetest) -- @returns bool,loaded,errors A success boolean, followed by the number of brushes loaded, followed by the number of errors encountered while loading brushes (errors are logged as warnings with Minetest)
return function(dirpath, overwrite_existing) return function(dirpath, overwrite_existing)

View file

@ -9,28 +9,39 @@ local Vector3 = wea_c.Vector3
---Selection helpers and modifiers ---Selection helpers and modifiers
local selection = {} local selection = {}
--- Additively adds a point to the current selection or --- Additively adds a point to the current selection defined by pos1..pos2 or
-- makes a selection from the provided point. -- makes a selection from the provided point.
-- @param name string Player name. -- @param name string Player name.
-- @param pos vector The position to include. -- @param pos vector The position to include.
function selection.add_point(name, pos) function selection.add_point(name, newpos)
if pos ~= nil then if newpos ~= nil then
local created_new = not worldedit.pos1[name] or not worldedit.pos2[name] -- print("DEBUG:selection.add_point newpos", newpos)
-- print("[set_pos1]", name, "("..pos.x..", "..pos.y..", "..pos.z..")") local has_pos1 = not not wea_c.pos.get1(name)
if not worldedit.pos1[name] then worldedit.pos1[name] = Vector3.clone(pos) end local has_pos2 = not not wea_c.pos.get2(name)
if not worldedit.pos2[name] then worldedit.pos2[name] = Vector3.clone(pos) end local created_new = not has_pos1 or not has_pos2
if not has_pos1 then wea_c.pos.set1(name, Vector3.clone(newpos)) end
if not has_pos2 then wea_c.pos.set2(name, Vector3.clone(newpos)) end
worldedit.marker_update(name) -- Now no longer needed, given that the new sysstem uses an event listener to push updates to the selected region automatically
-- worldedit.marker_update(name)
local volume_before = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) local pos1, pos2 = wea_c.pos.get1(name), wea_c.pos.get2(name)
worldedit.pos1[name], worldedit.pos2[name] = Vector3.expand_region( local volume_before = worldedit.volume(pos1, pos2)
Vector3.clone(worldedit.pos1[name]),
Vector3.clone(worldedit.pos2[name]),
pos local new_pos1, new_pos2 = Vector3.expand_region(
Vector3.clone(pos1),
Vector3.clone(pos2),
newpos
) )
local volume_after = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) wea_c.pos.set1(name, new_pos1)
wea_c.pos.set2(name, new_pos2)
local volume_after = worldedit.volume(new_pos1, new_pos2)
local volume_difference = volume_after - volume_before local volume_difference = volume_after - volume_before
if volume_difference == 0 and created_new then if volume_difference == 0 and created_new then
@ -42,21 +53,20 @@ function selection.add_point(name, pos)
msg = msg..volume_difference.." node" msg = msg..volume_difference.." node"
if volume_difference ~= 1 then msg = msg.."s" end if volume_difference ~= 1 then msg = msg.."s" end
worldedit.marker_update(name) -- Done automatically
-- worldedit.marker_update(name)
worldedit.player_notify(name, msg) worldedit.player_notify(name, msg)
else else
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)") worldedit.player_notify(name, "Error. Too far away (try raising your maxdist with //farwand maxdist <number>)")
-- print("[set_pos1]", name, "nil") -- print("[set_pos1]", name, "nil")
end end
end end
--- Clears current selection. --- Clears current selection, *but only pos1 and pos2!
-- @param name string Player name. -- @param name string Player name.
function selection.clear_points(name) function selection.clear_points(name)
worldedit.pos1[name] = nil wea_c.pos.clear(name)
worldedit.pos2[name] = nil -- worldedit.marker_update(name)
worldedit.marker_update(name)
worldedit.set_pos[name] = nil
worldedit.player_notify(name, "Region cleared") worldedit.player_notify(name, "Region cleared")
end end

View file

@ -3,6 +3,12 @@ local wea = worldeditadditions
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
local function parse_stage2(name, parts) local function parse_stage2(name, parts)
local do_airapply = false
if parts[#parts] == "aa" or parts[#parts] == "airapply" then
do_airapply = true
table.remove(parts, #parts)
end
local success, vpos1, vpos2 = wea_c.parse.axes( local success, vpos1, vpos2 = wea_c.parse.axes(
parts, parts,
wea_c.player_dir(name) wea_c.player_dir(name)
@ -17,7 +23,7 @@ local function parse_stage2(name, parts)
return false, "Refusing to copy region a distance of 0 nodes" return false, "Refusing to copy region a distance of 0 nodes"
end end
return true, offset:floor() return true, offset:floor(), do_airapply
end end
-- ██████ ██████ ██████ ██ ██ -- ██████ ██████ ██████ ██ ██
@ -26,7 +32,7 @@ end
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ██████ ██████ ██ ██ -- ██████ ██████ ██ ██
worldeditadditions_core.register_command("copy+", { -- TODO: Make this an override worldeditadditions_core.register_command("copy+", { -- TODO: Make this an override
params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]", params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]] [aa|airapply]",
description = "Copies the defined region to another location - potentially across multiple axes at once.", description = "Copies the defined region to another location - potentially across multiple axes at once.",
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 2, require_pos = 2,
@ -43,7 +49,7 @@ worldeditadditions_core.register_command("copy+", { -- TODO: Make this an overri
func = function(name, parts) func = function(name, parts)
local start_time = wea_c.get_ms_time() local start_time = wea_c.get_ms_time()
local success_a, copy_offset = parse_stage2(name, parts) local success_a, copy_offset, do_airapply = parse_stage2(name, parts)
if not success_a then return success_a, copy_offset end if not success_a then return success_a, copy_offset end
local source_pos1 = Vector3.clone(worldedit.pos1[name]) local source_pos1 = Vector3.clone(worldedit.pos1[name])
@ -54,7 +60,8 @@ worldeditadditions_core.register_command("copy+", { -- TODO: Make this an overri
local success_b, nodes_modified = wea.copy( local success_b, nodes_modified = wea.copy(
source_pos1, source_pos2, source_pos1, source_pos2,
target_pos1, target_pos2 target_pos1, target_pos2,
do_airapply
) )
if not success_b then return success_b, nodes_modified end if not success_b then return success_b, nodes_modified end

View file

@ -29,7 +29,7 @@ worldeditadditions_core.register_command("count", {
) )
if not success then return success, counts end if not success then return success, counts end
local result = wea_c.format.make_ascii_table(counts).."\n".. local result = "\n"..wea_c.format.make_ascii_table(counts).."\n"..
string.rep("=", 6 + #tostring(total) + 6).."\n".. string.rep("=", 6 + #tostring(total) + 6).."\n"..
"Total "..total.." nodes\n" "Total "..total.." nodes\n"

View file

@ -116,9 +116,11 @@ worldeditadditions_core.register_command("subdivide", {
end end
worldedit.player_notify_suppress(name) worldedit.player_notify_suppress(name)
worldedit.pos1[name] = cpos1 wea_c.pos.set1(name, cpos1)
worldedit.pos2[name] = cpos2 wea_c.pos.set2(name, cpos2)
worldedit.marker_update(name) -- worldedit.pos1[name] = cpos1
-- worldedit.pos2[name] = cpos2
-- worldedit.marker_update(name)
cmd.func(name, wea_c.table.unpack(cmd_args_parsed)) cmd.func(name, wea_c.table.unpack(cmd_args_parsed))
if will_trigger_saferegion(name, cmd_name, args) then if will_trigger_saferegion(name, cmd_name, args) then
minetest.chatcommands["/y"].func(name) minetest.chatcommands["/y"].func(name)
@ -141,9 +143,12 @@ worldeditadditions_core.register_command("subdivide", {
time_last_msg = wea_c.get_ms_time() time_last_msg = wea_c.get_ms_time()
end end
end, function(_, _, stats) end, function(_, _, stats)
worldedit.pos1[name] = pos1
worldedit.pos2[name] = pos2 wea_c.pos.set1(name, pos1)
worldedit.marker_update(name) wea_c.pos.set2(name, pos2)
-- worldedit.pos1[name] = pos1
-- worldedit.pos2[name] = pos2
-- worldedit.marker_update(name)
-- Called on completion -- Called on completion
minetest.log("action", string.format("%s used //subdivide at %s - %s, with %d chunks and %d total nodes in %s", minetest.log("action", string.format("%s used //subdivide at %s - %s, with %d chunks and %d total nodes in %s",

View file

@ -3,6 +3,12 @@ local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
local function parse_stage2(name, parts) local function parse_stage2(name, parts)
local do_airapply = false
if parts[#parts] == "aa" or parts[#parts] == "airapply" then
do_airapply = true
table.remove(parts, #parts)
end
local success, vpos1, vpos2 = wea_c.parse.axes( local success, vpos1, vpos2 = wea_c.parse.axes(
parts, parts,
wea_c.player_dir(name) wea_c.player_dir(name)
@ -17,7 +23,7 @@ local function parse_stage2(name, parts)
return false, "Refusing to move region a distance of 0 nodes" return false, "Refusing to move region a distance of 0 nodes"
end end
return true, offset:floor() return true, offset:floor(), do_airapply
end end
-- ███ ███ ██████ ██ ██ ███████ -- ███ ███ ██████ ██ ██ ███████
@ -26,7 +32,7 @@ end
-- ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██████ ████ ███████ -- ██ ██ ██████ ████ ███████
worldeditadditions_core.register_command("move+", { -- TODO: Make this an override worldeditadditions_core.register_command("move+", { -- TODO: Make this an override
params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]", params = "<axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]] [aa|airapply]",
description = "Moves the defined region to another location - potentially across multiple axes at once.", description = "Moves the defined region to another location - potentially across multiple axes at once.",
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 2, require_pos = 2,
@ -43,7 +49,7 @@ worldeditadditions_core.register_command("move+", { -- TODO: Make this an overri
func = function(name, parts) func = function(name, parts)
local start_time = wea_c.get_ms_time() local start_time = wea_c.get_ms_time()
local success_a, copy_offset = parse_stage2(name, parts) local success_a, copy_offset, do_airapply = parse_stage2(name, parts)
if not success_a then return success_a, copy_offset end if not success_a then return success_a, copy_offset end
--- 1: Calculate the source & target regions --- 1: Calculate the source & target regions
@ -58,15 +64,18 @@ worldeditadditions_core.register_command("move+", { -- TODO: Make this an overri
----------------------------------------------------------------------- -----------------------------------------------------------------------
local success_b, nodes_modified = wea.move( local success_b, nodes_modified = wea.move(
source_pos1, source_pos2, source_pos1, source_pos2,
target_pos1, target_pos2 target_pos1, target_pos2,
do_airapply
) )
if not success_b then return success_b, nodes_modified end if not success_b then return success_b, nodes_modified end
-- 3: Update the defined region -- 3: Update the defined region
----------------------------------------------------------------------- -----------------------------------------------------------------------
worldedit.pos1[name] = target_pos1 wea_c.pos.set1(name, target_pos1)
worldedit.pos2[name] = target_pos2 wea_c.pos.set2(name, target_pos2)
worldedit.marker_update(name) -- worldedit.pos1[name] = target_pos1
-- worldedit.pos2[name] = target_pos2
-- worldedit.marker_update(name)
local time_taken = wea_c.get_ms_time() - start_time local time_taken = wea_c.get_ms_time() - start_time

View file

@ -117,9 +117,11 @@ wea_c.register_command("scale", {
) )
if not success then return success, stats end if not success then return success, stats end
worldedit.pos1[name] = stats.pos1 wea_c.pos.set1(name, stats.pos1)
worldedit.pos2[name] = stats.pos2 wea_c.pos.set2(name, stats.pos2)
worldedit.marker_update(name) -- worldedit.pos1[name] = stats.pos1
-- worldedit.pos2[name] = stats.pos2
-- worldedit.marker_update(name)
local time_taken = wea_c.get_ms_time() - start_time local time_taken = wea_c.get_ms_time() - start_time

View file

@ -9,20 +9,20 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ███████ ██ ██ -- ███████ ██████ ██████ ███████ ██ ██
worldeditadditions_core.register_command("sculpt", { worldeditadditions_core.register_command("sculpt", {
params = "[<brush_name=default> [<height=5> [<brush_size=10>]]]", params = "[<brush_name=default> [<brush_size=8> [<height=1>]]]",
description = "Applies a sculpting brush to the terrain with a given height. See //sculptlist to list all available brushes. Note that while the brush size is configurable, the actual brush size you end up with may be slightly different to that which you request due to brush size restrictions.", description = "Applies a sculpting brush to the terrain with a given height. See //sculptlist to list all available brushes. Note that while the brush size is configurable, the actual brush size you end up with may be slightly different to that which you request due to brush size restrictions.",
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 1, require_pos = 1,
parse = function(params_text) parse = function(params_text)
if not params_text or params_text == "" then if not params_text or params_text == "" then
params_text = "circle_soft1" params_text = "circle"
end end
local parts = wea_c.split_shell(params_text) local parts = wea_c.split_shell(params_text)
local brush_name = "circle_soft1" local brush_name = "circle"
local height = 5 local brush_size = 8
local brush_size = 10 local height = 1
if #parts >= 1 then if #parts >= 1 then
brush_name = table.remove(parts, 1) brush_name = table.remove(parts, 1)
@ -30,24 +30,25 @@ worldeditadditions_core.register_command("sculpt", {
return false, "A brush with the name '"..brush_name.."' doesn't exist. Try using //sculptlist to list all available brushes." return false, "A brush with the name '"..brush_name.."' doesn't exist. Try using //sculptlist to list all available brushes."
end end
end end
if #parts >= 1 then
height = tonumber(table.remove(parts, 1))
if not height then
return false, "Invalid height value (must be an integer - negative values lower terrain instead of raising it)"
end
end
if #parts >= 1 then if #parts >= 1 then
brush_size = tonumber(table.remove(parts, 1)) brush_size = tonumber(table.remove(parts, 1))
if not brush_size or brush_size < 1 then if not brush_size or brush_size < 1 then
return false, "Invalid brush size. Brush sizes must be a positive integer." return false, "Invalid brush size. Brush sizes must be a positive integer."
end end
end end
if #parts >= 1 then
height = tonumber(table.remove(parts, 1))
if not height then
return false,
"Invalid height value (must be an integer - negative values lower terrain instead of raising it)"
end
end
brush_size = Vector3.new(brush_size, brush_size, 0):floor() brush_size = Vector3.new(brush_size, brush_size, 0):floor()
return true, brush_name, math.floor(height), brush_size return true, brush_name, brush_size, math.floor(height)
end, end,
nodes_needed = function(name, brush_name, height, brush_size) nodes_needed = function(name, brush_name, brush_size, height)
local success, brush, size_actual = wea.sculpt.make_brush(brush_name, brush_size) local success, brush, size_actual = wea.sculpt.make_brush(brush_name, brush_size)
if not success then return 0 end if not success then return 0 end
@ -60,13 +61,13 @@ worldeditadditions_core.register_command("sculpt", {
return size_actual.x * size_actual.y * range_nodes return size_actual.x * size_actual.y * range_nodes
end, end,
func = function(name, brush_name, height, brush_size) func = function(name, brush_name, brush_size, height)
local start_time = wea_c.get_ms_time() local start_time = wea_c.get_ms_time()
local pos1 = wea_c.Vector3.clone(worldedit.pos1[name]) local pos1 = wea_c.Vector3.clone(worldedit.pos1[name])
local success, stats = wea.sculpt.apply( local success, stats = wea.sculpt.apply(
pos1, pos1,
brush_name, height, brush_size brush_name, brush_size, height
) )
if not success then return success, stats.added end if not success then return success, stats.added end

View file

@ -1,4 +1,5 @@
local wea = worldeditadditions local wea = worldeditadditions
local weac = worldeditadditions_core
-- ███████ ██████ ██████ ██████ -- ███████ ██████ ██████ ██████
@ -20,9 +21,11 @@ worldeditadditions_core.register_command("spop", {
local success, pos1, pos2 = wea.spop(name) local success, pos1, pos2 = wea.spop(name)
if not success then return success, pos1 end if not success then return success, pos1 end
worldedit.pos1[name] = pos1 weac.pos.set1(name, pos1)
worldedit.pos2[name] = pos2 weac.pos.set2(name, pos2)
worldedit.marker_update(name) -- worldedit.pos1[name] = pos1
-- worldedit.pos2[name] = pos2
-- worldedit.marker_update(name)
local new_count = wea.scount(name) local new_count = wea.scount(name)
local plural = "s are" local plural = "s are"

View file

@ -1,5 +1,5 @@
--- WorldEditAdditions-ChatCommands --- WorldEditAdditions-ChatCommands
-- @module worldeditadditions_commands -- @namespace worldeditadditions_commands
-- @release 0.1 -- @release 0.1
-- @copyright 2018 Starbeamrainbowlabs -- @copyright 2018 Starbeamrainbowlabs
-- @license Mozilla Public License, 2.0 -- @license Mozilla Public License, 2.0

View file

@ -11,4 +11,5 @@ local wea_c = worldeditadditions_core
return { return {
pos_marker = dofile(wea_c.modpath.."/core/entities/pos_marker.lua"), pos_marker = dofile(wea_c.modpath.."/core/entities/pos_marker.lua"),
pos_marker_wall = dofile(wea_c.modpath.."/core/entities/pos_marker_wall.lua")
} }

View file

@ -14,12 +14,12 @@ local WEAPositionMarker = {
static_save = false, static_save = false,
textures = { textures = {
"worldeditadditions_bg.png", "worldeditadditions_core_bg.png",
"worldeditadditions_bg.png", "worldeditadditions_core_bg.png",
"worldeditadditions_bg.png", "worldeditadditions_core_bg.png",
"worldeditadditions_bg.png", "worldeditadditions_core_bg.png",
"worldeditadditions_bg.png", "worldeditadditions_core_bg.png",
"worldeditadditions_bg.png", "worldeditadditions_core_bg.png",
} }
}, },
@ -89,17 +89,17 @@ local function set_number(entity, display_number)
if display_number < 100 then if display_number < 100 then
local number_right = display_number % 10 local number_right = display_number % 10
local number_left = (display_number - number_right) / 10 local number_left = (display_number - number_right) / 10
texture_name = texture_name.."worldeditadditions_l"..number_left..".png" texture_name = texture_name .. "worldeditadditions_core_l" .. number_left .. ".png"
texture_name = texture_name.."^worldeditadditions_r"..number_right..".png" texture_name = texture_name .. "^worldeditadditions_core_r" .. number_right .. ".png"
-- print("DEBUG:set_number number_left", number_left, "number_right", number_right) -- print("DEBUG:set_number number_left", number_left, "number_right", number_right)
local colour_id = ((display_number - 1) % 12) + 1 -- Lua starts from 1, not 0 :-/ local colour_id = ((display_number - 1) % 12) + 1 -- Lua starts from 1, not 0 :-/
texture_name = "("..texture_name..")^[colorize:"..number_colours[colour_id]..":255" texture_name = "("..texture_name..")^[colorize:"..number_colours[colour_id]..":255"
end end
if #texture_name > 0 then if #texture_name > 0 then
texture_name = "worldeditadditions_bg.png^("..texture_name..")" texture_name = "worldeditadditions_core_bg.png^(" .. texture_name .. ")"
else else
texture_name = "worldeditadditions_bg.png" texture_name = "worldeditadditions_core_bg.png"
end end
-- print("DEBUG:set_number texture_name", texture_name) -- print("DEBUG:set_number texture_name", texture_name)

View file

@ -0,0 +1,456 @@
local wea_c = worldeditadditions_core
local EventEmitter = worldeditadditions_core.EventEmitter
local Vector3 = wea_c.Vector3
local anchor
local entity_wall_size = 10
local collision_thickness = 0.2
local last_reset = tostring(wea_c.get_ms_time())
local WEAPositionMarkerWall = {
initial_properties = {
visual = "cube",
visual_size = { x = 1, y = 1, z = 1 },
collisionbox = { -0.55, -0.55, -0.55, 0.55, 0.55, 0.55 },
-- ^^ { xmin, ymin, zmin, xmax, ymax, zmax } relative to obj pos
physical = false,
collide_with_objects = false,
static_save = false,
textures = {
"worldeditadditions_core_marker_wall.png",
"worldeditadditions_core_marker_wall.png",
"worldeditadditions_core_marker_wall.png",
"worldeditadditions_core_marker_wall.png",
"worldeditadditions_core_marker_wall.png",
"worldeditadditions_core_marker_wall.png",
}
},
on_activate = function(self, staticdata)
if staticdata ~= last_reset then
-- print("DEBUG:marker_wall/remove staticdata", staticdata, "last_reset", last_reset)
self.object:remove()
-- else
-- print("DEBUG:marker_wall/ok staticdata", staticdata, "type", type(staticdata), "last_reset", last_reset, "type", type(last_reset))
end
end,
on_punch = function(self, _)
anchor.delete(self)
end,
on_blast = function(self, damage)
return false, false, {} -- Do not damage or knockback the player
end
}
minetest.register_entity(
":worldeditadditions:marker_wall",
WEAPositionMarkerWall
)
--- Updates the properties of a single wall to match it's side and size
local function single_setup(entity, size, side)
local new_props = {
visual_size = Vector3.min(
Vector3.new(10, 10, 10),
size:abs()
)
-- x = math.min(10, math.abs(size.x)),
-- y = math.min(10, math.abs(size.y)),
-- z = math.min(10, math.abs(size.z))
}
local cthick = Vector3.new(
collision_thickness,
collision_thickness,
collision_thickness
)
local cpos1 = Vector3.clone(new_props.visual_size):multiply(-1):divide(2):add(cthick)
local cpos2 = Vector3.clone(new_props.visual_size):divide(2):subtract(cthick)
if side == "x" or side == "-x" then
new_props.visual_size = new_props.visual_size + Vector3.new(
collision_thickness - 0.1
)
cpos1.x = -collision_thickness
cpos2.x = collision_thickness
end
if side == "y" or side == "-y" then
new_props.visual_size.y = collision_thickness - 0.1
cpos1.y = -collision_thickness
cpos2.y = collision_thickness
end
if side == "z" or side == "-z" then
new_props.visual_size.z = collision_thickness - 0.1
cpos1.z = -collision_thickness
cpos2.z = collision_thickness
end
new_props.collisionbox = {
cpos1.x, cpos1.y, cpos1.z,
cpos2.x, cpos2.y, cpos2.z
}
-- print("DEBUG:setup_single size", size, "side", side, "new_props", wea_c.inspect(new_props))
entity:set_properties(new_props)
end
--- Creates a single marker wall entity.
-- @param player_name string The name of the player it should belong to.
-- @param pos1 Vector3 The pos1 corner of the area the SINGLE marker should cover.
-- @param pos2 Vector3 The pos2 corner of the area the SINGLE marker should cover.
-- @param side string The side that this wall is on. Valid values: x, -x, y, -y, z, -z.
-- @returns Entity<WEAPositionMarkerWall>
local function create_single(player_name, pos1, pos2, side)
local pos_centre = ((pos2 - pos1) / 2) + pos1
local entity = minetest.add_entity(pos_centre, "worldeditadditions:marker_wall", last_reset)
-- print("DEBUG:marker_wall create_single --> START player_name", player_name, "pos1", pos1, "pos2", pos2, "side", side, "SPAWN", pos_centre, "last_reset", last_reset)
entity:get_luaentity().player_name = player_name
single_setup(entity, pos2 - pos1, side)
return entity
end
--- Creates a marker wall around the defined region.
-- @param player_name string The name of the player that the wall belongs to.
-- @param pos1 Vector3 pos1 of the defined region.
-- @param pos2 Vector3 pos2 of the defined region.
-- @param sides_to_display string The sides of the marker wall that should actually be displayed, squished together into a single string. Defaults to "+x-x+z-z". Use "+x-x+z-z+y-y" to display all sides; add and remove sides as desired.
-- @returns table<entitylist> A list of all created entities.
local function create_wall(player_name, pos1, pos2, sides_to_display)
if not sides_to_display then
sides_to_display = "+x-x+z-z" -- this matches WorldEdit
-- To display all of them:
-- sides_to_display = "+x-x+z-z+y-y"
end
-- print("DEBUG:marker_wall create_wall --> START player_name", player_name, "pos1", pos1, "pos2", pos2)
local pos1s, pos2s = Vector3.sort(pos1, pos2)
local entities = {}
-- local dim1, dim2
-- if side == "x" or side == "-x" then dim1, dim2 = size.z, size.y
-- elseif side == "z" or size == "-z" then dim1, dim2 = size.x, size.y
-- elseif side == "y" or size == "-y" then dim1, dim2 = size.x, size.z
-- end
-- x → z, y
-- z → x, y
-- y → x, z
-- ██ ██
-- ██ ██ ██
-- ██████ ███
-- ██ ██ ██
-- ██ ██
-- First, do positive x
if string.find(sides_to_display, "+x") then
local posx_pos1 = Vector3.new(
math.max(pos1s.x, pos2s.x) + 0.5,
math.min(pos1s.y, pos2s.y) - 0.5,
math.min(pos1s.z, pos2s.z) - 0.5
)
local posx_pos2 = Vector3.new(
math.max(pos1s.x, pos2s.x) + 0.5,
math.max(pos1s.y, pos2s.y) + 0.5,
math.max(pos1s.z, pos2s.z) + 0.5
)
-- print("DEBUG ************ +X pos1", posx_pos1, "pos2", posx_pos2)
for z = posx_pos2.z, posx_pos1.z, -entity_wall_size do
for y = posx_pos2.y, posx_pos1.y, -entity_wall_size do
local single_pos1 = Vector3.new(
posx_pos1.x,
y,
z
)
local single_pos2 = Vector3.new(
posx_pos1.x,
math.max(y - entity_wall_size, posx_pos1.y),
math.max(z - entity_wall_size, posx_pos1.z)
)
local entity = create_single(player_name,
single_pos1, single_pos2,
"x"
)
table.insert(entities, entity)
end
end
end
-- ██ ██
-- ██ ██
-- ██████ ███
-- ██ ██
-- ██ ██
-- Now, do negative x
if string.find(sides_to_display, "-x") then
local negx_pos1 = Vector3.new(
math.min(pos1s.x, pos2s.x) - 0.5,
math.min(pos1s.y, pos2s.y) - 0.5,
math.min(pos1s.z, pos2s.z) - 0.5
)
local negx_pos2 = Vector3.new(
math.min(pos1s.x, pos2s.x) - 0.5,
math.max(pos1s.y, pos2s.y) + 0.5,
math.max(pos1s.z, pos2s.z) + 0.5
)
-- print("DEBUG ************ -X pos1", negx_pos1, "pos2", negx_pos2)
for z = negx_pos2.z, negx_pos1.z, -entity_wall_size do
for y = negx_pos2.y, negx_pos1.y, -entity_wall_size do
local single_pos1 = Vector3.new(
negx_pos1.x,
y,
z
)
local single_pos2 = Vector3.new(
negx_pos1.x,
math.max(y - entity_wall_size, negx_pos1.y),
math.max(z - entity_wall_size, negx_pos1.z)
)
local entity = create_single(player_name,
single_pos1, single_pos2,
"-x"
)
table.insert(entities, entity)
end
end
end
-- ██ ██
-- ██ ██ ██
-- ██████ ████
-- ██ ██
-- ██
-- Now, positive y
if string.find(sides_to_display, "+y") then
local posy_pos1 = Vector3.new(
math.min(pos1s.x, pos2s.x) - 0.5,
math.max(pos1s.y, pos2s.y) + 0.5,
math.min(pos1s.z, pos2s.z) - 0.5
)
local posy_pos2 = Vector3.new(
math.max(pos1s.x, pos2s.x) + 0.5,
math.max(pos1s.y, pos2s.y) + 0.5,
math.max(pos1s.z, pos2s.z) + 0.5
)
-- print("DEBUG ************ +Y pos1", posy_pos1, "pos2", posy_pos2)
for z = posy_pos2.z, posy_pos1.z, -entity_wall_size do
for x = posy_pos2.x, posy_pos1.x, -entity_wall_size do
local single_pos1 = Vector3.new(
x,
posy_pos1.y,
z
)
local single_pos2 = Vector3.new(
math.max(x - entity_wall_size, posy_pos1.x),
posy_pos1.y,
math.max(z - entity_wall_size, posy_pos1.z)
)
local entity = create_single(player_name,
single_pos1, single_pos2,
"y"
)
table.insert(entities, entity)
end
end
end
-- ██ ██
-- ██ ██
-- ██████ ████
-- ██
-- ██
-- Now, negative y
if string.find(sides_to_display, "-y") then
local negy_pos1 = Vector3.new(
math.min(pos1s.x, pos2s.x) - 0.5,
math.min(pos1s.y, pos2s.y) - 0.5,
math.min(pos1s.z, pos2s.z) - 0.5
)
local negy_pos2 = Vector3.new(
math.max(pos1s.x, pos2s.x) + 0.5,
math.min(pos1s.y, pos2s.y) - 0.5,
math.max(pos1s.z, pos2s.z) + 0.5
)
-- print("DEBUG ************ -Y pos1", negy_pos1, "pos2", negy_pos2)
for z = negy_pos2.z, negy_pos1.z, -entity_wall_size do
for x = negy_pos2.x, negy_pos1.x, -entity_wall_size do
local single_pos1 = Vector3.new(
x,
negy_pos1.y,
z
)
local single_pos2 = Vector3.new(
math.max(x - entity_wall_size, negy_pos1.x),
negy_pos1.y,
math.max(z - entity_wall_size, negy_pos1.z)
)
local entity = create_single(player_name,
single_pos1, single_pos2,
"-y"
)
table.insert(entities, entity)
end
end
end
-- ███████
-- ██ ███
-- ██████ ███
-- ██ ███
-- ███████
-- Now, positive z. Almost there!
if string.find(sides_to_display, "+z") then
local posz_pos1 = Vector3.new(
math.min(pos1s.x, pos2s.x) - 0.5,
math.min(pos1s.y, pos2s.y) - 0.5,
math.max(pos1s.z, pos2s.z) + 0.5
)
local posz_pos2 = Vector3.new(
math.max(pos1s.x, pos2s.x) + 0.5,
math.max(pos1s.y, pos2s.y) + 0.5,
math.max(pos1s.z, pos2s.z) + 0.5
)
-- print("DEBUG ************ +Z pos1", posz_pos1, "pos2", posz_pos2)
for x = posz_pos2.x, posz_pos1.x, -entity_wall_size do
for y = posz_pos2.y, posz_pos1.y, -entity_wall_size do
local single_pos1 = Vector3.new(
x,
y,
posz_pos1.z
)
local single_pos2 = Vector3.new(
math.max(x - entity_wall_size, posz_pos1.x),
math.max(y - entity_wall_size, posz_pos1.y),
posz_pos1.z
)
local entity = create_single(player_name,
single_pos1, single_pos2,
"z"
)
table.insert(entities, entity)
end
end
end
-- ███████
-- ███
-- ██████ ███
-- ███
-- ███████
-- Finally, negative z. Last one!
if string.find(sides_to_display, "-z") then
local negz_pos1 = Vector3.new(
math.min(pos1s.x, pos2s.x) - 0.5,
math.min(pos1s.y, pos2s.y) - 0.5,
math.min(pos1s.z, pos2s.z) - 0.5
)
local negz_pos2 = Vector3.new(
math.max(pos1s.x, pos2s.x) + 0.5,
math.max(pos1s.y, pos2s.y) + 0.5,
math.min(pos1s.z, pos2s.z) - 0.5
)
-- print("DEBUG ************ -Z pos1", negz_pos1, "pos2", negz_pos2)
for x = negz_pos2.x, negz_pos1.x, -entity_wall_size do
for y = negz_pos2.y, negz_pos1.y, -entity_wall_size do
local single_pos1 = Vector3.new(
x,
y,
negz_pos1.z
)
local single_pos2 = Vector3.new(
math.max(x - entity_wall_size, negz_pos1.x),
math.max(y - entity_wall_size, negz_pos1.y),
negz_pos1.z
)
local entity = create_single(player_name,
single_pos1, single_pos2,
"-z"
)
table.insert(entities, entity)
end
end
end
-- TODO: All the other sides. For testing we're doing 1 side for now, so we can vaoid having to do everything all over again if we make a mistake.
-- for z = pos2.z, pos1.z, -entity_wall_size do
-- for y = pos2.y, pos1.y, -entity_wall_size do
-- for x = pos2.x, pos1.x, -entity_wall_size do
-- end
-- end
-- end
return entities
end
--- Deletes all entities in the given entity list
-- @param entitylist table<entity> A list of wall entities that make up the wall to delete.
local function delete(entitylist)
-- print("DEBUG:marker_wall delete --> START with "..#entitylist.." entities")
local player_name
for _, entity in ipairs(entitylist) do
if not entity.get_luaentity or not entity:get_luaentity() then return end -- Ensure the entity is still valid
if not player_name then
player_name = entity:get_luaentity().player_name
end
entity:remove()
end
last_reset = tostring(wea_c.get_ms_time())
-- print("DEBUG:marker_wall delete --> LAST_RESET is now", last_reset, "type", type(last_reset))
anchor:emit("delete", {
player_name = player_name
})
end
anchor = EventEmitter.new({
create = create_wall,
delete = delete
})
return anchor

View file

@ -5,12 +5,32 @@ local positions_count_limit = 999
local positions = {} local positions = {}
--- Position manager. --- Position manager.
-- @event set { player_name: string, i: number, pos: Vector3 } A new position has been set in a player's list at a specific position. -- @namespace worldeditadditions_core.pos
-- @event push { player_name: string, i: number, pos: Vector3 } A new position has been pushed onto a player's stack.
-- @event pop { player_name: string, i: number, pos: Vector3 } A new position has been pushed onto a player's stack.
-- @event clear { player_name: string } The positions for a player have been cleared.
local anchor = nil local anchor = nil
--- A new position has been set in a player's list at a specific position.
-- @event set
-- @format { player_name: string, i: number, pos: Vector3 }
-- @example
-- {
-- player_name = "some_player_name",
-- i = 3,
-- pos = <Vector3> { x = 456, y = 64, z = 9045 }
-- }
--- A new position has been pushed onto a player's stack.
-- @event push
-- @format { player_name: string, i: number, pos: Vector3 }
--- A new position has been pushed onto a player's stack.
-- @event pop
-- @format { player_name: string, i: number, pos: Vector3 }
--- The positions for a player have been cleared.
-- @event clear
-- @format { player_name: string }
--- Ensures that a table exists for the given player. --- Ensures that a table exists for the given player.
-- @param player_name string The name of the player to check. -- @param player_name string The name of the player to check.
local function ensure_player(player_name) local function ensure_player(player_name)
@ -146,6 +166,7 @@ end
-- @param pos Vector3 The position to set. -- @param pos Vector3 The position to set.
-- @returns bool Whether the operation was successful or not (players aren't allowed more than positions_count_limit number of positions at a time). -- @returns bool Whether the operation was successful or not (players aren't allowed more than positions_count_limit number of positions at a time).
local function set(player_name, i, pos) local function set(player_name, i, pos)
-- It's a shame that Lua doesn't have a throw/raise, 'cause we could sure use it here
if i > positions_count_limit then return false end if i > positions_count_limit then return false end
ensure_player(player_name) ensure_player(player_name)
@ -196,6 +217,7 @@ local function clear(player_name)
if worldedit then if worldedit then
if worldedit.pos1 then worldedit.pos1[player_name] = nil end if worldedit.pos1 then worldedit.pos1[player_name] = nil end
if worldedit.pos2 then worldedit.pos2[player_name] = nil end if worldedit.pos2 then worldedit.pos2[player_name] = nil end
if worldedit.set_pos then worldedit.set_pos[player_name] = nil end
end end
anchor:emit("clear", { player_name = player_name }) anchor:emit("clear", { player_name = player_name })
end end
@ -245,6 +267,6 @@ anchor = wea_c.EventEmitter.new({
set_all = set_all, set_all = set_all,
compat_worldedit_get = compat_worldedit_get compat_worldedit_get = compat_worldedit_get
}) })
anchor.debug = true anchor.debug = false
return anchor return anchor

View file

@ -77,5 +77,9 @@ wea_c.pos:addEventListener("clear", function(event)
wea_c.entities.pos_marker.delete(entity) wea_c.entities.pos_marker.delete(entity)
end end
end end
-- For compatibility, ensure that we also clear the legacy worldedit region too
if worldedit and worldedit.marker_update then
worldedit.marker_update(event.player_name)
end
position_entities[event.player_name] = nil position_entities[event.player_name] = nil
end) end)

View file

@ -0,0 +1,72 @@
local weac = worldeditadditions_core
local wall_entity_lists = {}
--- Ensures that a table exists for the given player.
-- @param player_name string The name of the player to check.
local function ensure_player(player_name)
if player_name == nil then
minetest.log("error", "[wea core:pos_manage:ensure_player] player_name is nil")
end
if not wall_entity_lists[player_name] then
wall_entity_lists[player_name] = {}
end
end
--- Deletes the currently displayed marker wall.
-- @param event EventArgs<wea_c.pos.set> The event args for the set, push, pop, or clear events on wea_c.pos.
-- @returns void
local function do_delete(event)
if not wall_entity_lists[event.player_name] then return end
-- print("DEBUG:marker_wall_manage do_delete --> deleting pre-existing wall with "..tostring(#wall_entity_lists[event.player_name]).." entities")
if #wall_entity_lists[event.player_name] > 0 then
weac.entities.pos_marker_wall.delete(wall_entity_lists[event.player_name])
end
wall_entity_lists[event.player_name] = nil
end
--- Updates the marker wall as appropriate.
-- @param event EventArgs<wea_c.pos.set> The event args for the set, push, or pop events on wea_c.pos.
-- @returns void
local function do_update(event)
-- print("DEBUG:marker_wall_manage do_update --> START")
-- We don't dynamically update, as that'd be too much work.
-- Instead, we just delete & recreate each time.
if wall_entity_lists[event.player_name] then
-- print("DEBUG:marker_wall_manage do_update --> do_delete")
do_delete(event)
end
local pos1 = weac.pos.get1(event.player_name)
local pos2 = weac.pos.get2(event.player_name)
-- print("DEBUG:marker_wall_manage do_update --> pos1", pos1, "pos2", pos2)
if not pos1 or not pos2 then return end
wall_entity_lists[event.player_name] = weac.entities.pos_marker_wall.create(
event.player_name,
pos1,
pos2
)
-- print("DEBUG:marker_wall_manage do_update --> entitylist", weac.inspect(wall_entity_lists[event.player_name]))
end
local function needs_update(event)
if event.i > 2 then
return false
end
return true
end
local function handle_event(event)
if needs_update(event) then do_update(event) end
end
weac.pos:addEventListener("set", handle_event)
weac.pos:addEventListener("pop", handle_event)
weac.pos:addEventListener("push", handle_event)
weac.pos:addEventListener("clear", do_delete)

View file

@ -1,14 +1,10 @@
--- WorldEditAdditions-Core --- WorldEditAdditions-Core
-- @module worldeditadditions_core -- @namespace worldeditadditions_core
-- @release 1.13 -- @release 1.13
-- @copyright 2021 Starbeamrainbowlabs and VorTechnix -- @copyright 2021 Starbeamrainbowlabs and VorTechnix
-- @license Mozilla Public License, 2.0 -- @license Mozilla Public License, 2.0
-- @author Starbeamrainbowlabs and VorTechnix -- @author Starbeamrainbowlabs and VorTechnix
-- local temp = true
-- if temp then return end
-- This mod isn't finished yet, so it will not be executed for now.
local modpath = minetest.get_modpath("worldeditadditions_core") local modpath = minetest.get_modpath("worldeditadditions_core")
@ -67,6 +63,7 @@ wea_c.fetch_command_def = dofile(modpath.."/core/fetch_command_def.lua")
wea_c.register_alias = dofile(modpath.."/core/register_alias.lua") wea_c.register_alias = dofile(modpath.."/core/register_alias.lua")
wea_c.entities = dofile(modpath.."/core/entities/init.lua") -- AFTER pos wea_c.entities = dofile(modpath.."/core/entities/init.lua") -- AFTER pos
dofile(modpath.."/core/pos_marker_manage.lua") -- AFTER pos, entities dofile(modpath.."/core/pos_marker_manage.lua") -- AFTER pos, entities
dofile(modpath.."/core/pos_marker_wall_manage.lua") -- AFTER pos, entities
-- Initialise WorldEdit stuff if the WorldEdit mod is not present -- Initialise WorldEdit stuff if the WorldEdit mod is not present
if minetest.global_exists("worldedit") then if minetest.global_exists("worldedit") then

View file

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 512 B

View file

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 506 B

View file

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 511 B

View file

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 505 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

View file

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 522 B

View file

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 507 B

View file

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 506 B

View file

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

View file

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 506 B

View file

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 511 B

View file

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 507 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

View file

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 513 B

View file

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 521 B

View file

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 509 B

View file

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 505 B

View file

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 512 B

View file

@ -6,7 +6,7 @@ else
end end
--- A least-recently-used cache implementation. --- A least-recently-used cache implementation.
-- @class -- @class worldeditadditions_core.LRU
local LRU = {} local LRU = {}
LRU.__index = LRU LRU.__index = LRU

View file

@ -8,7 +8,7 @@ local wea_c = worldeditadditions_core
-- ██ ██ ██ ██████ ███████ -- ██ ██ ██ ██████ ███████
--- A single face of a Mesh. --- A single face of a Mesh.
-- @class -- @class worldeditadditions_core.Face
local Face = {} local Face = {}
Face.__index = Face Face.__index = Face
@ -42,7 +42,7 @@ function Face.__eq(a, b) return Face.equal(a, b) end
-- ██ ██ ███████ ███████ ██ ██ -- ██ ██ ███████ ███████ ██ ██
--- A mesh of faces. --- A mesh of faces.
-- @class -- @class worldeditadditions_core.Mesh
local Mesh = {} local Mesh = {}
Mesh.__index = Mesh Mesh.__index = Mesh

View file

@ -1,6 +1,7 @@
--- A container for transmitting (axis table, sign) or (dir, sign) pairs --- A container for transmitting (axis table, sign) or (dir, sign) pairs
-- and other data within parsing functions. -- and other data within parsing functions.
-- @class -- @internal
-- @class worldeditadditions_core.parse.key_instance
local key_instance = {} local key_instance = {}
key_instance.__index = key_instance key_instance.__index = key_instance
key_instance.__name = "Key Instance" key_instance.__name = "Key Instance"

View file

@ -1,8 +1,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
--- A Queue implementation --- A Queue implementation
-- Taken & adapted from https://www.lua.org/pil/11.4.html -- Taken & adapted from https://www.lua.org/pil/11.4.html
-- @submodule worldeditadditions.utils.queue -- @class worldeditadditions_core.Queue
-- @class
local Queue = {} local Queue = {}
Queue.__index = Queue Queue.__index = Queue

View file

@ -1,4 +1,3 @@
--- A wrapper to simultaniously handle global and world settings.
-- Initialize settings container -- Initialize settings container
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
@ -8,7 +7,8 @@ wea_c.settings = {}
local path = minetest.get_worldpath() .. "/worldeditadditions" local path = minetest.get_worldpath() .. "/worldeditadditions"
minetest.mkdir(path) minetest.mkdir(path)
-- @class --- A wrapper to simultaniously handle global and world settings.
-- @namespace worldeditadditions_core.setting_handler
local setting_handler = {} local setting_handler = {}
--- Reads world settings into WEA core settings object --- Reads world settings into WEA core settings object

View file

@ -1,7 +1,16 @@
-- worldeditadditions_core = { modpath="/home/sbrl/.minetest/worlds/Mod-Sandbox/worldmods/WorldEditAdditions/worldeditadditions_core/" } -- worldeditadditions_core = { modpath="/home/sbrl/.minetest/worlds/Mod-Sandbox/worldmods/WorldEditAdditions/worldeditadditions_core/" }
local wea_c = worldeditadditions_core
local table_map = dofile(wea_c.modpath.."/utils/table/table_map.lua")
local table_map, polyfill
if minetest then
local wea_c = worldeditadditions_core
table_map = dofile(wea_c.modpath.."/utils/table/table_map.lua")
polyfill = wea_c
else
table_map = require("worldeditadditions_core.utils.table.table_map")
polyfill = require("worldeditadditions_core.utils.strings.polyfill")
end
local function is_whitespace(char) local function is_whitespace(char)
return char:match("%s") return char:match("%s")
end end
@ -13,6 +22,7 @@ local function split_shell(text, autotrim)
local acc = {} local acc = {}
local mode = "NORMAL" -- NORMAL, INSIDE_QUOTES_SINGLE, INSIDE_QUOTES_DOUBLE local mode = "NORMAL" -- NORMAL, INSIDE_QUOTES_SINGLE, INSIDE_QUOTES_DOUBLE
-- print("\n\n\n\n\nDEBUG:split_shell START text", text, "autotrim", autotrim)
for i=1,text_length do for i=1,text_length do
local prevchar = "" local prevchar = ""
@ -27,7 +37,7 @@ local function split_shell(text, autotrim)
if mode == "NORMAL" then if mode == "NORMAL" then
if is_whitespace(curchar) and #acc > 0 then if is_whitespace(curchar) and #acc > 0 then
local nextval = wea_c.trim(table.concat(acc, "")) local nextval = polyfill.trim(table.concat(acc, ""))
if #nextval > 0 then if #nextval > 0 then
table.insert(result, table.concat(acc, "")) table.insert(result, table.concat(acc, ""))
end end
@ -54,7 +64,7 @@ local function split_shell(text, autotrim)
table.insert(acc, curchar) table.insert(acc, curchar)
end end
elseif mode == "INSIDE_QUOTES_SINGLE" then elseif mode == "INSIDE_QUOTES_SINGLE" then
if curchar == "'" and prevchar ~= "\\" and is_whitespace(nextchar) then if curchar == "'" and prevchar ~= "\\" and (is_whitespace(nextchar) or nextchar == "") then
-- It's the end of a quote! -- It's the end of a quote!
mode = "NORMAL" mode = "NORMAL"
elseif (curchar == "\\" and ( elseif (curchar == "\\" and (

View file

@ -1,5 +1,5 @@
--- A 3-dimensional vector. --- A 3-dimensional vector.
-- @class -- @class worldeditadditions_core.Vector3
local Vector3 = {} local Vector3 = {}
Vector3.__index = Vector3 Vector3.__index = Vector3
Vector3.__name = "Vector3" -- A hack to allow identification in wea.inspect Vector3.__name = "Vector3" -- A hack to allow identification in wea.inspect
@ -126,7 +126,6 @@ function Vector3.round(a)
end end
--- Rounds the components of this vector to the specified number of decimal places. --- Rounds the components of this vector to the specified number of decimal places.
-- TODO: Document this.
-- @param a Vector3 The vector to round. -- @param a Vector3 The vector to round.
-- @param dp number The number of decimal places to round to. -- @param dp number The number of decimal places to round to.
-- @returns Vector3 A new instance with the components rounded to the specified number of decimal places. -- @returns Vector3 A new instance with the components rounded to the specified number of decimal places.
@ -174,7 +173,7 @@ function Vector3.length(a)
return math.sqrt(a:length_squared()) return math.sqrt(a:length_squared())
end end
--- Calculates the volume of the region bounded by 1 points. --- Calculates the volume of the region bounded by 2 points.
-- @param a Vector3 The first point bounding the target region. -- @param a Vector3 The first point bounding the target region.
-- @param b Vector3 The second point bounding the target region. -- @param b Vector3 The second point bounding the target region.
-- @returns number The volume of the defined region. -- @returns number The volume of the defined region.
@ -419,13 +418,13 @@ end
-- function. Either that, or Blender 3 (https://blender.org/) is quite useful to visualise what's going on. -- function. Either that, or Blender 3 (https://blender.org/) is quite useful to visualise what's going on.
-- @source GitHub Copilot, generated 2023-01-17 -- @source GitHub Copilot, generated 2023-01-17
-- @warning Not completely tested! Pending a thorough evaluation. Seems to basically work, after some tweaks to the fluff around the edges? -- @warning Not completely tested! Pending a thorough evaluation. Seems to basically work, after some tweaks to the fluff around the edges?
-- @param {Vector3} origin The origin point to rotate around -- @param origin Vector3 The origin point to rotate around
-- @param {Vector3} point The point to rotate. -- @param point Vector3 The point to rotate.
-- @param {Vector3} rotate Rotate this much around the 3 different axes, x, y, and z. Axial rotations are handled in this order: X→Y→Z. -- @param rotate Vector3 Rotate this much around the 3 different axes, x, y, and z. Axial rotations are handled in this order: X→Y→Z.
-- @param {Number} x Rotate this much around the X axis (yz plane), in radians. -- @param x number Rotate this much around the X axis (yz plane), in radians.
-- @param {Number} y Rotate this much around the Y axis (xz plane), in radians. -- @param y number Rotate this much around the Y axis (xz plane), in radians.
-- @param {Number} z Rotate this much around the Z axis (xy plane), in radians. -- @param z number Rotate this much around the Z axis (xy plane), in radians.
-- @return {Vector3} The rotated point. -- @return Vector3 The rotated point.
function Vector3.rotate3d(origin, point, rotate) function Vector3.rotate3d(origin, point, rotate)
local point_norm = point - origin local point_norm = point - origin

View file

@ -0,0 +1 @@
{"modelVersion":2,"piskel":{"name":"New Piskel","description":"","fps":0,"height":16,"width":16,"layers":["{\"name\":\"bg\",\"opacity\":1,\"frameCount\":2,\"chunks\":[{\"layout\":[[0],[1]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAAArklEQVRIS2NkGGDAOMD2M4w6YDQERkNg+ITAz9df/4PKFHZRbpI8RZJifIUWyAHTWo8xFE5wJclMkhTjc0BVyIb/AqJcDGXT3cBm2m9z+i/EK8Sw3nYNXjuo4gCQ5SBLQQ4AgbVmDWCaQ5wDTB/02ofTHqo4INtz7n9+bmF4AIEcgu4IXA6h2AGx9h3/+bhEwZbDHIEcFebzrcChAwInE49h2EexA0ipTWGOQXYIAGP4KxGpw5fjAAAAAElFTkSuQmCC\"}]}","{\"name\":\"main\",\"opacity\":1,\"frameCount\":2,\"chunks\":[{\"layout\":[[0],[1]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAABJ0lEQVRIS2PcWav7n4GBgcG9+TIjiCYVUKqfcUGOOtgBkoJsZDmCUv1gX7dHK/4HOYBch1CiHx7sIENgDiAnNMjVD3dAnpc0igNIdQS5+lESHrIhsNBImHKT6MRJjn4Mw2GGwBxgpMRDUuIkVT9W34VaioITJTImJZuSoh9n8JJiCLayg1j9eOOXWENwFV7E6CeYwDwMhf6rSXLCo4OURAlyGCH9BB0AMiTBUeI/HyczQ0q6EQOPmAHYw0qWzUTpJaSfaEPOLDP9z8YtAbYc5AhSHADSg0s/0Q7YOlnvv7gwO9gBIIeQ6ghk/dk/WRk4xDkYDnrtYyTaASCLQYaAaJBDQI7Q899Msv6ie3/AloPwycRjpDkAltphviHHESAz1Au1/sMcAQADxIMgEWtmUwAAAABJRU5ErkJggg==\"}]}","{\"name\":\"overlay\",\"opacity\":1,\"frameCount\":2,\"chunks\":[{\"layout\":[[0],[1]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAAAIklEQVRIS2NkGGDAOMD2M4w6YDQERkNgNARGQ2A0BEZDAABMmAARhe0y0gAAAABJRU5ErkJggg==\"}]}"]}}

View file

@ -1 +1 @@
{"modelVersion":2,"piskel":{"name":"worldedit_wand","description":"","fps":0,"height":16,"width":16,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":5,\"chunks\":[{\"layout\":[[0],[1],[2],[3],[4]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAQCAYAAACBSfjBAAADRElEQVRYR+3YW0hTcRwH8O9Jp25uml1kupBIG+tqF60gVIoigi7kQwhBdK/Hoig1JMJuFtjlwUh6CYkuVHQhCSoqSypbdhlFZo7M1mYtnTu7uO3MX5zFxErr/18PDek87eF8f+e3z/9y/ptQI4A0BuChX4uKFpuACK6eW+kkmEUE3SJit4CrhnZjPKlGxWGo+h4crgKYy0SufLhdIiJruwtuj4SxY1K4aySfT6LAIwmeSg9XVqidJNCjJsKUxfkovFjHFZabp6saCnYFEONVhADhB2J3sCMuqmkhh+gKOXh8QTRumcbdwyebSElJSjidXnQ4vJhoSGWuYZ87lxwmE6aWeCEDfpxlhL+pC+knZjPVEIqz0ijbkIGX9x9DP2M0Vt98zxTsHflqDYU/RwrY/HU6AmIQMYrbWHktH2X13Vw9NJs7KVEVG9Hs+6zX02u7HQWHlDC3WqCJeYZuox0Z1+Yz9dB70/E8LVltNqjG56D4ipEpLMNJh9ELKM++/VcTuACmnk0lnxhA0E3w289hzZ2lXHl56b5q+oL4OAUsVjscjk6Y9rEPwoYrrWS0eNHgM6DODNQ38vX/A9TRojwyN97HuNxsbDr9gh2x4juijJeTFgej1c+FIGfLZyeEavDm79Y3ky5tBHz+AN61vA8BtlWxD4K8hbxta8eSY1VIzrzA/fxfkHbP05Kzyw5V1nSUn3nMjBhexjIEL0Lf9xZv/vL1BpLRVq1YIEQ6CJUZm+mp+w1Of70RqsHTf79A2wqzSTvECUGpxtYaU9Qj/nxw4EX4m/yAOCUL0yhJqUDPsEzsPHnnP+IAx7vfwuxdN4cUHWZIggKlF99FhKhNVsLW5eXeE8P7YrTn/4hStTaHPrW2QKGbgF2nHvzx/v6Wg4zQ/MWLg0/4jid9EaM1zwRSWpRL6bEutHc4UV5rYcr0hdyem0BjR0aOGM15Zow9y8aQSiHgg5SII5deMufCkNGMwPLrdaD+uSA2F04mXZwIl9SD3RdaubJyk4MRkRthxxItpQ5XwyKpUVnznDs/2BC5AeSZVLJuJvmsJuh1w7Gpuo27xmBC5P7y4f3iwHINpUgiNl5i/+elvxfL+trOiHoID8K/zn8DbQVu1uI+550AAAAASUVORK5CYII=\"}]}"]}} {"modelVersion":2,"piskel":{"name":"worldedit_wand","description":"","fps":0,"height":16,"width":16,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":5,\"chunks\":[{\"layout\":[[0],[1],[2],[3],[4]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAQCAYAAACBSfjBAAADB0lEQVRYR+2WXUhTYRjH/wd1btacgZmaeZFpRgrZF4GQIEQE9gERCEKF9HWpN1oXIlIkGRgUCHVVlGBlUErRRdEHDEqXXZiRLUdipubMsx2348623niPvGsz3c57CmTgA7s4O/yf53l/z8d5BQD49SybCA4JQY+ExFoI9D+tlnk6maTkGJC28hXEmTI4GiQuPYtDCCFjEzPweAPIX7+K24flXirxvwnA2+rl1oaftbLqGKHPHe23NPkRSJeZBF1+JMhJKkAoQGK9dogVt4eIKM2oOXh9QfTVbtUUODzp7+MSSU01we2W8VOUUVSYodmHs7yciP39KDkngwL8tssGZdCF7Oulmn2wXJoaW1R4zBqb6mL6EMgNc0ikF6B9ahv8UhAJSc9xtHs3GqyzMQOHJ2p3TJMVKYm6uu9HQQH56HSi7LIJjuFRmBPeY9bmRG73Hq4cGLz6szURw2c0GSL80A7dtGEzNhbl4VDFfgiBK/hDXQGau4xcAEo6MohP8iPoIVCcd1H94iCXno7uwOAkkg1JGB1zQhSn0X9RexFOPRomtlEZPb5CvHYA1j6+/CmtWVkJMbjZficEMM1iQeWRw1ELob4MXJqDSOFtzzLANqZwQaDa86VG1Qev/qXVTtZmpcOn+PFl6KsKcKRNexHoCvk8MoEDV9tgyevkjt9x/4Gat+hygQKjNvhhKKILo43yX3QpCF4I4dF49Q8f9xAK7XjVXkFvEVpza8g7zye0Tz1VffDmP3/30fOsWZceOtaZE9WLduGCL/Qk8S8Q53/xlyI+7UTahcy2FBerXUl/0cZ4UbJLcYilLgKFyMb4rbVPTWfnwAUkZ7lRfm3hm0nUBUkhZlpMGHfJ3DuR7cV41LORzu5tViHmpAQXhRjzU88g2idltPTyXU/CIcajvm6HkeSvNkWFGBMgVTNHeiHEsz4WRE0AlyEaidkA0HVEx5navk63yk4zwGWIcxDZXVcXwGWIkZ1IIXJ1ILtmxPNO+59NcPLJtD6A4UlQJ/MvwlqeWRHiVc/O+Bv+1GgX7gdMgAAAAABJRU5ErkJggg==\"}]}"]}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B