Compare commits
299 Commits
Author | SHA1 | Date |
---|---|---|
Starbeamrainbowlabs | e89d61034f | |
Starbeamrainbowlabs | 8efceeaa4c | |
Starbeamrainbowlabs | 5ca5d1f1de | |
Starbeamrainbowlabs | 2a9dca6926 | |
Starbeamrainbowlabs | dd6e34bde1 | |
Starbeamrainbowlabs | 125638a079 | |
Starbeamrainbowlabs | 7be2fb125e | |
Starbeamrainbowlabs | 6231132055 | |
Starbeamrainbowlabs | abfd76930a | |
Starbeamrainbowlabs | aec204de8e | |
Starbeamrainbowlabs | daae15eb57 | |
Starbeamrainbowlabs | 200e588c59 | |
Starbeamrainbowlabs | 2840ba4dea | |
Starbeamrainbowlabs | 564785ddf6 | |
Starbeamrainbowlabs | a465a50244 | |
Starbeamrainbowlabs | 5505575cf9 | |
Starbeamrainbowlabs | fd3ee43728 | |
Starbeamrainbowlabs | ee4b3e09bb | |
Starbeamrainbowlabs | 0645bc7b8e | |
VorTechnix | 7766af8a8a | |
VorTechnix | d2fb6db3d0 | |
Starbeamrainbowlabs | a6e8753e64 | |
Starbeamrainbowlabs | 1a227e3bd8 | |
Starbeamrainbowlabs | ffc90ed179 | |
Starbeamrainbowlabs | ab3120fe79 | |
Starbeamrainbowlabs | 87a80d2faf | |
Starbeamrainbowlabs | 83b4b02b40 | |
Starbeamrainbowlabs | 95a82add86 | |
Starbeamrainbowlabs | 5baa72cf6f | |
Starbeamrainbowlabs | 177aea02a8 | |
Starbeamrainbowlabs | 7f4798432e | |
Starbeamrainbowlabs | 7236232dc0 | |
Starbeamrainbowlabs | c8e717a6d6 | |
Starbeamrainbowlabs | 4745009632 | |
Starbeamrainbowlabs | f7429fa40e | |
Starbeamrainbowlabs | 277f3a8155 | |
Starbeamrainbowlabs | d63400e697 | |
Starbeamrainbowlabs | 5c632df658 | |
Starbeamrainbowlabs | 693fc145d5 | |
Starbeamrainbowlabs | 2c30ed8634 | |
Starbeamrainbowlabs | bf365d2d11 | |
Starbeamrainbowlabs | f2214150f2 | |
Starbeamrainbowlabs | 3bfc62be24 | |
Starbeamrainbowlabs | e916057133 | |
Starbeamrainbowlabs | 3194a2c53e | |
Starbeamrainbowlabs | 20935d8379 | |
Starbeamrainbowlabs | 74cc6669c0 | |
Starbeamrainbowlabs | 2edc9f6f6c | |
Starbeamrainbowlabs | a78d385666 | |
Starbeamrainbowlabs | fd9d76353d | |
VorTechnix | 5cf155772d | |
VorTechnix | 55ddc16b2e | |
Starbeamrainbowlabs | 6bc1987916 | |
Starbeamrainbowlabs | 365b491aa1 | |
Starbeamrainbowlabs | 5d18f6d0db | |
Starbeamrainbowlabs | 66c257c146 | |
Starbeamrainbowlabs | 7bdefc8d42 | |
Starbeamrainbowlabs | 78c4a67c7b | |
Starbeamrainbowlabs | 02ad40eaae | |
Starbeamrainbowlabs | c988daeda6 | |
Starbeamrainbowlabs | 047033a6ef | |
Starbeamrainbowlabs | c6bc6e26e7 | |
Starbeamrainbowlabs | 8981bc4baa | |
Starbeamrainbowlabs | 464ea53465 | |
Starbeamrainbowlabs | e9c3d51863 | |
Starbeamrainbowlabs | 5a76b3eaa9 | |
Starbeamrainbowlabs | 2e8642b4d7 | |
Starbeamrainbowlabs | 9b4a777a67 | |
Starbeamrainbowlabs | 9be7854297 | |
Starbeamrainbowlabs | 09ee1ae3fa | |
Starbeamrainbowlabs | f970a8d6bb | |
Starbeamrainbowlabs | 61403108d6 | |
Starbeamrainbowlabs | f5e2ada5bd | |
Starbeamrainbowlabs | 2756eb0eb5 | |
Starbeamrainbowlabs | 92fd201b42 | |
Starbeamrainbowlabs | f800d7e3d7 | |
Starbeamrainbowlabs | b3f7ae9d7c | |
Starbeamrainbowlabs | 46587164bd | |
Starbeamrainbowlabs | e3dac29a80 | |
Starbeamrainbowlabs | 109c69502a | |
Starbeamrainbowlabs | 277da67dda | |
Starbeamrainbowlabs | a173edfbc4 | |
Starbeamrainbowlabs | c622fb554f | |
Starbeamrainbowlabs | 1b0001d88e | |
Starbeamrainbowlabs | 5fe84ada79 | |
Starbeamrainbowlabs | f7530da608 | |
Starbeamrainbowlabs | 8cc2c735b1 | |
Starbeamrainbowlabs | 61e04952e0 | |
Starbeamrainbowlabs | 141ea2f0a7 | |
Starbeamrainbowlabs | bfecc2da80 | |
Starbeamrainbowlabs | 53bbe14c63 | |
VorTechnix | 8d9f51de35 | |
VorTechnix | 67baee7465 | |
VorTechnix | 8de2892907 | |
VorTechnix | 16afb9eddc | |
Starbeamrainbowlabs | 1c163186b6 | |
Starbeamrainbowlabs | daad494c7e | |
Starbeamrainbowlabs | b2556934b1 | |
Starbeamrainbowlabs | 7cb1160e65 | |
VorTechnix | b88cec64d4 | |
Starbeamrainbowlabs | 8652ad6335 | |
Starbeamrainbowlabs | ebbaed88d8 | |
Starbeamrainbowlabs | 558fc1c04b | |
Starbeamrainbowlabs | 1d53233621 | |
Starbeamrainbowlabs | 66aab7ba64 | |
Starbeamrainbowlabs | fcde8e581c | |
Starbeamrainbowlabs | cfef411bbd | |
Starbeamrainbowlabs | cf8406b80f | |
Starbeamrainbowlabs | e942e4fb2f | |
Starbeamrainbowlabs | d1b9d1c1c1 | |
Starbeamrainbowlabs | 392708b190 | |
Starbeamrainbowlabs | 87812679d9 | |
Starbeamrainbowlabs | 2ecc8cb2d7 | |
Starbeamrainbowlabs | 66227153d0 | |
Starbeamrainbowlabs | 42fb6571da | |
Starbeamrainbowlabs | 9c66288827 | |
Starbeamrainbowlabs | cbd2ae7df8 | |
Starbeamrainbowlabs | 3e123ddc56 | |
Starbeamrainbowlabs | 8372a12f48 | |
Starbeamrainbowlabs | 53609ac83e | |
Starbeamrainbowlabs | 2e275e3993 | |
Starbeamrainbowlabs | cfc1fe3639 | |
Starbeamrainbowlabs | 97bb896829 | |
Starbeamrainbowlabs | 58dd3ea875 | |
Starbeamrainbowlabs | 554bc86b3d | |
Starbeamrainbowlabs | 902d03ca0b | |
Starbeamrainbowlabs | 410841562a | |
Starbeamrainbowlabs | dea51cb9a3 | |
Starbeamrainbowlabs | 9f2bfb33c7 | |
Starbeamrainbowlabs | b493c0073a | |
Starbeamrainbowlabs | bc0276875d | |
Starbeamrainbowlabs | c319be5840 | |
Starbeamrainbowlabs | fe30fd5c51 | |
Starbeamrainbowlabs | 2748f4cdfa | |
Starbeamrainbowlabs | 9a4b4beb76 | |
Starbeamrainbowlabs | 9806828760 | |
Starbeamrainbowlabs | 888e4b0f78 | |
Starbeamrainbowlabs | 5d9dee9523 | |
Starbeamrainbowlabs | 268e123221 | |
Starbeamrainbowlabs | 363acde496 | |
Starbeamrainbowlabs | 7a14e8a590 | |
Starbeamrainbowlabs | f998110303 | |
Starbeamrainbowlabs | 285572e16b | |
Starbeamrainbowlabs | 258a9c1cde | |
Starbeamrainbowlabs | a768378dfd | |
Starbeamrainbowlabs | c2d19105ed | |
Starbeamrainbowlabs | 3583de4731 | |
Starbeamrainbowlabs | dfb24e679b | |
Starbeamrainbowlabs | da9f578e86 | |
Starbeamrainbowlabs | 8f03c6473b | |
Starbeamrainbowlabs | bdaedf0e7a | |
Starbeamrainbowlabs | 38a72468d9 | |
Starbeamrainbowlabs | bc5dc6b2b4 | |
Starbeamrainbowlabs | c9fd68fac3 | |
Starbeamrainbowlabs | 84226a9909 | |
Starbeamrainbowlabs | 2473c1ce41 | |
Starbeamrainbowlabs | 6357d590d9 | |
Starbeamrainbowlabs | 440cdcc504 | |
VorTechnix | 98a74c63dc | |
Starbeamrainbowlabs | 7f2be74580 | |
Starbeamrainbowlabs | 3fa6dbefcf | |
Starbeamrainbowlabs | 1473074d4a | |
Starbeamrainbowlabs | fd1e3fb20f | |
Starbeamrainbowlabs | f7bd17131a | |
Starbeamrainbowlabs | ea6a7ced2b | |
Starbeamrainbowlabs | 7423bee714 | |
Starbeamrainbowlabs | e2e235ce6d | |
Starbeamrainbowlabs | 9910bfad44 | |
Starbeamrainbowlabs | 47b1c44b38 | |
Starbeamrainbowlabs | d9877b8c6c | |
Starbeamrainbowlabs | 6c8ce90593 | |
Starbeamrainbowlabs | 44e6ba8e3c | |
Starbeamrainbowlabs | c3ae96548e | |
Starbeamrainbowlabs | d03e333e35 | |
Starbeamrainbowlabs | c82c83d008 | |
Starbeamrainbowlabs | 073d621629 | |
Starbeamrainbowlabs | 4be7bf33d0 | |
Starbeamrainbowlabs | 9dc8165464 | |
Starbeamrainbowlabs | 5ec877f2b7 | |
Starbeamrainbowlabs | 8ec118eed7 | |
Starbeamrainbowlabs | 1024b19629 | |
Starbeamrainbowlabs | 7a688969b2 | |
Starbeamrainbowlabs | 1ae36d3898 | |
Starbeamrainbowlabs | 2af1226b49 | |
Starbeamrainbowlabs | 41e3ebc7dd | |
Starbeamrainbowlabs | a3acf3b16e | |
Starbeamrainbowlabs | 21c5b9d483 | |
Starbeamrainbowlabs | 6102a1adf5 | |
Starbeamrainbowlabs | 4a56d45c4b | |
Starbeamrainbowlabs | c3e8df3a9e | |
Starbeamrainbowlabs | 459e15b5c2 | |
Starbeamrainbowlabs | 128dc8f103 | |
Starbeamrainbowlabs | 0641a9f55c | |
Starbeamrainbowlabs | 927f2f90f8 | |
Starbeamrainbowlabs | 2f4c333ca0 | |
VorTechnix | e2947c4ae2 | |
Starbeamrainbowlabs | 8d6db6d3f5 | |
Starbeamrainbowlabs | adfc6d6499 | |
Starbeamrainbowlabs | e167fb1536 | |
Starbeamrainbowlabs | 6f3118036d | |
Starbeamrainbowlabs | 2625ed1a53 | |
Starbeamrainbowlabs | 58a7629ea1 | |
Starbeamrainbowlabs | 2b88fa867e | |
Starbeamrainbowlabs | 4a6cb2551c | |
Starbeamrainbowlabs | 52d24a63f3 | |
Starbeamrainbowlabs | 0983e09f50 | |
Starbeamrainbowlabs | cf0be6c1fb | |
Starbeamrainbowlabs | 17d92870b3 | |
Starbeamrainbowlabs | 817387d7f2 | |
Starbeamrainbowlabs | fa62864e16 | |
Starbeamrainbowlabs | 9932852053 | |
Starbeamrainbowlabs | fd42d96702 | |
Starbeamrainbowlabs | 08d40c8780 | |
Starbeamrainbowlabs | 763ae3db8d | |
Starbeamrainbowlabs | 382194321b | |
Starbeamrainbowlabs | e6a48c8799 | |
Starbeamrainbowlabs | d3900e2ad9 | |
Starbeamrainbowlabs | 63abd1f539 | |
Starbeamrainbowlabs | 0e7b292bb1 | |
Starbeamrainbowlabs | 8eec6a32f7 | |
Starbeamrainbowlabs | bd88f93296 | |
Starbeamrainbowlabs | c465d6efb0 | |
Starbeamrainbowlabs | 2d989acee5 | |
Starbeamrainbowlabs | 6e0779a4e5 | |
Starbeamrainbowlabs | 77c634dd7d | |
Starbeamrainbowlabs | fc4f64d25a | |
Starbeamrainbowlabs | e294d96efe | |
VorTechnix | dc73e0e1e9 | |
Starbeamrainbowlabs | be0612e6be | |
VorTechnix | 74ca84fcee | |
VorTechnix | 53600789d8 | |
VorTechnix | 6e3252b471 | |
Starbeamrainbowlabs | 5ac98cfa22 | |
Starbeamrainbowlabs | b94c38bb99 | |
VorTechnix | 98e9888897 | |
Starbeamrainbowlabs | 50b1df57c4 | |
VorTechnix | d79c187a9d | |
Starbeamrainbowlabs | cb75776164 | |
VorTechnix | 021fdac284 | |
Starbeamrainbowlabs | 64ff61a4d2 | |
VorTechnix | 6ed65074b8 | |
VorTechnix | a8655373a2 | |
VorTechnix | 3196a590f5 | |
VorTechnix | 1cef968731 | |
Starbeamrainbowlabs | af7d39110b | |
Starbeamrainbowlabs | 9bfb428c3f | |
VorTechnix | e17b2cce4e | |
VorTechnix | 23140467d2 | |
VorTechnix | 2cddccdca4 | |
VorTechnix | 8789b39d1d | |
VorTechnix | a0982c70f0 | |
VorTechnix | a6b4d047b8 | |
Starbeamrainbowlabs | 1750d62d3c | |
Starbeamrainbowlabs | c2c0fa5d8d | |
Starbeamrainbowlabs | 8de49ac0af | |
Starbeamrainbowlabs | 3bcca82b43 | |
Starbeamrainbowlabs | c53fb32d2b | |
Starbeamrainbowlabs | 95c0e96da9 | |
Starbeamrainbowlabs | b816133716 | |
Starbeamrainbowlabs | c60b5c5bad | |
Starbeamrainbowlabs | 4306035ef2 | |
Starbeamrainbowlabs | 8f6c3e020f | |
Starbeamrainbowlabs | d21f7ca5fb | |
Starbeamrainbowlabs | 1fda9725c7 | |
Starbeamrainbowlabs | 4ab386788d | |
Starbeamrainbowlabs | 9bdd7d2a25 | |
Starbeamrainbowlabs | fa9b511e33 | |
Starbeamrainbowlabs | 19c8d0e7b9 | |
Starbeamrainbowlabs | f984f5d7b7 | |
Starbeamrainbowlabs | d3119ee54a | |
Starbeamrainbowlabs | 10f350c967 | |
Starbeamrainbowlabs | 9d3a4ce263 | |
Starbeamrainbowlabs | 60c8f30109 | |
Starbeamrainbowlabs | 98909f0fae | |
Starbeamrainbowlabs | 722e62bae0 | |
Starbeamrainbowlabs | 3311d80a2a | |
Starbeamrainbowlabs | 0d7922d747 | |
VorTechnix | bcfe39cd21 | |
Starbeamrainbowlabs | f34617a3d3 | |
Starbeamrainbowlabs | 03fb033b70 | |
Starbeamrainbowlabs | 04e971e12d | |
Starbeamrainbowlabs | a99cef7bf9 | |
Starbeamrainbowlabs | 6d075be525 | |
Starbeamrainbowlabs | dbc056139a | |
Starbeamrainbowlabs | 8eb1faf1b9 | |
Starbeamrainbowlabs | 8b385690c0 | |
Starbeamrainbowlabs | a4237dcca5 | |
Starbeamrainbowlabs | 7622d85efa | |
dependabot[bot] | 1c80bbc031 | |
Starbeamrainbowlabs | d77799a871 | |
dependabot[bot] | 2ed3042bc8 | |
VorTechnix | 9995ea2fb9 | |
Starbeamrainbowlabs | c443459282 | |
Starbeamrainbowlabs | 3d12345972 | |
Starbeamrainbowlabs | 4c7bb6a980 | |
Starbeamrainbowlabs | 67b4495aad | |
Starbeamrainbowlabs | ee465070c9 | |
Starbeamrainbowlabs | 58933c6435 | |
VorTechnix | 104fe97be3 |
|
@ -7,8 +7,15 @@ const path = require("path");
|
|||
const debug = require("debug");
|
||||
const htmlentities = require("html-entities");
|
||||
const phin = require("phin");
|
||||
const CleanCSS = require("clean-css");
|
||||
const { minify: minify_html } = require("html-minifier-terser");
|
||||
|
||||
const HTMLPicture = require("./lib/HTMLPicture.js");
|
||||
const FileFetcher = require("./lib/FileFetcher.js");
|
||||
|
||||
const file_fetcher = new FileFetcher();
|
||||
|
||||
const is_production = typeof process.env.NODE_ENV === "string" && process.env.NODE_ENV === "production";
|
||||
|
||||
var nextid = 0;
|
||||
|
||||
|
@ -47,23 +54,19 @@ async function shortcode_image_urlpass(src) {
|
|||
return `/img/${filename}`;
|
||||
}
|
||||
|
||||
async function shortcode_gallerybox(content, src, idthis, idprev, idnext) {
|
||||
return `<figure class="gallerybox-item" id="${idthis}">
|
||||
async function shortcode_gallerybox(content, src, id_this, id_prev, id_next) {
|
||||
return `<figure class="gallerybox-item" id="${id_this}">
|
||||
<!-- ${await shortcode_image(src, "", "gallerybox-thumb", "300w")} -->
|
||||
${await shortcode_image(src, "", "", "1920w")}
|
||||
|
||||
<figcaption>${content}</figcaption>
|
||||
|
||||
<a class="gallerybox-prev" href="#${idprev}">❰</a>
|
||||
<a class="gallerybox-next" href="#${idnext}">❱</a>
|
||||
<a class="gallerybox-prev" href="#${id_prev}">❰</a>
|
||||
<a class="gallerybox-next" href="#${id_next}">❱</a>
|
||||
</figure>`;
|
||||
}
|
||||
|
||||
async function fetch(url) {
|
||||
const pkg_obj = JSON.parse(await fs.promises.readFile(
|
||||
path.join(__dirname, "package.json"), "utf8"
|
||||
));
|
||||
|
||||
return (await phin({
|
||||
url,
|
||||
headers: {
|
||||
|
@ -74,9 +77,56 @@ async function fetch(url) {
|
|||
})).body;
|
||||
}
|
||||
|
||||
function fetch_file(url) {
|
||||
return file_fetcher.fetch_file(url);
|
||||
}
|
||||
|
||||
function do_minify_css(source, output_path) {
|
||||
if(!output_path.endsWith(".css") || !is_production) return source;
|
||||
|
||||
const result = new CleanCSS({
|
||||
level: 2
|
||||
}).minify(source).styles.trim();
|
||||
console.log(`MINIFY ${output_path}`, source.length, `→`, result.length, `(${((1 - (result.length / source.length)) * 100).toFixed(2)}% reduction)`);
|
||||
return result;
|
||||
}
|
||||
|
||||
async function do_minify_html(source, output_path) {
|
||||
if(!output_path.endsWith(".html") || !is_production) return source;
|
||||
|
||||
const result = await minify_html(source, {
|
||||
collapseBooleanAttributes: true,
|
||||
collapseWhitespace: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
continueOnParseError: true,
|
||||
decodeEntities: true,
|
||||
keepClosingSlash: true,
|
||||
minifyCSS: true,
|
||||
quoteCharacter: `"`,
|
||||
removeComments: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
useShortDoctype: true
|
||||
});
|
||||
|
||||
console.log(`MINIFY ${output_path}`, source.length, `→`, result.length, `(${((1 - (result.length / source.length)) * 100).toFixed(2)}% reduction)`);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if(is_production) console.log("Production environment detected, minifying content");
|
||||
|
||||
module.exports = function(eleventyConfig) {
|
||||
eleventyConfig.addTransform("cssmin", do_minify_css);
|
||||
eleventyConfig.addTransform("htmlmin", do_minify_html);
|
||||
|
||||
eleventyConfig.addPassthroughCopy("img2brush/img2brush.js");
|
||||
eleventyConfig.addAsyncShortcode("fetch", fetch);
|
||||
eleventyConfig.addFilter("fetch_file", fetch_file);
|
||||
|
||||
// eleventyConfig.addPassthroughCopy("images");
|
||||
// eleventyConfig.addPassthroughCopy("css");
|
||||
|
|
|
@ -17,7 +17,7 @@ let { sections, categories } = parse_sections(fs.readFileSync(
|
|||
"utf-8"
|
||||
))
|
||||
|
||||
sections = sections.slice(1).sort((a, b) => a.title.replace(/^\/+/g, "").localeCompare(
|
||||
sections = sections.sort((a, b) => a.title.replace(/^\/+/g, "").localeCompare(
|
||||
b.title.replace(/^\/+/g, "")));
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{ title }} • WorldEditAdditions</title>
|
||||
|
||||
<link rel="stylesheet" href="/theme.css" />
|
||||
|
@ -45,11 +46,11 @@
|
|||
{{ content | safe }}
|
||||
|
||||
<footer class="shadow-top">
|
||||
<p>WorldEditAdditions built with ❤️ by these awesome people:</p>
|
||||
<p>WorldEditAdditions is built with ❤️ by these awesome people:</p>
|
||||
<ul class="contributor-list">
|
||||
{% for contributor in contributors %}
|
||||
<li><a href="{{ contributor.profile_url }}">
|
||||
<img class="icon large" src="{{ contributor.avatar_url }}" alt="{{ contributor.name }}" />
|
||||
<img class="icon large" src="{{ contributor.avatar_url | fetch_file }}" alt="{{ contributor.name }}" />
|
||||
<span>{{ contributor.name }}</span>
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
permalink: theme.css
|
||||
---
|
||||
|
||||
{% include "css/patterns.css" %}
|
||||
{% include "css/theme.css" %}
|
||||
{% include "css/gallerybox.css" %}
|
||||
{% include "css/smallscreens.css" %}
|
||||
{% include "css/prism-custom.css" %}
|
||||
|
||||
|
||||
{# {% fetch "https://unpkg.com/prismjs/themes/prism-okaidia.css" %} #}
|
||||
{# {% fetch "https://raw.githubusercontent.com/PrismJS/prism-themes/master/themes/prism-shades-of-purple.css" %} #}
|
||||
{# {% fetch "https://raw.githubusercontent.com/PrismJS/prism-themes/master/themes/prism-material-light.css" %} #}
|
||||
|
|
|
@ -10,7 +10,17 @@ be (slightly) less hacky once :target-within lands in browsers.
|
|||
|
||||
Exported to codepen: https://codepen.io/sbrl/details/YzZJYxg
|
||||
*/
|
||||
|
||||
|
||||
:root {
|
||||
--gallerybox-bg: hsla(0, 0%, 95%, 0.75);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--gallerybox-bg: hsla(0, 0%, 30%, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) {
|
||||
.gallerybox-gallery {
|
||||
max-width: 95vw;
|
||||
|
@ -58,7 +68,7 @@ Exported to codepen: https://codepen.io/sbrl/details/YzZJYxg
|
|||
}
|
||||
|
||||
.gallerybox-item > figcaption {
|
||||
background: hsla(0, 0%, 95%, 0.75);
|
||||
background: var(--gallerybox-bg);
|
||||
position: absolute;
|
||||
bottom: 0.25em; left: 0; right: 0;
|
||||
padding: 0.75em 1em;
|
||||
|
|
|
@ -32,6 +32,27 @@
|
|||
--shadow-dark: hsla(0, 0%, 25%, 0.5);
|
||||
|
||||
--highlight: hsl(353, 100%, 36%);
|
||||
|
||||
--bg-image: url('{% image_urlpass "images/clouds.png" %}');
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg-main: #020B18;
|
||||
--bg-bright: hsl(108, 46%, 34%);
|
||||
--bg-alt: hsl(228, 68%, 68%);
|
||||
|
||||
--bg-transcluscent: hsla(224, 20%, 16%, 0.85);
|
||||
--bg-transcluscent-alt: hsla(226, 79%, 70%, 0.8);
|
||||
|
||||
--text-main: hsl(227, 80%, 95%);
|
||||
|
||||
--bg-image: url('{% image_urlpass "images/clouds-night.png" %}');
|
||||
}
|
||||
|
||||
a { color: #7c7cff; }
|
||||
a:active { color: #ee0000; }
|
||||
a:not(.nav):not(.bigbutton):visited { color: hsl(296, 84%, 75%); }
|
||||
}
|
||||
|
||||
/* rem is relative to the html element, and em is relative to the current element. */
|
||||
|
@ -42,7 +63,7 @@ body
|
|||
margin: 0;
|
||||
font-family: sans-serif; /* Serif is awful :( */
|
||||
|
||||
background: url('{% image_urlpass "images/clouds.png" %}') center / cover fixed,
|
||||
background: var(--bg-image) center / cover fixed,
|
||||
var(--bg-main); /* Don't forget to update the @page one too for paged media */
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
@ -67,6 +88,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.linked-section-heading {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -90,6 +112,18 @@ h1, h2, h3, h4, h5, h6 {
|
|||
line-height: 1.5;
|
||||
align-self: flex-start;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 2em;
|
||||
padding-bottom: 0.2em;
|
||||
border-bottom: 0.1em solid var(--cat-colour);
|
||||
}
|
||||
h3, h3 > code {
|
||||
font-size: 1.1em;
|
||||
text-align: left;
|
||||
}
|
||||
h3 > code {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
nav {
|
||||
background: var(--bg-bright);
|
||||
|
@ -156,7 +190,6 @@ a { font-weight: bold; }
|
|||
a:not(.nav):hover { color: hsl(214, 67%, 50%); }
|
||||
a:not(.nav):active, a:focus { color: hsl(214, 87%, 60%); } */
|
||||
a:not(.nav):not(.bigbutton):visited { color: hsl(240, 77%, 60%); }
|
||||
|
||||
pre {
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{%
|
||||
# Lists of axes
|
||||
|
||||
In various commands such as `//copy+`, `//move+`, and others lists of axes are used. These are all underpinned by a single grammar and a single parser (located in `worldeditadditions/utils/parse/axes.lua`). While the parser itself requires pre-split tokens (see `split_shell` for that), the grammar which it parses is documnted here.
|
||||
In various commands such as `//copy+`, `//move+`, and others lists of axes are used. These are all underpinned by a single grammar and a single parser (located in `worldeditadditions/utils/parse/axes.lua`). While the parser itself requires pre-split tokens (see `split_shell` for that), the grammar which it parses is documented here.
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
After Width: | Height: | Size: 181 KiB |
|
@ -13,6 +13,23 @@ window.addEventListener("load", () => {
|
|||
})
|
||||
});
|
||||
|
||||
function get_source_channel_offset() {
|
||||
const select = document.querySelector("#img2brush-channel");
|
||||
console.info(`get_source_channel_offset: channel is ${select.value}`)
|
||||
switch(select.value) {
|
||||
case "alpha":
|
||||
return 3;
|
||||
case "red":
|
||||
return 0;
|
||||
case "green":
|
||||
return 1;
|
||||
case "blue":
|
||||
return 2;
|
||||
default:
|
||||
throw new Error(`Error : Unknown channel name ${select.value}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function select_output() {
|
||||
let output = document.querySelector("#brushimg-tsv");
|
||||
|
||||
|
@ -26,7 +43,6 @@ function select_output() {
|
|||
selection.addRange(range);
|
||||
}
|
||||
|
||||
|
||||
function handle_drag_enter(event) {
|
||||
event.target.classList.add("dropzone-active");
|
||||
}
|
||||
|
@ -80,12 +96,15 @@ function handle_new_image(image) {
|
|||
}
|
||||
|
||||
function pixels2tsv(pixels) {
|
||||
const offset = get_source_channel_offset();
|
||||
console.info(`pixels2tsv: offset is ${offset}`);
|
||||
let result = "";
|
||||
for(let y = 0; y < pixels.height; y++) {
|
||||
let row = [];
|
||||
for(let x = 0; x < pixels.width; x++) {
|
||||
// No need to rescale here - this is done automagically by WorldEditAdditions.
|
||||
row.push(pixels.data[((y*pixels.width + x) * 4) + 3] / 255);
|
||||
// r/b/g/alpha
|
||||
row.push(pixels.data[((y*pixels.width + x) * 4) + offset] / 255);
|
||||
}
|
||||
result += row.join(`\t`) + `\n`;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,17 @@ title: Image to brush converter
|
|||
<section class="panel-generic">
|
||||
<h1>Image to sculpting brush converter</h1>
|
||||
|
||||
<p>Convert any image to a sculpting brush here! The <strong>alpha (opacity) channel</strong> is used to determine the weight of the brush - <strong>all colour is ignored</strong>.</p>
|
||||
<p>Convert any image to a sculpting brush here!</p>
|
||||
<p>
|
||||
<strong>Use this channel to convert:</strong>
|
||||
<select name="img2brush-channel" id="img2brush-channel">
|
||||
<option value="alpha" selected>Alpha (opacity)</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>Only the selected channel is used to determine the weight of the brush - <strong>all other channels are ignored</strong>! Change the channel option <strong>before</strong> you drag + drop the image onto this page.</p>
|
||||
</section>
|
||||
|
||||
<section id="dropzone" class="bigbox panel-generic">
|
||||
|
|
|
@ -100,7 +100,10 @@ date: 2000-01-01
|
|||
|
||||
<pre><code>cd path/to/worldmods;
|
||||
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>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
|
||||
const phin = require("phin");
|
||||
|
||||
const a = require("./Ansi.js");
|
||||
const pretty_ms = require("pretty-ms");
|
||||
|
||||
class FileFetcher {
|
||||
#cache = [];
|
||||
|
||||
#pkg_obj = null;
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
fetch_file(url) {
|
||||
let target_client = path.join(`/img`, path.basename(url));
|
||||
|
||||
if(this.#cache.includes(url)) return target_client;
|
||||
|
||||
this.#cache.push(url);
|
||||
|
||||
this.download_file(url); // Returns a promise! We fire-and-forget it though 'cause this function *must* be synchronous :-/
|
||||
|
||||
return target_client;
|
||||
}
|
||||
|
||||
async download_file(url) {
|
||||
const time_start = new Date();
|
||||
|
||||
if(this.#pkg_obj === null) {
|
||||
this.#pkg_obj = JSON.parse(await fs.promises.readFile(
|
||||
path.join(path.dirname(__dirname), "package.json"), "utf8"
|
||||
));
|
||||
}
|
||||
|
||||
let target_download = path.join(`_site/img`, path.basename(url));
|
||||
|
||||
const response = await phin({
|
||||
url,
|
||||
headers: {
|
||||
"user-agent": `WorldEditAdditionsStaticBuilder/${this.#pkg_obj.version} (Node.js/${process.version}; ${os.platform()} ${os.arch()}) eleventy/${this.#pkg_obj.dependencies["@11ty/eleventy"].replace(/\^/, "")}`
|
||||
},
|
||||
followRedirects: true,
|
||||
parse: 'none' // Returns a Buffer
|
||||
// If we stream and pipe to a file, the build never ends :-/
|
||||
});
|
||||
|
||||
await fs.promises.writeFile(target_download, response.body);
|
||||
|
||||
console.log([
|
||||
`${a.fred}${a.hicol}FETCH_FILE${a.reset}`,
|
||||
`${a.fyellow}${pretty_ms(new Date() - time_start)}${a.reset}`,
|
||||
`${a.fgreen}${url}${a.reset}`
|
||||
].join("\t"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = FileFetcher;
|
|
@ -32,7 +32,6 @@ function calculate_size(width, height, size_spec) {
|
|||
|
||||
// Main task list - we make sure it completes before exiting.
|
||||
var queue = null;
|
||||
var pMemoize = null;
|
||||
|
||||
async function make_queue() {
|
||||
// 1: Setup task queue
|
||||
|
@ -75,7 +74,7 @@ async function srcset(source_image, target_dir, urlpath, format = "__AUTO__", si
|
|||
quality,
|
||||
strip
|
||||
});
|
||||
console.log(`IMAGE\t${a.fcyan}${queue.size}/${queue.pending} tasks${a.reset}\t${a.fyellow}${pretty_ms(new Date() - start)}${a.reset}\t${a.fgreen}${target_current}${a.reset}`);
|
||||
console.log(`${a.fmagenta}${a.hicol}IMAGE${a.reset}\t${a.fcyan}${queue.size}/${queue.pending} tasks${a.reset}\t${a.fyellow}${pretty_ms(new Date() - start)}${a.reset}\t${a.fgreen}${target_current}${a.reset}`);
|
||||
});
|
||||
// const size_target = await imagickal.dimensions(target_current);
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ function make_section(acc, cat_current, cats) {
|
|||
.replace(/\s+/g, "-")
|
||||
.replace(/-.*$/, ""),
|
||||
content: markdown.render(acc.slice(1).join("\n"))
|
||||
.replace(/<h4(\/?)>/g, "<h3$1>")
|
||||
.replace(/<h5(\/?)>/g, "<h4$1>")
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -57,23 +59,26 @@ module.exports = function parse_sections(source) {
|
|||
for(let line of lines) {
|
||||
|
||||
if(line.startsWith(`#`)) {
|
||||
let heading_level = line.match(/^#+/)[0].length;
|
||||
|
||||
// 1: Deal with the previous section
|
||||
if(acc.length > 0) {
|
||||
let heading_level_prev = acc[0].match(/^#+/)[0].length;
|
||||
if(heading_level_prev === 3 && acc.length > 0) {
|
||||
if(heading_level_prev === 3 && acc.length > 0 && heading_level <= 3) {
|
||||
result.push(make_section(acc, cat_current, cats));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 2: Deal with the new line
|
||||
let heading_level = line.match(/^#+/)[0].length
|
||||
|
||||
if(heading_level === 2)
|
||||
cat_current = extract_title(line);
|
||||
|
||||
acc = [ line ];
|
||||
if(heading_level > 3)
|
||||
acc.push(line)
|
||||
else
|
||||
acc = [ line ];
|
||||
}
|
||||
else
|
||||
acc.push(line);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "worldeditadditions",
|
||||
"version": "1.0.0",
|
||||
"version": "1.14.5",
|
||||
"description": "Documentation website for WorldEditAdditions",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
|
@ -19,19 +19,19 @@
|
|||
"url": "https://github.com/sbrl/Minetest-WorldEditAdditions/issues"
|
||||
},
|
||||
"homepage": "https://github.com/sbrl/Minetest-WorldEditAdditions#readme",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^0.12.1",
|
||||
"chroma-js": "^2.1.2",
|
||||
"columnify": "^1.5.4",
|
||||
"debug": "^4.3.2",
|
||||
"imagickal": "^5.0.1",
|
||||
"markdown-it-prism": "^2.2.1",
|
||||
"p-memoize": "^6.0.1",
|
||||
"p-queue": "^7.1.0",
|
||||
"phin": "^3.6.0",
|
||||
"pretty-ms": "^7.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"html-entities": "^2.3.2"
|
||||
"@11ty/eleventy": "^2.0.1",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clean-css": "^5.3.2",
|
||||
"columnify": "^1.6.0",
|
||||
"debug": "^4.3.4",
|
||||
"html-entities": "^2.4.0",
|
||||
"html-minifier-terser": "^7.0.0-beta.0",
|
||||
"imagickal": "^5.0.1",
|
||||
"markdown-it-prism": "^2.3.0",
|
||||
"p-memoize": "^7.1.1",
|
||||
"p-queue": "^7.3.4",
|
||||
"phin": "^3.7.0",
|
||||
"pretty-ms": "^7.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
What's the bug? Be clear and concise in our explanation. Don't forget to include any context, error messages, logs, and screenshots required to understand the issue if applicable.
|
||||
|
||||
**Reproduction steps:**
|
||||
Steps to reproduce the behaviour:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Enter this command to '....'
|
||||
4. See error
|
||||
|
||||
**System information (please complete the following information):**
|
||||
- **Operating system and version:** [e.g. iOS]
|
||||
- **Minetest version:** [e.g. 5.8.0]
|
||||
- **WorldEdit version:**
|
||||
- **WorldEditAdditions version:**
|
||||
|
||||
Please add any other additional specific system information here too if you think it would help.
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
A clear and concise description of what the problem you want to solve is. e.g. I'm always frustrated when [...]
|
||||
|
||||
## Solution
|
||||
Describe clearly the solution you'd like.
|
||||
|
||||
## Alternatives
|
||||
A clear and concise description of any alternative solutions or features you've considered, and how they relate to your proposed feature.
|
||||
|
||||
## Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
Don't forget to remove replace descriptions, but keep the headers.
|
|
@ -3,7 +3,7 @@ codes = true
|
|||
|
||||
exclude_files = {
|
||||
".luarocks/*",
|
||||
"worldeditadditions/utils/bit.lua"
|
||||
"worldeditadditions_core/utils/bit.lua"
|
||||
}
|
||||
|
||||
files["worldeditadditions_core/register/check.lua"] = { read_globals = { "table" } }
|
||||
|
@ -33,6 +33,7 @@ read_globals = {
|
|||
"describe",
|
||||
"bonemeal",
|
||||
"dofile",
|
||||
"PerlinNoise"
|
||||
"PerlinNoise",
|
||||
"Settings"
|
||||
}
|
||||
std = "max"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.abs", function()
|
||||
it("should work with a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.add", function()
|
||||
it("should add 2 positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.area", function()
|
||||
it("should work with a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.ceil", function()
|
||||
it("should ceil a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.clamp", function()
|
||||
it("should work by not changing values if it's already clamped", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.clone", function()
|
||||
it("should return a new Vector3 instance", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.__concat", function()
|
||||
it("should work when concatenating a Vector3 + Vector3", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.divide", function()
|
||||
it("should divide 2 positive vectors", function()
|
||||
|
@ -18,10 +18,10 @@ describe("Vector3.divide", function()
|
|||
)
|
||||
end)
|
||||
it("should work with scalar a", function()
|
||||
local a = 2
|
||||
local b = Vector3.new(12, 14, 16)
|
||||
local a = 100
|
||||
local b = Vector3.new(4, 5, 2)
|
||||
assert.are.same(
|
||||
Vector3.new(6, 7, 8),
|
||||
Vector3.new(25, 20, 50),
|
||||
a / b
|
||||
)
|
||||
end)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.dot", function()
|
||||
it("should work with a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.equals", function()
|
||||
it("should return true when identical", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.expand_region", function()
|
||||
it("should work with positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.floor", function()
|
||||
it("should floor a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.is_contained", function()
|
||||
it("should return true when inside", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
-- To find these numbers, in Javascript:
|
||||
-- function t(x) { return Math.sqrt((x*x)*3); }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.length_squared", function()
|
||||
it("should work with a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.limit_to", function()
|
||||
it("should limit_to a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.max", function()
|
||||
it("should work with positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.max_component", function()
|
||||
it("should work with a positive vector x", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.mean", function()
|
||||
it("should work with a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.min", function()
|
||||
it("should work with positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.min_component", function()
|
||||
it("should work with a positive vector x", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
-- To find these numbers, in Javascript:
|
||||
-- function t(x) { return Math.sqrt((x*x)*3); }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.multiply", function()
|
||||
it("should multiply 2 positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.add", function()
|
||||
it("should create a new Vector3", function()
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.rotate3d", function()
|
||||
it("should work with an origin of (0,0,0) X axis", function()
|
||||
local origin = Vector3.new(0, 0, 0)
|
||||
local point = Vector3.new(0, 1, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(180), -- x axis = yz plane
|
||||
math.rad(0), -- y axis = xz plane
|
||||
math.rad(0)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(0, -1, 0),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
it("should work with an origin of (0,0,0) Y axis", function()
|
||||
local origin = Vector3.new(0, 0, 0)
|
||||
local point = Vector3.new(1, 0, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(0), -- x axis = yz plane
|
||||
math.rad(180), -- y axis = xz plane
|
||||
math.rad(0)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(-1, 0, 0),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
it("should work with an origin of (0,0,0) Z axis", function()
|
||||
local origin = Vector3.new(0, 0, 0)
|
||||
local point = Vector3.new(1, 0, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(0), -- x axis = yz plane
|
||||
math.rad(0), -- y axis = xz plane
|
||||
math.rad(180)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(-1, 0, 0),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
it("should work with a non-zero origin X axis", function()
|
||||
local origin = Vector3.new(1, 1, 1)
|
||||
local point = Vector3.new(0, 2, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(180), -- x axis = yz plane
|
||||
math.rad(0), -- y axis = xz plane
|
||||
math.rad(0)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(0, 0, 2),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
it("should work with a non-zero origin Y axis", function()
|
||||
local origin = Vector3.new(1, 1, 1)
|
||||
local point = Vector3.new(0, 2, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(0), -- x axis = yz plane
|
||||
math.rad(180), -- y axis = xz plane
|
||||
math.rad(0)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(2, 2, 2),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
it("should work with a non-zero origin Z axis", function()
|
||||
local origin = Vector3.new(1, 1, 1)
|
||||
local point = Vector3.new(0, 2, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(0), -- x axis = yz plane
|
||||
math.rad(0), -- y axis = xz plane
|
||||
math.rad(180)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(2, 0, 0),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
it("should work with a non-zero origin Z axis -90 degrees", function()
|
||||
local origin = Vector3.new(1, 1, 1)
|
||||
local point = Vector3.new(0, 2, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(0), -- x axis = yz plane
|
||||
math.rad(0), -- y axis = xz plane
|
||||
math.rad(-90)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(2, 2, 0),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
it("should work with multiple axes X Y", function()
|
||||
local origin = Vector3.new(0, 0, 0)
|
||||
local point = Vector3.new(0, 2, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(90), -- x axis = yz plane
|
||||
math.rad(90), -- y axis = xz plane
|
||||
math.rad(0)-- z axis = xy plane
|
||||
)
|
||||
assert.are.same(
|
||||
Vector3.new(2, 0, 0),
|
||||
Vector3.rotate3d(origin, point, rotate):round_dp(4)
|
||||
)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
it("should return new Vector3 instances", function()
|
||||
local origin = Vector3.new(0, 0, 0)
|
||||
local point = Vector3.new(1, 0, 0)
|
||||
local rotate = Vector3.new(
|
||||
math.rad(0), -- x axis = yz plane
|
||||
math.rad(180), -- y axis = xz plane
|
||||
math.rad(0)-- z axis = xy plane
|
||||
)
|
||||
|
||||
local result = Vector3.rotate3d(origin, point, rotate):round(4)
|
||||
assert.are.same(Vector3.new(-1, 0, 0), result)
|
||||
|
||||
result.y = 999
|
||||
|
||||
assert.are.same(Vector3.new(0, 0, 0), origin)
|
||||
assert.are.same(Vector3.new(1, 0, 0), point)
|
||||
assert.are.same(Vector3.new(-1, 999, 0), result)
|
||||
end)
|
||||
end)
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.round", function()
|
||||
it("should round a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.set_to", function()
|
||||
it("should set_to a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.snap_to", function()
|
||||
it("should snap_to a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.sort", function()
|
||||
it("should work with positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.sqrt", function()
|
||||
it("should sqrt a positive vector", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.subtract", function()
|
||||
it("should subtract 2 positive vectors", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.__tostring", function()
|
||||
it("should stringify a Vector3", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
-- To find these numbers, in Javascript:
|
||||
-- function t(x) { return Math.sqrt((x*x)*3); }
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
describe("Vector3.volume", function()
|
||||
it("should work", function()
|
||||
local a = Vector3.new(4, 4, 4)
|
||||
local b = Vector3.new(8, 8, 8)
|
||||
assert.are.equal(
|
||||
64,
|
||||
Vector3.volume(a, b)
|
||||
)
|
||||
end)
|
||||
it("should work the other way around", function()
|
||||
local a = Vector3.new(4, 4, 4)
|
||||
local b = Vector3.new(8, 8, 8)
|
||||
assert.are.equal(
|
||||
64,
|
||||
Vector3.volume(b, a)
|
||||
)
|
||||
end)
|
||||
it("should work with negative values", function()
|
||||
local a = Vector3.new(-4, -4, -4)
|
||||
local b = Vector3.new(-8, -8, -8)
|
||||
assert.are.equal(
|
||||
64,
|
||||
Vector3.volume(a, b)
|
||||
)
|
||||
end)
|
||||
it("should work with different values", function()
|
||||
local a = Vector3.new(5, 6, 7)
|
||||
local b = Vector3.new(10, 10, 10)
|
||||
assert.are.equal(
|
||||
60,
|
||||
Vector3.volume(a, b)
|
||||
)
|
||||
end)
|
||||
it("should work with mixed values", function()
|
||||
local a = Vector3.new(10, 5, 8)
|
||||
local b = Vector3.new(5, 10, 2)
|
||||
assert.are.equal(
|
||||
150,
|
||||
Vector3.volume(a, b)
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,158 @@
|
|||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
local facing_dirs = dofile("./.tests/parse/axes/include_facing_dirs.lua")
|
||||
|
||||
local parse = require("worldeditadditions_core.utils.parse.axes_parser")
|
||||
local parse_axes = parse.keytable
|
||||
|
||||
|
||||
describe("parse_axes", function()
|
||||
|
||||
-- Basic tests
|
||||
it("should work on single horizontal axes", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"x", "3",
|
||||
"-z", "10",
|
||||
}, facing_dirs.x_pos)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(0, 0, -10), minv)
|
||||
assert.are.same(Vector3.new(3, 0, 0), maxv)
|
||||
end)
|
||||
|
||||
it("should handle axis clumps and orphan (universal) values", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"xz", "-3",
|
||||
"10",
|
||||
}, facing_dirs.x_pos)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(-3, 0, -3), minv)
|
||||
assert.are.same(Vector3.new(10, 10, 10), maxv)
|
||||
end)
|
||||
|
||||
it("should work on directions and their abriviations", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"l", "3", -- +z
|
||||
"-r", "-3", -- +z
|
||||
"b", "-10", -- -x
|
||||
}, facing_dirs.x_pos)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(0, 0, -3), minv)
|
||||
assert.are.same(Vector3.new(10, 0, 3), maxv)
|
||||
end)
|
||||
|
||||
it("should work with compass directions and their abriviations", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"n", "3", -- +z
|
||||
"south", "3", -- -z
|
||||
"-west", "10", -- +x
|
||||
}, facing_dirs.x_pos)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(0, 0, -3), minv)
|
||||
assert.are.same(Vector3.new(10, 0, 3), maxv)
|
||||
end)
|
||||
|
||||
it("should work with ?", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"?", "3", -- -z
|
||||
}, facing_dirs.z_neg)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(0, 0, -3), minv)
|
||||
assert.are.same(Vector3.new(0, 0, 0), maxv)
|
||||
end)
|
||||
|
||||
it("should work with complex relative / absolute combinations", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"f", "3", -- +x
|
||||
"left", "10", -- +z
|
||||
"y", "77",
|
||||
"x", "30",
|
||||
"back", "99",
|
||||
}, facing_dirs.x_pos)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(-99, 0, 0), minv)
|
||||
assert.are.same(Vector3.new(33, 77, 10), maxv)
|
||||
end)
|
||||
|
||||
it("should work with complex relative / absolute combinations with negative facing_dirs", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"f", "3", -- -z
|
||||
"l", "10", -- +x
|
||||
"y", "77",
|
||||
"x", "30",
|
||||
"b", "99", -- +z
|
||||
}, facing_dirs.z_neg)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(0, 0, -3), minv)
|
||||
assert.are.same(Vector3.new(40, 77, 99), maxv)
|
||||
end)
|
||||
|
||||
it("should return 2 0,0,0 vectors if no input", function()
|
||||
local minv, maxv = parse_axes({
|
||||
-- No input
|
||||
}, facing_dirs.z_neg)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(0, 0, 0), minv)
|
||||
assert.are.same(Vector3.new(0, 0, 0), maxv)
|
||||
end)
|
||||
|
||||
-- Options tests
|
||||
it("should mirror the max values of the two vectors if mirroring keyword is present", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"x", "3",
|
||||
"f", "-5", -- +x
|
||||
"z", "-10",
|
||||
"mir",
|
||||
}, facing_dirs.x_pos)
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(-5, 0, -10), minv)
|
||||
assert.are.same(Vector3.new(5, 0, 10), maxv)
|
||||
end)
|
||||
|
||||
it("should return a single vector if 'sum' input is truthy", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"x", "3",
|
||||
"z", "-10",
|
||||
}, facing_dirs.x_pos,"sum")
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(3, 0, -10), minv)
|
||||
assert.are.same(nil, maxv)
|
||||
end)
|
||||
|
||||
it("should dissable mirroring if 'sum' input is truthy", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"x", "3",
|
||||
"f", "-5", -- +x
|
||||
"z", "-10",
|
||||
"sym",
|
||||
}, facing_dirs.x_pos,"sum")
|
||||
assert.is.truthy(minv)
|
||||
assert.are.same(Vector3.new(-2, 0, -10), minv)
|
||||
assert.are.same(nil, maxv)
|
||||
end)
|
||||
|
||||
-- Error tests
|
||||
it("should return error if bad axis/dir", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"f", "3", -- +x
|
||||
"lift", "10", -- Invalid axis
|
||||
"y", "77",
|
||||
"x", "30",
|
||||
"back", "99", -- -x
|
||||
}, facing_dirs.x_pos)
|
||||
assert.are.same(false, minv)
|
||||
assert.are.same("string", type(maxv))
|
||||
end)
|
||||
|
||||
it("should return error if bad value", function()
|
||||
local minv, maxv = parse_axes({
|
||||
"f", "3", -- +x
|
||||
"left", "10", -- +z
|
||||
"y", "!Q", -- Invalid value
|
||||
"x", "30",
|
||||
"back", "99",
|
||||
}, facing_dirs.x_pos)
|
||||
assert.are.same(false, minv)
|
||||
assert.are.same("string", type(maxv))
|
||||
end)
|
||||
|
||||
end)
|
|
@ -1,6 +1,6 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
local axes = require("worldeditadditions.utils.parse.axes")
|
||||
local axes = require("worldeditadditions_core.utils.parse.axes")
|
||||
local parse_abs_axis_name = axes.parse_abs_axis_name
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
local facing_dirs = dofile("./.tests/parse/axes/include_facing_dirs.lua")
|
||||
|
||||
local axes = require("worldeditadditions.utils.parse.axes")
|
||||
local axes = require("worldeditadditions_core.utils.parse.axes")
|
||||
local parse_axes = axes.parse_axes
|
||||
|
||||
--[[ Original idea for how this function was supposed to work from @VorTechnix
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
local Vector3 = require("worldeditadditions.utils.vector3")
|
||||
local Vector3 = require("worldeditadditions_core.utils.vector3")
|
||||
|
||||
local facing_dirs = dofile("./.tests/parse/axes/include_facing_dirs.lua")
|
||||
|
||||
local axes = require("worldeditadditions.utils.parse.axes")
|
||||
local axes = require("worldeditadditions_core.utils.parse.axes")
|
||||
local parse_relative_axis_name = axes.parse_relative_axis_name
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
local parse_chance = require("worldeditadditions_core.utils.parse.chance")
|
||||
|
||||
describe("parse.chance", function()
|
||||
it("should work in 1-in-n mode by default", function()
|
||||
local source = "50%"
|
||||
|
||||
assert.are.equal(
|
||||
2,
|
||||
parse_chance(source)
|
||||
)
|
||||
end)
|
||||
it("should work with a different value in 1-in-n mode", function()
|
||||
local source = "25%"
|
||||
|
||||
assert.are.equal(
|
||||
4,
|
||||
parse_chance(source)
|
||||
)
|
||||
end)
|
||||
it("should work in weight mode", function()
|
||||
local source = "50%"
|
||||
|
||||
assert.are.equal(
|
||||
2,
|
||||
parse_chance(source, "weight")
|
||||
)
|
||||
end)
|
||||
it("should work in weight mode with different number", function()
|
||||
local source = "90%"
|
||||
|
||||
assert.are.equal(
|
||||
10,
|
||||
parse_chance(source, "weight")
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,122 @@
|
|||
_G.worldeditadditions_core = {
|
||||
split = require("worldeditadditions_core.utils.strings.split"),
|
||||
table = {
|
||||
contains = require("worldeditadditions_core.utils.table.table_contains")
|
||||
}
|
||||
}
|
||||
local parse_map = require("worldeditadditions_core.utils.parse.map")
|
||||
|
||||
describe("parse.map", function()
|
||||
it("should work with a single param", function()
|
||||
local success, result = parse_map("apples yay")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = "yay" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with 2 params", function()
|
||||
local success, result = parse_map("apples yay oranges yummy")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = "yay", oranges = "yummy" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with an int value", function()
|
||||
local success, result = parse_map("apples 2")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2 },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with a float value", function()
|
||||
local success, result = parse_map("apples 2.71")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2.71 },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with 2 int values", function()
|
||||
local success, result = parse_map("apples 2 banana 23")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = 23 },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with mixed values", function()
|
||||
local success, result = parse_map("apples 2 banana yummy")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = "yummy" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with a value that starts as a number and ends as a string", function()
|
||||
local success, result = parse_map("apples 20t banana yummy")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = "20t", banana = "yummy" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with a value that starts as a string and ends as a number", function()
|
||||
local success, result = parse_map("apples t20 banana yummy")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = "t20", banana = "yummy" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with multiple spaces", function()
|
||||
local success, result = parse_map("apples 2 banana \t yummy")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = "yummy" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should ignore a hanging item at the end", function()
|
||||
local success, result = parse_map("apples 2 banana")
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2 },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with hanging items declared as keywords at the end", function()
|
||||
local success, result = parse_map("apples 2 banana", { "banana" })
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = true },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with hanging items declared as keywords in the middle", function()
|
||||
local success, result = parse_map("apples 2 banana pear paris", { "banana" })
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = true, pear = "paris" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with some but not other hanging items declared as keywords", function()
|
||||
local success, result = parse_map("apples 2 banana pear paris arrange", { "banana" })
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = true, pear = "paris" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with hanging items declared as keywords at the beginning", function()
|
||||
local success, result = parse_map("banana apples 2 pear paris", { "banana" })
|
||||
assert.are.equal(true, success)
|
||||
assert.are.same(
|
||||
{ apples = 2, banana = true, pear = "paris" },
|
||||
result
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,24 @@
|
|||
local parse_seed = require("worldeditadditions_core.utils.parse.seed")
|
||||
|
||||
describe("parse.seed", function()
|
||||
it("should work", function()
|
||||
local source = "a test string"
|
||||
|
||||
local result = parse_seed(source)
|
||||
|
||||
assert.are.equal(
|
||||
"number",
|
||||
type(result)
|
||||
)
|
||||
end)
|
||||
it("should work with a long string", function()
|
||||
local source = "If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction. If it looks like a duck and quacks like a duck but it needs batteries, you probably have the wrong abstraction."
|
||||
|
||||
local result = parse_seed(source)
|
||||
|
||||
assert.are.equal(
|
||||
"number",
|
||||
type(result)
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,32 @@
|
|||
local makeset = require("worldeditadditions_core.utils.table.makeset")
|
||||
|
||||
describe("table.makeset", function()
|
||||
it("should work with a single item", function()
|
||||
local result = makeset({ "apples" })
|
||||
assert.are.same(
|
||||
{ apples = true },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with 2 items", function()
|
||||
local result = makeset({ "apples", "orange" })
|
||||
assert.are.same(
|
||||
{ apples = true, orange = true },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with duplicate items", function()
|
||||
local result = makeset({ "apples", "apples" })
|
||||
assert.are.same(
|
||||
{ apples = true },
|
||||
result
|
||||
)
|
||||
end)
|
||||
it("should work with duplicate items and non-duplicate items", function()
|
||||
local result = makeset({ "apples", "oranges", "apples" })
|
||||
assert.are.same(
|
||||
{ apples = true, oranges = true },
|
||||
result
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,48 @@
|
|||
local apply = require("worldeditadditions_core.utils.table.table_apply")
|
||||
|
||||
describe("table.makeset", function()
|
||||
it("should work", function()
|
||||
local source = { apples = 4 }
|
||||
local target = { oranges = 3 }
|
||||
|
||||
apply(source, target)
|
||||
|
||||
assert.are.same(
|
||||
{ apples = 4, oranges = 3 },
|
||||
target
|
||||
)
|
||||
end)
|
||||
it("should overwrite values in target", function()
|
||||
local source = { apples = 4 }
|
||||
local target = { apples = 3 }
|
||||
|
||||
apply(source, target)
|
||||
|
||||
assert.are.same(
|
||||
{ apples = 4 },
|
||||
target
|
||||
)
|
||||
end)
|
||||
it("should work with strings", function()
|
||||
local source = { apples = "4" }
|
||||
local target = { oranges = "3" }
|
||||
|
||||
apply(source, target)
|
||||
|
||||
assert.are.same(
|
||||
{ apples = "4", oranges = "3" },
|
||||
target
|
||||
)
|
||||
end)
|
||||
it("should overwrite values in target with strings", function()
|
||||
local source = { apples = "4" }
|
||||
local target = { apples = "3" }
|
||||
|
||||
apply(source, target)
|
||||
|
||||
assert.are.same(
|
||||
{ apples = "4" },
|
||||
target
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,40 @@
|
|||
local contains = require("worldeditadditions_core.utils.table.table_contains")
|
||||
|
||||
describe("table.makeset", function()
|
||||
it("should work with a string", function()
|
||||
assert.are.same(
|
||||
true,
|
||||
contains({ "apples" }, "apples")
|
||||
)
|
||||
end)
|
||||
it("should work with a number", function()
|
||||
assert.are.same(
|
||||
true,
|
||||
contains({ 4 }, 4)
|
||||
)
|
||||
end)
|
||||
it("should return false if a number doesn't exist", function()
|
||||
assert.are.same(
|
||||
false,
|
||||
contains({ 5 }, 4)
|
||||
)
|
||||
end)
|
||||
it("should work with a string and multiple items in the table", function()
|
||||
assert.are.same(
|
||||
true,
|
||||
contains({ "yay", "apples", "cat" }, "apples")
|
||||
)
|
||||
end)
|
||||
it("should return false if it doesn't exist", function()
|
||||
assert.are.same(
|
||||
false,
|
||||
contains({ "yay" }, "orange")
|
||||
)
|
||||
end)
|
||||
it("should return false if it doesn't exist wiith multiple items", function()
|
||||
assert.are.same(
|
||||
false,
|
||||
contains({ "yay", "apples", "cat" }, "orange")
|
||||
)
|
||||
end)
|
||||
end)
|
|
@ -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)
|
|
@ -1,4 +1,4 @@
|
|||
local polyfill = require("worldeditadditions.utils.strings.polyfill")
|
||||
local polyfill = require("worldeditadditions_core.utils.strings.polyfill")
|
||||
|
||||
describe("str_ends", function()
|
||||
it("should return true for a single character", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local polyfill = require("worldeditadditions.utils.strings.polyfill")
|
||||
local polyfill = require("worldeditadditions_core.utils.strings.polyfill")
|
||||
|
||||
describe("str_padend", function()
|
||||
it("should pad a string", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local polyfill = require("worldeditadditions.utils.strings.polyfill")
|
||||
local polyfill = require("worldeditadditions_core.utils.strings.polyfill")
|
||||
|
||||
describe("str_padstart", function()
|
||||
it("should pad a string", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local polyfill = require("worldeditadditions.utils.strings.polyfill")
|
||||
local polyfill = require("worldeditadditions_core.utils.strings.polyfill")
|
||||
|
||||
describe("str_starts", function()
|
||||
it("should return true for a single character", function()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local polyfill = require("worldeditadditions.utils.strings.polyfill")
|
||||
local polyfill = require("worldeditadditions_core.utils.strings.polyfill")
|
||||
|
||||
describe("trim", function()
|
||||
it("work for a string that's already trimmed", function()
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"Lua.diagnostics.disable": [
|
||||
"trailing-space",
|
||||
"undefined-global",
|
||||
"lowercase-global",
|
||||
"cast-local-type"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"ollow",
|
||||
"weacmd"
|
||||
]
|
||||
}
|
58
CHANGELOG.md
58
CHANGELOG.md
|
@ -3,6 +3,56 @@ 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.
|
||||
|
||||
|
||||
## v1.14.5: The multipoint update, hotfix 5 (1st August 2023)
|
||||
- Fix a bug where creative players in survival couldn't punch out position markers
|
||||
- Added `//listentities`, which lists all currently loaded `ObjectRef`s. This is intended for debugging mods - thanks to @Zughy in #103
|
||||
|
||||
|
||||
## v1.14.4: The multipoint update, hotfix 4 (31st July 2023)
|
||||
- When any segment of the marker wall is punched, unmark the entire wall
|
||||
|
||||
|
||||
## v1.14.3: The multipoint update, hotfix 3 (18th July 2023)
|
||||
- Fix regions not remembering their state and being unresettable
|
||||
|
||||
|
||||
## v1.14.2: The multipoint update, hotfix 2 (15th July 2023)
|
||||
- Fix crash in `//subdivide`, again due to the new position system
|
||||
|
||||
|
||||
## v1.14.1: The multipoint update, hotfix 1 (12th July 2023)
|
||||
- Fix issue #100, which caused a crash as `//pos1` and `//pos2` allowed non-integer positions to be set
|
||||
|
||||
|
||||
## v1.14: The multipoint update (11th July 2023)
|
||||
- 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
|
||||
- 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.
|
||||
- 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.
|
||||
- The new multipoint wand required this as a prerequisite
|
||||
- Add [`//pos`](https://worldeditadditions.mooncarrot.space/Reference/#pos), for setting any numbered point (i.e. not just pos1 and pos2, but pos3 and beyond)
|
||||
- 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
|
||||
- Respect node rotation (i.e. param2) when copying/moving
|
||||
- Override `//move` and `//copy` by default
|
||||
|
||||
### Bugfixes and changes
|
||||
- Migrate from `depends.txt` to `mod.conf`
|
||||
- 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
|
||||
- Make `//pos1`, `//1`, `//pos2`, `//2`, `//mark`, `//unmark`, and `//reset` aware of the new WEA positioning system
|
||||
|
||||
|
||||
## v1.13: The transformational update (2nd January 2022)
|
||||
- Add [`//sfactor`](https://worldeditadditions.mooncarrot.space/Reference/#sfactor) (_selection factor_) - Selection Tools by @VorTechnix are finished for now.
|
||||
- Add [`//mface`](https://worldeditadditions.mooncarrot.space/Reference/#mface) (_measure facing_), [`//midpos`](https://worldeditadditions.mooncarrot.space/Reference/#midpos) (_measure middle position_), [`//msize`](https://worldeditadditions.mooncarrot.space/Reference/#msize) (_measure size_), [`//mtrig`](#mtrig) (_measure trigonometry_) - Measuring Tools implemented by @VorTechnix.
|
||||
|
@ -19,10 +69,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.
|
||||
- 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
|
||||
- `//layers`: Add optional slope constraint (inspired by [WorldPainter](https://worldpainter.net/))
|
||||
- `//bonemeal`: Add optional node list constraint
|
||||
- `//walls`: Add optional thickness argument
|
||||
- `//sstack`: Add human-readable approx volumes of regions in the selection stack
|
||||
- [`//layers`](https://worldeditadditions.mooncarrot.space/Reference/#layers): Add optional slope constraint (inspired by [WorldPainter](https://worldpainter.net/))
|
||||
- [`//bonemeal`](https://worldeditadditions.mooncarrot.space/Reference/#bonemeal): Add optional node list constraint
|
||||
- [`//walls`](https://worldeditadditions.mooncarrot.space/Reference/#walls): Add optional thickness argument
|
||||
- [`//sstack`](https://worldeditadditions.mooncarrot.space/Reference/#sstack): Add human-readable approx volumes of regions in the selection stack
|
||||
|
||||
|
||||
### Bugfixes
|
||||
|
|
|
@ -37,7 +37,9 @@ When actually implementing stuff, here are a few guidelines that I recommend to
|
|||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██ ████ ██ ██ ██ ██ ███████
|
||||
local wea = worldeditadditions
|
||||
worldedit.register_command("{name}", {
|
||||
local weac = worldeditadditions_core
|
||||
local Vector3 = weac.Vector3
|
||||
worldeditadditions_core.register_command("{name}", {
|
||||
params = "<argument> <argument=default> <option1|option2|...> [<optional_argument> <optional_argument2> ...] | [<optional_argument> [<optional_argument2>]]",
|
||||
description = "A **brief** description of what this command does",
|
||||
privs = { worldedit = true },
|
||||
|
@ -47,18 +49,18 @@ worldedit.register_command("{name}", {
|
|||
return true, param1, param2
|
||||
end,
|
||||
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,
|
||||
func = function(name, param1, param2)
|
||||
-- Start a timer
|
||||
local start_time = wea.get_ms_time()
|
||||
local start_time = weac.get_ms_time()
|
||||
-- Do stuff
|
||||
|
||||
-- Finish timer
|
||||
local time_taken = wea.get_ms_time() - start_time
|
||||
local time_taken = weac.get_ms_time() - start_time
|
||||
|
||||
minetest.log("This is a logged message!")
|
||||
return true, "Completed command in " .. wea.format.human_time(time_taken)
|
||||
return true, "Completed command in " .. weac.format.human_time(time_taken)
|
||||
end
|
||||
})
|
||||
```
|
||||
|
|
|
@ -22,6 +22,74 @@ Other useful links:
|
|||
██████ ███████ ██████ ██ ██ ███████ ██ ██ ██ ██
|
||||
-->
|
||||
|
||||
### `//metaball add <radius> | remove <index> | render <replace_node> [<threshold=1>] | list | clear | volume`
|
||||
Draws two or more [metaballs](https://en.wikipedia.org/wiki/Metaballs). Based on a subcommand system, since each metaball may have a different radius.
|
||||
|
||||
Calling `//metaball render <replace_node>` actually makes changes to the world - all others manipulate an internal player-local list of metaballs to be drawn.
|
||||
|
||||
#### `//metaball add <radius>`
|
||||
Adds a new metaball to the (player-local) list of metaballs to be draw with the given radius. The position of the new metaball is taken from pos1.
|
||||
|
||||
```weacmd
|
||||
//metaball add 5
|
||||
//metaball add 10
|
||||
//metaball add 65
|
||||
```
|
||||
|
||||
#### `//metaball remove <index>`
|
||||
Removes the metaball with the given index from the list. See also `//metaball list`.
|
||||
|
||||
```weacmd
|
||||
//metaball remove 1
|
||||
//metaball remove 2
|
||||
//metaball remove 5
|
||||
```
|
||||
|
||||
#### `//metaball render <replace_node> [<threshold=1>]`
|
||||
Renders the current list of metaballs to the world using replace_node to draw with.
|
||||
|
||||
Threshold is a value that acts as an offset for large or small the metaballs should be. Defaults to 1, with larger values resulting in **smaller** metaballs and smaller values resulting in **larger** metaballs. It is not recommended to set threshold to a negative number.
|
||||
|
||||
Does not clear the list of metaballs after rendering - see `//metaball clear` for that.
|
||||
|
||||
```weacmd
|
||||
//metaball render dirt
|
||||
//metaball render glass
|
||||
//metaball render stone
|
||||
//metaball render stone 2
|
||||
//metaball render cobble 0.5
|
||||
```
|
||||
|
||||
#### `//metaball list`
|
||||
Lists all the metaballs currently registered for the current player (all `//metaball` commands operate on the current player's metaball list only), and their indexes, positions, and sizes. Useful when using `//metaball remove <index>` to identify which metaball you want to remove.
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
Index Position Radius
|
||||
1 (-495, 37, 150) 7
|
||||
2 (-506, 33, 142) 7
|
||||
3 (-516, 35, 152) 10
|
||||
```
|
||||
|
||||
```weacmd
|
||||
//metaball list
|
||||
```
|
||||
|
||||
#### `//metaball clear`
|
||||
Clears the list of metaballs for the current player.
|
||||
|
||||
```weacmd
|
||||
//metaball clear
|
||||
```
|
||||
|
||||
#### `//metaball volume`
|
||||
Calculate an estimated volume of the metaballs currently in the list.
|
||||
|
||||
```weacmd
|
||||
//metaball volume
|
||||
```
|
||||
|
||||
|
||||
### `//ellipsoid <rx> <ry> <rz> <node_name> [h[ollow]]`
|
||||
Creates a solid ellipsoid at position 1 with the radius `(rx, ry, rz)`.
|
||||
|
@ -105,6 +173,8 @@ Generates a maze using replace_node as the walls and air as the paths. Uses [an
|
|||
|
||||
Requires the currently selected area to be at least 3x3 on the x and z axes respectively.
|
||||
|
||||
Mazes are generated from the bottom to the top of the defined region. In other words, the height of the walls of the maze is equal to the height of the defined region.
|
||||
|
||||
The optional `path_length` and `path_width` arguments require additional explanation. When generating a maze, a multi-headed random walk is performed. When the generator decides to move forwards from a point, it does so `path_length` nodes at a time. `path_length` defaults to `2`.
|
||||
|
||||
`path_width` is easier to explain. It defaults to `1`, and is basically the number of nodes wide the path generated is.
|
||||
|
@ -126,7 +196,7 @@ The last example below shows how to set the path length and width:
|
|||
### `//maze3d <replace_node> [<path_length> [<path_width> [<path_depth> [<seed>]]]]`
|
||||
Same as `//maze`, but instead generates mazes in 3 dimensions instead of 2. Requires the currently selected area to be at least 3x3x3.
|
||||
|
||||
The optional `path_depth` parameter defaults to `1` and allows customisation of the height of the paths generated.
|
||||
The optional `path_depth` parameter defaults to `1` and allows customisation of the height of the paths generated. In other words, it customises the ceiling height, or the distance from the floor to the ceiling of the paths generated.
|
||||
|
||||
To get a better look at the generated maze, try inverting it like so:
|
||||
|
||||
|
@ -172,6 +242,37 @@ Generates both square and circular spiral shapes with the given `<replace_node>`
|
|||
```
|
||||
|
||||
|
||||
### `//dome+ <radius> <replace_node> [<pointing_dir:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> ...] [h[ollow]]`
|
||||
Creates a dome shape (i.e. a hemisphere; half a sphere) with a specified radius of the defined node, optionally specifying the direction it should be pointing in (defaults to the positive y direction).
|
||||
|
||||
For example, `//dome+ 5 stone y` creates a dome shape pointing upwards, but `//dome+ 5 stone -y` creates a dome shape pointing downwards.
|
||||
|
||||
Multiple pointing direction axes can be chained together to create multiple domes on top of each other. Multiple conflicting directions will cancel each other out.
|
||||
|
||||
If `h` or `hollow` is specified at the end, then the resulting dome shape is made hollow.
|
||||
|
||||
```
|
||||
//dome+ 5 stone y
|
||||
//dome+ 10 diamond -x
|
||||
//dome+ 25 cactus y z
|
||||
//dome+ 9 dirt x y z
|
||||
```
|
||||
|
||||
|
||||
### `//spline <replace_node> <width_start> [<width_end=width_start> [<steps=3>]]`
|
||||
Draws a curved line, using all the currently defined points as control points. The starting and ending widths of the line can be controlled, and the width will be linearly interpolated.
|
||||
|
||||
**Note:** `//spline` uses the **new** WorldEditAdditions positions! Use the [multipoint wand](#multi-point-wand) to define the control points for the resulting line.
|
||||
|
||||
For those interested, WorldEditAdditions uses the [chaikin curve algorithm](https://starbeamrainbowlabs.com/blog/article.php?article=posts/196-Chaikin-Curve-Generator.html) with linear interpolation for the width to draw the curve.
|
||||
|
||||
```weacmd
|
||||
//spline dirt 5 2
|
||||
//spline glass 3
|
||||
//spline bakedclay:violet 3
|
||||
```
|
||||
|
||||
![An example of what //spline can do.](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/.docs/images/reference/spline.jpeg)
|
||||
|
||||
## Misc
|
||||
<!--
|
||||
|
@ -196,6 +297,8 @@ Floods all connected nodes of the same type starting at _pos1_ with `<replace_no
|
|||
### `//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.
|
||||
|
||||
In other words, creates a wireframe of a box defined by the current selection.
|
||||
|
||||
```weacmd
|
||||
//wbox silver_sandstone
|
||||
//wbox dirt
|
||||
|
@ -300,7 +403,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.
|
||||
|
||||
|
||||
### `//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:
|
||||
|
||||
- `<axis>` is the name of the axis to move the defined region along
|
||||
|
@ -323,6 +426,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`.
|
||||
|
||||
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+ y 10 z 4
|
||||
|
@ -331,12 +436,17 @@ Additionally all the absolute axis names (`x`/`y`/`z`/`-x`/`-y`/`-z`) may also b
|
|||
//copy+ xz 50 front 22
|
||||
//copy+ yx 25
|
||||
//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.
|
||||
|
||||
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+ y 10 z 4
|
||||
|
@ -345,6 +455,8 @@ Identical to [`//copy+`](#copy), but instead moves the defined region instead of
|
|||
//move+ xz 50 front 22
|
||||
//move+ yx 25
|
||||
//move+ -xz-y 10
|
||||
//move+ back 20 aa
|
||||
//move+ -z 45 y 3 airapply
|
||||
```
|
||||
|
||||
|
||||
|
@ -406,6 +518,18 @@ Here are all the above examples together:
|
|||
```
|
||||
|
||||
|
||||
### `//revolve <times> [<pivot_point_number=last_point>]`
|
||||
Makes a given number of copies of the currently defined region (bounded by pos1 and pos2) at a given number of equally spaced points rotated around a given pivot/origin point.
|
||||
|
||||
For example, `//revolve 4` would make rotated copies of the currently defined region at intervals 0° (the source to copy), 90°, 180°, and 270° around the given pivot point.
|
||||
|
||||
`pivot_point_number` is the number of the defined position that should act as the pivot point, or origin for the revolve operation. It defaults to the last position defined. Note that it cannot be pos1 or pos2, as these are used to define the region that should be rotated. Use the [multi-point wand](#multi) to define a position with an index of 3 or more.
|
||||
|
||||
```weacmd
|
||||
//revolve 4
|
||||
//revolve 6 6
|
||||
```
|
||||
|
||||
|
||||
## Terrain
|
||||
<!--
|
||||
|
@ -507,7 +631,7 @@ Currently implemented algorithms:
|
|||
Algorithm | Mode | Description
|
||||
------------|-------|-------------------
|
||||
`snowballs` | 2D | The default - based on [this blog post](https://jobtalle.com/simulating_hydraulic_erosion.html). Simulates snowballs rolling across the terrain, eroding & depositing material. Then runs a 3x3 gaussian kernel over the result (i.e. like the `//conv` / `//smoothadv` command).
|
||||
`river` | 2D | Fills in potholes and lowers pillars using a cellular automata-like algorithm that analyses the height of neighouring columns.
|
||||
`river` | 2D | Fills in potholes and lowers pillars using a cellular automata-like algorithm that analyses the height of neighbouring columns.
|
||||
|
||||
Usage examples:
|
||||
|
||||
|
@ -571,7 +695,7 @@ Applies 2D noise to the terrain in the defined region. Like `//erode`, this comm
|
|||
Parameter | Type | Default Value | Description
|
||||
------------|-----------|---------------|-----------------------
|
||||
algorithm | `string` | perlinmt | The 2D noise algorithm to apply - see below.
|
||||
apply | `string|integer` | 5 | How to apply the noise to the terrain - see below.
|
||||
apply | `string¦integer` | 5 | How to apply the noise to the terrain - see below.
|
||||
scalex | `float` | 1 | The scale of the noise on the x axis.
|
||||
scaley | `float` | 1 | The scale of the noise on the y axis.
|
||||
scalez | `float` | 1 | The scale of the noise on the z axis.
|
||||
|
@ -593,7 +717,7 @@ The following algorithms are currently available:
|
|||
|
||||
Algorithm | Description
|
||||
------------|--------------------------
|
||||
`perlinmt` | **Default**.Perlin noise, backed by Minetest's inbuilt `PerlinNoise` class.
|
||||
`perlinmt` | **Default**. Perlin noise, backed by Minetest's inbuilt `PerlinNoise` class.
|
||||
`perlin` | Perlin noise, backed by a pure Lua perlin noise implementation. Functional, but currently contains artefacts I'm having difficulty tracking down.
|
||||
`sin` | A sine wave created with `math.sin()`.
|
||||
`white` | Random white noise.
|
||||
|
@ -625,7 +749,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.
|
||||
|
||||
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:
|
||||
|
@ -641,9 +765,9 @@ The selection of available brushes is limited at the moment, but see below on ho
|
|||
|
||||
```
|
||||
//sculpt
|
||||
//sculpt default 10 25
|
||||
//sculpt default 25 3
|
||||
//sculpt ellipse
|
||||
//sculpt circle 5 50
|
||||
//sculpt circle 50 3
|
||||
```
|
||||
|
||||
#### Create your own brushes
|
||||
|
@ -812,7 +936,75 @@ Returns the absolute canonical name of a node, given an alias or partial node na
|
|||
███████ ███████ ███████ ███████ ██████ ██ ██ ██████ ██ ████
|
||||
-->
|
||||
|
||||
### `//unmark`
|
||||
Hides the in-game UI that indicates where the current positions and region are located.
|
||||
|
||||
This hides both the WorldEditAdditions *and* the WorldEdit UI if displayed, but does **not** change or remove any points that are registered.
|
||||
|
||||
Should more than 2 points be defined, they are all hidden.
|
||||
|
||||
```weacmd
|
||||
//unmark
|
||||
```
|
||||
|
||||
### `//mark`
|
||||
Shows the in-game UI that indicates where the current positions and region are located once more.
|
||||
|
||||
Should more than 2 points be defined, they are all shown once more.
|
||||
|
||||
Often used after calling [`//unmark`](#unmark)
|
||||
|
||||
```weacmd
|
||||
//mark
|
||||
```
|
||||
|
||||
|
||||
### `//pos1`
|
||||
Sets pos1 to the location of the calling player.
|
||||
|
||||
This is, as with all other WorldEditAdditions commands, seamlessly synchronised with WorldEdit, allowing you to use any combination of WorldEditAdditions and WorldEdit commands and tools without them desynchronising from one another.
|
||||
|
||||
**Aliases:** `//1`
|
||||
|
||||
```weacmd
|
||||
//pos2
|
||||
```
|
||||
|
||||
### `//pos2`
|
||||
Sets pos1 to the location of the calling player.
|
||||
|
||||
This is, as with all other WorldEditAdditions commands, seamlessly synchronised with WorldEdit, allowing you to use any combination of WorldEditAdditions and WorldEdit commands and tools without them desynchronising from one another.
|
||||
|
||||
**Aliases:** `//2`
|
||||
|
||||
```weacmd
|
||||
//pos2
|
||||
```
|
||||
|
||||
### `//pos <index>`
|
||||
Sets position with the given index `<index>` to the location of the calling player.
|
||||
|
||||
Should the index be less than or equal to 2, then as with all other WorldEditAdditions commands, seamlessly synchronised with WorldEdit, allowing you to use any combination of WorldEditAdditions and WorldEdit commands and tools without them desynchronising from one another.
|
||||
|
||||
Should the index be greater than 2, the position will only be registered in WorldEditAdditions, as WorldEdit does not support defining more than 2 points.
|
||||
|
||||
If no index is specified, an error is returned and nothing is done.
|
||||
|
||||
```weacmd
|
||||
//pos 1
|
||||
//pos 3
|
||||
//pos 45
|
||||
//pos 2
|
||||
```
|
||||
|
||||
### `//reset`
|
||||
Clears all positions defined and the defined region.
|
||||
|
||||
This also synchronises with WorldEdit, as all other WorldEditAdditions commands do.
|
||||
|
||||
```weacmd
|
||||
//reset
|
||||
```
|
||||
|
||||
|
||||
### `//scol [<axis1> ] <length>`
|
||||
|
@ -1151,11 +1343,23 @@ This is equivalent to executing the following 3 commands in sequence:
|
|||
Here are some more examples:
|
||||
|
||||
```
|
||||
//for meselamp brick snowblock bamboo:wood do { //multi //maze %% //sshift y 5 }
|
||||
//for meselamp brick snowblock bamboo:wood do //multi //maze %% //sshift y 5
|
||||
//for 5 10 15 do //torus %% 3 dirt xz
|
||||
//for 1 2 3 4 5 do //multi //hollowcylinder y 1 5 meselamp //shift y %%
|
||||
```
|
||||
|
||||
|
||||
### `//listentities`
|
||||
Lists all currently loaded ObjectRefs. Displays their IDs, Names (if possible), and possitions.
|
||||
|
||||
This command is intended for development and modding. You will not normally need to use this command using WorldEditAdditions.
|
||||
|
||||
`//listentities` takes no arguments.
|
||||
|
||||
```
|
||||
//listentities
|
||||
```
|
||||
|
||||
|
||||
## Extras
|
||||
<!--
|
||||
|
@ -1198,10 +1402,12 @@ Prevents the execution of a command if it could potentially affect a large numbe
|
|||
-->
|
||||
|
||||
### Far Wand
|
||||
The far wand (`worldeditadditions:farwand`) is a variant on the traditional WorldEdit wand (`worldedit:wand`). It looks like this: ![A picture of the far wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/master/worldeditadditions_farwand/textures/worldeditadditions_farwand.png)
|
||||
The far wand (`worldeditadditions:farwand`) is a variant on the traditional WorldEdit wand (`worldedit:wand`). It looks like this: ![A picture of the far wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions_farwand/textures/worldeditadditions_farwand.png)
|
||||
|
||||
It functions very similarly to the regular WorldEdit wand, except that it has a _much_ longer range - which can be very useful for working on large-scale terrain for example. It also comes with an associated command to control it.
|
||||
|
||||
Note that punching out the positions **does not unset them**. Use `//reset` to reset the defined region.
|
||||
|
||||
### `//farwand skip_liquid (true|false) | maxdist <number>`
|
||||
This command helps control the behaviour of the [WorldEditAdditions far wand](#far-wand) and [cloud wand](#cloud-wand). Calling it without any arguments shows the current status:
|
||||
|
||||
|
@ -1227,8 +1433,23 @@ You can change the maximum range with the `maxdist` subcommand:
|
|||
Note that the number there isn't in blocks (because hard maths). It is however proportional to the distance the wand will raycast looks for nodes, so a higher value will result in it raycasting further.
|
||||
|
||||
### Cloud Wand
|
||||
The cloud wand (`worldeditadditions:cloudwand`) is a another variant the above _Far Wand_. It looks like this: ![A picture of the far wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/master/worldeditadditions_farwand/textures/worldeditadditions_cloudwand.png)
|
||||
The cloud wand (`worldeditadditions:cloudwand`) is a another variant the above _Far Wand_. It looks like this: ![A picture of the far wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions_farwand/textures/worldeditadditions_cloudwand.png)
|
||||
|
||||
Unlike the other 2 wands, this wand functions in an additive manner. Left-click on a node to expand the currently defined region (creating a new one if one isn't defined already) to include that node. Right click to clear the currently defined region.
|
||||
|
||||
It has the range of the _Far Wand_ mentioned above too, so you can select nodes from a great distance. It also abides by preferences set via the `//farwand` chat command.
|
||||
|
||||
Note that punching out the positions **does not unset them**. Use `//reset` to reset the defined region.
|
||||
|
||||
|
||||
### Multi-Point Wand
|
||||
The third type of wand provided by WorldEditAdditions is completely different, in that it allows you to select up to **999 points** at once! It looks like this: ![A picture of the multi-point wand](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions_farwand/textures/worldeditadditions_multiwand.png)
|
||||
|
||||
It is important to note that (at present) the points selected by this wand **are not compatible with normal points**. This will change in the future, but requires a lot of work to implement.
|
||||
|
||||
It has the following actions:
|
||||
|
||||
- **Left click:** Add a new point
|
||||
- **Right click:** Undo adding a point
|
||||
|
||||
It has the range of the other wands mentioned above though, so you can select nodes from a great distance. It also abides by preferences set via the `//farwand` chat command.
|
||||
|
|
53
README.md
53
README.md
|
@ -1,5 +1,5 @@
|
|||
# ![](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions-64.png) Minetest-WorldEditAdditions
|
||||
![GitHub release (latest by date)](https://img.shields.io/github/v/release/sbrl/Minetest-WorldEditAdditions?color=green&label=latest%20release) [![View the changelog](https://img.shields.io/badge/%F0%9F%93%B0-Changelog-informational)](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/main/CHANGELOG.md) [![ContentDB](https://content.minetest.net/packages/Starbeamrainbowlabs/worldeditadditions/shields/downloads/)](https://content.minetest.net/packages/Starbeamrainbowlabs/worldeditadditions/)
|
||||
# ![](https://raw.githubusercontent.com/sbrl/Minetest-WorldEditAdditions/main/worldeditadditions-64.png) WorldEditAdditions
|
||||
![GitHub release (latest by date)](https://img.shields.io/github/v/release/sbrl/Minetest-WorldEditAdditions?color=green&label=latest%20release) [![View the changelog](https://img.shields.io/badge/%F0%9F%93%B0-Changelog-informational)](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/main/CHANGELOG.md) [![ContentDB](https://content.minetest.net/packages/Starbeamrainbowlabs/worldeditadditions/shields/downloads/)](https://content.minetest.net/packages/Starbeamrainbowlabs/worldeditadditions/) [![Join the official discord server](https://worldeditadditions.mooncarrot.space/img/shield-discord.svg)](https://discord.gg/FzD73kuhsk)
|
||||
|
||||
> Extra tools and commands to extend WorldEdit for Minetest
|
||||
|
||||
|
@ -25,9 +25,12 @@ _(Do you have a cool build that you used WorldEditAdditions to build? [Get in to
|
|||
|
||||
|
||||
## Quick Command Reference
|
||||
The detailed explanations have moved! Check them out [here](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/main/Chat-Command-Reference.md), or click the links below.
|
||||
The detailed explanations have moved! Check them out [here](https://worldeditadditions.mooncarrot.space/Reference/) (or edit at [Chat-Command-Reference.md](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/main/Chat-Command-Reference.md)), or click the links below.
|
||||
|
||||
### Geometry
|
||||
- [`//spline <replace_node> <width_start> [<width_end=width_start> [<steps=3>]]`](https://worldeditadditions.mooncarrot.space/Reference/#spline)
|
||||
- [`//dome+ <radius> <replace_node> [<pointing_dir:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> ...] [h[ollow]]`](https://worldeditadditions.mooncarrot.space/Reference/#dome)
|
||||
- [`//metaball add <radius> | remove <index> | render <replace_node> [<threshold=1>] | list | clear | volume`](https://worldeditadditions.mooncarrot.space/Reference/#metaball)
|
||||
- [`//ellipsoid <rx> <ry> <rz> <node_name> [h[ollow]]`](https://worldeditadditions.mooncarrot.space/Reference/#ellipsoid)
|
||||
- [`//ellipsoid2 <node_name> [h[ollow]]`](https://worldeditadditions.mooncarrot.space/Reference/#ellipsoid2)
|
||||
- [`//hollowellipsoid <rx> <ry> <rz> <node_name>`](https://worldeditadditions.mooncarrot.space/Reference/#hollowellipsoid)
|
||||
|
@ -39,10 +42,14 @@ The detailed explanations have moved! Check them out [here](https://github.com/s
|
|||
- [`//maze <replace_node> [<path_length> [<path_width> [<seed>]]]`](https://worldeditadditions.mooncarrot.space/Reference/#maze)
|
||||
- [`//maze3d <replace_node> [<path_length> [<path_width> [<path_depth> [<seed>]]]]`](https://worldeditadditions.mooncarrot.space/Reference/#maze3d)
|
||||
- [`//spiral2 [<circle|square>] [<replace_node=dirt> [<interval=3> [<acceleration=0>] ] ]`](https://worldeditadditions.mooncarrot.space/Reference/#spiral2)
|
||||
- [`//wbox <replace_node>`](https://worldeditadditions.mooncarrot.space/Reference/#wbox)
|
||||
- [`//wcompass <replace_node> [<bead_node>]`](https://worldeditadditions.mooncarrot.space/Reference/#wcompass)
|
||||
- [`//wcorner <replace_node>`](https://worldeditadditions.mooncarrot.space/Reference/#wcorner)
|
||||
|
||||
### Misc
|
||||
- [`//copy+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]`](https://worldeditadditions.mooncarrot.space/Reference/#copy+)
|
||||
- [`//move+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]`](https://worldeditadditions.mooncarrot.space/Reference/#copy+)
|
||||
- [`//revolve <times> [<pivot_point_number=last_point>]`](https://worldeditadditions.mooncarrot.space/Reference/#revolve)
|
||||
- [`//copy+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]`](https://worldeditadditions.mooncarrot.space/Reference/#copy)
|
||||
- [`//move+ <axis:x|y|z|-x|-y|-z|?|front|back|left|right|up|down> <count> [<axis> <count> [...]]`](https://worldeditadditions.mooncarrot.space/Reference/#move)
|
||||
- [`//replacemix <target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ....`](https://worldeditadditions.mooncarrot.space/Reference/#replacemix)
|
||||
- [`//floodfill [<replace_node> [<radius>]]`](https://worldeditadditions.mooncarrot.space/Reference/#floodfill)
|
||||
- [`//scale <axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>]]`](https://worldeditadditions.mooncarrot.space/Reference/#scale) **experimental**
|
||||
|
@ -54,7 +61,7 @@ The detailed explanations have moved! Check them out [here](https://github.com/s
|
|||
- [`//convolve <kernel> [<width>[,<height>]] [<sigma>]`](https://worldeditadditions.mooncarrot.space/Reference/#convolve)
|
||||
- [`//erode [<snowballs|river> [<key_1> [<value_1>]] [<key_2> [<value_2>]] ...]`](https://worldeditadditions.mooncarrot.space/Reference/#erode) **experimental**
|
||||
- [`//noise2d [<key_1> [<value_1>]] [<key_2> [<value_2>]] ...]`](https://worldeditadditions.mooncarrot.space/Reference/#noise2d)
|
||||
- [`//sculpt [<brush_name=default> [<height=5> [<brush_size=10>]]]`](https://worldeditadditions.mooncarrot.space/Reference/#sculpt)
|
||||
- [`//sculpt [[<brush_name=default> [<brush_size=8> [<height=1>]]]`](https://worldeditadditions.mooncarrot.space/Reference/#sculpt)
|
||||
- [`//sculptlist [preview]`](https://worldeditadditions.mooncarrot.space/Reference/#sculptlist)
|
||||
|
||||
### Flora
|
||||
|
@ -77,6 +84,14 @@ The detailed explanations have moved! Check them out [here](https://github.com/s
|
|||
- [`//sstack`](https://worldeditadditions.mooncarrot.space/Reference/#sstack)
|
||||
- [`//spush`](https://worldeditadditions.mooncarrot.space/Reference/#spush)
|
||||
- [`//spop`](https://worldeditadditions.mooncarrot.space/Reference/#spop)
|
||||
- [`//sshift <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]`](https://worldeditadditions.mooncarrot.space/Reference/#sshift)
|
||||
- [`//sfactor <mode:grow|shrink|average> <factor> [<target=xz>]`](https://worldeditadditions.mooncarrot.space/Reference/#sfactor)
|
||||
- [`//pos <index>`](https://worldeditadditions.mooncarrot.space/Reference/#pos)
|
||||
- [`//pos1`](https://worldeditadditions.mooncarrot.space/Reference/#pos1)
|
||||
- [`//pos2`](https://worldeditadditions.mooncarrot.space/Reference/#pos2)
|
||||
- [`//mark`](https://worldeditadditions.mooncarrot.space/Reference/#mark)
|
||||
- [`//unmark`](https://worldeditadditions.mooncarrot.space/Reference/#unmark)
|
||||
- [`//reset`](https://worldeditadditions.mooncarrot.space/Reference/#reset)
|
||||
|
||||
### Measure
|
||||
- [`//mface`](https://worldeditadditions.mooncarrot.space/Reference/#mface) _(new in v1.13)_
|
||||
|
@ -91,6 +106,7 @@ The detailed explanations have moved! Check them out [here](https://github.com/s
|
|||
- [`//ellipsoidapply <command_name> <args>`](https://worldeditadditions.mooncarrot.space/Reference/#ellipsoidapply) _(new in v1.9)_
|
||||
- [`//airapply <command_name> <args>`](https://worldeditadditions.mooncarrot.space/Reference/#airapply) _(new in v1.9)_
|
||||
- [`//noiseapply2d <threshold> <scale> <command_name> <args>`](https://worldeditadditions.mooncarrot.space/Reference/#noiseapply2d) _(new in v1.13)_
|
||||
- [`//for <value1> <value2> <value3>... do //<command> <arg> %% <arg>`](https://worldeditadditions.mooncarrot.space/Reference/#for) _(new in v1.13)_
|
||||
|
||||
### Extras
|
||||
- [`//y`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/main/Chat-Command-Reference.md#y)
|
||||
|
@ -122,6 +138,8 @@ cd WorldEditAdditions
|
|||
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:
|
||||
|
||||
```bash
|
||||
|
@ -144,13 +162,34 @@ Please update to v1.8+. There was a bug in earlier versions that caused a race c
|
|||
### Why don't the [moretrees](https://content.minetest.net/packages/VanessaE/moretrees/) trees work with `//forest`?
|
||||
As far as I can tell, the saplings provided by the [`moretrees` mod](https://content.minetest.net/packages/VanessaE/moretrees/) don't properly interact with bonemeal from the [bonemeal mod](https://content.minetest.net/packages/TenPlus1/bonemeal/), which WorldEditAdditions uses to grow trees. As far as I can tell WorldEditAdditions has everything in place needed to support them, but until applying bonemeal to `moretrees` saplings results in a tree rather than waiting for one to grow over time, WorldEditAdditions will always fail to place trees provided by the `moretrees` mod, unfortunately.
|
||||
|
||||
### The region markers look weird!
|
||||
We've implemented a custom replacement for the WorldEdit region markers that supports selecting more than 2 points. This new WorldEditAdditions positioning system synchronises with WorldEdit's positioning system, so you can use both WorldEdit and WorldEditAdditions tools and commands interchangeably and they will seamlessly sync with each other.
|
||||
|
||||
The only side effect of this is that WorldEdit commands such as `//shift` are not aware of the new WorldEditAdditions positioning system, so you may encounter a situation where both WorldEdit and WorldEditAdditions region markers may display until you use another WorldEditAdditions command or tool to update them. This will be fixed in time as more commands and tools are implemented.
|
||||
|
||||
### The position markers disappear when far way
|
||||
This is a limitation of Minetest. You can workaround it though by setting [`active_block_range`](https://github.com/minetest/minetest/blob/5.6.1/minetest.conf.example#L2868) to a higher value - though be aware this also affects ABMs and other entities as well, so it can cause server lag.
|
||||
|
||||
The new positioning system now partially rectifies this issue with the region marker walls by creating a grid of entities instead of a single entity, such that a portion of them are more likely to be in range.
|
||||
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome! Please state in your pull request(s) that you release your contribution under the _Mozilla Public License 2.0_.
|
||||
|
||||
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
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
local Vector3 = dofile("worldeditadditions/utils/vector3.lua")
|
||||
|
||||
local function make_grid(size)
|
||||
local grid = {}
|
||||
|
||||
for y=1,size.y do
|
||||
for x=1,size.x do
|
||||
grid[(size.x * y) + x] = 0
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
size = size,
|
||||
data = grid
|
||||
}
|
||||
end
|
||||
|
||||
local function index(grid, pos)
|
||||
return (grid.size.x * pos.y) + pos.x
|
||||
end
|
||||
|
||||
local function grid_to_string(grid)
|
||||
local result = {}
|
||||
for y=1,grid.size.y do
|
||||
for x=1,grid.size.x do
|
||||
local i = index(grid, Vector3.new(x, y))
|
||||
if grid.data[i] == 0 then
|
||||
table.insert(result, "#")
|
||||
elseif grid.data[i] == 1 then
|
||||
table.insert(result, " ")
|
||||
elseif grid.data[i] < 10 then
|
||||
table.insert(result, tostring(grid.data[i]))
|
||||
else
|
||||
table.insert(result, "?")
|
||||
end
|
||||
end
|
||||
table.insert(result, "\n")
|
||||
end
|
||||
|
||||
return table.concat(result, "")
|
||||
end
|
||||
|
||||
--- Draws a line using Bresenham's algorithm in 2d.
|
||||
-- Implemented ported from https://github.com/anushaihalapathirana/Bresenham-line-drawing-algorithm/blob/f51f153459a1656bfb1f433edc75e6644a052bb2/src/index.js
|
||||
-- @param grid Grid The grid to draw on.
|
||||
-- @param pos1 Vector3 The starting point of the line.
|
||||
-- @param pos2 Vector3 The ending point of the line.
|
||||
-- @param value number The number to fill in grid cells we draw in.
|
||||
-- @returns void
|
||||
local function draw_line_2d(grid, pos1, pos2, value)
|
||||
if not value then value = 1 end
|
||||
|
||||
if pos1 == pos2 then
|
||||
grid.data[index(grid, pos1)] = value
|
||||
return
|
||||
end
|
||||
|
||||
grid.data[index(grid, pos1)] = value -- plot pos1
|
||||
local delta = pos2 - pos1
|
||||
local absdelta = delta:abs()
|
||||
local delta_error = (2 * absdelta.y) - absdelta.x -- d
|
||||
if absdelta.x <= absdelta.y then delta_error = (2 * absdelta.x) - absdelta.y end
|
||||
|
||||
local pos_current = pos1:clone()
|
||||
local steps = 0
|
||||
|
||||
local limit = absdelta.y
|
||||
if absdelta.x > absdelta.y then limit = absdelta.x end
|
||||
|
||||
for _=1,limit do
|
||||
local do_x = false
|
||||
local do_y = false
|
||||
if absdelta.x > absdelta.y then
|
||||
do_x = true
|
||||
if delta_error < 0 then
|
||||
delta_error = delta_error + 2*absdelta.y
|
||||
else
|
||||
do_y = true
|
||||
delta_error = delta_error + (2*absdelta.y - 2*absdelta.x)
|
||||
end
|
||||
else
|
||||
do_y = true
|
||||
if delta_error < 0 then
|
||||
delta_error = delta_error + 2*absdelta.x
|
||||
else
|
||||
do_x = true
|
||||
delta_error = delta_error + (2*absdelta.x) - (2*absdelta.y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if do_x then
|
||||
local acc = 1
|
||||
if delta.x < 0 then acc = -1 end
|
||||
pos_current.x = pos_current.x + acc
|
||||
end
|
||||
if do_y then
|
||||
local acc = 1
|
||||
if delta.y < 0 then acc = -1 end
|
||||
pos_current.y = pos_current.y + acc
|
||||
end
|
||||
|
||||
local i = index(grid, pos_current)
|
||||
grid.data[i] = value
|
||||
steps = steps + 1
|
||||
end
|
||||
end
|
||||
|
||||
local size = Vector3.new(120, 40)
|
||||
local grid = make_grid(size)
|
||||
|
||||
|
||||
|
||||
-- draw_line_2d(grid, Vector3.new(10, 10), Vector3.new(40, 10))
|
||||
-- draw_line_2d(grid, Vector3.new(70, 10), Vector3.new(70, 10))
|
||||
draw_line_2d(grid, Vector3.new(75, 10), Vector3.new(75, 40))
|
||||
draw_line_2d(grid, Vector3.new(80, 40), Vector3.new(80, 10))
|
||||
|
||||
-- local centre = Vector3.new(25, 25)
|
||||
-- draw_line_2d(grid, centre, Vector3.new(0, 0))
|
||||
-- draw_line_2d(grid, centre, Vector3.new(5, 0))
|
||||
-- draw_line_2d(grid, centre, Vector3.new(10, 0))
|
||||
-- draw_line_2d(grid, centre, Vector3.new(15, 0))
|
||||
-- draw_line_2d(grid, centre, Vector3.new(20, 0))
|
||||
-- draw_line_2d(grid, centre, Vector3.new(25, 0))
|
||||
-- draw_line_2d(grid, centre, Vector3.new(30, 0), 3)
|
||||
|
||||
for i=1,9 do
|
||||
print("LINE", i)
|
||||
local pos1 = Vector3.new(
|
||||
math.random(0, size.x),
|
||||
math.random(0, size.y)
|
||||
)
|
||||
local pos2 = Vector3.new(
|
||||
math.random(0, size.x),
|
||||
math.random(0, size.y)
|
||||
)
|
||||
draw_line_2d(grid, pos1, pos2)
|
||||
end
|
||||
-- draw_line_2d(grid,
|
||||
-- Vector3.new(3, 4),
|
||||
-- Vector3.new(8, 9)
|
||||
-- )
|
||||
-- draw_line_2d(grid,
|
||||
-- Vector3.new(10, 9),
|
||||
-- Vector3.new(15, 4)
|
||||
-- )
|
||||
-- draw_line_2d(grid,
|
||||
-- Vector3.new(20, 10),
|
||||
-- Vector3.new(40, 20)
|
||||
-- )
|
||||
-- draw_line_2d(grid,
|
||||
-- Vector3.new(10, 9),
|
||||
-- Vector3.new(55, 4)
|
||||
-- )
|
||||
|
||||
print(grid_to_string(grid))
|
7
build.sh
7
build.sh
|
@ -2,10 +2,10 @@
|
|||
set -e;
|
||||
|
||||
# current_branch="$(git rev-parse --abbrev-ref HEAD)";
|
||||
is_main="$(git branch --contains HEAD | awk '/HEAD/ { next } /main/ { print $1 }')";
|
||||
is_main="$(git branch --contains HEAD | awk '/HEAD/ { next } /dev/ { print $1 }')";
|
||||
|
||||
if [[ "${1}" == "ci" ]] && [[ ! -z "${is_main}" ]]; then
|
||||
echo "Skipping build, because this commit does not appear to be on the 'main' branch, and we only deploy commits on the 'main' branch.";
|
||||
echo "Skipping build, because this commit does not appear to be on the 'dev' branch, and we only deploy commits on the 'main' branch.";
|
||||
fi
|
||||
|
||||
# ██████ ██ ██████ ██ ██ ██ ██ ██████
|
||||
|
@ -91,6 +91,9 @@ npm install;
|
|||
|
||||
log_msg "Building website";
|
||||
|
||||
# This causes the eleventy docs site to minify stuff
|
||||
# Note that this is NOT before the npm install, as npm doesn't install everything if we do that
|
||||
export NODE_ENV=production;
|
||||
npm run build;
|
||||
|
||||
if [[ ! -d "_site" ]]; then
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
name = worldeditadditions
|
||||
description = Extra tools and commands to extend WorldEdit. Currently has over 22 commands!
|
||||
|
||||
depends = worldedit
|
||||
optional_depends = bonemeal,cool_trees,default,moretrees,ethereal
|
||||
min_minetest_version = 5.1
|
||||
min_minetest_version = 5.2
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Rotate a given point around an origin point in 3d space.
|
||||
* NOTE: This function is not as intuitive as it sounds.
|
||||
* A whiteboard and a head for mathematics is recommended before using this
|
||||
* function.
|
||||
* @source GitHub Copilot
|
||||
* @warning Not completely tested! Pending a thorough evaluation.
|
||||
* @param {Vector3} origin The origin point to rotate around
|
||||
* @param {Vector3} point The point to rotate.
|
||||
* @param {Number} x 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 {Number} z Rotate this much around the Z axis (xy plane), in radians.
|
||||
* @return {Vector3} The rotated point.
|
||||
*/
|
||||
function rotate_point_3d(origin, point, x, y, z)
|
||||
{
|
||||
point = [...point];
|
||||
point[0] = point[0] - origin[0];
|
||||
point[1] = point[1] - origin[1];
|
||||
point[2] = point[2] - origin[2];
|
||||
|
||||
// rotate around x
|
||||
var x1 = point[0];
|
||||
var y1 = point[1] * Math.cos(x) - point[2] * Math.sin(x);
|
||||
var z1 = point[1] * Math.sin(x) + point[2] * Math.cos(x);
|
||||
|
||||
// rotate around y
|
||||
var x2 = x1 * Math.cos(y) + z1 * Math.sin(y);
|
||||
var y2 = y1;
|
||||
var z2 = -x1 * Math.sin(y) + z1 * Math.cos(y);
|
||||
|
||||
// rotate around z
|
||||
var x3 = x2 * Math.cos(z) - y2 * Math.sin(z);
|
||||
var y3 = x2 * Math.sin(z) + y2 * Math.cos(z);
|
||||
var z3 = z2;
|
||||
|
||||
return [x3+origin[0], y3+origin[1], z3+origin[2]];
|
||||
}
|
||||
|
||||
function deg_to_rad(deg)
|
||||
{
|
||||
return deg * Math.PI / 180;
|
||||
}
|
||||
|
||||
function point_to_string(point, decimal_places=3)
|
||||
{
|
||||
return "(x " + point[0].toFixed(decimal_places) + ", y " + point[1].toFixed(decimal_places) + ", z " + point[2].toFixed(decimal_places) + ")";
|
||||
}
|
||||
|
||||
function do_test(origin, point, ...args) {
|
||||
const result = rotate_point_3d(origin, point, ...args);
|
||||
console.log(`ORIGIN`, point_to_string(origin), `\tPOINT`, point_to_string(point), `\tRESULT`, point_to_string(result));
|
||||
}
|
||||
|
||||
do_test(
|
||||
[1, 0, 0],
|
||||
[5, 0, 0],
|
||||
deg_to_rad(0), // rotate around x axis = yz plane
|
||||
deg_to_rad(0), // rotate around y axis = xz plane
|
||||
deg_to_rad(180) // rotate around z axis = xy plane
|
||||
);
|
||||
do_test(
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
deg_to_rad(0), // rotate around x axis = yz plane
|
||||
deg_to_rad(0), // rotate around y axis = xz plane
|
||||
deg_to_rad(180) // rotate around z axis = xy plane
|
||||
);
|
||||
do_test(
|
||||
[10, 100, 0],
|
||||
[0, 0, 0],
|
||||
deg_to_rad(0), // rotate around x axis = yz plane
|
||||
deg_to_rad(0), // rotate around y axis = xz plane
|
||||
deg_to_rad(180) // rotate around z axis = xy plane
|
||||
);
|
|
@ -0,0 +1,7 @@
|
|||
# https://github.com/minetest/minetest/blob/master/builtin/settingtypes.txt#L1
|
||||
|
||||
# Will WorldEditAdditions override commands?
|
||||
worldeditadditions.override_commands (Override WorldEdit Commands) bool false
|
||||
|
||||
# Will WorldEditAdditions wands be craftable?
|
||||
worldeditadditions.wand_craft (Craftable Wands) bool false
|
24
tests.sh
24
tests.sh
|
@ -31,7 +31,15 @@ check_command luarocks;
|
|||
luarocks_root="${PWD}/.luarocks";
|
||||
|
||||
# Setup the lua module path
|
||||
eval "$(luarocks --tree "${luarocks_root}" path)";
|
||||
if [[ "${OSTYPE}" == *"msys"* ]]; then
|
||||
PATH="$(luarocks --tree "${luarocks_root}" path --lr-bin):${PATH}";
|
||||
LUA_PATH="$(luarocks --tree "${luarocks_root}" path --lr-path);init.lua;./?.lua;${LUA_PATH}";
|
||||
LUA_CPATH="$(luarocks --tree "${luarocks_root}" path --lr-cpath);./?.so;${LUA_CPATH}";
|
||||
else
|
||||
eval "$(luarocks --tree "${luarocks_root}" path)";
|
||||
fi
|
||||
|
||||
export PATH LUA_PATH LUA_CPATH;
|
||||
|
||||
mode="${1}"; if [[ "$#" -gt 0 ]]; then shift; fi
|
||||
|
||||
|
@ -46,7 +54,15 @@ run_syntax_check() {
|
|||
}
|
||||
|
||||
run_test() {
|
||||
.luarocks/bin/busted --no-auto-insulate --pattern ".test.lua" .tests;
|
||||
busted_path=".luarocks/bin/busted";
|
||||
if [[ ! -r "${busted_path}" ]]; then
|
||||
busted_path=".luarocks/bin/busted.bat";
|
||||
fi
|
||||
if [[ ! -r "${busted_path}" ]]; then
|
||||
echo "Error: Failed to find busted at .luarocks/bin/busted or .luarocks/bin/busted.bat" >&2;
|
||||
exit 1;
|
||||
fi
|
||||
"${busted_path}" --no-auto-insulate --pattern ".test.lua" .tests;
|
||||
}
|
||||
|
||||
case "${mode}" in
|
||||
|
@ -62,10 +78,6 @@ case "${mode}" in
|
|||
run_test;
|
||||
;;
|
||||
|
||||
busted )
|
||||
.luarocks/bin/busted "${@}";
|
||||
;;
|
||||
|
||||
* )
|
||||
echo -e "Usage:
|
||||
path/to/run.sh setup # Setup to run the tests
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
worldedit
|
||||
bonemeal?
|
||||
cool_trees?
|
||||
default?
|
||||
moretrees?
|
||||
ethereal?
|
|
@ -1,7 +1,7 @@
|
|||
--- WorldEditAdditions
|
||||
-- @module worldeditadditions
|
||||
-- @release 0.1
|
||||
-- @copyright 2018 Starbeamrainbowlabs
|
||||
-- @namespace worldeditadditions
|
||||
-- @release 1.14.5
|
||||
-- @copyright 2023 Starbeamrainbowlabs
|
||||
-- @license Mozilla Public License, 2.0
|
||||
-- @author Starbeamrainbowlabs
|
||||
|
||||
|
@ -9,35 +9,6 @@ worldeditadditions = {}
|
|||
local wea = worldeditadditions
|
||||
wea.modpath = minetest.get_modpath("worldeditadditions")
|
||||
|
||||
wea.Set = dofile(wea.modpath.."/utils/set.lua")
|
||||
|
||||
wea.Vector3 = dofile(wea.modpath.."/utils/vector3.lua")
|
||||
wea.Mesh, wea.Face = dofile(wea.modpath.."/utils/mesh.lua")
|
||||
|
||||
wea.Queue = dofile(wea.modpath.."/utils/queue.lua")
|
||||
wea.LRU = dofile(wea.modpath.."/utils/lru.lua")
|
||||
wea.inspect = dofile(wea.modpath.."/utils/inspect.lua")
|
||||
|
||||
-- I/O compatibility layer
|
||||
wea.io = dofile(wea.modpath.."/utils/io.lua")
|
||||
|
||||
wea.bit = dofile(wea.modpath.."/utils/bit.lua")
|
||||
|
||||
wea.terrain = dofile(wea.modpath.."/utils/terrain/init.lua")
|
||||
|
||||
dofile(wea.modpath.."/utils/vector.lua")
|
||||
dofile(wea.modpath.."/utils/strings/init.lua")
|
||||
dofile(wea.modpath.."/utils/format/init.lua")
|
||||
dofile(wea.modpath.."/utils/parse/init.lua")
|
||||
dofile(wea.modpath.."/utils/tables/init.lua")
|
||||
|
||||
dofile(wea.modpath.."/utils/numbers.lua")
|
||||
dofile(wea.modpath.."/utils/nodes.lua")
|
||||
dofile(wea.modpath.."/utils/node_identification.lua")
|
||||
|
||||
dofile(wea.modpath.."/utils/raycast_adv.lua") -- For the farwand
|
||||
dofile(wea.modpath.."/utils/player.lua") -- Player info functions
|
||||
|
||||
dofile(wea.modpath.."/lib/compat/saplingnames.lua")
|
||||
|
||||
dofile(wea.modpath.."/lib/floodfill.lua")
|
||||
|
@ -58,10 +29,14 @@ dofile(wea.modpath.."/lib/scale_down.lua")
|
|||
dofile(wea.modpath.."/lib/scale.lua")
|
||||
dofile(wea.modpath.."/lib/spiral_square.lua")
|
||||
dofile(wea.modpath.."/lib/spiral_circle.lua")
|
||||
dofile(wea.modpath.."/lib/dome.lua")
|
||||
dofile(wea.modpath.."/lib/spline.lua")
|
||||
dofile(wea.modpath.."/lib/revolve.lua")
|
||||
dofile(wea.modpath.."/lib/conv/conv.lua")
|
||||
dofile(wea.modpath.."/lib/erode/erode.lua")
|
||||
dofile(wea.modpath.."/lib/noise/init.lua")
|
||||
wea.sculpt = dofile(wea.modpath.."/lib/sculpt/init.lua")
|
||||
wea.sculpt = dofile(wea.modpath.."/lib/sculpt/init.lua")
|
||||
wea.metaballs = dofile(wea.modpath.."/lib/metaballs/init.lua")
|
||||
|
||||
dofile(wea.modpath.."/lib/copy.lua")
|
||||
dofile(wea.modpath.."/lib/move.lua")
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- █████ ██ ██████
|
||||
-- ██ ██ ██ ██ ██
|
||||
-- ███████ ██ ██████
|
||||
|
@ -10,24 +13,24 @@
|
|||
-- ██ ██ ██ ██ ██ ██
|
||||
-- ██ ██ ██ ██ ███████ ██
|
||||
|
||||
--- 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
|
||||
-- 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} pos2 The 2nd positioon defining the region boundary
|
||||
-- @param {Function} func The function to call that performs the action in question. It is expected that the given function will accept no arguments.
|
||||
function worldeditadditions.airapply(pos1, pos2, func)
|
||||
local time_taken_all = worldeditadditions.get_ms_time()
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local time_taken_all = wea_c.get_ms_time()
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
local manip_before, area_before = worldedit.manip_helpers.init(pos1, pos2)
|
||||
local data_before = manip_before:get_data()
|
||||
|
||||
local time_taken_fn = worldeditadditions.get_ms_time()
|
||||
local time_taken_fn = wea_c.get_ms_time()
|
||||
func()
|
||||
time_taken_fn = worldeditadditions.get_ms_time() - time_taken_fn
|
||||
time_taken_fn = wea_c.get_ms_time() - time_taken_fn
|
||||
|
||||
local manip_after, area_after = worldedit.manip_helpers.init(pos1, pos2)
|
||||
local data_after = manip_after:get_data()
|
||||
|
@ -37,7 +40,7 @@ function worldeditadditions.airapply(pos1, pos2, func)
|
|||
for x = pos2.x, pos1.x, -1 do
|
||||
local i_before = area_before:index(x, y, z)
|
||||
local i_after = area_after:index(x, y, z)
|
||||
local old_is_airlike = worldeditadditions.is_airlike(data_before[i_before])
|
||||
local old_is_airlike = wea_c.is_airlike(data_before[i_before])
|
||||
|
||||
-- Roll everything that replaces nodes that aren't airlike
|
||||
if not old_is_airlike then
|
||||
|
@ -52,6 +55,6 @@ function worldeditadditions.airapply(pos1, pos2, func)
|
|||
worldedit.manip_helpers.finish(manip_after, data_after)
|
||||
|
||||
|
||||
time_taken_all = worldeditadditions.get_ms_time() - time_taken_all
|
||||
time_taken_all = wea_c.get_ms_time() - time_taken_all
|
||||
return true, { all = time_taken_all, fn = time_taken_fn }
|
||||
end
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
--- Bonemeal command.
|
||||
-- Applies bonemeal to all notes
|
||||
-- @module worldeditadditions.overlay
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- strength The strength to apply - see bonemeal:on_use
|
||||
-- chance Positive integer that represents the chance bonemealing will occur
|
||||
--- Bonemeal command.
|
||||
-- Applies bonemeal to all nodes with an air bloc above then.
|
||||
-- @param strength The strength to apply - see bonemeal:on_use
|
||||
-- @param chance Positive integer that represents the chance bonemealing will occur
|
||||
-- @returns bool,number,number 1. Whether the command succeeded or not.
|
||||
-- 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)
|
||||
if not nodename_list then nodename_list = {} end
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
-- This command requires the bonemeal mod to be installed
|
||||
|
@ -15,7 +19,7 @@ function worldeditadditions.bonemeal(pos1, pos2, strength, chance, nodename_list
|
|||
return false, "Bonemeal mod not loaded"
|
||||
end
|
||||
|
||||
local node_list = worldeditadditions.table.map(nodename_list, function(nodename)
|
||||
local node_list = wea_c.table.map(nodename_list, function(nodename)
|
||||
return minetest.get_content_id(nodename)
|
||||
end)
|
||||
local node_list_count = #nodename_list
|
||||
|
@ -34,9 +38,9 @@ function worldeditadditions.bonemeal(pos1, pos2, strength, chance, nodename_list
|
|||
for x = pos2.x, pos1.x, -1 do
|
||||
for y = pos2.y, pos1.y, -1 do
|
||||
local i = area:index(x, y, z)
|
||||
if not worldeditadditions.is_airlike(data[i]) then
|
||||
if not wea_c.is_airlike(data[i]) then
|
||||
local should_bonemeal = true
|
||||
if node_list_count > 0 and not worldeditadditions.table.contains(node_list, data[i]) then
|
||||
if node_list_count > 0 and not wea_c.table.contains(node_list, data[i]) then
|
||||
should_bonemeal = false
|
||||
end
|
||||
|
||||
|
@ -45,7 +49,7 @@ function worldeditadditions.bonemeal(pos1, pos2, strength, chance, nodename_list
|
|||
|
||||
if should_bonemeal and math.random(0, chance - 1) == 0 then
|
||||
bonemeal:on_use(
|
||||
{ x = x, y = y, z = z },
|
||||
Vector3.new(x, y, z),
|
||||
strength,
|
||||
nil
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
--[[
|
||||
This file contains sapling alias definitions for a number of different mods.
|
||||
If your mod is not listed here, please open a pull request to add it :-)
|
||||
|
@ -7,15 +8,16 @@ This mod's repository can be found here: https://github.com/sbrl/Minetest-WorldE
|
|||
Adding support for your mod is a 2 step process:
|
||||
|
||||
1. Update this file with your definitions
|
||||
2. Update depends.txt to add a soft dependency on your mod. Find that file here: https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/worldeditadditions/depends.txt - for example, add something line "my_awesome_mod?" (note the question mark at the end) on a new line(without quotes).
|
||||
2. Update depends.txt to add a soft dependency on your mod. Find that file here: https://github.com/sbrl/Minetest-WorldEditAdditions/blob/main/worldeditadditions/depends.txt - for example, add something line "my_awesome_mod?" (note the question mark at the end) on a new line(without quotes).
|
||||
|
||||
Alternatively, you can register support in your mod directly. Do that by adding a soft dependency on worldeditadditions, and then calling worldeditadditions.normalise_saplingname if worldeditadditions is loaded.
|
||||
Alternatively, you can register support in your mod directly. Do that by adding a soft dependency on worldeditadditions_core, and then calling worldeditadditions_core.normalise_saplingname if worldeditadditions is loaded.
|
||||
]]--
|
||||
|
||||
-- worldeditadditions.register_sapling_alias("")
|
||||
-- TODO: MOVE register_sapling_alias & register_sapling_alias_many back to worldeditadditions from worldeditadditions_core!!!!
|
||||
-- wea_c.register_sapling_alias("")
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
worldeditadditions.register_sapling_alias_many({
|
||||
wea_c.register_sapling_alias_many({
|
||||
{ "default:sapling", "apple" },
|
||||
{ "default:bush_sapling", "bush" },
|
||||
{ "default:pine_sapling", "pine" },
|
||||
|
@ -31,27 +33,27 @@ if minetest.get_modpath("default") then
|
|||
end
|
||||
|
||||
if minetest.get_modpath("moretrees") then
|
||||
worldeditadditions.register_sapling_alias_many({
|
||||
{ "moretrees:spruce_sapling_ongen", "spruce" },
|
||||
{ "moretrees:rubber_tree_sapling_ongen", "rubber" },
|
||||
{ "moretrees:beech_sapling_ongen", "beech" },
|
||||
{ "moretrees:jungletree_sapling_ongen", "jungle_moretrees" },
|
||||
{ "moretrees:fir_sapling_ongen", "fir" },
|
||||
{ "moretrees:willow_sapling_ongen", "willow_moretrees" },
|
||||
{ "moretrees:poplar_sapling_ongen", "poplar" },
|
||||
{ "moretrees:poplar_small_sapling_ongen", "poplar_small" },
|
||||
{ "moretrees:apple_tree_sapling_ongen", "apple_moretrees" },
|
||||
{ "moretrees:birch_sapling_ongen", "birch_moretrees" },
|
||||
{ "moretrees:palm_sapling_ongen", "palm_moretrees" },
|
||||
{ "moretrees:date_palm_sapling_ongen", "palm_date" },
|
||||
{ "moretrees:sequoia_sapling_ongen", "sequoia" },
|
||||
{ "moretrees:oak_sapling_ongen", "oak_moretrees" },
|
||||
{ "moretrees:cedar_sapling_ongen", "cedar" }
|
||||
wea_c.register_sapling_alias_many({
|
||||
{ "moretrees:spruce_sapling", "spruce" },
|
||||
{ "moretrees:rubber_tree_sapling", "rubber" },
|
||||
{ "moretrees:beech_sapling", "beech" },
|
||||
{ "moretrees:jungletree_sapling", "jungle_moretrees" },
|
||||
{ "moretrees:fir_sapling", "fir" },
|
||||
{ "moretrees:willow_sapling", "willow_moretrees" },
|
||||
{ "moretrees:poplar_sapling", "poplar" },
|
||||
{ "moretrees:poplar_small_sapling", "poplar_small" },
|
||||
{ "moretrees:apple_tree_sapling", "apple_moretrees" },
|
||||
{ "moretrees:birch_sapling", "birch_moretrees" },
|
||||
{ "moretrees:palm_sapling", "palm_moretrees" },
|
||||
{ "moretrees:date_palm_sapling", "palm_date" },
|
||||
{ "moretrees:sequoia_sapling", "sequoia" },
|
||||
{ "moretrees:oak_sapling", "oak_moretrees" },
|
||||
{ "moretrees:cedar_sapling", "cedar" }
|
||||
})
|
||||
end
|
||||
|
||||
if minetest.get_modpath("ethereal") then
|
||||
worldeditadditions.register_sapling_alias_many({
|
||||
wea_c.register_sapling_alias_many({
|
||||
{ "ethereal:mushroom_sapling", "mushroom" },
|
||||
{ "ethereal:sakura_sapling", "sakura" },
|
||||
{ "ethereal:birch_sapling", "birch" },
|
||||
|
@ -75,56 +77,56 @@ end
|
|||
-- ██████ ██████ ██████ ███████ ███████ ██ ██ ██ ███████ ███████ ███████
|
||||
|
||||
if minetest.get_modpath("lemontree") then
|
||||
worldeditadditions.register_sapling_alias("lemontree:sapling", "lemon")
|
||||
wea_c.register_sapling_alias("lemontree:sapling", "lemon")
|
||||
end
|
||||
if minetest.get_modpath("pineapple") then
|
||||
worldeditadditions.register_sapling_alias("pineapple:sapling", "pineapple")
|
||||
wea_c.register_sapling_alias("pineapple:sapling", "pineapple")
|
||||
end
|
||||
if minetest.get_modpath("baldcypress") then
|
||||
worldeditadditions.register_sapling_alias("baldcypress:sapling", "baldcypress")
|
||||
wea_c.register_sapling_alias("baldcypress:sapling", "baldcypress")
|
||||
end
|
||||
if minetest.get_modpath("bamboo") then
|
||||
worldeditadditions.register_sapling_alias("bamboo:sprout", "bamboo")
|
||||
wea_c.register_sapling_alias("bamboo:sprout", "bamboo")
|
||||
end
|
||||
if minetest.get_modpath("birch") then
|
||||
worldeditadditions.register_sapling_alias("birch:sapling", "birch")
|
||||
wea_c.register_sapling_alias("birch:sapling", "birch")
|
||||
end
|
||||
if minetest.get_modpath("cherrytree") then
|
||||
worldeditadditions.register_sapling_alias("cherrytree:sapling", "cherry")
|
||||
wea_c.register_sapling_alias("cherrytree:sapling", "cherry")
|
||||
end
|
||||
if minetest.get_modpath("clementinetree") then
|
||||
worldeditadditions.register_sapling_alias("clementinetree:sapling", "clementine")
|
||||
wea_c.register_sapling_alias("clementinetree:sapling", "clementine")
|
||||
end
|
||||
if minetest.get_modpath("ebony") then
|
||||
worldeditadditions.register_sapling_alias("ebony:sapling", "ebony")
|
||||
wea_c.register_sapling_alias("ebony:sapling", "ebony")
|
||||
end
|
||||
if minetest.get_modpath("jacaranda") then
|
||||
worldeditadditions.register_sapling_alias("jacaranda:sapling", "jacaranda")
|
||||
wea_c.register_sapling_alias("jacaranda:sapling", "jacaranda")
|
||||
end
|
||||
if minetest.get_modpath("larch") then
|
||||
worldeditadditions.register_sapling_alias("larch:sapling", "larch")
|
||||
wea_c.register_sapling_alias("larch:sapling", "larch")
|
||||
end
|
||||
if minetest.get_modpath("maple") then
|
||||
worldeditadditions.register_sapling_alias("maple:sapling", "maple")
|
||||
wea_c.register_sapling_alias("maple:sapling", "maple")
|
||||
end
|
||||
if minetest.get_modpath("palm") then
|
||||
worldeditadditions.register_sapling_alias("palm:sapling", "palm")
|
||||
wea_c.register_sapling_alias("palm:sapling", "palm")
|
||||
end
|
||||
if minetest.get_modpath("plumtree") then
|
||||
worldeditadditions.register_sapling_alias("plumtree:sapling", "plum")
|
||||
wea_c.register_sapling_alias("plumtree:sapling", "plum")
|
||||
end
|
||||
if minetest.get_modpath("hollytree") then
|
||||
worldeditadditions.register_sapling_alias("hollytree:sapling", "holly")
|
||||
wea_c.register_sapling_alias("hollytree:sapling", "holly")
|
||||
end
|
||||
if minetest.get_modpath("pomegranate") then
|
||||
worldeditadditions.register_sapling_alias("pomegranate:sapling", "pomegranate")
|
||||
wea_c.register_sapling_alias("pomegranate:sapling", "pomegranate")
|
||||
end
|
||||
if minetest.get_modpath("willow") then
|
||||
worldeditadditions.register_sapling_alias("willow:sapling", "willow")
|
||||
wea_c.register_sapling_alias("willow:sapling", "willow")
|
||||
end
|
||||
if minetest.get_modpath("mahogany") then
|
||||
worldeditadditions.register_sapling_alias("mahogany:sapling", "mahogany")
|
||||
wea_c.register_sapling_alias("mahogany:sapling", "mahogany")
|
||||
end
|
||||
if minetest.get_modpath("chestnuttree") then
|
||||
worldeditadditions.register_sapling_alias("chestnuttree:sapling", "chestnut")
|
||||
wea_c.register_sapling_alias("chestnuttree:sapling", "chestnut")
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local wea = worldeditadditions
|
||||
local Vector3 = wea.Vector3
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
wea.conv = {}
|
||||
|
||||
|
@ -62,8 +63,8 @@ function wea.convolve(pos1, pos2, kernel, kernel_size)
|
|||
|
||||
local node_id_air = minetest.get_content_id("air")
|
||||
|
||||
local heightmap, heightmap_size = wea.terrain.make_heightmap(pos1, pos2, manip, area, data)
|
||||
local heightmap_conv = wea.table.shallowcopy(heightmap)
|
||||
local heightmap, heightmap_size = wea_c.terrain.make_heightmap(pos1, pos2, manip, area, data)
|
||||
local heightmap_conv = wea_c.table.shallowcopy(heightmap)
|
||||
|
||||
wea.conv.convolve(
|
||||
heightmap_conv,
|
||||
|
@ -73,11 +74,11 @@ function wea.convolve(pos1, pos2, kernel, kernel_size)
|
|||
)
|
||||
|
||||
-- print("original")
|
||||
-- wea.format.array_2d(heightmap, (pos2.z - pos1.z) + 1)
|
||||
-- wea_c.format.array_2d(heightmap, (pos2.z - pos1.z) + 1)
|
||||
-- print("transformed")
|
||||
-- wea.format.array_2d(heightmap_conv, (pos2.z - pos1.z) + 1)
|
||||
-- wea_c.format.array_2d(heightmap_conv, (pos2.z - pos1.z) + 1)
|
||||
|
||||
wea.terrain.apply_heightmap_changes(
|
||||
wea_c.terrain.apply_heightmap_changes(
|
||||
pos1, pos2, area, data,
|
||||
heightmap, heightmap_conv, heightmap_size
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
local wea = worldeditadditions
|
||||
local Vector3 = wea.Vector3
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
--[[
|
||||
Convolves over a given 2D heightmap with a given matrix.
|
||||
Note that this *mutates* the given heightmap.
|
||||
|
@ -17,7 +17,7 @@ function worldeditadditions.conv.convolve(heightmap, heightmap_size, matrix, mat
|
|||
-- We need to reference a *copy* of the heightmap when convolving
|
||||
-- This is because we need the original values when we perform a
|
||||
-- convolution on a given pixel
|
||||
local heightmap_copy = wea.table.shallowcopy(heightmap)
|
||||
local heightmap_copy = wea_c.table.shallowcopy(heightmap)
|
||||
|
||||
local border_size = Vector3.new(
|
||||
(matrix_size.x-1) / 2, -- x = height
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
-- Ported from Javascript by Starbeamrainbowlabs
|
||||
-- Original source: https://github.com/sidorares/gaussian-convolution-kernel/
|
||||
-- From
|
||||
|
@ -27,7 +28,7 @@ function worldeditadditions.conv.kernel_gaussian(dimension, sigma)
|
|||
local sum = 0
|
||||
for i = 0, dimension-1 do
|
||||
for j = 0, dimension-1 do
|
||||
local distance = worldeditadditions.hypotenuse(i, j, centre, centre)
|
||||
local distance = wea_c.hypotenuse(i, j, centre, centre)
|
||||
|
||||
-- The following is an algorithm that came from the gaussian blur
|
||||
-- wikipedia page [1].
|
||||
|
@ -35,7 +36,7 @@ function worldeditadditions.conv.kernel_gaussian(dimension, sigma)
|
|||
-- http://en.wikipedia.org/w/index.php?title=Gaussian_blur&oldid=608793634#Mechanics
|
||||
local gaussian = (1 / math.sqrt(
|
||||
math.pi * two_sigma_square
|
||||
)) * math.exp((-1) * (math.pow(distance, 2) / two_sigma_square));
|
||||
)) * math.exp((-1) * ((distance ^ 2) / two_sigma_square));
|
||||
|
||||
sum = sum + gaussian
|
||||
kernel[i*dimension + j] = gaussian
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
--- Copies a region to another location, potentially overwriting the exiting region.
|
||||
-- @module worldeditadditions.copy
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
local wea = worldeditadditions
|
||||
local Vector3 = wea.Vector3
|
||||
|
||||
-- ██████ ██████ ██████ ██ ██
|
||||
-- ██ ██ ██ ██ ██ ██ ██
|
||||
|
@ -10,7 +8,16 @@ local Vector3 = wea.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)
|
||||
target_pos1, target_pos2 = Vector3.sort(target_pos1, target_pos2)
|
||||
|
||||
|
@ -20,28 +27,39 @@ function worldeditadditions.copy(source_pos1, source_pos2, target_pos1, target_p
|
|||
-- Fetch the nodes in the source area
|
||||
local manip_source, area_source = worldedit.manip_helpers.init(source_pos1, source_pos2)
|
||||
local data_source = manip_source:get_data()
|
||||
local data_source_param2 = manip_source:get_param2_data()
|
||||
|
||||
-- Fetch a manip for the target area
|
||||
local manip_target, area_target = worldedit.manip_helpers.init(target_pos1, target_pos2)
|
||||
local data_target = manip_target:get_data()
|
||||
local data_target_param2 = manip_target:get_param2_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)
|
||||
|
||||
local total_replaced = 0
|
||||
for z = source_pos2.z, source_pos1.z, -1 do
|
||||
for y = source_pos2.y, source_pos1.y, -1 do
|
||||
for x = source_pos2.x, source_pos1.x, -1 do
|
||||
local source = Vector3.new(x, y, z)
|
||||
local source_i = area_source:index(x, y, z)
|
||||
local target = source:subtract(offset)
|
||||
local target = source - offset
|
||||
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]
|
||||
data_target_param2[target_i] = data_source_param2[source_i]
|
||||
total_replaced = total_replaced + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Save the modified nodes back to disk & return
|
||||
manip_target:set_param2_data(data_target_param2)
|
||||
worldedit.manip_helpers.finish(manip_target, data_target)
|
||||
|
||||
return true, worldedit.volume(target_pos1, target_pos2)
|
||||
return true, total_replaced
|
||||
end
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
--- Counts the nodes in a given area.
|
||||
-- @module worldeditadditions.count
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- ██████ ██████ ██ ██ ███ ██ ████████
|
||||
-- ██ ██ ██ ██ ██ ████ ██ ██
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██████ ██████ ██████ ██ ████ ██
|
||||
|
||||
--- 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)
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
|
@ -29,7 +37,7 @@ function worldeditadditions.count(pos1, pos2, do_human_counts)
|
|||
for node_name, count in pairs(counts) do
|
||||
table.insert(results, {
|
||||
count,
|
||||
tostring(worldeditadditions.round((count / total) * 100, 2)).."%",
|
||||
tostring(wea_c.round((count / total) * 100, 2)).."%",
|
||||
minetest.get_name_from_content_id(node_name)
|
||||
})
|
||||
end
|
||||
|
@ -37,7 +45,7 @@ function worldeditadditions.count(pos1, pos2, do_human_counts)
|
|||
|
||||
if do_human_counts then
|
||||
for key,item in pairs(results) do
|
||||
item[1] = worldeditadditions.format.human_size(item[1], 2)
|
||||
item[1] = wea_c.format.human_size(item[1], 2)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- ██████ ██████ ███ ███ ███████
|
||||
-- ██ ██ ██ ██ ████ ████ ██
|
||||
-- ██ ██ ██ ██ ██ ████ ██ █████
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██████ ██████ ██ ██ ███████
|
||||
--- Creates a dome shape with the given node, optionally specifying the
|
||||
-- direction the point should point.
|
||||
-- @param pos Vector3 The central point to start drawing the dome from.
|
||||
-- @param radius number The radius of the dome to create.
|
||||
-- @param replace_node string The fully qualified name of the node to use to make the dome with.
|
||||
-- @param pointing_dir Vector3 Optional. The direction the dome should point. Defaults to (0, 1, 0). See also wea_c.parse.axis_name.
|
||||
-- @param hollow boolean Whether to make the dome hollow or not. Defaults to false.
|
||||
function worldeditadditions.dome(pos, radius, replace_node, pointing_dir, hollow)
|
||||
pos = Vector3.clone(pos)
|
||||
local pos1 = pos - radius
|
||||
local pos2 = pos + radius
|
||||
|
||||
if not pointing_dir then pointing_dir = Vector3.new(0, 1, 0) end
|
||||
if hollow == nil then hollow = false end
|
||||
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
||||
local data = manip:get_data()
|
||||
|
||||
local node_id_replace = minetest.get_content_id(replace_node)
|
||||
local radius_sq = radius * radius
|
||||
local radius_inner_sq = (radius-1) * (radius-1)
|
||||
local centrepoint = Vector3.mean(pos1, pos2)
|
||||
|
||||
|
||||
local replaced = 0
|
||||
for z = pos2.z, pos1.z, -1 do
|
||||
for y = pos2.y, pos1.y, -1 do
|
||||
for x = pos2.x, pos1.x, -1 do
|
||||
local distance_sq = (Vector3.new(x, y, z) - centrepoint):length_squared()
|
||||
|
||||
local is_in_range = distance_sq < radius_sq
|
||||
|
||||
if hollow and distance_sq < radius_inner_sq then
|
||||
is_in_range = false
|
||||
end
|
||||
|
||||
if is_in_range then
|
||||
-- It's inside the radius, but we're still not sure given this is a dome and not a sphere
|
||||
local should_include = false
|
||||
if x <= centrepoint.x and pointing_dir.x < 0 then
|
||||
should_include = true
|
||||
end
|
||||
if x >= centrepoint.x and pointing_dir.x > 0 then
|
||||
should_include = true
|
||||
end
|
||||
if y <= centrepoint.y and pointing_dir.y < 0 then
|
||||
should_include = true
|
||||
end
|
||||
if y >= centrepoint.y and pointing_dir.y > 0 then
|
||||
should_include = true
|
||||
end
|
||||
if z <= centrepoint.z and pointing_dir.z < 0 then
|
||||
should_include = true
|
||||
end
|
||||
if z >= centrepoint.z and pointing_dir.z > 0 then
|
||||
should_include = true
|
||||
end
|
||||
|
||||
if should_include then
|
||||
data[area:index(x, y, z)] = node_id_replace
|
||||
replaced = replaced + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Save the modified nodes back to disk & return
|
||||
worldedit.manip_helpers.finish(manip, data)
|
||||
|
||||
return true, replaced
|
||||
end
|
|
@ -1,3 +1,6 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- ███████ ██ ██ ██ ██████ ███████ ███████
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- █████ ██ ██ ██ ██████ ███████ █████
|
||||
|
@ -13,12 +16,9 @@
|
|||
-- @param hollow bool Whether the ellipsoid should be hollow or not.
|
||||
-- @returns number The number of nodes filled to create the (optionally hollow) ellipsoid. This number will be lower with hollow ellipsoids, since the internals of an ellipsoid aren't altered.
|
||||
function worldeditadditions.ellipsoid(position, radius, target_node, hollow)
|
||||
radius = Vector3.clone(radius)
|
||||
-- position = { x, y, z }
|
||||
local hollow_inner_radius = {
|
||||
x = radius.x - 1,
|
||||
y = radius.y - 1,
|
||||
z = radius.z - 1
|
||||
}
|
||||
local hollow_inner_radius = radius - 1
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
-- OPTIMIZE: We should be able to calculate a more efficient box-area here
|
||||
|
@ -39,21 +39,19 @@ function worldeditadditions.ellipsoid(position, radius, target_node, hollow)
|
|||
|
||||
local i = idx_y_base
|
||||
for x = -radius.x, radius.x do
|
||||
|
||||
local here = Vector3.new(x, y, z)
|
||||
-- If we're inside the ellipse, then fill it in
|
||||
local x_comp, y_comp, z_comp = x/radius.x, y/radius.y, z/radius.z
|
||||
local ellipsoid_dist = x_comp*x_comp + y_comp*y_comp + z_comp*z_comp
|
||||
local comp = here / radius
|
||||
|
||||
local ellipsoid_dist = Vector3.dot(comp, comp)
|
||||
if ellipsoid_dist <= 1 then
|
||||
local place_ok = not hollow;
|
||||
|
||||
if not place_ok then
|
||||
-- It must be hollow! Do some additional calculations.
|
||||
local hx_comp = x/hollow_inner_radius.x
|
||||
local hy_comp = y/hollow_inner_radius.y
|
||||
local hz_comp = z/hollow_inner_radius.z
|
||||
|
||||
local h_comp = here / hollow_inner_radius
|
||||
-- It's only ok to place it if it's outside our inner ellipse
|
||||
place_ok = hx_comp*hx_comp + hy_comp*hy_comp + hz_comp*hz_comp >= 1
|
||||
place_ok = Vector3.dot(h_comp, h_comp) >= 1
|
||||
end
|
||||
|
||||
if place_ok then
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local wea = worldeditadditions
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- ███████ ██ ██ ██ ██████ ███████ ███████
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██
|
||||
|
@ -8,21 +9,17 @@ local wea = worldeditadditions
|
|||
|
||||
|
||||
function worldeditadditions.ellipsoid2(pos1, pos2, target_node, hollow)
|
||||
pos1, pos2 = wea.Vector3.sort(pos1, pos2)
|
||||
local volume = pos2:subtract(pos1)
|
||||
local volume_half = volume:divide(2)
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
local volume = pos2 - pos1
|
||||
local volume_half = volume / 2
|
||||
|
||||
local radius = pos2:subtract(pos1):divide(2)
|
||||
local radius = (pos2 - pos1) / 2
|
||||
|
||||
print("DEBUG:ellipsoid2 | pos1: "..pos1..", pos2: "..pos2..", target_node: "..target_node)
|
||||
print("DEBUG:ellipsoid2 radius", radius)
|
||||
-- print("DEBUG:ellipsoid2 | pos1: "..pos1..", pos2: "..pos2..", target_node: "..target_node)
|
||||
-- print("DEBUG:ellipsoid2 radius", radius)
|
||||
|
||||
-- position = { x, y, z }
|
||||
local hollow_inner_radius = {
|
||||
x = radius.x - 1,
|
||||
y = radius.y - 1,
|
||||
z = radius.z - 1
|
||||
}
|
||||
local hollow_inner_radius = radius - 1
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
||||
|
@ -35,26 +32,23 @@ function worldeditadditions.ellipsoid2(pos1, pos2, target_node, hollow)
|
|||
for z = pos2.z, pos1.z, -1 do
|
||||
for y = pos2.y, pos1.y, -1 do
|
||||
for x = pos2.x, pos1.x, -1 do
|
||||
local here = Vector3.new(x, y, z)
|
||||
local pos_relative = (here - pos1) - volume_half
|
||||
|
||||
local pos_relative = wea.Vector3.new(x, y, z):subtract(pos1)
|
||||
:subtract(volume_half)
|
||||
|
||||
print("DEBUG pos1", pos1, "pos2", pos2, "volume_half", volume_half, "pos_relative", pos_relative)
|
||||
-- print("DEBUG pos1", pos1, "pos2", pos2, "volume_half", volume_half, "pos_relative", pos_relative)
|
||||
|
||||
-- If we're inside the ellipse, then fill it in
|
||||
local comp = pos_relative:divide(radius)
|
||||
local comp = pos_relative / radius
|
||||
local ellipsoid_dist = comp:length_squared()
|
||||
if ellipsoid_dist <= 1 then
|
||||
local place_ok = not hollow;
|
||||
|
||||
if not place_ok then
|
||||
-- It must be hollow! Do some additional calculations.
|
||||
local hx_comp = x/hollow_inner_radius.x
|
||||
local hy_comp = y/hollow_inner_radius.y
|
||||
local hz_comp = z/hollow_inner_radius.z
|
||||
local h_comp = here / hollow_inner_radius
|
||||
|
||||
-- It's only ok to place it if it's outside our inner ellipse
|
||||
place_ok = hx_comp*hx_comp + hy_comp*hy_comp + hz_comp*hz_comp >= 1
|
||||
place_ok = Vector3.dot(h_comp, h_comp) >= 1
|
||||
end
|
||||
|
||||
if place_ok then
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
-- ███████ ██ ██ ██ ██████ ███████ ██████ ██ ██████
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- █████ ██ ██ ██ ██████ ███████ ██ ██ ██ ██ ██
|
||||
|
@ -17,40 +20,29 @@
|
|||
-- @param {Position} pos2 The 2nd positioon defining the region boundary
|
||||
-- @param {Function} func The function to call that performs the action in question. It is expected that the given function will accept no arguments.
|
||||
function worldeditadditions.ellipsoidapply(pos1, pos2, func)
|
||||
local time_taken_all = worldeditadditions.get_ms_time()
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local time_taken_all = wea_c.get_ms_time()
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
local manip_before, area_before = worldedit.manip_helpers.init(pos1, pos2)
|
||||
local data_before = manip_before:get_data()
|
||||
|
||||
local time_taken_fn = worldeditadditions.get_ms_time()
|
||||
local time_taken_fn = wea_c.get_ms_time()
|
||||
func()
|
||||
time_taken_fn = worldeditadditions.get_ms_time() - time_taken_fn
|
||||
time_taken_fn = wea_c.get_ms_time() - time_taken_fn
|
||||
|
||||
local manip_after, area_after = worldedit.manip_helpers.init(pos1, pos2)
|
||||
local data_after = manip_after:get_data()
|
||||
|
||||
local radius = {
|
||||
x = (pos2.x - pos1.x) / 2,
|
||||
y = (pos2.y - pos1.y) / 2,
|
||||
z = (pos2.z - pos1.z) / 2
|
||||
}
|
||||
local e_centre = {
|
||||
x = pos2.x - radius.x,
|
||||
y = pos2.y - radius.y,
|
||||
z = pos2.z - radius.z
|
||||
}
|
||||
local radius = (pos2 - pos1) / 2
|
||||
local e_centre = pos2 - radius
|
||||
|
||||
for z = pos2.z, pos1.z, -1 do
|
||||
for y = pos2.y, pos1.y, -1 do
|
||||
for x = pos2.x, pos1.x, -1 do
|
||||
local x_comp = (x - e_centre.x) / radius.x
|
||||
local y_comp = (y - e_centre.y) / radius.y
|
||||
local z_comp = (z - e_centre.z) / radius.z
|
||||
|
||||
local distance_mult = x_comp*x_comp + y_comp*y_comp + z_comp*z_comp
|
||||
local comp = (Vector3.new(x, y, z) - e_centre) / radius
|
||||
local distance_mult = Vector3.dot(comp, comp)
|
||||
|
||||
-- Roll everything that's outside the ellipse back
|
||||
if distance_mult > 1 then
|
||||
|
@ -65,6 +57,6 @@ function worldeditadditions.ellipsoidapply(pos1, pos2, func)
|
|||
worldedit.manip_helpers.finish(manip_after, data_after)
|
||||
|
||||
|
||||
time_taken_all = worldeditadditions.get_ms_time() - time_taken_all
|
||||
time_taken_all = wea_c.get_ms_time() - time_taken_all
|
||||
return true, { all = time_taken_all, fn = time_taken_fn }
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local wea = worldeditadditions
|
||||
local wea_c = worldeditadditions_core
|
||||
wea.erode = {}
|
||||
|
||||
dofile(wea.modpath.."/lib/erode/snowballs.lua")
|
||||
|
@ -18,12 +19,12 @@ function wea.erode.run(pos1, pos2, algorithm, params)
|
|||
|
||||
local region_height = (pos2.y - pos1.y) + 1
|
||||
|
||||
local heightmap = wea.terrain.make_heightmap(pos1, pos2, manip, area, data)
|
||||
local heightmap_eroded = wea.table.shallowcopy(heightmap)
|
||||
local heightmap = wea_c.terrain.make_heightmap(pos1, pos2, manip, area, data)
|
||||
local heightmap_eroded = wea_c.table.shallowcopy(heightmap)
|
||||
|
||||
-- print("[erode.run] algorithm: "..algorithm..", params:");
|
||||
-- print(wea.format.map(params))
|
||||
-- wea.format.array_2d(heightmap, heightmap_size.x)
|
||||
-- print(wea_c.format.map(params))
|
||||
-- wea_c.format.array_2d(heightmap, heightmap_size.x)
|
||||
local success, msg, stats
|
||||
if algorithm == "snowballs" then
|
||||
success, msg = wea.erode.snowballs(
|
||||
|
@ -49,7 +50,7 @@ function wea.erode.run(pos1, pos2, algorithm, params)
|
|||
return false, "Error: Unknown algorithm '"..algorithm.."'. Currently implemented algorithms: snowballs (2d; hydraulic-like), river (2d; cellular automata-like; fills potholes and lowers towers). Ideas for algorithms to implement are welcome!"
|
||||
end
|
||||
|
||||
success, stats = wea.terrain.apply_heightmap_changes(
|
||||
success, stats = wea_c.terrain.apply_heightmap_changes(
|
||||
pos1, pos2, area, data,
|
||||
heightmap, heightmap_eroded, heightmap_size
|
||||
)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
local wea = worldeditadditions
|
||||
local wea_c = worldeditadditions_core
|
||||
|
||||
--- Parses a comma-separated side numbers list out into a list of numbers.
|
||||
-- @param list string The command separated list to parse.
|
||||
-- @returns number[] A list of side numbers.
|
||||
local function parse_sides_list(list)
|
||||
list = list:gsub("%s", "") -- Spaces are not permitted
|
||||
return wea.table.unique(wea.table.map(
|
||||
wea.split(list, ","),
|
||||
return wea_c.table.unique(wea_c.table.map(
|
||||
wea_c.split(list, ","),
|
||||
function(value) return tonumber(value) end
|
||||
))
|
||||
end
|
||||
|
@ -20,7 +21,7 @@ function worldeditadditions.erode.river(heightmap_initial, heightmap, heightmap_
|
|||
dolower = true -- Whether to do lower operations or not
|
||||
}
|
||||
-- Apply the custom settings
|
||||
wea.table.apply(params_custom, params)
|
||||
wea_c.table.apply(params_custom, params)
|
||||
|
||||
params.lower_sides = parse_sides_list(params.lower_sides)
|
||||
params.raise_sides = parse_sides_list(params.raise_sides)
|
||||
|
@ -30,8 +31,8 @@ function worldeditadditions.erode.river(heightmap_initial, heightmap, heightmap_
|
|||
local removed = 0
|
||||
for i=1,params.steps do
|
||||
-- print("[DEBUG:river] step ", i)
|
||||
-- wea.format.array_2d(heightmap, heightmap_size.x)
|
||||
local time_start = wea.get_ms_time()
|
||||
-- wea_c.format.array_2d(heightmap, heightmap_size.x)
|
||||
local time_start = wea_c.get_ms_time()
|
||||
|
||||
-- Store up changes to make and make them at the end of the step
|
||||
-- This is important, because decisions
|
||||
|
@ -105,7 +106,7 @@ function worldeditadditions.erode.river(heightmap_initial, heightmap, heightmap_
|
|||
removed = removed + 1
|
||||
end
|
||||
-- print("[DEBUG:river] sides_higher", sides_higher, "sides_lower", sides_lower, "action", action)
|
||||
-- wea.format.array_2d(heightmap, heightmap_size.x)
|
||||
-- wea_c.format.array_2d(heightmap, heightmap_size.x)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -116,8 +117,8 @@ function worldeditadditions.erode.river(heightmap_initial, heightmap, heightmap_
|
|||
heightmap[hi] = heightmap[hi] - 1
|
||||
end
|
||||
|
||||
table.insert(timings, wea.get_ms_time() - time_start)
|
||||
table.insert(timings, wea_c.get_ms_time() - time_start)
|
||||
end
|
||||
|
||||
return true, params.steps.." steps made, raising "..filled.." and lowering "..removed.." columns in "..wea.format.human_time(wea.sum(timings)).." (~"..wea.format.human_time(wea.average(timings)).." per step)"
|
||||
return true, params.steps.." steps made, raising "..filled.." and lowering "..removed.." columns in "..wea_c.format.human_time(wea_c.sum(timings)).." (~"..wea_c.format.human_time(wea_c.average(timings)).." per step)"
|
||||
end
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
local wea = worldeditadditions
|
||||
local Vector3 = wea.Vector3
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
|
||||
-- Test command: //multi //fp set1 1313 6 5540 //fp set2 1338 17 5521 //erode snowballs
|
||||
|
||||
local function snowball(heightmap, normalmap, heightmap_size, startpos, params)
|
||||
local sediment = 0
|
||||
local pos = { x = startpos.x, z = startpos.z }
|
||||
local pos_prev = { x = pos.x, z = pos.z }
|
||||
local velocity = {
|
||||
x = (math.random() * 2 - 1) * params.init_velocity,
|
||||
z = (math.random() * 2 - 1) * params.init_velocity
|
||||
}
|
||||
local pos = Vector3.new(startpos.x, 0, startpos.z) -- X/Z
|
||||
local pos_prev = Vector3.new(pos.x, 0, pos.z) -- X/Z
|
||||
local velocity = Vector3.new(
|
||||
(math.random() * 2 - 1) * params.init_velocity,
|
||||
0,
|
||||
(math.random() * 2 - 1) * params.init_velocity
|
||||
) -- X/Z
|
||||
local heightmap_length = #heightmap
|
||||
|
||||
-- print("[snowball] startpos ("..pos.x..", "..pos.z.."), velocity: ("..velocity.x..", "..velocity.z..")")
|
||||
|
@ -29,7 +32,7 @@ local function snowball(heightmap, normalmap, heightmap_size, startpos, params)
|
|||
end
|
||||
|
||||
if #hist_velocity > 0 and i > 5
|
||||
and wea.average(hist_velocity) < 0.03 then
|
||||
and wea_c.average(hist_velocity) < 0.03 then
|
||||
-- print("[snowball] It looks like we've stopped")
|
||||
return true, i
|
||||
end
|
||||
|
@ -50,13 +53,13 @@ local function snowball(heightmap, normalmap, heightmap_size, startpos, params)
|
|||
end
|
||||
|
||||
velocity.x = params.friction * velocity.x + normalmap[hi].x * params.speed
|
||||
velocity.y = 0 -- Just in case
|
||||
velocity.z = params.friction * velocity.z + normalmap[hi].y * params.speed
|
||||
|
||||
-- print("[snowball] now at ("..x..", "..z..") velocity "..wea.vector.lengthsquared(velocity)..", sediment "..sediment)
|
||||
local new_vel_sq = wea.vector.lengthsquared(velocity)
|
||||
-- print("[snowball] now at ("..x..", "..z..") velocity "..velocity:length_squared()..", sediment "..sediment)
|
||||
local new_vel_sq = velocity:length_squared()
|
||||
if new_vel_sq > 1 then
|
||||
-- print("[snowball] velocity squared over 1, normalising")
|
||||
velocity = wea.vector.normalize(velocity)
|
||||
velocity = velocity:normalise()
|
||||
end
|
||||
table.insert(hist_velocity, new_vel_sq)
|
||||
if #hist_velocity > params.velocity_hist_count then table.remove(hist_velocity, 1) end
|
||||
|
@ -89,12 +92,12 @@ function wea.erode.snowballs(heightmap_initial, heightmap, heightmap_size, regio
|
|||
count = 25000
|
||||
}
|
||||
-- Apply the custom settings
|
||||
wea.table.apply(params_custom, params)
|
||||
wea_c.table.apply(params_custom, params)
|
||||
|
||||
-- print("[erode/snowballs] params: ")
|
||||
-- print(wea.format.map(params))
|
||||
-- print(wea_c.format.map(params))
|
||||
|
||||
local normals = wea.terrain.calculate_normals(heightmap, heightmap_size)
|
||||
local normals = wea_c.terrain.calculate_normals(heightmap, heightmap_size)
|
||||
|
||||
local stats_steps = {}
|
||||
for i = 1, params.count do
|
||||
|
@ -142,5 +145,5 @@ function wea.erode.snowballs(heightmap_initial, heightmap, heightmap_size, regio
|
|||
)
|
||||
end
|
||||
|
||||
return true, ""..#stats_steps.." snowballs simulated, max "..params.max_steps.." steps (averaged ~"..wea.average(stats_steps).." steps)"
|
||||
return true, ""..#stats_steps.." snowballs simulated, max "..params.max_steps.." steps (averaged ~"..wea_c.average(stats_steps).." steps)"
|
||||
end
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
--- Overlap command. Places a specified node on top of each column.
|
||||
-- @module worldeditadditions.overlay
|
||||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
--- Fill caves command. Fills underneath non-air nodes in the defined region.
|
||||
-- @param pos1 Vector3 pos1 of the defined region to fill caves in.
|
||||
-- @param pos2 Vector3 pos2 of the defined region to fill caves in.
|
||||
-- @param node_name string The name of the node to replace air nodes with. Should have been already normalised, as with all other worldeditadditions.* functions.
|
||||
-- @returns bool,{replaced=number} 1. Whether the fill caves operation was successful or not.
|
||||
-- 2. A table of statistics about the operation. Currently the only key in this table is `replaced`, which is the number of (air-like) nodes that were replaced during the operation.
|
||||
function worldeditadditions.fillcaves(pos1, pos2, node_name)
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
|
@ -12,8 +18,8 @@ function worldeditadditions.fillcaves(pos1, pos2, node_name)
|
|||
local node_id_replace = minetest.get_content_id(node_name)
|
||||
local node_id_ignore = minetest.get_content_id("ignore")
|
||||
|
||||
-- minetest.log("action", "pos1: " .. worldeditadditions.vector.tostring(pos1))
|
||||
-- minetest.log("action", "pos2: " .. worldeditadditions.vector.tostring(pos2))
|
||||
-- minetest.log("action", "pos1: " .. pos1)
|
||||
-- minetest.log("action", "pos2: " .. pos2)
|
||||
|
||||
-- z y x is the preferred loop order, but that isn't really possible here
|
||||
|
||||
|
@ -27,7 +33,7 @@ function worldeditadditions.fillcaves(pos1, pos2, node_name)
|
|||
for y = pos2.y, pos1.y, -1 do
|
||||
local i = area:index(x, y, z)
|
||||
|
||||
local is_air = worldeditadditions.is_airlike(data[i]) or worldeditadditions.is_liquidlike(data[i])
|
||||
local is_air = wea_c.is_airlike(data[i]) or wea_c.is_liquidlike(data[i])
|
||||
local is_ignore = data[i] == node_id_ignore
|
||||
|
||||
-- If the previous node was air and this one isn't, then we've found the top level
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
local wea_c = worldeditadditions_core
|
||||
local Vector3 = wea_c.Vector3
|
||||
|
||||
|
||||
--- Flood-fill command for complex lakes etc.
|
||||
-- @module worldeditadditions.floodfill
|
||||
|
||||
local wea = worldeditadditions
|
||||
|
||||
-- @param start_pos Vector3 The position to start floodfilling from.
|
||||
-- @param radius number The maximum radius to limit the floodfill operation too.
|
||||
-- @param replace_node string The (normalised) name of the node to replace with when floodfilling.
|
||||
-- @returns number The number of nodes replaced.
|
||||
function worldeditadditions.floodfill(start_pos, radius, replace_node)
|
||||
start_pos = Vector3.clone(start_pos)
|
||||
|
||||
-- Calculate the area we want to modify
|
||||
local pos1 = vector.add(start_pos, { x = radius, y = 0, z = radius })
|
||||
local pos2 = vector.subtract(start_pos, { x = radius, y = radius, z = radius })
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local pos1 = start_pos + Vector3.new(radius, 0, radius)
|
||||
local pos2 = start_pos - Vector3.new(radius, radius, radius)
|
||||
pos1, pos2 = Vector3.sort(pos1, pos2)
|
||||
-- pos2 will always have the highest co-ordinates now
|
||||
|
||||
|
||||
|
@ -27,7 +33,7 @@ function worldeditadditions.floodfill(start_pos, radius, replace_node)
|
|||
end
|
||||
|
||||
local count = 0
|
||||
local remaining_nodes = wea.Queue.new() remaining_nodes:enqueue(start_pos_index)
|
||||
local remaining_nodes = wea_c.Queue.new() remaining_nodes:enqueue(start_pos_index)
|
||||
|
||||
-- Do the floodfill
|
||||
while remaining_nodes:is_empty() == false do
|
||||
|
@ -41,35 +47,35 @@ function worldeditadditions.floodfill(start_pos, radius, replace_node)
|
|||
-- We don't need to go upwards here, since we're filling in lake-style
|
||||
local xplus = cur + 1 -- +X
|
||||
if data[xplus] == search_id and
|
||||
worldeditadditions.vector.lengthsquared(vector.subtract(area:position(xplus), start_pos)) < radius_sq and
|
||||
(Vector3.clone(area:position(xplus)) - start_pos):length_squared() < radius_sq and
|
||||
not remaining_nodes:contains(xplus) then
|
||||
-- minetest.log("action", "[floodfill] [+X] index " .. xplus .. " is a " .. data[xplus] .. ", searching for a " .. search_id)
|
||||
remaining_nodes:enqueue(xplus)
|
||||
end
|
||||
local xminus = cur - 1 -- -X
|
||||
if data[xminus] == search_id and
|
||||
worldeditadditions.vector.lengthsquared(vector.subtract(area:position(xminus), start_pos)) < radius_sq and
|
||||
(Vector3.clone(area:position(xminus)) - start_pos):length_squared() < radius_sq and
|
||||
not remaining_nodes:contains(xminus) then
|
||||
-- minetest.log("action", "[floodfill] [-X] index " .. xminus .. " is a " .. data[xminus] .. ", searching for a " .. search_id)
|
||||
remaining_nodes:enqueue(xminus)
|
||||
end
|
||||
local zplus = cur + area.zstride -- +Z
|
||||
if data[zplus] == search_id and
|
||||
worldeditadditions.vector.lengthsquared(vector.subtract(area:position(zplus), start_pos)) < radius_sq and
|
||||
(Vector3.clone(area:position(zplus)) - start_pos):length_squared() < radius_sq and
|
||||
not remaining_nodes:contains(zplus) then
|
||||
-- minetest.log("action", "[floodfill] [+Z] index " .. zplus .. " is a " .. data[zplus] .. ", searching for a " .. search_id)
|
||||
remaining_nodes:enqueue(zplus)
|
||||
end
|
||||
local zminus = cur - area.zstride -- -Z
|
||||
if data[zminus] == search_id and
|
||||
worldeditadditions.vector.lengthsquared(vector.subtract(area:position(zminus), start_pos)) < radius_sq and
|
||||
(Vector3.clone(area:position(zminus)) - start_pos):length_squared() < radius_sq and
|
||||
not remaining_nodes:contains(zminus) then
|
||||
-- minetest.log("action", "[floodfill] [-Z] index " .. zminus .. " is a " .. data[zminus] .. ", searching for a " .. search_id)
|
||||
remaining_nodes:enqueue(zminus)
|
||||
end
|
||||
local yminus = cur - area.ystride -- -Y
|
||||
if data[yminus] == search_id and
|
||||
worldeditadditions.vector.lengthsquared(vector.subtract(area:position(yminus), start_pos)) < radius_sq and
|
||||
(Vector3.clone(area:position(yminus)) - start_pos):length_squared() < radius_sq and
|
||||
not remaining_nodes:contains(yminus) then
|
||||
-- minetest.log("action", "[floodfill] [-Y] index " .. yminus .. " is a " .. data[yminus] .. ", searching for a " .. search_id)
|
||||
remaining_nodes:enqueue(yminus)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue