From 5f7a64702aa1956a13f66a773eccdfd86e9e4a29 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 11 May 2020 02:02:02 +0100 Subject: [PATCH] Add //bonemeal command --- README.md | 41 ++++++++++--- worldeditadditions/bonemeal.lua | 61 +++++++++++++++++++ worldeditadditions/init.lua | 2 + .../commands/bonemeal.lua | 61 +++++++++++++++++++ worldeditadditions_commands/init.lua | 17 +++++- worldeditadditions_commands/utils/numbers.lua | 7 +++ 6 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 worldeditadditions/bonemeal.lua create mode 100644 worldeditadditions_commands/commands/bonemeal.lua create mode 100644 worldeditadditions_commands/utils/numbers.lua diff --git a/README.md b/README.md index 2ba9369..348a3f9 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ If you can dream of it, it probably belongs here! - [`//maze [ [ []]]`](#maze-replace_node-seed) - [`//maze3d [ [ [ []]]]`](#maze3d-replace_node-seed) - [`//multi .....`](#multi-command_a-command_b-command_c-) - - [`//yy`](#yy) - - [`//nn`](#nn) + - [`//y`](#y) + - [`//n`](#n) ### `//floodfill [ []]` Floods all connected nodes of the same type starting at _pos1_ with (which defaults to `water_source`), in a sphere with a radius of (which defaults to 50). @@ -108,6 +108,27 @@ The optional `path_depth` parameter defaults to `1` and allows customisation of //maze3d stone 6 3 3 54321 ``` +### `//bonemeal [ []]` +Requires the [`bonemeal`](https://content.minetest.net/packages/TenPlus1/bonemeal/) ([repo](https://notabug.org/TenPlus1/bonemeal/)) mod (otherwise _WorldEditAdditions_ will not register this command and outut a message to the server log). + +Bonemeals all eligible nodes in the current region. An eligible node is one that has an air node directly above it - note that just because a node is eligible doesn't mean to say that something will actually happen when the `bonemeal` mod bonemeals it. + +Optionally takes a strength value (that's passed to `bonemeal:on_use()`, the method in the `bonemeal` mod that is called to actually do the bonemealing). The strength value is a positive integer from 1 to 4 (i.e. 1, 2, 3, or 4) - the default is 1 (the lowest strength). + +I observe that a higher strength value gives a higher chance that something will actually grow. In the case of soil or sand nodes, I observe that it increases the area of effect of a single bonemeal action (thus at higher strengths generally you'll probably want a higher chance number - see below). See the [`bonemeal` mod README](https://notabug.org/TenPlus1/bonemeal) for more information. + +Also optionally takes a chance number. This is the chance that an eligible node will actually get bonemealed, and is a positive integer that defaults to 1. The chance number represents a 1-in-{number} chance to bonemeal any given eligible node, where {number} is the chance number. In other words, the higher the chance number the lower the chance that a node will be bonemealed. + +For example, a chance number of 2 would mean a 50% chance that any given eligible node will get bonemealed. A chance number of 16 would be a 6.25% chance, and a chance number of 25 would be 2%. + +``` +//bonemeal +//bonemeal 3 25 +//bonemeal 4 +//bonemeal 1 10 +//bonemeal 2 15 +``` + ### `//multi .....` Executes multi chat commands in sequence. Intended for _WorldEdit_ commands, but does work with others too. Don't forget a space between commands! @@ -117,18 +138,22 @@ Executes multi chat commands in sequence. Intended for _WorldEdit_ commands, but //multi /time 7:00 //1 //outset h 20 //outset v 5 //overlay dirt_with_grass //1 //2 //sphere 8 air //shift down 1 //floodfill //reset ``` -### `//yy` -Confirms the execution of a command if it could potentially affect a large number of nodes and take a while. Equivalent to _WorldEdit_'s `//y`, but because of security sandboxing issues it's not really possible to hook into WorldEdit's existing command. +### `//y` +Confirms the execution of a command if it could potentially affect a large number of nodes and take a while. This is a regular WorldEdit command. + + ``` -//yy +//y ``` -### `//nn` -Prevents the execution of a command if it could potentially affect a large number of nodes and take a while. Equivalent to _WorldEdit_'s `//y`, but because of security sandboxing issues it's not really possible to hook into WorldEdit's existing command. +### `//n` +Prevents the execution of a command if it could potentially affect a large number of nodes and take a while. This is a regular WorldEdit command. + + ``` -//nn +//n ``` ## Troubleshooting diff --git a/worldeditadditions/bonemeal.lua b/worldeditadditions/bonemeal.lua new file mode 100644 index 0000000..7c4c75d --- /dev/null +++ b/worldeditadditions/bonemeal.lua @@ -0,0 +1,61 @@ +--- Bonemeal command. +-- Applies bonemeal to all notes +-- @module worldeditadditions.overlay + +-- strength The strength to apply - see bonemeal:on_use +-- chance Positive integer that represents the chance bonemealing will occur +function worldeditadditions.bonemeal(pos1, pos2, strength, chance) + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + -- pos2 will always have the highest co-ordinates now + + -- This command requires the bonemeal mod to be installed + -- We check here too because other mods might call this function directly and bypass the chat command system + if not minetest.get_modpath("bonemeal") then + return false, "Bonemeal mod not loaded" + end + + -- Fetch the nodes in the specified area + local manip, area = worldedit.manip_helpers.init(pos1, pos2) + local data = manip:get_data() + + local node_id_air = minetest.get_content_id("air") + + -- z y x is the preferred loop order (because CPU cache I'd guess), but that isn't really possible here + local nodes_bonemealed = 0 + local candidates = 0 + for z = pos2.z, pos1.z, -1 do + for x = pos2.x, pos1.x, -1 do + local found_air = false + + for y = pos2.y, pos1.y, -1 do + if data[area:index(x, y, z)] ~= node_id_air then + if found_air then + -- We've found an air block previously, so this node definitely has air above it + + if math.random(0, chance - 1) == 0 then + bonemeal:on_use( + { x = x, y = y, z = z }, + strength, + nil + ) + nodes_bonemealed = nodes_bonemealed + 1 + end + + candidates = candidates + 1 + found_air = false + end + else + found_air = true + end + end + end + end + + + -- Save the modified nodes back to disk & return + -- Note that we do NOT save it back to disk here, because we haven't actually changed anything + -- We just grabbed the data via manip to allow for rapid node name lookups + -- worldedit.manip_helpers.finish(manip, data) + + return true, nodes_bonemealed, candidates +end diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index c7d06c4..15eadfe 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -14,3 +14,5 @@ dofile(minetest.get_modpath("worldeditadditions") .. "/ellipsoid.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/torus.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/maze2d.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/maze3d.lua") + +dofile(minetest.get_modpath("worldeditadditions") .. "/bonemeal.lua") diff --git a/worldeditadditions_commands/commands/bonemeal.lua b/worldeditadditions_commands/commands/bonemeal.lua new file mode 100644 index 0000000..3d57ea2 --- /dev/null +++ b/worldeditadditions_commands/commands/bonemeal.lua @@ -0,0 +1,61 @@ +local we_c = worldeditadditions_commands + +-- ██████ ██████ ███ ██ ███████ ███ ███ ███████ █████ ██ +-- ██ ██ ██ ██ ████ ██ ██ ████ ████ ██ ██ ██ ██ +-- ██████ ██ ██ ██ ██ ██ █████ ██ ████ ██ █████ ███████ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ██████ ██ ████ ███████ ██ ██ ███████ ██ ██ ███████ +worldedit.register_command("bonemeal", { + params = "[ []]", + description = "Bonemeals everything that's bonemeal-able that has an air node directly above it. Optionally takes a strength value to use (default: 1, maximum: 4), and a chance to actually bonemeal an eligible node (positive integer; nodes have a 1-in- chance to be bonemealed; higher values mean a lower chance; default: 1 - 100% chance).", + privs = { worldedit = true }, + require_pos = 2, + parse = function(params_text) + if not params_text or params_text == "" then + params_text = "1" + end + + local parts = we_c.split(params_text, "%s+", false) + + local strength = 1 + local chance = 1 + + if #parts >= 1 then + strength = tonumber(parts[1]) + if not strength then + return false, "Invalid strength value (value must be an integer)" + end + end + if #parts >= 2 then + chance = tonumber(parts[2]) + if not chance then + return false, "Invalid chance value (must be a positive integer)" + end + end + + if strength < 1 or strength > 4 then + return false, "Error: strength value out of bounds (value must be an integer between 1 and 4 inclusive)" + end + + -- We unconditionally math.floor here because when we tried to test for it directly it was unreliable + return true, math.floor(strength), math.floor(chance) + end, + nodes_needed = function(name) -- strength, chance + -- Since every node has to have an air block, in the best-case scenario + -- edit only half the nodes in the selected area + return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) / 2 + end, + func = function(name, strength, chance) + local start_time = os.clock() + local success, nodes_bonemealed, candidates = worldeditadditions.bonemeal(worldedit.pos1[name], worldedit.pos2[name], strength, chance) + if not success then + -- nodes_bonemealed is an error message here because success == false + return success, nodes_bonemealed + end + local percentage = we_c.round((nodes_bonemealed / candidates)*100, 2) + local time_taken = os.clock() - start_time + + minetest.log("action", name .. " used //bonemeal at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", bonemealing " .. nodes_bonemealed.." nodes (out of "..candidates.." nodes) at strength "..strength.." in "..time_taken.."s") + return true, nodes_bonemealed.." out of "..candidates.." (~"..percentage.."%) candidates bonemealed in "..time_taken.."s" + end +}) diff --git a/worldeditadditions_commands/init.lua b/worldeditadditions_commands/init.lua index deae62a..43e37a5 100644 --- a/worldeditadditions_commands/init.lua +++ b/worldeditadditions_commands/init.lua @@ -10,14 +10,25 @@ local we_c = worldeditadditions_commands we_c.modpath = minetest.get_modpath("worldeditadditions_commands") +dofile(we_c.modpath.."/utils/strings.lua") +dofile(we_c.modpath.."/utils/numbers.lua") + dofile(we_c.modpath.."/multi.lua") -we_c.safe_region, we_c.check_region, we_c.reset_pending - = dofile(we_c.modpath.."/safe.lua") +-- We no longer need our own implementation of safe_region thanks to @sfan5's +-- suggestion in issue #5 - yay! +-- we_c.safe_region, we_c.check_region, we_c.reset_pending +-- = dofile(we_c.modpath.."/safe.lua") -dofile(we_c.modpath.."/utils/strings.lua") dofile(we_c.modpath.."/commands/floodfill.lua") dofile(we_c.modpath.."/commands/overlay.lua") dofile(we_c.modpath.."/commands/ellipsoid.lua") dofile(we_c.modpath.."/commands/torus.lua") dofile(we_c.modpath.."/commands/maze.lua") + +-- Don't registry the //bonemeal command if the bonemeal mod isn't present +if minetest.get_modpath("bonemeal") then + dofile(we_c.modpath.."/commands/bonemeal.lua") +else + minetest.log("action", "[WorldEditAdditions] bonemeal mod not detected: //bonemeal command not registered") +end diff --git a/worldeditadditions_commands/utils/numbers.lua b/worldeditadditions_commands/utils/numbers.lua new file mode 100644 index 0000000..75c26bc --- /dev/null +++ b/worldeditadditions_commands/utils/numbers.lua @@ -0,0 +1,7 @@ +local we_c = worldeditadditions_commands + +-- From http://lua-users.org/wiki/SimpleRound +function we_c.round(num, numDecimalPlaces) + local mult = 10^(numDecimalPlaces or 0) + return math.floor(num * mult + 0.5) / mult +end