Merge pull request #1 from sbrl/master

Update 2.2
This commit is contained in:
VorTechnix 2021-03-01 13:27:35 -08:00 committed by GitHub
commit 7f4ebb8b8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 305 additions and 78 deletions

@ -2,7 +2,10 @@
It's about time I started a changelog! This will serve from now on as the main changelog for WorldEditAdditions. It's about time I started a changelog! This will serve from now on as the main changelog for WorldEditAdditions.
## v1.11 (unreleased) ## v1.12 (unreleased)
- Add `//srect` (_select rectangle_) - thanks, @VorTechnix!
## v1.11: The big data update (25th January 2021)
- Add `//scale` (currently **experimental**) - Add `//scale` (currently **experimental**)
- Scale operations that scale up and down at the same time are split into 2 separate operations automatically (scaling up is always performed first) - Scale operations that scale up and down at the same time are split into 2 separate operations automatically (scaling up is always performed first)
- `//count`: Make numbers human-readable - `//count`: Make numbers human-readable
@ -15,8 +18,12 @@ It's about time I started a changelog! This will serve from now on as the main c
- If you encounter any other issues with it over large areas (particularly 2000x150x2000 and larger), please let me know - If you encounter any other issues with it over large areas (particularly 2000x150x2000 and larger), please let me know
- Bugfix: Fix obscure crash in calls to `human_size` ("unknown" will now be returned if passed junk) - Bugfix: Fix obscure crash in calls to `human_size` ("unknown" will now be returned if passed junk)
- `//many` can now be used with commands with no arguments. - `//many` can now be used with commands with no arguments.
- `//conv`, `//erode`, `//fillcaves`: Treat liquids as air
- Add new [cloud wand](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#cloud-wand)
- `//conv`, `//erode`: Minor refactoring to improve code clarity
## v1.10 (16th January 2021)
## v1.10: The tidyup update (16th January 2021)
- `//maze`: Fix some parts of generated mazes staying solid - `//maze`: Fix some parts of generated mazes staying solid
- `//maze`, `//maze3d`: Allow non-number seeds (existing seeds aren't affected - they will still produce identical output) - `//maze`, `//maze3d`: Allow non-number seeds (existing seeds aren't affected - they will still produce identical output)
- `//many`: Improve format of progress messages, add ETA - `//many`: Improve format of progress messages, add ETA

@ -482,6 +482,15 @@ Executes the given command, and then clips the result to the largest ellipsoid t
//ellipsoidapply layers desert_sand sand 2 desert_sandstone 4 sandstone 10 //ellipsoidapply layers desert_sand sand 2 desert_sandstone 4 sandstone 10
``` ```
## `//srect [<axis1> [<axis2>]] <length>`
Short for _select rectangle_. Sets the pos2 at a set distance along 2 axes from pos1. If the axes aren't specified, defaults to positive y and the direction you are facing. Implementation thanks to @VorTechnix.
```
//srect x z 10
//srect 3
//srect -z y 25
```
## `//y` ## `//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. 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.
@ -530,3 +539,10 @@ 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. 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)
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.

@ -42,3 +42,29 @@ Clearing large amounts of foliage is easy!
``` ```
Adjust the numbers (and direction in the `//shift` command) to match your scenario. Adjust the numbers (and direction in the `//shift` command) to match your scenario.
## Flower Field
Make a flower field with no grass.
```
//overlay air 20 flowers:geranium 1
```
Adjust the air value to change flower density.
```
//overlay air 80 flowers:rose 1 flowers:tulip 1 flowers:dandelion_yellow 1 flowers:chrysanthemum_green 1 flowers:geranium 1 flowers:viola 1 flowers:dandelion_white 1 flowers:tulip_black 1
```
When working with many types of flowers the air values need to be higher to compensate. The best equation for the air value that I've found is `<desired spacing> * <sum of flower probabilities>`.
## Grass Field
Make a grass field
```
//overlay air 36 default:grass_2 2 default:grass_3 2 default:grass_4 1 default:grass_5 1
```
Adjust the air value to change grass density. As with flower field the best equation for the air value is `<desired spacing> * <sum of flower probabilities>`.

