mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-12-26 13:25:02 +00:00
commit
7f4ebb8b8d
23 changed files with 305 additions and 78 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -2,7 +2,10 @@
|
|||
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**)
|
||||
- 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
|
||||
|
@ -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
|
||||
- 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.
|
||||
- `//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`, `//maze3d`: Allow non-number seeds (existing seeds aren't affected - they will still produce identical output)
|
||||
- `//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
|
||||
```
|
||||
|
||||
## `//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`
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
|
26
Cookbook.md
26
Cookbook.md
|
@ -42,3 +42,29 @@ Clearing large amounts of foliage is easy!
|
|||
```
|
||||
|
||||
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
|
||||
- [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 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)
|
||||
- [Chat Command Cookbook](https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/Cookbook.md)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
@ -38,7 +39,8 @@ The detailed explanations have moved! Check them out [here](https://github.com/s
|
|||
### 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-)
|
||||
- [`//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
|
||||
- [`//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 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_size = {}
|
||||
heightmap_size[0] = (pos2.z - pos1.z) + 1
|
||||
heightmap_size[1] = (pos2.x - pos1.x) + 1
|
||||
|
||||
worldeditadditions.conv.convolve(
|
||||
heightmap_conv,
|
||||
heightmap_size,
|
||||
|
|
|
@ -18,18 +18,18 @@ function worldeditadditions.conv.convolve(heightmap, heightmap_size, matrix, mat
|
|||
border_size[1] = (matrix_size[1]-1) / 2 -- width
|
||||
-- print("[convolve] matrix_size", matrix_size[0], matrix_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] x: from", (heightmap_size[1]-border_size[1]) - 1, "to", border_size[1], "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.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
|
||||
for z = (heightmap_size[0]-border_size[0]) - 1, border_size[0], -1 do
|
||||
for x = (heightmap_size[1]-border_size[1]) - 1, border_size[1], -1 do
|
||||
for z = (heightmap_size.z-border_size[0]) - 1, border_size[0], -1 do
|
||||
for x = (heightmap_size.x-border_size[1]) - 1, border_size[1], -1 do
|
||||
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)
|
||||
|
||||
-- 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 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)
|
||||
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 data = manip:get_data()
|
||||
|
||||
local heightmap_size = {}
|
||||
heightmap_size[0] = (pos2.z - pos1.z) + 1
|
||||
heightmap_size[1] = (pos2.x - pos1.x) + 1
|
||||
local heightmap_size = {
|
||||
z = (pos2.z - pos1.z) + 1,
|
||||
x = (pos2.x - pos1.x) + 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(worldeditadditions.map_stringify(params))
|
||||
-- worldeditadditions.print_2d(heightmap, heightmap_size[1])
|
||||
-- worldeditadditions.print_2d(heightmap, heightmap_size.x)
|
||||
local success, msg, stats
|
||||
if algorithm == "snowballs" then
|
||||
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
|
||||
local x = pos.x
|
||||
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
|
||||
if x < 0 or z < 0
|
||||
or x >= heightmap_size[1]-1 or z >= heightmap_size[0]-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)
|
||||
or x >= heightmap_size.x-1 or z >= heightmap_size.z-1 then
|
||||
-- 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
|
||||
end
|
||||
|
||||
|
@ -100,8 +100,8 @@ function worldeditadditions.erode.snowballs(heightmap_initial, heightmap, height
|
|||
local success, steps = snowball(
|
||||
heightmap, normals, heightmap_size,
|
||||
{
|
||||
x = math.random() * (heightmap_size[1] - 1),
|
||||
z = math.random() * (heightmap_size[0] - 1)
|
||||
x = math.random() * (heightmap_size.x - 1),
|
||||
z = math.random() * (heightmap_size.z - 1)
|
||||
},
|
||||
params
|
||||
)
|
||||
|
|
|
@ -27,7 +27,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])
|
||||
local is_air = worldeditadditions.is_airlike(data[i]) or worldeditadditions.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
|
||||
|
|
3
worldeditadditions/lib/noise/engines.lua
Normal file
3
worldeditadditions/lib/noise/engines.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
function worldeditadditions.noise.engine_perlin_2d(x, y)
|
||||
|
||||
end
|
9
worldeditadditions/lib/noise/make_2d.lua
Normal file
9
worldeditadditions/lib/noise/make_2d.lua
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
-- ███ ███ █████ ██ ██ ███████ ██████ ██████
|
||||
-- ████ ████ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██ ████ ██ ███████ █████ █████ █████ ██ ██
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██ ██ ██ ██ ██ ██ ███████ ███████ ███████ ██████
|
||||
function worldeditadditions.noise.make_2d(size, noise_func)
|
||||
-- TODO: Follow https://www.redblobgames.com/maps/terrain-from-noise/
|
||||
end
|
3
worldeditadditions/lib/noise/noise.lua
Normal file
3
worldeditadditions/lib/noise/noise.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
worldeditadditions.noise = {}
|
||||
|
||||
dofile(worldeditadditions.modpath.."/lib/noise/alg_perlin.lua")
|
38
worldeditadditions/lib/noise/noise2d.lua
Normal file
38
worldeditadditions/lib/noise/noise2d.lua
Normal file
|
@ -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 area area The associated area 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)
|
||||
-- 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
|
||||
for y = pos2.y+1, pos1.y, -1 do
|
||||
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
|
||||
-- Start heightmap values from 1 (i.e. there's at least 1 node in the column)
|
||||
heightmap[hi] = (y - pos1.y) + 1
|
||||
|
@ -34,7 +34,12 @@ function worldeditadditions.make_heightmap(pos1, pos2, manip, area, data)
|
|||
end
|
||||
end
|
||||
|
||||
return heightmap
|
||||
local heightmap_size = {
|
||||
z = (pos2.z - pos1.z) + 1,
|
||||
x = (pos2.x - pos1.x) + 1
|
||||
}
|
||||
|
||||
return heightmap, heightmap_size
|
||||
end
|
||||
|
||||
--- 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 ]
|
||||
-- @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)
|
||||
-- print("heightmap_size: "..heightmap_size[1].."x"..heightmap_size[0])
|
||||
-- print("heightmap_size: "..heightmap_size.x.."x"..heightmap_size.z)
|
||||
local result = {}
|
||||
for z = heightmap_size[0]-1, 0, -1 do
|
||||
for x = heightmap_size[1]-1, 0, -1 do
|
||||
for z = heightmap_size.z-1, 0, -1 do
|
||||
for x = heightmap_size.x-1, 0, -1 do
|
||||
-- Algorithm ref https://stackoverflow.com/a/13983431/1460422
|
||||
-- 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
|
||||
local up = heightmap[hi]
|
||||
local down = heightmap[hi]
|
||||
local left = heightmap[hi]
|
||||
local right = heightmap[hi]
|
||||
if z - 1 > 0 then up = heightmap[(z-1)*heightmap_size[1] + x] end
|
||||
if z + 1 < heightmap_size[0]-1 then down = heightmap[(z+1)*heightmap_size[1] + x] end
|
||||
if x - 1 > 0 then left = heightmap[z*heightmap_size[1] + (x-1)] end
|
||||
if x + 1 < heightmap_size[1]-1 then right = heightmap[z*heightmap_size[1] + (x+1)] end
|
||||
if z - 1 > 0 then up = heightmap[(z-1)*heightmap_size.x + 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.x + (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] DOWN | index", (z+1)*heightmap_size[1] + x, "z", z, "z+1", z + 1, "down", down, "limit", heightmap_size[1]-1)
|
||||
-- print("[normals] LEFT | index", z*heightmap_size[1] + (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] 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.x + x, "z", z, "z+1", z + 1, "down", down, "limit", heightmap_size.x-1)
|
||||
-- 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.x + (x+1), "x", x, "x+1", x + 1, "right", right, "limit", heightmap_size.x-1)
|
||||
|
||||
result[hi] = worldeditadditions.vector.normalize({
|
||||
x = left - right,
|
||||
|
@ -82,10 +87,11 @@ end
|
|||
function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heightmap_old, heightmap_new, heightmap_size)
|
||||
local stats = { added = 0, removed = 0 }
|
||||
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 x = heightmap_size[1], 0, -1 do
|
||||
local hi = z*heightmap_size[1] + x
|
||||
for z = heightmap_size.z, 0, -1 do
|
||||
for x = heightmap_size.x, 0, -1 do
|
||||
local hi = z*heightmap_size.x + x
|
||||
|
||||
local height_old = heightmap_old[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
|
||||
-- noop
|
||||
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)
|
||||
local y = height_new
|
||||
while y < height_old do
|
||||
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]))
|
||||
data[ci] = node_id_air
|
||||
if data[ci] ~= node_id_ignore then
|
||||
data[ci] = node_id_replace
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
else -- height_new > height_old
|
||||
|
@ -113,7 +130,9 @@ function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heig
|
|||
while y < height_new do
|
||||
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]))
|
||||
data[ci] = node_id
|
||||
if data[ci] ~= node_id_ignore then
|
||||
data[ci] = node_id
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,3 +40,36 @@ function worldeditadditions.vector.floor(v)
|
|||
-- Some vectors are 2d
|
||||
if v.z then v.z = math.floor(v.z) 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 },
|
||||
require_pos = 2,
|
||||
parse = function(params_text)
|
||||
print("[DEBUG//scale] got params_text '"..params_text.."'")
|
||||
if not params_text then params_text = "" end
|
||||
|
||||
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", {
|
||||
params = "[<axis1> [<axis2>]] <length>",
|
||||
description = "Set WorldEdit region position 2 at a set distance along 2 axes.",
|
||||
privs = {worldedit=true},
|
||||
privs = { worldedit = true },
|
||||
require_pos = 1,
|
||||
parse = function(params_text)
|
||||
local values = {parse_params_srect(params_text)}
|
||||
return unpack(values)
|
||||
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,
|
||||
func = function(name, axis1, axis2, len)
|
||||
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")
|
||||
|
||||
dofile(modpath.."/lib/do_raycast.lua")
|
||||
dofile(modpath.."/lib/farwand.lua")
|
||||
dofile(modpath.."/lib/cloudwand.lua")
|
||||
dofile(modpath.."/lib/chatcommand.lua")
|
||||
dofile(modpath.."/lib/settings.lua")
|
||||
|
|
60
worldeditadditions_farwand/lib/cloudwand.lua
Normal file
60
worldeditadditions_farwand/lib/cloudwand.lua
Normal file
|
@ -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
|
||||
})
|
17
worldeditadditions_farwand/lib/do_raycast.lua
Normal file
17
worldeditadditions_farwand/lib/do_raycast.lua
Normal file
|
@ -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.player_notify(name, "pos1 set to "..worldeditadditions.vector.tostring(pos))
|
||||
else
|
||||
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
|
||||
-- print("[set_pos1]", name, "nil")
|
||||
end
|
||||
end
|
||||
|
@ -15,6 +16,7 @@ local function set_pos2(name, pos)
|
|||
worldedit.mark_pos2(name)
|
||||
worldedit.player_notify(name, "pos2 set to "..worldeditadditions.vector.tostring(pos))
|
||||
else
|
||||
worldedit.player_notify(name, "Error: Too far away (try raising your maxdist with //farwand maxdist <number>)")
|
||||
-- print("[set_pos2]", name, "nil")
|
||||
end
|
||||
end
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 181 B |
Loading…
Reference in a new issue