@ -13,6 +13,7 @@ _(Do you have a cool build that you used WorldEditAdditions to build? [Get in to
## Table of Contents ## Table of Contents
- [Quick Command Reference](#quick-command-reference) (including links to detailed explanations) - [Quick Command Reference](#quick-command-reference) (including links to detailed explanations)
- [Using the Far Wand](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#far-wand) - [Using the Far Wand](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#far-wand)
- [Using the Cloud Wand](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#cloud-wand)
- [Detailed Chat Command Explanations](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md) - [Detailed Chat Command Explanations](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md)
- [Chat Command Cookbook](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Cookbook.md) - [Chat Command Cookbook](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Cookbook.md)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
@ -38,7 +39,8 @@ The detailed explanations have moved! Check them out [here](https://github.com/s
### Misc ### Misc
- [`//replacemix <target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ....`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#replacemix-target_node-chance-replace_node_a-chance_a-replace_node_b-chance_b-replace_node_n-chance_n-) - [`//replacemix <target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ....`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#replacemix-target_node-chance-replace_node_a-chance_a-replace_node_b-chance_b-replace_node_n-chance_n-)
- [`//floodfill [<replace_node> [<radius>]]`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#floodfill-replace_node-radius-floodfill) - [`//floodfill [<replace_node> [<radius>]]`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#floodfill-replace_node-radius-floodfill)
- [`//scale <axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>]]`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#scale-axis-scale_factor--factor_x-factor_y-factor_z-anchor_x-anchor_y-anchor_z) - [`//scale <axis> <scale_factor> | <factor_x> [<factor_y> <factor_z> [<anchor_x> <anchor_y> <anchor_z>]]`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#scale-axis-scale_factor--factor_x-factor_y-factor_z-anchor_x-anchor_y-anchor_z) **experimental**
- [`//srect [<axis1> [<axis2>]] <length>`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#srect-axis1-axis2-length)
### Terrain ### Terrain
- [`//overlay <node_name_a> [<chance_a>] <node_name_b> [<chance_b>] [<node_name_N> [<chance_N>]] ...`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#overlay-node_name_a-chance_a-node_name_b-chance_b-node_name_n-chance_n-) - [`//overlay <node_name_a> [<chance_a>] <node_name_b> [<chance_b>] [<node_name_N> [<chance_N>]] ...`](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Chat-Command-Reference.md#overlay-node_name_a-chance_a-node_name_b-chance_b-node_name_n-chance_n-)

@ -57,13 +57,9 @@ function worldeditadditions.convolve(pos1, pos2, kernel, kernel_size)
local node_id_air = minetest.get_content_id("air") local node_id_air = minetest.get_content_id("air")
local heightmap = worldeditadditions.make_heightmap(pos1, pos2, manip, area, data) local heightmap, heightmap_size = worldeditadditions.make_heightmap(pos1, pos2, manip, area, data)
local heightmap_conv = worldeditadditions.shallowcopy(heightmap) local heightmap_conv = worldeditadditions.shallowcopy(heightmap)
local heightmap_size = {}
heightmap_size[0] = (pos2.z - pos1.z) + 1
heightmap_size[1] = (pos2.x - pos1.x) + 1
worldeditadditions.conv.convolve( worldeditadditions.conv.convolve(
heightmap_conv, heightmap_conv,
heightmap_size, heightmap_size,

@ -18,18 +18,18 @@ function worldeditadditions.conv.convolve(heightmap, heightmap_size, matrix, mat
border_size[1] = (matrix_size[1]-1) / 2 -- width border_size[1] = (matrix_size[1]-1) / 2 -- width
-- print("[convolve] matrix_size", matrix_size[0], matrix_size[1]) -- print("[convolve] matrix_size", matrix_size[0], matrix_size[1])
-- print("[convolve] border_size", border_size[0], border_size[1]) -- print("[convolve] border_size", border_size[0], border_size[1])
-- print("[convolve] heightmap_size: ", heightmap_size[0], heightmap_size[1]) -- print("[convolve] heightmap_size: ", heightmap_size.z, heightmap_size.x)
-- --
-- print("[convolve] z: from", (heightmap_size[0]-border_size[0]) - 1, "to", border_size[0], "step", -1) -- print("[convolve] z: from", (heightmap_size.z-border_size[0]) - 1, "to", border_size[0], "step", -1)
-- print("[convolve] x: from", (heightmap_size[1]-border_size[1]) - 1, "to", border_size[1], "step", -1) -- print("[convolve] x: from", (heightmap_size.x-border_size[1]) - 1, "to", border_size[1], "step", -1)
-- Convolve over only the bit that allows us to use the full convolution matrix -- Convolve over only the bit that allows us to use the full convolution matrix
for z = (heightmap_size[0]-border_size[0]) - 1, border_size[0], -1 do for z = (heightmap_size.z-border_size[0]) - 1, border_size[0], -1 do
for x = (heightmap_size[1]-border_size[1]) - 1, border_size[1], -1 do for x = (heightmap_size.x-border_size[1]) - 1, border_size[1], -1 do
local total = 0 local total = 0
local hi = (z * heightmap_size[1]) + x local hi = (z * heightmap_size.x) + x
-- print("[convolve/internal] z", z, "x", x, "hi", hi) -- print("[convolve/internal] z", z, "x", x, "hi", hi)
-- No continue statement in Lua :-/ -- No continue statement in Lua :-/
@ -40,7 +40,7 @@ function worldeditadditions.conv.convolve(heightmap, heightmap_size, matrix, mat
local cz = z + (mz - border_size[0]) local cz = z + (mz - border_size[0])
local cx = x + (mx - border_size[1]) local cx = x + (mx - border_size[1])
local i = (cz * heightmap_size[1]) + cx local i = (cz * heightmap_size.x) + cx
-- A value of -1 = nothing in this column (so we should ignore it) -- A value of -1 = nothing in this column (so we should ignore it)
if heightmap[i] ~= -1 then if heightmap[i] ~= -1 then

@ -9,9 +9,10 @@ function worldeditadditions.erode.run(pos1, pos2, algorithm, params)
local manip, area = worldedit.manip_helpers.init(pos1, pos2) local manip, area = worldedit.manip_helpers.init(pos1, pos2)
local data = manip:get_data() local data = manip:get_data()
local heightmap_size = {} local heightmap_size = {
heightmap_size[0] = (pos2.z - pos1.z) + 1 z = (pos2.z - pos1.z) + 1,
heightmap_size[1] = (pos2.x - pos1.x) + 1 x = (pos2.x - pos1.x) + 1
}
local region_height = (pos2.y - pos1.y) + 1 local region_height = (pos2.y - pos1.y) + 1
@ -20,7 +21,7 @@ function worldeditadditions.erode.run(pos1, pos2, algorithm, params)
-- print("[erode.run] algorithm: "..algorithm..", params:"); -- print("[erode.run] algorithm: "..algorithm..", params:");
-- print(worldeditadditions.map_stringify(params)) -- print(worldeditadditions.map_stringify(params))
-- worldeditadditions.print_2d(heightmap, heightmap_size[1]) -- worldeditadditions.print_2d(heightmap, heightmap_size.x)
local success, msg, stats local success, msg, stats
if algorithm == "snowballs" then if algorithm == "snowballs" then
success, msg = worldeditadditions.erode.snowballs(heightmap, heightmap_eroded, heightmap_size, region_height, params) success, msg = worldeditadditions.erode.snowballs(heightmap, heightmap_eroded, heightmap_size, region_height, params)

@ -18,11 +18,11 @@ local function snowball(heightmap, normalmap, heightmap_size, startpos, params)
for i = 1, params.max_steps do for i = 1, params.max_steps do
local x = pos.x local x = pos.x
local z = pos.z local z = pos.z
local hi = math.floor(z+0.5)*heightmap_size[1] + math.floor(x+0.5) local hi = math.floor(z+0.5)*heightmap_size.x + math.floor(x+0.5)
-- Stop if we go out of bounds -- Stop if we go out of bounds
if x < 0 or z < 0 if x < 0 or z < 0
or x >= heightmap_size[1]-1 or z >= heightmap_size[0]-1 then or x >= heightmap_size.x-1 or z >= heightmap_size.z-1 then
-- print("[snowball] hit edge; stopping at ("..x..", "..z.."), (bounds @ "..(heightmap_size[1]-1)..", "..(heightmap_size[0]-1)..")", "x", x, "/", heightmap_size[1]-1, "z", z, "/", heightmap_size[0]-1) -- print("[snowball] hit edge; stopping at ("..x..", "..z.."), (bounds @ "..(heightmap_size.x-1)..", "..(heightmap_size.z-1)..")", "x", x, "/", heightmap_size.x-1, "z", z, "/", heightmap_size.z-1)
return true, i return true, i
end end
@ -100,8 +100,8 @@ function worldeditadditions.erode.snowballs(heightmap_initial, heightmap, height
local success, steps = snowball( local success, steps = snowball(
heightmap, normals, heightmap_size, heightmap, normals, heightmap_size,
{ {
x = math.random() * (heightmap_size[1] - 1), x = math.random() * (heightmap_size.x - 1),
z = math.random() * (heightmap_size[0] - 1) z = math.random() * (heightmap_size.z - 1)
}, },
params params
) )

@ -27,7 +27,7 @@ function worldeditadditions.fillcaves(pos1, pos2, node_name)
for y = pos2.y, pos1.y, -1 do for y = pos2.y, pos1.y, -1 do
local i = area:index(x, y, z) local i = area:index(x, y, z)
local is_air = worldeditadditions.is_airlike(data[i]) local is_air = worldeditadditions.is_airlike(data[i]) or worldeditadditions.is_liquidlike(data[i])
local is_ignore = data[i] == node_id_ignore 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 -- If the previous node was air and this one isn't, then we've found the top level

@ -0,0 +1,3 @@
function worldeditadditions.noise.engine_perlin_2d(x, y)
end

@ -0,0 +1,9 @@
-- ███ ███ █████ ██ ██ ███████ ██████ ██████
-- ████ ████ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ████ ██ ███████ █████ █████ █████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ███████ ███████ ███████ ██████
function worldeditadditions.noise.make_2d(size, noise_func)
-- TODO: Follow https://www.redblobgames.com/maps/terrain-from-noise/
end

@ -0,0 +1,3 @@
worldeditadditions.noise = {}
dofile(worldeditadditions.modpath.."/lib/noise/alg_perlin.lua")

@ -0,0 +1,38 @@
--- Applies a layer of 2D noise over the terrain in the defined region.
-- @module worldeditadditions.noise2d
-- ███ ██ ██████ ██ ███████ ███████ ██████ ██████
-- ████ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ███████ █████ █████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ████ ██████ ██ ███████ ███████ ███████ ██████
--- Applies a layer of 2d noise over the terrain in the defined region.
-- @param pos1 Vector pos1 of the defined region
-- @param pos2 Vector pos2 of the defined region
-- @param noise_params table A noise parameters table. Will be passed unmodified to PerlinNoise() from the Minetest API.
function worldeditadditions.noise2d(pos1, pos2, noise_params)
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
-- 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 heightmap_old, heightmap_size = worldeditadditions.make_heightmap(
pos1, pos2,
manip, area,
data
)
local heightmap_new = worldeditadditions.shallowcopy(heightmap_old)
local perlin_map = PerlinNoiseMap(noise_params, heightmap_size)
local stats = { added = 0, removed = 0 }
-- Save the modified nodes back to disk & return
-- No need to save - this function doesn't actually change anything
worldedit.manip_helpers.finish(manip, data)
return true, stats
end

@ -7,7 +7,7 @@
-- @param manip VoxelManip The VoxelManip object. -- @param manip VoxelManip The VoxelManip object.
-- @param area area The associated area object. -- @param area area The associated area object.
-- @param data table The associated data object. -- @param data table The associated data object.
-- @return table The ZERO-indexed heightmap data (as 1 single flat array). -- @return table,table The ZERO-indexed heightmap data (as 1 single flat array), followed by the size of the heightmap in the form { 0 = size_z, 1 = size_x }.
function worldeditadditions.make_heightmap(pos1, pos2, manip, area, data) function worldeditadditions.make_heightmap(pos1, pos2, manip, area, data)
-- z y x (in reverse for little-endian machines) is the preferred loop order, but that isn't really possible here -- z y x (in reverse for little-endian machines) is the preferred loop order, but that isn't really possible here
@ -20,7 +20,7 @@ function worldeditadditions.make_heightmap(pos1, pos2, manip, area, data)
-- Scan each column top to bottom -- Scan each column top to bottom
for y = pos2.y+1, pos1.y, -1 do for y = pos2.y+1, pos1.y, -1 do
local i = area:index(x, y, z) local i = area:index(x, y, z)
if not worldeditadditions.is_airlike(data[i]) then if not (worldeditadditions.is_airlike(data[i]) or worldeditadditions.is_liquidlike(data[i])) then
-- It's the first non-airlike node in this column -- It's the first non-airlike node in this column
-- Start heightmap values from 1 (i.e. there's at least 1 node in the column) -- Start heightmap values from 1 (i.e. there's at least 1 node in the column)
heightmap[hi] = (y - pos1.y) + 1 heightmap[hi] = (y - pos1.y) + 1
@ -34,7 +34,12 @@ function worldeditadditions.make_heightmap(pos1, pos2, manip, area, data)
end end
end end
return heightmap local heightmap_size = {
z = (pos2.z - pos1.z) + 1,
x = (pos2.x - pos1.x) + 1
}
return heightmap, heightmap_size
end end
--- Calculates a normal map for the given heightmap. --- Calculates a normal map for the given heightmap.
@ -45,27 +50,27 @@ end
-- @param heightmap_size int[] The size of the heightmap in the form [ z, x ] -- @param heightmap_size int[] The size of the heightmap in the form [ z, x ]
-- @return Vector[] The calculated normal map, in the same form as the input heightmap. Each element of the array is a 3D Vector (i.e. { x, y, z }) representing a normal. -- @return Vector[] The calculated normal map, in the same form as the input heightmap. Each element of the array is a 3D Vector (i.e. { x, y, z }) representing a normal.
function worldeditadditions.calculate_normals(heightmap, heightmap_size) function worldeditadditions.calculate_normals(heightmap, heightmap_size)
-- print("heightmap_size: "..heightmap_size[1].."x"..heightmap_size[0]) -- print("heightmap_size: "..heightmap_size.x.."x"..heightmap_size.z)
local result = {} local result = {}
for z = heightmap_size[0]-1, 0, -1 do for z = heightmap_size.z-1, 0, -1 do
for x = heightmap_size[1]-1, 0, -1 do for x = heightmap_size.x-1, 0, -1 do
-- Algorithm ref https://stackoverflow.com/a/13983431/1460422 -- Algorithm ref https://stackoverflow.com/a/13983431/1460422
-- Also ref Vector.mjs, which I implemented myself (available upon request) -- Also ref Vector.mjs, which I implemented myself (available upon request)
local hi = z*heightmap_size[1] + x local hi = z*heightmap_size.x + x
-- Default to this pixel's height -- Default to this pixel's height
local up = heightmap[hi] local up = heightmap[hi]
local down = heightmap[hi] local down = heightmap[hi]
local left = heightmap[hi] local left = heightmap[hi]
local right = heightmap[hi] local right = heightmap[hi]
if z - 1 > 0 then up = heightmap[(z-1)*heightmap_size[1] + x] end if z - 1 > 0 then up = heightmap[(z-1)*heightmap_size.x + x] end
if z + 1 < heightmap_size[0]-1 then down = heightmap[(z+1)*heightmap_size[1] + x] end if z + 1 < heightmap_size.z-1 then down = heightmap[(z+1)*heightmap_size.x + x] end
if x - 1 > 0 then left = heightmap[z*heightmap_size[1] + (x-1)] end if x - 1 > 0 then left = heightmap[z*heightmap_size.x + (x-1)] end
if x + 1 < heightmap_size[1]-1 then right = heightmap[z*heightmap_size[1] + (x+1)] end if x + 1 < heightmap_size.x-1 then right = heightmap[z*heightmap_size.x + (x+1)] end
-- print("[normals] UP | index", (z-1)*heightmap_size[1] + x, "z", z, "z-1", z - 1, "up", up, "limit", 0) -- print("[normals] UP | index", (z-1)*heightmap_size.x + x, "z", z, "z-1", z - 1, "up", up, "limit", 0)
-- print("[normals] DOWN | index", (z+1)*heightmap_size[1] + x, "z", z, "z+1", z + 1, "down", down, "limit", heightmap_size[1]-1) -- print("[normals] DOWN | index", (z+1)*heightmap_size.x + x, "z", z, "z+1", z + 1, "down", down, "limit", heightmap_size.x-1)
-- print("[normals] LEFT | index", z*heightmap_size[1] + (x-1), "x", x, "x-1", x - 1, "left", left, "limit", 0) -- print("[normals] LEFT | index", z*heightmap_size.x + (x-1), "x", x, "x-1", x - 1, "left", left, "limit", 0)
-- print("[normals] RIGHT | index", z*heightmap_size[1] + (x+1), "x", x, "x+1", x + 1, "right", right, "limit", heightmap_size[1]-1) -- print("[normals] RIGHT | index", z*heightmap_size.x + (x+1), "x", x, "x+1", x + 1, "right", right, "limit", heightmap_size.x-1)
result[hi] = worldeditadditions.vector.normalize({ result[hi] = worldeditadditions.vector.normalize({
x = left - right, x = left - right,
@ -82,10 +87,11 @@ end
function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heightmap_old, heightmap_new, heightmap_size) function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heightmap_old, heightmap_new, heightmap_size)
local stats = { added = 0, removed = 0 } local stats = { added = 0, removed = 0 }
local node_id_air = minetest.get_content_id("air") local node_id_air = minetest.get_content_id("air")
local node_id_ignore = minetest.get_content_id("ignore")
for z = heightmap_size[0], 0, -1 do for z = heightmap_size.z, 0, -1 do
for x = heightmap_size[1], 0, -1 do for x = heightmap_size.x, 0, -1 do
local hi = z*heightmap_size[1] + x local hi = z*heightmap_size.x + x
local height_old = heightmap_old[hi] local height_old = heightmap_old[hi]
local height_new = heightmap_new[hi] local height_new = heightmap_new[hi]
@ -95,12 +101,23 @@ function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heig
if height_old == height_new then if height_old == height_new then
-- noop -- noop
elseif height_new < height_old then elseif height_new < height_old then
local node_id_replace = data[area:index(
pos1.x + x,
pos1.y + height_old + 1,
pos1.z + z
)]
-- Unlikely, but if it can happen, it *will* happen.....
if node_id_replace == node_id_ignore then
node_id_replace = node_id_air
end
stats.removed = stats.removed + (height_old - height_new) stats.removed = stats.removed + (height_old - height_new)
local y = height_new local y = height_new
while y < height_old do while y < height_old do
local ci = area:index(pos1.x + x, pos1.y + y, pos1.z + z) local ci = area:index(pos1.x + x, pos1.y + y, pos1.z + z)
-- print("[conv/save] remove at y", y, "→", pos1.y + y, "current:", minetest.get_name_from_content_id(data[ci])) -- print("[conv/save] remove at y", y, "→", pos1.y + y, "current:", minetest.get_name_from_content_id(data[ci]))
data[ci] = node_id_air if data[ci] ~= node_id_ignore then
data[ci] = node_id_replace
end
y = y + 1 y = y + 1
end end
else -- height_new > height_old else -- height_new > height_old
@ -113,7 +130,9 @@ function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heig
while y < height_new do while y < height_new do
local ci = area:index(pos1.x + x, pos1.y + y, pos1.z + z) local ci = area:index(pos1.x + x, pos1.y + y, pos1.z + z)
-- print("[conv/save] add at y", y, "→", pos1.y + y, "current:", minetest.get_name_from_content_id(data[ci])) -- print("[conv/save] add at y", y, "→", pos1.y + y, "current:", minetest.get_name_from_content_id(data[ci]))
data[ci] = node_id if data[ci] ~= node_id_ignore then
data[ci] = node_id
end
y = y + 1 y = y + 1
end end
end end

@ -40,3 +40,36 @@ function worldeditadditions.vector.floor(v)
-- Some vectors are 2d -- Some vectors are 2d
if v.z then v.z = math.floor(v.z) end if v.z then v.z = math.floor(v.z) end
end end
--- Determines if the target point is contained within the defined worldedit region.
-- @param pos1 Vector pos1 of the defined region.
-- @param pos2 Vector pos2 of the defined region.
-- @param target Vector The target vector to check.
-- @return boolean Whether the given target is contained within the defined worldedit region.
function worldeditadditions.vector.is_contained(pos1, pos2, target)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
return pos1.x >= target.x
and pos1.y >= target.y
and pos1.z >= target.z
and pos2.x <= target.x
and pos2.y <= target.y
and pos2.z <= target.z
end
--- Expands the defined region to include the given point.
-- @param pos1 Vector pos1 of the defined region.
-- @param pos2 Vector pos2 of the defined region.
-- @param target Vector The target vector to include.
function worldeditadditions.vector.expand_region(pos1, pos2, target)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
if target.x < pos1.x then pos1.x = target.x end
if target.y < pos1.y then pos1.y = target.y end
if target.z < pos1.z then pos1.z = target.z end
if target.x > pos2.x then pos2.x = target.x end
if target.y > pos2.y then pos2.y = target.y end
if target.z > pos2.z then pos2.z = target.z end
return pos1, pos2
end

@ -28,7 +28,6 @@ worldedit.register_command("scale", {
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 2, require_pos = 2,
parse = function(params_text) parse = function(params_text)
print("[DEBUG//scale] got params_text '"..params_text.."'")
if not params_text then params_text = "" end if not params_text then params_text = "" end
local parts = worldeditadditions.split(params_text, "%s+", false) local parts = worldeditadditions.split(params_text, "%s+", false)

@ -3,42 +3,36 @@
-- ███████ ██████ █████ ██ ██ -- ███████ ██████ █████ ██ ██
-- ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ███████ ██████ ██ -- ███████ ██ ██ ███████ ██████ ██
-- local -- TODO: set this to local once development is finished
function parse_params_srect(params_text)
local wea = worldeditadditions
local find = wea.split(params_text, "%s", false)
local ax1, ax2, len = find[1], find[2], find[table.maxn(find)]
-- If ax1 is bad set to player facing dir
if ax1 == len or not ax1:match('[xyz]') then ax1 = "get"
else
local success, value = wea.getsign(ax1, "int")
if not success then return success, value
else ax1 = { value, ax1:gsub('[^xyz]',''):sub(1,1) }
end
end
-- If ax2 is bad set to +y
if not ax2 or ax2 == len or not ax2:match('[xyz]') then ax2 = "y" end
local success, value = wea.getsign(ax2, "int")
if not success then return success, value end
ax2 = { value, ax2:gsub('[^xyz]',''):sub(1,1) }
len = tonumber(len)
-- If len == nill cancel the operation
if len == nil then
return false, "No length specified."
end
return true, ax1, ax2, len
end
worldedit.register_command("srect", { worldedit.register_command("srect", {
params = "[<axis1> [<axis2>]] <length>", params = "[<axis1> [<axis2>]] <length>",
description = "Set WorldEdit region position 2 at a set distance along 2 axes.", description = "Set WorldEdit region position 2 at a set distance along 2 axes.",
privs = {worldedit=true}, privs = { worldedit = true },
require_pos = 1, require_pos = 1,
parse = function(params_text) parse = function(params_text)
local values = {parse_params_srect(params_text)} local wea = worldeditadditions
return unpack(values) local find = wea.split(params_text, "%s", false)
local ax1, ax2, len = find[1], find[2], find[table.maxn(find)]
-- If ax1 is bad set to player facing dir
if ax1 == len or not ax1:match('[xyz]') then ax1 = "get"
else
local success, value = wea.getsign(ax1, "int")
if not success then return success, value
else ax1 = { value, ax1:gsub('[^xyz]', ''):sub(1, 1) } end
end
-- If ax2 is bad set to +y
if not ax2 or ax2 == len or not ax2:match('[xyz]') then ax2 = "y" end
local success, value = wea.getsign(ax2, "int")
if not success then return success, value end
ax2 = { value, ax2:gsub('[^xyz]', ''):sub(1, 1) }
len = tonumber(len)
-- If len == nill cancel the operation
if len == nil then
return false, "No length specified."
end
return true, ax1, ax2, len
end, end,
func = function(name, axis1, axis2, len) func = function(name, axis1, axis2, len)
if axis1 == "get" then axis1 = worldeditadditions.player_axis2d(name) end if axis1 == "get" then axis1 = worldeditadditions.player_axis2d(name) end

@ -1 +1 @@
{"modelVersion":2,"piskel":{"name":"worldedit_wand","description":"","fps":0,"height":16,"width":16,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":3,\"chunks\":[{\"layout\":[[0],[1],[2]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAACWklEQVRIS82WXUhTYRjH/yd31ra2viQ5cxFRJlbUKre6CI2gm6AP2kV4FVSGXiZFfoSErCi76OOmQLqJERWkkMFuKqQsShIxvQrbyMK2yMztzNa2sz1xFkdW2fa+BwXP9ft/zu/3vO/zniP4BJCtAniVlNAeCAvQ8WSelJIQlJGekmFoAFcNqW4hWVYasdT6DJOxXQi2ylx5wb9JoNfvCFv2V8PT+ZwrrLpSt43SkRSK4mJWAEnA0Mgusc8XoEk5lm3bj0QaAw3buBiEpjI7OStWYai3D+XbV+Po4w9cBajDRtqm6RUY+VaJlJxGkfgURx5Vo/XlT2aG6YU3qyQKhcOwbHCh6WE/cwHlKqYF1O5f7DZxAWy9V0IJOYX0FCE5fh/Heg5y5f8AvV5TRcGBXqx3O1F/5y27RPtvCRXeZTeiP5TkglCz3p2mbA3e/D+QbXskikbGYSmrhPduH7OEdoxUEF6I3HuDNz8j4GmPk6QFUQhmK075hue1xH/hmvfaabFZRGb5Wpy91TNvJfKCXajdTeJEEIogoqXzvS4JaYkZ4Uiceya0uSiULwh147iLPo8GIDo24tztFwXX//0dVM+0CjHyNY7Lb9ivx9yZypdnAmqpcVOpIYYvE1F4/WNMmVyRM24TrVuhXyJfnhnm/KE1ZBEFfFQW4VrXEHNOE5krCS6Qk57N5DDKiCkZtD0Y5cqqInMhwQ3ReECikmIrxhQrrvgGufOzLcENoHayuXYHJULDKHcUo77jE3eN2ZTgfrl2pi8dttEyRUZdF/uf50yDfcL/XReD1oRfxpVUxz8/zhUAAAAASUVORK5CYII=\"}]}"]}} {"modelVersion":2,"piskel":{"name":"worldedit_wand","description":"","fps":0,"height":16,"width":16,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":4,\"chunks\":[{\"layout\":[[0],[1],[2],[3]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAQCAYAAACm53kpAAACy0lEQVRYR92XXUhTYRzGn5M7083NMks2FxGmYp9Wal2ERhBE0Ad5EUIQfRh6mRT5ERJiX3bRx42BdBMjKkgpI28qLE3KEimlC5kbmugmms6d6dqX/ziLI1aa77sYjM71nv/7/H7vOe/ZEcwCSJ8JvPMZUGt1CAjjmnmZQoJNQnBKgqoUXDMMxbGkXaXGMt0bON27YKuSuPJKXSIi+4gbU9MBpKcmMs8QmjcJ9L6XsOVAPgoaWpmDsws36Sk46UeMRwwJgA9QlbFL2G+2klNyh8ZNe4PoKt3G3WHYIVFCggYulwfjTg82ZiYzzxDK04yUlbka3W0dyNi+Bide9DOH5dJUrydFRrgCLN+y4ZeCiBFf4dizfFS1f+fqYLFNULxWxb37cu/Zhe7kGcjucEC7PgflTzuZCwRuYlaAvPtXm+K4ALY+TCav5EdwiuAbe4STLYe48vKt/6V3FLFqEUP2MTidE+i5wi7xF9DbhXlk62rDutwslNz/zC6h9qcEGT7HqEan3ccFIWdrdsaFZvDmX7dbyGRcAa/Pjz5rf0jAYB27xD8gq/cYyDU5Bm1aNmoedDBLUB4DGYQXYu65y5t/8vwDydDHj+4VwpE4L+C5giwyLHFB0Ohw1twT9RJ+f3HxSFwQrmKfkRI0ImaWr8WFuy3/rYS/gl0u2k3iuA0BQURlQ19YEgxLNXBMerjPBOVciHR+Uai6Uzk0PGCFaNqAi/feLvr7+W5HGcIy6sH1j3yvt7kSIpVnAqoszKUUlRsj4y7UNA8xZeaKOJ8bR+krw5cQyTwzzKXDqaQVBXwNxONWYzdzThERSQiWf+8Lrc8FcqZgM5nUEtyBGVQ/HuDKyiWjUQI3RNlBAyUn6TAU0OGG+RN3PtokcAPIO1lRtIO89h5kmJJQUj/IPSOaJHCXV563a0f0lBiQUNzI/uU338F4unkirA6KxH/N/wCTFeDHmzMoJgAAAABJRU5ErkJggg==\"}]}"]}}

@ -4,6 +4,8 @@ worldeditadditions.farwand = {
local modpath = minetest.get_modpath("worldeditadditions_farwand") local modpath = minetest.get_modpath("worldeditadditions_farwand")
dofile(modpath.."/lib/do_raycast.lua")
dofile(modpath.."/lib/farwand.lua") dofile(modpath.."/lib/farwand.lua")
dofile(modpath.."/lib/cloudwand.lua")
dofile(modpath.."/lib/chatcommand.lua") dofile(modpath.."/lib/chatcommand.lua")
dofile(modpath.."/lib/settings.lua") dofile(modpath.."/lib/settings.lua")

@ -0,0 +1,60 @@
local function add_point(name, pos)
if pos ~= nil then
-- print("[set_pos1]", name, "("..pos.x..", "..pos.y..", "..pos.z..")")
if not worldedit.pos1[name] then worldedit.pos1[name] = vector.new(pos) end
if not worldedit.pos2[name] then worldedit.pos2[name] = vector.new(pos) end
worldedit.marker_update(name)
local volume_before = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
worldedit.pos1[name], worldedit.pos2[name] = worldeditadditions.vector.expand_region(worldedit.pos1[name], worldedit.pos2[name], pos)
local volume_after = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
local volume_difference = volume_after - volume_before
worldedit.marker_update(name)
worldedit.player_notify(name, "Expanded region by "..volume_difference.." nodes")
else
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
-- print("[set_pos1]", name, "nil")
end
end
local function clear_points(name, pos)
worldedit.pos1[name] = nil
worldedit.pos2[name] = nil
worldedit.marker_update(name)
worldedit.set_pos[name] = nil
worldedit.player_notify(name, "Region cleared")
end
minetest.register_tool(":worldeditadditions:cloudwand", {
description = "WorldEditAdditions far-reaching point cloud wand",
inventory_image = "worldeditadditions_cloudwand.png",
on_place = function(itemstack, player, pointed_thing)
local name = player:get_player_name()
-- print("[farwand] on_place", name)
-- Right click when pointing at something
-- Pointed thing: https://rubenwardy.com/minetest_modding_book/lua_api.html#pointed_thing
clear_points(name)
end,
on_use = function(itemstack, player, pointed_thing)
local name = player:get_player_name()
-- print("[farwand] on_use", name)
local looking_pos, node_id = worldeditadditions.farwand.do_raycast(player)
add_point(name, looking_pos)
-- Left click when pointing at something or nothing
end,
on_secondary_use = function(itemstack, player, pointed_thing)
local name = player:get_player_name()
-- Right click when pointing at nothing
-- print("[farwand] on_secondary_use", name)
clear_points(name)
end
})

@ -0,0 +1,17 @@
--- worldeditadditions.raycast() wrapper
function worldeditadditions.farwand.do_raycast(player)
if player == nil then return nil end
local player_name = player:get_player_name()
if worldeditadditions.farwand.player_data[player_name] == nil then
worldeditadditions.farwand.player_data[player_name] = { maxdist = 1000, skip_liquid = true }
end
local looking_pos, node_id = worldeditadditions.raycast(
player,
worldeditadditions.farwand.player_data[player_name].maxdist,
worldeditadditions.farwand.player_data[player_name].skip_liquid
)
return looking_pos, node_id
end

@ -5,6 +5,7 @@ local function set_pos1(name, pos)
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
worldedit.player_notify(name, "pos1 set to "..worldeditadditions.vector.tostring(pos)) worldedit.player_notify(name, "pos1 set to "..worldeditadditions.vector.tostring(pos))
else else
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
-- print("[set_pos1]", name, "nil") -- print("[set_pos1]", name, "nil")
end end
end end
@ -15,6 +16,7 @@ local function set_pos2(name, pos)
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.player_notify(name, "pos2 set to "..worldeditadditions.vector.tostring(pos)) worldedit.player_notify(name, "pos2 set to "..worldeditadditions.vector.tostring(pos))
else else
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
-- print("[set_pos2]", name, "nil") -- print("[set_pos2]", name, "nil")
end end
end end

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B